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 3bd8b8fc2ed3e2bf0c951120c0da1548f8850d94..07e4a442343872abd708661387ca1da73e6a3861 100644 |
| --- a/third_party/WebKit/Source/modules/webusb/USB.cpp |
| +++ b/third_party/WebKit/Source/modules/webusb/USB.cpp |
| @@ -4,7 +4,6 @@ |
| #include "modules/webusb/USB.h" |
| -#include "bindings/core/v8/CallbackPromiseAdapter.h" |
| #include "bindings/core/v8/ScriptPromise.h" |
| #include "bindings/core/v8/ScriptPromiseResolver.h" |
| #include "core/dom/DOMException.h" |
| @@ -12,117 +11,235 @@ |
| #include "core/dom/ExceptionCode.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 blink { |
| namespace { |
| -void convertDeviceFilter(const USBDeviceFilter& filter, WebUSBDeviceFilter* webFilter) |
| +const char kNoServiceError[] = "USB service unavailable."; |
| + |
| +device::usb::blink::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 = device::usb::blink::DeviceFilter::New(); |
| + mojoFilter->has_vendor_id = filter.hasVendorId(); |
| + if (mojoFilter->has_vendor_id) |
| + 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 device::usb::blink::DeviceManager::GetDeviceChangesCallback::Runnable { |
| + WTF_MAKE_NONCOPYABLE(DeviceChangeNotificationAdapter); |
| + |
| +public: |
| + DeviceChangeNotificationAdapter(USB* usb) : m_usb(usb) |
| + { |
| + } |
| + |
| + void Run(device::usb::blink::DeviceChangeNotificationPtr notification) const |
| + { |
| + if (m_usb) |
| + m_usb->onDeviceChanges(std::move(notification)); |
| } |
| -} |
| -// Allows using a CallbackPromiseAdapter with a WebVector to resolve the |
| -// getDevices() promise with a HeapVector owning USBDevices. |
| -class DeviceArray { |
| - STATIC_ONLY(DeviceArray); |
| + device::usb::blink::DeviceManager::GetDeviceChangesCallback callback() |
| + { |
| + return device::usb::blink::DeviceManager::GetDeviceChangesCallback(this); |
| + } |
| + |
| +private: |
| + WeakPersistent<USB> m_usb; |
| +}; |
| + |
| +template <typename Callback> |
| +class PromiseAdapterBase : public Callback::Runnable { |
| + WTF_MAKE_NONCOPYABLE(PromiseAdapterBase); |
| + |
| public: |
| - using WebType = OwnPtr<WebVector<WebUSBDevice*>>; |
| + PromiseAdapterBase(ScriptPromiseResolver* resolver) : m_resolver(resolver) |
| + { |
| + } |
| - static HeapVector<Member<USBDevice>> take(ScriptPromiseResolver* resolver, PassOwnPtr<WebVector<WebUSBDevice*>> webDevices) |
| + ~PromiseAdapterBase() override |
| { |
| + if (active()) |
| + m_resolver->reject(DOMException::create(NotFoundError, kNoServiceError)); |
| + } |
| + |
| + template <typename T> |
| + void resolve(T value) const |
| + { |
| + ASSERT(active()); |
| + m_resolver->resolve(value); |
| + m_resolver = nullptr; |
| + } |
| + |
| + template <typename T> |
| + void reject(T value) const |
| + { |
| + ASSERT(active()); |
| + m_resolver->reject(value); |
| + m_resolver = nullptr; |
| + } |
| + |
| + bool active() const |
| + { |
| + return m_resolver && executionContext() && !executionContext()->activeDOMObjectsAreStopped(); |
| + } |
| + |
| + Callback callback() |
| + { |
| + return Callback(this); |
| + } |
| + |
| + ExecutionContext* executionContext() const { return m_resolver->getExecutionContext(); } |
| + |
| +private: |
| + // This field is mutable so that resolve() and reject() can be called from |
| + // 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
|
| + mutable Persistent<ScriptPromiseResolver> m_resolver; |
| +}; |
| + |
| +class GetDevicesPromiseAdapter : public PromiseAdapterBase<device::usb::blink::DeviceManager::GetDevicesCallback> { |
| + WTF_MAKE_NONCOPYABLE(GetDevicesPromiseAdapter); |
| + |
| +public: |
| + GetDevicesPromiseAdapter(USB* usb, ScriptPromiseResolver* resolver) |
| + : PromiseAdapterBase(resolver) |
| + , m_usb(usb) |
| + { |
| + } |
| + |
| + void Run(mojo::WTFArray<device::usb::blink::DeviceInfoPtr> deviceInfos) const override |
| + { |
| + if (!active()) |
| + return; |
| + |
| HeapVector<Member<USBDevice>> devices; |
| - for (const auto webDevice : *webDevices) |
| - devices.append(USBDevice::create(adoptPtr(webDevice), resolver->getExecutionContext())); |
| - return devices; |
| + 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.
|
| + device::usb::blink::DevicePtr device; |
| + m_usb->deviceManager()->GetDevice(deviceInfos[i]->guid, mojo::GetProxy(&device)); |
| + devices.append(USBDevice::create(std::move(deviceInfos[i]), std::move(device), executionContext())); |
| + } |
| + resolve(devices); |
| + } |
| + |
| +private: |
| + Persistent<USB> m_usb; |
| +}; |
| + |
| +class GetPermissionPromiseAdapter : public PromiseAdapterBase<device::usb::blink::ChooserService::GetPermissionCallback> { |
| + WTF_MAKE_NONCOPYABLE(GetPermissionPromiseAdapter); |
| + |
| +public: |
| + GetPermissionPromiseAdapter(USB* usb, ScriptPromiseResolver* resolver) |
| + : PromiseAdapterBase(resolver) |
| + , m_usb(usb) |
| + { |
| + } |
| + |
| + void Run(device::usb::blink::DeviceInfoPtr deviceInfo) const override |
| + { |
| + if (!active()) |
| + return; |
| + |
| + if (deviceInfo) { |
| + device::usb::blink::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()) |
| { |
| - if (m_client) |
| - m_client->addObserver(this); |
| + frame.serviceRegistry()->connectToRemoteService(mojo::GetProxy(&m_deviceManager)); |
| + m_deviceManager.set_connection_error_handler([this]() { |
| + m_deviceManager.reset(); |
| + }); |
| + auto adapter = new DeviceChangeNotificationAdapter(this); |
| + m_deviceManager->GetDeviceChanges(adapter->callback()); |
| + adapter = new DeviceChangeNotificationAdapter(this); |
| + 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
|
| } |
| USB::~USB() |
| { |
| - if (m_client) |
| - m_client->removeObserver(this); |
| } |
| 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() ? static_cast<Document*>(getExecutionContext())->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.
|
| + 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 { |
| + mojo::WTFArray<device::usb::blink::DeviceFilterPtr> mojoFilters(options.hasFilters() ? options.filters().size() : 0); |
| + if (options.hasFilters()) { |
| + const auto& filters = options.filters(); |
| + for (size_t i = 0; i < filters.size(); ++i) |
| + mojoFilters[i] = convertDeviceFilter(filters[i]); |
| + } |
| + auto adapter = new GetPermissionPromiseAdapter(this, resolver); |
| + m_chooserService->GetPermission(std::move(mojoFilters), adapter->callback()); |
| + } |
| return promise; |
| } |
| @@ -138,19 +255,21 @@ const AtomicString& USB::interfaceName() const |
| void USB::contextDestroyed() |
| { |
| - if (m_client) |
| - m_client->removeObserver(this); |
| - m_client = nullptr; |
| + m_deviceManager.reset(); |
| + m_chooserService.reset(); |
| } |
| -void USB::onDeviceConnected(WebPassOwnPtr<WebUSBDevice> device) |
| +void USB::onDeviceChanges(device::usb::blink::DeviceChangeNotificationPtr notification) |
| { |
| - dispatchEvent(USBConnectionEvent::create(EventTypeNames::connect, USBDevice::create(device.release(), getExecutionContext()))); |
| -} |
| - |
| -void USB::onDeviceDisconnected(WebPassOwnPtr<WebUSBDevice> device) |
| -{ |
| - dispatchEvent(USBConnectionEvent::create(EventTypeNames::disconnect, USBDevice::create(device.release(), getExecutionContext()))); |
| + auto adapter = new DeviceChangeNotificationAdapter(this); |
| + m_deviceManager->GetDeviceChanges(adapter->callback()); |
| + for (size_t i = 0; i < notification->devices_added.size(); ++i) { |
| + device::usb::blink::DevicePtr device; |
| + m_deviceManager->GetDevice(notification->devices_added[i]->guid, mojo::GetProxy(&device)); |
| + dispatchEvent(USBConnectionEvent::create(EventTypeNames::connect, USBDevice::create(std::move(notification->devices_added[i]), std::move(device), getExecutionContext()))); |
| + } |
| + for (size_t i = 0; i < notification->devices_removed.size(); ++i) |
| + dispatchEvent(USBConnectionEvent::create(EventTypeNames::disconnect, USBDevice::create(std::move(notification->devices_removed[i]), nullptr, getExecutionContext()))); |
| } |
| DEFINE_TRACE(USB) |