| 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..4811ea22af4f0c53fa6bc874ea4271c4ea178d6c 100644
|
| --- a/third_party/WebKit/Source/modules/webusb/USB.cpp
|
| +++ b/third_party/WebKit/Source/modules/webusb/USB.cpp
|
| @@ -4,133 +4,138 @@
|
|
|
| #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"
|
| #include "core/dom/Document.h"
|
| #include "core/dom/ExceptionCode.h"
|
| +#include "device/usb/public/interfaces/device.mojom-wtf.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/MojoHelper.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"
|
| +#include "wtf/Functional.h"
|
| +
|
| +namespace usb = device::usb::wtf;
|
|
|
| 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)
|
| + 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)
|
| +bool isActive(ScriptPromiseResolver* resolver)
|
| {
|
| - 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]);
|
| - }
|
| + ExecutionContext* context = resolver->getExecutionContext();
|
| + return context && !context->activeDOMObjectsAreStopped();
|
| }
|
|
|
| -// Allows using a CallbackPromiseAdapter with a WebVector to resolve the
|
| -// getDevices() promise with a HeapVector owning USBDevices.
|
| -class DeviceArray {
|
| - STATIC_ONLY(DeviceArray);
|
| -public:
|
| - using WebType = OwnPtr<WebVector<WebUSBDevice*>>;
|
| -
|
| - static HeapVector<Member<USBDevice>> take(ScriptPromiseResolver* resolver, PassOwnPtr<WebVector<WebUSBDevice*>> webDevices)
|
| - {
|
| - HeapVector<Member<USBDevice>> devices;
|
| - for (const auto webDevice : *webDevices)
|
| - devices.append(USBDevice::create(adoptPtr(webDevice), resolver->getExecutionContext()));
|
| - return devices;
|
| - }
|
| -};
|
| -
|
| } // 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();
|
| + for (ScriptPromiseResolver* resolver : m_deviceManagerRequests) {
|
| + if (isActive(resolver))
|
| + resolver->reject(DOMException::create(NotFoundError, kNoServiceError));
|
| + }
|
| + m_deviceManagerRequests.clear();
|
| + });
|
| + // Set up two sequential calls to GetDeviceChanges to avoid latency.
|
| + m_deviceManager->GetDeviceChanges(createBaseCallback(bind<usb::DeviceChangeNotificationPtr>(&USB::onDeviceChanges, this)));
|
| + m_deviceManager->GetDeviceChanges(createBaseCallback(bind<usb::DeviceChangeNotificationPtr>(&USB::onDeviceChanges, this)));
|
| }
|
|
|
| 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;
|
| + DCHECK(!m_deviceManager);
|
| + DCHECK(m_deviceManagerRequests.isEmpty());
|
| + DCHECK(!m_chooserService);
|
| + DCHECK(m_chooserServiceRequests.isEmpty());
|
| }
|
|
|
| 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 {
|
| + m_deviceManagerRequests.add(resolver);
|
| + m_deviceManager->GetDevices(nullptr, createBaseCallback(bind<mojo::WTFArray<usb::DeviceInfoPtr>>(&USB::onGetDevices, this, resolver)));
|
| + }
|
| + }
|
| 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();
|
| + for (ScriptPromiseResolver* resolver : m_chooserServiceRequests) {
|
| + if (isActive(resolver))
|
| + resolver->reject(DOMException::create(NotFoundError, kNoServiceError));
|
| + }
|
| + m_chooserServiceRequests.clear();
|
| + });
|
| + }
|
|
|
| + 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));
|
| + }
|
| + m_chooserServiceRequests.add(resolver);
|
| + m_chooserService->GetPermission(std::move(filters), createBaseCallback(bind<usb::DeviceInfoPtr>(&USB::onGetPermission, this, resolver)));
|
| + }
|
| return promise;
|
| }
|
|
|
| @@ -146,25 +151,59 @@ const AtomicString& USB::interfaceName() const
|
|
|
| void USB::contextDestroyed()
|
| {
|
| - if (m_client)
|
| - m_client->removeObserver(this);
|
| - m_client = nullptr;
|
| + m_deviceManager.reset();
|
| + m_deviceManagerRequests.clear();
|
| + m_chooserService.reset();
|
| + m_chooserServiceRequests.clear();
|
| }
|
|
|
| -void USB::onDeviceConnected(std::unique_ptr<WebUSBDevice> device)
|
| +void USB::onGetDevices(ScriptPromiseResolver* resolver, mojo::WTFArray<usb::DeviceInfoPtr> deviceInfos)
|
| {
|
| - dispatchEvent(USBConnectionEvent::create(EventTypeNames::connect, USBDevice::create(adoptPtr(device.release()), getExecutionContext())));
|
| + if (!isActive(resolver))
|
| + return;
|
| +
|
| + HeapVector<Member<USBDevice>> devices;
|
| + for (auto& deviceInfo : deviceInfos.PassStorage()) {
|
| + usb::DevicePtr device;
|
| + m_deviceManager->GetDevice(deviceInfo->guid, mojo::GetProxy(&device));
|
| + devices.append(USBDevice::create(std::move(deviceInfo), std::move(device), resolver->getExecutionContext()));
|
| + }
|
| + resolver->resolve(devices);
|
| + m_deviceManagerRequests.remove(resolver);
|
| +}
|
| +
|
| +void USB::onGetPermission(ScriptPromiseResolver* resolver, usb::DeviceInfoPtr deviceInfo)
|
| +{
|
| + if (!isActive(resolver))
|
| + return;
|
| +
|
| + if (deviceInfo) {
|
| + usb::DevicePtr device;
|
| + m_deviceManager->GetDevice(deviceInfo->guid, mojo::GetProxy(&device));
|
| + resolver->resolve(USBDevice::create(std::move(deviceInfo), std::move(device), resolver->getExecutionContext()));
|
| + } else {
|
| + resolver->reject(DOMException::create(NotFoundError, "No device selected."));
|
| + }
|
| }
|
|
|
| -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())));
|
| + m_deviceManager->GetDeviceChanges(createBaseCallback(bind<usb::DeviceChangeNotificationPtr>(&USB::onDeviceChanges, this)));
|
| + 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)
|
| {
|
| EventTargetWithInlineData::trace(visitor);
|
| ContextLifecycleObserver::trace(visitor);
|
| + visitor->trace(m_deviceManagerRequests);
|
| + visitor->trace(m_chooserServiceRequests);
|
| }
|
|
|
| } // namespace blink
|
|
|