Components Communication
In this Extension we will demonstrate how to communicate between different components of the Extension and the Session.
- We will use the
browser.runtime.sendMessage
method to send a message to the Popup html and the Background. - We will use the
browser.tabs.sendMessage
method to send a message to the Content script. - And the
browser.runtime.onMessage.addListener
method to listen to messages from any Extension component. - And the
browser.webfuseSession.broadcastMessage
method to broadcast a message to all participants in the Session. - And the
browser.webfuseSession.on.addListener
method to listen to messages from the Session and broadcast messages from the Extension.
Directorymessaging-example-extension
- manifest.json
- popup.html
- content.js
- background.js
manifest.json
Section titled “manifest.json”{ "manifest_version": 3, "name": "Messaging example", "version": "1.0", "action": { "default_popup": "popup.html" }, "content_scripts": [{ "js": ["content.js"] }], "background": { "service_worker": "background.js" }}
popup.html
Section titled “popup.html”<!DOCTYPE html><html lang="en"> <head> <meta charset="UTF-8"> <title>Messaging example popup</title> <style> :root { --border-width: 1px; } * { font-family: Arial, sans-serif; } html, body { height: calc(100% - 2 * var(--border-width)); } body { margin: 0; padding: 0; background: #eee; border: 1px solid black; border-radius: 6px; } .message-item, .ping-item, .weather-item { padding: 4px 8px; border-bottom: dotted 1px #ccc; } .ping-item, .weather-item { color: #aaa; font-style: italic; font-size: .8em; } .weather-item { color: purple; } .message-item:last-child, .ping-item:last-child, .weather-item:last-child { border-bottom: none; } #messages-container { height: calc(100% - 22px); } </style> </head> <body> <div id="messages-container"></div> <div class="buttons"> <button id="ping-content-script">Ping content script</button> <button id="forecast">What's the weather today?</button> <button id="ping-chat">Ping chat from popup</button> </div> <script> const chatMessageToPopupPrefix = '/popup ' const messageContainerEl = document.getElementById('messages-container'); // chat message handler const chatMessageHandler = messageData => { // handle onbly `chat_message` events if ( messageData.event_type === 'chat_message' && messageData.message && messageData.message.startsWith(chatMessageToPopupPrefix) ) { const messageEl = document.createElement('div'); const messagePrefixEl = document.createElement('u'); const messageTextEl = document.createElement('span'); messageEl.classList.add('message-item'); messagePrefixEl.innerText = 'Message from chat:' messageTextEl.innerText = ` ${messageData.message.slice(chatMessageToPopupPrefix.length - 1).trim()}`; messageEl.appendChild(messagePrefixEl); messageEl.appendChild(messageTextEl); messageContainerEl.appendChild(messageEl); browser.browserAction.openPopup(); } };
const pingMessageHandler = (messageData, sender) => { if (messageData.event_type === 'ping_popup') { const messageEl = document.createElement('div'); messageEl.classList.add('ping-item'); messageEl.innerText = `Ping sent to popup from ${sender?.name || 'unknown'} script on ${messageData.time}`; messageContainerEl.appendChild(messageEl); browser.browserAction.openPopup(); } };
const weatherMessageHandler = (messageData, sender) => { if (messageData.event_type === 'to_popup_forecast') { const messageEl = document.createElement('div'); messageEl.classList.add('weather-item'); messageEl.innerText = `🌈 Weather forcast from background script: ${messageData.forecast}`; messageContainerEl.appendChild(messageEl); browser.browserAction.openPopup(); } };
// add event listener to all webfuse session JS api events and distinct event types inside handler browser.webfuseSession.on.addListener(chatMessageHandler); // add event listener to all extension messages and distinct event types inside handler browser.runtime.onMessage.addListener(pingMessageHandler); browser.runtime.onMessage.addListener(weatherMessageHandler);
const pingContentButton = document.getElementById('ping-content-script'); pingContentButton.addEventListener('click', () => { browser.tabs.sendMessage(null, {event_type: 'ping_content', time: String(new Date())}); });
const forecastButton = document.getElementById('forecast'); forecastButton.addEventListener('click', async () => { try { const forecast = await browser.runtime.sendMessage({event_type: 'forecast'}); } catch (e) { console.warn('Error fetching forecast:', e); } });
const pingChatButton = document.getElementById('ping-chat'); pingChatButton.addEventListener('click', () => { browser.webfuseSession.sendChatMessage('Ping from extension popup'); // Alternative way to trigger JS API method // browser.webfuseSession.apiRequest({ // cmd: 'send_chat_message', // message: 'Ping from extension popup', // }); });
// broadcast messsage for all the participants document.addEventListener('DOMContentLoaded', ev => { browser.webfuseSession.broadcastMessage(`extension_popup_${ev.type}`); }); </script> </body></html>
content.js
Section titled “content.js”document.addEventListener('DOMContentLoaded', () => { const chatMessageToContentPrefix = '/content '; const customPanel = document.createElement('div'); customPanel.style.backgound = 'white'; customPanel.style.border = '1px solid #555'; document.body.prepend(customPanel);
const messageContainer = document.createElement('section'); customPanel.appendChild(messageContainer);
const pingContentHandler = (messageData, sender) => { if (messageData.event_type === 'ping_content') { const messageEl = document.createElement('div'); messageEl.style.width = 'auto'; messageEl.style.margin = 'auto'; messageEl.style.padding = '4px 8px'; messageEl.style.backgrounfColor = 'transparent'; messageEl.style.borderRadius = 'none'; messageEl.style.boxShadow = 'none'; messageEl.style.color = '#aaa'; messageEl.style.fontStyle = 'italic'; messageEl.style.fontSize = '.8em'; messageEl.innerText = `Ping sent to constent script from ${sender?.name || 'unknown'} on ${messageData.time}`; messageContainer.appendChild(messageEl); } };
const chatMessageHandler = messageData => { // handle onbly `chat_message` events if ( messageData.event_type === 'chat_message' && messageData.message && messageData.message.startsWith(chatMessageToContentPrefix) ) { const messageEl = document.createElement('section'); const messagePrefixEl = document.createElement('u'); const messageTextEl = document.createElement('span'); messageEl.classList.add('message-item'); messagePrefixEl.innerText = 'Message from chat:' messageTextEl.innerText = ` ${messageData.message.slice(chatMessageToContentPrefix.length - 1).trim()}`; messageEl.appendChild(messagePrefixEl); messageEl.appendChild(messageTextEl); messageContainer.appendChild(messageEl); } };
const weatherMessageHandler = (messageData, sender) => { if (messageData.event_type === 'to_content_forecast') { const messageEl = document.createElement('section'); messageEl.style.color = 'purple'; messageEl.style.fontSize = '.8em' messageEl.style.fontStyle = 'italic'; messageEl.innerText = `🌈 Weather forcast from background script: ${messageData.forecast}`; messageContainer.appendChild(messageEl); } };
browser.webfuseSession.on.addListener(chatMessageHandler); browser.runtime.onMessage.addListener(pingContentHandler);
const pingPopupButton = document.createElement('button'); pingPopupButton.innerText = 'Ping popup'; pingPopupButton.addEventListener('click', () => { browser.runtime.sendMessage({event_type: 'ping_popup', time: String(new Date())}); }); customPanel.appendChild(pingPopupButton);
const forecastButton = document.createElement('button'); forecastButton.innerText = "What's the weather today?"; forecastButton.addEventListener('click', () => { browser.runtime.sendMessage({event_type: 'forecast'}); }); customPanel.appendChild(forecastButton);
browser.runtime.onMessage.addListener(weatherMessageHandler);
const pingChatButton = document.createElement('button'); pingChatButton.innerText = 'Ping chat from content script'; pingChatButton.addEventListener('click', () => { browser.webfuseSession.apiRequest({ cmd: 'send_chat_message', message: 'Ping from extension content script', }); // Alternative way to trigger JS API method // browser.webfuseSession.sendChatMessage('Ping from extension content script'); }); customPanel.appendChild(pingChatButton);});
background.js
Section titled “background.js”const weatherCodes = { 0: "Clear sky", 1: "Mainly clear", 2: "Partly cloudy", 3: "Overcast", 45: "Fog", 48: "Depositing rime fog", 51: "Light drizzle", 53: "Moderate drizzle", 55: "Dense drizzle", 56: "Light freezing drizzle", 57: "Dense freezing drizzle", 61: "Slight rain", 63: "Moderate rain", 65: "Heavy rain", 66: "Light freezing rain", 67: "Heavy freezing rain", 71: "Slight snow fall", 73: "Moderate snow fall", 75: "Heavy snow fall", 77: "Snow grains", 80: "Slight rain showers", 81: "Moderate rain showers", 82: "Violent rain showers", 85: "Slight snow showers", 86: "Heavy snow showers", 95: "Thunderstorm", 96: "Thunderstorm with slight hail", 99: "Thunderstorm with heavy hail"};const chatMessageToBackgroundPrefix = '/background 'const chatMessageHandler = messageData => { // handle onbly `chat_message` events if ( messageData.event_type === 'chat_message' && messageData.message && messageData.message.startsWith(chatMessageToBackgroundPrefix) ) { const message = `### Message from chat: ${messageData.message.slice(chatMessageToBackgroundPrefix.length - 1).trim()}`; console.log(message); }};
const forecastUrl = 'https://api.open-meteo.com/v1/forecast?latitude=52.52&longitude=13.41¤t=weather_code&forecast_days=1';const getForecast = async (messageData, sender) => { if (messageData.event_type === 'forecast') { const res = await fetch(forecastUrl); const data = await res.json(); const forecast = weatherCodes[data.current.weather_code] || 'Atmosphere is rolling a dice, outcome unknown'; if (sender?.name === 'content') { browser.tabs.sendMessage(null, {event_type: 'to_content_forecast', forecast }) } else { browser.runtime.sendMessage({event_type: 'to_popup_forecast', forecast }); } }};
browser.webfuseSession.on.addListener(chatMessageHandler);browser.runtime.onMessage.addListener(getForecast);
// Listen to the broadcast message from extension popupbrowser.webfuseSession.on.addListener((message, sender) => { if (message === 'extension_popup_DOMContentLoaded') { console.log('### Broadcasted message from popup ###', message, sender); }});
Key Points
Section titled “Key Points”- Listening for Webfuse Session Events:
- The Extension components utilize the
browser.webfuseSession.on.addListener()
method to listen forchat_message
events from the Session - The Extension components utilize the
browser.runtime.onMessage.addListener()
method to listen forforecast
events from the Extension.
- The Extension components utilize the
browser.webfuseSession.on.addListener(chatMessageHandler);browser.runtime.onMessage.addListener(getForecast);
- Sending Messages to the Extension components:
- The Extension components utilize the
browser.tabs.sendMessage()
method to send a message to the content script - The Extension components utilize the
browser.runtime.sendMessage()
method to send a message to the popup html (and any other Extension component).
- The Extension components utilize the
browser.tabs.sendMessage(null, {event_type: 'to_content_forecast', forecast });browser.runtime.sendMessage({event_type: 'to_popup_forecast', forecast });
- Triggering Webfuse Session API methods:
- The Extension components utilize the
browser.webfuseSession.apiRequest()
orbrowser.webfuseSession.sendChatMessage()
method to trigger thesend_chat_message
method to send a message to the chat.
- The Extension components utilize the
browser.webfuseSession.apiRequest({ cmd: 'send_chat_message', message: 'Ping from extension content script',});// ORbrowser.webfuseSession.sendChatMessage('Ping from extension content script');
- Broadcasting messages to all participants:
- The Extension popup utilizes the
browser.webfuseSession.broadcastMessage()
method to broadcast a message to all participants in the Session.
- The Extension popup utilizes the
browser.webfuseSession.broadcastMessage(`extension_popup_${ev.type}`);
- Listening to broadcast messages:
- The Extension background script utilizes the
browser.webfuseSession.on.addListener()
method to listen for broadcast messages from the Extension Popup.
- The Extension background script utilizes the
browser.webfuseSession.on.addListener((message, sender) => { if (message === 'extension_popup_DOMContentLoaded') { console.log('### Broadcasted message from popup ###', message, sender); }});