0

In my Chrome extension, I check each web request under a specific domain. If the URL matches a specific condition, and there's an existing open tab that matches another URL, I'd like to return {cancel: true}, which means the request is blocked.

So my code works this way: if the requested URL matches my condition, check all open tabs. If one of the open tabs matches my (second) condition, I want to return {cancel: true} from my initial (outer) function.

The Problem: The return from the outer function fires before all of the tabs are checked (= before all the forEach loops are executed), therefore it always returns {cancel: false}.

[I know there are many questions related to this, and one of the main solutions includes callback functions, but specifically in my case I haven't yet succeeded in making this work.]

Code:

function onBeforeRequestHandler (details) {
    var cancel = false;
    var url = details.url;
    // condition 1
    if (url.indexOf("condition_1") > -1){

        // check all open windows/tabs
        chrome.windows.getAll({populate:true}, function(windows){
            windows.forEach(function(single_window){
                single_window.tabs.forEach(function(tab){
                    // condition 2
                    if (tab.url.indexOf("condition_2") > -1){
                        cancel = true;
                        // this is less important - updating the tab works
                        chrome.tabs.update(tab.id, {url: url});
                    }
                });
            });
        });
        // always getting cancel = false here because it fires too quickly
        return {cancel: cancel};
    }
    else
        return {cancel: false};
}

chrome.webRequest.onBeforeRequest.addListener(onBeforeRequestHandler, {urls: ["some_domain"]}, ["blocking"]);
Cauthon
  • 674
  • 1
  • 7
  • 16
  • 1
    Possible duplicate of [How do I return the response from an asynchronous call?](https://stackoverflow.com/questions/14220321/how-do-i-return-the-response-from-an-asynchronous-call) – Teemu Jun 25 '18 at 05:08
  • @teemu this won't really help here. – Jonas Wilms Jun 25 '18 at 05:10
  • 1
    What you are trying to achieve is impossible. You cannot cancel something *now* if you get to know if you shouod cancel it in a few seconds. Instead you would have to cancel the action, and then if the check fails restart it. – Jonas Wilms Jun 25 '18 at 05:12
  • @JonasW. A better idea? Advice to use `async = false`..? – Teemu Jun 25 '18 at 05:12
  • @JonasW. Didn't get it - I don't mind having to wait for the check of all tabs to finish before returning the value for this request. I just don't know how to wait until that happens. – Cauthon Jun 25 '18 at 05:13
  • 1
    You can't, thats the thing. – Jonas Wilms Jun 25 '18 at 05:17
  • Any workaround someone can suggest? There has to be a way to cancel a web request if there's an open tab with a specified URL... no? – Cauthon Jun 25 '18 at 05:40
  • 1
    The listener must return the value synchronously but your code runs asynchronous API. These are hard facts which you can't magically change to accommodate to your needs. Instead, maintain an array of windows and tabs preemptively using chrome.tabs and chrome.windows events. – wOxxOm Jun 25 '18 at 05:42
  • @wOxxOm Thanks, I implemented this suggestion. – Cauthon Jun 25 '18 at 07:59

1 Answers1

1

Currently, you can't cancel the request based on asynchronous function in the Google Chrome.

In the Firefox, it already supported asynchronous function with Promise, you can implement that like following code.

function onBeforeRequestHandler (details) {
   var cancel = false;
   var url = details.url;
   // condition 1
   if (url.indexOf("condition_1") > -1){
       // check all open windows/tabs
       // return Promise which will be resolved after populate windows/tabs
       return browser.windows.getAll({populate:true})
           .then(function(windows) {
               windows.forEach(function(single_window) {
                   single_window.tabs.forEach(function(tab) {
                   // condition 2
                   if (tab.url.indexOf("condition_2") > -1) {
                       // this is less important - updating the tab works
                       browser.tabs.update(tab.id, {url: url});
                       return { cancel: true }
                   }
               });
           });
         })
   }
   else
       return {cancel: false};
}

history / references

According to webRequest.onBeforeRequest in MDN.

From Firefox 52 onwards, instead of returning BlockingResponse, the listener can return a Promise which is resolved with a BlockingResponse. This enables the listener to process the request asynchronously. https://developer.mozilla.org/en-US/Add-ons/WebExtensions/API/webRequest/onBeforeRequest

Already reported feature request on the Chrome like the Firefox, but this ticket has NOT been closed. https://bugs.chromium.org/p/chromium/issues/detail?id=625860

Sheile
  • 403
  • 4
  • 10