# Manifest V3 issues for my extension

**URL:** <https://discourse.mozilla.org/t/manifest-v3-issues-for-my-extension/142970>\
**Category:** Development\
**Created:** [May 4, 2025, 6:01am UTC](https://discourse.mozilla.org/t/manifest-v3-issues-for-my-extension/142970 "2025-05-04T06:01:05Z")\
**Posts on this page:** 7\
**Page:** 1

<div class="post-metadata">

**Author:** ![Offerel1](https://avatars.discourse-cdn.com/v4/letter/o/91b2a8/32.png) [@Offerel1](https://discourse.mozilla.org/u/Offerel1)\
**Post date:** [May 4, 2025, 6:01am UTC](https://discourse.mozilla.org/t/manifest-v3-issues-for-my-extension/142970/1 "2025-05-04T06:01:05Z")

</div>

I have converted my manifest v2 to a manifest v3 extension. I have a key

```auto
"background": {
       "scripts": ["background.js"]
   },

```

In my manifest. In background.js i have a function init(). I call this function with some of the first lines in background.js with simply

```auto
    init();

```

How can it be, that init() get automatically called again from time to time and not only after browser startup. How can i call init() only once after startup of the browser?

Using

```auto
    chrome.runtime.onStartup.addListener(async () => {
        init();
    });

```

does not work. It doesnt do anything. This seems to work only in Chromium bases browsers.

Does someone has a better idea?

---

<div class="post-metadata">

**Author:** ![juraj.masiar](https://sea1.discourse-cdn.com/flex001/user_avatar/discourse.mozilla.org/juraj.masiar/32/30587_2.png) [@juraj.masiar](https://discourse.mozilla.org/u/juraj.masiar)\
**Post date:** [May 4, 2025, 6:54am UTC](https://discourse.mozilla.org/t/manifest-v3-issues-for-my-extension/142970/2 "2025-05-04T06:54:58Z")

</div>

Make sure to read the migration guide (ideally both)

> **[Manifest V3 migration guide](https://extensionworkshop.com/documentation/develop/manifest-v3-migration-guide/)**
>
> Get help creating & publishing Firefox extensions.

> **[Migrate to Manifest V3  |  Chrome Extensions  |  Chrome...](https://developer.chrome.com/docs/extensions/develop/migrate)**
>
> A guide to converting Manifest V2 extensions to Manifest V3 extensions.

Basically, you background page/script is automatically killed after 30s of inactivity.  
So when there is an event that “wakes it up”, it’s not really being woken up, it’s just re-executed from the top.

In any case, if you have a piece of code that needs to be executed only ONCE per extension start, you should use `storage.session` - for storing some “started” flag boolean (or Date).

For example, this is my helper file for handling code that needs to run on start:

```ts
import {isRunningInBackground} from "../environment/context_detection";

browser.runtime.onStartup.addListener(() => {
  // NOTE: in MV3 the background script won't start without onStartup (or similar) listener
});

const KEY = '_ext_started';

// resolves to "TRUE" only if extension just booted. Resolves to "FALSE" if service worker just woke up OR if called from non-background script.
export const didExtensionStarted = (async () => {
  // only allowed in background:
  if (!isRunningInBackground) return false;
  // in MV3 we check runtime storage
  const {[KEY]: extensionStarted} = await browser.storage.session.get(KEY);
  if (extensionStarted) return false;
  // we save the time when extension started
  await browser.storage.session.set({[KEY]: Date.now()});
  return true;
})();

// resolved ONLY when extension starts - re-starting event page / service worker won't resolve!
export const extensionStarted = new Promise<void>(resolve => didExtensionStarted.then(b => b && resolve()));

export async function getExtensionStartTime(): Promise<number> {
  await didExtensionStarted;
  const {[KEY]: startTime} = await browser.storage.session.get(KEY);
  return startTime;
}

```

---

<div class="post-metadata">

**Author:** ![stig](https://sea1.discourse-cdn.com/flex001/user_avatar/discourse.mozilla.org/stig/32/24662_2.png) [@stig](https://discourse.mozilla.org/u/stig)\
**Post date:** [May 4, 2025, 7:10am UTC](https://discourse.mozilla.org/t/manifest-v3-issues-for-my-extension/142970/3 "2025-05-04T07:10:31Z")

</div>

Do you want to support both Firefox and Chrome from same source?

Just want to add, that to use `browser` namespace instead of `chrome` name space, you might add something like this in top of your scripts:

`globalThis.browser ??= chrome;`

Also Chrome and Firefox supports backgroundscripts in different ways. But you can make a manifest with something like

```auto

  "background": {
    "scripts": ["backgroundscript.js"],
    "service_worker": "backgroundscript.js",
    "type": "module"
  },

```

and make sure your backgroundscript.js is a “module” that is able to work both as a “service\_worker” (Chrome) and as a “classic” background script (Firefox). If a browser supports both ways, it will run it as a service worker.

---

<div class="post-metadata">

**Author:** ![Offerel1](https://avatars.discourse-cdn.com/v4/letter/o/91b2a8/32.png) [@Offerel1](https://discourse.mozilla.org/u/Offerel1)\
**Post date:** [May 4, 2025, 11:07am UTC](https://discourse.mozilla.org/t/manifest-v3-issues-for-my-extension/142970/4 "2025-05-04T11:07:48Z")

</div>

> [@stig](#):
>
> `"type": "module"`

Ahh the “module” part is new for me. Many thx for this tip.

---

<div class="post-metadata">

**Author:** ![Offerel1](https://avatars.discourse-cdn.com/v4/letter/o/91b2a8/32.png) [@Offerel1](https://discourse.mozilla.org/u/Offerel1)\
**Post date:** [May 4, 2025, 11:17am UTC](https://discourse.mozilla.org/t/manifest-v3-issues-for-my-extension/142970/5 "2025-05-04T11:17:11Z")

</div>

This looks… complicated. But anyway, i will give this a try.

In the meantime, i figured out, that testing my extension with `web-ext run --keep-changes` seems to ignore `chrome.runtime.onStartup.addListener()`, thats why it wasnt executed.

One question to :

> Basically, you background page/script is automatically killed after 30s of inactivity.  
> So when there is an event that “wakes it up”, it’s not really being woken up, it’s just re-executed from the top.

If i register something like `chrome.bookmarks.onCreated.addListener()` in the described init() function, does this listener survive killing the background script after 30s or must i re-register it with every wakeup?

---

<div class="post-metadata">

**Author:** ![juraj.masiar](https://sea1.discourse-cdn.com/flex001/user_avatar/discourse.mozilla.org/juraj.masiar/32/30587_2.png) [@juraj.masiar](https://discourse.mozilla.org/u/juraj.masiar)\
**Post date:** [May 4, 2025, 1:35pm UTC](https://discourse.mozilla.org/t/manifest-v3-issues-for-my-extension/142970/6 "2025-05-04T13:35:40Z")

</div>

The `onStartup` will be executed only when profile is started:

> **[runtime.onStartup - Mozilla | MDN](https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/API/runtime/onStartup)**
>
> Fired when a profile that has this extension installed first starts up. This event is not fired when a private browsing (incognito) profile is started, even if this extension is operating in 'split' incognito mode.

So it’s very likely it won’t run after installing/updating your extensions, so watch out! 🙂

Regarding `bookmarks.onCreated`, yes, you need to register that handler on top of your background script. In the “init” function it’s also fine, BUT only if it’s executed when the background scrip is executed. Also, it can’t be async, I think…

Basically, when your extension is installed, browser will quickly run your background script, to see, what handlers it’s listening to. And those are then used to “wake it up”.  
But if the handler is registered in some callback that runs “later” (async), then browser will not pick it up.

---

<div class="post-metadata">

**Author:** ![Offerel1](https://avatars.discourse-cdn.com/v4/letter/o/91b2a8/32.png) [@Offerel1](https://discourse.mozilla.org/u/Offerel1)\
**Post date:** [May 4, 2025, 7:20pm UTC](https://discourse.mozilla.org/t/manifest-v3-issues-for-my-extension/142970/7 "2025-05-04T19:20:00Z")

</div>

The init process does not have to run immediately after installation or update, for this, i use `chrome.runtime.onInstalled` . It is only important that init() starts once when the browser is started, but not multiple times.

Thanks to the tips here, I have now modified the script and uploaded an updated version to AMO. As far as I can tell, everything has worked. If something doesn’t work, I’ll monitor it over the next few days and report back. Until then, I consider it solved for now.
