Reputation: 681
I have a WebExtension with the following structure:
webextension [directory]
- background.js
- page1.html
- page1.js
- page2.html
- page2.js
The background.js listens for errors. If any, it instructs its callback function to update the tab with an HTML page, page2.js that contains a button.
The HTML script page2.js starts by sending a message to the background.js and background.js replies to page2.js. This part works fine.
Then, as you see in the code, page2.html contains a button if clicked, it will execute code in the callback function. Then, it will call refreshIndexPage
which should send a message to page1.js which is attached to page1.html.
The Problem: When I added the messaging APIs between page2.js and page1.js in the refreshIndexPage
, the message from page2 gets sent to the background.js. I do not want this to happen. As I will show in the output, I get: In background.js: received: undefined
Questions:
Here is the output after adding messages from page2.js to page1.js. Console output
inside refreshIndexPage
In background.js: received: undefined
inside handleRefreshResponse
<html>
<head>
<meta charset="UTF-8">
</head>
<body>
<h1>Pag1.html</h1>
<input type="button" id="page1-button" value="click"></input>
<script src="page1.js"></script>
</body>
</html>
function displayAll() {
console.log("inside display all");
} //end displayAll
//should listen to messages from pag2.js
browser.runtime.onMessage.addListener(handleRefreshMessage);
function handleRefreshMessage(request, sender, sendResponse) {
console.log("In page1.js: message received" + request.refreshRequest);
sendResponse("response from page1.js to page2.js");
}
console.log("inside background");
//add a listener for the toolbar icon. If clicked, open page1.html
browser.browserAction.onClicked.addListener((tab) => {
// disable the active tab
var creating = browser.tabs.create({"url": "page1.html"});
creating.then((tab) => {
browser.browserAction.setIcon({tabId: tab.id, path: "icons/red-64.png"});
});//end creating.then
});//end addListener
var target = "<all_urls>";
function log(responseDetails) {
console.log("inside response details");
errorTab = responseDetails.tabId;
if(true) {
console.log("inside if");
browser.tabs.update(responseDetails.tabId,{url: "page2.html"});
//this message to wait request from page2.js
browser.runtime.onMessage.addListener(handleMessage);
} //end if
}//end log
function handleMessage(request, sender, sendResponse) {
console.log("In background.js: received: " + request.scriptRequest);
sendResponse({errorTab: errorTab});
}
var errorListening = browser.webRequest.onErrorOccurred.addListener(log, {
urls: [target],
types: ["main_frame"]
});
<html>
<head>
<meta charset="UTF-8">
</head>
<body>
<h1>Pag2.html</h1>
<input type="button" id="page2-button" value="click"></input>
<script src="page2.js"></script>
</body>
</html>
/*self-calling function conatins sendMessage to background script*/
(function notifyBackgroundPage() {
var sending = browser.runtime.sendMessage({
scriptRequest: "From pag2.js. request for data"
});
sending.then(handleResponse);
})();
function handleResponse(message) {
console.log(`In page2.js: data from background is: ${message.errorTab}`);
} //handleResponse
function myFunction() {
refreshIndexPage();//end .then
}//end myFunction
//refreshIndexPage should send message to page1.js only when the button is clicked.
function refreshIndexPage() {
console.log("inside refreshIndexPage");
var sending = browser.runtime.sendMessage({
refreshRequest: "From page2.js"
});
sending.then(handleRefreshResponse);
}//end refreshIndex
function handleRefreshResponse() {
console.log("inside handleRefreshResponse");
}//end handleRefreshResponse
var page2Button = document.getElementById("page2-button");
page2Button.addEventListener('click', myFunction);
Upvotes: 0
Views: 421
Reputation: 33326
Messages sent by runtime.sendMessage()
are received in all scripts in the background context which have a runtime.onMessage
listener registered, except the script from which the message was sent.1 There is no way for you to restrict the recipients of such messages.
Thus, you have to create some methodology to determine if the message is intended for each script which receives it. This can be done in a wide variety of ways, but all of them are based on either:
Both of these are provided as arguments to the runtime.onMessage()
listener.
message
To use the message
you have to choose to impose some structure on the message
. The message
can be any JSON-ifiable data you choose to send. Imposing some structure allows you to more easily use messages more complex and more reliably communicate information between your scripts. There is nothing that is predefined. You can use whatever you desire. However, being consistent in your choices usually makes it easier to program and almost always makes code easier to maintain.
Personally, I'm usually sending messages for different reasons. For each extension, I'll usually choose to always send an Object with a particular format. That format may be different for each extension, but it usually looks something like:
var message = {
type: 'requestFoo',
//subType: 'Used if the type needs to be further split',
data: dataNeededForRequest
//You can add whatever other properties you want here.
};
If the extension has multiple possible recipients, then either A) only one recipient would understand how to handle a requestFoo
type
message, and all others would ignore such message
types
, or if there were multiple background context scripts which could handle requestFoo
types, then I would add a recipient
or destination
property. Thus, the message
would look like:
var message = {
type: 'requestFoo',
//subType: 'Used if the type needs to be further split',
recipient: 'page2.js',
data: dataNeededForRequest
//You can add whatever other properties you want here.
};
When each script received the message
they would check both that the recipient
matched the script which had received the message and that the code understood how to handle the type
of message
.
Keep in mind that the structure above is just what I happen to use. You can define whatever structure you desire which also fulfills your needs.
sender
If a script is never to act on messages received from a specific sender(s), or if it is to only act on messages from specific senders, then that script can check the sender
runtime.MessageSender
Object to see if the parameters match ones from a sender for which it is to act on the message
. If you use this methodology, you will most commonly be checking the sender.url
.
Basing a choice to act on a message
based solely on the sender
is significantly more limited than being based on the contents of the message
. However, it can be quite useful when used in addition to information provided in the message
. It also provides a way to know the sender of the message which can not be spoofed. In addition, it means that you don't need to communicate information as to which scope was the sender of the message, unless, of course, you have more than one possible sender in the scope (i.e. in the URL/page).
1. A bug in Firefox in versions prior to 51 results in messages being received in the script which sent them. If you expect your extension to be used in that version or earlier you must account for that possibility (i.e. ignore them), as some situations can result in locking up Firefox (e.g. if you always send a new message when you receive a message).
Upvotes: 1