Message passing with chrome.downloads - google-chrome-extension

I am working on a Chrome extension. I have a content script and an event page. From the content script, I send a message using chrome.runtime.sendMessage() to the event page. On the event page, I use onMessage event listener to send back a reponse -- however, I would like to send this reponse AFTER chrome has detected that a file has started downloading.
contentScript.js
window.location.href = download_link; //redirecting to download a file
chrome.runtime.sendMessage({greeting: "hello"}, function(response) {
console.log(response.farewell);
});
eventPage.js
chrome.runtime.onMessage.addListener(
function(request, sender, sendResponse) {
chrome.downloads.onCreated.addListener(function(DownloadItem downloadItem) {
sendResponse({farewell: "goodbye"});
return true;
});
});
Now, I haven't tried chrome.downloads.onCreated listener before, but I'm assuming this is the correct syntax. However, the code is not working, and the console is returning this error:
Error in event handler for (unknown): Cannot read property 'farewell' of undefined
Stack trace: TypeError: Cannot read property 'farewell' of undefined
at chrome-extension://dlkbhmbjncfpnmfgmpbmdfjocjbflmbj/ytmp3.js:59:31
at disconnectListener (extensions::messaging:335:9)
at Function.target.(anonymous function) (extensions::SafeBuiltins:19:14)
at EventImpl.dispatchToListener (extensions::event_bindings:395:22)
at Function.target.(anonymous function) (extensions::SafeBuiltins:19:14)
at Event.publicClass.(anonymous function) [as dispatchToListener] (extensions::utils:65:26)
at EventImpl.dispatch_ (extensions::event_bindings:378:35)
at EventImpl.dispatch (extensions::event_bindings:401:17)
at Function.target.(anonymous function) (extensions::SafeBuiltins:19:14)
at Event.publicClass.(anonymous function) [as dispatch] (extensions::utils:65:26)
I have tried this without the chrome.downloads.onCreated listener and it works, the response is fetched by the content script. I read online that you need to add return true; in order to make it work, but it's not working for me. I suspect it's because of the second event listener, which enters a new scope meaning that sendResponse cannot be called from there -- if that's the case, how do I call the sendResponse function?

You return true; from the wrong place.
The purpose of that return is to say "I have not called sendResponse yet, but I'm going to".
However, you are returning it from inside the onCreated callback - it is too late by then, as right after the listener is added your original onMessage handler terminates. You just have to move the line:
chrome.runtime.onMessage.addListener( function(request, sender, sendResponse) {
chrome.downloads.onCreated.addListener( function(DownloadItem downloadItem) {
sendResponse({farewell: "goodbye"});
});
return true;
});

Related

Error in invocation of runtime.sendMessage (Chrome API)

The goal is to send a message to a content script from a background script after a URL is changed.
Here is my function:
chrome.tabs.onUpdated.addListener(
function (tabId, changeInfo, tab){
if(changeInfo.url && changeInfo.url.includes('https://example')){
chrome.runtime.sendMessage(tabId, {warn: 'message'}, function(resp){})
}
});
But I get an error: Error, in event handler: TypeError: Error in invocation of runtime.sendMessage(optional string extensionId, any message, optional object options, optional function callback): Error at parameter 'options': Unexpected property: 'warn'.
I tried to code according to documentation, but can't find the error
I was getting a similar error until I did something like
chrome.tabs.sendMessage(tabId, {warn: 'message'});
Note: tabs, not runtime method.
I had to leave out the message parameter. Not sure about callback.
You are using the API wrong. sendMessage should receive two parameters. The first one can be an object and the second one would be a callback with the response. This way:
chrome.runtime.sendMessage(
{
message: "this is a message",
},
(response) => {
// ... do something with the response
}
);

Message passing with nested async call in chrome extension fails

This works: Simple message passing with no nested call to chrome API in the onMessage listener.
content_script
chrome.runtime.sendMessage({ message: "what is my windowId?" }, function(response) {
// clearLocalStorage(response.allWindowsId);
windowId = response.windowId;
console.log(windowId);
});
background_script
chrome.runtime.onMessage.addListener(function(request, sender,sendResponse) {
if (request.message === "buttonClick") {
chrome.tabs.reload(sender.tab.id);
sendResponse({message: 'handle button click'});
} else if (request.message === "what is my windowId?") {
sendResponse({
windowId: sender.tab.windowId
});
}
return;
});
This doesnot work: Nested call chrome.windows.getAll in the onMessage listener.
background_script
chrome.runtime.onMessage.addListener(function(request, sender,sendResponse) {
if (request.message === "buttonClick") {
chrome.tabs.reload(sender.tab.id);
sendResponse({message: 'handle button click'});
} else if (request.message === "what is my windowId?") {
// additional code here
chrome.windows.getAll(function(windows) {
sendResponse({
windowId: sender.tab.windowId,
windows: windows
});
});
}
return;
});
I've also tried to make the call chrome.windows.getAll async using chromeExtensionAsync, but no luck yet.
The following is the error message. It seems that the call to window.getAll happens after the function onMessage returns, even though I've marked this function async by the final return; statement.
Error handling response: TypeError: Cannot read property 'windowId' of undefined
Unchecked runtime.lastError: The message port closed before a response was received.
I just published an OSS library that helps with this case: #wranggle/rpc
Take a look at the BrowserExtensionTransport. It includes an example for making remote calls between a content script and the background window.

"Error: Parameter 1 required" when attempting to set a listener on onBeforeRequest

background.js :
chrome.extension.onRequest.addListener(
function(request, sender, sendResponse)
{
console.log(request.filter)
chrome.webRequest.onBeforeRequest.addListener(request.func, request.filter, ["blocking"]);
}
);
content script:
chrome.extension.sendRequest(
{
func: requestInterceptor,
filter: requestFilter
}
);
The line chrome.webRequest.onBeforeRequest.addListener(request.func, request.filter, ["blocking"]); is triggering the following error:
Error in event handler for 'undefined': Error: Parameter 1 is required.
How can I fix it?
On message passing, requests are JSON-serialized.
Functions are not serializable, so they're not sent.
chrome.extension.sendRequest( {
func: requestInterceptor, // Function
filter: requestFilter // Array of strings
});
is received as
{
filter: requestFilter
}
Move the function logic to the background page.
If you want to add a webRequest event listener from the content script, pass all necessary (JSON-serializable) data to the background page using chrome.extension.sendRequest. The receiver (background), also gets a MessageSender type, which can be used to read the tab of the origin (which supplies all necessary information, including the tabId).

Returning a value from a content script

When executing a content script from a popup is there a way for that content script to return a value to the popup where the script was executed.
Referring to Google's Docs, use the following code:
contentscript.js
chrome.runtime.sendMessage({value: "hello"}, null);
popup.html
chrome.runtime.onMessage.addListener(
function myFunc(request, sender, sendResponse) {
doStuffWithValue(request.value);
chrome.runtime.onMessage.removeListener(myFunc); //if you want to stop listening after receiving the message
});

Chrome Extension: Content Script -> Dynamic Function

I would like to call a dynamic function on a Content Script (Chrome Extension). But the common way doesn't work:
chrome.extension.onRequest.addListener(function cs_listener(request, sender, sendResponse) {
[request.action]();
}
request.action is blah. Where function blah() is a....and now it comes...a function!
Error thrown:
Error in event handler for 'undefined': TypeError: object is not a function
Someone got over this? I really don't like to make a switch for every action I need.
You have to use
window[request.action]();
as
[request.action]();
creates an array containing request.action, and tries to call that, which results in the error. window[request.action](); gets the property named request.action from window and calls that.
You also might want to check if the property is defined first:
if(typeof window[request.action] == "function")
window[request.action]();
Another way would be just calling that function from a background page, without sending a request:
chrome.tabs.executeScript(null, {code: "dynamic_function_name()"});

Resources