firefox sdk: how to use emit for sidebar outside widget - javascript

I am using this code from the SDK Tutorial Website:
var sidebar = require("sdk/ui/sidebar").Sidebar({
id: 'my-sidebar',
title: 'My Sidebar',
url: require("sdk/self").data.url("sidebar.html"),
onAttach: function (worker) {
// listen for a "ready" message from the script
worker.port.on("ready", function() {
// send an "init" message to the script
worker.port.emit("init", "message from main.js");
});
}
});
This works great. The problem is now that this only allows me to send something via the worker.port onAttach (and on 3 other events for the sidebar).
What I need is to use the emit function outside the scope of this sidebar. For example if I use in the same main.js an listener for a tab like
tabs.on('ready', function(tab) {
sidebar.worker.port.emit("init", "message from main.js");
}
This is not working. I also tried
sidebar.port.emit("init", "message from main.js");
or
worker.port.emit("init", "message from main.js");
Without success.
I have also tried to put the tab listener inside the onAttach of the sidebar (so a listener inside a listener) but that also is not working.
Does anybody have an idea on that one?
thanks.

Use the method described here:
var workers = [];
function detachWorker(worker, workerArray) {
var index = workerArray.indexOf(worker);
if(index != -1) {
workerArray.splice(index, 1);
}
}
var sidebar = require("sdk/ui/sidebar").Sidebar({
...
onAttach: function(worker) {
workers.push(worker);
worker.on('detach', function () {
detachWorker(this, workers);
});
}
});
Then to emit on open sidebars do:
workers.forEach(worker => {
worker.port.emit('some-message', data);
})

Related

Inject content script only once in a webpage

I am trying to inject content script on context menu click in an extension manifest version 3. I need to check if it is already injected or not. If it is not injected , inject the content script. This condition has to be satisfied. Can anyone help me with this?
We can use
ALREADY_INJECTED_FLAG
but this can be checked only in the content script, so this approach will not work as expected.
payload.js(content script)
function extract() {
htmlInnerText = document.documentElement.innerText;
url_exp = /[-a-zA-Z0-9#:%_\+.~#?&//=]{2,256}\.[a-z]{2,4}\b(\/[-a-zA-Z0-9#:%_\+.~#?&//=]*)?/gi;
regex = new RegExp(url_exp)
list_url = htmlInnerText.match(url_exp)
ip_exp = /\b\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}\b/;
list_ip = htmlInnerText.match(ip_exp)
hash_exp = /\b[A-Fa-f0-9]{32}\b|\b[A-Fa-f0-9]{40}\b|\b[A-Fa-f0-9]{64}\b/g
list_hash = htmlInnerText.match(hash_exp)
chrome.storage.local.set({ list_url: list_url, list_ip: list_ip, list_hash: list_hash });
}
chrome.runtime.sendMessage( extract());
background.js
genericOnClick = async () => {
// Inject the payload.js script into the current tab after the backdround has loaded
chrome.tabs.query({ active: true, currentWindow: true }, function (tabs) {
chrome.scripting.executeScript({
target: { tabId: tabs[0].id },
files: ["payload.js"]
},() => chrome.runtime.lastError);
});
// Listen to messages from the payload.js script and create output.
chrome.runtime.onMessage.addListener(async (message) => {
chrome.storage.local.get("list_url", function (data) {
if (typeof data.list_url != "undefined") {
urls = data.list_url
}
});
chrome.storage.local.get("list_ip", function (data) {
if (typeof data.list_ip != "undefined") {
ips = data.list_ip
}
});
chrome.storage.local.get("list_hash", function (data) {
if (typeof data.list_hash != "undefined") {
hashes = data.list_hash;
}
});
if ( hashes.length>0 || urls.length>0 || ips.length>0 ){
chrome.windows.create({url: "output.html", type: "popup", height:1000, width:1000});
}
});
}
on my first context menu click I get the output html once. Second time
I click, I get the output html twice likewise.
This behavior is caused by a combination of two factors.
First factor
You're calling chrome.runtime.onMessage.addListener() inside genericOnClick(). So every time the user clicks the context menu item, the code adds a new onMessage listener. That wouldn't be a problem if you passed a named function to chrome.runtime.onMessage.addListener(), because a named function can only be registered once for an event.
function on_message(message, sender, sendResponse) {
console.log("bg.on_message");
sendResponse("from bg");
}
chrome.runtime.onMessage.addListener(on_message);
Second factor
But you're not registering a named function as the onMessage handler. You're registering an anonymous function. Every click on the context menu item creates and registers a new anonymous function. So after the Nth click on the context menu item, there will be N different onMessage handlers, and each one will open a new window.
Solution
Define the onMessage handler as a named function, as shown above.
Call chrome.runtime.onMessage.addListener() outside of a function.
You don't have to do both 1 and 2. Doing either will solve your problem. But I recommend doing both, because it's cleaner.

onBeforeUnload being applied whole application

im using a service that basically popups a box when a user tries to refresh or close the page, and it works fine, but basically i just want to be apply in a specific page, but the problem is that being applied in the rest of the pages.
I tried applying inside of a if statement that includes a specifc state location, but still is being applied in the rest of the app.
Service:
angular.module('farm')
.factory('beforeUnload', function ($rootScope, $window) {
// Events are broadcast outside the Scope Lifecycle
$window.onbeforeunload = function (e) {
var confirmation = {};
var event = $rootScope.$broadcast('onBeforeUnload', confirmation);
if (event.defaultPrevented) {
return confirmation.message;
}
};
$window.onunload = function () {
$rootScope.$broadcast('onUnload');
};
return {};
})
.run(function (beforeUnload) {
// Must invoke the service at least once
});
Controller:
if ($state.includes("app.cal.lab.result")){
$scope.$on('onBeforeUnload', function (e, confirmation) {
confirmation.message = "All data willl be lost.";
e.preventDefault();
});
$scope.$on('onUnload', function (e) {
console.log('leaving page'); // Use 'Preserve Log' option in Console
});
}

How to call a content script function from main.js in firefox addon

I am new to Firefox addon development.
I need a way to call a contentscript function from main.js in firefox addon.
I have injected contentscript xyz.js on every opening webpage.
I want to call function abc() present in my contentscript xyz.js from my main.js on click of a button which i have place in navigation toolbar.
Below is my code.
Main.js
..
function addToolbarButton() {
var document = mediator.getMostRecentWindow('navigator:browser').document;
var navBar = document.getElementById('nav-bar');
if (!navBar) {
return;
}
var btn = document.createElement('toolbarbutton');
btn.setAttribute('id', 'mybutton-id');
btn.setAttribute('type', 'button');
btn.setAttribute('class', 'toolbarbutton-1');
btn.setAttribute('image', data.url('icon_16.png'));
btn.setAttribute('orient', 'vertical');
btn.setAttribute('label', 'Test');
btn.addEventListener('click', function() {
tabs.activeTab.attach({
//
abc() //here i want to call the function present in my contentscript
//
});
}, false)
navBar.appendChild(btn);
}
..
xyz.js
..
function abc(){
//here is my code logic
}
..
I came to know that message passing is way to do so but unable to implement in firefox.
Please help me i have got stuckd.
You cannot call the function directly, you need to send a message to the content script. Meaning something like that:
var worker = tabs.activeTab.attach({
...
});
// Some time later
worker.postMessage("doABC");
And in the content script:
self.on("message", function(message) {
if (message == "doABC")
abc();
});
For more information on communicating with content scripts see documentation.
According to documentation it should work this way;
However I have similar question Accessing pre-loaded content script from ActionButton not yet resolved.
// main.js
function handleClick(state) {
var myWorker = tabs.activeTab.attach({
});
myWorker.port.emit("initialize", "Message from the add-on");
}
// content.js
/*BEGIN Listen events coming from Add-on script*/
self.port.on("initialize", function () {
alert('self.port.on("initialize")');
return;
});

Add-on Builder: Multiple Workers Using port?

Referring to this question: Add-on Builder: ContentScript and back to Addon code?
Here is my addon code:
var widget = widgets.Widget({
id: "addon",
contentURL: data.url("icon.png"),
onClick: function() {
var workers = [];
for each (var tab in windows.activeWindow.tabs) {
var worker = tab.attach({contentScriptFile: [data.url("jquery.js"), data.url("myScript.js")]});
workers.push(worker);
}
}
});
And here is myScript.js:
var first = $(".avatar:first");
if (first.length !== 0) {
var url = first.attr("href");
self.port.emit('got-url', {url: url});
}
Now that I have multiple workers where do I put
worker.port.on('got-url', function(data) {
worker.tab.url = data.url;
});
Since in the other question I only had one worker but now I have an array of workers.
The code would be:
// main.js:
var data = require("self").data;
var windows = require("windows").browserWindows;
var widget = require("widget").Widget({
id: "addon",
label: "Some label",
contentURL: data.url("favicon.png"),
onClick: function() {
//var workers = [];
for each (var tab in windows.activeWindow.tabs) {
var worker = tab.attach({
contentScriptFile: [data.url("jquery.js"),
data.url("inject.js")]
});
worker.port.on('got-url', function(data) {
console.log(data.url);
// worker.tab.url = data.url;
});
worker.port.emit('init', true);
console.log("got here");
//workers.push(worker);
}
}
});
// inject.js
$(function() {
self.port.on('init', function() {
console.log('in init');
var first = $(".avatar:first");
if (first.length !== 0) {
var url = first.attr("href");
console.log('injected!');
self.port.emit('got-url', {url: url});
}
});
});
Edit: sorry, should have actually run the code, we had a timing issue there where the content script was injected before the worker listener was set up, so the listener was not yet created when the 'got-url' event was emitted. I work around this by deferring any action in the content script until the 'init' event is emitted into the content script.
Here's a working example on builder:
https://builder.addons.mozilla.org/addon/1045470/latest/
The remaining issue with this example is that there is no way to tell if a tab has been injected by our add-on, so we will 'leak' or use more memory every time the widget is clicked. A better approach might be to inject the content script using a page-mod when it is loaded, and only emit the 'init' event in the widget's onclick handler.

FireFox Toolbar Prefwindow unload/acceptdialog Event to Update the toolbar

I'm trying to develop a firefox toolbar ;)
so my structure is
In the options.xul is an PrefWindow which i'm opening over an
<toolbarbutton oncommand="esbTb_OpenPreferences()"/>
function esbTb_OpenPreferences() {
window.openDialog("chrome://Toolbar/content/options.xul", "einstellungen", "chrome,titlebar,toolbar,centerscreen,modal", this);}
so in my preferences i can set some checkboxes which indicates what links are presented in my toolbar. So when the preferences window is Closed or the "Ok" button is hitted I want to raise an event or an function which updates via DOM my toolbar.
So this is the function which is called when the toolbar is loaded. It sets the links visibility of the toolbar.
function esbTB_LoadMenue() {
var MenuItemNews = document.getElementById("esbTb_rss_reader");
var MenuItemEservice = document.getElementById("esbTb_estv");
if (!(prefManager.getBoolPref("extensions.esbtoolbar.ShowNews"))) {
MenuItemNews.style.display = 'none';
}
if (!(prefManager.getBoolPref("extensions.esbtoolbar.ShowEservice"))) {
MenuItemEservice.style.display = 'none';
}
}
So I tried some thinks like adding an eventlistener to the dialog which doesn't work... in the way I tried...
And i also tried to hand over the window object from the root window( the toolbar) as an argument of the opendialog function changed the function to this.
function esbTB_LoadMenue(RootWindow) {
var MenuItemNews = RootWindow.getElementById("esbTb_rss_reader");
var MenuItemEservice = RootWindow.getElementById("esbTb_estv");}
And then tried to Access the elements over the handover object, but this also not changed my toolbar at runtime.
So what i'm trying to do is to change the visibile links in my toolbar during the runtime and I don't get it how I should do that...
thanks in advance
-------edit-------
var prefManager = {
prefs: null,
start: function()
{
this.prefs = Components.classes["#mozilla.org/preferences-service;1"]
.getService(Components.interfaces.nsIPrefService)
.getBranch("extensions.esbtoolbar.");
this.prefs.QueryInterface(Components.interfaces.nsIPrefBranch2);
this.prefs.addObserver("", this, false);
},
end: function()
{
this.prefs.removeObserver("", this);
},
observe: function(subject, topic, data)
{
if (topic != "nsPref:changed")
{
return;
}
//Stuff what is done when Prefs have changed
esbTB_LoadMenue();
},
SetBoolPref: function(pref,value)
{
this.prefs.setBoolPref(pref,value);
},
GetBoolPref: function(pref)
{
this.prefs.getBoolPref(pref);
}
}
So this is my implementation.
The trick is to listen to preference changes. That way your toolbar updates whenever the prefs change -- regardless if it happened through your PrefWindow, about:config or some other mechanism.
In Toolbar.js you do the following
var esbTB_observe = function(subject, topic, data) {
if (topic != "nsPref:changed") {
return;
}
// find out which pref changed and do stuff
}
var esbTB_init = function() {
prefs =
Components.classes["#mozilla.org/preferences-service;1"]
.getService(Components.interfaces.nsIPrefService)
.getBranch("extensions.esbtoolbar.");
prefs.QueryInterface(Components.interfaces.nsIPrefBranch2);
prefs.addObserver("", esbTB_observe, false);
}
// Init addin after window loaded
window.addEventListener("load", esbTB_init, false);
Now, when the window loads, the esbTB_init() function is called in which the observer to the pref branch "extensions.esbtoolbar." is added. Later, when a pref in the branch is changed, the esbTB_observe() function is automatically called.
In esbTB_observe() you have to read the values of your prefs and adjust the toolbar.

Categories

Resources