Bug 1384493 - LoadStyleLink and LoadInlineStyle should use correct referrer policy draft
authorThomas Nguyen <tnguyen@mozilla.com>
Fri, 28 Jul 2017 13:47:22 +0800
changeset 620902 043173c6e91e42dd7a92f16c525faf9afb50efaa
parent 620901 47c86f298782068c2dfe929cbd84f2aa4b23cbce
child 640842 4957403d22638226a71fdd7147a096403ec6e878
push id72191
push userbmo:tnguyen@mozilla.com
push dateFri, 04 Aug 2017 03:03:33 +0000
bugs1384493
milestone57.0a1
Bug 1384493 - LoadStyleLink and LoadInlineStyle should use correct referrer policy If the link element has referrerpolicy attribute, we should use policy in the attribute with higher priority MozReview-Commit-ID: GZZeRaoxPUw
dom/base/nsContentSink.cpp
dom/base/nsContentSink.h
dom/base/nsStyleLinkElement.cpp
dom/xml/nsXMLContentSink.cpp
dom/xml/nsXMLContentSink.h
dom/xml/nsXMLFragmentContentSink.cpp
layout/style/Loader.cpp
layout/style/Loader.h
--- a/dom/base/nsContentSink.cpp
+++ b/dom/base/nsContentSink.cpp
@@ -453,16 +453,17 @@ nsContentSink::ProcessLinkHeader(const n
   nsAutoString href;
   nsAutoString rel;
   nsAutoString title;
   nsAutoString titleStar;
   nsAutoString type;
   nsAutoString media;
   nsAutoString anchor;
   nsAutoString crossOrigin;
+  nsAutoString referrerPolicy;
   nsAutoString as;
 
   crossOrigin.SetIsVoid(true);
 
   // copy to work buffer
   nsAutoString stringList(aLinkData);
 
   // put an extra null at the end
@@ -641,63 +642,74 @@ nsContentSink::ProcessLinkHeader(const n
               crossOrigin = value;
               crossOrigin.StripWhitespace();
             }
           } else if (attr.LowerCaseEqualsLiteral("as")) {
             if (as.IsEmpty()) {
               as = value;
               as.CompressWhitespace();
             }
+          } else if (attr.LowerCaseEqualsLiteral("referrerpolicy")) {
+            // https://html.spec.whatwg.org/multipage/urls-and-fetching.html#referrer-policy-attribute
+            // Specs says 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;
+            }
           }
         }
       }
     }
 
     if (endCh == kComma) {
       // hit a comma, process what we've got so far
 
       href.Trim(" \t\n\r\f"); // trim HTML5 whitespace
       if (!href.IsEmpty() && !rel.IsEmpty()) {
         rv = ProcessLink(anchor, href, rel,
                          // prefer RFC 5987 variant over non-I18zed version
                          titleStar.IsEmpty() ? title : titleStar,
-                         type, media, crossOrigin, as);
+                         type, media, crossOrigin, referrerPolicy, as);
       }
 
       href.Truncate();
       rel.Truncate();
       title.Truncate();
       type.Truncate();
       media.Truncate();
       anchor.Truncate();
+      referrerPolicy.Truncate();
       crossOrigin.SetIsVoid(true);
 
       seenParameters = false;
     }
 
     start = ++end;
   }
 
   href.Trim(" \t\n\r\f"); // trim HTML5 whitespace
   if (!href.IsEmpty() && !rel.IsEmpty()) {
     rv = ProcessLink(anchor, href, rel,
                      // prefer RFC 5987 variant over non-I18zed version
                      titleStar.IsEmpty() ? title : titleStar,
-                     type, media, crossOrigin, as);
+                     type, media, crossOrigin, referrerPolicy, as);
   }
 
   return rv;
 }
 
 
 nsresult
 nsContentSink::ProcessLink(const nsAString& aAnchor, const nsAString& aHref,
                            const nsAString& aRel, const nsAString& aTitle,
                            const nsAString& aType, const nsAString& aMedia,
                            const nsAString& aCrossOrigin,
+                           const nsAString& aReferrerPolicy,
                            const nsAString& aAs)
 {
   uint32_t linkTypes =
     nsStyleLinkElement::ParseLinkTypes(aRel);
 
   // The link relation may apply to a different resource, specified
   // in the anchor parameter. For the link relations supported so far,
   // we simply abort if the link applies to a resource different to the
@@ -735,26 +747,27 @@ nsContentSink::ProcessLink(const nsAStri
 
   // is it a stylesheet link?
   if (!(linkTypes & nsStyleLinkElement::eSTYLESHEET)) {
     return NS_OK;
   }
 
   bool isAlternate = linkTypes & nsStyleLinkElement::eALTERNATE;
   return ProcessStyleLink(nullptr, aHref, isAlternate, aTitle, aType,
-                          aMedia);
+                          aMedia, aReferrerPolicy);
 }
 
 nsresult
 nsContentSink::ProcessStyleLink(nsIContent* aElement,
                                 const nsAString& aHref,
                                 bool aAlternate,
                                 const nsAString& aTitle,
                                 const nsAString& aType,
-                                const nsAString& aMedia)
+                                const nsAString& aMedia,
+                                const nsAString& aReferrerPolicy)
 {
   if (aAlternate && aTitle.IsEmpty()) {
     // alternates must have title return without error, for now
     return NS_OK;
   }
 
   nsAutoString  mimeType;
   nsAutoString  params;
@@ -784,21 +797,26 @@ nsContentSink::ProcessStyleLink(nsIConte
     aElement->GetAttr(kNameSpaceID_None, nsGkAtoms::integrity, integrity);
   }
   if (!integrity.IsEmpty()) {
     MOZ_LOG(dom::SRILogHelper::GetSriLog(), mozilla::LogLevel::Debug,
             ("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;
   rv = mCSSLoader->LoadStyleLink(aElement, url, aTitle, aMedia, aAlternate,
-                                 CORS_NONE, mDocument->GetReferrerPolicy(),
+                                 CORS_NONE, referrerPolicy,
                                  integrity, mRunsToCompletion ? nullptr : this,
                                  &isAlternate);
   NS_ENSURE_SUCCESS(rv, rv);
 
   if (!isAlternate && !mRunsToCompletion) {
     ++mPendingSheetCount;
     mScriptLoader->AddParserBlockingScriptExecutionBlocker();
   }
--- a/dom/base/nsContentSink.h
+++ b/dom/base/nsContentSink.h
@@ -150,24 +150,26 @@ protected:
   nsresult ProcessHTTPHeaders(nsIChannel* aChannel);
   nsresult ProcessHeaderData(nsIAtom* aHeader, const nsAString& aValue,
                              nsIContent* aContent = nullptr);
   nsresult ProcessLinkHeader(const nsAString& aLinkData);
   nsresult ProcessLink(const nsAString& aAnchor,
                        const nsAString& aHref, const nsAString& aRel,
                        const nsAString& aTitle, const nsAString& aType,
                        const nsAString& aMedia, const nsAString& aCrossOrigin,
+                       const nsAString& aReferrerPolicy,
                        const nsAString& aAs);
 
   virtual nsresult ProcessStyleLink(nsIContent* aElement,
                                     const nsAString& aHref,
                                     bool aAlternate,
                                     const nsAString& aTitle,
                                     const nsAString& aType,
-                                    const nsAString& aMedia);
+                                    const nsAString& aMedia,
+                                    const nsAString& aReferrerPolicy);
 
   void PrefetchPreloadHref(const nsAString &aHref, nsINode *aSource,
                            uint32_t aLinkTypes, const nsAString& aAs,
                            const nsAString& aType,
                            const nsAString& aMedia);
 
   // For PrefetchDNS() aHref can either be the usual
   // URI format or of the form "//www.hostname.com" without a scheme.
--- a/dom/base/nsStyleLinkElement.cpp
+++ b/dom/base/nsStyleLinkElement.cpp
@@ -517,16 +517,26 @@ nsStyleLinkElement::DoUpdateStyleSheet(n
   Element* scopeElement = isScoped ? thisContent->GetParentElement() : nullptr;
   if (scopeElement) {
     NS_ASSERTION(isInline, "non-inline style must not have scope element");
     scopeElement->SetIsElementInStyleScopeFlagOnSubtree(true);
   }
 
   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)) {
       return NS_ERROR_OUT_OF_MEMORY;
     }
 
     MOZ_ASSERT(thisContent->NodeInfo()->NameAtom() != nsGkAtoms::link,
                "<link> is not 'inline', and needs different CSP checks");
@@ -534,36 +544,28 @@ nsStyleLinkElement::DoUpdateStyleSheet(n
                                            thisContent->NodePrincipal(),
                                            doc->GetDocumentURI(),
                                            mLineNumber, text, &rv))
       return rv;
 
     // Parse the style sheet.
     rv = doc->CSSLoader()->
       LoadInlineStyle(thisContent, text, mLineNumber, title, media,
-                      scopeElement, aObserver, &doneLoading, &isAlternate);
+                      referrerPolicy, scopeElement, aObserver, &doneLoading,
+                      &isAlternate);
   }
   else {
     nsAutoString integrity;
     thisContent->GetAttr(kNameSpaceID_None, nsGkAtoms::integrity, integrity);
     if (!integrity.IsEmpty()) {
       MOZ_LOG(SRILogHelper::GetSriLog(), mozilla::LogLevel::Debug,
               ("nsStyleLinkElement::DoUpdateStyleSheet, integrity=%s",
                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<nsIURI> clonedURI;
     uri->Clone(getter_AddRefs(clonedURI));
     NS_ENSURE_TRUE(clonedURI, NS_ERROR_OUT_OF_MEMORY);
     rv = doc->CSSLoader()->
       LoadStyleLink(thisContent, clonedURI, title, media, isAlternate,
                     GetCORSMode(), referrerPolicy, integrity,
                     aObserver, &isAlternate);
--- a/dom/xml/nsXMLContentSink.cpp
+++ b/dom/xml/nsXMLContentSink.cpp
@@ -657,17 +657,18 @@ nsXMLContentSink::LoadXSLStyleSheet(nsIU
 }
 
 nsresult
 nsXMLContentSink::ProcessStyleLink(nsIContent* aElement,
                                    const nsAString& aHref,
                                    bool aAlternate,
                                    const nsAString& aTitle,
                                    const nsAString& aType,
-                                   const nsAString& aMedia)
+                                   const nsAString& aMedia,
+                                   const nsAString& aReferrerPolicy)
 {
   nsresult rv = NS_OK;
   mPrettyPrintXML = false;
 
   nsAutoCString cmd;
   if (mParser)
     GetParser()->GetCommand(cmd);
   if (cmd.EqualsASCII(kLoadAsData))
@@ -715,17 +716,17 @@ nsXMLContentSink::ProcessStyleLink(nsICo
       return NS_OK;
     }
 
     return LoadXSLStyleSheet(url);
   }
 
   // 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.
 
   return rv;
 }
 
 void
@@ -1253,17 +1254,19 @@ nsXMLContentSink::HandleProcessingInstru
   nsAutoString href, title, media;
   bool isAlternate = false;
 
   // If there was no href, we can't do anything with this PI
   if (!ParsePIData(data, href, title, media, isAlternate)) {
       return DidProcessATokenImpl();
   }
 
-  rv = ProcessStyleLink(node, href, isAlternate, title, type, media);
+  // <?xml-stylesheet?> 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;
 }
 
 /* static */
 bool
 nsXMLContentSink::ParsePIData(const nsString &aData, nsString &aHref,
                               nsString &aTitle, nsString &aMedia,
                               bool &aIsAlternate)
--- a/dom/xml/nsXMLContentSink.h
+++ b/dom/xml/nsXMLContentSink.h
@@ -145,17 +145,18 @@ protected:
   }
 
   // nsContentSink override
   virtual nsresult ProcessStyleLink(nsIContent* aElement,
                                     const nsAString& aHref,
                                     bool aAlternate,
                                     const nsAString& aTitle,
                                     const nsAString& aType,
-                                    const nsAString& aMedia) override;
+                                    const nsAString& aMedia,
+                                    const nsAString& aReferrerPolicy) override;
 
   nsresult LoadXSLStyleSheet(nsIURI* aUrl);
 
   bool CanStillPrettyPrint();
 
   nsresult MaybePrettyPrint();
 
   bool IsMonolithicContainer(mozilla::dom::NodeInfo* aNodeInfo);
--- a/dom/xml/nsXMLFragmentContentSink.cpp
+++ b/dom/xml/nsXMLFragmentContentSink.cpp
@@ -91,17 +91,19 @@ protected:
   virtual void MaybeStartLayout(bool aIgnorePendingSheets) override;
 
   // nsContentSink overrides
   virtual nsresult ProcessStyleLink(nsIContent* aElement,
                                     const nsAString& aHref,
                                     bool aAlternate,
                                     const nsAString& aTitle,
                                     const nsAString& aType,
-                                    const nsAString& aMedia) override;
+                                    const nsAString& aMedia,
+                                    const nsAString& aReferrerPolicy) override;
+
   nsresult LoadXSLStyleSheet(nsIURI* aUrl);
   void StartLayout();
 
   nsCOMPtr<nsIDocument> mTargetDocument;
   // the fragment
   nsCOMPtr<nsIContent>  mRoot;
   bool                  mParseError;
 };
@@ -326,17 +328,19 @@ nsXMLFragmentContentSink::ReportError(co
 }
 
 nsresult
 nsXMLFragmentContentSink::ProcessStyleLink(nsIContent* aElement,
                                            const nsAString& aHref,
                                            bool aAlternate,
                                            const nsAString& aTitle,
                                            const nsAString& aType,
-                                           const nsAString& aMedia)
+                                           const nsAString& aMedia,
+                                           const nsAString& aReferrerPolicy)
+
 {
   // don't process until moved to document
   return NS_OK;
 }
 
 nsresult
 nsXMLFragmentContentSink::LoadXSLStyleSheet(nsIURI* aUrl)
 {
--- a/layout/style/Loader.cpp
+++ b/layout/style/Loader.cpp
@@ -1981,16 +1981,17 @@ Loader::DoSheetComplete(SheetLoadData* a
 }
 
 nsresult
 Loader::LoadInlineStyle(nsIContent* aElement,
                         const nsAString& aBuffer,
                         uint32_t aLineNumber,
                         const nsAString& aTitle,
                         const nsAString& aMedia,
+                        ReferrerPolicy aReferrerPolicy,
                         Element* aScopeElement,
                         nsICSSLoaderObserver* aObserver,
                         bool* aCompleted,
                         bool* aIsAlternate)
 {
   LOG(("css::Loader::LoadInlineStyle"));
   MOZ_ASSERT(mParsingDatas.IsEmpty(), "We're in the middle of a parse?");
 
@@ -2003,21 +2004,22 @@ Loader::LoadInlineStyle(nsIContent* aEle
 
   NS_ENSURE_TRUE(mDocument, NS_ERROR_NOT_INITIALIZED);
 
   nsCOMPtr<nsIStyleSheetLinkingElement> owningElement(do_QueryInterface(aElement));
   NS_ASSERTION(owningElement, "Element is not a style linking element!");
 
   // 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<StyleSheet> 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);
   NS_ENSURE_SUCCESS(rv, rv);
   NS_ASSERTION(state == eSheetNeedsParser,
                "Inline sheets should not be cached");
 
   LOG(("  Sheet is alternate: %d", *aIsAlternate));
--- a/layout/style/Loader.h
+++ b/layout/style/Loader.h
@@ -224,27 +224,29 @@ public:
    * stylesheet list of this CSSLoader's document.
    *
    * @param aElement the element linking to the stylesheet.  This must not be
    *                 null and must implement nsIStyleSheetLinkingElement.
    * @param aBuffer the stylesheet data
    * @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.
    * @param [out] aIsAlternate whether the stylesheet ended up being an
    *        alternate sheet.
    */
   nsresult LoadInlineStyle(nsIContent* aElement,
                            const nsAString& aBuffer,
                            uint32_t aLineNumber,
                            const nsAString& aTitle,
                            const nsAString& aMedia,
+                           ReferrerPolicy aReferrerPolicy,
                            mozilla::dom::Element* aScopeElement,
                            nsICSSLoaderObserver* aObserver,
                            bool* aCompleted,
                            bool* aIsAlternate);
 
   /**
    * Load a linked (document) stylesheet.  If a successful result is returned,
    * aObserver is guaranteed to be notified asynchronously once the sheet is