Bug 1342012 - Initial browser support for dynamic import from module scripts r=smaug

This commit is contained in:
Jon Coppeard 2018-12-06 16:52:15 -05:00
Родитель 20a91427ef
Коммит ce265eb48d
23 изменённых файлов: 450 добавлений и 166 удалений

Просмотреть файл

@ -5,6 +5,9 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "ModuleLoadRequest.h"
#include "mozilla/HoldDropJSObjects.h"
#include "ModuleScript.h"
#include "ScriptLoader.h"
@ -18,35 +21,92 @@ namespace dom {
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(ModuleLoadRequest)
NS_INTERFACE_MAP_END_INHERITING(ScriptLoadRequest)
NS_IMPL_CYCLE_COLLECTION_INHERITED(ModuleLoadRequest, ScriptLoadRequest,
mBaseURL, mLoader, mModuleScript, mImports)
NS_IMPL_CYCLE_COLLECTION_CLASS(ModuleLoadRequest)
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(ModuleLoadRequest,
ScriptLoadRequest)
NS_IMPL_CYCLE_COLLECTION_UNLINK(mBaseURL, mLoader, mModuleScript, mImports)
tmp->ClearDynamicImport();
NS_IMPL_CYCLE_COLLECTION_UNLINK_END
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(ModuleLoadRequest,
ScriptLoadRequest)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mBaseURL, mLoader, mModuleScript, mImports)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN_INHERITED(ModuleLoadRequest,
ScriptLoadRequest)
NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mDynamicReferencingPrivate)
NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mDynamicSpecifier)
NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mDynamicPromise)
NS_IMPL_CYCLE_COLLECTION_TRACE_END
NS_IMPL_ADDREF_INHERITED(ModuleLoadRequest, ScriptLoadRequest)
NS_IMPL_RELEASE_INHERITED(ModuleLoadRequest, ScriptLoadRequest)
ModuleLoadRequest::ModuleLoadRequest(nsIURI* aURI,
ScriptFetchOptions* aFetchOptions,
const SRIMetadata& aIntegrity,
nsIURI* aReferrer, ScriptLoader* aLoader)
static VisitedURLSet* NewVisitedSetForTopLevelImport(nsIURI* aURI) {
auto set = new VisitedURLSet();
set->PutEntry(aURI);
return set;
}
/* static */ ModuleLoadRequest* ModuleLoadRequest::CreateTopLevel(
nsIURI* aURI, ScriptFetchOptions* aFetchOptions,
const SRIMetadata& aIntegrity, nsIURI* aReferrer, ScriptLoader* aLoader) {
return new ModuleLoadRequest(aURI, aFetchOptions, aIntegrity, aReferrer,
true, /* is top level */
false, /* is dynamic import */
aLoader, NewVisitedSetForTopLevelImport(aURI));
}
/* static */ ModuleLoadRequest* ModuleLoadRequest::CreateStaticImport(
nsIURI* aURI, ModuleLoadRequest* aParent) {
auto request =
new ModuleLoadRequest(aURI, aParent->mFetchOptions, SRIMetadata(),
aParent->mURI, false, /* is top level */
false, /* is dynamic import */
aParent->mLoader, aParent->mVisitedSet);
request->mIsInline = false;
request->mScriptMode = aParent->mScriptMode;
return request;
}
/* static */ ModuleLoadRequest* ModuleLoadRequest::CreateDynamicImport(
nsIURI* aURI, ModuleScript* aScript,
JS::Handle<JS::Value> aReferencingPrivate, JS::Handle<JSString*> aSpecifier,
JS::Handle<JSObject*> aPromise) {
MOZ_ASSERT(aSpecifier);
MOZ_ASSERT(aPromise);
auto request = new ModuleLoadRequest(
aURI, aScript->FetchOptions(), SRIMetadata(), aScript->BaseURL(),
true, /* is top level */
true, /* is dynamic import */
aScript->Loader(), NewVisitedSetForTopLevelImport(aURI));
request->mIsInline = false;
request->mScriptMode = ScriptMode::eAsync;
request->mDynamicReferencingPrivate = aReferencingPrivate;
request->mDynamicSpecifier = aSpecifier;
request->mDynamicPromise = aPromise;
HoldJSObjects(request);
return request;
}
ModuleLoadRequest::ModuleLoadRequest(
nsIURI* aURI, ScriptFetchOptions* aFetchOptions,
const SRIMetadata& aIntegrity, nsIURI* aReferrer, bool aIsTopLevel,
bool aIsDynamicImport, ScriptLoader* aLoader, VisitedURLSet* aVisitedSet)
: ScriptLoadRequest(ScriptKind::eModule, aURI, aFetchOptions, aIntegrity,
aReferrer),
mIsTopLevel(true),
mIsTopLevel(aIsTopLevel),
mIsDynamicImport(aIsDynamicImport),
mLoader(aLoader),
mVisitedSet(new VisitedURLSet()) {
mVisitedSet->PutEntry(aURI);
}
ModuleLoadRequest::ModuleLoadRequest(nsIURI* aURI, ModuleLoadRequest* aParent)
: ScriptLoadRequest(ScriptKind::eModule, aURI, aParent->mFetchOptions,
SRIMetadata(), aParent->mURI),
mIsTopLevel(false),
mLoader(aParent->mLoader),
mVisitedSet(aParent->mVisitedSet) {
MOZ_ASSERT(mVisitedSet->Contains(aURI));
mIsInline = false;
mScriptMode = aParent->mScriptMode;
}
mVisitedSet(aVisitedSet) {}
void ModuleLoadRequest::Cancel() {
ScriptLoadRequest::Cancel();
@ -138,5 +198,11 @@ void ModuleLoadRequest::LoadFinished() {
mLoader = nullptr;
}
void ModuleLoadRequest::ClearDynamicImport() {
mDynamicReferencingPrivate = JS::UndefinedValue();
mDynamicSpecifier = nullptr;
mDynamicPromise = nullptr;
}
} // namespace dom
} // namespace mozilla

Просмотреть файл

@ -36,22 +36,40 @@ class ModuleLoadRequest final : public ScriptLoadRequest {
ModuleLoadRequest(const ModuleLoadRequest& aOther) = delete;
ModuleLoadRequest(ModuleLoadRequest&& aOther) = delete;
public:
NS_DECL_ISUPPORTS_INHERITED
NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(ModuleLoadRequest, ScriptLoadRequest)
// Create a top-level module load request.
ModuleLoadRequest(nsIURI* aURI, ScriptFetchOptions* aFetchOptions,
const SRIMetadata& aIntegrity, nsIURI* aReferrer,
ScriptLoader* aLoader);
bool aIsTopLevel, bool aIsDynamicImport,
ScriptLoader* aLoader, VisitedURLSet* aVisitedSet);
// Create a module load request for an imported module.
ModuleLoadRequest(nsIURI* aURI, ModuleLoadRequest* aParent);
public:
NS_DECL_ISUPPORTS_INHERITED
NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS_INHERITED(ModuleLoadRequest,
ScriptLoadRequest)
// Create a top-level module load request.
static ModuleLoadRequest* CreateTopLevel(nsIURI* aURI,
ScriptFetchOptions* aFetchOptions,
const SRIMetadata& aIntegrity,
nsIURI* aReferrer,
ScriptLoader* aLoader);
// Create a module load request for a static module import.
static ModuleLoadRequest* CreateStaticImport(nsIURI* aURI,
ModuleLoadRequest* aParent);
// Create a module load request for dynamic module import.
static ModuleLoadRequest* CreateDynamicImport(
nsIURI* aURI, ModuleScript* aScript,
JS::Handle<JS::Value> aReferencingPrivate,
JS::Handle<JSString*> aSpecifier, JS::Handle<JSObject*> aPromise);
bool IsTopLevel() const override { return mIsTopLevel; }
bool IsDynamicImport() const { return mIsDynamicImport; }
void SetReady() override;
void Cancel() override;
void ClearDynamicImport();
void ModuleLoaded();
void ModuleErrored();
@ -66,6 +84,9 @@ class ModuleLoadRequest final : public ScriptLoadRequest {
// Is this a request for a top level module script or an import?
const bool mIsTopLevel;
// Is this the top level request for a dynamic module import?
const bool mIsDynamicImport;
// The base URL used for resolving relative module imports.
nsCOMPtr<nsIURI> mBaseURL;
@ -88,6 +109,11 @@ class ModuleLoadRequest final : public ScriptLoadRequest {
// Set of module URLs visited while fetching the module graph this request is
// part of.
RefPtr<VisitedURLSet> mVisitedSet;
// For dynamic imports, the details to pass to FinishDynamicImport.
JS::Heap<JS::Value> mDynamicReferencingPrivate;
JS::Heap<JSString*> mDynamicSpecifier;
JS::Heap<JSObject*> mDynamicPromise;
};
} // namespace dom

Просмотреть файл

@ -5,7 +5,10 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "ModuleScript.h"
#include "mozilla/HoldDropJSObjects.h"
#include "jsfriendapi.h"
#include "ScriptLoader.h"
namespace mozilla {
@ -20,6 +23,7 @@ NS_IMPL_CYCLE_COLLECTION_CLASS(ModuleScript)
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(ModuleScript)
NS_IMPL_CYCLE_COLLECTION_UNLINK(mLoader)
NS_IMPL_CYCLE_COLLECTION_UNLINK(mFetchOptions)
NS_IMPL_CYCLE_COLLECTION_UNLINK(mBaseURL)
tmp->UnlinkModuleRecord();
tmp->mParseError.setUndefined();
@ -28,6 +32,7 @@ NS_IMPL_CYCLE_COLLECTION_UNLINK_END
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(ModuleScript)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mLoader)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mFetchOptions)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(ModuleScript)
@ -39,9 +44,14 @@ NS_IMPL_CYCLE_COLLECTION_TRACE_END
NS_IMPL_CYCLE_COLLECTING_ADDREF(ModuleScript)
NS_IMPL_CYCLE_COLLECTING_RELEASE(ModuleScript)
ModuleScript::ModuleScript(ScriptLoader* aLoader, nsIURI* aBaseURL)
: mLoader(aLoader), mBaseURL(aBaseURL), mSourceElementAssociated(false) {
ModuleScript::ModuleScript(ScriptLoader* aLoader,
ScriptFetchOptions* aFetchOptions, nsIURI* aBaseURL)
: mLoader(aLoader),
mFetchOptions(aFetchOptions),
mBaseURL(aBaseURL),
mSourceElementAssociated(false) {
MOZ_ASSERT(mLoader);
MOZ_ASSERT(mFetchOptions);
MOZ_ASSERT(mBaseURL);
MOZ_ASSERT(!mModuleRecord);
MOZ_ASSERT(!HasParseError());
@ -49,11 +59,14 @@ ModuleScript::ModuleScript(ScriptLoader* aLoader, nsIURI* aBaseURL)
}
void ModuleScript::UnlinkModuleRecord() {
// Remove module's back reference to this object request if present.
// Remove the module record's pointer to this object if present and
// decrement our reference count. The reference is added by
// SetModuleRecord() below.
if (mModuleRecord) {
MOZ_ASSERT(JS::GetModulePrivate(mModuleRecord).toPrivate() == this);
JS::SetModulePrivate(mModuleRecord, JS::UndefinedValue());
mModuleRecord = nullptr;
Release();
}
}
@ -70,10 +83,21 @@ void ModuleScript::SetModuleRecord(JS::Handle<JSObject*> aModuleRecord) {
mModuleRecord = aModuleRecord;
// Make module's host defined field point to this module script object.
// This is cleared in the UnlinkModuleRecord().
// Make module's host defined field point to this object and
// increment our reference count. This is decremented by
// UnlinkModuleRecord() above.
JS::SetModulePrivate(mModuleRecord, JS::PrivateValue(this));
HoldJSObjects(this);
AddRef();
}
void HostFinalizeTopLevelScript(JSFreeOp* aFop, const JS::Value& aPrivate) {
auto script = static_cast<ModuleScript*>(aPrivate.toPrivate());
if (script) {
MOZ_ASSERT(JS::GetModulePrivate(script->mModuleRecord.unbarrieredGet()) ==
aPrivate);
script->UnlinkModuleRecord();
}
}
void ModuleScript::SetParseError(const JS::Value& aError) {
@ -88,7 +112,6 @@ void ModuleScript::SetParseError(const JS::Value& aError) {
void ModuleScript::SetErrorToRethrow(const JS::Value& aError) {
MOZ_ASSERT(!aError.isUndefined());
MOZ_ASSERT(!HasErrorToRethrow());
// This is only called after SetModuleRecord() or SetParseError() so we don't
// need to call HoldJSObjects() here.

Просмотреть файл

@ -18,8 +18,11 @@ namespace dom {
class ScriptLoader;
void HostFinalizeTopLevelScript(JSFreeOp* aFop, const JS::Value& aPrivate);
class ModuleScript final : public nsISupports {
RefPtr<ScriptLoader> mLoader;
RefPtr<ScriptFetchOptions> mFetchOptions;
nsCOMPtr<nsIURI> mBaseURL;
JS::Heap<JSObject*> mModuleRecord;
JS::Heap<JS::Value> mParseError;
@ -32,7 +35,8 @@ class ModuleScript final : public nsISupports {
NS_DECL_CYCLE_COLLECTING_ISUPPORTS
NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(ModuleScript)
ModuleScript(ScriptLoader* aLoader, nsIURI* aBaseURL);
ModuleScript(ScriptLoader* aLoader, ScriptFetchOptions* aFetchOptions,
nsIURI* aBaseURL);
void SetModuleRecord(JS::Handle<JSObject*> aModuleRecord);
void SetParseError(const JS::Value& aError);
@ -40,6 +44,7 @@ class ModuleScript final : public nsISupports {
void SetSourceElementAssociated();
ScriptLoader* Loader() const { return mLoader; }
ScriptFetchOptions* FetchOptions() const { return mFetchOptions; }
JSObject* ModuleRecord() const { return mModuleRecord; }
nsIURI* BaseURL() const { return mBaseURL; }
JS::Value ParseError() const { return mParseError; }
@ -49,6 +54,8 @@ class ModuleScript final : public nsISupports {
bool SourceElementAssociated() const { return mSourceElementAssociated; }
void UnlinkModuleRecord();
friend void HostFinalizeTopLevelScript(JSFreeOp*, const JS::Value&);
};
} // namespace dom

Просмотреть файл

@ -4,7 +4,7 @@
* 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 "ModuleLoadRequest.h"
#include "ScriptLoadRequest.h"
#include "mozilla/HoldDropJSObjects.h"
#include "mozilla/Unused.h"
@ -52,6 +52,7 @@ NS_IMPL_CYCLE_COLLECTION_CLASS(ScriptLoadRequest)
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(ScriptLoadRequest)
NS_IMPL_CYCLE_COLLECTION_UNLINK(mFetchOptions)
NS_IMPL_CYCLE_COLLECTION_UNLINK(mCacheInfo)
tmp->mScript = nullptr;
tmp->DropBytecodeCacheReferences();
NS_IMPL_CYCLE_COLLECTION_UNLINK_END
@ -105,6 +106,8 @@ ScriptLoadRequest::~ScriptLoadRequest() {
if (mScript) {
DropBytecodeCacheReferences();
}
DropJSObjects(this);
}
void ScriptLoadRequest::SetReady() {
@ -207,6 +210,12 @@ void ScriptLoadRequest::ClearScriptSource() {
}
}
void ScriptLoadRequest::SetScript(JSScript* aScript) {
MOZ_ASSERT(!mScript);
mScript = aScript;
HoldJSObjects(this);
}
//////////////////////////////////////////////////////////////
// ScriptLoadRequestList
//////////////////////////////////////////////////////////////

Просмотреть файл

@ -210,6 +210,8 @@ class ScriptLoadRequest
void ClearScriptSource();
void SetScript(JSScript* aScript);
void MaybeCancelOffThreadScript();
void DropBytecodeCacheReferences();

Просмотреть файл

@ -113,9 +113,9 @@ NS_INTERFACE_MAP_END
NS_IMPL_CYCLE_COLLECTION(ScriptLoader, mNonAsyncExternalScriptInsertedRequests,
mLoadingAsyncRequests, mLoadedAsyncRequests,
mDeferRequests, mXSLTRequests, mParserBlockingRequest,
mBytecodeEncodingQueue, mPreloads,
mPendingChildLoaders, mFetchedModules)
mDeferRequests, mXSLTRequests, mDynamicImportRequests,
mParserBlockingRequest, mBytecodeEncodingQueue,
mPreloads, mPendingChildLoaders, mFetchedModules)
NS_IMPL_CYCLE_COLLECTING_ADDREF(ScriptLoader)
NS_IMPL_CYCLE_COLLECTING_RELEASE(ScriptLoader)
@ -133,6 +133,7 @@ ScriptLoader::ScriptLoader(nsIDocument* aDocument)
mGiveUpEncoding(false),
mReporter(new ConsoleReportCollector()) {
LOG(("ScriptLoader::ScriptLoader %p", this));
EnsureModuleHooksInitialized();
}
ScriptLoader::~ScriptLoader() {
@ -164,6 +165,11 @@ ScriptLoader::~ScriptLoader() {
req->FireScriptAvailable(NS_ERROR_ABORT);
}
for (ScriptLoadRequest* req = mDynamicImportRequests.getFirst(); req;
req = req->getNext()) {
FinishDynamicImport(req->AsModuleRequest(), NS_ERROR_ABORT);
}
for (ScriptLoadRequest* req =
mNonAsyncExternalScriptInsertedRequests.getFirst();
req; req = req->getNext()) {
@ -471,7 +477,7 @@ nsresult ScriptLoader::CreateModuleScript(ModuleLoadRequest* aRequest) {
MOZ_ASSERT(NS_SUCCEEDED(rv) == (module != nullptr));
RefPtr<ModuleScript> moduleScript =
new ModuleScript(this, aRequest->mBaseURL);
new ModuleScript(this, aRequest->mFetchOptions, aRequest->mBaseURL);
aRequest->mModuleScript = moduleScript;
if (!module) {
@ -689,7 +695,8 @@ RefPtr<GenericPromise> ScriptLoader::StartFetchingModuleAndDependencies(
ModuleLoadRequest* aParent, nsIURI* aURI) {
MOZ_ASSERT(aURI);
RefPtr<ModuleLoadRequest> childRequest = new ModuleLoadRequest(aURI, aParent);
RefPtr<ModuleLoadRequest> childRequest =
ModuleLoadRequest::CreateStaticImport(aURI, aParent);
aParent->mImports.AppendElement(childRequest);
@ -725,6 +732,12 @@ JSObject* HostResolveImportedModule(JSContext* aCx,
JS::Handle<JS::Value> aReferencingPrivate,
JS::Handle<JSString*> aSpecifier) {
// Let referencing module script be referencingModule.[[HostDefined]].
if (aReferencingPrivate.isUndefined()) {
JS_ReportErrorNumberUC(aCx, js::GetErrorMessage, nullptr,
JSMSG_IMPORT_SCRIPT_NOT_FOUND);
return nullptr;
}
auto script = static_cast<ModuleScript*>(aReferencingPrivate.toPrivate());
MOZ_ASSERT(JS::GetModulePrivate(script->ModuleRecord()) ==
aReferencingPrivate);
@ -774,14 +787,114 @@ bool HostPopulateImportMeta(JSContext* aCx,
JSPROP_ENUMERATE);
}
static void EnsureModuleResolveHook(JSContext* aCx) {
JSRuntime* rt = JS_GetRuntime(aCx);
bool HostImportModuleDynamically(JSContext* aCx,
JS::Handle<JS::Value> aReferencingPrivate,
JS::Handle<JSString*> aSpecifier,
JS::Handle<JSObject*> aPromise) {
if (aReferencingPrivate.isUndefined()) {
JS_ReportErrorNumberUC(aCx, js::GetErrorMessage, nullptr,
JSMSG_IMPORT_SCRIPT_NOT_FOUND);
return false;
}
auto script = static_cast<ModuleScript*>(aReferencingPrivate.toPrivate());
MOZ_ASSERT(JS::GetModulePrivate(script->ModuleRecord()) ==
aReferencingPrivate);
// Attempt to resolve the module specifier.
nsAutoJSString string;
if (!string.init(aCx, aSpecifier)) {
return false;
}
nsCOMPtr<nsIURI> uri = ResolveModuleSpecifier(script, string);
if (!uri) {
JS_ReportErrorNumberUC(aCx, js::GetErrorMessage, nullptr,
JSMSG_BAD_MODULE_SPECIFIER, string.get());
return false;
}
// Create a new top-level load request.
RefPtr<ModuleLoadRequest> request = ModuleLoadRequest::CreateDynamicImport(
uri, script, aReferencingPrivate, aSpecifier, aPromise);
script->Loader()->StartDynamicImport(request);
return true;
}
void ScriptLoader::StartDynamicImport(ModuleLoadRequest* aRequest) {
LOG(("ScriptLoadRequest (%p): Start dynamic import", aRequest));
mDynamicImportRequests.AppendElement(aRequest);
nsresult rv = StartLoad(aRequest);
if (NS_FAILED(rv)) {
FinishDynamicImport(aRequest, rv);
}
}
void ScriptLoader::FinishDynamicImport(ModuleLoadRequest* aRequest,
nsresult aResult) {
AutoJSAPI jsapi;
MOZ_ALWAYS_TRUE(jsapi.Init(aRequest->mDynamicPromise));
FinishDynamicImport(jsapi.cx(), aRequest, aResult);
}
void ScriptLoader::FinishDynamicImport(JSContext* aCx,
ModuleLoadRequest* aRequest,
nsresult aResult) {
LOG(("ScriptLoadRequest (%p): Finish dynamic import %d %d", aRequest,
unsigned(aResult), JS_IsExceptionPending(aCx)));
// Complete the dynamic import, report failures indicated by aResult or as a
// pending exception on the context.
if (NS_FAILED(aResult)) {
MOZ_ASSERT(!JS_IsExceptionPending(aCx));
JS_ReportErrorNumberUC(aCx, js::GetErrorMessage, nullptr,
JSMSG_IMPORT_SCRIPT_NOT_FOUND);
}
JS::Rooted<JS::Value> referencingScript(aCx,
aRequest->mDynamicReferencingPrivate);
JS::Rooted<JSString*> specifier(aCx, aRequest->mDynamicSpecifier);
JS::Rooted<JSObject*> promise(aCx, aRequest->mDynamicPromise);
JS::FinishDynamicModuleImport(aCx, referencingScript, specifier, promise);
// FinishDynamicModuleImport clears any pending exception.
MOZ_ASSERT(!JS_IsExceptionPending(aCx));
aRequest->ClearDynamicImport();
}
static void 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);
}
void ScriptLoader::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::SetScriptPrivateFinalizeHook(rt, HostFinalizeTopLevelScript);
Preferences::RegisterCallbackAndCall(DynamicImportPrefChangedCallback,
"javascript.options.dynamicImport",
(void*)nullptr);
}
void ScriptLoader::CheckModuleDependenciesLoaded(ModuleLoadRequest* aRequest) {
@ -815,18 +928,34 @@ class ScriptRequestProcessor : public Runnable {
: Runnable("dom::ScriptRequestProcessor"),
mLoader(aLoader),
mRequest(aRequest) {}
NS_IMETHOD Run() override { return mLoader->ProcessRequest(mRequest); }
NS_IMETHOD Run() override {
if (mRequest->IsModuleRequest() &&
mRequest->AsModuleRequest()->IsDynamicImport()) {
mLoader->ProcessDynamicImport(mRequest->AsModuleRequest());
return NS_OK;
}
return mLoader->ProcessRequest(mRequest);
}
};
void ScriptLoader::RunScriptWhenSafe(ScriptLoadRequest* aRequest) {
auto runnable = new ScriptRequestProcessor(this, aRequest);
nsContentUtils::AddScriptRunner(runnable);
}
void ScriptLoader::ProcessLoadedModuleTree(ModuleLoadRequest* aRequest) {
MOZ_ASSERT(aRequest->IsReadyToRun());
if (aRequest->IsTopLevel()) {
if (aRequest->mIsInline &&
aRequest->Element()->GetParserCreated() == NOT_FROM_PARSER) {
if (aRequest->IsDynamicImport()) {
MOZ_ASSERT(aRequest->isInList());
RefPtr<ScriptLoadRequest> req = mDynamicImportRequests.Steal(aRequest);
RunScriptWhenSafe(req);
} else if (aRequest->mIsInline &&
aRequest->Element()->GetParserCreated() == NOT_FROM_PARSER) {
MOZ_ASSERT(!aRequest->isInList());
nsContentUtils::AddScriptRunner(
new ScriptRequestProcessor(this, aRequest));
RunScriptWhenSafe(aRequest);
} else {
MaybeMoveToLoadedList(aRequest);
ProcessPendingRequests();
@ -884,8 +1013,6 @@ bool ScriptLoader::InstantiateModuleTree(ModuleLoadRequest* aRequest) {
return false;
}
EnsureModuleResolveHook(jsapi.cx());
JS::Rooted<JSObject*> module(jsapi.cx(), moduleScript->ModuleRecord());
bool ok = NS_SUCCEEDED(nsJSUtils::ModuleInstantiate(jsapi.cx(), module));
@ -1210,7 +1337,8 @@ ScriptLoadRequest* ScriptLoader::CreateLoadRequest(
}
MOZ_ASSERT(aKind == ScriptKind::eModule);
return new ModuleLoadRequest(aURI, fetchOptions, aIntegrity, referrer, this);
return ModuleLoadRequest::CreateTopLevel(aURI, fetchOptions, aIntegrity,
referrer, this);
}
bool ScriptLoader::ProcessScriptElement(nsIScriptElement* aElement) {
@ -1538,7 +1666,7 @@ bool ScriptLoader::ProcessInlineScript(nsIScriptElement* aElement,
NS_ASSERTION(
!nsContentUtils::IsSafeToRunScript(),
"A script-inserted script is inserted without an update batch?");
nsContentUtils::AddScriptRunner(new ScriptRequestProcessor(this, request));
RunScriptWhenSafe(request);
return false;
}
if (aElement->GetParserCreated() == FROM_PARSER_NETWORK &&
@ -1915,8 +2043,7 @@ nsresult ScriptLoader::ProcessRequest(ScriptLoadRequest* aRequest) {
if (aRequest->IsModuleRequest()) {
ModuleLoadRequest* request = aRequest->AsModuleRequest();
if (request->mModuleScript &&
!request->mModuleScript->HasErrorToRethrow()) {
if (request->mModuleScript) {
if (!InstantiateModuleTree(request)) {
request->mModuleScript = nullptr;
}
@ -2018,6 +2145,25 @@ nsresult ScriptLoader::ProcessRequest(ScriptLoadRequest* aRequest) {
return rv;
}
void ScriptLoader::ProcessDynamicImport(ModuleLoadRequest* aRequest) {
if (aRequest->mModuleScript) {
if (!InstantiateModuleTree(aRequest)) {
aRequest->mModuleScript = nullptr;
}
}
nsresult rv = NS_ERROR_FAILURE;
if (aRequest->mModuleScript) {
rv = EvaluateScript(aRequest);
}
if (NS_FAILED(rv)) {
FinishDynamicImport(aRequest, rv);
}
return;
}
void ScriptLoader::FireScriptAvailable(nsresult aResult,
ScriptLoadRequest* aRequest) {
for (int32_t i = 0; i < mObservers.Count(); i++) {
@ -2062,7 +2208,7 @@ already_AddRefed<nsIScriptGlobalObject> ScriptLoader::GetScriptGlobalObject() {
}
nsresult ScriptLoader::FillCompileOptionsForRequest(
const AutoJSAPI& jsapi, ScriptLoadRequest* aRequest,
const mozilla::dom::AutoJSAPI& jsapi, ScriptLoadRequest* aRequest,
JS::Handle<JSObject*> aScopeChain, JS::CompileOptions* aOptions) {
// It's very important to use aRequest->mURI, not the final URI of the channel
// aRequest ended up getting script data from, as the script filename.
@ -2203,6 +2349,19 @@ nsresult ScriptLoader::FillCompileOptionsForRequest(
return true;
}
class MOZ_RAII AutoSetProcessingScriptTag {
nsCOMPtr<nsIScriptContext> mContext;
bool mOldTag;
public:
explicit AutoSetProcessingScriptTag(nsIScriptContext* aContext)
: mContext(aContext), mOldTag(mContext->GetProcessingScriptTag()) {
mContext->SetProcessingScriptTag(true);
}
~AutoSetProcessingScriptTag() { mContext->SetProcessingScriptTag(mOldTag); }
};
nsresult ScriptLoader::EvaluateScript(ScriptLoadRequest* aRequest) {
using namespace mozilla::Telemetry;
MOZ_ASSERT(aRequest->IsReadyToRun());
@ -2242,8 +2401,8 @@ nsresult ScriptLoader::EvaluateScript(ScriptLoadRequest* aRequest) {
JSContext* cx = aes.cx();
JS::Rooted<JSObject*> global(cx, globalObject->GetGlobalJSObject());
bool oldProcessingScriptTag = context->GetProcessingScriptTag();
context->SetProcessingScriptTag(true);
AutoSetProcessingScriptTag setProcessingScriptTag(context);
nsresult rv;
{
if (aRequest->IsModuleRequest()) {
@ -2255,8 +2414,6 @@ nsresult ScriptLoader::EvaluateScript(ScriptLoadRequest* aRequest) {
// currentScript is set to null for modules.
AutoCurrentScriptUpdater scriptUpdater(this, nullptr);
EnsureModuleResolveHook(cx);
ModuleLoadRequest* request = aRequest->AsModuleRequest();
MOZ_ASSERT(request->mModuleScript);
MOZ_ASSERT(!request->mOffThreadToken);
@ -2267,7 +2424,13 @@ nsresult ScriptLoader::EvaluateScript(ScriptLoadRequest* aRequest) {
aRequest));
JS::Rooted<JS::Value> error(cx, moduleScript->ErrorToRethrow());
JS_SetPendingException(cx, error);
return NS_OK; // An error is reported by AutoEntryScript.
// For a dynamic import, the promise is rejected. Otherwise an error is
// either reported by AutoEntryScript.
if (request->IsDynamicImport()) {
FinishDynamicImport(cx, request, NS_OK);
}
return NS_OK;
}
JS::Rooted<JSObject*> module(cx, moduleScript->ModuleRecord());
@ -2280,9 +2443,16 @@ nsresult ScriptLoader::EvaluateScript(ScriptLoadRequest* aRequest) {
rv = nsJSUtils::ModuleEvaluate(cx, module);
MOZ_ASSERT(NS_FAILED(rv) == aes.HasException());
if (NS_FAILED(rv)) {
LOG(("ScriptLoadRequest (%p): evaluation failed", aRequest));
rv = NS_OK; // An error is reported by AutoEntryScript.
// For a dynamic import, the promise is rejected. Otherwise an error is
// either reported by AutoEntryScript.
rv = NS_OK;
}
if (request->IsDynamicImport()) {
FinishDynamicImport(cx, request, rv);
}
aRequest->mCacheInfo = nullptr;
@ -2354,8 +2524,7 @@ nsresult ScriptLoader::EvaluateScript(ScriptLoadRequest* aRequest) {
// Queue the current script load request to later save the bytecode.
if (script && encodeBytecode) {
aRequest->mScript = script;
HoldJSObjects(aRequest);
aRequest->SetScript(script);
TRACE_FOR_TEST(aRequest->Element(), "scriptloader_encode");
MOZ_ASSERT(aRequest->mBytecodeOffset ==
aRequest->mScriptBytecode.length());
@ -2379,7 +2548,6 @@ nsresult ScriptLoader::EvaluateScript(ScriptLoadRequest* aRequest) {
MaybeTriggerBytecodeEncoding();
}
context->SetProcessingScriptTag(oldProcessingScriptTag);
return rv;
}
@ -2567,7 +2735,8 @@ bool ScriptLoader::HasPendingRequests() {
return mParserBlockingRequest || !mXSLTRequests.isEmpty() ||
!mLoadedAsyncRequests.isEmpty() ||
!mNonAsyncExternalScriptInsertedRequests.isEmpty() ||
!mDeferRequests.isEmpty() || !mPendingChildLoaders.IsEmpty();
!mDeferRequests.isEmpty() || !mDynamicImportRequests.isEmpty() ||
!mPendingChildLoaders.IsEmpty();
}
void ScriptLoader::ProcessPendingRequestsAsync() {
@ -2980,12 +3149,31 @@ void ScriptLoader::HandleLoadError(ScriptLoadRequest* aRequest,
RefPtr<ScriptLoadRequest> req = mXSLTRequests.Steal(aRequest);
FireScriptAvailable(aResult, req);
}
} else if (aRequest->IsModuleRequest() && !aRequest->IsPreload()) {
} else if (aRequest->IsPreload()) {
if (aRequest->IsModuleRequest()) {
aRequest->Cancel();
}
if (aRequest->IsTopLevel()) {
MOZ_ALWAYS_TRUE(
mPreloads.RemoveElement(aRequest, PreloadRequestComparator()));
}
MOZ_ASSERT(!aRequest->isInList());
AccumulateCategorical(LABELS_DOM_SCRIPT_PRELOAD_RESULT::LoadError);
} else if (aRequest->IsModuleRequest()) {
ModuleLoadRequest* modReq = aRequest->AsModuleRequest();
MOZ_ASSERT(!modReq->IsTopLevel());
MOZ_ASSERT(!modReq->isInList());
modReq->Cancel();
// A single error is fired for the top level module.
if (modReq->IsDynamicImport()) {
MOZ_ASSERT(modReq->IsTopLevel());
if (aRequest->isInList()) {
RefPtr<ScriptLoadRequest> req = mDynamicImportRequests.Steal(aRequest);
modReq->Cancel();
FinishDynamicImport(modReq, aResult);
}
} else {
MOZ_ASSERT(!modReq->IsTopLevel());
MOZ_ASSERT(!modReq->isInList());
modReq->Cancel();
// The error is handled for the top level module.
}
} else if (mParserBlockingRequest == aRequest) {
MOZ_ASSERT(!aRequest->isInList());
mParserBlockingRequest = nullptr;
@ -3000,16 +3188,6 @@ void ScriptLoader::HandleLoadError(ScriptLoadRequest* aRequest,
FireScriptAvailable(aResult, aRequest);
ContinueParserAsync(aRequest);
mCurrentParserInsertedScript = oldParserInsertedScript;
} else if (aRequest->IsPreload()) {
if (aRequest->IsModuleRequest()) {
aRequest->Cancel();
}
if (aRequest->IsTopLevel()) {
MOZ_ALWAYS_TRUE(
mPreloads.RemoveElement(aRequest, PreloadRequestComparator()));
}
MOZ_ASSERT(!aRequest->isInList());
AccumulateCategorical(LABELS_DOM_SCRIPT_PRELOAD_RESULT::LoadError);
} else {
// This happens for blocking requests cancelled by ParsingComplete().
MOZ_ASSERT(aRequest->IsCanceled());
@ -3110,11 +3288,12 @@ nsresult ScriptLoader::PrepareLoadedRequest(ScriptLoadRequest* aRequest,
mLoadingAsyncRequests.Contains(aRequest) ||
mNonAsyncExternalScriptInsertedRequests.Contains(aRequest) ||
mXSLTRequests.Contains(aRequest) ||
mDynamicImportRequests.Contains(aRequest) ||
(aRequest->IsModuleRequest() &&
!aRequest->AsModuleRequest()->IsTopLevel() &&
!aRequest->isInList()) ||
mPreloads.Contains(aRequest, PreloadRequestComparator()) ||
mParserBlockingRequest,
mParserBlockingRequest == aRequest,
"aRequest should be pending!");
if (aRequest->IsModuleRequest()) {
@ -3191,6 +3370,13 @@ void ScriptLoader::ParsingComplete(bool aTerminated) {
mLoadedAsyncRequests.Clear();
mNonAsyncExternalScriptInsertedRequests.Clear();
mXSLTRequests.Clear();
for (ScriptLoadRequest* req = mDynamicImportRequests.getFirst(); req;
req = req->getNext()) {
req->Cancel();
FinishDynamicImport(req->AsModuleRequest(), NS_ERROR_ABORT);
}
if (mParserBlockingRequest) {
mParserBlockingRequest->Cancel();
mParserBlockingRequest = nullptr;

Просмотреть файл

@ -310,9 +310,16 @@ class ScriptLoader final : public nsISupports {
*/
void Destroy() { GiveUpBytecodeEncoding(); }
void StartDynamicImport(ModuleLoadRequest* aRequest);
void FinishDynamicImport(ModuleLoadRequest* aRequest, nsresult aResult);
void FinishDynamicImport(JSContext* aCx, ModuleLoadRequest* aRequest,
nsresult aResult);
private:
virtual ~ScriptLoader();
void EnsureModuleHooksInitialized();
ScriptLoadRequest* CreateLoadRequest(
ScriptKind aKind, nsIURI* aURI, nsIScriptElement* aElement,
nsIPrincipal* aTriggeringPrincipal, mozilla::CORSMode aCORSMode,
@ -419,6 +426,7 @@ class ScriptLoader final : public nsISupports {
nsresult AttemptAsyncScriptCompile(ScriptLoadRequest* aRequest,
bool* aCouldCompileOut);
nsresult ProcessRequest(ScriptLoadRequest* aRequest);
void ProcessDynamicImport(ModuleLoadRequest* aRequest);
nsresult CompileOffThreadOrProcessRequest(ScriptLoadRequest* aRequest);
void FireScriptAvailable(nsresult aResult, ScriptLoadRequest* aRequest);
void FireScriptEvaluated(nsresult aResult, ScriptLoadRequest* aRequest);
@ -500,6 +508,8 @@ class ScriptLoader final : public nsISupports {
nsresult AssociateSourceElementsForModuleTree(JSContext* aCx,
ModuleLoadRequest* aRequest);
void RunScriptWhenSafe(ScriptLoadRequest* aRequest);
nsIDocument* mDocument; // [WEAK]
nsCOMArray<nsIScriptLoaderObserver> mObservers;
ScriptLoadRequestList mNonAsyncExternalScriptInsertedRequests;
@ -509,6 +519,7 @@ class ScriptLoader final : public nsISupports {
ScriptLoadRequestList mLoadedAsyncRequests;
ScriptLoadRequestList mDeferRequests;
ScriptLoadRequestList mXSLTRequests;
ScriptLoadRequestList mDynamicImportRequests;
RefPtr<ScriptLoadRequest> mParserBlockingRequest;
// List of script load request that are holding a buffer which has to be saved

Просмотреть файл

@ -1584,6 +1584,9 @@ pref("javascript.options.streams", true);
// BigInt API
pref("javascript.options.bigint", false);
// Dynamic module import.
pref("javascript.options.dynamicImport", false);
// advanced prefs
pref("advanced.mailftp", false);
pref("image.animation_mode", "normal");

Просмотреть файл

@ -1 +1,2 @@
lsan-allowed: [Init, nsHostResolver::ResolveHost]
prefs: [javascript.options.dynamicImport:true, security.csp.experimentalEnabled:true]

Просмотреть файл

@ -2,6 +2,3 @@
[Basic dynamic imports]
expected: FAIL
[Dynamic imports should resolve module.]
expected: FAIL

Просмотреть файл

@ -1,5 +1,5 @@
[inline-event-handler.html]
expected: ERROR
expected: TIMEOUT
[dynamic import should work when triggered from inline event handlers]
expected: TIMEOUT

Просмотреть файл

@ -5,6 +5,3 @@
[propagate-nonce-external-module]
expected: FAIL
[Dynamically imported module should eval when imported from script w/ a valid nonce.]
expected: FAIL

Просмотреть файл

@ -5,6 +5,3 @@
[propagate-nonce-inline-module]
expected: FAIL
[Dynamically imported module should eval when imported from script w/ a valid nonce.]
expected: FAIL

Просмотреть файл

@ -1,17 +1,16 @@
[string-compilation-base-url-external-classic.html]
expected: ERROR
[setTimeout should successfully import]
expected: TIMEOUT
expected: FAIL
[eval should successfully import]
expected: NOTRUN
expected: FAIL
[Function should successfully import]
expected: NOTRUN
expected: FAIL
[reflected-inline-event-handlers should successfully import]
expected: NOTRUN
expected: FAIL
[inline-event-handlers-UA-code should successfully import]
expected: NOTRUN
expected: FAIL

Просмотреть файл

@ -1,17 +1,10 @@
[string-compilation-base-url-external-module.html]
expected: ERROR
[setTimeout should successfully import]
expected: TIMEOUT
[eval should successfully import]
expected: NOTRUN
[Function should successfully import]
expected: NOTRUN
expected: FAIL
[reflected-inline-event-handlers should successfully import]
expected: NOTRUN
expected: FAIL
[inline-event-handlers-UA-code should successfully import]
expected: NOTRUN
expected: FAIL

Просмотреть файл

@ -1,17 +1,16 @@
[string-compilation-base-url-inline-classic.html]
expected: ERROR
[setTimeout should successfully import]
expected: TIMEOUT
expected: FAIL
[eval should successfully import]
expected: NOTRUN
expected: FAIL
[the Function constructor should successfully import]
expected: NOTRUN
expected: FAIL
[reflected inline event handlers should successfully import]
expected: NOTRUN
expected: FAIL
[inline event handlers triggered via UA code should successfully import]
expected: NOTRUN
expected: FAIL

Просмотреть файл

@ -1,17 +1,10 @@
[string-compilation-base-url-inline-module.html]
expected: ERROR
[setTimeout should successfully import]
expected: TIMEOUT
[eval should successfully import]
expected: NOTRUN
[the Function constructor should successfully import]
expected: NOTRUN
expected: FAIL
[reflected inline event handlers should successfully import]
expected: NOTRUN
expected: FAIL
[inline event handlers triggered via UA code should successfully import]
expected: NOTRUN
expected: FAIL

Просмотреть файл

@ -1,17 +1,16 @@
[string-compilation-classic.html]
expected: ERROR
[eval should successfully import]
expected: FAIL
[setTimeout should successfully import]
expected: TIMEOUT
expected: FAIL
[the Function constructor should successfully import]
expected: NOTRUN
expected: FAIL
[reflected inline event handlers should successfully import]
expected: NOTRUN
expected: FAIL
[inline event handlers triggered via UA code should successfully import]
expected: NOTRUN
expected: FAIL

Просмотреть файл

@ -1,17 +1,10 @@
[string-compilation-module.html]
expected: ERROR
[eval should successfully import]
[setTimeout should successfully import]
expected: FAIL
[setTimeout should successfully import]
expected: TIMEOUT
[the Function constructor should successfully import]
expected: NOTRUN
[reflected inline event handlers should successfully import]
expected: NOTRUN
expected: FAIL
[inline event handlers triggered via UA code should successfully import]
expected: NOTRUN
expected: FAIL

Просмотреть файл

@ -1,20 +1,19 @@
[string-compilation-nonce-classic.html]
expected: ERROR
[setTimeout must inherit the nonce from the triggering script, thus execute]
expected: TIMEOUT
expected: FAIL
[direct eval must inherit the nonce from the triggering script, thus execute]
expected: NOTRUN
expected: FAIL
[indirect eval must inherit the nonce from the triggering script, thus execute]
expected: NOTRUN
expected: FAIL
[the Function constructor must inherit the nonce from the triggering script, thus execute]
expected: NOTRUN
expected: FAIL
[reflected inline event handlers must inherit the nonce from the triggering script, thus execute]
expected: NOTRUN
expected: FAIL
[inline event handlers triggered via UA code must inherit the nonce from the triggering script, thus execute]
expected: NOTRUN
expected: FAIL

Просмотреть файл

@ -1,20 +1,10 @@
[string-compilation-nonce-module.html]
expected: ERROR
[setTimeout must inherit the nonce from the triggering script, thus execute]
expected: TIMEOUT
[direct eval must inherit the nonce from the triggering script, thus execute]
expected: NOTRUN
[indirect eval must inherit the nonce from the triggering script, thus execute]
expected: NOTRUN
[the Function constructor must inherit the nonce from the triggering script, thus execute]
expected: NOTRUN
expected: FAIL
[reflected inline event handlers must inherit the nonce from the triggering script, thus execute]
expected: NOTRUN
expected: FAIL
[inline event handlers triggered via UA code must inherit the nonce from the triggering script, thus execute]
expected: NOTRUN
expected: FAIL

Просмотреть файл

@ -2,12 +2,6 @@
[Evaled the script via eval, successful import]
expected: FAIL
[Evaled the script via eval, failed import]
expected: FAIL
[Evaled the script via Function, successful import]
expected: FAIL
[Evaled the script via Function, failed import]
expected: FAIL