Content scripts and Service workers in Chrome extension

web

Content scripts

Content scripts are scripts that run in the context of web pages and are mainly used for DOM operations. CSS and JS are inserted into pages that match the matches field of the content_scripts field in the Manifest. Insertion timing is before DOM construction for CSS and can be specified by run_at field for JS. Default is document_idle, so it is guaranteed to be inserted after window.onload.

$ cat manifest.json
{
  "manifest_version": 3,
  "name": "test-extension",
  "version": "1.0",
  "content_scripts": [
     {
       "matches": ["https://*.sambaiz.net/*"],
       "css": ["content.css"],
       "js": ["content.js"]
     }
   ]
}

$ cat content.css
time {
    color: #ba4dab !important;
}

$ cat content.js
document.querySelectorAll('time').forEach((t) => {
    t.innerText = `+++ ${t.innerText} +++`;
});

chrome.runtime.onMessage.addListener((message) => {
    console.log(`content_script: ${JSON.stringify(message)}`);
})

Content scripts run in an environment that is isolated from the page and other extensions by default, but the DOM is shared. You can call chrome.runtime.connect(), postMessage(), and chrome.runtime.sendMessage() API to communicate with others, but most other APIs cannot be called.

ブラウザのwindow間の値渡し - sambaiz-net

Service workers

Service workers are loaded when needed and handle various events such as chrome.runtime.onInstalled() and chrome.action.onClicked(). In Manifest V2, background_script played this role, but it was removed in V3. Besides, in V3, permissions of the request destination is split into host_permissions.

Chrome ExtensionsとChrome Apps - sambaiz-net

Service workers cannot handle the DOM of the page directly, so you need to insert scripts with chrome.scripting.executeScript() or send messages to Content scripts.

$ cat manifest.json 
{
  "manifest_version": 3,
  ...
  "background": {
    "service_worker": "service-worker.js"
  },
  "action": {
    "default_title": "Add @@@"
  },
  "permissions": [
    "scripting",
    "activeTab"
  ],
  "host_permissions": [
    "https://example.com/*"
  ]
}

$ cat service-worker.js
chrome.action.onClicked.addListener((tab) => {
    chrome.scripting.executeScript({
      target: {tabId: tab.id},
      func: (c) => {
        document.querySelectorAll('time').forEach((t) => {
            t.innerText = `${c} ${t.innerText} ${c}`;
        });
      },
      args: ['@@@'] 
    });
    
    chrome.tabs.query({active: true, currentWindow: true}, (tabs) => {
        chrome.tabs.sendMessage(tabs[0].id, {
            aaaa: tabs
        })
    });
});

By the way, if default_popup in action field is specified, onClicked is not called.