Giovanni Di Toro
Giovanni Di Toro

Reputation: 809

Firefox sdk - content script can't access self.port

I'm trying to make a firefox addon using their sdk, but I don't know how to make my js scripts comunicate. 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:

exports.main = function() {};

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

var panel1 = require("sdk/panel").Panel({
    width: 215,
    height: 160,
    contentURL: data.url("panelDestroyer.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"),
    contentScriptFile: data.url("jquery.js"),
    panel: panel1
});

var tabs = require("sdk/tabs");

var tabWorkers = {};
tabs.on("ready", function(tab) {
    attachTabWorker(tab);
});
for each (var tab in tabs) {
    attachTabWorker(tab);
}

function attachTabWorker(tab) {
    // Attach and store
    var tabWorker = tabWorkers[tab.id] = tab.attach({
        contentScriptFile: [data.url("clear.js"),data.url("jquery.js")]
    });
    // Clean up
    tabWorker.on("detach", function() {
        if (getTabWorker(tab) === tabWorker) {
            delete tabWorkers[tab.id];
        }
    });
}

function getTabWorker(tab) {
    return tabWorkers[tab.id];
}

function getActiveTabWorker() {
    return getTabWorker(tabs.activeTab);
}


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

panel1.port.on("clear-tag",function(tag){
    getActiveTabWorker().port.emit("clear-tag", tag);
    console.log("worker emited");
})

;

painel.js:

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

Why do I keep getting that self.port is not defined in painel.js ?

Upvotes: 4

Views: 2395

Answers (2)

ignVinayak
ignVinayak

Reputation: 50

I was also experiencing the same problem, and then found this question on SO: self.port is undefined error [on mozilla add-on SDK 1.8]

The problem is in the HTML code, you must be calling the script in both the main.js and html. Removing it from the html, and declaring it directly in main.js will solve this error.

Upvotes: 1

Mattias Buelens
Mattias Buelens

Reputation: 20159

I believe the problem is that you're trying to attach a content script when the add-on is loaded.

let worker = require("sdk/tabs").activeTab.attach({
    contentScriptFile: data.url("clear.js")
});

I think activeTab may not be ready yet when your add-on is just loaded, which is usually when Firefox is starting. Perhaps the DOM is not fully loaded yet or the SDK could not properly initialize all components for the content script.

The SDK documentation states that you should wait until the ready event before using content-related properties and methods. I assume attaching content scripts also falls in that category, so you probably need to rework that to something like:

let worker = null;
require("sdk/tabs").activeTab.on("ready", function(tab) {
    worker = tab.attach({
        contentScriptFile: data.url("clear.js")
    });
});

This is still insufficient though, as the user may switch tabs. When you register the ready listener on the original activeTab, you won't be listening to the newly activated tab and no content scripts will be attached to that tab. You'll need to:

  • listen to the ready event of all tabs;
  • make sure they all have your content script attached;
  • select the appropriate worker depending on the currently active tab.

I quickly whipped up something which should do just that. You should be able to use getActiveTabWorker() in your panel port events to target the correct worker. It's still untested, so give it a try!

const tabs = require("sdk/tabs");

let tabWorkers = {};
tabs.on("ready", function(tab) {
    attachTabWorker(tab);
});
for each (let tab in tabs) {
    attachTabWorker(tab);
}

function attachTabWorker(tab) {
    // Attach and store
    let tabWorker = tabWorkers[tab.id] = tab.attach({
        contentScriptFile: data.url("clear.js")
    });
    // Clean up
    tabWorker.on("detach", function() {
        if (getTabWorker(tab) === tabWorker) {
            delete tabWorkers[tab.id];
        }
    });
}

function getTabWorker(tab) {
    return tabWorkers[tab.id];
}

function getActiveTabWorker() {
    return getTabWorker(tabs.activeTab);
}

EDIT: I have no idea how self.port can be undefined, but I found some more errors you need to fix. Perhaps one of those is indirectly interfering with self?

  • main.js is listening for hide-tag, whereas your panel emits clear-tag. Use clear-tag.
  • You're emitting an object to the worker, whereas your worker expects just a string. Simply emit tag instead of {tag:tag}.
  • You're not including jQuery in your tab worker. Add data.url("jquery.js") to contentScriptFile in attachTabWorker.

Upvotes: 5

Related Questions