質問

I'm currently playing around with Chrome extensions and can't find a convenient solution to send a message from the background context (lets call it BG) to the context of an injected script (lets call it INJ). Sending messages from INJ to BG works like a charm. But I want BG to do an XMLHttpRequest which might take some time, so I want to send the evaluation of this request to INJ.

Unfortunately, I'm not allowed to register a listener in the context of INJ. So the best solution I can think of consists of:

  • sending a message from INJ to BG, triggering the XMLHttpRequest
  • as soon as the request returns BG stores the result of the request locally
  • INJ repeatedly sends further messages to BG asking for the result, unless BG answers with the result.

Maybe something like this:

INJ:

function whaitForResult ()
{
  chrome.runtime.sendMessage ({method: "getResult"}, function (response)
  {
    if (response.finished === "true")
      // request finished, lets go on
    else
      setTimeout(whaitForResult, 100);
  }
}
chrome.runtime.sendMessage({method : "triggerRequest"}, function(response) {});
whaitForResult ();

BG:

chrome.runtime.onMessage.addListener (function(request, sender, sendResponse)
{
  if (request.method == "triggerRequest")
  {
    // startXMLHttpRequest ();
    sendResponse ({});
    return true;
  }
  else if (request.method == "getResult")
  {
    if (finishedXMLHttpRequest ())
      sendResponse ({finished:"true", result: resultFromXMLHttpRequest ()});
    else
      sendResponse ({finished:"false"});
    return true;
  }
});

I guess this should work, but in my opinion it's quite messy. So my question: Is there a convenient way to send a message to an injected script?

If the concept of my extension is crap, please propose an alternative. But to do the XMLHttpRequest I need some variables from the localStorage and since this data is quite sensitive I don't want to pass these vars to the context of INJ. Thus, doing the XMLHttpRequest in INJ isn't an option.

役に立ちましたか?

解決

Before I continue, I'd like to point out that you're just dealing with an ordinary content script, not an "injected script" (according to my definition).

Don't send the response at the first request (sendResponse({})). Instead, call sendResponse when the response has been finished:

// Content script
chrome.runtime.sendMessage({method: "getResultForRequest"}, function(response) {

});
// Background page
chrome.runtime.onMessage.addListener(function(request, sender, sendResponse) {
    if (request.method == "getResultForRequest") {
        $.ajax({success: sendResponse}); // Example
        return true;
    }
});

As said at the top of this answer, what you're dealing with is not an injected script, but a content script. The script execution environment of the content script and the page are strictly separated, so the page cannot read the variable. In other words, your premise is flawed; it's fine to pass credentials from the background page to the content script.

I recommend to not use localStorage, but the chrome.storage API. This asynchronous API allows content scripts to directly read persistent variables stored by the extension. Consequently, you don't need a background or event page any more. For a more detailed comparison and an example, I refer to this answer.

Note: There are two cases when it makes sense to let the background page process the XHR:

  • The Content security policy of the page blocks the target URL (this affects Chrome extensions)
  • The current page is served over https, whereas the requested resource is served over http. Mixing http and https will change the appearance of the padlock, which should be avoided if possible.

But otherwise, I suggest to use the chrome.storage API and do the cross-origin http request in your content script.

ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top