/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ /* 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 "ProxyAutoConfigChild.h" #include "mozilla/ipc/Endpoint.h" #include "mozilla/net/SocketProcessChild.h" #include "mozilla/SpinEventLoopUntil.h" #include "nsIObserver.h" #include "nsIObserverService.h" #include "nsThreadUtils.h" #include "ProxyAutoConfig.h" namespace mozilla::net { static bool sThreadLocalSetup = false; static uint32_t sThreadLocalIndex = 0xdeadbeef; StaticRefPtr ProxyAutoConfigChild::sPACThread; bool ProxyAutoConfigChild::sShutdownObserverRegistered = false; Atomic ProxyAutoConfigChild::sLiveActorCount(0); namespace { class ShutdownObserver final : public nsIObserver { public: ShutdownObserver() = default; NS_DECL_ISUPPORTS NS_DECL_NSIOBSERVER private: ~ShutdownObserver() = default; }; NS_IMPL_ISUPPORTS(ShutdownObserver, nsIObserver) NS_IMETHODIMP ShutdownObserver::Observe(nsISupports* aSubject, const char* aTopic, const char16_t* aData) { ProxyAutoConfigChild::ShutdownPACThread(); return NS_OK; } } // namespace // static bool ProxyAutoConfigChild::Create(Endpoint&& aEndpoint) { if (!sPACThread && !CreatePACThread()) { NS_WARNING("Failed to create pac thread!"); return false; } if (!sShutdownObserverRegistered) { nsCOMPtr obs = services::GetObserverService(); if (NS_WARN_IF(!obs)) { return false; } nsCOMPtr observer = new ShutdownObserver(); nsresult rv = obs->AddObserver(observer, "xpcom-shutdown-threads", false); if (NS_WARN_IF(NS_FAILED(rv))) { return false; } sShutdownObserverRegistered = true; } RefPtr actor = new ProxyAutoConfigChild(); if (NS_FAILED(sPACThread->Dispatch( NS_NewRunnableFunction("ProxyAutoConfigChild::ProxyAutoConfigChild", [actor = std::move(actor), endpoint = std::move(aEndpoint)]() mutable { MOZ_ASSERT(endpoint.IsValid()); // Transfer ownership to PAC thread. If // Bind() fails then we will release this // reference in Destroy. ProxyAutoConfigChild* actorTmp; actor.forget(&actorTmp); if (!endpoint.Bind(actorTmp)) { actorTmp->Destroy(); } })))) { NS_WARNING("Failed to dispatch runnable!"); return false; } return true; } // static bool ProxyAutoConfigChild::CreatePACThread() { MOZ_ASSERT(NS_IsMainThread()); if (SocketProcessChild::GetSingleton()->IsShuttingDown()) { NS_WARNING("Trying to create pac thread after shutdown has already begun!"); return false; } nsCOMPtr thread; if (NS_FAILED(NS_NewNamedThread("ProxyResolution", getter_AddRefs(thread)))) { NS_WARNING("NS_NewNamedThread failed!"); return false; } sPACThread = thread.forget(); return true; } // static void ProxyAutoConfigChild::ShutdownPACThread() { MOZ_ASSERT(NS_IsMainThread()); if (sPACThread) { // Wait until all actos are released. SpinEventLoopUntil("ProxyAutoConfigChild::ShutdownPACThread"_ns, [&]() { return !sLiveActorCount; }); nsCOMPtr thread = sPACThread.get(); sPACThread = nullptr; MOZ_ALWAYS_SUCCEEDS(thread->Shutdown()); } } ProxyAutoConfigChild::ProxyAutoConfigChild() : mPAC(MakeUnique()) { if (!sThreadLocalSetup) { sThreadLocalSetup = true; PR_NewThreadPrivateIndex(&sThreadLocalIndex, nullptr); } mPAC->SetThreadLocalIndex(sThreadLocalIndex); sLiveActorCount++; } ProxyAutoConfigChild::~ProxyAutoConfigChild() { MOZ_ASSERT(NS_IsMainThread()); sLiveActorCount--; } mozilla::ipc::IPCResult ProxyAutoConfigChild::RecvConfigurePAC( const nsCString& aPACURI, const nsCString& aPACScriptData, const bool& aIncludePath, const uint32_t& aExtraHeapSize) { mPAC->ConfigurePAC(aPACURI, aPACScriptData, aIncludePath, aExtraHeapSize, GetMainThreadSerialEventTarget()); return IPC_OK(); } mozilla::ipc::IPCResult ProxyAutoConfigChild::RecvGetProxyForURI( const nsCString& aTestURI, const nsCString& aTestHost, GetProxyForURIResolver&& aResolver) { // When PAC is waiting for DNS result, we need to wait. if (mPAC->WaitingForDNSResolve()) { RefPtr self = this; NS_DispatchToCurrentThread(NS_NewRunnableFunction( "ProxyAutoConfigChild::RecvGetProxyForURI", [self, testURI(aTestURI), testHost(aTestHost), resolver{std::move(aResolver)}]() mutable { self->RecvGetProxyForURI(testURI, testHost, std::move(resolver)); })); return IPC_OK(); } nsCString result; nsresult rv = mPAC->GetProxyForURI(aTestURI, aTestHost, result); aResolver(Tuple(rv, result)); return IPC_OK(); } mozilla::ipc::IPCResult ProxyAutoConfigChild::RecvGC() { mPAC->GC(); return IPC_OK(); } void ProxyAutoConfigChild::ActorDestroy(ActorDestroyReason aWhy) { UniquePtr pac(std::move(mPAC)); pac->Shutdown(); // To avoid racing with the main thread, we need to dispatch // ProxyAutoConfigChild::Destroy again. MOZ_ALWAYS_SUCCEEDS(NS_DispatchToCurrentThread(NewNonOwningRunnableMethod( "ProxyAutoConfigChild::Destroy", this, &ProxyAutoConfigChild::Destroy))); } void ProxyAutoConfigChild::Destroy() { // May be called on any thread! MOZ_ALWAYS_SUCCEEDS(NS_DispatchToMainThread(NewNonOwningRunnableMethod( "ProxyAutoConfigChild::MainThreadActorDestroy", this, &ProxyAutoConfigChild::MainThreadActorDestroy))); } void ProxyAutoConfigChild::MainThreadActorDestroy() { MOZ_ASSERT(NS_IsMainThread()); // This may be the last reference! Release(); } } // namespace mozilla::net