Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
1.1k views
in Technique[技术] by (71.8m points)

javascript - PageMod attaching worker to same URL multiple times

CLEAN SOLUTION FOUND

I found a very clean solution which really renders this whole question pointless, and I am certain it existed when I asked this question... I was much to ignorant too look for it.

Using attachTo: 'top' in the PageMod constructor only attaches the script to the top-level documents and not to any iframes.

So, if you find that PageMod is attaching multiple times for your add-on, it is probably due to it being attached to iframes, along with the top level tab document. Add attachTo: 'top' as a property for the object passed to the PageMod constructor, and you wouldn't need to worry about iframes.

For the question below, the solution would be

var _workers = [];

var pageMod = require("sdk/page-mod").PageMod({
    include: /https?://www.websitename.net.*/,
    contentScript: "self.port.on('hello', function() { " +
                   "console.log('['+document.location.href+']: " +
                   "My worker said hello to me!');",
    contentScriptWhen: "end",
    attachTo: 'top',   //<-- add this property to only attach to top level document
    onAttach: function(worker) {
        _workers.push(worker);
        worker.on("detach", function() {
            var ind = _workers.indexOf(this);
            if(ind !== -1) {
                _workers.splice(ind, 1);
            }
        });
        worker.port.emit("hello");
    }
});

Of course, without @Noitidart's help, I never would have pinpointed the reason to be iframes.

OLD QUESTION

I'm trying to write an add-on for Firefox which modifies the pages of a particular website. I thought PageMod was the perfect option for that, but I've run into some problems. The workers seem to be attaching to the same URL multiple times (4 to 2), and I haven't got a clue why this would be happening.

I tried the following code in the add-on's main.js:

var _workers = [];

var pageMod = require("sdk/page-mod").PageMod({
    include: /https?://www.websitename.net.*/,
    contentScript: "self.port.on('hello', function() { " +
                   "console.log('['+document.location.href+']: " +
                   "My worker said hello to me!');",
    contentScriptWhen: "end",
    onAttach: function(worker) {
        _workers.push(worker);
        worker.on("detach", function() {
            var ind = _workers.indexOf(this);
            if(ind !== -1) {
                _workers.splice(ind, 1);
            }
        });
        worker.port.emit("hello");
    }
});

On running this code and opening https://www.websitename.net in a single, solitary tab, the console had 2 to 4 instances of

[https://www.websitename.net/]: My worker said hello to me!

Which means that the same page has multiple workers attached to it. I don't know why this is happening, I certainly don't want it to because I intend to later use the _workers array to communicate with the attached scripts.

EDIT:

Thanks to @Noitidart, I've now confirmed that of the multiple workers, only one is the actual page and the others are iframes, which is why the DOM content available to the content scripts for each worker was different.

The question still stands, though. How to ensure that the workers (and thus, the content scripts) are not attached to the iframes?

I could ensure that the worker that isn't attached to an iframe is the one pushed to _workers, but is there a way to attach a content script to a worker once I've ensured it is the correct one? I don't want the rather large content script to be attached multiple times.

EDIT 2:

Again, thanks to @Noitidart, I think I have somewhat of a solution: destroy if worker is detected to be attached to an iframe.

A better solution would be to only attach the main content script to the correct worker, but presently I have been unable to find a way to attach a content script to the correct worker (worker.contentScriptFile = data.url("file.js") doesn't seem to work).

So, the current hack:

verify.js:

if(window.top !== window) {
    self.port.emit('iframe');
}
else {
    self.port.emit('hi');
}

main.js:

var _workers = [];

var pageMod = require("sdk/page-mod").PageMod({
    include: /https?://www.websitename.net.*/,
    contentScriptFile: [data.url("verify.js"), data.url("my_main_script.js")],
    contentScriptWhen: "end",
    onAttach: function(worker) {
        worker.on("detach", function() {
            var ind = _workers.indexOf(this);
            if(ind !== -1) {
                _workers.splice(ind, 1);
            }
        });

        worker.port.on("hi", function() {
            //Not iframe, keep this one.
            //If there is a way, attach 'my_main_script.js'
            //to this worker now, and not before.

            _workers.push(worker);

            //Emit 'ack' to tell my_main_script.js
            //that we're good to go.
            worker.port.emit('ack');
        });

        worker.port.on("iframe", function() {
            //iframe, get rid of it.
            worker.destroy();
        });
    }
});

I should note that the order in which the scrips appear in contentScriptFile doesn't matter, both scripts are loaded for each attachment. So no point hoping that verify.js destroys the worker before my_main_script.js is loaded.

And again, this solution is not complete. If I haven't emphasized enough on this point, I really hope there's a way to attach my_main_script.js to the correct worker, instead of having it load for all workers. Please comment/answer if there is such a way.

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

In the contentScript or whatever is running in the web site (i dont know much about sdk), check if (window.frameElement == true) if its true than its a frame then you want to cancel the attach.

edit: maybe window.frameElement wont work from within the frame. if it doesnt work do this if (window.top != window) then its a frame


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...