Bug 1759881 - Part 9: Move module loading hooks into the generic module loader r=yulia
Now we can move the JS engine integration hooks into the generic module loader. There is still some code there to deal with web extension globals. This will be removed in a later patch. Differential Revision: https://phabricator.services.mozilla.com/D141735
This commit is contained in:
@@ -18,13 +18,17 @@
|
||||
#include "js/OffThreadScriptCompilation.h"
|
||||
#include "js/PropertyAndElement.h" // JS_DefineProperty, JS_GetElement
|
||||
#include "js/SourceText.h"
|
||||
#include "mozilla/BasePrincipal.h"
|
||||
#include "mozilla/dom/AutoEntryScript.h"
|
||||
#include "mozilla/CycleCollectedJSContext.h" // nsAutoMicroTask
|
||||
#include "mozilla/Preferences.h"
|
||||
#include "nsContentUtils.h"
|
||||
#include "nsICacheInfoChannel.h" //nsICacheInfoChannel
|
||||
#include "nsNetUtil.h" // NS_NewURI
|
||||
#include "xpcpublic.h"
|
||||
|
||||
using JS::SourceText;
|
||||
using mozilla::Preferences;
|
||||
using mozilla::dom::AutoJSAPI;
|
||||
|
||||
namespace JS::loader {
|
||||
|
||||
@@ -105,6 +109,240 @@ NS_IMPL_CYCLE_COLLECTION(ModuleLoaderBase, mFetchingModules, mFetchedModules,
|
||||
NS_IMPL_CYCLE_COLLECTING_ADDREF(ModuleLoaderBase)
|
||||
NS_IMPL_CYCLE_COLLECTING_RELEASE(ModuleLoaderBase)
|
||||
|
||||
// static
|
||||
void ModuleLoaderBase::EnsureModuleHooksInitialized() {
|
||||
AutoJSAPI jsapi;
|
||||
jsapi.Init();
|
||||
JSRuntime* rt = JS_GetRuntime(jsapi.cx());
|
||||
if (JS::GetModuleResolveHook(rt)) {
|
||||
return;
|
||||
}
|
||||
|
||||
JS::SetModuleResolveHook(rt, HostResolveImportedModule);
|
||||
JS::SetModuleMetadataHook(rt, HostPopulateImportMeta);
|
||||
JS::SetScriptPrivateReferenceHooks(rt, HostAddRefTopLevelScript,
|
||||
HostReleaseTopLevelScript);
|
||||
JS::SetSupportedAssertionsHook(rt, HostGetSupportedImportAssertions);
|
||||
|
||||
Preferences::RegisterCallbackAndCall(DynamicImportPrefChangedCallback,
|
||||
"javascript.options.dynamicImport",
|
||||
(void*)nullptr);
|
||||
}
|
||||
|
||||
// static
|
||||
void ModuleLoaderBase::DynamicImportPrefChangedCallback(const char* aPrefName,
|
||||
void* aClosure) {
|
||||
bool enabled = Preferences::GetBool(aPrefName);
|
||||
JS::ModuleDynamicImportHook hook =
|
||||
enabled ? HostImportModuleDynamically : nullptr;
|
||||
|
||||
AutoJSAPI jsapi;
|
||||
jsapi.Init();
|
||||
JSRuntime* rt = JS_GetRuntime(jsapi.cx());
|
||||
JS::SetModuleDynamicImportHook(rt, hook);
|
||||
}
|
||||
|
||||
// 8.1.3.8.1 HostResolveImportedModule(referencingModule, moduleRequest)
|
||||
/**
|
||||
* Implement the HostResolveImportedModule abstract operation.
|
||||
*
|
||||
* Resolve a module specifier string and look this up in the module
|
||||
* map, returning the result. This is only called for previously
|
||||
* loaded modules and always succeeds.
|
||||
*
|
||||
* @param aReferencingPrivate A JS::Value which is either undefined
|
||||
* or contains a LoadedScript private pointer.
|
||||
* @param aModuleRequest A module request object.
|
||||
* @returns module This is set to the module found.
|
||||
*/
|
||||
// static
|
||||
JSObject* ModuleLoaderBase::HostResolveImportedModule(
|
||||
JSContext* aCx, JS::Handle<JS::Value> aReferencingPrivate,
|
||||
JS::Handle<JSObject*> aModuleRequest) {
|
||||
JS::Rooted<JSObject*> module(aCx);
|
||||
|
||||
{
|
||||
// LoadedScript should only live in this block, otherwise it will be a GC
|
||||
// hazard
|
||||
RefPtr<LoadedScript> script(
|
||||
GetLoadedScriptOrNull(aCx, aReferencingPrivate));
|
||||
|
||||
JS::Rooted<JSString*> specifierString(
|
||||
aCx, JS::GetModuleRequestSpecifier(aCx, aModuleRequest));
|
||||
if (!specifierString) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Let url be the result of resolving a module specifier given referencing
|
||||
// module script and specifier.
|
||||
nsAutoJSString string;
|
||||
if (!string.init(aCx, specifierString)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
RefPtr<ModuleLoaderBase> loader = GetCurrentModuleLoader(aCx);
|
||||
if (!loader) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIURI> uri =
|
||||
ModuleLoaderBase::ResolveModuleSpecifier(loader, script, string);
|
||||
|
||||
// This cannot fail because resolving a module specifier must have been
|
||||
// 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 (mozilla::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, global);
|
||||
MOZ_ASSERT(ms, "Resolved module not found in module map");
|
||||
MOZ_ASSERT(!ms->HasParseError());
|
||||
MOZ_ASSERT(ms->ModuleRecord());
|
||||
|
||||
module.set(ms->ModuleRecord());
|
||||
}
|
||||
return module;
|
||||
}
|
||||
|
||||
// static
|
||||
bool ModuleLoaderBase::HostPopulateImportMeta(
|
||||
JSContext* aCx, JS::Handle<JS::Value> aReferencingPrivate,
|
||||
JS::Handle<JSObject*> aMetaObject) {
|
||||
RefPtr<ModuleScript> script =
|
||||
static_cast<ModuleScript*>(aReferencingPrivate.toPrivate());
|
||||
MOZ_ASSERT(script->IsModuleScript());
|
||||
MOZ_ASSERT(JS::GetModulePrivate(script->ModuleRecord()) ==
|
||||
aReferencingPrivate);
|
||||
|
||||
nsAutoCString url;
|
||||
MOZ_DIAGNOSTIC_ASSERT(script->BaseURL());
|
||||
MOZ_ALWAYS_SUCCEEDS(script->BaseURL()->GetAsciiSpec(url));
|
||||
|
||||
JS::Rooted<JSString*> urlString(aCx, JS_NewStringCopyZ(aCx, url.get()));
|
||||
if (!urlString) {
|
||||
JS_ReportOutOfMemory(aCx);
|
||||
return false;
|
||||
}
|
||||
|
||||
return JS_DefineProperty(aCx, aMetaObject, "url", urlString,
|
||||
JSPROP_ENUMERATE);
|
||||
}
|
||||
|
||||
// static
|
||||
bool ModuleLoaderBase::HostImportModuleDynamically(
|
||||
JSContext* aCx, JS::Handle<JS::Value> aReferencingPrivate,
|
||||
JS::Handle<JSObject*> aModuleRequest, JS::Handle<JSObject*> aPromise) {
|
||||
RefPtr<LoadedScript> script(GetLoadedScriptOrNull(aCx, aReferencingPrivate));
|
||||
|
||||
JS::Rooted<JSString*> specifierString(
|
||||
aCx, JS::GetModuleRequestSpecifier(aCx, aModuleRequest));
|
||||
if (!specifierString) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Attempt to resolve the module specifier.
|
||||
nsAutoJSString specifier;
|
||||
if (!specifier.init(aCx, specifierString)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
RefPtr<ModuleLoaderBase> loader = GetCurrentModuleLoader(aCx);
|
||||
if (!loader) {
|
||||
return false;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIURI> uri =
|
||||
ModuleLoaderBase::ResolveModuleSpecifier(loader, script, specifier);
|
||||
if (!uri) {
|
||||
JS::Rooted<JS::Value> error(aCx);
|
||||
nsresult rv = ModuleLoaderBase::HandleResolveFailure(aCx, script, specifier,
|
||||
0, 0, &error);
|
||||
if (NS_FAILED(rv)) {
|
||||
JS_ReportOutOfMemory(aCx);
|
||||
return false;
|
||||
}
|
||||
|
||||
JS_SetPendingException(aCx, error);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Create a new top-level load request.
|
||||
RefPtr<ModuleLoadRequest> request = loader->CreateDynamicImport(
|
||||
aCx, uri, script, aReferencingPrivate, specifierString, aPromise);
|
||||
|
||||
loader->StartDynamicImport(request);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ModuleLoaderBase::HostGetSupportedImportAssertions(
|
||||
JSContext* aCx, JS::ImportAssertionVector& aValues) {
|
||||
MOZ_ASSERT(aValues.empty());
|
||||
|
||||
if (!aValues.reserve(1)) {
|
||||
JS_ReportOutOfMemory(aCx);
|
||||
return false;
|
||||
}
|
||||
|
||||
aValues.infallibleAppend(JS::ImportAssertion::Type);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// static
|
||||
ModuleLoaderBase* ModuleLoaderBase::GetCurrentModuleLoader(JSContext* aCx) {
|
||||
auto reportError = mozilla::MakeScopeExit([aCx]() {
|
||||
JS_ReportErrorASCII(aCx, "No ScriptLoader found for the current context");
|
||||
});
|
||||
|
||||
JS::Rooted<JSObject*> object(aCx, JS::CurrentGlobalOrNull(aCx));
|
||||
if (!object) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
nsIGlobalObject* global = xpc::NativeGlobal(object);
|
||||
if (!global) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
ModuleLoaderBase* loader = global->GetModuleLoader(aCx);
|
||||
if (!loader) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
reportError.release();
|
||||
return loader;
|
||||
}
|
||||
|
||||
// static
|
||||
LoadedScript* ModuleLoaderBase::GetLoadedScriptOrNull(
|
||||
JSContext* aCx, JS::Handle<JS::Value> aReferencingPrivate) {
|
||||
if (aReferencingPrivate.isUndefined()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
auto* script = static_cast<LoadedScript*>(aReferencingPrivate.toPrivate());
|
||||
if (script->IsEventScript()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
MOZ_ASSERT_IF(
|
||||
script->IsModuleScript(),
|
||||
JS::GetModulePrivate(script->AsModuleScript()->ModuleRecord()) ==
|
||||
aReferencingPrivate);
|
||||
|
||||
return script;
|
||||
}
|
||||
|
||||
bool ModuleLoaderBase::ModuleMapContainsURL(nsIURI* aURL,
|
||||
nsIGlobalObject* aGlobal) const {
|
||||
// Returns whether we have fetched, or are currently fetching, a module script
|
||||
@@ -241,7 +479,7 @@ nsresult ModuleLoaderBase::CreateModuleScript(ModuleLoadRequest* aRequest) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
mozilla::dom::AutoJSAPI jsapi;
|
||||
AutoJSAPI jsapi;
|
||||
if (!jsapi.Init(globalObject)) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
@@ -398,7 +636,7 @@ nsresult ModuleLoaderBase::ResolveRequestedModules(
|
||||
ModuleLoadRequest* aRequest, nsCOMArray<nsIURI>* aUrlsOut) {
|
||||
ModuleScript* ms = aRequest->mModuleScript;
|
||||
|
||||
mozilla::dom::AutoJSAPI jsapi;
|
||||
AutoJSAPI jsapi;
|
||||
if (!jsapi.Init(ms->ModuleRecord())) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
@@ -569,7 +807,7 @@ void ModuleLoaderBase::StartDynamicImport(ModuleLoadRequest* aRequest) {
|
||||
|
||||
void ModuleLoaderBase::FinishDynamicImportAndReject(ModuleLoadRequest* aRequest,
|
||||
nsresult aResult) {
|
||||
mozilla::dom::AutoJSAPI jsapi;
|
||||
AutoJSAPI jsapi;
|
||||
MOZ_ASSERT(NS_FAILED(aResult));
|
||||
MOZ_ALWAYS_TRUE(jsapi.Init(aRequest->mDynamicPromise));
|
||||
FinishDynamicImport(jsapi.cx(), aRequest, aResult, nullptr);
|
||||
@@ -613,7 +851,9 @@ void ModuleLoaderBase::FinishDynamicImport(
|
||||
}
|
||||
|
||||
ModuleLoaderBase::ModuleLoaderBase(ScriptLoaderInterface* aLoader)
|
||||
: mLoader(aLoader) {}
|
||||
: mLoader(aLoader) {
|
||||
EnsureModuleHooksInitialized();
|
||||
}
|
||||
|
||||
ModuleLoaderBase::~ModuleLoaderBase() {
|
||||
mDynamicImportRequests.CancelRequestsAndClear();
|
||||
@@ -688,7 +928,7 @@ bool ModuleLoaderBase::InstantiateModuleTree(ModuleLoadRequest* aRequest) {
|
||||
|
||||
MOZ_ASSERT(moduleScript->ModuleRecord());
|
||||
|
||||
mozilla::dom::AutoJSAPI jsapi;
|
||||
AutoJSAPI jsapi;
|
||||
if (NS_WARN_IF(!jsapi.Init(moduleScript->ModuleRecord()))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user