Bug 580313 - New resource hints for link. r=smaug

This commit is contained in:
Dragana Damjanovic 2016-02-26 02:41:00 +01:00
parent d99562513c
commit 155721f0d8
18 changed files with 492 additions and 140 deletions

View File

@ -11,6 +11,9 @@
#include "mozilla/dom/Element.h"
#include "nsIURL.h"
#include "nsISizeOf.h"
#include "nsIDocShell.h"
#include "nsIPrefetchService.h"
#include "nsCPrefetchService.h"
#include "nsEscape.h"
#include "nsGkAtoms.h"
@ -72,6 +75,70 @@ Link::CancelDNSPrefetch(nsWrapperCache::FlagsType aDeferredFlag,
}
}
void
Link::TryDNSPrefetchPreconnectOrPrefetch()
{
MOZ_ASSERT(mElement->IsInComposedDoc());
if (!ElementHasHref()) {
return;
}
nsAutoString rel;
if (!mElement->GetAttr(kNameSpaceID_None, nsGkAtoms::rel, rel)) {
return;
}
if (!nsContentUtils::PrefetchEnabled(mElement->OwnerDoc()->GetDocShell())) {
return;
}
uint32_t linkTypes = nsStyleLinkElement::ParseLinkTypes(rel,
mElement->NodePrincipal());
if ((linkTypes & nsStyleLinkElement::ePREFETCH) ||
(linkTypes & nsStyleLinkElement::eNEXT)){
nsCOMPtr<nsIPrefetchService> prefetchService(do_GetService(NS_PREFETCHSERVICE_CONTRACTID));
if (prefetchService) {
nsCOMPtr<nsIURI> uri(GetURI());
if (uri) {
nsCOMPtr<nsIDOMNode> domNode = GetAsDOMNode(mElement);
prefetchService->PrefetchURI(uri,
mElement->OwnerDoc()->GetDocumentURI(),
domNode, true);
return;
}
}
}
if (linkTypes & nsStyleLinkElement::ePRECONNECT) {
nsCOMPtr<nsIURI> uri(GetURI());
if (uri && mElement->OwnerDoc()) {
mElement->OwnerDoc()->MaybePreconnect(uri,
mElement->AttrValueToCORSMode(mElement->GetParsedAttr(nsGkAtoms::crossorigin)));
return;
}
}
if (linkTypes & nsStyleLinkElement::eDNS_PREFETCH) {
if (nsHTMLDNSPrefetch::IsAllowed(mElement->OwnerDoc())) {
nsHTMLDNSPrefetch::PrefetchLow(this);
}
}
}
void
Link::CancelPrefetch()
{
nsCOMPtr<nsIPrefetchService> prefetchService(do_GetService(NS_PREFETCHSERVICE_CONTRACTID));
if (prefetchService) {
nsCOMPtr<nsIURI> uri(GetURI());
if (uri) {
nsCOMPtr<nsIDOMNode> domNode = GetAsDOMNode(mElement);
prefetchService->CancelPrefetchURI(uri, domNode);
}
}
}
void
Link::SetLinkState(nsLinkState aState)
{

View File

@ -111,11 +111,15 @@ public:
bool ElementHasHref() const;
// This is called by HTMLAnchorElement.
void TryDNSPrefetch();
void CancelDNSPrefetch(nsWrapperCache::FlagsType aDeferredFlag,
nsWrapperCache::FlagsType aRequestedFlag);
// This is called by HTMLLinkElement.
void TryDNSPrefetchPreconnectOrPrefetch();
void CancelPrefetch();
protected:
virtual ~Link();

View File

@ -692,7 +692,11 @@ nsContentSink::ProcessLink(const nsSubstring& aAnchor, const nsSubstring& aHref,
if (!LinkContextIsOurDocument(aAnchor)) {
return NS_OK;
}
if (!nsContentUtils::PrefetchEnabled(mDocShell)) {
return NS_OK;
}
bool hasPrefetch = linkTypes & nsStyleLinkElement::ePREFETCH;
// prefetch href if relation is "next" or "prefetch"
if (hasPrefetch || (linkTypes & nsStyleLinkElement::eNEXT)) {
@ -827,35 +831,6 @@ nsContentSink::PrefetchHref(const nsAString &aHref,
nsINode *aSource,
bool aExplicit)
{
//
// SECURITY CHECK: disable prefetching from mailnews!
//
// walk up the docshell tree to see if any containing
// docshell are of type MAIL.
//
if (!mDocShell)
return;
nsCOMPtr<nsIDocShell> docshell = mDocShell;
nsCOMPtr<nsIDocShellTreeItem> parentItem;
do {
uint32_t appType = 0;
nsresult rv = docshell->GetAppType(&appType);
if (NS_FAILED(rv) || appType == nsIDocShell::APP_TYPE_MAIL)
return; // do not prefetch from mailnews
docshell->GetParent(getter_AddRefs(parentItem));
if (parentItem) {
docshell = do_QueryInterface(parentItem);
if (!docshell) {
NS_ERROR("cannot get a docshell from a treeItem!");
return;
}
}
} while (parentItem);
// OK, we passed the security check...
nsCOMPtr<nsIPrefetchService> prefetchService(do_GetService(NS_PREFETCHSERVICE_CONTRACTID));
if (prefetchService) {
// construct URI using document charset

View File

@ -7233,6 +7233,43 @@ nsContentUtils::GenerateUUIDInPlace(nsID& aUUID)
return NS_OK;
}
bool
nsContentUtils::PrefetchEnabled(nsIDocShell* aDocShell)
{
//
// SECURITY CHECK: disable prefetching from mailnews!
//
// walk up the docshell tree to see if any containing
// docshell are of type MAIL.
//
if (!aDocShell) {
return false;
}
nsCOMPtr<nsIDocShell> docshell = aDocShell;
nsCOMPtr<nsIDocShellTreeItem> parentItem;
do {
uint32_t appType = 0;
nsresult rv = docshell->GetAppType(&appType);
if (NS_FAILED(rv) || appType == nsIDocShell::APP_TYPE_MAIL) {
return false; // do not prefetch, preconnect from mailnews
}
docshell->GetParent(getter_AddRefs(parentItem));
if (parentItem) {
docshell = do_QueryInterface(parentItem);
if (!docshell) {
NS_ERROR("cannot get a docshell from a treeItem!");
return false;
}
}
} while (parentItem);
return true;
}
uint64_t
nsContentUtils::GetInnerWindowID(nsIRequest* aRequest)
{

View File

@ -917,6 +917,7 @@ public:
*/
static nsresult GenerateUUIDInPlace(nsID& aUUID);
static bool PrefetchEnabled(nsIDocShell* aDocShell);
/**
* Fill (with the parameters given) the localized string named |aKey| in

View File

@ -0,0 +1,134 @@
//Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
const Cc = Components.classes;
const Ci = Components.interfaces;
var prefetch = Cc["@mozilla.org/prefetch-service;1"].
getService(Ci.nsIPrefetchService);
var ios = Cc["@mozilla.org/network/io-service;1"].
getService(Ci.nsIIOService);
var prefs = Cc["@mozilla.org/preferences-service;1"].
getService(Ci.nsIPrefBranch);
var parser = Cc["@mozilla.org/xmlextras/domparser;1"].
createInstance(Ci.nsIDOMParser);
var doc;
var docbody = '<html xmlns="http://www.w3.org/1999/xhtml"><head></head><body>' +
'<link id="node1"/><link id="node2"/>' +
'</body></html>';
var node1;
var node2;
function run_test() {
prefs.setBoolPref("network.prefetch-next", true);
parser.init();
doc = parser.parseFromString(docbody, "text/html");
node1 = doc.getElementById("node1");
node2 = doc.getElementById("node2");
run_next_test();
}
add_test(function test_cancel1() {
var uri = ios.newURI("http://localhost/1", null, null);
prefetch.prefetchURI(uri, uri, node1, true);
do_check_true(prefetch.hasMoreElements(), 'There is a request in the queue');
// Trying to prefetch again the same uri with the same node will fail.
var didFail = 0;
try {
prefetch.prefetchURI(uri, uri, node1, true);
} catch(e) {
didFail = 1;
}
do_check_true(didFail == 1, 'Prefetching the same request with the same ' +
'node fails.');
do_check_true(prefetch.hasMoreElements(), 'There is still request in ' +
'the queue');
prefetch.cancelPrefetchURI(uri, node1);
do_check_false(prefetch.hasMoreElements(), 'There is no request in the ' +
'queue');
run_next_test();
});
add_test(function test_cancel2() {
// Prefetch a uri with 2 different nodes. There should be 2 request
// in the queue and canceling one will not cancel the other.
var uri = ios.newURI("http://localhost/1", null, null);
prefetch.prefetchURI(uri, uri, node1, true);
prefetch.prefetchURI(uri, uri, node2, true);
do_check_true(prefetch.hasMoreElements(), 'There are requests in the queue');
prefetch.cancelPrefetchURI(uri, node1);
do_check_true(prefetch.hasMoreElements(), 'There is still one more request ' +
'in the queue');
prefetch.cancelPrefetchURI(uri, node2);
do_check_false(prefetch.hasMoreElements(), 'There is no request in the queue');
run_next_test();
});
add_test(function test_cancel3() {
// Request a prefetch of a uri. Trying to cancel a prefetch for the same uri
// with a different node will fail.
var uri = ios.newURI("http://localhost/1", null, null);
prefetch.prefetchURI(uri, uri, node1, true);
do_check_true(prefetch.hasMoreElements(), 'There is a request in the queue');
var didFail = 0;
try {
prefetch.cancelPrefetchURI(uri, node2);
} catch(e) {
didFail = 1;
}
do_check_true(didFail == 1, 'Canceling the request failed');
do_check_true(prefetch.hasMoreElements(), 'There is still a request ' +
'in the queue');
prefetch.cancelPrefetchURI(uri, node1);
do_check_false(prefetch.hasMoreElements(), 'There is no request in the queue');
run_next_test();
});
add_test(function test_cancel4() {
// Request a prefetch of a uri. Trying to cancel a prefetch for a different uri
// with the same node will fail.
var uri1 = ios.newURI("http://localhost/1", null, null);
var uri2 = ios.newURI("http://localhost/2", null, null);
prefetch.prefetchURI(uri1, uri1, node1, true);
do_check_true(prefetch.hasMoreElements(), 'There is a request in the queue');
var didFail = 0;
try {
prefetch.cancelPrefetchURI(uri2, node1);
} catch(e) {
didFail = 1;
}
do_check_true(didFail == 1, 'Canceling the request failed');
do_check_true(prefetch.hasMoreElements(), 'There is still a request ' +
'in the queue');
prefetch.cancelPrefetchURI(uri1, node1);
do_check_false(prefetch.hasMoreElements(), 'There is no request in the queue');
run_next_test();
});

View File

@ -50,3 +50,4 @@ head = head_xml.js
[test_xml_serializer.js]
head = head_xml.js
[test_xmlserializer.js]
[test_cancelPrefetch.js]

View File

@ -180,10 +180,7 @@ HTMLLinkElement::BindToTree(nsIDocument* aDocument, nsIContent* aParent,
}
if (IsInComposedDoc()) {
UpdatePreconnect();
if (HasDNSPrefetchRel()) {
TryDNSPrefetch();
}
TryDNSPrefetchPreconnectOrPrefetch();
}
void (HTMLLinkElement::*update)() = &HTMLLinkElement::UpdateStyleSheetInternal;
@ -217,6 +214,7 @@ HTMLLinkElement::UnbindFromTree(bool aDeep, bool aNullParent)
// mCachedURI based on data that is invalid - due to a call to GetHostname.
CancelDNSPrefetch(HTML_LINK_DNS_PREFETCH_DEFERRED,
HTML_LINK_DNS_PREFETCH_REQUESTED);
CancelPrefetch();
// If this link is ever reinserted into a document, it might
// be under a different xml:base, so forget the cached state now.
@ -344,41 +342,6 @@ 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;
}
nsIDocument *owner = OwnerDoc();
if (owner) {
nsCOMPtr<nsIURI> uri = GetHrefURI();
if (uri) {
owner->MaybePreconnect(uri, GetCORSMode());
}
}
}
bool
HTMLLinkElement::HasDNSPrefetchRel()
{
nsAutoString rel;
if (GetAttr(kNameSpaceID_None, nsGkAtoms::rel, rel)) {
return !!(ParseLinkTypes(rel, NodePrincipal()) &
nsStyleLinkElement::eDNS_PREFETCH);
}
return false;
}
nsresult
HTMLLinkElement::BeforeSetAttr(int32_t aNameSpaceID, nsIAtom* aName,
nsAttrValueOrString* aValue, bool aNotify)
@ -387,6 +350,7 @@ HTMLLinkElement::BeforeSetAttr(int32_t aNameSpaceID, nsIAtom* aName,
(aName == nsGkAtoms::href || aName == nsGkAtoms::rel)) {
CancelDNSPrefetch(HTML_LINK_DNS_PREFETCH_DEFERRED,
HTML_LINK_DNS_PREFETCH_REQUESTED);
CancelPrefetch();
}
return nsGenericHTMLElement::BeforeSetAttr(aNameSpaceID, aName,
@ -427,21 +391,16 @@ HTMLLinkElement::AfterSetAttr(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();
}
}
if ((aName == nsGkAtoms::rel || aName == nsGkAtoms::href) &&
HasDNSPrefetchRel() && IsInComposedDoc()) {
TryDNSPrefetch();
IsInComposedDoc()) {
TryDNSPrefetchPreconnectOrPrefetch();
}
UpdateStyleSheetInternal(nullptr, nullptr,

View File

@ -44,7 +44,6 @@ public:
void LinkRemoved();
void UpdateImport();
void UpdatePreconnect();
// nsIDOMEventTarget
virtual nsresult PreHandleEvent(EventChainPreVisitor& aVisitor) override;
@ -174,8 +173,6 @@ protected:
virtual void GetItemValueText(DOMString& text) override;
virtual void SetItemValueText(const nsAString& text) override;
bool HasDNSPrefetchRel();
RefPtr<nsDOMTokenList > mRelList;
private:
RefPtr<ImportLoader> mImportLoader;

View File

@ -41,3 +41,4 @@ support-files =
[test_shadowroot_youngershadowroot.html]
[test_style_fallback_content.html]
[test_unresolved_pseudo_class.html]
[test_link_prefetch.html]

View File

@ -0,0 +1,110 @@
<!DOCTYPE HTML>
<html>
<!--
https://bugzilla.mozilla.org/show_bug.cgi?id=580313
-->
<head>
<title>Test Prefetch (bug 580313)</title>
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
</head>
<body>
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=580313">Mozilla Bug 580313</a>
<p id="display"></p>
<div id="content" style="display: none">
</div>
<pre id="test">
<script type="application/javascript">
SimpleTest.waitForExplicitFinish();
var prefetch = SpecialPowers.Cc["@mozilla.org/prefetch-service;1"].
getService(SpecialPowers.Ci.nsIPrefetchService);
var ios = SpecialPowers.Cc["@mozilla.org/network/io-service;1"].
getService(SpecialPowers.Ci.nsIIOService);
is(prefetch.hasMoreElements(), false, "No prefetches at the test start.");
var linkElem = document.createElement('link');
linkElem.rel = "prefetch";
// Href is empty.
document.head.appendChild(linkElem);
is(prefetch.hasMoreElements(), false,
"If href is not a valid uri, a prefetch has not been started.");
// Change uri of an existing link. Now it is a valid uri and
// a prefetch should start.
linkElem.href = "https://example.com/1";
is(prefetch.hasMoreElements(), true,
"Setting the href to a valid uri has started a new prefetch.");
// Removing a link, removes its prefetch.
document.head.removeChild(linkElem);
is(prefetch.hasMoreElements(), false,
"Removing the link has canceled the prefetch.");
// Add link again.
document.head.appendChild(linkElem);
is(prefetch.hasMoreElements(), true,
"Adding link again, has started the prefetch again.");
// Changing the href should cancel the current prefetch.
linkElem.href = "https://example.com/2";
is(prefetch.hasMoreElements(), true,
"Changing href, a new prefetch has been started.");
// To check if "https://example.com/1" prefetch has been canceled, we try to
// cancel it using PrefetService. Since the prefetch for
// "https://example.com/1" does not exist, the cancel will throw.
var cancelError = 0;
try {
var uri = ios.newURI("https://example.com/1", null, null);
prefetch.cancelPrefetchURI(uri, linkElem);
} catch(e) {
cancelError = 1;
}
is(cancelError, 1, "This prefetch has aleady been canceled");
// Now cancel the right uri.
cancelError = 0;
try {
var uri = ios.newURI("https://example.com/2", null, null);
prefetch.cancelPrefetchURI(uri, linkElem);
} catch(e) {
cancelError = 1;
}
is(cancelError, 0, "This prefetch has been canceled successfully");
is(prefetch.hasMoreElements(), false, "The prefetch has already been canceled.");
// Removing the link will do nothing regarding prefetch service.
document.head.removeChild(linkElem);
// Adding two links to the same uri and removing one will not remove the other.
document.head.appendChild(linkElem);
is(prefetch.hasMoreElements(), true,
"Added one prefetch for 'https://example.com/2'.");
var linkElem2 = document.createElement('link');
linkElem2.rel = "prefetch";
linkElem2.href = "https://example.com/2";
document.head.appendChild(linkElem2);
is(prefetch.hasMoreElements(), true,
"Added second prefetch for 'https://example.com/2'.");
// Remove first link element. This should not remove the prefetch.
document.head.removeChild(linkElem);
is(prefetch.hasMoreElements(), true,
"The prefetch for 'https://example.com/2' is still present.");
// Remove the second link element. This should remove the prefetch.
document.head.removeChild(linkElem2);
is(prefetch.hasMoreElements(), false,
"There is no prefetch.");
SimpleTest.finish();
</script>
</pre>
</body>
</html>

View File

@ -595,24 +595,6 @@ nsXMLContentSink::CloseElement(nsIContent* aContent)
mScriptLoader->AddExecuteBlocker();
}
}
// Look for <link rel="dns-prefetch" href="hostname">
// and look for <link rel="next" href="hostname"> like in HTML sink
if (nodeInfo->Equals(nsGkAtoms::link, kNameSpaceID_XHTML)) {
nsAutoString relVal;
aContent->GetAttr(kNameSpaceID_None, nsGkAtoms::rel, relVal);
if (!relVal.IsEmpty()) {
uint32_t linkTypes =
nsStyleLinkElement::ParseLinkTypes(relVal, aContent->NodePrincipal());
bool hasPrefetch = linkTypes & nsStyleLinkElement::ePREFETCH;
if (hasPrefetch || (linkTypes & nsStyleLinkElement::eNEXT)) {
nsAutoString hrefVal;
aContent->GetAttr(kNameSpaceID_None, nsGkAtoms::href, hrefVal);
if (!hrefVal.IsEmpty()) {
PrefetchHref(hrefVal, aContent, hasPrefetch);
}
}
}
}
}
return rv;

View File

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

View File

@ -70,11 +70,10 @@ function doTest()
// test the http link response header - the test contains both a
// normal and anonymous preconnect link header
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');
var iframe = document.createElement('iframe');
iframe.src = 'rel_preconnect.sjs?//localhost:' + srv.listener.port;
document.body.appendChild(iframe);
}
</script>

View File

@ -81,24 +81,6 @@ nsHtml5DocumentBuilder::UpdateStyleSheet(nsIContent* aElement)
mScriptLoader->AddExecuteBlocker();
}
if (aElement->IsHTMLElement(nsGkAtoms::link)) {
// look for <link rel="next" href="url">
nsAutoString relVal;
aElement->GetAttr(kNameSpaceID_None, nsGkAtoms::rel, relVal);
if (!relVal.IsEmpty()) {
uint32_t linkTypes =
nsStyleLinkElement::ParseLinkTypes(relVal, aElement->NodePrincipal());
bool hasPrefetch = linkTypes & nsStyleLinkElement::ePREFETCH;
if (hasPrefetch || (linkTypes & nsStyleLinkElement::eNEXT)) {
nsAutoString hrefVal;
aElement->GetAttr(kNameSpaceID_None, nsGkAtoms::href, hrefVal);
if (!hrefVal.IsEmpty()) {
PrefetchHref(hrefVal, aElement, hasPrefetch);
}
}
}
}
// Re-open update
BeginDocUpdate();
}

View File

@ -8,7 +8,7 @@ interface nsIURI;
interface nsIDOMNode;
interface nsISimpleEnumerator;
[scriptable, uuid(2df8b475-f536-4a1a-afea-b39843df8005)]
[scriptable, uuid(422a1807-4e7f-463d-b8d7-ca2ceb9b5d53)]
interface nsIPrefetchService : nsISupports
{
/**
@ -30,5 +30,8 @@ interface nsIPrefetchService : nsISupports
*/
boolean hasMoreElements();
// XXX do we need a way to cancel prefetch requests?
/**
* Cancel prefetch
*/
void cancelPrefetchURI(in nsIURI aURI, in nsIDOMNode aSource);
};

View File

@ -82,16 +82,29 @@ nsPrefetchNode::nsPrefetchNode(nsPrefetchService *aService,
, mChannel(nullptr)
, mBytesRead(0)
{
mSource = do_GetWeakReference(aSource);
nsCOMPtr<nsIWeakReference> source = do_GetWeakReference(aSource);
mSources.AppendElement(source);
}
nsresult
nsPrefetchNode::OpenChannel()
{
nsCOMPtr<nsINode> source = do_QueryReferent(mSource);
if (mSources.IsEmpty()) {
// Don't attempt to prefetch if we don't have a source node
// (which should never happen).
return NS_ERROR_FAILURE;
}
nsCOMPtr<nsINode> source;
while (!mSources.IsEmpty() && !(source = do_QueryReferent(mSources.ElementAt(0)))) {
// If source is null remove it.
// (which should never happen).
mSources.RemoveElementAt(0);
}
if (!source) {
// Don't attempt to prefetch if we don't have a source node
// (which should never happen).
return NS_ERROR_FAILURE;
}
nsCOMPtr<nsILoadGroup> loadGroup = source->OwnerDoc()->GetDocumentLoadGroup();
@ -595,25 +608,46 @@ nsPrefetchService::Prefetch(nsIURI *aURI,
}
//
// cancel if being prefetched
// Check whether it is being prefetched.
//
for (uint32_t i = 0; i < mCurrentNodes.Length(); ++i) {
bool equals;
if (NS_SUCCEEDED(mCurrentNodes[i]->mURI->Equals(aURI, &equals)) && equals) {
LOG(("rejected: URL is already being prefetched\n"));
return NS_ERROR_ABORT;
if (NS_SUCCEEDED(mCurrentNodes[i]->mURI->Equals(aURI, &equals)) &&
equals) {
nsCOMPtr<nsIWeakReference> source = do_GetWeakReference(aSource);
if (mCurrentNodes[i]->mSources.IndexOf(source) ==
mCurrentNodes[i]->mSources.NoIndex) {
LOG(("URL is already being prefetched, add a new reference "
"document\n"));
mCurrentNodes[i]->mSources.AppendElement(source);
return NS_OK;
} else {
LOG(("URL is already being prefetched by this document"));
return NS_ERROR_ABORT;
}
}
}
//
// cancel if already on the prefetch queue
// Check whether it is on the prefetch queue.
//
for (std::deque<RefPtr<nsPrefetchNode>>::iterator node = mQueue.begin();
node != mQueue.end(); node++) {
for (std::deque<RefPtr<nsPrefetchNode>>::iterator nodeIt = mQueue.begin();
nodeIt != mQueue.end(); nodeIt++) {
bool equals;
if (NS_SUCCEEDED(node->get()->mURI->Equals(aURI, &equals)) && equals) {
LOG(("rejected: URL is already on prefetch queue\n"));
return NS_ERROR_ABORT;
RefPtr<nsPrefetchNode> node = nodeIt->get();
if (NS_SUCCEEDED(node->mURI->Equals(aURI, &equals)) && equals) {
nsCOMPtr<nsIWeakReference> source = do_GetWeakReference(aSource);
if (node->mSources.IndexOf(source) ==
node->mSources.NoIndex) {
LOG(("URL is already being prefetched, add a new reference "
"document\n"));
node->mSources.AppendElement(do_GetWeakReference(aSource));
return NS_OK;
} else {
LOG(("URL is already being prefetched by this document"));
return NS_ERROR_ABORT;
}
}
}
@ -632,6 +666,72 @@ nsPrefetchService::Prefetch(nsIURI *aURI,
return NS_OK;
}
NS_IMETHODIMP
nsPrefetchService::CancelPrefetchURI(nsIURI* aURI,
nsIDOMNode* aSource)
{
NS_ENSURE_ARG_POINTER(aURI);
if (LOG_ENABLED()) {
nsAutoCString spec;
aURI->GetSpec(spec);
LOG(("CancelPrefetchURI [%s]\n", spec.get()));
}
//
// look in current prefetches
//
for (uint32_t i = 0; i < mCurrentNodes.Length(); ++i) {
bool equals;
if (NS_SUCCEEDED(mCurrentNodes[i]->mURI->Equals(aURI, &equals)) &&
equals) {
nsCOMPtr<nsIWeakReference> source = do_GetWeakReference(aSource);
if (mCurrentNodes[i]->mSources.IndexOf(source) !=
mCurrentNodes[i]->mSources.NoIndex) {
mCurrentNodes[i]->mSources.RemoveElement(source);
if (mCurrentNodes[i]->mSources.IsEmpty()) {
mCurrentNodes[i]->CancelChannel(NS_BINDING_ABORTED);
mCurrentNodes.RemoveElementAt(i);
}
return NS_OK;
}
return NS_ERROR_FAILURE;
}
}
//
// look into the prefetch queue
//
for (std::deque<RefPtr<nsPrefetchNode>>::iterator nodeIt = mQueue.begin();
nodeIt != mQueue.end(); nodeIt++) {
bool equals;
RefPtr<nsPrefetchNode> node = nodeIt->get();
if (NS_SUCCEEDED(node->mURI->Equals(aURI, &equals)) && equals) {
nsCOMPtr<nsIWeakReference> source = do_GetWeakReference(aSource);
if (node->mSources.IndexOf(source) !=
node->mSources.NoIndex) {
#ifdef DEBUG
int32_t inx = node->mSources.IndexOf(source);
nsCOMPtr<nsIDOMNode> domNode =
do_QueryReferent(node->mSources.ElementAt(inx));
MOZ_ASSERT(domNode);
#endif
node->mSources.RemoveElement(source);
if (node->mSources.IsEmpty()) {
mQueue.erase(nodeIt);
}
return NS_OK;
}
return NS_ERROR_FAILURE;
}
}
// not found!
return NS_ERROR_FAILURE;
}
NS_IMETHODIMP
nsPrefetchService::PrefetchURI(nsIURI *aURI,
nsIURI *aReferrerURI,

View File

@ -97,9 +97,9 @@ public:
nsresult OpenChannel();
nsresult CancelChannel(nsresult error);
nsCOMPtr<nsIURI> mURI;
nsCOMPtr<nsIURI> mReferrerURI;
nsCOMPtr<nsIWeakReference> mSource;
nsCOMPtr<nsIURI> mURI;
nsCOMPtr<nsIURI> mReferrerURI;
nsTArray<nsCOMPtr<nsIWeakReference>> mSources;
private:
~nsPrefetchNode() {}