/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* vim: set ts=8 sts=2 et sw=2 tw=80: */ /* 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 #include "mozilla/Hal.h" #include "ConnectionWorker.h" #include "mozilla/dom/WorkerPrivate.h" #include "mozilla/dom/WorkerRef.h" #include "mozilla/dom/WorkerRunnable.h" namespace mozilla::dom::network { class ConnectionProxy final : public hal::NetworkObserver { public: NS_INLINE_DECL_THREADSAFE_REFCOUNTING(ConnectionProxy) static already_AddRefed Create( WorkerPrivate* aWorkerPrivate, ConnectionWorker* aConnection) { RefPtr proxy = new ConnectionProxy(aConnection); RefPtr workerRef = StrongWorkerRef::Create( aWorkerPrivate, "ConnectionProxy", [proxy]() { proxy->Shutdown(); }); if (NS_WARN_IF(!workerRef)) { return nullptr; } proxy->mWorkerRef = new ThreadSafeWorkerRef(workerRef); return proxy.forget(); } ThreadSafeWorkerRef* WorkerRef() const { return mWorkerRef; } // For IObserver - main-thread only. void Notify(const hal::NetworkInformation& aNetworkInfo) override; void Shutdown(); void Update(ConnectionType aType, bool aIsWifi, uint32_t aDHCPGateway) { MOZ_ASSERT(mConnection); MOZ_ASSERT(IsCurrentThreadRunningWorker()); mConnection->Update(aType, aIsWifi, aDHCPGateway, true); } private: explicit ConnectionProxy(ConnectionWorker* aConnection) : mConnection(aConnection) {} ~ConnectionProxy() = default; // Raw pointer because the ConnectionWorker keeps alive the proxy. // This is touched only on the worker-thread and it's nullified when the // shutdown procedure starts. ConnectionWorker* mConnection; RefPtr mWorkerRef; }; namespace { // This class initializes the hal observer on the main-thread. class InitializeRunnable : public WorkerMainThreadRunnable { private: // raw pointer because this is a sync runnable. ConnectionProxy* mProxy; hal::NetworkInformation& mNetworkInfo; public: InitializeRunnable(WorkerPrivate* aWorkerPrivate, ConnectionProxy* aProxy, hal::NetworkInformation& aNetworkInfo) : WorkerMainThreadRunnable(aWorkerPrivate, "ConnectionWorker :: Initialize"_ns), mProxy(aProxy), mNetworkInfo(aNetworkInfo) { MOZ_ASSERT(aProxy); aWorkerPrivate->AssertIsOnWorkerThread(); } bool MainThreadRun() override { MOZ_ASSERT(NS_IsMainThread()); hal::RegisterNetworkObserver(mProxy); hal::GetCurrentNetworkInformation(&mNetworkInfo); return true; } }; // This class turns down the hal observer on the main-thread. class ShutdownRunnable : public WorkerMainThreadRunnable { private: // raw pointer because this is a sync runnable. ConnectionProxy* mProxy; public: ShutdownRunnable(WorkerPrivate* aWorkerPrivate, ConnectionProxy* aProxy) : WorkerMainThreadRunnable(aWorkerPrivate, "ConnectionWorker :: Shutdown"_ns), mProxy(aProxy) { MOZ_ASSERT(aProxy); aWorkerPrivate->AssertIsOnWorkerThread(); } bool MainThreadRun() override { MOZ_ASSERT(NS_IsMainThread()); hal::UnregisterNetworkObserver(mProxy); return true; } }; class NotifyRunnable : public WorkerRunnable { private: RefPtr mProxy; const ConnectionType mConnectionType; const bool mIsWifi; const uint32_t mDHCPGateway; public: NotifyRunnable(WorkerPrivate* aWorkerPrivate, ConnectionProxy* aProxy, ConnectionType aType, bool aIsWifi, uint32_t aDHCPGateway) : WorkerRunnable(aWorkerPrivate), mProxy(aProxy), mConnectionType(aType), mIsWifi(aIsWifi), mDHCPGateway(aDHCPGateway) { MOZ_ASSERT(aProxy); MOZ_ASSERT(NS_IsMainThread()); } bool WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override { aWorkerPrivate->AssertIsOnWorkerThread(); mProxy->Update(mConnectionType, mIsWifi, mDHCPGateway); return true; } }; } // anonymous namespace /* static */ already_AddRefed ConnectionWorker::Create( WorkerPrivate* aWorkerPrivate, ErrorResult& aRv) { RefPtr c = new ConnectionWorker(); c->mProxy = ConnectionProxy::Create(aWorkerPrivate, c); if (!c->mProxy) { aRv.ThrowTypeError("The Worker thread is shutting down."); return nullptr; } hal::NetworkInformation networkInfo; RefPtr runnable = new InitializeRunnable(aWorkerPrivate, c->mProxy, networkInfo); runnable->Dispatch(Canceling, aRv); if (NS_WARN_IF(aRv.Failed())) { return nullptr; } c->Update(static_cast(networkInfo.type()), networkInfo.isWifi(), networkInfo.dhcpGateway(), false); return c.forget(); } ConnectionWorker::ConnectionWorker() : Connection(nullptr) { MOZ_ASSERT(IsCurrentThreadRunningWorker()); } ConnectionWorker::~ConnectionWorker() { Shutdown(); } void ConnectionWorker::ShutdownInternal() { MOZ_ASSERT(IsCurrentThreadRunningWorker()); mProxy->Shutdown(); } void ConnectionProxy::Notify(const hal::NetworkInformation& aNetworkInfo) { MOZ_ASSERT(NS_IsMainThread()); RefPtr runnable = new NotifyRunnable(mWorkerRef->Private(), this, static_cast(aNetworkInfo.type()), aNetworkInfo.isWifi(), aNetworkInfo.dhcpGateway()); runnable->Dispatch(); } void ConnectionProxy::Shutdown() { MOZ_ASSERT(IsCurrentThreadRunningWorker()); // Already shut down. if (!mConnection) { return; } mConnection = nullptr; RefPtr runnable = new ShutdownRunnable(mWorkerRef->Private(), this); ErrorResult rv; // This runnable _must_ be executed. runnable->Dispatch(Killing, rv); if (NS_WARN_IF(rv.Failed())) { rv.SuppressException(); } mWorkerRef = nullptr; } } // namespace mozilla::dom::network