Chrome extension (onBeforeRequest) with async functions?

Is it possible within chrome extension to have some async actions?
So, I want to interfere some requests inside the extension, but only in case if some option is checked by the user.

That option is being kept inside chrome.storage, and that is method is not sync.
So, I wrote a small function that makes a promise out of that:

function getIsEnabled() {
    return new Promise(function(resolve) {
      chrome.storage.sync.get(["isEnabled"], function(result) {
        resolve(result.isEnabled);
      });
    });
  }

But I’ve tried to use it like this:

  async function logURL(requestDetails) {


    const enabled = await getIsEnabled();
    if (enabled && someOtherCondition) {
      return { cancel: true };
    }
  }

  chrome.webRequest.onBeforeRequest.addListener(
    logURL,
    {
      urls: [""]
    },
    ["blocking"]
  );

It doesn’t work like this. It does block the needed requests without async/await, but then it does it all the time.

I assume it’s because request happens and it’s not going wait for me to return something.

So, how could I go about this? Maybe there is some other solution.

JavaScript – wait for async functions to complete

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

why async option is not available for call stack in source panel of chrome devtools (32 bit windows)

I am not able to see async option for call stack panel in source panel for 32 bit windows chrome dev tools. i am getting async trace not enabled error in node js while debugging using chrome dev tools. to solve this issue i need to see async option in dev tools. please suggest any solution for the same.

Basic Chrome Extension programming: unable to dynamically block websites

I’m trying to write a simple chrome extension that blocks twitter when I click on it.

Here’s my background.js:

var enabled = true;
const global_blocked_urls = ["https://*.chess.com/*"];



chrome.webRequest.onBeforeRequest.addListener(
    (details) => {
        console.log(global_blocked_urls); //prints as expected 
        console.log("blocking:", details.url);
        return {
            cancel: enabled
        };
    }, {
        "urls": global_blocked_urls
    }, ["blocking"]
);

chrome.runtime.onMessage.addListener(block_twitter);

function block_twitter(msg) {


        var twitter_regex = "https://*.twitter.com/*";
        global_blocked_urls.push(twitter_regex);
        console.log(global_blocked_urls);

    console.log(msg);
}

I’m holding a list of links that I don’t want to visit, inside a global list.

The problem: Say the block_twitter listener is triggered first, and then I visit a new webpage – so the onBeforeRequest listener is triggered next. When I print out the global list inside callback to chrome.webRequest.onBeforeRequest.addListener, it prints out what I expect it to print out.

But: it still allows me to visit twitter, despite the regex twitter url being the in the global list.

How do I debug/fix this?

How do I run Chrome extension content script after a site’s javascript has finished building page?

I know that I can use run_at: document end to run a script after a page is loaded, but most sites now inject most of their content through some kind of js framework. From what I can tell the content.js script is running before those can finish.

I have some ideas on how to do this, but I’m not really if they really make sense, or even how to do them if they did.

  1. Run my script every time the DOM updates so that if a hyperlink is added I can just remove the hrefs again.

    I found this domwatcher thing in uBlock origin, I assume he’s doing something like this in order to remove any ads that show up, but it’s really complex and I’m not sure what things I would or wouldn’t need.

  2. Run the extension every second or two. This seems really clumsy, but maybe it’s a standard thing? I’m not sure. I also don’t know how I would get a content script to repeat, something this simple couldn’t cause performance issues right?

  3. Javascript scripts are executed in order when they are listed in html right? So maybe I add the script to the end of all those? But they run asynchronously anyway right so that probably wouldn’t work. Either way, some way to run my script after all the others are done. Can I put them in promises?

Also here is my GitHub page with all the code.

The relevant manifest:

 "content_scripts": [
    {
    "matches": [
        "http://*/*",
        "https://*/*"
        ],
    "js": ["content.js"],
    "run_at": "document_end"
    }
]

And content.js:

chrome.storage.sync.get('isEnabled', function (data) {
    if(!data.isEnabled) {
        return;
    }

    for (let link of document.links) {
        link.removeAttribute('href');
        console.log(link);
    }
});

array doesn’t get stored in `localStorage` on using `callback`

I am trying to store item in an array, newItemList, along with present hour and an object, which I get from a server as JSON response, which I parse into a js object. This simply means that a typical element of newItemList will be [item, 13(present_hour), obj]

Here’s the code,

 var item;
 //some code to obtain item

 chrome.storage.local.get({'itemList':[]}, function(item){
   var newItemList = item.itemList;
   var d = new Date();

   if (isItemInArray(newItemList,item) == -1) {
     //api call function as callback

     apiCall(function(result){
         newItemList.push([item,d.getHours(),result]);
     });

   } else {
     var indexOfitem = findItem(newItemList,item);//finding item in the array
     if(//some condition){

       apiCall(function(result){
           newItemList[indexOfTab][1] = d.getHours();
           newItemList[indexOfTab][2] = result;
       });

     } else {
       var indexOfitem = findItem(newItemList,item);
       storedApiCall(newItemList[indexOfTab][2]);//sending the stored JSON response
     }
  }
 chrome.storage.local.set({itemList: newItemList});
 })

 function apiCall(callback){
    //API call, to obtain the JSON response from the web server
   var xhttp = new XMLHttpRequest();
   xhttp.onreadystatechange = function() {
   if (this.readyState == 4 && this.status == 200) {
     var myObj = JSON.parse(this.responseText);//parsing the JSON response to js object

      callback(myObj);
      storedApiCall(myObj);//this function just works fine
     }
    };
  xhttp.open("GET", "example.com", true);
  xhttp.send();
 }

newItemList isn’t getting stored in local storage. It contains of only one element, [item, present hour, obj(from present apiCall)]. That’s why, only the if part of the code runs each time leading to api calls each time, rendering the else part obsolete.

I read about callback from many famous questions asked around about asynchronous calls, but none connected local storage with callbacks. Before implementing callback, newItemList got stored in local storage, but I couldn’t obtain the obj from JSON response for the first time, which is the typical behaviour of asynchronous calls.
Suggest edits, if any.

Javascript promises not acting synchronously

I am attempting to use JavaScript promises so that the rest of my code waits for my asynchronous ‘chrome.storage.local.get’ call. However, it seems that the code is still acting asynchronously, and as a result sending undefined data out.

JavaScript code:

else if( request.message === "fetch-local-storage" ){//if popup request local data
        var responseData;

        let store = new Promise(function(resolve, reject){
            chrome.storage.local.get('localSearchList', function(query){
                responseData = query.localSearchList;
                console.log("asynch data: " + JSON.stringify(responseData));
            }); //fetch the local storage data 
            resolve('done');
        });

        store.then(function(res){ //do this only after above code runs
            console.log("The data being sent to popup (via content.js pipeline): " + JSON.stringify(responseData));
            sendResponse({message: "local-storage-data", data: JSON.stringify(responseData)});
        });

}

The console shows the following output:

The data being sent to popup (via content.js pipeline): undefined
asynch data: "[{"word":"mo","color":"rgb(128,0,128)","id":"0"}]"

If you look back at the code, this display shows that it is not working synchronously…

How do I use Promise.all() with chrome.storage()?

I have several async functions running. I want to wait for them all to finish before taking the next steps. Here’s my code that I’m using to get all of the key/values from chrome.storage and the Promise.all() implementation.

var promise1 = Promise.resolve(3);
var promise2 = 42;
var promise3 = new Promise(function(resolve, reject) {
  setTimeout(resolve, 100, 'foo');
});

var getAll = chrome.storage.sync.get(function(result) {
  console.log(result)
});

Promise.all([promise1, promise2, promise3, getAll]).then(function(values) {
  console.log(values); // [3, 42, "foo", undefined]
});

This doesn’t work unfortunately. It returns undefined.

Most of the code above is taken from MDN here: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/all

Native Messaging in Chrome WebExtension: Waiting for async function

I am developing a WebExtension for Chrome. The purpose of this extension is to detect redirects and sending the redirect url to a native application. The native application decides if the redirect url should be called or should be modified. If the native application responded with “modify”, the redirect url should be changed, e.g., to about:blank. Otherwise the redirect should be called normally.

I am using the onHeadersReceived event to detect redirects with status code 302. The redirect url is handed over to a native application via native messaging.

chrome.webRequest.onHeadersReceived.addListener( ( details ) =>
{
    if ( details.statusCode == 302 )
    {
        var respHeaders = details.responseHeaders;
        //var calledUrl = details.url; // url called by the user
        var redirectUrl;
        blockingResponse = {};

        // Get redirectUrl
        var index = 0;
        for( var i = 0; i < respHeaders.length; ++i )
        {
            if( respHeaders[i].name == 'Location' )
            {
                redirectUrl = respHeaders[i].value;
                index = i;
                break;
            }
        }

        chrome.runtime.sendNativeMessage( "myNativeApp", { text : redirectUrl },
        function( response )
        {
            responseOfApp = response.text;
            if ( responseOfApp == "modify" )
            {
                    details.responseHeaders[index].value = "about:blank";
                    blockingResponse.responseHeaders = details.responseHeaders;
                    blockingResponse.redirectUrl = "about:blank";
            }
        });
    }

    return blockingResponse;

},
{urls: [ "" ]},['responseHeaders','blocking']);

The following problem is facing me:

The chrome.runtime.sendNativeMessage call is asynchronous. If the response is “modify” the blockingResponse is returned before “about:blank” is set in the blockingResponse.

How can be ensured that blockingResponse is returned not until the chrome.runtime.sendNativeMessage call finished?

Any help will be appreciated, thanks!

How should I get a large list of options that are saved to chrome.storage in my Chrome extensions’ content script?

I have an options page in my Chrome extension with a very large list of values that get saved to chrome.storage. These options get used across several different websites. In order to use the options I have to retrieve them in my content scripts that load on these websites.

I’m trying to figure out the best way to handle this. I’m fairly confident that once I get my options loaded during a session on a website that I would want to save them to sessionstorage or localStorage so that I can have quick synchronous access to them on subsequent page loads of that same site.

But the initial retrieval of those options is where I’m wondering what’s the best method. My gut says to just make all of the asynchronous calls for each individual option from my content script at document_start and return a promise once I’ve retrieved them all using Promise.all().

My other idea was using message passing to talk to the background page where all of my options can already be available because I will have done the async calls when Chrome started or because I could just skip chrome.storage and store them in the background pages’ localStorage. In this case, each different website that needs the options is just sending one message to the background page instead of making 40+ async calls to chrome.storage.