1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /* This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 #include "ProxyAutoConfigChild.h"
8 #include "mozilla/ipc/Endpoint.h"
9 #include "mozilla/net/SocketProcessChild.h"
10 #include "mozilla/SpinEventLoopUntil.h"
11 #include "nsIObserver.h"
12 #include "nsIObserverService.h"
13 #include "nsThreadUtils.h"
14 #include "ProxyAutoConfig.h"
16 namespace mozilla::net
{
18 static bool sThreadLocalSetup
= false;
19 static uint32_t sThreadLocalIndex
= 0xdeadbeef;
20 StaticRefPtr
<nsIThread
> ProxyAutoConfigChild::sPACThread
;
21 bool ProxyAutoConfigChild::sShutdownObserverRegistered
= false;
22 static StaticRefPtr
<ProxyAutoConfigChild
> sActor
;
26 class ShutdownObserver final
: public nsIObserver
{
28 ShutdownObserver() = default;
34 ~ShutdownObserver() = default;
37 NS_IMPL_ISUPPORTS(ShutdownObserver
, nsIObserver
)
40 ShutdownObserver::Observe(nsISupports
* aSubject
, const char* aTopic
,
41 const char16_t
* aData
) {
42 ProxyAutoConfigChild::ShutdownPACThread();
49 void ProxyAutoConfigChild::BindProxyAutoConfigChild(
50 RefPtr
<ProxyAutoConfigChild
>&& aActor
,
51 Endpoint
<PProxyAutoConfigChild
>&& aEndpoint
) {
52 // We only allow one ProxyAutoConfigChild at a time, so we need to
53 // wait until the old one to be destroyed.
55 NS_DispatchToCurrentThread(NS_NewRunnableFunction(
56 "BindProxyAutoConfigChild",
57 [actor
= std::move(aActor
), endpoint
= std::move(aEndpoint
)]() mutable {
58 ProxyAutoConfigChild::BindProxyAutoConfigChild(std::move(actor
),
64 if (aEndpoint
.Bind(aActor
)) {
70 bool ProxyAutoConfigChild::Create(Endpoint
<PProxyAutoConfigChild
>&& aEndpoint
) {
71 if (!sPACThread
&& !CreatePACThread()) {
72 NS_WARNING("Failed to create pac thread!");
76 if (!sShutdownObserverRegistered
) {
77 nsCOMPtr
<nsIObserverService
> obs
= services::GetObserverService();
78 if (NS_WARN_IF(!obs
)) {
81 nsCOMPtr
<nsIObserver
> observer
= new ShutdownObserver();
82 nsresult rv
= obs
->AddObserver(observer
, "xpcom-shutdown-threads", false);
83 if (NS_WARN_IF(NS_FAILED(rv
))) {
86 sShutdownObserverRegistered
= true;
89 RefPtr
<ProxyAutoConfigChild
> actor
= new ProxyAutoConfigChild();
90 if (NS_FAILED(sPACThread
->Dispatch(NS_NewRunnableFunction(
91 "ProxyAutoConfigChild::ProxyAutoConfigChild",
92 [actor
= std::move(actor
),
93 endpoint
= std::move(aEndpoint
)]() mutable {
94 MOZ_ASSERT(endpoint
.IsValid());
95 ProxyAutoConfigChild::BindProxyAutoConfigChild(std::move(actor
),
98 NS_WARNING("Failed to dispatch runnable!");
106 bool ProxyAutoConfigChild::CreatePACThread() {
107 MOZ_ASSERT(NS_IsMainThread());
109 if (SocketProcessChild::GetSingleton()->IsShuttingDown()) {
110 NS_WARNING("Trying to create pac thread after shutdown has already begun!");
114 nsCOMPtr
<nsIThread
> thread
;
115 if (NS_FAILED(NS_NewNamedThread("ProxyResolution", getter_AddRefs(thread
)))) {
116 NS_WARNING("NS_NewNamedThread failed!");
120 sPACThread
= thread
.forget();
125 void ProxyAutoConfigChild::ShutdownPACThread() {
126 MOZ_ASSERT(NS_IsMainThread());
129 // Wait until all actos are released.
130 SpinEventLoopUntil("ProxyAutoConfigChild::ShutdownPACThread"_ns
,
131 [&]() { return !sActor
; });
133 nsCOMPtr
<nsIThread
> thread
= sPACThread
.get();
134 sPACThread
= nullptr;
135 MOZ_ALWAYS_SUCCEEDS(thread
->Shutdown());
139 ProxyAutoConfigChild::ProxyAutoConfigChild()
140 : mPAC(MakeUnique
<ProxyAutoConfig
>()) {
141 if (!sThreadLocalSetup
) {
142 sThreadLocalSetup
= true;
143 PR_NewThreadPrivateIndex(&sThreadLocalIndex
, nullptr);
146 mPAC
->SetThreadLocalIndex(sThreadLocalIndex
);
149 ProxyAutoConfigChild::~ProxyAutoConfigChild() = default;
151 mozilla::ipc::IPCResult
ProxyAutoConfigChild::RecvConfigurePAC(
152 const nsACString
& aPACURI
, const nsACString
& aPACScriptData
,
153 const bool& aIncludePath
, const uint32_t& aExtraHeapSize
) {
154 mPAC
->ConfigurePAC(aPACURI
, aPACScriptData
, aIncludePath
, aExtraHeapSize
,
155 GetMainThreadSerialEventTarget());
157 NS_DispatchToCurrentThread(
158 NewRunnableMethod("ProxyAutoConfigChild::ProcessPendingQ", this,
159 &ProxyAutoConfigChild::ProcessPendingQ
));
163 void ProxyAutoConfigChild::PendingQuery::Resolve(nsresult aStatus
,
164 const nsACString
& aResult
) {
165 mResolver(std::tuple
<const nsresult
&, const nsACString
&>(aStatus
, aResult
));
168 mozilla::ipc::IPCResult
ProxyAutoConfigChild::RecvGetProxyForURI(
169 const nsACString
& aTestURI
, const nsACString
& aTestHost
,
170 GetProxyForURIResolver
&& aResolver
) {
171 mPendingQ
.insertBack(
172 new PendingQuery(aTestURI
, aTestHost
, std::move(aResolver
)));
177 void ProxyAutoConfigChild::ProcessPendingQ() {
178 while (ProcessPending()) {
185 // do GC while the thread has nothing pending
190 bool ProxyAutoConfigChild::ProcessPending() {
191 if (mPendingQ
.isEmpty()) {
195 if (mInProgress
|| !mPACLoaded
) {
204 RefPtr
<PendingQuery
> query
= mPendingQ
.popFirst();
206 nsresult rv
= mPAC
->GetProxyForURI(query
->URI(), query
->Host(), result
);
207 query
->Resolve(rv
, result
);
212 void ProxyAutoConfigChild::ActorDestroy(ActorDestroyReason aWhy
) {
217 // To avoid racing with the main thread, we need to dispatch
218 // ProxyAutoConfigChild::Destroy again.
219 MOZ_ALWAYS_SUCCEEDS(NS_DispatchToCurrentThread(NewNonOwningRunnableMethod(
220 "ProxyAutoConfigChild::Destroy", this, &ProxyAutoConfigChild::Destroy
)));
223 void ProxyAutoConfigChild::Destroy() { sActor
= nullptr; }
225 } // namespace mozilla::net