diff options
-rw-r--r-- | chrome/browser/extensions/api/device_permissions_manager_unittest.cc | 49 | ||||
-rw-r--r-- | device/usb/BUILD.gn | 2 | ||||
-rw-r--r-- | device/usb/usb.gyp | 2 | ||||
-rw-r--r-- | device/usb/usb_service.cc | 90 | ||||
-rw-r--r-- | device/usb/usb_service.h | 5 | ||||
-rw-r--r-- | device/usb/usb_service_impl.cc | 165 | ||||
-rw-r--r-- | device/usb/usb_service_impl.h | 82 | ||||
-rw-r--r-- | extensions/browser/api/hid/hid_apitest.cc | 2 | ||||
-rw-r--r-- | extensions/browser/api/usb/usb_apitest.cc | 78 | ||||
-rw-r--r-- | extensions/test/data/api_test/usb/add_event/background.js | 14 | ||||
-rw-r--r-- | extensions/test/data/api_test/usb/add_event/manifest.json | 15 | ||||
-rw-r--r-- | extensions/test/data/api_test/usb/remove_event/background.js | 21 | ||||
-rw-r--r-- | extensions/test/data/api_test/usb/remove_event/manifest.json | 15 |
13 files changed, 353 insertions, 187 deletions
diff --git a/chrome/browser/extensions/api/device_permissions_manager_unittest.cc b/chrome/browser/extensions/api/device_permissions_manager_unittest.cc index 6ce5345..6ce3660 100644 --- a/chrome/browser/extensions/api/device_permissions_manager_unittest.cc +++ b/chrome/browser/extensions/api/device_permissions_manager_unittest.cc @@ -123,20 +123,18 @@ class DevicePermissionsManagerTest : public testing::Test { protected: void SetUp() override { testing::Test::SetUp(); - env_.reset(new TestExtensionEnvironment()); - env_->GetExtensionPrefs(); // Force creation before adding extensions. - extension_ = - env_->MakeExtension(*base::test::ParseJson( - "{" - " \"app\": {" - " \"background\": {" - " \"scripts\": [\"background.js\"]" - " }" - " }," - " \"permissions\": [" - " \"usb\"" - " ]" - "}")); + env_.GetExtensionPrefs(); // Force creation before adding extensions. + extension_ = env_.MakeExtension(*base::test::ParseJson( + "{" + " \"app\": {" + " \"background\": {" + " \"scripts\": [\"background.js\"]" + " }" + " }," + " \"permissions\": [" + " \"usb\"" + " ]" + "}")); device0_ = new MockUsbDevice("ABCDE"); device1_ = new MockUsbDevice(""); device2_ = new MockUsbDevice("12345"); @@ -145,12 +143,7 @@ class DevicePermissionsManagerTest : public testing::Test { UsbService::SetInstanceForTest(usb_service_); } - void TearDown() override { - env_.reset(nullptr); - UsbService::SetInstanceForTest(nullptr); - } - - scoped_ptr<extensions::TestExtensionEnvironment> env_; + extensions::TestExtensionEnvironment env_; const extensions::Extension* extension_; MockUsbService* usb_service_; scoped_refptr<MockUsbDevice> device0_; @@ -161,7 +154,7 @@ class DevicePermissionsManagerTest : public testing::Test { TEST_F(DevicePermissionsManagerTest, AllowAndClearDevices) { DevicePermissionsManager* manager = - DevicePermissionsManager::Get(env_->profile()); + DevicePermissionsManager::Get(env_.profile()); AllowUsbDevice(manager, extension_, device0_); AllowUsbDevice(manager, extension_, device1_); @@ -205,7 +198,7 @@ TEST_F(DevicePermissionsManagerTest, AllowAndClearDevices) { TEST_F(DevicePermissionsManagerTest, SuspendExtension) { DevicePermissionsManager* manager = - DevicePermissionsManager::Get(env_->profile()); + DevicePermissionsManager::Get(env_.profile()); AllowUsbDevice(manager, extension_, device0_); AllowUsbDevice(manager, extension_, device1_); @@ -232,7 +225,7 @@ TEST_F(DevicePermissionsManagerTest, SuspendExtension) { TEST_F(DevicePermissionsManagerTest, DisconnectDevice) { DevicePermissionsManager* manager = - DevicePermissionsManager::Get(env_->profile()); + DevicePermissionsManager::Get(env_.profile()); AllowUsbDevice(manager, extension_, device0_); AllowUsbDevice(manager, extension_, device1_); @@ -260,7 +253,7 @@ TEST_F(DevicePermissionsManagerTest, DisconnectDevice) { TEST_F(DevicePermissionsManagerTest, RevokeAndRegrantAccess) { DevicePermissionsManager* manager = - DevicePermissionsManager::Get(env_->profile()); + DevicePermissionsManager::Get(env_.profile()); AllowUsbDevice(manager, extension_, device0_); AllowUsbDevice(manager, extension_, device1_); @@ -296,7 +289,7 @@ TEST_F(DevicePermissionsManagerTest, RevokeAndRegrantAccess) { TEST_F(DevicePermissionsManagerTest, UpdateLastUsed) { DevicePermissionsManager* manager = - DevicePermissionsManager::Get(env_->profile()); + DevicePermissionsManager::Get(env_.profile()); AllowUsbDevice(manager, extension_, device0_); scoped_ptr<DevicePermissions> device_permissions = @@ -321,11 +314,11 @@ TEST_F(DevicePermissionsManagerTest, LoadPrefs) { " \"vendor_id\": 0" " }" "]"); - env_->GetExtensionPrefs()->UpdateExtensionPref(extension_->id(), "devices", - prefs_value.release()); + env_.GetExtensionPrefs()->UpdateExtensionPref(extension_->id(), "devices", + prefs_value.release()); DevicePermissionsManager* manager = - DevicePermissionsManager::Get(env_->profile()); + DevicePermissionsManager::Get(env_.profile()); scoped_ptr<DevicePermissions> device_permissions = manager->GetForExtension(extension_->id()); ASSERT_TRUE(FindEntry(device_permissions.get(), device0_).get()); diff --git a/device/usb/BUILD.gn b/device/usb/BUILD.gn index ee793d3..d4be7cf 100644 --- a/device/usb/BUILD.gn +++ b/device/usb/BUILD.gn @@ -25,8 +25,10 @@ source_set("usb") { "usb_error.h", "usb_ids.cc", "usb_ids.h", + "usb_service.cc", "usb_service.h", "usb_service_impl.cc", + "usb_service_impl.h", generated_ids, ] diff --git a/device/usb/usb.gyp b/device/usb/usb.gyp index 301bea9..c0995a1 100644 --- a/device/usb/usb.gyp +++ b/device/usb/usb.gyp @@ -34,8 +34,10 @@ 'usb_error.h', 'usb_ids.cc', 'usb_ids.h', + 'usb_service.cc', 'usb_service.h', 'usb_service_impl.cc', + 'usb_service_impl.h', ], 'actions': [ { diff --git a/device/usb/usb_service.cc b/device/usb/usb_service.cc new file mode 100644 index 0000000..70f054d --- /dev/null +++ b/device/usb/usb_service.cc @@ -0,0 +1,90 @@ +// Copyright 2015 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 "device/usb/usb_service.h" + +#include "base/message_loop/message_loop.h" +#include "device/usb/usb_device.h" +#include "device/usb/usb_service_impl.h" + +namespace device { + +namespace { + +UsbService* g_service; + +} // namespace + +// This class manages the lifetime of the global UsbService instance so that +// it is destroyed when the current message loop is destroyed. A lazy instance +// cannot be used because this object does not live on the main thread. +class UsbService::Destroyer : private base::MessageLoop::DestructionObserver { + public: + explicit Destroyer(UsbService* usb_service) : usb_service_(usb_service) { + base::MessageLoop::current()->AddDestructionObserver(this); + } + ~Destroyer() override {} + + private: + // base::MessageLoop::DestructionObserver implementation. + void WillDestroyCurrentMessageLoop() override { + base::MessageLoop::current()->RemoveDestructionObserver(this); + delete usb_service_; + delete this; + g_service = nullptr; + } + + UsbService* usb_service_; +}; + +void UsbService::Observer::OnDeviceAdded(scoped_refptr<UsbDevice> device) { +} + +void UsbService::Observer::OnDeviceRemoved(scoped_refptr<UsbDevice> device) { +} + +// static +UsbService* UsbService::GetInstance( + scoped_refptr<base::SingleThreadTaskRunner> ui_task_runner) { + if (!g_service) { + g_service = UsbServiceImpl::Create(ui_task_runner); + // This object will clean itself up when the message loop is destroyed. + new Destroyer(g_service); + } + return g_service; +} + +// static +void UsbService::SetInstanceForTest(UsbService* instance) { + g_service = instance; + new Destroyer(instance); +} + +UsbService::UsbService() { +} + +UsbService::~UsbService() { +} + +void UsbService::AddObserver(Observer* observer) { + DCHECK(CalledOnValidThread()); + observer_list_.AddObserver(observer); +} + +void UsbService::RemoveObserver(Observer* observer) { + DCHECK(CalledOnValidThread()); + observer_list_.RemoveObserver(observer); +} + +void UsbService::NotifyDeviceAdded(scoped_refptr<UsbDevice> device) { + DCHECK(CalledOnValidThread()); + FOR_EACH_OBSERVER(Observer, observer_list_, OnDeviceAdded(device)); +} + +void UsbService::NotifyDeviceRemoved(scoped_refptr<UsbDevice> device) { + DCHECK(CalledOnValidThread()); + FOR_EACH_OBSERVER(Observer, observer_list_, OnDeviceRemoved(device)); +} + +} // namespace device diff --git a/device/usb/usb_service.h b/device/usb/usb_service.h index 076d363..4655bfd 100644 --- a/device/usb/usb_service.h +++ b/device/usb/usb_service.h @@ -55,8 +55,6 @@ class UsbService : public base::NonThreadSafe { void RemoveObserver(Observer* observer); protected: - friend struct base::DefaultDeleter<UsbService>; - UsbService(); virtual ~UsbService(); @@ -65,6 +63,9 @@ class UsbService : public base::NonThreadSafe { ObserverList<Observer, true> observer_list_; + private: + class Destroyer; + DISALLOW_COPY_AND_ASSIGN(UsbService); }; diff --git a/device/usb/usb_service_impl.cc b/device/usb/usb_service_impl.cc index 89fbd66a..d789e70 100644 --- a/device/usb/usb_service_impl.cc +++ b/device/usb/usb_service_impl.cc @@ -2,22 +2,17 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "device/usb/usb_service.h" +#include "device/usb/usb_service_impl.h" -#include <map> #include <set> #include "base/bind.h" -#include "base/lazy_instance.h" +#include "base/location.h" #include "base/memory/weak_ptr.h" -#include "base/message_loop/message_loop.h" #include "base/single_thread_task_runner.h" #include "base/stl_util.h" #include "base/thread_task_runner_handle.h" -#include "device/usb/usb_context.h" -#include "device/usb/usb_device_impl.h" #include "device/usb/usb_error.h" -#include "third_party/libusb/src/libusb/libusb.h" #if defined(OS_WIN) #include <usbiodef.h> @@ -28,79 +23,6 @@ namespace device { -namespace { - -base::LazyInstance<scoped_ptr<UsbService> >::Leaky g_usb_service_instance = - LAZY_INSTANCE_INITIALIZER; - -} // namespace - -typedef struct libusb_device* PlatformUsbDevice; -typedef struct libusb_context* PlatformUsbContext; - -class UsbServiceImpl : public UsbService, - private base::MessageLoop::DestructionObserver { - public: - explicit UsbServiceImpl( - PlatformUsbContext context, - scoped_refptr<base::SingleThreadTaskRunner> ui_task_runner); - ~UsbServiceImpl() override; - - private: - // device::UsbService implementation - scoped_refptr<UsbDevice> GetDeviceById(uint32 unique_id) override; - void GetDevices(std::vector<scoped_refptr<UsbDevice>>* devices) override; - - // base::MessageLoop::DestructionObserver implementation. - void WillDestroyCurrentMessageLoop() override; - - // Enumerate USB devices from OS and update devices_ map. - void RefreshDevices(); - - // Adds a new UsbDevice to the devices_ map based on the given libusb device. - scoped_refptr<UsbDeviceImpl> AddDevice(PlatformUsbDevice platform_device); - - // Handle hotplug events from libusb. - static int LIBUSB_CALL HotplugCallback(libusb_context* context, - PlatformUsbDevice device, - libusb_hotplug_event event, - void* user_data); - // These functions release a reference to the provided platform device. - void OnDeviceAdded(PlatformUsbDevice platform_device); - void OnDeviceRemoved(PlatformUsbDevice platform_device); - - scoped_refptr<UsbContext> context_; - scoped_refptr<base::SingleThreadTaskRunner> task_runner_; - scoped_refptr<base::SingleThreadTaskRunner> ui_task_runner_; - -#if defined(OS_WIN) - class UIThreadHelper; - UIThreadHelper* ui_thread_helper_; -#endif // OS_WIN - - // TODO(reillyg): Figure out a better solution for device IDs. - uint32 next_unique_id_; - - // When available the device list will be updated when new devices are - // connected instead of only when a full enumeration is requested. - // TODO(reillyg): Support this on all platforms. crbug.com/411715 - bool hotplug_enabled_; - libusb_hotplug_callback_handle hotplug_handle_; - - // The map from unique IDs to UsbDevices. - typedef std::map<uint32, scoped_refptr<UsbDeviceImpl> > DeviceMap; - DeviceMap devices_; - - // The map from PlatformUsbDevices to UsbDevices. - typedef std::map<PlatformUsbDevice, scoped_refptr<UsbDeviceImpl> > - PlatformDeviceMap; - PlatformDeviceMap platform_devices_; - - base::WeakPtrFactory<UsbServiceImpl> weak_factory_; - - DISALLOW_COPY_AND_ASSIGN(UsbServiceImpl); -}; - #if defined(OS_WIN) // This class lives on the application main thread so that it can listen for // device change notification window messages. It registers for notifications @@ -141,6 +63,23 @@ class UsbServiceImpl::UIThreadHelper final }; #endif +// static +UsbService* UsbServiceImpl::Create( + scoped_refptr<base::SingleThreadTaskRunner> ui_task_runner) { + PlatformUsbContext context = NULL; + const int rv = libusb_init(&context); + if (rv != LIBUSB_SUCCESS) { + VLOG(1) << "Failed to initialize libusb: " + << ConvertPlatformUsbErrorToString(rv); + return nullptr; + } + if (!context) { + return nullptr; + } + + return new UsbServiceImpl(context, ui_task_runner); +} + scoped_refptr<UsbDevice> UsbServiceImpl::GetDeviceById(uint32 unique_id) { DCHECK(CalledOnValidThread()); RefreshDevices(); @@ -165,11 +104,6 @@ void UsbServiceImpl::GetDevices( } } -void UsbServiceImpl::WillDestroyCurrentMessageLoop() { - DCHECK(CalledOnValidThread()); - g_usb_service_instance.Get().reset(NULL); -} - UsbServiceImpl::UsbServiceImpl( PlatformUsbContext context, scoped_refptr<base::SingleThreadTaskRunner> ui_task_runner) @@ -178,7 +112,6 @@ UsbServiceImpl::UsbServiceImpl( next_unique_id_(0), hotplug_enabled_(false), weak_factory_(this) { - base::MessageLoop::current()->AddDestructionObserver(this); task_runner_ = base::ThreadTaskRunnerHandle::Get(); int rv = libusb_hotplug_register_callback( context_->context(), @@ -200,7 +133,6 @@ UsbServiceImpl::UsbServiceImpl( } UsbServiceImpl::~UsbServiceImpl() { - base::MessageLoop::current()->RemoveDestructionObserver(this); if (hotplug_enabled_) { libusb_hotplug_deregister_callback(context_->context(), hotplug_handle_); } @@ -356,63 +288,4 @@ void UsbServiceImpl::OnDeviceRemoved(PlatformUsbDevice platform_device) { libusb_unref_device(platform_device); } -void UsbService::Observer::OnDeviceAdded(scoped_refptr<UsbDevice> device) { -} - -void UsbService::Observer::OnDeviceRemoved(scoped_refptr<UsbDevice> device) { -} - -// static -UsbService* UsbService::GetInstance( - scoped_refptr<base::SingleThreadTaskRunner> ui_task_runner) { - UsbService* instance = g_usb_service_instance.Get().get(); - if (!instance) { - PlatformUsbContext context = NULL; - - const int rv = libusb_init(&context); - if (rv != LIBUSB_SUCCESS) { - VLOG(1) << "Failed to initialize libusb: " - << ConvertPlatformUsbErrorToString(rv); - return NULL; - } - if (!context) - return NULL; - - instance = new UsbServiceImpl(context, ui_task_runner); - g_usb_service_instance.Get().reset(instance); - } - return instance; -} - -// static -void UsbService::SetInstanceForTest(UsbService* instance) { - g_usb_service_instance.Get().reset(instance); -} - -UsbService::UsbService() { -} - -UsbService::~UsbService() { -} - -void UsbService::AddObserver(Observer* observer) { - DCHECK(CalledOnValidThread()); - observer_list_.AddObserver(observer); -} - -void UsbService::RemoveObserver(Observer* observer) { - DCHECK(CalledOnValidThread()); - observer_list_.RemoveObserver(observer); -} - -void UsbService::NotifyDeviceAdded(scoped_refptr<UsbDevice> device) { - DCHECK(CalledOnValidThread()); - FOR_EACH_OBSERVER(Observer, observer_list_, OnDeviceAdded(device)); -} - -void UsbService::NotifyDeviceRemoved(scoped_refptr<UsbDevice> device) { - DCHECK(CalledOnValidThread()); - FOR_EACH_OBSERVER(Observer, observer_list_, OnDeviceRemoved(device)); -} - } // namespace device diff --git a/device/usb/usb_service_impl.h b/device/usb/usb_service_impl.h new file mode 100644 index 0000000..25cfb30 --- /dev/null +++ b/device/usb/usb_service_impl.h @@ -0,0 +1,82 @@ +// Copyright 2015 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 "device/usb/usb_service.h" + +#include <map> + +#include "base/memory/weak_ptr.h" +#include "base/single_thread_task_runner.h" +#include "device/usb/usb_context.h" +#include "device/usb/usb_device_impl.h" +#include "third_party/libusb/src/libusb/libusb.h" + +namespace device { + +typedef struct libusb_device* PlatformUsbDevice; +typedef struct libusb_context* PlatformUsbContext; + +class UsbServiceImpl : public UsbService { + public: + static UsbService* Create( + scoped_refptr<base::SingleThreadTaskRunner> ui_task_runner); + + private: + explicit UsbServiceImpl( + PlatformUsbContext context, + scoped_refptr<base::SingleThreadTaskRunner> ui_task_runner); + ~UsbServiceImpl() override; + + // device::UsbService implementation + scoped_refptr<UsbDevice> GetDeviceById(uint32 unique_id) override; + void GetDevices(std::vector<scoped_refptr<UsbDevice>>* devices) override; + + // Enumerate USB devices from OS and update devices_ map. + void RefreshDevices(); + + // Adds a new UsbDevice to the devices_ map based on the given libusb device. + scoped_refptr<UsbDeviceImpl> AddDevice(PlatformUsbDevice platform_device); + + // Handle hotplug events from libusb. + static int LIBUSB_CALL HotplugCallback(libusb_context* context, + PlatformUsbDevice device, + libusb_hotplug_event event, + void* user_data); + // These functions release a reference to the provided platform device. + void OnDeviceAdded(PlatformUsbDevice platform_device); + void OnDeviceRemoved(PlatformUsbDevice platform_device); + + scoped_refptr<UsbContext> context_; + scoped_refptr<base::SingleThreadTaskRunner> task_runner_; + scoped_refptr<base::SingleThreadTaskRunner> ui_task_runner_; + +#if defined(OS_WIN) + class UIThreadHelper; + UIThreadHelper* ui_thread_helper_; +#endif // OS_WIN + + // TODO(reillyg): Figure out a better solution for device IDs. + uint32 next_unique_id_; + + // When available the device list will be updated when new devices are + // connected instead of only when a full enumeration is requested. + // TODO(reillyg): Support this on all platforms. crbug.com/411715 + bool hotplug_enabled_; + libusb_hotplug_callback_handle hotplug_handle_; + + // The map from unique IDs to UsbDevices. + typedef std::map<uint32, scoped_refptr<UsbDeviceImpl>> DeviceMap; + DeviceMap devices_; + + // The map from PlatformUsbDevices to UsbDevices. + typedef std::map<PlatformUsbDevice, scoped_refptr<UsbDeviceImpl>> + PlatformDeviceMap; + PlatformDeviceMap platform_devices_; + + base::WeakPtrFactory<UsbServiceImpl> weak_factory_; + + DISALLOW_COPY_AND_ASSIGN(UsbServiceImpl); +}; + +} // namespace device diff --git a/extensions/browser/api/hid/hid_apitest.cc b/extensions/browser/api/hid/hid_apitest.cc index 0ea4cbd..c29c421 100644 --- a/extensions/browser/api/hid/hid_apitest.cc +++ b/extensions/browser/api/hid/hid_apitest.cc @@ -204,7 +204,7 @@ IN_PROC_BROWSER_TEST_F(HidApiTest, OnDeviceRemoved) { ASSERT_TRUE(LoadApp("api_test/hid/remove_event")); ASSERT_TRUE(load_listener.WaitUntilSatisfied()); - // Device C was not returned by chrome.usb.getDevices, the app will not get + // Device C was not returned by chrome.hid.getDevices, the app will not get // a notification. hid_service_->RemoveDevice("C"); // Device A was returned, the app will get a notification. diff --git a/extensions/browser/api/usb/usb_apitest.cc b/extensions/browser/api/usb/usb_apitest.cc index 57e073f..3960731 100644 --- a/extensions/browser/api/usb/usb_apitest.cc +++ b/extensions/browser/api/usb/usb_apitest.cc @@ -8,6 +8,7 @@ #include "device/usb/usb_service.h" #include "extensions/browser/api/usb/usb_api.h" #include "extensions/shell/test/shell_apitest.h" +#include "extensions/test/extension_test_message_listener.h" #include "net/base/io_buffer.h" #include "testing/gmock/include/gmock/gmock.h" @@ -126,6 +127,16 @@ class MockUsbService : public UsbService { public: explicit MockUsbService(scoped_refptr<UsbDevice> device) : device_(device) {} + // Public wrapper around the protected base class method. + void NotifyDeviceAdded(scoped_refptr<UsbDevice> device) { + UsbService::NotifyDeviceAdded(device); + } + + // Public wrapper around the protected base class method. + void NotifyDeviceRemoved(scoped_refptr<UsbDevice> device) { + UsbService::NotifyDeviceRemoved(device); + } + protected: scoped_refptr<UsbDevice> GetDeviceById(uint32 unique_id) override { EXPECT_EQ(unique_id, 0U); @@ -144,8 +155,12 @@ class UsbApiTest : public ShellApiTest { public: void SetUpOnMainThread() override { ShellApiTest::SetUpOnMainThread(); - mock_device_handle_ = new MockUsbDeviceHandle(); + mock_device_ = new MockUsbDevice(0, 0, 0); + EXPECT_CALL(*mock_device_.get(), GetSerialNumber(_)) + .WillRepeatedly(Return(false)); + + mock_device_handle_ = new MockUsbDeviceHandle(); mock_device_handle_->set_device(mock_device_.get()); EXPECT_CALL(*mock_device_.get(), RequestUsbAccess(_, _)) .WillRepeatedly(Invoke(RequestUsbAccess)); @@ -160,22 +175,26 @@ class UsbApiTest : public ShellApiTest { } void SetUpService() { - UsbService::SetInstanceForTest(new MockUsbService(mock_device_)); + mock_service_ = new MockUsbService(mock_device_); + UsbService::SetInstanceForTest(mock_service_); } - void TearDownOnMainThread() override { - UsbService* service = NULL; - base::RunLoop run_loop; - BrowserThread::PostTaskAndReply( - BrowserThread::FILE, FROM_HERE, - base::Bind(&UsbService::SetInstanceForTest, service), - run_loop.QuitClosure()); - run_loop.Run(); + void AddTestDevices() { + scoped_refptr<MockUsbDevice> device(new MockUsbDevice(0x18D1, 0x58F0, 1)); + EXPECT_CALL(*device.get(), GetSerialNumber(_)) + .WillRepeatedly(Return(false)); + mock_service_->NotifyDeviceAdded(device); + + device = new MockUsbDevice(0x18D1, 0x58F1, 2); + EXPECT_CALL(*device.get(), GetSerialNumber(_)) + .WillRepeatedly(Return(false)); + mock_service_->NotifyDeviceAdded(device); } protected: scoped_refptr<MockUsbDeviceHandle> mock_device_handle_; scoped_refptr<MockUsbDevice> mock_device_; + MockUsbService* mock_service_; }; } // namespace @@ -263,4 +282,43 @@ IN_PROC_BROWSER_TEST_F(UsbApiTest, InvalidLengthTransfer) { ASSERT_TRUE(RunAppTest("api_test/usb/invalid_length_transfer")); } +IN_PROC_BROWSER_TEST_F(UsbApiTest, OnDeviceAdded) { + ExtensionTestMessageListener load_listener("loaded", false); + ExtensionTestMessageListener result_listener("success", false); + result_listener.set_failure_message("failure"); + + ASSERT_TRUE(LoadApp("api_test/usb/add_event")); + ASSERT_TRUE(load_listener.WaitUntilSatisfied()); + + base::RunLoop run_loop; + BrowserThread::PostTaskAndReply( + BrowserThread::FILE, FROM_HERE, + base::Bind(&UsbApiTest::AddTestDevices, base::Unretained(this)), + run_loop.QuitClosure()); + run_loop.Run(); + + ASSERT_TRUE(result_listener.WaitUntilSatisfied()); + ASSERT_EQ("success", result_listener.message()); +} + +IN_PROC_BROWSER_TEST_F(UsbApiTest, OnDeviceRemoved) { + ExtensionTestMessageListener load_listener("loaded", false); + ExtensionTestMessageListener result_listener("success", false); + result_listener.set_failure_message("failure"); + + ASSERT_TRUE(LoadApp("api_test/usb/remove_event")); + ASSERT_TRUE(load_listener.WaitUntilSatisfied()); + + base::RunLoop run_loop; + BrowserThread::PostTaskAndReply( + BrowserThread::FILE, FROM_HERE, + base::Bind(&MockUsbService::NotifyDeviceRemoved, + base::Unretained(mock_service_), mock_device_), + run_loop.QuitClosure()); + run_loop.Run(); + + ASSERT_TRUE(result_listener.WaitUntilSatisfied()); + ASSERT_EQ("success", result_listener.message()); +} + } // namespace extensions diff --git a/extensions/test/data/api_test/usb/add_event/background.js b/extensions/test/data/api_test/usb/add_event/background.js new file mode 100644 index 0000000..1f8382a --- /dev/null +++ b/extensions/test/data/api_test/usb/add_event/background.js @@ -0,0 +1,14 @@ +// Copyright 2015 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. + +chrome.usb.onDeviceAdded.addListener(function(device) { + if (device.vendorId == 6353 && device.productId == 22768) { + chrome.test.sendMessage("success"); + } else { + console.error("Got unexpected device: vid:" + device.vendorId + + " pid:" + device.productId); + chrome.test.sendMessage("failure"); + } +}); +chrome.test.sendMessage("loaded"); diff --git a/extensions/test/data/api_test/usb/add_event/manifest.json b/extensions/test/data/api_test/usb/add_event/manifest.json new file mode 100644 index 0000000..d5a7d6f --- /dev/null +++ b/extensions/test/data/api_test/usb/add_event/manifest.json @@ -0,0 +1,15 @@ +{ + "name": "chrome.usb.onDeviceAdded", + "version": "0.1", + "description": "browser test for chrome.usb.onDeviceAdded event", + "app": { + "background": { + "scripts": ["background.js"] + } + }, + "permissions": [ + "usb", + // This is a test device emulated by the mocks enabled for the test. + { "usbDevices": [{ "vendorId": 6353, "productId": 22768 }]} + ] +} diff --git a/extensions/test/data/api_test/usb/remove_event/background.js b/extensions/test/data/api_test/usb/remove_event/background.js new file mode 100644 index 0000000..6c6bbb5 --- /dev/null +++ b/extensions/test/data/api_test/usb/remove_event/background.js @@ -0,0 +1,21 @@ +// Copyright 2014 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. + +var known_devices = {}; + +chrome.usb.onDeviceRemoved.addListener(function(device) { + if (device.device in known_devices) { + chrome.test.sendMessage("success"); + } else { + console.error("Unexpected device removed: " + device.device); + chrome.test.sendMessage("failure"); + } +}); + +chrome.usb.getDevices({}, function(devices) { + for (var device of devices) { + known_devices[device.device] = device; + } + chrome.test.sendMessage("loaded"); +}); diff --git a/extensions/test/data/api_test/usb/remove_event/manifest.json b/extensions/test/data/api_test/usb/remove_event/manifest.json new file mode 100644 index 0000000..2dce9e8 --- /dev/null +++ b/extensions/test/data/api_test/usb/remove_event/manifest.json @@ -0,0 +1,15 @@ +{ + "name": "chrome.usb.onDeviceRemoved", + "version": "0.1", + "description": "browser test for chrome.usb.onDeviceRemoved event", + "app": { + "background": { + "scripts": ["background.js"] + } + }, + "permissions": [ + "usb", + // This is a test device emulated by the mocks enabled for the test. + { "usbDevices": [{ "vendorId": 0, "productId": 0 }]} + ] +} |