diff options
Diffstat (limited to 'dom/script/ScriptLoader.cpp')
-rw-r--r-- | dom/script/ScriptLoader.cpp | 263 |
1 files changed, 150 insertions, 113 deletions
diff --git a/dom/script/ScriptLoader.cpp b/dom/script/ScriptLoader.cpp index adc046b7c..a53098974 100644 --- a/dom/script/ScriptLoader.cpp +++ b/dom/script/ScriptLoader.cpp @@ -418,21 +418,23 @@ void ScriptLoader::SetModuleFetchFinishedAndResumeWaitingRequests(ModuleLoadRequest *aRequest, nsresult aResult) { - // Update module map with the result of fetching a single module script. The - // module script pointer is nullptr on error. - - MOZ_ASSERT(!aRequest->IsReadyToRun()); + // Update module map with the result of fetching a single module script. + // + // If any requests for the same URL are waiting on this one to complete, they + // will have ModuleLoaded or LoadFailed on them when the promise is + // resolved/rejected. This is set up in StartLoad. RefPtr<GenericPromise::Private> promise; MOZ_ALWAYS_TRUE(mFetchingModules.Get(aRequest->mURI, getter_AddRefs(promise))); mFetchingModules.Remove(aRequest->mURI); - RefPtr<ModuleScript> ms(aRequest->mModuleScript); - MOZ_ASSERT(NS_SUCCEEDED(aResult) == (ms != nullptr)); - mFetchedModules.Put(aRequest->mURI, ms); + RefPtr<ModuleScript> moduleScript(aRequest->mModuleScript); + MOZ_ASSERT(NS_FAILED(aResult) == !moduleScript); + + mFetchedModules.Put(aRequest->mURI, moduleScript); if (promise) { - if (ms) { + if (moduleScript) { promise->Resolve(true, __func__); } else { promise->Reject(aResult, __func__); @@ -478,19 +480,29 @@ ScriptLoader::ProcessFetchedModuleSource(ModuleLoadRequest* aRequest) MOZ_ASSERT(!aRequest->mModuleScript); nsresult rv = CreateModuleScript(aRequest); + MOZ_ASSERT(NS_FAILED(rv) == !aRequest->mModuleScript); + SetModuleFetchFinishedAndResumeWaitingRequests(aRequest, rv); free(aRequest->mScriptTextBuf); aRequest->mScriptTextBuf = nullptr; aRequest->mScriptTextLength = 0; - if (NS_SUCCEEDED(rv)) { + if (NS_FAILED(rv)) { + aRequest->LoadFailed(); + return rv; + } + + if (!aRequest->mModuleScript->IsErrored()) { StartFetchingModuleDependencies(aRequest); } - return rv; + return NS_OK; } +static nsresult +ResolveRequestedModules(ModuleLoadRequest* aRequest, nsCOMArray<nsIURI>& aUrls); + nsresult ScriptLoader::CreateModuleScript(ModuleLoadRequest* aRequest) { @@ -544,9 +556,34 @@ ScriptLoader::CreateModuleScript(ModuleLoadRequest* aRequest) } } MOZ_ASSERT(NS_SUCCEEDED(rv) == (module != nullptr)); - if (module) { - aRequest->mModuleScript = - new ModuleScript(this, aRequest->mBaseURL, module); + RefPtr<ModuleScript> moduleScript = new ModuleScript(this, aRequest->mBaseURL); + aRequest->mModuleScript = moduleScript; + + if (!module) { + // Compilation failed + MOZ_ASSERT(aes.HasException()); + JS::Rooted<JS::Value> error(cx); + if (!aes.StealException(&error)) { + aRequest->mModuleScript = nullptr; + return NS_ERROR_FAILURE; + } + + moduleScript->SetPreInstantiationError(error); + aRequest->ModuleErrored(); + return NS_OK; + } + + moduleScript->SetModuleRecord(module); + + // Validate requested modules and treat failure to resolve module specifiers + // the same as a parse error. + nsCOMArray<nsIURI> urls; + rv = ResolveRequestedModules(aRequest, urls); + if (NS_FAILED(rv)) { + // ResolveRequestedModules sets pre-instanitation error on failure. + MOZ_ASSERT(moduleScript->IsErrored()); + aRequest->ModuleErrored(); + return NS_OK; } } @@ -556,8 +593,8 @@ ScriptLoader::CreateModuleScript(ModuleLoadRequest* aRequest) } static bool -ThrowTypeError(JSContext* aCx, ModuleScript* aScript, - const nsString& aMessage) +CreateTypeError(JSContext* aCx, ModuleScript* aScript, const nsString& aMessage, + JS::MutableHandle<JS::Value> aError) { JS::Rooted<JSObject*> module(aCx, aScript->ModuleRecord()); JS::Rooted<JSScript*> script(aCx, JS::GetModuleScript(aCx, module)); @@ -572,17 +609,11 @@ ThrowTypeError(JSContext* aCx, ModuleScript* aScript, return false; } - JS::Rooted<JS::Value> error(aCx); - if (!JS::CreateError(aCx, JSEXN_TYPEERR, nullptr, filename, 0, 0, nullptr, - message, &error)) { - return false; - } - - JS_SetPendingException(aCx, error); - return false; + return JS::CreateError(aCx, JSEXN_TYPEERR, nullptr, filename, 0, 0, nullptr, + message, aError); } -static bool +static nsresult HandleResolveFailure(JSContext* aCx, ModuleScript* aScript, const nsAString& aSpecifier) { @@ -591,19 +622,13 @@ HandleResolveFailure(JSContext* aCx, ModuleScript* aScript, nsAutoString message(NS_LITERAL_STRING("Error resolving module specifier: ")); message.Append(aSpecifier); - return ThrowTypeError(aCx, aScript, message); -} - -static bool -HandleModuleNotFound(JSContext* aCx, ModuleScript* aScript, - const nsAString& aSpecifier) -{ - // TODO: How can we get the line number of the failed import? - - nsAutoString message(NS_LITERAL_STRING("Resolved module not found in map: ")); - message.Append(aSpecifier); + JS::Rooted<JS::Value> error(aCx); + if (!CreateTypeError(aCx, aScript, message, &error)) { + return NS_ERROR_OUT_OF_MEMORY; + } - return ThrowTypeError(aCx, aScript, message); + aScript->SetPreInstantiationError(error); + return NS_OK; } static already_AddRefed<nsIURI> @@ -701,7 +726,8 @@ ResolveRequestedModules(ModuleLoadRequest* aRequest, nsCOMArray<nsIURI> &aUrls) ModuleScript* ms = aRequest->mModuleScript; nsCOMPtr<nsIURI> uri = ResolveModuleSpecifier(ms, specifier); if (!uri) { - HandleResolveFailure(cx, ms, specifier); + nsresult rv = HandleResolveFailure(cx, ms, specifier); + NS_ENSURE_SUCCESS(rv, rv); return NS_ERROR_FAILURE; } @@ -720,13 +746,15 @@ void ScriptLoader::StartFetchingModuleDependencies(ModuleLoadRequest* aRequest) { MOZ_ASSERT(aRequest->mModuleScript); + MOZ_ASSERT(!aRequest->mModuleScript->IsErrored()); MOZ_ASSERT(!aRequest->IsReadyToRun()); + aRequest->mProgress = ModuleLoadRequest::Progress::FetchingImports; nsCOMArray<nsIURI> urls; nsresult rv = ResolveRequestedModules(aRequest, urls); if (NS_FAILED(rv)) { - aRequest->LoadFailed(); + aRequest->ModuleErrored(); return; } @@ -750,7 +778,7 @@ ScriptLoader::StartFetchingModuleDependencies(ModuleLoadRequest* aRequest) GenericPromise::All(AbstractThread::GetCurrent(), importsReady); allReady->Then(AbstractThread::GetCurrent(), __func__, aRequest, &ModuleLoadRequest::DependenciesLoaded, - &ModuleLoadRequest::LoadFailed); + &ModuleLoadRequest::ModuleErrored); } RefPtr<GenericPromise> @@ -773,6 +801,7 @@ ScriptLoader::StartFetchingModuleAndDependencies(ModuleLoadRequest* aRequest, nsresult rv = StartLoad(childRequest, NS_LITERAL_STRING("module"), false); if (NS_FAILED(rv)) { + MOZ_ASSERT(!childRequest->mModuleScript); childRequest->mReady.Reject(rv, __func__); return ready; } @@ -781,6 +810,7 @@ ScriptLoader::StartFetchingModuleAndDependencies(ModuleLoadRequest* aRequest, return ready; } +// 8.1.3.8.1 HostResolveImportedModule(referencingModule, specifier) bool HostResolveImportedModule(JSContext* aCx, unsigned argc, JS::Value* vp) { @@ -795,31 +825,25 @@ HostResolveImportedModule(JSContext* aCx, unsigned argc, JS::Value* vp) MOZ_ASSERT(script->ModuleRecord() == module); // Let url be the result of resolving a module specifier given referencing - // module script and specifier. If the result is failure, throw a TypeError - // exception and abort these steps. + // module script and specifier. nsAutoJSString string; if (!string.init(aCx, specifier)) { return false; } nsCOMPtr<nsIURI> uri = ResolveModuleSpecifier(script, string); - if (!uri) { - return HandleResolveFailure(aCx, script, string); - } - // Let resolved module script be the value of the entry in module map whose - // key is url. If no such entry exists, throw a TypeError exception and abort - // these steps. + // 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"); + + // Let resolved module script be moduleMap[url]. (This entry must exist for us + // to have gotten to this point.) + ModuleScript* ms = script->Loader()->GetFetchedModule(uri); - if (!ms) { - return HandleModuleNotFound(aCx, script, string); - } + MOZ_ASSERT(ms, "Resolved module not found in module map"); - if (ms->InstantiationFailed()) { - JS::Rooted<JS::Value> exception(aCx, ms->Exception()); - JS_SetPendingException(aCx, exception); - return false; - } + MOZ_ASSERT(!ms->IsErrored()); *vp = JS::ObjectValue(*ms->ModuleRecord()); return true; @@ -844,9 +868,35 @@ EnsureModuleResolveHook(JSContext* aCx) } void +ScriptLoader::CheckModuleDependenciesLoaded(ModuleLoadRequest* aRequest) +{ + RefPtr<ModuleScript> moduleScript = aRequest->mModuleScript; + if (moduleScript && !moduleScript->IsErrored()) { + for (auto childRequest : aRequest->mImports) { + ModuleScript* childScript = childRequest->mModuleScript; + if (!childScript) { + // Load error + aRequest->mModuleScript = nullptr; + return; + } else if (childScript->IsErrored()) { + // Script error + moduleScript->SetPreInstantiationError(childScript->Error()); + return; + } + } + } +} + +void ScriptLoader::ProcessLoadedModuleTree(ModuleLoadRequest* aRequest) { if (aRequest->IsTopLevel()) { + ModuleScript* moduleScript = aRequest->mModuleScript; + if (moduleScript && !moduleScript->IsErrored()) { + if (!InstantiateModuleTree(aRequest)) { + aRequest->mModuleScript = nullptr; + } + } MaybeMoveToLoadedList(aRequest); ProcessPendingRequests(); } @@ -859,60 +909,35 @@ ScriptLoader::ProcessLoadedModuleTree(ModuleLoadRequest* aRequest) bool ScriptLoader::InstantiateModuleTree(ModuleLoadRequest* aRequest) { - // Perform eager instantiation of the loaded module tree. + // Instantiate a top-level module and record any error. MOZ_ASSERT(aRequest); + MOZ_ASSERT(aRequest->IsTopLevel()); - ModuleScript* ms = aRequest->mModuleScript; - MOZ_ASSERT(ms); - if (!ms || !ms->ModuleRecord()) { - return false; - } + ModuleScript* moduleScript = aRequest->mModuleScript; + MOZ_ASSERT(moduleScript); + MOZ_ASSERT(moduleScript->ModuleRecord()); + nsAutoMicroTask mt; AutoJSAPI jsapi; - if (NS_WARN_IF(!jsapi.Init(ms->ModuleRecord()))) { + if (NS_WARN_IF(!jsapi.Init(moduleScript->ModuleRecord()))) { return false; } nsresult rv = EnsureModuleResolveHook(jsapi.cx()); NS_ENSURE_SUCCESS(rv, false); - JS::Rooted<JSObject*> module(jsapi.cx(), ms->ModuleRecord()); - bool ok = NS_SUCCEEDED(nsJSUtils::ModuleDeclarationInstantiation(jsapi.cx(), module)); + JS::Rooted<JSObject*> module(jsapi.cx(), moduleScript->ModuleRecord()); + bool ok = NS_SUCCEEDED(nsJSUtils::ModuleInstantiate(jsapi.cx(), module)); - JS::RootedValue exception(jsapi.cx()); if (!ok) { MOZ_ASSERT(jsapi.HasException()); + JS::RootedValue exception(jsapi.cx()); if (!jsapi.StealException(&exception)) { return false; } MOZ_ASSERT(!exception.isUndefined()); - } - - // Mark this module and any uninstantiated dependencies found via depth-first - // search as instantiated and record any error. - - mozilla::Vector<ModuleLoadRequest*, 1> requests; - if (!requests.append(aRequest)) { - return false; - } - - while (!requests.empty()) { - ModuleLoadRequest* request = requests.popCopy(); - ModuleScript* ms = request->mModuleScript; - if (!ms->IsUninstantiated()) { - continue; - } - - ms->SetInstantiationResult(exception); - - for (auto import : request->mImports) { - if (import->mModuleScript->IsUninstantiated() && - !requests.append(import)) - { - return false; - } - } + // Ignore the exception. It will be recorded in the module record. } return true; @@ -1427,13 +1452,19 @@ ScriptLoader::ProcessScriptElement(nsIScriptElement *aElement) ModuleLoadRequest* modReq = request->AsModuleRequest(); modReq->mBaseURL = mDocument->GetDocBaseURI(); rv = CreateModuleScript(modReq); - NS_ENSURE_SUCCESS(rv, false); - StartFetchingModuleDependencies(modReq); + MOZ_ASSERT(NS_FAILED(rv) == !modReq->mModuleScript); + if (NS_FAILED(rv)) { + modReq->LoadFailed(); + return false; + } if (aElement->GetScriptAsync()) { mLoadingAsyncRequests.AppendElement(request); } else { AddDeferRequest(request); } + if (!modReq->mModuleScript->IsErrored()) { + StartFetchingModuleDependencies(modReq); + } return false; } request->mProgress = ScriptLoadRequest::Progress::Ready; @@ -1511,11 +1542,7 @@ ScriptLoader::ProcessOffThreadRequest(ScriptLoadRequest* aRequest) if (aRequest->IsModuleRequest()) { MOZ_ASSERT(aRequest->mOffThreadToken); ModuleLoadRequest* request = aRequest->AsModuleRequest(); - nsresult rv = ProcessFetchedModuleSource(request); - if (NS_FAILED(rv)) { - request->LoadFailed(); - } - return rv; + return ProcessFetchedModuleSource(request); } aRequest->SetReady(); @@ -1687,7 +1714,7 @@ ScriptLoader::ProcessRequest(ScriptLoadRequest* aRequest) if (aRequest->IsModuleRequest() && !aRequest->AsModuleRequest()->mModuleScript) { - // There was an error parsing a module script. Nothing to do here. + // There was an error fetching a module script. Nothing to do here. FireScriptAvailable(NS_ERROR_FAILURE, aRequest); return NS_OK; } @@ -1909,8 +1936,8 @@ ScriptLoader::EvaluateScript(ScriptLoadRequest* aRequest) // http://www.whatwg.org/specs/web-apps/current-work/#execute-the-script-block nsAutoMicroTask mt; AutoEntryScript aes(globalObject, "<script> element", true); - JS::Rooted<JSObject*> global(aes.cx(), - globalObject->GetGlobalJSObject()); + JSContext* cx = aes.cx(); + JS::Rooted<JSObject*> global(cx, globalObject->GetGlobalJSObject()); bool oldProcessingScriptTag = context->GetProcessingScriptTag(); context->SetProcessingScriptTag(true); @@ -1931,28 +1958,38 @@ ScriptLoader::EvaluateScript(ScriptLoadRequest* aRequest) } if (aRequest->IsModuleRequest()) { + rv = EnsureModuleResolveHook(cx); + NS_ENSURE_SUCCESS(rv, rv); + ModuleLoadRequest* request = aRequest->AsModuleRequest(); MOZ_ASSERT(request->mModuleScript); MOZ_ASSERT(!request->mOffThreadToken); - ModuleScript* ms = request->mModuleScript; - MOZ_ASSERT(!ms->IsUninstantiated()); - if (ms->InstantiationFailed()) { - JS::Rooted<JS::Value> exception(aes.cx(), ms->Exception()); - JS_SetPendingException(aes.cx(), exception); - rv = NS_ERROR_FAILURE; - } else { - JS::Rooted<JSObject*> module(aes.cx(), ms->ModuleRecord()); - MOZ_ASSERT(module); - rv = nsJSUtils::ModuleEvaluation(aes.cx(), module); + + ModuleScript* moduleScript = request->mModuleScript; + if (moduleScript->IsErrored()) { + // Module has an error status + JS::Rooted<JS::Value> error(cx, moduleScript->Error()); + JS_SetPendingException(cx, error); + return NS_OK; // An error is reported by AutoEntryScript. + } + + JS::Rooted<JSObject*> module(cx, moduleScript->ModuleRecord()); + MOZ_ASSERT(module); + + rv = nsJSUtils::ModuleEvaluate(cx, module); + MOZ_ASSERT(NS_FAILED(rv) == aes.HasException()); + if (NS_FAILED(rv)) { + // Evaluation failed + rv = NS_OK; // An error is reported by AutoEntryScript. } } else { - JS::CompileOptions options(aes.cx()); + JS::CompileOptions options(cx); rv = FillCompileOptionsForRequest(aes, aRequest, global, &options); if (NS_SUCCEEDED(rv)) { nsAutoString inlineData; SourceBufferHolder srcBuf = GetScriptSource(aRequest, inlineData); - rv = nsJSUtils::EvaluateString(aes.cx(), srcBuf, global, options, + rv = nsJSUtils::EvaluateString(cx, srcBuf, global, options, aRequest->OffThreadTokenPtr()); } } |