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 id25816
push userryanvm@gmail.com
push dateWed, 11 Dec 2013 18:24:33 +0000
treeherdermozilla-central@76551fba7121 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjdm
bugs945066
milestone29.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 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]