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:
XMLHttpRequest
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:
But otherwise, I suggest to use the chrome.storage
API and do the cross-origin http request in your content script.