Prerequisites and Setup
Loading Extensions into Chrome:
- Navigate to: chrome://extensions/
- Enable Developer mode
- Click "Load unpacked extension"
- Select folder containing manifest.json
WebStorm Configuration:
- Download: Chrome TypeScript definitions from DefinitelyTyped repository
- WebStorm Settings: Languages & Frameworks > JavaScript > Libraries
- Add library and attach the declaration file
Chrome Notification Fix:
- Visit: chrome://flags
- Search: "notifications"
- Set "Enable system notifications" to: Disabled
- Relaunch browser
Chrome Extension Development (Manifest V3)
Basic Extension Structure
manifest.json:
{
"manifest_version": 3,
"name": "Sample Extension",
"version": "1.0.0",
"description": "Example extension description",
"icons": {
"32": "icon32.png",
"48": "icon48.png",
"128": "icon128.png"
},
"action": {
"default_icon": {
"16": "icon16.png",
"32": "icon32.png"
},
"default_popup": "popup.html",
"default_title": "Sample Extension"
},
"content_scripts": [{
"matches": ["<all_urls>"],
"js": ["content.js"],
"run_at": "document_end"
}]
}
popup.html:
<html>
<head>
<meta charset="UTF-8">
<title>Sample Extension</title>
<script src="popup.js"></script>
<style>
.container {
width: 240px;
height: 320px;
text-align: center;
}
</style>
</head>
<body>
<div class="container">
<button id="actionBtn">Execute Action</button>
<p id="output"></p>
</div>
</body>
</html>
popup.js:
document.addEventListener('DOMContentLoaded', function() {
const actionBtn = document.getElementById('actionBtn');
const output = document.getElementById('output');
actionBtn.addEventListener('click', () => {
chrome.tabs.query({active: true, currentWindow: true}, tabs => {
chrome.tabs.sendMessage(tabs[0].id, {type: "fetchData"}, response => {
output.textContent = response?.data || "No response from content script";
});
});
});
});
content.js:
chrome.runtime.onMessage.addListener((request, sender, sendResponse) => {
if (request.type === "fetchData") {
sendResponse({
data: `Document title from content script: ${document.title}`
});
}
});
Core Extension Mechanisms
Manifest Configuration
Essential manifest.json fields include manifest_version, name, version, action, content_scripts, permissions, and host_permissions.
Message Communication
Simple Message Passing:
// Sending message from content script
chrome.runtime.sendMessage({command: "process"}, function(response) {
console.log(response.status);
});
// Receiving messages
chrome.runtime.onMessage.addListener((request, sender, sendResponse) => {
if (request.command === "process") {
sendResponse({status: "completed"});
}
});
Long-lived Connections:
// Establishing connection
const port = chrome.runtime.connect({name: "dataChannel"});
port.postMessage({data: "sample data"});
port.onMessage.addListener(msg => {
if (msg.query === "status") {
port.postMessage({response: "active"});
}
});
// Connection listener
chrome.runtime.onConnect.addListener(port => {
port.onMessage.addListener(msg => {
if (msg.data === "sample data") {
port.postMessage({query: "status"});
}
});
});
Publishing and Distribution
- Developer registration requires one-time $5 fee
- Package extension as ZIP file
- Upload via Chrome Web Store Developer Dashboard
Automatic Updates
manifest.json:
{
"update_url": "https://example.com/extension/updates.xml"
}
updates.xml:
<?xml version="1.0" encoding="UTF-8"?>
<gupdate xmlns="http://www.google.com/update2/response" protocol="2.0">
<app appid="extensionid">
<updatecheck codebase="https://example.com/extension/v2.crx"
version="2.0" prodversionmin="106.0.5249.119"/>
</app>
</gupdate>
Data Storage Options
chrome.storage API
manifest.json:
{
"permissions": ["storage"]
}
Usage:
// Storing data
chrome.storage.local.set({userPref: value}, () => {
console.log("Preference saved");
});
// Retrieving data
chrome.storage.local.get(["userPref"], result => {
console.log("Current preference: " + result.userPref);
});
Cookie Management
manifest.json:
{
"host_permissions": ["*://*.example.com/"],
"permissions": ["cookies"]
}
Usage:
chrome.cookies.get({url: "https://example.com", name: "session"}, cookie => {
console.log(cookie);
});
IndexedDB
const dbRequest = indexedDB.open("ExtensionDB", 1);
dbRequest.onsuccess = event => {
const database = event.target.result;
};
Cross-Origin Requests
manifest.json:
{
"host_permissions": [
"https://*.api.com/",
"http://*/"
]
}
Using fetch API:
fetch("https://api.example.com/data")
.then(response => response.json())
.then(data => console.log(data));
Extension Pages and Resources
// Opening extension page in new tab
chrome.tabs.create({url: chrome.runtime.getURL("dashboard.html")});
// Accessing extension resources
const resourceURL = chrome.runtime.getURL("images/icon.png");
Internationalization
manifest.json:
{
"default_locale": "en"
}
messages.json:
{
"extName": {
"message": "Sample Extension",
"description": "Extension name"
}
}
Usage in JavaScript:
const extensionName = chrome.i18n.getMessage("extName");
Background Scripts
manifest.json:
{
"background": {
"service_worker": "background.js"
}
}
background.js:
chrome.action.onClicked.addListener(tab => {
chrome.tabs.create({url: "https://example.com"});
});
Options Page
manifest.json:
{
"options_page": "options.html"
}
Content Security Policy
manifest.json:
{
"content_security_policy": {
"extension_pages": "script-src 'self'; object-src 'none'"
}
}
Other Browser Platforms
- Edge: Submit to Microsoft Edge Add-ons store
- Firefox: Submit to Mozilla Add-ons repository
Development Best Practices
- Follow principle of least privilege for permissions
- Use modern JavaScript modules in service workers
- Implement proper error handling
- Test across different browser versions