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
* 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<Promise> SerialPortWriteAlgorithms::CloseCallbackImpl(
JSContext* aCx, ErrorResult& aRv) {
return CloseOrAbortImpl(true, aRv);
}
already_AddRefed<Promise> SerialPortWriteAlgorithms::AbortCallbackImpl(
JSContext* aCx, const Optional<JS::Handle<JS::Value>>& aReason,
ErrorResult& aRv) {
return CloseOrAbortImpl(false, aRv);
}
already_AddRefed<Promise> SerialPortWriteAlgorithms::CloseOrAbortImpl(
bool aDrain, ErrorResult& aRv) {
if (!mPort) {
aRv.ThrowInvalidStateError("Port is not initialized");
return nullptr;
}
RefPtr<SerialPortChild> 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 = Promise::CreateInfallible(mPort->GetOwnerGlobal());
nsCOMPtr<nsISerialEventTarget> owningThread = GetCurrentSerialEventTarget();
nsISerialEventTarget* actorTarget = child->GetActorEventTarget();
if (!actorTarget) {
promise->MaybeRejectWithNetworkError("Actor not available");
return promise.forget();
}
RefPtr<SerialPortWriteAlgorithms> 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<uint32_t>(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<int>(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<Promise> SerialPortReadAlgorithms::CancelCallbackImpl(
JSContext* aCx, const Optional<JS::Handle<JS::Value>>& aReason,
ErrorResult& aRv) {
if (!mPort) {
aRv.ThrowInvalidStateError("Port is not initialized");
return nullptr;
}
RefPtr<SerialPortChild> 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 = Promise::CreateInfallible(mPort->GetOwnerGlobal());
nsCOMPtr<nsISerialEventTarget> owningThread = GetCurrentSerialEventTarget();
nsISerialEventTarget* actorTarget = child->GetActorEventTarget();
if (!actorTarget) {
promise->MaybeRejectWithNetworkError("Actor not available");
return promise.forget();
}
RefPtr<SerialPortReadAlgorithms> 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<uint32_t>(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<int>(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