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
266 lines
8.0 KiB
C++
266 lines
8.0 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 "TestSerialPlatformService.h"
|
|
|
|
#include "SerialLogging.h"
|
|
#include "nsIPipe.h"
|
|
|
|
namespace mozilla::dom {
|
|
|
|
TestSerialPlatformService::TestSerialPlatformService() {
|
|
AddDefaultMockPorts();
|
|
}
|
|
|
|
TestSerialPlatformService* TestSerialPlatformService::AsTestService() {
|
|
return this;
|
|
}
|
|
|
|
void TestSerialPlatformService::Shutdown() {
|
|
if (IsShutdown()) {
|
|
return;
|
|
}
|
|
MOZ_LOG(gWebSerialLog, LogLevel::Info,
|
|
("TestSerialPlatformService[%p]::Shutdown", this));
|
|
mMockPorts.Clear();
|
|
SerialPlatformService::Shutdown();
|
|
}
|
|
|
|
void TestSerialPlatformService::AddDefaultMockPorts() {
|
|
AddMockDevice(u"test-device-1"_ns, u"/dev/ttyUSB0"_ns, 0x2341, 0x0043);
|
|
AddMockDevice(u"test-device-2"_ns, u"/dev/ttyUSB1"_ns, 0x0403, 0x6002);
|
|
AddMockDevice(u"test-device-3"_ns, u"/dev/ttyACM0"_ns, 0x1a86, 0x7523);
|
|
}
|
|
|
|
nsresult TestSerialPlatformService::EnumeratePortsImpl(
|
|
SerialPortList& aPorts, bool* aLikelyAccessDenied) {
|
|
aPorts.Clear();
|
|
|
|
for (const auto& port : mMockPorts) {
|
|
aPorts.AppendElement(port.mInfo);
|
|
}
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
nsresult TestSerialPlatformService::OpenImpl(const nsString& aPortId,
|
|
const IPCSerialOptions& aOptions) {
|
|
MockSerialPort* port = FindPort(aPortId);
|
|
if (!port) {
|
|
return NS_ERROR_NOT_AVAILABLE;
|
|
}
|
|
|
|
if (port->mIsOpen) {
|
|
return NS_ERROR_ALREADY_INITIALIZED;
|
|
}
|
|
|
|
nsCOMPtr<nsIAsyncInputStream> reader;
|
|
nsCOMPtr<nsIAsyncOutputStream> writer;
|
|
NS_NewPipe2(getter_AddRefs(reader), getter_AddRefs(writer),
|
|
/* nonBlockingInput */ true, /* nonBlockingOutput */ true, 4096,
|
|
UINT32_MAX);
|
|
port->mPipeReadStream = reader;
|
|
port->mPipeWriteStream = writer;
|
|
|
|
port->mIsOpen = true;
|
|
port->mOptions = aOptions;
|
|
return NS_OK;
|
|
}
|
|
|
|
nsresult TestSerialPlatformService::CloseImpl(const nsString& aPortId) {
|
|
MockSerialPort* port = FindPort(aPortId);
|
|
if (!port) {
|
|
return NS_ERROR_NOT_AVAILABLE;
|
|
}
|
|
|
|
if (!port->mIsOpen) {
|
|
return NS_OK;
|
|
}
|
|
|
|
port->mIsOpen = false;
|
|
if (port->mPipeWriteStream) {
|
|
port->mPipeWriteStream->Close();
|
|
port->mPipeWriteStream = nullptr;
|
|
}
|
|
port->mPipeReadStream = nullptr;
|
|
return NS_OK;
|
|
}
|
|
|
|
nsresult TestSerialPlatformService::WriteImpl(const nsString& aPortId,
|
|
Span<const uint8_t> aData) {
|
|
MockSerialPort* port = FindPort(aPortId);
|
|
if (!port || !port->mIsOpen) {
|
|
return NS_ERROR_NOT_AVAILABLE;
|
|
}
|
|
|
|
if (aData.IsEmpty()) {
|
|
return NS_OK;
|
|
}
|
|
|
|
if (!port->mPipeWriteStream) {
|
|
return NS_ERROR_NOT_AVAILABLE;
|
|
}
|
|
|
|
uint32_t totalWritten = 0;
|
|
while (totalWritten < aData.Length()) {
|
|
uint32_t written = 0;
|
|
nsresult rv = port->mPipeWriteStream->Write(
|
|
reinterpret_cast<const char*>(aData.Elements() + totalWritten),
|
|
aData.Length() - totalWritten, &written);
|
|
if (NS_FAILED(rv)) {
|
|
return rv;
|
|
}
|
|
if (written == 0) {
|
|
return NS_ERROR_FAILURE;
|
|
}
|
|
totalWritten += written;
|
|
}
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
nsresult TestSerialPlatformService::DrainImpl(const nsString& aPortId) {
|
|
MockSerialPort* port = FindPort(aPortId);
|
|
if (!port || !port->mIsOpen) {
|
|
return NS_ERROR_NOT_AVAILABLE;
|
|
}
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
nsresult TestSerialPlatformService::FlushImpl(const nsString& aPortId,
|
|
bool aReceive) {
|
|
MockSerialPort* port = FindPort(aPortId);
|
|
if (!port || !port->mIsOpen) {
|
|
return NS_ERROR_NOT_AVAILABLE;
|
|
}
|
|
|
|
// Don't recreate the pipe pair on flush. PlatformPipeReader holds a
|
|
// duplicate of the read end and persists across flush/reattach cycles;
|
|
// recreating the pair would leave it reading from a stale pipe.
|
|
// Any buffered data is drained by SerialPortParent::StartReadPump.
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
nsresult TestSerialPlatformService::SetSignalsImpl(
|
|
const nsString& aPortId, const IPCSerialOutputSignals& aSignals) {
|
|
MockSerialPort* port = FindPort(aPortId);
|
|
if (!port || !port->mIsOpen) {
|
|
return NS_ERROR_NOT_AVAILABLE;
|
|
}
|
|
|
|
if (aSignals.dataTerminalReady().isSome()) {
|
|
port->mOutputSignals.dataTerminalReady() = aSignals.dataTerminalReady();
|
|
}
|
|
if (aSignals.requestToSend().isSome()) {
|
|
port->mOutputSignals.requestToSend() = aSignals.requestToSend();
|
|
}
|
|
if (aSignals.breakSignal().isSome()) {
|
|
port->mOutputSignals.breakSignal() = aSignals.breakSignal();
|
|
}
|
|
return NS_OK;
|
|
}
|
|
|
|
nsresult TestSerialPlatformService::GetSignalsImpl(
|
|
const nsString& aPortId, IPCSerialInputSignals& aSignals) {
|
|
MockSerialPort* port = FindPort(aPortId);
|
|
if (!port || !port->mIsOpen) {
|
|
return NS_ERROR_NOT_AVAILABLE;
|
|
}
|
|
|
|
aSignals = IPCSerialInputSignals{
|
|
port->mOutputSignals.dataTerminalReady().valueOr(
|
|
false), // dataCarrierDetect
|
|
port->mOutputSignals.requestToSend().valueOr(false), // clearToSend
|
|
false, // ringIndicator
|
|
port->mOutputSignals.dataTerminalReady().valueOr(false) // dataSetReady
|
|
};
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
nsresult TestSerialPlatformService::GetReadStreamImpl(
|
|
const nsString& aPortId, uint32_t aBufferSize,
|
|
nsIAsyncInputStream** aStream) {
|
|
MockSerialPort* port = FindPort(aPortId);
|
|
if (!port || !port->mIsOpen) {
|
|
return NS_ERROR_NOT_AVAILABLE;
|
|
}
|
|
if (!port->mPipeReadStream) {
|
|
return NS_ERROR_NOT_AVAILABLE;
|
|
}
|
|
nsCOMPtr<nsIAsyncInputStream> stream = port->mPipeReadStream;
|
|
stream.forget(aStream);
|
|
return NS_OK;
|
|
}
|
|
|
|
MockSerialPort TestSerialPlatformService::CreateMockPort(const nsString& aId,
|
|
const nsString& aPath,
|
|
uint16_t aVendorId,
|
|
uint16_t aProductId) {
|
|
MockSerialPort port;
|
|
port.mInfo.id() = aId;
|
|
port.mInfo.path() = aPath;
|
|
port.mInfo.friendlyName() = aId;
|
|
port.mInfo.usbVendorId() = Some(aVendorId);
|
|
port.mInfo.usbProductId() = Some(aProductId);
|
|
return port;
|
|
}
|
|
|
|
void TestSerialPlatformService::AddMockDevice(const nsString& aId,
|
|
const nsString& aPath,
|
|
uint16_t aVendorId,
|
|
uint16_t aProductId) {
|
|
mMockPorts.AppendElement(CreateMockPort(aId, aPath, aVendorId, aProductId));
|
|
}
|
|
|
|
MockSerialPort* TestSerialPlatformService::FindPort(const nsString& aPortId) {
|
|
for (auto& port : mMockPorts) {
|
|
if (port.mInfo.id() == aPortId) {
|
|
return &port;
|
|
}
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
void TestSerialPlatformService::SimulateDeviceConnection(const nsString& aId,
|
|
const nsString& aPath,
|
|
uint16_t aVendorId,
|
|
uint16_t aProductId) {
|
|
MockSerialPort port = CreateMockPort(aId, aPath, aVendorId, aProductId);
|
|
IPCSerialPortInfo info = port.mInfo;
|
|
mMockPorts.AppendElement(std::move(port));
|
|
NotifyPortConnected(info);
|
|
}
|
|
|
|
void TestSerialPlatformService::SimulateDeviceDisconnection(
|
|
const nsString& aId) {
|
|
mMockPorts.RemoveElementsBy(
|
|
[&aId](const MockSerialPort& port) { return port.mInfo.id() == aId; });
|
|
NotifyPortDisconnected(aId);
|
|
}
|
|
|
|
void TestSerialPlatformService::RemoveAllMockDevices() {
|
|
MOZ_LOG(gWebSerialLog, LogLevel::Info,
|
|
("TestSerialPlatformService::RemoveAllMockDevices removing %zu "
|
|
"devices",
|
|
mMockPorts.Length()));
|
|
|
|
// Notify disconnection for each device before removing
|
|
for (const auto& mockPort : mMockPorts) {
|
|
NotifyPortDisconnected(mockPort.mInfo.id());
|
|
}
|
|
|
|
mMockPorts.Clear();
|
|
}
|
|
|
|
void TestSerialPlatformService::ResetToDefaultMockDevices() {
|
|
MOZ_LOG(gWebSerialLog, LogLevel::Info,
|
|
("TestSerialPlatformService::ResetToDefaultMockDevices"));
|
|
RemoveAllMockDevices();
|
|
AddDefaultMockPorts();
|
|
}
|
|
} // namespace mozilla::dom
|