Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(63)

Side by Side Diff: third_party/WebKit/Source/modules/webusb/USB.cpp

Issue 1850023002: Consume Mojo services directly in Blink's WebUSB implementation. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Move promise adapter pattern into a header that can be shared. Created 4 years, 8 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
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" 7 #include "bindings/core/v8/MojoPromiseAdapter.h"
8 #include "bindings/core/v8/ScriptPromise.h" 8 #include "bindings/core/v8/ScriptPromise.h"
9 #include "bindings/core/v8/ScriptPromiseResolver.h" 9 #include "bindings/core/v8/ScriptPromiseResolver.h"
10 #include "core/dom/DOMException.h" 10 #include "core/dom/DOMException.h"
11 #include "core/dom/Document.h" 11 #include "core/dom/Document.h"
12 #include "core/dom/ExceptionCode.h" 12 #include "core/dom/ExceptionCode.h"
13 #include "device/usb/public/interfaces/device.mojom-blink.h"
13 #include "modules/EventTargetModules.h" 14 #include "modules/EventTargetModules.h"
14 #include "modules/webusb/USBConnectionEvent.h" 15 #include "modules/webusb/USBConnectionEvent.h"
15 #include "modules/webusb/USBController.h"
16 #include "modules/webusb/USBDevice.h" 16 #include "modules/webusb/USBDevice.h"
17 #include "modules/webusb/USBDeviceFilter.h" 17 #include "modules/webusb/USBDeviceFilter.h"
18 #include "modules/webusb/USBDeviceRequestOptions.h" 18 #include "modules/webusb/USBDeviceRequestOptions.h"
19 #include "modules/webusb/USBError.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
23 #include "public/platform/modules/webusb/WebUSBClient.h" 22 namespace usb = device::usb::blink;
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 23
28 namespace blink { 24 namespace blink {
29 namespace { 25 namespace {
30 26
31 void convertDeviceFilter(const USBDeviceFilter& filter, WebUSBDeviceFilter* webF ilter) 27 const char kNoServiceError[] = "USB service unavailable.";
32 { 28
33 webFilter->hasVendorID = filter.hasVendorId(); 29 usb::DeviceFilterPtr convertDeviceFilter(const USBDeviceFilter& filter)
34 if (filter.hasVendorId()) 30 {
35 webFilter->vendorID = filter.vendorId(); 31 auto mojoFilter = usb::DeviceFilter::New();
36 webFilter->hasProductID = filter.hasProductId(); 32 mojoFilter->has_vendor_id = filter.hasVendorId();
37 if (filter.hasProductId()) 33 if (mojoFilter->has_vendor_id)
juncai 2016/04/15 16:52:47 maybe can use: if (mojoFilter->has_vendor_id = fil
38 webFilter->productID = filter.productId(); 34 mojoFilter->vendor_id = filter.vendorId();
39 webFilter->hasClassCode = filter.hasClassCode(); 35 mojoFilter->has_product_id = filter.hasProductId();
40 if (filter.hasClassCode()) 36 if (mojoFilter->has_product_id)
41 webFilter->classCode = filter.classCode(); 37 mojoFilter->product_id = filter.productId();
42 webFilter->hasSubclassCode = filter.hasSubclassCode(); 38 mojoFilter->has_class_code = filter.hasClassCode();
43 if (filter.hasSubclassCode()) 39 if (mojoFilter->has_class_code)
44 webFilter->subclassCode = filter.subclassCode(); 40 mojoFilter->class_code = filter.classCode();
45 webFilter->hasProtocolCode = filter.hasProtocolCode(); 41 mojoFilter->has_subclass_code = filter.hasSubclassCode();
46 if (filter.hasProtocolCode()) 42 if (mojoFilter->has_subclass_code)
47 webFilter->protocolCode = filter.protocolCode(); 43 mojoFilter->subclass_code = filter.subclassCode();
48 } 44 mojoFilter->has_protocol_code = filter.hasProtocolCode();
49 45 if (mojoFilter->has_protocol_code)
50 void convertDeviceRequestOptions(const USBDeviceRequestOptions& options, WebUSBD eviceRequestOptions* webOptions) 46 mojoFilter->protocol_code = filter.protocolCode();
51 { 47 return mojoFilter;
52 DCHECK(options.hasFilters()); 48 }
53 webOptions->filters = WebVector<WebUSBDeviceFilter>(options.filters().size() ); 49
54 for (size_t i = 0; i < options.filters().size(); ++i) { 50 class DeviceChangeNotificationAdapter : public usb::DeviceManager::GetDeviceChan gesCallback::Runnable {
55 convertDeviceFilter(options.filters()[i], &webOptions->filters[i]); 51 WTF_MAKE_NONCOPYABLE(DeviceChangeNotificationAdapter);
56 } 52
57 }
58
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: 53 public:
64 using WebType = OwnPtr<WebVector<WebUSBDevice*>>; 54 DeviceChangeNotificationAdapter(USB* usb) : m_usb(usb)
65 55 {
66 static HeapVector<Member<USBDevice>> take(ScriptPromiseResolver* resolver, P assOwnPtr<WebVector<WebUSBDevice*>> webDevices) 56 }
67 { 57
58 void Run(usb::DeviceChangeNotificationPtr notification)
59 {
60 if (m_usb)
61 m_usb->onDeviceChanges(std::move(notification));
62 }
63
64 usb::DeviceManager::GetDeviceChangesCallback callback()
65 {
66 return usb::DeviceManager::GetDeviceChangesCallback(this);
67 }
68
69 private:
70 WeakPersistent<USB> m_usb;
71 };
72
73 class GetDevicesPromiseAdapter : public MojoPromiseAdapter<usb::DeviceManager::G etDevicesCallback> {
74 WTF_MAKE_NONCOPYABLE(GetDevicesPromiseAdapter);
75
76 public:
77 GetDevicesPromiseAdapter(USB* usb, ScriptPromiseResolver* resolver)
78 : MojoPromiseAdapter(resolver)
79 , m_usb(usb)
80 {
81 }
82
83 ~GetDevicesPromiseAdapter() override
84 {
85 if (active())
86 reject(DOMException::create(NotFoundError, kNoServiceError));
87 }
88
89 void Run(mojo::WTFArray<usb::DeviceInfoPtr> deviceInfos) override
90 {
91 if (!active())
92 return;
93
68 HeapVector<Member<USBDevice>> devices; 94 HeapVector<Member<USBDevice>> devices;
69 for (const auto webDevice : *webDevices) 95 for (auto& deviceInfo : deviceInfos.PassStorage()) {
70 devices.append(USBDevice::create(adoptPtr(webDevice), resolver->getE xecutionContext())); 96 usb::DevicePtr device;
71 return devices; 97 m_usb->deviceManager()->GetDevice(deviceInfo->guid, mojo::GetProxy(& device));
72 } 98 devices.append(USBDevice::create(std::move(deviceInfo), std::move(de vice), executionContext()));
99 }
100 resolve(devices);
101 }
102
103 private:
104 Persistent<USB> m_usb;
105 };
106
107 class GetPermissionPromiseAdapter : public MojoPromiseAdapter<usb::ChooserServic e::GetPermissionCallback> {
108 WTF_MAKE_NONCOPYABLE(GetPermissionPromiseAdapter);
109
110 public:
111 GetPermissionPromiseAdapter(USB* usb, ScriptPromiseResolver* resolver)
112 : MojoPromiseAdapter(resolver)
113 , m_usb(usb)
114 {
115 }
116
117 ~GetPermissionPromiseAdapter() override
118 {
119 if (active())
120 reject(DOMException::create(NotFoundError, kNoServiceError));
121 }
122
123 void Run(usb::DeviceInfoPtr deviceInfo) override
124 {
125 if (!active())
126 return;
127
128 if (deviceInfo) {
129 usb::DevicePtr device;
130 m_usb->deviceManager()->GetDevice(deviceInfo->guid, mojo::GetProxy(& device));
131 resolve(USBDevice::create(std::move(deviceInfo), std::move(device), executionContext()));
132 } else {
133 reject(DOMException::create(NotFoundError, "No device selected."));
134 }
135 }
136
137 private:
138 Persistent<USB> m_usb;
73 }; 139 };
74 140
75 } // namespace 141 } // namespace
76 142
77 USB::USB(LocalFrame& frame) 143 USB::USB(LocalFrame& frame)
78 : ContextLifecycleObserver(frame.document()) 144 : ContextLifecycleObserver(frame.document())
79 , m_client(USBController::from(frame).client()) 145 {
80 { 146 frame.serviceRegistry()->connectToRemoteService(mojo::GetProxy(&m_deviceMana ger));
81 ThreadState::current()->registerPreFinalizer(this); 147 m_deviceManager.set_connection_error_handler([this]() {
82 if (m_client) 148 m_deviceManager.reset();
83 m_client->addObserver(this); 149 });
150 // Set up two sequential calls to GetDeviceChanges to avoid latency.
151 auto adapter = new DeviceChangeNotificationAdapter(this);
152 m_deviceManager->GetDeviceChanges(adapter->callback());
153 adapter = new DeviceChangeNotificationAdapter(this);
154 m_deviceManager->GetDeviceChanges(adapter->callback());
84 } 155 }
85 156
86 USB::~USB() 157 USB::~USB()
87 { 158 {
88 } 159 }
89 160
90 void USB::dispose()
91 {
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 }
98
99 ScriptPromise USB::getDevices(ScriptState* scriptState) 161 ScriptPromise USB::getDevices(ScriptState* scriptState)
100 { 162 {
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) ; 163 ScriptPromiseResolver* resolver = ScriptPromiseResolver::create(scriptState) ;
109 ScriptPromise promise = resolver->promise(); 164 ScriptPromise promise = resolver->promise();
110 m_client->getDevices(new CallbackPromiseAdapter<DeviceArray, USBError>(resol ver)); 165 if (!m_deviceManager) {
111 166 resolver->reject(DOMException::create(NotSupportedError));
167 } else {
168 String errorMessage;
169 if (!scriptState->getExecutionContext()->isSecureContext(errorMessage)) {
170 resolver->reject(DOMException::create(SecurityError, errorMessage));
171 } else {
172 auto adapter = new GetDevicesPromiseAdapter(this, resolver);
173 m_deviceManager->GetDevices(nullptr, adapter->callback());
174 }
175 }
112 return promise; 176 return promise;
113 } 177 }
114 178
115 ScriptPromise USB::requestDevice(ScriptState* scriptState, const USBDeviceReques tOptions& options) 179 ScriptPromise USB::requestDevice(ScriptState* scriptState, const USBDeviceReques tOptions& options)
116 { 180 {
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) ; 181 ScriptPromiseResolver* resolver = ScriptPromiseResolver::create(scriptState) ;
128 ScriptPromise promise = resolver->promise(); 182 ScriptPromise promise = resolver->promise();
129 183
130 WebUSBDeviceRequestOptions webOptions; 184 if (!m_chooserService) {
131 convertDeviceRequestOptions(options, &webOptions); 185 LocalFrame* frame = getExecutionContext() ? toDocument(getExecutionConte xt())->frame() : nullptr;
132 m_client->requestDevice(webOptions, new CallbackPromiseAdapter<USBDevice, US BError>(resolver)); 186 if (!frame) {
133 187 resolver->reject(DOMException::create(NotSupportedError));
188 return promise;
189 }
190 frame->serviceRegistry()->connectToRemoteService(mojo::GetProxy(&m_choos erService));
191 m_chooserService.set_connection_error_handler([this]() {
192 m_chooserService.reset();
193 });
194 }
195
196 String errorMessage;
197 if (!scriptState->getExecutionContext()->isSecureContext(errorMessage)) {
198 resolver->reject(DOMException::create(SecurityError, errorMessage));
199 } else if (!UserGestureIndicator::consumeUserGesture()) {
200 resolver->reject(DOMException::create(SecurityError, "Must be handling a user gesture to show a permission request."));
201 } else {
202 Vector<usb::DeviceFilterPtr> filters;
203 if (options.hasFilters()) {
204 filters.reserveCapacity(options.filters().size());
205 for (const auto& filter : options.filters())
206 filters.append(convertDeviceFilter(filter));
207 }
208 auto adapter = new GetPermissionPromiseAdapter(this, resolver);
209 m_chooserService->GetPermission(std::move(filters), adapter->callback()) ;
210 }
134 return promise; 211 return promise;
135 } 212 }
136 213
137 ExecutionContext* USB::getExecutionContext() const 214 ExecutionContext* USB::getExecutionContext() const
138 { 215 {
139 return ContextLifecycleObserver::getExecutionContext(); 216 return ContextLifecycleObserver::getExecutionContext();
140 } 217 }
141 218
142 const AtomicString& USB::interfaceName() const 219 const AtomicString& USB::interfaceName() const
143 { 220 {
144 return EventTargetNames::USB; 221 return EventTargetNames::USB;
145 } 222 }
146 223
147 void USB::contextDestroyed() 224 void USB::contextDestroyed()
148 { 225 {
149 if (m_client) 226 m_deviceManager.reset();
150 m_client->removeObserver(this); 227 m_chooserService.reset();
151 m_client = nullptr;
152 } 228 }
153 229
154 void USB::onDeviceConnected(std::unique_ptr<WebUSBDevice> device) 230 void USB::onDeviceChanges(usb::DeviceChangeNotificationPtr notification)
155 { 231 {
156 dispatchEvent(USBConnectionEvent::create(EventTypeNames::connect, USBDevice: :create(adoptPtr(device.release()), getExecutionContext()))); 232 auto adapter = new DeviceChangeNotificationAdapter(this);
157 } 233 m_deviceManager->GetDeviceChanges(adapter->callback());
158 234 for (auto& deviceInfo : notification->devices_added.PassStorage()) {
159 void USB::onDeviceDisconnected(std::unique_ptr<WebUSBDevice> device) 235 usb::DevicePtr device;
160 { 236 m_deviceManager->GetDevice(deviceInfo->guid, mojo::GetProxy(&device));
161 dispatchEvent(USBConnectionEvent::create(EventTypeNames::disconnect, USBDevi ce::create(adoptPtr(device.release()), getExecutionContext()))); 237 dispatchEvent(USBConnectionEvent::create(EventTypeNames::connect, USBDev ice::create(std::move(deviceInfo), std::move(device), getExecutionContext())));
238 }
239 for (auto& deviceInfo : notification->devices_removed.PassStorage())
240 dispatchEvent(USBConnectionEvent::create(EventTypeNames::disconnect, USB Device::create(std::move(deviceInfo), nullptr, getExecutionContext())));
162 } 241 }
163 242
164 DEFINE_TRACE(USB) 243 DEFINE_TRACE(USB)
165 { 244 {
166 EventTargetWithInlineData::trace(visitor); 245 EventTargetWithInlineData::trace(visitor);
167 ContextLifecycleObserver::trace(visitor); 246 ContextLifecycleObserver::trace(visitor);
168 } 247 }
169 248
170 } // namespace blink 249 } // namespace blink
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698