diff options
Diffstat (limited to 'chromium/content/browser/hid')
-rw-r--r-- | chromium/content/browser/hid/hid_service.cc | 55 | ||||
-rw-r--r-- | chromium/content/browser/hid/hid_service.h | 27 | ||||
-rw-r--r-- | chromium/content/browser/hid/hid_service_unittest.cc | 122 | ||||
-rw-r--r-- | chromium/content/browser/hid/hid_test_utils.cc | 21 | ||||
-rw-r--r-- | chromium/content/browser/hid/hid_test_utils.h | 19 |
5 files changed, 219 insertions, 25 deletions
diff --git a/chromium/content/browser/hid/hid_service.cc b/chromium/content/browser/hid/hid_service.cc index 6c7da06458d..e182c460387 100644 --- a/chromium/content/browser/hid/hid_service.cc +++ b/chromium/content/browser/hid/hid_service.cc @@ -7,7 +7,9 @@ #include <utility> #include "base/bind.h" +#include "base/bind_helpers.h" #include "base/callback.h" +#include "base/debug/stack_trace.h" #include "content/browser/web_contents/web_contents_impl.h" #include "content/public/browser/content_browser_client.h" #include "content/public/browser/hid_chooser.h" @@ -24,9 +26,17 @@ HidService::HidService(RenderFrameHost* render_frame_host, : FrameServiceBase(render_frame_host, std::move(receiver)) { watchers_.set_disconnect_handler(base::BindRepeating( &HidService::OnWatcherConnectionError, base::Unretained(this))); + + HidDelegate* delegate = GetContentClient()->browser()->GetHidDelegate(); + if (delegate) + delegate->AddObserver(render_frame_host, this); } HidService::~HidService() { + HidDelegate* delegate = GetContentClient()->browser()->GetHidDelegate(); + if (delegate) + delegate->RemoveObserver(render_frame_host(), this); + // The remaining watchers will be closed from this end. if (!watchers_.empty()) DecrementActiveFrameCount(); @@ -55,6 +65,11 @@ void HidService::Create( new HidService(render_frame_host, std::move(receiver)); } +void HidService::RegisterClient( + device::mojom::HidManagerClientAssociatedPtrInfo client) { + clients_.Add(std::move(client)); +} + void HidService::GetDevices(GetDevicesCallback callback) { GetContentClient() ->browser() @@ -114,6 +129,46 @@ void HidService::DecrementActiveFrameCount() { web_contents_impl->DecrementHidActiveFrameCount(); } +void HidService::OnDeviceAdded( + const device::mojom::HidDeviceInfo& device_info) { + if (!GetContentClient()->browser()->GetHidDelegate()->HasDevicePermission( + WebContents::FromRenderFrameHost(render_frame_host()), origin(), + device_info)) { + return; + } + + for (auto& client : clients_) + client->DeviceAdded(device_info.Clone()); +} + +void HidService::OnDeviceRemoved( + const device::mojom::HidDeviceInfo& device_info) { + if (!GetContentClient()->browser()->GetHidDelegate()->HasDevicePermission( + WebContents::FromRenderFrameHost(render_frame_host()), origin(), + device_info)) { + return; + } + + for (auto& client : clients_) + client->DeviceRemoved(device_info.Clone()); +} + +void HidService::OnHidManagerConnectionError() { + // Close the connection with Blink. + clients_.Clear(); +} + +void HidService::OnPermissionRevoked(const url::Origin& requesting_origin, + const url::Origin& embedding_origin) { + if (requesting_origin_ != requesting_origin || + embedding_origin_ != embedding_origin) { + return; + } + + // TODO(mattreynolds): Close connection between Blink and the device if the + // device lost permission. +} + void HidService::FinishGetDevices( GetDevicesCallback callback, std::vector<device::mojom::HidDeviceInfoPtr> devices) { diff --git a/chromium/content/browser/hid/hid_service.h b/chromium/content/browser/hid/hid_service.h index ed75856108e..07abd4993ab 100644 --- a/chromium/content/browser/hid/hid_service.h +++ b/chromium/content/browser/hid/hid_service.h @@ -9,12 +9,14 @@ #include <string> #include <vector> -#include "base/macros.h" #include "base/memory/weak_ptr.h" +#include "base/scoped_observer.h" #include "content/public/browser/frame_service_base.h" +#include "content/public/browser/hid_delegate.h" #include "mojo/public/cpp/bindings/pending_receiver.h" #include "mojo/public/cpp/bindings/pending_remote.h" #include "mojo/public/cpp/bindings/receiver_set.h" +#include "mojo/public/cpp/bindings/remote_set.h" #include "services/device/public/mojom/hid.mojom.h" #include "third_party/blink/public/mojom/hid/hid.mojom.h" @@ -26,12 +28,18 @@ class RenderFrameHost; // HidService provides an implementation of the HidService mojom interface. This // interface is used by Blink to implement the WebHID API. class HidService : public content::FrameServiceBase<blink::mojom::HidService>, - public device::mojom::HidConnectionWatcher { + public device::mojom::HidConnectionWatcher, + public HidDelegate::Observer { public: + HidService(HidService&) = delete; + HidService& operator=(HidService&) = delete; + static void Create(RenderFrameHost*, mojo::PendingReceiver<blink::mojom::HidService>); // blink::mojom::HidService: + void RegisterClient( + device::mojom::HidManagerClientAssociatedPtrInfo client) override; void GetDevices(GetDevicesCallback callback) override; void RequestDevice(std::vector<blink::mojom::HidDeviceFilterPtr> filters, RequestDeviceCallback callback) override; @@ -39,6 +47,14 @@ class HidService : public content::FrameServiceBase<blink::mojom::HidService>, mojo::PendingRemote<device::mojom::HidConnectionClient> client, ConnectCallback callback) override; + // HidDelegate::Observer: + void OnDeviceAdded(const device::mojom::HidDeviceInfo& device_info) override; + void OnDeviceRemoved( + const device::mojom::HidDeviceInfo& device_info) override; + void OnHidManagerConnectionError() override; + void OnPermissionRevoked(const url::Origin& requesting_origin, + const url::Origin& embedding_origin) override; + private: HidService(RenderFrameHost*, mojo::PendingReceiver<blink::mojom::HidService>); ~HidService() override; @@ -57,14 +73,17 @@ class HidService : public content::FrameServiceBase<blink::mojom::HidService>, // The last shown HID chooser UI. std::unique_ptr<HidChooser> chooser_; + url::Origin requesting_origin_; + url::Origin embedding_origin_; + + // Used to bind with Blink. + mojo::AssociatedRemoteSet<device::mojom::HidManagerClient> clients_; // Each pipe here watches a connection created by Connect() in order to notify // the WebContentsImpl when an active connection indicator should be shown. mojo::ReceiverSet<device::mojom::HidConnectionWatcher> watchers_; base::WeakPtrFactory<HidService> weak_factory_{this}; - - DISALLOW_COPY_AND_ASSIGN(HidService); }; } // namespace content diff --git a/chromium/content/browser/hid/hid_service_unittest.cc b/chromium/content/browser/hid/hid_service_unittest.cc index bb564f59d88..56b693515a0 100644 --- a/chromium/content/browser/hid/hid_service_unittest.cc +++ b/chromium/content/browser/hid/hid_service_unittest.cc @@ -2,8 +2,12 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +#include <memory> +#include <vector> + #include "base/command_line.h" #include "base/test/bind_test_util.h" +#include "base/test/gmock_callback_support.h" #include "content/browser/hid/hid_test_utils.h" #include "content/public/browser/content_browser_client.h" #include "content/public/browser/hid_delegate.h" @@ -19,6 +23,11 @@ #include "testing/gtest/include/gtest/gtest.h" #include "third_party/blink/public/mojom/hid/hid.mojom.h" +using ::base::test::RunClosure; +using ::testing::_; +using ::testing::ByMove; +using ::testing::Return; + namespace content { namespace { @@ -30,6 +39,8 @@ const char kCrossOriginTestUrl[] = "https://www.chromium.org"; class FakeHidConnectionClient : public device::mojom::HidConnectionClient { public: FakeHidConnectionClient() = default; + FakeHidConnectionClient(FakeHidConnectionClient&) = delete; + FakeHidConnectionClient& operator=(FakeHidConnectionClient&) = delete; ~FakeHidConnectionClient() override = default; void Bind( @@ -43,17 +54,36 @@ class FakeHidConnectionClient : public device::mojom::HidConnectionClient { private: mojo::Receiver<device::mojom::HidConnectionClient> receiver_{this}; +}; + +class MockHidManagerClient : public device::mojom::HidManagerClient { + public: + MockHidManagerClient() = default; + MockHidManagerClient(MockHidManagerClient&) = delete; + MockHidManagerClient& operator=(MockHidManagerClient&) = delete; + ~MockHidManagerClient() override = default; + + void Bind(mojo::PendingAssociatedReceiver<device::mojom::HidManagerClient> + receiver) { + receiver_.Bind(std::move(receiver)); + } + + MOCK_METHOD1(DeviceAdded, void(device::mojom::HidDeviceInfoPtr device_info)); + MOCK_METHOD1(DeviceRemoved, + void(device::mojom::HidDeviceInfoPtr device_info)); - DISALLOW_COPY_AND_ASSIGN(FakeHidConnectionClient); + private: + mojo::AssociatedReceiver<device::mojom::HidManagerClient> receiver_{this}; }; +// Main test fixture. class HidServiceTest : public RenderViewHostImplTestHarness { public: HidServiceTest() { - ON_CALL(hid_delegate(), GetHidManager) - .WillByDefault(testing::Return(&hid_manager_)); + ON_CALL(hid_delegate(), GetHidManager).WillByDefault(Return(&hid_manager_)); } - + HidServiceTest(HidServiceTest&) = delete; + HidServiceTest& operator=(HidServiceTest&) = delete; ~HidServiceTest() override = default; void SetUp() override { @@ -69,8 +99,17 @@ class HidServiceTest : public RenderViewHostImplTestHarness { SetBrowserClientForTesting(original_client_); } + void ConnectDevice(const device::mojom::HidDeviceInfo& device) { + hid_manager_.AddDevice(device.Clone()); + hid_delegate().OnDeviceAdded(device); + } + + void DisconnectDevice(const device::mojom::HidDeviceInfo& device) { + hid_manager_.RemoveDevice(device.guid); + hid_delegate().OnDeviceRemoved(device); + } + MockHidDelegate& hid_delegate() { return test_client_.delegate(); } - device::FakeHidManager* hid_manager() { return &hid_manager_; } FakeHidConnectionClient* connection_client() { return &connection_client_; } private: @@ -78,8 +117,6 @@ class HidServiceTest : public RenderViewHostImplTestHarness { ContentBrowserClient* original_client_ = nullptr; device::FakeHidManager hid_manager_; FakeHidConnectionClient connection_client_; - - DISALLOW_COPY_AND_ASSIGN(HidServiceTest); }; } // namespace @@ -93,10 +130,9 @@ TEST_F(HidServiceTest, GetDevicesWithPermission) { auto device_info = device::mojom::HidDeviceInfo::New(); device_info->guid = kTestGuid; - hid_manager()->AddDevice(std::move(device_info)); + ConnectDevice(*device_info); - EXPECT_CALL(hid_delegate(), HasDevicePermission) - .WillOnce(testing::Return(true)); + EXPECT_CALL(hid_delegate(), HasDevicePermission).WillOnce(Return(true)); base::RunLoop run_loop; std::vector<device::mojom::HidDeviceInfoPtr> devices; @@ -118,10 +154,9 @@ TEST_F(HidServiceTest, GetDevicesWithoutPermission) { auto device_info = device::mojom::HidDeviceInfo::New(); device_info->guid = kTestGuid; - hid_manager()->AddDevice(std::move(device_info)); + ConnectDevice(*device_info); - EXPECT_CALL(hid_delegate(), HasDevicePermission) - .WillOnce(testing::Return(false)); + EXPECT_CALL(hid_delegate(), HasDevicePermission).WillOnce(Return(false)); base::RunLoop run_loop; std::vector<device::mojom::HidDeviceInfoPtr> devices; @@ -145,12 +180,12 @@ TEST_F(HidServiceTest, RequestDevice) { device_info->guid = kTestGuid; std::vector<device::mojom::HidDeviceInfoPtr> device_infos; device_infos.push_back(device_info.Clone()); - hid_manager()->AddDevice(std::move(device_info)); + ConnectDevice(*device_info); EXPECT_CALL(hid_delegate(), CanRequestDevicePermission) - .WillOnce(testing::Return(true)); + .WillOnce(Return(true)); EXPECT_CALL(hid_delegate(), RunChooserInternal) - .WillOnce(testing::Return(testing::ByMove(std::move(device_infos)))); + .WillOnce(Return(ByMove(std::move(device_infos)))); base::RunLoop run_loop; std::vector<device::mojom::HidDeviceInfoPtr> chosen_devices; @@ -175,7 +210,7 @@ TEST_F(HidServiceTest, OpenAndCloseHidConnection) { auto device_info = device::mojom::HidDeviceInfo::New(); device_info->guid = kTestGuid; - hid_manager()->AddDevice(std::move(device_info)); + ConnectDevice(*device_info); mojo::PendingRemote<device::mojom::HidConnectionClient> hid_connection_client; connection_client()->Bind( @@ -217,7 +252,7 @@ TEST_F(HidServiceTest, OpenAndNavigateCrossOrigin) { auto device_info = device::mojom::HidDeviceInfo::New(); device_info->guid = kTestGuid; - hid_manager()->AddDevice(std::move(device_info)); + ConnectDevice(*device_info); mojo::PendingRemote<device::mojom::HidConnectionClient> hid_connection_client; connection_client()->Bind( @@ -245,4 +280,55 @@ TEST_F(HidServiceTest, OpenAndNavigateCrossOrigin) { EXPECT_FALSE(contents()->IsConnectedToHidDevice()); } +TEST_F(HidServiceTest, RegisterClient) { + MockHidManagerClient mock_hid_manager_client; + + base::RunLoop device_added_loop; + EXPECT_CALL(mock_hid_manager_client, DeviceAdded(_)) + .WillOnce(RunClosure(device_added_loop.QuitClosure())); + + base::RunLoop device_removed_loop; + EXPECT_CALL(mock_hid_manager_client, DeviceRemoved(_)) + .WillOnce(RunClosure(device_removed_loop.QuitClosure())); + + EXPECT_CALL(hid_delegate(), HasDevicePermission) + .WillOnce(Return(true)) + .WillOnce(Return(true)); + + NavigateAndCommit(GURL(kTestUrl)); + + mojo::Remote<blink::mojom::HidService> service; + contents()->GetMainFrame()->GetHidService( + service.BindNewPipeAndPassReceiver()); + + mojo::PendingAssociatedRemote<device::mojom::HidManagerClient> + hid_manager_client; + mock_hid_manager_client.Bind( + hid_manager_client.InitWithNewEndpointAndPassReceiver()); + + // 1. Register the mock client with the service. Wait for GetDevices to + // return to ensure the client has been set. + service->RegisterClient(std::move(hid_manager_client)); + + base::RunLoop run_loop; + std::vector<device::mojom::HidDeviceInfoPtr> devices; + service->GetDevices(base::BindLambdaForTesting( + [&run_loop, &devices](std::vector<device::mojom::HidDeviceInfoPtr> d) { + devices = std::move(d); + run_loop.Quit(); + })); + run_loop.Run(); + EXPECT_TRUE(devices.empty()); + + // 2. Connect a device and wait for DeviceAdded. + auto device_info = device::mojom::HidDeviceInfo::New(); + device_info->guid = kTestGuid; + ConnectDevice(*device_info); + device_added_loop.Run(); + + // 3. Disconnect the device and wait for DeviceRemoved. + DisconnectDevice(*device_info); + device_removed_loop.Run(); +} + } // namespace content diff --git a/chromium/content/browser/hid/hid_test_utils.cc b/chromium/content/browser/hid/hid_test_utils.cc index 928bd3b3962..38c01d51f2f 100644 --- a/chromium/content/browser/hid/hid_test_utils.cc +++ b/chromium/content/browser/hid/hid_test_utils.cc @@ -23,6 +23,27 @@ std::unique_ptr<HidChooser> MockHidDelegate::RunChooser( return nullptr; } +void MockHidDelegate::AddObserver(RenderFrameHost* frame, Observer* observer) { + observer_list_.AddObserver(observer); +} + +void MockHidDelegate::RemoveObserver(RenderFrameHost* frame, + Observer* observer) { + observer_list_.RemoveObserver(observer); +} + +void MockHidDelegate::OnDeviceAdded( + const device::mojom::HidDeviceInfo& device) { + for (auto& observer : observer_list_) + observer.OnDeviceAdded(device); +} + +void MockHidDelegate::OnDeviceRemoved( + const device::mojom::HidDeviceInfo& device) { + for (auto& observer : observer_list_) + observer.OnDeviceRemoved(device); +} + HidTestContentBrowserClient::HidTestContentBrowserClient() = default; HidTestContentBrowserClient::~HidTestContentBrowserClient() = default; diff --git a/chromium/content/browser/hid/hid_test_utils.h b/chromium/content/browser/hid/hid_test_utils.h index 2589e62c1ab..bf800a6800f 100644 --- a/chromium/content/browser/hid/hid_test_utils.h +++ b/chromium/content/browser/hid/hid_test_utils.h @@ -5,6 +5,9 @@ #ifndef CONTENT_BROWSER_HID_HID_TEST_UTILS_H_ #define CONTENT_BROWSER_HID_HID_TEST_UTILS_H_ +#include <memory> +#include <vector> + #include "content/public/browser/content_browser_client.h" #include "content/public/browser/hid_delegate.h" #include "content/public/browser/web_contents.h" @@ -19,6 +22,8 @@ namespace content { class MockHidDelegate : public HidDelegate { public: MockHidDelegate(); + MockHidDelegate(MockHidDelegate&) = delete; + MockHidDelegate& operator=(MockHidDelegate&) = delete; ~MockHidDelegate() override; // Simulates opening the HID device chooser dialog and selecting an item. The @@ -29,6 +34,14 @@ class MockHidDelegate : public HidDelegate { std::vector<blink::mojom::HidDeviceFilterPtr> filters, HidChooser::Callback callback) override; + void AddObserver(RenderFrameHost* frame, Observer* observer) override; + void RemoveObserver(RenderFrameHost* frame, Observer* observer) override; + + // MockHidDelegate does not register to receive device connection events. Use + // these methods to broadcast device connections to all delegate observers. + void OnDeviceAdded(const device::mojom::HidDeviceInfo& device); + void OnDeviceRemoved(const device::mojom::HidDeviceInfo& device); + MOCK_METHOD0(RunChooserInternal, std::vector<device::mojom::HidDeviceInfoPtr>()); MOCK_METHOD2(CanRequestDevicePermission, @@ -42,7 +55,7 @@ class MockHidDelegate : public HidDelegate { device::mojom::HidManager*(content::WebContents* web_contents)); private: - DISALLOW_COPY_AND_ASSIGN(MockHidDelegate); + base::ObserverList<Observer> observer_list_; }; // Test implementation of ContentBrowserClient for HID tests. The test client @@ -50,6 +63,8 @@ class MockHidDelegate : public HidDelegate { class HidTestContentBrowserClient : public ContentBrowserClient { public: HidTestContentBrowserClient(); + HidTestContentBrowserClient(HidTestContentBrowserClient&) = delete; + HidTestContentBrowserClient& operator=(HidTestContentBrowserClient&) = delete; ~HidTestContentBrowserClient() override; MockHidDelegate& delegate() { return delegate_; } @@ -59,8 +74,6 @@ class HidTestContentBrowserClient : public ContentBrowserClient { private: MockHidDelegate delegate_; - - DISALLOW_COPY_AND_ASSIGN(HidTestContentBrowserClient); }; } // namespace content |