ConnectionWorker.cpp (6487B)
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */ 3 /* This Source Code Form is subject to the terms of the Mozilla Public 4 * License, v. 2.0. If a copy of the MPL was not distributed with this 5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 6 7 #include "ConnectionWorker.h" 8 9 #include "mozilla/Hal.h" 10 #include "mozilla/dom/WorkerPrivate.h" 11 #include "mozilla/dom/WorkerRef.h" 12 #include "mozilla/dom/WorkerRunnable.h" 13 #include "mozilla/dom/WorkerScope.h" 14 15 namespace mozilla::dom::network { 16 17 class ConnectionProxy final : public hal::NetworkObserver { 18 public: 19 NS_INLINE_DECL_THREADSAFE_REFCOUNTING(ConnectionProxy) 20 21 static already_AddRefed<ConnectionProxy> Create( 22 WorkerPrivate* aWorkerPrivate, ConnectionWorker* aConnection) { 23 RefPtr<ConnectionProxy> proxy = new ConnectionProxy(aConnection); 24 25 RefPtr<StrongWorkerRef> workerRef = StrongWorkerRef::Create( 26 aWorkerPrivate, "ConnectionProxy", [proxy]() { proxy->Shutdown(); }); 27 if (NS_WARN_IF(!workerRef)) { 28 return nullptr; 29 } 30 31 proxy->mWorkerRef = new ThreadSafeWorkerRef(workerRef); 32 return proxy.forget(); 33 } 34 35 ThreadSafeWorkerRef* WorkerRef() const { return mWorkerRef; } 36 37 // For IObserver - main-thread only. 38 void Notify(const hal::NetworkInformation& aNetworkInfo) override; 39 40 void Shutdown(); 41 42 void Update(ConnectionType aType, bool aIsWifi, uint32_t aDHCPGateway) { 43 MOZ_ASSERT(mConnection); 44 MOZ_ASSERT(IsCurrentThreadRunningWorker()); 45 mConnection->Update(aType, aIsWifi, aDHCPGateway, true); 46 } 47 48 private: 49 explicit ConnectionProxy(ConnectionWorker* aConnection) 50 : mConnection(aConnection) {} 51 52 ~ConnectionProxy() = default; 53 54 // Raw pointer because the ConnectionWorker keeps alive the proxy. 55 // This is touched only on the worker-thread and it's nullified when the 56 // shutdown procedure starts. 57 ConnectionWorker* mConnection; 58 59 RefPtr<ThreadSafeWorkerRef> mWorkerRef; 60 }; 61 62 namespace { 63 64 // This class initializes the hal observer on the main-thread. 65 class InitializeRunnable : public WorkerMainThreadRunnable { 66 private: 67 // raw pointer because this is a sync runnable. 68 ConnectionProxy* mProxy; 69 hal::NetworkInformation& mNetworkInfo; 70 71 public: 72 InitializeRunnable(WorkerPrivate* aWorkerPrivate, ConnectionProxy* aProxy, 73 hal::NetworkInformation& aNetworkInfo) 74 : WorkerMainThreadRunnable(aWorkerPrivate, 75 "ConnectionWorker :: Initialize"_ns), 76 mProxy(aProxy), 77 mNetworkInfo(aNetworkInfo) { 78 MOZ_ASSERT(aProxy); 79 aWorkerPrivate->AssertIsOnWorkerThread(); 80 } 81 82 bool MainThreadRun() override { 83 MOZ_ASSERT(NS_IsMainThread()); 84 hal::RegisterNetworkObserver(mProxy); 85 hal::GetCurrentNetworkInformation(&mNetworkInfo); 86 return true; 87 } 88 }; 89 90 // This class turns down the hal observer on the main-thread. 91 class ShutdownRunnable : public WorkerMainThreadRunnable { 92 private: 93 // raw pointer because this is a sync runnable. 94 ConnectionProxy* mProxy; 95 96 public: 97 ShutdownRunnable(WorkerPrivate* aWorkerPrivate, ConnectionProxy* aProxy) 98 : WorkerMainThreadRunnable(aWorkerPrivate, 99 "ConnectionWorker :: Shutdown"_ns), 100 mProxy(aProxy) { 101 MOZ_ASSERT(aProxy); 102 aWorkerPrivate->AssertIsOnWorkerThread(); 103 } 104 105 bool MainThreadRun() override { 106 MOZ_ASSERT(NS_IsMainThread()); 107 hal::UnregisterNetworkObserver(mProxy); 108 return true; 109 } 110 }; 111 112 class NotifyRunnable final : public WorkerThreadRunnable { 113 private: 114 RefPtr<ConnectionProxy> mProxy; 115 116 const ConnectionType mConnectionType; 117 const bool mIsWifi; 118 const uint32_t mDHCPGateway; 119 120 public: 121 NotifyRunnable(WorkerPrivate* aWorkerPrivate, ConnectionProxy* aProxy, 122 ConnectionType aType, bool aIsWifi, uint32_t aDHCPGateway) 123 : WorkerThreadRunnable("NotifyRunnable"), 124 mProxy(aProxy), 125 mConnectionType(aType), 126 mIsWifi(aIsWifi), 127 mDHCPGateway(aDHCPGateway) { 128 MOZ_ASSERT(aProxy); 129 MOZ_ASSERT(NS_IsMainThread()); 130 } 131 132 bool WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override { 133 aWorkerPrivate->AssertIsOnWorkerThread(); 134 mProxy->Update(mConnectionType, mIsWifi, mDHCPGateway); 135 return true; 136 } 137 }; 138 139 } // anonymous namespace 140 141 /* static */ 142 already_AddRefed<ConnectionWorker> ConnectionWorker::Create( 143 WorkerPrivate* aWorkerPrivate, ErrorResult& aRv) { 144 bool shouldResistFingerprinting = 145 aWorkerPrivate->ShouldResistFingerprinting(RFPTarget::NetworkConnection); 146 RefPtr<ConnectionWorker> c = new ConnectionWorker(shouldResistFingerprinting); 147 c->mProxy = ConnectionProxy::Create(aWorkerPrivate, c); 148 if (!c->mProxy) { 149 aRv.ThrowTypeError("The Worker thread is shutting down."); 150 return nullptr; 151 } 152 153 hal::NetworkInformation networkInfo; 154 RefPtr<InitializeRunnable> runnable = 155 new InitializeRunnable(aWorkerPrivate, c->mProxy, networkInfo); 156 157 runnable->Dispatch(aWorkerPrivate, Canceling, aRv); 158 if (NS_WARN_IF(aRv.Failed())) { 159 return nullptr; 160 } 161 162 c->Update(static_cast<ConnectionType>(networkInfo.type()), 163 networkInfo.isWifi(), networkInfo.dhcpGateway(), false); 164 return c.forget(); 165 } 166 167 ConnectionWorker::ConnectionWorker(bool aShouldResistFingerprinting) 168 : Connection(nullptr, aShouldResistFingerprinting) { 169 MOZ_ASSERT(IsCurrentThreadRunningWorker()); 170 } 171 172 ConnectionWorker::~ConnectionWorker() { Shutdown(); } 173 174 void ConnectionWorker::ShutdownInternal() { 175 MOZ_ASSERT(IsCurrentThreadRunningWorker()); 176 mProxy->Shutdown(); 177 } 178 179 void ConnectionProxy::Notify(const hal::NetworkInformation& aNetworkInfo) { 180 MOZ_ASSERT(NS_IsMainThread()); 181 182 RefPtr<NotifyRunnable> runnable = 183 new NotifyRunnable(mWorkerRef->Private(), this, 184 static_cast<ConnectionType>(aNetworkInfo.type()), 185 aNetworkInfo.isWifi(), aNetworkInfo.dhcpGateway()); 186 runnable->Dispatch(mWorkerRef->Private()); 187 } 188 189 void ConnectionProxy::Shutdown() { 190 MOZ_ASSERT(IsCurrentThreadRunningWorker()); 191 192 // Already shut down. 193 if (!mConnection) { 194 return; 195 } 196 197 mConnection = nullptr; 198 199 RefPtr<ShutdownRunnable> runnable = 200 new ShutdownRunnable(mWorkerRef->Private(), this); 201 202 ErrorResult rv; 203 // This runnable _must_ be executed. 204 runnable->Dispatch(mWorkerRef->Private(), Killing, rv); 205 if (NS_WARN_IF(rv.Failed())) { 206 rv.SuppressException(); 207 } 208 209 mWorkerRef = nullptr; 210 } 211 212 } // namespace mozilla::dom::network