onBeforeRequest not run every time something is clicked - javascript

Update 1:
Now thanks to Xan I no longer get to the start page, adding ["main_frame", "sub_frame", "script", "xmlhttprequest"].
What is still annoying is, if you reached your profile page and now click on start page (or the f-logo) nothing happens. It seams because this is an ajax call and my extension rewrites the url, no "new" page is loaded and we are stuck.
What can I do to get redirected back to facebook.com/messages even if its an ajax call or something else?
My background.js looks now like this:
var patternURL = new RegExp("http(s)?://www\.facebook\.com(/|/\\?ref=[^\/]*)?$");
var targetURL = "https://www.facebook.com/messages";
var patternShowIcon = new RegExp("http(s)?://www\.facebook\.com");
chrome.webRequest.onBeforeRequest.addListener(function(details) {
if (patternURL.test(details.url)) {
return {
redirectUrl : targetURL
};
}
}, {
urls : ["*://*.facebook.com/*"],
types : ["main_frame", "sub_frame", "script", "xmlhttprequest"]
}, ["blocking"]);
chrome.tabs.onUpdated.addListener(function(tabId, changeInfo, tab) {
chrome.tabs.get(tabId, function(tab) {
if (patternShowIcon.test(tab.url)) {
chrome.pageAction.show(tabId);
} else {
chrome.pageAction.hide(tabId);
}
});
});
The whole source code as a link to the extension is available on github.

Related

How to make url parameter in chrome.getCookies changed based on url of active tab

I am currently developing a chrome extension that can get cookies from a website, using chrome.getCookies.
However, the url parameter in
chrome.getCookies
must be a static url in order to work, like this:
chrome.cookies.get({url: 'https://example.org/#/', name:
'token'}, function(cookie) {
document.getElementById("token").innerHTML = cookie.value
});
But I want to make that url changed dynamically based on which tab I am using that extension. So I tried this:
var tabUrl;
chrome.tabs.getSelected(null, function(tab) {
tabUrl = tab.url
});
chrome.cookies.get({url: tabUrl, name:
'token'}, function(cookie) {
document.getElementById("token").innerHTML = cookie.value
});
And it didn't work. What should I to to achieve my goal?
Edit: If anyone ever reach this page, here the solution, you have to put subsequent code inside the callback, here's the correct one:
chrome.tabs.getSelected(null, function(tab) {
chrome.cookies.get({url: tab.url, name: 'expa_token'}, function(cookie) {
document.getElementById("token").innerHTML = cookie.value
});
});

How to run callback function from content script after loading page ? chrome extension

I want to run a callback function from content script after tab loading new page .
Here is my code :
content_script.js
chrome.runtime.onMessage.addListener(function(request, sender, callback) {
if (request.id == "content_script") {
// open google.com
chrome.runtime.sendMessage({
"id" : "openUrl",
"url" : "https://google.com"
}, function(response) {
});
// call background script
// go to the claim code page
chrome.runtime.sendMessage({
"id" : "background"
}, function() {
alert("test");
});
}
});
background.js
chrome.runtime.onMessage.addListener(function(msg, sender, sendResponse) {
if (msg.id == "openUrl") {
var tabId = sender.tab.id;
var url = msg.url;
openUrl(tabId, url);
} else if (msg.id == "background") {
setTimeout(function() {
sendResponse();
}, 5000);
}
});
function openUrl(tabId, url) {
chrome.tabs.update(tabId, {
url : url,
active : false
}, function() {
console.log("open tab url callback");
});
};
I also uploaded the source code to google drive, you can download it using the bellow link :
https://drive.google.com/open?id=15zSn40z4zYkvCZ8B-gclzixvy6b0C8Zr
as you can see the alert test don't show !
However if I remove the code which open new url , then alert ("test") appear !
I am not sure why ! but it looks like javascript lost the reference to the call back function when I open new url .
How can I solve the problem ? what's the correct way ?
Thank you
The sendResponse function becomes invalid after the message callback returns, unless you return true from the event listener to indicate you wish to send a response asynchronously. (https://developer.chrome.com/extensions/runtime#event-onMessage)
Add return true; in background.js to make this handler asynchronous.
Now you get an error Attempting to use a disconnected port object in the sendResponse(); call of background.js, and yes, that's a result of the page navigating away and losing the context that the content script was running in.
There's no way to make this work: The context in which you wanted to call alert() simply doesn't exist anymore.
Try using chrome.tabs.sendMessage instead. But this means you have to set up the listener at the top level, and not inside of any callback. Message passing is hard.

Differentiate webRequest.onBeforeRequest original page vs. injected

I'm using webRequest.onBeforeRequest in the background script. Then, in the content(?) script, that is injected to the original page I make an AJAX request to the same URL to which the page makes the request I'm detecting, so naturally it starts to loop which hangs the browser pretty soon!
As a temporary measure, I put another parameter at the end of the URL in my AJAX call (&me=1) and made a urls filter that ends like the original URL ends, but that doesn't seem the best way, since the original URL might change in the future.
What would be better way of doing it? For example, I looked at the requestDetails that my listener returned. But, since the script is injected in the original page, I can't find any difference. Or, maybe I could make a urls filter which would only accept URLs that don't end with "&me=1"...?
(I can't (won't) use a flag variable, because the page changes dynamically and again, it doesn't seem the best way, even if I could make it work somehow (I haven't).)
Of course, alternatively, I could just use the data that the original request provides, but I could not find the event/object that would do it, but maybe I've missed it somehow, since I'm pretty much a beginner in making web extensions.
Edit:
manifest.json
{
"manifest_version": 2,
"name": "testtube",
"version": "1.0",
"permissions": [
"webRequest",
"https://*/*",
"activeTab"
],
"web_accessible_resources": [
"https://www.youtube.com/player_api",
"index.html",
"js/jquery-3.2.1.min.js",
"js/player.js",
"js/jquery-ui.min.js",
"css/jquery-ui.css", "css/style.css"
],
"content_scripts": [
{
"matches": [ "https://www.youtube.com/watch*"],
"css": [ "css/jquery.dataTables.min.css", "css/jquery-ui.css", "css/style.css" ],
"js": [ "js/jquery-3.2.1.min.js","js/jquery-ui.min.js", "js/main.js" ]
}
]
,"background": {
"scripts": ["js/background.js" ]
}
}
background.js
"use strict";
var lastRequestId = 0;
function logURL(requestDetails) {
console.log("requestDetails: ", requestDetails);
//this is actually a solution as per my answer below
if ((lastRequestId != requestDetails.requestId) && (requestDetails.url.indexOf("&me=1") == -1))
{
browser.tabs.sendMessage(requestDetails.tabId, { ccurl: requestDetails.url }).then(response => {
console.log("Message from the content script:");
console.log(response.response);
}).catch(onError);;
}
}
browser.webRequest.onBeforeRequest.removeListener(logURL);
browser.webRequest.onBeforeRequest.addListener(
logURL,
{ urls: ["*://www.youtube.com/api/somepattern*"] }
);
function onError(error) {
console.error(`Error: ${error}`);
}
main.js
"use strict";
var jq;
jq = document.createElement('script');
jq.onload = function () { };
jq.src = chrome.extension.getURL("/js/jquery-3.2.1.min.js");
document.querySelector('head').appendChild(jq);
jq = document.createElement('script');
jq.onload = function () { };
jq.src = chrome.extension.getURL("/js/jquery-ui.min.js");
document.querySelector('head').appendChild(jq);
/*... some other scripts... */
var s = document.createElement('script');
s.src = chrome.extension.getURL('/js/player.js');
s.onload = function () {
this.remove();
};
(document.head || document.documentElement).appendChild(s);
var _url = "";
function UURL(request, sender, sendResponse) {
_url = request.url;
var loadR = " loadRequest('" + _url.toString() + "'); ";
var script = document.createElement('script');
script.textContent = loadR;
document.querySelector('head').appendChild(script);
return Promise.resolve({ response: "Hi from content script" });
}
$(function () {
console.log("jquery loaded");
browser.runtime.onMessage.removeListener(UURL)
browser.runtime.onMessage.addListener(UURL);
/* irrelevant code here... */
});
player.js
var loadRequest = function loadRequest(_url) {
_url = _url + "&me=1";
$.ajax({
type: "get",
url: _url,
dataType: "xml",
success: function (data) {
/* irrelevant code here */
If you are attempting to differentiate between the main page load and your XMLHttpRequest1
There are multiple ways that you could differentiate between different types of requests. Without your actual code to try it out, we have to guess as to what you are actually doing.
The webRequest.onBeforeRequest for a normal page load will look like:
webRequest.onBeforeRequest -> arg[0]= {"frameId":0,"method":"GET","parentFrameId":-1,"requestId":"260870","tabId":411,"timeStamp":1500401223979.044,"type":"main_frame","url":"http://www.example.com/"}
-----------------------------------------------------------------------------------------------------------------------------------------------------^^^^^^^^^^^^^^^^^^^
As you can see, the details Object contains a type property, which is a webRequest.ResourceType. For the load of HTML for the main frame it will contain "main_frame" (the details Object will also have "frameId":0,"parentFrameId":-1).
For your AJAX request, the value of the type property should be xmlhttprequest. However, it's possible that by "ajax request" you meant something other than an XMLHttpRequest. In which case, the type property might have some other value, but it should not be "main_frame".
1. I read the original version of the question differently than okkko intended. As currently written, this does not cover the case which they are interested in. However, it might have some value to someone else reading this question/answer, so I'm leaving it up.
Ok, the improved version of "temporary measure" is that in the listener I add another condition against requested url
(requestDetails.url.indexOf("&me=1") == -1)
And then make my ajax call... and in the urls filter for the request I just leave asterix (*) at the end. In this way, it doesn't matter if the original url changes at the end, however technically additional request is caught.. good enough if nobody else replies.

chrome extension, pass data from content to background

I've created a chrome extension that consists of manifest.json, content.js and background.js. in content.js, I'm extracting the current tab's URL and in background.js, I'm opening a new tab. what I want to do, which doesn't work is to pass the URL from content and append it to the URL that I'm calling in background.
content.js:
chrome.extension.onMessage.addListener(function(request, sender, sendResponse)
{
if(request.greeting=="gimmieyodatas")
{
var output ="URL=";
//check for the character '?' for any parameters in the URL
var paramIndex = document.URL.indexOf("?");
//if found, eliminate the parameters in the URL
if (paramIndex > -1)
{
output += document.URL.substring(0, paramIndex);
};
sendResponse({data: output});
}
else{
sendResponse({});
}
});
background.js:
var output2;
chrome.tabs.getSelected(null, function(tab) {
chrome.tabs.sendMessage(tab.id, {greeting:"gimmieyodatas"}, function(response) {
output2 = response.data;
});
});
chrome.browserAction.onClicked.addListener(function() {
chrome.tabs.create({url: "http://www.google.com?" + output2}, function(tab) {
chrome.tabs.executeScript(tab.id, {file: "content.js"}, function() {
sendMessage();
});
});
});
When I run the extension from an open tab, it opens google on a new tab, but it doesn't append the current tab's URL in google URL, meaning the 'output' data does not get passed to the background.js. What am I doing wrong?
The problem is that you are not telling the background page to send a message when a new tab is opened. The call to chrome.tabs.getSelected only happens once when the extension is first run -- it does not happen every time a new tab is opened.
You're on the right track by using the background page as an intermediary between the two content pages, but I suggest a different approach:
Load the content script every time a new tab is opened, via the manifest file:
"content_scripts": [
{
"matches" : [
"<all_urls>"
],
"js" : [
"content.js"
]
}
],
Use a much simpler content script that just sends a message to the background with the current URL page as soon as it loads:
(content.js)
var paramIndex = document.URL.indexOf('?');
if (paramIndex > -1) {
chrome.runtime.sendMessage({output2: 'URL=' + document.URL.substring(0, paramIndex)});
}
When the background page receives the message it saves the URL to a global variable:
(background.js)
var output2;
chrome.extension.onMessage.addListener(function(request, sender, sendResponse) {
output2 = request.output2;
});
You can then load that URL when the action button is clicked:
(background.js)
chrome.browserAction.onClicked.addListener(function() {
chrome.tabs.create({url: "http://www.google.com?" + output2});
});

Hiding loading overlay when popup loses focus

I am working on a chrome extension with a popup which shows up when you click on the extension icon. On popup, I have a button which once clicked shows loading box on the currently open tab page.
Screenshot:
The loading box is removed after some time using setTimeout. However this works only when popup itself is VISIBLE. If I click on button on popup and then go to some other tab and come back or click elsewhere on tab page, the popup hides BUT loading box remains visible.
Does any one know how to hide the loading box even if popup goes invisible/out of focus ? I thought it would go away since there is setTimeout function which removes it but it doesn't work when popup loses focus.
Instead of pasting all relevant code here, here is the download link for the extension so that you could see exactly what I mean.
In actual extension, I have ajax request though instead of setTimeout:
$.ajax({
url : 'localhost url here....',
data : data, // this is searialized form data
dataType : 'json',
method : 'post',
success : function (r) {
if (r.success) {
window.close();
var notification = webkitNotifications.createNotification(
'img/48.png',
'Done!',
'The page has been saved successfully :)'
);
notification.show();
setTimeout(function () {
notification.cancel();
}, 5000);
}
else {
if (r.error) {
$ediv.text(r.error).fadeIn('fast');
}
}
},
error : function (r) {
$ediv.text('Unknown error, please try again later.').fadeIn('fast');
},
complete : function (r) {
chrome.tabs.executeScript(
null, {code : "document.body.removeChild(document.getElementById('__wnoverlay__'))"}
);
}
});
Thanks for your help
Steps
Move this AJAX Request to Background Page.
On Click on Button(Where your dialog box is injected to page) pass message to background Scripts to Store tab.id(Check next point).
Using tab.id received from browser action execute your removal dialog box code(Tab id is needed because user can switch his active tab\window any time).
References
Message Passing
Fetching details of active tab
EDIT 1
Add following in manifest file ensure you register background and jquery with background Page.
"background":{
"scripts":["js/jquery.js","background.js"]
},
Add following code in background.js
This code migrates AJAX Call to background Page and executes removal of dialog box after 5 seconds threshold.
function invokeAJAX(tabid) {
$.ajax({
url: 'localhost url here....',
data: data, // this is searialized form data
dataType: 'json',
method: 'post',
success: function (r) {
if (r.success) {
window.close();
var notification = webkitNotifications.createNotification(
'img/48.png',
'Done!',
'The page has been saved successfully :)');
notification.show();
setTimeout(function () {
notification.cancel();
}, 5000);
} else {
if (r.error) {
$ediv.text(r.error).fadeIn('fast');
}
}
},
error: function (r) {
$ediv.text('Unknown error, please try again later.').fadeIn('fast');
},
complete: function (r) {
chrome.tabs.executeScript(
tabid, {
code: "document.body.removeChild(document.getElementById('__wnoverlay__'))"
});
}
});
}
Your popup.js looks like this where you invoke functions of background Page directly
document.addEventListener("DOMContentLoaded", function () {
$('#btn').click(function () {
// show loading message
// chrome.extension.sendRequest({}, function(response) {});
chrome.tabs.executeScript(null, {
"code": 'var __a=document.createElement("DIV");__a.id="__wnoverlay__";__a.style.width="300px";__a.style.height="80px";__a.style.position="fixed";__a.style.top="50%";__a.style.left="50%";__a.style.color="#fff";__a.style.zIndex=9999999;__a.style.opacity=0.8;__a.style.textAlign="center";__a.style.padding="10px";__a.style.border="12px solid #cccccc";__a.style.marginLeft="-150px";__a.style.marginTop="-40px";__a.style.fontWeight="bold";__a.style.fontSize="17px";__a.style.borderRadius="10px";__a.innerHTML="Working, please wait...";document.body.appendChild(__a);'
});
chrome.tabs.query({}, function (tab) {//Get current tab
chrome.extension.getBackgroundPage().invokeAJAX(tab.id);//DO Ajax call and delete div added after 5 sec to current tab only
});
});
});
EDIT 2
popup.js
Changes made to popup.js
Made tabs.query to fetch only current active browsing normal window
Call back returns tab array so used tab[0] index.
After these changes it sends correct message.
document.addEventListener("DOMContentLoaded", function () {
$('#btn').click(function () {
var $this = $(this);
chrome.tabs.executeScript(
null, {
"code": 'var __a=document.createElement("DIV");__a.id="__wnoverlay__";__a.style.width="300px";__a.style.height="80px";__a.style.position="fixed";__a.style.top="50%";__a.style.left="50%";__a.style.color="#fff";__a.style.background="url(http://groot.com/WebNote_HTML/ChromeExtension/img/spinner.gif) center no-repeat #999999";__a.style.zIndex=9999999;__a.style.opacity=0.8;__a.style.textAlign="center";__a.style.padding="10px";__a.style.border="12px solid #cccccc";__a.style.marginLeft="-150px";__a.style.marginTop="-40px";__a.style.fontWeight="bold";__a.style.fontSize="17px";__a.style.borderRadius="10px";__a.innerHTML="Working, please wait...";document.body.appendChild(__a);'
});
//Proper Query Formation
chrome.tabs.query({
"active": true,
"status": "complete",
"currentWindow": true,
"windowType": "normal"
}, function (tab) { //Get current tab
//DO Ajax call
//tab is an array so we need to access its first index
chrome.extension.getBackgroundPage().invokeAJAX(tab[0].id, $this.closest('form').serialize());
});
});
});
background.js
Changes made to background.js
Eliminated $ediv.text code references as it is undefined in background page.
After these changes this is final code.
function invokeAJAX(tabid, data) {
data = data || '';
$.ajax({
url: 'http://groot.com/WebNote_HTML/ChromeExtension/savePage.php',
data: data,
dataType: 'json',
method: 'post',
success: function (r) {
if (r.success) {
// window.close();
var notification = webkitNotifications.createNotification(
'img/48.png',
'Done!',
'The page has been saved successfully :)');
notification.show();
setTimeout(function () {
notification.cancel();
}, 5000);
} else {
if (r.error) {
//$ediv.text(r.error).fadeIn('fast');
console.log("Error .." + r);
}
}
},
error: function (r) {
//$ediv.text('Unknown error, please try again later.').fadeIn('fast');
console.log("Error .." + r);
},
complete: function (r) {
chrome.tabs.executeScript(
tabid, {
code: "document.body.removeChild(document.getElementById('__wnoverlay__'))"
});
}
});
}
EDIT 3
$('#btn').click(function () {
var $this = $(this);
//Proper Query Formation
chrome.tabs.query({
"active": true,
"status": "complete",
"currentWindow": true,
"windowType": "normal"
}, function (tab) { //Get current tab
//DO Ajax call
//tab is an array so we need to access its first index
chrome.tabs.executeScript(
tab[0].id, {
"code": 'var __a=document.createElement("DIV");__a.id="__wnoverlay__";__a.style.width="300px";__a.style.height="80px";__a.style.position="fixed";__a.style.top="50%";__a.style.left="50%";__a.style.color="#fff";__a.style.background="url(http://groot.com/WebNote_HTML/ChromeExtension/img/spinner.gif) center no-repeat #999999";__a.style.zIndex=9999999;__a.style.opacity=0.8;__a.style.textAlign="center";__a.style.padding="10px";__a.style.border="12px solid #cccccc";__a.style.marginLeft="-150px";__a.style.marginTop="-40px";__a.style.fontWeight="bold";__a.style.fontSize="17px";__a.style.borderRadius="10px";__a.innerHTML="Working, please wait...";document.body.appendChild(__a);'
});
$('#url').val(tab[0].url);
$('#title').val(tab[0].title);
$loader.hide();
chrome.extension.getBackgroundPage().invokeAJAX(tab[0].id, $this.closest('form').serialize());
});
});
The popup code stops executing when the is not shown. However, the injected code is always executed. So you should set the timeout in the injected code, like this:
chrome.tabs.executeScript(null, {"code": 'setTimeout(function(){ document.body.removeChild(document.getElementById("__wnoverlay__")); }, 5000)'});
Replace the code from line 13-15 with the above code and it should work.

Categories