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" |
13 #include "modules/EventTargetModules.h" | 12 #include "modules/EventTargetModules.h" |
14 #include "modules/webusb/USBConnectionEvent.h" | 13 #include "modules/webusb/USBConnectionEvent.h" |
15 #include "modules/webusb/USBController.h" | |
16 #include "modules/webusb/USBDevice.h" | 14 #include "modules/webusb/USBDevice.h" |
17 #include "modules/webusb/USBDeviceFilter.h" | 15 #include "modules/webusb/USBDeviceFilter.h" |
18 #include "modules/webusb/USBDeviceRequestOptions.h" | 16 #include "modules/webusb/USBDeviceRequestOptions.h" |
19 #include "modules/webusb/USBError.h" | |
20 #include "platform/UserGestureIndicator.h" | 17 #include "platform/UserGestureIndicator.h" |
21 #include "public/platform/Platform.h" | 18 #include "public/platform/ServiceRegistry.h" |
22 #include "public/platform/WebVector.h" | |
23 #include "public/platform/modules/webusb/WebUSBClient.h" | |
24 #include "public/platform/modules/webusb/WebUSBDeviceFilter.h" | |
25 #include "public/platform/modules/webusb/WebUSBDeviceRequestOptions.h" | |
26 #include "public/platform/modules/webusb/WebUSBError.h" | |
27 | 19 |
28 namespace blink { | 20 namespace blink { |
29 namespace { | 21 namespace { |
30 | 22 |
31 void convertDeviceFilter(const USBDeviceFilter& filter, WebUSBDeviceFilter* webF ilter) | 23 const char kNoServiceError[] = "USB service unavailable."; |
32 { | 24 |
33 webFilter->hasVendorID = filter.hasVendorId(); | 25 device::usb::blink::DeviceFilterPtr convertDeviceFilter(const USBDeviceFilter& f ilter) |
34 if (filter.hasVendorId()) | 26 { |
35 webFilter->vendorID = filter.vendorId(); | 27 auto mojoFilter = device::usb::blink::DeviceFilter::New(); |
36 webFilter->hasProductID = filter.hasProductId(); | 28 mojoFilter->has_vendor_id = filter.hasVendorId(); |
37 if (filter.hasProductId()) | 29 if (mojoFilter->has_vendor_id) |
38 webFilter->productID = filter.productId(); | 30 mojoFilter->vendor_id = filter.vendorId(); |
39 webFilter->hasClassCode = filter.hasClassCode(); | 31 mojoFilter->has_product_id = filter.hasProductId(); |
40 if (filter.hasClassCode()) | 32 if (mojoFilter->has_product_id) |
41 webFilter->classCode = filter.classCode(); | 33 mojoFilter->product_id = filter.productId(); |
42 webFilter->hasSubclassCode = filter.hasSubclassCode(); | 34 mojoFilter->has_class_code = filter.hasClassCode(); |
43 if (filter.hasSubclassCode()) | 35 if (mojoFilter->has_class_code) |
44 webFilter->subclassCode = filter.subclassCode(); | 36 mojoFilter->class_code = filter.classCode(); |
45 webFilter->hasProtocolCode = filter.hasProtocolCode(); | 37 mojoFilter->has_subclass_code = filter.hasSubclassCode(); |
46 if (filter.hasProtocolCode()) | 38 if (mojoFilter->has_subclass_code) |
47 webFilter->protocolCode = filter.protocolCode(); | 39 mojoFilter->subclass_code = filter.subclassCode(); |
48 } | 40 mojoFilter->has_protocol_code = filter.hasProtocolCode(); |
49 | 41 if (mojoFilter->has_protocol_code) |
50 void convertDeviceRequestOptions(const USBDeviceRequestOptions& options, WebUSBD eviceRequestOptions* webOptions) | 42 mojoFilter->protocol_code = filter.protocolCode(); |
51 { | 43 return mojoFilter; |
52 DCHECK(options.hasFilters()); | 44 } |
53 webOptions->filters = WebVector<WebUSBDeviceFilter>(options.filters().size() ); | 45 |
54 for (size_t i = 0; i < options.filters().size(); ++i) { | 46 class DeviceChangeNotificationAdapter : public device::usb::blink::DeviceManager ::GetDeviceChangesCallback::Runnable { |
55 convertDeviceFilter(options.filters()[i], &webOptions->filters[i]); | 47 WTF_MAKE_NONCOPYABLE(DeviceChangeNotificationAdapter); |
56 } | 48 |
57 } | 49 public: |
58 | 50 DeviceChangeNotificationAdapter(USB* usb) : m_usb(usb) |
59 // Allows using a CallbackPromiseAdapter with a WebVector to resolve the | 51 { |
60 // getDevices() promise with a HeapVector owning USBDevices. | 52 } |
61 class DeviceArray { | 53 |
62 STATIC_ONLY(DeviceArray); | 54 void Run(device::usb::blink::DeviceChangeNotificationPtr notification) const |
63 public: | 55 { |
64 using WebType = OwnPtr<WebVector<WebUSBDevice*>>; | 56 if (m_usb) |
65 | 57 m_usb->onDeviceChanges(std::move(notification)); |
66 static HeapVector<Member<USBDevice>> take(ScriptPromiseResolver* resolver, P assOwnPtr<WebVector<WebUSBDevice*>> webDevices) | 58 } |
67 { | 59 |
60 device::usb::blink::DeviceManager::GetDeviceChangesCallback callback() | |
61 { | |
62 return device::usb::blink::DeviceManager::GetDeviceChangesCallback(this) ; | |
63 } | |
64 | |
65 private: | |
66 WeakPersistent<USB> m_usb; | |
67 }; | |
68 | |
69 template <typename Callback> | |
70 class PromiseAdapterBase : public Callback::Runnable { | |
71 WTF_MAKE_NONCOPYABLE(PromiseAdapterBase); | |
72 | |
73 public: | |
74 PromiseAdapterBase(ScriptPromiseResolver* resolver) : m_resolver(resolver) | |
75 { | |
76 } | |
77 | |
78 ~PromiseAdapterBase() override | |
79 { | |
80 if (active()) | |
81 m_resolver->reject(DOMException::create(NotFoundError, kNoServiceErr or)); | |
82 } | |
83 | |
84 template <typename T> | |
85 void resolve(T value) const | |
86 { | |
87 ASSERT(active()); | |
88 m_resolver->resolve(value); | |
89 m_resolver = nullptr; | |
90 } | |
91 | |
92 template <typename T> | |
93 void reject(T value) const | |
94 { | |
95 ASSERT(active()); | |
96 m_resolver->reject(value); | |
97 m_resolver = nullptr; | |
98 } | |
99 | |
100 bool active() const | |
101 { | |
102 return m_resolver && executionContext() && !executionContext()->activeDO MObjectsAreStopped(); | |
103 } | |
104 | |
105 Callback callback() | |
106 { | |
107 return Callback(this); | |
108 } | |
109 | |
110 ExecutionContext* executionContext() const { return m_resolver->getExecution Context(); } | |
111 | |
112 private: | |
113 // This field is mutable so that resolve() and reject() can be called from | |
114 // a mojo::Callback::Runnable's const Run() method. | |
esprehn
2016/04/06 20:10:45
this is really weird, callback interfaces generall
Reilly Grant (use Gerrit)
2016/04/06 22:30:07
mojo::Callback<>::Runnable::Run() is const because
esprehn
2016/04/06 22:42:38
Having to make your members mutable all over the c
| |
115 mutable Persistent<ScriptPromiseResolver> m_resolver; | |
116 }; | |
117 | |
118 class GetDevicesPromiseAdapter : public PromiseAdapterBase<device::usb::blink::D eviceManager::GetDevicesCallback> { | |
119 WTF_MAKE_NONCOPYABLE(GetDevicesPromiseAdapter); | |
120 | |
121 public: | |
122 GetDevicesPromiseAdapter(USB* usb, ScriptPromiseResolver* resolver) | |
123 : PromiseAdapterBase(resolver) | |
124 , m_usb(usb) | |
125 { | |
126 } | |
127 | |
128 void Run(mojo::WTFArray<device::usb::blink::DeviceInfoPtr> deviceInfos) cons t override | |
129 { | |
130 if (!active()) | |
131 return; | |
132 | |
68 HeapVector<Member<USBDevice>> devices; | 133 HeapVector<Member<USBDevice>> devices; |
69 for (const auto webDevice : *webDevices) | 134 for (size_t i = 0; i < deviceInfos.size(); ++i) { |
esprehn
2016/04/06 20:10:45
range loop?
Reilly Grant (use Gerrit)
2016/04/06 22:30:07
Done.
| |
70 devices.append(USBDevice::create(adoptPtr(webDevice), resolver->getE xecutionContext())); | 135 device::usb::blink::DevicePtr device; |
71 return devices; | 136 m_usb->deviceManager()->GetDevice(deviceInfos[i]->guid, mojo::GetPro xy(&device)); |
72 } | 137 devices.append(USBDevice::create(std::move(deviceInfos[i]), std::mov e(device), executionContext())); |
138 } | |
139 resolve(devices); | |
140 } | |
141 | |
142 private: | |
143 Persistent<USB> m_usb; | |
144 }; | |
145 | |
146 class GetPermissionPromiseAdapter : public PromiseAdapterBase<device::usb::blink ::ChooserService::GetPermissionCallback> { | |
147 WTF_MAKE_NONCOPYABLE(GetPermissionPromiseAdapter); | |
148 | |
149 public: | |
150 GetPermissionPromiseAdapter(USB* usb, ScriptPromiseResolver* resolver) | |
151 : PromiseAdapterBase(resolver) | |
152 , m_usb(usb) | |
153 { | |
154 } | |
155 | |
156 void Run(device::usb::blink::DeviceInfoPtr deviceInfo) const override | |
157 { | |
158 if (!active()) | |
159 return; | |
160 | |
161 if (deviceInfo) { | |
162 device::usb::blink::DevicePtr device; | |
163 m_usb->deviceManager()->GetDevice(deviceInfo->guid, mojo::GetProxy(& device)); | |
164 resolve(USBDevice::create(std::move(deviceInfo), std::move(device), executionContext())); | |
165 } else { | |
166 reject(DOMException::create(NotFoundError, "No device selected.")); | |
167 } | |
168 } | |
169 | |
170 private: | |
171 Persistent<USB> m_usb; | |
73 }; | 172 }; |
74 | 173 |
75 } // namespace | 174 } // namespace |
76 | 175 |
77 USB::USB(LocalFrame& frame) | 176 USB::USB(LocalFrame& frame) |
78 : ContextLifecycleObserver(frame.document()) | 177 : ContextLifecycleObserver(frame.document()) |
79 , m_client(USBController::from(frame).client()) | 178 { |
80 { | 179 frame.serviceRegistry()->connectToRemoteService(mojo::GetProxy(&m_deviceMana ger)); |
81 if (m_client) | 180 m_deviceManager.set_connection_error_handler([this]() { |
82 m_client->addObserver(this); | 181 m_deviceManager.reset(); |
182 }); | |
183 auto adapter = new DeviceChangeNotificationAdapter(this); | |
184 m_deviceManager->GetDeviceChanges(adapter->callback()); | |
185 adapter = new DeviceChangeNotificationAdapter(this); | |
186 m_deviceManager->GetDeviceChanges(adapter->callback()); | |
esprehn
2016/04/06 20:10:45
this seems to just do the exact same thing twice i
Reilly Grant (use Gerrit)
2016/04/06 22:30:07
Sorry, there was a comment why in the original cod
| |
83 } | 187 } |
84 | 188 |
85 USB::~USB() | 189 USB::~USB() |
86 { | 190 { |
87 if (m_client) | |
88 m_client->removeObserver(this); | |
89 } | 191 } |
90 | 192 |
91 ScriptPromise USB::getDevices(ScriptState* scriptState) | 193 ScriptPromise USB::getDevices(ScriptState* scriptState) |
92 { | 194 { |
93 if (!m_client) | |
94 return ScriptPromise::rejectWithDOMException(scriptState, DOMException:: create(NotSupportedError)); | |
95 | |
96 String errorMessage; | |
97 if (!scriptState->getExecutionContext()->isSecureContext(errorMessage)) | |
98 return ScriptPromise::rejectWithDOMException(scriptState, DOMException:: create(SecurityError, errorMessage)); | |
99 | |
100 ScriptPromiseResolver* resolver = ScriptPromiseResolver::create(scriptState) ; | 195 ScriptPromiseResolver* resolver = ScriptPromiseResolver::create(scriptState) ; |
101 ScriptPromise promise = resolver->promise(); | 196 ScriptPromise promise = resolver->promise(); |
102 m_client->getDevices(new CallbackPromiseAdapter<DeviceArray, USBError>(resol ver)); | 197 if (!m_deviceManager) { |
103 | 198 resolver->reject(DOMException::create(NotSupportedError)); |
199 } else { | |
200 String errorMessage; | |
201 if (!scriptState->getExecutionContext()->isSecureContext(errorMessage)) { | |
202 resolver->reject(DOMException::create(SecurityError, errorMessage)); | |
203 } else { | |
204 auto adapter = new GetDevicesPromiseAdapter(this, resolver); | |
205 m_deviceManager->GetDevices(nullptr, adapter->callback()); | |
206 } | |
207 } | |
104 return promise; | 208 return promise; |
105 } | 209 } |
106 | 210 |
107 ScriptPromise USB::requestDevice(ScriptState* scriptState, const USBDeviceReques tOptions& options) | 211 ScriptPromise USB::requestDevice(ScriptState* scriptState, const USBDeviceReques tOptions& options) |
108 { | 212 { |
109 if (!m_client) | |
110 return ScriptPromise::rejectWithDOMException(scriptState, DOMException:: create(NotSupportedError)); | |
111 | |
112 String errorMessage; | |
113 if (!scriptState->getExecutionContext()->isSecureContext(errorMessage)) | |
114 return ScriptPromise::rejectWithDOMException(scriptState, DOMException:: create(SecurityError, errorMessage)); | |
115 | |
116 if (!UserGestureIndicator::consumeUserGesture()) | |
117 return ScriptPromise::rejectWithDOMException(scriptState, DOMException:: create(SecurityError, "Must be handling a user gesture to show a permission requ est.")); | |
118 | |
119 ScriptPromiseResolver* resolver = ScriptPromiseResolver::create(scriptState) ; | 213 ScriptPromiseResolver* resolver = ScriptPromiseResolver::create(scriptState) ; |
120 ScriptPromise promise = resolver->promise(); | 214 ScriptPromise promise = resolver->promise(); |
121 | 215 |
122 WebUSBDeviceRequestOptions webOptions; | 216 if (!m_chooserService) { |
123 convertDeviceRequestOptions(options, &webOptions); | 217 LocalFrame* frame = getExecutionContext() ? static_cast<Document*>(getEx ecutionContext())->frame() : nullptr; |
esprehn
2016/04/06 20:10:45
toDocument() don't static_cast types in blink, our
Reilly Grant (use Gerrit)
2016/04/06 22:30:07
Done.
| |
124 m_client->requestDevice(webOptions, new CallbackPromiseAdapter<USBDevice, US BError>(resolver)); | 218 if (!frame) { |
125 | 219 resolver->reject(DOMException::create(NotSupportedError)); |
220 return promise; | |
221 } | |
222 frame->serviceRegistry()->connectToRemoteService(mojo::GetProxy(&m_choos erService)); | |
223 m_chooserService.set_connection_error_handler([this]() { | |
224 m_chooserService.reset(); | |
225 }); | |
226 } | |
227 | |
228 String errorMessage; | |
229 if (!scriptState->getExecutionContext()->isSecureContext(errorMessage)) { | |
230 resolver->reject(DOMException::create(SecurityError, errorMessage)); | |
231 } else if (!UserGestureIndicator::consumeUserGesture()) { | |
232 resolver->reject(DOMException::create(SecurityError, "Must be handling a user gesture to show a permission request.")); | |
233 } else { | |
234 mojo::WTFArray<device::usb::blink::DeviceFilterPtr> mojoFilters(options. hasFilters() ? options.filters().size() : 0); | |
235 if (options.hasFilters()) { | |
236 const auto& filters = options.filters(); | |
237 for (size_t i = 0; i < filters.size(); ++i) | |
238 mojoFilters[i] = convertDeviceFilter(filters[i]); | |
239 } | |
240 auto adapter = new GetPermissionPromiseAdapter(this, resolver); | |
241 m_chooserService->GetPermission(std::move(mojoFilters), adapter->callbac k()); | |
242 } | |
126 return promise; | 243 return promise; |
127 } | 244 } |
128 | 245 |
129 ExecutionContext* USB::getExecutionContext() const | 246 ExecutionContext* USB::getExecutionContext() const |
130 { | 247 { |
131 return ContextLifecycleObserver::getExecutionContext(); | 248 return ContextLifecycleObserver::getExecutionContext(); |
132 } | 249 } |
133 | 250 |
134 const AtomicString& USB::interfaceName() const | 251 const AtomicString& USB::interfaceName() const |
135 { | 252 { |
136 return EventTargetNames::USB; | 253 return EventTargetNames::USB; |
137 } | 254 } |
138 | 255 |
139 void USB::contextDestroyed() | 256 void USB::contextDestroyed() |
140 { | 257 { |
141 if (m_client) | 258 m_deviceManager.reset(); |
142 m_client->removeObserver(this); | 259 m_chooserService.reset(); |
143 m_client = nullptr; | |
144 } | 260 } |
145 | 261 |
146 void USB::onDeviceConnected(WebPassOwnPtr<WebUSBDevice> device) | 262 void USB::onDeviceChanges(device::usb::blink::DeviceChangeNotificationPtr notifi cation) |
147 { | 263 { |
148 dispatchEvent(USBConnectionEvent::create(EventTypeNames::connect, USBDevice: :create(device.release(), getExecutionContext()))); | 264 auto adapter = new DeviceChangeNotificationAdapter(this); |
149 } | 265 m_deviceManager->GetDeviceChanges(adapter->callback()); |
150 | 266 for (size_t i = 0; i < notification->devices_added.size(); ++i) { |
151 void USB::onDeviceDisconnected(WebPassOwnPtr<WebUSBDevice> device) | 267 device::usb::blink::DevicePtr device; |
152 { | 268 m_deviceManager->GetDevice(notification->devices_added[i]->guid, mojo::G etProxy(&device)); |
153 dispatchEvent(USBConnectionEvent::create(EventTypeNames::disconnect, USBDevi ce::create(device.release(), getExecutionContext()))); | 269 dispatchEvent(USBConnectionEvent::create(EventTypeNames::connect, USBDev ice::create(std::move(notification->devices_added[i]), std::move(device), getExe cutionContext()))); |
270 } | |
271 for (size_t i = 0; i < notification->devices_removed.size(); ++i) | |
272 dispatchEvent(USBConnectionEvent::create(EventTypeNames::disconnect, USB Device::create(std::move(notification->devices_removed[i]), nullptr, getExecutio nContext()))); | |
154 } | 273 } |
155 | 274 |
156 DEFINE_TRACE(USB) | 275 DEFINE_TRACE(USB) |
157 { | 276 { |
158 RefCountedGarbageCollectedEventTargetWithInlineData<USB>::trace(visitor); | 277 RefCountedGarbageCollectedEventTargetWithInlineData<USB>::trace(visitor); |
159 ContextLifecycleObserver::trace(visitor); | 278 ContextLifecycleObserver::trace(visitor); |
160 } | 279 } |
161 | 280 |
162 } // namespace blink | 281 } // namespace blink |
OLD | NEW |