How can I track which tab or content script a message came from? - firefox-addon

In a Firefox add-on I need to track which tab messages are associated with. The content script will send data to the main.js. Later, when the user clicks the extension's button in the toolbar it will look for data associated with the active tab.
In Chrome extensions, when a message was received, I could ask which tab the message came from and track messages by the tab id. In Firefox, tabs have id's too, but there doesn't seem to be an easy way to access them from content scripts.

The answer depends on which way you are creating the content scripts. Below is an example main.js file for adding content scripts with PageMod.
var buttons = require('sdk/ui/button/action'),
pageMod = require('sdk/page-mod'),
data = require('sdk/self').data;
// Map of messages keyed by tab id
var messages = {};
pageMod.PageMod({
include: 'http://www.example.com',
contentScriptFile: [
data.url('my-script.js')
],
onAttach: function(worker){
// Get the tab id from the worker
var tabId = worker.tab.id;
// Save the message
worker.port.on('message', function(message){
messages[tabId] = message;
});
// Delete the messages when the tab is closed
// to prevent a memory leak
worker.on('detach', function(){
delete messages[tabId];
});
}
});
var button = buttons.ActionButton({
id: 'my-extension',
label: 'Example',
icon: {
'16': './icon-16.png',
'32': './icon-32.png',
'64': './icon-64.png'
},
onClick: function(state){
// Retrieve the message associated with the
// currently active tab, if there is one
var message = messages[tabs.activeTab.id];
// Do something with the message
}
});
I also recommend reading Content Scripts - Interacting with Page Scripts and Content Worker for a better understanding about what's going on and how to adapt it to your situation.

Related

Setting initial value for select2 with ajax data source

I use select2 for specifying recipients for the website's inner messaging system. There are users and they can send messages to each other. They can search other users by the user name.
I use the following config:
this.$select2.select2({
multiple: true,
ajax: {
url: "/userSearch",
dataType: "json",
},
templateResult: function(data) {
var user = new SomeComplexUserModel(data);
var $div = $(<div></div>");
$div.append("<img src='"+user.image.readPaths().crop+"'>");
$div.append("<span>"+user.fullName()+"</span>");
return $div;
},
templateSelection: ..the same as templateResult..
Now I want to set initial value for this. How to do that? I have the list of ids of the users that have to be selected on page load. I make the separate request to /userSearch and receive the data. Then I'm trying to push this data to the select2 somehow.
I can't create native var opt = new Option(text,value); select.append(opt) because this case templateSelection gets only id and text from the option, it can't construct the user model based on this data only. It does not show users with avatars.
I tried to trigger select2:select event with {originalEvent:null,data:$.extend(ajaxResult,{selected:true,disabled:false,element:null},_type:"select")}, but it seems it does not work this direction. It emits events but is not subscribed for them.
I also tried to set this.$select2.val(ajaxData); this.$select2.trigger('change'), after select2 initialization, but it does not work either.

Firefox addon opens tab with content, copy/paste of the tabs url will not load page fully

I have an addon that places an ActionButton on the toolbar. When the ActionButton is clicked the code below is run.
The code opens a new tab and provides some html and js, this acts as the addon's UI.
The url of the new tab is:
resource://jid1-qljswfs6someid-at-jetpack/addon-firefox/data/html/view.html
If I copy/paste that url into another new tab manually, the html displays but the js logic is not loaded. Is there a way to do this without clicking the ActionButton? So I could maybe bookmark the addon instead of having the ActionButton take up space.
Code:
Tabs.open({
url: require("sdk/self").data.url('html/view.html'),
onReady: function onReady(tab) {
worker = tab.attach({
contentScriptFile: [
require("sdk/self").data.url.get('lib/lib1.js'),
require("sdk/self").data.url.get('js/lib1.js')
],
onMessage: function(message) {
console.log('stuff done');
}
});
}
});
In order to run it whenever the site from data.url('html/view.html') is loaded you would have to use page-mod instead of manually attaching to the document to the tab.
Your include pattern would be something like data.url('html/view.html') + "*", so it also attaches to the page if there is a hash or a query to the document.

Can't recieve port message from main.js in content script

I am trying to send a message via port from my main.js to my content script for my panel. I've tried many things without luck, however sending a message from the content script to main.js works perfectly.
Here is what my main.js looks like:
var data = require("self").data;
var setting = require("panel").Panel({
width: 250,
height: 130,
contentURL: data.url("www.google.com"),
contentScriptFile: data.url("script.js")
});
require("widget").Widget({
id: "sorter1",
label: "Search Result Sorting",
contentURL: data.url("icon.ico"),
panel: setting
});
setting.port.emit("message");
And here is my content script:
self.on("message", function(addonMessage) {
document.innerHTML = "Got Message"
});
I had this figured out a few days ago, just haven't had the time to post here.
A few things to keep in mind when using panels:
Pannel page is loaded when extension is loaded, not when it is shown.
Content script of the panel page is injected into the panel page when the page is shown.(when contentScriptWhen property is default)
Content scripts dont have access to add-on SDK resources.
This is how I implemented it
In main.js
panel.on("show", function() {
panel.port.emit("message");
});
In panel content script
self.port.on("message", function() {
//doThings
});
port.emit() doesn't need a second argument, though second argument is what will be passed to the content script for
function(secondArg) {
}
[Update] Sorry, I didn't fully read the code.
First off, you actually need to send the panel a message, eg
setting.port.emit('message', someData);
For the second part: I think you want:
self.port.on("message", function(addonMessage) {
document.innerHTML = "Got Message"
});
The relevant docs are here:
https://addons.mozilla.org/en-US/developers/docs/sdk/latest/dev-guide/guides/content-scripts/using-port.html

How to get the URL of clicked link?

I am trying to create an add-on through Mozilla Add-On Builder. What I need to know is how to get the URL of a left clicked link in the active tab through the add-on and open it in a new tab.
I know this process involved adding an eventlistener through a page-mod and then using the tabs module, however I can't seem to get the syntax correct.
Edit: (This is what I have so far)
var Widget = require("widget").Widget;
var tabs = require('tabs');
var pageMod = require("page-mod");
exports.main = function() {
pageMod.PageMod({
include: '*',
contentScriptWhen: 'ready',
contentScript: "window.addEventListener('click', function(event) { self.port.emit( 'click',event.target.toString() )},false)",
onAttach: function(worker) {
worker.port.on("click", function(urlClicked) {
tabs.open(urlClicked);
});
}
});
};
The code you have there is mostly correct and works for me. There are two issues with your content script code however:
It needs to call event.preventDefault() to prevent the browser from following the link. Otherwise the linked page will be loaded both in the current tab and the new tab opened by your extension.
It doesn't check whether event.target is actually a link. It could be a child node of the link or it might not be a link at all.
Altogether, your content script should look like this:
window.addEventListener("click", function(event)
{
var link = event.target;
while (link && link.localName != "a")
link = link.parentNode;
if (link)
{
self.port.emit("click", link.href);
event.preventDefault();
}
}, false);
For a non-trivial content script like this, you shouldn't use contentScript parameter but rather put it into its own file in the data/ directory. You can then use contentScriptFile parameter when constructing the panel:
contentScriptFile: require("self").data.url("contentScript.js"),

Getting page title in Firefox add-on using Add-on SDK

I am trying to get page title on every page using new Firefox add-on builder. How can I do that?
Edit
More info
I want to get page title on every page load event .
It is actually the very first example for the tabs package:
var tabs = require("tabs");
for each (var tab in tabs)
console.log(tab.title);
See tab.title.
Edit: If you need to know the title of each page as it loads rather than capture the current state then you should use the page-mod package:
var pageMod = require("page-mod");
pageMod.PageMod({
include: "*",
contentScriptWhen: "end",
contentScript: 'console.log(document.title);'
});
The documentation has some info on how a content script can communicate with the add-on, e.g. to send it this page title.
If you are only interested in top-level documents then you can still use the tabs package:
var tabs = require("tabs");
tabs.on("ready", function(tab) {
console.log(tab.title);
});
"ready" events won't get fired if the page is served from back-forward cache.
'pageshow' event is the appropriate event to listen to.
var tabs = require("sdk/tabs");
function onOpen(tab) {
tab.on('pageshow', function(tab) {
console.log('title: '+ tab.title);
}
tabs.on('open', onOpen);

Resources