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