Home
Relevant source files
The following files were used as context for generating this wiki page:
webext-bridge is a library designed to simplify messaging between the different parts of a web extension. It provides a consistent, promise-based API for communication across various isolated JavaScript contexts, such as the background script, content scripts, popups, and devtools pages. The core goal is to reduce the boilerplate and complexity typically associated with runtime.sendMessage, tabs.sendMessage, and runtime.Port management.
The library establishes a centralized message routing system through the background script, allowing any part of the extension to communicate with any other part using a simple destination identifier. It also supports continuous, two-way communication channels called Streams, and facilitates communication between content scripts and the host web page's window context.
Sources: README.md:16-19
Core Concepts
Runtime Contexts
A web extension is composed of several distinct JavaScript environments, or "contexts". webext-bridge supports messaging between all of them. Each context-specific module of the library must be imported directly.
| Context | Description | Module Entry Point |
|---|---|---|
background | The central service worker or script of the extension. | webext-bridge/background |
content-script | Scripts injected into web pages. | webext-bridge/content-script |
popup | The script running in the browser action's popup window. | webext-bridge/popup |
options | The script for the extension's options page. | webext-bridge/options |
devtools | The script for the developer tools panel. | webext-bridge/devtools |
window | A script running in the context of a web page, to communicate with a content script. | webext-bridge/window |
Sources: package.json:28-55, src/types.ts:3-9
Endpoints
Every messageable location within the extension is identified as an Endpoint. An endpoint is defined by its context, and for tab-specific contexts, a tab ID and an optional frame ID. Endpoints are represented as strings.
The format for an endpoint string is: context[@tabId[.frameId]]
- Contexts like
background,popup, andoptionsare singletons and are identified just by their name. - Contexts like
content-script,devtools, andwindoware specific to a tab and require a tab ID.
The library provides parseEndpoint and formatEndpoint utilities to work with these identifiers.
Sources: src/internal/endpoint.ts:3-18, src/types.ts:11-15
Architecture
The messaging architecture of webext-bridge is built around a central hub model, with the background script acting as the primary message router.
Background Script as a Central Hub
All other contexts establish a persistent connection (runtime.Port) to the background script upon initialization. The background script maintains a map of all active connections (connMap), associating each port with its unique endpoint identifier and a session fingerprint.
This diagram illustrates the hub-and-spoke architecture, with all contexts connecting to the central background script.
This centralized design means a script in the popup, for example, doesn't need to know how to directly contact a content script; it simply sends a message addressed to the content script's endpoint, and the background script routes it.
Sources: src/background.ts:20-23, src/background.ts:241-253
Message Routing and Delivery
When sendMessage is called from any context, it creates an InternalMessage object and passes it to its local endpointRuntime. This runtime forwards the message to the background script (unless it's a local message within the same context).
The background script's endpointRuntime then performs the following steps:
- Resolves the destination endpoint string (e.g., adding a
tabIdif missing). - Looks up the destination's connection in its
connMap. - If a connection is found, it forwards the message through the corresponding
runtime.Port. - If no connection is found (e.g., the target tab/script is not yet active), it can queue the message and deliver it once the port connects.
- It tracks messages that expect a reply and routes the reply back to the original sender.
The following sequence diagram shows a message being sent from a popup script to a content script in a specific tab.
Communication Channels
webext-bridge uses two primary underlying communication mechanisms.
runtime.Port
For communication between the background script and other extension contexts (popup, options, devtools, content-script), the library uses browser.runtime.connect to create a long-lived message channel. This is managed by the createPersistentPort utility. This avoids the overhead of establishing a new connection for every message.
Sources: src/popup.ts:6, src/content-script.ts:11, src/background.ts:241
window.postMessage
To bridge the gap between a content-script and a regular script running in the web page's window context (which is a common requirement for devtools and web-page-interactive extensions), the library uses window.postMessage.
- The
content-scriptmust callallowWindowMessaging(namespace)to enable this channel. - The
windowscript must callsetNamespace(namespace)with the same namespace string.
This namespacing is critical to prevent message conflicts with other extensions or scripts on the same page.
Key Features
Request-Response Messaging
The primary interaction model is request-response via sendMessage and onMessage.
sendMessage(messageId, data, destination): Sends a message and returns aPromisethat resolves with the response from the destination.onMessage(messageId, callback): Registers a listener for a given message ID. The callback receives the message and can return a value or aPromise, which will be sent back as the response.
This is managed by the EndpointRuntime, which tracks pending requests in an openTransactions map using a unique transactionId for each call.
Sources: src/internal/endpoint-runtime.ts:36-39, src/internal/endpoint-runtime.ts:101-131, src/internal/endpoint-runtime.ts:132-135
Streams
For continuous, bi-directional communication, webext-bridge provides Streams. A Stream is a namespaced, one-to-one communication channel between two endpoints.
onOpenStreamChannel(channel, callback): One endpoint listens for an incoming stream request on a specificchannelname.openStream(channel, destination): The other endpoint initiates the connection. This returns aPromisethat resolves to aStreaminstance.
Once open, both sides can use stream.send(data) to transmit data and stream.onMessage(callback) to receive it. The stream can be terminated by either side using stream.close().
Core Data Structures
The following interfaces from src/types.ts are fundamental to the library's operation.
| Interface | Fields | Description |
|---|---|---|
Endpoint | context, tabId, frameId? | Represents a messageable location within the extension. |
BridgeMessage | sender, id, data, timestamp | The public-facing message object received by onMessage callbacks. |
InternalMessage | origin, destination, transactionId, hops, messageID, messageType, data?, err?, timestamp | The internal, comprehensive message object used for routing and state management. |
StreamInfo | streamId, channel, endpoint | Contains metadata about an active Stream connection. |
Sources: src/types.ts:3-44