Source code

Revision control

Copy as Markdown

Other Tools

/* -*- 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<nsIThread> ProxyAutoConfigChild::sPACThread;
bool ProxyAutoConfigChild::sShutdownObserverRegistered = false;
static StaticRefPtr<ProxyAutoConfigChild> sActor;
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
void ProxyAutoConfigChild::BindProxyAutoConfigChild(
RefPtr<ProxyAutoConfigChild>&& aActor,
Endpoint<PProxyAutoConfigChild>&& aEndpoint) {
// We only allow one ProxyAutoConfigChild at a time, so we need to
// wait until the old one to be destroyed.
if (sActor) {
NS_DispatchToCurrentThread(NS_NewRunnableFunction(
"BindProxyAutoConfigChild",
[actor = std::move(aActor), endpoint = std::move(aEndpoint)]() mutable {
ProxyAutoConfigChild::BindProxyAutoConfigChild(std::move(actor),
std::move(endpoint));
}));
return;
}
if (aEndpoint.Bind(aActor)) {
sActor = aActor;
}
}
// static
bool ProxyAutoConfigChild::Create(Endpoint<PProxyAutoConfigChild>&& aEndpoint) {
if (!sPACThread && !CreatePACThread()) {
NS_WARNING("Failed to create pac thread!");
return false;
}
if (!sShutdownObserverRegistered) {
nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
if (NS_WARN_IF(!obs)) {
return false;
}
nsCOMPtr<nsIObserver> observer = new ShutdownObserver();
nsresult rv = obs->AddObserver(observer, "xpcom-shutdown-threads", false);
if (NS_WARN_IF(NS_FAILED(rv))) {
return false;
}
sShutdownObserverRegistered = true;
}
RefPtr<ProxyAutoConfigChild> 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());
ProxyAutoConfigChild::BindProxyAutoConfigChild(std::move(actor),
std::move(endpoint));
})))) {
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<nsIThread> 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 !sActor; });
nsCOMPtr<nsIThread> thread = sPACThread.get();
sPACThread = nullptr;
MOZ_ALWAYS_SUCCEEDS(thread->Shutdown());
}
}
ProxyAutoConfigChild::ProxyAutoConfigChild()
: mPAC(MakeUnique<ProxyAutoConfig>()) {
if (!sThreadLocalSetup) {
sThreadLocalSetup = true;
PR_NewThreadPrivateIndex(&sThreadLocalIndex, nullptr);
}
mPAC->SetThreadLocalIndex(sThreadLocalIndex);
}
ProxyAutoConfigChild::~ProxyAutoConfigChild() = default;
mozilla::ipc::IPCResult ProxyAutoConfigChild::RecvConfigurePAC(
const nsACString& aPACURI, const nsACString& aPACScriptData,
const bool& aIncludePath, const uint32_t& aExtraHeapSize) {
mPAC->ConfigurePAC(aPACURI, aPACScriptData, aIncludePath, aExtraHeapSize,
GetMainThreadSerialEventTarget());
mPACLoaded = true;
NS_DispatchToCurrentThread(
NewRunnableMethod("ProxyAutoConfigChild::ProcessPendingQ", this,
&ProxyAutoConfigChild::ProcessPendingQ));
return IPC_OK();
}
void ProxyAutoConfigChild::PendingQuery::Resolve(nsresult aStatus,
const nsACString& aResult) {
mResolver(Tuple<const nsresult&, const nsACString&>(aStatus, aResult));
}
mozilla::ipc::IPCResult ProxyAutoConfigChild::RecvGetProxyForURI(
const nsACString& aTestURI, const nsACString& aTestHost,
GetProxyForURIResolver&& aResolver) {
mPendingQ.insertBack(
new PendingQuery(aTestURI, aTestHost, std::move(aResolver)));
ProcessPendingQ();
return IPC_OK();
}
void ProxyAutoConfigChild::ProcessPendingQ() {
while (ProcessPending()) {
;
}
if (mShutdown) {
mPAC->Shutdown();
} else {
// do GC while the thread has nothing pending
mPAC->GC();
}
}
bool ProxyAutoConfigChild::ProcessPending() {
if (mPendingQ.isEmpty()) {
return false;
}
if (mInProgress || !mPACLoaded) {
return false;
}
if (mShutdown) {
return true;
}
mInProgress = true;
RefPtr<PendingQuery> query = mPendingQ.popFirst();
nsCString result;
nsresult rv = mPAC->GetProxyForURI(query->URI(), query->Host(), result);
query->Resolve(rv, result);
mInProgress = false;
return true;
}
void ProxyAutoConfigChild::ActorDestroy(ActorDestroyReason aWhy) {
mPendingQ.clear();
mShutdown = true;
mPAC->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() { sActor = nullptr; }
} // namespace mozilla::net