0

I'm developing an extension for Chrome, and here's the workflow I'm trying to achieve:

popup sends message -> content script 1 listens -> content script 1 sends message -> content script 2 listens -> content script 2 performs action

In concept it's fine and dandy; what I've done is set up 2 listeners: one in each content script:

Popup:

chrome.tabs.query({active: true, currentWindow: true}, function(tabs) {
                console.log('popup send request');
                chrome.tabs.sendMessage(tabs[0].id, obj);
            });

Content script 1:

chrome.runtime.onMessage.addListener((function (request, sender) {
    this.log('wg got request', 'request', request, 'sender', sender);
    if (request.action == 'options-updated') {
        this.updateOptions(request, (function() {
            var obj = {action: 'refresh', WG: window.WG};
            this.log('wg forwarded request');
            chrome.runtime.sendMessage(obj); // attempting to forward another request
            return true;
        }).bind(this));
    }
    return true;
}).bind(window.WG));

Content script 2:

chrome.runtime.onMessage.addListener(function(request, sender, sendResponse) {
    console.log('content script got request', 'request', request, 'sender', sender);
    if (request.WG) {
        request.WG.log('message', request.action);
        if (request.action == 'refresh') {
            WGRefresh(request.WG, request.options);
        }
    }
    return true;
});

Problem is, content script 2 only receives the first message. So the output I'm getting is:

popup send request content script got request (first, to ignore) wg got request (same, first, don't ignore here) wg forward request

And then nothing. The content script should have fired it again, and in the request I always send "action", which I check for in the listener, but for the logs I don't differentiate (it should ALWAYS log, so this means the request never gets there).

I've tried returning true in all the listeners, according to the documentation it will keep the chain running and not stop after the first hit, but even so it's not working. What am I doing wrong?!

casraf
  • 21,085
  • 9
  • 56
  • 91

1 Answers1

1

There are 2 sendMessage functions in Chrome API.

So the call to chrome.runtime.sendMessage() in your first content script can't reach any other content script.

What's more, you can't call chrome.tabs directly from a content script.

To do what you want, you need to set up a background script that will act like a proxy between CS1 and CS2. Technically, you could use the popup, but it's unreliable, as the popup may be closed and then nobody would be listening. The background page (or better yet, an event page) is designed specifically for that purpose.

So the scheme becomes: popup -(tabs.sendMessage)-> CS1 -(runtime.sendMessage)-> background -(tabs.sendMessage)-> CS2

Do note that background page will need to know the tab ID to send the message to. If it's the same tab for some reason, e.g. you're trying to message across frames, you can use the sender parameter in the callback.

See Messaging docs for more details.

Xan
  • 74,770
  • 16
  • 179
  • 206
  • Ah, I see the problem now. I'll try that and update you on how it goes! Thanks – casraf Feb 03 '15 at 15:45
  • Just to make sure: why are you trying to pass a message _between_ content scripts? Are they in different tabs? Different frames? – Xan Feb 03 '15 at 15:46
  • I might be going about this the wrong way, but I wanted to use several content scripts in one tab, just for organizational purposes, to prevent them from getting really messy and large. I then learned that I can't pass data around them easily, and the `window` object they each see is encapsulated to them and not shared. So I've been trying to send messages around, which apparently is less than ideal to my liking... I'd love some tips if you have other ideas – casraf Feb 03 '15 at 15:53
  • This is wrong. Each _extension_ gets its own JavaScript context (per frame), isolated from each other and the page. But if you add multiple scripts to that context - they can interact. Otherwise you would not be able to, say, include jQuery. So one content script can simply call code from another, as long as they are from the same extension. – Xan Feb 03 '15 at 15:54
  • So how do I use methods from my class (script#1) inside script#2? I couldn't assign the instance to the window, and I couldn't initiate an instance of the class from script#1, on to script #2. What am I missing? I got undefined when I tried. e.g. window.WG was initiated from script#1, not accessible on script#2. When I tried initiating it from script#2, it couldn't see the class itself. – casraf Feb 03 '15 at 15:56
  • Let's discuss this [in chat](https://chat.stackoverflow.com/rooms/70149/room-for-xan-and-chen-asraf) – Xan Feb 03 '15 at 16:01