Messaging: Matching content script and owner window

Dear community,

Question:

Is there a way for the window message manager to detect, that the message was sent from the tab belonging to this window?

Description:

Suppose we have 3 windows A, B and C.
Each window has tabs e.g. window A has tabs A1, A2, A3, window B has tabs B1, B2, and B3 and window C has tabs C1, C2, and C3

According to the docs, each window has its own message manager, so when the content script from the tab B1 sends a message via sendAsyncMessage(), all the message managers (A, B and C) receive this message, and this is not good approach for my code.

What is the best way to organize messaging, so the messages from tabs A1, A2, … are processed only by the message manager A, messages from tabs B1, B2, …- by the message manager B, and so on?

For each message we have a field message.target.messageManager - can this help me?

You’ve said it yourself: each window has its own message manager. A message listener added to the message manager for that window only receives messages from frame scripts in that window.

There is also a message manager for each browser (tab) that only sends and receives messages for that tab.

The global frame message manager can be used to broadcast messages to all window and browser message managers, as well as to receive messages from all of them, but that isn’t what you want in this case.

A completely different set of message managers can be used to send messages between child and parent processes.

Thanks, I was suspecting this, but in my code window message manager received messages from all tabs, even from other windows.

The problem was in the loadFrameScript - I called it from the Global Message Manager:

var globalMM = Components.classes["@mozilla.org/globalmessagemanager;1"].getService(Components.interfaces.nsIMessageListenerManager); globalMM.loadFrameScript(...)

You’re right, to get my tabs talking only with their parent windows, this code should be changed to:

window.messageManager.loadFrameScript(...)

Thanks :grinning:

You can call loadFrameScript() from the global message manager. In fact this is often the most convenient way if you have a singleton handy, such as the top level script of an addon. Don’t call it from a window-scope script, that will cause all sorts of mayhem. You could also call loadFrameScript() separately from each window message manager to achieve the same result, but you need to make sure you do it in all new windows as well. Or do it from just one window message manager if you only need it in a particular window. Note that there is no way to unload a frame script so generally you’ll just load them into every browser/window and send messages if necessary to activate/deactivate them.

The important piece that must be done from the right message manager is the addMessageListener() call. Use the window message manager if you only want to receive messages from one window, the one whose message manager you used. Call addMessageListener() from the global message manager if you want to receive messages from any window, normally only done from a singleton such as the top level script of a javascript code module.

You don’t have to use the same message manager to load the frame script as you do to add the message listener - you can add the frame script from the global message manager and add the listener from a window (or browser) message manager, if you want.

Thanks Lithopsian, but for some reason this does not work for me.

var globalMM = Components.classes["@mozilla.org/globalmessagemanager;1"].getService(Components.interfaces.nsIMessageListenerManager);

globalMM.loadFrameScript('chrome://fireshot/content/fscontent.js', true);
window.messageManager.addMessageListener(this.MESSAGING_ID, FireShotMessaging);

Result: I’m getting messages from all content scripts even if their tabs do not belong to this window.

window.messageManager.loadFrameScript('chrome://fireshot/content/fscontent.js', true);
window.messageManager.addMessageListener(this.MESSAGING_ID, FireShotMessaging);

Result: the messaging goes as expected and I’m receiving messages only from content scripts, whose tabs belong to this window.

Do I miss anything?

You appear to be doing all this from the same script. A window-scope script I’m guessing? You can’t use the global message manager from a window-scope script - or rather you can but it will very rarely be the right thing to do. Every window will load now load the same script into every window, chaos. I suspect you are actually getting multiple messages from the same window rather than from other windows. Or maybe Firefox does something bizarre (probably a bug) when you load a frame script using the global message manager from a window scope. Short answer, don’t do it.

You should only use the global message manager from a singleton - the top level script of an SDK or bootstrap addon, an XPCOM component, or a javascript code module. If you don’t have those then not to worry. Your version using window.messageManager.loadFrameScript is fine.

Thanks Lithopsian,

Yes, I’m doing this in my overlay script, which is loaded into each window (which I suppose is usual behavior for the XUL addons).

If window.messageManager.loadFrameScript is fine, I’ll stick to it.

:heart:

As first i would recommend to use the global mm only if you want to send a message to all over the world.

For addressing frame script in tabs a message manager from category browser is sufficient.

let mm = window.getGroupMessageManager(“browsers”);
mm.loadFrameScript(“chrome://youraddon/content/content.js” , true);

For you initial question if you got a message from a framescript you can return to this frame script as following:

from your message listener:

let browserMM = msg.target.messageManager;
browserMM.sendAsyncMessage()…

or from events like clicks:

var gBrowser = evt.target.gBrowser
var browser = gBrowser.getBrowserForTab(gBrowser.mCurrentTab);
var browserMM = browser.messageManager;
browserMM.sendAsyncMessage()…

I didn’t even know there was such a thing as group message managers. Introduced in Firefox 32, but entirely undocumented.

Apparently frame scripts loaded into “all” frames can cause errors (or at least error messages) in custom browsers that some addons create. Hence the need to be able to load frame scripts only into proper Firefox browsers.

I’m starting to feel increasingly paranoid. As if Mozilla are deliberately trying to trip up addon developers so they can then blame them for all the woes of Firefox. Or just don’t give a flying …