I send a message from a ContextMenu within content.js to background.js. When I send a message, I expect to see an alert of just two variables which are sent with the request. When I send multiple request(few times in a row) I receive alerts including previously sent messages. It seems that all messages are stored somewhere. How do you disable this? I would like to see alerts of only the most recent message.
contents.js:
$(document).mousedown(function(event) {
var url = window.location.href;
var contents = window.getSelection().toString();
switch (event.which) {
case 3:
chrome.runtime.sendMessage({contents: contents, url: url}, function(response) {
//console.log(response.farewell);
});
break;
}
});
background.js
chrome.extension.onMessage.addListener(function (message, sender, sendResponse) {
if (message.url) {
chrome.contextMenus.onClicked.addListener(function testFunc2(info, tab){
alert(message.url);
alert(typeof message.contents);
}
)
}
});
manifest.json
"background": {
"scripts": ["jquery-1.11.1.min.js", "background.js"],
//"page": "background.html",
"persistent": true
},
It's because of this code
chrome.extension.onMessage.addListener(function (message, sender, sendResponse) {
if (message.url) {
chrome.contextMenus.onClicked.addListener(function testFunc2(info, tab){
alert(message.url);
alert(typeof message.contents);
}
)
}
});
What you are saying is that every onMessage event add a listener for onClicked events. So if you send three messages you end up with three testFunc2 methods acting on onClicked events.
Since you are trying to use information from two different asynchronous events. You will have to store one of them temporarily. Something like this would probably work.
var lastMessage;
chrome.extension.onMessage.addListener(function(message, sender, sendResponse) {
if (message.url) {
lastMessage = message;
} else {
lastMessage = undefined;
}
});
chrome.contextMenus.onClicked.addListener(function(info, tab) {
if(lastMessage !== undefined) {
testFunc2(message, info, tab);
}
});
function testFunc2(info, tab){
alert(message.url);
alert(typeof message.contents);
// cleanup
lastMessage = undefined;
});
Related
I am new to using chrome extensions and i have a run into a little problem with setting up a chrome extension. I want the extension to read specific values from a web page and then open up a specific page (form with a number of input fields) from a flask application that I have built in a new tab and then use the values that have been scraped to populate specific fields in the page from my flask app.
I have managed to get the extension to generate a new tab and to load the page from my flask app but I am unable to get the fields to populate. It would seem that the page gets loaded before the fields are populated. I have pasted some code to show you how far I have got. The other issue is that I am using the code parameter from executeScripts to perform the populating action but I don't seem to be able to pass arguments into the code string (I suspect this is not the way to do this but I am working off an answer that I have found very helpful up to this point from here https://stackoverflow.com/a/41094570/1977981
Any help would be much appreciated.
manifest.json
{
"manifest_version": 2,
"name": "My Cool Extension",
"version": "0.1",
"permissions": [
"http://localhost:****/lesson/1/note/new/"
],
"content_scripts": [
{
"matches": [
"<all_urls>"
],
"js": ["jquery-3.2.1.min.js", "content.js"]
}
],
"browser_action": {
"default_icon": "icon.png"
},
"background": {
"scripts": ["jquery-3.2.1.min.js", "background.js"]
}
}
content.js
// Triggered by sendMessage function in background.js
chrome.runtime.onMessage.addListener(
function(request, sender, sendResponse) {
// listening for request message
if( request.message === "clicked_browser_action" ) {
// Retrieve Lesson title from current tab
var lesson = $('._tabbed-sidebar--title--1Dx5w').find('span').text()
// output this value to the console
console.log(lesson);
// Send a single message to the event listener in your extension i.e. background.js
chrome.runtime.sendMessage({"message": "open_new_tab", "lesson": lesson})
}
}
);
background.js
// Called when the user clicks on the browser action icon.
chrome.browserAction.onClicked.addListener(function(tab) {
// Send a message to the active tab
chrome.tabs.query({active: true, currentWindow: true}, function(tabs) {
// take the current tab shown in the window
var activeTab = tabs[0];
// Send a message to contents.js - this message is being listened for by contents.js and the runtime.onMessage event is fired in content.js script
chrome.tabs.sendMessage(activeTab.id, {"message": "clicked_browser_action"});
});
});
// listening for "open_new_tab" message from content.js
chrome.runtime.onMessage.addListener(
function(request, sender, sendResponse) {
if( request.message === "open_new_tab" ) {
// create new tab but do not activate the tab yet
chrome.tabs.create({"url": "http://localhost:5000/lesson/1/note/new/", active: false }, function(tab){
// load jquery functionality execute script
chrome.tabs.executeScript(tab.id, {file: "jquery-3.2.1.min.js"}, function(results){
chrome.tabs.executeScript(tab.id,{code:`
(function(arguments){
var count = 100; //Only try 100 times
function autofill(){
var lesson = $('.lesson_subsection');
console.log(lesson);
if(lesson){
lesson.value = arguments[0].lesson;
} else {
if(count-- > 0 ){
//The elements we need don't exist yet, wait a bit to try again.
setTimeout(autofill,250);
}
}
}
autofill();
}, request)();
`}, function(results){
chrome.tabs.update(tab.id,{active:true});
}); //END OF second executeScript function
}); // END OF first executeScript function
} // END OF chrome.tabs.create anonymous function
); // END OF chrome.tabs.create
} // END OF if( request.message === "open_new_tab" ) {
}); // END OF addListener anonymous function
Thank you #wOxxOm your comments were very helpful. I was able to use JSON.stringify to load the arguments in to the injected code string. I also had to load the input element from my form using document.getElementsByClassName() instead of using the jquery version of the object. This also meant i didn't have to load the jquery library see line shown below
var less = document.getElementsByClassName('lesson_subsection')[0];
Now my chrome.runtime.onMessage.addListener function in my background.js script is as follows:
// See content.js function
chrome.runtime.onMessage.addListener(
function(request, sender, sendResponse) {
if( request.message === "open_new_tab" ) {
chrome.tabs.create({"url": "http://localhost:5000/lesson/1/note/new/", active: false }, function(tab){
console.log('Attempting to inject script into tab:',tab);
chrome.tabs.executeScript(tab.id,{code:`
(function(argument){
var count = 100; //Only try 100 times
function autofill(){
var less = document.getElementsByClassName('lesson_subsection')[0];
if(less){
less.value = argument;
} else {
if(count-- > 0 ){
//The elements we need don't exist yet, wait a bit to try again.
setTimeout(autofill,250);
}
}
}
autofill();
})(` + JSON.stringify(request.lesson) + `);
`}, function(results){
// chrome.tabs.sendMessage(tab.id, {"message": "need to update tab", "tab": tab.id});
chrome.tabs.update(tab.id, { active: true });
}); //END OF executeScript function
} // END OF chrome.tabs.create anonymous function
); // END OF chrome.tabs.create
} // END OF if( request.message === "open_new_tab" ) {
}); // END OF addListener anonymous function
I am trying to send a message from a content script to my background script. When the background receives the message it sends data back to the content script in the callback.
My popup also has a listener for messages from the content script, but does not respond to a message meant for the background script.
Then content is receiving an undefined back from the callback, which I think is caused by the popup receiving the message but not responding.
The reference says:
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.
So surely I should only get the response from my background script.
My content script does this:
function notifyReady() {
chrome.runtime.sendMessage({
type: 'ACTIVITY_HISTORY_READY'
},
function (response) {
console.log(">>>>Response: ", response);
if (response.type == 'HISTORY_DATA') {
processLog(response);
}
});
}
My background script listens like this:
chrome.runtime.onMessage.addListener(function (msg, sender, sendResponse) {
console.log("received " + msg.type);
if (msg.type = 'ACTIVITY_HISTORY_READY' && historyData) {
if (historyData) {
sendResponse({
type: "HISTORY_DATA",
position: historyData.position,
company: historyData.company
});
historyData = '';
} else {
sendResponse({
type: "NO_DATA"
});
}
}
});
And the listener in my popup is:
chrome.runtime.onMessage.addListener(function (msg, sender, sendResponse) {
if (msg.type == 'JOB_DETAILS') {
sendResponse("OK!");
document.getElementById('position').value = msg.position;
document.getElementById('company').value = msg.company;
document.getElementById('url').value = sender.tab.url;
}
});
if (msg.type = 'ACTIVITY_HISTORY_READY' && historyData) {
note that if historyData is falsey you are not sending any response. The else branch of the second if can never be taken.
You should remove historyData from the first if. The popup code has nothing to do with this.
This question already has an answer here:
Clipboard Copy / Paste on Content script (Chrome Extension)
(1 answer)
Closed 6 years ago.
Quick summary
The code below is a file called popup.js. It listens for a click, and sends a message to background.js. Background.js executes another script, and a variable is created.
I somehow need to pass this variable back to popup.js, and continue within the userHasClicked function. The way it is now the response I get is "undefined", and there's nowhere to go from there.
var theParent = document.querySelector("#MENY");
theParent.addEventListener("click", userHasClicked, false);
function userHasClicked(e) {
if (e.target !== e.currentTarget) {
var clickedItem = e.target.id;
chrome.runtime.sendMessage({type: "ResponseType", directive: clickedItem}, function(response) {
console.log(response);
this.close();
});
};
e.stopPropagation();
}
Routine:
User clicks on an option in popup.html
Event("click") -> sendmessage("type of click")
background.js listens for the message, and executes content.js
content.js creates the variable and can send it back to anyone who listens.
The problem:
The variable must come as a response argument to step 2 (within eventloop)
Save the response as variable.
document.execCommand("copy").
Done
This code below is the relevant part of background.js.
chrome.runtime.onMessage.addListener(
function(request, sender, sendResponse) {
switch (request.type) {
case "ResponseType":
var LoggType = request.directive;
console.log(LoggType)
chrome.tabs.executeScript(null, {
code: 'var LoggType = "'+LoggType+'";'
}, function() {
chrome.tabs.executeScript(null, {file:"content.js"});
});
chrome.runtime.onMessage.addListener(function(req, snd, sndRes) {
if (req.type = "LogIsGenerated") {
var Logg = req.directive;
console.log(Logg);
} sndRes({});
});
if (typeof Logg !== "undefined") {
alert("Feedback from content.js received");
sendResponse({type: "FinalVar", directive: Logg});
}
else {
alert("No feedback received");
sendResponse({});
};
break};
return true;
}
);
content.js ends with this line.
chrome.extension.sendMessage({type: "LogIsGenerated", directive: Logg});
Logg is the variable containing the text string I want to add to the clipboard.
At the moment this the code below alerts for No feedback received.
chrome.runtime.onMessage.addListener(function(req, snd, sndRes) {
if (req.type = "LogIsGenerated") {
var Logg = req.directive;
console.log(Logg);
} sndRes({});
});
if (typeof Logg !== "undefined") {
alert("Feedback from content.js received");
sendResponse({type: "FinalVar", directive: Logg});
}
else {
alert("No feedback received");
sendResponse({});
};
Rob W presented a solution here: Background script can write to clipboard in a very simple manner
Worked excellently.
I'm trying to get some user configs from the background page of my chrome extension to the content script (or popup) but I'm having some problems, I think the problem is that chrome.storage.sync.get is async, I tried using callbacks but I also read that callbacks can't return the value so I have no idea how to solve this.
Here's kinda how the code looks:
popup.js:
(function() {
chrome.runtime.sendMessage({
message: "loadconfig"
}, function(response) {
console.log(response);
if (response.status === 'success') {
console.log(response);
} else {
console.log(response.except);
}
});
})();
background.js
(function() {
chrome.runtime.onMessage.addListener(function(request, sender, sendResponse) {
switch (request.message) {
case "loadconfig":
sendResponse(loadStuff());
break;
default:
sendResponse({
reply: null
});
break;
}
});
function loadStuff() {
var to_return_configs = {
blocked_characters: '',
good_post: ''
};
var function_status = 'failed';
var exception = '';
var blocked_characters_parsed, good_post_parsed;
try {
var to_get = ["blocked_characters_saved", "good_post_saved"];
chrome.storage.sync.get(to_get, function(result) {
to_get.forEach(function(got) {
if (got === "good_post_saved") {
to_return_configs.good_post = result[got];
}
if (got === "blocked_characters_saved") {
to_return_configs.blocked_characters = result[got];
}
});
});
exception = '';
function_status = 'success';
} catch (err) {
exception = String(err);
function_status = 'failed';
}
var to_return = {
status: function_status,
configs: to_return_configs,
except: (exception)
};
return to_return;
}
})();
The problem here is that when I'm looking at the popup.js console, "blocked_characters" and "good_post" are both empty.
How can I solve this?
You do not need Message API for communication between Popup and Background. Popup in chrome extension can directly call methods of Background .
You can do something like this
BG = chrome.extension.getBackgroundPage();
And then you can call BG.loadStuff() in your popup js.
From within loadStuff, you can pass a callback which can return data to you. So it should look like
BG.loadStuff(function(items) {
console.log(items);
});
background.js
function loadStuff(cb) {
chrome.storage.sync.get(null, function(superObj) {
cb.call(null, superObj);
});
}
For more understanding, read these
http://blog.papersapp.com/chrome-development-parent-and-child-windows/
https://stackoverflow.com/a/17276475/816213
https://stackoverflow.com/a/17378016/816213
sendResponse(function) becomes invalid when the event listener returns, unless you return true from the event listener to indicate you wish to send a response asynchronously (this will keep the message channel open to the other end until sendResponse is called). See the reference: onMessage.
Because sendResponse is called asynchronously in chrome.storage.sync.get's callback, you need to return true from the onMessage listener to prevent the function from being invalidated. Code similar is Like:
chrome.runtime.onMessage.addListener(function(request, sender, sendResponse) {
if (request.message === 'loadconfig') {
sendResponse(loadStuff());
return true;
}
return false;
});
I'm trying to build a chrome extension that involve sending request for data from popup script to Content script (through background script) analyze the request on the content script side and send back a response (again through the background script).
The popup script code:
chrome.runtime.sendMessage({action:"getLanguages",data:"hi hi",}, function(response) {
document.write(response.msg);
});
The background script:
chrome.runtime.onMessage.addListener(
function(request, sender, sendResponse) {
var returnedLangs;
if (request.action == "getLanguages"){
returnedLangs = getLangs();
alert("got langs " + returnedLangs);
//sendResponse({msg: "goodbye"});
}
});
function getLangs() {
var langs;
chrome.tabs.query({active: true, currentWindow: true}, function(tabs) {
chrome.tabs.sendMessage(tabs[0].id, {action: "getLanguages"}, function(response) {
langs = response.langs;
alert(langs);
return langs;
});
});
}
The content script code:
chrome.extension.onMessage.addListener( function(request, sender, sendResponse)
{
getLanguages(sendResponse,sendBackLangs);
});
function getLanguages(sendResponse,callback) {
var acceptLangs = [];
chrome.i18n.getAcceptLanguages(function(langs) {
langs.forEach( function(lang) {
acceptLangs.push(lang);
});
callback(sendResponse,acceptLangs);
});
}
function sendBackLangs(sendResponse, acceptedLangs) {
sendResponse({langs: "acceptedLangs"});
}
With the current content script code no response is sent back to the background but if i do sendResponse directly from the chrome.extension.onMessage... function the response is arriving back to the background.
Can anyone help me understand what causes ths behavior and how can i fix it ?
chrome.tabs.sendMessage(tabs[0].id, {action: "getLanguages"}, function(response) {
langs = response.langs;
alert(langs);
return langs; // <---- Return where?
});
See the above snippet. You can't return a value from an asyncronous callback. In fact, this code block just finishes before the inner function executes and getLangs() returns undefined.
What you want to do, however, is doable. You can pass the sendResponse function into the callback to be called later; you just need to indicate to Chrome that you will call it later.
chrome.runtime.onMessage.addListener(
function(request, sender, sendResponse) {
if (request.action == "getLanguages"){
getLangs(sendResponse); // Pass the callback
return true; // Indicates to Chrome to keep the message channel open
}
});
function getLangs(callback) {
var langs;
chrome.tabs.query(
{active: true, currentWindow: true},
function(tabs) {
chrome.tabs.sendMessage(
tabs[0].id,
{action: "getLanguages"},
function(response) {
// Here it sends it to the content script
callback(response.langs);
}
);
}
);
}
That said, there's absolutely no reason to route the request through background. Just do the tabs.query directly in the popup.