1 // Copyright 2015 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/webusb/usb_device.h"
6
7 #include <algorithm>
8 #include <iterator>
9 #include <utility>
10
11 #include "third_party/blink/public/platform/platform.h"
12 #include "third_party/blink/renderer/bindings/core/v8/script_promise.h"
13 #include "third_party/blink/renderer/bindings/core/v8/script_promise_resolver.h"
14 #include "third_party/blink/renderer/bindings/core/v8/to_v8_for_core.h"
15 #include "third_party/blink/renderer/bindings/modules/v8/v8_usb_control_transfer_parameters.h"
16 #include "third_party/blink/renderer/core/dom/dom_exception.h"
17 #include "third_party/blink/renderer/core/inspector/console_message.h"
18 #include "third_party/blink/renderer/core/typed_arrays/dom_array_buffer.h"
19 #include "third_party/blink/renderer/core/typed_arrays/dom_array_buffer_view.h"
20 #include "third_party/blink/renderer/modules/webusb/usb_configuration.h"
21 #include "third_party/blink/renderer/modules/webusb/usb_in_transfer_result.h"
22 #include "third_party/blink/renderer/modules/webusb/usb_isochronous_in_transfer_result.h"
23 #include "third_party/blink/renderer/modules/webusb/usb_isochronous_out_transfer_result.h"
24 #include "third_party/blink/renderer/modules/webusb/usb_out_transfer_result.h"
25 #include "third_party/blink/renderer/platform/heap/heap.h"
26 #include "third_party/blink/renderer/platform/mojo/mojo_helper.h"
27 #include "third_party/blink/renderer/platform/wtf/assertions.h"
28 #include "third_party/blink/renderer/platform/wtf/functional.h"
29
30 using device::mojom::blink::UsbControlTransferParamsPtr;
31 using device::mojom::blink::UsbControlTransferRecipient;
32 using device::mojom::blink::UsbControlTransferType;
33 using device::mojom::blink::UsbDevice;
34 using device::mojom::blink::UsbDeviceInfoPtr;
35 using device::mojom::blink::UsbIsochronousPacketPtr;
36 using device::mojom::blink::UsbOpenDeviceError;
37 using device::mojom::blink::UsbTransferDirection;
38 using device::mojom::blink::UsbTransferStatus;
39
40 namespace blink {
41
42 namespace {
43
44 const char kBufferTooBig[] = "The data buffer exceeded its maximum size.";
45 const char kDetachedBuffer[] = "The data buffer has been detached.";
46 const char kDeviceStateChangeInProgress[] =
47 "An operation that changes the device state is in progress.";
48 const char kDeviceDisconnected[] = "The device was disconnected.";
49 const char kInterfaceNotFound[] =
50 "The interface number provided is not supported by the device in its "
51 "current configuration.";
52 const char kInterfaceStateChangeInProgress[] =
53 "An operation that changes interface state is in progress.";
54 const char kOpenRequired[] = "The device must be opened first.";
55
56 #if defined(OS_CHROMEOS)
57 const char kExtensionProtocol[] = "chrome-extension";
58
59 // These whitelisted Imprivata extensions can claim the protected HID interface
60 // class (used as badge readers), see crbug.com/1065112 and crbug.com/995294.
61 // This list needs to be alphabetically sorted for quick access via binary
62 // search.
63 const char* kImprivataExtensionIds[] = {
64 "baobpecgllpajfeojepgedjdlnlfffde", "bnfoibgpjolimhppjmligmcgklpboloj",
65 "cdgickkdpbekbnalbmpgochbninibkko", "cjakdianfealdjlapagfagpdpemoppba",
66 "cokoeepjbmmnhgdhlkpahohdaiedfjgn", "dahgfgiifpnaoajmloofonkndaaafacp",
67 "dbknmmkopacopifbkgookcdbhfnggjjh", "ddcjglpbfbibgepfffpklmpihphbcdco",
68 "dhodapiemamlmhlhblgcibabhdkohlen", "dlahpllbhpbkfnoiedkgombmegnnjopi",
69 "egfpnfjeaopimgpiioeedbpmojdapaip", "fnbibocngjnefolmcodjkkghijpdlnfm",
70 "jcnflhjcfjkplgkcinikhbgbhfldkadl", "jkfjfbelolphkjckiolfcakgalloegek",
71 "kmhpgpnbglclbaccjjgoioogjlnfgbne", "lpimkpkllnkdlcigdbgmabfplniahkgm",
72 "odehonhhkcjnbeaomlodfkjaecbmhklm", "olnmflhcfkifkgbiegcoabineoknmbjc",
73 "omificdfgpipkkpdhbjmefgfgbppehke", "phjobickjiififdadeoepbdaciefacfj",
74 "pkeacbojooejnjolgjdecbpnloibpafm", "pllbepacblmgialkkpcceohmjakafnbb",
75 "plpogimmgnkkiflhpidbibfmgpkaofec", "pmhiabnkkchjeaehcodceadhdpfejmmd",
76 };
77 const char** kExtensionNameMappingsEnd = std::end(kImprivataExtensionIds);
78
IsCStrBefore(const char * first,const char * second)79 bool IsCStrBefore(const char* first, const char* second) {
80 return strcmp(first, second) < 0;
81 }
82
IsClassWhitelistedForExtension(uint8_t class_code,const KURL & url)83 bool IsClassWhitelistedForExtension(uint8_t class_code, const KURL& url) {
84 if (url.Protocol() != kExtensionProtocol)
85 return false;
86
87 switch (class_code) {
88 case 0x03: // HID
89 DCHECK(std::is_sorted(kImprivataExtensionIds, kExtensionNameMappingsEnd,
90 IsCStrBefore));
91 return std::binary_search(kImprivataExtensionIds,
92 kExtensionNameMappingsEnd,
93 url.Host().Utf8().c_str(), IsCStrBefore);
94 default:
95 return false;
96 }
97 }
98 #endif // defined(OS_CHROMEOS)
99
ConvertFatalTransferStatus(const UsbTransferStatus & status)100 DOMException* ConvertFatalTransferStatus(const UsbTransferStatus& status) {
101 switch (status) {
102 case UsbTransferStatus::TRANSFER_ERROR:
103 return MakeGarbageCollected<DOMException>(
104 DOMExceptionCode::kNetworkError, "A transfer error has occurred.");
105 case UsbTransferStatus::PERMISSION_DENIED:
106 return MakeGarbageCollected<DOMException>(
107 DOMExceptionCode::kSecurityError, "The transfer was not allowed.");
108 case UsbTransferStatus::TIMEOUT:
109 return MakeGarbageCollected<DOMException>(DOMExceptionCode::kTimeoutError,
110 "The transfer timed out.");
111 case UsbTransferStatus::CANCELLED:
112 return MakeGarbageCollected<DOMException>(DOMExceptionCode::kAbortError,
113 "The transfer was cancelled.");
114 case UsbTransferStatus::DISCONNECT:
115 return MakeGarbageCollected<DOMException>(
116 DOMExceptionCode::kNotFoundError, kDeviceDisconnected);
117 case UsbTransferStatus::COMPLETED:
118 case UsbTransferStatus::STALLED:
119 case UsbTransferStatus::BABBLE:
120 case UsbTransferStatus::SHORT_PACKET:
121 return nullptr;
122 default:
123 NOTREACHED();
124 return nullptr;
125 }
126 }
127
ConvertTransferStatus(const UsbTransferStatus & status)128 String ConvertTransferStatus(const UsbTransferStatus& status) {
129 switch (status) {
130 case UsbTransferStatus::COMPLETED:
131 case UsbTransferStatus::SHORT_PACKET:
132 return "ok";
133 case UsbTransferStatus::STALLED:
134 return "stall";
135 case UsbTransferStatus::BABBLE:
136 return "babble";
137 default:
138 NOTREACHED();
139 return "";
140 }
141 }
142
ConvertBufferSource(const ArrayBufferOrArrayBufferView & buffer_source,Vector<uint8_t> * vector,ScriptPromiseResolver * resolver)143 bool ConvertBufferSource(const ArrayBufferOrArrayBufferView& buffer_source,
144 Vector<uint8_t>* vector,
145 ScriptPromiseResolver* resolver) {
146 DCHECK(!buffer_source.IsNull());
147 if (buffer_source.IsArrayBuffer()) {
148 DOMArrayBuffer* array_buffer = buffer_source.GetAsArrayBuffer();
149 if (array_buffer->IsDetached()) {
150 resolver->Reject(MakeGarbageCollected<DOMException>(
151 DOMExceptionCode::kInvalidStateError, kDetachedBuffer));
152 return false;
153 }
154 if (array_buffer->ByteLengthAsSizeT() >
155 std::numeric_limits<wtf_size_t>::max()) {
156 resolver->Reject(MakeGarbageCollected<DOMException>(
157 DOMExceptionCode::kDataError, kBufferTooBig));
158 return false;
159 }
160
161 vector->Append(static_cast<uint8_t*>(array_buffer->Data()),
162 static_cast<wtf_size_t>(array_buffer->ByteLengthAsSizeT()));
163 } else {
164 DOMArrayBufferView* view = buffer_source.GetAsArrayBufferView().View();
165 if (!view->buffer() || view->buffer()->IsDetached()) {
166 resolver->Reject(MakeGarbageCollected<DOMException>(
167 DOMExceptionCode::kInvalidStateError, kDetachedBuffer));
168 return false;
169 }
170 if (view->byteLengthAsSizeT() > std::numeric_limits<wtf_size_t>::max()) {
171 resolver->Reject(MakeGarbageCollected<DOMException>(
172 DOMExceptionCode::kDataError, kBufferTooBig));
173 return false;
174 }
175
176 vector->Append(static_cast<uint8_t*>(view->BaseAddress()),
177 static_cast<wtf_size_t>(view->byteLengthAsSizeT()));
178 }
179 return true;
180 }
181
182 } // namespace
183
USBDevice(UsbDeviceInfoPtr device_info,mojo::PendingRemote<UsbDevice> device,ExecutionContext * context)184 USBDevice::USBDevice(UsbDeviceInfoPtr device_info,
185 mojo::PendingRemote<UsbDevice> device,
186 ExecutionContext* context)
187 : ExecutionContextLifecycleObserver(context),
188 device_info_(std::move(device_info)),
189 device_(std::move(device)),
190 opened_(false),
191 device_state_change_in_progress_(false),
192 configuration_index_(kNotFound) {
193 if (device_) {
194 device_.set_disconnect_handler(
195 WTF::Bind(&USBDevice::OnConnectionError, WrapWeakPersistent(this)));
196 }
197 wtf_size_t configuration_index =
198 FindConfigurationIndex(Info().active_configuration);
199 if (configuration_index != kNotFound)
200 OnConfigurationSelected(true /* success */, configuration_index);
201 }
202
~USBDevice()203 USBDevice::~USBDevice() {
204 // |m_device| may still be valid but there should be no more outstanding
205 // requests because each holds a persistent handle to this object.
206 DCHECK(device_requests_.IsEmpty());
207 }
208
IsInterfaceClaimed(wtf_size_t configuration_index,wtf_size_t interface_index) const209 bool USBDevice::IsInterfaceClaimed(wtf_size_t configuration_index,
210 wtf_size_t interface_index) const {
211 return configuration_index_ != kNotFound &&
212 configuration_index_ == configuration_index &&
213 claimed_interfaces_[interface_index];
214 }
215
SelectedAlternateInterface(wtf_size_t interface_index) const216 wtf_size_t USBDevice::SelectedAlternateInterface(
217 wtf_size_t interface_index) const {
218 return selected_alternates_[interface_index];
219 }
220
configuration() const221 USBConfiguration* USBDevice::configuration() const {
222 if (configuration_index_ != kNotFound)
223 return USBConfiguration::Create(this, configuration_index_);
224 return nullptr;
225 }
226
configurations() const227 HeapVector<Member<USBConfiguration>> USBDevice::configurations() const {
228 wtf_size_t num_configurations = Info().configurations.size();
229 HeapVector<Member<USBConfiguration>> configurations(num_configurations);
230 for (wtf_size_t i = 0; i < num_configurations; ++i)
231 configurations[i] = USBConfiguration::Create(this, i);
232 return configurations;
233 }
234
open(ScriptState * script_state)235 ScriptPromise USBDevice::open(ScriptState* script_state) {
236 auto* resolver = MakeGarbageCollected<ScriptPromiseResolver>(script_state);
237 ScriptPromise promise = resolver->Promise();
238 if (EnsureNoDeviceOrInterfaceChangeInProgress(resolver)) {
239 if (opened_) {
240 resolver->Resolve();
241 } else {
242 device_state_change_in_progress_ = true;
243 device_requests_.insert(resolver);
244 device_->Open(WTF::Bind(&USBDevice::AsyncOpen, WrapPersistent(this),
245 WrapPersistent(resolver)));
246 }
247 }
248 return promise;
249 }
250
close(ScriptState * script_state)251 ScriptPromise USBDevice::close(ScriptState* script_state) {
252 auto* resolver = MakeGarbageCollected<ScriptPromiseResolver>(script_state);
253 ScriptPromise promise = resolver->Promise();
254 if (EnsureNoDeviceOrInterfaceChangeInProgress(resolver)) {
255 if (!opened_) {
256 resolver->Resolve();
257 } else {
258 device_state_change_in_progress_ = true;
259 device_requests_.insert(resolver);
260 device_->Close(WTF::Bind(&USBDevice::AsyncClose, WrapPersistent(this),
261 WrapPersistent(resolver)));
262 }
263 }
264 return promise;
265 }
266
selectConfiguration(ScriptState * script_state,uint8_t configuration_value)267 ScriptPromise USBDevice::selectConfiguration(ScriptState* script_state,
268 uint8_t configuration_value) {
269 auto* resolver = MakeGarbageCollected<ScriptPromiseResolver>(script_state);
270 ScriptPromise promise = resolver->Promise();
271 if (EnsureNoDeviceOrInterfaceChangeInProgress(resolver)) {
272 if (!opened_) {
273 resolver->Reject(MakeGarbageCollected<DOMException>(
274 DOMExceptionCode::kInvalidStateError, kOpenRequired));
275 } else {
276 wtf_size_t configuration_index =
277 FindConfigurationIndex(configuration_value);
278 if (configuration_index == kNotFound) {
279 resolver->Reject(
280 MakeGarbageCollected<DOMException>(DOMExceptionCode::kNotFoundError,
281 "The configuration value "
282 "provided is not supported by "
283 "the device."));
284 } else if (configuration_index_ == configuration_index) {
285 resolver->Resolve();
286 } else {
287 device_state_change_in_progress_ = true;
288 device_requests_.insert(resolver);
289 device_->SetConfiguration(
290 configuration_value,
291 WTF::Bind(&USBDevice::AsyncSelectConfiguration,
292 WrapPersistent(this), configuration_index,
293 WrapPersistent(resolver)));
294 }
295 }
296 }
297 return promise;
298 }
299
claimInterface(ScriptState * script_state,uint8_t interface_number)300 ScriptPromise USBDevice::claimInterface(ScriptState* script_state,
301 uint8_t interface_number) {
302 auto* resolver = MakeGarbageCollected<ScriptPromiseResolver>(script_state);
303 ScriptPromise promise = resolver->Promise();
304 if (EnsureDeviceConfigured(resolver)) {
305 wtf_size_t interface_index = FindInterfaceIndex(interface_number);
306 if (interface_index == kNotFound) {
307 resolver->Reject(MakeGarbageCollected<DOMException>(
308 DOMExceptionCode::kNotFoundError, kInterfaceNotFound));
309 } else if (interface_state_change_in_progress_[interface_index]) {
310 resolver->Reject(MakeGarbageCollected<DOMException>(
311 DOMExceptionCode::kInvalidStateError,
312 kInterfaceStateChangeInProgress));
313 } else if (claimed_interfaces_[interface_index]) {
314 resolver->Resolve();
315 } else if (IsProtectedInterfaceClass(interface_index)) {
316 GetExecutionContext()->AddConsoleMessage(
317 MakeGarbageCollected<ConsoleMessage>(
318 mojom::ConsoleMessageSource::kJavaScript,
319 mojom::ConsoleMessageLevel::kWarning,
320 "An attempt to claim a USB device interface "
321 "has been blocked because it "
322 "implements a protected interface class."));
323 resolver->Reject(MakeGarbageCollected<DOMException>(
324 DOMExceptionCode::kSecurityError,
325 "The requested interface implements a protected class."));
326 } else {
327 interface_state_change_in_progress_[interface_index] = true;
328 device_requests_.insert(resolver);
329 device_->ClaimInterface(
330 interface_number,
331 WTF::Bind(&USBDevice::AsyncClaimInterface, WrapPersistent(this),
332 interface_index, WrapPersistent(resolver)));
333 }
334 }
335 return promise;
336 }
337
releaseInterface(ScriptState * script_state,uint8_t interface_number)338 ScriptPromise USBDevice::releaseInterface(ScriptState* script_state,
339 uint8_t interface_number) {
340 auto* resolver = MakeGarbageCollected<ScriptPromiseResolver>(script_state);
341 ScriptPromise promise = resolver->Promise();
342 if (EnsureDeviceConfigured(resolver)) {
343 wtf_size_t interface_index = FindInterfaceIndex(interface_number);
344 if (interface_index == kNotFound) {
345 resolver->Reject(
346 MakeGarbageCollected<DOMException>(DOMExceptionCode::kNotFoundError,
347 "The interface number provided is "
348 "not supported by the device in "
349 "its current configuration."));
350 } else if (interface_state_change_in_progress_[interface_index]) {
351 resolver->Reject(MakeGarbageCollected<DOMException>(
352 DOMExceptionCode::kInvalidStateError,
353 kInterfaceStateChangeInProgress));
354 } else if (!claimed_interfaces_[interface_index]) {
355 resolver->Resolve();
356 } else {
357 // Mark this interface's endpoints unavailable while its state is
358 // changing.
359 SetEndpointsForInterface(interface_index, false);
360 interface_state_change_in_progress_[interface_index] = true;
361 device_requests_.insert(resolver);
362 device_->ReleaseInterface(
363 interface_number,
364 WTF::Bind(&USBDevice::AsyncReleaseInterface, WrapPersistent(this),
365 interface_index, WrapPersistent(resolver)));
366 }
367 }
368 return promise;
369 }
370
selectAlternateInterface(ScriptState * script_state,uint8_t interface_number,uint8_t alternate_setting)371 ScriptPromise USBDevice::selectAlternateInterface(ScriptState* script_state,
372 uint8_t interface_number,
373 uint8_t alternate_setting) {
374 auto* resolver = MakeGarbageCollected<ScriptPromiseResolver>(script_state);
375 ScriptPromise promise = resolver->Promise();
376 if (EnsureInterfaceClaimed(interface_number, resolver)) {
377 // TODO(reillyg): This is duplicated work.
378 wtf_size_t interface_index = FindInterfaceIndex(interface_number);
379 DCHECK_NE(interface_index, kNotFound);
380 wtf_size_t alternate_index =
381 FindAlternateIndex(interface_index, alternate_setting);
382 if (alternate_index == kNotFound) {
383 resolver->Reject(MakeGarbageCollected<DOMException>(
384 DOMExceptionCode::kNotFoundError,
385 "The alternate setting provided is "
386 "not supported by the device in "
387 "its current configuration."));
388 } else {
389 // Mark this old alternate interface's endpoints unavailable while
390 // the change is in progress.
391 SetEndpointsForInterface(interface_index, false);
392 interface_state_change_in_progress_[interface_index] = true;
393 device_requests_.insert(resolver);
394 device_->SetInterfaceAlternateSetting(
395 interface_number, alternate_setting,
396 WTF::Bind(&USBDevice::AsyncSelectAlternateInterface,
397 WrapPersistent(this), interface_number, alternate_setting,
398 WrapPersistent(resolver)));
399 }
400 }
401 return promise;
402 }
403
controlTransferIn(ScriptState * script_state,const USBControlTransferParameters * setup,unsigned length)404 ScriptPromise USBDevice::controlTransferIn(
405 ScriptState* script_state,
406 const USBControlTransferParameters* setup,
407 unsigned length) {
408 auto* resolver = MakeGarbageCollected<ScriptPromiseResolver>(script_state);
409 ScriptPromise promise = resolver->Promise();
410 if (!EnsureNoDeviceOrInterfaceChangeInProgress(resolver))
411 return promise;
412
413 if (!opened_) {
414 resolver->Reject(MakeGarbageCollected<DOMException>(
415 DOMExceptionCode::kInvalidStateError, kOpenRequired));
416 return promise;
417 }
418
419 auto parameters = ConvertControlTransferParameters(setup, resolver);
420 if (!parameters)
421 return promise;
422
423 device_requests_.insert(resolver);
424 device_->ControlTransferIn(
425 std::move(parameters), length, 0,
426 WTF::Bind(&USBDevice::AsyncControlTransferIn, WrapPersistent(this),
427 WrapPersistent(resolver)));
428 return promise;
429 }
430
controlTransferOut(ScriptState * script_state,const USBControlTransferParameters * setup)431 ScriptPromise USBDevice::controlTransferOut(
432 ScriptState* script_state,
433 const USBControlTransferParameters* setup) {
434 auto* resolver = MakeGarbageCollected<ScriptPromiseResolver>(script_state);
435 ScriptPromise promise = resolver->Promise();
436 if (!EnsureNoDeviceOrInterfaceChangeInProgress(resolver))
437 return promise;
438
439 if (!opened_) {
440 resolver->Reject(MakeGarbageCollected<DOMException>(
441 DOMExceptionCode::kInvalidStateError, kOpenRequired));
442 return promise;
443 }
444
445 auto parameters = ConvertControlTransferParameters(setup, resolver);
446 if (!parameters)
447 return promise;
448
449 device_requests_.insert(resolver);
450 device_->ControlTransferOut(
451 std::move(parameters), Vector<uint8_t>(), 0,
452 WTF::Bind(&USBDevice::AsyncControlTransferOut, WrapPersistent(this), 0,
453 WrapPersistent(resolver)));
454 return promise;
455 }
456
controlTransferOut(ScriptState * script_state,const USBControlTransferParameters * setup,const ArrayBufferOrArrayBufferView & data)457 ScriptPromise USBDevice::controlTransferOut(
458 ScriptState* script_state,
459 const USBControlTransferParameters* setup,
460 const ArrayBufferOrArrayBufferView& data) {
461 auto* resolver = MakeGarbageCollected<ScriptPromiseResolver>(script_state);
462 ScriptPromise promise = resolver->Promise();
463 if (!EnsureNoDeviceOrInterfaceChangeInProgress(resolver))
464 return promise;
465
466 if (!opened_) {
467 resolver->Reject(MakeGarbageCollected<DOMException>(
468 DOMExceptionCode::kInvalidStateError, kOpenRequired));
469 return promise;
470 }
471
472 auto parameters = ConvertControlTransferParameters(setup, resolver);
473 if (!parameters)
474 return promise;
475
476 Vector<uint8_t> buffer;
477 if (!ConvertBufferSource(data, &buffer, resolver))
478 return promise;
479
480 unsigned transfer_length = buffer.size();
481 device_requests_.insert(resolver);
482 device_->ControlTransferOut(
483 std::move(parameters), buffer, 0,
484 WTF::Bind(&USBDevice::AsyncControlTransferOut, WrapPersistent(this),
485 transfer_length, WrapPersistent(resolver)));
486 return promise;
487 }
488
clearHalt(ScriptState * script_state,String direction,uint8_t endpoint_number)489 ScriptPromise USBDevice::clearHalt(ScriptState* script_state,
490 String direction,
491 uint8_t endpoint_number) {
492 auto* resolver = MakeGarbageCollected<ScriptPromiseResolver>(script_state);
493 ScriptPromise promise = resolver->Promise();
494 if (EnsureEndpointAvailable(direction == "in", endpoint_number, resolver)) {
495 device_requests_.insert(resolver);
496 device_->ClearHalt(endpoint_number, WTF::Bind(&USBDevice::AsyncClearHalt,
497 WrapPersistent(this),
498 WrapPersistent(resolver)));
499 }
500 return promise;
501 }
502
transferIn(ScriptState * script_state,uint8_t endpoint_number,unsigned length)503 ScriptPromise USBDevice::transferIn(ScriptState* script_state,
504 uint8_t endpoint_number,
505 unsigned length) {
506 auto* resolver = MakeGarbageCollected<ScriptPromiseResolver>(script_state);
507 ScriptPromise promise = resolver->Promise();
508 if (EnsureEndpointAvailable(true /* in */, endpoint_number, resolver)) {
509 device_requests_.insert(resolver);
510 device_->GenericTransferIn(
511 endpoint_number, length, 0,
512 WTF::Bind(&USBDevice::AsyncTransferIn, WrapPersistent(this),
513 WrapPersistent(resolver)));
514 }
515 return promise;
516 }
517
transferOut(ScriptState * script_state,uint8_t endpoint_number,const ArrayBufferOrArrayBufferView & data)518 ScriptPromise USBDevice::transferOut(ScriptState* script_state,
519 uint8_t endpoint_number,
520 const ArrayBufferOrArrayBufferView& data) {
521 auto* resolver = MakeGarbageCollected<ScriptPromiseResolver>(script_state);
522 ScriptPromise promise = resolver->Promise();
523 if (!EnsureEndpointAvailable(false /* out */, endpoint_number, resolver))
524 return promise;
525
526 Vector<uint8_t> buffer;
527 if (!ConvertBufferSource(data, &buffer, resolver))
528 return promise;
529
530 unsigned transfer_length = buffer.size();
531 device_requests_.insert(resolver);
532 device_->GenericTransferOut(
533 endpoint_number, buffer, 0,
534 WTF::Bind(&USBDevice::AsyncTransferOut, WrapPersistent(this),
535 transfer_length, WrapPersistent(resolver)));
536 return promise;
537 }
538
isochronousTransferIn(ScriptState * script_state,uint8_t endpoint_number,Vector<unsigned> packet_lengths)539 ScriptPromise USBDevice::isochronousTransferIn(
540 ScriptState* script_state,
541 uint8_t endpoint_number,
542 Vector<unsigned> packet_lengths) {
543 auto* resolver = MakeGarbageCollected<ScriptPromiseResolver>(script_state);
544 ScriptPromise promise = resolver->Promise();
545 if (EnsureEndpointAvailable(true /* in */, endpoint_number, resolver)) {
546 device_requests_.insert(resolver);
547 device_->IsochronousTransferIn(
548 endpoint_number, packet_lengths, 0,
549 WTF::Bind(&USBDevice::AsyncIsochronousTransferIn, WrapPersistent(this),
550 WrapPersistent(resolver)));
551 }
552 return promise;
553 }
554
isochronousTransferOut(ScriptState * script_state,uint8_t endpoint_number,const ArrayBufferOrArrayBufferView & data,Vector<unsigned> packet_lengths)555 ScriptPromise USBDevice::isochronousTransferOut(
556 ScriptState* script_state,
557 uint8_t endpoint_number,
558 const ArrayBufferOrArrayBufferView& data,
559 Vector<unsigned> packet_lengths) {
560 auto* resolver = MakeGarbageCollected<ScriptPromiseResolver>(script_state);
561 ScriptPromise promise = resolver->Promise();
562 if (!EnsureEndpointAvailable(false /* out */, endpoint_number, resolver))
563 return promise;
564
565 Vector<uint8_t> buffer;
566 if (!ConvertBufferSource(data, &buffer, resolver))
567 return promise;
568
569 device_requests_.insert(resolver);
570 device_->IsochronousTransferOut(
571 endpoint_number, buffer, packet_lengths, 0,
572 WTF::Bind(&USBDevice::AsyncIsochronousTransferOut, WrapPersistent(this),
573 WrapPersistent(resolver)));
574 return promise;
575 }
576
reset(ScriptState * script_state)577 ScriptPromise USBDevice::reset(ScriptState* script_state) {
578 auto* resolver = MakeGarbageCollected<ScriptPromiseResolver>(script_state);
579 ScriptPromise promise = resolver->Promise();
580 if (EnsureNoDeviceOrInterfaceChangeInProgress(resolver)) {
581 if (!opened_) {
582 resolver->Reject(MakeGarbageCollected<DOMException>(
583 DOMExceptionCode::kInvalidStateError, kOpenRequired));
584 } else {
585 device_requests_.insert(resolver);
586 device_->Reset(WTF::Bind(&USBDevice::AsyncReset, WrapPersistent(this),
587 WrapPersistent(resolver)));
588 }
589 }
590 return promise;
591 }
592
ContextDestroyed()593 void USBDevice::ContextDestroyed() {
594 device_.reset();
595 device_requests_.clear();
596 }
597
Trace(Visitor * visitor)598 void USBDevice::Trace(Visitor* visitor) {
599 visitor->Trace(device_requests_);
600 ScriptWrappable::Trace(visitor);
601 ExecutionContextLifecycleObserver::Trace(visitor);
602 }
603
FindConfigurationIndex(uint8_t configuration_value) const604 wtf_size_t USBDevice::FindConfigurationIndex(
605 uint8_t configuration_value) const {
606 const auto& configurations = Info().configurations;
607 for (wtf_size_t i = 0; i < configurations.size(); ++i) {
608 if (configurations[i]->configuration_value == configuration_value)
609 return i;
610 }
611 return kNotFound;
612 }
613
FindInterfaceIndex(uint8_t interface_number) const614 wtf_size_t USBDevice::FindInterfaceIndex(uint8_t interface_number) const {
615 DCHECK_NE(configuration_index_, kNotFound);
616 const auto& interfaces =
617 Info().configurations[configuration_index_]->interfaces;
618 for (wtf_size_t i = 0; i < interfaces.size(); ++i) {
619 if (interfaces[i]->interface_number == interface_number)
620 return i;
621 }
622 return kNotFound;
623 }
624
FindAlternateIndex(uint32_t interface_index,uint8_t alternate_setting) const625 wtf_size_t USBDevice::FindAlternateIndex(uint32_t interface_index,
626 uint8_t alternate_setting) const {
627 DCHECK_NE(configuration_index_, kNotFound);
628 const auto& alternates = Info()
629 .configurations[configuration_index_]
630 ->interfaces[interface_index]
631 ->alternates;
632 for (wtf_size_t i = 0; i < alternates.size(); ++i) {
633 if (alternates[i]->alternate_setting == alternate_setting)
634 return i;
635 }
636 return kNotFound;
637 }
638
IsProtectedInterfaceClass(wtf_size_t interface_index) const639 bool USBDevice::IsProtectedInterfaceClass(wtf_size_t interface_index) const {
640 DCHECK_NE(configuration_index_, kNotFound);
641 DCHECK_NE(interface_index, kNotFound);
642
643 // USB Class Codes are defined by the USB-IF:
644 // https://www.usb.org/defined-class-codes
645 const uint8_t kProtectedClasses[] = {
646 0x01, // Audio
647 0x03, // HID
648 0x08, // Mass Storage
649 0x0B, // Smart Card
650 0x0E, // Video
651 0x10, // Audio/Video
652 0xE0, // Wireless Controller (Bluetooth and Wireless USB)
653 };
654 DCHECK(std::is_sorted(std::begin(kProtectedClasses),
655 std::end(kProtectedClasses)));
656
657 const auto& alternates = Info()
658 .configurations[configuration_index_]
659 ->interfaces[interface_index]
660 ->alternates;
661 for (const auto& alternate : alternates) {
662 if (std::binary_search(std::begin(kProtectedClasses),
663 std::end(kProtectedClasses),
664 alternate->class_code)) {
665 #if defined(OS_CHROMEOS)
666 return !IsClassWhitelistedForExtension(alternate->class_code,
667 GetExecutionContext()->Url());
668 #else
669 return true;
670 #endif
671 }
672 }
673
674 return false;
675 }
676
EnsureNoDeviceChangeInProgress(ScriptPromiseResolver * resolver) const677 bool USBDevice::EnsureNoDeviceChangeInProgress(
678 ScriptPromiseResolver* resolver) const {
679 if (!device_) {
680 resolver->Reject(MakeGarbageCollected<DOMException>(
681 DOMExceptionCode::kNotFoundError, kDeviceDisconnected));
682 return false;
683 }
684
685 if (device_state_change_in_progress_) {
686 resolver->Reject(MakeGarbageCollected<DOMException>(
687 DOMExceptionCode::kInvalidStateError, kDeviceStateChangeInProgress));
688 return false;
689 }
690
691 return true;
692 }
693
EnsureNoDeviceOrInterfaceChangeInProgress(ScriptPromiseResolver * resolver) const694 bool USBDevice::EnsureNoDeviceOrInterfaceChangeInProgress(
695 ScriptPromiseResolver* resolver) const {
696 if (!EnsureNoDeviceChangeInProgress(resolver))
697 return false;
698
699 if (AnyInterfaceChangeInProgress()) {
700 resolver->Reject(MakeGarbageCollected<DOMException>(
701 DOMExceptionCode::kInvalidStateError, kInterfaceStateChangeInProgress));
702 return false;
703 }
704
705 return true;
706 }
707
EnsureDeviceConfigured(ScriptPromiseResolver * resolver) const708 bool USBDevice::EnsureDeviceConfigured(ScriptPromiseResolver* resolver) const {
709 if (!EnsureNoDeviceChangeInProgress(resolver))
710 return false;
711
712 if (!opened_) {
713 resolver->Reject(MakeGarbageCollected<DOMException>(
714 DOMExceptionCode::kInvalidStateError, kOpenRequired));
715 return false;
716 }
717
718 if (configuration_index_ == kNotFound) {
719 resolver->Reject(MakeGarbageCollected<DOMException>(
720 DOMExceptionCode::kInvalidStateError,
721 "The device must have a configuration selected."));
722 return false;
723 }
724
725 return true;
726 }
727
EnsureInterfaceClaimed(uint8_t interface_number,ScriptPromiseResolver * resolver) const728 bool USBDevice::EnsureInterfaceClaimed(uint8_t interface_number,
729 ScriptPromiseResolver* resolver) const {
730 if (!EnsureDeviceConfigured(resolver))
731 return false;
732
733 wtf_size_t interface_index = FindInterfaceIndex(interface_number);
734 if (interface_index == kNotFound) {
735 resolver->Reject(MakeGarbageCollected<DOMException>(
736 DOMExceptionCode::kNotFoundError, kInterfaceNotFound));
737 return false;
738 }
739
740 if (interface_state_change_in_progress_[interface_index]) {
741 resolver->Reject(MakeGarbageCollected<DOMException>(
742 DOMExceptionCode::kInvalidStateError, kInterfaceStateChangeInProgress));
743 return false;
744 }
745
746 if (!claimed_interfaces_[interface_index]) {
747 resolver->Reject(MakeGarbageCollected<DOMException>(
748 DOMExceptionCode::kInvalidStateError,
749 "The specified interface has not been claimed."));
750 return false;
751 }
752
753 return true;
754 }
755
EnsureEndpointAvailable(bool in_transfer,uint8_t endpoint_number,ScriptPromiseResolver * resolver) const756 bool USBDevice::EnsureEndpointAvailable(bool in_transfer,
757 uint8_t endpoint_number,
758 ScriptPromiseResolver* resolver) const {
759 if (!EnsureDeviceConfigured(resolver))
760 return false;
761
762 if (endpoint_number == 0 || endpoint_number >= kEndpointsBitsNumber) {
763 resolver->Reject(MakeGarbageCollected<DOMException>(
764 DOMExceptionCode::kIndexSizeError,
765 "The specified endpoint number is out of range."));
766 return false;
767 }
768
769 auto& bit_vector = in_transfer ? in_endpoints_ : out_endpoints_;
770 if (!bit_vector[endpoint_number - 1]) {
771 resolver->Reject(MakeGarbageCollected<DOMException>(
772 DOMExceptionCode::kNotFoundError,
773 "The specified endpoint is not part "
774 "of a claimed and selected alternate "
775 "interface."));
776 return false;
777 }
778
779 return true;
780 }
781
AnyInterfaceChangeInProgress() const782 bool USBDevice::AnyInterfaceChangeInProgress() const {
783 for (wtf_size_t i = 0; i < interface_state_change_in_progress_.size(); ++i) {
784 if (interface_state_change_in_progress_[i])
785 return true;
786 }
787 return false;
788 }
789
ConvertControlTransferParameters(const USBControlTransferParameters * parameters,ScriptPromiseResolver * resolver) const790 UsbControlTransferParamsPtr USBDevice::ConvertControlTransferParameters(
791 const USBControlTransferParameters* parameters,
792 ScriptPromiseResolver* resolver) const {
793 auto mojo_parameters = device::mojom::blink::UsbControlTransferParams::New();
794
795 if (parameters->requestType() == "standard") {
796 mojo_parameters->type = UsbControlTransferType::STANDARD;
797 } else if (parameters->requestType() == "class") {
798 mojo_parameters->type = UsbControlTransferType::CLASS;
799 } else if (parameters->requestType() == "vendor") {
800 mojo_parameters->type = UsbControlTransferType::VENDOR;
801 } else {
802 resolver->Reject(MakeGarbageCollected<DOMException>(
803 DOMExceptionCode::kTypeMismatchError,
804 "The control transfer requestType parameter is invalid."));
805 return nullptr;
806 }
807
808 if (parameters->recipient() == "device") {
809 mojo_parameters->recipient = UsbControlTransferRecipient::DEVICE;
810 } else if (parameters->recipient() == "interface") {
811 uint8_t interface_number = parameters->index() & 0xff;
812 if (!EnsureInterfaceClaimed(interface_number, resolver))
813 return nullptr;
814 mojo_parameters->recipient = UsbControlTransferRecipient::INTERFACE;
815 } else if (parameters->recipient() == "endpoint") {
816 bool in_transfer = parameters->index() & 0x80;
817 uint8_t endpoint_number = parameters->index() & 0x0f;
818 if (!EnsureEndpointAvailable(in_transfer, endpoint_number, resolver))
819 return nullptr;
820 mojo_parameters->recipient = UsbControlTransferRecipient::ENDPOINT;
821 } else if (parameters->recipient() == "other") {
822 mojo_parameters->recipient = UsbControlTransferRecipient::OTHER;
823 } else {
824 resolver->Reject(MakeGarbageCollected<DOMException>(
825 DOMExceptionCode::kTypeMismatchError,
826 "The control transfer recipient parameter is invalid."));
827 return nullptr;
828 }
829
830 mojo_parameters->request = parameters->request();
831 mojo_parameters->value = parameters->value();
832 mojo_parameters->index = parameters->index();
833 return mojo_parameters;
834 }
835
SetEndpointsForInterface(wtf_size_t interface_index,bool set)836 void USBDevice::SetEndpointsForInterface(wtf_size_t interface_index, bool set) {
837 const auto& configuration = *Info().configurations[configuration_index_];
838 const auto& interface = *configuration.interfaces[interface_index];
839 const auto& alternate =
840 *interface.alternates[selected_alternates_[interface_index]];
841 for (const auto& endpoint : alternate.endpoints) {
842 uint8_t endpoint_number = endpoint->endpoint_number;
843 if (endpoint_number == 0 || endpoint_number >= kEndpointsBitsNumber)
844 continue; // Ignore endpoints with invalid indices.
845 auto& bit_vector = endpoint->direction == UsbTransferDirection::INBOUND
846 ? in_endpoints_
847 : out_endpoints_;
848 if (set)
849 bit_vector.set(endpoint_number - 1);
850 else
851 bit_vector.reset(endpoint_number - 1);
852 }
853 }
854
AsyncOpen(ScriptPromiseResolver * resolver,UsbOpenDeviceError error)855 void USBDevice::AsyncOpen(ScriptPromiseResolver* resolver,
856 UsbOpenDeviceError error) {
857 if (!MarkRequestComplete(resolver))
858 return;
859
860 switch (error) {
861 case UsbOpenDeviceError::ALREADY_OPEN:
862 NOTREACHED();
863 FALLTHROUGH;
864 case UsbOpenDeviceError::OK:
865 OnDeviceOpenedOrClosed(true /* opened */);
866 resolver->Resolve();
867 return;
868 case UsbOpenDeviceError::ACCESS_DENIED:
869 OnDeviceOpenedOrClosed(false /* not opened */);
870 resolver->Reject(MakeGarbageCollected<DOMException>(
871 DOMExceptionCode::kSecurityError, "Access denied."));
872 return;
873 }
874 }
875
AsyncClose(ScriptPromiseResolver * resolver)876 void USBDevice::AsyncClose(ScriptPromiseResolver* resolver) {
877 if (!MarkRequestComplete(resolver))
878 return;
879
880 OnDeviceOpenedOrClosed(false /* closed */);
881 resolver->Resolve();
882 }
883
OnDeviceOpenedOrClosed(bool opened)884 void USBDevice::OnDeviceOpenedOrClosed(bool opened) {
885 opened_ = opened;
886 if (!opened_) {
887 claimed_interfaces_.Fill(false);
888 selected_alternates_.Fill(0);
889 in_endpoints_.reset();
890 out_endpoints_.reset();
891 }
892 device_state_change_in_progress_ = false;
893 }
894
AsyncSelectConfiguration(wtf_size_t configuration_index,ScriptPromiseResolver * resolver,bool success)895 void USBDevice::AsyncSelectConfiguration(wtf_size_t configuration_index,
896 ScriptPromiseResolver* resolver,
897 bool success) {
898 if (!MarkRequestComplete(resolver))
899 return;
900
901 OnConfigurationSelected(success, configuration_index);
902 if (success) {
903 resolver->Resolve();
904 } else {
905 resolver->Reject(MakeGarbageCollected<DOMException>(
906 DOMExceptionCode::kNetworkError,
907 "Unable to set device configuration."));
908 }
909 }
910
OnConfigurationSelected(bool success,wtf_size_t configuration_index)911 void USBDevice::OnConfigurationSelected(bool success,
912 wtf_size_t configuration_index) {
913 if (success) {
914 configuration_index_ = configuration_index;
915 wtf_size_t num_interfaces =
916 Info().configurations[configuration_index_]->interfaces.size();
917 claimed_interfaces_.resize(num_interfaces);
918 claimed_interfaces_.Fill(false);
919 interface_state_change_in_progress_.resize(num_interfaces);
920 interface_state_change_in_progress_.Fill(false);
921 selected_alternates_.resize(num_interfaces);
922 selected_alternates_.Fill(0);
923 in_endpoints_.reset();
924 out_endpoints_.reset();
925 }
926 device_state_change_in_progress_ = false;
927 }
928
AsyncClaimInterface(wtf_size_t interface_index,ScriptPromiseResolver * resolver,bool success)929 void USBDevice::AsyncClaimInterface(wtf_size_t interface_index,
930 ScriptPromiseResolver* resolver,
931 bool success) {
932 if (!MarkRequestComplete(resolver))
933 return;
934
935 OnInterfaceClaimedOrUnclaimed(success, interface_index);
936 if (success) {
937 resolver->Resolve();
938 } else {
939 resolver->Reject(MakeGarbageCollected<DOMException>(
940 DOMExceptionCode::kNetworkError, "Unable to claim interface."));
941 }
942 }
943
AsyncReleaseInterface(wtf_size_t interface_index,ScriptPromiseResolver * resolver,bool success)944 void USBDevice::AsyncReleaseInterface(wtf_size_t interface_index,
945 ScriptPromiseResolver* resolver,
946 bool success) {
947 if (!MarkRequestComplete(resolver))
948 return;
949
950 OnInterfaceClaimedOrUnclaimed(!success, interface_index);
951 if (success) {
952 resolver->Resolve();
953 } else {
954 resolver->Reject(MakeGarbageCollected<DOMException>(
955 DOMExceptionCode::kNetworkError, "Unable to release interface."));
956 }
957 }
958
OnInterfaceClaimedOrUnclaimed(bool claimed,wtf_size_t interface_index)959 void USBDevice::OnInterfaceClaimedOrUnclaimed(bool claimed,
960 wtf_size_t interface_index) {
961 if (claimed) {
962 claimed_interfaces_[interface_index] = true;
963 } else {
964 claimed_interfaces_[interface_index] = false;
965 selected_alternates_[interface_index] = 0;
966 }
967 SetEndpointsForInterface(interface_index, claimed);
968 interface_state_change_in_progress_[interface_index] = false;
969 }
970
AsyncSelectAlternateInterface(wtf_size_t interface_index,wtf_size_t alternate_index,ScriptPromiseResolver * resolver,bool success)971 void USBDevice::AsyncSelectAlternateInterface(wtf_size_t interface_index,
972 wtf_size_t alternate_index,
973 ScriptPromiseResolver* resolver,
974 bool success) {
975 if (!MarkRequestComplete(resolver))
976 return;
977
978 if (success)
979 selected_alternates_[interface_index] = alternate_index;
980 SetEndpointsForInterface(interface_index, success);
981 interface_state_change_in_progress_[interface_index] = false;
982
983 if (success) {
984 resolver->Resolve();
985 } else {
986 resolver->Reject(MakeGarbageCollected<DOMException>(
987 DOMExceptionCode::kNetworkError, "Unable to set device interface."));
988 }
989 }
990
AsyncControlTransferIn(ScriptPromiseResolver * resolver,UsbTransferStatus status,const Vector<uint8_t> & data)991 void USBDevice::AsyncControlTransferIn(ScriptPromiseResolver* resolver,
992 UsbTransferStatus status,
993 const Vector<uint8_t>& data) {
994 if (!MarkRequestComplete(resolver))
995 return;
996
997 DOMException* error = ConvertFatalTransferStatus(status);
998 if (error) {
999 resolver->Reject(error);
1000 } else {
1001 resolver->Resolve(
1002 USBInTransferResult::Create(ConvertTransferStatus(status), data));
1003 }
1004 }
1005
AsyncControlTransferOut(unsigned transfer_length,ScriptPromiseResolver * resolver,UsbTransferStatus status)1006 void USBDevice::AsyncControlTransferOut(unsigned transfer_length,
1007 ScriptPromiseResolver* resolver,
1008 UsbTransferStatus status) {
1009 if (!MarkRequestComplete(resolver))
1010 return;
1011
1012 DOMException* error = ConvertFatalTransferStatus(status);
1013 if (error) {
1014 resolver->Reject(error);
1015 } else {
1016 resolver->Resolve(USBOutTransferResult::Create(
1017 ConvertTransferStatus(status), transfer_length));
1018 }
1019 }
1020
AsyncClearHalt(ScriptPromiseResolver * resolver,bool success)1021 void USBDevice::AsyncClearHalt(ScriptPromiseResolver* resolver, bool success) {
1022 if (!MarkRequestComplete(resolver))
1023 return;
1024
1025 if (success) {
1026 resolver->Resolve();
1027 } else {
1028 resolver->Reject(MakeGarbageCollected<DOMException>(
1029 DOMExceptionCode::kNetworkError, "Unable to clear endpoint."));
1030 }
1031 }
1032
AsyncTransferIn(ScriptPromiseResolver * resolver,UsbTransferStatus status,const Vector<uint8_t> & data)1033 void USBDevice::AsyncTransferIn(ScriptPromiseResolver* resolver,
1034 UsbTransferStatus status,
1035 const Vector<uint8_t>& data) {
1036 if (!MarkRequestComplete(resolver))
1037 return;
1038
1039 DOMException* error = ConvertFatalTransferStatus(status);
1040 if (error) {
1041 resolver->Reject(error);
1042 } else {
1043 resolver->Resolve(
1044 USBInTransferResult::Create(ConvertTransferStatus(status), data));
1045 }
1046 }
1047
AsyncTransferOut(unsigned transfer_length,ScriptPromiseResolver * resolver,UsbTransferStatus status)1048 void USBDevice::AsyncTransferOut(unsigned transfer_length,
1049 ScriptPromiseResolver* resolver,
1050 UsbTransferStatus status) {
1051 if (!MarkRequestComplete(resolver))
1052 return;
1053
1054 DOMException* error = ConvertFatalTransferStatus(status);
1055 if (error) {
1056 resolver->Reject(error);
1057 } else {
1058 resolver->Resolve(USBOutTransferResult::Create(
1059 ConvertTransferStatus(status), transfer_length));
1060 }
1061 }
1062
AsyncIsochronousTransferIn(ScriptPromiseResolver * resolver,const Vector<uint8_t> & data,Vector<UsbIsochronousPacketPtr> mojo_packets)1063 void USBDevice::AsyncIsochronousTransferIn(
1064 ScriptPromiseResolver* resolver,
1065 const Vector<uint8_t>& data,
1066 Vector<UsbIsochronousPacketPtr> mojo_packets) {
1067 if (!MarkRequestComplete(resolver))
1068 return;
1069
1070 DOMArrayBuffer* buffer = DOMArrayBuffer::Create(data.data(), data.size());
1071 HeapVector<Member<USBIsochronousInTransferPacket>> packets;
1072 packets.ReserveCapacity(mojo_packets.size());
1073 uint32_t byte_offset = 0;
1074 for (const auto& packet : mojo_packets) {
1075 DOMException* error = ConvertFatalTransferStatus(packet->status);
1076 if (error) {
1077 resolver->Reject(error);
1078 return;
1079 }
1080 DOMDataView* data_view = nullptr;
1081 if (buffer) {
1082 data_view =
1083 DOMDataView::Create(buffer, byte_offset, packet->transferred_length);
1084 }
1085 packets.push_back(USBIsochronousInTransferPacket::Create(
1086 ConvertTransferStatus(packet->status),
1087 NotShared<DOMDataView>(data_view)));
1088 byte_offset += packet->length;
1089 }
1090 resolver->Resolve(USBIsochronousInTransferResult::Create(buffer, packets));
1091 }
1092
AsyncIsochronousTransferOut(ScriptPromiseResolver * resolver,Vector<UsbIsochronousPacketPtr> mojo_packets)1093 void USBDevice::AsyncIsochronousTransferOut(
1094 ScriptPromiseResolver* resolver,
1095 Vector<UsbIsochronousPacketPtr> mojo_packets) {
1096 if (!MarkRequestComplete(resolver))
1097 return;
1098
1099 HeapVector<Member<USBIsochronousOutTransferPacket>> packets;
1100 packets.ReserveCapacity(mojo_packets.size());
1101 for (const auto& packet : mojo_packets) {
1102 DOMException* error = ConvertFatalTransferStatus(packet->status);
1103 if (error) {
1104 resolver->Reject(error);
1105 return;
1106 }
1107 packets.push_back(USBIsochronousOutTransferPacket::Create(
1108 ConvertTransferStatus(packet->status), packet->transferred_length));
1109 }
1110 resolver->Resolve(USBIsochronousOutTransferResult::Create(packets));
1111 }
1112
AsyncReset(ScriptPromiseResolver * resolver,bool success)1113 void USBDevice::AsyncReset(ScriptPromiseResolver* resolver, bool success) {
1114 if (!MarkRequestComplete(resolver))
1115 return;
1116
1117 if (success) {
1118 resolver->Resolve();
1119 } else {
1120 resolver->Reject(MakeGarbageCollected<DOMException>(
1121 DOMExceptionCode::kNetworkError, "Unable to reset the device."));
1122 }
1123 }
1124
OnConnectionError()1125 void USBDevice::OnConnectionError() {
1126 device_.reset();
1127 opened_ = false;
1128
1129 // Move the set to a local variable to prevent script execution in Reject()
1130 // from invalidating the iterator used by the loop.
1131 HeapHashSet<Member<ScriptPromiseResolver>> device_requests;
1132 device_requests.swap(device_requests_);
1133 for (auto& resolver : device_requests) {
1134 resolver->Reject(MakeGarbageCollected<DOMException>(
1135 DOMExceptionCode::kNotFoundError, kDeviceDisconnected));
1136 }
1137 }
1138
MarkRequestComplete(ScriptPromiseResolver * resolver)1139 bool USBDevice::MarkRequestComplete(ScriptPromiseResolver* resolver) {
1140 auto request_entry = device_requests_.find(resolver);
1141 if (request_entry == device_requests_.end())
1142 return false;
1143 device_requests_.erase(request_entry);
1144 return true;
1145 }
1146
1147 } // namespace blink
1148