OLD | NEW |
1 // Copyright 2015 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "modules/webusb/USB.h" | 5 #include "modules/webusb/USB.h" |
6 | 6 |
7 #include "bindings/core/v8/CallbackPromiseAdapter.h" | |
8 #include "bindings/core/v8/ScriptPromise.h" | 7 #include "bindings/core/v8/ScriptPromise.h" |
9 #include "bindings/core/v8/ScriptPromiseResolver.h" | 8 #include "bindings/core/v8/ScriptPromiseResolver.h" |
10 #include "core/dom/DOMException.h" | 9 #include "core/dom/DOMException.h" |
11 #include "core/dom/Document.h" | 10 #include "core/dom/Document.h" |
12 #include "core/dom/ExceptionCode.h" | 11 #include "core/dom/ExceptionCode.h" |
| 12 #include "device/usb/public/interfaces/device.mojom-wtf.h" |
13 #include "modules/EventTargetModules.h" | 13 #include "modules/EventTargetModules.h" |
14 #include "modules/webusb/USBConnectionEvent.h" | 14 #include "modules/webusb/USBConnectionEvent.h" |
15 #include "modules/webusb/USBController.h" | |
16 #include "modules/webusb/USBDevice.h" | 15 #include "modules/webusb/USBDevice.h" |
17 #include "modules/webusb/USBDeviceFilter.h" | 16 #include "modules/webusb/USBDeviceFilter.h" |
18 #include "modules/webusb/USBDeviceRequestOptions.h" | 17 #include "modules/webusb/USBDeviceRequestOptions.h" |
19 #include "modules/webusb/USBError.h" | 18 #include "platform/MojoHelper.h" |
20 #include "platform/UserGestureIndicator.h" | 19 #include "platform/UserGestureIndicator.h" |
21 #include "public/platform/Platform.h" | 20 #include "public/platform/ServiceRegistry.h" |
22 #include "public/platform/WebVector.h" | 21 #include "wtf/Functional.h" |
23 #include "public/platform/modules/webusb/WebUSBClient.h" | 22 |
24 #include "public/platform/modules/webusb/WebUSBDeviceFilter.h" | 23 namespace usb = device::usb::wtf; |
25 #include "public/platform/modules/webusb/WebUSBDeviceRequestOptions.h" | |
26 #include "public/platform/modules/webusb/WebUSBError.h" | |
27 | 24 |
28 namespace blink { | 25 namespace blink { |
29 namespace { | 26 namespace { |
30 | 27 |
31 void convertDeviceFilter(const USBDeviceFilter& filter, WebUSBDeviceFilter* webF
ilter) | 28 const char kNoServiceError[] = "USB service unavailable."; |
| 29 |
| 30 usb::DeviceFilterPtr convertDeviceFilter(const USBDeviceFilter& filter) |
32 { | 31 { |
33 webFilter->hasVendorID = filter.hasVendorId(); | 32 auto mojoFilter = usb::DeviceFilter::New(); |
34 if (filter.hasVendorId()) | 33 mojoFilter->has_vendor_id = filter.hasVendorId(); |
35 webFilter->vendorID = filter.vendorId(); | 34 if (mojoFilter->has_vendor_id) |
36 webFilter->hasProductID = filter.hasProductId(); | 35 mojoFilter->vendor_id = filter.vendorId(); |
37 if (filter.hasProductId()) | 36 mojoFilter->has_product_id = filter.hasProductId(); |
38 webFilter->productID = filter.productId(); | 37 if (mojoFilter->has_product_id) |
39 webFilter->hasClassCode = filter.hasClassCode(); | 38 mojoFilter->product_id = filter.productId(); |
40 if (filter.hasClassCode()) | 39 mojoFilter->has_class_code = filter.hasClassCode(); |
41 webFilter->classCode = filter.classCode(); | 40 if (mojoFilter->has_class_code) |
42 webFilter->hasSubclassCode = filter.hasSubclassCode(); | 41 mojoFilter->class_code = filter.classCode(); |
43 if (filter.hasSubclassCode()) | 42 mojoFilter->has_subclass_code = filter.hasSubclassCode(); |
44 webFilter->subclassCode = filter.subclassCode(); | 43 if (mojoFilter->has_subclass_code) |
45 webFilter->hasProtocolCode = filter.hasProtocolCode(); | 44 mojoFilter->subclass_code = filter.subclassCode(); |
46 if (filter.hasProtocolCode()) | 45 mojoFilter->has_protocol_code = filter.hasProtocolCode(); |
47 webFilter->protocolCode = filter.protocolCode(); | 46 if (mojoFilter->has_protocol_code) |
| 47 mojoFilter->protocol_code = filter.protocolCode(); |
| 48 return mojoFilter; |
48 } | 49 } |
49 | 50 |
50 void convertDeviceRequestOptions(const USBDeviceRequestOptions& options, WebUSBD
eviceRequestOptions* webOptions) | 51 bool isActive(ScriptPromiseResolver* resolver) |
51 { | 52 { |
52 DCHECK(options.hasFilters()); | 53 ExecutionContext* context = resolver->getExecutionContext(); |
53 webOptions->filters = WebVector<WebUSBDeviceFilter>(options.filters().size()
); | 54 return context && !context->activeDOMObjectsAreStopped(); |
54 for (size_t i = 0; i < options.filters().size(); ++i) { | |
55 convertDeviceFilter(options.filters()[i], &webOptions->filters[i]); | |
56 } | |
57 } | 55 } |
58 | 56 |
59 // Allows using a CallbackPromiseAdapter with a WebVector to resolve the | |
60 // getDevices() promise with a HeapVector owning USBDevices. | |
61 class DeviceArray { | |
62 STATIC_ONLY(DeviceArray); | |
63 public: | |
64 using WebType = OwnPtr<WebVector<WebUSBDevice*>>; | |
65 | |
66 static HeapVector<Member<USBDevice>> take(ScriptPromiseResolver* resolver, P
assOwnPtr<WebVector<WebUSBDevice*>> webDevices) | |
67 { | |
68 HeapVector<Member<USBDevice>> devices; | |
69 for (const auto webDevice : *webDevices) | |
70 devices.append(USBDevice::create(adoptPtr(webDevice), resolver->getE
xecutionContext())); | |
71 return devices; | |
72 } | |
73 }; | |
74 | |
75 } // namespace | 57 } // namespace |
76 | 58 |
77 USB::USB(LocalFrame& frame) | 59 USB::USB(LocalFrame& frame) |
78 : ContextLifecycleObserver(frame.document()) | 60 : ContextLifecycleObserver(frame.document()) |
79 , m_client(USBController::from(frame).client()) | |
80 { | 61 { |
81 ThreadState::current()->registerPreFinalizer(this); | 62 frame.serviceRegistry()->connectToRemoteService(mojo::GetProxy(&m_deviceMana
ger)); |
82 if (m_client) | 63 m_deviceManager.set_connection_error_handler([this]() { |
83 m_client->addObserver(this); | 64 m_deviceManager.reset(); |
| 65 for (ScriptPromiseResolver* resolver : m_deviceManagerRequests) { |
| 66 if (isActive(resolver)) |
| 67 resolver->reject(DOMException::create(NotFoundError, kNoServiceE
rror)); |
| 68 } |
| 69 m_deviceManagerRequests.clear(); |
| 70 }); |
| 71 // Set up two sequential calls to GetDeviceChanges to avoid latency. |
| 72 m_deviceManager->GetDeviceChanges(createBaseCallback(bind<usb::DeviceChangeN
otificationPtr>(&USB::onDeviceChanges, this))); |
| 73 m_deviceManager->GetDeviceChanges(createBaseCallback(bind<usb::DeviceChangeN
otificationPtr>(&USB::onDeviceChanges, this))); |
84 } | 74 } |
85 | 75 |
86 USB::~USB() | 76 USB::~USB() |
87 { | 77 { |
88 } | 78 DCHECK(!m_deviceManager); |
89 | 79 DCHECK(m_deviceManagerRequests.isEmpty()); |
90 void USB::dispose() | 80 DCHECK(!m_chooserService); |
91 { | 81 DCHECK(m_chooserServiceRequests.isEmpty()); |
92 // Promptly clears a raw reference from content/ to an on-heap object | |
93 // so that content/ doesn't access it in a lazy sweeping phase. | |
94 if (m_client) | |
95 m_client->removeObserver(this); | |
96 m_client = nullptr; | |
97 } | 82 } |
98 | 83 |
99 ScriptPromise USB::getDevices(ScriptState* scriptState) | 84 ScriptPromise USB::getDevices(ScriptState* scriptState) |
100 { | 85 { |
101 if (!m_client) | |
102 return ScriptPromise::rejectWithDOMException(scriptState, DOMException::
create(NotSupportedError)); | |
103 | |
104 String errorMessage; | |
105 if (!scriptState->getExecutionContext()->isSecureContext(errorMessage)) | |
106 return ScriptPromise::rejectWithDOMException(scriptState, DOMException::
create(SecurityError, errorMessage)); | |
107 | |
108 ScriptPromiseResolver* resolver = ScriptPromiseResolver::create(scriptState)
; | 86 ScriptPromiseResolver* resolver = ScriptPromiseResolver::create(scriptState)
; |
109 ScriptPromise promise = resolver->promise(); | 87 ScriptPromise promise = resolver->promise(); |
110 m_client->getDevices(new CallbackPromiseAdapter<DeviceArray, USBError>(resol
ver)); | 88 if (!m_deviceManager) { |
111 | 89 resolver->reject(DOMException::create(NotSupportedError)); |
| 90 } else { |
| 91 String errorMessage; |
| 92 if (!scriptState->getExecutionContext()->isSecureContext(errorMessage))
{ |
| 93 resolver->reject(DOMException::create(SecurityError, errorMessage)); |
| 94 } else { |
| 95 m_deviceManagerRequests.add(resolver); |
| 96 m_deviceManager->GetDevices(nullptr, createBaseCallback(bind<mojo::W
TFArray<usb::DeviceInfoPtr>>(&USB::onGetDevices, this, resolver))); |
| 97 } |
| 98 } |
112 return promise; | 99 return promise; |
113 } | 100 } |
114 | 101 |
115 ScriptPromise USB::requestDevice(ScriptState* scriptState, const USBDeviceReques
tOptions& options) | 102 ScriptPromise USB::requestDevice(ScriptState* scriptState, const USBDeviceReques
tOptions& options) |
116 { | 103 { |
117 if (!m_client) | |
118 return ScriptPromise::rejectWithDOMException(scriptState, DOMException::
create(NotSupportedError)); | |
119 | |
120 String errorMessage; | |
121 if (!scriptState->getExecutionContext()->isSecureContext(errorMessage)) | |
122 return ScriptPromise::rejectWithDOMException(scriptState, DOMException::
create(SecurityError, errorMessage)); | |
123 | |
124 if (!UserGestureIndicator::consumeUserGesture()) | |
125 return ScriptPromise::rejectWithDOMException(scriptState, DOMException::
create(SecurityError, "Must be handling a user gesture to show a permission requ
est.")); | |
126 | |
127 ScriptPromiseResolver* resolver = ScriptPromiseResolver::create(scriptState)
; | 104 ScriptPromiseResolver* resolver = ScriptPromiseResolver::create(scriptState)
; |
128 ScriptPromise promise = resolver->promise(); | 105 ScriptPromise promise = resolver->promise(); |
129 | 106 |
130 WebUSBDeviceRequestOptions webOptions; | 107 if (!m_chooserService) { |
131 convertDeviceRequestOptions(options, &webOptions); | 108 LocalFrame* frame = getExecutionContext() ? toDocument(getExecutionConte
xt())->frame() : nullptr; |
132 m_client->requestDevice(webOptions, new CallbackPromiseAdapter<USBDevice, US
BError>(resolver)); | 109 if (!frame) { |
| 110 resolver->reject(DOMException::create(NotSupportedError)); |
| 111 return promise; |
| 112 } |
| 113 frame->serviceRegistry()->connectToRemoteService(mojo::GetProxy(&m_choos
erService)); |
| 114 m_chooserService.set_connection_error_handler([this]() { |
| 115 m_chooserService.reset(); |
| 116 for (ScriptPromiseResolver* resolver : m_chooserServiceRequests) { |
| 117 if (isActive(resolver)) |
| 118 resolver->reject(DOMException::create(NotFoundError, kNoServ
iceError)); |
| 119 } |
| 120 m_chooserServiceRequests.clear(); |
| 121 }); |
| 122 } |
133 | 123 |
| 124 String errorMessage; |
| 125 if (!scriptState->getExecutionContext()->isSecureContext(errorMessage)) { |
| 126 resolver->reject(DOMException::create(SecurityError, errorMessage)); |
| 127 } else if (!UserGestureIndicator::consumeUserGesture()) { |
| 128 resolver->reject(DOMException::create(SecurityError, "Must be handling a
user gesture to show a permission request.")); |
| 129 } else { |
| 130 Vector<usb::DeviceFilterPtr> filters; |
| 131 if (options.hasFilters()) { |
| 132 filters.reserveCapacity(options.filters().size()); |
| 133 for (const auto& filter : options.filters()) |
| 134 filters.append(convertDeviceFilter(filter)); |
| 135 } |
| 136 m_chooserServiceRequests.add(resolver); |
| 137 m_chooserService->GetPermission(std::move(filters), createBaseCallback(b
ind<usb::DeviceInfoPtr>(&USB::onGetPermission, this, resolver))); |
| 138 } |
134 return promise; | 139 return promise; |
135 } | 140 } |
136 | 141 |
137 ExecutionContext* USB::getExecutionContext() const | 142 ExecutionContext* USB::getExecutionContext() const |
138 { | 143 { |
139 return ContextLifecycleObserver::getExecutionContext(); | 144 return ContextLifecycleObserver::getExecutionContext(); |
140 } | 145 } |
141 | 146 |
142 const AtomicString& USB::interfaceName() const | 147 const AtomicString& USB::interfaceName() const |
143 { | 148 { |
144 return EventTargetNames::USB; | 149 return EventTargetNames::USB; |
145 } | 150 } |
146 | 151 |
147 void USB::contextDestroyed() | 152 void USB::contextDestroyed() |
148 { | 153 { |
149 if (m_client) | 154 m_deviceManager.reset(); |
150 m_client->removeObserver(this); | 155 m_deviceManagerRequests.clear(); |
151 m_client = nullptr; | 156 m_chooserService.reset(); |
| 157 m_chooserServiceRequests.clear(); |
152 } | 158 } |
153 | 159 |
154 void USB::onDeviceConnected(std::unique_ptr<WebUSBDevice> device) | 160 void USB::onGetDevices(ScriptPromiseResolver* resolver, mojo::WTFArray<usb::Devi
ceInfoPtr> deviceInfos) |
155 { | 161 { |
156 dispatchEvent(USBConnectionEvent::create(EventTypeNames::connect, USBDevice:
:create(adoptPtr(device.release()), getExecutionContext()))); | 162 if (!isActive(resolver)) |
| 163 return; |
| 164 |
| 165 HeapVector<Member<USBDevice>> devices; |
| 166 for (auto& deviceInfo : deviceInfos.PassStorage()) { |
| 167 usb::DevicePtr device; |
| 168 m_deviceManager->GetDevice(deviceInfo->guid, mojo::GetProxy(&device)); |
| 169 devices.append(USBDevice::create(std::move(deviceInfo), std::move(device
), resolver->getExecutionContext())); |
| 170 } |
| 171 resolver->resolve(devices); |
| 172 m_deviceManagerRequests.remove(resolver); |
157 } | 173 } |
158 | 174 |
159 void USB::onDeviceDisconnected(std::unique_ptr<WebUSBDevice> device) | 175 void USB::onGetPermission(ScriptPromiseResolver* resolver, usb::DeviceInfoPtr de
viceInfo) |
160 { | 176 { |
161 dispatchEvent(USBConnectionEvent::create(EventTypeNames::disconnect, USBDevi
ce::create(adoptPtr(device.release()), getExecutionContext()))); | 177 if (!isActive(resolver)) |
| 178 return; |
| 179 |
| 180 if (deviceInfo) { |
| 181 usb::DevicePtr device; |
| 182 m_deviceManager->GetDevice(deviceInfo->guid, mojo::GetProxy(&device)); |
| 183 resolver->resolve(USBDevice::create(std::move(deviceInfo), std::move(dev
ice), resolver->getExecutionContext())); |
| 184 } else { |
| 185 resolver->reject(DOMException::create(NotFoundError, "No device selected
.")); |
| 186 } |
| 187 } |
| 188 |
| 189 void USB::onDeviceChanges(usb::DeviceChangeNotificationPtr notification) |
| 190 { |
| 191 m_deviceManager->GetDeviceChanges(createBaseCallback(bind<usb::DeviceChangeN
otificationPtr>(&USB::onDeviceChanges, this))); |
| 192 for (auto& deviceInfo : notification->devices_added.PassStorage()) { |
| 193 usb::DevicePtr device; |
| 194 m_deviceManager->GetDevice(deviceInfo->guid, mojo::GetProxy(&device)); |
| 195 dispatchEvent(USBConnectionEvent::create(EventTypeNames::connect, USBDev
ice::create(std::move(deviceInfo), std::move(device), getExecutionContext()))); |
| 196 } |
| 197 for (auto& deviceInfo : notification->devices_removed.PassStorage()) |
| 198 dispatchEvent(USBConnectionEvent::create(EventTypeNames::disconnect, USB
Device::create(std::move(deviceInfo), nullptr, getExecutionContext()))); |
162 } | 199 } |
163 | 200 |
164 DEFINE_TRACE(USB) | 201 DEFINE_TRACE(USB) |
165 { | 202 { |
166 EventTargetWithInlineData::trace(visitor); | 203 EventTargetWithInlineData::trace(visitor); |
167 ContextLifecycleObserver::trace(visitor); | 204 ContextLifecycleObserver::trace(visitor); |
| 205 visitor->trace(m_deviceManagerRequests); |
| 206 visitor->trace(m_chooserServiceRequests); |
168 } | 207 } |
169 | 208 |
170 } // namespace blink | 209 } // namespace blink |
OLD | NEW |