Communication between background.js and content-script? Response is UNDEFINED - javascript

From content-script:
chrome.runtime.sendMessage({ type: "getFormatOption" }, function (response) {
return response === 'csv';
});
I have inspected in console: the value, which is used in SendResponse() from background.js method is OK. The problem is that response is always UNDEFINED. What am I doing wrong?
background.js:
chrome.runtime.onMessage.addListener(
function (message, sender, sendResponse) {
switch (message.type) {
case 'getFormatOption':
var response = $('input[name=csvOrEnter_radio]:checked', '#csvOrEnter_form').val();
console.log('formatOption: ' + response);
sendResponse(response);
break;
case 'getFilteringStrategy':
var response = $('input[name=filteringStrategy_radio]:checked', '#filteringStrategy_form').val();
console.log('filteringStrategy: ' + response);
sendResponse(response);
break;
default:
console.error('Unrecognised message: ', message);
}
}
);
The idea that I take some values from radiobuttons from my plugin popup.html.
Manifest:
{
// default for the latest version of Chrome extensions
"manifest_version": 2,
// extension related general info
"name": "FB Interest Search Tool",
"short_name": "FB Interest Search Tool",
"description": "FB Interest Search Tool",
"version": "1.0.0",
"default_locale": "en",
// sets path to popup files
"browser_action": {
"default_icon": "img/128.png",
"default_popup": "popups/popup.html",
"default_title": "FB Interest Search Tool"
},
// sets path to content scripts and when they are injected onto the page
"content_scripts": [
{
"matches": [ "http://*/*", "https://*/*" ],
"css": [ "styles/styles.css" ],
"js": [
"bower_components/jquery.min.js",
"bower_components/jquery.cookie.js"
]
}
],
// sets path to background scripts
"background": {
"scripts": [
"bower_components/jquery.min.js",
"bower_components/jquery.cookie.js",
"bg/background.js",
"content-scripts/rewriteStorage.js"
]
},
"permissions": [
"activeTab",
"http://*/",
"https://*/",
"file:///*/*",
"<all_urls>",
"tabs",
"storage",
"unlimitedStorage",
"storage",
"cookies"
],
"web_accessible_resources": [ "styles/commentblocker_on.css" ]
}

Problem 1: it's called background.js.
I'm not even remotely joking. Well maybe a little.
Since it's included BOTH in the popup and as a background script, there are 2 message listeners registered. And only one can answer:
Note: If multiple pages are listening for onMessage events, only the first to call sendResponse() for a particular event will succeed in sending the response. All other responses to that event will be ignored.
Guess which gets to answer first? My bets are on the background, since it registered the listener first.
While you still see the listener execute in the popup if you try to debug, its sendResponse is ignored because the background already answered.
This one's easy to fix: make a copy, call it popup.js, and keep only relevant logic in both. Don't include the same file in both places unless you're 100% certain code needs to run in both.
Problem 2: Popup is mortal.
When the popup is closed — it's dead, Jim. It simply does not exist: neither the listener, nor the radiobuttons. Therefore, it cannot answer the message.
Fixing this requires reworking the architecture. The only 2 places that can provide storage you can query at any time are:
Background page. It needs to hold the state in a way other than selected element, and the popup needs to inform about the change of state.
chrome.storage API. Both the popup and the content script can read/write to it.
For options, the second one is preferable.

Related

Firefox Extension / Webextensions: Why doesn't the MDN example of Connection Based Messaging work?

This is the first time I read about writing Firefox extensions.
What I need is obviously only viable via WebExtensions and both a background and a contentscript. I actually only want to write all open tabs as links in a new tab and then File->Save it. Another alternative Idea was to put it into a JSON Object and save that through a dialog, then I probably could even spare the contentscript but I haven't found anything in the API to download a JSON Object via asking the user to download it via Download Dialog.
Whatever. I think I need to communicate with the content-script then.
I tried to run the following example, but it is not working. When I load the manifest file and open the debugger for extensions, it doesn't log anything and nothing has happened except that the variables myPort and portFromCS seem to be declared without any value.
https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/Content_scripts#connection-based_messaging
// manifest.json
{
"manifest_version": 2,
"name": "Save Open Tabs",
"version": "1.0",
"description": "Save my tabs",
"background": {
"scripts": ["background.js"]
},
"content_scripts": [
{
"matches": ["*://*/*"],
"js": ["content.js"]
}
],
"permissions": [
"activeTab",
"tabs"
]
}
// content.js
let myPort=browser.runtime.connect({name:"port-from-cs"});
myPort.postMessage({greeting: "hello from content script"});
myPort.onMessage.addListener((m) => {
console.log("In content script, received message from background script: ");
console.log(m.greeting);
});
// background.js
let portFromCS;
function connected(p) {
portFromCS = p;
portFromCS.postMessage({greeting: "hi there content script!"});
portFromCS.onMessage.addListener((m) => {
portFromCS.postMessage({greeting: "In background script, received message from content script:" + m.greeting});
});
}
browser.runtime.onConnect.addListener(connected);
Why doesn't the example work? Maybe wrong URL matching in the manifest file?

Content script not listening to message event

I am developing my first browser extension for my website.
What this extension should basically do is to have a browser action which opens a pop-up where you can edit specific cookie values for the current page.
However, cookie A can exist on the page / while cookie B can exist on the page /checkout. So I don't want to list every cookie inside the pop-up, only the one which is active on the current page.
So, I searched the documentation and found that in order to communicate between web page and add-on you have to use the message system as described here
https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/Content_scripts#Communicating_with_the_web_page
To do so, my website has a JavaScript file which is loaded on every page. In this JavaScript I'm executing the following code
window.postMessage({type: 'FROM_PAGE', data: visibleCookies}, '*');
This piece of code is definitely executed, because I put a console.log before or after that statement, I can see that it's being logged.
Now, in my content script I want to listen to this by executing the following code
// experimentManager.js
console.log('testd');
window.addEventListener('message', (event) => {
console.log(event);
if (event.source !== window) {
return;
}
});
However, the console.log(event); is never executed. The listener is never activated. When I press the browser action so that the popup opens testd is logged into console, but still, the listener doesn't get any events. It's just not getting executed.
I don't know which files are relevant, but this is my manifest.json
// manifest.json
{
"manifest_version": 2,
"name": "My first addon",
"version": "1.0",
"description": "My first addon description",
"icons": {
"48": "icons/icon.png"
},
"browser_action": {
"default_icon": "icons/icon.png",
"default_title": "My first addon",
"default_popup": "popup/manage_experiment.html",
"browser_style": true
},
"permissions": [
"tabs",
"cookies",
"<all_urls>"
],
"content_scripts": [
{
"matches": ["*://*.mydomain/*"],
"js": ["experimentManager.js"]
}
]
}
And inside the pop-up script I'm executing this code among other things
browser.tabs.executeScript({file: "/content_scripts/experimentManager.js"})
.then(manageExperiments)
.catch(handleError);
which is probably the reason why the console.log('testd') gets executed, but nothing else?
What am I missing?

Content script not executing in new window

I am trying to create a Chrome extension that, when clicked, opens a new incognito window and performs some DOM action on it. These are the files I'm using:
manifest.json
{
"manifest_version": 2,
"name": "SampleExtension",
"description": "",
"version": "1.0",
"incognito": "spanning",
"browser_action": {
"default_icon": "icon.png",
"default_popup": "popup.html"
},
"content_scripts": [
{
"matches": ["http://www.google.com"],
"js": ["myscript.js"]
}
],
"permissions": [
"tabs",
"activeTab",
"http://www.google.com"
]
}
popup.js
chrome.windows.create({
"url": "http://www.google.com",
"focused": true,
"incognito": true
});
chrome.tabs.executeScript(null, {
"file": "myscript.js",
"run_at": "document_end",
"all_frames": true
});
myscript.js
document.querySelector('a[target]').click();
The extension opens the new window, but my content script doesn't seem to be executing. Any thoughts?
Edit: Added "incognito": "spanning" to the manifest. Still doesn't work, however.
First of all, I understand that you have enabled to run in Incognito Mode. Extensions are disabled by default and, hence, it would not run otherwise.
Secondly, your match pattern needs to end with a slash:
"matches": ["http://www.google.com/"],
Thirdly, Google will redirect you to its https version, hence I would improve the match pattern like this:
"matches": ["*://www.google.com/"],
Still, it didn't work for me as I was redirected to my local Google domain. Hence, I had to do add more:
"matches": [
"*://www.google.com/*",
"*://www.google.com.sg/*"
],
Also, I added the final wildcard, because Google was adding some ?urlParams that I had to match too. And this made it work. Note that I tried with other pages like "*://www.stackoverflow.com/*", and it was easier than Google :)
In case your Google page was just a test, I'd advise to use some less redirected pages to test with.
A final note: I do not think it's possible to use the wildcard for the domain (I tried). However, you can request all the main domains, or request all_pages and then add the logic for Google only on my_script.js to decide whether to execute the action or not. (However, this last piece is not ideal).
Edit post comments:
It seems your function fails because the element is not loaded yet. An easy way to solve this is by doing an interval which checks whether the element is on the page. When it finds it, clicks it and removes the interval.
// Function which clicks element if existing and clears interval after doing it.
var clickLink = function() {
if (document.querySelectorAll('a[target]').length > 0) {
clearInterval(waitAndClick); // stop interval
document.querySelector('a[target]').click(); // click element.
}
}
// Run click function every second, until it clicks it.
var waitAndClick = setInterval(clickLink, 1000);

Auto start chrome extension

I am trying to make a chrome extension that redirects to a other page when it's loaded. For example, if it's google.nl, go to google.nl?example I managed to get that working, but only when i press the extension button. I want to run the script from the background.js but i get the error:
Uncaught TypeError: Cannot read property 'onBeforeRequest' of undefined
Why do i wan't to reload? Well i don't. it's just to test. The original plan is reading the URL and put the open graph data in my extension.
manifest (part)
"content_scripts": [{
"matches": [
"<all_urls>"
],
"js": ["background.js"]
}],
"permissions": [
"tabs",
"activeTab",
"webRequest"
],
"background": {
"scripts": ["background.js"]
}
background.js
chrome.webRequest.onBeforeRequest.addListener(function(a) {
if (a.url.indexOf("http://www.google.com/") >= 0) {
reloadExtensions();
chrome.tabs.get(a.tabId, function(b) {
if ((b.selected == false)) {
chrome.tabs.remove(a.tabId)
}
});
return {redirectUrl: chrome.extension.getURL("close.html")}
}
return {cancel: false}
}, {urls: ["http://reload.extensions/"],types: ["main_frame"]}, ["blocking"]);
iirc the docs say you need to put all the URL's you want to redirect in the permissions array.
If you don't you will see the following error
Checkout Catblock example https://developer.chrome.com/extensions/samples#search:webrequest by removing
I don't think you need the content script parts at all, in fact I suspect that is where you are seeing the error.

Communication between scripts in Chrome Extension

I know there are many variations of this question already in existence here, but none of them seem to work for me.
Details:
I'm writing an extension that pulls some email data from emails you send in gmail. In order to achieve this I am using this version of Gmailr https://github.com/joscha/gmailr.
In effect, I have three content scripts: Gmailr.js and main.js (which are pretty much identical to those in the link above) allow me to pull out the information I'm looking for. Then content.js I use to send a message to the background page of the extension.
The problem is that from gmailr.js and main.js I cannot use any of the Chrome APIs, and I'm not really sure why, so I can't send messages from these back to the background page.
That is why I made content.js which can communicate with the background page. However, it does not seem to be able to see anything the other content scripts do. For example, main.js inserts a div at the top of the page. When I try to attach an event listener to a button in this div from content.js, I am told that no such element exists.
How can I get the data pulled out by main.js to be seen by content.js? (I also tried to put the data in local storage, then trigger a custom event listener to tell content.js to read local storage, but no luck because they don't seem to be able to hear each other's event being triggered).
Any insight or alternatives are much appreciated.
(I can post code if necessary, but it's fragmented and long)
My manifest file:
{
"manifest_version": 2,
"name": "Email extractor",
"description": "Extracts data from emails",
"version": "1.0",
"background": {
"script": "background.js"
},
"content_scripts": [
{
"matches": [
"*://mail.google.com/*",
"*://*/*"
],
"js": [
"lib/yepnope.js/yepnope.1.5.4-min.js",
"lib/bootstrap.js",
"main.js",
"gmailr.js",
"content.js"
],
"css": [
"main.css"
],
"run_at": "document_end"
}
],
"permissions": [
"tabs",
"storage",
"background",
"*://mail.google.com/*",
"*://*/*"
],
"browser_action": {
"default_icon": "img/icon.png",
"default_popup": "popup.html"
},
"web_accessible_resources" : [
"writeForm.js",
"disp.js",
"/calendar/jsDatePick.min.1.3.js",
"/calendar/jsDatePick_ltr.min.css",
"lib/gmailr.js",
"lib/jquery-bbq/jquery.ba-bbq.min.js",
"content.js",
"main.js",
"background.js"
]
}
This is main.js:
Gmailr.init(function(G) {
sender = G.emailAddress();
G.insertTop($("<div id='gmailr'><span></span> <span id='status'></span>)");
el = document.getElementById("testid");
el.addEventListener('click', mg, false);
var status = function(msg) {
G.$('#gmailr #status').html(msg); };
G.observe(Gmailr.EVENT_COMPOSE, function(details) {
....
status(" user: " + user);
console.log('user:', user);
//now try to send a message to the background page
//this always returns the error that method sendMessage does not exist for undefined
chrome.runtime.sendMessage({greeting: "test from gmailr"}, function(response) {
console.log("did it send?");
});
});
});
gmailr.js is quite long and is also not my own code but it can be seen here: http://pastebin.com/pK4EG9vh
Hi perhaps 3 likely reason to your problem :
The way you send messages to bgp from main.js and gmailr.js are perhaps wrong because you must arrive to communicate from any content script to your bgp. (in your manifest content script key the gmailr.js is missing). Show us your code it would help.
You seems to have a problem with the moment you search from content.js to access to the element created in main.js. Do you try to access your element with the jQuery $("").on() method ? A simple test must be to declare a function in one cs and to use it in another. If it's not working it's a manifest problem. The order you declare .js file in manifest content script key is important also.
try to in the manifest content script array "run_at":"document_end"
Hope it help !

Categories

Resources