简体   繁体   中英

Use asynchronous calls in a blocking webRequest handler

Summary

I'm using a browser.webRequest.onBeforeRequest handler. I need to block the webRequest until I have information back from calls to asynchronous methods made within the handler. How can I do that?

The details

First, my apology for the long question. But I hope someone can help.

I have an embedded extension which contains a browser.webRequest.onBeforeRequest (I need to use embedded extension at the moment to deal with some SDK legacy code).

The browser.webRequest.onBeforeRequest callback function connects to an SDK extension and instructs it to perform some functions. The SDK sends reply to the webextension when it is done from the task. I used await in the browser.runtime.sendMessage to ensure that I stop the execution until I get a reply from SDK. To use await , I had to use async (but in fact I do not want async function). When I do not use await , I only get the reply from the SDK after the loop finishes all its iterations and not for each iteration.

The sample code below contains a lot of console messages for debugging just to monitor the execution.

The problem is: I am not getting a reliable outcome. In some cases (not all), the http request goes out before the SDK code is effective. I could identify this because the request properties must be affected by the SDK code. The console shows the execution sequence as expected. But, the http request is not affected by the SDK (in some cases).

In this simple example, the SDK just sends a message to the webextension, but assume that it performs some functions, read/write operations, etc. All the SDK tasks must be completed before the request goes out.

What I really need is to guarantee that the web request will NOT go out until all the SDK code is performed.

Reference to MDN documentation, it says that the browser.webRequest.onBeforeRequest is an async function. I wonder if this is the source of the problem? if so, how to enforce it to be sync?

embedding-extension [directory]
    - index.js
    - package.json
    - webextension [directory]
       - main.js
       - manifest.json

1) package.json :

{
  "title": "testhybrid",
  "name": "testhybrid",
  "version": "0.0.1",
  "description": "A basic add-on",
  "main": "index.js",
  "author": "",
  "engines": {
    "firefox": ">=38.0a1",
    "fennec": ">=38.0a1"
  },
  "license": "MIT",
  "hasEmbeddedWebExtension": true,
  "keywords": [
    "jetpack"
  ]
}

2) index.js :

const webExtension = require("sdk/webextension");
console.log("in SDK: inside embedding extension");

// Start the embedded webextension
  webExtension.startup().then(api => {
    const {browser} = api;
    browser.runtime.onMessage.addListener((msg, sender, sendReply) => {
      if (msg == "send-to-sdk") {
         console.log("in SDK: message from webExt has been received");
        sendReply({
          content: "reply from SDK"
        }); //end send reply
      }//end if

    }); //end browser.runtime.onMessage

  }); //end webExtension.startup

3) manifest.json :

{
  "manifest_version": 2,
  "name": "webExt",
  "version": "1.0",
  "description": "No description.",
  "background": {
    "scripts": ["main.js"]
  },

  "permissions": [
    "activeTab",
    "webRequest",
  "<all_urls>"
],

"browser_action": {
  "default_icon": {
    "64": "icons/black-64.png"
  },
  "default_title": "webExt"
}
}

4) main.js :

var flag=true;
async function aMethod() {
  console.log("in webExt: inside aMethod");
  for(var x=0; x<2; x++)
  {
    console.log("loop iteration: "+x);
    if(flag==true)
      {
        console.log("inside if");
        console.log("will send message to SDK");
        const reply = await browser.runtime.sendMessage("send-to-sdk").then(reply => {
        if(reply)
        {
          console.log("in webExt: " + reply.content);
        }
        else {
           console.log("<<no response message>>");
        }
        });
      }//end if flag

    else
      {
        console.log("inside else");
      }//end else
  }//end for
}

browser.webRequest.onBeforeRequest.addListener(
  aMethod,
  {urls: ["<all_urls>"],
   types: ["main_frame"]}
);

The webRequest.onBeforeRequest documenation states (emphasis mine):

To cancel or redirect the request, first include "blocking" in the extraInfoSpec array argument to addListener() . Then, in the listener function, return a BlockingResponse object, setting the appropriate property:

  • to cancel the request, include a property cancel with the value true.
  • to redirect the request, include a property redirectUrl with the value set to the URL to which you want to redirect.

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.

You appear to have done none of the above. Thus, the event is handled completely asynchronously, with no possibility of delaying the request until your handler returns. In other words, as currently written, your webRequest.onBeforeRequest has absolutely no effect on the webRequest. You handler is just notified that the webRequest is in process.

To accomplish what you desire, delay the request until after you perform some asynchronous operations, you would need to:

  1. Add "webRequestBlocking" to your manifest.json permissions:

      "permissions": [ "activeTab", "webRequest", "webRequestBlocking", "<all_urls>" ], 
  2. Pass "blocking" in the extraInfoSpec array argument to addListener() :

     browser.webRequest.onBeforeRequest.addListener( aMethod, {urls: ["<all_urls>"], types: ["main_frame"]}, ["blocking"] ); 
  3. Return a Promise from your handler, which resolves with a BlockingResponse :

     function aMethod() { //Exactly how this is coded will depend on exactly what you are doing. var promises = []; console.log("in webExt: inside aMethod"); for (var x = 0; x < 2; x++) { console.log("loop iteration: " + x); if (flag == true) { console.log("inside if"); console.log("will send message to SDK"); promises.push(browser.runtime.sendMessage("send-to-sdk").then(reply => { if (reply) { console.log("in webExt: " + reply.content); } else { console.log("<<no response message>>"); } }); } else { console.log("inside else"); } } return Promise.all(promises).then(function(){ //Resolve with an empty Object, which is a valid blockingResponse that permits // the webRequest to complete normally, after it is resolved. return {}; }); } 

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM