Bug 945066: Make nsIDNSService work on child processes. r=jdm
☠☠ backed out by beb3f7812a1e ☠ ☠
authorJason Duell <jduell.mcbugs@gmail.com>
Wed, 11 Dec 2013 01:37:57 -0800
changeset 159872 3072c9af15b309f1814d6e55f6a7768134c1eeb2
parent 159871 32ea6e1e7950276e46aeec22fc74f448e2e2dc6e
child 159873 e4ba261ab3d030e9bbfa5e4b161a49a93c5e3d6b
push id1
push userroot
push dateMon, 20 Oct 2014 17:29:22 +0000
reviewersjdm
bugs945066
milestone29.0a1
Bug 945066: Make nsIDNSService work on child processes. r=jdm
netwerk/build/nsNetModule.cpp
netwerk/dns/ChildDNSService.cpp
netwerk/dns/ChildDNSService.h
netwerk/dns/DNS.cpp
netwerk/dns/DNS.h
netwerk/dns/DNSListenerProxy.cpp
netwerk/dns/DNSListenerProxy.h
netwerk/dns/DNSRequestChild.cpp
netwerk/dns/DNSRequestChild.h
netwerk/dns/DNSRequestParent.cpp
netwerk/dns/DNSRequestParent.h
netwerk/dns/PDNSParams.h
netwerk/dns/PDNSRequest.ipdl
netwerk/dns/PDNSRequestParams.ipdlh
netwerk/dns/moz.build
netwerk/dns/nsDNSService2.cpp
netwerk/dns/nsDNSService2.h
netwerk/dns/nsEffectiveTLDService.h
netwerk/ipc/NeckoChild.cpp
netwerk/ipc/NeckoChild.h
netwerk/ipc/NeckoParent.cpp
netwerk/ipc/NeckoParent.h
netwerk/ipc/PNecko.ipdl
netwerk/test/unit_ipc/test_dns_service_wrap.js
netwerk/test/unit_ipc/xpcshell.ini
--- a/netwerk/build/nsNetModule.cpp
+++ b/netwerk/build/nsNetModule.cpp
@@ -1,9 +1,10 @@
-/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* vim: set sw=4 ts=8 et tw=80 : */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "necko-config.h"
 
 #define ALLOW_LATE_HTTPLOG_H_INCLUDE 1
 #include "base/basictypes.h"
@@ -52,18 +53,19 @@ NS_HIDDEN_(ContentSnifferCache*) gNetSni
 NS_HIDDEN_(ContentSnifferCache*) gDataSniffers = nullptr;
 
 ///////////////////////////////////////////////////////////////////////////////
 
 #include "nsIOService.h"
 NS_GENERIC_FACTORY_SINGLETON_CONSTRUCTOR(nsIOService, nsIOService::GetInstance)
 
 #include "nsDNSService2.h"
-NS_GENERIC_FACTORY_CONSTRUCTOR_INIT(nsDNSService, Init)
-  
+NS_GENERIC_FACTORY_SINGLETON_CONSTRUCTOR(nsIDNSService,
+  nsDNSService::GetXPCOMSingleton)
+
 #include "nsProtocolProxyService.h"
 NS_GENERIC_FACTORY_CONSTRUCTOR_INIT(nsProtocolProxyService, Init)
 
 #include "nsStreamTransportService.h"
 NS_GENERIC_FACTORY_CONSTRUCTOR_INIT(nsStreamTransportService, Init)
 
 #include "nsSocketTransportService2.h"
 #undef LOG
@@ -820,17 +822,17 @@ NS_DEFINE_NAMED_CID(NS_NETWORKSEER_CID);
 
 static const mozilla::Module::CIDEntry kNeckoCIDs[] = {
     { &kNS_IOSERVICE_CID, false, nullptr, nsIOServiceConstructor },
     { &kNS_STREAMTRANSPORTSERVICE_CID, false, nullptr, nsStreamTransportServiceConstructor },
     { &kNS_SOCKETTRANSPORTSERVICE_CID, false, nullptr, nsSocketTransportServiceConstructor },
     { &kNS_SERVERSOCKET_CID, false, nullptr, nsServerSocketConstructor },
     { &kNS_UDPSOCKET_CID, false, nullptr, nsUDPSocketConstructor },
     { &kNS_SOCKETPROVIDERSERVICE_CID, false, nullptr, nsSocketProviderService::Create },
-    { &kNS_DNSSERVICE_CID, false, nullptr, nsDNSServiceConstructor },
+    { &kNS_DNSSERVICE_CID, false, nullptr, nsIDNSServiceConstructor },
     { &kNS_IDNSERVICE_CID, false, nullptr, nsIDNServiceConstructor },
     { &kNS_EFFECTIVETLDSERVICE_CID, false, nullptr, nsEffectiveTLDServiceConstructor },
     { &kNS_SIMPLEURI_CID, false, nullptr, nsSimpleURIConstructor },
     { &kNS_SIMPLENESTEDURI_CID, false, nullptr, nsSimpleNestedURIConstructor },
     { &kNS_ASYNCSTREAMCOPIER_CID, false, nullptr, nsAsyncStreamCopierConstructor },
     { &kNS_INPUTSTREAMPUMP_CID, false, nullptr, nsInputStreamPumpConstructor },
     { &kNS_INPUTSTREAMCHANNEL_CID, false, nullptr, nsInputStreamChannelConstructor },
     { &kNS_STREAMLOADER_CID, false, nullptr, nsStreamLoader::Create },
new file mode 100644
--- /dev/null
+++ b/netwerk/dns/ChildDNSService.cpp
@@ -0,0 +1,231 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "mozilla/net/ChildDNSService.h"
+#include "nsIDNSListener.h"
+#include "nsNetUtil.h"
+#include "nsIThread.h"
+#include "nsThreadUtils.h"
+#include "nsIXPConnect.h"
+#include "nsIPrefService.h"
+#include "nsIProtocolProxyService.h"
+#include "mozilla/net/NeckoChild.h"
+#include "mozilla/net/DNSRequestChild.h"
+#include "mozilla/net/DNSListenerProxy.h"
+
+namespace mozilla {
+namespace net {
+
+//-----------------------------------------------------------------------------
+// ChildDNSService
+//-----------------------------------------------------------------------------
+
+static ChildDNSService *gChildDNSService;
+static const char kPrefNameDisablePrefetch[] = "network.dns.disablePrefetch";
+
+ChildDNSService* ChildDNSService::GetSingleton()
+{
+  MOZ_ASSERT(IsNeckoChild());
+
+  if (!gChildDNSService) {
+    gChildDNSService = new ChildDNSService();
+  }
+
+  NS_ADDREF(gChildDNSService);
+  return gChildDNSService;
+}
+
+NS_IMPL_ISUPPORTS3(ChildDNSService,
+                   nsIDNSService,
+                   nsPIDNSService,
+                   nsIObserver)
+
+ChildDNSService::ChildDNSService()
+  : mFirstTime(true)
+  , mOffline(false)
+{
+  MOZ_ASSERT(IsNeckoChild());
+}
+
+ChildDNSService::~ChildDNSService()
+{
+
+}
+
+//-----------------------------------------------------------------------------
+// ChildDNSService::nsIDNSService
+//-----------------------------------------------------------------------------
+
+NS_IMETHODIMP
+ChildDNSService::AsyncResolve(const nsACString  &hostname,
+                              uint32_t           flags,
+                              nsIDNSListener    *listener,
+                              nsIEventTarget    *target_,
+                              nsICancelable    **result)
+{
+  NS_ENSURE_TRUE(gNeckoChild != nullptr, NS_ERROR_FAILURE);
+
+  if (mDisablePrefetch && (flags & RESOLVE_SPECULATE)) {
+    return NS_ERROR_DNS_LOOKUP_QUEUE_FULL;
+  }
+
+  // Support apps being 'offline' even if parent is not: avoids DNS traffic by
+  // apps that have been told they are offline.
+  if (mOffline) {
+    flags |= RESOLVE_OFFLINE;
+  }
+
+  // make sure JS callers get notification on the main thread
+  nsCOMPtr<nsIEventTarget> target = target_;
+  nsCOMPtr<nsIXPConnectWrappedJS> wrappedListener = do_QueryInterface(listener);
+  if (wrappedListener && !target) {
+    nsCOMPtr<nsIThread> mainThread;
+    NS_GetMainThread(getter_AddRefs(mainThread));
+    target = do_QueryInterface(mainThread);
+  }
+  if (target) {
+    // Guarantee listener freed on main thread.  Not sure we need this in child
+    // (or in parent in nsDNSService.cpp) but doesn't hurt.
+    listener = new DNSListenerProxy(listener, target);
+  }
+
+  nsRefPtr<DNSRequestChild> childReq =
+    new DNSRequestChild(nsCString(hostname), flags, listener, target);
+
+  childReq->StartRequest();
+
+  childReq.forget(result);
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+ChildDNSService::CancelAsyncResolve(const nsACString  &aHostname,
+                                    uint32_t           aFlags,
+                                    nsIDNSListener    *aListener,
+                                    nsresult           aReason)
+{
+  if (mDisablePrefetch && (aFlags & RESOLVE_SPECULATE)) {
+    return NS_ERROR_DNS_LOOKUP_QUEUE_FULL;
+  }
+
+  // TODO: keep a hashtable of pending requests, so we can obey cancel semantics
+  // (call OnLookupComplete with aReason).  Also possible we could send IPDL to
+  // parent to cancel.
+  return NS_ERROR_NOT_AVAILABLE;
+}
+
+NS_IMETHODIMP
+ChildDNSService::Resolve(const nsACString &hostname,
+                         uint32_t          flags,
+                         nsIDNSRecord    **result)
+{
+  // not planning to ever support this, since sync IPDL is evil.
+  return NS_ERROR_NOT_AVAILABLE;
+}
+
+NS_IMETHODIMP
+ChildDNSService::GetDNSCacheEntries(nsTArray<mozilla::net::DNSCacheEntries> *args)
+{
+  // Only used by networking dashboard, so may not ever need this in child.
+  // (and would provide a way to spy on what hosts other apps are connecting to,
+  // unless we start keeping per-app DNS caches).
+  return NS_ERROR_NOT_AVAILABLE;
+}
+
+NS_IMETHODIMP
+ChildDNSService::GetMyHostName(nsACString &result)
+{
+  // TODO: get value from parent during PNecko construction?
+  return NS_ERROR_NOT_AVAILABLE;
+}
+
+//-----------------------------------------------------------------------------
+// ChildDNSService::nsPIDNSService
+//-----------------------------------------------------------------------------
+
+nsresult
+ChildDNSService::Init()
+{
+  // Disable prefetching either by explicit preference or if a manual proxy
+  // is configured
+  bool disablePrefetch = false;
+  int  proxyType = nsIProtocolProxyService::PROXYCONFIG_DIRECT;
+
+  nsCOMPtr<nsIPrefBranch> prefs = do_GetService(NS_PREFSERVICE_CONTRACTID);
+  prefs->GetBoolPref(kPrefNameDisablePrefetch, &disablePrefetch);
+  if (prefs) {
+    prefs->GetIntPref("network.proxy.type", &proxyType);
+    prefs->GetBoolPref(kPrefNameDisablePrefetch, &disablePrefetch);
+  }
+
+  if (mFirstTime) {
+    mFirstTime = false;
+    if (prefs) {
+      prefs->AddObserver(kPrefNameDisablePrefetch, this, false);
+
+      // Monitor these to see if there is a change in proxy configuration
+      // If a manual proxy is in use, disable prefetch implicitly
+      prefs->AddObserver("network.proxy.type", this, false);
+    }
+  }
+
+  mDisablePrefetch = disablePrefetch ||
+                     (proxyType == nsIProtocolProxyService::PROXYCONFIG_MANUAL);
+
+  return NS_OK;
+}
+
+nsresult
+ChildDNSService::Shutdown()
+{
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+ChildDNSService::GetPrefetchEnabled(bool *outVal)
+{
+  *outVal = !mDisablePrefetch;
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+ChildDNSService::SetPrefetchEnabled(bool inVal)
+{
+  mDisablePrefetch = !inVal;
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+ChildDNSService::GetOffline(bool* aResult)
+{
+  *aResult = mOffline;
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+ChildDNSService::SetOffline(bool value)
+{
+  mOffline = value;
+  return NS_OK;
+}
+
+//-----------------------------------------------------------------------------
+// ChildDNSService::nsIObserver
+//-----------------------------------------------------------------------------
+
+NS_IMETHODIMP
+ChildDNSService::Observe(nsISupports *subject, const char *topic,
+                         const PRUnichar *data)
+{
+  // we are only getting called if a preference has changed.
+  NS_ASSERTION(strcmp(topic, NS_PREFBRANCH_PREFCHANGE_TOPIC_ID) == 0,
+               "unexpected observe call");
+
+  // Reread prefs
+  Init();
+  return NS_OK;
+}
+
+} // namespace net
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/netwerk/dns/ChildDNSService.h
@@ -0,0 +1,42 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set sw=2 ts=8 et tw=80 : */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_net_ChildDNSService_h
+#define mozilla_net_ChildDNSService_h
+
+
+#include "nsPIDNSService.h"
+#include "nsIObserver.h"
+#include "mozilla/Attributes.h"
+
+namespace mozilla {
+namespace net {
+
+class ChildDNSService MOZ_FINAL
+  : public nsPIDNSService
+  , public nsIObserver
+{
+public:
+  // AsyncResolve (and CancelAsyncResolve) can be called off-main
+  NS_DECL_THREADSAFE_ISUPPORTS
+  NS_DECL_NSPIDNSSERVICE
+  NS_DECL_NSIDNSSERVICE
+  NS_DECL_NSIOBSERVER
+
+  ChildDNSService();
+  virtual ~ChildDNSService();
+
+  static ChildDNSService* GetSingleton();
+
+private:
+  bool mFirstTime;
+  bool mOffline;
+  bool mDisablePrefetch;
+};
+
+} // namespace net
+} // namespace mozilla
+#endif // mozilla_net_ChildDNSService_h
--- a/netwerk/dns/DNS.cpp
+++ b/netwerk/dns/DNS.cpp
@@ -1,16 +1,17 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "mozilla/net/DNS.h"
 
 #include "mozilla/Assertions.h"
 #include "mozilla/mozalloc.h"
+#include "mozilla/ArrayUtils.h"
 #include <string.h>
 
 #ifdef XP_WIN
 #include "ws2tcpip.h"
 #endif
 
 namespace mozilla {
 namespace net {
@@ -198,16 +199,41 @@ bool IsIPAddrLocal(const NetAddr *addr)
         addr16 >> 6 == 0xfe80 >> 6) { // fe80::/10 Link Local Address.
       return true;
     }
   }
   // Not an IPv4/6 local address.
   return false;
 }
 
+bool
+NetAddr::operator == (const NetAddr& other) const
+{
+  if (this->raw.family != other.raw.family) {
+    return false;
+  } else if (this->raw.family == AF_INET) {
+    return (this->inet.port == other.inet.port) &&
+           (this->inet.ip == other.inet.ip);
+  } else if (this->raw.family == AF_INET6) {
+    return (this->inet6.port == other.inet6.port) &&
+           (this->inet6.flowinfo == other.inet6.flowinfo) &&
+           (memcmp(&this->inet6.ip, &other.inet6.ip,
+                   sizeof(this->inet6.ip)) == 0) &&
+           (this->inet6.scope_id == other.inet6.scope_id);
+#if defined(XP_UNIX) || defined(XP_OS2)
+  } else if (this->raw.family == AF_LOCAL) {
+    return PL_strncmp(this->local.path, other.local.path,
+                      ArrayLength(this->local.path));
+#endif
+  }
+  return false;
+}
+
+
+
 NetAddrElement::NetAddrElement(const PRNetAddr *prNetAddr)
 {
   PRNetAddrToNetAddr(prNetAddr, &mAddress);
 }
 
 NetAddrElement::NetAddrElement(const NetAddrElement& netAddr)
 {
   mAddress = netAddr.mAddress;
--- a/netwerk/dns/DNS.h
+++ b/netwerk/dns/DNS.h
@@ -5,16 +5,17 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef DNS_h_
 #define DNS_h_
 
 #include "nscore.h"
 #include "prio.h"
 #include "prnetdb.h"
+#include "plstr.h"
 #include "mozilla/LinkedList.h"
 #include "mozilla/MemoryReporting.h"
 
 #if !defined(XP_WIN) && !defined(XP_OS2)
 #include <arpa/inet.h>
 #endif
 
 #ifdef XP_WIN
@@ -107,16 +108,18 @@ union NetAddr {
     uint16_t family;                /* address family (AF_UNIX) */
 #ifdef XP_OS2
     char path[108];                 /* null-terminated pathname */
 #else
     char path[104];                 /* null-terminated pathname */
 #endif
   } local;
 #endif
+  // introduced to support nsTArray<NetAddr> (for DNSRequestParent.cpp)
+  bool operator == (const NetAddr& other) const;
 };
 
 // This class wraps a NetAddr union to provide C++ linked list
 // capabilities and other methods. It is created from a PRNetAddr,
 // which is converted to a mozilla::dns::NetAddr.
 class NetAddrElement : public LinkedListElement<NetAddrElement> {
 public:
   NetAddrElement(const PRNetAddr *prNetAddr);
new file mode 100644
--- /dev/null
+++ b/netwerk/dns/DNSListenerProxy.cpp
@@ -0,0 +1,36 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* vim: set sw=4 ts=8 et tw=80 : */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "mozilla/net/DNSListenerProxy.h"
+#include "nsICancelable.h"
+#include "nsIEventTarget.h"
+
+namespace mozilla {
+namespace net {
+
+NS_IMPL_ISUPPORTS1(DNSListenerProxy, nsIDNSListener)
+
+NS_IMETHODIMP
+DNSListenerProxy::OnLookupComplete(nsICancelable* aRequest,
+                                   nsIDNSRecord* aRecord,
+                                   nsresult aStatus)
+{
+  nsRefPtr<OnLookupCompleteRunnable> r =
+    new OnLookupCompleteRunnable(mListener, aRequest, aRecord, aStatus);
+  return mTargetThread->Dispatch(r, NS_DISPATCH_NORMAL);
+}
+
+NS_IMETHODIMP
+DNSListenerProxy::OnLookupCompleteRunnable::Run()
+{
+  mListener->OnLookupComplete(mRequest, mRecord, mStatus);
+  return NS_OK;
+}
+
+
+
+} // namespace net
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/netwerk/dns/DNSListenerProxy.h
@@ -0,0 +1,66 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* vim: set sw=4 ts=8 et tw=80 : */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef DNSListenerProxy_h__
+#define DNSListenerProxy_h__
+
+#include "nsIDNSListener.h"
+#include "nsIDNSRecord.h"
+#include "nsProxyRelease.h"
+#include "nsThreadUtils.h"
+
+class nsIEventTarget;
+class nsICancelable;
+
+namespace mozilla {
+namespace net {
+
+class DNSListenerProxy MOZ_FINAL : public nsIDNSListener
+{
+public:
+  DNSListenerProxy(nsIDNSListener* aListener, nsIEventTarget* aTargetThread)
+    // Sometimes aListener is a main-thread only object like XPCWrappedJS, and
+    // sometimes it's a threadsafe object like nsSOCKSSocketInfo. Use a main-
+    // thread pointer holder, but disable strict enforcement of thread invariants.
+    // The AddRef implementation of XPCWrappedJS will assert if we go wrong here.
+    : mListener(new nsMainThreadPtrHolder<nsIDNSListener>(aListener, false))
+    , mTargetThread(aTargetThread)
+  { }
+
+  NS_DECL_THREADSAFE_ISUPPORTS
+  NS_DECL_NSIDNSLISTENER
+
+  class OnLookupCompleteRunnable : public nsRunnable
+  {
+  public:
+    OnLookupCompleteRunnable(const nsMainThreadPtrHandle<nsIDNSListener>& aListener,
+                             nsICancelable* aRequest,
+                             nsIDNSRecord* aRecord,
+                             nsresult aStatus)
+      : mListener(aListener)
+      , mRequest(aRequest)
+      , mRecord(aRecord)
+      , mStatus(aStatus)
+    { }
+
+    NS_DECL_NSIRUNNABLE
+
+  private:
+    nsMainThreadPtrHandle<nsIDNSListener> mListener;
+    nsCOMPtr<nsICancelable> mRequest;
+    nsCOMPtr<nsIDNSRecord> mRecord;
+    nsresult mStatus;
+  };
+
+private:
+  nsMainThreadPtrHandle<nsIDNSListener> mListener;
+  nsCOMPtr<nsIEventTarget> mTargetThread;
+};
+
+
+} // namespace net
+} // namespace mozilla
+#endif // DNSListenerProxy_h__
new file mode 100644
--- /dev/null
+++ b/netwerk/dns/DNSRequestChild.cpp
@@ -0,0 +1,247 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set sw=2 ts=8 et tw=80 : */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "mozilla/net/DNSRequestChild.h"
+#include "mozilla/net/NeckoChild.h"
+#include "nsIDNSRecord.h"
+#include "nsHostResolver.h"
+#include "nsTArray.h"
+#include "nsNetAddr.h"
+#include "nsIThread.h"
+#include "nsThreadUtils.h"
+
+using namespace mozilla::ipc;
+
+namespace mozilla {
+namespace net {
+
+//-----------------------------------------------------------------------------
+// ChildDNSRecord:
+// A simple class to provide nsIDNSRecord on the child
+//-----------------------------------------------------------------------------
+
+class ChildDNSRecord : public nsIDNSRecord
+{
+public:
+  NS_DECL_THREADSAFE_ISUPPORTS
+  NS_DECL_NSIDNSRECORD
+
+  ChildDNSRecord(const DNSRecord& reply, uint16_t flags);
+  virtual ~ChildDNSRecord();
+
+private:
+  nsCString mCanonicalName;
+  nsTArray<NetAddr> mAddresses;
+  uint32_t mCurrent; // addr iterator
+  uint32_t mLength;  // number of addrs
+  uint16_t mFlags;
+};
+
+NS_IMPL_ISUPPORTS1(ChildDNSRecord, nsIDNSRecord)
+
+ChildDNSRecord::ChildDNSRecord(const DNSRecord& reply, uint16_t flags)
+  : mCurrent(0)
+  , mFlags(flags)
+{
+  mCanonicalName = reply.canonicalName();
+
+  // A shame IPDL gives us no way to grab ownership of array: so copy it.
+  const nsTArray<NetAddr>& addrs = reply.addrs();
+  uint32_t i = 0;
+  mLength = addrs.Length();
+  for (; i < mLength; i++) {
+    mAddresses.AppendElement(addrs[i]);
+  }
+}
+
+ChildDNSRecord::~ChildDNSRecord()
+{
+}
+
+//-----------------------------------------------------------------------------
+// ChildDNSRecord::nsIDNSRecord
+//-----------------------------------------------------------------------------
+
+NS_IMETHODIMP
+ChildDNSRecord::GetCanonicalName(nsACString &result)
+{
+  if (!(mFlags & nsHostResolver::RES_CANON_NAME)) {
+    return NS_ERROR_NOT_AVAILABLE;
+  }
+
+  result = mCanonicalName;
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+ChildDNSRecord::GetNextAddr(uint16_t port, NetAddr *addr)
+{
+  if (mCurrent >= mLength) {
+    return NS_ERROR_NOT_AVAILABLE;
+  }
+
+  memcpy(addr, &mAddresses[mCurrent++], sizeof(NetAddr));
+
+  // both Ipv4/6 use same bits for port, so safe to just use ipv4's field
+  addr->inet.port = port;
+
+  return NS_OK;
+}
+
+// shamelessly copied from nsDNSRecord
+NS_IMETHODIMP
+ChildDNSRecord::GetScriptableNextAddr(uint16_t port, nsINetAddr **result)
+{
+  NetAddr addr;
+  nsresult rv = GetNextAddr(port, &addr);
+  if (NS_FAILED(rv)) return rv;
+
+  NS_ADDREF(*result = new nsNetAddr(&addr));
+
+  return NS_OK;
+}
+
+// also copied from nsDNSRecord
+NS_IMETHODIMP
+ChildDNSRecord::GetNextAddrAsString(nsACString &result)
+{
+  NetAddr addr;
+  nsresult rv = GetNextAddr(0, &addr);
+  if (NS_FAILED(rv)) {
+    return rv;
+  }
+
+  char buf[kIPv6CStrBufSize];
+  if (NetAddrToString(&addr, buf, sizeof(buf))) {
+    result.Assign(buf);
+    return NS_OK;
+  }
+  NS_ERROR("NetAddrToString failed unexpectedly");
+  return NS_ERROR_FAILURE; // conversion failed for some reason
+}
+
+NS_IMETHODIMP
+ChildDNSRecord::HasMore(bool *result)
+{
+  *result = mCurrent < mLength;
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+ChildDNSRecord::Rewind()
+{
+  mCurrent = 0;
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+ChildDNSRecord::ReportUnusable(uint16_t aPort)
+{
+  // "We thank you for your feedback" == >/dev/null
+  // TODO: we could send info back to parent.
+  return NS_OK;
+}
+
+//-----------------------------------------------------------------------------
+// DNSRequestChild
+//-----------------------------------------------------------------------------
+
+DNSRequestChild::DNSRequestChild(const nsCString& aHost,
+                                 const uint32_t& aFlags,
+                                 nsIDNSListener *aListener,
+                                 nsIEventTarget *target)
+  : mListener(aListener)
+  , mTarget(target)
+  , mResultStatus(NS_OK)
+  , mHost(aHost)
+  , mFlags(aFlags)
+{
+}
+
+void
+DNSRequestChild::StartRequest()
+{
+  // we can only do IPDL on the main thread
+  if (!NS_IsMainThread()) {
+    NS_DispatchToMainThread(
+      NS_NewRunnableMethod(this, &DNSRequestChild::StartRequest));
+    return;
+  }
+
+  // Send request to Parent process.
+  gNeckoChild->SendPDNSRequestConstructor(this, mHost, mFlags);
+
+  // IPDL holds a reference until IPDL channel gets destroyed
+  AddIPDLReference();
+}
+
+void
+DNSRequestChild::CallOnLookupComplete()
+{
+  MOZ_ASSERT(mListener);
+
+  mListener->OnLookupComplete(this, mResultRecord, mResultStatus);
+}
+
+bool
+DNSRequestChild::Recv__delete__(const DNSRequestResponse& reply)
+{
+  MOZ_ASSERT(mListener);
+
+  switch (reply.type()) {
+  case DNSRequestResponse::TDNSRecord: {
+    mResultRecord = new ChildDNSRecord(reply.get_DNSRecord(), mFlags);
+    break;
+  }
+  case DNSRequestResponse::Tnsresult: {
+    mResultStatus = reply.get_nsresult();
+    break;
+  }
+  default:
+    NS_NOTREACHED("unknown type");
+    return false;
+  }
+
+  MOZ_ASSERT(NS_IsMainThread());
+
+  bool targetIsMain = false;
+  if (!mTarget) {
+    targetIsMain = true;
+  } else {
+    mTarget->IsOnCurrentThread(&targetIsMain);
+  }
+
+  if (targetIsMain) {
+    CallOnLookupComplete();
+  } else {
+    nsCOMPtr<nsIRunnable> event =
+      NS_NewRunnableMethod(this, &DNSRequestChild::CallOnLookupComplete);
+    mTarget->Dispatch(event, NS_DISPATCH_NORMAL);
+  }
+
+  return true;
+}
+
+//-----------------------------------------------------------------------------
+// DNSRequestChild::nsISupports
+//-----------------------------------------------------------------------------
+
+NS_IMPL_ISUPPORTS1(DNSRequestChild,
+                   nsICancelable)
+
+//-----------------------------------------------------------------------------
+// DNSRequestChild::nsICancelable
+//-----------------------------------------------------------------------------
+
+NS_IMETHODIMP
+DNSRequestChild::Cancel(nsresult reason)
+{
+  // for now Cancel is a no-op
+  return NS_OK;
+}
+
+//------------------------------------------------------------------------------
+}} // mozilla::net
new file mode 100644
--- /dev/null
+++ b/netwerk/dns/DNSRequestChild.h
@@ -0,0 +1,58 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set sw=2 ts=8 et tw=80 : */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_net_DNSRequestChild_h
+#define mozilla_net_DNSRequestChild_h
+
+#include "mozilla/net/PDNSRequestChild.h"
+#include "nsICancelable.h"
+#include "nsIDNSRecord.h"
+#include "nsIDNSListener.h"
+#include "nsIEventTarget.h"
+
+namespace mozilla {
+namespace net {
+
+class DNSRequestChild
+  : public PDNSRequestChild
+  , public nsICancelable
+{
+public:
+  NS_DECL_THREADSAFE_ISUPPORTS
+  NS_DECL_NSICANCELABLE
+
+  DNSRequestChild(const nsCString& aHost, const uint32_t& aFlags,
+                  nsIDNSListener *aListener, nsIEventTarget *target);
+  virtual ~DNSRequestChild() {}
+
+  void AddIPDLReference() {
+    AddRef();
+  }
+  void ReleaseIPDLReference() {
+    // we don't need an 'mIPCOpen' variable until/unless we add calls that might
+    // try to send IPDL msgs to parent after ReleaseIPDLReference is called
+    // (when IPDL channel torn down).
+    Release();
+  }
+
+  // Sends IPDL request to parent
+  void StartRequest();
+  void CallOnLookupComplete();
+
+private:
+  virtual bool Recv__delete__(const DNSRequestResponse& reply) MOZ_OVERRIDE;
+
+  nsCOMPtr<nsIDNSListener>  mListener;
+  nsCOMPtr<nsIEventTarget>  mTarget;
+  nsCOMPtr<nsIDNSRecord>    mResultRecord;
+  nsresult                  mResultStatus;
+  nsCString                 mHost;
+  uint16_t                  mFlags;
+};
+
+} // namespace net
+} // namespace mozilla
+#endif // mozilla_net_DNSRequestChild_h
new file mode 100644
--- /dev/null
+++ b/netwerk/dns/DNSRequestParent.cpp
@@ -0,0 +1,105 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set sw=2 ts=8 et tw=80 : */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "mozilla/net/DNSRequestParent.h"
+#include "nsIDNSService.h"
+#include "nsNetCID.h"
+#include "nsThreadUtils.h"
+#include "nsIServiceManager.h"
+#include "nsICancelable.h"
+#include "nsIDNSRecord.h"
+#include "nsHostResolver.h"
+#include "mozilla/unused.h"
+
+using namespace mozilla::ipc;
+
+namespace mozilla {
+namespace net {
+
+DNSRequestParent::DNSRequestParent()
+  : mIPCClosed(false)
+{
+
+}
+
+DNSRequestParent::~DNSRequestParent()
+{
+
+}
+
+void
+DNSRequestParent::DoAsyncResolve(const nsACString &hostname, uint32_t flags)
+{
+  nsresult rv;
+  mFlags = flags;
+  nsCOMPtr<nsIDNSService> dns = do_GetService(NS_DNSSERVICE_CONTRACTID, &rv);
+  if (NS_SUCCEEDED(rv)) {
+    nsCOMPtr<nsIThread> mainThread = do_GetMainThread();
+    nsCOMPtr<nsICancelable> unused;
+    rv = dns->AsyncResolve(hostname, flags, this, mainThread,
+                           getter_AddRefs(unused));
+  }
+
+  if (NS_FAILED(rv) && !mIPCClosed) {
+    unused << Send__delete__(this, DNSRequestResponse(rv));
+  }
+}
+
+void
+DNSRequestParent::ActorDestroy(ActorDestroyReason why)
+{
+  // We may still have refcount>0 if DNS hasn't called our OnLookupComplete
+  // yet, but child process has crashed.  We must not send any more msgs
+  // to child, or IPDL will kill chrome process, too.
+  mIPCClosed = true;
+}
+//-----------------------------------------------------------------------------
+// DNSRequestParent::nsISupports
+//-----------------------------------------------------------------------------
+
+NS_IMPL_ISUPPORTS1(DNSRequestParent,
+                   nsIDNSListener)
+
+//-----------------------------------------------------------------------------
+// nsIDNSListener functions
+//-----------------------------------------------------------------------------
+
+NS_IMETHODIMP
+DNSRequestParent::OnLookupComplete(nsICancelable *request,
+                                   nsIDNSRecord  *rec,
+                                   nsresult       status)
+{
+  if (mIPCClosed) {
+    // nothing to do: child probably crashed
+    return NS_OK;
+  }
+
+  if (NS_SUCCEEDED(status)) {
+    MOZ_ASSERT(rec);
+
+    nsAutoCString cname;
+    if (mFlags & nsHostResolver::RES_CANON_NAME) {
+      rec->GetCanonicalName(cname);
+    }
+
+    // Get IP addresses for hostname (use port 80 as dummy value for NetAddr)
+    NetAddrArray array;
+    NetAddr addr;
+    while (NS_SUCCEEDED(rec->GetNextAddr(80, &addr))) {
+      array.AppendElement(addr);
+    }
+
+    unused << Send__delete__(this, DNSRequestResponse(DNSRecord(cname, array)));
+  } else {
+    unused << Send__delete__(this, DNSRequestResponse(status));
+  }
+
+  return NS_OK;
+}
+
+
+
+}} // mozilla::net
new file mode 100644
--- /dev/null
+++ b/netwerk/dns/DNSRequestParent.h
@@ -0,0 +1,39 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set sw=2 ts=8 et tw=80 : */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_net_DNSRequestParent_h
+#define mozilla_net_DNSRequestParent_h
+
+#include "mozilla/net/PDNSRequestParent.h"
+#include "nsIDNSService.h"
+#include "nsIDNSListener.h"
+
+namespace mozilla {
+namespace net {
+
+class DNSRequestParent
+  : public PDNSRequestParent
+  , public nsIDNSListener
+{
+public:
+  NS_DECL_ISUPPORTS
+  NS_DECL_NSIDNSLISTENER
+
+  DNSRequestParent();
+  virtual ~DNSRequestParent();
+
+  void DoAsyncResolve(const nsACString  &hostname, uint32_t flags);
+
+protected:
+  virtual void ActorDestroy(ActorDestroyReason why);
+private:
+  uint32_t mFlags;
+  bool mIPCClosed;  // true if IPDL channel has been closed (child crash)
+};
+
+} // namespace net
+} // namespace mozilla
+#endif // mozilla_net_DNSRequestParent_h
new file mode 100644
--- /dev/null
+++ b/netwerk/dns/PDNSParams.h
@@ -0,0 +1,23 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set sw=2 ts=8 et tw=80 ft=c: */
+
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef PDNSParams_h
+#define PDNSParams_h
+
+#include "DNS.h"
+#include "nsTArray.h"
+
+namespace mozilla {
+namespace net {
+
+// Need to define typedef in .h file--can't seem to in ipdl.h file?
+typedef nsTArray<NetAddr> NetAddrArray;
+
+} // namespace net
+} // namespace mozilla
+
+#endif // PDNSParams_h
new file mode 100644
--- /dev/null
+++ b/netwerk/dns/PDNSRequest.ipdl
@@ -0,0 +1,31 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set sw=2 ts=8 et tw=80 ft=cpp : */
+
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+include protocol PNecko;
+
+include PDNSRequestParams;
+
+include "mozilla/net/NeckoMessageUtils.h";
+
+namespace mozilla {
+namespace net {
+
+async protocol PDNSRequest
+{
+  manager PNecko;
+
+//parent:
+  // constructor in PNecko takes AsyncResolve args that initialize request
+
+
+child:
+  __delete__(DNSRequestResponse reply);
+
+};
+
+} //namespace net
+} //namespace mozilla
new file mode 100644
--- /dev/null
+++ b/netwerk/dns/PDNSRequestParams.ipdlh
@@ -0,0 +1,31 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set sw=2 ts=8 et tw=80 ft=cpp : */
+
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+using NetAddrArray from "mozilla/net/PDNSParams.h";
+
+namespace mozilla {
+namespace net {
+
+//-----------------------------------------------------------------------------
+// DNS IPDL structs
+//-----------------------------------------------------------------------------
+
+struct DNSRecord
+{
+  nsCString canonicalName;
+  NetAddrArray addrs;
+};
+
+union DNSRequestResponse
+{
+  DNSRecord;
+  nsresult;   // if error
+};
+
+
+} // namespace ipc
+} // namespace mozilla
--- a/netwerk/dns/moz.build
+++ b/netwerk/dns/moz.build
@@ -11,37 +11,53 @@ XPIDL_SOURCES += [
     'nsIEffectiveTLDService.idl',
     'nsIIDNService.idl',
     'nsPIDNSService.idl',
 ]
 
 XPIDL_MODULE = 'necko_dns'
 
 EXPORTS.mozilla.net += [
+    'ChildDNSService.h',
     'DNS.h',
+    'DNSListenerProxy.h',
+    'DNSRequestChild.h',
+    'DNSRequestParent.h',
+    'PDNSParams.h',
 ]
 
 SOURCES += [
     'nsEffectiveTLDService.cpp', # Excluded from UNIFIED_SOURCES due to special build flags.
     'nsHostResolver.cpp',        # Excluded from UNIFIED_SOURCES due to NSPR forced logging.
 ]
 
 UNIFIED_SOURCES += [
+    'ChildDNSService.cpp',
     'DNS.cpp',
+    'DNSListenerProxy.cpp',
+    'DNSRequestChild.cpp',
+    'DNSRequestParent.cpp',
     'nameprep.c',
     'nsDNSService2.cpp',
     'nsIDNService.cpp',
     'punycode.c',
     'race.c',
 ]
 
+IPDL_SOURCES = [
+    'PDNSRequest.ipdl',
+    'PDNSRequestParams.ipdlh',
+]
+
 FAIL_ON_WARNINGS = True
 
 MSVC_ENABLE_PGO = True
 
+include('/ipc/chromium/chromium-config.mozbuild')
+
 FINAL_LIBRARY = 'necko'
 
 GENERATED_FILES = [
     'etld_data.inc',
 ]
 
 # need to include etld_data.inc
 LOCAL_INCLUDES += [
--- a/netwerk/dns/nsDNSService2.cpp
+++ b/netwerk/dns/nsDNSService2.cpp
@@ -1,9 +1,10 @@
-/* vim:set ts=4 sw=4 sts=4 et cin: */
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* vim: set sw=4 ts=8 et tw=80 : */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "nsDNSService2.h"
 #include "nsIDNSRecord.h"
 #include "nsIDNSListener.h"
 #include "nsICancelable.h"
@@ -28,16 +29,19 @@
 #include "nsIOService.h"
 #include "nsCharSeparatedTokenizer.h"
 #include "nsNetAddr.h"
 #include "nsProxyRelease.h"
 #include "nsIObserverService.h"
 
 #include "mozilla/Attributes.h"
 #include "mozilla/VisualEventTracer.h"
+#include "mozilla/net/NeckoCommon.h"
+#include "mozilla/net/ChildDNSService.h"
+#include "mozilla/net/DNSListenerProxy.h"
 
 using namespace mozilla;
 using namespace mozilla::net;
 
 static const char kPrefDnsCacheEntries[]    = "network.dnsCacheEntries";
 static const char kPrefDnsCacheExpiration[] = "network.dnsCacheExpiration";
 static const char kPrefDnsCacheGrace[]      = "network.dnsCacheExpirationGracePeriod";
 static const char kPrefIPv4OnlyDomains[]    = "network.dns.ipv4OnlyDomains";
@@ -405,16 +409,53 @@ nsDNSService::nsDNSService()
 
 nsDNSService::~nsDNSService()
 {
 }
 
 NS_IMPL_ISUPPORTS_INHERITED3(nsDNSService, MemoryUniReporter, nsIDNSService,
                              nsPIDNSService, nsIObserver)
 
+/******************************************************************************
+ * nsDNSService impl:
+ * singleton instance ctor/dtor methods
+ ******************************************************************************/
+static nsDNSService *gDNSService;
+
+nsIDNSService*
+nsDNSService::GetXPCOMSingleton()
+{
+    if (IsNeckoChild()) {
+        return ChildDNSService::GetSingleton();
+    }
+
+    return GetSingleton();
+}
+
+nsDNSService*
+nsDNSService::GetSingleton()
+{
+    NS_ASSERTION(!IsNeckoChild(), "not a parent process");
+
+    if (gDNSService) {
+        NS_ADDREF(gDNSService);
+        return gDNSService;
+    }
+
+    gDNSService = new nsDNSService();
+    if (gDNSService) {
+        NS_ADDREF(gDNSService);
+        if (NS_FAILED(gDNSService->Init())) {
+              NS_RELEASE(gDNSService);
+        }
+    }
+
+    return gDNSService;
+}
+
 NS_IMETHODIMP
 nsDNSService::Init()
 {
     if (mResolver)
         return NS_OK;
     NS_ENSURE_TRUE(!mResolver, NS_ERROR_ALREADY_INITIALIZED);
 
     // prefs
@@ -558,80 +599,16 @@ nsDNSService::GetPrefetchEnabled(bool *o
 
 NS_IMETHODIMP
 nsDNSService::SetPrefetchEnabled(bool inVal)
 {
     mDisablePrefetch = !inVal;
     return NS_OK;
 }
 
-namespace {
-
-class DNSListenerProxy MOZ_FINAL : public nsIDNSListener
-{
-public:
-  DNSListenerProxy(nsIDNSListener* aListener, nsIEventTarget* aTargetThread)
-    // Sometimes aListener is a main-thread only object like XPCWrappedJS, and
-    // sometimes it's a threadsafe object like nsSOCKSSocketInfo. Use a main-
-    // thread pointer holder, but disable strict enforcement of thread invariants.
-    // The AddRef implementation of XPCWrappedJS will assert if we go wrong here.
-    : mListener(new nsMainThreadPtrHolder<nsIDNSListener>(aListener, false))
-    , mTargetThread(aTargetThread)
-  { }
-
-  NS_DECL_THREADSAFE_ISUPPORTS
-  NS_DECL_NSIDNSLISTENER
-
-  class OnLookupCompleteRunnable : public nsRunnable
-  {
-  public:
-    OnLookupCompleteRunnable(const nsMainThreadPtrHandle<nsIDNSListener>& aListener,
-                             nsICancelable* aRequest,
-                             nsIDNSRecord* aRecord,
-                             nsresult aStatus)
-      : mListener(aListener)
-      , mRequest(aRequest)
-      , mRecord(aRecord)
-      , mStatus(aStatus)
-    { }
-
-    NS_DECL_NSIRUNNABLE
-
-  private:
-    nsMainThreadPtrHandle<nsIDNSListener> mListener;
-    nsCOMPtr<nsICancelable> mRequest;
-    nsCOMPtr<nsIDNSRecord> mRecord;
-    nsresult mStatus;
-  };
-
-private:
-  nsMainThreadPtrHandle<nsIDNSListener> mListener;
-  nsCOMPtr<nsIEventTarget> mTargetThread;
-};
-
-NS_IMPL_ISUPPORTS1(DNSListenerProxy, nsIDNSListener)
-
-NS_IMETHODIMP
-DNSListenerProxy::OnLookupComplete(nsICancelable* aRequest,
-                                   nsIDNSRecord* aRecord,
-                                   nsresult aStatus)
-{
-  nsRefPtr<OnLookupCompleteRunnable> r =
-    new OnLookupCompleteRunnable(mListener, aRequest, aRecord, aStatus);
-  return mTargetThread->Dispatch(r, NS_DISPATCH_NORMAL);
-}
-
-NS_IMETHODIMP
-DNSListenerProxy::OnLookupCompleteRunnable::Run()
-{
-  mListener->OnLookupComplete(mRequest, mRecord, mStatus);
-  return NS_OK;
-}
-
-} // anonymous namespace
 
 NS_IMETHODIMP
 nsDNSService::AsyncResolve(const nsACString  &hostname,
                            uint32_t           flags,
                            nsIDNSListener    *listener,
                            nsIEventTarget    *target_,
                            nsICancelable    **result)
 {
@@ -665,16 +642,17 @@ nsDNSService::AsyncResolve(const nsACStr
 
     nsresult rv;
     nsAutoCString hostACE;
     if (idn && !IsASCII(*hostPtr)) {
         if (NS_SUCCEEDED(idn->ConvertUTF8toACE(*hostPtr, hostACE)))
             hostPtr = &hostACE;
     }
 
+    // make sure JS callers get notification on the main thread
     nsCOMPtr<nsIXPConnectWrappedJS> wrappedListener = do_QueryInterface(listener);
     if (wrappedListener && !target) {
         nsCOMPtr<nsIThread> mainThread;
         NS_GetMainThread(getter_AddRefs(mainThread));
         target = do_QueryInterface(mainThread);
     }
 
     if (target) {
--- a/netwerk/dns/nsDNSService2.h
+++ b/netwerk/dns/nsDNSService2.h
@@ -1,8 +1,10 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* vim: set sw=4 ts=8 et tw=80 : */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef nsDNSService2_h__
 #define nsDNSService2_h__
 
 #include "nsPIDNSService.h"
@@ -25,23 +27,27 @@ public:
     NS_DECL_THREADSAFE_ISUPPORTS
     NS_DECL_NSPIDNSSERVICE
     NS_DECL_NSIDNSSERVICE
     NS_DECL_NSIOBSERVER
 
     nsDNSService();
     ~nsDNSService();
 
+    static nsIDNSService* GetXPCOMSingleton();
+
     int64_t Amount() MOZ_OVERRIDE
     {
         return SizeOfIncludingThis(MallocSizeOf);
     }
     size_t SizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf) const;
 
 private:
+    static nsDNSService* GetSingleton();
+
     uint16_t GetAFForLookup(const nsACString &host, uint32_t flags);
 
     nsRefPtr<nsHostResolver>  mResolver;
     nsCOMPtr<nsIIDNService>   mIDN;
 
     // mLock protects access to mResolver and mIPv4OnlyDomains
     mozilla::Mutex            mLock;
 
--- a/netwerk/dns/nsEffectiveTLDService.h
+++ b/netwerk/dns/nsEffectiveTLDService.h
@@ -1,13 +1,16 @@
 //* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
+#ifndef EffectiveTLDService_h
+#define EffectiveTLDService_h
+
 #include "nsIEffectiveTLDService.h"
 
 #include "nsIMemoryReporter.h"
 #include "nsTHashtable.h"
 #include "nsString.h"
 #include "nsCOMPtr.h"
 #include "mozilla/Attributes.h"
 #include "mozilla/MemoryReporting.h"
@@ -119,8 +122,10 @@ public:
 private:
   nsresult GetBaseDomainInternal(nsCString &aHostname, int32_t aAdditionalParts, nsACString &aBaseDomain);
   nsresult NormalizeHostname(nsCString &aHostname);
   ~nsEffectiveTLDService();
 
   nsTHashtable<nsDomainEntry> mHash;
   nsCOMPtr<nsIIDNService>     mIDNService;
 };
+
+#endif // EffectiveTLDService_h
--- a/netwerk/ipc/NeckoChild.cpp
+++ b/netwerk/ipc/NeckoChild.cpp
@@ -9,16 +9,17 @@
 #include "nsHttp.h"
 #include "mozilla/net/NeckoChild.h"
 #include "mozilla/dom/ContentChild.h"
 #include "mozilla/net/HttpChannelChild.h"
 #include "mozilla/net/CookieServiceChild.h"
 #include "mozilla/net/WyciwygChannelChild.h"
 #include "mozilla/net/FTPChannelChild.h"
 #include "mozilla/net/WebSocketChannelChild.h"
+#include "mozilla/net/DNSRequestChild.h"
 #include "mozilla/net/RemoteOpenFileChild.h"
 #include "mozilla/dom/network/TCPSocketChild.h"
 #include "mozilla/dom/network/TCPServerSocketChild.h"
 #include "mozilla/dom/network/UDPSocketChild.h"
 #ifdef NECKO_PROTOCOL_rtsp
 #include "mozilla/net/RtspControllerChild.h"
 #endif
 #include "SerializedLoadContext.h"
@@ -225,16 +226,34 @@ bool
 NeckoChild::DeallocPUDPSocketChild(PUDPSocketChild* child)
 {
 
   UDPSocketChild* p = static_cast<UDPSocketChild*>(child);
   p->ReleaseIPDLReference();
   return true;
 }
 
+PDNSRequestChild*
+NeckoChild::AllocPDNSRequestChild(const nsCString& aHost,
+                                  const uint32_t& aFlags)
+{
+  // We don't allocate here: instead we always use IPDL constructor that takes
+  // an existing object
+  NS_NOTREACHED("AllocPDNSRequestChild should not be called on child");
+  return nullptr;
+}
+
+bool
+NeckoChild::DeallocPDNSRequestChild(PDNSRequestChild* aChild)
+{
+  DNSRequestChild *p = static_cast<DNSRequestChild*>(aChild);
+  p->ReleaseIPDLReference();
+  return true;
+}
+
 PRemoteOpenFileChild*
 NeckoChild::AllocPRemoteOpenFileChild(const URIParams&, const OptionalURIParams&)
 {
   // We don't allocate here: instead we always use IPDL constructor that takes
   // an existing RemoteOpenFileChild
   NS_NOTREACHED("AllocPRemoteOpenFileChild should not be called on child");
   return nullptr;
 }
--- a/netwerk/ipc/NeckoChild.h
+++ b/netwerk/ipc/NeckoChild.h
@@ -46,16 +46,19 @@ protected:
   virtual PTCPServerSocketChild* AllocPTCPServerSocketChild(const uint16_t& aLocalPort,
                                                        const uint16_t& aBacklog,
                                                        const nsString& aBinaryType);
   virtual bool DeallocPTCPServerSocketChild(PTCPServerSocketChild*);
   virtual PUDPSocketChild* AllocPUDPSocketChild(const nsCString& aHost,
                                                 const uint16_t& aPort,
                                                 const nsCString& aFilter);
   virtual bool DeallocPUDPSocketChild(PUDPSocketChild*);
+  virtual PDNSRequestChild* AllocPDNSRequestChild(const nsCString& aHost,
+                                                  const uint32_t& aFlags);
+  virtual bool DeallocPDNSRequestChild(PDNSRequestChild*);
   virtual PRemoteOpenFileChild* AllocPRemoteOpenFileChild(const URIParams&,
                                                           const OptionalURIParams&);
   virtual bool DeallocPRemoteOpenFileChild(PRemoteOpenFileChild*);
   virtual PRtspControllerChild* AllocPRtspControllerChild();
   virtual bool DeallocPRtspControllerChild(PRtspControllerChild*);
 };
 
 /**
--- a/netwerk/ipc/NeckoParent.cpp
+++ b/netwerk/ipc/NeckoParent.cpp
@@ -11,16 +11,17 @@
 #include "mozilla/net/HttpChannelParent.h"
 #include "mozilla/net/CookieServiceParent.h"
 #include "mozilla/net/WyciwygChannelParent.h"
 #include "mozilla/net/FTPChannelParent.h"
 #include "mozilla/net/WebSocketChannelParent.h"
 #ifdef NECKO_PROTOCOL_rtsp
 #include "mozilla/net/RtspControllerParent.h"
 #endif
+#include "mozilla/net/DNSRequestParent.h"
 #include "mozilla/net/RemoteOpenFileParent.h"
 #include "mozilla/dom/ContentParent.h"
 #include "mozilla/dom/TabParent.h"
 #include "mozilla/dom/network/TCPSocketParent.h"
 #include "mozilla/dom/network/TCPServerSocketParent.h"
 #include "mozilla/dom/network/UDPSocketParent.h"
 #include "mozilla/ipc/URIUtils.h"
 #include "mozilla/LoadContext.h"
@@ -423,16 +424,42 @@ NeckoParent::RecvPUDPSocketConstructor(P
 bool
 NeckoParent::DeallocPUDPSocketParent(PUDPSocketParent* actor)
 {
   UDPSocketParent* p = static_cast<UDPSocketParent*>(actor);
   p->Release();
   return true;
 }
 
+PDNSRequestParent*
+NeckoParent::AllocPDNSRequestParent(const nsCString& aHost,
+                                    const uint32_t& aFlags)
+{
+  DNSRequestParent *p = new DNSRequestParent();
+  p->AddRef();
+  return p;
+}
+
+bool
+NeckoParent::RecvPDNSRequestConstructor(PDNSRequestParent* aActor,
+                                        const nsCString& aHost,
+                                        const uint32_t& aFlags)
+{
+  static_cast<DNSRequestParent*>(aActor)->DoAsyncResolve(aHost, aFlags);
+  return true;
+}
+
+bool
+NeckoParent::DeallocPDNSRequestParent(PDNSRequestParent* aParent)
+{
+  DNSRequestParent *p = static_cast<DNSRequestParent*>(aParent);
+  p->Release();
+  return true;
+}
+
 PRemoteOpenFileParent*
 NeckoParent::AllocPRemoteOpenFileParent(const URIParams& aURI,
                                         const OptionalURIParams& aAppURI)
 {
   nsCOMPtr<nsIURI> uri = DeserializeURI(aURI);
   nsCOMPtr<nsIFileURL> fileURL = do_QueryInterface(uri);
   if (!fileURL) {
     return nullptr;
--- a/netwerk/ipc/NeckoParent.h
+++ b/netwerk/ipc/NeckoParent.h
@@ -119,16 +119,22 @@ protected:
   virtual PUDPSocketParent* AllocPUDPSocketParent(const nsCString& aHost,
                                                   const uint16_t& aPort,
                                                   const nsCString& aFilter);
   virtual bool RecvPUDPSocketConstructor(PUDPSocketParent*,
                                          const nsCString& aHost,
                                          const uint16_t& aPort,
                                          const nsCString& aFilter);
   virtual bool DeallocPUDPSocketParent(PUDPSocketParent*);
+  virtual PDNSRequestParent* AllocPDNSRequestParent(const nsCString& aHost,
+                                                    const uint32_t& aFlags);
+  virtual bool RecvPDNSRequestConstructor(PDNSRequestParent* actor,
+                                          const nsCString& hostName,
+                                          const uint32_t& flags);
+  virtual bool DeallocPDNSRequestParent(PDNSRequestParent*);
   virtual bool RecvHTMLDNSPrefetch(const nsString& hostname,
                                    const uint16_t& flags);
   virtual bool RecvCancelHTMLDNSPrefetch(const nsString& hostname,
                                          const uint16_t& flags,
                                          const nsresult& reason);
 
   virtual mozilla::ipc::IProtocol*
   CloneProtocol(Channel* aChannel,
--- a/netwerk/ipc/PNecko.ipdl
+++ b/netwerk/ipc/PNecko.ipdl
@@ -11,16 +11,17 @@ include protocol PCookieService;
 include protocol PBrowser;
 include protocol PWyciwygChannel;
 include protocol PFTPChannel;
 include protocol PWebSocket;
 include protocol PTCPSocket;
 include protocol PTCPServerSocket;
 include protocol PUDPSocket;
 include protocol PRemoteOpenFile;
+include protocol PDNSRequest;
 include protocol PBlob; //FIXME: bug #792908
 
 include protocol PRtspController;
 include URIParams;
 include InputStreamParams;
 include NeckoChannelParams;
 
 
@@ -36,16 +37,17 @@ sync protocol PNecko
   manages PHttpChannel;
   manages PCookieService;
   manages PWyciwygChannel;
   manages PFTPChannel;
   manages PWebSocket;
   manages PTCPSocket;
   manages PTCPServerSocket;
   manages PUDPSocket;
+  manages PDNSRequest;
   manages PRemoteOpenFile;
   manages PRtspController;
 
 parent:
   __delete__();
 
   PCookieService();
   PHttpChannel(nullable PBrowser browser,
@@ -54,16 +56,18 @@ parent:
   PWyciwygChannel();
   PFTPChannel(PBrowser browser, SerializedLoadContext loadContext,
               FTPChannelCreationArgs args);
 
   PWebSocket(PBrowser browser, SerializedLoadContext loadContext);
   PTCPServerSocket(uint16_t localPort, uint16_t backlog, nsString binaryType);
   PUDPSocket(nsCString host, uint16_t port, nsCString filter);
 
+  PDNSRequest(nsCString hostName, uint32_t flags);
+
   PRemoteOpenFile(URIParams fileuri, OptionalURIParams appuri);
 
   HTMLDNSPrefetch(nsString hostname, uint16_t flags);
   CancelHTMLDNSPrefetch(nsString hostname, uint16_t flags, nsresult reason);
   PRtspController();
 
 both:
   PTCPSocket();
new file mode 100644
--- /dev/null
+++ b/netwerk/test/unit_ipc/test_dns_service_wrap.js
@@ -0,0 +1,7 @@
+//
+// Run test script in content process instead of chrome (xpcshell's default)
+//
+
+function run_test() {
+  run_test_in_child("../unit/test_dns_service.js");
+}
--- a/netwerk/test/unit_ipc/xpcshell.ini
+++ b/netwerk/test/unit_ipc/xpcshell.ini
@@ -4,16 +4,17 @@ tail =
 support-files = disabled_test_bug528292_wrap.js
 
 [test_bug248970_cookie_wrap.js]
 [test_cacheflags_wrap.js]
 [test_cache_jar_wrap.js]
 [test_channel_close_wrap.js]
 [test_cookie_header_wrap.js]
 [test_cookiejars_wrap.js]
+[test_dns_service_wrap.js]
 [test_duplicate_headers_wrap.js]
 [test_event_sink_wrap.js]
 [test_head_wrap.js]
 [test_headers_wrap.js]
 [test_httpsuspend_wrap.js]
 [test_post_wrap.js]
 [test_progress_wrap.js]
 [test_redirect-caching_canceled_wrap.js]