Send one Chrome Extension desktop notification at a time - google-chrome-extension

I am still learning how to create Chrome extensions, but my issue is with the desktop notifications. I am able to trigger the notifications, but when that happens for example I trigger desktop notification for content script 1. The desktop notification also triggers for content script 2. How do I make it so that they do not trigger at the same time, and only when they are called?
Background page
chrome.extension.onRequest.addListener(function(request, sender, sendResponse) {
// Create a simple text notification
var notifyWinner = webkitNotifications.createNotification('48.png', 'Notification', request.winnerMessage);
notifyWinner.show();
setTimeout(function(){ notifyWinner.cancel(); },10000);
});
chrome.extension.onRequest.addListener(function(request, sender, sendResponse) {
// Create a simple text notification
var notifyVideo = webkitNotifications.createNotification('48.png', 'Notification', request.videoMessage);
notifyVideo.show();
setTimeout(function(){ notifyVideo.cancel(); },10000);
});
Content Script 1
chrome.extension.sendRequest({winnerMessage: "You won!!!"}, function(response) {
return response;
});
Content Script 2
chrome.extension.sendRequest({videoMessage: "There is a video" + videoURL}, function(response) {
return response;
});

You can simplify the code to only use one onRequest listener and then it will stop displaying duplicate notifications.
background_page
chrome.extension.onRequest.addListener(function(request, sender, sendResponse) {
// Create a simple text notification
var notify = webkitNotifications.createNotification('48.png', 'Notification', request.message);
notify.show();
setTimeout(function(){ notify.cancel(); },10000);
});
content_script
chrome.extension.sendRequest({
message: "There is a video" + videoURL}, // Change the message here as needed.
function(response) {
return response;
});

Related

How to set input on chrome extension to text from dom?

I am trying to get text from the web page on click into the input on my chrome extension.
Content script
document.addEventListener('click', function (e) {
let target = e.target
let text = target.textContent || target.innerText;
chrome.runtime.sendMessage({ text }, function (response) {
console.log(response.farewell);
});
}, false);
and background script
chrome.runtime.onMessage.addListener(
function (request, sender, sendResponse) {
document.getElementById("userinput").value = "My value";
sendResponse({ farewell: request.text });
}
);
My popup.html just has id of 'userinput' and i am getting unchecked runtime.lastError: The message port closed before a response was received. Error

How can I receive data of an external API with Chrome extension? [duplicate]

I am trying to pass messages between content script and the extension
Here is what I have in content-script
chrome.runtime.sendMessage({type: "getUrls"}, function(response) {
console.log(response)
});
And in the background script I have
chrome.runtime.onMessage.addListener(
function(request, sender, sendResponse) {
if (request.type == "getUrls"){
getUrls(request, sender, sendResponse)
}
});
function getUrls(request, sender, sendResponse){
var resp = sendResponse;
$.ajax({
url: "http://localhost:3000/urls",
method: 'GET',
success: function(d){
resp({urls: d})
}
});
}
Now if I send the response before the ajax call in the getUrls function, the response is sent successfully, but in the success method of the ajax call when I send the response it doesn't send it, when I go into debugging I can see that the port is null inside the code for sendResponse function.
From the documentation for chrome.runtime.onMessage.addListener:
This 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).
So you just need to add return true; after the call to getUrls to indicate that you'll call the response function asynchronously.
The accepted answer is correct, I just wanted to add sample code that simplifies this.
The problem is that the API (in my view) is not well designed because it forces us developers to know if a particular message will be handled async or not. If you handle many different messages this becomes an impossible task because you never know if deep down some function a passed-in sendResponse will be called async or not.
Consider this:
chrome.extension.onMessage.addListener(function (request, sender, sendResponseParam) {
if (request.method == "method1") {
handleMethod1(sendResponse);
}
How can I know if deep down handleMethod1 the call will be async or not? How can someone that modifies handleMethod1 knows that it will break a caller by introducing something async?
My solution is this:
chrome.extension.onMessage.addListener(function (request, sender, sendResponseParam) {
var responseStatus = { bCalled: false };
function sendResponse(obj) { //dummy wrapper to deal with exceptions and detect async
try {
sendResponseParam(obj);
} catch (e) {
//error handling
}
responseStatus.bCalled= true;
}
if (request.method == "method1") {
handleMethod1(sendResponse);
}
else if (request.method == "method2") {
handleMethod2(sendResponse);
}
...
if (!responseStatus.bCalled) { //if its set, the call wasn't async, else it is.
return true;
}
});
This automatically handles the return value, regardless of how you choose to handle the message. Note that this assumes that you never forget to call the response function. Also note that chromium could have automated this for us, I don't see why they didn't.
You can use my library https://github.com/lawlietmester/webextension to make this work in both Chrome and FF with Firefox way without callbacks.
Your code will look like:
Browser.runtime.onMessage.addListener( request => new Promise( resolve => {
if( !request || typeof request !== 'object' || request.type !== "getUrls" ) return;
$.ajax({
'url': "http://localhost:3000/urls",
'method': 'GET'
}).then( urls => { resolve({ urls }); });
}) );

Chrome Run-time API callback called immediately [duplicate]

I am trying to pass messages between content script and the extension
Here is what I have in content-script
chrome.runtime.sendMessage({type: "getUrls"}, function(response) {
console.log(response)
});
And in the background script I have
chrome.runtime.onMessage.addListener(
function(request, sender, sendResponse) {
if (request.type == "getUrls"){
getUrls(request, sender, sendResponse)
}
});
function getUrls(request, sender, sendResponse){
var resp = sendResponse;
$.ajax({
url: "http://localhost:3000/urls",
method: 'GET',
success: function(d){
resp({urls: d})
}
});
}
Now if I send the response before the ajax call in the getUrls function, the response is sent successfully, but in the success method of the ajax call when I send the response it doesn't send it, when I go into debugging I can see that the port is null inside the code for sendResponse function.
From the documentation for chrome.runtime.onMessage.addListener:
This 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).
So you just need to add return true; after the call to getUrls to indicate that you'll call the response function asynchronously.
The accepted answer is correct, I just wanted to add sample code that simplifies this.
The problem is that the API (in my view) is not well designed because it forces us developers to know if a particular message will be handled async or not. If you handle many different messages this becomes an impossible task because you never know if deep down some function a passed-in sendResponse will be called async or not.
Consider this:
chrome.extension.onMessage.addListener(function (request, sender, sendResponseParam) {
if (request.method == "method1") {
handleMethod1(sendResponse);
}
How can I know if deep down handleMethod1 the call will be async or not? How can someone that modifies handleMethod1 knows that it will break a caller by introducing something async?
My solution is this:
chrome.extension.onMessage.addListener(function (request, sender, sendResponseParam) {
var responseStatus = { bCalled: false };
function sendResponse(obj) { //dummy wrapper to deal with exceptions and detect async
try {
sendResponseParam(obj);
} catch (e) {
//error handling
}
responseStatus.bCalled= true;
}
if (request.method == "method1") {
handleMethod1(sendResponse);
}
else if (request.method == "method2") {
handleMethod2(sendResponse);
}
...
if (!responseStatus.bCalled) { //if its set, the call wasn't async, else it is.
return true;
}
});
This automatically handles the return value, regardless of how you choose to handle the message. Note that this assumes that you never forget to call the response function. Also note that chromium could have automated this for us, I don't see why they didn't.
You can use my library https://github.com/lawlietmester/webextension to make this work in both Chrome and FF with Firefox way without callbacks.
Your code will look like:
Browser.runtime.onMessage.addListener( request => new Promise( resolve => {
if( !request || typeof request !== 'object' || request.type !== "getUrls" ) return;
$.ajax({
'url': "http://localhost:3000/urls",
'method': 'GET'
}).then( urls => { resolve({ urls }); });
}) );

How to send responses from onMessageExternal Listener?

I am sending a message from a content script to my extension using chrome.runtime.sendMessage.
Content Script:
chrome.runtime.sendMessage(extensionId, {
"some" : "request"
}
},
function(response) {
alert("got response");
}
);
The receiving part in the background script looks like that:
Background Script A
chrome.runtime.onMessageExternal.addListener(function(request, sender, sendResponse) {
console.log("send response");
sendResponse({
"some" : "response"
});
});
This works well: send response is logged to the console of the background page and I receive the alert got response from the content script.
I then introduced some asynchronous JavaScript to the listener:
Background Script B
chrome.runtime.onMessageExternal.addListener(function(request, sender, sendResponse) {
window.crypto.subtle.generateKey({
name: "RSASSA-PKCS1-v1_5",
modulusLength: 2048,
publicExponent: new Uint8Array([1, 0, 1]),
hash: {
name: "SHA-256"
}
}, false, ["sign"]).then(function(keyPair) {
console.log("send response");
sendResponse({
"some" : "response"
});
});
});
here the callback is called regularly (meaning, send response is logged to the console of the background page after completion. The content script however never receives the response (i.e. there is no got response alert).
What am I doing wrong here? Why is the responseCallback called in A but not in B? That's exactly what the callback chain is made for or isn't it?
This seems to be a bug/feature in chrome. Apparently you need to return true, otherwise the message channel will be closed:
https://developer.chrome.com/extensions/runtime.html#event-onMessageExternal
This 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).
Adding return true; to the background script like this solves the issue:
chrome.runtime.onMessageExternal.addListener(function(request, sender, sendResponse) {
window.crypto.subtle.generateKey({
name: "RSASSA-PKCS1-v1_5",
modulusLength: 2048,
publicExponent: new Uint8Array([1, 0, 1]),
hash: {
name: "SHA-256"
}
}, false, ["sign"]).then(function(keyPair) {
console.log("send response");
sendResponse({
"some" : "response"
});
});
return true;
});

How can I receive message from onClick event of web page using chrome extension?

I want to catch click event from injected script on all tabs of chrome browser.
like below method
event.js is main javascript and test.js is injected js.
event.js
chrome.tabs.onUpdated.addListener(function(tabId, changeInfo) {
if (changeInfo.status === 'complete') {
chrome.tabs.executeScript(tabId, { file: 'test.js'}, function(){
console.log('executed');
});
}
});
test.js
var fragment = create('<div onClick="sendMessage to my extension">Hellow</div>');
document.body.insertBefore(fragment, document.body.childNodes[0]);
how can I receive message from other website to my extenstion?
Is it impossible?
Thanks for reading.
Assuming you are properly injecting test.js as a content script (e.g. by declaring the appropriate properties in your manifest.json), you need to make the following modifications:
In event.js add:
chrome.runtime.onMessage.addListener(function(msg, sender) {
console.log('Tab with ID ' + sender.tab.id + ' sent a message:\n' + msg.text);
});
In test.js replace your code with:
var mydiv = document.createElement('div');
mydiv.textContent = 'Hello';
mydiv.addEventListener('click', function() {
chrome.runtime.sendMessage({ text: 'Someone clicked my DIV !' });
});
document.body.insertBefore(mydiv, document.body.childNodes[0]);

Resources