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" | 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 |
OLD | NEW |