Errors using webextension-polyfill to make a cross-browser add-on

Hi everyone,

I have been attempting to improve my add-on by making it cross-browser so that it’ll work on Google Chrome.

My add-on is a simple pop-up that modifies JS on the webpage that it is opened on via content scripts. Here my file structure to have an idea of the different scripts being used (I developed my add-on for Firefox, so it works as expected on this browser):

After looking for relevant documentation to make it cross-browser, I found the following two links:

I have tried implementing what is described on the documentation by installing browser-polyfill via npm, and using it like by adding those lines in my manifest.json:

"background": {
    "scripts": [
      "node_modules/webextension-polyfill/dist/browser-polyfill.js"
    ]
  }

I also tried using this line at the top of my popup_listener.js file but it was not successful:

browser.tabs.executeScript({file: "browser-polyfill.js"});

Currently, the extension loads with no errors on Chrome, and when opening the add-on on the toolbar the UI loads correctly as well. However the following error occurs when using the functionality of the add-on:

Uncaught ReferenceError: browser is not defined

Here is the full stack trace on Chrome:

I am sure I am missing something from the documentation but I cannot find anything that is helpful. Any help from anyone who managed to make a cross-browser add-on would be appreciated.

Thanks for your help :slight_smile:

The browser polyfill library is super simple code.
You could say, that all it does is run a script with code like: window.browser = {...}

Now that you know how it works, you should know how to use it.
All you need to do is MAKE SURE the library code is executed BEFORE your code.

By your code I mean in all contexts of your addon.
So add it in your background script array in manifest, add it in the popup.html page as a script tag above your own script tags. And if you are injecting JS into current page, inject the library before that.

And that should be it :slight_smile:.

I’m not sure though how do you bundle it though when it’s in the “node_modules” folder. Are you using webpack or some other bundler? If not, then just download it or copy it from there into some “library” folder in your source folder.

Hi Juraj,

Thanks for the quick and helpful reply!

What you said makes sense, I have made sure that the library is being used in every location before a reference to browser.. However, I am still having trouble getting it to work properly as I am getting the same error on Chrome.

I have imported the library in the manifest.json using

"background": {
    "scripts": [
      "node_modules/webextension-polyfill/dist/browser-polyfill.js"
    ]
  },

  "content_scripts": [{
    "js": [
      "node_modules/webextension-polyfill/dist/browser-polyfill.js",
      "content_scripts/create_total_cards_button.js",
      "content_scripts/hide_card_counters.js",
      "content_scripts/show_card_counters.js"
    ]
  }]

And I have references the library before my popup listener by adding the script first:

<script type="application/javascript" src="node_modules/webextension-polyfill/dist/browser-polyfill.js"></script>
<script type="application/javascript" src="popup_listener.js"></script>

However this still does not seem to be working and browser.tabs is not being recognised. Did you ever run into an issue like this that required additional steps?

Thanks for the help, I appreciate it!

PS: checked out your website and add-ons, they’re really cool and look amazing! :slight_smile:

1 Like

I’m pretty sure I got the same error, but it was over a year ago and I don’t remember too well :frowning: I think it didn’t work for me when I copied the file directly from the github page ( https://github.com/mozilla/webextension-polyfill/blob/master/src/browser-polyfill.js ).

I use this file and I don’t have any problems:
https://unpkg.com/browse/webextension-polyfill@0.9.0/dist/browser-polyfill.js

I think I see the problem, you are using wrong “src” path (relative). There is no “node_moudles” in the “popup” folder, there is only your javascript.

So to target “node_modules” you need to either go one folder up, like “../node_modules/...” or even better, use slash to make it absolute, like this: “/node_modules/…”.

This applies to all imports and all file access. If you are in the nested folder and you type name of the file, it will look for the file in the current folder. So it’s best to use full path from the root folder, that can be done by adding “/” prefix and then full path to the target file.

But just in case, let’s make it simple, don’t use it from node_modules directory but download it from their repo directly (like Adam said), see their releases page:


(you can use “Ctrl+s” on this page)

Download the file and place it to the root folder. Then when you use it, use “/browser-polyfill.min.js” as path.

Hi,

Thanks for the help Juraj and Adam.

Unfortunately I am yet to get my add-on to work on Chrome despite your help!

I have tried the following:

  • Installed both the browser-polyfill.js and browser-polyfill.min.js libraries manually in my root directory.
  • Referenced the library directly using the root path as you guys recommended /browser-polyfill.js in the following locations:
    • My manifest.json using "background": {"scripts": ["browser-polyfill.js"]},
    • My main popup HTML document before the popup_listener.js is called using <script src="/browser-polyfill.js"></script>

It seems to be loading correctly now but I am getting a mountain of errors that seem to be originating from the browser-polyfill library, and consequently errors in my JS code saying "Uncaught ReferenceError: browser is not defined".

Here are some of the errors that I am facing on Chrome when loading the add-on and using it for the first time:

If it helps clarify what I am doing, I have pushed my code to a GitHub branch.

I am curious, has this happened to any of you guys? Am I missing something fundamental? Perhaps I am supposed to add some code myself once I’ve loaded this library to make sure browser is recognised by Chrome? Is there maybe a better way to do this? Maybe just create a separate extension for Chrome directly?

Anyway, thanks again for the help, I appreciate it!

You have downloaded the wrong file :smiley:.

In the screenshot source code you see:

  // The build process adds a UMD wrapper around this file, which makes the
  // `module` variable available.

And then error saying “module” not found.
The file you have is a source file, it needs to be build first.

Use the browser-polyfill.min.js file instead, that’s already build and minified so it’s small. You won’t really need to debug it anyway :slight_smile:.

EDIT:
But I agree it’s way too complicated even for my taste.
Not only they don’t have a “dist” folder in their sources (which may be understandable), but even the “releases” page doesn’t have the dist package included. Instead they are hosted on some totally random looking domain unpkg.com.

It’s still better than it was before when you had to BUILD IT YOURSELF! :smiley: And it was not even working on Windows :smiley:.

I did indeed download the wrong file, my bad. Thanks for spotting it!

I’ve downloaded the correct file now and am using the minified version correctly.

However, I am still getting the Uncaught ReferenceError: browser is not defined errors on this line: browser.tabs.executeScript({file: "../content_scripts/show_card_counters.js"});

I might be a bit confused about how to use this extension, but it was my understanding that using this library would allow Chrome to recognise browser instead of creating a new add-on that uses chrome?

Am I missing something obvious?

PS: that sounds dreadful, but yeah the documentation and releases can definitely be improved :smiley:

Don’t worry, we can fix this easily.
You are obviously not injecting the polyfill with your content scripts.

For example:

window.onload = (event) => { // https://developer.mozilla.org/en-US/docs/Web/API/Window/load_event
  browser.tabs.executeScript({file: "../content_scripts/create_total_cards_button.js"});
};

You can change it into something like this:

window.onload = async event => { // https://developer.mozilla.org/en-US/docs/Web/API/Window/load_event
  await browser.tabs.executeScript({file: "/browser-polyfill.min.js"});
  await browser.tabs.executeScript({file: "/content_scripts/create_total_cards_button.js"});
};

Note that I made it async so that I can await the polyfill injection.

Also, regarding the script injection, use the “/” in the beginning, it’s also mentioned in the docs:

https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/API/tabs/executeScript#file

Also few more things to mention after looking at your github page:

  • you can use ko-fi.com for donations, it has no fees and it’s full of features
  • in your manifest file:
  "background": {
    "scripts": ["browser-polyfill.min.js"]
  },

You can remove it completely since you don’t have any background scripts to be polyfilled :slight_smile:.

  • your content scripts are in a wrong order, I’m pretty sure they are executed in the defined order, so you need to make the polyfill first!
  "content_scripts": [{
    "matches" : ["<all_urls>"],
    "js": [
      "content_scripts/create_total_cards_button.js",
      "content_scripts/hide_card_counters.js",
      "content_scripts/show_card_counters.js",
      "browser-polyfill.min.js"  // this is bad, it needs to be first
    ]
  }],

Also, you do you really need to inject all those scripts into all pages? Isn’t this extension for some specific site only?

Wow , thanks for all the help not just with this question but with my whole extension in general. You’re very knowledgeable about browser extensions, very impressive!

So I’ve implemented your recommendations again and it is partially working. I re-ordered the content script to have browser-polyfill be first, turned the window.onload event into an async one and used await like you recommended.

However, I am unsure how to use for the the next content script to execute and the ones inside an event listener. Do I basically wrap my whole code in popup_listener.js into an async event? Or do I create multiple async events? I am currently getting more errors linked to polyfill library when using this code, which is weird because I am definitely using it correctly now as it works for the first 2 executeScript() but not the first one:

window.onload = async () => { // https://developer.mozilla.org/en-US/docs/Web/API/Window/load_event
    await browser.tabs.executeScript({file: "/browser-polyfill.min.js"});
    await browser.tabs.executeScript({file: "/content_scripts/create_total_cards_button.js"});
    await browser.tabs.executeScript({file: "../content_scripts/show_card_counters.js"}); // this does not run
};

// Listen for events on the on/off toggle switch button to decide which script to run.
document.addEventListener("click", (e) => {

    // Show the  number of Trello card in each column.
    if (e.target.checked === true) {
        browser.tabs.executeScript({file: "../content_scripts/show_card_counters.js"});
    }

    // Hide the  number of Trello card in each column.
    if (e.target.checked === false) {
        browser.tabs.executeScript({file: "../content_scripts/hide_card_counters.js"});
    }

});

Additionally I addresses some of your recommendations (most of them came while I was trying to get my extension to work on Chrome so I’ve been trying so many different things I found online :D):

  • added "/" at the beginning like you said
  • removed polyfill being used in background scripts as I don’t have any
  • modified the"matches" key to only relevant pages.

PS: I’ve been using “Buy Me A Coffee” but they take fees, so thanks for sharing ko-fi with me! :slight_smile:

So, in your code you still use wrong path: executeScript({file: "../content_scripts

Don’t use .., always start with “/”.

Also, I’ve just noticed, you are using old API that’s gonna disappear in 4 months in Chrome :slight_smile:.
Did you hear about Manifest V3? You need to migrate your chrome extension into MV3 till January 2023.

The good news is, the new API supports injecting multiple files, for example:

await browser.scripting.executeScript({
  target: {
    tabId: tabId, 
    allFrames: false,
  }, 
  files: [
    '/browser-polyfill.min.js', 
    '/content_scripts/foobar.js',
  ],
});

More info:

That did it, it’s working perfectly on Chrome now! :slight_smile:

Didn’t even had to change the event listener, just a combination of all the tips you shared and using root paths. Thanks a lot for the help!

I’ll definitely try to update it to use manifest V3 next, might need your help again, I had a look at the documentation and there isn’t much info shared there as well :laughing:

Thanks again for the help!

1 Like