Chromium Code Reviews| 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 |