mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-12-02 10:00:54 +00:00
Bug 622232: Cancel DNS prefetches for HTML Anchor Elems after a tab is closed; r=mcmanus sr=bz
This commit is contained in:
parent
3cf23f6507
commit
06e7d450e9
@ -131,12 +131,18 @@ public:
|
||||
virtual nsXPCClassInfo* GetClassInfo();
|
||||
};
|
||||
|
||||
// Indicates if a DNS Prefetch has been requested from this Anchor elem
|
||||
#define HTML_ANCHOR_DNS_PREFETCH_REQUESTED \
|
||||
(1 << ELEMENT_TYPE_SPECIFIC_BITS_OFFSET)
|
||||
|
||||
// Make sure we have enough space for those bits
|
||||
PR_STATIC_ASSERT(ELEMENT_TYPE_SPECIFIC_BITS_OFFSET < 32);
|
||||
|
||||
NS_IMPL_NS_NEW_HTML_ELEMENT(Anchor)
|
||||
|
||||
nsHTMLAnchorElement::nsHTMLAnchorElement(already_AddRefed<nsINodeInfo> aNodeInfo)
|
||||
: nsGenericHTMLElement(aNodeInfo),
|
||||
Link(this)
|
||||
: nsGenericHTMLElement(aNodeInfo)
|
||||
, Link(this)
|
||||
{
|
||||
}
|
||||
|
||||
@ -206,6 +212,7 @@ nsHTMLAnchorElement::BindToTree(nsIDocument* aDocument, nsIContent* aParent,
|
||||
// Prefetch links
|
||||
if (aDocument && nsHTMLDNSPrefetch::IsAllowed(OwnerDoc())) {
|
||||
nsHTMLDNSPrefetch::PrefetchLow(this);
|
||||
SetFlags(HTML_ANCHOR_DNS_PREFETCH_REQUESTED);
|
||||
}
|
||||
return rv;
|
||||
}
|
||||
@ -213,6 +220,14 @@ nsHTMLAnchorElement::BindToTree(nsIDocument* aDocument, nsIContent* aParent,
|
||||
void
|
||||
nsHTMLAnchorElement::UnbindFromTree(bool aDeep, bool aNullParent)
|
||||
{
|
||||
// Cancel any DNS prefetches
|
||||
// Note: Must come before ResetLinkState. If called after, it will recreate
|
||||
// mCachedURI based on data that is invalid - due to a call to GetHostname.
|
||||
if (HasFlag(HTML_ANCHOR_DNS_PREFETCH_REQUESTED)) {
|
||||
nsHTMLDNSPrefetch::CancelPrefetchLow(this, NS_ERROR_ABORT);
|
||||
UnsetFlags(HTML_ANCHOR_DNS_PREFETCH_REQUESTED);
|
||||
}
|
||||
|
||||
// If this link is ever reinserted into a document, it might
|
||||
// be under a different xml:base, so forget the cached state now.
|
||||
Link::ResetLinkState(false);
|
||||
|
@ -192,7 +192,8 @@ nsHTMLDNSPrefetch::Prefetch(nsAString &hostname, PRUint16 flags)
|
||||
return NS_ERROR_NOT_AVAILABLE;
|
||||
|
||||
nsCOMPtr<nsICancelable> tmpOutstanding;
|
||||
return sDNSService->AsyncResolve(NS_ConvertUTF16toUTF8(hostname), flags | nsIDNSService::RESOLVE_SPECULATE,
|
||||
return sDNSService->AsyncResolve(NS_ConvertUTF16toUTF8(hostname),
|
||||
flags | nsIDNSService::RESOLVE_SPECULATE,
|
||||
sDNSListener, nsnull, getter_AddRefs(tmpOutstanding));
|
||||
}
|
||||
|
||||
@ -214,6 +215,71 @@ nsHTMLDNSPrefetch::PrefetchHigh(nsAString &hostname)
|
||||
return Prefetch(hostname, 0);
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsHTMLDNSPrefetch::CancelPrefetch(Link *aElement, PRUint16 flags, nsresult aReason)
|
||||
{
|
||||
nsAutoString hostname;
|
||||
nsresult rv = aElement->GetHostname(hostname);
|
||||
if (IsNeckoChild()) {
|
||||
// Instead of transporting the Link object to the other process
|
||||
// we are using the hostname based function here, too. Compared to the
|
||||
// IPC the performance hit should be negligible.
|
||||
NS_ENSURE_SUCCESS(rv,rv);
|
||||
|
||||
// Forward the cancellation to the string based CancelPrefetch()
|
||||
return CancelPrefetch(hostname, flags, aReason);
|
||||
}
|
||||
|
||||
if (!(sInitialized && sPrefetches && sDNSService && sDNSListener))
|
||||
return NS_ERROR_NOT_AVAILABLE;
|
||||
|
||||
// Attempt to remove the prefetch request from the Deferrals FIFO first ...
|
||||
bool found = false;
|
||||
rv = sPrefetches->Remove(flags, aElement, &found);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
// ... If no request was found, it may have been sent to the DNS Service.
|
||||
// Forward the cancellation to the string based CancelPrefetch
|
||||
if (!found)
|
||||
rv = CancelPrefetch(hostname, flags, aReason);
|
||||
return rv;
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsHTMLDNSPrefetch::CancelPrefetch(nsAString &hostname, PRUint16 flags, nsresult aReason)
|
||||
{
|
||||
// Forward this request to Necko Parent if we're a child process
|
||||
if (IsNeckoChild()) {
|
||||
// We need to check IsEmpty() because net_IsValidHostName()
|
||||
// considers empty strings to be valid hostnames
|
||||
if (!hostname.IsEmpty() &&
|
||||
net_IsValidHostName(NS_ConvertUTF16toUTF8(hostname))) {
|
||||
gNeckoChild->SendCancelHTMLDNSPrefetch(nsAutoString(hostname), flags, aReason);
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
if (!(sInitialized && sDNSService && sPrefetches && sDNSListener))
|
||||
return NS_ERROR_NOT_AVAILABLE;
|
||||
|
||||
// Forward cancellation to DNS service
|
||||
return sDNSService->CancelAsyncResolve(NS_ConvertUTF16toUTF8(hostname),
|
||||
flags | nsIDNSService::RESOLVE_SPECULATE,
|
||||
sDNSListener, aReason);
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsHTMLDNSPrefetch::CancelPrefetchLow(Link *aElement, nsresult aReason)
|
||||
{
|
||||
return CancelPrefetch(aElement, nsIDNSService::RESOLVE_PRIORITY_LOW, aReason);
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsHTMLDNSPrefetch::CancelPrefetchLow(nsAString &hostname, nsresult aReason)
|
||||
{
|
||||
return CancelPrefetch(hostname, nsIDNSService::RESOLVE_PRIORITY_LOW, aReason);
|
||||
}
|
||||
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
NS_IMPL_THREADSAFE_ISUPPORTS1(nsHTMLDNSPrefetch::nsListener,
|
||||
@ -283,6 +349,42 @@ nsHTMLDNSPrefetch::nsDeferrals::Add(PRUint16 flags, Link *aElement)
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsHTMLDNSPrefetch::nsDeferrals::Remove(PRUint16 aFlags, Link *aElement, bool *aFound)
|
||||
{
|
||||
// The FIFO has no lock, so it can only be accessed on main thread
|
||||
NS_ASSERTION(NS_IsMainThread(), "nsDeferrals::Remove must be on main thread");
|
||||
|
||||
// Search the deferrals FIFO for this Link elem and remove it
|
||||
// Note: Element removal will leave holes in the queue. However:
|
||||
// -- FIFO is flushed in SubmitQueue, so holes are temporary.
|
||||
// -- holes are only created if a tab is closed before page is loaded.
|
||||
bool found = false;
|
||||
PRUint16 curr = mTail;
|
||||
while (curr != mHead) {
|
||||
nsCOMPtr<nsIContent> content = do_QueryReferent(mEntries[curr].mElement);
|
||||
if (content) {
|
||||
nsCOMPtr<Link> link = do_QueryInterface(content);
|
||||
if (link && (link == aElement) && (mEntries[curr].mFlags == aFlags)) {
|
||||
// Null mElements will be ignored in SubmitQueue; requests won't be sent
|
||||
mEntries[curr].mElement = NULL;
|
||||
mEntries[curr].mFlags = 0;
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
curr = (curr + 1) & sMaxDeferredMask;
|
||||
}
|
||||
// Minor optimization: If we removed an element at the tail, increment the
|
||||
// the tail end to shrink the FIFO.
|
||||
if (found && (mTail != mHead))
|
||||
mTail = (mTail + 1) & sMaxDeferredMask;
|
||||
|
||||
// Report "found" status back to caller
|
||||
*aFound = found;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
void
|
||||
nsHTMLDNSPrefetch::nsDeferrals::SubmitQueue()
|
||||
{
|
||||
|
@ -87,10 +87,18 @@ public:
|
||||
static nsresult PrefetchHigh(nsAString &host);
|
||||
static nsresult PrefetchMedium(nsAString &host);
|
||||
static nsresult PrefetchLow(nsAString &host);
|
||||
static nsresult CancelPrefetchLow(nsAString &host, nsresult aReason);
|
||||
static nsresult CancelPrefetchLow(mozilla::dom::Link *aElement, nsresult aReason);
|
||||
|
||||
private:
|
||||
static nsresult Prefetch(nsAString &host, PRUint16 flags);
|
||||
static nsresult Prefetch(mozilla::dom::Link *aElement, PRUint16 flags);
|
||||
static nsresult CancelPrefetch(nsAString &hostname,
|
||||
PRUint16 flags,
|
||||
nsresult aReason);
|
||||
static nsresult CancelPrefetch(mozilla::dom::Link *aElement,
|
||||
PRUint16 flags,
|
||||
nsresult aReason);
|
||||
|
||||
public:
|
||||
class nsListener : public nsIDNSListener
|
||||
@ -118,6 +126,7 @@ public:
|
||||
|
||||
void Activate();
|
||||
nsresult Add(PRUint16 flags, mozilla::dom::Link *aElement);
|
||||
nsresult Remove(PRUint16 aFlags, mozilla::dom::Link *aElement, bool *aFound);
|
||||
|
||||
private:
|
||||
~nsDeferrals();
|
||||
|
@ -278,6 +278,10 @@ public:
|
||||
~nsDNSAsyncRequest() {}
|
||||
|
||||
void OnLookupComplete(nsHostResolver *, nsHostRecord *, nsresult);
|
||||
// Returns TRUE if the DNS listener arg is the same as the member listener
|
||||
// Used in Cancellations to remove DNS requests associated with a
|
||||
// particular hostname and nsIDNSListener
|
||||
bool EqualsAsyncListener(nsIDNSListener *aListener);
|
||||
|
||||
nsRefPtr<nsHostResolver> mResolver;
|
||||
nsCString mHost; // hostname we're resolving
|
||||
@ -310,6 +314,12 @@ nsDNSAsyncRequest::OnLookupComplete(nsHostResolver *resolver,
|
||||
NS_RELEASE_THIS();
|
||||
}
|
||||
|
||||
bool
|
||||
nsDNSAsyncRequest::EqualsAsyncListener(nsIDNSListener *aListener)
|
||||
{
|
||||
return (aListener == mListener);
|
||||
}
|
||||
|
||||
NS_IMPL_THREADSAFE_ISUPPORTS1(nsDNSAsyncRequest, nsICancelable)
|
||||
|
||||
NS_IMETHODIMP
|
||||
@ -332,6 +342,7 @@ public:
|
||||
virtual ~nsDNSSyncRequest() {}
|
||||
|
||||
void OnLookupComplete(nsHostResolver *, nsHostRecord *, nsresult);
|
||||
bool EqualsAsyncListener(nsIDNSListener *aListener);
|
||||
|
||||
bool mDone;
|
||||
nsresult mStatus;
|
||||
@ -355,6 +366,13 @@ nsDNSSyncRequest::OnLookupComplete(nsHostResolver *resolver,
|
||||
PR_ExitMonitor(mMonitor);
|
||||
}
|
||||
|
||||
bool
|
||||
nsDNSSyncRequest::EqualsAsyncListener(nsIDNSListener *aListener)
|
||||
{
|
||||
// Sync request: no listener to compare
|
||||
return false;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
nsDNSService::nsDNSService()
|
||||
@ -584,6 +602,42 @@ nsDNSService::AsyncResolve(const nsACString &hostname,
|
||||
return rv;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsDNSService::CancelAsyncResolve(const nsACString &aHostname,
|
||||
PRUint32 aFlags,
|
||||
nsIDNSListener *aListener,
|
||||
nsresult aReason)
|
||||
{
|
||||
// grab reference to global host resolver and IDN service. beware
|
||||
// simultaneous shutdown!!
|
||||
nsRefPtr<nsHostResolver> res;
|
||||
nsCOMPtr<nsIIDNService> idn;
|
||||
{
|
||||
MutexAutoLock lock(mLock);
|
||||
|
||||
if (mDisablePrefetch && (aFlags & RESOLVE_SPECULATE))
|
||||
return NS_ERROR_DNS_LOOKUP_QUEUE_FULL;
|
||||
|
||||
res = mResolver;
|
||||
idn = mIDN;
|
||||
}
|
||||
if (!res)
|
||||
return NS_ERROR_OFFLINE;
|
||||
|
||||
nsCString hostname(aHostname);
|
||||
|
||||
nsCAutoString hostACE;
|
||||
if (idn && !IsASCII(aHostname)) {
|
||||
if (NS_SUCCEEDED(idn->ConvertUTF8toACE(aHostname, hostACE)))
|
||||
hostname = hostACE;
|
||||
}
|
||||
|
||||
PRUint16 af = GetAFForLookup(hostname, aFlags);
|
||||
|
||||
res->CancelAsyncRequest(hostname.get(), aFlags, af, aListener, aReason);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsDNSService::Resolve(const nsACString &hostname,
|
||||
PRUint32 flags,
|
||||
|
@ -38,6 +38,7 @@
|
||||
#include "nsIIDNService.h"
|
||||
#include "nsIObserver.h"
|
||||
#include "nsHostResolver.h"
|
||||
#include "nsICancelable.h"
|
||||
#include "nsAutoPtr.h"
|
||||
#include "nsString.h"
|
||||
#include "mozilla/Mutex.h"
|
||||
|
@ -891,6 +891,50 @@ nsHostResolver::OnLookupComplete(nsHostRecord *rec, nsresult status, PRAddrInfo
|
||||
NS_RELEASE(rec);
|
||||
}
|
||||
|
||||
void
|
||||
nsHostResolver::CancelAsyncRequest(const char *host,
|
||||
PRUint16 flags,
|
||||
PRUint16 af,
|
||||
nsIDNSListener *aListener,
|
||||
nsresult status)
|
||||
|
||||
{
|
||||
MutexAutoLock lock(mLock);
|
||||
|
||||
// Lookup the host record associated with host, flags & address family
|
||||
nsHostKey key = { host, flags, af };
|
||||
nsHostDBEnt *he = static_cast<nsHostDBEnt *>
|
||||
(PL_DHashTableOperate(&mDB, &key, PL_DHASH_LOOKUP));
|
||||
if (he && he->rec) {
|
||||
nsHostRecord* recPtr = NULL;
|
||||
PRCList *node = he->rec->callbacks.next;
|
||||
// Remove the first nsDNSAsyncRequest callback which matches the
|
||||
// supplied listener object
|
||||
while (node != &he->rec->callbacks) {
|
||||
nsResolveHostCallback *callback
|
||||
= static_cast<nsResolveHostCallback *>(node);
|
||||
if (callback && (callback->EqualsAsyncListener(aListener))) {
|
||||
// Remove from the list of callbacks
|
||||
PR_REMOVE_LINK(callback);
|
||||
recPtr = he->rec;
|
||||
callback->OnLookupComplete(this, recPtr, status);
|
||||
break;
|
||||
}
|
||||
node = node->next;
|
||||
}
|
||||
|
||||
// If there are no more callbacks, remove the hash table entry
|
||||
if (recPtr && PR_CLIST_IS_EMPTY(&recPtr->callbacks)) {
|
||||
PL_DHashTableOperate(&mDB, (nsHostKey *)recPtr, PL_DHASH_REMOVE);
|
||||
// If record is on a Queue, remove it and then deref it
|
||||
if (recPtr->next != recPtr) {
|
||||
PR_REMOVE_LINK(recPtr);
|
||||
NS_RELEASE(recPtr);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
void
|
||||
|
@ -46,6 +46,7 @@
|
||||
#include "mozilla/CondVar.h"
|
||||
#include "mozilla/Mutex.h"
|
||||
#include "nsISupportsImpl.h"
|
||||
#include "nsIDNSListener.h"
|
||||
#include "nsString.h"
|
||||
#include "nsTArray.h"
|
||||
|
||||
@ -191,6 +192,20 @@ public:
|
||||
virtual void OnLookupComplete(nsHostResolver *resolver,
|
||||
nsHostRecord *record,
|
||||
nsresult status) = 0;
|
||||
/**
|
||||
* EqualsAsyncListener
|
||||
*
|
||||
* Determines if the listener argument matches the listener member var.
|
||||
* For subclasses not implementing a member listener, should return false.
|
||||
* For subclasses having a member listener, the function should check if
|
||||
* they are the same. Used for cases where a pointer to an object
|
||||
* implementing nsResolveHostCallback is unknown, but a pointer to
|
||||
* the original listener is known.
|
||||
*
|
||||
* @param aListener
|
||||
* nsIDNSListener object associated with the original request
|
||||
*/
|
||||
virtual bool EqualsAsyncListener(nsIDNSListener *aListener) = 0;
|
||||
};
|
||||
|
||||
/**
|
||||
@ -244,6 +259,18 @@ public:
|
||||
nsResolveHostCallback *callback,
|
||||
nsresult status);
|
||||
|
||||
/**
|
||||
* Cancels an async request associated with the hostname, flags,
|
||||
* address family and listener. Cancels first callback found which matches
|
||||
* these criteria. These parameters should correspond to the parameters
|
||||
* passed to ResolveHost. If this is the last callback associated with the
|
||||
* host record, it is removed from any request queues it might be on.
|
||||
*/
|
||||
void CancelAsyncRequest(const char *host,
|
||||
PRUint16 flags,
|
||||
PRUint16 af,
|
||||
nsIDNSListener *aListener,
|
||||
nsresult status);
|
||||
/**
|
||||
* values for the flags parameter passed to ResolveHost and DetachCallback
|
||||
* that may be bitwise OR'd together.
|
||||
|
@ -46,7 +46,7 @@ interface nsIDNSListener;
|
||||
/**
|
||||
* nsIDNSService
|
||||
*/
|
||||
[scriptable, uuid(c1a56a45-8fa3-44e6-9f01-38c91c858cf9)]
|
||||
[scriptable, uuid(F6E05CC3-8A13-463D-877F-D59B20B59724)]
|
||||
interface nsIDNSService : nsISupports
|
||||
{
|
||||
/**
|
||||
@ -72,6 +72,26 @@ interface nsIDNSService : nsISupports
|
||||
in nsIDNSListener aListener,
|
||||
in nsIEventTarget aListenerTarget);
|
||||
|
||||
/**
|
||||
* Attempts to cancel a previously requested async DNS lookup
|
||||
*
|
||||
* @param aHostName
|
||||
* the hostname or IP-address-literal to resolve.
|
||||
* @param aFlags
|
||||
* a bitwise OR of the RESOLVE_ prefixed constants defined below.
|
||||
* @param aListener
|
||||
* the original listener which was to be notified about the host lookup
|
||||
* result - used to match request information to requestor.
|
||||
* @param aReason
|
||||
* nsresult reason for the cancellation
|
||||
*
|
||||
* @return An object that can be used to cancel the host lookup.
|
||||
*/
|
||||
void cancelAsyncResolve(in AUTF8String aHostName,
|
||||
in unsigned long aFlags,
|
||||
in nsIDNSListener aListener,
|
||||
in nsresult aReason);
|
||||
|
||||
/**
|
||||
* called to synchronously resolve a hostname. warning this method may
|
||||
* block the calling thread for a long period of time. it is extremely
|
||||
|
@ -150,5 +150,15 @@ NeckoParent::RecvHTMLDNSPrefetch(const nsString& hostname,
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
NeckoParent::RecvCancelHTMLDNSPrefetch(const nsString& hostname,
|
||||
const PRUint16& flags,
|
||||
const nsresult& reason)
|
||||
{
|
||||
nsAutoString h(hostname);
|
||||
nsHTMLDNSPrefetch::CancelPrefetch(h, flags, reason);
|
||||
return true;
|
||||
}
|
||||
|
||||
}} // mozilla::net
|
||||
|
||||
|
@ -68,6 +68,10 @@ protected:
|
||||
virtual bool DeallocPWebSocket(PWebSocketParent*);
|
||||
virtual bool RecvHTMLDNSPrefetch(const nsString& hostname,
|
||||
const PRUint16& flags);
|
||||
virtual bool RecvCancelHTMLDNSPrefetch(const nsString& hostname,
|
||||
const PRUint16& flags,
|
||||
const nsresult& reason);
|
||||
|
||||
};
|
||||
|
||||
} // namespace net
|
||||
|
@ -69,6 +69,7 @@ parent:
|
||||
PWebSocket(PBrowser browser);
|
||||
|
||||
HTMLDNSPrefetch(nsString hostname, PRUint16 flags);
|
||||
CancelHTMLDNSPrefetch(nsString hostname, PRUint16 flags, nsresult reason);
|
||||
|
||||
both:
|
||||
PHttpChannel(nullable PBrowser browser);
|
||||
|
Loading…
Reference in New Issue
Block a user