Bug 622232: Cancel DNS prefetches for HTML Anchor Elems after a tab is closed; r=mcmanus sr=bz
☠☠ backed out by aedc89b90ba5 ☠ ☠
authorSteve Workman <sworkman@mozilla.com>
Tue, 04 Oct 2011 16:22:43 -0700
changeset 79155 0144dca654e9cdbc38730d0b44888675c9cbe99c
parent 79154 7c5f9e6c34c2f5a1cd6d1296ad358e2c835f6a15
child 79156 ce4005246dc96ec22d8210b027467695a2be46d4
push id1
push userroot
push dateMon, 20 Oct 2014 17:29:22 +0000
reviewersmcmanus, bz
bugs622232
milestone10.0a1
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,27 +207,34 @@ 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)
 {
   // 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);
 
+  // Cancel any DNS prefetches
+  if (HasFlag(HTML_ANCHOR_DNS_PREFETCH_REQUESTED)) {
+    nsHTMLDNSPrefetch::CancelPrefetchLow(this, NS_ERROR_ABORT);
+    UnsetFlags(HTML_ANCHOR_DNS_PREFETCH_REQUESTED);
+  }
+    
   nsGenericHTMLElement::UnbindFromTree(aDeep, aNullParent);
 }
 
 bool
 nsHTMLAnchorElement::IsHTMLFocusable(bool aWithMouse,
                                      bool *aIsFocusable, PRInt32 *aTabIndex)
 {
   if (nsGenericHTMLElement::IsHTMLFocusable(aWithMouse, aIsFocusable, aTabIndex)) {
--- 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