Skip to content

Commit

Permalink
[webhid] Add WebContents::IsConnectedToHidDevice
Browse files Browse the repository at this point in the history
IsConnectedToHidDevice returns true if any frame in the
WebContents is connected to a HID device. This will enable a
tab indicator icon to be displayed when a tab has an open HID
connection.

HidService monitors the number of open HID connections for a
frame and notifies WebContentsImpl when the count transitions
between zero and one.

BUG=1007539

Change-Id: I4b7020af97bcb50e2b26e5865519bcd5f77c63a4
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1823727
Reviewed-by: John Abd-El-Malek <jam@chromium.org>
Reviewed-by: Reilly Grant <reillyg@chromium.org>
Commit-Queue: Matt Reynolds <mattreynolds@chromium.org>
Cr-Commit-Position: refs/heads/master@{#711979}
  • Loading branch information
Matt Reynolds authored and Commit Bot committed Nov 2, 2019
1 parent 4738af3 commit e8c6c1f
Show file tree
Hide file tree
Showing 6 changed files with 93 additions and 7 deletions.
36 changes: 30 additions & 6 deletions content/browser/hid/hid_service.cc
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@

#include "base/bind.h"
#include "base/callback.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"
#include "content/public/browser/hid_delegate.h"
Expand All @@ -20,9 +21,16 @@ namespace content {

HidService::HidService(RenderFrameHost* render_frame_host,
mojo::PendingReceiver<blink::mojom::HidService> receiver)
: FrameServiceBase(render_frame_host, std::move(receiver)) {}
: FrameServiceBase(render_frame_host, std::move(receiver)) {
watchers_.set_disconnect_handler(base::BindRepeating(
&HidService::OnWatcherConnectionError, base::Unretained(this)));
}

HidService::~HidService() = default;
HidService::~HidService() {
// The remaining watchers will be closed from this end.
if (!watchers_.empty())
DecrementActiveFrameCount();
}

// static
void HidService::Create(
Expand Down Expand Up @@ -76,19 +84,35 @@ void HidService::Connect(
const std::string& device_guid,
mojo::PendingRemote<device::mojom::HidConnectionClient> client,
ConnectCallback callback) {
if (watchers_.empty()) {
auto* web_contents_impl = static_cast<WebContentsImpl*>(
WebContents::FromRenderFrameHost(render_frame_host()));
web_contents_impl->IncrementHidActiveFrameCount();
}

mojo::PendingRemote<device::mojom::HidConnectionWatcher> watcher;
watchers_.Add(this, watcher.InitWithNewPipeAndPassReceiver());
GetContentClient()
->browser()
->GetHidDelegate()
->GetHidManager(web_contents())
->Connect(
device_guid, std::move(client),
// TODO(mattreynolds): Bind |this| as a HidConnectionWatcher and
// track the number of active watchers.
/*watcher=*/mojo::NullRemote(),
device_guid, std::move(client), std::move(watcher),
base::BindOnce(&HidService::FinishConnect, weak_factory_.GetWeakPtr(),
std::move(callback)));
}

void HidService::OnWatcherConnectionError() {
if (watchers_.empty())
DecrementActiveFrameCount();
}

void HidService::DecrementActiveFrameCount() {
auto* web_contents_impl = static_cast<WebContentsImpl*>(
WebContents::FromRenderFrameHost(render_frame_host()));
web_contents_impl->DecrementHidActiveFrameCount();
}

void HidService::FinishGetDevices(
GetDevicesCallback callback,
std::vector<device::mojom::HidDeviceInfoPtr> devices) {
Expand Down
11 changes: 10 additions & 1 deletion content/browser/hid/hid_service.h
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
#include "content/public/browser/frame_service_base.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 "services/device/public/mojom/hid.mojom.h"
#include "third_party/blink/public/mojom/hid/hid.mojom.h"

Expand All @@ -24,7 +25,8 @@ 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> {
class HidService : public content::FrameServiceBase<blink::mojom::HidService>,
public device::mojom::HidConnectionWatcher {
public:
static void Create(RenderFrameHost*,
mojo::PendingReceiver<blink::mojom::HidService>);
Expand All @@ -41,6 +43,9 @@ class HidService : public content::FrameServiceBase<blink::mojom::HidService> {
HidService(RenderFrameHost*, mojo::PendingReceiver<blink::mojom::HidService>);
~HidService() override;

void OnWatcherConnectionError();
void DecrementActiveFrameCount();

void FinishGetDevices(GetDevicesCallback callback,
std::vector<device::mojom::HidDeviceInfoPtr> devices);
void FinishRequestDevice(RequestDeviceCallback callback,
Expand All @@ -52,6 +57,10 @@ class HidService : public content::FrameServiceBase<blink::mojom::HidService> {
// The last shown HID chooser UI.
std::unique_ptr<HidChooser> chooser_;

// 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);
Expand Down
11 changes: 11 additions & 0 deletions content/browser/hid/hid_service_unittest.cc
Original file line number Diff line number Diff line change
Expand Up @@ -177,6 +177,8 @@ TEST_F(HidServiceTest, OpenAndCloseHidConnection) {
connection_client()->Bind(
hid_connection_client.InitWithNewPipeAndPassReceiver());

EXPECT_FALSE(contents()->IsConnectedToHidDevice());

base::RunLoop run_loop;
mojo::PendingRemote<device::mojom::HidConnection> connection;
service->Connect(
Expand All @@ -190,7 +192,16 @@ TEST_F(HidServiceTest, OpenAndCloseHidConnection) {
run_loop.Run();
EXPECT_TRUE(connection);

EXPECT_TRUE(contents()->IsConnectedToHidDevice());

// Destroying |connection| will also disconnect the watcher.
connection.reset();

// Allow the watcher's disconnect handler to run. This will update the
// WebContents active frame count.
base::RunLoop().RunUntilIdle();

EXPECT_FALSE(contents()->IsConnectedToHidDevice());
}

} // namespace content
31 changes: 31 additions & 0 deletions content/browser/web_contents/web_contents_impl.cc
Original file line number Diff line number Diff line change
Expand Up @@ -1532,6 +1532,10 @@ bool WebContentsImpl::IsConnectedToSerialPort() {
return serial_active_frame_count_ > 0;
}

bool WebContentsImpl::IsConnectedToHidDevice() {
return hid_active_frame_count_ > 0;
}

bool WebContentsImpl::HasNativeFileSystemHandles() {
return native_file_system_handle_count_ > 0;
}
Expand Down Expand Up @@ -6925,6 +6929,33 @@ void WebContentsImpl::DecrementSerialActiveFrameCount() {
NotifyNavigationStateChanged(INVALIDATE_TYPE_TAB);
}

void WebContentsImpl::IncrementHidActiveFrameCount() {
// Trying to invalidate the tab state while being destroyed could result in a
// use after free.
if (IsBeingDestroyed())
return;

// Notify for UI updates if the active frame count transitions from zero to
// non-zero.
hid_active_frame_count_++;
if (hid_active_frame_count_ == 1)
NotifyNavigationStateChanged(INVALIDATE_TYPE_TAB);
}

void WebContentsImpl::DecrementHidActiveFrameCount() {
// Trying to invalidate the tab state while being destroyed could result in a
// use after free.
if (IsBeingDestroyed())
return;

// Notify for UI updates if the active frame count transitions from non-zero
// to zero.
DCHECK_NE(0u, hid_active_frame_count_);
hid_active_frame_count_--;
if (hid_active_frame_count_ == 0)
NotifyNavigationStateChanged(INVALIDATE_TYPE_TAB);
}

void WebContentsImpl::IncrementNativeFileSystemHandleCount() {
// Trying to invalidate the tab state while being destroyed could result in a
// use after free.
Expand Down
7 changes: 7 additions & 0 deletions content/browser/web_contents/web_contents_impl.h
Original file line number Diff line number Diff line change
Expand Up @@ -358,6 +358,7 @@ class CONTENT_EXPORT WebContentsImpl : public WebContents,
bool IsCurrentlyAudible() override;
bool IsConnectedToBluetoothDevice() override;
bool IsConnectedToSerialPort() override;
bool IsConnectedToHidDevice() override;
bool HasNativeFileSystemHandles() override;
bool HasNativeFileSystemDirectoryHandles() override;
std::vector<base::FilePath> GetNativeFileSystemDirectoryHandles() override;
Expand Down Expand Up @@ -997,6 +998,11 @@ class CONTENT_EXPORT WebContentsImpl : public WebContents,
void IncrementSerialActiveFrameCount();
void DecrementSerialActiveFrameCount();

// Modify the counter of frames in this WebContents actively using HID
// devices.
void IncrementHidActiveFrameCount();
void DecrementHidActiveFrameCount();

// Modify the counter of native file system handles for this WebContents.
void IncrementNativeFileSystemHandleCount();
void DecrementNativeFileSystemHandleCount();
Expand Down Expand Up @@ -1811,6 +1817,7 @@ class CONTENT_EXPORT WebContentsImpl : public WebContents,

size_t bluetooth_connected_device_count_ = 0;
size_t serial_active_frame_count_ = 0;
size_t hid_active_frame_count_ = 0;

size_t native_file_system_handle_count_ = 0;
std::map<base::FilePath, size_t> native_file_system_directory_handles_;
Expand Down
4 changes: 4 additions & 0 deletions content/public/browser/web_contents.h
Original file line number Diff line number Diff line change
Expand Up @@ -508,6 +508,10 @@ class WebContents : public PageNavigator,
// port.
virtual bool IsConnectedToSerialPort() = 0;

// Indicates whether any frame in the WebContents is connected to a HID
// device.
virtual bool IsConnectedToHidDevice() = 0;

// Indicates whether any frame in the WebContents has native file system
// handles.
virtual bool HasNativeFileSystemHandles() = 0;
Expand Down

0 comments on commit e8c6c1f

Please sign in to comment.