Commit c6bc9419 authored by Suman Nelson Kancherla's avatar Suman Nelson Kancherla Committed by Commit Bot

Added XR session request consent dialog for Android

The consent dialog strings - title, body, button texts - are all shared
between Android and Windows.

Bug: 963637, 962755
Change-Id: I4d3f090db1ee2487f305b6993b2851c112df594c
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1620695Reviewed-by: default avatarMichael Thiessen <mthiesse@chromium.org>
Reviewed-by: default avatarCarlos Pizano <cpu@chromium.org>
Reviewed-by: default avatarKlaus Weidner <klausw@chromium.org>
Reviewed-by: default avatarTibor Goldschwendt <tiborg@chromium.org>
Reviewed-by: default avatarBill Orr <billorr@chromium.org>
Commit-Queue: Suman Kancherla <sumankancherla@chromium.org>
Cr-Commit-Position: refs/heads/master@{#664808}
parent b0639275
......@@ -143,6 +143,7 @@ generate_jni("jni_headers") {
sources = [
"java/src/org/chromium/chrome/browser/vr/AndroidUiGestureTarget.java",
"java/src/org/chromium/chrome/browser/vr/AndroidVSyncHelper.java",
"java/src/org/chromium/chrome/browser/vr/VrConsentDialog.java",
"java/src/org/chromium/chrome/browser/vr/VrCoreInfo.java",
"java/src/org/chromium/chrome/browser/vr/VrInputConnection.java",
"java/src/org/chromium/chrome/browser/vr/VrModuleProvider.java",
......@@ -151,5 +152,6 @@ generate_jni("jni_headers") {
"java/src/org/chromium/chrome/browser/vr/keyboard/GvrKeyboardLoaderClient.java",
"java/src/org/chromium/chrome/browser/vr/keyboard/TextEditAction.java",
]
jni_package = "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.
package org.chromium.chrome.browser.vr;
import android.content.res.Resources;
import android.support.annotation.IntDef;
import android.support.annotation.NonNull;
import org.chromium.base.VisibleForTesting;
import org.chromium.base.annotations.CalledByNative;
import org.chromium.base.annotations.JNINamespace;
import org.chromium.base.annotations.NativeMethods;
import org.chromium.base.metrics.RecordHistogram;
import org.chromium.chrome.R;
import org.chromium.chrome.browser.ChromeActivity;
import org.chromium.chrome.browser.tab.Tab;
import org.chromium.ui.modaldialog.DialogDismissalCause;
import org.chromium.ui.modaldialog.ModalDialogManager;
import org.chromium.ui.modaldialog.ModalDialogProperties;
import org.chromium.ui.modelutil.PropertyModel;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
/**
* Provides a consent dialog shown before entering an immersive VR session.
*/
@JNINamespace("vr")
public class VrConsentDialog implements ModalDialogProperties.Controller {
@NativeMethods
/* package */ interface VrConsentUiHelperImpl {
void onUserConsentResult(long nativeGvrConsentHelperImpl, boolean allowed);
}
@Retention(RetentionPolicy.SOURCE)
@IntDef({UserAction.ALLOWED, UserAction.DENIED, UserAction.CONSENT_FLOW_ABORTED})
@interface UserAction {
// Don't reuse or reorder values. If you add something, update NUM_ENTRIES.
// Also, keep these in sync with ConsentDialogAction enums in
// XrSessionRequestConsentDialogDelegate
int ALLOWED = 0;
int DENIED = 1;
int CONSENT_FLOW_ABORTED = 2;
int NUM_ENTRIES = 3;
}
private ModalDialogManager mModalDialogManager;
private VrConsentListener mListener;
private static long sDialogOpenTimeStampMillis;
private long mNativeInstance;
private VrConsentDialog(long instance) {
mNativeInstance = instance;
}
@CalledByNative
private static VrConsentDialog promptForUserConsent(long instance, final Tab tab) {
VrConsentDialog dialog = new VrConsentDialog(instance);
dialog.show(tab.getActivity(), new VrConsentListener() {
@Override
public void onUserConsent(boolean allowed) {
dialog.onUserGesture(allowed);
}
});
return dialog;
}
@VisibleForTesting
protected void onUserGesture(boolean allowed) {
VrConsentDialogJni.get().onUserConsentResult(mNativeInstance, allowed);
}
public void show(@NonNull ChromeActivity activity, @NonNull VrConsentListener listener) {
mListener = listener;
Resources resources = activity.getResources();
PropertyModel model = new PropertyModel.Builder(ModalDialogProperties.ALL_KEYS)
.with(ModalDialogProperties.CONTROLLER, this)
.with(ModalDialogProperties.TITLE, resources,
R.string.xr_consent_dialog_title)
.with(ModalDialogProperties.MESSAGE, resources,
R.string.xr_consent_dialog_description)
.with(ModalDialogProperties.POSITIVE_BUTTON_TEXT, resources,
R.string.xr_consent_dialog_button_allow_and_enter_vr)
.with(ModalDialogProperties.NEGATIVE_BUTTON_TEXT, resources,
R.string.xr_consent_dialog_button_deny_vr)
.with(ModalDialogProperties.CANCEL_ON_TOUCH_OUTSIDE, true)
.build();
mModalDialogManager = activity.getModalDialogManager();
mModalDialogManager.showDialog(model, ModalDialogManager.ModalDialogType.TAB);
sDialogOpenTimeStampMillis = System.currentTimeMillis();
}
@Override
public void onClick(PropertyModel model, int buttonType) {
if (buttonType == ModalDialogProperties.ButtonType.NEGATIVE) {
mModalDialogManager.dismissDialog(model, DialogDismissalCause.NEGATIVE_BUTTON_CLICKED);
} else {
mModalDialogManager.dismissDialog(model, DialogDismissalCause.POSITIVE_BUTTON_CLICKED);
}
}
@Override
public void onDismiss(PropertyModel model, int dismissalCause) {
if (dismissalCause == DialogDismissalCause.POSITIVE_BUTTON_CLICKED) {
mListener.onUserConsent(true);
logUserAction(UserAction.ALLOWED);
logConsentFlowDurationWhenConsentGranted();
} else if (dismissalCause == DialogDismissalCause.NEGATIVE_BUTTON_CLICKED) {
mListener.onUserConsent(false);
logUserAction(UserAction.DENIED);
logConsentFlowDurationWhenConsentNotGranted();
} else {
mListener.onUserConsent(false);
logUserAction(UserAction.CONSENT_FLOW_ABORTED);
logConsentFlowDurationWhenUserAborted();
}
}
private static void logUserAction(@UserAction int action) {
// TODO(crbug.com/965538): call c++ functions that update the same histogram instead of
// calling this java method.
RecordHistogram.recordEnumeratedHistogram(
"XR.WebXR.ConsentFlow", action, UserAction.NUM_ENTRIES);
}
private static void logConsentFlowDurationWhenConsentGranted() {
// TODO(crbug.com/965538): call c++ functions that update the same histogram instead of
// calling this java method.
RecordHistogram.recordMediumTimesHistogram("XR.WebXR.ConsentFlowDuration.ConsentGranted",
System.currentTimeMillis() - sDialogOpenTimeStampMillis);
}
private static void logConsentFlowDurationWhenConsentNotGranted() {
// TODO(crbug.com/965538): call c++ functions that update the same histogram instead of
// calling this java method.
RecordHistogram.recordMediumTimesHistogram("XR.WebXR.ConsentFlowDuration.ConsentNotGranted",
System.currentTimeMillis() - sDialogOpenTimeStampMillis);
}
private static void logConsentFlowDurationWhenUserAborted() {
// TODO(crbug.com/965538): call c++ functions that update the same histogram instead of
// calling this java method.
RecordHistogram.recordMediumTimesHistogram(
"XR.WebXR.ConsentFlowDuration.ConsentFlowAborted",
System.currentTimeMillis() - sDialogOpenTimeStampMillis);
}
// TODO(sumankancherla): Add bounce metrics by addressing crbug.com/965538.
}
// 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.
package org.chromium.chrome.browser.vr;
/**
* Interface used by {@link VrConsentDialog} to notify user consent to a session
* permission dialog.
*/
public interface VrConsentListener {
/**
* Needs to be called in response to user's action to notify the listeners
* about their decision.
**/
public void onUserConsent(boolean allowed);
}
......@@ -4,6 +4,8 @@
public_vr_java_sources = [
"//chrome/android/features/vr/java/src/org/chromium/chrome/browser/vr/OnExitVrRequestListener.java",
"//chrome/android/features/vr/java/src/org/chromium/chrome/browser/vr/VrConsentDialog.java",
"//chrome/android/features/vr/java/src/org/chromium/chrome/browser/vr/VrConsentListener.java",
"//chrome/android/features/vr/java/src/org/chromium/chrome/browser/vr/VrDelegate.java",
"//chrome/android/features/vr/java/src/org/chromium/chrome/browser/vr/VrDelegateFallback.java",
"//chrome/android/features/vr/java/src/org/chromium/chrome/browser/vr/VrFallbackUtils.java",
......
......@@ -3805,6 +3805,10 @@ To change this setting, <ph name="BEGIN_LINK">&lt;resetlink&gt;</ph>reset sync<p
Reopen Chrome.
</message>
<if expr="enable_vr">
<part file="../../../app/xr_consent_ui_strings.grdp" />
</if>
<if expr="enable_arcore">
<!-- ARCore check infobar -->
<message name="IDS_AR_CORE_CHECK_INFOBAR_INSTALL_TEXT" desc="Text to be displayed in the ARCore check infobar. When a WebXR page is loaded, if ARCore is needed to display AR content and it is not installed, an infobar will be shown to the user prompting them to install ARCore.">
......
......@@ -33,10 +33,10 @@ public class WebXrArTestFramework extends WebXrTestFramework {
enterSessionWithUserGesture(webContents);
// We expect the AR-specific AR session consent prompt but should not get
// prompted for page camera permission.
PermissionUtils.waitForArConsentPrompt(getRule().getActivity());
PermissionUtils.acceptArConsentPrompt(getRule().getActivity());
// We expect a session consent prompt (in this case the AR-specific one), but should not
// get prompted for page camera permission.
PermissionUtils.waitForConsentPrompt(getRule().getActivity());
PermissionUtils.acceptConsentPrompt(getRule().getActivity());
pollJavaScriptBooleanOrFail("sessionInfos[sessionTypes.AR].currentSession != null",
POLL_TIMEOUT_LONG_MS, webContents);
......@@ -56,8 +56,8 @@ public class WebXrArTestFramework extends WebXrTestFramework {
// We expect the AR-specific AR session consent prompt but should not get
// prompted for page camera permission.
PermissionUtils.waitForArConsentPrompt(getRule().getActivity());
PermissionUtils.declineArConsentPrompt(getRule().getActivity());
PermissionUtils.waitForConsentPrompt(getRule().getActivity());
PermissionUtils.declineConsentPrompt(getRule().getActivity());
pollJavaScriptBooleanOrFail(
"sessionInfos[sessionTypes.AR].error != null", POLL_TIMEOUT_LONG_MS, webContents);
......
......@@ -7,6 +7,7 @@ package org.chromium.chrome.browser.vr;
import org.junit.Assert;
import org.chromium.chrome.browser.vr.rules.VrTestRule;
import org.chromium.chrome.browser.vr.util.PermissionUtils;
import org.chromium.chrome.browser.vr.util.VrShellDelegateUtils;
import org.chromium.chrome.test.ChromeActivityTestRule;
import org.chromium.content_public.browser.WebContents;
......@@ -50,6 +51,10 @@ public class WebXrVrTestFramework extends WebXrTestFramework {
runJavaScriptOrFail(
"sessionTypeToRequest = sessionTypes.IMMERSIVE", POLL_TIMEOUT_LONG_MS, webContents);
enterSessionWithUserGesture(webContents);
PermissionUtils.waitForConsentPrompt(getRule().getActivity());
PermissionUtils.acceptConsentPrompt(getRule().getActivity());
pollJavaScriptBooleanOrFail("sessionInfos[sessionTypes.IMMERSIVE].currentSession != null",
POLL_TIMEOUT_LONG_MS, webContents);
Assert.assertTrue("Immersive session started, but VR Shell not in presentation mode",
......
......@@ -7,6 +7,7 @@ package org.chromium.chrome.browser.vr.util;
import org.chromium.chrome.browser.ChromeActivity;
import org.chromium.chrome.browser.permissions.PermissionDialogController;
import org.chromium.chrome.browser.vr.ArConsentDialog;
import org.chromium.chrome.browser.vr.VrConsentDialog;
import org.chromium.content_public.browser.test.util.CriteriaHelper;
import org.chromium.content_public.browser.test.util.TestThreadUtils;
import org.chromium.ui.modaldialog.ModalDialogManager;
......@@ -18,6 +19,7 @@ import org.chromium.ui.modelutil.PropertyModel;
* in the VR Browser, see NativeUiUtils.
*/
public class PermissionUtils {
public static final long DIALOG_POLLING_INTERVAL_MS = 250;
/**
* Blocks until a permission prompt appears.
*/
......@@ -48,49 +50,52 @@ public class PermissionUtils {
}
/**
* Blocks until the AR session consent prompt appears.
* Blocks until the session consent prompt appears.
*/
public static void waitForArConsentPrompt(ChromeActivity activity) {
CriteriaHelper.pollUiThread(() -> {
return isArConsentDialogShown(activity);
}, "AR consent prompt did not appear in allotted time");
public static void waitForConsentPrompt(ChromeActivity activity) {
CriteriaHelper.pollUiThread(()
-> { return isConsentDialogShown(activity); },
"Consent prompt did not appear in allotted time",
CriteriaHelper.DEFAULT_MAX_TIME_TO_POLL, DIALOG_POLLING_INTERVAL_MS);
}
/**
* Accepts the currently displayed AR session consent prompt.
* Accepts the currently displayed session consent prompt.
*/
public static void acceptArConsentPrompt(ChromeActivity activity) {
public static void acceptConsentPrompt(ChromeActivity activity) {
TestThreadUtils.runOnUiThreadBlocking(() -> {
clickArConsentDialogButton(activity, ModalDialogProperties.ButtonType.POSITIVE);
clickConsentDialogButton(activity, ModalDialogProperties.ButtonType.POSITIVE);
});
}
/**
* Declines the currently displayed AR session consent prompt.
* Declines the currently displayed session consent prompt.
*/
public static void declineArConsentPrompt(ChromeActivity activity) {
public static void declineConsentPrompt(ChromeActivity activity) {
TestThreadUtils.runOnUiThreadBlocking(() -> {
clickArConsentDialogButton(activity, ModalDialogProperties.ButtonType.NEGATIVE);
clickConsentDialogButton(activity, ModalDialogProperties.ButtonType.NEGATIVE);
});
}
/**
* Helper function to check if the AR consent dialog is being shown.
* Helper function to check if the consent dialog is being shown.
*/
public static boolean isArConsentDialogShown(ChromeActivity activity) {
public static boolean isConsentDialogShown(ChromeActivity activity) {
ModalDialogManager manager = activity.getModalDialogManager();
PropertyModel model = manager.getCurrentDialogForTest();
if (model == null) return false;
return model.get(ModalDialogProperties.CONTROLLER) instanceof ArConsentDialog;
ModalDialogProperties.Controller controller = model.get(ModalDialogProperties.CONTROLLER);
return controller instanceof ArConsentDialog || controller instanceof VrConsentDialog;
}
/**
* Helper function to click a button in the AR consent dialog.
* Helper function to click a button in the consent dialog.
*/
public static void clickArConsentDialogButton(ChromeActivity activity, int buttonType) {
public static void clickConsentDialogButton(ChromeActivity activity, int buttonType) {
ModalDialogManager manager = activity.getModalDialogManager();
PropertyModel model = manager.getCurrentDialogForTest();
ArConsentDialog dialog = (ArConsentDialog) (model.get(ModalDialogProperties.CONTROLLER));
ModalDialogProperties.Controller dialog =
(ModalDialogProperties.Controller) (model.get(ModalDialogProperties.CONTROLLER));
dialog.onClick(model, buttonType);
}
}
......@@ -30,6 +30,7 @@ per-file profiles_strings.grdp=file://chrome/browser/profiles/OWNERS
per-file settings*strings*=file://chrome/browser/resources/settings/OWNERS
per-file vr_strings.grdp=file://chrome/browser/vr/OWNERS
per-file xr_support_ui_strings.grdp=file://chrome/browser/vr/OWNERS
# Non-GRD rules.
......
......@@ -226,6 +226,9 @@ are declared in tools/grit/grit_rule.gni.
<!-- Strings that are only used when VR devices are supported -->
<if expr="enable_vr">
<part file="vr_strings.grdp" />
<if expr="not is_android">
<part file="xr_consent_ui_strings.grdp" />
</if>
</if>
<!-- TODO add all of your "string table" messages here. Remember to
......
......@@ -10,25 +10,6 @@
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 learn about 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
......
<?xml version="1.0" encoding="utf-8"?>
<!-- XR specific strings (included from generated_resources.grd and android_chrome_strings.grd). -->
<!-- These strings are used in the consent flow dialog. -->
<grit-part>
<!-- 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 learn about 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>
</grit-part>
......@@ -89,6 +89,13 @@ static_library("vr_android") {
]
}
if (enable_gvr_services) {
sources += [
"gvr_consent_helper_impl.cc",
"gvr_consent_helper_impl.h",
]
}
deps = [
":ui_factory",
"//base",
......
// 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/android/vr/gvr_consent_helper_impl.h"
#include <utility>
#include "base/android/jni_string.h"
#include "base/callback.h"
#include "base/macros.h"
#include "chrome/browser/android/tab_android.h"
#include "content/public/browser/render_frame_host.h"
#include "content/public/browser/web_contents.h"
#include "jni/VrConsentDialog_jni.h"
using base::android::AttachCurrentThread;
using base::android::ScopedJavaLocalRef;
namespace vr {
namespace {
base::android::ScopedJavaLocalRef<jobject> GetTabFromRenderer(
int render_process_id,
int render_frame_id) {
content::RenderFrameHost* render_frame_host =
content::RenderFrameHost::FromID(render_process_id, render_frame_id);
DCHECK(render_frame_host);
content::WebContents* web_contents =
content::WebContents::FromRenderFrameHost(render_frame_host);
DCHECK(web_contents);
TabAndroid* tab_android = TabAndroid::FromWebContents(web_contents);
DCHECK(tab_android);
base::android::ScopedJavaLocalRef<jobject> j_tab_android =
tab_android->GetJavaObject();
DCHECK(!j_tab_android.is_null());
return j_tab_android;
}
} // namespace
GvrConsentHelperImpl::GvrConsentHelperImpl() = default;
GvrConsentHelperImpl::~GvrConsentHelperImpl() = default;
void GvrConsentHelperImpl::PromptUserAndGetConsent(
int render_process_id,
int render_frame_id,
OnUserConsentCallback on_user_consent_callback) {
DCHECK(!on_user_consent_callback_);
on_user_consent_callback_ = std::move(on_user_consent_callback);
JNIEnv* env = AttachCurrentThread();
ScopedJavaLocalRef<jobject> jdelegate =
Java_VrConsentDialog_promptForUserConsent(
env, reinterpret_cast<jlong>(this),
GetTabFromRenderer(render_process_id, render_frame_id));
if (jdelegate.is_null()) {
std::move(on_user_consent_callback_).Run(false);
return;
}
}
void GvrConsentHelperImpl::OnUserConsentResult(
JNIEnv* env,
const base::android::JavaParamRef<jobject>& j_caller,
jboolean is_granted) {
DCHECK(on_user_consent_callback_);
std::move(on_user_consent_callback_).Run(!!is_granted);
}
} // 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_ANDROID_VR_GVR_CONSENT_HELPER_IMPL_H_
#define CHROME_BROWSER_ANDROID_VR_GVR_CONSENT_HELPER_IMPL_H_
#include <jni.h>
#include "base/android/jni_android.h"
#include "base/callback.h"
#include "chrome/browser/vr/service/gvr_consent_helper.h"
namespace vr {
class GvrConsentHelperImpl : public GvrConsentHelper {
public:
GvrConsentHelperImpl();
~GvrConsentHelperImpl() override;
// Caller must ensure not to call this a second time before the first dialog
// is dismissed.
void PromptUserAndGetConsent(int render_process_id,
int render_frame_id,
OnUserConsentCallback) override;
void OnUserConsentResult(JNIEnv* env,
const base::android::JavaParamRef<jobject>& j_caller,
jboolean is_granted);
private:
OnUserConsentCallback on_user_consent_callback_;
DISALLOW_COPY_AND_ASSIGN(GvrConsentHelperImpl);
};
} // namespace vr
#endif // CHROME_BROWSER_ANDROID_VR_GVR_CONSENT_HELPER_IMPL_H_
......@@ -4,8 +4,11 @@
#include "chrome/browser/android/vr/vr_module_provider.h"
#include <memory>
#include <utility>
#include "chrome/browser/android/vr/gvr_consent_helper_impl.h"
#include "chrome/browser/android/vr/register_jni.h"
#include "chrome/browser/android/vr/vr_module_provider.h"
#include "content/public/browser/render_frame_host.h"
#include "content/public/browser/web_contents.h"
#include "device/vr/android/gvr/vr_module_delegate.h"
......@@ -53,7 +56,7 @@ void VrModuleProvider::OnInstalledModule(
const base::android::JavaParamRef<jobject>& obj,
bool success) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
DCHECK(on_finished_callbacks_.size() > 0);
DCHECK_GT(on_finished_callbacks_.size(), 0UL);
while (!on_finished_callbacks_.empty()) {
std::move(on_finished_callbacks_.front()).Run(success);
on_finished_callbacks_.pop();
......@@ -80,6 +83,7 @@ VrModuleProviderFactory::CreateDelegate(int render_process_id,
static void JNI_VrModuleProvider_Init(JNIEnv* env) {
device::VrModuleDelegateFactory::Set(
std::make_unique<VrModuleProviderFactory>());
GvrConsentHelper::SetInstance(std::make_unique<vr::GvrConsentHelperImpl>());
}
static void JNI_VrModuleProvider_RegisterJni(JNIEnv* env) {
......
......@@ -241,6 +241,8 @@ component("vr_common") {
"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_impl.cc",
......
// 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/gvr_consent_helper.h"
#include "base/logging.h"
namespace vr {
namespace {
GvrConsentHelper* g_instance = nullptr;
} // namespace
GvrConsentHelper::GvrConsentHelper() = default;
void GvrConsentHelper::SetInstance(std::unique_ptr<GvrConsentHelper> instance) {
if (g_instance)
delete g_instance;
g_instance = instance.release();
}
GvrConsentHelper* GvrConsentHelper::GetInstance() {
DCHECK(g_instance);
return g_instance;
}
GvrConsentHelper::~GvrConsentHelper() = 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_GVR_CONSENT_HELPER_H_
#define CHROME_BROWSER_VR_SERVICE_GVR_CONSENT_HELPER_H_
#include <memory>
#include "base/callback_forward.h"
#include "base/macros.h"
#include "chrome/browser/vr/vr_export.h"
namespace vr {
typedef base::OnceCallback<void(bool)> OnUserConsentCallback;
// Used for displaying a user consent dialog. Breaks the cyclic dependency
// between device/vr and the Android UI code in browser.
class VR_EXPORT GvrConsentHelper {
public:
static void SetInstance(std::unique_ptr<GvrConsentHelper>);
static GvrConsentHelper* GetInstance();
virtual ~GvrConsentHelper();
virtual void PromptUserAndGetConsent(int render_process_id,
int render_frame_id,
OnUserConsentCallback) = 0;
protected:
GvrConsentHelper();
private:
DISALLOW_COPY_AND_ASSIGN(GvrConsentHelper);
};
} // namespace vr
#endif // CHROME_BROWSER_VR_SERVICE_GVR_CONSENT_HELPER_H_
......@@ -27,6 +27,8 @@
#if defined(OS_WIN)
#include "chrome/browser/vr/service/xr_session_request_consent_manager.h"
#elif defined(OS_ANDROID)
#include "chrome/browser/vr/service/gvr_consent_helper.h"
#endif
namespace vr {
......@@ -136,38 +138,81 @@ 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),
std::move(callback)));
// Inline sessions do not need permissions. WebVR did not require
// permissions.
// TODO(crbug.com/968221): Address privacy requirements for inline sessions
if (!options->immersive || options->use_legacy_webvr_render_path) {
DoRequestSession(std::move(options), std::move(callback));
return;
}
#endif
OnUserConsent(std::move(options), std::move(callback), true);
// TODO(crbug.com/968233): Unify the below consent flow.
#if defined(OS_ANDROID)
if (options->environment_integration) {
// The session has requested AR mode. This will only work if
// the ARCore-backed runtime is available and enabled, and that has
// its own separate consent prompt. Otherwise, the session request will
// fail since no other runtimes support environment integration. In
// either case, there's no need to show the VR-specific consent prompt.
DoRequestSession(std::move(options), std::move(callback));
return;
} else {
// GVR.
if (!render_frame_host_) {
// Reject promise.
std::move(callback).Run(nullptr);
} else {
GvrConsentHelper::GetInstance()->PromptUserAndGetConsent(
render_frame_host_->GetProcess()->GetID(),
render_frame_host_->GetRoutingID(),
base::BindOnce(&XRDeviceImpl::OnConsentResult,
weak_ptr_factory_.GetWeakPtr(), std::move(options),
std::move(callback)));
}
return;
}
#elif defined(OS_WIN)
DCHECK(!options->environment_integration);
XRSessionRequestConsentManager::Instance()->ShowDialogAndGetConsent(
GetWebContents(),
base::BindOnce(&XRDeviceImpl::OnConsentResult,
weak_ptr_factory_.GetWeakPtr(), std::move(options),
std::move(callback)));
return;
#else
NOTREACHED();
#endif
}
void XRDeviceImpl::OnUserConsent(
void XRDeviceImpl::OnConsentResult(
device::mojom::XRSessionOptionsPtr options,
device::mojom::XRDevice::RequestSessionCallback callback,
bool allowed) {
if (!allowed) {
bool is_consent_granted) {
if (!is_consent_granted) {
std::move(callback).Run(nullptr);
return;
}
// Re-check for another device instance after a potential user consent.
// TODO(crbug.com/967513): prevent such races.
if (XRRuntimeManager::GetInstance()->IsOtherDevicePresenting(this)) {
// Can't create sessions while an immersive session exists.
std::move(callback).Run(nullptr);
return;
}
DoRequestSession(std::move(options), std::move(callback));
}
void XRDeviceImpl::DoRequestSession(
device::mojom::XRSessionOptionsPtr options,
device::mojom::XRDevice::RequestSessionCallback callback) {
// Get the runtime we'll be creating a session with.
BrowserXRRuntime* runtime =
XRRuntimeManager::GetInstance()->GetRuntimeForOptions(options.get());
......
......@@ -84,9 +84,12 @@ class XRDeviceImpl : public device::mojom::XRDevice {
device::mojom::XRDeviceId session_runtime_id,
device::mojom::XRDevice::RequestSessionCallback callback,
device::mojom::XRSessionPtr session);
void OnUserConsent(device::mojom::XRSessionOptionsPtr options,
device::mojom::XRDevice::RequestSessionCallback callback,
bool allowed);
void DoRequestSession(
device::mojom::XRSessionOptionsPtr options,
device::mojom::XRDevice::RequestSessionCallback callback);
void OnConsentResult(device::mojom::XRSessionOptionsPtr options,
device::mojom::XRDevice::RequestSessionCallback callback,
bool is_consent_granted);
// TODO(https://crbug.com/837538): Instead, check before returning this
// object.
......
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