/* -*- 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 "SerialPortStreamAlgorithms.h" #include "SerialLogging.h" #include "mozilla/dom/Promise.h" #include "mozilla/dom/ReadableStream.h" #include "mozilla/dom/SerialPort.h" #include "mozilla/dom/SerialPortChild.h" #include "nsThreadUtils.h" namespace mozilla::dom { NS_IMPL_CYCLE_COLLECTION_INHERITED(SerialPortWriteAlgorithms, WritableStreamToOutputAlgorithms, mPort) NS_IMPL_ADDREF_INHERITED(SerialPortWriteAlgorithms, WritableStreamToOutputAlgorithms) NS_IMPL_RELEASE_INHERITED(SerialPortWriteAlgorithms, WritableStreamToOutputAlgorithms) NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(SerialPortWriteAlgorithms) NS_INTERFACE_MAP_END_INHERITING(WritableStreamToOutputAlgorithms) SerialPortWriteAlgorithms::SerialPortWriteAlgorithms( nsIGlobalObject* aParent, nsIAsyncOutputStream* aOutput, SerialPort* aPort) : WritableStreamToOutputAlgorithms(aParent, aOutput), mPort(aPort) { MOZ_LOG(gWebSerialLog, LogLevel::Debug, ("SerialPortWriteAlgorithms[%p] created for port %p", this, aPort)); } already_AddRefed SerialPortWriteAlgorithms::CloseCallbackImpl( JSContext* aCx, ErrorResult& aRv) { return CloseOrAbortImpl(true, aRv); } already_AddRefed SerialPortWriteAlgorithms::AbortCallbackImpl( JSContext* aCx, const Optional>& aReason, ErrorResult& aRv) { return CloseOrAbortImpl(false, aRv); } already_AddRefed SerialPortWriteAlgorithms::CloseOrAbortImpl( bool aDrain, ErrorResult& aRv) { if (!mPort) { aRv.ThrowInvalidStateError("Port is not initialized"); return nullptr; } RefPtr child = mPort->GetChild(); if (!child) { aRv.ThrowInvalidStateError("Port is not connected"); return nullptr; } if (aDrain) { MOZ_LOG( gWebSerialLog, LogLevel::Debug, ("SerialPortWriteAlgorithms[%p]::CloseCallbackImpl draining", this)); // Close the DataPipe so the parent knows no more data is coming. CloseOutput(); } else { MOZ_LOG( gWebSerialLog, LogLevel::Debug, ("SerialPortWriteAlgorithms[%p]::AbortCallbackImpl flushing", this)); CloseOutputWithStatus(NS_ERROR_ABORT); } RefPtr promise = Promise::CreateInfallible(mPort->GetOwnerGlobal()); nsCOMPtr owningThread = GetCurrentSerialEventTarget(); nsISerialEventTarget* actorTarget = child->GetActorEventTarget(); if (!actorTarget) { promise->MaybeRejectWithNetworkError("Actor not available"); return promise.forget(); } RefPtr self = this; nsCString operationName(aDrain ? "drain" : "flush"); InvokeAsync(actorTarget, aDrain ? "SerialPortWriteAlgorithms::SendDrain" : "SerialPortWriteAlgorithms::SendFlush", [child, aDrain]() { return aDrain ? child->SendDrain() : child->SendFlush(false); }) ->Then( owningThread, __func__, [promise, self, aDrain, operationName](nsresult aResult) { if (NS_SUCCEEDED(aResult)) { MOZ_LOG(gWebSerialLog, LogLevel::Debug, ("SerialPortWriteAlgorithms[%p] %s succeeded", self.get(), operationName.get())); promise->MaybeResolveWithUndefined(); } else { MOZ_LOG(gWebSerialLog, LogLevel::Error, ("SerialPortWriteAlgorithms[%p] %s failed: 0x%08x", self.get(), operationName.get(), static_cast(aResult))); if (aDrain) { promise->MaybeRejectWithNetworkError( "Failed to drain transmit buffers"); } else { promise->MaybeRejectWithNetworkError( "Failed to discard transmit buffers"); } } }, [promise, self, aDrain, operationName](mozilla::ipc::ResponseRejectReason aReason) { MOZ_LOG( gWebSerialLog, LogLevel::Error, ("SerialPortWriteAlgorithms[%p] %s IPC error " "(reason: %d)", self.get(), operationName.get(), static_cast(aReason))); if (aDrain) { promise->MaybeRejectWithNetworkError( "Failed to drain transmit buffers: IPC " "communication error"); } else { promise->MaybeRejectWithNetworkError( "Failed to discard transmit buffers: IPC " "communication error"); } }); return promise.forget(); } SerialPortWriteAlgorithms::~SerialPortWriteAlgorithms() = default; void SerialPortWriteAlgorithms::ReleaseObjects() { MOZ_LOG(gWebSerialLog, LogLevel::Debug, ("SerialPortWriteAlgorithms[%p]::ReleaseObjects", this)); WritableStreamToOutputAlgorithms::ReleaseObjects(); mPort = nullptr; } NS_IMPL_CYCLE_COLLECTION_INHERITED(SerialPortReadAlgorithms, InputToReadableStreamAlgorithms, mPort) NS_IMPL_ADDREF_INHERITED(SerialPortReadAlgorithms, InputToReadableStreamAlgorithms) NS_IMPL_RELEASE_INHERITED(SerialPortReadAlgorithms, InputToReadableStreamAlgorithms) NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(SerialPortReadAlgorithms) NS_INTERFACE_MAP_END_INHERITING(InputToReadableStreamAlgorithms) SerialPortReadAlgorithms::SerialPortReadAlgorithms(JSContext* aCx, nsIAsyncInputStream* aInput, ReadableStream* aStream, SerialPort* aPort) : InputToReadableStreamAlgorithms(aCx, aInput, aStream), mPort(aPort), mInputStream(aInput) { MOZ_LOG(gWebSerialLog, LogLevel::Debug, ("SerialPortReadAlgorithms[%p] created for port %p", this, aPort)); } already_AddRefed SerialPortReadAlgorithms::CancelCallbackImpl( JSContext* aCx, const Optional>& aReason, ErrorResult& aRv) { if (!mPort) { aRv.ThrowInvalidStateError("Port is not initialized"); return nullptr; } RefPtr child = mPort->GetChild(); if (!child) { aRv.ThrowInvalidStateError("Port is not connected"); return nullptr; } MOZ_LOG(gWebSerialLog, LogLevel::Debug, ("SerialPortReadAlgorithms[%p]::CancelCallbackImpl flushing receive", this)); // Close the DataPipe to signal cancellation to the parent. if (mInputStream) { mInputStream->CloseWithStatus(NS_ERROR_ABORT); } RefPtr promise = Promise::CreateInfallible(mPort->GetOwnerGlobal()); nsCOMPtr owningThread = GetCurrentSerialEventTarget(); nsISerialEventTarget* actorTarget = child->GetActorEventTarget(); if (!actorTarget) { promise->MaybeRejectWithNetworkError("Actor not available"); return promise.forget(); } RefPtr self = this; InvokeAsync(actorTarget, "SerialPortReadAlgorithms::SendFlush", [child]() { return child->SendFlush(true); }) ->Then( owningThread, __func__, [promise, self](nsresult aResult) { if (NS_SUCCEEDED(aResult)) { MOZ_LOG(gWebSerialLog, LogLevel::Debug, ("SerialPortReadAlgorithms[%p] flush receive succeeded", self.get())); promise->MaybeResolveWithUndefined(); } else { MOZ_LOG( gWebSerialLog, LogLevel::Error, ("SerialPortReadAlgorithms[%p] flush receive failed: 0x%08x", self.get(), static_cast(aResult))); promise->MaybeRejectWithNetworkError( "Failed to discard receive buffers"); } }, [promise, self](mozilla::ipc::ResponseRejectReason aReason) { MOZ_LOG(gWebSerialLog, LogLevel::Error, ("SerialPortReadAlgorithms[%p] flush receive IPC error " "(reason: %d)", self.get(), static_cast(aReason))); promise->MaybeRejectWithNetworkError( "Failed to discard receive buffers: IPC communication error"); }); return promise.forget(); } SerialPortReadAlgorithms::~SerialPortReadAlgorithms() = default; void SerialPortReadAlgorithms::ReleaseObjects() { MOZ_LOG(gWebSerialLog, LogLevel::Debug, ("SerialPortReadAlgorithms[%p]::ReleaseObjects", this)); InputToReadableStreamAlgorithms::ReleaseObjects(); mPort = nullptr; mInputStream = nullptr; } } // namespace mozilla::dom