summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorgdk@chromium.org <gdk@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2012-04-28 02:13:09 +0000
committergdk@chromium.org <gdk@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2012-04-28 02:13:09 +0000
commit0b81c3fd9f6614fe27acdbd48c1947a86d22e8c0 (patch)
tree89a2ad56663e27bccbe09ccc171530b981bdb4c8
parent064fd6c0eaa0e5cccf2161ffadcbd80701f17cd9 (diff)
downloadchromium_src-0b81c3fd9f6614fe27acdbd48c1947a86d22e8c0.zip
chromium_src-0b81c3fd9f6614fe27acdbd48c1947a86d22e8c0.tar.gz
chromium_src-0b81c3fd9f6614fe27acdbd48c1947a86d22e8c0.tar.bz2
Experimental USB API
This change adds a new API for low-level access to USB devices. The change depends on https://chromiumcodereview.appspot.com/10161035/ BUG=none TEST=none Review URL: http://codereview.chromium.org/10224009 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@134423 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r--chrome/app/generated_resources.grd3
-rw-r--r--chrome/browser/extensions/api/api_resource.h3
-rw-r--r--chrome/browser/extensions/api/api_resource_controller.cc7
-rw-r--r--chrome/browser/extensions/api/api_resource_controller.h2
-rw-r--r--chrome/browser/extensions/api/api_resource_event_notifier.cc48
-rw-r--r--chrome/browser/extensions/api/api_resource_event_notifier.h13
-rw-r--r--chrome/browser/extensions/api/usb/usb_api.cc149
-rw-r--r--chrome/browser/extensions/api/usb/usb_api.h105
-rw-r--r--chrome/browser/extensions/api/usb/usb_device_resource.cc199
-rw-r--r--chrome/browser/extensions/api/usb/usb_device_resource.h58
-rw-r--r--chrome/chrome_browser_extensions.gypi12
-rw-r--r--chrome/chrome_renderer.gypi10
-rw-r--r--chrome/common/extensions/api/_permission_features.json4
-rw-r--r--chrome/common/extensions/api/api.gyp8
-rw-r--r--chrome/common/extensions/api/experimental.usb.idl146
-rw-r--r--chrome/common/extensions/extension_permission_set.cc3
-rw-r--r--chrome/common/extensions/extension_permission_set.h1
-rw-r--r--chrome/common/extensions/extension_permission_set_unittest.cc1
-rw-r--r--chrome/renderer/extensions/experimental.usb_custom_bindings.cc26
-rw-r--r--chrome/renderer/extensions/experimental.usb_custom_bindings.h20
-rw-r--r--chrome/renderer/extensions/extension_dispatcher.cc6
-rw-r--r--chrome/renderer/renderer_resources.grd1
-rw-r--r--chrome/renderer/resources/extensions/experimental.usb_custom_bindings.js46
23 files changed, 853 insertions, 18 deletions
diff --git a/chrome/app/generated_resources.grd b/chrome/app/generated_resources.grd
index a8b7452..f9674d7 100644
--- a/chrome/app/generated_resources.grd
+++ b/chrome/app/generated_resources.grd
@@ -4305,6 +4305,9 @@ Update checks have repeatedly failed for the extension "<ph name="EXTENSION_NAME
<message name="IDS_EXTENSION_PROMPT_WARNING_MANAGED_MODE" desc="Permission string for access to managed mode.">
Managed mode
</message>
+ <message name="IDS_EXTENSION_PROMPT_WARNING_USB" desc="Permission string for access to USB devices.">
+ Your USB peripherals
+ </message>
<!-- Extension/App error messages -->
<message name="IDS_EXTENSION_CANT_GET_ABSOLUTE_PATH" desc="Warning displayed in pack dialog when the absolute path to the extension directory can not be found.">
diff --git a/chrome/browser/extensions/api/api_resource.h b/chrome/browser/extensions/api/api_resource.h
index 44a6d8e..24f1afc 100644
--- a/chrome/browser/extensions/api/api_resource.h
+++ b/chrome/browser/extensions/api/api_resource.h
@@ -23,7 +23,8 @@ class APIResource {
protected:
enum APIResourceType {
SocketResource,
- SerialConnectionResource
+ SerialConnectionResource,
+ UsbDeviceResource,
};
APIResource(APIResourceType api_resource_type,
APIResourceEventNotifier* event_notifier);
diff --git a/chrome/browser/extensions/api/api_resource_controller.cc b/chrome/browser/extensions/api/api_resource_controller.cc
index b101429..28d7e74 100644
--- a/chrome/browser/extensions/api/api_resource_controller.cc
+++ b/chrome/browser/extensions/api/api_resource_controller.cc
@@ -5,6 +5,7 @@
#include "chrome/browser/extensions/api/api_resource_controller.h"
#include "chrome/browser/extensions/api/serial/serial_connection.h"
#include "chrome/browser/extensions/api/socket/socket.h"
+#include "chrome/browser/extensions/api/usb/usb_device_resource.h"
namespace extensions {
@@ -79,4 +80,10 @@ SerialConnection* APIResourceController::GetSerialConnection(
GetAPIResource(APIResource::SerialConnectionResource, api_resource_id));
}
+UsbDeviceResource* APIResourceController::GetUsbDeviceResource(
+ int api_resource_id) const {
+ return static_cast<UsbDeviceResource*>(GetAPIResource(
+ APIResource::UsbDeviceResource, api_resource_id));
+}
+
} // namespace extensions
diff --git a/chrome/browser/extensions/api/api_resource_controller.h b/chrome/browser/extensions/api/api_resource_controller.h
index 3e79182..1df2944 100644
--- a/chrome/browser/extensions/api/api_resource_controller.h
+++ b/chrome/browser/extensions/api/api_resource_controller.h
@@ -16,6 +16,7 @@ namespace extensions {
class SerialConnection;
class Socket;
+class UsbDeviceResource;
// kSrcIdKey, or "srcId," binds an APIResource to the onEvent closure that was
// optionally passed to the APIResource's create() method. We generated it in
@@ -42,6 +43,7 @@ class APIResourceController {
// codebase.
Socket* GetSocket(int api_resource_id) const;
SerialConnection* GetSerialConnection(int api_resource_id) const;
+ UsbDeviceResource* GetUsbDeviceResource(int api_resource_id) const;
private:
int next_api_resource_id_;
diff --git a/chrome/browser/extensions/api/api_resource_event_notifier.cc b/chrome/browser/extensions/api/api_resource_event_notifier.cc
index ef33c58..6a15809 100644
--- a/chrome/browser/extensions/api/api_resource_event_notifier.cc
+++ b/chrome/browser/extensions/api/api_resource_event_notifier.cc
@@ -14,18 +14,20 @@
using content::BrowserThread;
namespace events {
-// TODO(miket): This should be generic, but at the moment only socket sends
-// onEvent events. We'll fix this when serial becomes nonblocking.
-const char kOnAPIResourceEvent[] = "experimental.socket.onEvent";
+const char kExperimentalSocketOnEvent[] = "experimental.socket.onEvent";
+const char kExperimentalUsbOnEvent[] = "experimental.usb.onEvent";
};
namespace extensions {
const char kEventTypeKey[] = "type";
+
const char kEventTypeConnectComplete[] = "connectComplete";
const char kEventTypeDataRead[] = "dataRead";
const char kEventTypeWriteComplete[] = "writeComplete";
+const char kEventTypeTransferComplete[] = "transferComplete";
+
const char kSrcIdKey[] = "srcId";
const char kIsFinalEventKey[] = "isFinalEvent";
@@ -48,7 +50,8 @@ APIResourceEventNotifier::APIResourceEventNotifier(
}
void APIResourceEventNotifier::OnConnectComplete(int result_code) {
- SendEventWithResultCode(API_RESOURCE_EVENT_CONNECT_COMPLETE, result_code);
+ SendEventWithResultCode(events::kExperimentalSocketOnEvent,
+ API_RESOURCE_EVENT_CONNECT_COMPLETE, result_code);
}
void APIResourceEventNotifier::OnDataRead(int result_code,
@@ -70,11 +73,26 @@ void APIResourceEventNotifier::OnDataRead(int result_code,
event->Set(kDataKey, data);
event->SetString(kAddressKey, address);
event->SetInteger(kPortKey, port);
- DispatchEvent(event);
+ DispatchEvent(events::kExperimentalSocketOnEvent, event);
}
void APIResourceEventNotifier::OnWriteComplete(int result_code) {
- SendEventWithResultCode(API_RESOURCE_EVENT_WRITE_COMPLETE, result_code);
+ SendEventWithResultCode(events::kExperimentalSocketOnEvent,
+ API_RESOURCE_EVENT_WRITE_COMPLETE, result_code);
+}
+
+void APIResourceEventNotifier::OnTransferComplete(int result_code,
+ base::ListValue* data) {
+ if (src_id_ < 0) {
+ delete data;
+ return;
+ }
+
+ DictionaryValue* event = CreateAPIResourceEvent(
+ API_RESOURCE_EVENT_TRANSFER_COMPLETE);
+ event->SetInteger(kResultCodeKey, result_code);
+ event->Set(kDataKey, data);
+ DispatchEvent(events::kExperimentalUsbOnEvent, event);
}
// static
@@ -87,6 +105,8 @@ std::string APIResourceEventNotifier::APIResourceEventTypeToString(
return kEventTypeDataRead;
case API_RESOURCE_EVENT_WRITE_COMPLETE:
return kEventTypeWriteComplete;
+ case API_RESOURCE_EVENT_TRANSFER_COMPLETE:
+ return kEventTypeTransferComplete;
}
NOTREACHED();
@@ -95,15 +115,17 @@ std::string APIResourceEventNotifier::APIResourceEventTypeToString(
APIResourceEventNotifier::~APIResourceEventNotifier() {}
-void APIResourceEventNotifier::DispatchEvent(DictionaryValue* event) {
+void APIResourceEventNotifier::DispatchEvent(const std::string &extension,
+ DictionaryValue* event) {
BrowserThread::PostTask(
BrowserThread::UI, FROM_HERE,
base::Bind(
- &APIResourceEventNotifier::DispatchEventOnUIThread, this, event));
+ &APIResourceEventNotifier::DispatchEventOnUIThread, this, extension,
+ event));
}
void APIResourceEventNotifier::DispatchEventOnUIThread(
- DictionaryValue* event) {
+ const std::string &extension, DictionaryValue* event) {
ListValue args;
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
@@ -111,9 +133,8 @@ void APIResourceEventNotifier::DispatchEventOnUIThread(
args.Set(0, event);
std::string json_args;
base::JSONWriter::Write(&args, &json_args);
- router_->DispatchEventToExtension(src_extension_id_,
- events::kOnAPIResourceEvent,
- json_args, profile_, src_url_);
+ router_->DispatchEventToExtension(src_extension_id_, extension, json_args,
+ profile_, src_url_);
}
DictionaryValue* APIResourceEventNotifier::CreateAPIResourceEvent(
@@ -132,6 +153,7 @@ DictionaryValue* APIResourceEventNotifier::CreateAPIResourceEvent(
}
void APIResourceEventNotifier::SendEventWithResultCode(
+ const std::string &extension,
APIResourceEventType event_type,
int result_code) {
if (src_id_ < 0)
@@ -139,7 +161,7 @@ void APIResourceEventNotifier::SendEventWithResultCode(
DictionaryValue* event = CreateAPIResourceEvent(event_type);
event->SetInteger(kResultCodeKey, result_code);
- DispatchEvent(event);
+ DispatchEvent(extension, event);
}
} // namespace extensions
diff --git a/chrome/browser/extensions/api/api_resource_event_notifier.h b/chrome/browser/extensions/api/api_resource_event_notifier.h
index 286a9c0..09f4070 100644
--- a/chrome/browser/extensions/api/api_resource_event_notifier.h
+++ b/chrome/browser/extensions/api/api_resource_event_notifier.h
@@ -25,7 +25,8 @@ namespace extensions {
enum APIResourceEventType {
API_RESOURCE_EVENT_CONNECT_COMPLETE,
API_RESOURCE_EVENT_DATA_READ,
- API_RESOURCE_EVENT_WRITE_COMPLETE
+ API_RESOURCE_EVENT_WRITE_COMPLETE,
+ API_RESOURCE_EVENT_TRANSFER_COMPLETE,
};
extern const char kSrcIdKey[];
@@ -59,6 +60,8 @@ class APIResourceEventNotifier
virtual void OnWriteComplete(int result_code);
+ virtual void OnTransferComplete(int result_code, base::ListValue* data);
+
static std::string APIResourceEventTypeToString(
APIResourceEventType event_type);
@@ -68,11 +71,13 @@ class APIResourceEventNotifier
virtual ~APIResourceEventNotifier();
- void DispatchEvent(DictionaryValue* event);
- void DispatchEventOnUIThread(DictionaryValue* event);
+ void DispatchEvent(const std::string &extension, DictionaryValue* event);
+ void DispatchEventOnUIThread(const std::string& extension,
+ DictionaryValue* event);
DictionaryValue* CreateAPIResourceEvent(APIResourceEventType event_type);
- void SendEventWithResultCode(APIResourceEventType event_type,
+ void SendEventWithResultCode(const std::string& extension,
+ APIResourceEventType event_type,
int result_code);
ExtensionEventRouter* router_;
diff --git a/chrome/browser/extensions/api/usb/usb_api.cc b/chrome/browser/extensions/api/usb/usb_api.cc
new file mode 100644
index 0000000..df6d9ee
--- /dev/null
+++ b/chrome/browser/extensions/api/usb/usb_api.cc
@@ -0,0 +1,149 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/extensions/api/usb/usb_api.h"
+
+#include <vector>
+
+#include "base/memory/scoped_ptr.h"
+#include "chrome/browser/extensions/api/api_resource_controller.h"
+#include "chrome/browser/extensions/api/usb/usb_device_resource.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/usb/usb_service_factory.h"
+#include "chrome/browser/usb/usb_service.h"
+#include "chrome/common/extensions/api/experimental.usb.h"
+
+namespace BulkTransfer = extensions::api::experimental_usb::BulkTransfer;
+namespace CloseDevice = extensions::api::experimental_usb::CloseDevice;
+namespace ControlTransfer = extensions::api::experimental_usb::ControlTransfer;
+namespace FindDevice = extensions::api::experimental_usb::FindDevice;
+namespace InterruptTransfer =
+ extensions::api::experimental_usb::InterruptTransfer;
+using extensions::api::experimental_usb::Device;
+using std::vector;
+
+namespace extensions {
+
+UsbFindDeviceFunction::UsbFindDeviceFunction() {}
+
+UsbFindDeviceFunction::~UsbFindDeviceFunction() {}
+
+bool UsbFindDeviceFunction::Prepare() {
+ parameters_ = FindDevice::Params::Create(*args_);
+ EXTENSION_FUNCTION_VALIDATE(parameters_.get());
+ event_notifier_ = CreateEventNotifier(ExtractSrcId(2));
+ return true;
+}
+
+void UsbFindDeviceFunction::Work() {
+ UsbService* const service =
+ UsbServiceFactory::GetInstance()->GetForProfile(profile());
+ DCHECK(service) << "No UsbService associated with profile.";
+
+ UsbDevice* const device = service->FindDevice(parameters_->vendor_id,
+ parameters_->product_id);
+ if (!device) {
+ result_.reset(base::Value::CreateNullValue());
+ return;
+ }
+
+ UsbDeviceResource* const resource = new UsbDeviceResource(event_notifier_,
+ device);
+
+ Device result;
+ result.handle = controller()->AddAPIResource(resource);
+ result.vendor_id = parameters_->vendor_id;
+ result.product_id = parameters_->product_id;
+ result_ = result.ToValue();
+}
+
+bool UsbFindDeviceFunction::Respond() {
+ return true;
+}
+
+UsbCloseDeviceFunction::UsbCloseDeviceFunction() {}
+
+UsbCloseDeviceFunction::~UsbCloseDeviceFunction() {}
+
+bool UsbCloseDeviceFunction::Prepare() {
+ parameters_ = CloseDevice::Params::Create(*args_);
+ EXTENSION_FUNCTION_VALIDATE(parameters_.get());
+ return true;
+}
+
+void UsbCloseDeviceFunction::Work() {
+ controller()->RemoveAPIResource(parameters_->device.handle);
+}
+
+bool UsbCloseDeviceFunction::Respond() {
+ return true;
+}
+
+UsbControlTransferFunction::UsbControlTransferFunction() {}
+
+UsbControlTransferFunction::~UsbControlTransferFunction() {}
+
+bool UsbControlTransferFunction::Prepare() {
+ parameters_ = ControlTransfer::Params::Create(*args_);
+ EXTENSION_FUNCTION_VALIDATE(parameters_.get());
+ return true;
+}
+
+void UsbControlTransferFunction::Work() {
+ UsbDeviceResource* const device = controller()->GetUsbDeviceResource(
+ parameters_->device.handle);
+ if (device) {
+ device->ControlTransfer(parameters_->transfer_info);
+ }
+}
+
+bool UsbControlTransferFunction::Respond() {
+ return true;
+}
+
+bool UsbBulkTransferFunction::Prepare() {
+ parameters_ = BulkTransfer::Params::Create(*args_);
+ EXTENSION_FUNCTION_VALIDATE(parameters_.get());
+ return true;
+}
+
+UsbBulkTransferFunction::UsbBulkTransferFunction() {}
+
+UsbBulkTransferFunction::~UsbBulkTransferFunction() {}
+
+void UsbBulkTransferFunction::Work() {
+ UsbDeviceResource* const device = controller()->GetUsbDeviceResource(
+ parameters_->device.handle);
+ if (device) {
+ device->BulkTransfer(parameters_->transfer_info);
+ }
+}
+
+bool UsbBulkTransferFunction::Respond() {
+ return true;
+}
+
+UsbInterruptTransferFunction::UsbInterruptTransferFunction() {}
+
+UsbInterruptTransferFunction::~UsbInterruptTransferFunction() {}
+
+bool UsbInterruptTransferFunction::Prepare() {
+ parameters_ = InterruptTransfer::Params::Create(*args_);
+ EXTENSION_FUNCTION_VALIDATE(parameters_.get());
+ return true;
+}
+
+void UsbInterruptTransferFunction::Work() {
+ UsbDeviceResource* const device = controller()->GetUsbDeviceResource(
+ parameters_->device.handle);
+ if (device) {
+ device->InterruptTransfer(parameters_->transfer_info);
+ }
+}
+
+bool UsbInterruptTransferFunction::Respond() {
+ return true;
+}
+
+} // namespace extensions
diff --git a/chrome/browser/extensions/api/usb/usb_api.h b/chrome/browser/extensions/api/usb/usb_api.h
new file mode 100644
index 0000000..b182f65
--- /dev/null
+++ b/chrome/browser/extensions/api/usb/usb_api.h
@@ -0,0 +1,105 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_EXTENSIONS_API_USB_USB_API_H_
+#define CHROME_BROWSER_EXTENSIONS_API_USB_USB_API_H_
+#pragma once
+
+#include <string>
+
+#include "chrome/browser/extensions/api/api_function.h"
+#include "chrome/common/extensions/api/experimental.usb.h"
+
+namespace extensions {
+
+class APIResourceEventNotifier;
+
+class UsbFindDeviceFunction : public AsyncIOAPIFunction {
+ public:
+ UsbFindDeviceFunction();
+ virtual ~UsbFindDeviceFunction();
+
+ protected:
+ virtual bool Prepare() OVERRIDE;
+ virtual void Work() OVERRIDE;
+ virtual bool Respond() OVERRIDE;
+
+ private:
+ scoped_ptr<extensions::api::experimental_usb::FindDevice::Params> parameters_;
+ APIResourceEventNotifier* event_notifier_;
+
+ DECLARE_EXTENSION_FUNCTION_NAME("experimental.usb.findDevice");
+};
+
+class UsbCloseDeviceFunction : public AsyncIOAPIFunction {
+ public:
+ UsbCloseDeviceFunction();
+ virtual ~UsbCloseDeviceFunction();
+
+ protected:
+ virtual bool Prepare() OVERRIDE;
+ virtual void Work() OVERRIDE;
+ virtual bool Respond() OVERRIDE;
+
+ private:
+ scoped_ptr<extensions::api::experimental_usb::CloseDevice::Params>
+ parameters_;
+
+ DECLARE_EXTENSION_FUNCTION_NAME("experimental.usb.closeDevice");
+};
+
+class UsbControlTransferFunction : public AsyncIOAPIFunction {
+ public:
+ UsbControlTransferFunction();
+ virtual ~UsbControlTransferFunction();
+
+ protected:
+ virtual bool Prepare() OVERRIDE;
+ virtual void Work() OVERRIDE;
+ virtual bool Respond() OVERRIDE;
+
+ private:
+ scoped_ptr<extensions::api::experimental_usb::ControlTransfer::Params>
+ parameters_;
+
+ DECLARE_EXTENSION_FUNCTION_NAME("experimental.usb.controlTransfer");
+};
+
+class UsbBulkTransferFunction : public AsyncIOAPIFunction {
+ public:
+ UsbBulkTransferFunction();
+ virtual ~UsbBulkTransferFunction();
+
+ protected:
+ virtual bool Prepare() OVERRIDE;
+ virtual void Work() OVERRIDE;
+ virtual bool Respond() OVERRIDE;
+
+ private:
+ scoped_ptr<extensions::api::experimental_usb::BulkTransfer::Params>
+ parameters_;
+
+ DECLARE_EXTENSION_FUNCTION_NAME("experimental.usb.bulkTransfer");
+};
+
+class UsbInterruptTransferFunction : public AsyncIOAPIFunction {
+ public:
+ UsbInterruptTransferFunction();
+ virtual ~UsbInterruptTransferFunction();
+
+ protected:
+ virtual bool Prepare() OVERRIDE;
+ virtual void Work() OVERRIDE;
+ virtual bool Respond() OVERRIDE;
+
+ private:
+ scoped_ptr<extensions::api::experimental_usb::InterruptTransfer::Params>
+ parameters_;
+
+ DECLARE_EXTENSION_FUNCTION_NAME("experimental.usb.interruptTransfer");
+};
+
+} // namespace extensions
+
+#endif // CHROME_BROWSER_EXTENSIONS_API_USB_USB_API_H_
diff --git a/chrome/browser/extensions/api/usb/usb_device_resource.cc b/chrome/browser/extensions/api/usb/usb_device_resource.cc
new file mode 100644
index 0000000..a659d4fb
--- /dev/null
+++ b/chrome/browser/extensions/api/usb/usb_device_resource.cc
@@ -0,0 +1,199 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/extensions/api/usb/usb_device_resource.h"
+
+#include <string>
+#include <vector>
+
+#include "base/bind.h"
+#include "base/bind_helpers.h"
+#include "base/synchronization/lock.h"
+#include "chrome/browser/extensions/api/api_resource_event_notifier.h"
+#include "chrome/browser/extensions/api/api_resource.h"
+#include "chrome/browser/usb/usb_device.h"
+#include "chrome/common/extensions/api/experimental.usb.h"
+
+using extensions::api::experimental_usb::ControlTransferInfo;
+using extensions::api::experimental_usb::GenericTransferInfo;
+using std::string;
+using std::vector;
+
+static const char* kDirectionIn = "in";
+static const char* kDirectionOut = "out";
+
+static const char* kRequestTypeStandard = "standard";
+static const char* kRequestTypeClass = "class";
+static const char* kRequestTypeVendor = "vendor";
+static const char* kRequestTypeReserved = "reserved";
+
+static const char* kRecipientDevice = "device";
+static const char* kRecipientInterface = "interface";
+static const char* kRecipientEndpoint = "endpoint";
+static const char* kRecipientOther = "other";
+
+namespace {
+
+static bool ConvertDirection(const string& input,
+ UsbDevice::TransferDirection* output) {
+ if (input == kDirectionIn) {
+ *output = UsbDevice::INBOUND;
+ return true;
+ } else if (input == kDirectionOut) {
+ *output = UsbDevice::OUTBOUND;
+ return true;
+ }
+ return false;
+}
+
+static bool ConvertRequestType(const string& input,
+ UsbDevice::TransferRequestType* output) {
+ if (input == kRequestTypeStandard) {
+ *output = UsbDevice::STANDARD;
+ return true;
+ } else if (input == kRequestTypeClass) {
+ *output = UsbDevice::CLASS;
+ return true;
+ } else if (input == kRequestTypeVendor) {
+ *output = UsbDevice::VENDOR;
+ return true;
+ } else if (input == kRequestTypeReserved) {
+ *output = UsbDevice::RESERVED;
+ return true;
+ }
+ return false;
+}
+
+static bool ConvertRecipient(const string& input,
+ UsbDevice::TransferRecipient* output) {
+ if (input == kRecipientDevice) {
+ *output = UsbDevice::DEVICE;
+ return true;
+ } else if (input == kRecipientInterface) {
+ *output = UsbDevice::INTERFACE;
+ return true;
+ } else if (input == kRecipientEndpoint) {
+ *output = UsbDevice::ENDPOINT;
+ return true;
+ } else if (input == kRecipientOther) {
+ *output = UsbDevice::OTHER;
+ return true;
+ }
+ return false;
+}
+
+template<class T>
+static bool GetTransferSize(const T& input, unsigned int* output) {
+ if (input.direction == kDirectionIn) {
+ const int* length = input.length.get();
+ if (length) {
+ *output = *length;
+ return true;
+ }
+ } else if (input.direction == kDirectionOut) {
+ if (input.data.get()) {
+ *output = input.data->size();
+ return true;
+ }
+ }
+ return false;
+}
+
+template<class T>
+static scoped_refptr<net::IOBuffer> CreateBufferForTransfer(const T& input) {
+ unsigned int size = 0;
+ if (!GetTransferSize(input, &size)) {
+ return NULL;
+ }
+
+ scoped_refptr<net::IOBuffer> buffer = new net::IOBuffer(size);
+ if (!input.data.get()) {
+ return buffer;
+ }
+
+ const vector<int>& input_buffer = *input.data.get();
+ for (unsigned int i = 0; i < size; ++i) {
+ buffer->data()[i] = input_buffer[i];
+ }
+
+ return buffer;
+}
+
+} // namespace
+
+namespace extensions {
+
+UsbDeviceResource::UsbDeviceResource(APIResourceEventNotifier* notifier,
+ UsbDevice* device)
+ : APIResource(APIResource::UsbDeviceResource, notifier), device_(device) {}
+
+UsbDeviceResource::~UsbDeviceResource() {}
+
+void UsbDeviceResource::ControlTransfer(const ControlTransferInfo& transfer) {
+ UsbDevice::TransferDirection direction;
+ UsbDevice::TransferRequestType request_type;
+ UsbDevice::TransferRecipient recipient;
+ unsigned int size;
+ scoped_refptr<net::IOBuffer> buffer = CreateBufferForTransfer(transfer);
+
+ if (!ConvertDirection(transfer.direction, &direction) ||
+ !ConvertRequestType(transfer.request_type, &request_type) ||
+ !ConvertRecipient(transfer.recipient, &recipient) ||
+ !GetTransferSize(transfer, &size) || !buffer) {
+ LOG(INFO) << "Malformed transfer parameters.";
+ return;
+ }
+
+ device_->ControlTransfer(direction, request_type, recipient, transfer.request,
+ transfer.value, transfer.index, buffer, size, 0,
+ base::Bind(&UsbDeviceResource::TransferComplete,
+ base::Unretained(this), buffer, size));
+}
+
+void UsbDeviceResource::InterruptTransfer(const GenericTransferInfo& transfer) {
+ unsigned int size;
+ UsbDevice::TransferDirection direction;
+ scoped_refptr<net::IOBuffer> buffer = CreateBufferForTransfer(transfer);
+
+ if (!ConvertDirection(transfer.direction, &direction) ||
+ !GetTransferSize(transfer, &size) || !buffer) {
+ LOG(INFO) << "Malformed transfer parameters.";
+ return;
+ }
+
+ device_->InterruptTransfer(direction, transfer.endpoint, buffer, size, 0,
+ base::Bind(&UsbDeviceResource::TransferComplete,
+ base::Unretained(this), buffer, size));
+}
+
+void UsbDeviceResource::BulkTransfer(const GenericTransferInfo& transfer) {
+ unsigned int size;
+ UsbDevice::TransferDirection direction;
+ scoped_refptr<net::IOBuffer> buffer = CreateBufferForTransfer(transfer);
+
+ if (!ConvertDirection(transfer.direction, &direction) ||
+ !GetTransferSize(transfer, &size) || !buffer) {
+ LOG(INFO) << "Malformed transfer parameters.";
+ return;
+ }
+
+ device_->BulkTransfer(direction, transfer.endpoint, buffer, size, 0,
+ base::Bind(&UsbDeviceResource::TransferComplete,
+ base::Unretained(this), buffer, size));
+}
+
+void UsbDeviceResource::TransferComplete(net::IOBuffer* buffer,
+ const size_t length,
+ int success) {
+ if (buffer) {
+ base::ListValue *const response_buffer = new base::ListValue();
+ for (unsigned int i = 0; i < length; ++i) {
+ const uint8_t value = buffer->data()[i] & 0xFF;
+ response_buffer->Append(base::Value::CreateIntegerValue(value));
+ }
+ event_notifier()->OnTransferComplete(success, response_buffer);
+ }
+}
+
+} // namespace extensions
diff --git a/chrome/browser/extensions/api/usb/usb_device_resource.h b/chrome/browser/extensions/api/usb/usb_device_resource.h
new file mode 100644
index 0000000..03c3793
--- /dev/null
+++ b/chrome/browser/extensions/api/usb/usb_device_resource.h
@@ -0,0 +1,58 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_EXTENSIONS_API_USB_USB_DEVICE_RESOURCE_H_
+#define CHROME_BROWSER_EXTENSIONS_API_USB_USB_DEVICE_RESOURCE_H_
+#pragma once
+
+#include <set>
+
+#include "base/basictypes.h"
+#include "base/memory/linked_ptr.h"
+#include "base/synchronization/lock.h"
+#include "chrome/browser/extensions/api/api_resource.h"
+#include "chrome/common/extensions/api/experimental.usb.h"
+
+class UsbDevice;
+
+namespace net {
+class IOBuffer;
+} // namespace net
+
+namespace extensions {
+
+class APIResourceEventNotifier;
+
+// A UsbDeviceResource is an APIResource wrapper for a UsbDevice. When invoking
+// transfers on the underlying device it will use the APIResourceEventNotifier
+// associated with the underlying APIResource to deliver completion messages.
+class UsbDeviceResource : public APIResource {
+ public:
+ UsbDeviceResource(APIResourceEventNotifier* notifier, UsbDevice* device);
+ virtual ~UsbDeviceResource();
+
+ // All of the *Transfer variants that are exposed here adapt their arguments
+ // for the underlying UsbDevice's interface and invoke the corresponding
+ // methods with completion callbacks that call OnTransferComplete on the event
+ // notifier.
+ void ControlTransfer(
+ const api::experimental_usb::ControlTransferInfo& transfer);
+ void InterruptTransfer(
+ const api::experimental_usb::GenericTransferInfo& transfer);
+ void BulkTransfer(const api::experimental_usb::GenericTransferInfo& transfer);
+
+ private:
+ // Invoked by the underlying device's transfer callbacks. Indicates transfer
+ // completion to the APIResource's event notifier.
+ void TransferComplete(net::IOBuffer* buffer, const size_t length,
+ int success);
+
+ scoped_refptr<UsbDevice> device_;
+
+ DISALLOW_COPY_AND_ASSIGN(UsbDeviceResource);
+};
+
+} // namespace extensions
+
+#endif // CHROME_BROWSER_EXTENSIONS_API_USB_USB_DEVICE_RESOURCE_H_
diff --git a/chrome/chrome_browser_extensions.gypi b/chrome/chrome_browser_extensions.gypi
index e968452..8b3b9c8 100644
--- a/chrome/chrome_browser_extensions.gypi
+++ b/chrome/chrome_browser_extensions.gypi
@@ -150,6 +150,10 @@
'browser/extensions/api/terminal/terminal_extension_helper.h',
'browser/extensions/api/terminal/terminal_private_api.cc',
'browser/extensions/api/terminal/terminal_private_api.h',
+ 'browser/extensions/api/usb/usb_api.cc',
+ 'browser/extensions/api/usb/usb_api.h',
+ 'browser/extensions/api/usb/usb_device_resource.cc',
+ 'browser/extensions/api/usb/usb_device_resource.h',
'browser/extensions/api/web_navigation/web_navigation_api.cc',
'browser/extensions/api/web_navigation/web_navigation_api.h',
'browser/extensions/api/web_navigation/web_navigation_api_constants.cc',
@@ -640,6 +644,14 @@
}],
],
}],
+ ['OS=="android"', {
+ 'sources!': [
+ 'browser/extensions/api/usb/usb_api.cc',
+ 'browser/extensions/api/usb/usb_api.h',
+ 'browser/extensions/api/usb/usb_device_resource.cc',
+ 'browser/extensions/api/usb/usb_device_resource.h',
+ ],
+ }],
],
},
],
diff --git a/chrome/chrome_renderer.gypi b/chrome/chrome_renderer.gypi
index cc41699..06ff06e 100644
--- a/chrome/chrome_renderer.gypi
+++ b/chrome/chrome_renderer.gypi
@@ -78,6 +78,8 @@
'renderer/extensions/event_bindings.h',
'renderer/extensions/experimental.socket_custom_bindings.cc',
'renderer/extensions/experimental.socket_custom_bindings.h',
+ 'renderer/extensions/experimental.usb_custom_bindings.cc',
+ 'renderer/extensions/experimental.usb_custom_bindings.h',
'renderer/extensions/extension_custom_bindings.cc',
'renderer/extensions/extension_custom_bindings.h',
'renderer/extensions/extension_dispatcher.cc',
@@ -145,6 +147,7 @@
'renderer/resources/extensions/experimental.offscreenTabs_custom_bindings.js',
'renderer/resources/extensions/experimental.runtime_custom_bindings.js',
'renderer/resources/extensions/experimental.socket_custom_bindings.js',
+ 'renderer/resources/extensions/experimental.usb_custom_bindings.js',
'renderer/resources/extensions/experimental.webrequest_custom_bindings.js',
'renderer/resources/extensions/extension_custom_bindings.js',
'renderer/resources/extensions/file_browser_handler_custom_bindings.js',
@@ -299,6 +302,13 @@
}],
],
}],
+ ['OS=="android"', {
+ 'sources!': [
+ 'renderer/extensions/experimental.usb_custom_bindings.cc',
+ 'renderer/extensions/experimental.usb_custom_bindings.h',
+ 'renderer/resources/extensions/experimental.usb_custom_bindings.js',
+ ],
+ }],
],
},
],
diff --git a/chrome/common/extensions/api/_permission_features.json b/chrome/common/extensions/api/_permission_features.json
index 04dea29..9df9c02 100644
--- a/chrome/common/extensions/api/_permission_features.json
+++ b/chrome/common/extensions/api/_permission_features.json
@@ -220,6 +220,10 @@
"extension", "packaged_app", "hosted_app", "platform_app"
]
},
+ "usb": {
+ "channel": "stable",
+ "extension_types": ["platform_app"]
+ },
"webNavigation": {
"channel": "stable",
"extension_types": ["extension", "packaged_app"]
diff --git a/chrome/common/extensions/api/api.gyp b/chrome/common/extensions/api/api.gyp
index 7646399..863bfd5 100644
--- a/chrome/common/extensions/api/api.gyp
+++ b/chrome/common/extensions/api/api.gyp
@@ -31,10 +31,18 @@
'experimental.dns.idl',
'experimental.serial.idl',
'experimental.socket.idl',
+ 'experimental.usb.idl',
],
'cc_dir': 'chrome/common/extensions/api',
'root_namespace': 'extensions::api',
},
+ 'conditions': [
+ ['OS=="android"', {
+ 'idl_schema_files!': [
+ 'experimental.usb.idl',
+ ],
+ }],
+ ],
},
],
}
diff --git a/chrome/common/extensions/api/experimental.usb.idl b/chrome/common/extensions/api/experimental.usb.idl
new file mode 100644
index 0000000..82d0a02
--- /dev/null
+++ b/chrome/common/extensions/api/experimental.usb.idl
@@ -0,0 +1,146 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// TODO(gdk): The string-style enumerations are temporary, and will be removed
+// once full enumeration support is added. Also, the array-of-longs are
+// temporary and will be removed once there is full ArrayBuffer support.
+
+[nodoc] namespace experimental.usb {
+
+ // A Device encapsulates everything that is needed to communicate with a USB
+ // device. They are returned by findDevice calls and have all of their
+ // fields populated before being returned.
+ dictionary Device {
+ long handle;
+ long vendorId;
+ long productId;
+ };
+
+ // ControlTransferInfo represents that parameters to a single USB control
+ // transfer.
+ dictionary ControlTransferInfo {
+ // The direction of this transfer. Must be one of either in or out.
+ DOMString direction;
+
+ // The intended recipient for this transfer. Must be one of device,
+ // interface, endpoint, or other.
+ DOMString recipient;
+
+ // The type of this request. Must be one of standard, class, vendor,
+ // or reserved.
+ DOMString requestType;
+
+ long request;
+ long value;
+ long index;
+
+ // If this transfer is an input transfer, then this field must be set to
+ // indicate the expected data length. If this is an output transfer, then
+ // this field is ignored.
+ long? length;
+
+ // The data payload carried by this transfer. If this is an output tranfer
+ // then this field must be set.
+ long[]? data;
+ };
+
+ // GenericTransferInfo is used by both bulk and interrupt transfers to
+ // specify the parameters of the transfer.
+ dictionary GenericTransferInfo {
+ // The direction of this transfer. Must be one of in or out.
+ DOMString direction;
+
+ long endpoint;
+
+ // If this is an input transfer then this field indicates the size of the
+ // input buffer. If this is an output transfer then this field is ignored.
+ long? length;
+
+ // If this is an output transfer then this field must be populated.
+ // Otherwise, it will be ignored.
+ long[]? data;
+ };
+
+ // When a USB event occurs the event handler specified by the DeviceOptions
+ // provided to findDevice will have a UsbEvent delivered to it which will
+ // contain the result of a transfer, including returned data.
+ dictionary UsbEvent {
+ // A string indicating the type of the event. Currently will only contain
+ // the value 'transferResult'.
+ DOMString type;
+
+ // A value of 0 indicates that the transfer was a success. Other values
+ // indicate failure.
+ long? resultCode;
+
+ // If the transfer was an input transfer then this field will contain all
+ // of the input data requested.
+ long[]? data;
+
+ // The following fields are used for internal event routing and can be
+ // ignored.
+ [nodoc] boolean isFinalEvent;
+ [nodoc] long srcId;
+ };
+
+ callback OnEventCallback = void (UsbEvent event);
+
+ dictionary DeviceOptions {
+ // The schema generator does not support dictionaries with only events.
+ // Ignore this field.
+ [nodoc] long? dummyValue;
+
+ // Invoked by the extension API whenever an event occurs for the device(s)
+ // that this DeviceOptions is associated with.
+ OnEventCallback? onEvent;
+ };
+
+ callback FindDeviceCallback = void (optional Device device);
+ callback TransferCallback = void ();
+
+ interface Functions {
+ // Finds the first instance of the USB device specified by the vendorId/
+ // productId pair and, if permissions allow, opens it for use.
+ // Upon successfully opening a device the callback is invoked with a
+ // populated Device object. On failure, the callback is invoked with null.
+ // |vendorId|: The vendor ID of the USB device to find.
+ // |productId|: The product ID of the USB device to find.
+ // |callback|: Invoked with the opened Device on success.
+ static void findDevice(long vendorId, long productId,
+ DeviceOptions options, FindDeviceCallback callback);
+
+ // Closes an open device instance. Invoking operations on a device after it
+ // has been closed is a safe operation, but causes no action to be taken.
+ // |device|: The device to close.
+ static void closeDevice(Device device);
+
+ // Performs a control transfer on the specified device. See the
+ // ControlTransferInfo structure for the parameters required to make a
+ // transfer.
+ // |device|: An open device to make the transfer on.
+ // |transferInfo|: The parameters to the transfer. See ControlTransferInfo.
+ // |callback|: Invoked once the transfer has completed.
+ static void controlTransfer(Device device,
+ ControlTransferInfo transferInfo, optional TransferCallback callback);
+
+ // Performs a bulk transfer on the specified device.
+ // |device|: An open device to make the transfer on.
+ // |transferInfo|: The paramters to the transfer. See GenericTransferInfo.
+ // |callback|: Invoked once the transfer has completed.
+ static void bulkTransfer(Device device, GenericTransferInfo transferInfo,
+ optional TransferCallback callback);
+
+ // Performs an interrupt transfer on the specified device.
+ // |device|: An open device to make the transfer on.
+ // |transferInfo|: The paramters to the transfer. See GenericTransferInfo.
+ // |callback|: Invoked once the transfer has completed.
+ static void interruptTransfer(Device device,
+ GenericTransferInfo transferInfo, optional TransferCallback callback);
+ };
+
+ interface Events {
+ static void onEvent(UsbEvent event);
+ };
+
+};
diff --git a/chrome/common/extensions/extension_permission_set.cc b/chrome/common/extensions/extension_permission_set.cc
index 1f51c72..b34a79a 100644
--- a/chrome/common/extensions/extension_permission_set.cc
+++ b/chrome/common/extensions/extension_permission_set.cc
@@ -262,6 +262,9 @@ void ExtensionAPIPermission::RegisterAllPermissions(
ExtensionPermissionMessage::kTtsEngine,
kFlagCannotBeOptional);
info->RegisterPermission(
+ kUsb, "usb", IDS_EXTENSION_PROMPT_WARNING_USB,
+ ExtensionPermissionMessage::kNone, kFlagNone);
+ info->RegisterPermission(
kWebNavigation, "webNavigation",
IDS_EXTENSION_PROMPT_WARNING_TABS, ExtensionPermissionMessage::kTabs,
kFlagNone);
diff --git a/chrome/common/extensions/extension_permission_set.h b/chrome/common/extensions/extension_permission_set.h
index 79e8742f..75c4370 100644
--- a/chrome/common/extensions/extension_permission_set.h
+++ b/chrome/common/extensions/extension_permission_set.h
@@ -136,6 +136,7 @@ class ExtensionAPIPermission {
kTts,
kTtsEngine,
kUnlimitedStorage,
+ kUsb,
kWebNavigation,
kWebRequest,
kWebRequestBlocking,
diff --git a/chrome/common/extensions/extension_permission_set_unittest.cc b/chrome/common/extensions/extension_permission_set_unittest.cc
index dfd5e85..026858f 100644
--- a/chrome/common/extensions/extension_permission_set_unittest.cc
+++ b/chrome/common/extensions/extension_permission_set_unittest.cc
@@ -614,6 +614,7 @@ TEST(ExtensionPermissionsTest, PermissionMessages) {
// Platform apps. TODO(miket): must we skip?
skip.insert(ExtensionAPIPermission::kSocket);
+ skip.insert(ExtensionAPIPermission::kUsb);
ExtensionPermissionsInfo* info = ExtensionPermissionsInfo::GetInstance();
ExtensionAPIPermissionSet permissions = info->GetAll();
diff --git a/chrome/renderer/extensions/experimental.usb_custom_bindings.cc b/chrome/renderer/extensions/experimental.usb_custom_bindings.cc
new file mode 100644
index 0000000..a188f06
--- /dev/null
+++ b/chrome/renderer/extensions/experimental.usb_custom_bindings.cc
@@ -0,0 +1,26 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/renderer/extensions/experimental.usb_custom_bindings.h"
+
+#include "base/logging.h"
+#include "v8/include/v8.h"
+
+namespace {
+
+v8::Handle<v8::Value> GetNextUsbEventId(const v8::Arguments &arguments) {
+ static int next_event_id = 1;
+ return v8::Integer::New(next_event_id++);
+}
+
+} // namespace
+
+namespace extensions {
+
+ExperimentalUsbCustomBindings::ExperimentalUsbCustomBindings()
+ : ChromeV8Extension(NULL) {
+ RouteStaticFunction("GetNextUsbEventId", &GetNextUsbEventId);
+}
+
+} // namespace extensions
diff --git a/chrome/renderer/extensions/experimental.usb_custom_bindings.h b/chrome/renderer/extensions/experimental.usb_custom_bindings.h
new file mode 100644
index 0000000..05cbb87
--- /dev/null
+++ b/chrome/renderer/extensions/experimental.usb_custom_bindings.h
@@ -0,0 +1,20 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_RENDERER_EXTENSIONS_EXPERIMENTAL_USB_CUSTOM_BINDINGS_H_
+#define CHROME_RENDERER_EXTENSIONS_EXPERIMENTAL_USB_CUSTOM_BINDINGS_H_
+#pragma once
+
+#include "chrome/renderer/extensions/chrome_v8_extension.h"
+
+namespace extensions {
+
+class ExperimentalUsbCustomBindings : public ChromeV8Extension {
+ public:
+ ExperimentalUsbCustomBindings();
+};
+
+} // namespace extensions
+
+#endif // CHROME_RENDERER_EXTENSIONS_EXPERIMENTAL_USB_CUSTOM_BINDINGS_H_
diff --git a/chrome/renderer/extensions/extension_dispatcher.cc b/chrome/renderer/extensions/extension_dispatcher.cc
index c808efa..90e86b5 100644
--- a/chrome/renderer/extensions/extension_dispatcher.cc
+++ b/chrome/renderer/extensions/extension_dispatcher.cc
@@ -24,6 +24,7 @@
#include "chrome/renderer/extensions/context_menus_custom_bindings.h"
#include "chrome/renderer/extensions/event_bindings.h"
#include "chrome/renderer/extensions/experimental.socket_custom_bindings.h"
+#include "chrome/renderer/extensions/experimental.usb_custom_bindings.h"
#include "chrome/renderer/extensions/extension_custom_bindings.h"
#include "chrome/renderer/extensions/extension_groups.h"
#include "chrome/renderer/extensions/extension_helper.h"
@@ -70,6 +71,7 @@ using content::RenderThread;
using extensions::ApiDefinitionsNatives;
using extensions::ContextMenusCustomBindings;
using extensions::ExperimentalSocketCustomBindings;
+using extensions::ExperimentalUsbCustomBindings;
using extensions::ExtensionAPI;
using extensions::ExtensionCustomBindings;
using extensions::Feature;
@@ -471,6 +473,8 @@ void ExtensionDispatcher::RegisterNativeHandlers(ModuleSystem* module_system,
new ExtensionCustomBindings(this)));
module_system->RegisterNativeHandler("experimental_socket",
scoped_ptr<NativeHandler>(new ExperimentalSocketCustomBindings()));
+ module_system->RegisterNativeHandler("experimental_usb",
+ scoped_ptr<NativeHandler>(new ExperimentalUsbCustomBindings()));
module_system->RegisterNativeHandler("file_browser_handler",
scoped_ptr<NativeHandler>(new FileBrowserHandlerCustomBindings()));
module_system->RegisterNativeHandler("file_browser_private",
@@ -522,6 +526,8 @@ void ExtensionDispatcher::PopulateSourceMap() {
IDR_EXPERIMENTAL_RUNTIME_CUSTOM_BINDINGS_JS);
source_map_.RegisterSource("experimental.socket",
IDR_EXPERIMENTAL_SOCKET_CUSTOM_BINDINGS_JS);
+ source_map_.RegisterSource("experimental.usb",
+ IDR_EXPERIMENTAL_USB_CUSTOM_BINDINGS_JS);
source_map_.RegisterSource("experimental.webRequest",
IDR_EXPERIMENTAL_WEBREQUEST_CUSTOM_BINDINGS_JS);
source_map_.RegisterSource("extension", IDR_EXTENSION_CUSTOM_BINDINGS_JS);
diff --git a/chrome/renderer/renderer_resources.grd b/chrome/renderer/renderer_resources.grd
index 60eb28f5..e7ba3c6 100644
--- a/chrome/renderer/renderer_resources.grd
+++ b/chrome/renderer/renderer_resources.grd
@@ -41,6 +41,7 @@ without changes to the corresponding grd file. fb9 -->
<include name="IDR_EXPERIMENTAL_OFFSCREENTABS_CUSTOM_BINDINGS_JS" file="resources\extensions\experimental.offscreenTabs_custom_bindings.js" type="BINDATA" />
<include name="IDR_EXPERIMENTAL_RUNTIME_CUSTOM_BINDINGS_JS" file="resources\extensions\experimental.runtime_custom_bindings.js" type="BINDATA" />
<include name="IDR_EXPERIMENTAL_SOCKET_CUSTOM_BINDINGS_JS" file="resources\extensions\experimental.socket_custom_bindings.js" type="BINDATA" />
+ <include name="IDR_EXPERIMENTAL_USB_CUSTOM_BINDINGS_JS" file="resources\extensions\experimental.usb_custom_bindings.js" type="BINDATA" />
<include name="IDR_EXPERIMENTAL_WEBREQUEST_CUSTOM_BINDINGS_JS" file="resources\extensions\experimental.webrequest_custom_bindings.js" type="BINDATA" />
<include name="IDR_EXTENSION_CUSTOM_BINDINGS_JS" file="resources\extensions\extension_custom_bindings.js" type="BINDATA" />
<include name="IDR_FILE_BROWSER_HANDLER_CUSTOM_BINDINGS_JS" file="resources\extensions\file_browser_handler_custom_bindings.js" type="BINDATA" />
diff --git a/chrome/renderer/resources/extensions/experimental.usb_custom_bindings.js b/chrome/renderer/resources/extensions/experimental.usb_custom_bindings.js
new file mode 100644
index 0000000..758af52
--- /dev/null
+++ b/chrome/renderer/resources/extensions/experimental.usb_custom_bindings.js
@@ -0,0 +1,46 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// TODO(gdk): This all looks very similar to the socket custom bindings, and for
+// a good reason, because they essentially do the same work. Refactor onEvent
+// bindings out of a per-extension mechanism into a generalized mechanism.
+
+ var experimentalUsbNatives = requireNative('experimental_usb');
+ var GetNextUsbEventId = experimentalUsbNatives.GetNextUsbEventId;
+
+ var chromeHidden = requireNative('chrome_hidden').GetChromeHidden();
+ var sendRequest = require('sendRequest').sendRequest;
+ var lazyBG = requireNative('lazy_background_page');
+
+ chromeHidden.registerCustomHook('experimental.usb', function(api) {
+ var apiFunctions = api.apiFunctions;
+
+ apiFunctions.setHandleRequest('findDevice', function() {
+ var args = arguments;
+ if (args[2] && args[2].onEvent) {
+ var id = GetNextUsbEventId();
+ args[2].srcId = id;
+ chromeHidden.usb.handlers[id] = args[2].onEvent;
+ lazyBG.IncrementKeepaliveCount();
+ }
+ sendRequest(this.name, args, this.definition.parameters);
+ return id;
+ });
+
+ chromeHidden.usb = {};
+ chromeHidden.usb.handlers = {};
+ chrome.experimental.usb.onEvent.addListener(function(event) {
+ var eventHandler = chromeHidden.usb.handlers[event.srcId];
+ if (eventHandler) {
+ switch (event.type) {
+ case 'transferComplete':
+ eventHandler({resultCode: event.resultCode, data: event.data});
+ break;
+ default:
+ console.error('Unexpected UsbEvent, type ' + event.type);
+ break;
+ }
+ }
+ });
+ });