I'm learning how to make chrome extensions. I have a content script that will obtain some data and I want to pass them to the popup.html page to display them on the popup DOM. I've read about the message passing in the Chrome documentation but I'm unable to make it work. Can anyone help me?
My code:
content script file: main.js
(function($){
$(document).ready(function(){
console.log('Extension Started!');
var el = $(document).find('#stories_tray');
var child = el.find('._827c');
$.each(child, function(i){
var div = $(child[i])
.find('._7h4p')
.attr('data-onkeypress');
var d = JSON.parse(div);
if( typeof d[0].a != 'undefined' ){
console.log(d[0].a[0].preloadImageURIs[0]);
var l = d[0].a[0].preloadImageURIs[0];
chrome.runtime.sendMessage({imageURIs: l}, function(response) {
console.log(response.farewell);
});
}
});
});
}(jQuery));
popup javascript file: popup.js
// window.onload = function(){
// $('.spinner-grow').delay('300')
// .css({'transition':'ease-out','display':'none'});
// }
(function($){
$(document).ready(function(){
console.log('Extension Started!');
chrome.runtime.onMessage.addListner(function(request, sender, sendResponse){
console.log(sender.tab ? "from a content script:" + sender.tab.url : "from the extension");
console.log(request.imageURIs);
sendResponse({farwell: "ok"});
});
});
}(jQuery));
Maybe I'm doing something wrong with the code.
I get this errors in the console:
// content script console error
Error handling response: TypeError: Cannot read property 'farewell' of undefined
//popup.js console error
jQuery.Deferred exception: chrome.runtime.onMessage.addListner is not a function TypeError: chrome.runtime.onMessage.addListner is not a function:
Uncaught TypeError: chrome.runtime.onMessage.addListner is not a function
UPDATE
I've managed how to pass the message from the content script to the popup.js but I need a way to hold the message until the user click on the extension icon. How can I achieve also this?
In general, it will not work to send a message from a content script to a popup unless you know the popup is open: the popup does not exist until you open it.
Given this, it will likely be most decoupled to have your content script send its message to a persistent background (which is the default btw) and serve as the repository for your messages until the popup requests them.
background.js
const messageQueue = [];
chrome.runtime.onMessage.addListener((message, sender, sendResponse) => {
// Arbitrary string allowing the background to distinguish
// message types. You might also be able to determine this
// from the `sender`.
if (message.type === 'from_content_script') {
messageQueue.push(message);
} else if (message.type === 'from_popup') {
sendResponse(messageQueue);
}
});
Now from content script, send chrome.runtime.sendMessage({imageURIs: l, type: 'from_content_script'}... and from the popup send
chrome.runtime.sendMessage({type: 'from_popup'}, (response) => {
// do stuff with response (which will be the value of messageQueue
// sent from background.js).
});
Of course the strings 'from_popup' and 'from_content_script' mean nothing; they are just there for clarity.
If you need the popup to initiate the flow, you will need to:
send a message from the popup
to the background, to send a message to the content scripts
which should respond to the background
which should respond to the popup
Chrome runtime have not onMessage method please see this link,hope this will hep you
Related
I'm building a Chrome extension and want to access a background script global variable from the content script, so I'm using the browser runtime API to pass messages between the content and background scripts.
This results in "Uncaught ReferenceError: browser is not defined"
Image of error message
content script:
//alert for debugging purposes
alert("content.js is loaded.");
document.body.onLoad = sending;
function sending(){
//alert for debugging purposes
alert("content.js is loaded, and sending() has been called.");
//sends a Promise to the receiver
document.addEventListener('DOMContentLoaded', function() {
browser.runtime.sendMessage({
type: "getText"
}).then(function(message) {
alert("Value of text is: ", message.result);
});
}
background script:
//alert for debugging purposes
alert("background.js is loaded.");
//'text' is the variable I'm trying to access from content script.
var text = prompt("Why are you here on this page?","E.g: To watch Daniel Schiffman's first video.");
//add a listener for the message sent from the content script
browser.runtime.onMessage.addListener(function(request, sender, sendResponse) {
if (request.type == "getText") {
//alert for debugging purposes
alert("browser.runtime is working from background.js");
//send the response to content script
sendResponse({result: text});
}
});
Notes
I have tried replacing all appearances of browser.* with chrome.*. The error message disappears, but the alerts inside the browser.runtime functions don't appear when I refresh the page, meaning the sendMessage() and onMessage() did not execute.
Seeing other advice from Stack Overflow, I also tried this: var browser = browser || chrome;, which is essentially the same thing as above.
Both background.js and content.js have been added to my manifest.json
I am building a Chrome extension and am trying to send a message to my event background.js page from inside of chrome.tabs.captureVisibleTab(). For some reason the message will not send to my content script...
chrome.runtime.onMessage.addListener(
function(request, sender, sendResponse) {
var responseObj = new Object();
if(request.screenshotRequest == true){
chrome.tabs.captureVisibleTab({ format: "png"}, function(dataUrl){
responseObj.screenshotRequest = dataUrl;
alert(sendResponse);
sendResponse(responseObj);
});
}
});
My manifest.json allows for the correct permissions to allow this message passing to happen. When I try and pass messages using sendResponse() outside of the chrome.tabs.captureVisibleTab() the message passes correctly and I can access it from the content script. Any idea why this isn't passing?
UPDATE: alert(sendResponse); proves that chrome.tabs.captureVisibleTabs() has access to sendResponse because the object is correctly displayed in the alert message.
I dug into the console of the unpacked extension (apparently it is different from the regular extension's console: Where to read console messages from background.js in a Chrome extension?) and it said that chrome.runtime.onMessage.addListener needed to return true if sendResponse was called inside of a callback in the function. It worked!
I'm trying to extend some of the handling of messaging between my background process and content scripts. In the normal course of things my background process sends a message via postMessage() and the content script replies via another channel with a response. However I would like to now extend the background process to fall-back to something else if the content script can't find a valid thing on the page. It's when looking at this I discovered a problem when sending a message to blank or system pages. As the tabs don't have content scripts loaded there is nothing to receive the posted message. This generates warnings in the console logs but otherwise no ill effects. However:
// Called when the user clicks on the browser action.
//
// When clicked we send a message to the current active tab's
// content script. It will then use heuristics to decide which text
// area to spawn an edit request for.
chrome.browserAction.onClicked.addListener(function(tab) {
var find_msg = {
msg: "find_edit"
};
try {
// sometimes there is no tab to talk to
var tab_port = chrome.tabs.connect(tab.id);
tab_port.postMessage(find_msg);
updateUserFeedback("sent request to content script", "green");
} catch (err) {
if (settings.get("enable_foreground")) {
handleForegroundMessage(msg);
} else {
updateUserFeedback("no text area listener on this page", "red");
}
}
});
Doesn't work. I would expect the connect or the postMessage to throw an error I can trap, however the console log is filled with error messages including:
Port: Could not establish connection. Receiving end does not exist.
But I do not end up in the catch statement.
In the end I couldn't do it with connect, I had to use the one shot sendMessage() which has a call-back function when the response comes in. That can then be interrogated for success and the state of lastError. The code now looks like this:
// Called when the user clicks on the browser action.
//
// When clicked we send a message to the current active tab's
// content script. It will then use heuristics to decide which text
// area to spawn an edit request for.
chrome.browserAction.onClicked.addListener(function(tab) {
var find_msg = {
msg: "find_edit"
};
// sometimes there is no content script to talk to which we need to detect
console.log("sending find_edit message");
chrome.tabs.sendMessage(tab.id, find_msg, function(response) {
console.log("sendMessage: "+response);
if (chrome.runtime.lastError && settings.get("enable_foreground")) {
handleForegroundMessage();
} else {
updateUserFeedback("sent request to content script", "green");
}
});
});
I've checked numerous questions regarding message passing in a Chrome extension but I haven't found much specifically relating to this.
I'm using the chrome.devtools* APIs and I'm having trouble sending messages between content scripts when the developer toolbar is docked. Everything works fine when it is not docked i.e. floating.
Here's a brief example of what I'm doing.
devtools.js
chrome.devtools.panels.create("myExtension", "img/icon.png",
"/panel.html", function(extensionPanel) {
var myData; //this variable gets manipulated before a user
//clicks on the panel
extensionPanel.onShown.addListener(function(panelWindow) {
chrome.extension.sendMessage({greeting: "update my data", data: myData}, function(response) {});
});
});
Then in my background script (eventPage.js) I listen for this message and pass it on to panel.js
chrome.extension.onMessage.addListener(function(request, sender, sendResponse) {
if (request.greeting == "update my data"){
chrome.tabs.sendMessage(sender.tab.id, {greeting: "show data", showResource: request.data}, function(response) {});
}
});
And then finally I listen for the 'show data' call in my panel.js (which is loaded from panel.html)
chrome.extension.onMessage.addListener(function(request, sender, sendResponse) {
if (request.greeting == "show data") {
//do stuff with my data here
});
Essentially, I need to pass messages between devtools.js to panel.js but the only way to do it is by using the background script as an intermediary.
devtools.js -> background.js -> panel.js.
This actually works fine as long as the dev tools panel is not docked to the window. When it is docked I get an error because sendMessage() won't except a tab id of -1, which is what sender.tab.id equals when the dev tools are docked to the window. I also tried using chrome.tabs.connect - long lasting connections - but ran into the same problem.
I also just found out recently how to do this.
A technique used by "Google Pagespeed" is to get the tab id of the inspected window and pass it back and forth between the extension and background using a port.
Whenever you want to send the extension a message, you look for the tab id and get its port.
panel.js
// get the inspected windows tab id from the panel
var tabId = chrome.devtools.inspectedWindow.tabId;
// create the port
var port = chrome.extension.connect({name: 'connection'});
// keep track of the port and tab id on the background by
// sending the inspected windows tab id
port.postMessage(['connect', tabId])
eventPage.js
var activeListeners = {};
chrome.extension.onConnect.addListener(function(port) {
port.onMessage.addListener(function(message) {
if (message[0] === 'connect') {
// get the tab id
var tabId = message[1];
// save the reference to the port
activeListeners[tabId] = port;
// make sure we clean up after disconnecting (closing the panel)
activeListeners[tabId].onDisconnect.addListener(function() {
delete activeListeners[tabId];
});
}
});
});
This is not a very thorough explanation but I hope you get the point.
I am using the following code to send a sample message from the content script:
function sendjsontrue()
{
var arrCars = new Array("Toyota", "Mercedes", "BMW");
jsonStr = JSON.stringify(arrCars);
chrome.extension.sendRequest({greeting: jsonStr}, function(response){
console.log(response.farewell);
});
console.log("Message with header=greeting has been sent...");
}
The code in my popup.html to handle the message sent by content script, is given below--
<script>
chrome.extension.onRequest.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"});
else
sendResponse({notfarewell:"Not goodbye"}); .
});
</script>
The error that I am getting is given below--
Port error: Could not establish connection. Receiving end does not exist.
chrome/EventBindings:184Error in event handler for 'undefined': TypeError: Cannot read property 'farewell' of undefined
at chrome-extension://nkmkgjckmjekpbghhildcfdlnbjeglkd/obtainformdata.js:81:25
at chrome/RendererExtensionBindings:238:13
at [object Object].dispatch (chrome/EventBindings:182:28)
at Object.<anonymous> (chrome/RendererExtensionBindings:134:27)
What have I done wrong here?
You should write the receiver code in a background page, instead of at the popup. Content scripts communicate with the background page, by default.
If you're in need of decent documentation, have a look at:
Chrome extension methods
Background pages
I was having same problem when i was listening the request from option.html . I found following solution to that :
You can also write receiver code in a popup page but the limitation is that your request is listened by chrome.extension.onRequest.addListener() only when popup.html is active .
So if u want to listen the request all the time you have to write
chrome.extension.onRequest.addListener()
in background page and register this page to manifest.json .