Skip to content

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_version": 3,
"name": "Messaging example",
"version": "1.0",
"action": {
"default_popup": "popup.html"
},
"content_scripts": [{
"js": ["content.js"]
}],
"background": {
"service_worker": "background.js"
}
}
<!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>
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);
});
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&current=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 popup
browser.webfuseSession.on.addListener((message, sender) => {
if (message === 'extension_popup_DOMContentLoaded') {
console.log('### Broadcasted message from popup ###', message, sender);
}
});
  • Listening for Webfuse Session Events:
    • The Extension components utilize the browser.webfuseSession.on.addListener() method to listen for chat_message events from the Session
    • The Extension components utilize the browser.runtime.onMessage.addListener() method to listen for forecast events from the Extension.
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).
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() or browser.webfuseSession.sendChatMessage() method to trigger the send_chat_message method to send a message to the chat.
browser.webfuseSession.apiRequest({
cmd: 'send_chat_message',
message: 'Ping from extension content script',
});
// OR
browser.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.
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.
browser.webfuseSession.on.addListener((message, sender) => {
if (message === 'extension_popup_DOMContentLoaded') {
console.log('### Broadcasted message from popup ###', message, sender);
}
});