Notification not shown in Chrome extension using Mozilla's webextension-polyfill - google-chrome-extension

I just started with developing a Firefox add-on. It works fine in Firefox so I'd like to make it "compatible" to a Coogle Chrome extension.
For this I inject Mozilla webextension-polyfill and basically the add-on also runs in Chrome. There is one thing however I can't get to work...
In Firefox a notifcation is shown to the user if the content script sends a message which is received by the background script. Running this in Chrome results in the following exception:
Uncaught (in promise)
{message: "The message port closed before a response was received."}
callbackArgs # VM18 browser-polyfill.js:630
sendResponseAndClearCallback # VM29 extensions::messaging:417
disconnectListener # VM29 extensions::messaging:441
EventImpl.dispatchToListener # VM19 extensions::event_bindings:403
publicClassPrototype.(anonymous function) # VM25 extensions::utils:138
EventImpl.dispatch_ # VM19 extensions::event_bindings:387
EventImpl.dispatch # VM19 extensions::event_bindings:409
publicClassPrototype.(anonymous function) # VM25 extensions::utils:138
dispatchOnDisconnect # VM29 extensions::messaging:378
I can tell that this comes from the webextension-polyfill but I cannot find a way so that the notification is also shown in Chrome.
Here are the relevant code snippets...
manifest.json
{
"manifest_version": 2,
// ...
"background": {
"scripts": [
"lib/browser-polyfill.js",
"background-script.js"
],
"persistent": false
},
"options_ui": {
"page": "settings/options.html"
}
}
background-script.js
function notify(message) {
if (message.copied) {
browser.notifications.create({
"type": "basic",
"title": "Notifaction title",
"message": "Hello, world!"
});
}
}
browser.browserAction.onClicked.addListener(() => {
browser.tabs.executeScript({file: "lib/browser-polyfill.js"});
browser.tabs.executeScript({file: "content-script.js"});
});
browser.runtime.onMessage.addListener(notify);
content-script.js
browser.storage.local.get({elementId: ""})
.then(() => {
browser.runtime.sendMessage({copied: true});
});

There are two problems here...
Issue 1
The onMessage handler needs a return true. Only then the polyfill seems to be able to handle the messages correctly.
Issue 2
This seems to look like a bug in the polyfill. In Chrome the iconUrl option is required when creating a notification whereas it is optional in Firefox.
If I apply these two things the notification works in Firefox and Chrome.

Related

How can I modify response headers using webRequest.onHeadersRecieved?

I'm writing an extension that gives a Chrome (and Firefox) user the ability to selectively modify response headers. Mainly it's for QA people to test that the front-end handles error modes properly (e.g. if a given route returns a 500, then the correct dialog displays).
This implementation uses the Chrome webRequest API.I know that the extension is loading, and I can block requests with listener1. However, listener2 does not behave as expected, as it neither modifies the status code nor does it add the josh header. I have verified this in Chrome's network monitor tab. Both filters work, as far as it goes (I was testing with an explicit https filter because I know the webRequest API doesn't work with file:// urls.)
As part of my troubleshooting process, I've disabled all other Chrome extensions, so I'm pretty sure nothing else is modifying headers.
let count = 0;
function listener1(details){
return {cancel: true};
}
function listener2(details){
console.log(count++, details);
if (!details) return;
for (let i = 0; i < details.responseHeaders.length; i++){
if (details.responseHeaders[i].name === 'status'){
details.responseHeaders[i].value = '500';
break;
}
}
details.responseHeaders.push({name: 'josh', value: 'count: ' + count});
// Response is of type "Blocking Response"
return {responseHeaders: details.responseHeaders};
}
const filter1 = '<all_urls>';
const filter2 = 'https://www.google.com/'
chrome.webRequest.onHeadersReceived.addListener(listener2, { urls: [filter2] }, ['blocking', 'responseHeaders']);
Here is my manifest:
{
"name": "Server Error Simulation Extension",
"description" : "Base Level Extension",
"version": "1.0",
"manifest_version": 2,
"browser_action": {
"default_popup": "popup.html",
"default_icon": "logo.png"
},
"background": {
"persistent": true,
"scripts": ["background.js"]
},
"permissions": [
"activeTab",
"storage",
"webRequest",
"webRequestBlocking",
"*://*.com/"
]
}
Note that I'm only wanting to modify headers, so this Chrome bug about modifying response bodies doesn't apply. Also, Firefox copied this API from Chrome so I've tagged Firefox as well.
Partial answer: HTTP status is not a header, according to the RFC 2616 which defines HTTP 1.1. Status is it's own thing, called a Status-Line. And it does look different, textually, since it doesn't have a key: prefix unlike everything else in the header.
As for why my custom header doesn't show up in network monitor, I'm not sure. Maybe it's a bug in the devtools, or something else.
In any event, whether or not its officially a header, the Chrome webRequest API exposes it as a "status" header, and I believe it should support modification in a way consistent with other headers.

chrome-extension: How to detect and retrieve url from tab when redirect happens?

Just started on chrome - extension development. I'm trying to retrieve the url from a newly created tab but my expected url comes from the server as response with 302 status code and browser redirects to it. I tried with chrome.tabs.onUpdated.addListener() but its not detecting the URL with 302. I searched in chrome.tabs API and i don't see any events for redirect. What is the best way to detect if any redirection is happening in the tab and get the url from that object?
My use case is, when user clicks on one button, it opens a new tab with a specific url and since this url is SSO protected which will go through SSO dance and finally goes to target page.During SSO dance there are 3 redirects happening before serving the target page and i'm trying to detect and retrieve one of the url in the redirect process.
P.S: This is my first question and i'm excited to be part of this community. Thanks in advance.
Update1: I tried with chrome.webRequest.onBeforeRedirect.addListener
manifest.json
{
"name": "CatBlock",
"version": "1.0",
"description": "I can't has cheezburger!",
"permissions": ["alarms", "webRequest", "webRequestBlocking", "activeTab", "tabs",
"https://*/*"],
"background": {
"scripts": ["background.js"]
},
"manifest_version": 2
}
background.js
chrome.webRequest.onBeforeRequest.addListener(
function(info) {
console.log("Cat intercepteddd: " + info.url);
});
chrome.webRequest.onBeforeRedirect.addListener(
function(info) {
console.log("Cat intercepteddd: " + info.url);
});
chrome.webRequest.onResponseStarted.addListener(
function(info) {
console.log("Cat intercepteddd: " + info.url);
});
const responseListener = function(details) {
const headers = details.responseHeaders;
for (let i = 0; i < headers.length; i++) {
if (headers[i].name == "location") {
console.log("redirectURL:" + headers[i].value);
}
}
return { responseHeaders: newHeaders};
};
chrome.webRequest.onHeadersReceived.addListener(
responseListener,{urls: [URL_TO_DETECT]}, ["blocking", "responseHeaders"]);
Usually 303 redirects will be caught on the response headers, use the above code to get the redirect url from response header, make sure to have the code in background.js

Getting basic message passing to work in Chrome extension

I'm trying to get basic message passing to work in a Chrome extension. There are a number of related questions on SF on this (most notably here: Basic google chrome extension message passing not working), but unfortunately the advice there seems not to work for me.
Here's my manifest.json file:
{
"name": "Steve Screenshot",
"description": "Do something",
"version": "0.1",
"manifest_version": 2,
"content_scripts": [
{
"matches" : ["*://*/*"],
"js": ["simple.js"]
}
]
}
And my simple.js file:
chrome.runtime.onMessage.addListener(function(request, sender, callback) {
console.log('message received');
});
// Invoke it!!!
chrome.runtime.sendMessage({
msg: 'whatever'
}, function(response) {
console.log('message sent');
});
At runtime, the console log shows the "message sent" message but NOT the "message received" message. To the best of my understanding, I am ?correctly? invoking chrome.runtime.onMessage/sendMessage. Can anyone comment?
Thanks,
--Steve
The problem is that you are trying to use chrome.runtime.sendMessage to send a message to a content script. You cannot send messages to content scripts using runtime.sendMessage, you must use tabs.sendMessage. (Read about that here).
A good setup for message passing is to use a background page as sort of a 'router' for your messages. When you need to pass a message to content scripts you do so from the background page using the tabs api. For a simple test, you could add a tabs.query call in the background script to send a message to ALL content scripts like so:
chrome.tabs.query({}, function(tabs) {
for(var i = 0; i < tabs.length; i++) {
chrome.tabs.sendMessage(tabs[i].id, {msg: 'whatever'});
}
}
To send messages to individual content scripts will require some additional logic but is very doable (one way is to maintain some associative queue structure in the background page).
Also, note that you can use chrome.runtime.sendMessage from inside a content script, but this will send a message to event listeners in your extension that are not inside content scripts. (So you can send a message to the background page using this method)

Message Passing Example From Chrome Extensions

I'm using the example from the Google tutorial and finding it difficult to pass a simple message to the content script from the popup.
Can you provide some suggestions on how to pass a simple message and view it either in the console log or alert?
manifest.json
{
"manifest_version": 2,
"name": "msg-test",
"description": "message test",
"version": "1.0",
"browser_action": {
"default_icon": "icon.png",
"default_popup": "popup.html"
},
"background": {
"scripts": ["background.js"],
"persistent": true
},
"content_scripts": [{
"matches": ["http://*/*","http://www.site.com/*"],
"js": ["content.js"],
"run_at": "document_end"
}],
"permissions": [
"tabs",
"http://*/*"
]
}
background.js
chrome.runtime.onConnect.addListener(function(port){
port.postMessage({greeting:"hello"});
});
content.js
var port = chrome.runtime.connect({name:"content"});
port.onMessage.addListener(function(message,sender){
if(message.greeting === "hello"){
alert(message.greeting);
}
});
popup.js
window.onload = function() {
document.getElementById('btn2').onclick = function() {
alert("button 2 was clicked");
};
document.getElementById('btn1').onclick = function() {
alert("button 1 was clicked");
};
}
*Note: In this example the content script will fire when the page matches manifest.json and the alert box will show.
First, I wouldn't message pass between your popup and your content script. I would message pass between your Background page and your content scripts. Your popup page should only be used to show some ui to interact with your app.
With that being said, I will show you the way to pass messages between your background and your content script.
In your content script:
//This line opens up a long-lived connection to your background page.
var port = chrome.runtime.connect({name:"mycontentscript"});
port.onMessage.addListener(function(message,sender){
if(message.greeting === "hello"){
alert(message.greeting);
}
});
In your background page(possibly your popup? but I don't recommend it)
chrome.runtime.onConnect.addListener(function(port){
port.postMessage({greeting:"hello"});
});
Here is the sequence of events that will take place:
Your application will inject your content script into the page
Your content script will open up a port to communicate with the background script.
Your background script will be notified that a port was open, allowing it to send a message to it, or attach a message listener to it.
In the background script or the content script, you can listen for messages by using port.onMessage.addListener(). provided that port is in scope. Using ports is much easier to grasp and allows for simple, two way communication!
Edit:
If you would like to pass messages to your background page from your popup script, use the exact same method:
var port = chrome.runtime.connect({name: "popup-port"});
port.postMessage({status:"poppedup"});
Edit 2:
To navigate your user to a new page, do this:
function navigateToPage(url){
chrome.tabs.query({url: url}, function(tabs) {
var tab = tabs[0];
return tab ? chrome.tabs.update(tab.id, {active:true}) : chrome.tabs.create({url: url});
});
}
});
What this function does is, it checks to see if there is a tab with the url you want to go to, if there is, switch to it, else, create a tab with that url and navigate to it.

http requests not working chrome extension

I am developing a chrome extension that requires corss domain XHR. So I need to make get requests to a server and get some text out of it. I am currently loading the unpacked extension from my computer. The script doesn't seem to be working.
Here is my manifest.json:
{
"name": "My extension",
"version": "1.1",
"manifest_version": 2,
"description": "Testing http requests",
"browser_action": {
"default_icon": "icon.png",
"default_popup": "popup.html"
},
"permissions": [
"http://*/"
]
}
And here is the script that performs the get request (from this tutorial):
function showresponse(){
var query = document.getElementById("query").value;
var url = "http://blah.com/search.php?term="+query;
var xhr = new XMLHttpRequest();
xhr.open("GET", url, true);
xhr.onreadystatechange = function() {
if (xhr.readyState == 4) {
console.log("hello world");
document.getElementById("container").innerHTML = xhr.responseText;
}
}
xhr.send(null);
}
The id's etc are set according to my popup.html file and that's set up correctly and it includes this js file containing the showresponse() function definition.
I also tried packaging my extension to get a myextension.crx file after reading this question and I tried opening the file in my browser, but chrome doesn't allow installing the extension from localhost or unknown servers for security reasons I suppose.
My question is how do I make a cross domain XHR in a chrome extension?
Also the response from the get request to the server is actually an html document and I need to filter some text out of the returned html tags. As I am making a query to a php script, can I receive and therefore play around with the html output if I make a get request in javascript?
How do I go about acheiving this?

Resources