preface

Currently, most extensions in the Chrome store are based on the MV2 version.

MV3 has been supported in Chrome since Version 88 (Manifest Version 3). The default Version is MV3 when viewing the official documentation, and the Chrome Store will begin accepting MV3 extension submissions in January 2021.

This article is a cheat sheet for Chrome extension development migrating from MV2 to MV3.

Why migration is needed

Two reasons:

  • Security, privacy, and performance will be enhanced with extensions to MV3;
  • Like MV1, MV2 will be phased out.

As of now, no timeline has been given for the demise of MV2, but developers will be given at least a year to migrate once MV3 support becomes stable. The original text reads as follows:

While there is not an exact date for removing support for Manifest V2 extensions, developers can expect the migration period to last at least a year from when Manifest V3 lands in the stable channel.

MV3 is not completely stable yet, but we can understand the differences between the two versions and try to avoid using features no longer supported by MV3 during development to reduce the workload of future migrations.

Modify the item checklist

The following are some modifications during the migration to MV3. In the current development, the transformation of background to service worker and the use of webRequest to monitor and modify Web requests have relatively large changes, and some codes need to be adjusted to complete the migration. The rest is mostly configuration changes, with a few code changes.

The manifest version number

In the first step, set the version number in the configuration file to 3.

Manifest V3 "Manifest_version ": 3 // The version number is changed from 2 to 3Copy the code

From background Pages to Service workers

In MV3, Service Worker was used to replace the original background page, which also changed a lot during the upgrade.

For details on Service workers, see the Service Worker Introduction, which only discusses the changes that need to be made when the extension is upgraded to MV3.

First, the changes in manifest.json:

  • usebackground.service_workerReplace the originalbackground.pageorbackground.scripts
  • File path configuration changed from array to single string;
  • The service worker file must be placed in the root directory of the extension folder. If you still use the relative path in MV2, an error will be reported and the service worker cannot be registered successfully. Service workers must be registered at root level: they cannot be in a nested directory.
  • removebackground.persistentfield
  // Manifest V2
  "background": {
   	"scripts": ["js/pages/background.js"]
   },



  // Manifest V3
  "background": {
    // Required
    "service_worker": 'background.js'
  },

Copy the code

Second, adjust the code to make sure it works in the service worker:

The biggest difference between the two is that background scrips in MV2 is always running after being successfully registered, while the Service Worker in MV3 is stopped when not in use and restarted the next time it is needed. It is also important to note that the Service Worker does not have direct access to the DOM.

Replace global variables with caching

Because the Service Worker is not permanent, but repeats the “start-do-something-terminate” process in the browser session and is only available when needed, if you still use global variables to store some data, the program will be lost on restart.

In MV3, you can use the Storage API to store needed data in a cache and retrieve it when needed.

// MV2 let name = undefined; chrome.runtime.onMessage.addListener(({ type, name }) => { if (msg.type === "set-name") { name = msg.name; }}); chrome.browserAction.onClicked.addListener((tab) => { chrome.tabs.sendMessage(tab.id, { name }); }); // MV3 chrome.runtime.onMessage.addListener(({ type, name }) => { if (type === "set-name") { chrome.storage.local.set({ name }); }}); chrome.action.onClicked.addListener((tab) => { chrome.storage.local.get(["name"], ({ name }) => { chrome.tabs.sendMessage(tab.id, { name }); }); });Copy the code

Replace Timers with Alarms

SetTimeout and setInterval may fail in service workers because the scheduler cancelling the timer when the service worker is terminated.

You can replace timers with the Alarms API in the extension program to avoid timer failures.

// MV2 timers const TIMEOUT = 3 * 60 * 1000; // 3 minutes window.setTimeout(() => { chrome.action.setIcon({ path: getRandomIconPath(), }); }, TIMEOUT); // MV3 => Alarms chrom.alarms. Create ({delayInMinutes: 3.0}); chrome.alarms.onAlarm.addListener(() => { chrome.action.setIcon({ path: getRandomIconPath(), }); });Copy the code

Draw the canvas

If you have a canvas scene in the background page (for example, to display or cache resources), don’t forget that the service worker doesn’t have direct access to the DOM, but can use the OffscreenCanvas API instead.

Use new OffscreenCanvas(width, height) instead of document.createElement(‘canvas’)

// MV2
function buildCanvas(width, height) {
  const canvas = document.createElement("canvas");
  canvas.width = width;
  canvas.height = height;
  return canvas;
}



// MV3
function buildCanvas(width, height) {
  const canvas = new OffscreenCanvas(width, height);
  return canvas;
}
Copy the code

Modifying a Network Request

In MV2, we use chrome. WebRequest related apis to intercept and modify Web requests;

In MV3, need to use the new chrome. DeclarativeNetRequest instead.

The new API has the following differences:

  • It is up to Chrome itself to calculate and modify requests, rather than blocking and modifying javascript programs as in MV2.
  • You need to specify rules to modify the request. When the browser matches a request or operation that meets the rules, the browser will modify the request according to the rules defined in rules. The program cannot directly view the actual request content.

Taking changing headers in a Web request as an example,

In MV2 we might make some changes to headers in the listener event before the request is sent:

// MV2

chrome.webRequest.onBeforeSendHeaders.addListener(
details => {
  
  // some modify logic...
  
  return { requestHeaders: details.requestHeaders };
},
{ urls: ['<all_urls>'] },
['blocking', 'requestHeaders', 'extraHeaders']
);

Copy the code

In MV3, you first specify the rules file in manifest.json, declaring declarativeNetRequest permissions:

// MV3 [ { "name": "My extension", ... "declarative_net_request" : { "rule_resources" : [{ "id": "ruleset_1", "enabled": true, "path": "rules.json" }] }, "permissions": [" declarativeNetRequest declarativeNetRequestFeedback ", ", "/ / only when needed statement can]," host_permissions ": ["http://www.blogger.com/", "http://*.headers.com/" // If you want to redirect or modify headers, you need to declare the corresponding hosts; otherwise, you do not need to],...}]Copy the code

In the rules.json file, define the change rules. Example file:

[
  {
    "id": 1,
    "priority": 1,
    "action": {
      "type": "modifyHeaders",
      "requestHeaders": [
        { "header": "token", "operation": "set", "value": "token value" },
      ]
      "responseHeaders": [
        { "header": "h1", "operation": "remove" },
        { "header": "h2", "operation": "set", "value": "v2" },
        { "header": "h3", "operation": "append", "value": "v3" }
      ]
    },
    "condition": { "urlFilter": "headers.com/123", "resourceTypes": ["main_frame"] }
  },
]  
Copy the code

As you can see, a rule consists of the following four parts:

  • Id: indicates the unique ID of the rule
  • Priority: indicates the priority of the rule
  • Condition: Condition under which the rule is triggered
  • Action: Indicates the operation performed when the rule is matched

Action Specifies the type of action to modify the Web request:

  • Block: blocks the request
  • Redirect: Redirects requests
  • upgradeScheme
  • allow
  • allowAllRequests
  • ModifyHeaders: Modifies the request headers

Requests are processed based on the priority specified in rules, which is covered in the Matching Algorithm section.

Json static file rules that have been enabled can be updated using updateEnabledRulesetsAPI. However, the number of rules is limited. For details about limits, see the Global Static Rule Limit section.

Overall, the new API feels a little more complex in its form than MV2. Here are only some of the things I used in my development, and you can read the official documentation carefully when migrating, depending on the business scenario.

Host permissions

MV3 has split permission declarations. The host_permissions field is added to declare host access permissions separately, while other permissions are still declared under the permissions field.

// Manifest V2
"permissions": [
  "tabs",
  "bookmarks",
  "http://www.blogger.com/",
],



// Manifest V3
"permissions": [
  "tabs",
  "bookmarks"
],
"host_permissions": [
  "http://www.blogger.com/",
  "*://*/*"
],
Copy the code

Merge Action API

Browser_action and page_Action from MV2 are merged into a separate Action API in MV3, which involves modification of two scenarios:

Manifest. Json configuration changes

// manifest.json

// Manifest V2
{
  "browser_action": { … },
  "page_action": { … }
}


// Manifest V3
{
  "action": { … }
}

Copy the code

Changes to apis used in javascript:

/ / background. Js / / Manifest V2. Chrome browserAction. OnClicked. AddListener (TAB = > {... }); Chrome. PageAction. OnClicked. AddListener (TAB = > {... }); / / the Manifest V3 chrome. Action. OnClicked. AddListener (TAB = > {... });Copy the code

Content security policy

Content Security Policy (CSP) declaration for extenders changed from strings to objects.

// Manifest v2 "content_security_policy": "..." // Manifest v3 "content_security_policy": { "extension_pages": "..." , "sandbox": "..." }Copy the code

Other changes

  • MV3 no longer supports execution of JS code that is not in an extended package, including JS files obtained from a remote server and passed to the application at runtimeeval, so all code needs to be packaged in a package.
  • The rest is not commonly used here, so you can refer to the documentation. If there is a problem when upgrading, there will be a corresponding error prompt in the developer tool, and then modify it according to the documentation.

The resources

  • Chromium Blog
  • Migrating to Manifest V3
  • Manifest V3 migration checklist
  • Introduction of the Service Worker
  • Migrating from background pages to service workers
  • chrome.declarativeNetRequest API