Chrome Extension¶
Manifest V3 Chrome extension with a React + PatternFly 5 popup, options page, context menus, and content scripts.
Module Structure¶
packages/extension/
src/
background/
service-worker.ts # Main entry, message routing, lifecycle events
lifecycle-manager.ts # State engine with mutex (snooze/queue/watch/wake)
alarm-handler.ts # Chrome Alarms handler (snooze wake, sync, poll)
badge-manager.ts # Badge count + color thresholds
bridge-client.ts # WebSocket + HTTP sync to bridge
context-menu.ts # Right-click menu setup and handlers
meeting-mode.ts # Snooze all / restore all
notification-manager.ts # Chrome notifications for wakes and changes
tab-tracker.ts # In-memory active tab tracking
watch-poller.ts # Content-script-based element polling
content/
element-selector.ts # Click-to-select overlay for watch targets
extract-element.ts # Injected to extract watched element content
popup/
App.tsx # Main popup with tabbed views
components/
Header.tsx # Tab count summary
ActiveTabList.tsx # Active tabs grouped by window
SnoozedList.tsx # Snoozed tabs with wake controls
QueueList.tsx # Ordered queue with drag reorder
WatchingList.tsx # Watched tabs with change status
TabActions.tsx # Snooze/Queue/Watch action buttons
SnoozeDialog.tsx # Time picker for snooze
ErrorBoundary.tsx # React error boundary
options/
App.tsx # Settings form
types.ts # Shared types and utilities
Service Worker Lifecycle¶
The service worker registers on chrome.runtime.onInstalled and chrome.runtime.onStartup:
- Sets up recurring alarms (
tabSyncevery 1 min,watchPollevery 5 min) - Creates context menus
- Updates badge count
- Connects WebSocket to bridge (if available)
- Checks for overdue snoozes (in case alarms were missed)
Message Handlers¶
The service worker handles 11 message types from the popup and content scripts:
| Message | Source | Action |
|---|---|---|
GET_TABS |
Popup | Returns tracked active tabs |
GET_CONFIG |
Popup | Returns extension config |
GET_LIFECYCLE |
Popup | Returns lifecycle tabs by state |
MEETING_MODE |
Popup | Activates meeting mode |
END_MEETING |
Popup | Deactivates meeting mode |
QUEUE_NEXT |
Popup | Pulls and opens next queued tab |
WAKE_TAB |
Popup | Wakes a specific lifecycle tab |
REMOVE_TAB |
Popup | Removes a lifecycle tab permanently |
REORDER_QUEUE |
Popup | Updates queue positions |
CLEAR_ATTENTION |
Popup | Clears attention indicators |
WATCH_ELEMENT_SELECTED |
Content script | Creates a watching lifecycle tab |
SNOOZE_FROM_POPUP |
Popup | Snoozes an active tab |
QUEUE_FROM_POPUP |
Popup | Queues an active tab |
All handlers have .catch() error handling and return fallback responses on failure.
Watch Mechanism¶
Element Selection¶
- User right-clicks > "Watch tab for changes..."
- Extension injects
element-selector.tscontent script - User hovers (highlight overlay) and clicks the target element
- Content script generates a CSS selector using
CSS.escape()for safety - Sends
WATCH_ELEMENT_SELECTEDto service worker (which usessender.tab.url, not the message URL, to prevent spoofing)
Polling¶
Every 5 minutes (via Chrome Alarm), up to 3 tabs are checked per cycle:
- Opens a background tab with the watched URL
- Waits for page load (10s timeout)
- Injects
extract-element.tswhich queries the CSS selector - Extracts
textContent(not innerHTML, for security) - Hashes the content with SHA-256
- Compares against stored hash
- If changed: stores new hash, sets
changedAt, sends notification - Closes the background tab
After 3 consecutive extraction failures, the tab is marked as changed (to surface the problem).
Security¶
- URL validation:
isAllowedUrl()enforced at allchrome.tabs.createcallsites and before storing URLs - Bridge lockdown:
isLocalhostUrl()enforced ingetBridgeUrl(), returns null for non-localhost - Content script trust:
WATCH_ELEMENT_SELECTEDusessender.tab.urlnotmessage.url - CSS injection prevention:
CSS.escape()on all selector components - Attention cap: arrays capped at 50 entries to prevent unbounded storage growth
- Host permissions: restricted to
http://*/*andhttps://*/*(not<all_urls>)
Tests¶
21 tests covering lifecycle-manager operations: