bug 1174152 link rel=preconnect crossorigin=anonymous r=hsivonen

This commit is contained in:
Patrick McManus 2015-05-06 10:09:27 -04:00
parent 0e1bca815b
commit 78c4fb4975
11 changed files with 87 additions and 33 deletions

View File

@ -446,6 +446,9 @@ nsContentSink::ProcessLinkHeader(const nsAString& aLinkData)
nsAutoString type;
nsAutoString media;
nsAutoString anchor;
nsAutoString crossOrigin;
crossOrigin.SetIsVoid(true);
// copy to work buffer
nsAutoString stringList(aLinkData);
@ -620,6 +623,12 @@ nsContentSink::ProcessLinkHeader(const nsAString& aLinkData)
anchor = value;
anchor.StripWhitespace();
}
} else if (attr.LowerCaseEqualsLiteral("crossorigin")) {
if (crossOrigin.IsVoid()) {
crossOrigin.SetIsVoid(false);
crossOrigin = value;
crossOrigin.StripWhitespace();
}
}
}
}
@ -633,7 +642,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);
type, media, crossOrigin);
}
href.Truncate();
@ -642,6 +651,7 @@ nsContentSink::ProcessLinkHeader(const nsAString& aLinkData)
type.Truncate();
media.Truncate();
anchor.Truncate();
crossOrigin.SetIsVoid(true);
seenParameters = false;
}
@ -654,7 +664,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);
type, media, crossOrigin);
}
return rv;
@ -664,7 +674,8 @@ nsContentSink::ProcessLinkHeader(const nsAString& aLinkData)
nsresult
nsContentSink::ProcessLink(const nsSubstring& aAnchor, const nsSubstring& aHref,
const nsSubstring& aRel, const nsSubstring& aTitle,
const nsSubstring& aType, const nsSubstring& aMedia)
const nsSubstring& aType, const nsSubstring& aMedia,
const nsSubstring& aCrossOrigin)
{
uint32_t linkTypes =
nsStyleLinkElement::ParseLinkTypes(aRel, mDocument->NodePrincipal());
@ -688,7 +699,7 @@ nsContentSink::ProcessLink(const nsSubstring& aAnchor, const nsSubstring& aHref,
}
if (!aHref.IsEmpty() && (linkTypes & nsStyleLinkElement::ePRECONNECT)) {
Preconnect(aHref);
Preconnect(aHref, aCrossOrigin);
}
// is it a stylesheet link?
@ -875,7 +886,7 @@ nsContentSink::PrefetchDNS(const nsAString &aHref)
}
void
nsContentSink::Preconnect(const nsAString &aHref)
nsContentSink::Preconnect(const nsAString& aHref, const nsAString& aCrossOrigin)
{
// construct URI using document charset
const nsACString& charset = mDocument->GetDocumentCharacterSet();
@ -885,7 +896,7 @@ nsContentSink::Preconnect(const nsAString &aHref)
mDocument->GetDocBaseURI());
if (uri && mDocument) {
mDocument->MaybePreconnect(uri);
mDocument->MaybePreconnect(uri, dom::Element::StringToCORSMode(aCrossOrigin));
}
}

View File

@ -151,7 +151,7 @@ protected:
nsresult ProcessLink(const nsSubstring& aAnchor,
const nsSubstring& aHref, const nsSubstring& aRel,
const nsSubstring& aTitle, const nsSubstring& aType,
const nsSubstring& aMedia);
const nsSubstring& aMedia, const nsSubstring& aCrossOrigin);
virtual nsresult ProcessStyleLink(nsIContent* aElement,
const nsSubstring& aHref,
@ -225,7 +225,7 @@ public:
// For Preconnect() aHref can either be the usual
// URI format or of the form "//www.hostname.com" without a scheme.
void Preconnect(const nsAString &aHref);
void Preconnect(const nsAString& aHref, const nsAString& aCrossOrigin);
protected:
// Tries to scroll to the URI's named anchor. Once we've successfully

View File

@ -9765,8 +9765,26 @@ nsDocument::MaybePreLoadImage(nsIURI* uri, const nsAString &aCrossOriginAttr,
}
void
nsDocument::MaybePreconnect(nsIURI* uri)
nsDocument::MaybePreconnect(nsIURI* aOrigURI, mozilla::CORSMode aCORSMode)
{
nsCOMPtr<nsIURI> uri;
if (NS_FAILED(aOrigURI->Clone(getter_AddRefs(uri)))) {
return;
}
// The URI created here is used in 2 contexts. One is nsISpeculativeConnect
// which ignores the path and uses only the origin. The other is for the
// document mPreloadedPreconnects de-duplication hash. Anonymous vs
// non-Anonymous preconnects create different connections on the wire and
// therefore should not be considred duplicates of each other and we
// normalize the path before putting it in the hash to accomplish that.
if (aCORSMode == CORS_ANONYMOUS) {
uri->SetPath(NS_LITERAL_CSTRING("/anonymous"));
} else {
uri->SetPath(NS_LITERAL_CSTRING("/"));
}
if (mPreloadedPreconnects.Contains(uri)) {
return;
}
@ -9778,7 +9796,11 @@ nsDocument::MaybePreconnect(nsIURI* uri)
return;
}
speculator->SpeculativeConnect(uri, nullptr);
if (aCORSMode == CORS_ANONYMOUS) {
speculator->SpeculativeAnonymousConnect(uri, nullptr);
} else {
speculator->SpeculativeConnect(uri, nullptr);
}
}
void

View File

@ -1115,7 +1115,8 @@ public:
ReferrerPolicy aReferrerPolicy) override;
virtual void ForgetImagePreload(nsIURI* aURI) override;
virtual void MaybePreconnect(nsIURI* uri) override;
virtual void MaybePreconnect(nsIURI* uri,
mozilla::CORSMode aCORSMode) override;
virtual void PreloadStyle(nsIURI* uri, const nsAString& charset,
const nsAString& aCrossOriginAttr,

View File

@ -30,6 +30,7 @@
#include "nsExpirationTracker.h"
#include "nsClassHashtable.h"
#include "prclist.h"
#include "mozilla/CORSMode.h"
#include <bitset> // for member
class imgIRequest;
@ -2049,7 +2050,8 @@ public:
/**
* Called by Parser for link rel=preconnect
*/
virtual void MaybePreconnect(nsIURI* uri) = 0;
virtual void MaybePreconnect(nsIURI* uri,
mozilla::CORSMode aCORSMode) = 0;
enum DocumentTheme {
Doc_Theme_Uninitialized, // not determined yet

View File

@ -316,7 +316,7 @@ HTMLLinkElement::UpdatePreconnect()
if (owner) {
nsCOMPtr<nsIURI> uri = GetHrefURI();
if (uri) {
owner->MaybePreconnect(uri);
owner->MaybePreconnect(uri, GetCORSMode());
}
}
}

View File

@ -6,7 +6,10 @@ function handleRequest(request, response)
response.setHeader("Cache-Control", "no-cache", false);
response.setHeader("Link", "<" +
request.getHeader('X-Link') +
">; rel=preconnect");
">; rel=preconnect" + ", " +
"<" +
request.getHeader('X-Link') +
">; rel=preconnect; crossOrigin=anonymous");
response.write("check that header");
}

View File

@ -22,6 +22,8 @@ function TestServer1(nextTest) {
}
TestServer1.prototype = {
remainder : 2,
QueryInterface: function(iid) {
iid = SpecialPowers.wrap(iid);
if (iid.equals(Ci.nsIServerSocketListener) ||
@ -32,12 +34,12 @@ TestServer1.prototype = {
onSocketAccepted: function(socket, trans) {
try { socket.close(); } catch(e) {}
try { trans.close(); } catch(e) {}
ok(true, "received connect");
srv1.listener.close();
if(srv1.nextTest != null) {
setTimeout(srv1.nextTest, 0);
this.remainder--;
ok(true, "received connect remainder = " + this.remainder);
if (!this.remainder) {
srv1.listener.close();
setTimeout(srv1.nextTest, 0);
}
srv1.nextTest = null;
},
onStopListening: function(socket) {}
};
@ -51,6 +53,8 @@ function TestServer2(nextTest) {
}
TestServer2.prototype = {
remainder : 2,
QueryInterface: function(iid) {
iid = SpecialPowers.wrap(iid);
if (iid.equals(Ci.nsIServerSocketListener) ||
@ -61,12 +65,12 @@ TestServer2.prototype = {
onSocketAccepted: function(socket, trans) {
try { socket.close(); } catch(e) {}
try { trans.close(); } catch(e) {}
ok(true, "received connect");
srv2.listener.close();
if(srv2.nextTest != null) {
setTimeout(srv2.nextTest, 0);
this.remainder--;
ok(true, "received connect srv2 remainder = " + this.remainder);
if (!this.remainder) {
srv2.listener.close();
setTimeout(srv2.nextTest, 0);
}
srv2.nextTest = null;
},
onStopListening: function(socket) {}
};
@ -75,18 +79,25 @@ var originalLimit = SpecialPowers.getIntPref("network.http.speculative-parallel-
function testElement()
{
// test the link rel=preconnect element in the head
// test the link rel=preconnect element in the head for both normal
// and crossOrigin=anonymous
srv1 = new TestServer1(testHeader);
SpecialPowers.setIntPref("network.http.speculative-parallel-limit", 1);
SpecialPowers.setIntPref("network.http.speculative-parallel-limit", 2);
var link = document.createElement("link");
link.rel = "preconnect";
link.href = "//localhost:" + srv1.listener.port;
document.head.appendChild(link);
link = document.createElement("link");
link.rel = "preconnect";
link.href = "//localhost:" + srv1.listener.port;
link.crossOrigin = "anonymous";
document.head.appendChild(link);
}
function testHeader()
{
// test the http link response header
// test the http link response header - the test contains both a
// normal and anonymous preconnect link header
srv2 = new TestServer2(testDone);
var xhr = new XMLHttpRequest();
xhr.open("GET", 'rel_preconnect.sjs', false);

View File

@ -68,7 +68,7 @@ nsHtml5SpeculativeLoad::Perform(nsHtml5TreeOpExecutor* aExecutor)
}
break;
case eSpeculativeLoadPreconnect:
aExecutor->Preconnect(mUrl);
aExecutor->Preconnect(mUrl, mCrossOrigin);
break;
default:
NS_NOTREACHED("Bogus speculative load.");

View File

@ -164,12 +164,14 @@ class nsHtml5SpeculativeLoad {
mTypeOrCharsetSource.Assign((char16_t)aCharsetSource);
}
inline void InitPreconnect(const nsAString& aUrl)
inline void InitPreconnect(const nsAString& aUrl,
const nsAString& aCrossOrigin)
{
NS_PRECONDITION(mOpCode == eSpeculativeLoadUninitialized,
"Trying to reinitialize a speculative load!");
mOpCode = eSpeculativeLoadPreconnect;
mUrl.Assign(aUrl);
mCrossOrigin.Assign(aCrossOrigin);
}
void Perform(nsHtml5TreeOpExecutor* aExecutor);
@ -193,9 +195,9 @@ class nsHtml5SpeculativeLoad {
*/
nsString mTypeOrCharsetSource;
/**
* If mOpCode is eSpeculativeLoadImage or eSpeculativeLoadScript[FromHead],
* this is the value of the "crossorigin" attribute. If the
* attribute is not set, this will be a void string.
* 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.
*/
nsString mCrossOrigin;
/**

View File

@ -191,8 +191,10 @@ nsHtml5TreeBuilder::createElement(int32_t aNamespace, nsIAtom* aName,
} else if (rel->LowerCaseEqualsASCII("preconnect")) {
nsString* url = aAttributes->getValue(nsHtml5AttributeName::ATTR_HREF);
if (url) {
nsString* crossOrigin =
aAttributes->getValue(nsHtml5AttributeName::ATTR_CROSSORIGIN);
mSpeculativeLoadQueue.AppendElement()->
InitPreconnect(*url);
InitPreconnect(*url, (crossOrigin) ? *crossOrigin : NullString());
}
}
}