Introduction
This article is a summary for those who can read and write basic JavaScript to quickly understand how Chrome extensions work.
Chrome extensions are not as difficult as they seem. The contents are HTML, CSS, and JavaScript, and no special languages are used. However, there are unique rules that differ slightly from regular web applications, and if you start writing without knowing them, you will likely get stuck without understanding why your code does not work.
This article briefly summarizes only the concepts you should grasp first, using actual extension code as examples. Once you understand what is written here, you should be able to build extensions step by step while referring to the official documentation and sample code.
Basic Design Philosophy
The basic design philosophy is that multiple components run in their own isolated environments and communicate via messages only when necessary to perform processes.
Message Passing
To reference variables or call methods between files, the message passing API is used.
(Direct referencing or calling is not possible.)
Available APIs Depend on the Component (File)
For example, Content Scripts are mostly limited to front-end APIs, so advanced features must be requested indirectly through the Service Worker. On the other hand, the Service Worker can use many APIs responsible for background processing, such as server communication.
Main Components
(1) UI Pages (popup.html / popup.css / popup.js)
There are no special notes regarding this.
(2) Content Scripts
Handles the front-end, such as DOM manipulation.
// content.js
// Message passing
chrome.runtime.onMessage.addListener((message, sender, sendResponse) => {
if (message.action !== 'extractMetadata') return
const title = document.title
// Get DOM values using standard browser APIs
const published_at = document
.querySelector('meta[property="article:published_time"]')
?.getAttribute('content')
?.slice(0, 10)
sendResponse({
title: title,
published_at: published_at
})
})
(3) Service Worker
- Handles the back-end
- Event-driven
- Cannot access the DOM directly
📖 Basics of Extension Service Workers
📖 Service Worker event handling
- Send a message in response to a tab selection event
// popup.js
async function lookupBuzz(url) {
try { // Message passing
const response = await chrome.runtime.sendMessage({
action: 'lookupBuzz',
url: url
})
if (!response) {
throw new Error('Cannot retrieve buzz')
}
return response
} catch (error) {
throw new Error(`chrome api error: ${error.message}`)
}
}
- Receive the message in the Service Worker and call the function specified by the action
// background.js
chrome.runtime.onMessage.addListener((message, sender, sendResponse) => {
// Based on the action
if (message.action === 'lookupBuzz') {
fetchBuzz(message.url)
.then((response) => {
sendResponse(response)
})
.catch((error) => {
sendResponse({ error: error.message })
})
return true
}
})
// Advanced APIs can be called in the Service Worker
async function fetchBuzz(url) {
const response = await fetch(....)
📖
chrome.runtime.onMessagereference
📖 Whyreturn trueis required for asynchronous responses (Message passing)
About activeTab
A mechanism that grants temporary access rights triggered by explicit user actions.
- Access rights to the currently open tab are granted the moment the extension is invoked.
- The rights are lost the moment the tab is switched (specifically, navigating to another domain) or closed.
While the rights are active, it is possible to inject scripts, retrieve tab information (URL, title, etc.), and listen to network requests.
// Example of getting property values of the current tab
const tabId = activeTab.id
// Example of processing by getting the tabId when switching tabs
chrome.tabs.onActivated.addListener(async (activeInfo) => {
const tab = await chrome.tabs.get(activeInfo.tabId)
await updateBuzzIcon(tab)
})
manifest.json
The configuration file for the extension.
{
"name": "fjord-buzz-extension",
"version": "1.0",
"manifest_version": 3, // Current version
"background": {
"service_worker": "background.js",
"type": "module" // Required to import config.js
},
"description": "Google extension for fjord buzz page",
"content_scripts": [
{
"matches": [
"http://*/*",
"https://*/*"
],
"js": [
"content.js"
],
// Inject script after page load is complete
"run_at": "document_idle"
}
],
"permissions": [
"cookies",
// Permission to get information (like url and title) from the source currently displayed in the tab
"tabs"
],
"host_permissions": [
"http://localhost:3000/*"
],
// Extension icon
"action": {
"default_icon": {
"16": "icons/buzz_icon16.png"
},
// Text shown when hovering over the icon
"default_title": "fjord-buzz-extension",
// Specify the file to open when the icon is clicked
"default_popup": "popup/popup.html"
}
}
📖
content_scriptsmanifest key details
📖chrome.cookiesAPI reference
📖chrome.actionAPI reference
Illustration by Shahid Mehmood on Unsplash

No comments yet.