API: sendMessage
Relevant source files
The following files were used as context for generating this wiki page:The sendMessage function is the primary method for one-time, promise-based communication between different parts of a web extension. It provides a simple and consistent API for sending a message and receiving a response, abstracting the complexity of the underlying transport mechanisms like runtime.sendMessage, tabs.sendMessage, and postMessage. This API is designed to reduce boilerplate and simplify data synchronization across contexts such as the background script, content scripts, popups, and devtools pages.
sendMessage is part of an "endpoint runtime" created for each specific context. This runtime also provides the corresponding onMessage listener to handle incoming requests. The system is built to be robust, handling message queuing, delivery confirmation, and error propagation, ensuring that messages are reliably delivered or the sender is notified of failure.
Sources: README.md, src/internal/endpoint-runtime.ts:15-23
Core Architecture
The sendMessage API is not a standalone function but a product of the createEndpointRuntime factory. This factory is invoked within each specific context module (e.g., background.ts, content-script.ts, popup.ts) to establish a communication endpoint.
Sources: src/internal/endpoint-runtime.ts:35-39, src/popup.ts:5-8
Endpoint Runtime
The createEndpointRuntime function initializes and returns an EndpointRuntime object, which contains the sendMessage and onMessage methods. It manages the state for an endpoint, including:
- Open Transactions: A
Mapthat stores pending messages awaiting a reply. Each entry maps a uniquetransactionIdto theresolveandrejectfunctions of thePromisereturned bysendMessage. - Message Listeners: A
Mapthat storesonMessagecallbacks, keyed by amessageID. - Message Routing: A
routeMessagefunction passed during creation, which dictates how to forward messages to other contexts.
This diagram illustrates the creation and components of an EndpointRuntime.
Message Lifecycle
When sendMessage is called, it initiates a transaction-based message flow that ensures a response or error is returned to the caller.
- Initiation: The caller invokes
sendMessagewith amessageID,data, and an optionaldestination. - Payload Creation: An
InternalMessageobject is created with a uniquetransactionId. - Promise Management: A new
Promiseis created, and itsresolveandrejecthandlers are stored in theopenTransactionsmap against thetransactionId. - Routing: The
InternalMessageis passed to thehandleMessagefunction, which determines if the message is for the local context or needs to be routed elsewhere via therouteMessagefunction. - Reception & Handling: The destination endpoint's
handleMessagereceives the message. It looks up themessageIDin itsonMessageListenersmap and executes the corresponding callback. - Reply: The return value of the
onMessagecallback is wrapped in a newInternalMessageof typereplyand sent back to the original sender. - Transaction Completion: The original endpoint receives the
replymessage. It uses thetransactionIdto find the pending promise inopenTransactions, resolves it with the reply data, and deletes the transaction.
Sources: src/internal/endpoint-runtime.ts:167-188, src/internal/endpoint-runtime.ts:77-124
Sequence Diagram: Popup to Background
This diagram shows a typical message flow from a popup script to the background script.
Routing and Delivery
Message routing is primarily managed by the background script, which acts as a central hub. Other contexts establish a connection to the background script to send and receive messages.
Transport Mechanisms
-
Persistent Port (
persistent-port.ts): Contexts likepopup,options, anddevtoolsusecreatePersistentPortto establish a long-lived connection to the background script. This wrapper aroundruntime.connectautomatically reconnects if the background service worker is terminated and restarts, ensuring message delivery is resilient. It also queues undelivered messages.
Sources: src/internal/persistent-port.ts:14-20, src/popup.ts:5, src/options.ts:5, src/devtools.ts:6 -
Post Message (
post-message.ts): Communication between a content script and an isolatedwindowcontext (e.g., a script injected into the main page) useswindow.postMessage. Thewebext-bridgelibrary requires setting a unique namespace to avoid conflicts with other scripts using this global channel.
Sources: src/internal/post-message.ts:1-5, src/content-script.ts:10-12, src/window.ts:7-9
Background Script as a Hub
The background.ts script listens for connections from other contexts using runtime.onConnect. It maintains a connMap that maps a formatted endpoint string (e.g., content-script@123) to its port object and a unique fingerprint.
When a message arrives, the background script:
- Resolves the destination endpoint string.
- Looks up the destination in
connMap. - If a connection exists, it forwards the
InternalMessagethrough the corresponding port. - If no connection exists, it queues the message (if the sender is not the background script itself) and notifies the sender when the destination becomes available.
This diagram shows the central role of the background script in routing messages.
Usage and Parameters
The sendMessage function is strongly typed if a ProtocolMap is defined, allowing for type inference on the data payload and the return value.
Sources: src/types.ts:63-70
Function Signature
sendMessage<
ReturnType extends JsonValue,
K extends DataTypeKey = DataTypeKey,
>(
messageID: K,
data: GetDataType<K, JsonValue>,
destination?: Destination
): Promise<GetReturnType<K, ReturnType>>
Sources: src/internal/endpoint-runtime.ts:16-23
Parameters
| Parameter | Type | Description |
|---|---|---|
messageID | string | A unique string identifier for the message type. Used to route the message to the correct onMessage listener. |
data | JsonValue | The payload to send. The type can be inferred from the ProtocolMap. |
destination | Destination | The target context. Can be a RuntimeContext string (e.g., 'background'), an Endpoint object, or a formatted string like 'content-script@tabId'. Defaults to 'background'. |
Sources: src/internal/endpoint-runtime.ts:16-23, src/types.ts:54
Error Handling
The sendMessage promise will reject under several conditions:
- No Handler: If the destination endpoint has no
onMessagelistener registered for the givenmessageID, an error is thrown and propagated back to the sender.
Sources: src/internal/endpoint-runtime.ts:110-114 - Handler Error: If the
onMessagecallback itself throws an error, the error is serialized and sent back, causing the original promise to reject.
Sources: src/internal/endpoint-runtime.ts:117-120 - Recipient Termination: If the recipient's context is destroyed (e.g., tab is closed) before a reply is sent, the background script detects the disconnect. It notifies any pending senders that the session has ended, causing their promises to reject.
Sources: src/internal/persistent-port.ts:80-86, src/background.ts:269-281
The serialize-error library is used to ensure that Error objects can be correctly transmitted as JSON across different contexts.
Sources: src/internal/endpoint-runtime.ts:4, src/internal/endpoint-runtime.ts:118