Commit 0055cd2a authored by Suman Kancherla's avatar Suman Kancherla Committed by Commit Bot

[ui/xr] Added consent dialog before creating XRSession

This change displays a tab modal dialog seeking the user's consent before
proceeding with an XRSession creation when a website calls requestSession.
If the user allows, execution continues as before but if they don't,
the promise is rejected.

Bug: 958033
Change-Id: I198f91d8dd6a9c1a48c69501e264ab897f0b1322
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1603205
Commit-Queue: Suman Kancherla <sumankancherla@chromium.org>
Reviewed-by: default avatarLei Zhang <thestig@chromium.org>
Reviewed-by: default avatarEmily Stark <estark@chromium.org>
Reviewed-by: default avatarBill Orr <billorr@chromium.org>
Reviewed-by: default avatarMichael Wasserman <msw@chromium.org>
Cr-Commit-Position: refs/heads/master@{#659327}
parent 8d2cfc84
......@@ -10,6 +10,25 @@
XR Isolated Device Service
</message>
<!-- XR consent dialog. -->
<message name="IDS_XR_CONSENT_DIALOG_TITLE" desc="Title of the user consent dialog displayed before allowing a website to start a VR presentation">
Allow this site to access your VR sensors?
</message>
<message name="IDS_XR_CONSENT_DIALOG_DESCRIPTION" desc="Body of the user consent dialog displayed before allowing a website to start a VR presentation">
Sensor data will only be shared while you're in this VR experience. The site may be able to recognize you using certain info, such as:
- Your location
- Your physical features, like eye position
- Your movements, like how you walk
Make sure you trust this site before you allow access.
</message>
<message name="IDS_XR_CONSENT_DIALOG_BUTTON_DENY_VR" desc="Text on the button of a user consent dialog which denies a website from starting a VR presentation">
Don&#39;t allow
</message>
<message name="IDS_XR_CONSENT_DIALOG_BUTTON_ALLOW_AND_ENTER_VR" desc="Text on the button of a user consent dialog which allows a website to start a VR presentation">
Allow &amp; Enter VR
</message>
<!-- Powerful feature use and permission indicators. -->
<message name="IDS_VR_SHELL_SITE_IS_TRACKING_LOCATION" desc="Text displayed in a transient bubble to inform the user that the page is tracking location.">
Site is tracking your location
......
......@@ -4889,6 +4889,8 @@ jumbo_split_static_library("browser") {
# window shows alongside the head-mounted display is only supported on
# Windows, and that's the only situation where we need the UI host.
sources += [
"vr/service/xr_session_request_consent_manager_impl.cc",
"vr/service/xr_session_request_consent_manager_impl.h",
"vr/ui_host/vr_ui_host_impl.cc",
"vr/ui_host/vr_ui_host_impl.h",
]
......
......@@ -312,6 +312,7 @@
#include "chrome/browser/component_updater/vr_assets_component_installer.h"
#include "chrome/browser/vr/service/vr_service_impl.h"
#if defined(OS_WIN)
#include "chrome/browser/vr/service/xr_session_request_consent_manager_impl.h"
#include "chrome/browser/vr/ui_host/vr_ui_host_impl.h"
#endif
#endif
......@@ -1083,6 +1084,8 @@ int ChromeBrowserMainParts::PreCreateThreadsImpl() {
#if defined(OS_WIN)
vr::VRUiHost::SetFactory(&vr::VRUiHostImpl::Create);
vr::XRSessionRequestConsentManager::SetInstance(
new vr::XRSessionRequestConsentManagerImpl());
#endif // defined(OS_WIN)
#endif // BUILDFLAG(ENABLE_VR)
......
......@@ -17,6 +17,7 @@ import("//components/feed/features.gni")
import("//components/nacl/features.gni")
import("//components/offline_pages/buildflags/features.gni")
import("//components/signin/features.gni")
import("//device/vr/buildflags/buildflags.gni")
import("//extensions/buildflags/buildflags.gni")
import("//ppapi/buildflags/buildflags.gni")
import("//printing/buildflags/buildflags.gni")
......@@ -339,6 +340,15 @@ jumbo_split_static_library("ui") {
"webui/webui_util.h",
]
if (enable_vr) {
if (is_win) {
sources += [
"xr/xr_session_request_consent_dialog_delegate.cc",
"xr/xr_session_request_consent_dialog_delegate.h",
]
}
}
if (safe_browsing_mode == 1) {
sources += [
"webui/reset_password/reset_password_ui.cc",
......
file://chrome/browser/vr/OWNERS
# TEAM: xr-dev@chromium.org
# COMPONENT: UI>Browser>VR
// Copyright 2019 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/browser/ui/xr/xr_session_request_consent_dialog_delegate.h"
#include <utility>
#include "chrome/browser/ui/tab_modal_confirm_dialog.h"
#include "chrome/grit/generated_resources.h"
#include "ui/base/l10n/l10n_util.h"
namespace vr {
XrSessionRequestConsentDialogDelegate::XrSessionRequestConsentDialogDelegate(
content::WebContents* web_contents,
base::OnceCallback<void(bool)> response_callback)
: TabModalConfirmDialogDelegate(web_contents),
response_callback_(std::move(response_callback)) {}
XrSessionRequestConsentDialogDelegate::
~XrSessionRequestConsentDialogDelegate() = default;
base::string16 XrSessionRequestConsentDialogDelegate::GetTitle() {
return l10n_util::GetStringUTF16(IDS_XR_CONSENT_DIALOG_TITLE);
}
base::string16 XrSessionRequestConsentDialogDelegate::GetDialogMessage() {
return l10n_util::GetStringUTF16(IDS_XR_CONSENT_DIALOG_DESCRIPTION);
}
base::string16 XrSessionRequestConsentDialogDelegate::GetAcceptButtonTitle() {
return l10n_util::GetStringUTF16(
IDS_XR_CONSENT_DIALOG_BUTTON_ALLOW_AND_ENTER_VR);
}
base::string16 XrSessionRequestConsentDialogDelegate::GetCancelButtonTitle() {
return l10n_util::GetStringUTF16(IDS_XR_CONSENT_DIALOG_BUTTON_DENY_VR);
}
void XrSessionRequestConsentDialogDelegate::OnAccepted() {
std::move(response_callback_).Run(true);
}
void XrSessionRequestConsentDialogDelegate::OnCanceled() {
std::move(response_callback_).Run(false);
}
void XrSessionRequestConsentDialogDelegate::OnClosed() {
std::move(response_callback_).Run(false);
}
} // namespace vr
// Copyright 2019 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef CHROME_BROWSER_UI_XR_XR_SESSION_REQUEST_CONSENT_DIALOG_DELEGATE_H_
#define CHROME_BROWSER_UI_XR_XR_SESSION_REQUEST_CONSENT_DIALOG_DELEGATE_H_
#include "base/callback.h"
#include "chrome/browser/ui/tab_modal_confirm_dialog_delegate.h"
namespace content {
class WebContents;
}
namespace vr {
// Delegate to return appropriate strings for UI elements, and handle user's
// responses from a TabModalConfirmDialog.
class XrSessionRequestConsentDialogDelegate
: public TabModalConfirmDialogDelegate {
public:
XrSessionRequestConsentDialogDelegate(
content::WebContents* web_contents,
base::OnceCallback<void(bool)> response_callback);
~XrSessionRequestConsentDialogDelegate() override;
// TabModalConfirmDialogDelegate:
base::string16 GetTitle() override;
base::string16 GetDialogMessage() override;
base::string16 GetAcceptButtonTitle() override;
base::string16 GetCancelButtonTitle() override;
private:
// TabModalConfirmDialogDelegate:
void OnAccepted() override;
void OnCanceled() override;
void OnClosed() override;
base::OnceCallback<void(bool)> response_callback_;
DISALLOW_COPY_AND_ASSIGN(XrSessionRequestConsentDialogDelegate);
};
} // namespace vr
#endif // CHROME_BROWSER_UI_XR_XR_SESSION_REQUEST_CONSENT_DIALOG_DELEGATE_H_
......@@ -237,6 +237,8 @@ component("vr_common") {
"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",
"sounds_manager_audio_delegate.cc",
"sounds_manager_audio_delegate.h",
"vr_export.h",
......@@ -669,6 +671,8 @@ if (!is_android) {
"test/conditional_skipping.h",
"test/mock_xr_device_hook_base.cc",
"test/mock_xr_device_hook_base.h",
"test/mock_xr_session_request_consent_manager.cc",
"test/mock_xr_session_request_consent_manager.h",
"test/webvr_browser_test.cc",
"test/webvr_browser_test.h",
"test/webxr_browser_test.cc",
......
......@@ -4,9 +4,12 @@
#include "chrome/browser/vr/service/xr_device_impl.h"
#include <utility>
#include "base/bind.h"
#include "base/command_line.h"
#include "base/trace_event/common/trace_event_common.h"
#include "build/build_config.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"
......@@ -21,6 +24,10 @@
#include "content/public/common/origin_util.h"
#include "device/vr/vr_display_impl.h"
#if defined(OS_WIN)
#include "chrome/browser/vr/service/xr_session_request_consent_manager.h"
#endif
namespace vr {
namespace {
......@@ -125,6 +132,40 @@ void XRDeviceImpl::RequestSession(
return;
}
#if defined(OS_WIN)
if (!options->environment_integration && // disable consent dialog for AR
options->immersive) {
// Present a consent dialog.
XRSessionRequestConsentManager::Instance()->ShowDialogAndGetConsent(
GetWebContents(),
base::BindOnce(&XRDeviceImpl::OnUserConsent,
weak_ptr_factory_.GetWeakPtr(), std::move(options),
triggered_by_displayactive, std::move(callback)));
return;
}
#endif
OnUserConsent(std::move(options), triggered_by_displayactive,
std::move(callback), true);
}
void XRDeviceImpl::OnUserConsent(
device::mojom::XRSessionOptionsPtr options,
bool triggered_by_displayactive,
device::mojom::XRDevice::RequestSessionCallback callback,
bool allowed) {
if (!allowed) {
std::move(callback).Run(nullptr);
return;
}
// Re-check for another device instance after a potential user consent.
if (XRRuntimeManager::GetInstance()->IsOtherDevicePresenting(this)) {
// Can't create sessions while an immersive session exists.
std::move(callback).Run(nullptr);
return;
}
// Get the runtime we'll be creating a session with.
BrowserXRRuntime* runtime =
XRRuntimeManager::GetInstance()->GetRuntimeForOptions(options.get());
......
......@@ -83,6 +83,10 @@ class XRDeviceImpl : public device::mojom::XRDevice {
void OnSessionCreated(
device::mojom::XRDevice::RequestSessionCallback callback,
device::mojom::XRSessionPtr session);
void OnUserConsent(device::mojom::XRSessionOptionsPtr options,
bool triggered_by_displayactive,
device::mojom::XRDevice::RequestSessionCallback callback,
bool allowed);
// TODO(https://crbug.com/837538): Instead, check before returning this
// object.
......
// Copyright 2019 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/browser/vr/service/xr_session_request_consent_manager.h"
namespace vr {
namespace {
XRSessionRequestConsentManager* g_consent_manager = nullptr;
XRSessionRequestConsentManager* g_consent_manager_for_testing = nullptr;
} // namespace
XRSessionRequestConsentManager* XRSessionRequestConsentManager::Instance() {
DCHECK(g_consent_manager || g_consent_manager_for_testing);
if (g_consent_manager_for_testing)
return g_consent_manager_for_testing;
return g_consent_manager;
}
void XRSessionRequestConsentManager::SetInstance(
XRSessionRequestConsentManager* instance) {
DCHECK(instance);
DCHECK(!g_consent_manager);
g_consent_manager = instance;
}
void XRSessionRequestConsentManager::SetInstanceForTesting(
XRSessionRequestConsentManager* instance) {
g_consent_manager_for_testing = instance;
}
XRSessionRequestConsentManager::XRSessionRequestConsentManager() = default;
XRSessionRequestConsentManager::~XRSessionRequestConsentManager() = default;
} // namespace vr
// Copyright 2019 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef CHROME_BROWSER_VR_SERVICE_XR_SESSION_REQUEST_CONSENT_MANAGER_H_
#define CHROME_BROWSER_VR_SERVICE_XR_SESSION_REQUEST_CONSENT_MANAGER_H_
#include "base/callback.h"
#include "chrome/browser/vr/vr_export.h"
namespace content {
class WebContents;
}
namespace vr {
// Abstract class to break a dependency loop between the "vr_common" component
// when accessing "chrome/browser/ui" component functionality such as
// the TabModalConfirmDialogDelegate. A concrete
// XRSessionRequestConsentManagerImpl object is injected through
// SetInstance() in chrome_browser_main.cc.
class VR_EXPORT XRSessionRequestConsentManager {
public:
// Must be called only after either SetInstance() or SetInstanceForTesting()
// are called. If both are called, the pointer set using
// SetInstanceForTesting() is returned.
static XRSessionRequestConsentManager* Instance();
// Must be called only once. The passed-in pointer is not owned. |instance|
// cannot be nullptr.
static void SetInstance(XRSessionRequestConsentManager* instance);
// Can be called any number of times. The passed-in pointer is not owned.
// |instance| can be nullptr.
static void SetInstanceForTesting(XRSessionRequestConsentManager* instance);
XRSessionRequestConsentManager();
virtual ~XRSessionRequestConsentManager();
// Displays a tab-modal consent dialog passing a delegate instantiated
// using |web_contents| as an argument.
// |response_callback| is guaranteed to be called with 'true' as arg if
// the user presses the 'accept' button, or with 'false' if the user
// either closes the dialog by any means or clicks on 'cancel' button.
virtual void ShowDialogAndGetConsent(
content::WebContents* web_contents,
base::OnceCallback<void(bool)> response_callback) = 0;
};
} // namespace vr
#endif // CHROME_BROWSER_VR_SERVICE_XR_SESSION_REQUEST_CONSENT_MANAGER_H_
// Copyright 2019 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/browser/vr/service/xr_session_request_consent_manager_impl.h"
#include <utility>
#include "chrome/browser/ui/tab_modal_confirm_dialog.h"
#include "chrome/browser/ui/xr/xr_session_request_consent_dialog_delegate.h"
#include "content/public/browser/web_contents.h"
namespace vr {
XRSessionRequestConsentManagerImpl::XRSessionRequestConsentManagerImpl() =
default;
XRSessionRequestConsentManagerImpl::~XRSessionRequestConsentManagerImpl() =
default;
void XRSessionRequestConsentManagerImpl::ShowDialogAndGetConsent(
content::WebContents* web_contents,
base::OnceCallback<void(bool)> response_callback) {
TabModalConfirmDialog::Create(new XrSessionRequestConsentDialogDelegate(
web_contents, std::move(response_callback)),
web_contents);
}
} // namespace vr
// Copyright 2019 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef CHROME_BROWSER_VR_SERVICE_XR_SESSION_REQUEST_CONSENT_MANAGER_IMPL_H_
#define CHROME_BROWSER_VR_SERVICE_XR_SESSION_REQUEST_CONSENT_MANAGER_IMPL_H_
#include "base/macros.h"
#include "chrome/browser/vr/service/xr_session_request_consent_manager.h"
namespace vr {
// Concrete implementation of XRSessionRequestConsentManager, part of
// "browser" component. Used on the browser's main thread.
class XRSessionRequestConsentManagerImpl
: public XRSessionRequestConsentManager {
public:
XRSessionRequestConsentManagerImpl();
~XRSessionRequestConsentManagerImpl() override;
// XRSessionRequestConsentManager:
void ShowDialogAndGetConsent(
content::WebContents* web_contents,
base::OnceCallback<void(bool)> response_callback) override;
private:
DISALLOW_COPY_AND_ASSIGN(XRSessionRequestConsentManagerImpl);
};
} // namespace vr
#endif // CHROME_BROWSER_VR_SERVICE_XR_SESSION_REQUEST_CONSENT_MANAGER_IMPL_H_
// Copyright 2019 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/browser/vr/test/mock_xr_session_request_consent_manager.h"
namespace vr {
MockXRSessionRequestConsentManager::MockXRSessionRequestConsentManager() =
default;
MockXRSessionRequestConsentManager::~MockXRSessionRequestConsentManager() =
default;
} // namespace vr
// Copyright 2019 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef CHROME_BROWSER_VR_TEST_MOCK_XR_SESSION_REQUEST_CONSENT_MANAGER_H_
#define CHROME_BROWSER_VR_TEST_MOCK_XR_SESSION_REQUEST_CONSENT_MANAGER_H_
#include "base/macros.h"
#include "chrome/browser/vr/service/xr_session_request_consent_manager.h"
#include "testing/gmock/include/gmock/gmock.h"
namespace vr {
class MockXRSessionRequestConsentManager
: public XRSessionRequestConsentManager {
public:
MockXRSessionRequestConsentManager();
~MockXRSessionRequestConsentManager() override;
MOCK_METHOD2(ShowDialogAndGetConsent,
void(content::WebContents* web_contents,
base::OnceCallback<void(bool)> response_callback));
private:
DISALLOW_COPY_AND_ASSIGN(MockXRSessionRequestConsentManager);
};
} // namespace vr
#endif // CHROME_BROWSER_VR_TEST_MOCK_XR_SESSION_REQUEST_CONSENT_MANAGER_H_
......@@ -7,6 +7,10 @@
#include "chrome/browser/vr/test/webvr_browser_test.h"
#include "content/public/browser/web_contents.h"
#include "content/public/test/browser_test_utils.h"
#include "testing/gmock/include/gmock/gmock.h"
using testing::_;
using testing::Invoke;
namespace vr {
......@@ -16,6 +20,14 @@ bool WebVrBrowserTestBase::XrDeviceFound(content::WebContents* web_contents) {
void WebVrBrowserTestBase::EnterSessionWithUserGesture(
content::WebContents* web_contents) {
#if defined(OS_WIN)
XRSessionRequestConsentManager::SetInstanceForTesting(&consent_manager_);
ON_CALL(consent_manager_, ShowDialogAndGetConsent(_, _))
.WillByDefault(Invoke(
[](content::WebContents*, base::OnceCallback<void(bool)> callback) {
std::move(callback).Run(true);
}));
#endif
// ExecuteScript runs with a user gesture, so we can just directly call
// requestPresent instead of having to do the hacky workaround the
// instrumentation tests use of actually sending a click event to the canvas.
......@@ -30,6 +42,9 @@ void WebVrBrowserTestBase::EnterSessionWithUserGestureOrFail(
}
void WebVrBrowserTestBase::EndSession(content::WebContents* web_contents) {
#if defined(OS_WIN)
XRSessionRequestConsentManager::SetInstanceForTesting(nullptr);
#endif
RunJavaScriptOrFail("vrDisplay.exitPresent()", web_contents);
}
......
......@@ -15,6 +15,7 @@
#include "device/vr/buildflags/buildflags.h"
#if defined(OS_WIN)
#include "chrome/browser/vr/test/mock_xr_session_request_consent_manager.h"
#include "services/service_manager/sandbox/features.h"
#endif
......
......@@ -7,11 +7,23 @@
#include "chrome/browser/vr/test/webxr_vr_browser_test.h"
#include "content/public/browser/web_contents.h"
#include "content/public/test/browser_test_utils.h"
#include "testing/gmock/include/gmock/gmock.h"
using testing::_;
using testing::Invoke;
namespace vr {
void WebXrVrBrowserTestBase::EnterSessionWithUserGesture(
content::WebContents* web_contents) {
#if defined(OS_WIN)
XRSessionRequestConsentManager::SetInstanceForTesting(&consent_manager_);
ON_CALL(consent_manager_, ShowDialogAndGetConsent(_, _))
.WillByDefault(Invoke(
[](content::WebContents*, base::OnceCallback<void(bool)> callback) {
std::move(callback).Run(true);
}));
#endif
// ExecuteScript runs with a user gesture, so we can just directly call
// requestSession instead of having to do the hacky workaround the
// instrumentation tests use of actually sending a click event to the canvas.
......@@ -27,6 +39,9 @@ void WebXrVrBrowserTestBase::EnterSessionWithUserGestureOrFail(
}
void WebXrVrBrowserTestBase::EndSession(content::WebContents* web_contents) {
#if defined(OS_WIN)
XRSessionRequestConsentManager::SetInstanceForTesting(nullptr);
#endif
RunJavaScriptOrFail(
"sessionInfos[sessionTypes.IMMERSIVE].currentSession.end()",
web_contents);
......
......@@ -16,6 +16,7 @@
#include "device/vr/buildflags/buildflags.h"
#if defined(OS_WIN)
#include "chrome/browser/vr/test/mock_xr_session_request_consent_manager.h"
#include "services/service_manager/sandbox/features.h"
#endif
......@@ -37,6 +38,10 @@ class WebXrVrBrowserTestBase : public WebXrBrowserTestBase {
using WebXrBrowserTestBase::EnterSessionWithUserGestureOrFail;
using WebXrBrowserTestBase::EndSession;
using WebXrBrowserTestBase::EndSessionOrFail;
#if defined(OS_WIN)
MockXRSessionRequestConsentManager consent_manager_;
#endif
};
// Test class with OpenVR disabled.
......
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