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