Bug 1536094 - Support dynamic import from content scripts (sandboxed code) r=smaug,jonco

Firstly we need to find a usable ScriptLoader for code in the content script sandbox,
for that we use the normal ScriptLoader associated with DOMWindow wrapped by the sandbox.

Secondly we need to execute the module in the global of the sandbox instead of the
"ScriptGlobal" the ScriptLoader is actually associated with. The main
behavior change here comes from using xpc::NativeGlobal in HostImportModuleDynamically
and passing that global around inside ScriptFetchOptions.

To ensure that content-scripts and the webpage don't share imported modules,
the module map (mFetchingModules and mFetchedModules) now uses a complex key
of <URI, Global>. The Global is a nullptr for normal imports from a webpage.

Differential Revision: https://phabricator.services.mozilla.com/D107076
This commit is contained in:
Tom Schuster
2021-03-23 11:15:11 +00:00
parent 2b862d9727
commit 26c4c51d5e
14 changed files with 278 additions and 69 deletions

View File

@@ -1027,7 +1027,7 @@ nsresult EventListenerManager::CompileEventHandlerInternal(
RefPtr<ScriptFetchOptions> fetchOptions = new ScriptFetchOptions(
CORS_NONE, aElement->OwnerDoc()->GetReferrerPolicy(), aElement,
aElement->OwnerDoc()->NodePrincipal());
aElement->OwnerDoc()->NodePrincipal(), nullptr);
RefPtr<EventScript> eventScript = new EventScript(fetchOptions, uri);

View File

@@ -317,6 +317,7 @@ ModuleSourceMalformed=Module source URI is malformed: “%S”.
# LOCALIZATION NOTE: Do not translate "<script>".
ScriptSourceNotAllowed=<script> source URI is not allowed in this document: “%S”.
ModuleSourceNotAllowed=Module source URI is not allowed in this document: “%S”.
WebExtContentScriptModuleSourceNotAllowed=WebExtension content scripts may only load modules with moz-extension URLs and not: “%S”.
ModuleResolveFailure=Error resolving module specifier “%S”. Relative module specifiers must start with “./”, “../” or “/”.
# LOCALIZATION NOTE: %1$S is the invalid property value and %2$S is the property name.
InvalidKeyframePropertyValue=Keyframe property value “%1$S” is invalid according to the syntax for “%2$S”.

View File

@@ -149,7 +149,7 @@ void ModuleLoadRequest::ModuleLoaded() {
LOG(("ScriptLoadRequest (%p): Module loaded", this));
mModuleScript = mLoader->GetFetchedModule(mURI);
mModuleScript = mLoader->GetFetchedModule(mURI, GetWebExtGlobal());
if (!mModuleScript || mModuleScript->HasParseError()) {
ModuleErrored();
return;

View File

@@ -8,10 +8,10 @@
#define mozilla_dom_ModuleLoadRequest_h
#include "ScriptLoadRequest.h"
#include "mozilla/MozPromise.h"
#include "js/RootingAPI.h"
#include "js/Value.h"
#include "nsURIHashKey.h"
#include "mozilla/MozPromise.h"
#include "nsTHashtable.h"
namespace mozilla {

View File

@@ -0,0 +1,38 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "ModuleMapKey.h"
using namespace mozilla::dom;
ModuleMapKey::ModuleMapKey(const nsIURI* aURI, nsIGlobalObject* aWebExtGlobal)
: nsURIHashKey(aURI), mWebExtGlobal(aWebExtGlobal) {}
ModuleMapKey::ModuleMapKey(const ModuleMapKey* aKey)
: nsURIHashKey(aKey->mKey) {
*this = *aKey;
}
ModuleMapKey::ModuleMapKey(ModuleMapKey&& aToMove) noexcept
: nsURIHashKey(std::move(aToMove)) {
mWebExtGlobal = std::move(aToMove.mWebExtGlobal);
}
bool ModuleMapKey::KeyEquals(KeyTypePointer aOther) const {
if (mWebExtGlobal != aOther->mWebExtGlobal) {
return false;
}
return nsURIHashKey::KeyEquals(
static_cast<const nsURIHashKey*>(aOther)->GetKey());
}
// static
PLDHashNumber ModuleMapKey::HashKey(KeyTypePointer aKey) {
PLDHashNumber hash = nsURIHashKey::HashKey(aKey->mKey);
// XXX This seems wrong.
hash =
AddToHash(hash, reinterpret_cast<uintptr_t>(aKey->mWebExtGlobal.get()));
return hash;
}

38
dom/script/ModuleMapKey.h Normal file
View File

@@ -0,0 +1,38 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef ModuleMapKey_h__
#define ModuleMapKey_h__
#include "nsURIHashKey.h"
#include "nsIGlobalObject.h"
namespace mozilla::dom {
class ModuleMapKey : public nsURIHashKey {
public:
using KeyType = const ModuleMapKey&;
using KeyTypePointer = const ModuleMapKey*;
ModuleMapKey() = default;
ModuleMapKey(const nsIURI* aURI, nsIGlobalObject* aWebExtGlobal);
explicit ModuleMapKey(const ModuleMapKey* aKey);
ModuleMapKey(ModuleMapKey&& aToMove) noexcept;
ModuleMapKey& operator=(const ModuleMapKey& aOther) = default;
KeyType GetKey() const { return *this; }
KeyTypePointer GetKeyPointer() const { return this; }
static KeyTypePointer KeyToPointer(KeyType aKey) { return &aKey; }
bool KeyEquals(KeyTypePointer aOther) const;
static PLDHashNumber HashKey(KeyTypePointer aKey);
private:
nsCOMPtr<nsIGlobalObject> mWebExtGlobal;
};
} // namespace mozilla::dom
#endif

View File

@@ -27,7 +27,8 @@ namespace dom {
// ScriptFetchOptions
//////////////////////////////////////////////////////////////
NS_IMPL_CYCLE_COLLECTION(ScriptFetchOptions, mElement, mTriggeringPrincipal)
NS_IMPL_CYCLE_COLLECTION(ScriptFetchOptions, mElement, mTriggeringPrincipal,
mWebExtGlobal)
NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(ScriptFetchOptions, AddRef)
NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(ScriptFetchOptions, Release)
@@ -35,12 +36,14 @@ NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(ScriptFetchOptions, Release)
ScriptFetchOptions::ScriptFetchOptions(mozilla::CORSMode aCORSMode,
ReferrerPolicy aReferrerPolicy,
Element* aElement,
nsIPrincipal* aTriggeringPrincipal)
nsIPrincipal* aTriggeringPrincipal,
nsIGlobalObject* aWebExtGlobal)
: mCORSMode(aCORSMode),
mReferrerPolicy(aReferrerPolicy),
mIsPreload(false),
mElement(aElement),
mTriggeringPrincipal(aTriggeringPrincipal) {
mTriggeringPrincipal(aTriggeringPrincipal),
mWebExtGlobal(aWebExtGlobal) {
MOZ_ASSERT(mTriggeringPrincipal);
}
@@ -59,6 +62,7 @@ NS_IMPL_CYCLE_COLLECTING_RELEASE(ScriptLoadRequest)
NS_IMPL_CYCLE_COLLECTION_CLASS(ScriptLoadRequest)
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(ScriptLoadRequest)
// XXX missing mLoadBlockedDocument ?
NS_IMPL_CYCLE_COLLECTION_UNLINK(mFetchOptions, mCacheInfo)
tmp->mScript = nullptr;
if (Runnable* runnable = tmp->mRunnable.exchange(nullptr)) {

View File

@@ -22,6 +22,7 @@
#include "mozilla/Vector.h"
#include "nsCOMPtr.h"
#include "nsCycleCollectionParticipant.h"
#include "nsIGlobalObject.h"
#include "nsIScriptElement.h"
#include "ScriptKind.h"
@@ -55,13 +56,17 @@ class ScriptFetchOptions {
ScriptFetchOptions(mozilla::CORSMode aCORSMode,
enum ReferrerPolicy aReferrerPolicy, Element* aElement,
nsIPrincipal* aTriggeringPrincipal);
nsIPrincipal* aTriggeringPrincipal,
nsIGlobalObject* aWebExtGlobal);
const mozilla::CORSMode mCORSMode;
const enum ReferrerPolicy mReferrerPolicy;
bool mIsPreload;
nsCOMPtr<Element> mElement;
nsCOMPtr<nsIPrincipal> mTriggeringPrincipal;
// Global that initiated this request, when using a WebExtension
// content-script.
nsCOMPtr<nsIGlobalObject> mWebExtGlobal;
};
/*
@@ -258,6 +263,13 @@ class ScriptLoadRequest
return mFetchOptions->mTriggeringPrincipal;
}
// This will return nullptr in most cases,
// unless this is a module being imported by a WebExtension content script.
// In that case it's the Sandbox global executing that code.
nsIGlobalObject* GetWebExtGlobal() const {
return mFetchOptions->mWebExtGlobal;
}
// Make this request a preload (speculative) request.
void SetIsPreloadRequest() {
MOZ_ASSERT(!GetScriptElement());

View File

@@ -441,25 +441,23 @@ bool ScriptLoader::IsAboutPageLoadingChromeURI(ScriptLoadRequest* aRequest,
return true;
}
bool ScriptLoader::ModuleMapContainsURL(nsIURI* aURL) const {
bool ScriptLoader::ModuleMapContainsURL(nsIURI* aURL,
nsIGlobalObject* aGlobal) const {
// Returns whether we have fetched, or are currently fetching, a module script
// for a URL.
return mFetchingModules.Contains(aURL) || mFetchedModules.Contains(aURL);
}
bool ScriptLoader::IsFetchingModule(ModuleLoadRequest* aRequest) const {
bool fetching = mFetchingModules.Contains(aRequest->mURI);
MOZ_ASSERT_IF(fetching, !mFetchedModules.Contains(aRequest->mURI));
return fetching;
ModuleMapKey key(aURL, aGlobal);
return mFetchingModules.Contains(key) || mFetchedModules.Contains(key);
}
void ScriptLoader::SetModuleFetchStarted(ModuleLoadRequest* aRequest) {
// Update the module map to indicate that a module is currently being fetched.
MOZ_ASSERT(aRequest->IsLoading());
MOZ_ASSERT(!ModuleMapContainsURL(aRequest->mURI));
MOZ_ASSERT(
!ModuleMapContainsURL(aRequest->mURI, aRequest->GetWebExtGlobal()));
mFetchingModules.InsertOrUpdate(
aRequest->mURI, RefPtr<GenericNonExclusivePromise::Private>{});
ModuleMapKey(aRequest->mURI, aRequest->GetWebExtGlobal()),
RefPtr<GenericNonExclusivePromise::Private>{});
}
void ScriptLoader::SetModuleFetchFinishedAndResumeWaitingRequests(
@@ -475,14 +473,14 @@ void ScriptLoader::SetModuleFetchFinishedAndResumeWaitingRequests(
"%u)",
aRequest, aRequest->mModuleScript.get(), unsigned(aResult)));
ModuleMapKey key(aRequest->mURI, aRequest->GetWebExtGlobal());
RefPtr<GenericNonExclusivePromise::Private> promise;
MOZ_ALWAYS_TRUE(
mFetchingModules.Remove(aRequest->mURI, getter_AddRefs(promise)));
MOZ_ALWAYS_TRUE(mFetchingModules.Remove(key, getter_AddRefs(promise)));
RefPtr<ModuleScript> moduleScript(aRequest->mModuleScript);
MOZ_ASSERT(NS_FAILED(aResult) == !moduleScript);
mFetchedModules.InsertOrUpdate(aRequest->mURI, RefPtr{moduleScript});
mFetchedModules.InsertOrUpdate(key, RefPtr{moduleScript});
if (promise) {
if (moduleScript) {
@@ -496,10 +494,11 @@ void ScriptLoader::SetModuleFetchFinishedAndResumeWaitingRequests(
}
RefPtr<GenericNonExclusivePromise> ScriptLoader::WaitForModuleFetch(
nsIURI* aURL) {
MOZ_ASSERT(ModuleMapContainsURL(aURL));
nsIURI* aURL, nsIGlobalObject* aGlobal) {
MOZ_ASSERT(ModuleMapContainsURL(aURL, aGlobal));
if (auto entry = mFetchingModules.Lookup(aURL)) {
ModuleMapKey key(aURL, aGlobal);
if (auto entry = mFetchingModules.Lookup(key)) {
if (!entry.Data()) {
entry.Data() = new GenericNonExclusivePromise::Private(__func__);
}
@@ -507,7 +506,7 @@ RefPtr<GenericNonExclusivePromise> ScriptLoader::WaitForModuleFetch(
}
RefPtr<ModuleScript> ms;
MOZ_ALWAYS_TRUE(mFetchedModules.Get(aURL, getter_AddRefs(ms)));
MOZ_ALWAYS_TRUE(mFetchedModules.Get(key, getter_AddRefs(ms)));
if (!ms) {
return GenericNonExclusivePromise::CreateAndReject(NS_ERROR_FAILURE,
__func__);
@@ -516,15 +515,17 @@ RefPtr<GenericNonExclusivePromise> ScriptLoader::WaitForModuleFetch(
return GenericNonExclusivePromise::CreateAndResolve(true, __func__);
}
ModuleScript* ScriptLoader::GetFetchedModule(nsIURI* aURL) const {
ModuleScript* ScriptLoader::GetFetchedModule(nsIURI* aURL,
nsIGlobalObject* aGlobal) const {
if (LOG_ENABLED()) {
nsAutoCString url;
aURL->GetAsciiSpec(url);
LOG(("GetFetchedModule %s", url.get()));
LOG(("GetFetchedModule %s %p", url.get(), aGlobal));
}
bool found;
ModuleScript* ms = mFetchedModules.GetWeak(aURL, &found);
ModuleScript* ms =
mFetchedModules.GetWeak(ModuleMapKey(aURL, aGlobal), &found);
MOZ_ASSERT(found);
return ms;
}
@@ -562,14 +563,23 @@ nsresult ScriptLoader::CreateModuleScript(ModuleLoadRequest* aRequest) {
LOG(("ScriptLoadRequest (%p): Create module script", aRequest));
nsCOMPtr<nsIScriptGlobalObject> globalObject = GetScriptGlobalObject();
if (!globalObject) {
return NS_ERROR_FAILURE;
}
nsCOMPtr<nsIGlobalObject> globalObject;
nsCOMPtr<nsIScriptContext> context;
if (aRequest->GetWebExtGlobal()) {
globalObject = aRequest->GetWebExtGlobal();
} else {
nsCOMPtr<nsIScriptGlobalObject> scriptGlobal =
GetScriptGlobalObject(WebExtGlobal::Handled);
if (!scriptGlobal) {
return NS_ERROR_FAILURE;
}
nsCOMPtr<nsIScriptContext> context = globalObject->GetScriptContext();
if (!context) {
return NS_ERROR_FAILURE;
context = scriptGlobal->GetScriptContext();
if (!context) {
return NS_ERROR_FAILURE;
}
globalObject = scriptGlobal;
}
nsAutoMicroTask mt;
@@ -578,8 +588,11 @@ nsresult ScriptLoader::CreateModuleScript(ModuleLoadRequest* aRequest) {
AutoEntryScript aes(globalObject, "CompileModule", true);
bool oldProcessingScriptTag = context->GetProcessingScriptTag();
context->SetProcessingScriptTag(true);
bool oldProcessingScriptTag = false;
if (context) {
oldProcessingScriptTag = context->GetProcessingScriptTag();
context->SetProcessingScriptTag(true);
}
nsresult rv;
{
@@ -644,7 +657,9 @@ nsresult ScriptLoader::CreateModuleScript(ModuleLoadRequest* aRequest) {
}
}
context->SetProcessingScriptTag(oldProcessingScriptTag);
if (context) {
context->SetProcessingScriptTag(oldProcessingScriptTag);
}
LOG(("ScriptLoadRequest (%p): module script == %p", aRequest,
aRequest->mModuleScript.get()));
@@ -809,6 +824,7 @@ void ScriptLoader::StartFetchingModuleDependencies(
nsCOMArray<nsIURI> urls;
nsresult rv = ResolveRequestedModules(aRequest, &urls);
if (NS_FAILED(rv)) {
aRequest->mModuleScript = nullptr;
aRequest->ModuleErrored();
return;
}
@@ -878,6 +894,8 @@ RefPtr<GenericPromise> ScriptLoader::StartFetchingModuleAndDependencies(
MOZ_ASSERT(!childRequest->mModuleScript);
LOG(("ScriptLoadRequest (%p): rejecting %p", aParent,
&childRequest->mReady));
ReportErrorToConsole(childRequest, rv);
childRequest->mReady.Reject(rv, __func__);
return ready;
}
@@ -890,7 +908,7 @@ static ScriptLoader* GetCurrentScriptLoader(JSContext* aCx) {
JS_ReportErrorASCII(aCx, "No ScriptLoader found for the current context");
});
JSObject* object = JS::CurrentGlobalOrNull(aCx);
JS::Rooted<JSObject*> object(aCx, JS::CurrentGlobalOrNull(aCx));
if (!object) {
return nullptr;
}
@@ -900,8 +918,13 @@ static ScriptLoader* GetCurrentScriptLoader(JSContext* aCx) {
return nullptr;
}
nsCOMPtr<nsPIDOMWindowInner> win = do_QueryInterface(global);
nsGlobalWindowInner* innerWindow = nsGlobalWindowInner::Cast(win);
nsGlobalWindowInner* innerWindow = nullptr;
if (nsCOMPtr<nsPIDOMWindowInner> win = do_QueryInterface(global)) {
innerWindow = nsGlobalWindowInner::Cast(win);
} else {
innerWindow = xpc::SandboxWindowOrNull(object, aCx);
}
if (!innerWindow) {
return nullptr;
}
@@ -975,9 +998,19 @@ void ScriptLoader::ResolveImportedModule(
// previously successful with these same two arguments.
MOZ_ASSERT(uri, "Failed to resolve previously-resolved module specifier");
// Use sandboxed global when doing a WebExtension content-script load.
nsCOMPtr<nsIGlobalObject> global;
if (BasePrincipal::Cast(nsContentUtils::SubjectPrincipal(aCx))
->ContentScriptAddonPolicy()) {
global = xpc::CurrentNativeGlobal(aCx);
MOZ_ASSERT(global);
MOZ_ASSERT(
xpc::IsWebExtensionContentScriptSandbox(global->GetGlobalJSObject()));
}
// Let resolved module script be moduleMap[url]. (This entry must exist for us
// to have gotten to this point.)
ModuleScript* ms = loader->GetFetchedModule(uri);
ModuleScript* ms = loader->GetFetchedModule(uri, global);
MOZ_ASSERT(ms, "Resolved module not found in module map");
MOZ_ASSERT(!ms->HasParseError());
MOZ_ASSERT(ms->ModuleRecord());
@@ -1050,9 +1083,24 @@ bool HostImportModuleDynamically(JSContext* aCx,
// triggers an inline event handler, as there is no active script
// there.
Document* document = loader->GetDocument();
// Use the document's principal for all loads, except WebExtension
// content-scripts.
// Only remember the global for content-scripts as well.
nsCOMPtr<nsIPrincipal> principal = nsContentUtils::SubjectPrincipal(aCx);
nsCOMPtr<nsIGlobalObject> global = xpc::CurrentNativeGlobal(aCx);
if (!BasePrincipal::Cast(principal)->ContentScriptAddonPolicy()) {
principal = document->NodePrincipal();
MOZ_ASSERT(global);
global = nullptr; // Null global is the usual case for most loads.
} else {
MOZ_ASSERT(
xpc::IsWebExtensionContentScriptSandbox(global->GetGlobalJSObject()));
}
options = new ScriptFetchOptions(mozilla::CORS_NONE,
document->GetReferrerPolicy(), nullptr,
document->NodePrincipal());
principal, global);
baseURL = document->GetDocBaseURI();
}
@@ -1070,6 +1118,7 @@ void ScriptLoader::StartDynamicImport(ModuleLoadRequest* aRequest) {
nsresult rv = StartLoad(aRequest);
if (NS_FAILED(rv)) {
ReportErrorToConsole(aRequest, rv);
FinishDynamicImportAndReject(aRequest, rv);
}
}
@@ -1389,12 +1438,20 @@ nsresult ScriptLoader::StartLoad(ScriptLoadRequest* aRequest) {
}
if (aRequest->IsModuleRequest()) {
// To prevent dynamic code execution, content scripts can only
// load moz-extension URLs.
nsCOMPtr<nsIPrincipal> principal = aRequest->TriggeringPrincipal();
if (BasePrincipal::Cast(principal)->ContentScriptAddonPolicy() &&
!aRequest->mURI->SchemeIs("moz-extension")) {
return NS_ERROR_DOM_WEBEXT_CONTENT_SCRIPT_URI;
}
// Check whether the module has been fetched or is currently being fetched,
// and if so wait for it rather than starting a new fetch.
ModuleLoadRequest* request = aRequest->AsModuleRequest();
if (ModuleMapContainsURL(request->mURI)) {
if (ModuleMapContainsURL(request->mURI, aRequest->GetWebExtGlobal())) {
LOG(("ScriptLoadRequest (%p): Waiting for module fetch", aRequest));
WaitForModuleFetch(request->mURI)
WaitForModuleFetch(request->mURI, aRequest->GetWebExtGlobal())
->Then(GetMainThreadSerialEventTarget(), __func__, request,
&ModuleLoadRequest::ModuleLoaded,
&ModuleLoadRequest::LoadFailed);
@@ -1469,7 +1526,9 @@ nsresult ScriptLoader::StartLoad(ScriptLoadRequest* aRequest) {
}
}
nsCOMPtr<nsIScriptGlobalObject> globalObject = GetScriptGlobalObject();
// Module requests aren't cached, so ignore WebExtGlobal here.
nsCOMPtr<nsIScriptGlobalObject> globalObject =
GetScriptGlobalObject(WebExtGlobal::Ignore);
if (!globalObject) {
return NS_ERROR_FAILURE;
}
@@ -1484,6 +1543,8 @@ nsresult ScriptLoader::StartLoad(ScriptLoadRequest* aRequest) {
!js::GlobalHasInstrumentation(globalObject->GetGlobalJSObject()) &&
// Bug 1436400: no bytecode cache support for modules yet.
!aRequest->IsModuleRequest()) {
MOZ_ASSERT(!aRequest->GetWebExtGlobal(),
"Can not bytecode cache WebExt code");
if (!aRequest->IsLoadingSource()) {
// Inform the HTTP cache that we prefer to have information coming from
// the bytecode cache instead of the sources, if such entry is already
@@ -1675,7 +1736,7 @@ ScriptLoadRequest* ScriptLoader::CreateLoadRequest(
nsIURI* referrer = mDocument->GetDocumentURIAsReferrer();
nsCOMPtr<Element> domElement = do_QueryInterface(aElement);
ScriptFetchOptions* fetchOptions = new ScriptFetchOptions(
aCORSMode, aReferrerPolicy, domElement, aTriggeringPrincipal);
aCORSMode, aReferrerPolicy, domElement, aTriggeringPrincipal, nullptr);
if (aKind == ScriptKind::eClassic) {
return new ScriptLoadRequest(aKind, aURI, fetchOptions, aIntegrity,
@@ -2393,7 +2454,7 @@ nsresult ScriptLoader::AttemptAsyncScriptCompile(ScriptLoadRequest* aRequest,
return NS_OK;
}
nsCOMPtr<nsIScriptGlobalObject> globalObject = GetScriptGlobalObject();
nsCOMPtr<nsIGlobalObject> globalObject = GetGlobalForRequest(aRequest);
if (!globalObject) {
return NS_ERROR_FAILURE;
}
@@ -2738,7 +2799,18 @@ void ScriptLoader::FireScriptEvaluated(nsresult aResult,
aRequest->FireScriptEvaluated(aResult);
}
already_AddRefed<nsIScriptGlobalObject> ScriptLoader::GetScriptGlobalObject() {
already_AddRefed<nsIGlobalObject> ScriptLoader::GetGlobalForRequest(
ScriptLoadRequest* aRequest) {
if (aRequest->GetWebExtGlobal()) {
nsCOMPtr<nsIGlobalObject> global = aRequest->GetWebExtGlobal();
return global.forget();
}
return GetScriptGlobalObject(WebExtGlobal::Handled);
}
already_AddRefed<nsIScriptGlobalObject> ScriptLoader::GetScriptGlobalObject(
WebExtGlobal) {
if (!mDocument) {
return nullptr;
}
@@ -2953,17 +3025,28 @@ nsresult ScriptLoader::EvaluateScript(ScriptLoadRequest* aRequest) {
}
}
nsCOMPtr<nsIScriptGlobalObject> globalObject = GetScriptGlobalObject();
if (!globalObject) {
return NS_ERROR_FAILURE;
}
nsCOMPtr<nsIGlobalObject> globalObject;
nsCOMPtr<nsIScriptContext> context;
if (aRequest->GetWebExtGlobal()) {
// Executing a module from a WebExtension content-script.
globalObject = aRequest->GetWebExtGlobal();
} else {
// Otherwise we have to ensure that there is a nsIScriptContext.
nsCOMPtr<nsIScriptGlobalObject> scriptGlobal =
GetScriptGlobalObject(WebExtGlobal::Handled);
if (!scriptGlobal) {
return NS_ERROR_FAILURE;
}
// Make sure context is a strong reference since we access it after
// we've executed a script, which may cause all other references to
// the context to go away.
nsCOMPtr<nsIScriptContext> context = globalObject->GetScriptContext();
if (!context) {
return NS_ERROR_FAILURE;
// Make sure context is a strong reference since we access it after
// we've executed a script, which may cause all other references to
// the context to go away.
context = scriptGlobal->GetScriptContext();
if (!context) {
return NS_ERROR_FAILURE;
}
globalObject = scriptGlobal;
}
#ifdef MOZ_GECKO_PROFILER
@@ -2980,7 +3063,10 @@ nsresult ScriptLoader::EvaluateScript(ScriptLoadRequest* aRequest) {
JSContext* cx = aes.cx();
JS::Rooted<JSObject*> global(cx, globalObject->GetGlobalJSObject());
AutoSetProcessingScriptTag setProcessingScriptTag(context);
Maybe<AutoSetProcessingScriptTag> setProcessingScriptTag;
if (context) {
setProcessingScriptTag.emplace(context);
}
nsresult rv;
{
@@ -3316,7 +3402,9 @@ void ScriptLoader::EncodeBytecode() {
return;
}
nsCOMPtr<nsIScriptGlobalObject> globalObject = GetScriptGlobalObject();
// Should not be encoding modules at all.
nsCOMPtr<nsIScriptGlobalObject> globalObject =
GetScriptGlobalObject(WebExtGlobal::Ignore);
if (!globalObject) {
GiveUpBytecodeEncoding();
return;
@@ -3334,6 +3422,8 @@ void ScriptLoader::EncodeBytecode() {
RefPtr<ScriptLoadRequest> request;
while (!mBytecodeEncodingQueue.isEmpty()) {
request = mBytecodeEncodingQueue.StealFirst();
MOZ_ASSERT(!request->IsModuleRequest());
MOZ_ASSERT(!request->GetWebExtGlobal(), "Not handling global above");
EncodeRequestBytecode(aes.cx(), request);
request->mScriptBytecode.clearAndFree();
request->DropBytecodeCacheReferences();
@@ -3421,7 +3511,8 @@ void ScriptLoader::GiveUpBytecodeEncoding() {
// would not keep a large buffer around. If we cannot, we fallback on the
// removal of all request from the current list and these large buffers would
// be removed at the same time as the source object.
nsCOMPtr<nsIScriptGlobalObject> globalObject = GetScriptGlobalObject();
nsCOMPtr<nsIScriptGlobalObject> globalObject =
GetScriptGlobalObject(WebExtGlobal::Ignore);
AutoAllowLegacyScriptExecution exemption;
Maybe<AutoEntryScript> aes;
@@ -3437,6 +3528,8 @@ void ScriptLoader::GiveUpBytecodeEncoding() {
LOG(("ScriptLoadRequest (%p): Cannot serialize bytecode", request.get()));
TRACE_FOR_TEST_NONE(request->GetScriptElement(),
"scriptloader_bytecode_failed");
MOZ_ASSERT(!request->IsModuleRequest());
MOZ_ASSERT(!request->GetWebExtGlobal());
if (aes.isSome()) {
JS::RootedScript script(aes->cx(), request->mScript);
@@ -3822,6 +3915,9 @@ void ScriptLoader::ReportErrorToConsole(ScriptLoadRequest* aRequest,
message = isScript ? "ScriptSourceMalformed" : "ModuleSourceMalformed";
} else if (aResult == NS_ERROR_DOM_BAD_URI) {
message = isScript ? "ScriptSourceNotAllowed" : "ModuleSourceNotAllowed";
} else if (aResult == NS_ERROR_DOM_WEBEXT_CONTENT_SCRIPT_URI) {
MOZ_ASSERT(!isScript);
message = "WebExtContentScriptModuleSourceNotAllowed";
} else if (net::UrlClassifierFeatureFactory::IsClassifierBlockingErrorCode(
aResult)) {
// Blocking classifier error codes already show their own console messages.

View File

@@ -24,6 +24,7 @@
#include "mozilla/MaybeOneOf.h"
#include "mozilla/MozPromise.h"
#include "ScriptKind.h"
#include "ModuleMapKey.h"
class nsCycleCollectionTraversalCallback;
class nsIChannel;
@@ -626,7 +627,16 @@ class ScriptLoader final : public nsISupports {
void GiveUpBytecodeEncoding();
already_AddRefed<nsIScriptGlobalObject> GetScriptGlobalObject();
already_AddRefed<nsIGlobalObject> GetGlobalForRequest(
ScriptLoadRequest* aRequest);
// This is a marker class to ensure proper handling of requests with a
// WebExtGlobal.
enum class WebExtGlobal { Ignore, Handled };
already_AddRefed<nsIScriptGlobalObject> GetScriptGlobalObject(
WebExtGlobal aWebExtGlobal);
nsresult FillCompileOptionsForRequest(const mozilla::dom::AutoJSAPI& jsapi,
ScriptLoadRequest* aRequest,
JS::Handle<JSObject*> aScopeChain,
@@ -658,11 +668,10 @@ class ScriptLoader final : public nsISupports {
void SetModuleFetchFinishedAndResumeWaitingRequests(
ModuleLoadRequest* aRequest, nsresult aResult);
bool IsFetchingModule(ModuleLoadRequest* aRequest) const;
bool ModuleMapContainsURL(nsIURI* aURL) const;
RefPtr<mozilla::GenericNonExclusivePromise> WaitForModuleFetch(nsIURI* aURL);
ModuleScript* GetFetchedModule(nsIURI* aURL) const;
bool ModuleMapContainsURL(nsIURI* aURL, nsIGlobalObject* aGlobal) const;
RefPtr<mozilla::GenericNonExclusivePromise> WaitForModuleFetch(
nsIURI* aURL, nsIGlobalObject* aGlobal);
ModuleScript* GetFetchedModule(nsIURI* aURL, nsIGlobalObject* aGlobal) const;
friend JSObject* HostResolveImportedModule(
JSContext* aCx, JS::Handle<JS::Value> aReferencingPrivate,
@@ -749,9 +758,9 @@ class ScriptLoader final : public nsISupports {
bool mGiveUpEncoding;
// Module map
nsRefPtrHashtable<nsURIHashKey, mozilla::GenericNonExclusivePromise::Private>
nsRefPtrHashtable<ModuleMapKey, mozilla::GenericNonExclusivePromise::Private>
mFetchingModules;
nsRefPtrHashtable<nsURIHashKey, ModuleScript> mFetchedModules;
nsRefPtrHashtable<ModuleMapKey, ModuleScript> mFetchedModules;
nsCOMPtr<nsIConsoleReportCollector> mReporter;

View File

@@ -20,6 +20,7 @@ EXPORTS += [
EXPORTS.mozilla.dom += [
"AutoEntryScript.h",
"LoadedScript.h",
"ModuleMapKey.h",
"ScriptDecoding.h",
"ScriptElement.h",
"ScriptKind.h",
@@ -32,6 +33,7 @@ UNIFIED_SOURCES += [
"AutoEntryScript.cpp",
"LoadedScript.cpp",
"ModuleLoadRequest.cpp",
"ModuleMapKey.cpp",
"nsIScriptElement.cpp",
"ScriptElement.cpp",
"ScriptLoader.cpp",

View File

@@ -558,6 +558,11 @@ bool IsSandboxPrototypeProxy(JSObject* obj) {
return js::IsProxy(obj) && js::GetProxyHandler(obj) == &sandboxProxyHandler;
}
bool IsWebExtensionContentScriptSandbox(JSObject* obj) {
return IsSandbox(obj) &&
CompartmentPrivate::Get(obj)->isWebExtensionContentScript;
}
} // namespace xpc
// A proxy handler that lets us wrap callables and invoke them with

View File

@@ -138,6 +138,7 @@ bool AllowContentXBLScope(JS::Realm* realm);
JSObject* NACScope(JSObject* global);
bool IsSandboxPrototypeProxy(JSObject* obj);
bool IsWebExtensionContentScriptSandbox(JSObject* obj);
// The JSContext argument represents the Realm that's asking the question. This
// is needed to properly answer without exposing information unnecessarily

View File

@@ -790,6 +790,9 @@ with modules["DOM"]:
# objects being loaded.
errors["NS_ERROR_RECURSIVE_DOCUMENT_LOAD"] = FAILURE(1038)
# WebExtension content script may not load this URL.
errors["NS_ERROR_DOM_WEBEXT_CONTENT_SCRIPT_URI"] = FAILURE(1039)
# May be used to indicate when e.g. setting a property value didn't
# actually change the value, like for obj.foo = "bar"; obj.foo = "bar";
# the second assignment throws NS_SUCCESS_DOM_NO_OPERATION.