FluentRead Browser Extension Architecture: Content Script and Background Script Design
FluentRead uses a two-script browser extension architecture where the content script handles page-level UI and user interactions while the background script manages global state, context menus, and translation service routing.
FluentRead is an open-source translation browser extension built with the WXT framework, enabling modern TypeScript and Vue development while compiling to standard WebExtension files. The browser extension architecture centers on a clean separation between page-specific operations and extension-wide functionality, implemented through two primary scripts in the entrypoints/ directory.
Overview of the FluentRead Browser Extension Architecture
The architecture follows the standard WebExtension pattern with two distinct execution contexts:
| Script | Execution Context | Primary Responsibilities |
|---|---|---|
| Content Script | Runs in every web page | UI injection, gesture handling, translation requests |
| Background Script | Runs once per extension instance | Context menus, per-tab state, service routing |
Both scripts are defined using WXT helper functions that handle packaging and permissions. The content script uses defineContentScript() in entrypoints/content.ts, while the background script uses defineBackground() in entrypoints/background.ts.
Content Script: Page-Level UI and Interaction
The content script serves as the user-facing layer of the browser extension architecture, mounting components directly into web pages and handling all direct user interactions.
Initialization and Configuration
Upon injection, the script waits for user configuration to load from chrome.storage via the utility in entrypoints/utils/config.ts. If the extension is disabled globally, the script aborts immediately to avoid unnecessary overhead.
// entrypoints/content.ts
if (config.disableFloatingBall !== true) {
mountFloatingBall();
}
UI Component Mounting
The script mounts three primary Vue components:
- Floating Ball: A draggable widget for quick translation toggles
- Selection Translator: A panel that appears when text is selected
- Status Indicator: Optional component showing translation progress
These components are defined in entrypoints/utils/floatingBall.ts and entrypoints/utils/selectionTranslator.ts.
Event Handling and Gestures
The content script registers multiple input listeners defined in entrypoints/utils/constant.ts, including:
- Double-click and long-press gestures for word translation
- Multi-finger taps for paragraph translation
- Hotkey combinations for full-page translation via
setupFloatingBallHotkey()
A custom DOM event fluentread-toggle-translation decouples hotkey handling from the core translation logic, allowing the background script to trigger translations programmatically.
Message Handling from Background
The script listens for runtime messages from the background script to perform actions outside the scope of user gestures:
browser.runtime.onMessage.addListener((msg, sender, respond) => {
if (msg.type === 'contextMenuTranslate') {
if (msg.action === 'fullPage') autoTranslateEnglishPage();
else if (msg.action === 'restore') restoreOriginalContent();
respond({ status: 'success' });
return true;
}
});
This handler enables context menu actions to trigger full-page translation or restoration of original content.
Background Script: Global State and Service Routing
The background script operates as the central coordinator in the browser extension architecture, managing extension-wide state and interfacing with translation APIs.
Context Menu Creation
During initialization, the script creates a parent menu item "FluentRead" with two child items:
- 全文翻译 (Full Page Translation)
- 撤销翻译 (Restore Original)
These menus are dynamically enabled or disabled based on the per-tab translation state.
Per-Tab Translation State
The background script maintains a Map<number, boolean> tracking which tabs have active translations. This state determines whether the restore option should be available and prevents duplicate translation requests.
Translation Service Routing
When the content script or context menu initiates a translation, the background script routes the request to the appropriate service implementation:
_service[config.service](message)
.then(resp => resolve(resp))
.catch(error => reject(error));
The _service registry in entrypoints/service/_service.ts supports multiple providers including Microsoft and DeepL. The background script also handles special cases like input-box translation (inputBoxTranslation), which calls Microsoft's API directly to bypass CORS restrictions in Firefox.
Communicating with Content Scripts
The background script sends messages to specific tabs using browser.tabs.sendMessage():
browser.tabs.sendMessage(tab.id, {
type: 'contextMenuTranslate',
action: 'fullPage'
});
This pattern allows the background script to trigger page-specific actions without direct access to the DOM.
Communication Flow Between Scripts
The browser extension architecture employs two primary communication mechanisms:
-
Runtime Messaging: Standard
browser.runtime.sendMessage()andbrowser.tabs.sendMessage()for request/response patterns between background and content scripts. -
Custom DOM Events: The
fluentread-toggle-translationevent allows the background script to signal translation toggles without tightly coupling to the content script's internal implementation.
User Action → Background Script → Runtime Message → Content Script → Translation Service
↓ ↓ ↓ ↓
Context Menu State Check Message Handler UI Update
Key Files and Configuration
Supporting the core browser extension architecture are several utility modules:
entrypoints/utils/config.ts: Manages user preferences and storage synchronization.entrypoints/utils/constant.ts: Defines gesture constants likeTwoFingerandDoubleClick.entrypoints/utils/floatingBall.ts: Implements the draggable translation toggle widget.entrypoints/utils/selectionTranslator.ts: Handles the text selection translation interface.entrypoints/service/_service.ts: Registry for translation provider implementations.wxt.config.ts: Configures build permissions includingstorage,contextMenus, andoffscreen.
Summary
- FluentRead's browser extension architecture separates concerns between content scripts (page UI and interactions) and background scripts (global state and API routing).
- The WXT framework compiles TypeScript/Vue source files into standard WebExtension manifests.
- Content scripts in
entrypoints/content.tsmount Vue components, handle gestures, and listen for background messages. - Background scripts in
entrypoints/background.tsmanage context menus, track per-tab translation states, and route requests to translation services. - Communication uses standard
browser.runtimemessaging and custom DOM events for loose coupling.
Frequently Asked Questions
How does the content script communicate with the background script in FluentRead?
The content script uses browser.runtime.sendMessage() to send translation requests and configuration queries to the background script. Conversely, it listens for incoming messages via browser.runtime.onMessage.addListener() to handle context menu commands like full-page translation or cache clearing. This standard WebExtension messaging API ensures secure cross-context communication.
What is the role of the background script in FluentRead's browser extension architecture?
The background script acts as the central coordinator that persists beyond individual page lifecycles. It creates and manages context menu items, maintains a Map of per-tab translation states to track which pages are currently translated, and routes translation requests to the appropriate service provider (Microsoft, DeepL, etc.) based on user configuration stored in chrome.storage.
How does FluentRead handle user gestures like double-click or long-press translation?
The content script registers event listeners for various input gestures defined in entrypoints/utils/constant.ts, including DoubleClick, LongPress, and multi-finger taps. When a gesture matches the user's configured trigger, the content script invokes handleTranslation() to capture the selected text and send a translation request to the background script, which then returns the translated result for display in the selection translator panel.
Why does FluentRead use the WXT framework for its browser extension architecture?
FluentRead uses WXT to write modern TypeScript and Vue code while automatically compiling to the manifest and file structure required by WebExtension APIs. WXT provides helper functions like defineContentScript() and defineBackground() that handle packaging, permissions management, and cross-browser compatibility, allowing the developers to focus on the translation logic rather than extension boilerplate.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →