Commit 1cf1a551 authored by Alex Cooper's avatar Alex Cooper Committed by Commit Bot

Add Permissions Request for WebXr Sessions

This adds code (behind a flag) to allow WebXr sessions to determine the
needed permissions and prompt the user for them on both Desktop and
Android.

In order to properly link to the PermissionManager and related classes,
VRServiceImpl and the classes that call into it needed to be moved into
the chrome/browser Build target.  Given that this target depends on
vr_common (where these other classes are), vr_common couldn't depend on
it, or else it would introduce a circular dependency. Therefore, these
files were moved into chrome/browser's target, which seemed to be common
for other classes that relied on PermissionManager.

vr_headset.png's generated from source SVG in crbug.com/912203 (which is
the source of the currently checked-in vr_headset.icon)

Note that strings are currently placeholders.

Showing the permission status in the page info bubble and site settings
will be done in future changes.

Bug: 1033592
Change-Id: I5be7f8b6e62c00fcb11ddeb6c2410720ec9c1bf0
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1992508
Commit-Queue: Alexander Cooper <alcooper@chromium.org>
Reviewed-by: default avatarYusuf Ozuysal <yusufo@chromium.org>
Reviewed-by: default avatarBalazs Engedy <engedy@chromium.org>
Reviewed-by: default avatarMounir Lamouri <mlamouri@chromium.org>
Reviewed-by: default avatarKlaus Weidner <klausw@chromium.org>
Cr-Commit-Position: refs/heads/master@{#731726}
parent c072c278
......@@ -9026,6 +9026,23 @@ Please help our engineers fix this problem. Tell us what happened right before y
</message>
</if>
<!-- WebXr permissions -->
<!-- TODO(crbug.com/1041009): Finalize WebXr Permissions strings -->
<message name="IDS_VR_PERMISSION_FRAGMENT" desc="Permission request shown if the user is visiting a site that wants to use VR. Follows a prompt: 'This site would like to:">
Access Virtual Reality devices and data
</message>
<message name="IDS_AR_PERMISSION_FRAGMENT" desc="Permission request shown if the user is visiting a site that wants to use AR. Follows a prompt: 'This site would like to:">
Track camera position and map your room
</message>
<if expr="is_android">
<message name="IDS_VR_INFOBAR_TEXT" desc="Text requesting permission for a site to use VR">
<ph name="URL">$1<ex>google.com</ex></ph> wants to be able to start virtual reality sessions
</message>
<message name="IDS_AR_INFOBAR_TEXT" desc="Text requesting permission for a site to use AR">
<ph name="URL">$1<ex>google.com</ex></ph> wants to track camera position and map your room
</message>
</if>
<!-- Sensor messages -->
<message name="IDS_SENSORS_ALLOWED_TOOLTIP" desc="Location bar icon tooltip text when a page is allowed to use device's sensors.">
This site is using motion or light sensors.
......
......@@ -5265,6 +5265,12 @@ jumbo_static_library("browser") {
sources += [
"component_updater/vr_assets_component_installer.cc",
"component_updater/vr_assets_component_installer.h",
"vr/service/browser_xr_runtime.cc",
"vr/service/browser_xr_runtime.h",
"vr/service/vr_service_impl.cc",
"vr/service/vr_service_impl.h",
"vr/service/xr_runtime_manager.cc",
"vr/service/xr_runtime_manager.h",
]
deps += [ "//chrome/browser/vr:vr_common" ]
......
......@@ -64,6 +64,7 @@ DECLARE_RESOURCE_ID(IDR_ANDROID_INFOBAR_SAVE_PASSWORD,
R.drawable.ic_vpn_key_blue)
DECLARE_RESOURCE_ID(IDR_ANDROID_INFOBAR_TRANSLATE, R.drawable.infobar_translate)
DECLARE_RESOURCE_ID(IDR_ANDROID_INFOBAR_WARNING, R.drawable.infobar_warning)
DECLARE_RESOURCE_ID(IDR_ANDROID_INFOBAR_VR_HEADSET, R.drawable.vr_headset)
DECLARE_RESOURCE_ID(IDR_ANDROID_INFOBAR_PHONE_ICON,
R.drawable.smartphone_black_24dp)
LINK_RESOURCE_ID(IDR_AUTOFILL_GOOGLE_PAY_WITH_DIVIDER,
......
......@@ -58,6 +58,9 @@ PermissionRequest::IconId PermissionRequestImpl::GetIconId() const {
return IDR_ANDROID_INFOBAR_CLIPBOARD;
case ContentSettingsType::NFC:
return IDR_ANDROID_INFOBAR_NFC;
case ContentSettingsType::VR:
case ContentSettingsType::AR:
return IDR_ANDROID_INFOBAR_VR_HEADSET;
default:
NOTREACHED();
return IDR_ANDROID_INFOBAR_WARNING;
......@@ -85,6 +88,9 @@ PermissionRequest::IconId PermissionRequestImpl::GetIconId() const {
return vector_icons::kAccessibilityIcon;
case ContentSettingsType::CLIPBOARD_READ_WRITE:
return kContentPasteIcon;
case ContentSettingsType::VR:
case ContentSettingsType::AR:
return kVrHeadsetIcon;
default:
NOTREACHED();
return kExtensionIcon;
......@@ -123,6 +129,12 @@ base::string16 PermissionRequestImpl::GetTitleText() const {
case ContentSettingsType::NFC:
message_id = IDS_NFC_PERMISSION_TITLE;
break;
// TODO(andypaicu): GetTitleText is no longer used, but we have to return
// something at present to avoid crashing. This both avoids the crash and
// avoids adding an unused string.
case ContentSettingsType::VR:
case ContentSettingsType::AR:
return base::string16();
default:
NOTREACHED();
return base::string16();
......@@ -163,6 +175,12 @@ base::string16 PermissionRequestImpl::GetMessageText() const {
case ContentSettingsType::NFC:
message_id = IDS_NFC_INFOBAR_TEXT;
break;
case ContentSettingsType::VR:
message_id = IDS_VR_INFOBAR_TEXT;
break;
case ContentSettingsType::AR:
message_id = IDS_AR_INFOBAR_TEXT;
break;
default:
NOTREACHED();
return base::string16();
......@@ -231,6 +249,12 @@ base::string16 PermissionRequestImpl::GetMessageTextFragment() const {
case ContentSettingsType::NFC:
message_id = IDS_NFC_PERMISSION_FRAGMENT;
break;
case ContentSettingsType::VR:
message_id = IDS_VR_PERMISSION_FRAGMENT;
break;
case ContentSettingsType::AR:
message_id = IDS_AR_PERMISSION_FRAGMENT;
break;
default:
NOTREACHED();
return base::string16();
......
......@@ -98,9 +98,9 @@ std::string GetPermissionRequestString(PermissionRequestType type) {
case PermissionRequestType::PERMISSION_CLIPBOARD_READ_WRITE:
return "ClipboardReadWrite";
case PermissionRequestType::PERMISSION_VR:
return "Vr";
return "VR";
case PermissionRequestType::PERMISSION_AR:
return "Ar";
return "AR";
default:
NOTREACHED();
return "";
......@@ -427,7 +427,7 @@ void PermissionUmaUtil::RecordPermissionAction(
bool secure_origin = content::IsOriginSecure(requesting_origin);
switch (permission) {
// Geolocation, MidiSysEx, Push, Media and Clipboard permissions are
// Geolocation, MidiSysEx, Push, Media, Clipboard, and AR/VR permissions are
// disabled on insecure origins, so there's no need to record separate
// metrics for secure/insecure.
case ContentSettingsType::GEOLOCATION:
......@@ -475,6 +475,14 @@ void PermissionUmaUtil::RecordPermissionAction(
base::UmaHistogramEnumeration("Permissions.Action.Nfc", action,
PermissionAction::NUM);
break;
case ContentSettingsType::VR:
base::UmaHistogramEnumeration("Permissions.Action.VR", action,
PermissionAction::NUM);
break;
case ContentSettingsType::AR:
base::UmaHistogramEnumeration("Permissions.Action.AR", action,
PermissionAction::NUM);
break;
// The user is not prompted for these permissions, thus there is no
// permission action recorded for them.
default:
......
......@@ -244,16 +244,10 @@ component("vr_common") {
"platform_controller.h",
"scheduler_browser_renderer_interface.h",
"scheduler_delegate.h",
"service/browser_xr_runtime.cc",
"service/browser_xr_runtime.h",
"service/gvr_consent_helper.cc",
"service/gvr_consent_helper.h",
"service/vr_service_impl.cc",
"service/vr_service_impl.h",
"service/xr_device_service.cc",
"service/xr_device_service.h",
"service/xr_runtime_manager.cc",
"service/xr_runtime_manager.h",
"service/xr_runtime_manager_observer.h",
"service/xr_session_request_consent_manager.cc",
"service/xr_session_request_consent_manager.h",
......@@ -516,15 +510,21 @@ test("vr_common_unittests") {
deps = [
":vr_test_support",
"//chrome/browser",
"//chrome/test:test_support",
"//components/url_formatter",
"//components/vector_icons",
"//content/test:test_support",
"//mojo/public/cpp/bindings",
"//services/network:test_support",
"//testing/gmock",
"//ui/gfx/geometry",
]
if (is_android) {
deps += [ "//ui/android:ui_java" ]
deps += [
"//chrome:chrome_android_core",
"//ui/android:ui_java",
]
}
}
......
......@@ -6,6 +6,7 @@
#define CHROME_BROWSER_VR_SERVICE_ISOLATED_DEVICE_PROVIDER_H_
#include "base/containers/flat_map.h"
#include "chrome/browser/vr/vr_export.h"
#include "device/vr/public/mojom/isolated_xr_service.mojom.h"
#include "device/vr/vr_device.h"
#include "device/vr/vr_device_provider.h"
......@@ -17,7 +18,7 @@ namespace vr {
class VRUiHost;
class IsolatedVRDeviceProvider
class VR_EXPORT IsolatedVRDeviceProvider
: public device::VRDeviceProvider,
public device::mojom::IsolatedXRRuntimeProviderClient {
public:
......
......@@ -8,15 +8,19 @@
#include "base/bind.h"
#include "base/command_line.h"
#include "base/feature_list.h"
#include "base/metrics/histogram_macros.h"
#include "base/trace_event/common/trace_event_common.h"
#include "build/build_config.h"
#include "chrome/browser/permissions/permission_manager.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/vr/metrics/session_metrics_helper.h"
#include "chrome/browser/vr/mode.h"
#include "chrome/browser/vr/service/browser_xr_runtime.h"
#include "chrome/browser/vr/service/xr_runtime_manager.h"
#include "chrome/common/chrome_switches.h"
#include "components/ukm/content/source_url_recorder.h"
#include "content/public/browser/browser_context.h"
#include "content/public/browser/render_frame_host.h"
#include "content/public/browser/render_process_host.h"
#include "content/public/browser/render_widget_host.h"
......@@ -85,6 +89,17 @@ vr::XrConsentPromptLevel GetRequiredConsentLevel(
return vr::XrConsentPromptLevel::kDefault;
}
ContentSettingsType GetRequiredPermission(device::mojom::XRSessionMode mode) {
switch (mode) {
case device::mojom::XRSessionMode::kInline:
return ContentSettingsType::SENSORS;
case device::mojom::XRSessionMode::kImmersiveVr:
return ContentSettingsType::VR;
case device::mojom::XRSessionMode::kImmersiveAr:
return ContentSettingsType::AR;
}
}
} // namespace
namespace vr {
......@@ -409,19 +424,41 @@ void VRServiceImpl::ShowConsentPrompt(
DCHECK_NE(options->mode, device::mojom::XRSessionMode::kImmersiveAr);
#endif
bool consent_granted = false;
XrConsentPromptLevel consent_level =
GetRequiredConsentLevel(options->mode, runtime, requested_features);
if (!base::FeatureList::IsEnabled(features::kWebXrPermissionsApi)) {
consent_granted =
((consent_level == XrConsentPromptLevel::kNone) ||
IsConsentGrantedForDevice(runtime->GetId(), consent_level));
}
// Skip the consent prompt if the user has already consented for this device,
// or if consent is not needed.
if (consent_level == XrConsentPromptLevel::kNone ||
IsConsentGrantedForDevice(runtime->GetId(), consent_level) ||
IsXrDeviceConsentPromptDisabledForTesting()) {
if (consent_granted || IsXrDeviceConsentPromptDisabledForTesting()) {
DoRequestSession(std::move(options), std::move(callback), runtime,
std::move(requested_features));
return;
}
if (base::FeatureList::IsEnabled(features::kWebXrPermissionsApi)) {
PermissionManager* permission_manager = PermissionManager::Get(
Profile::FromBrowserContext(GetWebContents()->GetBrowserContext()));
DCHECK(permission_manager);
// Need to calculate the permission before the call below, as otherwise
// std::move nulls options out before GetRequiredPermission runs.
ContentSettingsType permission = GetRequiredPermission(options->mode);
permission_manager->RequestPermission(
permission, render_frame_host_,
render_frame_host_->GetLastCommittedURL(), true,
base::BindOnce(&VRServiceImpl::OnPermissionResult,
weak_ptr_factory_.GetWeakPtr(), std::move(options),
std::move(callback), runtime->GetId(),
std::move(requested_features), consent_level));
return;
}
// TODO(crbug.com/968233): Unify the below consent flow.
#if defined(OS_ANDROID)
if (options->mode == device::mojom::XRSessionMode::kImmersiveAr) {
......@@ -462,6 +499,20 @@ void VRServiceImpl::ShowConsentPrompt(
NOTREACHED();
}
// TODO(alcooper): Once the ConsentFlow can be removed expected_runtime_id and
// consent_level shouldn't be needed.
void VRServiceImpl::OnPermissionResult(
device::mojom::XRSessionOptionsPtr options,
device::mojom::VRService::RequestSessionCallback callback,
device::mojom::XRDeviceId expected_runtime_id,
std::set<device::mojom::XRSessionFeature> enabled_features,
XrConsentPromptLevel consent_level,
ContentSetting setting_value) {
OnConsentResult(std::move(options), std::move(callback), expected_runtime_id,
std::move(enabled_features), consent_level,
setting_value == ContentSetting::CONTENT_SETTING_ALLOW);
}
void VRServiceImpl::OnConsentResult(
device::mojom::XRSessionOptionsPtr options,
device::mojom::VRService::RequestSessionCallback callback,
......
......@@ -15,7 +15,7 @@
#include "chrome/browser/vr/metrics/session_metrics_helper.h"
#include "chrome/browser/vr/service/xr_consent_prompt_level.h"
#include "chrome/browser/vr/vr_export.h"
#include "components/content_settings/core/common/content_settings.h"
#include "content/public/browser/web_contents_observer.h"
#include "device/vr/public/mojom/vr_service.mojom.h"
#include "device/vr/vr_device.h"
......@@ -38,8 +38,8 @@ class BrowserXRRuntime;
// Browser process implementation of the VRService mojo interface. Instantiated
// through Mojo once the user loads a page containing WebXR.
class VR_EXPORT VRServiceImpl : public device::mojom::VRService,
content::WebContentsObserver {
class VRServiceImpl : public device::mojom::VRService,
content::WebContentsObserver {
public:
static bool IsXrDeviceConsentPromptDisabledForTesting();
......@@ -139,6 +139,13 @@ class VR_EXPORT VRServiceImpl : public device::mojom::VRService,
std::set<device::mojom::XRSessionFeature> enabled_features,
XrConsentPromptLevel consent_level,
bool is_consent_granted);
void OnPermissionResult(
device::mojom::XRSessionOptionsPtr options,
device::mojom::VRService::RequestSessionCallback callback,
device::mojom::XRDeviceId expected_runtime_id,
std::set<device::mojom::XRSessionFeature> enabled_features,
XrConsentPromptLevel consent_level,
ContentSetting setting_value);
bool IsConsentGrantedForDevice(device::mojom::XRDeviceId device_id,
XrConsentPromptLevel consent_level);
......
......@@ -20,7 +20,6 @@
#include "base/timer/timer.h"
#include "chrome/browser/vr/service/vr_service_impl.h"
#include "chrome/browser/vr/service/xr_runtime_manager_observer.h"
#include "chrome/browser/vr/vr_export.h"
#include "device/vr/public/mojom/vr_service.mojom.h"
#include "device/vr/vr_device.h"
#include "mojo/public/cpp/bindings/pending_remote.h"
......@@ -36,7 +35,7 @@ class XRRuntimeManagerTest;
// Singleton used to provide the platform's XR Runtimes to VRServiceImpl
// instances.
class VR_EXPORT XRRuntimeManager : public base::RefCounted<XRRuntimeManager> {
class XRRuntimeManager : public base::RefCounted<XRRuntimeManager> {
public:
friend base::RefCounted<XRRuntimeManager>;
static constexpr auto kRefCountPreference =
......
......@@ -20,25 +20,6 @@ WebXrPermissionContext::WebXrPermissionContext(
WebXrPermissionContext::~WebXrPermissionContext() = default;
ContentSetting WebXrPermissionContext::GetPermissionStatusInternal(
content::RenderFrameHost* render_frame_host,
const GURL& requesting_origin,
const GURL& embedding_origin) const {
return CONTENT_SETTING_BLOCK;
}
void WebXrPermissionContext::DecidePermission(
content::WebContents* web_contents,
const PermissionRequestID& id,
const GURL& requesting_origin,
const GURL& embedding_origin,
bool user_gesture,
BrowserPermissionCallback callback) {
// The user shouldn't be prompted to authorize AR/VR while it's being
// implemented.
NOTREACHED();
}
bool WebXrPermissionContext::IsRestrictedToSecureOrigins() const {
return true;
}
......@@ -18,16 +18,6 @@ class WebXrPermissionContext : public PermissionContextBase {
private:
// PermissionContextBase:
ContentSetting GetPermissionStatusInternal(
content::RenderFrameHost* render_frame_host,
const GURL& requesting_origin,
const GURL& embedding_origin) const override;
void DecidePermission(content::WebContents* web_contents,
const PermissionRequestID& id,
const GURL& requesting_origin,
const GURL& embedding_origin,
bool user_gesture,
BrowserPermissionCallback callback) override;
bool IsRestrictedToSecureOrigins() const override;
ContentSettingsType content_settings_type_;
......
......@@ -11,7 +11,6 @@
#include "chrome/browser/vr/browser_renderer.h"
#include "chrome/browser/vr/model/capturing_state_model.h"
#include "chrome/browser/vr/model/web_vr_model.h"
#include "chrome/browser/vr/service/browser_xr_runtime.h"
#include "chrome/browser/vr/vr_export.h"
#include "content/public/browser/web_contents.h"
#include "device/vr/public/mojom/isolated_xr_service.mojom.h"
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment