Background to content-script of preloading page message fails - google-chrome-extension

Something strange happens in the Chrome extension.
Content script:
console.log('content');
chrome.extension.onRequest.addListener(function(request, sender, sendResponse){
console.log('request received');
sendResponse();
});
chrome.extension.sendRequest( JSON.stringify({'msg': 'page_loaded'}) );
It is just listens for extension messaging and sends the message to the background when page loaded.
Background script:
console.log('bg');
chrome.extension.onRequest.addListener(function(request, sender, sendResponse){
sendResponse();
chrome.tabs.sendRequest(
sender.tab.id,
JSON.stringify({'msg': 'page_loaded_bg_receive'}),
function(){
console.log('sendRequest page_loaded_bg_receive callback');
});
});
It is listens for messages and sends message to the sender tab.
And it seems to be working, at least in most cases in the page log appears 'request received'.
While url entering Chrome sometimes loads typed address before user hits 'enter'. And that's a strange behavior: page loading, content-script runs, sends the message to the background, but when the background sends the message back — it fails with the background log message:
Port error: Could not establish connection. Receiving end does not exist. miscellaneous_bindings:184
chromeHidden.Port.dispatchOnDisconnect miscellaneous_bindings:184
Is this a Chrome bug? What to do to send message to the preloading tab?
This is the arfificial minimal sample to reproduce such behavior. I need to call 'chrome.tabs.sendRequest' after the handling the message and more than once, so calling the 'sendResponse' is not the solution.

Solution based on the article https://developers.google.com/chrome/whitepapers/pagevisibility. I run content-script code if document.webkitVisibilityState is not 'hidden' or 'prerender', elsewhere I listen for 'webkitvisibilitychange' and wait for document.webkitVisibilityState is not 'hidden' or 'prerender'. I think check for 'prerender' is enough, but when I open a new empty tab it loads page with document.webkitVisibilityState='hidden' and this page also did not receive background messages.
function isDocumentReady() {
return document.webkitVisibilityState != "hidden" && document.webkitVisibilityState != "prerender";
}
if (isDocumentReady())
main();
else {
function onVisibilityChange() {
if (!isDocumentReady())
return;
document.removeEventListener(
"webkitvisibilitychange",
onVisibilityChange,
false);
main();
}
document.addEventListener(
"webkitvisibilitychange",
onVisibilityChange,
false);
}

Related

sendMessage from backgrond page to another backgrond page chrome extension mv3

Migrating extension mv2 to mv3 version. There was a problem with sending messages from one background page to another.
There are two utils (background pages), in order to use the methods of the first in the second and the second in the first, do I need to send messages between these two background pages? or through the main service worker?
I send messages without a service-worker, directly from one utility to another.
chrome.runtime.sendMessage({ type: 'typeMessage' }, (res) => {
return res;
});
But in another utility the listener does not receive this message
chrome.runtime.onMessage.addListener(function (message, sender, sendResponse) {
const { type, payload } = message;
switch (type) {
case 'typeMessage':
// any code
return;
default:
return Promise.resolve(`Message received: ${type}`);
}
});
Please tell me what am I doing wrong?
Sending messages from jsx (foreground) to js (service-worker) works. But from js (background) to another js (background) no.

Communicating between two chrome extensions

I am trying to communicate between two chrome extensions, but unable to do so.
Any help would be great in resolving this issue.
1st extension sending msg in background.js:
chrome.browserAction.onClicked.addListener(
function(tab)
{
chrome.runtime.onConnect.addListener(function(port)
{
port.postMessage({status:"hello"});
});
2nd extension receiving msg in background.js:
var port = chrome.runtime.connect({name: "lkddmaimhocofkfhngkdhdicmldnfdpn"});
port.onMessage.addListener(function(message,sender)
{
alert('listened bg');
});
It seems you are confused with the sending part and receiving part.
Also, there are some differences between onConnect
which fires when a connection is made from either an extension process or a content script,
and onConnectExternal
which fires when a connection is made from another extension.
Take a look at Message External and you can use the following sample code to communicate between two extensions.
1st extension sending msg in background.js:
chrome.browserAction.onClicked.addListener(function() {
var port = chrome.runtime.connect("lkddmaimhocofkfhngkdhdicmldnfdpn");
port.postMessage(...);
});
2nd extension receiving msg in background.js:
chrome.runtime.onConnectExternal.addListener(function(port) {
port.onMessage.addListener(function(msg) {
// Handle your msg
});
});

Communication between injected iframe and content script -- message passing via background page

I am injecting an iframe in a tab. Now inside the the iframe, based on user's actions, I need to show some error/warning/success notifications. However I want to display these notifications in the tab not in iframe. So I need to communicate between iframe and content script. Now these notifications are dynamic based on user's actions so I thought of message passing between iframe and content script via background page.
So what i have done is send messages from iframe to background. Now both background page and content script listens to these messages but only background page is able to receive them. On receiving messages it reflects them back to sender tab. Now content script can receive these messages as they are sent from background page.
Now I tried the same using custom events but it didn't work.
But i would like to know any other method which is more efficient than what i am doing??
EDIT : Here's the relevant code
iframe.js:
$scope.hideFrame = function(){
sendMessageToBackground("hideFrame");
};
$scope.checkIfFormValid = function(){
if(!($scope.taskName === "" || $scope.group.selectedGroup === null )){
$scope.addTask();
}
else{
sendMessageToBackground("invalidForm");
}
};
function sendMessageToBackground(msg){
chrome.runtime.sendMessage({type: msg});
}
background.js:
chrome.runtime.onMessage.addListener(function(request, sender, sendResponse){
switch (request.type){
case "hideFrame":
chrome.tabs.sendMessage(sender.tab.id,{type:"hideFrame"});
break;
case "invalidForm":
chrome.tabs.sendMessage(sender.tab.id,{type:"invalidForm"});
break;
}
});
content.js:
chrome.runtime.onMessage.addListener(function(request, sender, sendResponse){
switch (request.type){
case "invalidForm":
var n = noty({
text : ' Please fill all details',
type : 'error',
layout : 'topRight',
timeout : 10000,
theme : 'defaultTheme'
});
break;
case "hideFrame":
$("#isolatedFrame").hide();
break;
}
});
Using window.parent.postMessage(not working):
iframe.js:
function sendMessageToContent(msg){
// chrome.runtime.sendMessage({type: msg});
window.parent.postMessage({ type: "fromFrame", message: msg }, "*");
}
content.js:
window.addEventListener("message", function(event) {
if (event.source != window)
return;
if (event.data.type && (event.data.type == "fromFrame")) {
console.log("Content script received: " + event.data.message);
}
}, false);
Also, when i add a breakpoint at window.parent.postMessage({ type: "fromFrame", message: msg }, "*"); and try to see window.parent object , inspected target is disconnected. I don't why this is happening??
Yes, there's a more effective way to achieve it - using tabs.sendMessage.
extensions cannot send messages to content scripts using this method. To send messages to content scripts, use tabs.sendMessage. (see runtime.sendMessage document )
Here's the code.
iframe.js:
chrome.runtime.sendMessage({greeting: "hello"}, function(response) {
console.log(response.farewell);
});
content.js:
chrome.runtime.onMessage.addListener(
function(request, sender, sendResponse) {
console.log(sender.tab ?
"from a content script:" + sender.tab.url :
"from the extension");
console.log('greeting:", request.greeting);
sendResponse({farewell: "goodbye"});
You can also refer to https://developer.chrome.com/extensions/messaging#simple for more detail.
EDIT
Since iframe.js is a content script, you should send the message with chrome.runtime.sendMessage and without using chrome.tabs API. Otherwise, chrome.tabs will be undefined.

chrome extension contentscript not receiving messages from background script long life connection

I'm having a frustrating problem with message handling in Chrome,
I have a long lived connection running across my background page and popup page, the content page is able to send messages but not receive.
I've tired a number of methods but no joy.
Has anyone else encountered this?
content.js
coms = chrome.extension.connect({name: "coms"});
coms.onMessage.addListener(function(data) {
console.log("com chatter");
});
popup.js
coms = chrome.extension.connect({name: "coms"});
chrome.extension.onConnect.addListener(function(port) {
console.assert(port.name == "coms");
port.onMessage.addListener(function(msg) {
console.log("com chatter");
});
});

chrome extension messaging from background to content script gives :Port error: Could not establish connection. Receiving end does not exist [duplicate]

For my Chrome extension, I am attempting to post selected text to a PHP webpage. A solved question on this website (Chrome Extension: how to capture selected text and send to a web service) has helped me a lot in achieving this, but I want a different way of posting the text.
Instead of XMLHttpRequest as mentioned there, I want to send a hidden JS form from the content script. This method allows me to view or change the text before importing it to the database.
The problem is to get the trigger from the background to the content script. I already have a message the other way, so using the function(response) is desired. However, outside the “sendMessage”, I can’t listen for the response.cmd. And inside the “sendMessage”, I can’t get the response.cmd to trigger a function. Is there a solution for this, other than sending an all new message from the background script?
The code I am referring to:
Background.js
chrome.extension.onMessage.addListener(function(request, sender, sendResponse) {
if(request.cmd == "createSelectionMenu") {
sendResponse({cmd: "saveText"}); //Do things
}
});
Content_script.js
chrome.extension.sendMessage({ cmd: "createSelectionMenu", data: selectedText },
function(response) {
if(response.cmd == "saveText") {
createForm();
}
}
});
What I do is following:
I keep track of my opened tabs
content script:
// connect to the background script
var port = chrome.extension.connect();
background script
// a tab requests connection to the background script
chrome.extension.onConnect.addListener(function(port) {
var tabId = port.sender.tab.id;
console.log('Received request from content script', port);
// add tab when opened
if (channelTabs.indexOf(tabId) == -1) {
channelTabs.push(tabId);
}
// remove when closed/directed to another url
port.onDisconnect.addListener(function() {
channelTabs.splice(channelTabs.indexOf(tabId), 1);
});
});
Now I can notify all my registered tabs (i.e. content scripts) from my background script when a certain action happened:
var notification = { foo: 'bar' };
for(var i = 0, len = channelTabs.length; i < len; i++) {
chrome.tabs.sendMessage(channelTabs[i], notification, function(responseMessage) {
// message coming back from content script
console.log(responseMessage);
});
}
And again, on the other side in the content script, you can add a listener on these messages:
chrome.extension.onMessage.addListener(function(request, sender, sendResponse) {
if (request.foo == 'bar') {
executeStuff();
// if a callback is given:
sendResponse && sendResponse('success');
}
});
It's a bit of a brainf*ck, because it's redundant at some places. But I like it best that way, because you can wrap it and make it a bit easier.
If you want to see how I am using this, see my repository on GitHub: chrome-extension-communicator.

Resources