Giovanni Di Toro
Giovanni Di Toro

Reputation: 809

Communication between scripts in Firefox addon sdk - self.port doesn't exist

I'm trying to make a firefox addon using their sdk, but I don't know how to make my js scripts communicate. The goal is to make a panel with a form in witch there are 3 checkboxes that when selected can hide/show certain elements on the active tab.

Here are the scripts: main.js:

var data = require("sdk/self").data;

var painel1 = require("sdk/panel").Panel({
    width: 215,
    height: 160,
    contentURL: data.url("painelDestroyer.html"),
    contentScriptFile: [data.url("jquery.js"),data.url("panel.js")]
});

require("sdk/widget").Widget({
    id: "open-form-btn",
    label: "Clear",
    contentURL: data.url("mozico.png"),
    panel: painel1
});

// Attach a content script to the current active tab
let worker = require("sdk/tabs").activeTab.attach({
    contentScriptFile: data.url("clear.js")
});


painel1.port.on("show-tag",function(tag){
    worker.port.emit("show-tag", {tag:tag});
    console.log("worker emited");
});

painel1.port.on("hide-tag",function(tag){
    worker.port.emit("clear-tag", {tag:tag});
    console.log("worker emited");
});

painel.js:

$("#imgD").click(function() {
    if ($(this).is(":checked")) {
        panel.port.emit("clear-tag","img");
        console.log("panel emited");
    } else {
       panel.port.emit("show-tag","img");
       console.log("panel emited");
    }
});
$("#aD").click(function() {
    if ($(this).is(":checked")) {
        panel.port.emit("clear-tag","a");
        console.log("panel emited");
    } else {
        panel.port.emit("show-tag","a");
        console.log("panel emited");
    }
});
$("#iframeD").click(function() {
    if ($(this).is(":checked")) {
       panel.port.emit("clear-tag","iframe");
       console.log("panel emited");
    } else {
        panel.port.emit("show-tag","iframe");
        console.log("panel emited");
    }
});

clear.js:

    function tagHide (tag, hide) {
    $(tag).each(function() {
        if (hide === false) {
            $(this).css({
                "visibility": "visible"
            });
        } else {
            $(this).css({
                "visibility": "hidden"
            });
        }
    });
}

self.port.on('show-tag', function(tag) {
    tagHide(tag, false);
});
self.port.on('clear-tag', function(tag) {
    tagHide(tag, true);
});

Question: How do I make this work, how to comunicated between this 3 scripts? My guess is that I have to send a message from the painel.js to main.js and then to the clean.js, but how do I do that? How do I receive a message in clean.js or painel.js? I keep getting that self.port doesn't exist in painel.js.

Upvotes: 3

Views: 2794

Answers (1)

Mattias Buelens
Mattias Buelens

Reputation: 20159

The problem is that you're trying to access and modify the content of a page from a module script instead of a content script. There's no window or document in the scope of a module script, as explained in the documentation. As such, jQuery's $ won't have anything to work with in your add-on script.

You need to attach another content script to the current tab. You can then show/hide elements on the page by sending messages to its worker port, similar to how you communicate between your add-on module script and your panel script.

// Attach a content script to the current active tab
let worker = require("sdk/tabs").activeTab.attach({
    contentScriptFile: data.url("tabscript.js")
});
// Send it a message
worker.port.emit("show-tag", tag);

You would then move your tagHide function to that content script for the active tab and put some port listeners in there. That way, the add-on script can emit some messages and have the content script call tagHide appropriately.

Note: You're probably better off using a page-mod rather than attaching to a tab. In the end, you probably don't want to manually attach your script every time the user navigates to a different page, so a global PageMod (include: "*") is an easy solution. Still, you'll need to make sure you're targeting the worker of the current tab when forwarding your messages. Therefore, you'll need to keep track of all those workers and clean them up when the worker is detached (using worker.on('detach', callback). Finally, you should iterate over all currently attached workers and find the worker attached to the page of the currently active tab (by comparing each worker.tab to tabs.activeTab).

Alternatively, you could simply inject the commands straight into the tab page as JavaScript as shown in Attaching Content Scripts to Tabs. I find this quite ugly and it doesn't allow you to retain some state in the content script (variables on a page retained across commands) which you might need for more complex scripts.

UPDATE 1: As for your updated question, the answer is very well documented in the SDK guides. You just need to bind a listener using self.port.on:

self.port.on('show-tag', function(tag) {
    tagHide(tag, false);
});
self.port.on('clear-tag', function(tag) {
    tagHide(tag);
});

On another note, your current code only ever works with the currently active tab when the add-on starts. Most likely, the active tab changes and you'll need to inject your content script into other tabs over time. See my previous note on how you could manage the workers of all these scripts.

Upvotes: 5

Related Questions