Chrome extension: match opened tab to loaded tab in background.js - google-chrome-extension

I have a Chrome extension. I need to pass a variable to the tab that is opened, and then have that variable be available when the tab's webpage has completed loading. I need to be able to uniquely match the opened tab with the loaded tab.
chrome.browserAction.onClicked.addListener(function(tab) {
url = "my_url";
unique_id = "some id"; // I need to pass this on
chrome.tabs.create({ url: url }, function(tab){});
// I cannot use any global vars because this function actually loops and opens lots of tabs.
});
// Called when page has finished loading
chrome.webNavigation.onCompleted.addListener(function(tab) {
if(tab.frameId == 0){
// I need to identify the tab (unique_id) that was created in chrome.browserAction.onClicked.addListener()
// tab.url won't work because it's different if the orginal url was redirected
}
});

Tabs have tab IDs which are unique within a browser session.
chrome.browserAction.onClicked.addListener(function(tab) {
url = "my_url";
chrome.tabs.create({ url: url }, function(tab){
unique_id = tab.id; // I need to pass this on
});
});
// Called when page has finished loading
chrome.webNavigation.onCompleted.addListener(function(details) {
if(details.frameId == 0){
unique_id = details.tabId;
}
});

Related

Prevent user to click on browsers back button Angular2

I am working with Angular 2 JS with Typescript. I have a requirement where I don't want to click/navigate to previous page via Browsers back button.
So how we can disable or prevent user to click or move to previous page via browser.
I have tried some example which are available over internet but those are not working properly. Like -
this.router.navigate(['/view'], { replaceUrl: true });
or
this.router.navigate(['/view'], { skipLocationChange: true });
Use this on page load, it will stop user on navigating back from page on which it placed.
put the code as it is in index.html page of the angular
<script type = "text/javascript">
function changeHashOnLoad() {
window.location.href += "#";
setTimeout("changeHashAgain()", "50");
}
function changeHashAgain()
{
window.location.href += "1";
}
var storedHash = window.location.hash;
window.setInterval(function () {
if (window.location.hash != storedHash) {
window.location.hash = storedHash;
}
}, 50);
</script>

Content script programatically injected by Chrome extension background page got injected multiple times

I have a chrome extension, say it injects content script a.js into urls matching 'http://example.com/*'. Then when I click some page elements in example.com page, a.js would ask background to create a new tab with defined url, and programatically inject a content script b.js into the new tab, you can check the code down:
chrome.tabs.create({
url: 'http://example.com'
}, function (tab) {
var taskTab = tab.id;
chrome.tabs.onUpdated.addListener(function (tabId, changeInfo, tab) {
if (tabId === taskTab) {
chrome.tabs.executeScript(taskTab, {
file: 'scripts/b.js'
});
}
});
});
The problem is, the content script b.js would get injected into the new tab for a lot times. I also tried the chrome.webNavigation, the result is the same.
So if I want the b.js got injected into the new tab for only one time? how can i make it?
By the way, the b.js is actually a remote file, I load it into page by jQuery, here to simplify the question I make it a local file.
Thanks very much.
update
I had figure out what's going on with my code.
First, it's wrong for me to wrap the onUpdated event listener inside the create callback, every time I create a new tab, close it, create it again, the event listener would got bind one more time. The more, the more.
Second, #Xan was right, I could do it by checking changeInfo.status. So the code would be:
var taskTab;
chrome.tabs.create({
url: 'http://example.com'
}, function (tab) {
taskTab = tab.id;
});
chrome.tabs.onUpdated.addListener(function (tabId, changeInfo, tab) {
if (changeInfo.status === 'complete'){
if (tabId === taskTab) {
chrome.tabs.executeScript(taskTab, {
file: 'scripts/b.js'
});
}
}
});
And that b.js would only be injected for once, no matter it's a local file or a remote file loaded by jQuery.
onUpdated event can fire for a lot of reasons:
object changeInfo:
Lists the changes to the state of the tab that was updated.
string (optional) status: The status of the tab. Can be either loading or complete.
string (optional) url: The tab's URL if it has changed.
boolean (optional) pinned: The tab's new pinned state.
string (optional) favIconUrl: The tab's new favicon URL.
You might want to filter onUpdated events by those properties of changeInfo
Failsafe method: when you inject a second script, set some guard variable that is checked for undefined before you execute the rest of the script. Any other attempts to inject the script will be in the same context and will see the variable set. Kind of like C's #ifndef includes.

Chrome/FF/Safari extension: Load hidden web page in incognito-like mode

Is it possible to build an 'incognito mode' for loading background web-pages in a browser extension?
I am writing a non-IE cross-browser extension that periodically checks web-pages on the user's behalf. There are two requirements:
Page checks are done in the background, to be as unobtrusive as possible. I believe this could be done by opening the page in a new unfocussed browser tab, or hidden in a sandboxed iframe in the extension's background page.
The page checks should operate in 'incognito mode', and not use/update the user's cookies, history, or local storage. This is to stop the checks polluting the user's actual browsing behavior as much as possible.
Any thoughts on how to implement this 'incognito mode'?
It would ideally work in as many browser types as possible (not IE).
My current ideas are:
Filter out cookie headers from incoming/outgoing http requests associated with the page checks (if I can identify all of these) (not possible in Safari?)
After each page check, filter out the page from the user's history.
Useful SO questions I've found:
Chrome extension: loading a hidden page (without iframe)
Firefox addon development, open a hidden web browser
Identify requests originating in the hiddenDOMWindow (or one of its iframes)
var Cu = Components.utils;
Cu.import('resource://gre/modules/Services.jsm');
Cu.import('resource://gre/modules/devtools/Console.jsm');
var win = Services.appShell.hiddenDOMWindow
var iframe = win.document.createElementNS('http://www.w3.org/1999/xhtml', 'iframe');
iframe.addEventListener('DOMContentLoaded', function(e) {
var win = e.originalTarget.defaultView;
console.log('done loaded', e.document.location);
if (win.frameElement && win.frameElement != iframe) {
//its a frame in the in iframe that load
}
}, false);
win.document.documentElement.appendChild(iframe);
must keep a global var reference to iframe we added.
then you can change the iframe location like this, and when its loaded it triggers the event listener above
iframe.contentWindow.location = 'http://www.bing.com/'
that DOMContentLoaded identifies all things loaded in that iframe. if the page has frames it detects that too.
to remove from history, into the DOMContentLoaded function use the history service to remove win.location from history:
https://developer.mozilla.org/en-US/docs/Using_the_Places_history_service
now to strip the cookies from requests in that page use this code:
const {classes: Cc, Constructor: CC, interfaces: Ci, utils: Cu, results: Cr, manager: Cm} = Components;
Cu.import('resource://gre/modules/Services.jsm');
var myTabToSpoofIn = Services.wm.getMostRecentBrowser('navigator:browser').gBrowser.tabContainer[0]; //will spoof in the first tab of your browser
var httpRequestObserver = {
observe: function (subject, topic, data) {
var httpChannel, requestURL;
if (topic == "http-on-modify-request") {
httpChannel = subject.QueryInterface(Ci.nsIHttpChannel);
var goodies = loadContextGoodies(httpChannel)
if (goodies) {
if (goodies.contentWindow.top == iframe.contentWindow.top) {
httpChannel.setRequestHeader('Cookie', '', false);
} else {
//this page load isnt in our iframe so ignore it
}
}
}
}
};
Services.obs.addObserver(httpRequestObserver, "http-on-modify-request", false);
//Services.obs.removeObserver(httpRequestObserver, "http-on-modify-request", false); //run this on shudown of your addon otherwise the observer stags registerd
//this function gets the contentWindow and other good stuff from loadContext of httpChannel
function loadContextGoodies(httpChannel) {
//httpChannel must be the subject of http-on-modify-request QI'ed to nsiHTTPChannel as is done on line 8 "httpChannel = subject.QueryInterface(Ci.nsIHttpChannel);"
//start loadContext stuff
var loadContext;
try {
var interfaceRequestor = httpChannel.notificationCallbacks.QueryInterface(Ci.nsIInterfaceRequestor);
//var DOMWindow = interfaceRequestor.getInterface(Components.interfaces.nsIDOMWindow); //not to be done anymore because: https://developer.mozilla.org/en-US/docs/Updating_extensions_for_Firefox_3.5#Getting_a_load_context_from_a_request //instead do the loadContext stuff below
try {
loadContext = interfaceRequestor.getInterface(Ci.nsILoadContext);
} catch (ex) {
try {
loadContext = subject.loadGroup.notificationCallbacks.getInterface(Ci.nsILoadContext);
} catch (ex2) {}
}
} catch (ex0) {}
if (!loadContext) {
//no load context so dont do anything although you can run this, which is your old code
//this probably means that its loading an ajax call or like a google ad thing
return null;
} else {
var contentWindow = loadContext.associatedWindow;
if (!contentWindow) {
//this channel does not have a window, its probably loading a resource
//this probably means that its loading an ajax call or like a google ad thing
return null;
} else {
var aDOMWindow = contentWindow.top.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIWebNavigation)
.QueryInterface(Ci.nsIDocShellTreeItem)
.rootTreeItem
.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIDOMWindow);
var gBrowser = aDOMWindow.gBrowser;
var aTab = gBrowser._getTabForContentWindow(contentWindow.top); //this is the clickable tab xul element, the one found in the tab strip of the firefox window, aTab.linkedBrowser is same as browser var above //can stylize tab like aTab.style.backgroundColor = 'blue'; //can stylize the tab like aTab.style.fontColor = 'red';
var browser = aTab.linkedBrowser; //this is the browser within the tab //this is where the example in the previous section ends
return {
aDOMWindow: aDOMWindow,
gBrowser: gBrowser,
aTab: aTab,
browser: browser,
contentWindow: contentWindow
};
}
}
//end loadContext stuff
}

Create a tab and inject content-script in it gives permission error

In the background page of my Chrome extension I need to do the following:
create a tab with an html page that resides in my extension folder or that is dynamically generated by the background page;
once the new tab is loaded, inject a content-script in it in order to use message passing;
once the script has been injected, send a message to the new tab with some data.
This always turns into the error:
tabs.executeScript: Cannot access contents of url "". Extension manifest must request permission to access this host. or, if the page or script are stored into .html and .js dedicated files in my extensions folder:
tabs.executeScript: Cannot access contents of url "chrome-extension://ehdjfh.../page.html". Extension manifest must request permission to access this host.
The error is generated when chrome.tabs.executeScript() is executed and the content-script injection is blocked. (I would need this structure to pass some big data to the tab that will be truncated if passed directly as string into the coded html url)
Here is a code sample that can be used to test the behavior into a working extension, just copy-paste and load the extension in Chrome:
background.js
chrome.browserAction.onClicked.addListener(function() {
var getSampleHTML = function() {
return 'javascript:\'<!doctype html><html>' +
'<head></head>' +
'<body>' +
'<p id="myId">page created...</p>' +
'</body>' +
'</html>\'';
};
// create a new tab with an html page that resides in extension domain:
chrome.tabs.create({'url': getSampleHTML(), 'active': false}, function(tab){
var getSampleScript = function() {
return 'chrome.extension.onRequest.addListener(' +
'function(request, sender, sendResponse) {' +
'if(request.action == "print_data" && sender.tab.id == ' + tab.id + '){' +
'var p = document.getElementById("myId");' +
'p += "<br>data received: " + request.data;' +
'}' +
'});'
'document.getElementById("myId").innerHTML += "<br>content-script loaded...";'
};
// inject the content-script in the page created:
chrome.tabs.executeScript(tab.id, {code: getSampleScript()}, function(){
// send the data to the content-script:
chrome.tabs.sendRequest(tab.id, {action: "print_data",
data: "some long data"});
});
});
});
manifest.json
{
"name": "ContentScript Injection Sample",
"description": "",
"version": "0.1",
"permissions": ["tabs"],
"background": {
"persistent": false,
"scripts": ["background.js"]
},
"browser_action": {
"default_title": "open a new tab and inject contentscript"
},
"manifest_version": 2
}
ok, this is a possible solution, thanks to #RobW and #方觉 for pointing out my mistake (my error was to consider a local web page opened into a new created tab as normal web page, instead it has direct access to chrome.extension API like eg. a popup):
background.js
chrome.browserAction.onClicked.addListener(function() {
// create a new tab with an html page that resides in extension domain:
chrome.tabs.create({'url': chrome.extension.getURL("page.html"),
'active': false}, function(tab){
var selfTabId = tab.id;
chrome.tabs.onUpdated.addListener(function(tabId, changeInfo, tab) {
if (changeInfo.status == "complete" && tabId == selfTabId){
// send the data to the page's script:
var tabs = chrome.extension.getViews({type: "tab"});
tabs[0].doSomething("some long data");
}
});
});
});
page.html
<!doctype html>
<html>
<head>
</head>
<body>
<p id="myId">page created...</p>
<script src="page.js" type="text/javascript"></script>
</body>
</html>
page.js
var alreadyRun = false;
function doSomething(data){
if (!alreadyRun) {
var p = document.getElementById("myId");
p.innerHTML += "<br>data received: " + data;
// needed because opening eg. DevTools to inpect the page
// will trigger both the "complete" state and the tabId conditions
// in background.js:
alreadyRun = true;
}
}
document.getElementById("myId").innerHTML += "<br>script loaded...";
what I don't like so much of this solution is that use of chrome.tabs.onUpdated.addListener() to check if the created tab is completely loaded and that in this way it's triggered for any change of state of any tabs opened (it's useless... would be great a way to run the check only for the interested tab)

getting the current url from chrome extension

i'm making a chrome extension that opens a web-site if the current tab is not the same web-site, so i managed to get all of the tabs like this:
chrome.tabs.getAllInWindow(null, allTabs);
and i wrote a function to display it:
function allTabs(tabs) {
var tabsURLS = '';
for (var i = 0; i < tabs.length; i++) {
tabsURLS = tabs[i].url + '\n';
}
alert(tabsURLS);
}
but i need to get the current page url so i get the current tab by this:
var object=chrome.tabs.getCurrent(function(){;});
but i cant get to page properties like id or url and this alert shows "undefined" ...
alert(object);
while this alert doesn't work at all
alert(object.id);
in the end, i read this page chrome.tabs and i was shocked when i read this line
getCurrent
chrome.tabs.getCurrent(function callback)
Gets the tab that this script call is being made from. May be undefined if called from a non-tab context (for example: a background page or popup view).
so i don't think that there is a solution of getting the current opened tab from chrome extension...
I believe that you need to use getSelected instead
<html>
<head>
<script>
chrome.tabs.getSelected(null, function(tab) {
var tabId = tab.id;
var tabUrl = tab.url;
alert(tabUrl);
});
</script>
</head>
the final code was like this, and it worked just fine.. :
var tabUrl;
chrome.browserAction.onClicked.addListener(function(activeTab) {
var x=activeTab.url;
var newURL = "https://www.google.com";
if (x!= newURL) {
//to open a page in a new tab
chrome.tabs.create({url: newURL,"selected":true});
//to open the page with the current tab
chrome.tabs.update(activeTab.id, {url:newURL});
}
});
The current accepted answer is out of date.
According to MDN, tabs.getSelected() is depricated
Use this instead:
tabs.query({active: true})
Ensure to set the correct permissions in manifest.json to access tab information:
"permissions": [
"tabs",
"http://*/*"
],
After that, you can determine the URL by using
chrome.tabs.getSelected(null, function (tab) {
alert(tab.url);
});

Resources