Chromium Code Reviews| Index: third_party/WebKit/Source/modules/webusb/USB.cpp |
| diff --git a/third_party/WebKit/Source/modules/webusb/USB.cpp b/third_party/WebKit/Source/modules/webusb/USB.cpp |
| index cd4b4aae2853894f8c7c83cbc119d74b7fdbb85d..bbd7b623d52dbacb3062953f6004b20592f42ca2 100644 |
| --- a/third_party/WebKit/Source/modules/webusb/USB.cpp |
| +++ b/third_party/WebKit/Source/modules/webusb/USB.cpp |
| @@ -4,133 +4,210 @@ |
| #include "modules/webusb/USB.h" |
| -#include "bindings/core/v8/CallbackPromiseAdapter.h" |
| +#include "bindings/core/v8/MojoPromiseAdapter.h" |
| #include "bindings/core/v8/ScriptPromise.h" |
| #include "bindings/core/v8/ScriptPromiseResolver.h" |
| #include "core/dom/DOMException.h" |
| #include "core/dom/Document.h" |
| #include "core/dom/ExceptionCode.h" |
| +#include "device/usb/public/interfaces/device.mojom-blink.h" |
| #include "modules/EventTargetModules.h" |
| #include "modules/webusb/USBConnectionEvent.h" |
| -#include "modules/webusb/USBController.h" |
| #include "modules/webusb/USBDevice.h" |
| #include "modules/webusb/USBDeviceFilter.h" |
| #include "modules/webusb/USBDeviceRequestOptions.h" |
| -#include "modules/webusb/USBError.h" |
| #include "platform/UserGestureIndicator.h" |
| -#include "public/platform/Platform.h" |
| -#include "public/platform/WebVector.h" |
| -#include "public/platform/modules/webusb/WebUSBClient.h" |
| -#include "public/platform/modules/webusb/WebUSBDeviceFilter.h" |
| -#include "public/platform/modules/webusb/WebUSBDeviceRequestOptions.h" |
| -#include "public/platform/modules/webusb/WebUSBError.h" |
| +#include "public/platform/ServiceRegistry.h" |
| + |
| +namespace usb = device::usb::blink; |
| namespace blink { |
| namespace { |
| -void convertDeviceFilter(const USBDeviceFilter& filter, WebUSBDeviceFilter* webFilter) |
| +const char kNoServiceError[] = "USB service unavailable."; |
| + |
| +usb::DeviceFilterPtr convertDeviceFilter(const USBDeviceFilter& filter) |
| { |
| - webFilter->hasVendorID = filter.hasVendorId(); |
| - if (filter.hasVendorId()) |
| - webFilter->vendorID = filter.vendorId(); |
| - webFilter->hasProductID = filter.hasProductId(); |
| - if (filter.hasProductId()) |
| - webFilter->productID = filter.productId(); |
| - webFilter->hasClassCode = filter.hasClassCode(); |
| - if (filter.hasClassCode()) |
| - webFilter->classCode = filter.classCode(); |
| - webFilter->hasSubclassCode = filter.hasSubclassCode(); |
| - if (filter.hasSubclassCode()) |
| - webFilter->subclassCode = filter.subclassCode(); |
| - webFilter->hasProtocolCode = filter.hasProtocolCode(); |
| - if (filter.hasProtocolCode()) |
| - webFilter->protocolCode = filter.protocolCode(); |
| + auto mojoFilter = usb::DeviceFilter::New(); |
| + mojoFilter->has_vendor_id = filter.hasVendorId(); |
| + if (mojoFilter->has_vendor_id) |
|
juncai
2016/04/15 16:52:47
maybe can use:
if (mojoFilter->has_vendor_id = fil
|
| + mojoFilter->vendor_id = filter.vendorId(); |
| + mojoFilter->has_product_id = filter.hasProductId(); |
| + if (mojoFilter->has_product_id) |
| + mojoFilter->product_id = filter.productId(); |
| + mojoFilter->has_class_code = filter.hasClassCode(); |
| + if (mojoFilter->has_class_code) |
| + mojoFilter->class_code = filter.classCode(); |
| + mojoFilter->has_subclass_code = filter.hasSubclassCode(); |
| + if (mojoFilter->has_subclass_code) |
| + mojoFilter->subclass_code = filter.subclassCode(); |
| + mojoFilter->has_protocol_code = filter.hasProtocolCode(); |
| + if (mojoFilter->has_protocol_code) |
| + mojoFilter->protocol_code = filter.protocolCode(); |
| + return mojoFilter; |
| } |
| -void convertDeviceRequestOptions(const USBDeviceRequestOptions& options, WebUSBDeviceRequestOptions* webOptions) |
| -{ |
| - DCHECK(options.hasFilters()); |
| - webOptions->filters = WebVector<WebUSBDeviceFilter>(options.filters().size()); |
| - for (size_t i = 0; i < options.filters().size(); ++i) { |
| - convertDeviceFilter(options.filters()[i], &webOptions->filters[i]); |
| +class DeviceChangeNotificationAdapter : public usb::DeviceManager::GetDeviceChangesCallback::Runnable { |
| + WTF_MAKE_NONCOPYABLE(DeviceChangeNotificationAdapter); |
| + |
| +public: |
| + DeviceChangeNotificationAdapter(USB* usb) : m_usb(usb) |
| + { |
| } |
| -} |
| -// Allows using a CallbackPromiseAdapter with a WebVector to resolve the |
| -// getDevices() promise with a HeapVector owning USBDevices. |
| -class DeviceArray { |
| - STATIC_ONLY(DeviceArray); |
| + void Run(usb::DeviceChangeNotificationPtr notification) |
| + { |
| + if (m_usb) |
| + m_usb->onDeviceChanges(std::move(notification)); |
| + } |
| + |
| + usb::DeviceManager::GetDeviceChangesCallback callback() |
| + { |
| + return usb::DeviceManager::GetDeviceChangesCallback(this); |
| + } |
| + |
| +private: |
| + WeakPersistent<USB> m_usb; |
| +}; |
| + |
| +class GetDevicesPromiseAdapter : public MojoPromiseAdapter<usb::DeviceManager::GetDevicesCallback> { |
| + WTF_MAKE_NONCOPYABLE(GetDevicesPromiseAdapter); |
| + |
| public: |
| - using WebType = OwnPtr<WebVector<WebUSBDevice*>>; |
| + GetDevicesPromiseAdapter(USB* usb, ScriptPromiseResolver* resolver) |
| + : MojoPromiseAdapter(resolver) |
| + , m_usb(usb) |
| + { |
| + } |
| + |
| + ~GetDevicesPromiseAdapter() override |
| + { |
| + if (active()) |
| + reject(DOMException::create(NotFoundError, kNoServiceError)); |
| + } |
| - static HeapVector<Member<USBDevice>> take(ScriptPromiseResolver* resolver, PassOwnPtr<WebVector<WebUSBDevice*>> webDevices) |
| + void Run(mojo::WTFArray<usb::DeviceInfoPtr> deviceInfos) override |
| { |
| + if (!active()) |
| + return; |
| + |
| HeapVector<Member<USBDevice>> devices; |
| - for (const auto webDevice : *webDevices) |
| - devices.append(USBDevice::create(adoptPtr(webDevice), resolver->getExecutionContext())); |
| - return devices; |
| + for (auto& deviceInfo : deviceInfos.PassStorage()) { |
| + usb::DevicePtr device; |
| + m_usb->deviceManager()->GetDevice(deviceInfo->guid, mojo::GetProxy(&device)); |
| + devices.append(USBDevice::create(std::move(deviceInfo), std::move(device), executionContext())); |
| + } |
| + resolve(devices); |
| } |
| + |
| +private: |
| + Persistent<USB> m_usb; |
| +}; |
| + |
| +class GetPermissionPromiseAdapter : public MojoPromiseAdapter<usb::ChooserService::GetPermissionCallback> { |
| + WTF_MAKE_NONCOPYABLE(GetPermissionPromiseAdapter); |
| + |
| +public: |
| + GetPermissionPromiseAdapter(USB* usb, ScriptPromiseResolver* resolver) |
| + : MojoPromiseAdapter(resolver) |
| + , m_usb(usb) |
| + { |
| + } |
| + |
| + ~GetPermissionPromiseAdapter() override |
| + { |
| + if (active()) |
| + reject(DOMException::create(NotFoundError, kNoServiceError)); |
| + } |
| + |
| + void Run(usb::DeviceInfoPtr deviceInfo) override |
| + { |
| + if (!active()) |
| + return; |
| + |
| + if (deviceInfo) { |
| + usb::DevicePtr device; |
| + m_usb->deviceManager()->GetDevice(deviceInfo->guid, mojo::GetProxy(&device)); |
| + resolve(USBDevice::create(std::move(deviceInfo), std::move(device), executionContext())); |
| + } else { |
| + reject(DOMException::create(NotFoundError, "No device selected.")); |
| + } |
| + } |
| + |
| +private: |
| + Persistent<USB> m_usb; |
| }; |
| } // namespace |
| USB::USB(LocalFrame& frame) |
| : ContextLifecycleObserver(frame.document()) |
| - , m_client(USBController::from(frame).client()) |
| { |
| - ThreadState::current()->registerPreFinalizer(this); |
| - if (m_client) |
| - m_client->addObserver(this); |
| + frame.serviceRegistry()->connectToRemoteService(mojo::GetProxy(&m_deviceManager)); |
| + m_deviceManager.set_connection_error_handler([this]() { |
| + m_deviceManager.reset(); |
| + }); |
| + // Set up two sequential calls to GetDeviceChanges to avoid latency. |
| + auto adapter = new DeviceChangeNotificationAdapter(this); |
| + m_deviceManager->GetDeviceChanges(adapter->callback()); |
| + adapter = new DeviceChangeNotificationAdapter(this); |
| + m_deviceManager->GetDeviceChanges(adapter->callback()); |
| } |
| USB::~USB() |
| { |
| } |
| -void USB::dispose() |
| -{ |
| - // Promptly clears a raw reference from content/ to an on-heap object |
| - // so that content/ doesn't access it in a lazy sweeping phase. |
| - if (m_client) |
| - m_client->removeObserver(this); |
| - m_client = nullptr; |
| -} |
| - |
| ScriptPromise USB::getDevices(ScriptState* scriptState) |
| { |
| - if (!m_client) |
| - return ScriptPromise::rejectWithDOMException(scriptState, DOMException::create(NotSupportedError)); |
| - |
| - String errorMessage; |
| - if (!scriptState->getExecutionContext()->isSecureContext(errorMessage)) |
| - return ScriptPromise::rejectWithDOMException(scriptState, DOMException::create(SecurityError, errorMessage)); |
| - |
| ScriptPromiseResolver* resolver = ScriptPromiseResolver::create(scriptState); |
| ScriptPromise promise = resolver->promise(); |
| - m_client->getDevices(new CallbackPromiseAdapter<DeviceArray, USBError>(resolver)); |
| - |
| + if (!m_deviceManager) { |
| + resolver->reject(DOMException::create(NotSupportedError)); |
| + } else { |
| + String errorMessage; |
| + if (!scriptState->getExecutionContext()->isSecureContext(errorMessage)) { |
| + resolver->reject(DOMException::create(SecurityError, errorMessage)); |
| + } else { |
| + auto adapter = new GetDevicesPromiseAdapter(this, resolver); |
| + m_deviceManager->GetDevices(nullptr, adapter->callback()); |
| + } |
| + } |
| return promise; |
| } |
| ScriptPromise USB::requestDevice(ScriptState* scriptState, const USBDeviceRequestOptions& options) |
| { |
| - if (!m_client) |
| - return ScriptPromise::rejectWithDOMException(scriptState, DOMException::create(NotSupportedError)); |
| - |
| - String errorMessage; |
| - if (!scriptState->getExecutionContext()->isSecureContext(errorMessage)) |
| - return ScriptPromise::rejectWithDOMException(scriptState, DOMException::create(SecurityError, errorMessage)); |
| - |
| - if (!UserGestureIndicator::consumeUserGesture()) |
| - return ScriptPromise::rejectWithDOMException(scriptState, DOMException::create(SecurityError, "Must be handling a user gesture to show a permission request.")); |
| - |
| ScriptPromiseResolver* resolver = ScriptPromiseResolver::create(scriptState); |
| ScriptPromise promise = resolver->promise(); |
| - WebUSBDeviceRequestOptions webOptions; |
| - convertDeviceRequestOptions(options, &webOptions); |
| - m_client->requestDevice(webOptions, new CallbackPromiseAdapter<USBDevice, USBError>(resolver)); |
| + if (!m_chooserService) { |
| + LocalFrame* frame = getExecutionContext() ? toDocument(getExecutionContext())->frame() : nullptr; |
| + if (!frame) { |
| + resolver->reject(DOMException::create(NotSupportedError)); |
| + return promise; |
| + } |
| + frame->serviceRegistry()->connectToRemoteService(mojo::GetProxy(&m_chooserService)); |
| + m_chooserService.set_connection_error_handler([this]() { |
| + m_chooserService.reset(); |
| + }); |
| + } |
| + String errorMessage; |
| + if (!scriptState->getExecutionContext()->isSecureContext(errorMessage)) { |
| + resolver->reject(DOMException::create(SecurityError, errorMessage)); |
| + } else if (!UserGestureIndicator::consumeUserGesture()) { |
| + resolver->reject(DOMException::create(SecurityError, "Must be handling a user gesture to show a permission request.")); |
| + } else { |
| + Vector<usb::DeviceFilterPtr> filters; |
| + if (options.hasFilters()) { |
| + filters.reserveCapacity(options.filters().size()); |
| + for (const auto& filter : options.filters()) |
| + filters.append(convertDeviceFilter(filter)); |
| + } |
| + auto adapter = new GetPermissionPromiseAdapter(this, resolver); |
| + m_chooserService->GetPermission(std::move(filters), adapter->callback()); |
| + } |
| return promise; |
| } |
| @@ -146,19 +223,21 @@ const AtomicString& USB::interfaceName() const |
| void USB::contextDestroyed() |
| { |
| - if (m_client) |
| - m_client->removeObserver(this); |
| - m_client = nullptr; |
| -} |
| - |
| -void USB::onDeviceConnected(std::unique_ptr<WebUSBDevice> device) |
| -{ |
| - dispatchEvent(USBConnectionEvent::create(EventTypeNames::connect, USBDevice::create(adoptPtr(device.release()), getExecutionContext()))); |
| + m_deviceManager.reset(); |
| + m_chooserService.reset(); |
| } |
| -void USB::onDeviceDisconnected(std::unique_ptr<WebUSBDevice> device) |
| +void USB::onDeviceChanges(usb::DeviceChangeNotificationPtr notification) |
| { |
| - dispatchEvent(USBConnectionEvent::create(EventTypeNames::disconnect, USBDevice::create(adoptPtr(device.release()), getExecutionContext()))); |
| + auto adapter = new DeviceChangeNotificationAdapter(this); |
| + m_deviceManager->GetDeviceChanges(adapter->callback()); |
| + for (auto& deviceInfo : notification->devices_added.PassStorage()) { |
| + usb::DevicePtr device; |
| + m_deviceManager->GetDevice(deviceInfo->guid, mojo::GetProxy(&device)); |
| + dispatchEvent(USBConnectionEvent::create(EventTypeNames::connect, USBDevice::create(std::move(deviceInfo), std::move(device), getExecutionContext()))); |
| + } |
| + for (auto& deviceInfo : notification->devices_removed.PassStorage()) |
| + dispatchEvent(USBConnectionEvent::create(EventTypeNames::disconnect, USBDevice::create(std::move(deviceInfo), nullptr, getExecutionContext()))); |
| } |
| DEFINE_TRACE(USB) |