diff --git a/content/base/src/nsContentSink.cpp b/content/base/src/nsContentSink.cpp index e4009f12f13..b70259c93ff 100644 --- a/content/base/src/nsContentSink.cpp +++ b/content/base/src/nsContentSink.cpp @@ -869,10 +869,9 @@ nsContentSink::PrefetchDNS(const nsAString &aHref) } else nsGenericHTMLElement::GetHostnameFromHrefString(aHref, hostname); - - nsRefPtr prefetch = new nsHTMLDNSPrefetch(hostname, mDocument); - if (prefetch) { - prefetch->PrefetchLow(); + + if (nsHTMLDNSPrefetch::IsAllowed(mDocument)) { + nsHTMLDNSPrefetch::PrefetchLow(hostname); } } diff --git a/content/html/content/src/nsHTMLAnchorElement.cpp b/content/html/content/src/nsHTMLAnchorElement.cpp index 1887aaf79cf..f741bf25cc5 100644 --- a/content/html/content/src/nsHTMLAnchorElement.cpp +++ b/content/html/content/src/nsHTMLAnchorElement.cpp @@ -137,27 +137,11 @@ public: protected: // The cached visited state nsLinkState mLinkState; - - void PrefetchDNS(); }; NS_IMPL_NS_NEW_HTML_ELEMENT(Anchor) -void -nsHTMLAnchorElement::PrefetchDNS() -{ - nsCOMPtr hrefURI; - GetHrefURI(getter_AddRefs(hrefURI)); - - if (hrefURI) { - nsRefPtr prefetch = - new nsHTMLDNSPrefetch(hrefURI, GetOwnerDoc()); - if (prefetch) - prefetch->PrefetchLow(); - } -} - nsHTMLAnchorElement::nsHTMLAnchorElement(nsINodeInfo *aNodeInfo) : nsGenericHTMLElement(aNodeInfo), mLinkState(eLinkState_Unknown) @@ -229,7 +213,14 @@ nsHTMLAnchorElement::BindToTree(nsIDocument* aDocument, nsIContent* aParent, RegUnRegAccessKey(PR_TRUE); } - PrefetchDNS(); + // Prefetch links + if (aDocument && nsHTMLDNSPrefetch::IsAllowed(GetOwnerDoc())) { + nsCOMPtr hrefURI; + + GetHrefURI(getter_AddRefs(hrefURI)); + if (hrefURI) + nsHTMLDNSPrefetch::PrefetchLow(hrefURI); + } return rv; } diff --git a/content/html/content/src/nsHTMLDNSPrefetch.cpp b/content/html/content/src/nsHTMLDNSPrefetch.cpp index f81b0428b78..5ba4c86a532 100644 --- a/content/html/content/src/nsHTMLDNSPrefetch.cpp +++ b/content/html/content/src/nsHTMLDNSPrefetch.cpp @@ -43,17 +43,22 @@ #include "nsNetUtil.h" #include "nsIDNSListener.h" +#include "nsIWebProgressListener.h" +#include "nsIWebProgress.h" +#include "nsCURILoader.h" #include "nsIDNSRecord.h" #include "nsIDNSService.h" #include "nsICancelable.h" #include "nsContentUtils.h" #include "nsGkAtoms.h" #include "nsIDocument.h" +#include "nsThreadUtils.h" static NS_DEFINE_CID(kDNSServiceCID, NS_DNSSERVICE_CID); static PRBool sDisablePrefetchHTTPSPref; static PRBool sInitialized = PR_FALSE; static nsIDNSService *sDNSService = nsnull; +static nsHTMLDNSPrefetch::nsDeferrals *sPrefetches = nsnull; nsresult nsHTMLDNSPrefetch::Initialize() @@ -63,6 +68,13 @@ nsHTMLDNSPrefetch::Initialize() return NS_OK; } + sPrefetches = new nsHTMLDNSPrefetch::nsDeferrals(); + if (!sPrefetches) + return NS_ERROR_OUT_OF_MEMORY; + + NS_ADDREF(sPrefetches); + sPrefetches->Activate(); + nsContentUtils::AddBoolPrefVarCache("network.dns.disablePrefetchFromHTTPS", &sDisablePrefetchHTTPSPref); @@ -88,26 +100,9 @@ nsHTMLDNSPrefetch::Shutdown() } sInitialized = PR_FALSE; NS_IF_RELEASE(sDNSService); - return NS_OK; -} - -nsHTMLDNSPrefetch::nsHTMLDNSPrefetch(nsAString &hostname, nsIDocument *aDocument) -{ - NS_ASSERTION(aDocument, "Document Required"); - NS_ASSERTION(sInitialized, "nsHTMLDNSPrefetch is not initialized"); + NS_IF_RELEASE(sPrefetches); - mAllowed = IsAllowed(aDocument); - CopyUTF16toUTF8(hostname, mHostname); -} - -nsHTMLDNSPrefetch::nsHTMLDNSPrefetch(nsIURI *aURI, nsIDocument *aDocument) -{ - NS_ASSERTION(aDocument, "Document Required"); - NS_ASSERTION(aURI, "URI Required"); - NS_ASSERTION(sInitialized, "nsHTMLDNSPrefetch is not initialized"); - - mAllowed = IsAllowed(aDocument); - aURI->GetAsciiHost(mHostname); + return NS_OK; } PRBool @@ -123,7 +118,7 @@ PRBool nsHTMLDNSPrefetch::IsAllowed (nsIDocument *aDocument) { if (IsSecureBaseContext(aDocument) && sDisablePrefetchHTTPSPref) - return PR_FALSE; + return PR_FALSE; // Check whether the x-dns-prefetch-control HTTP response header is set to override // the default. This may also be set by meta tag. Chromium treats any value other @@ -142,48 +137,195 @@ nsHTMLDNSPrefetch::IsAllowed (nsIDocument *aDocument) return PR_TRUE; } -nsresult -nsHTMLDNSPrefetch::Prefetch(PRUint16 flags) +nsresult +nsHTMLDNSPrefetch::Prefetch(nsIURI *aURI, PRUint16 flags) { - if (mHostname.IsEmpty()) - return NS_ERROR_NOT_AVAILABLE; - - if (!mAllowed) + if (!(sInitialized && sPrefetches && sDNSService)) return NS_ERROR_NOT_AVAILABLE; - if (!sDNSService) + return sPrefetches->Add(flags, aURI); +} + +nsresult +nsHTMLDNSPrefetch::PrefetchLow(nsIURI *aURI) +{ + return Prefetch(aURI, nsIDNSService::RESOLVE_PRIORITY_LOW); +} + +nsresult +nsHTMLDNSPrefetch::PrefetchMedium(nsIURI *aURI) +{ + return Prefetch(aURI, nsIDNSService::RESOLVE_PRIORITY_MEDIUM); +} + +nsresult +nsHTMLDNSPrefetch::PrefetchHigh(nsIURI *aURI) +{ + return Prefetch(aURI, 0); +} + +nsresult +nsHTMLDNSPrefetch::Prefetch(nsAString &hostname, PRUint16 flags) +{ + if (!(sInitialized && sDNSService && sPrefetches)) return NS_ERROR_NOT_AVAILABLE; - + nsCOMPtr tmpOutstanding; - - return sDNSService->AsyncResolve(mHostname, flags, this, nsnull, - getter_AddRefs(tmpOutstanding)); + return sDNSService->AsyncResolve(NS_ConvertUTF16toUTF8(hostname), flags, + sPrefetches, nsnull, getter_AddRefs(tmpOutstanding)); } nsresult -nsHTMLDNSPrefetch::PrefetchLow() +nsHTMLDNSPrefetch::PrefetchLow(nsAString &hostname) { - return Prefetch(nsIDNSService::RESOLVE_PRIORITY_LOW); + return Prefetch(hostname, nsIDNSService::RESOLVE_PRIORITY_LOW); } nsresult -nsHTMLDNSPrefetch::PrefetchMedium() +nsHTMLDNSPrefetch::PrefetchMedium(nsAString &hostname) { - return Prefetch(nsIDNSService::RESOLVE_PRIORITY_MEDIUM); + return Prefetch(hostname, nsIDNSService::RESOLVE_PRIORITY_MEDIUM); } nsresult -nsHTMLDNSPrefetch::PrefetchHigh() +nsHTMLDNSPrefetch::PrefetchHigh(nsAString &hostname) { - return Prefetch(0); + return Prefetch(hostname, 0); } -NS_IMPL_THREADSAFE_ISUPPORTS1(nsHTMLDNSPrefetch, nsIDNSListener) + +///////////////////////////////////////////////////////////////////////////////////////////////////////// + +nsHTMLDNSPrefetch::nsDeferrals::nsDeferrals() + : mHead(0), + mTail(0), + mActiveLoaderCount(0) +{ +} + +NS_IMPL_THREADSAFE_ISUPPORTS3(nsHTMLDNSPrefetch::nsDeferrals, + nsIDNSListener, + nsIWebProgressListener, + nsISupportsWeakReference) + +nsresult +nsHTMLDNSPrefetch::nsDeferrals::Add(PRUint16 flags, nsIURI *aURI) +{ + // The FIFO has no lock, so it can only be accessed on main thread + NS_ASSERTION(NS_IsMainThread(), "nsDeferrals::Add must be on main thread"); + + if (((mHead + 1) & sMaxDeferredMask) == mTail) + return NS_ERROR_DNS_LOOKUP_QUEUE_FULL; + + mEntries[mHead].mFlags = flags; + mEntries[mHead].mURI = aURI; + mHead = (mHead + 1) & sMaxDeferredMask; + + return NS_OK; +} + +void +nsHTMLDNSPrefetch::nsDeferrals::SubmitQueue() +{ + nsCString hostName; + if (!sDNSService) return; + + while (mHead != mTail) { + mEntries[mTail].mURI->GetAsciiHost(hostName); + if (!hostName.IsEmpty()) { + + nsCOMPtr tmpOutstanding; + + sDNSService->AsyncResolve(hostName, + mEntries[mTail].mFlags, + this, nsnull, getter_AddRefs(tmpOutstanding)); + } + mEntries[mTail].mURI = nsnull; + mTail = (mTail + 1) & sMaxDeferredMask; + } +} + +void +nsHTMLDNSPrefetch::nsDeferrals::Activate() +{ + // Register as an observer for the document loader + nsCOMPtr progress = + do_GetService(NS_DOCUMENTLOADER_SERVICE_CONTRACTID); + if (progress) + progress->AddProgressListener(this, nsIWebProgress::NOTIFY_STATE_DOCUMENT); +} + +//////////// nsIDNSListener method NS_IMETHODIMP -nsHTMLDNSPrefetch::OnLookupComplete(nsICancelable *request, - nsIDNSRecord *rec, - nsresult status) +nsHTMLDNSPrefetch::nsDeferrals::OnLookupComplete(nsICancelable *request, + nsIDNSRecord *rec, + nsresult status) +{ + return NS_OK; +} + +//////////// nsIWebProgressListener methods + +NS_IMETHODIMP +nsHTMLDNSPrefetch::nsDeferrals::OnStateChange(nsIWebProgress* aWebProgress, + nsIRequest *aRequest, + PRUint32 progressStateFlags, + nsresult aStatus) +{ + // The FIFO has no lock, so it can only be accessed on main thread + NS_ASSERTION(NS_IsMainThread(), "nsDeferrals::OnStateChange must be on main thread"); + + if (progressStateFlags & STATE_IS_DOCUMENT) { + if (progressStateFlags & STATE_STOP) { + + // Initialization may have missed a STATE_START notification, so do + // not go negative + if (mActiveLoaderCount) + mActiveLoaderCount--; + + if (!mActiveLoaderCount) + SubmitQueue(); + } + else if (progressStateFlags & STATE_START) + mActiveLoaderCount++; + } + + return NS_OK; +} + +NS_IMETHODIMP +nsHTMLDNSPrefetch::nsDeferrals::OnProgressChange(nsIWebProgress *aProgress, + nsIRequest *aRequest, + PRInt32 curSelfProgress, + PRInt32 maxSelfProgress, + PRInt32 curTotalProgress, + PRInt32 maxTotalProgress) +{ + return NS_OK; +} + +NS_IMETHODIMP +nsHTMLDNSPrefetch::nsDeferrals::OnLocationChange(nsIWebProgress* aWebProgress, + nsIRequest* aRequest, + nsIURI *location) +{ + return NS_OK; +} + +NS_IMETHODIMP +nsHTMLDNSPrefetch::nsDeferrals::OnStatusChange(nsIWebProgress* aWebProgress, + nsIRequest* aRequest, + nsresult aStatus, + const PRUnichar* aMessage) +{ + return NS_OK; +} + +NS_IMETHODIMP +nsHTMLDNSPrefetch::nsDeferrals::OnSecurityChange(nsIWebProgress *aWebProgress, + nsIRequest *aRequest, + PRUint32 state) { return NS_OK; } diff --git a/content/html/content/src/nsHTMLDNSPrefetch.h b/content/html/content/src/nsHTMLDNSPrefetch.h index ac46e712926..1e26dfa024d 100644 --- a/content/html/content/src/nsHTMLDNSPrefetch.h +++ b/content/html/content/src/nsHTMLDNSPrefetch.h @@ -36,44 +36,83 @@ * * ***** END LICENSE BLOCK ***** */ -#ifndef nsHMTLDNSPrefetch_h___ +#ifndef nsHTMLDNSPrefetch_h___ +#define nsHTMLDNSPrefetch_h___ #include "nsCOMPtr.h" #include "nsString.h" #include "nsIDNSListener.h" +#include "nsIWebProgressListener.h" +#include "nsWeakReference.h" class nsIURI; class nsIDocument; -class nsHTMLDNSPrefetch : public nsIDNSListener +class nsHTMLDNSPrefetch { public: - NS_DECL_ISUPPORTS - NS_DECL_NSIDNSLISTENER - // The required aDocument parameter is the context requesting the prefetch - under // certain circumstances (e.g. headers, or security context) associated with // the context the prefetch will not be performed. - - nsHTMLDNSPrefetch(nsAString &aHostname, nsIDocument *aDocument); - nsHTMLDNSPrefetch(nsIURI *aURI, nsIDocument *aDocument); - + static PRBool IsAllowed(nsIDocument *aDocument); + static nsresult Initialize(); static nsresult Shutdown(); - // Call one of the following methods to start the Prefetch. - nsresult PrefetchHigh(); - nsresult PrefetchMedium(); - nsresult PrefetchLow(); - + // Call one of the Prefetch* methods to start the lookup. + // + // The URI versions will defer DNS lookup until pageload is + // complete, while the string versions submit the lookup to + // the DNS system immediately. The URI version is somewhat lighter + // weight, but its request is also more likely to be dropped due to a + // full queue and it may only be used from the main thread. + + static nsresult PrefetchHigh(nsIURI *aURI); + static nsresult PrefetchMedium(nsIURI *aURI); + static nsresult PrefetchLow(nsIURI *aURI); + static nsresult PrefetchHigh(nsAString &host); + static nsresult PrefetchMedium(nsAString &host); + static nsresult PrefetchLow(nsAString &host); + private: - nsCString mHostname; - PRBool mAllowed; + static nsresult Prefetch(nsAString &host, PRUint16 flags); + static nsresult Prefetch(nsIURI *aURI, PRUint16 flags); + static PRBool IsSecureBaseContext(nsIDocument *aDocument); + +public: + class nsDeferrals : public nsIDNSListener + , public nsIWebProgressListener + , public nsSupportsWeakReference + { + public: + NS_DECL_ISUPPORTS + NS_DECL_NSIDNSLISTENER + NS_DECL_NSIWEBPROGRESSLISTENER - nsresult Prefetch(PRUint16 flags); - PRBool IsSecureBaseContext(nsIDocument *aDocument); - PRBool IsAllowed(nsIDocument *aDocument); + nsDeferrals(); + + void Activate(); + nsresult Add(PRUint16 flags, nsIURI *aURI); + + private: + ~nsDeferrals() {} + + void SubmitQueue(); + + PRUint16 mHead; + PRUint16 mTail; + PRUint32 mActiveLoaderCount; + + static const int sMaxDeferred = 512; // keep power of 2 for masking + static const int sMaxDeferredMask = (sMaxDeferred - 1); + + struct deferred_entry + { + PRUint16 mFlags; + nsCOMPtr mURI; + } mEntries[sMaxDeferred]; + }; }; #endif diff --git a/netwerk/base/src/nsDNSPrefetch.h b/netwerk/base/src/nsDNSPrefetch.h index b4c0a918f95..9c4535cb1f0 100644 --- a/netwerk/base/src/nsDNSPrefetch.h +++ b/netwerk/base/src/nsDNSPrefetch.h @@ -37,6 +37,7 @@ * ***** END LICENSE BLOCK ***** */ #ifndef nsDNSPrefetch_h___ +#define nsDNSPrefetch_h___ #include "nsCOMPtr.h" #include "nsString.h" diff --git a/netwerk/protocol/http/src/nsHttpChannel.cpp b/netwerk/protocol/http/src/nsHttpChannel.cpp index 85fc68e9d9a..e5d68682e9e 100644 --- a/netwerk/protocol/http/src/nsHttpChannel.cpp +++ b/netwerk/protocol/http/src/nsHttpChannel.cpp @@ -4018,13 +4018,16 @@ nsHttpChannel::AsyncOpen(nsIStreamListener *listener, nsISupports *context) if (NS_FAILED(rv)) return rv; - // Start a DNS lookup very early in case the real open is queued the DNS can - // happen in parallel. - nsRefPtr prefetch = new nsDNSPrefetch(mURI); - if (prefetch) { - prefetch->PrefetchHigh(); + if (!(mConnectionInfo && mConnectionInfo->UsingHttpProxy())) { + // Start a DNS lookup very early in case the real open is queued the DNS can + // happen in parallel. Do not do so in the presence of an HTTP proxy as + // all lookups other than for the proxy itself are done by the proxy. + nsRefPtr prefetch = new nsDNSPrefetch(mURI); + if (prefetch) { + prefetch->PrefetchHigh(); + } } - + // Remember the cookie header that was set, if any const char *cookieHeader = mRequestHead.PeekHeader(nsHttp::Cookie); if (cookieHeader)