1 // Copyright 2019 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "third_party/blink/renderer/modules/serial/serial_port_underlying_sink.h"
6
7 #include "third_party/blink/renderer/bindings/core/v8/script_promise_resolver.h"
8 #include "third_party/blink/renderer/core/dom/dom_exception.h"
9 #include "third_party/blink/renderer/modules/serial/serial_port.h"
10 #include "third_party/blink/renderer/platform/bindings/exception_state.h"
11
12 namespace blink {
13
SerialPortUnderlyingSink(SerialPort * serial_port,mojo::ScopedDataPipeProducerHandle handle)14 SerialPortUnderlyingSink::SerialPortUnderlyingSink(
15 SerialPort* serial_port,
16 mojo::ScopedDataPipeProducerHandle handle)
17 : data_pipe_(std::move(handle)),
18 watcher_(FROM_HERE, mojo::SimpleWatcher::ArmingPolicy::MANUAL),
19 serial_port_(serial_port) {
20 watcher_.Watch(data_pipe_.get(), MOJO_HANDLE_SIGNAL_WRITABLE,
21 MOJO_TRIGGER_CONDITION_SIGNALS_SATISFIED,
22 WTF::BindRepeating(&SerialPortUnderlyingSink::OnHandleReady,
23 WrapWeakPersistent(this)));
24 }
25
start(ScriptState * script_state,WritableStreamDefaultController * controller,ExceptionState & exception_state)26 ScriptPromise SerialPortUnderlyingSink::start(
27 ScriptState* script_state,
28 WritableStreamDefaultController* controller,
29 ExceptionState& exception_state) {
30 return ScriptPromise::CastUndefined(script_state);
31 }
32
write(ScriptState * script_state,ScriptValue chunk,WritableStreamDefaultController * controller,ExceptionState & exception_state)33 ScriptPromise SerialPortUnderlyingSink::write(
34 ScriptState* script_state,
35 ScriptValue chunk,
36 WritableStreamDefaultController* controller,
37 ExceptionState& exception_state) {
38 // There can only be one call to write() in progress at a time.
39 DCHECK(buffer_source_.IsNull());
40 DCHECK_EQ(0u, offset_);
41 DCHECK(!pending_operation_);
42
43 if (pending_exception_) {
44 DOMException* exception = pending_exception_;
45 pending_exception_ = nullptr;
46 serial_port_->UnderlyingSinkClosed();
47 exception_state.RethrowV8Exception(ToV8(exception, script_state));
48 return ScriptPromise();
49 }
50
51 V8ArrayBufferOrArrayBufferView::ToImpl(
52 script_state->GetIsolate(), chunk.V8Value(), buffer_source_,
53 UnionTypeConversionMode::kNotNullable, exception_state);
54 if (exception_state.HadException())
55 return ScriptPromise();
56
57 pending_operation_ =
58 MakeGarbageCollected<ScriptPromiseResolver>(script_state);
59 ScriptPromise promise = pending_operation_->Promise();
60
61 WriteData();
62 return promise;
63 }
64
close(ScriptState * script_state,ExceptionState & exception_state)65 ScriptPromise SerialPortUnderlyingSink::close(ScriptState* script_state,
66 ExceptionState& exception_state) {
67 // The specification guarantees that this will only be called after all
68 // pending writes have been completed.
69 DCHECK(!pending_operation_);
70
71 watcher_.Cancel();
72 data_pipe_.reset();
73
74 if (pending_exception_) {
75 DOMException* exception = pending_exception_;
76 pending_exception_ = nullptr;
77 exception_state.RethrowV8Exception(ToV8(exception, script_state));
78 serial_port_->UnderlyingSinkClosed();
79 return ScriptPromise();
80 }
81
82 pending_operation_ =
83 MakeGarbageCollected<ScriptPromiseResolver>(script_state);
84 serial_port_->Drain(WTF::Bind(&SerialPortUnderlyingSink::OnFlushOrDrain,
85 WrapPersistent(this)));
86 return pending_operation_->Promise();
87 }
88
abort(ScriptState * script_state,ScriptValue reason,ExceptionState & exception_state)89 ScriptPromise SerialPortUnderlyingSink::abort(ScriptState* script_state,
90 ScriptValue reason,
91 ExceptionState& exception_state) {
92 // The specification guarantees that this will only be called after all
93 // pending writes have been completed.
94 DCHECK(!pending_operation_);
95
96 watcher_.Cancel();
97 data_pipe_.reset();
98
99 if (pending_exception_) {
100 DOMException* exception = pending_exception_;
101 pending_exception_ = nullptr;
102 exception_state.RethrowV8Exception(ToV8(exception, script_state));
103 serial_port_->UnderlyingSinkClosed();
104 return ScriptPromise();
105 }
106
107 pending_operation_ =
108 MakeGarbageCollected<ScriptPromiseResolver>(script_state);
109 serial_port_->Flush(device::mojom::blink::SerialPortFlushMode::kTransmit,
110 WTF::Bind(&SerialPortUnderlyingSink::OnFlushOrDrain,
111 WrapPersistent(this)));
112 return pending_operation_->Promise();
113 }
114
SignalErrorOnClose(DOMException * exception)115 void SerialPortUnderlyingSink::SignalErrorOnClose(DOMException* exception) {
116 if (data_pipe_ || !pending_operation_) {
117 // Pipe is still open or we don't have a write operation that can be failed.
118 // Wait for PipeClosed() to be called.
119 pending_exception_ = exception;
120 return;
121 }
122
123 if (pending_operation_) {
124 pending_operation_->Reject(exception);
125 pending_operation_ = nullptr;
126 serial_port_->UnderlyingSinkClosed();
127 }
128 }
129
Trace(Visitor * visitor) const130 void SerialPortUnderlyingSink::Trace(Visitor* visitor) const {
131 visitor->Trace(serial_port_);
132 visitor->Trace(pending_exception_);
133 visitor->Trace(buffer_source_);
134 visitor->Trace(pending_operation_);
135 UnderlyingSinkBase::Trace(visitor);
136 }
137
OnHandleReady(MojoResult result,const mojo::HandleSignalsState &)138 void SerialPortUnderlyingSink::OnHandleReady(MojoResult result,
139 const mojo::HandleSignalsState&) {
140 switch (result) {
141 case MOJO_RESULT_OK:
142 WriteData();
143 break;
144 case MOJO_RESULT_FAILED_PRECONDITION:
145 PipeClosed();
146 break;
147 default:
148 NOTREACHED();
149 }
150 }
151
OnFlushOrDrain()152 void SerialPortUnderlyingSink::OnFlushOrDrain() {
153 ScriptPromiseResolver* resolver = pending_operation_;
154 pending_operation_ = nullptr;
155
156 DOMException* exception = pending_exception_;
157 pending_exception_ = nullptr;
158
159 serial_port_->UnderlyingSinkClosed();
160
161 if (exception) {
162 resolver->Reject(exception);
163 } else {
164 resolver->Resolve();
165 }
166 }
167
WriteData()168 void SerialPortUnderlyingSink::WriteData() {
169 DCHECK(data_pipe_);
170 DCHECK(pending_operation_);
171 DCHECK(!buffer_source_.IsNull());
172
173 const uint8_t* data = nullptr;
174 uint32_t length = 0;
175 size_t byte_size = 0;
176 if (buffer_source_.IsArrayBuffer()) {
177 DOMArrayBuffer* array = buffer_source_.GetAsArrayBuffer();
178 byte_size = array->ByteLength();
179 data = static_cast<const uint8_t*>(array->Data());
180 } else {
181 DOMArrayBufferView* view = buffer_source_.GetAsArrayBufferView().View();
182 byte_size = view->byteLength();
183 data = static_cast<const uint8_t*>(view->BaseAddress());
184 }
185 if (byte_size > std::numeric_limits<uint32_t>::max()) {
186 pending_exception_ = DOMException::Create(
187 "Buffer size exceeds maximum heap object size.", "DataError");
188 PipeClosed();
189 return;
190 }
191 length = static_cast<uint32_t>(byte_size);
192
193 DCHECK_LT(offset_, length);
194 data += offset_;
195 uint32_t num_bytes = length - offset_;
196
197 MojoResult result =
198 data_pipe_->WriteData(data, &num_bytes, MOJO_WRITE_DATA_FLAG_NONE);
199 switch (result) {
200 case MOJO_RESULT_OK:
201 offset_ += num_bytes;
202 if (offset_ == length) {
203 buffer_source_ = ArrayBufferOrArrayBufferView();
204 offset_ = 0;
205 pending_operation_->Resolve();
206 pending_operation_ = nullptr;
207 break;
208 }
209 FALLTHROUGH;
210 case MOJO_RESULT_SHOULD_WAIT:
211 watcher_.ArmOrNotify();
212 break;
213 case MOJO_RESULT_FAILED_PRECONDITION:
214 PipeClosed();
215 break;
216 default:
217 NOTREACHED();
218 }
219 }
220
PipeClosed()221 void SerialPortUnderlyingSink::PipeClosed() {
222 DCHECK(pending_operation_);
223
224 watcher_.Cancel();
225 data_pipe_.reset();
226
227 if (pending_exception_) {
228 DOMException* exception = pending_exception_;
229 pending_exception_ = nullptr;
230 serial_port_->UnderlyingSinkClosed();
231 pending_operation_->Reject(exception);
232 pending_operation_ = nullptr;
233 }
234 }
235
236 } // namespace blink
237