Using Electron window objects from window Manager - window

I'm trying to send messages to my main window like show and hide, but it keeps saying that my main window is undefined. I'm not sure why. When i try assigning the result of open on the window manager the api says that it returns the object, but that doesn't seem to work how i understand it. Could you guys help me?
const electron = require('electron')
const Menu = require('electron').Menu
var path = require('electron').path
var windowManager = require('electron-window-manager');
const app = electron.app
const BrowserWindow = electron.BrowserWindow
var Tray = require('electron').Tray
var nativeImage = require('electron').nativeImage
var tray = null
var myValue;
var mainWindow;
const notifier = require('node-notifier');
var notified = 0;
// Modify the user agent for all requests to the following urls.
const filter = {
urls: ['https://*.github.com/*', '*://electron.github.io']
}
app.on('ready', function(){
var icon1 = nativeImage.createFromPath(`${__dirname}/../build/timer.png`)
tray = new Tray(icon1)
tray.setToolTip('Timer')
var icon2 = nativeImage.createFromPath(`${__dirname}/../build/timer.png`)
var contextMenu = Menu.buildFromTemplate([
{ label: 'Open Window', click: function(){
windowManager.get('home').show();
} },
{ label: 'Exit', click: function(){
app.isQuiting = true;
app.quit();
}
}
]);
tray.setContextMenu(contextMenu)
tray.setImage(icon1)
tray.on('click', () => {
windowManager.open('home', 'welcome', `file://${__dirname}/index.html`)
})
windowManager.init({
'onclose': function(event){
event.preventDefault()
}
});
// Open a window
windowManager.open('home', 'welcome', `file://${__dirname}/index.html`);
//mainWindow.loadURL(`file://${__dirname}/index.html`)
notifier.on('click', function(notifierObject, options) {
windowManager.get("home").show();
notified = 0;
})
notifier.on('timeout', function (notifierObject, options) {
// Triggers if `wait: true` and notification closes
mainWindow.show();
notified = 0;
});
/*
mainWindow.on('minimize',function(event){
event.preventDefault()
mainWindow.hide();
});
mainWindow.on('close', function (event) {
if( !app.isQuiting){
event.preventDefault()
mainWindow.hide();
}
return false;
});
*/
})

I found the solution. windowManager.createNew(....) only gives you a window object, in order to get a browser window, which allows you to show, hide, and add listeners, you do something like this
mainWindow = windowManager.createNew('home', 'welcome', file://${__dirname}/index.html);
mainWindow.create();
mainWindow.object.show();

Related

response from await browser.tabs.sendMessage is set in chrome, but not in firefox

I have successfully used await browser.tabs.sendMessage in chrome to get response from the listener, but the same code in firefox does not work. await browser.tabs.sendMessage return immediately and sets response to undefined. In content script inject.js, sendResponse should be called after 1000ms timeout.
I attached a minimalistic example. Any idea why await browser.tabs.sendMessage
returns what sendResponse set only in chrome, but not in firefox?
//inject.js
(async () => {
if (typeof browser === "undefined") {
var browser = chrome;
}
browser.runtime.onMessage.addListener((msg, sender, sendResponse) => {
console.log(msg);
setTimeout(function(){
let pageObject = {a:1};
sendResponse(pageObject);
},1000)
return true;
});
})();
//background.js
(async () => {
if (typeof browser === "undefined") {
var browser = chrome;
}
//**code for injecting content scripts on extension reload**
browser.runtime.onInstalled.addListener(async () => {
let manifest = browser.runtime.getManifest();
for (const cs of manifest.content_scripts) {
for (const tab of await browser.tabs.query({ url: cs.matches })) {
browser.scripting.executeScript({
target: { tabId: tab.id },
files: cs.js,
});
}
}
});
async function SendMessageToFront(message) {
let resolve;
const promise = new Promise(r => resolve = r);
browser.tabs.query({}, async function (tabs) {
for (let index = 0; index < tabs.length; index++) {
const tab = tabs[index];
if (tab.url) {
let url = new URL(tab.url)
if (url.hostname.includes("tragetdomain.com")) {
var startTime = performance.now()
let response = await browser.tabs.sendMessage(tab.id, { message: message });
var endTime = performance.now()
console.log(`Call to doSomething took ${endTime - startTime} milliseconds`) // this takes 0ms
console.log("got response");
console.log(response); // this is undefined
console.log(browser.runtime.lastError); // this is empty
resolve(response);
break;
}
}
}
});
return promise;
}
await SendMessageToFront();
})();
I guess for the tests in firefox you do the reload of the background script (F5 or the specific button in devtools)
Just as you have coded the background you have little hope of getting an answer because every time you reload the background you break the wire with all content scripts injected into the page(s).
Move the browser check inside the "SendMessageToFront" function. Move the "SendMessageToFront" function (async is not needed) to the main thread and run that function in the main thread.
/*async*/ function SendMessageToFront(message) {
if (typeof browser === "undefined")
var browser = chrome;
let resolve;
const promise = new Promise(r => resolve = r);
browser.tabs.query({}, async function(tabs) {
for (let index = 0; index < tabs.length; index++) {
const tab = tabs[index];
if (tab.url) {
let url = new URL(tab.url);
if (url.hostname.includes("tragetdomain.com")) {
var startTime = performance.now()
let response = await browser.tabs.sendMessage(tab.id, {'message': message});
var endTime = performance.now()
console.log(`Call to doSomething took ${endTime - startTime} milliseconds`) // this takes 0ms
console.log("got response");
console.log(response); // this is undefined
console.log(browser.runtime.lastError); // this is empty
resolve(response);
break
}
}
}
});
return promise
}
(async _ => {
await SendMessageToFront()
})();
in this way you will get an error message as soon as the background is ready which tells you that the content script on the other side does not exists or it's not ready yet, but now, when the content script will be ready, you should just re-launch the function from the background script devtools
(async _ => {
await SendMessageToFront()
})();
this time you will get the correct answer {a: 1}

Firefox Addon - Accessing pre-loaded content script from ActionButton

I am attaching content script to every tab on my firefox add-on.
And if user clicks on ActionButton (top right icon on browser) I am trying to access content script handler function, but it does not work.
I am using walkthroughs from Content Scripts but still could not make it work.
Am I doing something wrong?
Please see code below the TODO marked areas are not working;
// main.js
tabs.on('ready', function (tab) {
var worker = tab.attach({
include: "*",
//contentScript: 'window.alert("Page matches ruleset");',
contentStyleFile: [data.url("scripts/myTestContent1.js")]
});
worker.port.on('listener1', function (params) {
// I will listen some messages coming from myTestContent1.js
});
console.log("main.js: tab is ready!");
});
// browser icon typically at top right
var button = buttons.ActionButton({
id: "myActionButton",
label: "My pretty add-on",
icon: {
"16": "./icon-16.png",
"32": "./icon-32.png",
"64": "./icon-64.png"
},
onClick: handleClick
});
// when top right browser icon button is clicked
function handleClick(state) {
var myWorker = tabs.activeTab.attach({
//contentScriptFile: // I do not want to attach anything, just get Active tab!
});
// TODO this code is not handled by active tab's content script
// should be handled by myTestContent1.js but does not work
myWorker.port.emit("initialize", "Message from the add-on");
}
myWorker.port.emit is not calling handler function on my content script.
// myTestContent1.js
// TODO this code is not calling :(
self.port.on("initialize", function () {
alert('self.port.on("initialize")');
});
I fixed the problem by keep tracking tab-worker pairs, so
var TabWorkerPair = function (tabid, worker) {
this.tabid = tabid;
this.worker = worker;
};
tabs.on('close', function (tab) {
for (var i = 0; i < TabWorkerPairList.length; i++) {
var pair = TabWorkerPairList[i];
if (pair.tabid == tab.id) {
// remove object from list
TabWorkerPairList.splice(i, 1);
break;
}
}
console.log("main.js > tabs.on('close') > TabWorkerPairList count: " + TabWorkerPairList.length);
});
tabs.on('ready', function (tab) {
var worker = tab.attach({
include: "*",
contentScriptFile: [data.url("scripts/myTestContent1.js")]
});
// queue workers for tab
var pair = new TabWorkerPair(tab.id, worker);
TabWorkerPairList.push(pair);
console.log("main.js: tab is ready!");
});
and finally I can now emit worker function;
// when top right browser icon button is clicked
function handleClick(state) {
for (var i = 0; i < TabWorkerPairList.length; i++) {
var pair = TabWorkerPairList[i];
if (pair.tabid == tabs.activeTab.id) {
pair.worker.port.emit("initialize", pair.tabid);
break;
}
}
}
main reason: a tab can have multiple workers. So you should manually access the worker you are interested in.

Why my extension sends duplicates on request in geometric progression?

I've created extension that makes some JSON request & send it to some receiver.
My problem is:
Open popup window
After it closing, extensions sends 1 request
Open it on the same page again, and extension will send 2 requests
Open again, 4 requests
Open again, 8 requests
In each uses of popup, extension will be duplicate outgoing data in geometric progression.
Why that happens?
From the panel I'm send addnewurl to the port:
AddDialog.port.on("addnewurl", function (data) {
{
AddDialog is my popup
here It handle port messages aftre popup is closed(hidded)
}
var http = require("sdk/request").Request;
var req = CreateRequest("add_url", {});
req.params = {...};
var sreq = encodeURIComponent(JSON.stringify(req));
count += 1; //Global counter, u will see it in video
console.log('count = '+count);
var cfg = {
url : getRequestURL(),
contentType : "text/html",
content : sreq,
onComplete : function (response) {
var data = {
code : response.status,
body : response.json
};
AddDialog.port.emit("addnewurldone", data);
}
};
http(cfg).post();
});
For more sense I've created a AVI video record of that. See it here:
https://dl.dropboxusercontent.com/u/86175609/Project002.avi
1.6 MB
How to resolve that?
ADDED by request more info
That function emit addnewurl:
function AddNewURL() {
var node = $("#Tree").dynatree("getActiveNode");
if (node == null) {
$("#ServerStatus").text(LocalizedStr.Status_NoGroupSelected);
$("#ServerStatus").css("color", "red");
return;
};
var nkey = node.data.key;
var aImg = null;
var data = {
ownerId : nkey,
name : $("#LinkTitle").val(),
description : $("#LinkDesc").val(),
url : $("#CurrentURL").val(),
scrcapt:$("#ScrCaptureCB :selected").val()
};
$("#load").css("display", "inline");
$("#ServerStatus").text(LocalizedStr.Status_AddURL);
self.port.emit("addnewurl", data);
};
and it calls by button:
self.port.on("showme", function onShow(data) {
....
document.querySelector('#BtnOk').addEventListener('click', function () {
AddNewURL();
});
...
});
"swomme" goes from here(main.js):
AddDialog.on("show", function () {
count = 0;
AddDialog.port.emit("showme", locTbl);
});
function addToolbarButton() {
var enumerator = mediator.getEnumerator("navigator:browser");
while (enumerator.hasMoreElements()) {
var document = enumerator.getNext().document;
var navBar = document.getElementById('nav-bar');
if (!navBar) {
return;
}
var btn = document.createElement('toolbarbutton');
btn.setAttribute('id', cBtnId);
btn.setAttribute('type', 'button');
btn.setAttribute('class', 'FLAToolButton');
btn.setAttribute('image', data.url('icons/Add.png'));
btn.setAttribute('orient', 'horizontal');
btn.setAttribute('label', loc("Main_ContextMenu"));
btn.addEventListener('click', function () {
AddDialog.show();
}, false)
navBar.appendChild(btn);
}
}
I think the problem is here
document.querySelector('#BtnOk').addEventListener('click', function () {
AddNewURL();
});
If you are running AddDialog.port.emit("showme", locTbl); when you click your toolbar button then you're adding a click listener to #BtnOk every time as well.
On the first toolbar click it will have one click listener, on the second click two, and so on. You should remove the above code from that function and only run it once.

How to create a button near the address bar?

I am designing a firefox extension, and I want to add a button near the address bar. And then I need to attach a bookmarklet to that button.
Someone can tell me what APIs do I have to use to create that button and to add the bookmarklet ?
Here's an example that uses Erik Vold's toolbarbutton library to add a button near the addressbar:
const data = require("self").data;
const tabs = require("tabs");
exports.main = function(options) {
var btn = require("toolbarbutton").ToolbarButton({
id: 'my-toolbar-button',
label: 'Add skull!',
image: data.url('skull-16.png'),
onCommand: function() {
if (typeof(tabs.activeTab._worker) == 'undefined') {
let worker = tabs.activeTab.attach({
contentScript: 'self.port.on("sayhello", function() { alert("Hello world!"); })'
});
tabs.activeTab._worker = worker;
}
tabs.activeTab._worker.port.emit("sayhello");
}
});
if (options.loadReason === "install") {
btn.moveTo({
toolbarID: "nav-bar",
forceMove: false // only move from palette
});
}
};
You can also see this as a runnable example on the Add-on Builder site:
https://builder.addons.mozilla.org/addon/1044724/latest/

How to add a dropdown menu to a firefox addon sdk powered addon toolbar button?

I use the https://github.com/erikvold/toolbarbutton-jplib/ package to add a toolbar button from an addon-sdk addon to the firefox navigation bar. Is it possible to add a drop down menu to this button (as I understand there are no easy ways)?
This is how I do it; hope it works for you.
var winUtils = require("window-utils");
var delegate = {
onTrack: function(window) {
if(window.location != "chrome://browser/content/browser.xul") {
// console.log("=> win location false");
return;
}
console.log("window tracked");
var document = window.document;
var navBar = document.getElementById('nav-bar');
var btn = document.createElement('toolbarbutton');
btn.setAttribute('id', 'button');
btn.setAttribute('type', 'menu-button');
btn.setAttribute('class', 'toolbarbutton-1');
btn.setAttribute('image', 'http://www.facebook.com/favicon.ico');
btn.addEventListener('command', function() {
console.log("this=" + this.id);
// your callback code here
}
, false);
var menupopup = document.createElement('menupopup');
menupopup.setAttribute('id', 'menupopup');
menupopup.addEventListener('command', function(event) {
// TODO your callback
}
, false);
//menu items
var menuitem1 = document.createElement('menuitem');
menuitem1.setAttribute('id', 'menuitem1');
menuitem1.setAttribute('label', 'Menu item1');
menuitem1.setAttribute('class', 'menuitem-iconic');
menuitem1.addEventListener('command', function(event) {
// CODE
}
, false);
}
winUtils.WindowTracker(delegate);
This code requires a few more lines to actually work:
var delegate = {
onTrack: function(window) {
if(window.location != "chrome://browser/content/browser.xul") {
// console.log("=> win location false");
return;
}
var document = window.document;
var navBar = document.getElementById('nav-bar');
var btn = document.createElement('toolbarbutton');
btn.setAttribute('id', 'button');
btn.setAttribute('type', 'menu-button');
btn.setAttribute('class', 'toolbarbutton-1');
btn.setAttribute('image', 'http://www.facebook.com/favicon.ico');
btn.addEventListener('command', function() {
console.log("this=" + this.id);
// your callback code here
}
, false);
var menupopup = document.createElement('menupopup');
menupopup.setAttribute('id', 'menupopup');
menupopup.addEventListener('command', function(event) {
// TODO your callback
}
, false);
//menu items
var menuitem1 = document.createElement('menuitem');
menuitem1.setAttribute('id', 'menuitem1');
menuitem1.setAttribute('label', 'Menu item1');
menuitem1.setAttribute('class', 'menuitem-iconic');
menuitem1.addEventListener('command', function(event) {
// CODE
}
, false);
menupopup.appendChild(menuitem1);
btn.appendChild(menupopup);
navBar.appendChild(btn);
console.log("window tracked");
}
};
//let utils = require('api-utils/window-utils');
let utils = require('sdk/deprecated/window-utils'); // for new style sdk
utils.WindowTracker(delegate);

Resources