/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* vim: set ts=2 sw=2 et tw=78: */ /* This Source Code Form is subject to the terms of the Mozilla Public * 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 "ErrorList.h" #include "nsError.h" #include "nsHtml5AttributeName.h" #include "nsHtml5HtmlAttributes.h" #include "nsHtml5String.h" #include "nsNetUtil.h" #include "mozilla/dom/FetchPriority.h" #include "mozilla/dom/ShadowRoot.h" #include "mozilla/dom/ShadowRootBinding.h" #include "mozilla/CheckedInt.h" #include "mozilla/Likely.h" #include "mozilla/StaticPrefs_dom.h" #include "mozilla/StaticPrefs_network.h" #include "mozilla/UniquePtr.h" #include "mozilla/UniquePtrExtensions.h" nsHtml5TreeBuilder::nsHtml5TreeBuilder(nsHtml5OplessBuilder* aBuilder) : mode(0), originalMode(0), framesetOk(false), tokenizer(nullptr), scriptingEnabled(false), needToDropLF(false), fragment(false), contextName(nullptr), contextNamespace(kNameSpaceID_None), contextNode(nullptr), templateModePtr(0), stackNodesIdx(0), numStackNodes(0), currentPtr(0), listPtr(0), formPointer(nullptr), headPointer(nullptr), charBufferLen(0), quirks(false), forceNoQuirks(false), allowDeclarativeShadowRoots(false), keepBuffer(false), mBuilder(aBuilder), mViewSource(nullptr), mOpSink(nullptr), mHandles(nullptr), mHandlesUsed(0), mSpeculativeLoadStage(nullptr), mBroken(NS_OK), mCurrentHtmlScriptCannotDocumentWriteOrBlock(false), mPreventScriptExecution(false), mGenerateSpeculativeLoads(false), mHasSeenImportMap(false) #ifdef DEBUG , mActive(false) #endif { MOZ_COUNT_CTOR(nsHtml5TreeBuilder); } nsHtml5TreeBuilder::nsHtml5TreeBuilder(nsAHtml5TreeOpSink* aOpSink, nsHtml5TreeOpStage* aStage, bool aGenerateSpeculativeLoads) : mode(0), originalMode(0), framesetOk(false), tokenizer(nullptr), scriptingEnabled(false), needToDropLF(false), fragment(false), contextName(nullptr), contextNamespace(kNameSpaceID_None), contextNode(nullptr), templateModePtr(0), stackNodesIdx(0), numStackNodes(0), currentPtr(0), listPtr(0), formPointer(nullptr), headPointer(nullptr), charBufferLen(0), quirks(false), forceNoQuirks(false), allowDeclarativeShadowRoots(false), keepBuffer(false), mBuilder(nullptr), mViewSource(nullptr), mOpSink(aOpSink), mHandles(new nsIContent*[NS_HTML5_TREE_BUILDER_HANDLE_ARRAY_LENGTH]), mHandlesUsed(0), mSpeculativeLoadStage(aStage), mBroken(NS_OK), mCurrentHtmlScriptCannotDocumentWriteOrBlock(false), mPreventScriptExecution(false), mGenerateSpeculativeLoads(aGenerateSpeculativeLoads), mHasSeenImportMap(false) #ifdef DEBUG , mActive(false) #endif { MOZ_ASSERT(!(!aStage && aGenerateSpeculativeLoads), "Must not generate speculative loads without a stage"); MOZ_COUNT_CTOR(nsHtml5TreeBuilder); } nsHtml5TreeBuilder::~nsHtml5TreeBuilder() { MOZ_COUNT_DTOR(nsHtml5TreeBuilder); NS_ASSERTION(!mActive, "nsHtml5TreeBuilder deleted without ever calling end() on it!"); mOpQueue.Clear(); } static void getTypeString(nsHtml5String& aType, nsAString& aTypeString) { aType.ToString(aTypeString); // Since `typeString` after trimming and lowercasing is only checked // for "module" and " importmap", we don't need to remember // pre-trimming emptiness here. // ASCII whitespace https://infra.spec.whatwg.org/#ascii-whitespace: // U+0009 TAB, U+000A LF, U+000C FF, U+000D CR, or U+0020 SPACE. static const char kASCIIWhitespace[] = "\t\n\f\r "; aTypeString.Trim(kASCIIWhitespace); } nsIContentHandle* nsHtml5TreeBuilder::createElement( int32_t aNamespace, nsAtom* aName, nsHtml5HtmlAttributes* aAttributes, nsIContentHandle* aIntendedParent, nsHtml5ContentCreatorFunction aCreator) { MOZ_ASSERT(aAttributes, "Got null attributes."); MOZ_ASSERT(aName, "Got null name."); MOZ_ASSERT(aNamespace == kNameSpaceID_XHTML || aNamespace == kNameSpaceID_SVG || aNamespace == kNameSpaceID_MathML, "Bogus namespace."); if (mBuilder) { nsIContent* intendedParent = aIntendedParent ? static_cast(aIntendedParent) : nullptr; // intendedParent == nullptr is a special case where the // intended parent is the document. nsNodeInfoManager* nodeInfoManager = intendedParent ? intendedParent->OwnerDoc()->NodeInfoManager() : mBuilder->GetNodeInfoManager(); nsIContent* elem; if (aNamespace == kNameSpaceID_XHTML) { elem = nsHtml5TreeOperation::CreateHTMLElement( aName, aAttributes, mozilla::dom::FROM_PARSER_FRAGMENT, nodeInfoManager, mBuilder, aCreator.html); } else if (aNamespace == kNameSpaceID_SVG) { elem = nsHtml5TreeOperation::CreateSVGElement( aName, aAttributes, mozilla::dom::FROM_PARSER_FRAGMENT, nodeInfoManager, mBuilder, aCreator.svg); } else { MOZ_ASSERT(aNamespace == kNameSpaceID_MathML); elem = nsHtml5TreeOperation::CreateMathMLElement( aName, aAttributes, nodeInfoManager, mBuilder); } if (MOZ_UNLIKELY(aAttributes != tokenizer->GetAttributes() && aAttributes != nsHtml5HtmlAttributes::EMPTY_ATTRIBUTES)) { delete aAttributes; } return elem; } nsIContentHandle* content = AllocateContentHandle(); nsHtml5TreeOperation* treeOp = mOpQueue.AppendElement(mozilla::fallible); if (MOZ_UNLIKELY(!treeOp)) { MarkAsBrokenAndRequestSuspensionWithoutBuilder(NS_ERROR_OUT_OF_MEMORY); return nullptr; } if (aNamespace == kNameSpaceID_XHTML) { opCreateHTMLElement opeation( content, aName, aAttributes, aCreator.html, aIntendedParent, (!!mSpeculativeLoadStage) ? mozilla::dom::FROM_PARSER_NETWORK : mozilla::dom::FROM_PARSER_DOCUMENT_WRITE); treeOp->Init(mozilla::AsVariant(opeation)); } else if (aNamespace == kNameSpaceID_SVG) { opCreateSVGElement operation( content, aName, aAttributes, aCreator.svg, aIntendedParent, (!!mSpeculativeLoadStage) ? mozilla::dom::FROM_PARSER_NETWORK : mozilla::dom::FROM_PARSER_DOCUMENT_WRITE); treeOp->Init(mozilla::AsVariant(operation)); } else { // kNameSpaceID_MathML opCreateMathMLElement operation(content, aName, aAttributes, aIntendedParent); treeOp->Init(mozilla::AsVariant(operation)); } // mSpeculativeLoadStage is non-null only in the off-the-main-thread // tree builder, which handles the network stream // Start wall of code for speculative loading and line numbers if (mGenerateSpeculativeLoads && mode != IN_TEMPLATE) { switch (aNamespace) { case kNameSpaceID_XHTML: if (nsGkAtoms::img == aName) { nsHtml5String loading = aAttributes->getValue(nsHtml5AttributeName::ATTR_LOADING); if (!loading.LowerCaseEqualsASCII("lazy")) { nsHtml5String url = aAttributes->getValue(nsHtml5AttributeName::ATTR_SRC); nsHtml5String srcset = aAttributes->getValue(nsHtml5AttributeName::ATTR_SRCSET); nsHtml5String crossOrigin = aAttributes->getValue(nsHtml5AttributeName::ATTR_CROSSORIGIN); nsHtml5String referrerPolicy = aAttributes->getValue( nsHtml5AttributeName::ATTR_REFERRERPOLICY); nsHtml5String sizes = aAttributes->getValue(nsHtml5AttributeName::ATTR_SIZES); nsHtml5String fetchPriority = aAttributes->getValue(nsHtml5AttributeName::ATTR_FETCHPRIORITY); mSpeculativeLoadQueue.AppendElement()->InitImage( url, crossOrigin, /* aMedia = */ nullptr, referrerPolicy, srcset, sizes, false, fetchPriority); } } else if (nsGkAtoms::source == aName) { nsHtml5String srcset = aAttributes->getValue(nsHtml5AttributeName::ATTR_SRCSET); // Sources without srcset cannot be selected. The source could also be // for a media element, but in that context doesn't use srcset. See // comments in nsHtml5SpeculativeLoad.h about preloading if (srcset) { nsHtml5String sizes = aAttributes->getValue(nsHtml5AttributeName::ATTR_SIZES); nsHtml5String type = aAttributes->getValue(nsHtml5AttributeName::ATTR_TYPE); nsHtml5String media = aAttributes->getValue(nsHtml5AttributeName::ATTR_MEDIA); mSpeculativeLoadQueue.AppendElement()->InitPictureSource( srcset, sizes, type, media); } } else if (nsGkAtoms::script == aName) { nsHtml5TreeOperation* treeOp = mOpQueue.AppendElement(mozilla::fallible); if (MOZ_UNLIKELY(!treeOp)) { MarkAsBrokenAndRequestSuspensionWithoutBuilder( NS_ERROR_OUT_OF_MEMORY); return nullptr; } opSetScriptLineAndColumnNumberAndFreeze operation( content, tokenizer->getLineNumber(), // NOTE: tokenizer->getColumnNumber() points '>'. tokenizer->getColumnNumber() + 1); treeOp->Init(mozilla::AsVariant(operation)); nsHtml5String type = aAttributes->getValue(nsHtml5AttributeName::ATTR_TYPE); nsAutoString typeString; getTypeString(type, typeString); bool isModule = typeString.LowerCaseEqualsASCII("module"); bool importmap = typeString.LowerCaseEqualsASCII("importmap"); bool async = false; bool defer = false; bool nomodule = aAttributes->contains(nsHtml5AttributeName::ATTR_NOMODULE); // For microtask semantics, we need to queue either // `opRunScriptThatMayDocumentWriteOrBlock` or // `opRunScriptThatCannotDocumentWriteOrBlock` for every script // element--even ones that we already know won't run. // `mCurrentHtmlScriptCannotDocumentWriteOrBlock` controls which // kind of operation is used for an HTML script, and this is the // place where `mCurrentHtmlScriptCannotDocumentWriteOrBlock` // needs to be set correctly. // // Non-async, non-defer classic scripts that will run MUST use // `opRunScriptThatMayDocumentWriteOrBlock` in order to run // the more complex code that // 1. is able to resume the HTML parse after a parser-blocking // scripts no longer blocks the parser // 2. is able to receive more content to parse on the main thread // via document.write // 3. is able to throw away off-the-main-thread parsing results // if what's document.written on the main thread invalidates // the speculation. // // Async and defer classic scripts as well as module scripts and // importmaps MUST use `opRunScriptThatCannotDocumentWriteOrBlock`. // This is necessary particularly because the relevant main-thread // code assumes it doesn't need to deal with resuming the HTML // parse some time afterwards, so using a tree operation with // mismatching expectations regarding that responsibility may // cause the HTML parse to stall. // // Various scripts that won't actually run work with either type // of tree op in the sense that the HTML parse won't stall. // However, in the case where a script cannot block or insert // data to the HTML parser via document.write, unnecessary use // of `opRunScriptThatMayDocumentWriteOrBlock` instead of // `opRunScriptThatCannotDocumentWriteOrBlock` causes the HTML // parser to enter the speculative mode when doing so isn't // actually required. // // Ideally, we would check for `type`/`language` attribute // combinations that are known to cause non-execution as well as // ScriptLoader::IsScriptEventHandler equivalent. That way, we // wouldn't unnecessarily speculate after scripts that won't // execute. https://bugzilla.mozilla.org/show_bug.cgi?id=1848311 if (importmap) { // If we see an importmap, we don't want to later start speculative // loads for modulepreloads, since such load might finish before // the importmap is created. This also applies to module scripts so // that any modulepreload integrity checks can be performed before // the modules scripts are loaded. // This state is not part of speculation rollback: If an importmap // is seen speculatively and the speculation is rolled back, the // importmap is still considered seen. // TODO: Sync importmap seenness between the main thread and the // parser thread. // https://bugzilla.mozilla.org/show_bug.cgi?id=1848312 mHasSeenImportMap = true; } nsHtml5String url = aAttributes->getValue(nsHtml5AttributeName::ATTR_SRC); if (url) { async = aAttributes->contains(nsHtml5AttributeName::ATTR_ASYNC); defer = aAttributes->contains(nsHtml5AttributeName::ATTR_DEFER); if ((isModule && !mHasSeenImportMap) || (!isModule && !importmap && !nomodule)) { nsHtml5String charset = aAttributes->getValue(nsHtml5AttributeName::ATTR_CHARSET); nsHtml5String crossOrigin = aAttributes->getValue(nsHtml5AttributeName::ATTR_CROSSORIGIN); nsHtml5String nonce = aAttributes->getValue(nsHtml5AttributeName::ATTR_NONCE); nsHtml5String fetchPriority = aAttributes->getValue( nsHtml5AttributeName::ATTR_FETCHPRIORITY); nsHtml5String integrity = aAttributes->getValue(nsHtml5AttributeName::ATTR_INTEGRITY); nsHtml5String referrerPolicy = aAttributes->getValue( nsHtml5AttributeName::ATTR_REFERRERPOLICY); mSpeculativeLoadQueue.AppendElement()->InitScript( url, charset, type, crossOrigin, /* aMedia = */ nullptr, nonce, fetchPriority, integrity, referrerPolicy, mode == nsHtml5TreeBuilder::IN_HEAD, async, defer, false); } } // `mCurrentHtmlScriptCannotDocumentWriteOrBlock` MUST be computed to // match the ScriptLoader-perceived kind of the script regardless of // enqueuing a speculative load. Scripts with the `nomodule` attribute // never block or document.write: Either the attribute prevents a // classic script execution or is ignored on a module script or // importmap. mCurrentHtmlScriptCannotDocumentWriteOrBlock = isModule || importmap || async || defer || nomodule; } else if (nsGkAtoms::link == aName) { nsHtml5String rel = aAttributes->getValue(nsHtml5AttributeName::ATTR_REL); // Not splitting on space here is bogus but the old parser didn't even // do a case-insensitive check. if (rel) { if (rel.LowerCaseEqualsASCII("stylesheet")) { nsHtml5String url = aAttributes->getValue(nsHtml5AttributeName::ATTR_HREF); if (url) { nsHtml5String charset = aAttributes->getValue(nsHtml5AttributeName::ATTR_CHARSET); nsHtml5String crossOrigin = aAttributes->getValue( nsHtml5AttributeName::ATTR_CROSSORIGIN); nsHtml5String nonce = aAttributes->getValue(nsHtml5AttributeName::ATTR_NONCE); nsHtml5String integrity = aAttributes->getValue(nsHtml5AttributeName::ATTR_INTEGRITY); nsHtml5String referrerPolicy = aAttributes->getValue( nsHtml5AttributeName::ATTR_REFERRERPOLICY); nsHtml5String media = aAttributes->getValue(nsHtml5AttributeName::ATTR_MEDIA); nsHtml5String fetchPriority = aAttributes->getValue( nsHtml5AttributeName::ATTR_FETCHPRIORITY); mSpeculativeLoadQueue.AppendElement()->InitStyle( url, charset, crossOrigin, media, referrerPolicy, nonce, integrity, false, fetchPriority); } } else if (rel.LowerCaseEqualsASCII("preconnect")) { nsHtml5String url = aAttributes->getValue(nsHtml5AttributeName::ATTR_HREF); if (url) { nsHtml5String crossOrigin = aAttributes->getValue( nsHtml5AttributeName::ATTR_CROSSORIGIN); mSpeculativeLoadQueue.AppendElement()->InitPreconnect( url, crossOrigin); } } else if (rel.LowerCaseEqualsASCII("preload")) { nsHtml5String url = aAttributes->getValue(nsHtml5AttributeName::ATTR_HREF); if (url) { nsHtml5String as = aAttributes->getValue(nsHtml5AttributeName::ATTR_AS); nsHtml5String charset = aAttributes->getValue(nsHtml5AttributeName::ATTR_CHARSET); nsHtml5String crossOrigin = aAttributes->getValue( nsHtml5AttributeName::ATTR_CROSSORIGIN); nsHtml5String nonce = aAttributes->getValue(nsHtml5AttributeName::ATTR_NONCE); nsHtml5String integrity = aAttributes->getValue(nsHtml5AttributeName::ATTR_INTEGRITY); nsHtml5String referrerPolicy = aAttributes->getValue( nsHtml5AttributeName::ATTR_REFERRERPOLICY); nsHtml5String media = aAttributes->getValue(nsHtml5AttributeName::ATTR_MEDIA); nsHtml5String fetchPriority = aAttributes->getValue( nsHtml5AttributeName::ATTR_FETCHPRIORITY); // Note that respective speculative loaders for scripts and // styles check all additional attributes to be equal to use the // speculative load. So, if any of them is specified and the // preload has to take the expected effect, those attributes // must also be specified on the actual tag to use the preload. // Omitting an attribute on both will make the values equal // (empty) and thus use the preload. if (as.LowerCaseEqualsASCII("script")) { nsHtml5String type = aAttributes->getValue(nsHtml5AttributeName::ATTR_TYPE); mSpeculativeLoadQueue.AppendElement()->InitScript( url, charset, type, crossOrigin, media, nonce, /* aFetchPriority */ fetchPriority, integrity, referrerPolicy, mode == nsHtml5TreeBuilder::IN_HEAD, false, false, true); } else if (as.LowerCaseEqualsASCII("style")) { mSpeculativeLoadQueue.AppendElement()->InitStyle( url, charset, crossOrigin, media, referrerPolicy, nonce, integrity, true, fetchPriority); } else if (as.LowerCaseEqualsASCII("image")) { nsHtml5String srcset = aAttributes->getValue( nsHtml5AttributeName::ATTR_IMAGESRCSET); nsHtml5String sizes = aAttributes->getValue( nsHtml5AttributeName::ATTR_IMAGESIZES); mSpeculativeLoadQueue.AppendElement()->InitImage( url, crossOrigin, media, referrerPolicy, srcset, sizes, true, fetchPriority); } else if (as.LowerCaseEqualsASCII("font")) { mSpeculativeLoadQueue.AppendElement()->InitFont( url, crossOrigin, media, referrerPolicy, fetchPriority); } else if (as.LowerCaseEqualsASCII("fetch")) { mSpeculativeLoadQueue.AppendElement()->InitFetch( url, crossOrigin, media, referrerPolicy, fetchPriority); } // Other "as" values will be supported later. } } else if (mozilla::StaticPrefs::network_modulepreload() && rel.LowerCaseEqualsASCII("modulepreload") && !mHasSeenImportMap) { nsHtml5String url = aAttributes->getValue(nsHtml5AttributeName::ATTR_HREF); if (url && url.Length() != 0) { nsHtml5String as = aAttributes->getValue(nsHtml5AttributeName::ATTR_AS); nsAutoString asString; as.ToString(asString); if (mozilla::net::IsScriptLikeOrInvalid(asString)) { nsHtml5String charset = aAttributes->getValue(nsHtml5AttributeName::ATTR_CHARSET); RefPtr moduleType = nsGkAtoms::_module; nsHtml5String type = nsHtml5String::FromAtom(moduleType.forget()); nsHtml5String crossOrigin = aAttributes->getValue( nsHtml5AttributeName::ATTR_CROSSORIGIN); nsHtml5String media = aAttributes->getValue(nsHtml5AttributeName::ATTR_MEDIA); nsHtml5String nonce = aAttributes->getValue(nsHtml5AttributeName::ATTR_NONCE); nsHtml5String integrity = aAttributes->getValue( nsHtml5AttributeName::ATTR_INTEGRITY); nsHtml5String referrerPolicy = aAttributes->getValue( nsHtml5AttributeName::ATTR_REFERRERPOLICY); nsHtml5String fetchPriority = aAttributes->getValue( nsHtml5AttributeName::ATTR_FETCHPRIORITY); mSpeculativeLoadQueue.AppendElement()->InitScript( url, charset, type, crossOrigin, media, nonce, /* aFetchPriority */ fetchPriority, integrity, referrerPolicy, mode == nsHtml5TreeBuilder::IN_HEAD, false, false, true); } } } } } else if (nsGkAtoms::video == aName) { nsHtml5String url = aAttributes->getValue(nsHtml5AttributeName::ATTR_POSTER); if (url) { // Fetch priority is not supported for video. Nullptr will map to // the auto state // (https://html.spec.whatwg.org/#fetch-priority-attribute). auto fetchPriority = nullptr; mSpeculativeLoadQueue.AppendElement()->InitImage( url, nullptr, nullptr, nullptr, nullptr, nullptr, false, fetchPriority); } } else if (nsGkAtoms::style == aName) { mImportScanner.Start(); nsHtml5TreeOperation* treeOp = mOpQueue.AppendElement(mozilla::fallible); if (MOZ_UNLIKELY(!treeOp)) { MarkAsBrokenAndRequestSuspensionWithoutBuilder( NS_ERROR_OUT_OF_MEMORY); return nullptr; } opSetStyleLineNumber operation(content, tokenizer->getLineNumber()); treeOp->Init(mozilla::AsVariant(operation)); } else if (nsGkAtoms::html == aName) { nsHtml5String url = aAttributes->getValue(nsHtml5AttributeName::ATTR_MANIFEST); mSpeculativeLoadQueue.AppendElement()->InitManifest(url); } else if (nsGkAtoms::base == aName) { nsHtml5String url = aAttributes->getValue(nsHtml5AttributeName::ATTR_HREF); if (url) { mSpeculativeLoadQueue.AppendElement()->InitBase(url); } } else if (nsGkAtoms::meta == aName) { if (nsHtml5Portability::lowerCaseLiteralEqualsIgnoreAsciiCaseString( "content-security-policy", aAttributes->getValue( nsHtml5AttributeName::ATTR_HTTP_EQUIV))) { nsHtml5String csp = aAttributes->getValue(nsHtml5AttributeName::ATTR_CONTENT); if (csp) { mSpeculativeLoadQueue.AppendElement()->InitMetaCSP(csp); } } else if (nsHtml5Portability:: lowerCaseLiteralEqualsIgnoreAsciiCaseString( "referrer", aAttributes->getValue( nsHtml5AttributeName::ATTR_NAME))) { nsHtml5String referrerPolicy = aAttributes->getValue(nsHtml5AttributeName::ATTR_CONTENT); if (referrerPolicy) { mSpeculativeLoadQueue.AppendElement()->InitMetaReferrerPolicy( referrerPolicy); } } } break; case kNameSpaceID_SVG: if (nsGkAtoms::image == aName) { nsHtml5String url = aAttributes->getValue(nsHtml5AttributeName::ATTR_HREF); if (!url) { url = aAttributes->getValue(nsHtml5AttributeName::ATTR_XLINK_HREF); } if (url) { // Currently SVG's `` element lacks support for // `fetchpriority`, see bug 1847712. Hence passing nullptr which // maps to the auto state // (https://html.spec.whatwg.org/#fetch-priority-attribute). auto fetchPriority = nullptr; mSpeculativeLoadQueue.AppendElement()->InitImage( url, nullptr, nullptr, nullptr, nullptr, nullptr, false, fetchPriority); } } else if (nsGkAtoms::script == aName) { nsHtml5TreeOperation* treeOp = mOpQueue.AppendElement(mozilla::fallible); if (MOZ_UNLIKELY(!treeOp)) { MarkAsBrokenAndRequestSuspensionWithoutBuilder( NS_ERROR_OUT_OF_MEMORY); return nullptr; } opSetScriptLineAndColumnNumberAndFreeze operation( content, tokenizer->getLineNumber(), // NOTE: tokenizer->getColumnNumber() points '>'. tokenizer->getColumnNumber() + 1); treeOp->Init(mozilla::AsVariant(operation)); nsHtml5String type = aAttributes->getValue(nsHtml5AttributeName::ATTR_TYPE); nsAutoString typeString; getTypeString(type, typeString); bool isModule = typeString.LowerCaseEqualsASCII("module"); bool importmap = typeString.LowerCaseEqualsASCII("importmap"); bool async = false; bool defer = false; if (importmap) { // If we see an importmap, we don't want to later start speculative // loads for modulepreloads, since such load might finish before // the importmap is created. This also applies to module scripts so // that any modulepreload integrity checks can be performed before // the modules scripts are loaded. // This state is not part of speculation rollback: If an importmap // is seen speculatively and the speculation is rolled back, the // importmap is still considered seen. // TODO: Sync importmap seenness between the main thread and the // parser thread. // https://bugzilla.mozilla.org/show_bug.cgi?id=1848312 mHasSeenImportMap = true; } nsHtml5String url = aAttributes->getValue(nsHtml5AttributeName::ATTR_HREF); if (!url) { url = aAttributes->getValue(nsHtml5AttributeName::ATTR_XLINK_HREF); } if (url) { async = aAttributes->contains(nsHtml5AttributeName::ATTR_ASYNC); defer = aAttributes->contains(nsHtml5AttributeName::ATTR_DEFER); if ((isModule && !mHasSeenImportMap) || (!isModule && !importmap)) { nsHtml5String type = aAttributes->getValue(nsHtml5AttributeName::ATTR_TYPE); nsHtml5String crossOrigin = aAttributes->getValue(nsHtml5AttributeName::ATTR_CROSSORIGIN); nsHtml5String nonce = aAttributes->getValue(nsHtml5AttributeName::ATTR_NONCE); nsHtml5String integrity = aAttributes->getValue(nsHtml5AttributeName::ATTR_INTEGRITY); nsHtml5String referrerPolicy = aAttributes->getValue( nsHtml5AttributeName::ATTR_REFERRERPOLICY); // Bug 1847712: SVG's `