Bug 622232: Cancel DNS prefetches for HTML Anchor Elems after a tab is closed; r=mcmanus sr=bz
☠☠ backed out by 7edf9d07812c ☠ ☠
authorSteve Workman <sworkman@mozilla.com>
Tue, 04 Oct 2011 16:22:43 -0700
changeset 79198 08a63bc26c755fa740ab19c9d12b4b603a1e1508
parent 79197 aacc24a428856b1b0e8723703fcf70ecf612ac0e
child 79212 cc66accc81813cd35c5da8f7d50b559c4185ecc2
push id2913
push userjlebar@mozilla.com
push dateTue, 25 Oct 2011 22:46:57 +0000
treeherdermozilla-inbound@08a63bc26c75 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmcmanus, bz
bugs622232
milestone10.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 622232: Cancel DNS prefetches for HTML Anchor Elems after a tab is closed; r=mcmanus sr=bz
content/html/content/src/nsHTMLAnchorElement.cpp
content/html/content/src/nsHTMLDNSPrefetch.cpp
content/html/content/src/nsHTMLDNSPrefetch.h
netwerk/dns/nsDNSService2.cpp
netwerk/dns/nsDNSService2.h
netwerk/dns/nsHostResolver.cpp
netwerk/dns/nsHostResolver.h
netwerk/dns/nsIDNSService.idl
netwerk/ipc/NeckoParent.cpp
netwerk/ipc/NeckoParent.h
netwerk/ipc/PNecko.ipdl
--- a/content/html/content/src/nsHTMLAnchorElement.cpp
+++ b/content/html/content/src/nsHTMLAnchorElement.cpp
@@ -126,22 +126,28 @@ public:
 
   virtual nsresult Clone(nsINodeInfo *aNodeInfo, nsINode **aResult) const;
 
   virtual nsEventStates IntrinsicState() const;
 
   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)
 {
 }
 
 nsHTMLAnchorElement::~nsHTMLAnchorElement()
 {
 }
 
 
@@ -201,23 +207,32 @@ nsHTMLAnchorElement::BindToTree(nsIDocum
   nsresult rv = nsGenericHTMLElement::BindToTree(aDocument, aParent,
                                                  aBindingParent,
                                                  aCompileEventHandlers);
   NS_ENSURE_SUCCESS(rv, rv);
 
   // Prefetch links
   if (aDocument && nsHTMLDNSPrefetch::IsAllowed(OwnerDoc())) {
     nsHTMLDNSPrefetch::PrefetchLow(this);
+    SetFlags(HTML_ANCHOR_DNS_PREFETCH_REQUESTED);
   }
   return rv;
 }
 
 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);
 
   nsGenericHTMLElement::UnbindFromTree(aDeep, aNullParent);
 }
 
 bool
--- a/content/html/content/src/nsHTMLDNSPrefetch.cpp
+++ b/content/html/content/src/nsHTMLDNSPrefetch.cpp
@@ -187,17 +187,18 @@ nsHTMLDNSPrefetch::Prefetch(nsAString &h
     }
     return NS_OK;
   }
 
   if (!(sInitialized && sDNSService && sPrefetches && sDNSListener))
     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));
 }
 
 nsresult
 nsHTMLDNSPrefetch::PrefetchLow(nsAString &hostname)
 {
   return Prefetch(hostname, nsIDNSService::RESOLVE_PRIORITY_LOW);
 }
@@ -209,16 +210,81 @@ nsHTMLDNSPrefetch::PrefetchMedium(nsAStr
 }
 
 nsresult
 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,
                               nsIDNSListener)
 
 NS_IMETHODIMP
 nsHTMLDNSPrefetch::nsListener::OnLookupComplete(nsICancelable *request,
                                               nsIDNSRecord  *rec,
@@ -278,16 +344,52 @@ nsHTMLDNSPrefetch::nsDeferrals::Add(PRUi
   if (!mActiveLoaderCount && !mTimerArmed && mTimer) {
     mTimerArmed = true;
     mTimer->InitWithFuncCallback(Tick, this, 2000, nsITimer::TYPE_ONE_SHOT);
   }
   
   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()
 {
   NS_ASSERTION(NS_IsMainThread(), "nsDeferrals::SubmitQueue must be on main thread");
   nsCString hostName;
   if (!sDNSService) return;
 
   while (mHead != mTail) {
--- a/content/html/content/src/nsHTMLDNSPrefetch.h
+++ b/content/html/content/src/nsHTMLDNSPrefetch.h
@@ -82,20 +82,28 @@ public:
   // full queue and it may only be used from the main thread.
 
   static nsresult PrefetchHigh(mozilla::dom::Link *aElement);
   static nsresult PrefetchMedium(mozilla::dom::Link *aElement);
   static nsresult PrefetchLow(mozilla::dom::Link *aElement);
   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
   {
     // This class exists to give a safe callback no-op DNSListener
   public:
     NS_DECL_ISUPPORTS
     NS_DECL_NSIDNSLISTENER
@@ -113,16 +121,17 @@ public:
     NS_DECL_ISUPPORTS
     NS_DECL_NSIWEBPROGRESSLISTENER
     NS_DECL_NSIOBSERVER
 
     nsDeferrals();
     
     void Activate();
     nsresult Add(PRUint16 flags, mozilla::dom::Link *aElement);
+    nsresult Remove(PRUint16 aFlags, mozilla::dom::Link *aElement, bool *aFound);
     
   private:
     ~nsDeferrals();
     void Flush();
     
     void SubmitQueue();
     
     PRUint16                  mHead;
--- a/netwerk/dns/nsDNSService2.cpp
+++ b/netwerk/dns/nsDNSService2.cpp
@@ -273,16 +273,20 @@ public:
         : mResolver(res)
         , mHost(host)
         , mListener(listener)
         , mFlags(flags)
         , mAF(af) {}
     ~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
     nsCOMPtr<nsIDNSListener> mListener;
     PRUint16                 mFlags;
     PRUint16                 mAF;
 };
 
@@ -305,16 +309,22 @@ nsDNSAsyncRequest::OnLookupComplete(nsHo
     mListener->OnLookupComplete(this, rec, status);
     mListener = nsnull;
 
     // release the reference to ourselves that was added before we were
     // handed off to the host resolver.
     NS_RELEASE_THIS();
 }
 
+bool
+nsDNSAsyncRequest::EqualsAsyncListener(nsIDNSListener *aListener)
+{
+    return (aListener == mListener);
+}
+
 NS_IMPL_THREADSAFE_ISUPPORTS1(nsDNSAsyncRequest, nsICancelable)
 
 NS_IMETHODIMP
 nsDNSAsyncRequest::Cancel(nsresult reason)
 {
     NS_ENSURE_ARG(NS_FAILED(reason));
     mResolver->DetachCallback(mHost.get(), mFlags, mAF, this, reason);
     return NS_OK;
@@ -327,16 +337,17 @@ class nsDNSSyncRequest : public nsResolv
 public:
     nsDNSSyncRequest(PRMonitor *mon)
         : mDone(false)
         , mStatus(NS_OK)
         , mMonitor(mon) {}
     virtual ~nsDNSSyncRequest() {}
 
     void OnLookupComplete(nsHostResolver *, nsHostRecord *, nsresult);
+    bool EqualsAsyncListener(nsIDNSListener *aListener);
 
     bool                   mDone;
     nsresult               mStatus;
     nsRefPtr<nsHostRecord> mHostRecord;
 
 private:
     PRMonitor             *mMonitor;
 };
@@ -350,16 +361,23 @@ nsDNSSyncRequest::OnLookupComplete(nsHos
     PR_EnterMonitor(mMonitor);
     mDone = true;
     mStatus = status;
     mHostRecord = hostRecord;
     PR_Notify(mMonitor);
     PR_ExitMonitor(mMonitor);
 }
 
+bool
+nsDNSSyncRequest::EqualsAsyncListener(nsIDNSListener *aListener)
+{
+    // Sync request: no listener to compare
+    return false;
+}
+
 //-----------------------------------------------------------------------------
 
 nsDNSService::nsDNSService()
     : mLock("nsDNSServer.mLock")
     , mFirstTime(true)
 {
 }
 
@@ -580,16 +598,52 @@ nsDNSService::AsyncResolve(const nsACStr
     if (NS_FAILED(rv)) {
         NS_RELEASE(req);
         NS_RELEASE(*result);
     }
     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,
                       nsIDNSRecord    **result)
 {
     // grab reference to global host resolver and IDN service.  beware
     // simultaneous shutdown!!
     nsRefPtr<nsHostResolver> res;
     nsCOMPtr<nsIIDNService> idn;
--- a/netwerk/dns/nsDNSService2.h
+++ b/netwerk/dns/nsDNSService2.h
@@ -33,16 +33,17 @@
  * the terms of any one of the MPL, the GPL or the LGPL.
  *
  * ***** END LICENSE BLOCK ***** */
 
 #include "nsPIDNSService.h"
 #include "nsIIDNService.h"
 #include "nsIObserver.h"
 #include "nsHostResolver.h"
+#include "nsICancelable.h"
 #include "nsAutoPtr.h"
 #include "nsString.h"
 #include "mozilla/Mutex.h"
 
 class nsDNSService : public nsPIDNSService
                    , public nsIObserver
 {
 public:
--- a/netwerk/dns/nsHostResolver.cpp
+++ b/netwerk/dns/nsHostResolver.cpp
@@ -886,16 +886,60 @@ nsHostResolver::OnLookupComplete(nsHostR
             callback->OnLookupComplete(this, rec, status);
             // NOTE: callback must not be dereferenced after this point!!
         }
     }
 
     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
 nsHostResolver::ThreadFunc(void *arg)
 {
     LOG(("nsHostResolver::ThreadFunc entering\n"));
 #if defined(RES_RETRY_ON_FAILURE)
     nsResState rs;
--- a/netwerk/dns/nsHostResolver.h
+++ b/netwerk/dns/nsHostResolver.h
@@ -41,16 +41,17 @@
 #include "nscore.h"
 #include "nsAtomicRefcnt.h"
 #include "prclist.h"
 #include "prnetdb.h"
 #include "pldhash.h"
 #include "mozilla/CondVar.h"
 #include "mozilla/Mutex.h"
 #include "nsISupportsImpl.h"
+#include "nsIDNSListener.h"
 #include "nsString.h"
 #include "nsTArray.h"
 
 class nsHostResolver;
 class nsHostRecord;
 class nsResolveHostCallback;
 
 /* XXX move this someplace more generic */
@@ -186,16 +187,30 @@ public:
      * @param record
      *        the host record containing the results of the lookup
      * @param status
      *        if successful, |record| contains non-null results
      */
     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;
 };
 
 /**
  * nsHostResolver - an asynchronous host name resolver.
  */
 class nsHostResolver
 {
     typedef mozilla::CondVar CondVar;
@@ -240,16 +255,28 @@ public:
      */
     void DetachCallback(const char            *hostname,
                         PRUint16               flags,
                         PRUint16               af,
                         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.
      *
      * NOTE: in this implementation, these flags correspond exactly in value
      *       to the flags defined on nsIDNSService.
      */
     enum {
         RES_BYPASS_CACHE = 1 << 0,
--- a/netwerk/dns/nsIDNSService.idl
+++ b/netwerk/dns/nsIDNSService.idl
@@ -41,17 +41,17 @@
 interface nsICancelable;
 interface nsIEventTarget;
 interface nsIDNSRecord;
 interface nsIDNSListener;
 
 /**
  * nsIDNSService
  */
-[scriptable, uuid(c1a56a45-8fa3-44e6-9f01-38c91c858cf9)]
+[scriptable, uuid(F6E05CC3-8A13-463D-877F-D59B20B59724)]
 interface nsIDNSService : nsISupports
 {
     /**
      * kicks off an asynchronous host lookup.
      *
      * @param aHostName
      *        the hostname or IP-address-literal to resolve.
      * @param aFlags
@@ -68,16 +68,36 @@ interface nsIDNSService : nsISupports
      * @return An object that can be used to cancel the host lookup.
      */
     nsICancelable asyncResolve(in AUTF8String       aHostName,
                                in unsigned long     aFlags,
                                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
      * unwise to call this function on the UI thread of an application.
      *
      * @param aHostName
      *        the hostname or IP-address-literal to resolve.
      * @param aFlags
      *        a bitwise OR of the RESOLVE_ prefixed constants defined below.
--- a/netwerk/ipc/NeckoParent.cpp
+++ b/netwerk/ipc/NeckoParent.cpp
@@ -145,10 +145,20 @@ bool
 NeckoParent::RecvHTMLDNSPrefetch(const nsString& hostname,
                                  const PRUint16& flags)
 {
   nsAutoString h(hostname);
   nsHTMLDNSPrefetch::Prefetch(h, flags);
   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
 
--- a/netwerk/ipc/NeckoParent.h
+++ b/netwerk/ipc/NeckoParent.h
@@ -63,14 +63,18 @@ protected:
   virtual PWyciwygChannelParent* AllocPWyciwygChannel();
   virtual bool DeallocPWyciwygChannel(PWyciwygChannelParent*);
   virtual PFTPChannelParent* AllocPFTPChannel();
   virtual bool DeallocPFTPChannel(PFTPChannelParent*);
   virtual PWebSocketParent* AllocPWebSocket(PBrowserParent* browser);
   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
 } // namespace mozilla
 
 #endif // mozilla_net_NeckoParent_h
--- a/netwerk/ipc/PNecko.ipdl
+++ b/netwerk/ipc/PNecko.ipdl
@@ -64,16 +64,17 @@ parent:
   __delete__();
 
   PCookieService();
   PWyciwygChannel();
   PFTPChannel();
   PWebSocket(PBrowser browser);
 
   HTMLDNSPrefetch(nsString hostname, PRUint16 flags);
+  CancelHTMLDNSPrefetch(nsString hostname, PRUint16 flags, nsresult reason);
 
 both:
   PHttpChannel(nullable PBrowser browser);
 };
 
 
 } // namespace net
 } // namespace mozilla