Bug 1518863: Part 2 - Delay extension protocol requests until extension is ready. r=aswan

We don't want extension protocol load requests to begin loading until the
extension is far enough initialized to run code. If we load it before then,
the extension framework will either fail to recognize the extension entirely,
or may begin running its scripts in an incomplete environment.

This patch adds a slow path which adds a promise handler and creats a stub
channel only in the case when the extension is not ready. In the normal,
already-initialized case, we take the more direct path.

Differential Revision: https://phabricator.services.mozilla.com/D21447

--HG--
extra : rebase_source : ca770d241ff68192716feace67cd512565ae6b24
This commit is contained in:
Kris Maglione 2019-02-27 11:54:31 -08:00
parent 78eef88f98
commit 1a8317619b
2 changed files with 86 additions and 29 deletions

View File

@ -234,7 +234,11 @@ void Promise::Then(JSContext* aCx,
void PromiseNativeThenHandlerBase::ResolvedCallback(
JSContext* aCx, JS::Handle<JS::Value> aValue) {
RefPtr<Promise> promise = CallResolveCallback(aCx, aValue);
mPromise->MaybeResolve(promise);
if (promise) {
mPromise->MaybeResolve(promise);
} else {
mPromise->MaybeResolve(JS::UndefinedHandleValue);
}
}
void PromiseNativeThenHandlerBase::RejectedCallback(

View File

@ -8,6 +8,8 @@
#include "mozilla/ClearOnShutdown.h"
#include "mozilla/dom/ContentChild.h"
#include "mozilla/dom/Promise.h"
#include "mozilla/dom/Promise-inl.h"
#include "mozilla/ExtensionPolicyService.h"
#include "mozilla/FileUtils.h"
#include "mozilla/ipc/IPCStreamUtils.h"
@ -49,6 +51,7 @@
#endif
#define EXTENSION_SCHEME "moz-extension"
using mozilla::dom::Promise;
using mozilla::ipc::FileDescriptor;
namespace mozilla {
@ -420,6 +423,25 @@ Result<Ok, nsresult> ExtensionProtocolHandler::SubstituteRemoteChannel(
return Ok();
}
void OpenWhenReady(
Promise* aPromise, nsIStreamListener* aListener, nsIChannel* aChannel,
const std::function<nsresult(nsIStreamListener*, nsIChannel*)>& aCallback) {
nsCOMPtr<nsIStreamListener> listener(aListener);
nsCOMPtr<nsIChannel> channel(aChannel);
Unused << aPromise->ThenWithCycleCollectedArgs(
[channel, aCallback](
JSContext* aCx, JS::HandleValue aValue,
nsIStreamListener* aListener) -> already_AddRefed<Promise> {
nsresult rv = aCallback(aListener, channel);
if (NS_FAILED(rv)) {
CancelRequest(aListener, channel, rv);
}
return nullptr;
},
listener);
}
nsresult ExtensionProtocolHandler::SubstituteChannel(nsIURI* aURI,
nsILoadInfo* aLoadInfo,
nsIChannel** result) {
@ -427,43 +449,75 @@ nsresult ExtensionProtocolHandler::SubstituteChannel(nsIURI* aURI,
MOZ_TRY(SubstituteRemoteChannel(aURI, aLoadInfo, result));
}
auto* policy = EPS().GetByURL(aURI);
NS_ENSURE_TRUE(policy, NS_ERROR_UNEXPECTED);
RefPtr<dom::Promise> readyPromise(policy->ReadyPromise());
nsresult rv;
nsCOMPtr<nsIURL> url = do_QueryInterface(aURI, &rv);
MOZ_TRY(rv);
nsAutoCString ext;
MOZ_TRY(url->GetFileExtension(ext));
if (!ext.LowerCaseEqualsLiteral("css")) {
nsCOMPtr<nsIChannel> channel;
if (ext.LowerCaseEqualsLiteral("css")) {
// Filter CSS files to replace locale message tokens with localized strings.
static const auto convert = [](nsIStreamListener* listener,
nsIChannel* channel,
nsIChannel* origChannel) -> nsresult {
nsresult rv;
nsCOMPtr<nsIStreamConverterService> convService =
do_GetService(NS_STREAMCONVERTERSERVICE_CONTRACTID, &rv);
MOZ_TRY(rv);
nsCOMPtr<nsIURI> uri;
MOZ_TRY(channel->GetURI(getter_AddRefs(uri)));
const char* kFromType = "application/vnd.mozilla.webext.unlocalized";
const char* kToType = "text/css";
nsCOMPtr<nsIStreamListener> converter;
MOZ_TRY(convService->AsyncConvertData(kFromType, kToType, listener, uri,
getter_AddRefs(converter)));
return origChannel->AsyncOpen(converter);
};
channel = NS_NewSimpleChannel(
aURI, aLoadInfo, *result,
[readyPromise](nsIStreamListener* listener, nsIChannel* channel,
nsIChannel* origChannel) -> RequestOrReason {
if (readyPromise) {
nsCOMPtr<nsIChannel> chan(channel);
OpenWhenReady(
readyPromise, listener, origChannel,
[chan](nsIStreamListener* aListener, nsIChannel* aChannel) {
return convert(aListener, chan, aChannel);
});
} else {
MOZ_TRY(convert(listener, channel, origChannel));
}
return RequestOrReason(origChannel);
});
} else if (readyPromise) {
channel = NS_NewSimpleChannel(
aURI, aLoadInfo, *result,
[readyPromise](nsIStreamListener* listener, nsIChannel* channel,
nsIChannel* origChannel) -> RequestOrReason {
OpenWhenReady(readyPromise, listener, origChannel,
[](nsIStreamListener* aListener, nsIChannel* aChannel) {
return aChannel->AsyncOpen(aListener);
});
return RequestOrReason(origChannel);
});
} else {
return NS_OK;
}
// Filter CSS files to replace locale message tokens with localized strings.
nsCOMPtr<nsIChannel> channel = NS_NewSimpleChannel(
aURI, aLoadInfo, *result,
[](nsIStreamListener* listener, nsIChannel* channel,
nsIChannel* origChannel) -> RequestOrReason {
nsresult rv;
nsCOMPtr<nsIStreamConverterService> convService =
do_GetService(NS_STREAMCONVERTERSERVICE_CONTRACTID, &rv);
MOZ_TRY(rv);
nsCOMPtr<nsIURI> uri;
MOZ_TRY(channel->GetURI(getter_AddRefs(uri)));
const char* kFromType = "application/vnd.mozilla.webext.unlocalized";
const char* kToType = "text/css";
nsCOMPtr<nsIStreamListener> converter;
MOZ_TRY(convService->AsyncConvertData(kFromType, kToType, listener, uri,
getter_AddRefs(converter)));
MOZ_TRY(origChannel->AsyncOpen(converter));
return RequestOrReason(origChannel);
});
NS_ENSURE_TRUE(channel, NS_ERROR_OUT_OF_MEMORY);
if (aLoadInfo) {
nsCOMPtr<nsILoadInfo> loadInfo =
static_cast<LoadInfo*>(aLoadInfo)->CloneForNewRequest();
@ -471,7 +525,6 @@ nsresult ExtensionProtocolHandler::SubstituteChannel(nsIURI* aURI,
}
channel.swap(*result);
return NS_OK;
}