I want to restrict the browser to within a set of URLs. I'm using:
chrome.webNavigation.onBeforeNavigate.addListener(functon(details){
if (notAllowed(details.url)) {
// Do something to stop navigation
}
});
I know that I can cancel chrome.webRequest.onBeforeRequest. But, I don't want to block requests, like XHR or any other. I want this filter to be applied only for navigation.
For the user, it should looks like, the link (e.g. <a href="http://...">foo</a>) click event was stopped.
The following extension adds a listener to webNavigation.onCompleted which is used to remember, indexed by tabId, both the most recent URL in frameId==0 for which the event is fired, and the prior URL.
A listener is added to webNavigation.onBeforeNavigate which watches for matching URLs, in this case, stackexchange.com. If the URL matches, the tab URL is updated, via tabs.update, to navigate back to the last URL for which a webNavigation.onCompleted event was fired.
If the onBeforeNavigate event is for a frameId other than 0, then the tab is navigated to the previous URL for which a onCompleted event was fired for frameId==0. If the prior URL was not used, then we could get into a loop where the current URL is repeatedly re-loaded due to the URL in one of its frames matching the URL we are blocking. A better way to handle this would be to inject a content script to change the src attribute for the frame. We would then need to handle frames within frames.
blockNavigation.js:
//Remember tab URLs
var tabsInfo = {};
function completedLoadingUrlInTab(details) {
//console.log('details:',details);
//We have completed loading a URL.
createTabRecordIfNeeded(details.tabId);
if(details.frameId !== 0){
//Only record inforamtion for the main frame
return;
}
//Remember the newUrl so we can check against it the next time
// an event is fired.
tabsInfo[details.tabId].priorCompleteUrl = tabsInfo[details.tabId].completeUrl;
tabsInfo[details.tabId].completeUrl = details.url;
}
function InfoForTab(_url,_priorUrl) {
this.completeUrl = (typeof _url !== 'string') ? "" : _url;
this.priorCompleteUrl = (typeof _priorUrl !== 'string') ? "" : _priorUrl;
}
function createTabRecordIfNeeded(tabId) {
if(!tabsInfo.hasOwnProperty(tabId) || typeof tabsInfo[tabId] !== 'object') {
//This is the first time we have encountered this tab.
//Create an object to hold the collected info for the tab.
tabsInfo[tabId] = new InfoForTab();
}
}
//Block URLs
function blockUrlIfMatch(details){
createTabRecordIfNeeded(details.tabId);
if(/^[^:/]+:\/\/[^/]*stackexchange\.[^/.]+\//.test(details.url)){
//Block this URL by navigating to the already current URL
console.log('Blocking URL:',details.url);
console.log('Returning to URL:',tabsInfo[details.tabId].completeUrl);
if(details.frameId !==0){
//This navigation is in a subframe. We currently handle that by
// navigating to the page prior to the current one.
// Probably should handle this by changing the src of the frame.
// This would require injecting a content script to change the src.
// Would also need to handle frames within frames.
//Must navigate to priorCmpleteUrl as we can not load the current one.
tabsInfo[details.tabId].completeUrl = tabsInfo[details.tabId].priorCompleteUrl;
}
var urlToUse = tabsInfo[details.tabId].completeUrl;
urlToUse = (typeof urlToUse === 'string') ? urlToUse : '';
chrome.tabs.update(details.tabId,{url: urlToUse},function(tab){
if(chrome.runtime.lastError){
if(chrome.runtime.lastError.message.indexOf('No tab with id:') > -1){
//Chrome is probably loading a page in a tab which it is expecting to
// swap out with a current tab. Need to decide how to handle this
// case.
//For now just output the error message
console.log('Error:',chrome.runtime.lastError.message)
} else {
console.log('Error:',chrome.runtime.lastError.message)
}
}
});
//Notify the user URL was blocked.
notifyOfBlockedUrl(details.url);
}
}
function notifyOfBlockedUrl(url){
//This will fail if you have not provided an icon.
chrome.notifications.create({
type: 'basic',
iconUrl: 'blockedUrl.png',
title:'Blocked URL',
message:url
});
}
//Startup
chrome.webNavigation.onCompleted.addListener(completedLoadingUrlInTab);
chrome.webNavigation.onBeforeNavigate.addListener(blockUrlIfMatch);
//Get the URLs for all current tabs when add-on is loaded.
//Block any currently matching URLs. Does not check for URLs in frames.
chrome.tabs.query({},tabs => {
tabs.forEach(tab => {
createTabRecordIfNeeded(tab.id);
tabsInfo[tab.id].completeUrl = tab.url;
blockUrlIfMatch({
tabId : tab.id,
frameId : 1, //use 1. This will result in going to '' at this time.
url : tab.url
});
});
});
manifest.json:
{
"description": "Watch webNavigation events and block matching URLs",
"manifest_version": 2,
"name": "webNavigation based block navigation to matched URLs",
"version": "0.1",
"permissions": [
"notifications",
"webNavigation",
"tabs"
],
"background": {
"scripts": ["blockNavigation.js"]
}
}
It is possible to prevent navigation altogether. Use redirectURL and set a link which generates 204 (no content) response.
chrome.webRequest.onBeforeRequest.addListener(
function(details) {
//just don't navigate at all if the requested url is example.com
if (details.url.indexOf("://example.com/") != -1) {
return {redirectUrl: 'http://google.com/gen_204'};
} else {
return { cancel: false };
}
},
{ urls: ["<all_urls>"] },
["blocking"]
);
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With