diff options
author | Moonchild <moonchild@palemoon.org> | 2020-08-31 05:54:39 +0000 |
---|---|---|
committer | Moonchild <moonchild@palemoon.org> | 2020-08-31 05:54:39 +0000 |
commit | a6f632714fcb1be3dd00b0fc76fbf6bfc693155b (patch) | |
tree | b04c82f9af4a0d288a6d4350d774ad8fe6dac903 /dom/script | |
parent | 2ed0607c747b21cadaf7401d4ba706097578e74d (diff) | |
parent | b28effe2ea93e43e362f7ce263d23b55adcb6da7 (diff) | |
download | UXP-a6f632714fcb1be3dd00b0fc76fbf6bfc693155b.tar UXP-a6f632714fcb1be3dd00b0fc76fbf6bfc693155b.tar.gz UXP-a6f632714fcb1be3dd00b0fc76fbf6bfc693155b.tar.lz UXP-a6f632714fcb1be3dd00b0fc76fbf6bfc693155b.tar.xz UXP-a6f632714fcb1be3dd00b0fc76fbf6bfc693155b.zip |
Merge branch 'redwood' into releaseRELBASE_20200831
Diffstat (limited to 'dom/script')
-rw-r--r-- | dom/script/ModuleLoadRequest.cpp | 45 | ||||
-rw-r--r-- | dom/script/ModuleLoadRequest.h | 33 | ||||
-rw-r--r-- | dom/script/ModuleScript.cpp | 43 | ||||
-rw-r--r-- | dom/script/ModuleScript.h | 12 | ||||
-rw-r--r-- | dom/script/ScriptElement.cpp | 8 | ||||
-rw-r--r-- | dom/script/ScriptLoader.cpp | 680 | ||||
-rw-r--r-- | dom/script/ScriptLoader.h | 89 | ||||
-rw-r--r-- | dom/script/nsIScriptElement.h | 29 | ||||
-rw-r--r-- | dom/script/nsIScriptLoaderObserver.idl | 9 |
9 files changed, 560 insertions, 388 deletions
diff --git a/dom/script/ModuleLoadRequest.cpp b/dom/script/ModuleLoadRequest.cpp index d62214304..743f30fb9 100644 --- a/dom/script/ModuleLoadRequest.cpp +++ b/dom/script/ModuleLoadRequest.cpp @@ -17,26 +17,54 @@ NS_INTERFACE_MAP_END_INHERITING(ScriptLoadRequest) NS_IMPL_CYCLE_COLLECTION_INHERITED(ModuleLoadRequest, ScriptLoadRequest, mBaseURL, mLoader, - mParent, mModuleScript, mImports) NS_IMPL_ADDREF_INHERITED(ModuleLoadRequest, ScriptLoadRequest) NS_IMPL_RELEASE_INHERITED(ModuleLoadRequest, ScriptLoadRequest) -ModuleLoadRequest::ModuleLoadRequest(nsIScriptElement* aElement, +ModuleLoadRequest::ModuleLoadRequest(nsIURI* aURI, + nsIScriptElement* aElement, uint32_t aVersion, CORSMode aCORSMode, const SRIMetadata &aIntegrity, + nsIURI* aReferrer, + mozilla::net::ReferrerPolicy aReferrerPolicy, ScriptLoader* aLoader) : ScriptLoadRequest(ScriptKind::Module, + aURI, aElement, aVersion, aCORSMode, - aIntegrity), + aIntegrity, + aReferrer, + aReferrerPolicy), mIsTopLevel(true), - mLoader(aLoader) -{} + mLoader(aLoader), + mVisitedSet(new VisitedURLSet()) +{ + mVisitedSet->PutEntry(aURI); +} + +ModuleLoadRequest::ModuleLoadRequest(nsIURI* aURI, + ModuleLoadRequest* aParent) + : ScriptLoadRequest(ScriptKind::Module, + aURI, + aParent->mElement, + aParent->mJSVersion, + aParent->mCORSMode, + SRIMetadata(), + aParent->mURI, + aParent->mReferrerPolicy), + mIsTopLevel(false), + mLoader(aParent->mLoader), + mVisitedSet(aParent->mVisitedSet) +{ + MOZ_ASSERT(mVisitedSet->Contains(aURI)); + + mIsInline = false; + mScriptMode = aParent->mScriptMode; +} void ModuleLoadRequest::Cancel() { @@ -83,7 +111,7 @@ ModuleLoadRequest::ModuleLoaded() // been loaded. mModuleScript = mLoader->GetFetchedModule(mURI); - if (!mModuleScript || mModuleScript->IsErrored()) { + if (!mModuleScript || mModuleScript->HasParseError()) { ModuleErrored(); return; } @@ -95,7 +123,7 @@ void ModuleLoadRequest::ModuleErrored() { mLoader->CheckModuleDependenciesLoaded(this); - MOZ_ASSERT(!mModuleScript || mModuleScript->IsErrored()); + MOZ_ASSERT(!mModuleScript || mModuleScript->HasParseError()); CancelImports(); SetReady(); @@ -132,8 +160,7 @@ ModuleLoadRequest::LoadFinished() { mLoader->ProcessLoadedModuleTree(this); mLoader = nullptr; - mParent = nullptr; } } // dom namespace -} // mozilla namespace
\ No newline at end of file +} // mozilla namespace diff --git a/dom/script/ModuleLoadRequest.h b/dom/script/ModuleLoadRequest.h index 7b06dd2cf..eefb7dad5 100644 --- a/dom/script/ModuleLoadRequest.h +++ b/dom/script/ModuleLoadRequest.h @@ -8,6 +8,7 @@ #define mozilla_dom_ModuleLoadRequest_h #include "mozilla/dom/ScriptLoader.h" +#include "nsURIHashKey.h" #include "mozilla/MozPromise.h" namespace mozilla { @@ -16,6 +17,16 @@ namespace dom { class ModuleScript; class ScriptLoader; +// A reference counted set of URLs we have visited in the process of loading a +// module graph. +class VisitedURLSet : public nsTHashtable<nsURIHashKey> +{ + NS_INLINE_DECL_REFCOUNTING(VisitedURLSet) + +private: + ~VisitedURLSet() = default; +}; + // A load request for a module, created for every top level module script and // every module import. Load request can share a ModuleScript if there are // multiple imports of the same module. @@ -31,12 +42,20 @@ public: NS_DECL_ISUPPORTS_INHERITED NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(ModuleLoadRequest, ScriptLoadRequest) - ModuleLoadRequest(nsIScriptElement* aElement, + // Create a top-level module load request. + ModuleLoadRequest(nsIURI* aURI, + nsIScriptElement* aElement, uint32_t aVersion, CORSMode aCORSMode, const SRIMetadata& aIntegrity, + nsIURI* aReferrer, + mozilla::net::ReferrerPolicy, ScriptLoader* aLoader); + // Create a module load request for an imported module. + ModuleLoadRequest(nsIURI* aURI, + ModuleLoadRequest* aParent); + bool IsTopLevel() const { return mIsTopLevel; } @@ -55,7 +74,7 @@ private: public: // Is this a request for a top level module script or an import? - bool mIsTopLevel; + const bool mIsTopLevel; // The base URL used for resolving relative module imports. nsCOMPtr<nsIURI> mBaseURL; @@ -64,10 +83,6 @@ public: // finishes. RefPtr<ScriptLoader> mLoader; - // The importing module, or nullptr for top level module scripts. Used to - // implement the ancestor list checked when fetching module dependencies. - RefPtr<ModuleLoadRequest> mParent; - // Set to a module script object after a successful load or nullptr on // failure. RefPtr<ModuleScript> mModuleScript; @@ -79,9 +94,13 @@ public: // Array of imported modules. nsTArray<RefPtr<ModuleLoadRequest>> mImports; + + // Set of module URLs visited while fetching the module graph this request is + // part of. + RefPtr<VisitedURLSet> mVisitedSet; }; } // dom namespace } // mozilla namespace -#endif // mozilla_dom_ModuleLoadRequest_h
\ No newline at end of file +#endif // mozilla_dom_ModuleLoadRequest_h diff --git a/dom/script/ModuleScript.cpp b/dom/script/ModuleScript.cpp index 28b97a3cb..1bf9d0b0f 100644 --- a/dom/script/ModuleScript.cpp +++ b/dom/script/ModuleScript.cpp @@ -26,7 +26,8 @@ NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(ModuleScript) NS_IMPL_CYCLE_COLLECTION_UNLINK(mLoader) NS_IMPL_CYCLE_COLLECTION_UNLINK(mBaseURL) tmp->UnlinkModuleRecord(); - tmp->mError.setUndefined(); + tmp->mParseError.setUndefined(); + tmp->mErrorToRethrow.setUndefined(); NS_IMPL_CYCLE_COLLECTION_UNLINK_END NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(ModuleScript) @@ -35,7 +36,8 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(ModuleScript) NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mModuleRecord) - NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mError) + NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mParseError) + NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mErrorToRethrow) NS_IMPL_CYCLE_COLLECTION_TRACE_END NS_IMPL_CYCLE_COLLECTING_ADDREF(ModuleScript) @@ -48,7 +50,8 @@ ModuleScript::ModuleScript(ScriptLoader *aLoader, nsIURI* aBaseURL) MOZ_ASSERT(mLoader); MOZ_ASSERT(mBaseURL); MOZ_ASSERT(!mModuleRecord); - MOZ_ASSERT(mError.isUndefined()); + MOZ_ASSERT(!HasParseError()); + MOZ_ASSERT(!HasErrorToRethrow()); } void @@ -74,7 +77,8 @@ void ModuleScript::SetModuleRecord(JS::Handle<JSObject*> aModuleRecord) { MOZ_ASSERT(!mModuleRecord); - MOZ_ASSERT(mError.isUndefined()); + MOZ_ASSERT(!HasParseError()); + MOZ_ASSERT(!HasErrorToRethrow()); mModuleRecord = aModuleRecord; @@ -85,37 +89,24 @@ ModuleScript::SetModuleRecord(JS::Handle<JSObject*> aModuleRecord) } void -ModuleScript::SetPreInstantiationError(const JS::Value& aError) +ModuleScript::SetParseError(const JS::Value& aError) { MOZ_ASSERT(!aError.isUndefined()); + MOZ_ASSERT(!HasParseError()); + MOZ_ASSERT(!HasErrorToRethrow()); UnlinkModuleRecord(); - mError = aError; - + mParseError = aError; HoldJSObjects(this); } -bool -ModuleScript::IsErrored() const -{ - if (!mModuleRecord) { - MOZ_ASSERT(!mError.isUndefined()); - return true; - } - - return JS::IsModuleErrored(mModuleRecord); -} - -JS::Value -ModuleScript::Error() const +void +ModuleScript::SetErrorToRethrow(const JS::Value& aError) { - MOZ_ASSERT(IsErrored()); - - if (!mModuleRecord) { - return mError; - } + MOZ_ASSERT(!aError.isUndefined()); + MOZ_ASSERT(!HasErrorToRethrow()); - return JS::GetModuleError(mModuleRecord); + mErrorToRethrow = aError; } } // dom namespace diff --git a/dom/script/ModuleScript.h b/dom/script/ModuleScript.h index 571359859..f765aa0fa 100644 --- a/dom/script/ModuleScript.h +++ b/dom/script/ModuleScript.h @@ -23,7 +23,8 @@ class ModuleScript final : public nsISupports RefPtr<ScriptLoader> mLoader; nsCOMPtr<nsIURI> mBaseURL; JS::Heap<JSObject*> mModuleRecord; - JS::Heap<JS::Value> mError; + JS::Heap<JS::Value> mParseError; + JS::Heap<JS::Value> mErrorToRethrow; ~ModuleScript(); @@ -35,14 +36,17 @@ public: nsIURI* aBaseURL); void SetModuleRecord(JS::Handle<JSObject*> aModuleRecord); - void SetPreInstantiationError(const JS::Value& aError); + void SetParseError(const JS::Value& aError); + void SetErrorToRethrow(const JS::Value& aError); ScriptLoader* Loader() const { return mLoader; } JSObject* ModuleRecord() const { return mModuleRecord; } nsIURI* BaseURL() const { return mBaseURL; } - bool IsErrored() const; - JS::Value Error() const; + JS::Value ParseError() const { return mParseError; } + JS::Value ErrorToRethrow() const { return mErrorToRethrow; } + bool HasParseError() const { return !mParseError.isUndefined(); } + bool HasErrorToRethrow() const { return !mErrorToRethrow.isUndefined(); } void UnlinkModuleRecord(); }; diff --git a/dom/script/ScriptElement.cpp b/dom/script/ScriptElement.cpp index 0cb17dcb0..eb20dbf32 100644 --- a/dom/script/ScriptElement.cpp +++ b/dom/script/ScriptElement.cpp @@ -21,11 +21,11 @@ using namespace mozilla::dom; NS_IMETHODIMP ScriptElement::ScriptAvailable(nsresult aResult, nsIScriptElement *aElement, - bool aIsInline, + bool aIsInlineClassicScript, nsIURI *aURI, int32_t aLineNo) { - if (!aIsInline && NS_FAILED(aResult)) { + if (!aIsInlineClassicScript && NS_FAILED(aResult)) { nsCOMPtr<nsIParser> parser = do_QueryReferent(mCreatorParser); if (parser) { parser->PushDefinedInsertionPoint(); @@ -128,11 +128,11 @@ ScriptElement::MaybeProcessScript() return false; } - FreezeUriAsyncDefer(); + nsIDocument* ownerDoc = cont->OwnerDoc(); + FreezeExecutionAttrs(ownerDoc); mAlreadyStarted = true; - nsIDocument* ownerDoc = cont->OwnerDoc(); nsCOMPtr<nsIParser> parser = ((nsIScriptElement*) this)->GetCreatorParser(); if (parser) { nsCOMPtr<nsIContentSink> sink = parser->GetContentSink(); diff --git a/dom/script/ScriptLoader.cpp b/dom/script/ScriptLoader.cpp index a53098974..989301b91 100644 --- a/dom/script/ScriptLoader.cpp +++ b/dom/script/ScriptLoader.cpp @@ -140,6 +140,18 @@ ScriptLoadRequest::AsModuleRequest() return static_cast<ModuleLoadRequest*>(this); } +void +ScriptLoadRequest::SetScriptMode(bool aDeferAttr, bool aAsyncAttr) +{ + if (aAsyncAttr) { + mScriptMode = ScriptMode::eAsync; + } else if (aDeferAttr || IsModuleRequest()) { + mScriptMode = ScriptMode::eDeferred; + } else { + mScriptMode = ScriptMode::eBlocking; + } +} + ////////////////////////////////////////////////////////////// // ScriptLoadRequestList @@ -299,8 +311,12 @@ ScriptLoader::~ScriptLoader() // <script for=... event=...> element. static bool -IsScriptEventHandler(nsIContent* aScriptElement) +IsScriptEventHandler(ScriptKind kind, nsIContent* aScriptElement) { + if (kind != ScriptKind::Classic) { + return false; + } + if (!aScriptElement->IsHTMLElement()) { return false; } @@ -375,25 +391,12 @@ ScriptLoader::CheckContentPolicy(nsIDocument* aDocument, } bool -ScriptLoader::ModuleScriptsEnabled() -{ - static bool sEnabledForContent = false; - static bool sCachedPref = false; - if (!sCachedPref) { - sCachedPref = true; - Preferences::AddBoolVarCache(&sEnabledForContent, "dom.moduleScripts.enabled", false); - } - - return nsContentUtils::IsChromeDoc(mDocument) || sEnabledForContent; -} - -bool -ScriptLoader::ModuleMapContainsModule(ModuleLoadRequest *aRequest) const +ScriptLoader::ModuleMapContainsURL(nsIURI* aURL) const { // Returns whether we have fetched, or are currently fetching, a module script - // for the request's URL. - return mFetchingModules.Contains(aRequest->mURI) || - mFetchedModules.Contains(aRequest->mURI); + // for a URL. + return mFetchingModules.Contains(aURL) || + mFetchedModules.Contains(aURL); } bool @@ -410,7 +413,7 @@ ScriptLoader::SetModuleFetchStarted(ModuleLoadRequest *aRequest) // Update the module map to indicate that a module is currently being fetched. MOZ_ASSERT(aRequest->IsLoading()); - MOZ_ASSERT(!ModuleMapContainsModule(aRequest)); + MOZ_ASSERT(!ModuleMapContainsURL(aRequest->mURI)); mFetchingModules.Put(aRequest->mURI, nullptr); } @@ -443,21 +446,21 @@ ScriptLoader::SetModuleFetchFinishedAndResumeWaitingRequests(ModuleLoadRequest * } RefPtr<GenericPromise> -ScriptLoader::WaitForModuleFetch(ModuleLoadRequest *aRequest) +ScriptLoader::WaitForModuleFetch(nsIURI* aURL) { - MOZ_ASSERT(ModuleMapContainsModule(aRequest)); + MOZ_ASSERT(ModuleMapContainsURL(aURL)); RefPtr<GenericPromise::Private> promise; - if (mFetchingModules.Get(aRequest->mURI, getter_AddRefs(promise))) { + if (mFetchingModules.Get(aURL, getter_AddRefs(promise))) { if (!promise) { promise = new GenericPromise::Private(__func__); - mFetchingModules.Put(aRequest->mURI, promise); + mFetchingModules.Put(aURL, promise); } return promise; } RefPtr<ModuleScript> ms; - MOZ_ALWAYS_TRUE(mFetchedModules.Get(aRequest->mURI, getter_AddRefs(ms))); + MOZ_ALWAYS_TRUE(mFetchedModules.Get(aURL, getter_AddRefs(ms))); if (!ms) { return GenericPromise::CreateAndReject(NS_ERROR_FAILURE, __func__); } @@ -482,8 +485,6 @@ ScriptLoader::ProcessFetchedModuleSource(ModuleLoadRequest* aRequest) nsresult rv = CreateModuleScript(aRequest); MOZ_ASSERT(NS_FAILED(rv) == !aRequest->mModuleScript); - SetModuleFetchFinishedAndResumeWaitingRequests(aRequest, rv); - free(aRequest->mScriptTextBuf); aRequest->mScriptTextBuf = nullptr; aRequest->mScriptTextLength = 0; @@ -493,7 +494,11 @@ ScriptLoader::ProcessFetchedModuleSource(ModuleLoadRequest* aRequest) return rv; } - if (!aRequest->mModuleScript->IsErrored()) { + if (!aRequest->mIsInline) { + SetModuleFetchFinishedAndResumeWaitingRequests(aRequest, rv); + } + + if (!aRequest->mModuleScript->HasParseError()) { StartFetchingModuleDependencies(aRequest); } @@ -501,7 +506,7 @@ ScriptLoader::ProcessFetchedModuleSource(ModuleLoadRequest* aRequest) } static nsresult -ResolveRequestedModules(ModuleLoadRequest* aRequest, nsCOMArray<nsIURI>& aUrls); +ResolveRequestedModules(ModuleLoadRequest* aRequest, nsCOMArray<nsIURI>* aUrlsOut); nsresult ScriptLoader::CreateModuleScript(ModuleLoadRequest* aRequest) @@ -528,7 +533,6 @@ ScriptLoader::CreateModuleScript(ModuleLoadRequest* aRequest) nsresult rv; { // Update our current script. - AutoCurrentScriptUpdater scriptUpdater(this, aRequest->mElement); Maybe<AutoCurrentScriptUpdater> masterScriptUpdater; nsCOMPtr<nsIDocument> master = mDocument->MasterDocument(); if (master != mDocument) { @@ -568,7 +572,7 @@ ScriptLoader::CreateModuleScript(ModuleLoadRequest* aRequest) return NS_ERROR_FAILURE; } - moduleScript->SetPreInstantiationError(error); + moduleScript->SetParseError(error); aRequest->ModuleErrored(); return NS_OK; } @@ -577,11 +581,8 @@ ScriptLoader::CreateModuleScript(ModuleLoadRequest* aRequest) // Validate requested modules and treat failure to resolve module specifiers // the same as a parse error. - nsCOMArray<nsIURI> urls; - rv = ResolveRequestedModules(aRequest, urls); + rv = ResolveRequestedModules(aRequest, nullptr); if (NS_FAILED(rv)) { - // ResolveRequestedModules sets pre-instanitation error on failure. - MOZ_ASSERT(moduleScript->IsErrored()); aRequest->ModuleErrored(); return NS_OK; } @@ -627,7 +628,7 @@ HandleResolveFailure(JSContext* aCx, ModuleScript* aScript, return NS_ERROR_OUT_OF_MEMORY; } - aScript->SetPreInstantiationError(error); + aScript->SetParseError(error); return NS_OK; } @@ -667,33 +668,7 @@ ResolveModuleSpecifier(ModuleScript* aScript, } static nsresult -RequestedModuleIsInAncestorList(ModuleLoadRequest* aRequest, nsIURI* aURL, bool* aResult) -{ - const size_t ImportDepthLimit = 100; - - *aResult = false; - size_t depth = 0; - while (aRequest) { - if (depth++ == ImportDepthLimit) { - return NS_ERROR_FAILURE; - } - - bool equal; - nsresult rv = aURL->Equals(aRequest->mURI, &equal); - NS_ENSURE_SUCCESS(rv, rv); - if (equal) { - *aResult = true; - return NS_OK; - } - - aRequest = aRequest->mParent; - } - - return NS_OK; -} - -static nsresult -ResolveRequestedModules(ModuleLoadRequest* aRequest, nsCOMArray<nsIURI> &aUrls) +ResolveRequestedModules(ModuleLoadRequest* aRequest, nsCOMArray<nsIURI>* aUrlsOut) { ModuleScript* ms = aRequest->mModuleScript; @@ -723,7 +698,6 @@ ResolveRequestedModules(ModuleLoadRequest* aRequest, nsCOMArray<nsIURI> &aUrls) } // Let url be the result of resolving a module specifier given module script and requested. - ModuleScript* ms = aRequest->mModuleScript; nsCOMPtr<nsIURI> uri = ResolveModuleSpecifier(ms, specifier); if (!uri) { nsresult rv = HandleResolveFailure(cx, ms, specifier); @@ -731,11 +705,8 @@ ResolveRequestedModules(ModuleLoadRequest* aRequest, nsCOMArray<nsIURI> &aUrls) return NS_ERROR_FAILURE; } - bool isAncestor; - nsresult rv = RequestedModuleIsInAncestorList(aRequest, uri, &isAncestor); - NS_ENSURE_SUCCESS(rv, rv); - if (!isAncestor) { - aUrls.AppendElement(uri.forget()); + if (aUrlsOut) { + aUrlsOut->AppendElement(uri.forget()); } } @@ -746,20 +717,36 @@ void ScriptLoader::StartFetchingModuleDependencies(ModuleLoadRequest* aRequest) { MOZ_ASSERT(aRequest->mModuleScript); - MOZ_ASSERT(!aRequest->mModuleScript->IsErrored()); + MOZ_ASSERT(!aRequest->mModuleScript->HasParseError()); MOZ_ASSERT(!aRequest->IsReadyToRun()); + auto visitedSet = aRequest->mVisitedSet; + MOZ_ASSERT(visitedSet->Contains(aRequest->mURI)); + aRequest->mProgress = ModuleLoadRequest::Progress::FetchingImports; nsCOMArray<nsIURI> urls; - nsresult rv = ResolveRequestedModules(aRequest, urls); + nsresult rv = ResolveRequestedModules(aRequest, &urls); if (NS_FAILED(rv)) { aRequest->ModuleErrored(); return; } - if (urls.Length() == 0) { - // There are no descendents to load so this request is ready. + // Remove already visited URLs from the list. Put unvisited URLs into the + // visited set. + int32_t i = 0; + while (i < urls.Count()) { + nsIURI* url = urls[i]; + if (visitedSet->Contains(url)) { + urls.RemoveObjectAt(i); + } else { + visitedSet->PutEntry(url); + i++; + } + } + + if (urls.Count() == 0) { + // There are no descendants to load so this request is ready. aRequest->DependenciesLoaded(); return; } @@ -782,20 +769,14 @@ ScriptLoader::StartFetchingModuleDependencies(ModuleLoadRequest* aRequest) } RefPtr<GenericPromise> -ScriptLoader::StartFetchingModuleAndDependencies(ModuleLoadRequest* aRequest, +ScriptLoader::StartFetchingModuleAndDependencies(ModuleLoadRequest* aParent, nsIURI* aURI) { MOZ_ASSERT(aURI); - RefPtr<ModuleLoadRequest> childRequest = - new ModuleLoadRequest(aRequest->mElement, aRequest->mJSVersion, - aRequest->mCORSMode, aRequest->mIntegrity, this); + RefPtr<ModuleLoadRequest> childRequest = new ModuleLoadRequest(aURI, aParent); - childRequest->mIsTopLevel = false; - childRequest->mURI = aURI; - childRequest->mIsInline = false; - childRequest->mReferrerPolicy = aRequest->mReferrerPolicy; - childRequest->mParent = aRequest; + aParent->mImports.AppendElement(childRequest); RefPtr<GenericPromise> ready = childRequest->mReady.Ensure(__func__); @@ -806,29 +787,24 @@ ScriptLoader::StartFetchingModuleAndDependencies(ModuleLoadRequest* aRequest, return ready; } - aRequest->mImports.AppendElement(childRequest); return ready; } // 8.1.3.8.1 HostResolveImportedModule(referencingModule, specifier) -bool -HostResolveImportedModule(JSContext* aCx, unsigned argc, JS::Value* vp) +JSObject* +HostResolveImportedModule(JSContext* aCx, JS::Handle<JSObject*> aModule, + JS::Handle<JSString*> aSpecifier) { - MOZ_ASSERT(argc == 2); - JS::CallArgs args = JS::CallArgsFromVp(argc, vp); - JS::Rooted<JSObject*> module(aCx, &args[0].toObject()); - JS::Rooted<JSString*> specifier(aCx, args[1].toString()); - // Let referencing module script be referencingModule.[[HostDefined]]. - JS::Value value = JS::GetModuleHostDefinedField(module); + JS::Value value = JS::GetModuleHostDefinedField(aModule); auto script = static_cast<ModuleScript*>(value.toPrivate()); - MOZ_ASSERT(script->ModuleRecord() == module); + MOZ_ASSERT(script->ModuleRecord() == aModule); // Let url be the result of resolving a module specifier given referencing // module script and specifier. nsAutoJSString string; - if (!string.init(aCx, specifier)) { - return false; + if (!string.init(aCx, aSpecifier)) { + return nullptr; } nsCOMPtr<nsIURI> uri = ResolveModuleSpecifier(script, string); @@ -843,62 +819,79 @@ HostResolveImportedModule(JSContext* aCx, unsigned argc, JS::Value* vp) ModuleScript* ms = script->Loader()->GetFetchedModule(uri); MOZ_ASSERT(ms, "Resolved module not found in module map"); - MOZ_ASSERT(!ms->IsErrored()); + MOZ_ASSERT(!ms->HasParseError()); + MOZ_ASSERT(ms->ModuleRecord()); - *vp = JS::ObjectValue(*ms->ModuleRecord()); - return true; + return ms->ModuleRecord(); } -static nsresult +static void EnsureModuleResolveHook(JSContext* aCx) { - if (JS::GetModuleResolveHook(aCx)) { - return NS_OK; - } - - JS::Rooted<JSFunction*> func(aCx); - func = JS_NewFunction(aCx, HostResolveImportedModule, 2, 0, - "HostResolveImportedModule"); - if (!func) { - return NS_ERROR_FAILURE; + JSRuntime* rt = JS_GetRuntime(aCx); + if (JS::GetModuleResolveHook(rt)) { + return; } - JS::SetModuleResolveHook(aCx, func); - return NS_OK; + JS::SetModuleResolveHook(rt, HostResolveImportedModule); } 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; - } + if (!moduleScript || moduleScript->HasParseError()) { + return; + } + + for (auto childRequest : aRequest->mImports) { + ModuleScript* childScript = childRequest->mModuleScript; + if (!childScript) { + aRequest->mModuleScript = nullptr; + // Load error on script load request; bail. + return; } } } +class ScriptRequestProcessor : public Runnable +{ +private: + RefPtr<ScriptLoader> mLoader; + RefPtr<ScriptLoadRequest> mRequest; +public: + ScriptRequestProcessor(ScriptLoader* aLoader, + ScriptLoadRequest* aRequest) + : mLoader(aLoader) + , mRequest(aRequest) + {} + NS_IMETHOD Run() override + { + return mLoader->ProcessRequest(mRequest); + } +}; + void ScriptLoader::ProcessLoadedModuleTree(ModuleLoadRequest* aRequest) { if (aRequest->IsTopLevel()) { ModuleScript* moduleScript = aRequest->mModuleScript; - if (moduleScript && !moduleScript->IsErrored()) { + if (moduleScript && !moduleScript->HasErrorToRethrow()) { if (!InstantiateModuleTree(aRequest)) { aRequest->mModuleScript = nullptr; } } - MaybeMoveToLoadedList(aRequest); - ProcessPendingRequests(); + + if (aRequest->mIsInline && + aRequest->mElement->GetParserCreated() == NOT_FROM_PARSER) + { + MOZ_ASSERT(!aRequest->isInList()); + nsContentUtils::AddScriptRunner( + new ScriptRequestProcessor(this, aRequest)); + } else { + MaybeMoveToLoadedList(aRequest); + ProcessPendingRequests(); + } } if (aRequest->mWasCompiledOMT) { @@ -906,6 +899,28 @@ ScriptLoader::ProcessLoadedModuleTree(ModuleLoadRequest* aRequest) } } +JS::Value +ScriptLoader::FindFirstParseError(ModuleLoadRequest* aRequest) +{ + MOZ_ASSERT(aRequest); + + ModuleScript* moduleScript = aRequest->mModuleScript; + MOZ_ASSERT(moduleScript); + + if (moduleScript->HasParseError()) { + return moduleScript->ParseError(); + } + + for (ModuleLoadRequest* childRequest : aRequest->mImports) { + JS::Value error = FindFirstParseError(childRequest); + if (!error.isUndefined()) { + return error; + } + } + + return JS::UndefinedValue(); +} + bool ScriptLoader::InstantiateModuleTree(ModuleLoadRequest* aRequest) { @@ -916,6 +931,14 @@ ScriptLoader::InstantiateModuleTree(ModuleLoadRequest* aRequest) ModuleScript* moduleScript = aRequest->mModuleScript; MOZ_ASSERT(moduleScript); + + JS::Value parseError = FindFirstParseError(aRequest); + if (!parseError.isUndefined()) { + // Parse error found in the requested script + moduleScript->SetErrorToRethrow(parseError); + return true; + } + MOZ_ASSERT(moduleScript->ModuleRecord()); nsAutoMicroTask mt; @@ -924,8 +947,7 @@ ScriptLoader::InstantiateModuleTree(ModuleLoadRequest* aRequest) return false; } - nsresult rv = EnsureModuleResolveHook(jsapi.cx()); - NS_ENSURE_SUCCESS(rv, false); + EnsureModuleResolveHook(jsapi.cx()); JS::Rooted<JSObject*> module(jsapi.cx(), moduleScript->ModuleRecord()); bool ok = NS_SUCCEEDED(nsJSUtils::ModuleInstantiate(jsapi.cx(), module)); @@ -937,7 +959,7 @@ ScriptLoader::InstantiateModuleTree(ModuleLoadRequest* aRequest) return false; } MOZ_ASSERT(!exception.isUndefined()); - // Ignore the exception. It will be recorded in the module record. + moduleScript->SetErrorToRethrow(exception); } return true; @@ -959,8 +981,8 @@ ScriptLoader::StartLoad(ScriptLoadRequest *aRequest, const nsAString &aType, // Check whether the module has been fetched or is currently being fetched, // and if so wait for it. ModuleLoadRequest* request = aRequest->AsModuleRequest(); - if (ModuleMapContainsModule(request)) { - WaitForModuleFetch(request) + if (ModuleMapContainsURL(request->mURI)) { + WaitForModuleFetch(request->mURI) ->Then(AbstractThread::GetCurrent(), __func__, request, &ModuleLoadRequest::ModuleLoaded, &ModuleLoadRequest::LoadFailed); @@ -1026,16 +1048,14 @@ ScriptLoader::StartLoad(ScriptLoadRequest *aRequest, const nsAString &aType, NS_ENSURE_SUCCESS(rv, rv); - nsIScriptElement *script = aRequest->mElement; nsCOMPtr<nsIClassOfService> cos(do_QueryInterface(channel)); if (cos) { - if (aScriptFromHead && - !(script && (script->GetScriptAsync() || script->GetScriptDeferred()))) { + if (aScriptFromHead && aRequest->IsBlockingScript()) { // synchronous head scripts block lading of most other non js/css // content such as images cos->AddClassFlags(nsIClassOfService::Leader); - } else if (!(script && script->GetScriptDeferred())) { + } else if (!aRequest->IsDeferredScript()) { // other scripts are neither blocked nor prioritized unless marked deferred cos->AddClassFlags(nsIClassOfService::Unblocked); } @@ -1047,7 +1067,7 @@ ScriptLoader::StartLoad(ScriptLoadRequest *aRequest, const nsAString &aType, httpChannel->SetRequestHeader(NS_LITERAL_CSTRING("Accept"), NS_LITERAL_CSTRING("*/*"), false); - httpChannel->SetReferrerWithPolicy(mDocument->GetDocumentURI(), + httpChannel->SetReferrerWithPolicy(aRequest->mReferrer, aRequest->mReferrerPolicy); nsCOMPtr<nsIHttpChannelInternal> internalChannel(do_QueryInterface(httpChannel)); @@ -1095,23 +1115,6 @@ ScriptLoader::PreloadURIComparator::Equals(const PreloadInfo &aPi, same; } -class ScriptRequestProcessor : public Runnable -{ -private: - RefPtr<ScriptLoader> mLoader; - RefPtr<ScriptLoadRequest> mRequest; -public: - ScriptRequestProcessor(ScriptLoader* aLoader, - ScriptLoadRequest* aRequest) - : mLoader(aLoader) - , mRequest(aRequest) - {} - NS_IMETHOD Run() override - { - return mLoader->ProcessRequest(mRequest); - } -}; - static inline bool ParseTypeAttribute(const nsAString& aType, JSVersion* aVersion) { @@ -1176,17 +1179,24 @@ CSPAllowsInlineScript(nsIScriptElement *aElement, nsIDocument *aDocument) ScriptLoadRequest* ScriptLoader::CreateLoadRequest(ScriptKind aKind, + nsIURI* aURI, nsIScriptElement* aElement, uint32_t aVersion, CORSMode aCORSMode, - const SRIMetadata &aIntegrity) + const SRIMetadata& aIntegrity, + mozilla::net::ReferrerPolicy aReferrerPolicy) { + nsIURI* referrer = mDocument->GetDocumentURI(); + if (aKind == ScriptKind::Classic) { - return new ScriptLoadRequest(aKind, aElement, aVersion, aCORSMode, - aIntegrity); + return new ScriptLoadRequest(aKind, aURI, aElement, + aVersion, aCORSMode, + aIntegrity, + referrer, aReferrerPolicy); } MOZ_ASSERT(aKind == ScriptKind::Module); - return new ModuleLoadRequest(aElement, aVersion, aCORSMode, aIntegrity, this); + return new ModuleLoadRequest(aURI, aElement, aVersion, aCORSMode, + aIntegrity, referrer, aReferrerPolicy, this); } bool @@ -1204,35 +1214,36 @@ ScriptLoader::ProcessScriptElement(nsIScriptElement *aElement) nsCOMPtr<nsIContent> scriptContent = do_QueryInterface(aElement); + nsAutoString type; + bool hasType = aElement->GetScriptType(type); + + ScriptKind scriptKind = aElement->GetScriptIsModule() ? + ScriptKind::Module : + ScriptKind::Classic; + // Step 13. Check that the script is not an eventhandler - if (IsScriptEventHandler(scriptContent)) { + if (IsScriptEventHandler(scriptKind, scriptContent)) { return false; } JSVersion version = JSVERSION_DEFAULT; - // Check the type attribute to determine language and version. - // If type exists, it trumps the deprecated 'language=' - nsAutoString type; - bool hasType = aElement->GetScriptType(type); - - ScriptKind scriptKind = ScriptKind::Classic; - if (!type.IsEmpty()) { - if (ModuleScriptsEnabled() && type.LowerCaseEqualsASCII("module")) { - scriptKind = ScriptKind::Module; - } else { + // For classic scripts, check the type attribute to determine language and + // version. If type exists, it trumps the deprecated 'language=' + if (scriptKind == ScriptKind::Classic) { + if (!type.IsEmpty()) { NS_ENSURE_TRUE(ParseTypeAttribute(type, &version), false); - } - } else if (!hasType) { - // no 'type=' element - // "language" is a deprecated attribute of HTML, so we check it only for - // HTML script elements. - if (scriptContent->IsHTMLElement()) { - nsAutoString language; - scriptContent->GetAttr(kNameSpaceID_None, nsGkAtoms::language, language); - if (!language.IsEmpty()) { - if (!nsContentUtils::IsJavaScriptLanguage(language)) { - return false; + } else if (!hasType) { + // no 'type=' element + // "language" is a deprecated attribute of HTML, so we check it only for + // HTML script elements. + if (scriptContent->IsHTMLElement()) { + nsAutoString language; + scriptContent->GetAttr(kNameSpaceID_None, nsGkAtoms::language, language); + if (!language.IsEmpty()) { + if (!nsContentUtils::IsJavaScriptLanguage(language)) { + return false; + } } } } @@ -1242,7 +1253,7 @@ ScriptLoader::ProcessScriptElement(nsIScriptElement *aElement) // the nomodule attribute will be ignored". // "The nomodule attribute must not be specified on module scripts (and will // be ignored if it is)." - if (ModuleScriptsEnabled() && + if (mDocument->ModuleScriptsEnabled() && scriptKind == ScriptKind::Classic && scriptContent->IsHTMLElement() && scriptContent->HasAttr(kNameSpaceID_None, nsGkAtoms::nomodule)) { @@ -1252,6 +1263,7 @@ ScriptLoader::ProcessScriptElement(nsIScriptElement *aElement) // Step 15. and later in the HTML5 spec nsresult rv = NS_OK; RefPtr<ScriptLoadRequest> request; + mozilla::net::ReferrerPolicy ourRefPolicy = mDocument->GetReferrerPolicy(); if (aElement->GetScriptExternal()) { // external script nsCOMPtr<nsIURI> scriptURI = aElement->GetScriptURI(); @@ -1264,7 +1276,6 @@ ScriptLoader::ProcessScriptElement(nsIScriptElement *aElement) } // Double-check that the preload matches what we're asked to load now. - mozilla::net::ReferrerPolicy ourRefPolicy = mDocument->GetReferrerPolicy(); CORSMode ourCORSMode = aElement->GetCORSMode(); nsTArray<PreloadInfo>::index_type i = mPreloads.IndexOf(scriptURI.get(), 0, PreloadURIComparator()); @@ -1297,8 +1308,15 @@ ScriptLoader::ProcessScriptElement(nsIScriptElement *aElement) } } - if (!request) { - // no usable preload + if (request) { + // Use a preload request. + + // It's possible these attributes changed since we started the preload, so + // update them here. + request->SetScriptMode(aElement->GetScriptDeferred(), + aElement->GetScriptAsync()); + } else { + // No usable preload found. SRIMetadata sriMetadata; { @@ -1318,13 +1336,14 @@ ScriptLoader::ProcessScriptElement(nsIScriptElement *aElement) } } - request = CreateLoadRequest(scriptKind, aElement, version, ourCORSMode, - sriMetadata); - request->mURI = scriptURI; + request = CreateLoadRequest(scriptKind, scriptURI, aElement, + version, ourCORSMode, sriMetadata, + ourRefPolicy); request->mIsInline = false; - request->mReferrerPolicy = ourRefPolicy; + request->SetScriptMode(aElement->GetScriptDeferred(), + aElement->GetScriptAsync()); - // set aScriptFromHead to false so we don't treat non preloaded scripts as + // Set aScriptFromHead to false so we don't treat non-preloaded scripts as // blockers for full page load. See bug 792438. rv = StartLoad(request, type, false); if (NS_FAILED(rv)) { @@ -1342,18 +1361,15 @@ ScriptLoader::ProcessScriptElement(nsIScriptElement *aElement) request->mJSVersion = version; - if (aElement->GetScriptAsync()) { - request->mIsAsync = true; + if (request->IsAsyncScript()) { + AddAsyncRequest(request); if (request->IsReadyToRun()) { - mLoadedAsyncRequests.AppendElement(request); // The script is available already. Run it ASAP when the event // loop gets a chance to spin. // KVKV TODO: Instead of processing immediately, try off-thread-parsing // it and only schedule a pending ProcessRequest if that fails. ProcessPendingRequestsAsync(); - } else { - mLoadingAsyncRequests.AppendElement(request); } return false; } @@ -1372,7 +1388,7 @@ ScriptLoader::ProcessScriptElement(nsIScriptElement *aElement) } // we now have a parser-inserted request that may or may not be still // loading - if (aElement->GetScriptDeferred() || request->IsModuleRequest()) { + if (request->IsDeferredScript()) { // We don't want to run this yet. // If we come here, the script is a parser-created script and it has // the defer attribute but not the async attribute. Since a @@ -1440,31 +1456,36 @@ ScriptLoader::ProcessScriptElement(nsIScriptElement *aElement) return false; } - // Inline scripts ignore ther CORS mode and are always CORS_NONE - request = CreateLoadRequest(scriptKind, aElement, version, CORS_NONE, - SRIMetadata()); // SRI doesn't apply + // Inline scripts ignore their CORS mode and are always CORS_NONE. + request = CreateLoadRequest(scriptKind, mDocument->GetDocumentURI(), aElement, + version, CORS_NONE, + SRIMetadata(), // SRI doesn't apply + ourRefPolicy); request->mJSVersion = version; request->mIsInline = true; - request->mURI = mDocument->GetDocumentURI(); request->mLineNo = aElement->GetScriptLineNumber(); + // Only the 'async' attribute is heeded on an inline module script and + // inline classic scripts ignore both these attributes. + MOZ_ASSERT(!aElement->GetScriptDeferred()); + MOZ_ASSERT_IF(!request->IsModuleRequest(), !aElement->GetScriptAsync()); + request->SetScriptMode(false, aElement->GetScriptAsync()); + if (request->IsModuleRequest()) { ModuleLoadRequest* modReq = request->AsModuleRequest(); modReq->mBaseURL = mDocument->GetDocBaseURI(); - rv = CreateModuleScript(modReq); - MOZ_ASSERT(NS_FAILED(rv) == !modReq->mModuleScript); - if (NS_FAILED(rv)) { - modReq->LoadFailed(); - return false; - } + if (aElement->GetScriptAsync()) { - mLoadingAsyncRequests.AppendElement(request); + AddAsyncRequest(modReq); } else { - AddDeferRequest(request); + AddDeferRequest(modReq); } - if (!modReq->mModuleScript->IsErrored()) { - StartFetchingModuleDependencies(modReq); + + nsresult rv = ProcessFetchedModuleSource(modReq); + if (NS_FAILED(rv)) { + HandleLoadError(modReq, rv); } + return false; } request->mProgress = ScriptLoadRequest::Progress::Ready; @@ -1943,8 +1964,6 @@ ScriptLoader::EvaluateScript(ScriptLoadRequest* aRequest) context->SetProcessingScriptTag(true); nsresult rv; { - // Update our current script. - AutoCurrentScriptUpdater scriptUpdater(this, aRequest->mElement); Maybe<AutoCurrentScriptUpdater> masterScriptUpdater; nsCOMPtr<nsIDocument> master = mDocument->MasterDocument(); if (master != mDocument) { @@ -1958,17 +1977,19 @@ ScriptLoader::EvaluateScript(ScriptLoadRequest* aRequest) } if (aRequest->IsModuleRequest()) { - rv = EnsureModuleResolveHook(cx); - NS_ENSURE_SUCCESS(rv, rv); + // For modules, currentScript is set to null. + AutoCurrentScriptUpdater scriptUpdater(this, nullptr); + + EnsureModuleResolveHook(cx); ModuleLoadRequest* request = aRequest->AsModuleRequest(); MOZ_ASSERT(request->mModuleScript); MOZ_ASSERT(!request->mOffThreadToken); ModuleScript* moduleScript = request->mModuleScript; - if (moduleScript->IsErrored()) { - // Module has an error status - JS::Rooted<JS::Value> error(cx, moduleScript->Error()); + if (moduleScript->HasErrorToRethrow()) { + // Module has an error status to be rethrown + JS::Rooted<JS::Value> error(cx, moduleScript->ErrorToRethrow()); JS_SetPendingException(cx, error); return NS_OK; // An error is reported by AutoEntryScript. } @@ -1983,6 +2004,9 @@ ScriptLoader::EvaluateScript(ScriptLoadRequest* aRequest) rv = NS_OK; // An error is reported by AutoEntryScript. } } else { + // Update our current script. + AutoCurrentScriptUpdater scriptUpdater(this, aRequest->mElement); + JS::CompileOptions options(cx); rv = FillCompileOptionsForRequest(aes, aRequest, global, &options); @@ -2236,19 +2260,41 @@ ScriptLoader::OnStreamComplete(nsIIncrementalStreamLoader* aLoader, nsresult aChannelStatus, nsresult aSRIStatus, mozilla::Vector<char16_t> &aString, - mozilla::dom::SRICheckDataVerifier* aSRIDataVerifier) + SRICheckDataVerifier* aSRIDataVerifier) { ScriptLoadRequest* request = static_cast<ScriptLoadRequest*>(aContext); NS_ASSERTION(request, "null request in stream complete handler"); NS_ENSURE_TRUE(request, NS_ERROR_FAILURE); + nsresult rv = VerifySRI(request, aLoader, aSRIStatus, aSRIDataVerifier); + + if (NS_SUCCEEDED(rv)) { + rv = PrepareLoadedRequest(request, aLoader, aChannelStatus, aString); + } + + if (NS_FAILED(rv)) { + HandleLoadError(request, rv); + } + + // Process our request and/or any pending ones + ProcessPendingRequests(); + + return NS_OK; +} + +nsresult +ScriptLoader::VerifySRI(ScriptLoadRequest* aRequest, + nsIIncrementalStreamLoader* aLoader, + nsresult aSRIStatus, + SRICheckDataVerifier* aSRIDataVerifier) const +{ nsCOMPtr<nsIRequest> channelRequest; aLoader->GetRequest(getter_AddRefs(channelRequest)); nsCOMPtr<nsIChannel> channel; channel = do_QueryInterface(channelRequest); nsresult rv = NS_OK; - if (!request->mIntegrity.IsEmpty() && + if (!aRequest->mIntegrity.IsEmpty() && NS_SUCCEEDED((rv = aSRIStatus))) { MOZ_ASSERT(aSRIDataVerifier); MOZ_ASSERT(mReporter); @@ -2257,7 +2303,7 @@ ScriptLoader::OnStreamComplete(nsIIncrementalStreamLoader* aLoader, if (mDocument && mDocument->GetDocumentURI()) { mDocument->GetDocumentURI()->GetAsciiSpec(sourceUri); } - rv = aSRIDataVerifier->Verify(request->mIntegrity, channel, sourceUri, + rv = aSRIDataVerifier->Verify(aRequest->mIntegrity, channel, sourceUri, mReporter); mReporter->FlushConsoleReports(mDocument); if (NS_FAILED(rv)) { @@ -2273,7 +2319,7 @@ ScriptLoader::OnStreamComplete(nsIIncrementalStreamLoader* aLoader, loadInfo->LoadingPrincipal()->GetCsp(getter_AddRefs(csp)); nsAutoCString violationURISpec; mDocument->GetDocumentURI()->GetAsciiSpec(violationURISpec); - uint32_t lineNo = request->mElement ? request->mElement->GetScriptLineNumber() : 0; + uint32_t lineNo = aRequest->mElement ? aRequest->mElement->GetScriptLineNumber() : 0; csp->LogViolationDetails( nsIContentSecurityPolicy::VIOLATION_TYPE_REQUIRE_SRI_FOR_SCRIPT, NS_ConvertUTF8toUTF16(violationURISpec), @@ -2281,78 +2327,79 @@ ScriptLoader::OnStreamComplete(nsIIncrementalStreamLoader* aLoader, rv = NS_ERROR_SRI_CORRUPT; } } + + return rv; +} - if (NS_SUCCEEDED(rv)) { - rv = PrepareLoadedRequest(request, aLoader, aChannelStatus, aString); +void +ScriptLoader::HandleLoadError(ScriptLoadRequest *aRequest, nsresult aResult) { + /* + * Handle script not loading error because source was a tracking URL. + * We make a note of this script node by including it in a dedicated + * array of blocked tracking nodes under its parent document. + */ + if (aResult == NS_ERROR_TRACKING_URI) { + nsCOMPtr<nsIContent> cont = do_QueryInterface(aRequest->mElement); + mDocument->AddBlockedTrackingNode(cont); } - if (NS_FAILED(rv)) { - /* - * Handle script not loading error because source was a tracking URL. - * We make a note of this script node by including it in a dedicated - * array of blocked tracking nodes under its parent document. - */ - if (rv == NS_ERROR_TRACKING_URI) { - nsCOMPtr<nsIContent> cont = do_QueryInterface(request->mElement); - mDocument->AddBlockedTrackingNode(cont); - } - - if (request->mIsDefer) { - MOZ_ASSERT_IF(request->IsModuleRequest(), - request->AsModuleRequest()->IsTopLevel()); - if (request->isInList()) { - RefPtr<ScriptLoadRequest> req = mDeferRequests.Steal(request); - FireScriptAvailable(rv, req); - } - } else if (request->mIsAsync) { - MOZ_ASSERT_IF(request->IsModuleRequest(), - request->AsModuleRequest()->IsTopLevel()); - if (request->isInList()) { - RefPtr<ScriptLoadRequest> req = mLoadingAsyncRequests.Steal(request); - FireScriptAvailable(rv, req); - } - } else if (request->mIsNonAsyncScriptInserted) { - if (request->isInList()) { - RefPtr<ScriptLoadRequest> req = - mNonAsyncExternalScriptInsertedRequests.Steal(request); - FireScriptAvailable(rv, req); - } - } else if (request->mIsXSLT) { - if (request->isInList()) { - RefPtr<ScriptLoadRequest> req = mXSLTRequests.Steal(request); - FireScriptAvailable(rv, req); - } - } else if (request->IsModuleRequest()) { - ModuleLoadRequest* modReq = request->AsModuleRequest(); - MOZ_ASSERT(!modReq->IsTopLevel()); - MOZ_ASSERT(!modReq->isInList()); - modReq->Cancel(); - FireScriptAvailable(rv, request); - } else if (mParserBlockingRequest == request) { - MOZ_ASSERT(!request->isInList()); - mParserBlockingRequest = nullptr; - UnblockParser(request); - - // Ensure that we treat request->mElement as our current parser-inserted - // script while firing onerror on it. - MOZ_ASSERT(request->mElement->GetParserCreated()); - nsCOMPtr<nsIScriptElement> oldParserInsertedScript = - mCurrentParserInsertedScript; - mCurrentParserInsertedScript = request->mElement; - FireScriptAvailable(rv, request); - ContinueParserAsync(request); - mCurrentParserInsertedScript = oldParserInsertedScript; - } else { - mPreloads.RemoveElement(request, PreloadRequestComparator()); - } + if (aRequest->IsModuleRequest() && !aRequest->mIsInline) { + auto request = aRequest->AsModuleRequest(); + SetModuleFetchFinishedAndResumeWaitingRequests(request, aResult); } - // Process our request and/or any pending ones - ProcessPendingRequests(); + if (aRequest->mInDeferList) { + MOZ_ASSERT_IF(aRequest->IsModuleRequest(), + aRequest->AsModuleRequest()->IsTopLevel()); + if (aRequest->isInList()) { + RefPtr<ScriptLoadRequest> req = mDeferRequests.Steal(aRequest); + FireScriptAvailable(aResult, req); + } + } else if (aRequest->mInAsyncList) { + MOZ_ASSERT_IF(aRequest->IsModuleRequest(), + aRequest->AsModuleRequest()->IsTopLevel()); + if (aRequest->isInList()) { + RefPtr<ScriptLoadRequest> req = mLoadingAsyncRequests.Steal(aRequest); + FireScriptAvailable(aResult, req); + } + } else if (aRequest->mIsNonAsyncScriptInserted) { + if (aRequest->isInList()) { + RefPtr<ScriptLoadRequest> req = + mNonAsyncExternalScriptInsertedRequests.Steal(aRequest); + FireScriptAvailable(aResult, req); + } + } else if (aRequest->mIsXSLT) { + if (aRequest->isInList()) { + RefPtr<ScriptLoadRequest> req = mXSLTRequests.Steal(aRequest); + FireScriptAvailable(aResult, req); + } + } 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, so don't use + // FireScriptAvailable here. + } else if (mParserBlockingRequest == aRequest) { + MOZ_ASSERT(!aRequest->isInList()); + mParserBlockingRequest = nullptr; + UnblockParser(aRequest); - return NS_OK; + // Ensure that we treat request->mElement as our current parser-inserted + // script while firing onerror on it. + MOZ_ASSERT(aRequest->mElement->GetParserCreated()); + nsCOMPtr<nsIScriptElement> oldParserInsertedScript = + mCurrentParserInsertedScript; + mCurrentParserInsertedScript = aRequest->mElement; + FireScriptAvailable(aResult, aRequest); + ContinueParserAsync(aRequest); + mCurrentParserInsertedScript = oldParserInsertedScript; + } else { + mPreloads.RemoveElement(aRequest, PreloadRequestComparator()); + } } + void ScriptLoader::UnblockParser(ScriptLoadRequest* aParserBlockingRequest) { @@ -2377,23 +2424,6 @@ ScriptLoader::NumberOfProcessors() return mNumberOfProcessors; } -void -ScriptLoader::MaybeMoveToLoadedList(ScriptLoadRequest* aRequest) -{ - MOZ_ASSERT(aRequest->IsReadyToRun()); - - // If it's async, move it to the loaded list. aRequest->mIsAsync really - // _should_ be in a list, but the consequences if it's not are bad enough we - // want to avoid trying to move it if it's not. - if (aRequest->mIsAsync) { - MOZ_ASSERT(aRequest->isInList()); - if (aRequest->isInList()) { - RefPtr<ScriptLoadRequest> req = mLoadingAsyncRequests.Steal(aRequest); - mLoadedAsyncRequests.AppendElement(req); - } - } -} - nsresult ScriptLoader::PrepareLoadedRequest(ScriptLoadRequest* aRequest, nsIIncrementalStreamLoader* aLoader, @@ -2440,10 +2470,10 @@ ScriptLoader::PrepareLoadedRequest(ScriptLoadRequest* aRequest, } nsCOMPtr<nsIChannel> channel = do_QueryInterface(req); - // If this load was subject to a CORS check; don't flag it with a - // separate origin principal, so that it will treat our document's - // principal as the origin principal - if (aRequest->mCORSMode == CORS_NONE) { + // If this load was subject to a CORS check, don't flag it with a separate + // origin principal, so that it will treat our document's principal as the + // origin principal. Module loads always use CORS. + if (!aRequest->IsModuleRequest() && aRequest->mCORSMode == CORS_NONE) { rv = nsContentUtils::GetSecurityManager()-> GetChannelResultPrincipal(channel, getter_AddRefs(aRequest->mOriginPrincipal)); NS_ENSURE_SUCCESS(rv, rv); @@ -2551,6 +2581,8 @@ ScriptLoader::PreloadURI(nsIURI *aURI, const nsAString &aCharset, const nsAString &aCrossOrigin, const nsAString& aIntegrity, bool aScriptFromHead, + bool aAsync, + bool aDefer, const mozilla::net::ReferrerPolicy aReferrerPolicy) { NS_ENSURE_TRUE_VOID(mDocument); @@ -2560,7 +2592,7 @@ ScriptLoader::PreloadURI(nsIURI *aURI, const nsAString &aCharset, } // TODO: Preload module scripts. - if (ModuleScriptsEnabled() && aType.LowerCaseEqualsASCII("module")) { + if (mDocument->ModuleScriptsEnabled() && aType.LowerCaseEqualsASCII("module")) { return; } @@ -2577,11 +2609,11 @@ ScriptLoader::PreloadURI(nsIURI *aURI, const nsAString &aCharset, } RefPtr<ScriptLoadRequest> request = - CreateLoadRequest(ScriptKind::Classic, nullptr, 0, - Element::StringToCORSMode(aCrossOrigin), sriMetadata); - request->mURI = aURI; + CreateLoadRequest(ScriptKind::Classic, aURI, nullptr, 0, + Element::StringToCORSMode(aCrossOrigin), sriMetadata, + aReferrerPolicy); request->mIsInline = false; - request->mReferrerPolicy = aReferrerPolicy; + request->SetScriptMode(aDefer, aAsync); nsresult rv = StartLoad(request, aType, aScriptFromHead); if (NS_FAILED(rv)) { @@ -2596,7 +2628,10 @@ ScriptLoader::PreloadURI(nsIURI *aURI, const nsAString &aCharset, void ScriptLoader::AddDeferRequest(ScriptLoadRequest* aRequest) { - aRequest->mIsDefer = true; + MOZ_ASSERT(aRequest->IsDeferredScript()); + MOZ_ASSERT(!aRequest->mInDeferList && !aRequest->mInAsyncList); + + aRequest->mInDeferList = true; mDeferRequests.AppendElement(aRequest); if (mDeferEnabled && aRequest == mDeferRequests.getFirst() && mDocument && !mBlockingDOMContentLoaded) { @@ -2606,6 +2641,37 @@ ScriptLoader::AddDeferRequest(ScriptLoadRequest* aRequest) } } +void +ScriptLoader::AddAsyncRequest(ScriptLoadRequest* aRequest) +{ + MOZ_ASSERT(aRequest->IsAsyncScript()); + MOZ_ASSERT(!aRequest->mInDeferList && !aRequest->mInAsyncList); + + aRequest->mInAsyncList = true; + if (aRequest->IsReadyToRun()) { + mLoadedAsyncRequests.AppendElement(aRequest); + } else { + mLoadingAsyncRequests.AppendElement(aRequest); + } +} + +void +ScriptLoader::MaybeMoveToLoadedList(ScriptLoadRequest* aRequest) +{ + MOZ_ASSERT(aRequest->IsReadyToRun()); + + // If it's async, move it to the loaded list. aRequest->mInAsyncList really + // _should_ be in a list, but the consequences if it's not are bad enough we + // want to avoid trying to move it if it's not. + if (aRequest->mInAsyncList) { + MOZ_ASSERT(aRequest->isInList()); + if (aRequest->isInList()) { + RefPtr<ScriptLoadRequest> req = mLoadingAsyncRequests.Steal(aRequest); + mLoadedAsyncRequests.AppendElement(req); + } + } +} + bool ScriptLoader::MaybeRemovedDeferRequests() { diff --git a/dom/script/ScriptLoader.h b/dom/script/ScriptLoader.h index e6b75bf3b..ed1e6acbc 100644 --- a/dom/script/ScriptLoader.h +++ b/dom/script/ScriptLoader.h @@ -62,17 +62,21 @@ protected: public: ScriptLoadRequest(ScriptKind aKind, + nsIURI* aURI, nsIScriptElement* aElement, uint32_t aVersion, mozilla::CORSMode aCORSMode, - const mozilla::dom::SRIMetadata &aIntegrity) + const SRIMetadata& aIntegrity, + nsIURI* aReferrer, + mozilla::net::ReferrerPolicy aReferrerPolicy) : mKind(aKind), mElement(aElement), mProgress(Progress::Loading), + mScriptMode(ScriptMode::eBlocking), mIsInline(true), mHasSourceMapURL(false), - mIsDefer(false), - mIsAsync(false), + mInDeferList(false), + mInAsyncList(false), mIsNonAsyncScriptInserted(false), mIsXSLT(false), mIsCanceled(false), @@ -81,10 +85,12 @@ public: mScriptTextBuf(nullptr), mScriptTextLength(0), mJSVersion(aVersion), + mURI(aURI), mLineNo(1), mCORSMode(aCORSMode), mIntegrity(aIntegrity), - mReferrerPolicy(mozilla::net::RP_Default) + mReferrer(aReferrer), + mReferrerPolicy(aReferrerPolicy) { } @@ -100,7 +106,8 @@ public: void FireScriptAvailable(nsresult aResult) { - mElement->ScriptAvailable(aResult, mElement, mIsInline, mURI, mLineNo); + bool isInlineClassicScript = mIsInline && !IsModuleRequest(); + mElement->ScriptAvailable(aResult, mElement, isInlineClassicScript, mURI, mLineNo); } void FireScriptEvaluated(nsresult aResult) { @@ -143,6 +150,29 @@ public: (IsReadyToRun() && mWasCompiledOMT); } + enum class ScriptMode : uint8_t { + eBlocking, + eDeferred, + eAsync + }; + + void SetScriptMode(bool aDeferAttr, bool aAsyncAttr); + + bool IsBlockingScript() const + { + return mScriptMode == ScriptMode::eBlocking; + } + + bool IsDeferredScript() const + { + return mScriptMode == ScriptMode::eDeferred; + } + + bool IsAsyncScript() const + { + return mScriptMode == ScriptMode::eAsync; + } + void MaybeCancelOffThreadScript(); using super::getNext; @@ -151,10 +181,11 @@ public: const ScriptKind mKind; nsCOMPtr<nsIScriptElement> mElement; Progress mProgress; // Are we still waiting for a load to complete? + ScriptMode mScriptMode; // Whether this script is blocking, deferred or async. bool mIsInline; // Is the script inline or loaded? bool mHasSourceMapURL; // Does the HTTP header have a source map url? - bool mIsDefer; // True if we live in mDeferRequests. - bool mIsAsync; // True if we live in mLoadingAsyncRequests or mLoadedAsyncRequests. + bool mInDeferList; // True if we live in mDeferRequests. + bool mInAsyncList; // True if we live in mLoadingAsyncRequests or mLoadedAsyncRequests. bool mIsNonAsyncScriptInserted; // True if we live in mNonAsyncExternalScriptInsertedRequests bool mIsXSLT; // True if we live in mXSLTRequests. bool mIsCanceled; // True if we have been explicitly canceled. @@ -164,13 +195,14 @@ public: char16_t* mScriptTextBuf; // Holds script text for non-inline scripts. Don't size_t mScriptTextLength; // use nsString so we can give ownership to jsapi. uint32_t mJSVersion; - nsCOMPtr<nsIURI> mURI; + const nsCOMPtr<nsIURI> mURI; nsCOMPtr<nsIPrincipal> mOriginPrincipal; nsAutoCString mURL; // Keep the URI's filename alive during off thread parsing. int32_t mLineNo; const mozilla::CORSMode mCORSMode; - const mozilla::dom::SRIMetadata mIntegrity; - mozilla::net::ReferrerPolicy mReferrerPolicy; + const SRIMetadata mIntegrity; + const nsCOMPtr<nsIURI> mReferrer; + const mozilla::net::ReferrerPolicy mReferrerPolicy; }; class ScriptLoadRequestList : private mozilla::LinkedList<ScriptLoadRequest> @@ -452,6 +484,8 @@ public: const nsAString &aCrossOrigin, const nsAString& aIntegrity, bool aScriptFromHead, + bool aAsync, + bool aDefer, const mozilla::net::ReferrerPolicy aReferrerPolicy); /** @@ -467,12 +501,13 @@ public: private: virtual ~ScriptLoader(); - ScriptLoadRequest* CreateLoadRequest( - ScriptKind aKind, - nsIScriptElement* aElement, - uint32_t aVersion, - mozilla::CORSMode aCORSMode, - const mozilla::dom::SRIMetadata &aIntegrity); + ScriptLoadRequest* CreateLoadRequest(ScriptKind aKind, + nsIURI* aURI, + nsIScriptElement* aElement, + uint32_t aVersion, + mozilla::CORSMode aCORSMode, + const SRIMetadata& aIntegrity, + mozilla::net::ReferrerPolicy aReferrerPolicy); /** * Unblocks the creator parser of the parser-blocking scripts. @@ -500,6 +535,8 @@ private: nsresult StartLoad(ScriptLoadRequest *aRequest, const nsAString &aType, bool aScriptFromHead); + void HandleLoadError(ScriptLoadRequest *aRequest, nsresult aResult); + /** * Process any pending requests asynchronously (i.e. off an event) if there * are any. Note that this is a no-op if there aren't any currently pending @@ -534,6 +571,11 @@ private: return mEnabled && !mBlockerCount; } + nsresult VerifySRI(ScriptLoadRequest *aRequest, + nsIIncrementalStreamLoader* aLoader, + nsresult aSRIStatus, + SRICheckDataVerifier* aSRIDataVerifier) const; + nsresult AttemptAsyncScriptCompile(ScriptLoadRequest* aRequest); nsresult ProcessRequest(ScriptLoadRequest* aRequest); nsresult CompileOffThreadOrProcessRequest(ScriptLoadRequest* aRequest); @@ -556,6 +598,7 @@ private: mozilla::Vector<char16_t> &aString); void AddDeferRequest(ScriptLoadRequest* aRequest); + void AddAsyncRequest(ScriptLoadRequest* aRequest); bool MaybeRemovedDeferRequests(); void MaybeMoveToLoadedList(ScriptLoadRequest* aRequest); @@ -563,30 +606,30 @@ private: JS::SourceBufferHolder GetScriptSource(ScriptLoadRequest* aRequest, nsAutoString& inlineData); - bool ModuleScriptsEnabled(); - void SetModuleFetchStarted(ModuleLoadRequest *aRequest); void SetModuleFetchFinishedAndResumeWaitingRequests(ModuleLoadRequest *aRequest, nsresult aResult); bool IsFetchingModule(ModuleLoadRequest *aRequest) const; - bool ModuleMapContainsModule(ModuleLoadRequest *aRequest) const; - RefPtr<mozilla::GenericPromise> WaitForModuleFetch(ModuleLoadRequest *aRequest); + bool ModuleMapContainsURL(nsIURI* aURL) const; + RefPtr<mozilla::GenericPromise> WaitForModuleFetch(nsIURI* aURL); ModuleScript* GetFetchedModule(nsIURI* aURL) const; - friend bool - HostResolveImportedModule(JSContext* aCx, unsigned argc, JS::Value* vp); + friend JSObject* + HostResolveImportedModule(JSContext* aCx, JS::Handle<JSObject*> aModule, + JS::Handle<JSString*> aSpecifier); nsresult CreateModuleScript(ModuleLoadRequest* aRequest); nsresult ProcessFetchedModuleSource(ModuleLoadRequest* aRequest); void CheckModuleDependenciesLoaded(ModuleLoadRequest* aRequest); void ProcessLoadedModuleTree(ModuleLoadRequest* aRequest); + JS::Value FindFirstParseError(ModuleLoadRequest* aRequest); bool InstantiateModuleTree(ModuleLoadRequest* aRequest); void StartFetchingModuleDependencies(ModuleLoadRequest* aRequest); RefPtr<mozilla::GenericPromise> - StartFetchingModuleAndDependencies(ModuleLoadRequest* aRequest, nsIURI* aURI); + StartFetchingModuleAndDependencies(ModuleLoadRequest* aParent, nsIURI* aURI); nsIDocument* mDocument; // [WEAK] nsCOMArray<nsIScriptLoaderObserver> mObservers; diff --git a/dom/script/nsIScriptElement.h b/dom/script/nsIScriptElement.h index 470d51c94..e3e1bc49a 100644 --- a/dom/script/nsIScriptElement.h +++ b/dom/script/nsIScriptElement.h @@ -13,6 +13,7 @@ #include "nsIScriptLoaderObserver.h" #include "nsWeakPtr.h" #include "nsIParser.h" +#include "nsIDocument.h" #include "nsContentCreatorFunctions.h" #include "nsIDOMHTMLScriptElement.h" #include "mozilla/CORSMode.h" @@ -37,6 +38,7 @@ public: mForceAsync(aFromParser == mozilla::dom::NOT_FROM_PARSER || aFromParser == mozilla::dom::FROM_PARSER_FRAGMENT), mFrozen(false), + mIsModule(false), mDefer(false), mAsync(false), mExternal(false), @@ -73,11 +75,25 @@ public: virtual void GetScriptCharset(nsAString& charset) = 0; /** - * Freezes the return values of GetScriptDeferred(), GetScriptAsync() and - * GetScriptURI() so that subsequent modifications to the attributes don't - * change execution behavior. + * Freezes the return values of the following methods so that subsequent + * modifications to the attributes don't change execution behavior: + * - GetScriptIsModule() + * - GetScriptDeferred() + * - GetScriptAsync() + * - GetScriptURI() + * - GetScriptExternal() */ - virtual void FreezeUriAsyncDefer() = 0; + virtual void FreezeExecutionAttrs(nsIDocument* aOwnerDoc) = 0; + + /** + * Is the script a module script? + * Currently only supported by HTML scripts. + */ + bool GetScriptIsModule() + { + NS_PRECONDITION(mFrozen, "Execution attributes not yet frozen: Not ready for this call!"); + return mIsModule; + } /** * Is the script deferred. Currently only supported by HTML scripts. @@ -293,6 +309,11 @@ protected: bool mFrozen; /** + * The effective moduleness. + */ + bool mIsModule; + + /** * The effective deferredness. */ bool mDefer; diff --git a/dom/script/nsIScriptLoaderObserver.idl b/dom/script/nsIScriptLoaderObserver.idl index ed7196525..880738360 100644 --- a/dom/script/nsIScriptLoaderObserver.idl +++ b/dom/script/nsIScriptLoaderObserver.idl @@ -20,15 +20,16 @@ interface nsIScriptLoaderObserver : nsISupports { * a script. If this is a failure code, script evaluation * will not occur. * @param aElement The element being processed. - * @param aIsInline Is this an inline script or externally loaded? + * @param aIsInline Is this an inline classic script (as opposed to an + * externally loaded classic script or module script)? * @param aURI What is the URI of the script (the document URI if * it is inline). * @param aLineNo At what line does the script appear (generally 1 * if it is a loaded script). */ - void scriptAvailable(in nsresult aResult, + void scriptAvailable(in nsresult aResult, in nsIScriptElement aElement, - in boolean aIsInline, + in boolean aIsInlineClassicScript, in nsIURI aURI, in int32_t aLineNo); @@ -40,7 +41,7 @@ interface nsIScriptLoaderObserver : nsISupports { * @param aElement The element being processed. * @param aIsInline Is this an inline script or externally loaded? */ - void scriptEvaluated(in nsresult aResult, + void scriptEvaluated(in nsresult aResult, in nsIScriptElement aElement, in boolean aIsInline); |