getBackgroundPage in private mode

my addon uses a background script which collects information (using webRequest API, so I think I need to use a background script) and it has a browser action which opens a popup. The popup needs to get some info from the background script. For this, the popup script calls runtime.getBackgroundPage, and this works properly in normal tabs/windows. However, for tabs/windows in private mode, getBackgroundPage always returns (or more precisely, gets fulfilled with) null. Is this intended? If so, what would be the right approach for communicating between popup and background script?

1 Like


I am having the same issue. The popup scripts returns null for getBackgroundPage. Please resolve the issue.

To be honest, I don’t really see how this improves the behavior of an extension, but I also don’t think that it will change soon.

what would be the right approach for communicating between popup and background script?

You will have to use this:

Thanks for the reply. So it is intended? Is this documented anywhere? I think it should be. Perhaps their should be separate instances of the background page for private mode?

Now I’m trying to use the runtime.connect approach. Right away I noticed that it’s not possible to send arbitrary JS objects, e.g. the object I’m sending contains a Map instance, but this gets silently dropped. The documentation does say that one can only send “JSON objects”, which I guess means objects which can be completely expressed in JSON. :persevere:

Chrome can do that, and if you decide to go with a single instance (and the user explicitly enables the extension for incognito) it won’t load extension tabs in the incognito windows. I haven’t tested panels yet.

I guess means objects which can be completely expressed in JSON.

Exactly that.

A little update on this:

The mozilla sources contain a test case browser/components/extensions/test/browser/browser_ext_incognito_views.js in which it says: "Should not be able to access background page in incognito context".
So this is intended behavior.

On the other hand it does not happen if Firefox starts in the Private-only mode, so there should always be at least one window that can hold tabs that do have access to the background page.

Since Chrome (in spanning mode) won’t open extension tabs in incognito windows at all (network error), I think it is acceptable to tell the user to open extension pages in a non-private window in Firefox too (or rather, do that automatically).
Especially because there are other problems too, for example blob:-URLs created in the non-private context won’t be available in private windows.

Which still leaves the problem which panels in Firefox Private Windows.
What I decided to do is to open a (non-private) poup window at the same spot where the panel would be. Then I manually implement the close-on-blur behavior. The visual difference is quite small and the performance can be acceptable too.

My implementation can be found here:

Hi, new here and trying to understand how this works.

So I have a popup that uses the background page’s variables and functions directly, accessed via runtime.getBackgroundPage.

But in order for the popup to work in private mode, I’m supposed to have the popup use runtime.connect to send a JSON message to the background, then background does its jobs, and then sends results in a JSON message back to the popup. Like that?

And does this mean for an extension to support private mode, I should forget about runtime.getBackgroundPage altogether? Or should I accommodate with both pathways, one for private one for normal?

For what it’s worth: In popup.js, I use browser.runtime.sendMessage() to request data I want or to send changes to global vars. In background.js I use browser.runtime.onMessage.addListener() to respond to the requests. I don’t know whether it is better to use browser.runtime.connect().


Disclaimer: As you can tell, I’m not a trained programmer.


I had this exact problem before doing a refactor of my addon.

In my non background page process I do this:

let port = browser.runtime.connect({
    name: 'someUniqueName'
something.addEventListener('change', () => {
        thisCanBeAnyJSONObject: true

Then in my background process I have this

browser.runtime.onConnect.addListener((port) => {
    if ( === 'someUniqueName') {
        port.onMessage.addListener((msg) => {
            // here the `msg` variable holds the JSON we sent from the non background script
            // // I just needed to check for a flag, in your case you probably need to deseralise your data
            if (msg.thisCanBeAnyJSONObject) {

This setup a control flow of user clicks element -> message background page -> background page does something

The advantage of using a Port is you can establish two way communication if you later need to add extra steps such as a control flow of
Non background process -> Background process -> Non background process again
by registering a listener to the port in your non background process as well.

Note the functions for Port based communication with content scripts are different :confused:

Hi everyone,

Belated thanks for your responses. I have since refactored and worked with browser.runtime.connect() to pass data between popup and background.

New question. This message is frequently seen in the addon console:

Unchecked lastError value: Error: Could not establish connection. Receiving end does not exist.

Do I just ignore, or is this a sign to use browser.runtime.sendMessage() instead? I assume because a popup is too brief an instance, compared to a content script, for connect() to be appropriate?

That is a sign that you’re trying to connect when the popup isn’t open. You likely want to open the connection from the popup’s side.

Yes it was opened from the popup’s side.

Reworked my code to use sendMessage instead, to see if the error will go away. It didn’t. I now suspect it’s an error from a different extension (I’m working on this ‘manually’ in my regular Firefox profile, not via web-ext) so it’s really nothing of mine to worry about.

I think sendMessage seems more appropriate anyway; it’s just the popup requesting data from the background a few times within its brief lifetimes. The flow of code is more straightforward too, since response handling can be placed immediately after a request, unlike with connect.

For anyone else ending up here, I’ll share some helpful info that are all over, found in not-so-intuitive places, and should probably be put into one article somewhere:

Content scripts - Choosing between one-off messages and connection-based messaging

1 Like