Search code examples
javascriptjqueryfirefoxpostmessagefirefox-addon-webextensions

What are the alternatives to window.postMessage for sending a message between the page and content script?


I'm using a Firefox WebExtension that makes the link between a web page and a native executable (using native messaging API).

So far, I've been using a pair of content / background scripts, and I send / receive messages using window.PostMessage like this:

Page Script

// Method used to communicate with content sript of the Firefox WebExtension

var callExtension = function(JSONmessage, timeout, callbackFnk) {

    var promiseRequest = new Promise((resolve, reject) => {
      const listener = (event) => {
        if (
          event.source == window && event.data.direction
          && event.data.direction == "my-extension-from-content-script"
        ) {
          resolve(event.data.message);
        }
      }

      setTimeout(() => {
        window.removeEventListener("message", listener);
        resolve(false); // Timeout 
      }, timeout);

      window.addEventListener("message", listener);

      window.postMessage({
        direction: "my-extension-from-page-script",
        message: JSONmessage
        }, "*");
    });

    promiseRequest.then((result) => { 

         // now we are calling our own callback function
          if(typeof callbackFnk == 'function'){
            callbackFnk.call(this, result);
          }

    });

};

// Checks the presence of the extension

callExtension("areYouThere", 200, function(result) { 
    if(result) {
        $("#result").text("Extension installed");
    } else {
        $("#result").text("Extension not installed");
    }
});

Content Script

window.addEventListener("message", function(event) {
  if (event.source == window &&
      event.data.direction &&
      event.data.direction == "my-extension-from-page-script") {

    if(event.data.message == "areYouThere") { 

        /** Checks the presence of the extension **/

        window.postMessage({
        direction: "my-extension-from-content-script",
        message: "OK"
      }, "*");
    } 
  } 
});

The code works fine on a simple web page, but when I try to make it work on a page that already uses a window.postMessage and window.addEventListener ("message", ...), the message sent from the page is not captured by the extension, and so my extension cannot work.

  1. Is there a way to send a message from the page script to the content script that does not use window.postMessage and window.addEventListener?

  2. If not, how to be sure the message that is sent from window.postMessage from the page will be sent only to my extension and will not be captured by another listener?


Solution

  • It appears that the web page that you are working with is not playing nice with window.postMessage. This is understandable, as such a web page would normally be expecting to be the only thing using it in the page.

    You have four alternatives:

    1. Install your message event listener on the window before the web page scritps install any listeners so that you receive the event first. This will most likely be done by injecting at document_start and installing your listener prior to the scripts for the page being loaded. In addition to your call to .addEventListener() being prior to any script on the page acting (i.e. injecting at document_start), you should indicate that your listener will useCapture. This will cause your listener to be called prior to any listener added by page scripts. You will need to uniquely identify in the message content that messages are to/from your extension code. You will need to cancel the event (.stopImmediatePropagation() and .stopPropagation()) for those messages which are yours, and allow the event to propagate for those that are not.
    2. Use a custom event just for your extension.
    3. Directly manipulate variables/functions and/or call functions in the page context by inserting additional <script> elements.
    4. Dynamically change the code of the web page's scripts so they play nice. This will only work on a single page/domain and could easily break if the web page changes its code. While possible, this is not a good option.