Jérémie O. Lumbroso, Ph.D
Revised in Spring 2025
What is a Chrome Extension?
A small software program that customizes the browsing experience.
Why build Chrome Extensions?
Our Goal
You Know
You Might Not Know
Don't worry! This technological stack is not overwhelming and there is enough time for you to familiarize yourself with it.
Create a new folder, e.g., hello-world-extension
.
Add manifest.json
, background.js
, contentScript.js
, popup.html
, popup.js
.
Load it in Chrome:
Go to chrome://extensions
Enable Developer Mode
Click Load unpacked
Select the folder
Test your extension!
hello-world-extension/
├── manifest.json
├── background.js
├── contentScript.js
├── popup.html
└── popup.js
A JSON file that has:
Name, Description, Version
Permissions (e.g., tabs, storage)
Scripts (background, content)
Browser Action or Page Action definition
Example:
{
"name": "Hello World Extension",
"version": "1.0",
"manifest_version": 3,
"description": "A simple Chrome extension example",
"permissions": ["storage", "activeTab"],
"action": {
"default_popup": "popup.html"
},
"background": {
"service_worker": "background.js"
},
"content_scripts": [
{
"matches": ["<all_urls>"],
"js": ["contentScript.js"]
}
]
}
Background script (or service worker in Manifest V3) runs separately from any web page.
Responsibilities:
Manages global state
Listens for events (e.g., extension installed, message from content script)
Communicates with content scripts via message passing
Example (background.js):
chrome.runtime.onInstalled.addListener(() => {
console.log("Extension installed!");
});
chrome.runtime.onMessage.addListener((request, sender, sendResponse) => {
if (request.greeting === "hello") {
sendResponse({ reply: "Hi from background!" });
}
});
Runs inside the web page context (DOM).
Can read and modify the webpage directly.
Interacts with the background script using chrome.runtime.sendMessage or chrome.runtime.onMessage.
Example (contentScript.js):
console.log("Content script loaded.");
// Send a message to background
chrome.runtime.sendMessage({ greeting: "hello" }, (response) => {
console.log("Received response:", response);
});
Popup: The small UI that appears when the user clicks your extension’s icon.
Typically an HTML file (popup.html
), with a corresponding JavaScript file (popup.js
).
Access to some Chrome APIs from the popup, but not the web page DOM.
Example (popup.html
):
<!DOCTYPE html>
<html>
<head>
<title>My Extension Popup</title>
</head>
<body>
<h1>Hello!</h1>
<button id="myButton">Click me</button>
<script src="popup.js"></script>
</body>
</html>
// popup.js
document.getElementById("myButton").addEventListener("click", () => {
chrome.tabs.query({ active: true, currentWindow: true }, function(tabs) {
// Send a message to the content script in the active tab
chrome.tabs.sendMessage(tabs[0].id, { greeting: "hello from popup" }, (response) => {
console.log("Response from content script:", response);
});
});
});
Official Docs: Chrome Extensions Developer Documentation
MDN: JavaScript Basics
Tutorials:
Google Codelab: Build a Chrome Extension (with Gemini)
Browser Action (Popup)
The small window shown when user clicks the extension icon
Badges
Little numbers or icons that appear on the extension icon
Context Menus
Right-click options that integrate with the page or highlighted text
Omnibox
Autocomplete or custom commands in the address bar (advanced usage)
Injected UI
Tooltips, overlays, or sidebars inserted into a page via content scripts
https://github.com/jlumbroso/chrome-extension-text-collector
Navigate to chrome://extensions
Click "Load unpacked"
Select your extension folder
Click the extension icon in the toolbar and see your popup
Open the Console (in DevTools) to see background script and content script logs
Chrome Extensions require explicit permissions in manifest.json
.
Common permissions:
tabs
(to access tab info)
storage
(to store data locally)
<all_urls>
(to run content scripts on all sites; be mindful of security!)
Extensions also follow Content Security Policy (CSP):
Blocks inline scripts by default
Encourages external JavaScript files
Least Privilege Principle
Request only the permissions absolutely needed
e.g., tabs
, storage
, activeTab
, contextMenus
, etc.
Content Scripts
Runs in a sandbox but can see/modify DOM
Communicate with background scripts via messaging
Protecting User Data
No direct DB unless you implement external APIs or use local storage
Manifest V3 Changes
Service worker replaces persistent background page
Different approach for network request interception (Declarative Net Request)
Definition: A browser security mechanism that governs when a web page (on Domain A) can make HTTP requests to a different domain (Domain B).
Why It Exists:
Prevent malicious sites from secretly fetching or submitting sensitive data to services in another domain without user’s knowledge.
Enforces the Same-Origin Policy by default; requests outside the same origin may be restricted unless explicitly allowed.
How It Manifests:
Error Example: “Access to fetch at 'https://api.other.com' from origin 'https://yourdomain.com' has been blocked by CORS policy.”
The server needs to include response headers like Access-Control-Allow-Origin: *
or the requesting site’s origin to grant permission.
In Chrome Extensions:
Manifest permissions ("host_permissions"
) or server-side CORS headers might be needed to allow cross-origin requests.
If the server doesn’t set the correct CORS headers, the browser blocks or restricts the request.
chrome.storage
API
localStorage
(per-extension origin)
window.localStorage
chrome-extension://<id>
)IndexedDB
Other Approaches
Key Tip:
chrome.storage
for straightforward settings or small data (especially if you want sync). Low Complexity
Simple To-Do list, Pomodoro Timer, Quick Note Taker
UPenn Intranet linker (quickly find classes, professors)
Medium Complexity
Real-time language translator for pages
Secure password generator with local storage
Weather or News aggregator leveraging external APIs
Higher Complexity
AI-based summarizer for articles (e.g., GPT-based)
Ad blocking / advanced network interception
Calendar scheduling with interactive overlays
Here are projects created in Spring 2024, in CIS 3500 ("Introduction to Software Engineering" / "How to build in a Team Joyfully")
Penn Course Search
Web Styler
Tamadoro
Chromagotchi
tabsOFF
These projects (& all projects in CIS 3500) are available to examine + the code is public
⚠️ CHROME EXTENSION:
BUTTON + POPUP
by Franci Branda-Chen, Eshaan Chichula, Matt Fu, Jake Murphy
Penn Course Search is a Chrome extension that allows students to search for courses in the Penn catalog from the comfort of their Chrome toolbar. Our Chrome extension eliminates the need for students to open extra tabs to gather information about a course. Everything they need is conveniently accessible through the pop-up interface.
by Judah Nouriyelian, Adam Gorka, Kevin Liu, Andy Li
WebStyler is a Chrome extension that allows users to change visual features (e.g., text size, color) on websites to suit their preferences. Giving users a way to interact with these visual elements more directly and easily, this extension can be used for accessibility as well as personalization.
⚠️ CHROME EXTENSION:
BUTTON + POPUP
by Amanda Lee, Sydney Simon, Ash Fujiyama, Gabby Gu
Tamadoro is a time management Chrome extension which enhances user productivity based on the pomodoro study method. Users are encouraged to develop good productivity habits by having to choose and take care of their Tamagotchi inspired pet. Each focus session rewards users with they can use to feed their pet to keep them happy and healthy.
⚠️ CHROME EXTENSION:
SIDEPAGE
by Clara Fee, Via Liu, John Otto, Laya Yalamanchili
Chromagotchi is a simple chrome extension for improved productivity and focus, based on the classic Tamagotchi game. Download the extension and customize your Chromagotchi avatar in the browser. Keep your tabs organized, and spend time on school, work, or other on-task websites, or else your Chromagotchi digital pet will die!
⚠️ CHROME EXTENSION:
LANDING PAGE
by Chenxi Leng, Brian Lu, Boya Zeng, Ani Petrosyan
TabsOFF is a Chrome Extension designed to enhance productivity and efficiency for web users by organizing and managing open browser tabs. It helps users reduce clutter, improve memory usage on their PCs, and navigate open tabs more effectively.
⚠️ CHROME EXTENSION:
BUTTON + POPUP
by Caroline Begg, Runzhi (Mars) Gu, Sam Bhatta
Summarizer: AI Research Assistant is an Google Chrome extension that acts as a research assistant for all sorts of users by allowing them to quickly and easily summarize text right in their browser. This is done by selecting the text, clicking on the Summarizer icon in the context menu, inputting your OpenAI API key into the API key bar in the popup, and then clicking "Summarize." Summarizer helps streamline research and increases productivity
Symptom: Extension tries to access tabs
, storage
, or other APIs but fails
Error Example: “Unchecked runtime.lastError: The ‘tabs’ permission is not specified in the manifest.”
Solution:
Add the correct permission in manifest.json
, e.g.
{
"permissions": ["tabs", "storage"],
...
}
Use least privilege: only request what you truly need
Symptom: “Refused to execute inline script because it violates the extension's Content Security Policy”
Why?: Manifest V3 enforces a strict Content Security Policy disallowing inline scripts
Solution:
Move any inline JS into a separate .js
file and reference it via a <script src="...">
Don’t use eval()
or other unsafe patterns
Symptom: Background code stopping unexpectedly after some idle time
Why?: MV3 uses a service worker instead of a persistent background page
Solution:
Adopt an event-driven design: respond to events (e.g., chrome.runtime.onMessage
)
Store long-lived data in chrome.storage
, not in global variables
Symptom: Messages never arrive, or “Port closed before response”
Why?: Incomplete or incorrect message passing setup
Solution:
In content_script.js
:
chrome.runtime.sendMessage({type: 'GREET'}, response => {
console.log('Background says:', response);
});
In service_worker.js
(background):
chrome.runtime.onMessage.addListener((msg, sender, sendResponse) => {
if (msg.type === 'GREET') {
sendResponse('Hello from background!');
}
// return true if asynchronous response
});
Symptom: Fetch or XMLHttpRequest fails with CORS or “Access denied” errors
Why?: Browser blocks requests to unlisted domains
Solution:
Add relevant domains in "host_permissions"
or "permissions"
in manifest.json
:
{
"host_permissions": [
"https://api.example.com/*"
]
}
Use fetch
with correct headers or fallback to a server proxy if needed
Symptom: Changes not showing up, or you’re seeing old code
Why?: Extension is cached if you don’t reload
Solution:
Go to chrome://extensions
Enable Developer Mode > Load Unpacked
Hit Reload each time you update the code
Symptom: Extension works in Chrome but not in Firefox/Edge
Why?: Some browser APIs differ or are unsupported
Solution:
Check MDN WebExtensions docs for cross-browser API availability
Polyfill missing features or conditionally check which APIs are available
For security and efficiency purposes, Chrome changed the architecture in 2019, from Manifest V2 (MV2) to the more recent Manifest V3 (MV3)
Google allowed MV2 extensions until 2025
A lot of advice on the Internet + a lot of training data in LLMs refers to the MV2 framework
It is important to confirm you are getting MV3 code and snippets
Brainstorm extension ideas that:
Solve a small but real problem
Are feasible in a short time
Start Simple:
Basic UI + minimal functionality
Focus on minimal feature set
Iterate:
Add complexity/features once the core works
Collaborate:
Ensure to commit to GitHub regularly
Have Fun:
Explore, learn, and be creative!
Official Docs & Tutorials
Building Browser Extensions (Book)
By Matt Frisbie (on Safari, Amazon, or PDF if accessible)
Hands-On Practice
Use the sample "Hello World" to get comfortable with manifest, popup, background script
Look at the "Snippet Collector" once you are comfortable
Then explore the sample projects
Let's now start finding good project ideas
Keep your scope achievable and have fun experimenting!
Can't wait to see your Chrome extensions!
lumbroso@seas.upenn.edu