最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - Block the navigation indicated by an onBeforeNavigate event in a Chrome extension, but not non-navigation requests

programmeradmin1浏览0评论

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.

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.

Share Improve this question edited Jun 28, 2020 at 16:42 Makyen 33.3k12 gold badges92 silver badges125 bronze badges asked Sep 5, 2016 at 15:08 dkiselevdkiselev 9408 silver badges20 bronze badges 9
  • 1 Hmm. I guess you can inject window.stop() as shown here – woxxom Commented Sep 5, 2016 at 16:33
  • You tell us that you are using it, but don't explicitly say what exactly it is doing which you don't desire. In addition, we need a specific case where it is not doing what you desire (e.g. a website, and the user action within that website which caused what you don't want). In other words, we need all of a description of exactly what you want to happen, a description of exactly what it is doing that you don't desire and a complete minimal reproducible example (along with the test case, i.e. a website) which we can use to duplicate the problem you are having. – Makyen Commented Sep 5, 2016 at 16:34
  • 2 @wOxxOm, it seems that events are processed asynchromusly, so it's possible that I'll stop the window in wrong state. Especially for pages which uses the redirects, like google search results. – dkiselev Commented Sep 5, 2016 at 16:43
  • Yes, chrome.* API is async but I don't see why it could be a problem here. Anyway, there are no other methods I can think of even theoretically. – woxxom Commented Sep 5, 2016 at 16:46
  • My mistake, it was not clear to me that what you are really asking is: "From an onBeforeNavigate event handler, how do I prevent/cancel the navigation to the new URL?" – Makyen Commented Sep 5, 2016 at 16:46
 |  Show 4 more comments

3 Answers 3

Reset to default 6

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"]
  );

webRequest.onBeforeRequest has a field .type which can be used to determine why the request was made. For only requests which are loading a page, check details.type === "main_frame" and to include requests which are navigating inside an iframe, also check details.type === "sub_frame".

browser.webRequest.onBeforeRequest.addListener(
    (details) => {
        if (details.type !== "main_frame" || details.method !== "GET") {
            return;
        }
    },
    { urls: ["<all_urls>"] },
    ["blocking"],
);
        permissions: [
            "webRequest",
            "webRequestBlocking",
            "http://*/",
            "https://*/",
        ],

与本文相关的文章

发布评论

评论列表(0)

  1. 暂无评论