admin管理员组

文章数量:1427361

I have made a Firefox WebExtension that interacts with an executable (using runtime.connectNative).

Here is the manifest of the extension:

// manifest.json file

{
  "name": "My Extension",
  "short_name": "myext",
  "version": "2.7",
  "manifest_version": 2,
  "background": {
    "scripts": ["background-script.js"],
    "persistent": true
    },
    "externally_connectable": {
      "ids": ["*"],
      "http://localhost/*"]
  },
   "content_scripts": [
    {
      "matches": ["<all_urls>"],
      "js": ["content-script.js"]
    }
  ],
  "applications": {
    "gecko": {
      "id": "[email protected]",
      "strict_min_version": "50.0.0"
    }
  },
  "icons": {
    "16": "icon-16.png",
    "32": "icon-32.png",
    "128": "icon-128.png"
  },
  "permissions": [
       "nativeMessaging",
       "webRequest"
    ]
}

Here is the content script:

// content-script.js

window.addEventListener("message", function(event) {
  if (event.source == window &&
      event.data.direction &&
      event.data.direction == "from-page-script") {
    alert("Content script received message: \"" + JSON.stringify(event.data.message) + "\"");

    var sending = browser.runtime.sendMessage(event.data.message);

    sending.then(handleResponse, handleError);

  }
});

function handleResponse(message) 
{
  console.log("content-script : handleResponse : message = " + JSON.stringify(message));
  window.wrappedJSObject.foo.p = "OK : " + JSON.stringify(message);
}

function handleError(error) {
  console.log("Message from the background script : " + error.message);
  window.wrappedJSObject.foo.p = "error";
}

And here is my background script:

// background-script.js

var port;

browser.runtime.onMessage.addListener(function(request, sender, sendResponse) 
{
    port = browser.runtime.connectNative("ping_pong");
    var result = port.postMessage(request);

    port.onMessage.addListener(function(response)
    {
        console.log("Received: " + JSON.stringify(response));
        sendResponse(response);
    });
});

I have a problem regarding the following line in the background script:

sendResponse(response);

The method handleResponse() of the content script is called before the sendResponse() of the background script.

So, when the executable takes a longer time to do an action, the result of the executable is not sent to the content script, and the content script receives an undefined result.

Is there another way to send the result of the executable from the background script to the content script? Or use the callback another way?

I have made a Firefox WebExtension that interacts with an executable (using runtime.connectNative).

Here is the manifest of the extension:

// manifest.json file

{
  "name": "My Extension",
  "short_name": "myext",
  "version": "2.7",
  "manifest_version": 2,
  "background": {
    "scripts": ["background-script.js"],
    "persistent": true
    },
    "externally_connectable": {
      "ids": ["*"],
      "http://localhost/*"]
  },
   "content_scripts": [
    {
      "matches": ["<all_urls>"],
      "js": ["content-script.js"]
    }
  ],
  "applications": {
    "gecko": {
      "id": "[email protected]",
      "strict_min_version": "50.0.0"
    }
  },
  "icons": {
    "16": "icon-16.png",
    "32": "icon-32.png",
    "128": "icon-128.png"
  },
  "permissions": [
       "nativeMessaging",
       "webRequest"
    ]
}

Here is the content script:

// content-script.js

window.addEventListener("message", function(event) {
  if (event.source == window &&
      event.data.direction &&
      event.data.direction == "from-page-script") {
    alert("Content script received message: \"" + JSON.stringify(event.data.message) + "\"");

    var sending = browser.runtime.sendMessage(event.data.message);

    sending.then(handleResponse, handleError);

  }
});

function handleResponse(message) 
{
  console.log("content-script : handleResponse : message = " + JSON.stringify(message));
  window.wrappedJSObject.foo.p = "OK : " + JSON.stringify(message);
}

function handleError(error) {
  console.log("Message from the background script : " + error.message);
  window.wrappedJSObject.foo.p = "error";
}

And here is my background script:

// background-script.js

var port;

browser.runtime.onMessage.addListener(function(request, sender, sendResponse) 
{
    port = browser.runtime.connectNative("ping_pong");
    var result = port.postMessage(request);

    port.onMessage.addListener(function(response)
    {
        console.log("Received: " + JSON.stringify(response));
        sendResponse(response);
    });
});

I have a problem regarding the following line in the background script:

sendResponse(response);

The method handleResponse() of the content script is called before the sendResponse() of the background script.

So, when the executable takes a longer time to do an action, the result of the executable is not sent to the content script, and the content script receives an undefined result.

Is there another way to send the result of the executable from the background script to the content script? Or use the callback another way?

Share Improve this question edited Nov 23, 2016 at 21:02 Makyen 33.4k12 gold badges92 silver badges125 bronze badges asked Nov 23, 2016 at 19:44 ThordaxThordax 1,7334 gold badges29 silver badges59 bronze badges
Add a ment  | 

1 Answer 1

Reset to default 6

The use of Promises is causing some confusion here. Your handleResponse() is being called with the Promise fulfilled with no arguments because you exit the background script's runtime.onMessage listener without calling sendResponse(). If sendResponse() had been called, then the argument would be the message1. Thus, you will need to adjust your .then() to differentiate between receiving no arguments (sendResponse() not called) and an argument containing the response message (sendResponse() called). If I recall correctly, if you were using chrome.runtime.sendMessage(), your responseCallback function would not be called unless sendResponse() is called.

MDN describes the Promise as:

If the sender sent a response, this will be fulfilled with the response as a JSON object. Otherwise it will be fulfilled with no arguments. If an error occurs while connecting to the extension, the promise will be rejected with an error message.

To call sendResponse() asynchronously, you need to return true; from your runtime.onMessage listener

You are calling sendResponse() from within an asynchronous callback. As a result, you exit the runtime.onMessage listener prior to sendResponse() being called. If you are going to do that, and still want to call sendResponse(), you need to return true from your runtime.onMessage listener.

From MDN runtime.onMessage regarding sendResponse():

This function returns a boolean. It should return true from the event listener if you wish to call sendResponse after the event listener returns.

So your code could be:

browser.runtime.onMessage.addListener(function(request, sender, sendResponse) 
{
    port = browser.runtime.connectNative("ping_pong");
    var result = port.postMessage(request);

    port.onMessage.addListener(function portOnMessageListener(response)
    {
        //sendResponse is only valid once. So, if this is how you want to use it, you need
        //  to remove the listener, or in some other way not call sendResponse twice.
        port.onMessage.removeListener(portOnMessageListener);
        console.log("Received: " + JSON.stringify(response));
        sendResponse(response);
    });
    return true;
});

sendResponse() is valid only once

The sendResponse() function is valid for only one message (i.e. you get to send only one response per message sent). If your desire is to send multiple messages to the content script, you will need to set up doing so in a different way.


  1. The MDN docs state the message is a "JSON object". What they probably mean that it is an Object that was converted from JSON, which was used to represent it while the message was in transit.

本文标签: