I'm trying to send over a json-formatted object from my React-application to my chrome-extension devtool. In my React application I have a method that runs window.postMessage(data, '*'). I'm really not sure what the relation is between the background.js and content-scripts.
I'm able to receive this data through content-scripts.js by adding window.addEventListener('message') but I don't get how to send this data over to my devtools.js
manifest.json
{
"name": "visualize-test",
"version": "1.0.0",
"description": "testing chrome-ext order of execution",
"devtools_page": "devtools.html",
"manifest_version": 2,
"background": {
"scripts": [
"background.js"
],
"persistent": true
},
"content_scripts": [
{
"matches": ["<all_urls>"],
"js": ["content-script.js"]
}
],
"permissions": [
"tabs",
"http://*/*",
"https://*/*",
"file:///*"
]
}
This is the script file I'm injecting into my devtools.html
devtools.js
chrome.devtools.panels.create("visualize-test", null, "devtools.html", () => {
const port = chrome.extension.connect({
name: "visualize-test"
})
port.onMessage.addListener(data => {
console.log('RECEIVING DATA IN CONTENT-SCRIPTS: ', data)
}
chrome.extension.onMessage.addListener(function (e) {
console.log('DEVTOOLS MESSAGE HANDLER: ', e)
})
})
content-scripts.js
window.addEventListener('message', e => {
chrome.extension.sendMessage(e, (message) => {
console.log('Received message..sending to chrome extension: ', e.data)
})
})
background.js
chrome.extension.onConnect.addListener(function (port) {
console.log('Connected to PORT: ', port)
chrome.extension.onMessage.addListener(function (message, sender) {
console.log('BACKGROUND.JS: ', message)
port.postMessage(message)
})
port.onDisconnect.addListener(function (message) {
console.log('PORT DISCONNECTED')
})
})
I'm not ever receiving data from my background.js or the devtools.js after my data handler in content scripts runs chrome.extension.sendMessage()
Related
I'm migrating an extension from manifest v2 to manifest v3 (and refactor and redesign...)
In the old version we were using an iframe within the tab, like this:
background.js
const open = () => {
const oldIframe = document.getElementById('cm-frame');
if (oldIframe) {
oldIframe.remove();
return;
}
const iframe = document.createElement('iframe');
iframe.setAttribute('id', 'cm-frame');
iframe.setAttribute('style', 'top: 10px;right: 10px;width: 450px;height: 100%;z-index: 2147483650;border: none; position:fixed;');
iframe.setAttribute('allow', '');
iframe.src = chrome.extension.getURL('index.html');
iframe.frameBorder = 0;
document.body.appendChild(iframe);
};
chrome.browserAction.onClicked.addListener(function (tab) {
chrome.tabs.executeScript({
code: '(' + open.toString() + ')();'
}, () => { });
});
I've tried the same approach using chrome.actions.onClicked.addListener with chrome.scripting.executeScript and had no luck.
I know we could just use a popup ("default_popup": "popup.html",) but the iframe works better in terms of integration and design.
I was able to make it work.
background.js
chrome.action.onClicked.addListener(async function (tab) {
chrome.scripting.executeScript({
target: { tabId: tab.id },
func: () => {
const oldIframe = document.getElementById('cm-frame');
if (oldIframe) {
oldIframe.remove();
return;
}
const iframe = document.createElement('iframe');
iframe.setAttribute('id', 'cm-frame');
iframe.setAttribute(
'style',
'top: 10px;right: 10px;width: 400px;height: calc(100% - 20px);z-index: 2147483650;border: none; position:fixed;'
);
iframe.setAttribute('allow', '');
iframe.src = chrome.runtime.getURL('popup.html');
document.body.appendChild(iframe);
},
});
});
This my manifest.json file in case someone also needs it (I had to add stuff to permissions and web_accessible_resources to make it work and load correctly):
{
"manifest_version": 3,
"name": "Contact Mapping Extension",
"background": { "service_worker": "background.bundle.js" },
"action": {
"default_icon": "icon-34.png"
},
"icons": {
"128": "icon-128.png"
},
"content_scripts": [
{
"matches": ["http://*/*", "https://*/*", "<all_urls>"],
"js": ["contentScript.bundle.js"],
"css": ["content.styles.css"]
}
],
"web_accessible_resources": [
{
"resources": ["content.styles.css", "icon-128.png", "icon-34.png", "popup.html"],
"matches": ["<all_urls>"]
}
],
"permissions": [
"tabs",
"activeTab",
"scripting"
]
}
I have created a Chrome extension that queries for elements on some webpages that feature dynamic content loading via AJAX; -- when on some of these webpages, my content.js script triggers too early (prior to the loading of the elements I need).
As a result, I set up a listener in my background script to listen for when the page has loaded enough of the elements and then it re-injects the content.js script.
background.js:
chrome.runtime.onMessage.addListener(
function (request, sender, sendResponse) {
if (request.type == "hats_men") {
chrome.windows.get(sender.tab.windowId, function () {
/*get user's size based on product type (ie. hat)*/
var res = findSize();
if (res == -1) {
/*do nothing since no size exists for them*/
}
else {
/*send size back to content script via messaging*/
chrome.tabs.query({ active: true, currentWindow: true }, function (tabs) {
chrome.tabs.sendMessage(tabs[0].id, { type: "res_size", size: res });
});
}
});
}
});
var oldTab;
chrome.tabs.onUpdated.addListener(
function (tabId, changeInfo, tab) {
var temp = tab.url;
/*IF: on a product page, execute script.*/
if (temp && (temp.indexOf("shop.com/shop/") > -1)) && (changeInfo.url === undefined) && (temp != oldTab)) {
chrome.tabs.executeScript(tabId, { file: "content.js" });
oldTab = tab.url;
}
});
Unfortunately, I am unable to call any functions from content.js that exist in my other js files. Is there a way to invoke them? For context, I've included the js files in question into "web_accessible_resources" and "content_scripts" in my manifest.json.
external.js:
function monk(x) {
if (x != 0) {
console.log(x);
}
}
content.js:
/*get current url*/
var cur_url = window.location.href;
/*check if user is on supported site*/
if (cur_url.indexOf("hello.com/shop/") > -1 && cur_url.indexOf("quantity=1") > -1) {
/*get relevant user data*/
chrome.storage.sync.get({ data: [] }, async function (user) {
/*call prod_res() to get item details from dynamically loaded product page*/
const res = await prod_res();
/*if the product is for men*/
if (res[1] == "man") {
/*if the product is a hat*/
if (res[0] == "hat") {
/*send message to background script that this is a mens' hat*/
chrome.runtime.sendMessage({ type: "hats_men" },
function (response) {
/*listen for user's hat size in the request/message from background script*/
chrome.runtime.onMessage.addListener(
function (request, sender, sendResponse) {
/*log user's hat size to console*/
if(request.type == "res_size"){
monk(request.size][1]);
}
}
});
}
);
}
}
});
}
/nothing logs to console/
manifest.json:
{
"manifest_version": 2,
"name": "E-Co",
"version": "0.2",
"background": {
"scripts": [ "jquery-3.5.1.min.js", "reduce.js", "background.js" ],
"persistent": false
},
"content_scripts": [
{
"matches": [
"<all_urls>"
],
"js": [
"jquery-3.5.1.min.js",
"content.js",
"external.js"
]
}
],
"web_accessible_resources": [
"external.js"
],
"browser_action": {
"default_icon": "logo_active.png",
"default_popup": "popup.html",
"default_title": "E-co Pop-Up"
},
"icons": {
"16": "icon_16.png",
"48": "icon_48.png",
"128": "icon_128.png"
},
"permissions": [
"storage",
"notifications",
"tabs",
"*://*/*"
],
"content_security_policy": "script-src 'self' https://ajax.aspnetcdn.com; object-src 'self'"
}
Your content.js sends a message and uses a callback to wait for a response, which must be sent back by using sendResponse. Your background script doesn't send a response, it only sends a new message, so the first callback never runs.
Use the simple messaging example from the documentation:
content.js
chrome.runtime.sendMessage({type: 'hats_men'}, monk);
background.js:
chrome.runtime.onMessage.addListener((request, sender, sendResponse) => {
if (request.type == 'hats_men') {
sendResponse(findSize());
}
});
Note that sendResponse must be called for each message that was sent with a callback like we did above, otherwise you'll see errors in the console (unless you suppress them).
I create a extension. When user click on extension icon it will send message to the content script and then content script again call a function. In side that function it will send message to the background script. I face some strange behavior chrome.runtime.onMessage.addListener() in background script execute multiple times.
manifest.json
{
"manifest_version": 2,
"name": "Reportar",
"version": "1.0",
"description": "Loreipsum.",
"background": {
"scripts": ["bootstrap.js"],
"persistent": false
},
"browser_action": {
"default_icon": "img/icon48.png",
"default_title": "Gitlab Issue"
},
"web_accessible_resources": [
"templates/*.html"
],
"content_scripts": [{
"all_frames": false,
"css": ["content_style.css"],
"js": ["content_script.js"],
"matches": ["http://*/*", "*/*"]
}],
"icons": {
"16": "img/icon20.png",
"48": "img/icon48.png",
"128": "img/icon128.png"
},
"permissions": [
"tabs",
"activeTab",
"<all_urls>",
"storage"
]
}
background.js
function clickOnIssue() {
chrome.tabs.query({active: true, currentWindow: true}, function (tabs) {
console.log('Going to send message to content script that user click on browserAction icon');
chrome.tabs.sendMessage(tabs[0].id, {id: 111});
});
}
chrome.tabs.onUpdated.addListener(function (id, info, tab) {
if (info.status === 'complete') {
chrome.browserAction.onClicked.removeListener(clickOnIssue);
chrome.browserAction.onClicked.addListener(clickOnIssue);
chrome.runtime.onMessage.addListener(function (request, sender, sendResponse) {
var _t = new Date().getTime();
console.log("Request received for getProjectList(" + _t + ")");
sendResponse({t: _t});
return true;
});
}
});
content_script.js
chrome.runtime.onMessage.addListener(function (request, sender, sendResponse) {
console.log(request);
//sendResponse({msg: 'received'});
chrome.runtime.sendMessage({action: 'submitSettings'}, function (resp) {
console.log('Received the message that user click on browserAction icon');
updateProjectDropDown();
});
return true;
});
function updateProjectDropDown() {
console.log('Request dispatch for getProjectList');
chrome.runtime.sendMessage({action: 'getProjectList'}, function (resp) {
console.log(resp.t + ': bootstrap.js JS received the message');
});
}
This is browser's console
This is backgound js console
Edit: Add manifest file
I think bellow code will solve your issue
chrome.runtime.onInstalled is run once so your listeners will not bind multiple times.
chrome.runtime.onInstalled.addListener(function (details) {
chrome.browserAction.onClicked.addListener(clickOnIssue);
chrome.runtime.onMessage.addListener(function (request, sender, sendResponse) {
//TODO: your code
});
});
i try to connect to just created new tab , my end mission is to update html widget on all open tabs
any way each time i try to connect to tabs via id i fail with error . im using Version 23.0.1271.64 m
var channelTabs = [];
function isInclude(arr,obj) {
return (arr.indexOf(obj) != -1);
}
chrome.tabs.onCreated.addListener(function(tab) {
// add tab when opened
if (channelTabs.indexOf(tab.id) == -1) {
channelTabs.push(tab.id);
}
chrome.windows.getCurrent(function(win)
{
// get an array of the tabs in the window
chrome.tabs.getAllInWindow(win.id, function(tabs)
{
for (i in tabs) // loop over the tabs
{
// if the tab is not the selected one
if(isInclude(channelTabs,tabs[i].id))
{
if(/^(https?|file):/.test(tabs[i].url))
{
console.log('Debug Background sending update to open tab id:'+tabs[i].id);
var port = chrome.tabs.connect(tabs[i].id,{name: "content_tab_request"});
port.postMessage({resp: "tab_update",data:"some string song1"});
}
}
}
});
});
chrome.tabs.onUpdated.addListener(function(tabId, changeInfo,tab) {
// Example: allow http:, https: and file:
if (/^(https?|file):/.test(tab.url)) {
console.log('Debug Background sending onUpdated to open tab id:'+tabId);
var port = chrome.tabs.connect(tabId,{name: "content_tab_request"});
port.postMessage({resp: "tab_update",data:"some string song1"});
}
});
});
but each time it try to chrome.tabs.connect it gives me :
Port error: Could not establish connection. Receiving end does not exist. miscellaneous_bindings:235
chromeHidden.Port.dispatchOnDisconnect miscellaneous_bindings:235
chrome.Event.dispatch_ event_bindings:371
dispatchArgs event_bindings:249
chromeHidden.Event.dispatchEvent
and the content script :
// Register this tab to the background script
var port = chrome.extension.connect({name: "content_request"});
port.postMessage({req: "Hello"});
port.onMessage.addListener(function(msg) {
if (msg.resp == "World")
{
port.postMessage({answer: "good"});
}
else if(msg.answer == "bye")
{
console.log('Debug contentscript.js reciving answer from background msg.answer:'+msg.answer);
}
else
{
console.log('Debug contentscript.js reciving answer from background is wrong:'+msg);
}
if(port.name == "content_tab_request")
{
console.log('Debug contentscript.js reciving request from background Tab function:'+msg);
if(msg.resp=="tab_update ")
{
var data_recive = msg.data;
console.log('Debug contentscript.js reciving request data from background Tab to update page data_recive:'+data_recive);
}
}
});
json file :
{
"background": {
"page": "background.html"
},
"content_scripts": [
{
"matches": ["<all_urls>"],
"js": ["contentscript.js"],
"run_at": "document_start",
"all_frames": true
}
],
"web_accessible_resources": [
"script_inpage.js"
],
"browser_action": {
"default_icon": "icon19.png",
"default_popup": "popup.html",
"default_title": "Simple test"
},
"content_security_policy": "script-src 'self'; media-src *; object-src 'self'",
"description": "Simple test.",
"icons": {
"128": "icon128.png",
"16": "icon16.png",
"32": "icon32.png",
"48": "icon48.png"
},
"manifest_version": 2,
"minimum_chrome_version": "20",
"name": "Simple test",
"permissions": [
"unlimitedStorage",
"http://*/",
"<all_urls>",
"tabs"
],
"version": "2.6"
}
by the way connection from content script to background working just fine !
Ok, it seems you are trying to synchronize asynchronous events, after simplification of your code i got it running with modifications and achieved your functional requirement of posting message to all tabs when a new tab is created.
Output:
I was able to see this message on all tabs
manifest.json
{
"name":"Sample communication from content to background",
"description":"This is a sample for Simulating communication from content to background",
"manifest_version":2,
"version":"2",
"background":{
"scripts":["background.js"]
},
"content_scripts": [
{
"matches": ["<all_urls>"],
"js": ["myscript.js"],
"run_at":"document_start"
}
]
}
"background.js"
chrome.tabs.onCreated.addListener(function(tab) {
chrome.windows.getCurrent(function(win){
chrome.tabs.getAllInWindow(win.id, function(tabs){
for (i=0;i<tabs.length;i++){
console.log(tabs[i]);
chrome.tabs.sendMessage(tabs[i].id,{name: "content_tab_request"});
}
});
});
});
"myscript.js"
chrome.extension.onMessage.addListener(function(msg) {
console.log("Message Recieved "+ msg);
});
Also some chrome extentions may produce this error. i had this problem and the problem was Aside extention (for saving articles to Pocket).
the error was:
Port error: Could not establish connection. Receiving end does not exist. miscellaneous_bindings:235
chromeHidden.Port.dispatchOnDisconnect
I have this strange problem:
on content_script:
function getText(){
var = text;
chrome.extension.sendMessage({}, function(response){
text = response.data;
});
return text;
}
if (getText()) {
console.log('OK')
} else{
console.log('Not OK')
}
on background.js:
var text = 'tests';
chrome.extension.onMessage.addListener(function(request, sender, sendResponse){
sendResponse( { 'data': text } );
})
Place a breakpoint on line return text and you get OK on console.
Disable breakpoints and you get only Not OK.;
Seems to be some timing problem, like text not being defined at the time of return, unless you give Chrome some time by using a breakpoint.
manifest.json:
{
...
"permissions": ["tabs"],
"background": {
"scripts": ["js/background.js"]
},
"content_scripts": [
{
...
"js": [ "js/content_script.js"],
"run_at": "document_end"
}
],
"manifest_version": 2
}
Can someone reproduce this?