ProxyAutoConfigChild.cpp (6617B)
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/. */ 5 6 #include "ProxyAutoConfigChild.h" 7 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" 15 16 namespace mozilla::net { 17 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; 23 24 namespace { 25 26 class ShutdownObserver final : public nsIObserver { 27 public: 28 ShutdownObserver() = default; 29 30 NS_DECL_ISUPPORTS 31 NS_DECL_NSIOBSERVER 32 33 private: 34 ~ShutdownObserver() = default; 35 }; 36 37 NS_IMPL_ISUPPORTS(ShutdownObserver, nsIObserver) 38 39 NS_IMETHODIMP 40 ShutdownObserver::Observe(nsISupports* aSubject, const char* aTopic, 41 const char16_t* aData) { 42 ProxyAutoConfigChild::ShutdownPACThread(); 43 return NS_OK; 44 } 45 46 } // namespace 47 48 // static 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. 54 if (sActor) { 55 NS_DispatchToCurrentThread(NS_NewRunnableFunction( 56 "BindProxyAutoConfigChild", 57 [actor = std::move(aActor), endpoint = std::move(aEndpoint)]() mutable { 58 ProxyAutoConfigChild::BindProxyAutoConfigChild(std::move(actor), 59 std::move(endpoint)); 60 })); 61 return; 62 } 63 64 if (aEndpoint.Bind(aActor)) { 65 sActor = aActor; 66 } 67 } 68 69 // static 70 bool ProxyAutoConfigChild::Create(Endpoint<PProxyAutoConfigChild>&& aEndpoint) { 71 if (!sPACThread && !CreatePACThread()) { 72 NS_WARNING("Failed to create pac thread!"); 73 return false; 74 } 75 76 if (!sShutdownObserverRegistered) { 77 nsCOMPtr<nsIObserverService> obs = services::GetObserverService(); 78 if (NS_WARN_IF(!obs)) { 79 return false; 80 } 81 nsCOMPtr<nsIObserver> observer = new ShutdownObserver(); 82 nsresult rv = obs->AddObserver(observer, "xpcom-shutdown-threads", false); 83 if (NS_WARN_IF(NS_FAILED(rv))) { 84 return false; 85 } 86 sShutdownObserverRegistered = true; 87 } 88 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), 96 std::move(endpoint)); 97 })))) { 98 NS_WARNING("Failed to dispatch runnable!"); 99 return false; 100 } 101 102 return true; 103 } 104 105 // static 106 bool ProxyAutoConfigChild::CreatePACThread() { 107 MOZ_ASSERT(NS_IsMainThread()); 108 109 if (SocketProcessChild::GetSingleton()->IsShuttingDown()) { 110 NS_WARNING("Trying to create pac thread after shutdown has already begun!"); 111 return false; 112 } 113 114 nsCOMPtr<nsIThread> thread; 115 if (NS_FAILED(NS_NewNamedThread("ProxyResolution", getter_AddRefs(thread)))) { 116 NS_WARNING("NS_NewNamedThread failed!"); 117 return false; 118 } 119 120 sPACThread = thread.forget(); 121 return true; 122 } 123 124 // static 125 void ProxyAutoConfigChild::ShutdownPACThread() { 126 MOZ_ASSERT(NS_IsMainThread()); 127 128 if (sPACThread) { 129 // Wait until all actos are released. 130 SpinEventLoopUntil("ProxyAutoConfigChild::ShutdownPACThread"_ns, 131 [&]() { return !sActor; }); 132 133 nsCOMPtr<nsIThread> thread = sPACThread.get(); 134 sPACThread = nullptr; 135 MOZ_ALWAYS_SUCCEEDS(thread->Shutdown()); 136 } 137 } 138 139 ProxyAutoConfigChild::ProxyAutoConfigChild() 140 : mPAC(MakeUnique<ProxyAutoConfig>()) { 141 if (!sThreadLocalSetup) { 142 sThreadLocalSetup = true; 143 PR_NewThreadPrivateIndex(&sThreadLocalIndex, nullptr); 144 } 145 146 mPAC->SetThreadLocalIndex(sThreadLocalIndex); 147 } 148 149 ProxyAutoConfigChild::~ProxyAutoConfigChild() = default; 150 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()); 156 mPACLoaded = true; 157 NS_DispatchToCurrentThread( 158 NewRunnableMethod("ProxyAutoConfigChild::ProcessPendingQ", this, 159 &ProxyAutoConfigChild::ProcessPendingQ)); 160 return IPC_OK(); 161 } 162 163 void ProxyAutoConfigChild::PendingQuery::Resolve(nsresult aStatus, 164 const nsACString& aResult) { 165 mResolver(std::tuple<const nsresult&, const nsACString&>(aStatus, aResult)); 166 } 167 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))); 173 ProcessPendingQ(); 174 return IPC_OK(); 175 } 176 177 void ProxyAutoConfigChild::ProcessPendingQ() { 178 while (ProcessPending()) { 179 ; 180 } 181 182 if (mShutdown) { 183 mPAC->Shutdown(); 184 } else { 185 // do GC while the thread has nothing pending 186 mPAC->GC(); 187 } 188 } 189 190 bool ProxyAutoConfigChild::ProcessPending() { 191 if (mPendingQ.isEmpty()) { 192 return false; 193 } 194 195 if (mInProgress || !mPACLoaded) { 196 return false; 197 } 198 199 if (mShutdown) { 200 return true; 201 } 202 203 mInProgress = true; 204 RefPtr<PendingQuery> query = mPendingQ.popFirst(); 205 nsCString result; 206 nsresult rv = mPAC->GetProxyForURI(query->URI(), query->Host(), result); 207 query->Resolve(rv, result); 208 mInProgress = false; 209 return true; 210 } 211 212 void ProxyAutoConfigChild::ActorDestroy(ActorDestroyReason aWhy) { 213 mPendingQ.clear(); 214 mShutdown = true; 215 mPAC->Shutdown(); 216 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))); 221 } 222 223 void ProxyAutoConfigChild::Destroy() { sActor = nullptr; } 224 225 } // namespace mozilla::net