# Handling Extension Updates

When releasing an update to your extension, there's a couple of things you need to keep in mind:

[[toc]]

## Content Script Cleanup

Old content scripts are not automatically stopped when an extension updates and reloads. Often, this leads to "Invalidated context" errors in production when a content script from an old version of your extension tries to use a web extension API (ie, the `browser` or `chrome` globals).

WXT provides a utility for handling this process: `ContentScriptContext`. An instance of this class is provided to you automatically inside the `main` function of each content script.

When your extension updates or reloads, the context will become invalidated, and will trigger any `ctx.onInvalidated` listeners you add:

```ts
export default defineContentScript({
  main(ctx) {
    ctx.onInvalidated(() => {
      // Do something
    });
  },
)
```

The `ctx` also provides other convenient APIs for stopping your content script without manually calling `onInvalidated` to add a listener:

1. Setting timers:
   ```ts
   ctx.setTimeout(() => { ... }, ...);
   ctx.setInterval(() => { ... }, ...);
   ctx.requestAnimationFrame(() => { ... });
   ```
1. Adding DOM events:
   ```ts
   ctx.addEventListener(window, "mousemove", (event) => { ... });
   ```
1. Implements [`AbortController`](https://developer.mozilla.org/en-US/docs/Web/API/AbortController) for canceling standard APIs:
   ```ts
   fetch('...', {
     signal: ctx.signal,
   });
   ```

Other WXT APIs require a `ctx` object so they can clean themselves up. For example, [`createIntegratedUi`](/guide/key-concepts/content-script-ui#integrated), [`createShadowRootUi`](/guide/key-concepts/content-script-ui#shadow-root), and [`createIframeUi`](/guide/key-concepts/content-script-ui#iframe) automatically unmount and stop a UI when the script is invalidated.

:::warning
When working with content scripts, **you should always use the `ctx` object to stop any async or future work.**

This prevents old content scripts from interfering with new content scripts, and prevents error messages from being logged to the console in production.
:::

## Testing Permission Changes

When `permissions`/`host_permissions` change during an update, depending on what exactly changed, Chrome will disable your extension until the user accepts the new permissions.

It is possible to test this before you release an update, but it's not a simple process:

1. Get 2 ZIPs of your extension, both generated by `wxt zip`. The first contains a previous version of your extension, the second contains the latest version. Make sure the second ZIP's version is higher than the first's.
2. Unzip the two ZIP files somewhere next to each other that's easy to locate.
3. In Chrome, open `chrome://extensions` and make sure developer mode is enabled
4. Pack the first extension into a CRX, generating a new private key:
   1. Click "Pack Extension" in the top left
   2. For "Extension root directory", enter the path to the first extracted zip directory. The directory should contain a `manifest.json` file
   3. Leave "Private key file" blank
   4. Click "Pack Extension". This will generate a `.crx` and `.pem` file
5. Pack the second extension into a CRX, reusing the private key generated by the previous step
   1. Click "Pack Extension" in the top left
   2. For "Extension root directory", enter the path to the second extracted zip directory.
   3. For "Private key file", enter the path to the generated `.pem` private key file
   4. Click "Pack Extension". This will generate a second `.crx` file.
6. Install the first CRX file by dragging and dropping it onto the `chrome://extensions` page
7. Install the second CRX file by dragging and dropping it onthe the `chrome://extensions` page

If a new permission must be accepted, you'll be prompted to accept it after dropping the second CRX file onto the page.

:::Info Note
Note: Chrome no longer allows self-signed CRX extensions to run, but that's OK for this test case. We're still prompted to accept new permissions, even if we can't interact with the installed extension.

To validate this, you can create a third ZIP file with a rare permission like `geolocation` in the manifest, that's guarenteed to reprompt permissions when added.
:::

## Update Event

You can setup a callback that runs after your extension updates like so:

```ts
browser.runtime.onInstalled.addListener(({ reason }) => {
  if (reason === 'update') {
    // Do something
  }
});
```

If the logic is simple, write a unit test to cover this logic. If you feel the need to manually test this callback, you can either:

1. In dev mode, remove the `if` statement and reload the extension from `chrome://extensions`
2. Build two ZIPs with the same runtime ID and actually update the extension

The first approach is very straightforward. The second is more complicated...

Here are the steps:

So the steps:

1. Checkout an old commit.
2. [Add a `key`](https://developer.chrome.com/docs/extensions/reference/manifest/key#keep-consistent-id) to the `manifest` in your `wxt.config.ts`.
3. Run `wxt zip` to create the first ZIP.
4. Stash or reset changes and checkout your latest code.
5. Add back the same `key` to your manifest.
6. Make sure the extension's version is higher than the first zip. It can be any version that's higher, since you won't be releasing this version.
7. Run `wxt zip` to create the second ZIP.
8. In a fresh chrome profile, go to `chrome://extensions`, enable dev mode, and drag and drop the first zip onto the page to install it.
9. In the extension, play around and setup your test case.
10. Back on `chrome://extensions`, drag and drop your second zip onto the page.

If you setup the `key` correctly, it will cause the extension to act like it was updated instead of installing a second version of your extension.
