/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* vim:set ts=2 sw=2 sts=2 et cindent: */ /* 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" #ifdef XP_WIN # include "Win32SerialPlatformService.h" #elif defined(XP_MACOSX) || (defined(XP_LINUX) && !defined(ANDROID)) # include "PosixSerialPlatformService.h" #endif namespace mozilla::dom { 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 { #ifdef XP_WIN mPtr = MakeRefPtr(); #elif defined(XP_MACOSX) || (defined(XP_LINUX) && !defined(ANDROID)) mPtr = MakeRefPtr(); #else mPtr = MakeRefPtr(); #endif } 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) { { auto observerState = mObserverState.Lock(); if (observerState->shutdown) { return NS_ERROR_NOT_AVAILABLE; } } return EnumeratePortsImpl(aPorts); } 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::Read(const nsString& aPortId, Span aBuf, uint32_t& aBytesRead) { aBytesRead = 0; { auto observerState = mObserverState.Lock(); if (observerState->shutdown) { return NS_ERROR_NOT_AVAILABLE; } } return ReadImpl(aPortId, aBuf, aBytesRead); } 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); } 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