Passing message from background.js to popup.js - google-chrome

I'm trying to implement my own chrome extension on which, on a certain event, create a browser notification and fills the popup with data calculated in background.js
Here is my manifest.json file:
{
"name": "Dummy name",
"description": "Description",
"manifest_version": 2,
"version": "1.1.3",
"icons": {
"16": "icon_16.png",
"48": "icon_48.png",
"128": "icon_128.png",
"256": "icon_256.png"
},
"browser_action": {
"default_icon": "icon_48.png",
"default_title": "Test",
"default_popup": "popup.html"
},
"permissions": ["background","webRequest","webRequestBlocking","webNavigation","tabs","notifications"],
"background": {
"scripts":["jquery-1.8.1.min.js","classy.js","background.js"]
}
}
My call to sendMessage in background.js
show : function(result) {
var that = this;
chrome.extension.sendMessage({greeting: "hello"}, function(response) {
console.log(response);
});
if(window.webkitNotifications) {
var notification = webkitNotifications.createHTMLNotification('notification.html');
notification.show();
setTimeout(function(){
notification.cancel();
}, '7000');
}
}
My message listener in popup.js (from chrome extension samples)
chrome.extension.onMessage.addListener(
function(request, sender, sendResponse) {
console.log(sender.tab ?
"from a content script:" + sender.tab.url :
"from the extension");
if (request.greeting == "hello")
sendResponse({farewell: "goodbye"});
});
The only error I get is a
Port error: Could not establish connection. Receiving end does not
exist.
Thank you for your help!

Popup doesn't have tab id so you will get the error.
You can use chrome.runtime.sendMessage and chrome.runtime.onMessage.addListener in that case.
So in background.js
chrome.runtime.sendMessage({
msg: "something_completed",
data: {
subject: "Loading",
content: "Just completed!"
}
});
And in popup.js
chrome.runtime.onMessage.addListener(
function(request, sender, sendResponse) {
if (request.msg === "something_completed") {
// To do something
console.log(request.data.subject)
console.log(request.data.content)
}
}
);
I hope it would be helpful to you.

To solve this you need to first send a handshake message to background.js and then send the actual data from background.js to popup.js
For Example: In my case what i did was
popup.js
chrome.runtime.sendMessage({data:"Handshake"},function(response){
});
chrome.runtime.onMessage.addListener(function(message,sender,sendResponse){
str = JSON.stringify(message.data);
});
background.js
chrome.runtime.onMessage.addListener(function(message,sender,sendResponse){
//alert(message.data);
chrome.runtime.sendMessage({data:datax},function(response){
});
});
What iam trying to do is that as soon as we click on icon the handshake message is sent to the background.js and when it recieves it we can then send the variable or any data whick we wanted to send on popup.js to render it on popup.html.

These are the two simplest ways I've found to send data from background.js to popup.js:
1) Using storage
Save values into storage and once popup gets opened, it gets the values from storage and displays them in the popup.
background.js
chrome.storage.sync.set({ 'dataValue1': 'Some data 1.' });
chrome.storage.sync.set({ 'dataValue2': 'Some data 2.' });
popup.js
function updatePopup() {
chrome.storage.sync.get(['dataValue1', 'dataValue2'], function (data) {
document.getElementById("popupElement1").innerText = data.dataValue1;
document.getElementById("popupElement2").innerText = data.dataValue2;
});
}
document.addEventListener('DOMContentLoaded', updatePopup);
popup.html
<html>
<head>
<script src="popup.js"></script>
</head>
<body>
<p id="popupElement1"></p>
<p id="popupElement2"></p>
</body>
</html>
manifest.json
{
"name": "Background2popup",
"version": "1.0",
"manifest_version": 2,
"description": "This is a demo",
"browser_action": {
"default_popup": "popup.html"
},
"background": {
"scripts": [
"background.js"
]
},
"permissions": [
"<all_urls>",
"storage",
"tabs"
]
}
2) Using chrome.runtime.sendMessage()
Once popup opens, you send a message from popup to background to establish the connection/handshake (otherwise, you would get a 'Unchecked runtime.lastError: Could not establish connection. Receiving end does not exist.' if you try to send a message from background to popup and popup isn't open). Once with the connection established, you use sendResponse from background to send the data you wanted to send to popup in the first place.
background.js
chrome.runtime.onMessage.addListener(function (request, sender, sendResponse) {
if (request.method == "getStatus") {
console.log(request.data)
sendResponse({ method: "peepee", data: "poopoo" })
}
});
popup.js
chrome.runtime.sendMessage({ method: "getStatus", data: "xxx" }, function (res) {
document.getElementById("popupElement1").innerText = res.method;
document.getElementById("popupElement2").innerText = res.data;
return true;
});
popup.html & manifest.json are the same as in the first example

localStorage solution
Because the popup does not have a persistent state, you may want to use localStorage to store the popup state and preload it when popup opens and the storage event to keep track of changes to the state while the popup is open.
Background:
localStorage.setItem('popupData', JSON.stringify({ tabReady: true }));
Popup:
// Load the state from localStorage when popup opens
let popupData = JSON.parse(localStorage.getItem('popupData'));
// Keep track of changes to the popup state while the popup is open
window.addEventListener('storage', (e) => {
if (e.key === 'popupData') {
popupData = JSON.parse(e.newValue);
console.log(popupData.tabReady);
}
});

Use runtime.sendMessage to send messages to background script, and tabs.sendMessage from background to content script.
Please note that you need to specify tab id:
chrome.tabs.query({ active: true }, (tabs) => {
chrome.tabs.sendMessage(tabs[0].id, { greeting: 'hello' }, (response) => {
console.log(response);
});
});
You can find full example and documentation here: https://developer.chrome.com/extensions/messaging#simple

Related

Cannot send message from background.js to content.js with manifest version 3

I am trying to learn how to make Chrome Extensions with the new manifest version 3. My goal is to send a message from background.js to content.js.
I am using the following example that I found online: link to github.
manifest.json
{
"manifest_version": 3,
"version": "1.0",
"name": "Manifest 3 first version",
"content_scripts": [{ "matches": ["<all_urls>"], "js": ["content.js"] }],
"background": {
"service_worker": "background.js"
},
"action": { "default_icon": "icon.png" }
}
content.js
chrome.runtime.onMessage.addListener(function (request, sender, sendResponse) {
console.log(
sender.tab
? "from a content script:" + sender.tab.url
: "from the extension"
);
if (request.greeting === "hello") sendResponse({ farewell: "goodbye" });
});
background.js
try {
// This is the background script for the extension
// A listener for when the user clicks on the extension button
// chrome.action.onClicked.addListener(buttonClicked);
chrome.action.onClicked.addListener(buttonClicked);
// Handle that click
function buttonClicked(tab) {
// Send a message to the active tab
console.log("button clicked!");
// Send a message to the tab that is open when button was clicked
console.log("sending message");
chrome.tabs.sendMessage(tab.id, { message: "browser action" });
}
// Listening for messages
chrome.runtime.onMessage.addListener(receiver);
function receiver(request, sender, sendResponse) {
if (request.message === "thank you") {
// Not doing anything for messages received but I could!
}
}
} catch (err) {
console.log(err);
}
Basically no matter what I do I get the following error in the DevTools:
Uncaught (in promise) Error: Could not establish connection. Receiving end does not exist.
Would you be able to suggest a reason of such behavior and how I could solve it please?

Chrome Extension cause "Uncaught (in promise) Error: Cannot access a chrome:// URL"

I am following how to make chrome extension getting started that is posted in chrome official site https://developer.chrome.com/docs/extensions/mv3/getstarted/
And I copied and pasted all code and run on the same way. But in my case, When I run chrome.scripting.executeScript, It causes "Uncaught (in promise) Error: Cannot access a chrome:// URL" error.
I don't know what is problem. Here is my code that had been copied from the link above.
manifest.json
{
"name": "Getting Started Example",
"description": "Build an Extension!",
"version": "1.0",
"manifest_version": 3,
"background": {
"service_worker": "background.js"
},
"permissions": ["storage", "activeTab", "scripting"],
"action": {
"default_popup": "popup.html",
"default_icon": {
"16": "/images/get_started16.png",
"32": "/images/get_started32.png",
"48": "/images/get_started48.png",
"128": "/images/get_started128.png"
}
},
"icons": {
"16": "/images/get_started16.png",
"32": "/images/get_started32.png",
"48": "/images/get_started48.png",
"128": "/images/get_started128.png"
}
}
background.js
let color = '#3aa757';
chrome.runtime.onInstalled.addListener(() => {
chrome.storage.sync.set({ color });
console.log(`default color: ${color}`);
});
popoup.js
// Initialize button with user's preferred color
let changeColor = document.getElementById('changeColor');
chrome.storage.sync.get('color', ({ color }) => {
changeColor.style.backgroundColor = color;
});
// When the button is clicked, inject setPageBackgroundColor into current page
changeColor.addEventListener('click', async () => {
console.log('clicked');
console.log(chrome.tabs);
let [tab] = await chrome.tabs.query({ active: true, currentWindow: true });
console.log(tab);
chrome.scripting.executeScript({
target: { tabId: tab.id },
function: setPageBackgroundColor,
});
});
// The body of this function will be executed as a content script inside the
// current page
function setPageBackgroundColor() {
chrome.storage.sync.get('color', ({ color }) => {
document.body.style.backgroundColor = color;
});
}
popup.html
<!DOCTYPE html>
<html>
<head>
<link rel="stylesheet" href="button.css" />
</head>
<body>
<button id="changeColor"></button>
<script src="popup.js"></script>
</body>
</html>
Do you have an idea??
When you try to trigger the event make sure that you are not in chrome://extensions/ or something similar.
You can check the URL and avoid inject the script for "chrome://":
// skip urls like "chrome://" to avoid extension error
if (tab.url?.startsWith("chrome://")) return undefined;
chrome.scripting.executeScript({
//...
I also do this in the background.js because I'm injecting the script there:
chrome.tabs.onUpdated.addListener((tabId, changeInfo, tab) => {
// skip urls like "chrome://" to avoid extension error
if (tab.url?.startsWith("chrome://")) return undefined;
if (tab.active && changeInfo.status === "complete") {
chrome.scripting.executeScript({
//...
Note. To have access to tab.url you need "tabs" in manifest (V3) "permissions".

Add to Home Screen not prompt after Beforeinstallprompt event fired

Add to home screen not prompt even after its meets all PWA specification and checked by on Light House.
I have tried below code to check whether app already installed or not. but appinstalled Event not getting triggered and beforeinstallprompt Event gets fired successfully.
// appinstalled
window.addEventListener('appinstalled', (evt) => {
app.logEvent('a2hs', 'installed');
});
// beforeinstallprompt
window.addEventListener('beforeinstallprompt', (event) => {
event.preventDefault();
deferredPrompt = event;
});```
// manifest.json
`{
"name": "demo",
"short_name": "demo",
"icons": [{
"src": "/static/public/icon/icon-192x192.png",
"sizes": "512x512",
"type": "image/png"
},
{
"src": "/static/public/icon/icon-512x512.png",
"sizes": "192x192",
"type": "image/png"
}
],
"start_url": "/",
"orientation": "portrait",
"display": "standalone",
"theme_color": "#085689",
"background_color": "#085689",
"gcm_sender_id": "103xx3xxx50x",
"gcm_user_visible_only": true
}
`
// service worker
`self.addEventListener('fetch', (event) => {
console.log('event', event);
});`
Remove this line from your code
event.preventDefault();
Starting with Chrome 76, preventDefault() stopped the automatic mini-infobar from appearing
More details here
https://developers.google.com/web/fundamentals/app-install-banners/
event.preventDefault();
This is causing you the problem. Remove it.
By removing the event.preventDefault() you have no longer control over the event !
I suggest a lean manner to control the event, and get the information about installation, try the code below :
window.addEventListener('beforeinstallprompt', (event) => {
event.preventDefault();
deferredPrompt = event;
deferredPrompt.prompt();
deferredPrompt.userChoice.then(result => {
if(result.outcome === "accepted") {
// TODO whatever you want to do when the user accept to install the app
} else {
// TODO whatever you want to do when the user refuse to install the app
});
})

Chrome - Message passing - From popup click to context script on specific tab

Can you tell me why the following code is not working. Here is my code :
Popup.js (not a backgorund script) :
chrome.tabs.create({url: url}, function(tab) {
chrome.tabs.executeScript(tab.id, {file: 'connect.js', allFrames:true}, function() {
chrome.tabs.sendMessage(tab.id, 'whatever value; String, object, whatever');
});
});
content script :
chrome.runtime.onMessage.addListener(function(message, sender, sendResponse) {
console.log(message);
// Handle message.
// In this example, message === 'whatever value; String, object, whatever'
});
And my manifest :
{
"name": "AN App",
"version": "1.0",
"description": "To connect",
"permissions": [
"storage",
"activeTab",
"tabs",
"https://*/*"],
"browser_action": {
"default_popup": "popup.html"
},
"content_scripts": [{
"matches": ["https://*/*"],
"js": ["connect.js"]
}],
/*
"background": {
"scripts": ["background.js"]
},*/
"manifest_version": 2
}
I don't understand, the console debug in the tab do not display anything...
I also try from the popup to the background and then from the background to the tab but nothing happen neither (I'm quite new at chrome extension so I hope u can help me)
Thanks,
Regards
Martin
I found the solution. When I call chrome.tabs.create from the JS inside the popup it closes the code running in the popup and the message is never sent.
So instead of calling the chrome.tabs.create inside the JS linked to the popup, I just send a message to the background script, the background script call chrome.tabs.create (like this it runs in background and the code do not stop from executing).
And then the message function works correctly like chrome doc says.
Martin

Content Script is not executing..(it is not receiving the message sent by background)

I m working on extension that changes the background color of all divs on page to red but the control doesnot pass to the content script. Using the developers tools i m able to set breakpoints in background.js and popup.js but not in content.js so i used console.log but it is not executing plz help
popup.html
<!DOCTYPE html>
<html>
<head>
<script src="popup.js"></script>
</head>
<body>
<button id="change-btn">Change color of Divs</button>
</body>
</html>
popup.js
window.onload=init;
function init(){
var btn=document.getElementById("change-btn");
btn.onclick = function(){
chrome.runtime.sendMessage({type:"color-div" });
}
};
background.js
chrome.runtime.onMessage.addListener(function(request, sender, response){
if(request.type==="color-div"){
colordiv();
}
});
function colordiv(){
chrome.tabs.query({ active:true, currentWindow:true} , function(tab){
chrome.tabs.sendMessage( tab[0].id, {type: "colors-div", color: "#F00"});
});
};
content.js
chrome.runtime.onMessage.addListener(function(request, sender, response){
if(request.type==="colors-div"){
var divs= document.querySelectorAll("div");
if(divs.length===0)
alert("There r no divs on this page ");
else{
console.log("Before for loop");
for(var i=0; i<divs.length; i++)
{
divs[i].style.backgroundColor=request.color;
}
console.log("After for loop");
}
}
});
manifest.json
{
"name": "BrowserExtension",
"version": "0.0.1",
"manifest_version": 2,
"description" : "Description ...",
"content_scripts": [{
"matches": ["http://*/*", "https://*/*"],
"js": ["content.js"]
}],
"browser_action": {
"default_icon": "icon.png",
"default_title": "That's the tool tip",
"default_popup": "popup.html"
},
"background": {
"scripts": ["background.js"],
"persistent": false
},
"permissions":["tabs"]
}
This code works for me and the console.log statements show up in the Javascript console for the page when I open developer tools. Make sure that you refresh the tab you are targeting so that the content script is injected and be aware that this script will not be injected to local HTML documents because "file:///*" is not one of the matching rules specified for the content script.