bug 1135160 - implement link rel=preconnect r=smaug

This commit is contained in:
Patrick McManus 2015-02-20 17:01:36 -05:00
parent e20913b8b2
commit 43ec2f268a
13 changed files with 186 additions and 3 deletions

View File

@ -33,6 +33,7 @@
#include "nsIApplicationCacheContainer.h"
#include "nsIApplicationCacheChannel.h"
#include "nsIScriptSecurityManager.h"
#include "nsISpeculativeConnect.h"
#include "nsICookieService.h"
#include "nsContentUtils.h"
#include "nsNodeInfoManager.h"
@ -686,6 +687,10 @@ nsContentSink::ProcessLink(const nsSubstring& aAnchor, const nsSubstring& aHref,
PrefetchDNS(aHref);
}
if (!aHref.IsEmpty() && (linkTypes & nsStyleLinkElement::ePRECONNECT)) {
Preconnect(aHref);
}
// is it a stylesheet link?
if (!(linkTypes & nsStyleLinkElement::eSTYLESHEET)) {
return NS_OK;
@ -857,6 +862,26 @@ nsContentSink::PrefetchDNS(const nsAString &aHref)
}
}
void
nsContentSink::Preconnect(const nsAString &aHref)
{
nsCOMPtr<nsISpeculativeConnect>
speculator(do_QueryInterface(nsContentUtils::GetIOService()));
if (!speculator) {
return;
}
// construct URI using document charset
const nsACString& charset = mDocument->GetDocumentCharacterSet();
nsCOMPtr<nsIURI> uri;
NS_NewURI(getter_AddRefs(uri), aHref,
charset.IsEmpty() ? nullptr : PromiseFlatCString(charset).get(),
mDocument->GetDocBaseURI());
if (uri) {
speculator->SpeculativeConnect(uri, nullptr);
}
}
nsresult
nsContentSink::SelectDocAppCache(nsIApplicationCache *aLoadApplicationCache,
nsIURI *aManifestURI,

View File

@ -164,9 +164,10 @@ protected:
void PrefetchHref(const nsAString &aHref, nsINode *aSource,
bool aExplicit);
// aHref can either be the usual URI format or of the form "//www.hostname.com"
// without a scheme.
// For both PrefetchDNS() and Preconnect() aHref can either be the usual
// URI format or of the form "//www.hostname.com" without a scheme.
void PrefetchDNS(const nsAString &aHref);
void Preconnect(const nsAString &aHref);
// Gets the cache key (used to identify items in a cache) of the channel.
nsresult GetChannelCacheKey(nsIChannel* aChannel, nsACString& aCacheKey);

View File

@ -150,6 +150,8 @@ static uint32_t ToLinkMask(const nsAString& aLink, nsIPrincipal* aPrincipal)
else if (aLink.EqualsLiteral("import") &&
nsStyleLinkElement::IsImportEnabled())
return nsStyleLinkElement::eHTMLIMPORT;
else if (aLink.EqualsLiteral("preconnect"))
return nsStyleLinkElement::ePRECONNECT;
else
return 0;
}

View File

@ -59,7 +59,8 @@ public:
eSTYLESHEET = 0x00000004,
eNEXT = 0x00000008,
eALTERNATE = 0x00000010,
eHTMLIMPORT = 0x00000020
eHTMLIMPORT = 0x00000020,
ePRECONNECT = 0x00000040
};
// The return value is a bitwise or of 0 or more RelValues.

View File

@ -21,6 +21,7 @@
#include "nsIDOMEvent.h"
#include "nsIDOMStyleSheet.h"
#include "nsINode.h"
#include "nsISpeculativeConnect.h"
#include "nsIStyleSheet.h"
#include "nsIStyleSheetLinkingElement.h"
#include "nsIURL.h"
@ -147,6 +148,10 @@ HTMLLinkElement::BindToTree(nsIDocument* aDocument, nsIContent* aParent,
aDocument->RegisterPendingLinkUpdate(this);
}
if (IsInComposedDoc()) {
UpdatePreconnect();
}
void (HTMLLinkElement::*update)() = &HTMLLinkElement::UpdateStyleSheetInternal;
nsContentUtils::AddScriptRunner(NS_NewRunnableMethod(this, update));
@ -292,6 +297,30 @@ HTMLLinkElement::UpdateImport()
}
}
void
HTMLLinkElement::UpdatePreconnect()
{
// rel type should be preconnect
nsAutoString rel;
if (!GetAttr(kNameSpaceID_None, nsGkAtoms::rel, rel)) {
return;
}
uint32_t linkTypes = nsStyleLinkElement::ParseLinkTypes(rel, NodePrincipal());
if (!(linkTypes & ePRECONNECT)) {
return;
}
nsCOMPtr<nsISpeculativeConnect>
speculator(do_QueryInterface(nsContentUtils::GetIOService()));
if (speculator) {
nsCOMPtr<nsIURI> uri = GetHrefURI();
if (uri) {
speculator->SpeculativeConnect(uri, nullptr);
}
}
}
nsresult
HTMLLinkElement::SetAttr(int32_t aNameSpaceID, nsIAtom* aName,
nsIAtom* aPrefix, const nsAString& aValue,
@ -326,11 +355,16 @@ HTMLLinkElement::SetAttr(int32_t aNameSpaceID, nsIAtom* aName,
dropSheet = !(linkTypes & nsStyleLinkElement::eSTYLESHEET);
} else if (linkTypes & eHTMLIMPORT) {
UpdateImport();
} else if ((linkTypes & ePRECONNECT) && IsInComposedDoc()) {
UpdatePreconnect();
}
}
if (aName == nsGkAtoms::href) {
UpdateImport();
if (IsInComposedDoc()) {
UpdatePreconnect();
}
}
UpdateStyleSheetInternal(nullptr, nullptr,

View File

@ -43,6 +43,7 @@ public:
void LinkRemoved();
void UpdateImport();
void UpdatePreconnect();
// nsIDOMEventTarget
virtual nsresult PreHandleEvent(EventChainPreVisitor& aVisitor) MOZ_OVERRIDE;

View File

@ -40,6 +40,7 @@
#include "nsPrincipal.h"
#include "nsIOService.h"
#include "mozilla/net/OfflineObserver.h"
#include "nsISpeculativeConnect.h"
using mozilla::dom::ContentParent;
using mozilla::dom::TabContext;
@ -668,6 +669,17 @@ NeckoParent::DeallocPRemoteOpenFileParent(PRemoteOpenFileParent* actor)
return true;
}
bool
NeckoParent::RecvSpeculativeConnect(const URIParams &aURI)
{
nsCOMPtr<nsISpeculativeConnect> speculator(gIOService);
nsCOMPtr<nsIURI> uri = DeserializeURI(aURI);
if (uri && speculator) {
speculator->SpeculativeConnect(uri, nullptr);
}
return true;
}
bool
NeckoParent::RecvHTMLDNSPrefetch(const nsString& hostname,
const uint16_t& flags)

View File

@ -160,6 +160,7 @@ protected:
const uint32_t& flags,
const nsCString& aNetworkInterface) MOZ_OVERRIDE;
virtual bool DeallocPDNSRequestParent(PDNSRequestParent*) MOZ_OVERRIDE;
virtual bool RecvSpeculativeConnect(const URIParams& aURI) MOZ_OVERRIDE;
virtual bool RecvHTMLDNSPrefetch(const nsString& hostname,
const uint16_t& flags) MOZ_OVERRIDE;
virtual bool RecvCancelHTMLDNSPrefetch(const nsString& hostname,

View File

@ -73,6 +73,7 @@ parent:
URIParams fileuri,
OptionalURIParams appuri);
SpeculativeConnect(URIParams uri);
HTMLDNSPrefetch(nsString hostname, uint16_t flags);
CancelHTMLDNSPrefetch(nsString hostname, uint16_t flags, nsresult reason);
PRtspController();

View File

@ -50,6 +50,7 @@
#include "nsINetworkLinkService.h"
#include "mozilla/net/NeckoChild.h"
#include "mozilla/ipc/URIUtils.h"
#include "mozilla/Telemetry.h"
#if defined(XP_UNIX)
@ -1989,6 +1990,13 @@ NS_IMETHODIMP
nsHttpHandler::SpeculativeConnect(nsIURI *aURI,
nsIInterfaceRequestor *aCallbacks)
{
if (IsNeckoChild()) {
ipc::URIParams params;
SerializeURI(aURI, params);
gNeckoChild->SendSpeculativeConnect(params);
return NS_OK;
}
if (!mHandlerActive)
return NS_OK;

View File

@ -4,11 +4,13 @@ skip-if = buildapp == 'b2g' || e10s || toolkit == 'android' # Android: Bug 11111
support-files =
method.sjs
partial_content.sjs
rel_preconnect.sjs
user_agent.sjs
user_agent_update.sjs
[test_arraybufferinputstream.html]
[test_partially_cached_content.html]
[test_rel_preconnect.html]
[test_uri_scheme.html]
[test_user_agent_overrides.html]
[test_user_agent_updates.html]

View File

@ -0,0 +1,12 @@
// Generate response header "Link: <HREF>; rel=preconnect"
// HREF is provided by the request header X-Link
function handleRequest(request, response)
{
response.setHeader("Cache-Control", "no-cache", false);
response.setHeader("Link", "<" +
request.getHeader('X-Link') +
">; rel=preconnect");
response.write("check that header");
}

View File

@ -0,0 +1,83 @@
<!DOCTYPE HTML>
<html>
<!--
-->
<head>
<title>Test for link rel=preconnect</title>
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
<script type="text/javascript">
SimpleTest.waitForExplicitFinish();
const Cc = SpecialPowers.Cc, Ci = SpecialPowers.Ci, Cr = SpecialPowers.Cr;
var srv;
function TestServer(nextTest) {
this.listener= Cc["@mozilla.org/network/server-socket;1"]
.createInstance(Ci.nsIServerSocket);
this.listener.init(-1, true, -1);
this.listener.asyncListen(SpecialPowers.wrapCallbackObject(this));
this.nextTest = nextTest;
}
TestServer.prototype = {
QueryInterface: function(iid) {
iid = SpecialPowers.wrap(iid);
if (iid.equals(Ci.nsIServerSocketListener) ||
iid.equals(Ci.nsISupports))
return this;
throw Cr.NS_ERROR_NO_INTERFACE;
},
onSocketAccepted: function(socket, trans) {
try { socket.close(); } catch(e) {}
try { trans.close(); } catch(e) {}
ok(true, "received connect");
setTimeout(srv.nextTest, 0);
},
onStopListening: function(socket) {}
};
var originalLimit = SpecialPowers.getIntPref("network.http.speculative-parallel-limit");
function testElement()
{
// test the link rel=preconnect element in the head
srv = new TestServer(testHeader);
SpecialPowers.setIntPref("network.http.speculative-parallel-limit", 1);
var link = document.createElement("link");
link.rel = "preconnect";
link.href = "//localhost:" + srv.listener.port;
document.head.appendChild(link);
}
function testHeader()
{
// test the http link response header
srv.listener.close();
srv = new TestServer(testDone);
var xhr = new XMLHttpRequest();
xhr.open("GET", 'rel_preconnect.sjs', false);
xhr.setRequestHeader("X-Link", "//localhost:" + srv.listener.port);
xhr.send();
is(xhr.status, 200, 'xhr cool');
}
function testDone()
{
SpecialPowers.setIntPref("network.http.speculative-parallel-limit",
originalLimit);
srv.listener.close();
SimpleTest.finish();
}
</script>
</head>
<body onload="testElement();">
<p id="display"></p>
<div id="content" style="display: none"></div>
<pre id="test">
</pre>
</body>
</html>