WebView: add hid permission request
This CL adds hid permission to WebView to enable WebViews sending
permission requests for HID to the embedding Chrome App.
Design doc: http://goto.google.com/webhid-in-webview
BUG=b:281855555
Change-Id: I555542c2191e558d14083efd77320b9dafc9e130
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/5136796
Commit-Queue: Viktoriia Kovalova <vkovalova@google.com>
Reviewed-by: Juliet Lévesque <julietlevesque@google.com>
Reviewed-by: Kevin McNee <mcnee@chromium.org>
Code-Coverage: findit-for-me@appspot.gserviceaccount.com <findit-for-me@appspot.gserviceaccount.com>
Reviewed-by: Finnur Thorarinsson <finnur@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1246432}
diff --git a/chrome/browser/apps/guest_view/web_view_browsertest.cc b/chrome/browser/apps/guest_view/web_view_browsertest.cc
index 4c3248c..506bfab5 100644
--- a/chrome/browser/apps/guest_view/web_view_browsertest.cc
+++ b/chrome/browser/apps/guest_view/web_view_browsertest.cc
@@ -22,6 +22,7 @@
#include "base/test/bind.h"
#include "base/test/run_until.h"
#include "base/test/scoped_feature_list.h"
+#include "base/test/test_future.h"
#include "base/test/test_mock_time_task_runner.h"
#include "base/test/test_timeouts.h"
#include "base/threading/thread_restrictions.h"
@@ -3151,6 +3152,56 @@
NEEDS_TEST_SERVER);
}
+class WebHidWebViewTest : public WebViewTest {
+ public:
+ WebHidWebViewTest() {
+ scoped_feature_list_.InitAndEnableFeature(
+ extensions_features::kEnableWebHidInWebView);
+ }
+
+ private:
+ base::test::ScopedFeatureList scoped_feature_list_;
+};
+
+IN_PROC_BROWSER_TEST_F(WebHidWebViewTest,
+ PermissionsAPIEmbedderHasAccessAllowHid) {
+ TestHelper("testAllowHid",
+ "web_view/permissions_test/embedder_has_permission",
+ NEEDS_TEST_SERVER);
+
+ auto* guest = GetGuestViewManager()->GetLastGuestViewCreated();
+ auto* web_view = extensions::WebViewGuest::FromGuestViewBase(guest);
+ ASSERT_TRUE(web_view);
+
+ base::test::TestFuture<bool> allowed_future;
+ // TODO(b/281855555): Replace this C++ call with an actual call to WebHID from
+ // webview script, when WebHID support is fully implemented.
+ web_view->web_view_permission_helper()->RequestHidPermission(
+ guest->GetGuestMainFrame()->GetLastCommittedURL(),
+ allowed_future.GetCallback());
+
+ ASSERT_TRUE(allowed_future.Take());
+}
+
+IN_PROC_BROWSER_TEST_F(WebHidWebViewTest,
+ PermissionsAPIEmbedderHasAccessDenyHid) {
+ TestHelper("testDenyHid", "web_view/permissions_test/embedder_has_permission",
+ NEEDS_TEST_SERVER);
+
+ auto* guest = GetGuestViewManager()->GetLastGuestViewCreated();
+ auto* web_view = extensions::WebViewGuest::FromGuestViewBase(guest);
+ ASSERT_TRUE(web_view);
+
+ base::test::TestFuture<bool> allowed_future;
+ // TODO(b/281855555): Replace this C++ call with an actual call to WebHID from
+ // webview script, when WebHID support is fully implemented.
+ web_view->web_view_permission_helper()->RequestHidPermission(
+ guest->GetGuestMainFrame()->GetLastCommittedURL(),
+ allowed_future.GetCallback());
+
+ ASSERT_FALSE(allowed_future.Take());
+}
+
IN_PROC_BROWSER_TEST_F(WebViewTest,
PermissionsAPIEmbedderHasNoAccessAllowGeolocation) {
TestHelper("testAllowGeolocation",
diff --git a/chrome/browser/guest_view/web_view/chrome_web_view_permission_helper_delegate.cc b/chrome/browser/guest_view/web_view/chrome_web_view_permission_helper_delegate.cc
index 2870a0a..12890d2 100644
--- a/chrome/browser/guest_view/web_view/chrome_web_view_permission_helper_delegate.cc
+++ b/chrome/browser/guest_view/web_view/chrome_web_view_permission_helper_delegate.cc
@@ -194,6 +194,28 @@
std::move(callback));
}
+void ChromeWebViewPermissionHelperDelegate::RequestHidPermission(
+ const GURL& requesting_frame_url,
+ base::OnceCallback<void(bool)> callback) {
+ auto request_info =
+ base::Value::Dict().Set(guest_view::kUrl, requesting_frame_url.spec());
+
+ WebViewPermissionHelper::PermissionResponseCallback permission_callback =
+ base::BindOnce(
+ &ChromeWebViewPermissionHelperDelegate::OnHidPermissionResponse,
+ weak_factory_.GetWeakPtr(), std::move(callback));
+ web_view_permission_helper()->RequestPermission(
+ WEB_VIEW_PERMISSION_TYPE_HID, std::move(request_info),
+ std::move(permission_callback), false /* allowed_by_default */);
+}
+
+void ChromeWebViewPermissionHelperDelegate::OnHidPermissionResponse(
+ base::OnceCallback<void(bool)> callback,
+ bool allow,
+ const std::string& user_input) {
+ std::move(callback).Run(allow && web_view_guest()->attached());
+}
+
void ChromeWebViewPermissionHelperDelegate::RequestFileSystemPermission(
const GURL& url,
bool allowed_by_default,
diff --git a/chrome/browser/guest_view/web_view/chrome_web_view_permission_helper_delegate.h b/chrome/browser/guest_view/web_view/chrome_web_view_permission_helper_delegate.h
index 30f968b..5db1cd5 100644
--- a/chrome/browser/guest_view/web_view/chrome_web_view_permission_helper_delegate.h
+++ b/chrome/browser/guest_view/web_view/chrome_web_view_permission_helper_delegate.h
@@ -55,6 +55,8 @@
const GURL& requesting_frame,
bool user_gesture,
base::OnceCallback<void(bool)> callback) override;
+ void RequestHidPermission(const GURL& requesting_frame_url,
+ base::OnceCallback<void(bool)> callback) override;
void RequestFileSystemPermission(
const GURL& url,
bool allowed_by_default,
@@ -80,6 +82,10 @@
bool allow,
const std::string& user_input);
+ void OnHidPermissionResponse(base::OnceCallback<void(bool)> callback,
+ bool allow,
+ const std::string& user_input);
+
void OnFileSystemPermissionResponse(base::OnceCallback<void(bool)> callback,
bool allow,
const std::string& user_input);
diff --git a/chrome/test/data/extensions/platform_apps/web_view/permissions_test/embedder_has_permission/embedder.js b/chrome/test/data/extensions/platform_apps/web_view/permissions_test/embedder_has_permission/embedder.js
index b13d3cca..7a4aedb 100644
--- a/chrome/test/data/extensions/platform_apps/web_view/permissions_test/embedder_has_permission/embedder.js
+++ b/chrome/test/data/extensions/platform_apps/web_view/permissions_test/embedder_has_permission/embedder.js
@@ -235,6 +235,32 @@
embedder.registerAndWaitForPostMessage_('testMedia', 'access-denied');
}
+function testAllowHid() {
+ const webview = embedder.setUpGuest_();
+ const onPermissionRequest = function(e) {
+ e.request.allow();
+ };
+ webview.addEventListener('permissionrequest', onPermissionRequest);
+
+ const onWebViewLoadStop = function(e) {
+ embedder.test.succeed();
+ }
+ webview.addEventListener('loadstop', onWebViewLoadStop);
+}
+
+function testDenyHid() {
+ var webview = embedder.setUpGuest_();
+ var onPermissionRequest = function(e) {
+ e.request.deny();
+ };
+ webview.addEventListener('permissionrequest', onPermissionRequest);
+
+ const onWebViewLoadStop = function(e) {
+ embedder.test.succeed();
+ }
+ webview.addEventListener('loadstop', onWebViewLoadStop);
+}
+
embedder.test.testList = {
'testAllowGeolocation': testAllowGeolocation,
'testDenyGeolocation': testDenyGeolocation,
@@ -243,7 +269,9 @@
'testAllowMicrophone': testAllowMicrophone,
'testDenyMicrophone': testDenyMicrophone,
'testAllowMedia': testAllowMedia,
- 'testDenyMedia': testDenyMedia
+ 'testDenyMedia': testDenyMedia,
+ 'testAllowHid': testAllowHid,
+ 'testDenyHid': testDenyHid,
};
onload = function() {
diff --git a/extensions/browser/guest_view/web_view/web_view_constants.cc b/extensions/browser/guest_view/web_view/web_view_constants.cc
index 81d1335..82bf861 100644
--- a/extensions/browser/guest_view/web_view/web_view_constants.cc
+++ b/extensions/browser/guest_view/web_view/web_view_constants.cc
@@ -59,6 +59,7 @@
const char kPermissionTypeFileSystem[] = "filesystem";
const char kPermissionTypeFullscreen[] = "fullscreen";
const char kPermissionTypeGeolocation[] = "geolocation";
+const char kPermissionTypeHid[] = "hid";
const char kPermissionTypeLoadPlugin[] = "loadplugin";
const char kPermissionTypeMedia[] = "media";
const char kPermissionTypeNewWindow[] = "newwindow";
diff --git a/extensions/browser/guest_view/web_view/web_view_constants.h b/extensions/browser/guest_view/web_view/web_view_constants.h
index a6d77b6..92c442c1 100644
--- a/extensions/browser/guest_view/web_view/web_view_constants.h
+++ b/extensions/browser/guest_view/web_view/web_view_constants.h
@@ -64,6 +64,7 @@
extern const char kPermissionTypeFileSystem[];
extern const char kPermissionTypeFullscreen[];
extern const char kPermissionTypeGeolocation[];
+extern const char kPermissionTypeHid[];
extern const char kPermissionTypeLoadPlugin[];
extern const char kPermissionTypeMedia[];
extern const char kPermissionTypeNewWindow[];
diff --git a/extensions/browser/guest_view/web_view/web_view_permission_helper.cc b/extensions/browser/guest_view/web_view/web_view_permission_helper.cc
index 9a9a5017..bf7ca86 100644
--- a/extensions/browser/guest_view/web_view/web_view_permission_helper.cc
+++ b/extensions/browser/guest_view/web_view/web_view_permission_helper.cc
@@ -6,9 +6,11 @@
#include <utility>
+#include "base/feature_list.h"
#include "base/functional/bind.h"
#include "base/location.h"
#include "base/metrics/user_metrics.h"
+#include "base/metrics/user_metrics_action.h"
#include "base/task/single_thread_task_runner.h"
#include "base/values.h"
#include "components/guest_view/browser/guest_view_event.h"
@@ -18,6 +20,7 @@
#include "extensions/browser/guest_view/web_view/web_view_guest.h"
#include "extensions/browser/guest_view/web_view/web_view_permission_helper_delegate.h"
#include "extensions/browser/guest_view/web_view/web_view_permission_types.h"
+#include "extensions/common/extension_features.h"
#include "ppapi/buildflags/buildflags.h"
#include "third_party/blink/public/mojom/mediastream/media_stream.mojom-shared.h"
#include "third_party/blink/public/mojom/mediastream/media_stream.mojom.h"
@@ -39,6 +42,8 @@
return webview::kPermissionTypeFullscreen;
case WEB_VIEW_PERMISSION_TYPE_GEOLOCATION:
return webview::kPermissionTypeGeolocation;
+ case WEB_VIEW_PERMISSION_TYPE_HID:
+ return webview::kPermissionTypeHid;
case WEB_VIEW_PERMISSION_TYPE_JAVASCRIPT_DIALOG:
return webview::kPermissionTypeDialog;
case WEB_VIEW_PERMISSION_TYPE_LOAD_PLUGIN:
@@ -81,6 +86,9 @@
base::RecordAction(
UserMetricsAction("WebView.PermissionAllow.Geolocation"));
break;
+ case WEB_VIEW_PERMISSION_TYPE_HID:
+ base::RecordAction(UserMetricsAction("WebView.PermissionAllow.HID"));
+ break;
case WEB_VIEW_PERMISSION_TYPE_JAVASCRIPT_DIALOG:
base::RecordAction(
UserMetricsAction("WebView.PermissionAllow.JSDialog"));
@@ -121,6 +129,9 @@
base::RecordAction(
UserMetricsAction("WebView.PermissionDeny.Geolocation"));
break;
+ case WEB_VIEW_PERMISSION_TYPE_HID:
+ base::RecordAction(UserMetricsAction("WebView.PermissionDeny.HID"));
+ break;
case WEB_VIEW_PERMISSION_TYPE_JAVASCRIPT_DIALOG:
base::RecordAction(
UserMetricsAction("WebView.PermissionDeny.JSDialog"));
@@ -252,11 +263,24 @@
}
void WebViewPermissionHelper::RequestGeolocationPermission(
- const GURL& requesting_frame,
+ const GURL& requesting_frame_url,
bool user_gesture,
base::OnceCallback<void(bool)> callback) {
web_view_permission_helper_delegate_->RequestGeolocationPermission(
- requesting_frame, user_gesture, std::move(callback));
+ requesting_frame_url, user_gesture, std::move(callback));
+}
+
+void WebViewPermissionHelper::RequestHidPermission(
+ const GURL& requesting_frame_url,
+ base::OnceCallback<void(bool)> callback) {
+ if (!base::FeatureList::IsEnabled(
+ extensions_features::kEnableWebHidInWebView)) {
+ std::move(callback).Run(false);
+ return;
+ }
+
+ web_view_permission_helper_delegate_->RequestHidPermission(
+ requesting_frame_url, std::move(callback));
}
void WebViewPermissionHelper::RequestFileSystemPermission(
diff --git a/extensions/browser/guest_view/web_view/web_view_permission_helper.h b/extensions/browser/guest_view/web_view/web_view_permission_helper.h
index e5f02cf..958364a6 100644
--- a/extensions/browser/guest_view/web_view/web_view_permission_helper.h
+++ b/extensions/browser/guest_view/web_view/web_view_permission_helper.h
@@ -81,6 +81,10 @@
void RequestGeolocationPermission(const GURL& requesting_frame,
bool user_gesture,
base::OnceCallback<void(bool)> callback);
+ // Requests permission from the embedder to request access to Human
+ // Interface Devices.
+ void RequestHidPermission(const GURL& requesting_frame,
+ base::OnceCallback<void(bool)> callback);
void RequestFileSystemPermission(const GURL& url,
bool allowed_by_default,
diff --git a/extensions/browser/guest_view/web_view/web_view_permission_helper_delegate.h b/extensions/browser/guest_view/web_view/web_view_permission_helper_delegate.h
index ac42ac6f..31d15cc 100644
--- a/extensions/browser/guest_view/web_view/web_view_permission_helper_delegate.h
+++ b/extensions/browser/guest_view/web_view/web_view_permission_helper_delegate.h
@@ -37,10 +37,13 @@
// Requests Geolocation Permission from the embedder.
virtual void RequestGeolocationPermission(
- const GURL& requesting_frame,
+ const GURL& requesting_frame_url,
bool user_gesture,
base::OnceCallback<void(bool)> callback) {}
+ virtual void RequestHidPermission(const GURL& requesting_frame_url,
+ base::OnceCallback<void(bool)> callback) {}
+
virtual void RequestFileSystemPermission(
const GURL& url,
bool allowed_by_default,
diff --git a/extensions/browser/guest_view/web_view/web_view_permission_types.h b/extensions/browser/guest_view/web_view/web_view_permission_types.h
index d3e01418b..e121c7a 100644
--- a/extensions/browser/guest_view/web_view/web_view_permission_types.h
+++ b/extensions/browser/guest_view/web_view/web_view_permission_types.h
@@ -17,6 +17,8 @@
WEB_VIEW_PERMISSION_TYPE_FULLSCREEN,
WEB_VIEW_PERMISSION_TYPE_GEOLOCATION,
+ // Permission to request access to Human Interface Devices.
+ WEB_VIEW_PERMISSION_TYPE_HID,
// JavaScript Dialogs: prompt, alert, confirm
// Note: Even through dialogs do not use the permission API, the dialog API
diff --git a/extensions/common/extension_features.cc b/extensions/common/extension_features.cc
index 4908555..8efd82be 100644
--- a/extensions/common/extension_features.cc
+++ b/extensions/common/extension_features.cc
@@ -57,6 +57,10 @@
"EMF_NO_EXTENSION_ID_FOR_EXTENSION_SOURCE",
base::FEATURE_ENABLED_BY_DEFAULT);
+BASE_FEATURE(kEnableWebHidInWebView,
+ "EnableWebHidInWebView",
+ base::FEATURE_DISABLED_BY_DEFAULT);
+
BASE_FEATURE(kExtensionDynamicURLRedirection,
"ExtensionDynamicURLRedirection",
base::FEATURE_DISABLED_BY_DEFAULT);
diff --git a/extensions/common/extension_features.h b/extensions/common/extension_features.h
index a5438d9b..72ba75af 100644
--- a/extensions/common/extension_features.h
+++ b/extensions/common/extension_features.h
@@ -79,6 +79,10 @@
// extension).
BASE_DECLARE_FEATURE(kCheckingNoExtensionIdInExtensionIpcs);
+// If enabled, <webview>s will be allowed to request permission from an
+// embedding Chrome App to request access to Human Interface Devices.
+BASE_DECLARE_FEATURE(kEnableWebHidInWebView);
+
// Determine if dynamic extension URLs are handled and redirected.
BASE_DECLARE_FEATURE(kExtensionDynamicURLRedirection);
diff --git a/extensions/renderer/resources/guest_view/web_view/web_view_action_requests.js b/extensions/renderer/resources/guest_view/web_view/web_view_action_requests.js
index 99ac9e1..b7e549b 100644
--- a/extensions/renderer/resources/guest_view/web_view/web_view_action_requests.js
+++ b/extensions/renderer/resources/guest_view/web_view/web_view_action_requests.js
@@ -16,7 +16,11 @@
'download',
'loadplugin',
'filesystem',
- 'fullscreen'];
+ 'fullscreen',
+ // TODO(b/319100930): update the the documentation in
+ // chrome/common/extensions/api/webview_tag.json once
+ // the feature launches.
+ 'hid'];
// The browser will kill us if we send it a bad instance ID.
// TODO(780728): Remove once the cause of the bad ID is known.
diff --git a/tools/metrics/actions/actions.xml b/tools/metrics/actions/actions.xml
index 0f26cba..8e7ccbc 100644
--- a/tools/metrics/actions/actions.xml
+++ b/tools/metrics/actions/actions.xml
@@ -40126,6 +40126,14 @@
</description>
</action>
+<action name="WebView.PermissionAllow.HID">
+ <owner>vkovalova@google.com</owner>
+ <description>
+ Tracks when showing the HID selection dialog is explicitly allowed on a
+ webview.
+ </description>
+</action>
+
<action name="WebView.PermissionAllow.JSDialog">
<owner>fsamuel@chromium.org</owner>
<owner>lazyboy@chromium.org</owner>
@@ -40182,6 +40190,14 @@
</description>
</action>
+<action name="WebView.PermissionDeny.HID">
+ <owner>vkovalova@google.com</owner>
+ <description>
+ Tracks when showing the HID selection dialog is explicitly denied on a
+ webview.
+ </description>
+</action>
+
<action name="WebView.PermissionDeny.JSDialog">
<owner>fsamuel@chromium.org</owner>
<owner>lazyboy@chromium.org</owner>