<

Things I Learned from Developing Chrome Extensions (Manifest V3)

Do you know about Chrome extensions? Chrome extensions are features for customizing the Chrome browser.

I have created two Chrome extensions in the past (a few years ago), and at that time, I was following the Chrome extension specification, Manifest V2. And now, I have something I want to create with Chrome extensions again, so I decided to create it after a long time. However, it seems that the current Chrome extension specification recommends Manifest V3. So, I will summarize what I learned during development this time.

By the way, what I actually created is the following.

※ If you want to know more about Chrome extensions, please check out What are extensions? - Chrome Developers.

Chrome Extensions Components

Chrome extensions mainly consist of the following four components.

  • Background Scripts
    • They operate on service workers and respond to event-driven actions on the browser (such as page transitions and bookmark differences).
    • Set in the background field of the manifest.
  • Content Scripts
    • They operate in the context of a web page and can access the DOM.
    • Set in the content_scripts field of the manifest.
  • UI Elements
    • This is the UI that appears when you press the button on the right side of the URL bar (Action).
    • It is recommended to provide only the minimum necessary functions that do not impair the browser experience.
    • Set in the action field of the manifest.
  • Options Page
    • This is the UI that appears when you right-click the Chrome extension icon and select options.
    • It is used for the settings page if you want to customize the Chrome extension.
    • Set in the options_page field of the manifest.

In my opinion, the use of these components can be divided as follows:

  • When you need to access the DOM
    • Use Content Scripts
  • When there is a process that does not depend on the page
    • Use Background Scripts
  • When you need to set environment variables
    • Use Option Page

I thought that UI Elements are basically not necessary.

Debug

How do you debug?

The method is written here. From my interpretation, I think it is divided into the following two.


  • ① When the Chrome extension cannot be loaded in the first place

There are times when the Chrome extension cannot be loaded due to errors in the manifest.json file, etc. In such a case, perform the following steps.

  1. Access chrome://extensions
  2. Click the ERROR button as shown in the next figure
chrome extensions debug
chrome extensions debug

Probably, some error message is output. Let's solve it.


  • ② Cases other than ①

There are times when the Chrome extension can be loaded, but it does not work as expected. In such a case, open DevTools.

  • In the case of Background Scripts
    • Access chrome://extensions and click the link to the right of inspect views. (See the above figure)
      • DevTools will open.
  • In the case of Content Scripts, UI Elements, Options Page
    • Right-click on the UI and click Inspect
      • DevTools will open.

There should be a console tab in DevTools. Check the log messages there.

Message Passing

How should I communicate between each component? For example, when you want to pass data from Content Scripts to Background Scripts. The following document will be helpful.

Reading the document, it seems that you can communicate in the following patterns.

  • Communication between each component
    • Such as Background Scripts ⇔ Content Scripts
  • Communication between Chrome extensions
    • A Chrome extension ⇔ B Chrome extension
    • Communicate using the ID of the Chrome extension
  • Communication from web pages (Sending messages from web pages)
    • Web page ⇔ Component of Chrome extension

The specific code for communication uses the chrome.runtime.sendMessage method. When communicating from Background Scripts to Content Scripts, you need to find the id in advance with chrome.tabs.query to determine which Chrome tab to send to.

Also, as I will introduce later, when you Inject Javascript accessible with Web Accessible Resources to a web page (document.querySelector('body').append()), use window.postMessage and window.addEventListener for communication between that Javascript and Content Scripts. Because you can't use chrome.runtime.

Web Accessible Resources

You can access the DOM of a web page from Content Scripts, but you cannot access variables in the window object.

To access the window object, you can use Web Accessible Resources.


Let's explain in code.

The example of the necessary fields in manifest.json is as follows.

{
  "manifest_version": 3,
  "content_scripts": [
    {
      "js": ["content-script.js"],
      "matches": ["https://*/*"]
    }
  ],
  "web_accessible_resources": [
    {
      "resources": ["web_accessible_resources.js"],
      "matches": ["https://*/*"]
    }
  ]
}

The Javascript of Content Scripts and Web Accessible Resources is as follows.

// content-script.js
const injectScript = (filePath, tag) => {
  var node = document.getElementsByTagName(tag)[0];
  var script = document.createElement("script");
  script.setAttribute("type", "text/javascript");
  script.setAttribute("src", filePath);
  node.appendChild(script);
};
injectScript(chrome.runtime.getURL("web_accessible_resources.js"), "body");
// web_accessible_resources.js
console.log(window["hoge"]);
// To communicate with Content Scripts, use window.postMessage.

In this way, append web_accessible_resources.js to the body tag of the Web page. In that web_accessible_resources.js, you can access the window object.

chrome.webRequest API

There is an API for Chrome extensions that monitor network traffic in the Chrome browser. That is chrome.webRequest.

With this, you can see what requests are being made on the Web page. You need to set host_permissions in the fields of manifest.json.


As a sample, I will introduce the code of Background Scripts. First, write the necessary fields in manifest.json.

{
  "manifest_version": 3,
  "host_permissions": ["https://*/*"],
  "background": {
    "service_worker": "background.js"
  }
}

Next, write the code to monitor the event when a request from the Web page is completed (onCompleted).

// background.js
chrome.webRequest.onCompleted.addListener(
  async (details) => {
    console.log(`request url is ${details.url}`);
  },
  {
    urls: ["https://*/*"],
  },
  ["responseHeaders"] // responseHeadersをdetailsオブジェクトに含めることができます。
);

This details includes the URL of the request. If you want to know more, please check here.

Finally

Developing Chrome extensions after a long time, I struggled to catch up with the evolution. I hope this can be of help to those who are in the same situation as me.

If it was helpful, support me with a ☕!

Share

Related tags