forked from mirrors/gecko-dev
Bug 1803810 - Part 5: Add global option to ChromeUtils.importESModule. r=jonco,ochameau
Add { global: "current" } option to import the module into the current global,
either shared or non-shared.
Also add the following:
* { global: "shared" }: the existing default behavior, except for the implicit contextual behavior
* { global: "devtools" }: equivalent of loadInDevToolsLoader: true
* { global: "contextual" }: the explicit version of the contextual behavior
The later patch is going to drop the loadInDevToolsLoader option and also the
implicit contextual behavior, and require global option for modules in the
DevTools global.
Differential Revision: https://phabricator.services.mozilla.com/D199457
This commit is contained in:
parent
fe5f5aa268
commit
7d0c8da822
2 changed files with 154 additions and 6 deletions
|
|
@ -32,6 +32,7 @@
|
|||
#include "mozilla/ScopeExit.h"
|
||||
#include "mozilla/ScrollingMetrics.h"
|
||||
#include "mozilla/SharedStyleSheetCache.h"
|
||||
#include "mozilla/SpinEventLoopUntil.h"
|
||||
#include "mozilla/TimeStamp.h"
|
||||
#include "mozilla/dom/ContentParent.h"
|
||||
#include "mozilla/dom/IdleDeadline.h"
|
||||
|
|
@ -618,22 +619,127 @@ static mozJSModuleLoader* GetContextualESLoader(
|
|||
return mozJSModuleLoader::Get();
|
||||
}
|
||||
|
||||
static mozJSModuleLoader* GetModuleLoaderForCurrentGlobal(
|
||||
JSContext* aCx, const GlobalObject& aGlobal,
|
||||
Maybe<loader::NonSharedGlobalSyncModuleLoaderScope>&
|
||||
aMaybeSyncLoaderScope) {
|
||||
nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(aGlobal.GetAsSupports());
|
||||
|
||||
if (mozJSModuleLoader::IsSharedSystemGlobal(global)) {
|
||||
return mozJSModuleLoader::Get();
|
||||
}
|
||||
if (mozJSModuleLoader::IsDevToolsLoaderGlobal(global)) {
|
||||
return mozJSModuleLoader::GetOrCreateDevToolsLoader();
|
||||
}
|
||||
|
||||
if (loader::NonSharedGlobalSyncModuleLoaderScope::IsActive()) {
|
||||
mozJSModuleLoader* moduleloader =
|
||||
loader::NonSharedGlobalSyncModuleLoaderScope::ActiveLoader();
|
||||
|
||||
if (!moduleloader->IsLoaderGlobal(global->GetGlobalJSObject())) {
|
||||
JS_ReportErrorASCII(aCx,
|
||||
"global: \"current\" option cannot be used for "
|
||||
"different global while other importESModule "
|
||||
"with global: \"current\" is on the stack");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return moduleloader;
|
||||
}
|
||||
|
||||
RefPtr targetModuleLoader = global->GetModuleLoader(aCx);
|
||||
if (!targetModuleLoader) {
|
||||
// Sandbox without associated window returns nullptr for GetModuleLoader.
|
||||
JS_ReportErrorASCII(aCx, "No ModuleLoader found for the current context");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (targetModuleLoader->HasFetchingModules()) {
|
||||
if (!mozilla::SpinEventLoopUntil(
|
||||
"importESModule for current global"_ns, [&]() -> bool {
|
||||
return !targetModuleLoader->HasFetchingModules();
|
||||
})) {
|
||||
JS_ReportErrorASCII(aCx, "Failed to wait for ongoing module requests");
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
aMaybeSyncLoaderScope.emplace(aCx, global);
|
||||
return aMaybeSyncLoaderScope->ActiveLoader();
|
||||
}
|
||||
|
||||
static mozJSModuleLoader* GetModuleLoaderForOptions(
|
||||
JSContext* aCx, const GlobalObject& aGlobal,
|
||||
const ImportESModuleOptionsDictionary& aOptions,
|
||||
Maybe<loader::NonSharedGlobalSyncModuleLoaderScope>&
|
||||
aMaybeSyncLoaderScope) {
|
||||
if (!aOptions.mGlobal.WasPassed()) {
|
||||
return GetContextualESLoader(aOptions.mLoadInDevToolsLoader, aGlobal.Get());
|
||||
}
|
||||
|
||||
switch (aOptions.mGlobal.Value()) {
|
||||
case ImportESModuleTargetGlobal::Shared:
|
||||
return mozJSModuleLoader::Get();
|
||||
|
||||
case ImportESModuleTargetGlobal::Devtools:
|
||||
return mozJSModuleLoader::GetOrCreateDevToolsLoader();
|
||||
|
||||
case ImportESModuleTargetGlobal::Contextual: {
|
||||
RefPtr devToolsModuleloader = mozJSModuleLoader::GetDevToolsLoader();
|
||||
if (devToolsModuleloader &&
|
||||
devToolsModuleloader->IsLoaderGlobal(aGlobal.Get())) {
|
||||
return mozJSModuleLoader::GetOrCreateDevToolsLoader();
|
||||
}
|
||||
return mozJSModuleLoader::Get();
|
||||
}
|
||||
|
||||
case ImportESModuleTargetGlobal::Current:
|
||||
return GetModuleLoaderForCurrentGlobal(aCx, aGlobal,
|
||||
aMaybeSyncLoaderScope);
|
||||
|
||||
default:
|
||||
MOZ_CRASH("Unknown ImportESModuleTargetGlobal");
|
||||
}
|
||||
}
|
||||
|
||||
static bool ValidateImportOptions(
|
||||
JSContext* aCx, const ImportESModuleOptionsDictionary& aOptions) {
|
||||
if (aOptions.mGlobal.WasPassed() &&
|
||||
aOptions.mLoadInDevToolsLoader.WasPassed()) {
|
||||
JS_ReportErrorASCII(aCx,
|
||||
"global option and loadInDevToolsLoader option "
|
||||
"cannot be used at the same time");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/* static */
|
||||
void ChromeUtils::ImportESModule(
|
||||
const GlobalObject& aGlobal, const nsAString& aResourceURI,
|
||||
const ImportESModuleOptionsDictionary& aOptions,
|
||||
JS::MutableHandle<JSObject*> aRetval, ErrorResult& aRv) {
|
||||
RefPtr moduleloader =
|
||||
GetContextualESLoader(aOptions.mLoadInDevToolsLoader, aGlobal.Get());
|
||||
MOZ_ASSERT(moduleloader);
|
||||
JSContext* cx = aGlobal.Context();
|
||||
|
||||
if (!ValidateImportOptions(cx, aOptions)) {
|
||||
aRv.Throw(NS_ERROR_FAILURE);
|
||||
return;
|
||||
}
|
||||
|
||||
Maybe<loader::NonSharedGlobalSyncModuleLoaderScope> maybeSyncLoaderScope;
|
||||
RefPtr<mozJSModuleLoader> moduleloader =
|
||||
GetModuleLoaderForOptions(cx, aGlobal, aOptions, maybeSyncLoaderScope);
|
||||
if (!moduleloader) {
|
||||
aRv.Throw(NS_ERROR_FAILURE);
|
||||
return;
|
||||
}
|
||||
|
||||
NS_ConvertUTF16toUTF8 registryLocation(aResourceURI);
|
||||
|
||||
AUTO_PROFILER_LABEL_DYNAMIC_NSCSTRING_NONSENSITIVE(
|
||||
"ChromeUtils::ImportESModule", OTHER, registryLocation);
|
||||
|
||||
JSContext* cx = aGlobal.Context();
|
||||
|
||||
JS::Rooted<JSObject*> moduleNamespace(cx);
|
||||
nsresult rv =
|
||||
moduleloader->ImportESModule(cx, registryLocation, &moduleNamespace);
|
||||
|
|
@ -649,6 +755,10 @@ void ChromeUtils::ImportESModule(
|
|||
return;
|
||||
}
|
||||
aRetval.set(moduleNamespace);
|
||||
|
||||
if (maybeSyncLoaderScope) {
|
||||
maybeSyncLoaderScope->Finish();
|
||||
}
|
||||
}
|
||||
|
||||
namespace lazy_getter {
|
||||
|
|
|
|||
|
|
@ -513,6 +513,7 @@ partial namespace ChromeUtils {
|
|||
* 'aResourceURI'.
|
||||
*
|
||||
* @param aResourceURI A resource:// URI string to load the module from.
|
||||
* @param aOption An option to specify where to load the module into.
|
||||
* @returns the module's namespace object.
|
||||
*
|
||||
* The implementation maintains a hash of aResourceURI->global obj.
|
||||
|
|
@ -520,7 +521,8 @@ partial namespace ChromeUtils {
|
|||
* the same file will not cause the module to be re-evaluated.
|
||||
*/
|
||||
[Throws]
|
||||
object importESModule(DOMString aResourceURI, optional ImportESModuleOptionsDictionary options = {});
|
||||
object importESModule(DOMString aResourceURI,
|
||||
optional ImportESModuleOptionsDictionary aOptions = {});
|
||||
|
||||
/**
|
||||
* Defines a property on the given target which lazily imports a JavaScript
|
||||
|
|
@ -983,6 +985,40 @@ dictionary CompileScriptOptionsDictionary {
|
|||
boolean hasReturnValue = false;
|
||||
};
|
||||
|
||||
/**
|
||||
* Where the modules are loaded into with importESModule.
|
||||
*/
|
||||
enum ImportESModuleTargetGlobal {
|
||||
/**
|
||||
* Load into the shared system global.
|
||||
* This is the default value.
|
||||
*/
|
||||
"shared",
|
||||
|
||||
/**
|
||||
* Load into a distinct system global for DevTools, so that the DevTools can
|
||||
* load a distinct set of modules and do not interfere with its debuggee.
|
||||
*/
|
||||
"devtools",
|
||||
|
||||
/**
|
||||
* If the current global is DevTools' distinct system global, load into the
|
||||
* DevTools' distinct system global.
|
||||
* Otherwise load into the shared system global.
|
||||
*
|
||||
* This is a temporary workaround until DevTools modules are ESMified.
|
||||
*/
|
||||
"contextual",
|
||||
|
||||
/**
|
||||
* Load into current global.
|
||||
*
|
||||
* This can be used for any global. If this is used for shared global or
|
||||
* devtools global, this has the same effect as "shared" or "devtools".
|
||||
*/
|
||||
"current",
|
||||
};
|
||||
|
||||
dictionary ImportESModuleOptionsDictionary {
|
||||
/**
|
||||
* If true, a distinct module loader will be used, in the system principal,
|
||||
|
|
@ -990,6 +1026,8 @@ dictionary ImportESModuleOptionsDictionary {
|
|||
* of modules and do not interfere with its debuggee.
|
||||
*/
|
||||
boolean loadInDevToolsLoader;
|
||||
|
||||
ImportESModuleTargetGlobal global;
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
|
|||
Loading…
Reference in a new issue