Pockets
Pockets

Reputation: 1264

Chrome Extension Messaging Architecture

I recently got started on developing my first Chrome extension, mostly just to see if I could do it, and I got to wondering about the mechanics behind how exactly messages are passed around. This may turn out to be a more general question about the windows.postMessage API, but I was wondering if someone could explain the behind-the-scenes processes that control the receipt and propagation of messages.

This is my current understanding of messaging w.r.t Chrome extensions:

There's a couple of things I have questions about specifically - the first one being, of course, if I've even described things correctly. The others are these:

Upvotes: 1

Views: 1321

Answers (1)

Rob W
Rob W

Reputation: 348962

  • There are four classes of Javascript: (1) JS tied to the popup, (2) JS running in the background or as an event, (3) JS in content scripts, and (4) JS injected into the page.

(1) and (2) are actually the same ("popup" also falls under "extension code" - see Contexts and methods for communication between the browser action, background scripts, and content scripts of chrome extensions?).

  • (2) -> (1) and (3) -> (1) are sent by runtime.sendMessage(). They're only received when the popup is open.
  • (1) -> (2) and (3) -> (2) are sent by runtime.sendMessage() or tabs.sendMessage() from (3) and (1) respectively. They're received ASAP, because background/event JS is persistent (I'm not quite sure if that's the right word, since event JS has to be tied into eventListeners).

runtime.sendMessage can indeed be sent by (1), (2) and (3). In the role of a receiver, there is no special difference between (1) and (2). Here is where my classification of scripts comes in: A message sent by chrome.runtime.sendMessage is received by extension code, except for the frame of the sender. For instance, if you put an <iframe> in the background page and call chrome.runtime.sendMessage from the background page, chrome.runtime.onMessage is going to be triggered in that frame.

When event pages are in use, the message event is only dispatched after having waited for the event page to load.

  • (1) -> (3) and (2) -> (3) are sent by tabs.sendMessage(). They're received ASAP, since the content script is active as long as the webpage is (since it exists in a parallel sandbox).

chrome.tabs.sendMessage makes sense as long as you have a valid tab ID. Since background pages have no tab ID, it's not possible to send a message to a background page using tabs.sendMessage. Content scripts, extension pages in tabs, extension frames in tabs can all receive messages that are sent by chrome.tabs.sendMessage, since they are part of the tab.

  • (*) -> (4) and (4) -> (*) can't be handled by chrome.* for security issues, but (3) -> (4) and (4) -> (3) can be handled by window.postMessage(), since they both exist within the context of the webpage.

(4) -> (1,2) (web page to extension code) is possible via externally_connectable.

And (4) -> (4) is also possible with window.postMessage, but not directly with *.sendMessage (because this excludes the sender).

  • What exactly is the distinction between runtime.sendMessage() and tabs.sendMessage() that dictates which JS can actually use them?

tabs.sendMessage can only be used when the tabs API is available, to send a message to tabs, whereas runtime.sendMessage can also be used by content scripts.

Succintly, use tabs.sendMessage to send messages to a tab, use runtime.sendMessage to send messages to another part of your extension.

  • How do messages get passed around? What happens in the background when *.sendMessage() or window.postMessage() is called?
  • window.postMessage has exactly one receiver, i.e. the message event is called only once. *.sendMessage has zero or more receivers (if you use tabs.sendMessage, then you can restrict this to one by setting the frameId parameter), so chrome.runtime.onMessage could be called multiple times (once for every frame).

  • If you are not careful, you can accidentally leak information to untrusted scripts via window.postMessage. Make sure that you validate all messages in both directions (from and to the web page) - see the Window.postMessage#Security concerns article on MDN for more info. *.sendMessage only passes messages around within your extension, so that is generally safe.

  • *.sendMessage messages are JSON-serialized, which is much more restricted than the structured cloning algorithm as used by postMessage.

  • *.sendMessage always requires at least two IPC messages (sender to browser process, browser process to every receiver). postMessage can be handled more efficiently, since the messages are dealt with within the same renderer process.
    This is an internal implementation detail, and is not guaranteed to hold in the future. What it means in practice is that if you send lots of data around, and profile the performance, then you will most likely observe more issues with *.sendMessage than window.postMessage (for a one-sided benchmark, see e.g. Is there a size limit like 32bytes or 64Bytes? for message passing between content scripts and background pages for chrome extensions?).

  • *.sendMessage(any message, optional function responseCallback) is internally implemented as something that is equivalent to calling chrome.runtime.connect (or chrome.tabs.connect), registering responseCallback using port.onMessage.addListener and then disconnecting the port (immediately if no callback is set, otherwise only after the callback was called).

    window.postMessage queues a message. The queue is not immediately drained, but the message event is dispatched "as soon as possible".

Upvotes: 4

Related Questions