I tested this on my Linux machine and verified that the message shows up if I remove my permission to access /dev/ttyS0, etc. Differential Revision: https://phabricator.services.mozilla.com/D303917
231 lines
6.5 KiB
C++
231 lines
6.5 KiB
C++
/* 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>
|
|
SerialPlatformService::GetInstanceImpl() {
|
|
return MakeAndAddRef<TestSerialPlatformService>();
|
|
}
|
|
#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> SerialPlatformService::GetInstance() {
|
|
struct Holder {
|
|
Holder() {
|
|
bool useMockService = StaticPrefs::dom_webserial_testing_enabled();
|
|
|
|
if (useMockService) {
|
|
mPtr = MakeRefPtr<TestSerialPlatformService>();
|
|
} 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<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,
|
|
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<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);
|
|
}
|
|
|
|
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
|