From f6f046930dbd603819c4b5e96aa2462b569e8d6f Mon Sep 17 00:00:00 2001 From: Moonchild Date: Sun, 20 Oct 2024 09:35:39 +0200 Subject: [PATCH] Issue #2641 - Speculative load changes for referrerpolicy --- dom/base/nsContentSink.cpp | 28 +++- dom/base/nsContentSink.h | 4 +- dom/base/nsStyleLinkElement.cpp | 21 +-- dom/html/HTMLScriptElement.cpp | 6 + dom/html/HTMLScriptElement.h | 9 ++ dom/html/test/test_bug1260664.html | 2 +- dom/script/ScriptLoader.cpp | 19 ++- dom/script/ScriptLoader.h | 6 + dom/script/nsIScriptElement.h | 9 ++ dom/webidl/HTMLScriptElement.webidl | 2 + dom/xml/nsXMLContentSink.cpp | 9 +- dom/xml/nsXMLContentSink.h | 3 +- dom/xml/nsXMLFragmentContentSink.cpp | 7 +- layout/style/Loader.cpp | 6 +- layout/style/Loader.h | 2 + parser/html/nsHtml5SpeculativeLoad.cpp | 52 ++++--- parser/html/nsHtml5SpeculativeLoad.h | 130 ++++++++++-------- parser/html/nsHtml5TreeBuilderCppSupplement.h | 21 ++- parser/html/nsHtml5TreeOpExecutor.cpp | 50 ++++--- parser/html/nsHtml5TreeOpExecutor.h | 4 + 20 files changed, 262 insertions(+), 128 deletions(-) diff --git a/dom/base/nsContentSink.cpp b/dom/base/nsContentSink.cpp index 26e1ea5f89..066ec84466 100644 --- a/dom/base/nsContentSink.cpp +++ b/dom/base/nsContentSink.cpp @@ -472,6 +472,7 @@ nsContentSink::ProcessLinkHeader(const nsAString& aLinkData) nsAutoString media; nsAutoString anchor; nsAutoString crossOrigin; + nsAutoString referrerPolicy; nsAutoString destination; crossOrigin.SetIsVoid(true); @@ -660,6 +661,15 @@ nsContentSink::ProcessLinkHeader(const nsAString& aLinkData) destination = value; destination.StripWhitespace(); } + } else if (attr.LowerCaseEqualsLiteral("referrerpolicy")) { + // https://html.spec.whatwg.org/multipage/urls-and-fetching.html#referrer-policy-attribute + // The spec says that the referrer policy attribute is an enumerated attribute, + // case insensitive and includes the empty string. + // We will parse the value with AttributeReferrerPolicyFromString + // later, which will handle parsing it as an enumerated attribute. + if (referrerPolicy.IsEmpty()) { + referrerPolicy = value; + } } } } @@ -673,7 +683,7 @@ nsContentSink::ProcessLinkHeader(const nsAString& aLinkData) rv = ProcessLink(anchor, href, rel, // prefer RFC 5987 variant over non-I18zed version titleStar.IsEmpty() ? title : titleStar, - type, media, crossOrigin, destination); + type, media, crossOrigin, referrerPolicy, destination); } href.Truncate(); @@ -682,6 +692,7 @@ nsContentSink::ProcessLinkHeader(const nsAString& aLinkData) type.Truncate(); media.Truncate(); anchor.Truncate(); + referrerPolicy.Truncate(); crossOrigin.SetIsVoid(true); destination.Truncate(); @@ -696,7 +707,7 @@ nsContentSink::ProcessLinkHeader(const nsAString& aLinkData) rv = ProcessLink(anchor, href, rel, // prefer RFC 5987 variant over non-I18zed version titleStar.IsEmpty() ? title : titleStar, - type, media, crossOrigin, destination); + type, media, crossOrigin, referrerPolicy, destination); } return rv; @@ -708,6 +719,7 @@ nsContentSink::ProcessLink(const nsSubstring& aAnchor, const nsSubstring& aHref, const nsSubstring& aRel, const nsSubstring& aTitle, const nsSubstring& aType, const nsSubstring& aMedia, const nsSubstring& aCrossOrigin, + const nsAString& aReferrerPolicy, const nsSubstring& aDestination) { uint32_t linkTypes = @@ -749,7 +761,7 @@ nsContentSink::ProcessLink(const nsSubstring& aAnchor, const nsSubstring& aHref, bool isAlternate = linkTypes & nsStyleLinkElement::eALTERNATE; return ProcessStyleLink(nullptr, aHref, isAlternate, aTitle, aType, - aMedia); + aMedia, aReferrerPolicy); } nsresult @@ -758,7 +770,8 @@ nsContentSink::ProcessStyleLink(nsIContent* aElement, bool aAlternate, const nsSubstring& aTitle, const nsSubstring& aType, - const nsSubstring& aMedia) + const nsSubstring& aMedia, + const nsSubstring& aReferrerPolicy) { if (aAlternate && aTitle.IsEmpty()) { // alternates must have title return without error, for now @@ -797,13 +810,18 @@ nsContentSink::ProcessStyleLink(nsIContent* aElement, ("nsContentSink::ProcessStyleLink, integrity=%s", NS_ConvertUTF16toUTF8(integrity).get())); } + mozilla::net::ReferrerPolicy referrerPolicy = + mozilla::net::AttributeReferrerPolicyFromString(aReferrerPolicy); + if (referrerPolicy == net::RP_Unset) { + referrerPolicy = mDocument->GetReferrerPolicy(); + } // If this is a fragment parser, we don't want to observe. // We don't support CORS for processing instructions bool isAlternate; bool isExplicitlyEnabled; rv = mCSSLoader->LoadStyleLink(aElement, url, aTitle, aMedia, aAlternate, - CORS_NONE, mDocument->GetReferrerPolicy(), + CORS_NONE, referrerPolicy, integrity, mRunsToCompletion ? nullptr : this, &isAlternate, &isExplicitlyEnabled); NS_ENSURE_SUCCESS(rv, rv); diff --git a/dom/base/nsContentSink.h b/dom/base/nsContentSink.h index bed7392ca3..d6605af8d9 100644 --- a/dom/base/nsContentSink.h +++ b/dom/base/nsContentSink.h @@ -154,6 +154,7 @@ protected: const nsSubstring& aHref, const nsSubstring& aRel, const nsSubstring& aTitle, const nsSubstring& aType, const nsSubstring& aMedia, const nsSubstring& aCrossOrigin, + const nsSubstring& aReferrerPolicy, const nsSubstring& aDestination); virtual nsresult ProcessStyleLink(nsIContent* aElement, @@ -161,7 +162,8 @@ protected: bool aAlternate, const nsSubstring& aTitle, const nsSubstring& aType, - const nsSubstring& aMedia); + const nsSubstring& aMedia, + const nsSubstring& aReferrerPolicy); void PrefetchOrPreloadHref(const nsAString &aHref, nsINode *aSource, diff --git a/dom/base/nsStyleLinkElement.cpp b/dom/base/nsStyleLinkElement.cpp index 7a0a563350..971af21b4c 100644 --- a/dom/base/nsStyleLinkElement.cpp +++ b/dom/base/nsStyleLinkElement.cpp @@ -411,6 +411,16 @@ nsStyleLinkElement::DoUpdateStyleSheet(nsIDocument* aOldDocument, bool doneLoading = false; nsresult rv = NS_OK; + + // Load the link's referrerpolicy attribute. If the link does not provide a + // referrerpolicy attribute, ignore this and use the document's referrer + // policy + + net::ReferrerPolicy referrerPolicy = GetLinkReferrerPolicy(); + if (referrerPolicy == net::RP_Unset) { + referrerPolicy = doc->GetReferrerPolicy(); + } + if (isInline) { nsAutoString text; if (!nsContentUtils::GetNodeTextContent(thisContent, false, text, fallible)) { @@ -429,7 +439,7 @@ nsStyleLinkElement::DoUpdateStyleSheet(nsIDocument* aOldDocument, // Parse the style sheet. rv = doc->CSSLoader()-> - LoadInlineStyle(thisContent, text, mLineNumber, title, media, + LoadInlineStyle(thisContent, text, mLineNumber, title, media, referrerPolicy, scopeElement, aObserver, &doneLoading, &isAlternate, &isExplicitlyEnabled); } else { nsAutoString integrity; @@ -440,15 +450,6 @@ nsStyleLinkElement::DoUpdateStyleSheet(nsIDocument* aOldDocument, NS_ConvertUTF16toUTF8(integrity).get())); } - // if referrer attributes are enabled in preferences, load the link's referrer - // attribute. If the link does not provide a referrer attribute, ignore this - // and use the document's referrer policy - - net::ReferrerPolicy referrerPolicy = GetLinkReferrerPolicy(); - if (referrerPolicy == net::RP_Unset) { - referrerPolicy = doc->GetReferrerPolicy(); - } - // XXXbz clone the URI here to work around content policies modifying URIs. nsCOMPtr clonedURI; uri->Clone(getter_AddRefs(clonedURI)); diff --git a/dom/html/HTMLScriptElement.cpp b/dom/html/HTMLScriptElement.cpp index 48fa96ac90..a1a58e4fa2 100644 --- a/dom/html/HTMLScriptElement.cpp +++ b/dom/html/HTMLScriptElement.cpp @@ -324,6 +324,12 @@ HTMLScriptElement::GetCORSMode() const return AttrValueToCORSMode(GetParsedAttr(nsGkAtoms::crossorigin)); } +mozilla::net::ReferrerPolicy +HTMLScriptElement::GetReferrerPolicy() +{ + return GetReferrerPolicyAsEnum(); +} + bool HTMLScriptElement::HasScriptContent() { diff --git a/dom/html/HTMLScriptElement.h b/dom/html/HTMLScriptElement.h index a80f0262a1..b9affc49a1 100644 --- a/dom/html/HTMLScriptElement.h +++ b/dom/html/HTMLScriptElement.h @@ -42,6 +42,7 @@ public: virtual void GetScriptCharset(nsAString& charset) override; virtual void FreezeExecutionAttrs(nsIDocument* aOwnerDoc) override; virtual CORSMode GetCORSMode() const override; + virtual mozilla::net::ReferrerPolicy GetReferrerPolicy() override; // nsIContent virtual nsresult BindToTree(nsIDocument* aDocument, nsIContent* aParent, @@ -96,6 +97,14 @@ public: { SetHTMLAttr(nsGkAtoms::integrity, aIntegrity, rv); } + void SetReferrerPolicy(const nsAString& aReferrerPolicy, ErrorResult& aError) + { + SetHTMLAttr(nsGkAtoms::referrerpolicy, aReferrerPolicy, aError); + } + void GetReferrerPolicy(nsAString& aReferrerPolicy) + { + GetEnumAttr(nsGkAtoms::referrerpolicy, EmptyCString().get(), aReferrerPolicy); + } bool Async(); void SetAsync(bool aValue, ErrorResult& rv); bool NoModule(); diff --git a/dom/html/test/test_bug1260664.html b/dom/html/test/test_bug1260664.html index f034328956..9bc43fdb3c 100644 --- a/dom/html/test/test_bug1260664.html +++ b/dom/html/test/test_bug1260664.html @@ -24,7 +24,7 @@ SimpleTest.waitForExplicitFinish(); SimpleTest.waitForFocus(runTests); function runTests() { - var elements = [ "iframe", "img", "a", "area", "link" ]; + var elements = [ "iframe", "img", "a", "area", "link", "script"]; for (var i = 0; i < elements.length; ++i) { reflectLimitedEnumerated({ diff --git a/dom/script/ScriptLoader.cpp b/dom/script/ScriptLoader.cpp index d7553b024e..01b03c3f27 100644 --- a/dom/script/ScriptLoader.cpp +++ b/dom/script/ScriptLoader.cpp @@ -1563,7 +1563,7 @@ ScriptLoader::ProcessScriptElement(nsIScriptElement *aElement) // Step 15. and later in the HTML5 spec nsresult rv = NS_OK; RefPtr request; - mozilla::net::ReferrerPolicy ourRefPolicy = mDocument->GetReferrerPolicy(); + mozilla::net::ReferrerPolicy referrerPolicy = GetReferrerPolicy(aElement); if (aElement->GetScriptExternal()) { // external script nsCOMPtr scriptURI = aElement->GetScriptURI(); @@ -1593,7 +1593,7 @@ ScriptLoader::ProcessScriptElement(nsIScriptElement *aElement) aElement->GetScriptCharset(elementCharset); if (elementCharset.Equals(preloadCharset) && ourCORSMode == request->CORSMode() && - ourRefPolicy == request->ReferrerPolicy() && + referrerPolicy == request->ReferrerPolicy() && scriptKind == request->mKind) { rv = CheckContentPolicy(mDocument, aElement, request->mURI, type, false); if (NS_FAILED(rv)) { @@ -1639,7 +1639,7 @@ ScriptLoader::ProcessScriptElement(nsIScriptElement *aElement) nsCOMPtr principal = scriptContent->NodePrincipal(); request = CreateLoadRequest(scriptKind, scriptURI, aElement, principal, - ourCORSMode, sriMetadata, ourRefPolicy); + ourCORSMode, sriMetadata, referrerPolicy); request->mIsInline = false; request->SetScriptMode(aElement->GetScriptDeferred(), aElement->GetScriptAsync()); @@ -1760,7 +1760,7 @@ ScriptLoader::ProcessScriptElement(nsIScriptElement *aElement) mDocument->NodePrincipal(), CORS_NONE, SRIMetadata(), // SRI doesn't apply - ourRefPolicy); + referrerPolicy); request->mIsInline = true; request->mLineNo = aElement->GetScriptLineNumber(); @@ -1826,6 +1826,17 @@ ScriptLoader::ProcessScriptElement(nsIScriptElement *aElement) return ProcessRequest(request) == NS_ERROR_HTMLPARSER_BLOCK; } +mozilla::net::ReferrerPolicy +ScriptLoader::GetReferrerPolicy(nsIScriptElement* aElement) +{ + mozilla::net::ReferrerPolicy scriptReferrerPolicy = + aElement->GetReferrerPolicy(); + if (scriptReferrerPolicy != mozilla::net::RP_Unset) { + return scriptReferrerPolicy; + } + return mDocument->GetReferrerPolicy(); +} + namespace { class NotifyOffThreadScriptLoadCompletedRunnable : public Runnable diff --git a/dom/script/ScriptLoader.h b/dom/script/ScriptLoader.h index 6cf108e331..f66c9ede1d 100644 --- a/dom/script/ScriptLoader.h +++ b/dom/script/ScriptLoader.h @@ -619,6 +619,12 @@ private: void ContinueParserAsync(ScriptLoadRequest* aParserBlockingRequest); + /** + * Given a script element, get the referrer policy that should be applied to + * load requests. + */ + mozilla::net::ReferrerPolicy GetReferrerPolicy(nsIScriptElement* aElement); + /** * Helper function to check the content policy for a given request. */ diff --git a/dom/script/nsIScriptElement.h b/dom/script/nsIScriptElement.h index a654291aa0..6a6f985d33 100644 --- a/dom/script/nsIScriptElement.h +++ b/dom/script/nsIScriptElement.h @@ -16,6 +16,7 @@ #include "nsContentCreatorFunctions.h" #include "nsIDOMHTMLScriptElement.h" #include "mozilla/CORSMode.h" +#include "mozilla/net/ReferrerPolicy.h" #define NS_ISCRIPTELEMENT_IID \ { 0xe60fca9b, 0x1b96, 0x4e4e, \ @@ -263,6 +264,14 @@ public: return mozilla::CORS_NONE; } + /** + * Get referrer policy of the script element + */ + virtual mozilla::net::ReferrerPolicy GetReferrerPolicy() + { + return mozilla::net::RP_Unset; + } + /** * Fire an error event */ diff --git a/dom/webidl/HTMLScriptElement.webidl b/dom/webidl/HTMLScriptElement.webidl index 6b48a52443..bb9618428a 100644 --- a/dom/webidl/HTMLScriptElement.webidl +++ b/dom/webidl/HTMLScriptElement.webidl @@ -25,6 +25,8 @@ interface HTMLScriptElement : HTMLElement { [CEReactions, SetterThrows] attribute DOMString? crossOrigin; [CEReactions, SetterThrows] + attribute DOMString referrerPolicy; + [CEReactions, SetterThrows] attribute DOMString text; [CEReactions, SetterThrows, Pure] attribute DOMString nonce; diff --git a/dom/xml/nsXMLContentSink.cpp b/dom/xml/nsXMLContentSink.cpp index 7db1ea4a6a..2125416d73 100644 --- a/dom/xml/nsXMLContentSink.cpp +++ b/dom/xml/nsXMLContentSink.cpp @@ -655,7 +655,8 @@ nsXMLContentSink::ProcessStyleLink(nsIContent* aElement, bool aAlternate, const nsSubstring& aTitle, const nsSubstring& aType, - const nsSubstring& aMedia) + const nsSubstring& aMedia, + const nsSubstring& aReferrerPolicy) { nsresult rv = NS_OK; mPrettyPrintXML = false; @@ -714,7 +715,7 @@ nsXMLContentSink::ProcessStyleLink(nsIContent* aElement, // Let nsContentSink deal with css. rv = nsContentSink::ProcessStyleLink(aElement, aHref, aAlternate, - aTitle, aType, aMedia); + aTitle, aType, aMedia, aReferrerPolicy); // nsContentSink::ProcessStyleLink handles the bookkeeping here wrt // pending sheets. @@ -1261,7 +1262,9 @@ nsXMLContentSink::HandleProcessingInstruction(const char16_t *aTarget, return DidProcessATokenImpl(); } - rv = ProcessStyleLink(node, href, isAlternate, title, type, media); + // processing instructions don't have a referrerpolicy + // pseudo-attribute, so we pass in an empty string + rv = ProcessStyleLink(node, href, isAlternate, title, type, media, EmptyString()); return NS_SUCCEEDED(rv) ? DidProcessATokenImpl() : rv; } diff --git a/dom/xml/nsXMLContentSink.h b/dom/xml/nsXMLContentSink.h index ea190954a2..d7399349e8 100644 --- a/dom/xml/nsXMLContentSink.h +++ b/dom/xml/nsXMLContentSink.h @@ -150,7 +150,8 @@ protected: bool aAlternate, const nsSubstring& aTitle, const nsSubstring& aType, - const nsSubstring& aMedia) override; + const nsSubstring& aMedia, + const nsSubstring& aReferrerPolicy) override; nsresult LoadXSLStyleSheet(nsIURI* aUrl); diff --git a/dom/xml/nsXMLFragmentContentSink.cpp b/dom/xml/nsXMLFragmentContentSink.cpp index a4bb406351..664fbbeb6a 100644 --- a/dom/xml/nsXMLFragmentContentSink.cpp +++ b/dom/xml/nsXMLFragmentContentSink.cpp @@ -97,7 +97,9 @@ protected: bool aAlternate, const nsSubstring& aTitle, const nsSubstring& aType, - const nsSubstring& aMedia) override; + const nsSubstring& aMedia, + const nsSubstring& aReferrerPolicy) override; + nsresult LoadXSLStyleSheet(nsIURI* aUrl); void StartLayout(); @@ -332,7 +334,8 @@ nsXMLFragmentContentSink::ProcessStyleLink(nsIContent* aElement, bool aAlternate, const nsSubstring& aTitle, const nsSubstring& aType, - const nsSubstring& aMedia) + const nsSubstring& aMedia, + const nsSubstring& aReferrerPolicy) { // don't process until moved to document return NS_OK; diff --git a/layout/style/Loader.cpp b/layout/style/Loader.cpp index 4e1a1d6fe1..3b445eb567 100644 --- a/layout/style/Loader.cpp +++ b/layout/style/Loader.cpp @@ -1941,6 +1941,7 @@ Loader::LoadInlineStyle(nsIContent* aElement, uint32_t aLineNumber, const nsAString& aTitle, const nsAString& aMedia, + ReferrerPolicy aReferrerPolicy, Element* aScopeElement, nsICSSLoaderObserver* aObserver, bool* aCompleted, @@ -1964,11 +1965,12 @@ Loader::LoadInlineStyle(nsIContent* aElement, // Since we're not planning to load a URI, no need to hand a principal to the // load data or to CreateSheet(). Also, OK to use CORS_NONE for the CORS - // mode and mDocument's ReferrerPolicy. + // mode. + StyleSheetState state; RefPtr sheet; nsresult rv = CreateSheet(nullptr, aElement, nullptr, eAuthorSheetFeatures, - CORS_NONE, mDocument->GetReferrerPolicy(), + CORS_NONE, aReferrerPolicy, EmptyString(), // no inline integrity checks false, false, aTitle, state, aIsAlternate, &sheet); diff --git a/layout/style/Loader.h b/layout/style/Loader.h index c9af2f39e8..2319164a2f 100644 --- a/layout/style/Loader.h +++ b/layout/style/Loader.h @@ -224,6 +224,7 @@ public: * @param aLineNumber the line number at which the stylesheet data started. * @param aTitle the title of the sheet. * @param aMedia the media string for the sheet. + * @param aReferrerPolicy the referrer policy for loading the sheet. * @param aObserver the observer to notify when the load completes. * May be null. * @param [out] aCompleted whether parsing of the sheet completed. @@ -237,6 +238,7 @@ public: uint32_t aLineNumber, const nsAString& aTitle, const nsAString& aMedia, + ReferrerPolicy aReferrerPolicy, mozilla::dom::Element* aScopeElement, nsICSSLoaderObserver* aObserver, bool* aCompleted, diff --git a/parser/html/nsHtml5SpeculativeLoad.cpp b/parser/html/nsHtml5SpeculativeLoad.cpp index 35c11b739d..14e8f9ea9c 100644 --- a/parser/html/nsHtml5SpeculativeLoad.cpp +++ b/parser/html/nsHtml5SpeculativeLoad.cpp @@ -28,16 +28,18 @@ nsHtml5SpeculativeLoad::Perform(nsHtml5TreeOpExecutor* aExecutor) { switch (mOpCode) { case eSpeculativeLoadBase: - aExecutor->SetSpeculationBase(mUrl); + aExecutor->SetSpeculationBase(mUrlOrSizes); break; case eSpeculativeLoadCSP: - aExecutor->AddSpeculationCSP(mMetaCSP); + aExecutor->AddSpeculationCSP(mTypeOrCharsetSourceOrDocumentModeOrMetaCSPOrSizesOrIntegrity); break; case eSpeculativeLoadMetaReferrer: - aExecutor->SetSpeculationReferrerPolicy(mReferrerPolicy); + aExecutor->SetSpeculationReferrerPolicy(mReferrerPolicyOrIntegrity); break; case eSpeculativeLoadImage: - aExecutor->PreloadImage(mUrl, mCrossOrigin, mSrcset, mSizes, mReferrerPolicy); + aExecutor->PreloadImage(mUrlOrSizes, mCrossOriginOrMedia, mCharsetOrSrcset, + mTypeOrCharsetSourceOrDocumentModeOrMetaCSPOrSizesOrIntegrity, + mReferrerPolicyOrIntegrity); break; case eSpeculativeLoadOpenPicture: aExecutor->PreloadOpenPicture(); @@ -46,55 +48,61 @@ nsHtml5SpeculativeLoad::Perform(nsHtml5TreeOpExecutor* aExecutor) aExecutor->PreloadEndPicture(); break; case eSpeculativeLoadPictureSource: - aExecutor->PreloadPictureSource(mSrcset, mSizes, mTypeOrCharsetSourceOrDocumentMode, - mMedia); + aExecutor->PreloadPictureSource(mCharsetOrSrcset, mUrlOrSizes, + mTypeOrCharsetSourceOrDocumentModeOrMetaCSPOrSizesOrIntegrity, + mCrossOriginOrMedia); break; case eSpeculativeLoadScript: - aExecutor->PreloadScript(mUrl, mCharset, mTypeOrCharsetSourceOrDocumentMode, - mCrossOrigin, mIntegrity, false, + aExecutor->PreloadScript(mUrlOrSizes, mCharsetOrSrcset, + mTypeOrCharsetSourceOrDocumentModeOrMetaCSPOrSizesOrIntegrity, + mCrossOriginOrMedia, mReferrerPolicyOrIntegrity, mScriptReferrerPolicy, false, mIsAsync, mIsDefer, false); break; case eSpeculativeLoadScriptFromHead: - aExecutor->PreloadScript(mUrl, mCharset, mTypeOrCharsetSourceOrDocumentMode, - mCrossOrigin, mIntegrity, true, + aExecutor->PreloadScript(mUrlOrSizes, mCharsetOrSrcset, + mTypeOrCharsetSourceOrDocumentModeOrMetaCSPOrSizesOrIntegrity, + mCrossOriginOrMedia, mReferrerPolicyOrIntegrity, mScriptReferrerPolicy, true, mIsAsync, mIsDefer, false); break; case eSpeculativeLoadNoModuleScript: - aExecutor->PreloadScript(mUrl, mCharset, mTypeOrCharsetSourceOrDocumentMode, - mCrossOrigin, mIntegrity, false, + aExecutor->PreloadScript(mUrlOrSizes, mCharsetOrSrcset, + mTypeOrCharsetSourceOrDocumentModeOrMetaCSPOrSizesOrIntegrity, + mCrossOriginOrMedia, mReferrerPolicyOrIntegrity, mScriptReferrerPolicy, false, mIsAsync, mIsDefer, true); break; case eSpeculativeLoadNoModuleScriptFromHead: - aExecutor->PreloadScript(mUrl, mCharset, mTypeOrCharsetSourceOrDocumentMode, - mCrossOrigin, mIntegrity, true, + aExecutor->PreloadScript(mUrlOrSizes, mCharsetOrSrcset, + mTypeOrCharsetSourceOrDocumentModeOrMetaCSPOrSizesOrIntegrity, + mCrossOriginOrMedia, mReferrerPolicyOrIntegrity, mScriptReferrerPolicy, true, mIsAsync, mIsDefer, true); break; case eSpeculativeLoadStyle: - aExecutor->PreloadStyle(mUrl, mCharset, mCrossOrigin, mIntegrity); + aExecutor->PreloadStyle(mUrlOrSizes, mCharsetOrSrcset, mCrossOriginOrMedia, mReferrerPolicyOrIntegrity, + mTypeOrCharsetSourceOrDocumentModeOrMetaCSPOrSizesOrIntegrity); break; case eSpeculativeLoadManifest: - aExecutor->ProcessOfflineManifest(mUrl); + aExecutor->ProcessOfflineManifest(mUrlOrSizes); break; case eSpeculativeLoadSetDocumentCharset: { nsAutoCString narrowName; - CopyUTF16toUTF8(mCharset, narrowName); - NS_ASSERTION(mTypeOrCharsetSourceOrDocumentMode.Length() == 1, + CopyUTF16toUTF8(mCharsetOrSrcset, narrowName); + NS_ASSERTION(mTypeOrCharsetSourceOrDocumentModeOrMetaCSPOrSizesOrIntegrity.Length() == 1, "Unexpected charset source string"); - int32_t intSource = (int32_t)mTypeOrCharsetSourceOrDocumentMode.First(); + int32_t intSource = (int32_t)mTypeOrCharsetSourceOrDocumentModeOrMetaCSPOrSizesOrIntegrity.First(); aExecutor->SetDocumentCharsetAndSource(narrowName, intSource); } break; case eSpeculativeLoadSetDocumentMode: { - NS_ASSERTION(mTypeOrCharsetSourceOrDocumentMode.Length() == 1, + NS_ASSERTION(mTypeOrCharsetSourceOrDocumentModeOrMetaCSPOrSizesOrIntegrity.Length() == 1, "Unexpected document mode string"); nsHtml5DocumentMode mode = - (nsHtml5DocumentMode)mTypeOrCharsetSourceOrDocumentMode.First(); + (nsHtml5DocumentMode)mTypeOrCharsetSourceOrDocumentModeOrMetaCSPOrSizesOrIntegrity.First(); aExecutor->SetDocumentMode(mode); } break; case eSpeculativeLoadPreconnect: - aExecutor->Preconnect(mUrl, mCrossOrigin); + aExecutor->Preconnect(mUrlOrSizes, mCrossOriginOrMedia); break; default: NS_NOTREACHED("Bogus speculative load."); diff --git a/parser/html/nsHtml5SpeculativeLoad.h b/parser/html/nsHtml5SpeculativeLoad.h index 1f4a617416..d9467d3875 100644 --- a/parser/html/nsHtml5SpeculativeLoad.h +++ b/parser/html/nsHtml5SpeculativeLoad.h @@ -7,6 +7,7 @@ #include "nsString.h" #include "nsContentUtils.h" +#include "mozilla/net/ReferrerPolicy.h" class nsHtml5TreeOpExecutor; @@ -43,7 +44,7 @@ class nsHtml5SpeculativeLoad { NS_PRECONDITION(mOpCode == eSpeculativeLoadUninitialized, "Trying to reinitialize a speculative load!"); mOpCode = eSpeculativeLoadBase; - aUrl.ToString(mUrl); + aUrl.ToString(mUrlOrSizes); } inline void InitMetaCSP(nsHtml5String aCSP) @@ -53,7 +54,7 @@ class nsHtml5SpeculativeLoad { mOpCode = eSpeculativeLoadCSP; nsString csp; // Not Auto, because using it to hold nsStringBuffer* aCSP.ToString(csp); - mMetaCSP.Assign( + mTypeOrCharsetSourceOrDocumentModeOrMetaCSPOrSizesOrIntegrity.Assign( nsContentUtils::TrimWhitespace(csp)); } @@ -65,7 +66,7 @@ class nsHtml5SpeculativeLoad { nsString referrerPolicy; // Not Auto, because using it to hold nsStringBuffer* aReferrerPolicy.ToString(referrerPolicy); - mReferrerPolicy.Assign( + mReferrerPolicyOrIntegrity.Assign( nsContentUtils::TrimWhitespace( referrerPolicy)); } @@ -79,16 +80,16 @@ class nsHtml5SpeculativeLoad { NS_PRECONDITION(mOpCode == eSpeculativeLoadUninitialized, "Trying to reinitialize a speculative load!"); mOpCode = eSpeculativeLoadImage; - aUrl.ToString(mUrl); - aCrossOrigin.ToString(mCrossOrigin); + aUrl.ToString(mUrlOrSizes); + aCrossOrigin.ToString(mCrossOriginOrMedia); nsString referrerPolicy; // Not Auto, because using it to hold nsStringBuffer* aReferrerPolicy.ToString(referrerPolicy); - mReferrerPolicy.Assign( + mReferrerPolicyOrIntegrity.Assign( nsContentUtils::TrimWhitespace( referrerPolicy)); - aSrcset.ToString(mSrcset); - aSizes.ToString(mSizes); + aSrcset.ToString(mCharsetOrSrcset); + aSizes.ToString(mTypeOrCharsetSourceOrDocumentModeOrMetaCSPOrSizesOrIntegrity); } // elements have multiple nodes followed by an , @@ -120,10 +121,10 @@ class nsHtml5SpeculativeLoad { NS_PRECONDITION(mOpCode == eSpeculativeLoadUninitialized, "Trying to reinitialize a speculative load!"); mOpCode = eSpeculativeLoadPictureSource; - aSrcset.ToString(mSrcset); - aSizes.ToString(mSizes); - aType.ToString(mTypeOrCharsetSourceOrDocumentMode); - aMedia.ToString(mMedia); + aSrcset.ToString(mCharsetOrSrcset); + aSizes.ToString(mUrlOrSizes); + aType.ToString(mTypeOrCharsetSourceOrDocumentModeOrMetaCSPOrSizesOrIntegrity); + aMedia.ToString(mCrossOriginOrMedia); } inline void InitScript(nsHtml5String aUrl, @@ -131,6 +132,7 @@ class nsHtml5SpeculativeLoad { nsHtml5String aType, nsHtml5String aCrossOrigin, nsHtml5String aIntegrity, + nsHtml5String aReferrerPolicy, bool aParserInHead, bool aAsync, bool aDefer, @@ -145,11 +147,19 @@ class nsHtml5SpeculativeLoad { mOpCode = aParserInHead ? eSpeculativeLoadScriptFromHead : eSpeculativeLoadScript; } - aUrl.ToString(mUrl); - aCharset.ToString(mCharset); - aType.ToString(mTypeOrCharsetSourceOrDocumentMode); - aCrossOrigin.ToString(mCrossOrigin); - aIntegrity.ToString(mIntegrity); + aUrl.ToString(mUrlOrSizes); + aCharset.ToString(mCharsetOrSrcset); + aType.ToString(mTypeOrCharsetSourceOrDocumentModeOrMetaCSPOrSizesOrIntegrity); + aCrossOrigin.ToString(mCrossOriginOrMedia); + aIntegrity.ToString(mReferrerPolicyOrIntegrity); + nsAutoString referrerPolicy; + aReferrerPolicy.ToString(referrerPolicy); + referrerPolicy = + nsContentUtils::TrimWhitespace< + nsContentUtils::IsHTMLWhitespace>(referrerPolicy); + mScriptReferrerPolicy = + mozilla::net::AttributeReferrerPolicyFromString(referrerPolicy); + mIsAsync = aAsync; mIsDefer = aDefer; } @@ -157,15 +167,22 @@ class nsHtml5SpeculativeLoad { inline void InitStyle(nsHtml5String aUrl, nsHtml5String aCharset, nsHtml5String aCrossOrigin, + nsHtml5String aReferrerPolicy, nsHtml5String aIntegrity) { NS_PRECONDITION(mOpCode == eSpeculativeLoadUninitialized, "Trying to reinitialize a speculative load!"); mOpCode = eSpeculativeLoadStyle; - aUrl.ToString(mUrl); - aCharset.ToString(mCharset); - aCrossOrigin.ToString(mCrossOrigin); - aIntegrity.ToString(mIntegrity); + aUrl.ToString(mUrlOrSizes); + aCharset.ToString(mCharsetOrSrcset); + aCrossOrigin.ToString(mCrossOriginOrMedia); + nsString + referrerPolicy; // Not Auto, because using it to hold nsStringBuffer* + aReferrerPolicy.ToString(referrerPolicy); + mReferrerPolicyOrIntegrity.Assign( + nsContentUtils::TrimWhitespace( + referrerPolicy)); + aIntegrity.ToString(mTypeOrCharsetSourceOrDocumentModeOrMetaCSPOrSizesOrIntegrity); } /** @@ -184,7 +201,7 @@ class nsHtml5SpeculativeLoad { NS_PRECONDITION(mOpCode == eSpeculativeLoadUninitialized, "Trying to reinitialize a speculative load!"); mOpCode = eSpeculativeLoadManifest; - aUrl.ToString(mUrl); + aUrl.ToString(mUrlOrSizes); } /** @@ -203,8 +220,8 @@ class nsHtml5SpeculativeLoad { NS_PRECONDITION(mOpCode == eSpeculativeLoadUninitialized, "Trying to reinitialize a speculative load!"); mOpCode = eSpeculativeLoadSetDocumentCharset; - CopyUTF8toUTF16(aCharset, mCharset); - mTypeOrCharsetSourceOrDocumentMode.Assign((char16_t)aCharsetSource); + CopyUTF8toUTF16(aCharset, mCharsetOrSrcset); + mTypeOrCharsetSourceOrDocumentModeOrMetaCSPOrSizesOrIntegrity.Assign((char16_t)aCharsetSource); } /** @@ -218,7 +235,7 @@ class nsHtml5SpeculativeLoad { NS_PRECONDITION(mOpCode == eSpeculativeLoadUninitialized, "Trying to reinitialize a speculative load!"); mOpCode = eSpeculativeLoadSetDocumentMode; - mTypeOrCharsetSourceOrDocumentMode.Assign((char16_t)aMode); + mTypeOrCharsetSourceOrDocumentModeOrMetaCSPOrSizesOrIntegrity.Assign((char16_t)aMode); } inline void InitPreconnect(nsHtml5String aUrl, nsHtml5String aCrossOrigin) @@ -226,8 +243,8 @@ class nsHtml5SpeculativeLoad { NS_PRECONDITION(mOpCode == eSpeculativeLoadUninitialized, "Trying to reinitialize a speculative load!"); mOpCode = eSpeculativeLoadPreconnect; - aUrl.ToString(mUrl); - aCrossOrigin.ToString(mCrossOrigin); + aUrl.ToString(mUrlOrSizes); + aCrossOrigin.ToString(mCrossOriginOrMedia); } void Perform(nsHtml5TreeOpExecutor* aExecutor); @@ -241,55 +258,56 @@ class nsHtml5SpeculativeLoad { bool mIsAsync; bool mIsDefer; - nsString mUrl; - nsString mReferrerPolicy; - nsString mMetaCSP; + /* If mOpCode is eSpeculativeLoadPictureSource, this is the value of the + * "sizes" attribute. If the attribute is not set, this will be a void + * string. Otherwise it empty or the value of the url. + */ + nsString mUrlOrSizes; + /** + * If mOpCode is eSpeculativeLoadScript[FromHead], this is the value of the + * "integrity" attribute. If the attribute is not set, this will be a void + * string. Otherwise it is empty or the value of the referrer policy. + */ + nsString mReferrerPolicyOrIntegrity; /** * If mOpCode is eSpeculativeLoadStyle or eSpeculativeLoadScript[FromHead] * then this is the value of the "charset" attribute. For * eSpeculativeLoadSetDocumentCharset it is the charset that the - * document's charset is being set to. Otherwise it's empty. + * document's charset is being set to. If mOpCode is eSpeculativeLoadImage + * or eSpeculativeLoadPictureSource, this is the value of the "srcset" attribute. + * If the attribute is not set, this will be a void string. Otherwise it's empty. */ - nsString mCharset; + nsString mCharsetOrSrcset; /** * If mOpCode is eSpeculativeLoadSetDocumentCharset, this is a * one-character string whose single character's code point is to be * interpreted as a charset source integer. If mOpCode is * eSpeculativeLoadSetDocumentMode, this is a one-character string whose * single character's code point is to be interpreted as an - * nsHtml5DocumentMode. Otherwise, it is empty or the value of the type - * attribute. + * nsHtml5DocumentMode. If mOpCode is eSpeculativeLoadCSP, this is a meta + * element's CSP value. If mOpCode is eSpeculativeLoadImage, this is the + * value of the "sizes" attribute. If the attribute is not set, this will + * be a void string. If mOpCode is eSpeculativeLoadStyle, this + * is the value of the "integrity" attribute. If the attribute is not set, + * this will be a void string. Otherwise it is empty or the value of the + * referrer policy. Otherwise, it is empty or the value of the type attribute. */ - nsString mTypeOrCharsetSourceOrDocumentMode; + nsString mTypeOrCharsetSourceOrDocumentModeOrMetaCSPOrSizesOrIntegrity; /** * If mOpCode is eSpeculativeLoadImage or eSpeculativeLoadScript[FromHead] * or eSpeculativeLoadPreconnect this is the value of the "crossorigin" * attribute. If the attribute is not set, this will be a void string. + * If mOpCode is eSpeculativeLoadPictureSource, this is the value of the + * "media" attribute. If the attribute is not set, this will be a void string. */ - nsString mCrossOrigin; + nsString mCrossOriginOrMedia; /** - * If mOpCode is eSpeculativeLoadImage or eSpeculativeLoadPictureSource, - * this is the value of "srcset" attribute. If the attribute is not set, - * this will be a void string. + * If mOpCode is eSpeculativeLoadScript[FromHead] this represents the value + * of the "referrerpolicy" attribute. This field holds one of the values + * (REFERRER_POLICY_*) defined in nsIHttpChannel. */ - nsString mSrcset; - /** - * If mOpCode is eSpeculativeLoadPictureSource, this is the value of "sizes" - * attribute. If the attribute is not set, this will be a void string. - */ - nsString mSizes; - /** - * If mOpCode is eSpeculativeLoadPictureSource, this is the value of "media" - * attribute. If the attribute is not set, this will be a void string. - */ - nsString mMedia; - /** - * If mOpCode is eSpeculativeLoadScript[FromHead], this is the value of the - * "integrity" attribute. If the attribute is not set, this will be a void - * string. - */ - nsString mIntegrity; + mozilla::net::ReferrerPolicy mScriptReferrerPolicy; }; #endif // nsHtml5SpeculativeLoad_h diff --git a/parser/html/nsHtml5TreeBuilderCppSupplement.h b/parser/html/nsHtml5TreeBuilderCppSupplement.h index a3a7e92155..d17216b758 100644 --- a/parser/html/nsHtml5TreeBuilderCppSupplement.h +++ b/parser/html/nsHtml5TreeBuilderCppSupplement.h @@ -184,6 +184,8 @@ nsHtml5TreeBuilder::createElement(int32_t aNamespace, aAttributes->getValue(nsHtml5AttributeName::ATTR_CROSSORIGIN); nsHtml5String integrity = aAttributes->getValue(nsHtml5AttributeName::ATTR_INTEGRITY); + nsHtml5String referrerPolicy = + aAttributes->getValue(nsHtml5AttributeName::ATTR_REFERRERPOLICY); bool async = aAttributes->contains(nsHtml5AttributeName::ATTR_ASYNC); bool defer = @@ -196,6 +198,7 @@ nsHtml5TreeBuilder::createElement(int32_t aNamespace, type, crossOrigin, integrity, + referrerPolicy, mode == nsHtml5TreeBuilder::IN_HEAD, async, defer, @@ -218,8 +221,10 @@ nsHtml5TreeBuilder::createElement(int32_t aNamespace, aAttributes->getValue(nsHtml5AttributeName::ATTR_CROSSORIGIN); nsHtml5String integrity = aAttributes->getValue(nsHtml5AttributeName::ATTR_INTEGRITY); + nsHtml5String referrerPolicy = + aAttributes->getValue(nsHtml5AttributeName::ATTR_REFERRERPOLICY); mSpeculativeLoadQueue.AppendElement()->InitStyle( - url, charset, crossOrigin, integrity); + url, charset, crossOrigin, referrerPolicy, integrity); } } else if (rel.LowerCaseEqualsASCII("preconnect")) { nsHtml5String url = @@ -245,12 +250,15 @@ nsHtml5TreeBuilder::createElement(int32_t aNamespace, aAttributes->getValue(nsHtml5AttributeName::ATTR_CROSSORIGIN); nsHtml5String integrity = aAttributes->getValue(nsHtml5AttributeName::ATTR_INTEGRITY); + nsHtml5String referrerPolicy = + aAttributes->getValue(nsHtml5AttributeName::ATTR_REFERRERPOLICY); mSpeculativeLoadQueue.AppendElement()->InitScript( url, charset, type, crossOrigin, integrity, + referrerPolicy, mode == nsHtml5TreeBuilder::IN_HEAD, false, false, @@ -263,8 +271,10 @@ nsHtml5TreeBuilder::createElement(int32_t aNamespace, aAttributes->getValue(nsHtml5AttributeName::ATTR_CROSSORIGIN); nsHtml5String integrity = aAttributes->getValue(nsHtml5AttributeName::ATTR_INTEGRITY); + nsHtml5String referrerPolicy = + aAttributes->getValue(nsHtml5AttributeName::ATTR_REFERRERPOLICY); mSpeculativeLoadQueue.AppendElement()->InitStyle( - url, charset, crossOrigin, integrity); + url, charset, crossOrigin, referrerPolicy, integrity); } else if (preloadAs.LowerCaseEqualsASCII("image")) { nsHtml5String srcset = aAttributes->getValue(nsHtml5AttributeName::ATTR_SRCSET); @@ -345,12 +355,15 @@ nsHtml5TreeBuilder::createElement(int32_t aNamespace, aAttributes->getValue(nsHtml5AttributeName::ATTR_CROSSORIGIN); nsHtml5String integrity = aAttributes->getValue(nsHtml5AttributeName::ATTR_INTEGRITY); + nsHtml5String referrerPolicy = + aAttributes->getValue(nsHtml5AttributeName::ATTR_REFERRERPOLICY); mSpeculativeLoadQueue.AppendElement()->InitScript( url, nullptr, type, crossOrigin, integrity, + referrerPolicy, mode == nsHtml5TreeBuilder::IN_HEAD, false /* async */, false /* defer */, @@ -368,8 +381,10 @@ nsHtml5TreeBuilder::createElement(int32_t aNamespace, aAttributes->getValue(nsHtml5AttributeName::ATTR_CROSSORIGIN); nsHtml5String integrity = aAttributes->getValue(nsHtml5AttributeName::ATTR_INTEGRITY); + nsHtml5String referrerPolicy = + aAttributes->getValue(nsHtml5AttributeName::ATTR_REFERRERPOLICY); mSpeculativeLoadQueue.AppendElement()->InitStyle( - url, nullptr, crossOrigin, integrity); + url, nullptr, crossOrigin, referrerPolicy, integrity); } } break; diff --git a/parser/html/nsHtml5TreeOpExecutor.cpp b/parser/html/nsHtml5TreeOpExecutor.cpp index 33fb3d0c7d..7fe1fdd39e 100644 --- a/parser/html/nsHtml5TreeOpExecutor.cpp +++ b/parser/html/nsHtml5TreeOpExecutor.cpp @@ -914,12 +914,23 @@ nsHtml5TreeOpExecutor::ShouldPreloadURI(nsIURI *aURI) return true; } +net::ReferrerPolicy +nsHtml5TreeOpExecutor::GetPreloadReferrerPolicy( + const nsAString& aReferrerPolicy) +{ + net::ReferrerPolicy referrerPolicy = + net::AttributeReferrerPolicyFromString(aReferrerPolicy); + return referrerPolicy != net::RP_Unset ? referrerPolicy : + mSpeculationReferrerPolicy; +} + void nsHtml5TreeOpExecutor::PreloadScript(const nsAString& aURL, const nsAString& aCharset, const nsAString& aType, const nsAString& aCrossOrigin, const nsAString& aIntegrity, + net::ReferrerPolicy aReferrerPolicy, bool aScriptFromHead, bool aAsync, bool aDefer, @@ -929,24 +940,35 @@ nsHtml5TreeOpExecutor::PreloadScript(const nsAString& aURL, if (!uri) { return; } - mDocument->ScriptLoader()->PreloadURI(uri, aCharset, aType, aCrossOrigin, - aIntegrity, aScriptFromHead, aAsync, - aDefer, aNoModule, - mSpeculationReferrerPolicy); + net::ReferrerPolicy referrerPolicy = aReferrerPolicy != net::RP_Unset ? + aReferrerPolicy : mSpeculationReferrerPolicy; + mDocument->ScriptLoader() + ->PreloadURI(uri, + aCharset, + aType, + aCrossOrigin, + aIntegrity, + aScriptFromHead, + aAsync, + aDefer, + aNoModule, + referrerPolicy); } void nsHtml5TreeOpExecutor::PreloadStyle(const nsAString& aURL, const nsAString& aCharset, const nsAString& aCrossOrigin, + const nsAString& aReferrerPolicy, const nsAString& aIntegrity) { nsCOMPtr uri = ConvertIfNotPreloadedYet(aURL); if (!uri) { return; } - mDocument->PreloadStyle(uri, aCharset, aCrossOrigin, - mSpeculationReferrerPolicy, aIntegrity); + + mDocument->PreloadStyle(uri, aCharset, aCrossOrigin, GetPreloadReferrerPolicy(aReferrerPolicy), + aIntegrity); } void @@ -962,18 +984,10 @@ nsHtml5TreeOpExecutor::PreloadImage(const nsAString& aURL, aSizes, &isImgSet); if (uri && ShouldPreloadURI(uri)) { // use document wide referrer policy - mozilla::net::ReferrerPolicy referrerPolicy = mSpeculationReferrerPolicy; - // if enabled in preferences, use the referrer attribute from the image, if provided - bool referrerAttributeEnabled = Preferences::GetBool("network.http.enablePerElementReferrer", true); - if (referrerAttributeEnabled) { - mozilla::net::ReferrerPolicy imageReferrerPolicy = - mozilla::net::AttributeReferrerPolicyFromString(aImageReferrerPolicy); - if (imageReferrerPolicy != mozilla::net::RP_Unset) { - referrerPolicy = imageReferrerPolicy; - } - } - - mDocument->MaybePreLoadImage(uri, aCrossOrigin, referrerPolicy, isImgSet); + mDocument->MaybePreLoadImage(uri, + aCrossOrigin, + GetPreloadReferrerPolicy(aImageReferrerPolicy), + isImgSet); } } diff --git a/parser/html/nsHtml5TreeOpExecutor.h b/parser/html/nsHtml5TreeOpExecutor.h index 878f359c55..6a1a9e674a 100644 --- a/parser/html/nsHtml5TreeOpExecutor.h +++ b/parser/html/nsHtml5TreeOpExecutor.h @@ -249,6 +249,7 @@ class nsHtml5TreeOpExecutor final : public nsHtml5DocumentBuilder, const nsAString& aType, const nsAString& aCrossOrigin, const nsAString& aIntegrity, + ReferrerPolicy aReferrerPolicy, bool aScriptFromHead, bool aAsync, bool aDefer, @@ -256,6 +257,7 @@ class nsHtml5TreeOpExecutor final : public nsHtml5DocumentBuilder, void PreloadStyle(const nsAString& aURL, const nsAString& aCharset, const nsAString& aCrossOrigin, + const nsAString& aReferrerPolicy, const nsAString& aIntegrity); void PreloadImage(const nsAString& aURL, @@ -304,6 +306,8 @@ class nsHtml5TreeOpExecutor final : public nsHtml5DocumentBuilder, * list of preloaded URIs */ bool ShouldPreloadURI(nsIURI *aURI); + + ReferrerPolicy GetPreloadReferrerPolicy(const nsAString& aReferrerPolicy); }; #endif // nsHtml5TreeOpExecutor_h