Source code
Revision control
Copy as Markdown
Other Tools
/* -*- 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
#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> SerialPlatformService::GetInstance() {
struct Holder {
Holder() {
bool useMockService = StaticPrefs::dom_webserial_testing_enabled();
if (useMockService) {
mPtr = MakeRefPtr<TestSerialPlatformService>();
} else {
#ifdef XP_WIN
mPtr = MakeRefPtr<Win32SerialPlatformService>();
#elif defined(XP_MACOSX) || (defined(XP_LINUX) && !defined(ANDROID))
mPtr = MakeRefPtr<PosixSerialPlatformService>();
#else
mPtr = MakeRefPtr<TestSerialPlatformService>();
#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<uint32_t>(rv)));
mPtr = nullptr;
}
NS_DispatchToMainThread(NS_NewRunnableFunction(
"SerialPlatformService::GetInstance::Shutdown", [this] {
RunOnShutdown(
[this] {
if (mPtr) {
mPtr->Shutdown();
mPtr = nullptr;
}
},
ShutdownPhase::XPCOMShutdownThreads);
}));
}
RefPtr<SerialPlatformService> 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<uint8_t> 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<const uint8_t> 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