Bug 507976: Check the anchor parameter while processing link header fields. r=bz
authorJulian Reschke <julian.reschke@gmx.de>
Tue, 07 Jun 2011 13:19:06 -0700
changeset 70756 c1e3ab99d34d642d03474a8f3596bf895c597aab
parent 70755 235fa8782613e929d9db65cf0c46856bbd137830
child 70757 c3be577cd86a73e4abe755997c62ce960bf83058
push id8
push userjdrew@mozilla.com
push dateThu, 09 Jun 2011 04:28:13 +0000
treeherdermozilla-inbound@9cab4f87ebd7 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbz
bugs507976
milestone7.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 507976: Check the anchor parameter while processing link header fields. r=bz
content/base/src/nsContentSink.cpp
content/base/src/nsContentSink.h
--- a/content/base/src/nsContentSink.cpp
+++ b/content/base/src/nsContentSink.cpp
@@ -554,28 +554,74 @@ nsContentSink::DoProcessLinkHeader()
 }
 
 static const PRUnichar kSemiCh = PRUnichar(';');
 static const PRUnichar kCommaCh = PRUnichar(',');
 static const PRUnichar kEqualsCh = PRUnichar('=');
 static const PRUnichar kLessThanCh = PRUnichar('<');
 static const PRUnichar kGreaterThanCh = PRUnichar('>');
 
+
+// check whether the Link header field applies to the context resource
+// see <http://tools.ietf.org/html/rfc5988#section-5.2>
+
+PRBool
+nsContentSink::LinkContextIsOurDocument(const nsSubstring& aAnchor)
+{
+  if (aAnchor.IsEmpty()) {
+    // anchor parameter not present or empty -> same document reference
+    return PR_TRUE;
+  }
+
+  nsIURI* docUri = mDocument->GetDocumentURI();
+
+  // the document URI might contain a fragment identifier ("#...')
+  // we want to ignore that because it's invisible to the server
+  // and just affects the local interpretation in the recipient
+  nsCOMPtr<nsIURI> contextUri;
+  nsresult rv = docUri->CloneIgnoringRef(getter_AddRefs(contextUri));
+  
+  if (NS_FAILED(rv)) {
+    // copying failed
+    return PR_FALSE;
+  }
+  
+  // resolve anchor against context    
+  nsCOMPtr<nsIURI> resolvedUri;
+  rv = NS_NewURI(getter_AddRefs(resolvedUri), aAnchor,
+      nsnull, contextUri);
+  
+  if (NS_FAILED(rv)) {
+    // resolving failed
+    return PR_FALSE;
+  }
+
+  PRBool same;
+  rv = contextUri->Equals(resolvedUri, &same); 
+  if (NS_FAILED(rv)) {
+    // comparison failed
+    return PR_FALSE;
+  }
+
+  return same;
+}
+
 nsresult
 nsContentSink::ProcessLinkHeader(nsIContent* aElement,
                                  const nsAString& aLinkData)
 {
   nsresult rv = NS_OK;
 
   // parse link content and call process style link
   nsAutoString href;
   nsAutoString rel;
   nsAutoString title;
   nsAutoString type;
   nsAutoString media;
+  nsAutoString anchor;
 
   // copy to work buffer
   nsAutoString stringList(aLinkData);
 
   // put an extra null at the end
   stringList.Append(kNullCh);
 
   PRUnichar* start = stringList.BeginWriting();
@@ -694,58 +740,72 @@ nsContentSink::ProcessLinkHeader(nsICont
             }
           } else if (attr.LowerCaseEqualsLiteral("media")) {
             if (media.IsEmpty()) {
               media = value;
 
               // HTML4.0 spec is inconsistent, make it case INSENSITIVE
               ToLowerCase(media);
             }
+          } else if (attr.LowerCaseEqualsLiteral("anchor")) {
+            if (anchor.IsEmpty()) {
+              anchor = value;
+              anchor.StripWhitespace();
+            }
           }
         }
       }
     }
 
     if (endCh == kCommaCh) {
       // 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(aElement, href, rel, title, type, media);
+        rv = ProcessLink(aElement, anchor, href, rel, title, type, media);
       }
 
       href.Truncate();
       rel.Truncate();
       title.Truncate();
       type.Truncate();
       media.Truncate();
+      anchor.Truncate();
     }
 
     start = ++end;
   }
-
+                
   href.Trim(" \t\n\r\f"); // trim HTML5 whitespace
   if (!href.IsEmpty() && !rel.IsEmpty()) {
-    rv = ProcessLink(aElement, href, rel, title, type, media);
+    rv = ProcessLink(aElement, anchor, href, rel, title, type, media);
   }
 
   return rv;
 }
 
 
 nsresult
 nsContentSink::ProcessLink(nsIContent* aElement,
-                           const nsSubstring& aHref, const nsSubstring& aRel,
-                           const nsSubstring& aTitle, const nsSubstring& aType,
-                           const nsSubstring& aMedia)
+                           const nsSubstring& aAnchor, const nsSubstring& aHref,
+                           const nsSubstring& aRel, const nsSubstring& aTitle,
+                           const nsSubstring& aType, const nsSubstring& aMedia)
 {
   // XXX seems overkill to generate this string array
   nsTArray<nsString> linkTypes;
   nsStyleLinkElement::ParseLinkTypes(aRel, linkTypes);
 
+  // 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
+  // one we've loaded
+  if (!LinkContextIsOurDocument(aAnchor)) {
+    return NS_OK;
+  }
+  
   PRBool hasPrefetch = linkTypes.Contains(NS_LITERAL_STRING("prefetch"));
   // prefetch href if relation is "next" or "prefetch"
   if (hasPrefetch || linkTypes.Contains(NS_LITERAL_STRING("next"))) {
     PrefetchHref(aHref, aElement, hasPrefetch);
   }
 
   if ((!aHref.IsEmpty()) && linkTypes.Contains(NS_LITERAL_STRING("dns-prefetch"))) {
     PrefetchDNS(aHref);
--- a/content/base/src/nsContentSink.h
+++ b/content/base/src/nsContentSink.h
@@ -146,16 +146,17 @@ class nsContentSink : public nsICSSLoade
 
   // nsIDocumentObserver
   NS_DECL_NSIDOCUMENTOBSERVER_BEGINUPDATE
   NS_DECL_NSIDOCUMENTOBSERVER_ENDUPDATE
 
   virtual void UpdateChildCounts() = 0;
 
   PRBool IsTimeToNotify();
+  PRBool LinkContextIsOurDocument(const nsSubstring& aAnchor);
 
   static void InitializeStatics();
 
 protected:
   nsContentSink();
   virtual ~nsContentSink();
 
   enum CacheSelectionAction {
@@ -183,19 +184,20 @@ protected:
   nsresult Init(nsIDocument* aDoc, nsIURI* aURI,
                 nsISupports* aContainer, nsIChannel* aChannel);
 
   nsresult ProcessHTTPHeaders(nsIChannel* aChannel);
   nsresult ProcessHeaderData(nsIAtom* aHeader, const nsAString& aValue,
                              nsIContent* aContent = nsnull);
   nsresult ProcessLinkHeader(nsIContent* aElement,
                              const nsAString& aLinkData);
-  nsresult ProcessLink(nsIContent* aElement, const nsSubstring& aHref,
-                       const nsSubstring& aRel, const nsSubstring& aTitle,
-                       const nsSubstring& aType, const nsSubstring& aMedia);
+  nsresult ProcessLink(nsIContent* aElement, const nsSubstring& aAnchor,
+                       const nsSubstring& aHref, const nsSubstring& aRel,
+                       const nsSubstring& aTitle, const nsSubstring& aType,
+                       const nsSubstring& aMedia);
 
   virtual nsresult ProcessStyleLink(nsIContent* aElement,
                                     const nsSubstring& aHref,
                                     PRBool aAlternate,
                                     const nsSubstring& aTitle,
                                     const nsSubstring& aType,
                                     const nsSubstring& aMedia);