Bug 464838: Tweak DNS prefetch to hopefully fix a tp3 regression. r+sr=bz

This commit is contained in:
Patrick McManus 2008-12-05 12:53:24 -08:00
parent e4ad029acb
commit 36e0dca600
6 changed files with 262 additions and 87 deletions

View File

@ -869,10 +869,9 @@ nsContentSink::PrefetchDNS(const nsAString &aHref)
}
else
nsGenericHTMLElement::GetHostnameFromHrefString(aHref, hostname);
nsRefPtr<nsHTMLDNSPrefetch> prefetch = new nsHTMLDNSPrefetch(hostname, mDocument);
if (prefetch) {
prefetch->PrefetchLow();
if (nsHTMLDNSPrefetch::IsAllowed(mDocument)) {
nsHTMLDNSPrefetch::PrefetchLow(hostname);
}
}

View File

@ -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<nsIURI> hrefURI;
GetHrefURI(getter_AddRefs(hrefURI));
if (hrefURI) {
nsRefPtr<nsHTMLDNSPrefetch> 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<nsIURI> hrefURI;
GetHrefURI(getter_AddRefs(hrefURI));
if (hrefURI)
nsHTMLDNSPrefetch::PrefetchLow(hrefURI);
}
return rv;
}

View File

@ -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<nsICancelable> 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<nsICancelable> 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<nsIWebProgress> 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;
}

View File

@ -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<nsIURI> mURI;
} mEntries[sMaxDeferred];
};
};
#endif

View File

@ -37,6 +37,7 @@
* ***** END LICENSE BLOCK ***** */
#ifndef nsDNSPrefetch_h___
#define nsDNSPrefetch_h___
#include "nsCOMPtr.h"
#include "nsString.h"

View File

@ -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<nsDNSPrefetch> 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<nsDNSPrefetch> 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)