michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #include "mozilla/net/ChildDNSService.h" michael@0: #include "nsIDNSListener.h" michael@0: #include "nsNetUtil.h" michael@0: #include "nsIThread.h" michael@0: #include "nsThreadUtils.h" michael@0: #include "nsIXPConnect.h" michael@0: #include "nsIPrefService.h" michael@0: #include "nsIProtocolProxyService.h" michael@0: #include "mozilla/net/NeckoChild.h" michael@0: #include "mozilla/net/DNSRequestChild.h" michael@0: #include "mozilla/net/DNSListenerProxy.h" michael@0: michael@0: namespace mozilla { michael@0: namespace net { michael@0: michael@0: //----------------------------------------------------------------------------- michael@0: // ChildDNSService michael@0: //----------------------------------------------------------------------------- michael@0: michael@0: static ChildDNSService *gChildDNSService; michael@0: static const char kPrefNameDisablePrefetch[] = "network.dns.disablePrefetch"; michael@0: michael@0: ChildDNSService* ChildDNSService::GetSingleton() michael@0: { michael@0: MOZ_ASSERT(IsNeckoChild()); michael@0: michael@0: if (!gChildDNSService) { michael@0: gChildDNSService = new ChildDNSService(); michael@0: } michael@0: michael@0: NS_ADDREF(gChildDNSService); michael@0: return gChildDNSService; michael@0: } michael@0: michael@0: NS_IMPL_ISUPPORTS(ChildDNSService, michael@0: nsIDNSService, michael@0: nsPIDNSService, michael@0: nsIObserver) michael@0: michael@0: ChildDNSService::ChildDNSService() michael@0: : mFirstTime(true) michael@0: , mOffline(false) michael@0: { michael@0: MOZ_ASSERT(IsNeckoChild()); michael@0: } michael@0: michael@0: ChildDNSService::~ChildDNSService() michael@0: { michael@0: michael@0: } michael@0: michael@0: //----------------------------------------------------------------------------- michael@0: // ChildDNSService::nsIDNSService michael@0: //----------------------------------------------------------------------------- michael@0: michael@0: NS_IMETHODIMP michael@0: ChildDNSService::AsyncResolve(const nsACString &hostname, michael@0: uint32_t flags, michael@0: nsIDNSListener *listener, michael@0: nsIEventTarget *target_, michael@0: nsICancelable **result) michael@0: { michael@0: NS_ENSURE_TRUE(gNeckoChild != nullptr, NS_ERROR_FAILURE); michael@0: michael@0: if (mDisablePrefetch && (flags & RESOLVE_SPECULATE)) { michael@0: return NS_ERROR_DNS_LOOKUP_QUEUE_FULL; michael@0: } michael@0: michael@0: // Support apps being 'offline' even if parent is not: avoids DNS traffic by michael@0: // apps that have been told they are offline. michael@0: if (mOffline) { michael@0: flags |= RESOLVE_OFFLINE; michael@0: } michael@0: michael@0: // make sure JS callers get notification on the main thread michael@0: nsCOMPtr target = target_; michael@0: nsCOMPtr wrappedListener = do_QueryInterface(listener); michael@0: if (wrappedListener && !target) { michael@0: nsCOMPtr mainThread; michael@0: NS_GetMainThread(getter_AddRefs(mainThread)); michael@0: target = do_QueryInterface(mainThread); michael@0: } michael@0: if (target) { michael@0: // Guarantee listener freed on main thread. Not sure we need this in child michael@0: // (or in parent in nsDNSService.cpp) but doesn't hurt. michael@0: listener = new DNSListenerProxy(listener, target); michael@0: } michael@0: michael@0: nsRefPtr childReq = michael@0: new DNSRequestChild(nsCString(hostname), flags, listener, target); michael@0: michael@0: childReq->StartRequest(); michael@0: michael@0: childReq.forget(result); michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: ChildDNSService::CancelAsyncResolve(const nsACString &aHostname, michael@0: uint32_t aFlags, michael@0: nsIDNSListener *aListener, michael@0: nsresult aReason) michael@0: { michael@0: if (mDisablePrefetch && (aFlags & RESOLVE_SPECULATE)) { michael@0: return NS_ERROR_DNS_LOOKUP_QUEUE_FULL; michael@0: } michael@0: michael@0: // TODO: keep a hashtable of pending requests, so we can obey cancel semantics michael@0: // (call OnLookupComplete with aReason). Also possible we could send IPDL to michael@0: // parent to cancel. michael@0: return NS_ERROR_NOT_AVAILABLE; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: ChildDNSService::Resolve(const nsACString &hostname, michael@0: uint32_t flags, michael@0: nsIDNSRecord **result) michael@0: { michael@0: // not planning to ever support this, since sync IPDL is evil. michael@0: return NS_ERROR_NOT_AVAILABLE; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: ChildDNSService::GetDNSCacheEntries(nsTArray *args) michael@0: { michael@0: // Only used by networking dashboard, so may not ever need this in child. michael@0: // (and would provide a way to spy on what hosts other apps are connecting to, michael@0: // unless we start keeping per-app DNS caches). michael@0: return NS_ERROR_NOT_AVAILABLE; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: ChildDNSService::GetMyHostName(nsACString &result) michael@0: { michael@0: // TODO: get value from parent during PNecko construction? michael@0: return NS_ERROR_NOT_AVAILABLE; michael@0: } michael@0: michael@0: //----------------------------------------------------------------------------- michael@0: // ChildDNSService::nsPIDNSService michael@0: //----------------------------------------------------------------------------- michael@0: michael@0: nsresult michael@0: ChildDNSService::Init() michael@0: { michael@0: // Disable prefetching either by explicit preference or if a manual proxy michael@0: // is configured michael@0: bool disablePrefetch = false; michael@0: int proxyType = nsIProtocolProxyService::PROXYCONFIG_DIRECT; michael@0: michael@0: nsCOMPtr prefs = do_GetService(NS_PREFSERVICE_CONTRACTID); michael@0: prefs->GetBoolPref(kPrefNameDisablePrefetch, &disablePrefetch); michael@0: if (prefs) { michael@0: prefs->GetIntPref("network.proxy.type", &proxyType); michael@0: prefs->GetBoolPref(kPrefNameDisablePrefetch, &disablePrefetch); michael@0: } michael@0: michael@0: if (mFirstTime) { michael@0: mFirstTime = false; michael@0: if (prefs) { michael@0: prefs->AddObserver(kPrefNameDisablePrefetch, this, false); michael@0: michael@0: // Monitor these to see if there is a change in proxy configuration michael@0: // If a manual proxy is in use, disable prefetch implicitly michael@0: prefs->AddObserver("network.proxy.type", this, false); michael@0: } michael@0: } michael@0: michael@0: mDisablePrefetch = disablePrefetch || michael@0: (proxyType == nsIProtocolProxyService::PROXYCONFIG_MANUAL); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsresult michael@0: ChildDNSService::Shutdown() michael@0: { michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: ChildDNSService::GetPrefetchEnabled(bool *outVal) michael@0: { michael@0: *outVal = !mDisablePrefetch; michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: ChildDNSService::SetPrefetchEnabled(bool inVal) michael@0: { michael@0: mDisablePrefetch = !inVal; michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: ChildDNSService::GetOffline(bool* aResult) michael@0: { michael@0: *aResult = mOffline; michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: ChildDNSService::SetOffline(bool value) michael@0: { michael@0: mOffline = value; michael@0: return NS_OK; michael@0: } michael@0: michael@0: //----------------------------------------------------------------------------- michael@0: // ChildDNSService::nsIObserver michael@0: //----------------------------------------------------------------------------- michael@0: michael@0: NS_IMETHODIMP michael@0: ChildDNSService::Observe(nsISupports *subject, const char *topic, michael@0: const char16_t *data) michael@0: { michael@0: // we are only getting called if a preference has changed. michael@0: NS_ASSERTION(strcmp(topic, NS_PREFBRANCH_PREFCHANGE_TOPIC_ID) == 0, michael@0: "unexpected observe call"); michael@0: michael@0: // Reread prefs michael@0: Init(); michael@0: return NS_OK; michael@0: } michael@0: michael@0: } // namespace net michael@0: } // namespace mozilla