Communication between a Chrome extension and a web app

  • TypeScript

  • JavaScript

Communication between a Chrome extension and a web app

In developing Chrome extensions that interact with embedded web applications, establishing effective communication channels is crucial for seamless functionality. This article outlines techniques to facilitate robust event exchanges between a Chrome extension and a web app running within its iframe.

To facilitate clear communication, we start by defining the interface for the events used in our examples:

TypeScript

interface TSendMessageEvent {
payload?: Record<string, string>;
provider: string;
type: string;
}

Sending events from the web app to the extension

When sending events from the web app to the Chrome extension, the first step involves implementing a function to send messages:

TypeScript

const CHROME_EXTENSION_ID = 'abcdefghijklmnopqrstuvwxyzabcdef';
 
const sendMessageToExtension = async (event: TSendMessageEvent): Promise<void> => {
if (!window.chrome?.runtime) {
return;
}
 
try {
await window.chrome.runtime.sendMessage(CHROME_EXTENSION_ID, event);
} catch (error) {
console.warn('Failed to communicate with the extension:', error);
}
};
 
sendMessageToExtension({
payload: {
lorem: 'ipsum',
},
provider: 'my-web-app',
type: 'event-type-name',
});

Next, the Chrome extension's service worker listens for these messages and relays them to active tabs:

TypeScript

chrome.runtime.onMessageExternal.addListener((event: TSendMessageEvent) => {
if (event.provider !== 'my-web-app') {
return;
}
 
chrome.tabs.query({}, tabs => {
tabs.forEach(async tab => {
if (!tab.id) {
return;
}
 
try {
await chrome.tabs.sendMessage(tab.id, event);
} catch (error) {
console.warn(`Tab ${tab.id} is not reachable`);
}
});
});
});

In the content script of the extension, the event is processed based on its type and origin:

TypeScript

chrome.runtime.onMessage.addListener((event: TSendMessageEvent) => {
if (event.provider !== 'my-web-app' || event.type !== 'event-type-name') {
return;
}
 
// Handle the event.
});

Sending events from the Chrome extension to the web app

Conversely, events can be sent from the Chrome extension to the embedded web app using postMessage:

TypeScript

const sendMessageToWebApp = (event: TSendMessageEvent) => {
const elementMyAppFrame = window.document.querySelector<HTMLIFrameElement>('#my-app');
 
if (!elementMyAppFrame || !elementMyAppFrame.contentWindow) {
return;
}
 
elementMyAppFrame.contentWindow.postMessage(event, '*');
};
 
sendMessageToWebApp({
payload: {
lorem: 'ipsum',
},
provider: 'my-chrome-extension',
type: 'event-type-name',
});

In the web app, handle the incoming messages securely within the iframe:

TypeScript

const handleMessage = ({ data }: TSendMessageEvent) => {
const { payload, provider, type } = data;
 
if (provider !== 'my-chrome-extension' || type !== 'event-type-name') {
return;
}
 
// Handle the event.
};
 
window.addEventListener('load', () => {
window.addEventListener('message', handleMessage);
});
 
window.addEventListener('unload', () => {
window.removeEventListener('message', handleMessage);
});

By following these structured approaches, you can ensure robust communication between a Chrome extension and an embedded web app, enhancing user experience through seamless interaction and data exchange. This integration not only simplifies workflows but also expands the capabilities of both the extension and the web app in tandem.