summaryrefslogtreecommitdiff
path: root/chromium/content/browser/hid
diff options
context:
space:
mode:
Diffstat (limited to 'chromium/content/browser/hid')
-rw-r--r--chromium/content/browser/hid/hid_service.cc55
-rw-r--r--chromium/content/browser/hid/hid_service.h27
-rw-r--r--chromium/content/browser/hid/hid_service_unittest.cc122
-rw-r--r--chromium/content/browser/hid/hid_test_utils.cc21
-rw-r--r--chromium/content/browser/hid/hid_test_utils.h19
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