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?
Related
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
});
});
So I'm trying my hand at a google chrome extension which in theory should be extremely straight forward. I know there are tons of stack queries and I've tried a few, I've even copied and pasted direct solutions for testing and I have been unsuccessful. So here is the project. When a page loads, check the html content, and change the icon programmatically.
This is my content.js
chrome.tabs.onUpdated.addListener( function (tabId, changeInfo, tab) {
if (changeInfo.status == 'complete' && tab.active) {
var markup = document.documentElement.innerHTML;
var m = markup.indexOf("candy");
if (m > -1) {
chrome.browserAction.setIcon({path: "check.png"});
} else {
chrome.browserAction.setIcon({path: "cross.png"});
}
}
})
This is my manifest.json
{
"manifest_version": 2,
"name": "Tag Analyzer Plugin",
"description": "Check if tag exist on page",
"version": "1.0",
"browser_action": {
"default_icon": "cross.png"
},
"content_scripts": [
{
"matches": [
"<all_urls>"
],
"js": ["content.js"]
}
],
"permissions": ["<all_urls>"]
}
Right now I'm running this item as a content script because as a content script I can use the logic
var markup = document.documentElement.innerHTML;
var m = markup.indexOf("candy");
if (m > -1) {} else {}
However as a content script the chrome API stuff doesn't work. When I run this script as a background script the script works, except that
var markup = document.documentElement.innerHTML;
doesn't return the pages html, it returns the injected script html.
I've read this stack which was informative as to what the difference was and I've read and tested many stacks like here this, without much success. So obviously i'm missing something and doing something wrong. Thank in advanced for any help.
UPDATES:
So I've made some modifications, but it's still not working, though I think that it's closer to working.
As per the comments below I am now using a content script and background script. No errors are being thrown, however the background script isn't doesn't anything.
content.js
var markup = document.documentElement.innerHTML;
var m = markup.indexOf("candy");
if (m > -1) {
chrome.runtime.sendMessage({"found" : true});
} else {
chrome.runtime.sendMessage({"found": false});
}
background.js
chrome.runtime.onMessage.addListener(
function(request, sender, sendResponse) {
if request.found {
alert("HERE");
chrome.browserAction.setIcon({
path: "check.png",
tabId: sender.tab.id
});
} else {
alert("HERE2");
chrome.browserAction.setIcon({
path: "cross.png",
tabId: sender.tab.id
});
}
});
manifest.json
{
"manifest_version": 2,
"name": "Tag Analyzer Plugin",
"description": "find tag on page",
"version": "1.0",
"browser_action": {
"default_icon": "cross.png"
},
"background": {
"scripts": ["background.js"],
"persistent": false
},
"content_scripts": [
{
"matches": [
"<all_urls>"
],
"js": ["content.js"]
}
],
"permissions": ["<all_urls>"]
}
I've added some alerts on the background.js to see if it was being trigger and nothing, not does the icon change.
I am trying to develop a Chrome extension to open Office documents stored in Confluence in a new tab using the IE Tab extension.
In the 'View Page Attachment' screen, there is an 'Edit in Office' link for Office file attachments. The link has a click event that creates a new instance of a URLLauncher, which is used to open the document. This feature is not supported in Chrome, so I want to add my own URLLauncher prototype into the web page to make it work.
In short, this is my idea:
Create a Chrome extension with a content script that injects a URLLauncher prototype into the 'View Page Attachment' page (I don't know if this is the right approach, so I am open to suggestions).
When the user clicks on the 'Edit in Office' link, the URLLauncher.open method opens the file attachment in a new tab by calling the IE Tab extension.
I can see the 'Hi there!' alert every time I load a web page, and that confirms that content.js is being injected. Nevertheless, the URLLauncher is not available in the web page. I think this is because the global window object of the content script is distinct from the page/extension's global namespace (i.e., window.URLLauncher is undefined). How could I reorganize my code to overcome this obstacle?
These are my files:
manifest.json
{
"manifest_version": 2,
"background": {
"scripts": [
"background.js"
]
},
"content_scripts": [ {
"js": [ "content.js" ],
"matches": [ "<all_urls>" ]
} ],
"description": "This is a test extension",
"permissions": [
"tabs", "http://*/*", "https://*/*"
],
"name": "Test extension",
"version": "1.0.0"
}
background.js
chrome.tabs.executeScript(null, {
code: "document.body.appendChild(document.createElement('script')).src='" +
chrome.extension.getURL("content.js") + "';"
}, null);
chrome.runtime.onMessage.addListener(
function(request, sender, sendResponse) {
console.log(sender.tab ?
"from a content script:" + sender.tab.url :
"from the extension");
if (request.id == "doUrlLaunch") {
chrome.tabs.create({ url: request.nUrl, selected: true });
sendResponse({result: "goodbye"});
}
}
);
content.js
var prefixUrl = 'chrome-extension://hehijbfgiekmjfkfjpbkbammjbdenadd/iecontainer.html#url=';
alert('Hi there!');
function URLLauncher() {
}
URLLauncher.prototype = {
open : function(urlStr) {
var newUrl = prefixUrl + 'https://host.com' + encodeURI(urlStr);
chrome.runtime.sendMessage({id: "doUrlLaunch", nUrl: newUrl}, function(response) {
});
}
}
Thanks in advance.
UPDATE 1
I edited the files following the instructions given by Rob W and this page ('Message Passing'); now the code is injected in the page itself, but a major problem still remains. The actual JS code sends a message to the content script, but the message is not caught by the listener, so the new tab is not created and the callback function does not receive a response; the error message I got: Error in event handler for (unknown): TypeError: Cannot read property 'success' of undefined.
These are the updated files:
manifest.json
{
"manifest_version": 2,
"content_scripts": [ {
"js": [ "content.js" ],
"matches": [ "<all_urls>" ]
} ],
"web_accessible_resources": [ "script.js" ],
"description": "This is a test extension",
"permissions": [
"tabs", "http://*/*", "https://*/*"
],
"name": "Test extension",
"version": "1.0.0",
"externally_connectable": {
"ids": ["*"],
"matches": ["*://*.hostname.com/*"]
}
}
content.js
var s = document.createElement('script');
s.src = chrome.extension.getURL("script.js");
s.onload = function() {
this.parentNode.removeChild(this);
};
(document.head||document.documentElement).appendChild(s);
chrome.runtime.onMessage.addListener(
//Unreachable code!
function(request, sender, sendResponse) {
console.log(sender.tab ?
"from a content script:" + sender.tab.url :
"from the extension");
if (request.id == "doUrlLaunch") {
chrome.tabs.create({ url: request.nUrl, selected: true });
sendResponse({result: "goodbye"});
}
}
);
script.js
var prefixUrl = 'chrome-extension://hehijbfgiekmjfkfjpbkbammjbdenadd/iecontainer.html#url=';
function URLLauncher() {
}
URLLauncher.prototype = {
open : function(urlStr) {
var newUrl = prefixUrl + 'https://hostname.com' + encodeURI(urlStr);
chrome.runtime.sendMessage({id: "doUrlLaunch", nUrl: newUrl}, function(response) {
if (!response.success)
console.log('Something went wrong...');
});
}
}
Not sure if you're still interested in an answer, but in your edited files your problem is where your listener sits.
chrome.runtime.sendMessage does not reach content scripts; it is intended for extension pages. Message passing to content scripts works through chrome.tabs.sendMessage, but this is irrelevant for this task.
Content scripts can't call chrome.tabs as they do not have the required API access.
A solution would be to call a background script, that can receive those messages and can call the required API.
Therefore, you need a third script, taking out this code from content.js:
// background.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.id == "doUrlLaunch") {
chrome.tabs.create({ url: request.nUrl, selected: true });
sendResponse({result: "goodbye"});
}
}
);
And modify your manifest:
"background": { "scripts": [ "background.js" ] },
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