/* 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 "mozilla/dom/SerialPlatformService.h" #include "SerialLogging.h" #include "TestSerialPlatformService.h" #include "mozilla/ClearOnShutdown.h" #include "mozilla/ShutdownPhase.h" #include "mozilla/StaticMutex.h" #include "mozilla/StaticPrefs_dom.h" #include "nsThreadUtils.h" namespace mozilla::dom { #if !defined(XP_WIN) && !defined(XP_MACOSX) && \ !(defined(XP_LINUX) && !defined(ANDROID)) already_AddRefed SerialPlatformService::GetInstanceImpl() { return MakeAndAddRef(); } #endif SerialPlatformService::SerialPlatformService() { auto ioThread = mIOThread.Lock(); MOZ_ALWAYS_SUCCEEDS(NS_CreateBackgroundTaskQueue("SerialTaskQueue", getter_AddRefs(*ioThread))); } nsISerialEventTarget* SerialPlatformService::IOThread() { auto ioThread = mIOThread.Lock(); return *ioThread; } already_AddRefed SerialPlatformService::GetInstance() { struct Holder { Holder() { bool useMockService = StaticPrefs::dom_webserial_testing_enabled(); if (useMockService) { mPtr = MakeRefPtr(); } else { mPtr = GetInstanceImpl(); } nsresult rv = mPtr->Init(); if (NS_WARN_IF(NS_FAILED(rv))) { MOZ_LOG(gWebSerialLog, LogLevel::Error, ("SerialPlatformService::GetInstance Init failed: 0x%08x", static_cast(rv))); mPtr = nullptr; } NS_DispatchToMainThread(NS_NewRunnableFunction( "SerialPlatformService::GetInstance::Shutdown", [this] { RunOnShutdown( [this] { if (mPtr) { mPtr->Shutdown(); mPtr = nullptr; } }, ShutdownPhase::XPCOMShutdownThreads); })); } RefPtr mPtr; }; static Holder holder; return do_AddRef(holder.mPtr); } void SerialPlatformService::Shutdown() { MOZ_LOG(gWebSerialLog, LogLevel::Info, ("SerialPlatformService[%p]::Shutdown", this)); auto observerState = mObserverState.Lock(); observerState->shutdown = true; observerState->observers.Clear(); // Don't clear mIOThread, as things may still be running on it. Setting // shutdown to true above will ensure that we don't queue any new work // to it. } bool SerialPlatformService::IsShutdown() { auto observerState = mObserverState.Lock(); return observerState->shutdown; } nsresult SerialPlatformService::EnumeratePorts(SerialPortList& aPorts, bool* aLikelyAccessDenied) { if (aLikelyAccessDenied) { *aLikelyAccessDenied = false; } { auto observerState = mObserverState.Lock(); if (observerState->shutdown) { return NS_ERROR_NOT_AVAILABLE; } } return EnumeratePortsImpl(aPorts, aLikelyAccessDenied); } nsresult SerialPlatformService::Open(const nsString& aPortId, const IPCSerialOptions& aOptions) { { auto observerState = mObserverState.Lock(); if (observerState->shutdown) { return NS_ERROR_NOT_AVAILABLE; } } return OpenImpl(aPortId, aOptions); } nsresult SerialPlatformService::Close(const nsString& aPortId) { { auto observerState = mObserverState.Lock(); if (observerState->shutdown) { return NS_ERROR_NOT_AVAILABLE; } } return CloseImpl(aPortId); } nsresult SerialPlatformService::Write(const nsString& aPortId, Span aData) { { auto observerState = mObserverState.Lock(); if (observerState->shutdown) { return NS_ERROR_NOT_AVAILABLE; } } return WriteImpl(aPortId, aData); } nsresult SerialPlatformService::Drain(const nsString& aPortId) { { auto observerState = mObserverState.Lock(); if (observerState->shutdown) { return NS_ERROR_NOT_AVAILABLE; } } return DrainImpl(aPortId); } nsresult SerialPlatformService::Flush(const nsString& aPortId, bool aReceive) { { auto observerState = mObserverState.Lock(); if (observerState->shutdown) { return NS_ERROR_NOT_AVAILABLE; } } return FlushImpl(aPortId, aReceive); } nsresult SerialPlatformService::SetSignals( const nsString& aPortId, const IPCSerialOutputSignals& aSignals) { { auto observerState = mObserverState.Lock(); if (observerState->shutdown) { return NS_ERROR_NOT_AVAILABLE; } } return SetSignalsImpl(aPortId, aSignals); } nsresult SerialPlatformService::GetSignals(const nsString& aPortId, IPCSerialInputSignals& aSignals) { { auto observerState = mObserverState.Lock(); if (observerState->shutdown) { return NS_ERROR_NOT_AVAILABLE; } } return GetSignalsImpl(aPortId, aSignals); } nsresult SerialPlatformService::GetReadStream(const nsString& aPortId, uint32_t aBufferSize, nsIAsyncInputStream** aStream) { { auto observerState = mObserverState.Lock(); if (observerState->shutdown) { return NS_ERROR_NOT_AVAILABLE; } } return GetReadStreamImpl(aPortId, aBufferSize, aStream); } void SerialPlatformService::AddDeviceChangeObserver( SerialDeviceChangeObserver* aObserver) { auto observerState = mObserverState.Lock(); if (observerState->shutdown) { return; } if (!observerState->observers.Contains(aObserver)) { observerState->observers.AppendElement(aObserver); } } void SerialPlatformService::RemoveDeviceChangeObserver( SerialDeviceChangeObserver* aObserver) { auto observerState = mObserverState.Lock(); if (observerState->shutdown) { return; } observerState->observers.RemoveElement(aObserver); } void SerialPlatformService::NotifyPortConnected( const IPCSerialPortInfo& aPortInfo) { auto observerState = mObserverState.Lock(); if (observerState->shutdown) { return; } for (const auto& observer : observerState->observers) { observer->OnPortConnected(aPortInfo); } } void SerialPlatformService::NotifyPortDisconnected(const nsAString& aPortId) { auto observerState = mObserverState.Lock(); if (observerState->shutdown) { return; } for (const auto& observer : observerState->observers) { observer->OnPortDisconnected(aPortId); } } } // namespace mozilla::dom