Commit 51d1888e authored by Reilly Grant's avatar Reilly Grant Committed by Commit Bot

[serial] Replace SerialPort.clearReadError()

This change replaces the clearReadError() method with implicit behavior
in the getters for the readable and writable attributes. This should be
much more convenient for developers without any change in functionality.

Bug: 893334
Change-Id: I76cab6656a2ad117fcc3d1fda5eaebd052e4d2b7
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1722011Reviewed-by: default avatarMatt Reynolds <mattreynolds@chromium.org>
Commit-Queue: Reilly Grant <reillyg@chromium.org>
Cr-Commit-Position: refs/heads/master@{#682828}
parent 661a9865
...@@ -17,6 +17,10 @@ ...@@ -17,6 +17,10 @@
namespace blink { namespace blink {
namespace { namespace {
const char kResourcesExhaustedReadBuffer[] =
"Resources exhausted allocating read buffer.";
const char kResourcesExhaustedWriteBuffer[] =
"Resources exhausted allocation write buffer.";
const char kOpenError[] = "Failed to open serial port."; const char kOpenError[] = "Failed to open serial port.";
const char kUnexpectedCloseError[] = "The port closed unexpectedly."; const char kUnexpectedCloseError[] = "The port closed unexpectedly.";
const int kMaxBufferSize = 16 * 1024 * 1024; /* 16 MiB */ const int kMaxBufferSize = 16 * 1024 * 1024; /* 16 MiB */
...@@ -122,7 +126,7 @@ ScriptPromise SerialPort::open(ScriptState* script_state, ...@@ -122,7 +126,7 @@ ScriptPromise SerialPort::open(ScriptState* script_state,
return ScriptPromise::RejectWithDOMException( return ScriptPromise::RejectWithDOMException(
script_state, MakeGarbageCollected<DOMException>( script_state, MakeGarbageCollected<DOMException>(
DOMExceptionCode::kQuotaExceededError, DOMExceptionCode::kQuotaExceededError,
"Resources exhausted allocating read buffer.")); kResourcesExhaustedReadBuffer));
} }
// Pipe handle pair for the WritableStream. // Pipe handle pair for the WritableStream.
...@@ -132,7 +136,7 @@ ScriptPromise SerialPort::open(ScriptState* script_state, ...@@ -132,7 +136,7 @@ ScriptPromise SerialPort::open(ScriptState* script_state,
return ScriptPromise::RejectWithDOMException( return ScriptPromise::RejectWithDOMException(
script_state, MakeGarbageCollected<DOMException>( script_state, MakeGarbageCollected<DOMException>(
DOMExceptionCode::kQuotaExceededError, DOMExceptionCode::kQuotaExceededError,
"Resources exhausted allocating read buffer.")); kResourcesExhaustedWriteBuffer));
} }
device::mojom::blink::SerialPortClientPtr client_ptr; device::mojom::blink::SerialPortClientPtr client_ptr;
...@@ -153,37 +157,54 @@ ScriptPromise SerialPort::open(ScriptState* script_state, ...@@ -153,37 +157,54 @@ ScriptPromise SerialPort::open(ScriptState* script_state,
return open_resolver_->Promise(); return open_resolver_->Promise();
} }
void SerialPort::clearReadError(ScriptState* script_state, ReadableStream* SerialPort::readable(ScriptState* script_state,
ExceptionState& exception_state) { ExceptionState& exception_state) {
if (!port_) { if (readable_)
exception_state.ThrowDOMException(DOMExceptionCode::kInvalidStateError, return readable_;
"Port is not open.");
return;
}
if (underlying_source_) { if (!port_ || open_resolver_)
exception_state.ThrowDOMException(DOMExceptionCode::kInvalidStateError, return nullptr;
"No error to clear.");
return;
}
mojo::ScopedDataPipeConsumerHandle readable_pipe; mojo::ScopedDataPipeConsumerHandle readable_pipe;
mojo::ScopedDataPipeProducerHandle readable_pipe_producer; mojo::ScopedDataPipeProducerHandle readable_pipe_producer;
if (!CreateDataPipe(&readable_pipe_producer, &readable_pipe)) { if (!CreateDataPipe(&readable_pipe_producer, &readable_pipe)) {
exception_state.ThrowDOMException( exception_state.ThrowDOMException(DOMExceptionCode::kQuotaExceededError,
DOMExceptionCode::kQuotaExceededError, kResourcesExhaustedReadBuffer);
"Resources exhausted allocating read buffer."); return nullptr;
return;
} }
port_->ClearReadError(std::move(readable_pipe_producer)); port_->ClearReadError(std::move(readable_pipe_producer));
InitializeReadableStream(script_state, std::move(readable_pipe)); InitializeReadableStream(script_state, std::move(readable_pipe));
return readable_;
}
WritableStream* SerialPort::writable(ScriptState* script_state,
ExceptionState& exception_state) {
if (writable_)
return writable_;
if (!port_ || open_resolver_)
return nullptr;
mojo::ScopedDataPipeProducerHandle writable_pipe;
mojo::ScopedDataPipeConsumerHandle writable_pipe_consumer;
if (!CreateDataPipe(&writable_pipe, &writable_pipe_consumer)) {
exception_state.ThrowDOMException(DOMExceptionCode::kQuotaExceededError,
kResourcesExhaustedWriteBuffer);
return nullptr;
}
port_->ClearSendError(std::move(writable_pipe_consumer));
InitializeWritableStream(script_state, std::move(writable_pipe));
return writable_;
} }
void SerialPort::close() { void SerialPort::close() {
if (underlying_source_) { if (underlying_source_) {
// The ReadableStream will report "done" when the data pipe is closed. // The ReadableStream will report "done" when the data pipe is closed.
underlying_source_->ExpectClose(); underlying_source_->ExpectClose();
underlying_source_ = nullptr;
readable_ = nullptr;
} }
if (underlying_sink_) { if (underlying_sink_) {
// TODO(crbug.com/893334): Rather than triggering an error on the // TODO(crbug.com/893334): Rather than triggering an error on the
...@@ -191,8 +212,12 @@ void SerialPort::close() { ...@@ -191,8 +212,12 @@ void SerialPort::close() {
// is locked. // is locked.
underlying_sink_->SignalErrorOnClose(MakeGarbageCollected<DOMException>( underlying_sink_->SignalErrorOnClose(MakeGarbageCollected<DOMException>(
DOMExceptionCode::kInvalidStateError, "The port has been closed.")); DOMExceptionCode::kInvalidStateError, "The port has been closed."));
underlying_sink_ = nullptr;
writable_ = nullptr;
} }
ContextDestroyed(); port_.reset();
if (client_binding_.is_bound())
client_binding_.Unbind();
} }
void SerialPort::UnderlyingSourceClosed() { void SerialPort::UnderlyingSourceClosed() {
...@@ -208,12 +233,6 @@ void SerialPort::UnderlyingSinkClosed() { ...@@ -208,12 +233,6 @@ void SerialPort::UnderlyingSinkClosed() {
void SerialPort::ContextDestroyed() { void SerialPort::ContextDestroyed() {
// Release connection-related resources as quickly as possible. // Release connection-related resources as quickly as possible.
port_.reset(); port_.reset();
if (client_binding_.is_bound())
client_binding_.Unbind();
readable_ = nullptr;
underlying_source_ = nullptr;
writable_ = nullptr;
underlying_sink_ = nullptr;
} }
void SerialPort::Trace(Visitor* visitor) { void SerialPort::Trace(Visitor* visitor) {
...@@ -266,6 +285,7 @@ bool SerialPort::CreateDataPipe(mojo::ScopedDataPipeProducerHandle* producer, ...@@ -266,6 +285,7 @@ bool SerialPort::CreateDataPipe(mojo::ScopedDataPipeProducerHandle* producer,
} }
void SerialPort::OnConnectionError() { void SerialPort::OnConnectionError() {
port_.reset();
if (open_resolver_) { if (open_resolver_) {
open_resolver_->Reject(MakeGarbageCollected<DOMException>( open_resolver_->Reject(MakeGarbageCollected<DOMException>(
DOMExceptionCode::kNetworkError, kOpenError)); DOMExceptionCode::kNetworkError, kOpenError));
...@@ -279,7 +299,8 @@ void SerialPort::OnConnectionError() { ...@@ -279,7 +299,8 @@ void SerialPort::OnConnectionError() {
underlying_sink_->SignalErrorOnClose(MakeGarbageCollected<DOMException>( underlying_sink_->SignalErrorOnClose(MakeGarbageCollected<DOMException>(
DOMExceptionCode::kNetworkError, kUnexpectedCloseError)); DOMExceptionCode::kNetworkError, kUnexpectedCloseError));
} }
ContextDestroyed(); if (client_binding_.is_bound())
client_binding_.Unbind();
} }
void SerialPort::OnOpen( void SerialPort::OnOpen(
......
...@@ -37,11 +37,9 @@ class SerialPort final : public ScriptWrappable, ...@@ -37,11 +37,9 @@ class SerialPort final : public ScriptWrappable,
~SerialPort() override; ~SerialPort() override;
// Web-exposed functions // Web-exposed functions
ReadableStream* readable() const { return readable_; }
WritableStream* writable() const { return writable_; }
ScriptPromise open(ScriptState*, const SerialOptions* options); ScriptPromise open(ScriptState*, const SerialOptions* options);
void clearReadError(ScriptState*, ExceptionState&); ReadableStream* readable(ScriptState*, ExceptionState&);
WritableStream* writable(ScriptState*, ExceptionState&);
void close(); void close();
const base::UnguessableToken& token() const { return info_->token; } const base::UnguessableToken& token() const { return info_->token; }
......
...@@ -8,15 +8,14 @@ ...@@ -8,15 +8,14 @@
Exposed(Window Serial,DedicatedWorker Serial), Exposed(Window Serial,DedicatedWorker Serial),
SecureContext SecureContext
] interface SerialPort { ] interface SerialPort {
[CallWith=ScriptState, RaisesException]
readonly attribute ReadableStream readable; readonly attribute ReadableStream readable;
[CallWith=ScriptState, RaisesException]
readonly attribute WritableStream writable; readonly attribute WritableStream writable;
[CallWith=ScriptState, MeasureAs=SerialPortOpen] [CallWith=ScriptState, MeasureAs=SerialPortOpen]
Promise<void> open(SerialOptions options); Promise<void> open(SerialOptions options);
[CallWith=ScriptState, RaisesException]
void clearReadError();
[MeasureAs=SerialPortClose] [MeasureAs=SerialPortClose]
void close(); void close();
}; };
...@@ -10,33 +10,15 @@ ...@@ -10,33 +10,15 @@
serial_test(async (t, fake) => { serial_test(async (t, fake) => {
const { port, fakePort } = await getFakeSerialPort(fake); const { port, fakePort } = await getFakeSerialPort(fake);
assert_throws('InvalidStateError', () => port.clearReadError()); port.close();
}, 'clearReadError() not allowed on a closed port.'); }, 'A SerialPort can be closed if it was never opened.');
serial_test(async (t, fake) => { serial_test(async (t, fake) => {
const { port, fakePort } = await getFakeSerialPort(fake); const { port, fakePort } = await getFakeSerialPort(fake);
await port.open({ baudrate: 9600 }); await port.open({ baudrate: 9600 });
assert_throws('InvalidStateError', () => port.clearReadError()); port.close();
}, 'clearReadError() not allowed without a reported error'); port.close();
}, 'A SerialPort can be closed multiple times.');
serial_test(async (t, fake) => {
const { port, fakePort } = await getFakeSerialPort(fake);
await port.open({ baudrate: 9600 });
fakePort.simulateParityError();
assert_throws('InvalidStateError', () => port.clearReadError());
}, 'clearReadError() not allowed before readable has been aborted');
serial_test(async (t, fake) => {
const { port, fakePort } = await getFakeSerialPort(fake);
await port.open({ baudrate: 9600 });
fakePort.simulateParityError();
await promise_rejects(t, 'NetworkError', port.readable.getReader().read());
port.clearReadError();
}, 'clearReadError() can be called after readable reports error');
</script> </script>
...@@ -13,28 +13,17 @@ serial_test(async (t, fake) => { ...@@ -13,28 +13,17 @@ serial_test(async (t, fake) => {
assert_equals(port.readable, null); assert_equals(port.readable, null);
await port.open({ baudrate: 9600 }); await port.open({ baudrate: 9600 });
assert_true(port.readable instanceof ReadableStream); const readable = port.readable;
assert_true(readable instanceof ReadableStream);
port.close(); port.close();
assert_equals(port.readable, null); assert_equals(port.readable, null);
}, 'open() and close() set and unset SerialPort.readable');
serial_test(async (t, fake) => {
const { port, fakePort } = await getFakeSerialPort(fake);
assert_equals(port.readable, null);
await port.open({ baudrate: 9600 });
assert_true(port.readable instanceof ReadableStream);
const reader = port.readable.getReader();
port.close();
const reader = readable.getReader();
const { value, done } = await reader.read(); const { value, done } = await reader.read();
assert_true(done); assert_true(done);
assert_equals(value, undefined); assert_equals(value, undefined);
assert_equals(port.readable, null); }, 'SerialPort.readable is set by open() and closes on port close');
}, 'SerialPort.readable closes on port close');
serial_test(async (t, fake) => { serial_test(async (t, fake) => {
const { port, fakePort } = await getFakeSerialPort(fake); const { port, fakePort } = await getFakeSerialPort(fake);
...@@ -81,16 +70,17 @@ serial_test(async (t, fake) => { ...@@ -81,16 +70,17 @@ serial_test(async (t, fake) => {
fakePort.write(data); fakePort.write(data);
fakePort.simulateParityError(); fakePort.simulateParityError();
let reader = port.readable.getReader(); let readable = port.readable;
let reader = readable.getReader();
let { value, done } = await reader.read(); let { value, done } = await reader.read();
assert_false(done); assert_false(done);
compareArrays(data, value); compareArrays(data, value);
await promise_rejects(t, 'NetworkError', reader.read()); await promise_rejects(t, 'NetworkError', reader.read());
assert_equals(port.readable, null); assert_not_equals(port.readable, readable);
port.clearReadError(); readable = port.readable;
assert_true(port.readable instanceof ReadableStream); assert_true(readable instanceof ReadableStream);
reader = port.readable.getReader(); reader = port.readable.getReader();
await fakePort.waitForErrorCleared(); await fakePort.waitForErrorCleared();
...@@ -101,10 +91,11 @@ serial_test(async (t, fake) => { ...@@ -101,10 +91,11 @@ serial_test(async (t, fake) => {
compareArrays(data, value); compareArrays(data, value);
port.close(); port.close();
assert_equals(port.readable, null);
({ value, done } = await reader.read()); ({ value, done } = await reader.read());
assert_true(done); assert_true(done);
assert_equals(undefined, value); assert_equals(undefined, value);
}, 'Parity error closes readable, can be cleared and more data received'); }, 'Parity error closes readable and replaces it with a new stream');
</script> </script>
...@@ -1202,7 +1202,6 @@ Starting worker: resources/global-interface-listing-worker.js ...@@ -1202,7 +1202,6 @@ Starting worker: resources/global-interface-listing-worker.js
[Worker] attribute @@toStringTag [Worker] attribute @@toStringTag
[Worker] getter readable [Worker] getter readable
[Worker] getter writable [Worker] getter writable
[Worker] method clearReadError
[Worker] method close [Worker] method close
[Worker] method constructor [Worker] method constructor
[Worker] method open [Worker] method open
......
...@@ -7448,7 +7448,6 @@ interface SerialPort ...@@ -7448,7 +7448,6 @@ interface SerialPort
attribute @@toStringTag attribute @@toStringTag
getter readable getter readable
getter writable getter writable
method clearReadError
method close method close
method constructor method constructor
method open method open
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment