Commit 44114ee8 authored by bsheedy's avatar bsheedy Committed by Commit Bot

Automate VR Browser permission test

Automates the VR Browser manual test for ensuring that permissions
granted to a site in 2D persist through reloads and VR entry.

Bug: 861941
Change-Id: I2f2ae07705713a97f8e428ec325e3d5412b37fbb
Reviewed-on: https://chromium-review.googlesource.com/1171600Reviewed-by: default avatarAmirhossein Simjour <asimjour@chromium.org>
Commit-Queue: Brian Sheedy <bsheedy@chromium.org>
Cr-Commit-Position: refs/heads/master@{#583009}
parent 61067e7c
...@@ -735,6 +735,7 @@ if (enable_vr || (enable_arcore && package_arcore)) { ...@@ -735,6 +735,7 @@ if (enable_vr || (enable_arcore && package_arcore)) {
"javatests/src/org/chromium/chrome/browser/vr/rules/XrActivityRestrictionRule.java", "javatests/src/org/chromium/chrome/browser/vr/rules/XrActivityRestrictionRule.java",
"javatests/src/org/chromium/chrome/browser/vr/rules/XrTestRule.java", "javatests/src/org/chromium/chrome/browser/vr/rules/XrTestRule.java",
"javatests/src/org/chromium/chrome/browser/vr/rules/WebappActivityXrTestRule.java", "javatests/src/org/chromium/chrome/browser/vr/rules/WebappActivityXrTestRule.java",
"javatests/src/org/chromium/chrome/browser/vr/util/PermissionUtils.java",
"javatests/src/org/chromium/chrome/browser/vr/util/XrTestRuleUtils.java", "javatests/src/org/chromium/chrome/browser/vr/util/XrTestRuleUtils.java",
"javatests/src/org/chromium/chrome/browser/vr/WebXrTestFramework.java", "javatests/src/org/chromium/chrome/browser/vr/WebXrTestFramework.java",
"javatests/src/org/chromium/chrome/browser/vr/XrTestFramework.java", "javatests/src/org/chromium/chrome/browser/vr/XrTestFramework.java",
......
...@@ -40,6 +40,7 @@ import org.chromium.chrome.browser.vr.mock.MockVrDaydreamApi; ...@@ -40,6 +40,7 @@ import org.chromium.chrome.browser.vr.mock.MockVrDaydreamApi;
import org.chromium.chrome.browser.vr.rules.ChromeTabbedActivityVrTestRule; import org.chromium.chrome.browser.vr.rules.ChromeTabbedActivityVrTestRule;
import org.chromium.chrome.browser.vr.util.NativeUiUtils; import org.chromium.chrome.browser.vr.util.NativeUiUtils;
import org.chromium.chrome.browser.vr.util.NfcSimUtils; import org.chromium.chrome.browser.vr.util.NfcSimUtils;
import org.chromium.chrome.browser.vr.util.PermissionUtils;
import org.chromium.chrome.browser.vr.util.VrBrowserTransitionUtils; import org.chromium.chrome.browser.vr.util.VrBrowserTransitionUtils;
import org.chromium.chrome.browser.vr.util.VrShellDelegateUtils; import org.chromium.chrome.browser.vr.util.VrShellDelegateUtils;
import org.chromium.chrome.test.ChromeJUnit4ClassRunner; import org.chromium.chrome.test.ChromeJUnit4ClassRunner;
...@@ -47,6 +48,7 @@ import org.chromium.chrome.test.util.ActivityUtils; ...@@ -47,6 +48,7 @@ import org.chromium.chrome.test.util.ActivityUtils;
import org.chromium.content.browser.test.util.CriteriaHelper; import org.chromium.content.browser.test.util.CriteriaHelper;
import org.chromium.content.browser.test.util.DOMUtils; import org.chromium.content.browser.test.util.DOMUtils;
import org.chromium.content.browser.test.util.JavaScriptUtils; import org.chromium.content.browser.test.util.JavaScriptUtils;
import org.chromium.net.test.EmbeddedTestServer;
import java.util.concurrent.TimeoutException; import java.util.concurrent.TimeoutException;
import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicBoolean;
...@@ -531,4 +533,47 @@ public class VrBrowserTransitionTest { ...@@ -531,4 +533,47 @@ public class VrBrowserTransitionTest {
Assert.assertTrue( Assert.assertTrue(
"Creating VrShell didn't fail when Async Reprojection failed.", failed.get()); "Creating VrShell didn't fail when Async Reprojection failed.", failed.get());
} }
/**
* Verifies that permissions granted outside of VR persist while in VR, even after the page is
* refreshed. Automation of a manutal test from https://crbug.com/861941.
*/
@Test
@Restriction({RESTRICTION_TYPE_VIEWER_DAYDREAM})
@MediumTest
public void testPermissionsPersistWhenEnteringVrBrowser() throws InterruptedException {
// Permissions don't work on file:// URLs, so use a local server.
EmbeddedTestServer server =
EmbeddedTestServer.createAndStartServer(InstrumentationRegistry.getContext());
mVrBrowserTestFramework.loadUrlAndAwaitInitialization(
server.getURL(VrBrowserTestFramework.getEmbeddedServerPathForHtmlTestFile(
"test_permissions_persist_when_entering_vr_browser")),
PAGE_LOAD_TIMEOUT_S);
// Ensure that permission requests initially trigger a prompt.
Assert.assertTrue("Camera permission would not trigger prompt",
mVrBrowserTestFramework.permissionRequestWouldTriggerPrompt("camera"));
Assert.assertTrue("Microphone permission would not trigger prompt",
mVrBrowserTestFramework.permissionRequestWouldTriggerPrompt("microphone"));
// Request camera and microphone permissions.
mVrBrowserTestFramework.runJavaScriptOrFail(
"stepRequestPermission()", POLL_TIMEOUT_SHORT_MS);
// Accept the resulting prompt and wait for the permissions to be granted to the site.
PermissionUtils.waitForPermissionPrompt();
PermissionUtils.acceptPermissionPrompt();
mVrBrowserTestFramework.waitOnJavaScriptStep();
// Reload the page and ensure that the permissions are still granted.
mVrBrowserTestFramework.loadUrlAndAwaitInitialization(
server.getURL(VrBrowserTestFramework.getEmbeddedServerPathForHtmlTestFile(
"test_permissions_persist_when_entering_vr_browser")),
PAGE_LOAD_TIMEOUT_S);
Assert.assertFalse("Camera permission would trigger prompt after reload",
mVrBrowserTestFramework.permissionRequestWouldTriggerPrompt("camera"));
Assert.assertFalse("Microphone permission would trigger prompt after reload",
mVrBrowserTestFramework.permissionRequestWouldTriggerPrompt("microphone"));
// Enter the VR Browser and ensure the permission request auto-succeeds.
VrBrowserTransitionUtils.forceEnterVrBrowserOrFail(POLL_TIMEOUT_LONG_MS);
mVrBrowserTestFramework.executeStepAndWait("stepRequestPermission()");
mVrBrowserTestFramework.endTest();
server.stopAndDestroyServer();
}
} }
...@@ -128,13 +128,13 @@ public class WebXrArSessionTest { ...@@ -128,13 +128,13 @@ public class WebXrArSessionTest {
"test_ar_request_session_succeeds")), "test_ar_request_session_succeeds")),
PAGE_LOAD_TIMEOUT_S); PAGE_LOAD_TIMEOUT_S);
Assert.assertTrue("First AR session request did not trigger permission prompt", Assert.assertTrue("First AR session request did not trigger permission prompt",
mWebXrArTestFramework.arSessionRequestWouldTriggerPermissionPrompt()); mWebXrArTestFramework.permissionRequestWouldTriggerPrompt("camera"));
mWebXrArTestFramework.enterSessionWithUserGestureOrFail(); mWebXrArTestFramework.enterSessionWithUserGestureOrFail();
mWebXrArTestFramework.endSession(); mWebXrArTestFramework.endSession();
// Manually run through the same steps as enterArSessionOrFail so that we don't trigger // Manually run through the same steps as enterArSessionOrFail so that we don't trigger
// its automatic permission acceptance. // its automatic permission acceptance.
Assert.assertFalse("Second AR session request triggered permission prompt", Assert.assertFalse("Second AR session request triggered permission prompt",
mWebXrArTestFramework.arSessionRequestWouldTriggerPermissionPrompt()); mWebXrArTestFramework.permissionRequestWouldTriggerPrompt("camera"));
mWebXrArTestFramework.enterSessionWithUserGesture(); mWebXrArTestFramework.enterSessionWithUserGesture();
mWebXrArTestFramework.pollJavaScriptBooleanOrFail( mWebXrArTestFramework.pollJavaScriptBooleanOrFail(
"sessionInfos[sessionTypes.AR].currentSession != null", POLL_TIMEOUT_LONG_MS); "sessionInfos[sessionTypes.AR].currentSession != null", POLL_TIMEOUT_LONG_MS);
......
...@@ -4,33 +4,14 @@ ...@@ -4,33 +4,14 @@
package org.chromium.chrome.browser.vr; package org.chromium.chrome.browser.vr;
import android.content.DialogInterface; import org.chromium.chrome.browser.vr.util.PermissionUtils;
import org.chromium.base.ThreadUtils;
import org.chromium.chrome.browser.permissions.PermissionDialogController;
import org.chromium.chrome.test.ChromeActivityTestRule; import org.chromium.chrome.test.ChromeActivityTestRule;
import org.chromium.content.browser.test.util.CriteriaHelper;
import org.chromium.content_public.browser.WebContents; import org.chromium.content_public.browser.WebContents;
/** /**
* WebXR for AR-specific implementation of the WebXrTestFramework. * WebXR for AR-specific implementation of the WebXrTestFramework.
*/ */
public class WebXrArTestFramework extends WebXrTestFramework { public class WebXrArTestFramework extends WebXrTestFramework {
/**
* Checks whether an AR session request would prompt the user for Camera permissions.
*
* @param webContents The WebContents to check for the permission in.
* @return True if an AR session request will cause a permission prompt, false otherwise.
*/
public static boolean arSessionRequestWouldTriggerPermissionPrompt(WebContents webContents) {
runJavaScriptOrFail("checkIfArSessionWouldTriggerPermissionPrompt()", POLL_TIMEOUT_SHORT_MS,
webContents);
pollJavaScriptBooleanOrFail("arSessionRequestWouldTriggerPermissionPrompt !== null",
POLL_TIMEOUT_SHORT_MS, webContents);
return Boolean.valueOf(runJavaScriptOrFail("arSessionRequestWouldTriggerPermissionPrompt",
POLL_TIMEOUT_SHORT_MS, webContents));
}
/** /**
* Must be constructed after the rule has been applied (e.g. in whatever method is * Must be constructed after the rule has been applied (e.g. in whatever method is
* tagged with @Before). * tagged with @Before).
...@@ -52,37 +33,17 @@ public class WebXrArTestFramework extends WebXrTestFramework { ...@@ -52,37 +33,17 @@ public class WebXrArTestFramework extends WebXrTestFramework {
// Requesting an AR session for the first time on a page will always prompt for camera // Requesting an AR session for the first time on a page will always prompt for camera
// permissions, but not on subsequent requests, so check to see if we'll need to accept it // permissions, but not on subsequent requests, so check to see if we'll need to accept it
// after requesting the session. // after requesting the session.
boolean expectPermissionPrompt = arSessionRequestWouldTriggerPermissionPrompt(webContents); boolean expectPermissionPrompt = permissionRequestWouldTriggerPrompt("camera", webContents);
// TODO(bsheedy): Rename enterPresentation since it's used for both presentation and AR? // TODO(bsheedy): Rename enterPresentation since it's used for both presentation and AR?
enterSessionWithUserGesture(webContents); enterSessionWithUserGesture(webContents);
if (expectPermissionPrompt) { if (expectPermissionPrompt) {
// Wait for the permission prompt to appear. PermissionUtils.waitForPermissionPrompt();
CriteriaHelper.pollUiThread(() -> { PermissionUtils.acceptPermissionPrompt();
return PermissionDialogController.getInstance().getCurrentDialogForTesting()
!= null;
}, "Camera permission prompt did not appear");
// Accept the permission prompt.
ThreadUtils.runOnUiThreadBlocking(() -> {
PermissionDialogController.getInstance()
.getCurrentDialogForTesting()
.getButton(DialogInterface.BUTTON_POSITIVE)
.performClick();
});
} }
pollJavaScriptBooleanOrFail("sessionInfos[sessionTypes.AR].currentSession != null", pollJavaScriptBooleanOrFail("sessionInfos[sessionTypes.AR].currentSession != null",
POLL_TIMEOUT_LONG_MS, webContents); POLL_TIMEOUT_LONG_MS, webContents);
} }
/**
* Helper function to run arSessionRequestWouldTriggerPermissionPrompt with the first tab's
* WebContents.
*
* @return True if an AR session request will cause a permission prompt, false otherwise.
*/
public boolean arSessionRequestWouldTriggerPermissionPrompt() {
return arSessionRequestWouldTriggerPermissionPrompt(mFirstTabWebContents);
}
/** /**
* Exits a WebXR AR session. * Exits a WebXR AR session.
* *
......
...@@ -98,6 +98,22 @@ public abstract class XrTestFramework { ...@@ -98,6 +98,22 @@ public abstract class XrTestFramework {
return "/" + TEST_DIR + "/html/" + testName + ".html"; return "/" + TEST_DIR + "/html/" + testName + ".html";
} }
/**
* Checks whether a request for the given permission would trigger a permission prompt.
*
* @param permission The name of the permission to check.
* @param webContents The WebContents to run the JavaScript in.
* @return True if the permission request would trigger a prompt, false otherwise.
*/
public static boolean permissionRequestWouldTriggerPrompt(
String permission, WebContents webContents) {
runJavaScriptOrFail("checkPermissionRequestWouldTriggerPrompt('" + permission + "')",
POLL_TIMEOUT_SHORT_MS, webContents);
pollJavaScriptBooleanOrFail("wouldPrompt !== null", POLL_TIMEOUT_SHORT_MS, webContents);
return Boolean.valueOf(
runJavaScriptOrFail("wouldPrompt", POLL_TIMEOUT_SHORT_MS, webContents));
}
/** /**
* Helper function to run the given JavaScript, return the return value, and fail if a * Helper function to run the given JavaScript, return the return value, and fail if a
* timeout/interrupt occurs so we don't have to catch or declare exceptions all the time. * timeout/interrupt occurs so we don't have to catch or declare exceptions all the time.
...@@ -375,6 +391,16 @@ public abstract class XrTestFramework { ...@@ -375,6 +391,16 @@ public abstract class XrTestFramework {
return result; return result;
} }
/**
* Helper method to run permissionRequestWouldTriggerPrompt with the first tab's WebContents.
*
* @param permission The name of the permission to check.
* @return True if the permission request would trigger a prompt, false otherwise.
*/
public boolean permissionRequestWouldTriggerPrompt(String permission) {
return permissionRequestWouldTriggerPrompt(permission, mFirstTabWebContents);
}
/** /**
* Helper method to run runJavaScriptOrFail with the first tab's WebContents. * Helper method to run runJavaScriptOrFail with the first tab's WebContents.
* *
......
// Copyright 2018 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.util;
import android.content.DialogInterface;
import org.chromium.base.ThreadUtils;
import org.chromium.chrome.browser.permissions.PermissionDialogController;
import org.chromium.content.browser.test.util.CriteriaHelper;
/**
* Utility class for interacting with permission prompts outside of the VR Browser. For interaction
* in the VR Browser, see NativeUiUtils.
*/
public class PermissionUtils {
/**
* Blocks until a permission prompt appears.
*/
public static void waitForPermissionPrompt() {
CriteriaHelper.pollUiThread(() -> {
return PermissionDialogController.getInstance().getCurrentDialogForTesting() != null;
}, "Permission prompt did not appear in allotted time");
}
/**
* Accepts the currently displayed permission prompt.
*/
public static void acceptPermissionPrompt() {
ThreadUtils.runOnUiThreadBlocking(() -> {
PermissionDialogController.getInstance()
.getCurrentDialogForTesting()
.getButton(DialogInterface.BUTTON_POSITIVE)
.performClick();
});
}
/**
* Denies the currently displayed permission prompt.
*/
public static void denyPermissionPrompt() {
ThreadUtils.runOnUiThreadBlocking(() -> {
PermissionDialogController.getInstance()
.getCurrentDialogForTesting()
.getButton(DialogInterface.BUTTON_NEGATIVE)
.performClick();
});
}
}
\ No newline at end of file
<!doctype html>
<!--
Tests that permissions granted in 2D Chrome persist in the VR Browser.
-->
<html>
<body>
<script src="../../../../../../third_party/WebKit/LayoutTests/resources/testharness.js"></script>
<script src="../resources/webxr_e2e.js"></script>
<script>
var t = async_test("Permissions granted in 2D persist in VR Browser");
function stepRequestPermission() {
navigator.getUserMedia(
{audio: true, video: true},
() => {
t.done();
},
(err) => {
t.step(() => {
assert_unreached("Permission request rejected: " + err);
});
});
}
</script>
</body>
</html>
...@@ -6,11 +6,21 @@ var testPassed = false; ...@@ -6,11 +6,21 @@ var testPassed = false;
var resultString = ""; var resultString = "";
var javascriptDone = false; var javascriptDone = false;
var initializationSteps = {load: false}; var initializationSteps = {load: false};
var wouldPrompt = null;
function finishJavaScriptStep() { function finishJavaScriptStep() {
javascriptDone = true; javascriptDone = true;
} }
function checkPermissionRequestWouldTriggerPrompt(permissionName) {
wouldPrompt = null;
navigator.permissions.query({ name: permissionName }).then( (p) => {
wouldPrompt = p.state == 'prompt';
}, (err) => {
throw 'Permission query rejected: ' + err;
});
}
// Used to check when JavaScript is in an acceptable state to start testing // Used to check when JavaScript is in an acceptable state to start testing
// after a page load, as Chrome thinking that the page has finished loading // after a page load, as Chrome thinking that the page has finished loading
// is not always sufficient. By default waits until the load event is fired. // is not always sufficient. By default waits until the load event is fired.
......
...@@ -169,15 +169,6 @@ function onXRFrame(t, frame) { ...@@ -169,15 +169,6 @@ function onXRFrame(t, frame) {
hasPresentedFrame = true; hasPresentedFrame = true;
} }
function checkIfArSessionWouldTriggerPermissionPrompt() {
arSessionRequestWouldTriggerPermissionPrompt = null;
navigator.permissions.query({name: 'camera'}).then( (permission) => {
arSessionRequestWouldTriggerPermissionPrompt = permission.state == 'prompt';
}, () => {
throw 'Permission query rejected';
});
}
// Try to get an XRDevice and set up a non-immersive session with it // Try to get an XRDevice and set up a non-immersive session with it
if (navigator.xr) { if (navigator.xr) {
navigator.xr.requestDevice().then( (device) => { navigator.xr.requestDevice().then( (device) => {
......
...@@ -6,11 +6,21 @@ var testPassed = false; ...@@ -6,11 +6,21 @@ var testPassed = false;
var resultString = ""; var resultString = "";
var javascriptDone = false; var javascriptDone = false;
var initializationSteps = {load: false}; var initializationSteps = {load: false};
var wouldPrompt = null;
function finishJavaScriptStep() { function finishJavaScriptStep() {
javascriptDone = true; javascriptDone = true;
} }
function checkPermissionRequestWouldTriggerPrompt(permissionName) {
wouldPrompt = null;
navigator.permissions.query({ name: permissionName }).then( (p) => {
wouldPrompt = p.state == 'prompt';
}, (err) => {
throw 'Permission query rejected: ' + err;
});
}
// Used to check when JavaScript is in an acceptable state to start testing // Used to check when JavaScript is in an acceptable state to start testing
// after a page load, as Chrome thinking that the page has finished loading // after a page load, as Chrome thinking that the page has finished loading
// is not always sufficient. By default waits until the load event is fired. // is not always sufficient. By default waits until the load event is fired.
......
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