1

We have a Firefox/Chrome web extension which contains both background and content scripts. The background script maintains a cache of shared key/value pairs which are needed by the content scripts. It is not difficult to access them via the browser.runtime.SendMessage functionality.

However, we need access to these key/value pairs as quickly as possible on page load, before any scripts run on the original page. This is generally not possible because the async nature of SendMessage means that the background script will not respond to requests made by the content scripts fast enough.

We've been looking for solutions along the following lines:

1. Sync/blocking lookup

It doesn't appear that there are any mechanisms for this.

2. Initializing the content script

We can register content scripts using browser.contentScripts.register and potentially pass a copy of the entire cache in via javascript. However, this is called only once and all subsequent tabs/pages will load whatever was specified in it. We might be able - with great difficulty - create a listener for global cache changes and re-register a new content script each time.

Are there any better approaches than this?

Update: By adding a listener to browser.webNavigation.onBeforeNavigate, I am now able to inject a global variable which displays in the console output:

In background.js:

browser.webNavigation.onBeforeNavigate.addListener(function(details) {
    browser.tabs.executeScript(
        details.tabId,
        {
            code: `window.scope.somevariable=true;
            console.log("I executed in content script:", "somevariable", window.scope.somevariable);`
        }
    );
});

In my library which was injected by register, I am also able to print window.scope out to the console and I see the variable there.

However... when I try to access the variable programmatically, it returns "undefined".

In content script:

// this displays "somevariable" among the window scope properties:
console.log("window.scope", window.scope);

// this displays "undefined":
console.log("somevariable", window.scope.somevariable);

Why can the registered js library output the variable to the console window but can't actually read it?

Steve Cohen
  • 53
  • 1
  • 7
  • Re-registering is the correct approach. Another one is [abusing the document.cookies via webRequest](https://stackoverflow.com/questions/45102497/injecting-javascript-variable-before-content-script) but it'll fail on superfast load sequence like cached history navigation. – wOxxOm Feb 07 '19 at 04:47
  • I really don't think this is feasible as it could potentially inject a large array into every tab. See update. – Steve Cohen Feb 07 '19 at 15:59
  • 1) An array is an easy to process object, injecting it shouldn't incur a noticeable performance penalty. 2) Your main content script can set the array to null if it's not needed so it'll get garbage-collected. 3) Anyway there are no other solutions. – wOxxOm Feb 07 '19 at 16:01
  • onBeforeNavigate will run on a previous tab (before navigation) as well as executeScript. – wOxxOm Feb 07 '19 at 16:05
  • "injecting it shouldn't incur a noticeable performance penalty." That's a pretty big assumption when one could potentially be dealing with dozens of open tabs and several different browsers. – Steve Cohen Feb 07 '19 at 16:18
  • "onBeforeNavigate will run on a previous tab (before navigation) as well as executeScript." Can you clarify what you mean by this? – Steve Cohen Feb 07 '19 at 16:19
  • 1. browsers are really fast nowadays especially for inert declarations like an array/object so unless your measurements indicate otherwise there's no need to worry preemptively, 2. uhm, onBeforeNavigate runs before navigation so executeScript inserts the code into the document that's currently present in a tab, not in a new document that's about to be loaded. – wOxxOm Feb 07 '19 at 16:32
  • "onBeforeNavigate runs before navigation" - Thank you. I wasn't aware of this and just confirmed. If I use onCommitted, the event fires too late to beat the page load. So it is starting to appear that I'm going to have to reregister the content script each time the underlying map changes. – Steve Cohen Feb 07 '19 at 16:48
  • BTW maybe you can split the array into chunks (e.g. 10) and reregister just the changed parts. – wOxxOm Feb 07 '19 at 16:51

0 Answers0