Commit fe88da84 authored by Klaus Weidner's avatar Klaus Weidner Committed by Commit Bot

WebXR: Fix DOM overlay for out of process iframes (OOPIF)

Propagate the "is_xr_overlay" fullscreen request type flag for OOPIF
requests via mojo, and ensure that visibility is set correctly for
out of process container iframe elements.

Note for testing: Currently, Chrome on Android only enforces site
isolation for sites with login active, and not on low memory devices.
Run with the --site-per-process command line flag to override this.

Bug: 1101193
Change-Id: Ic8736e90f28fbda2d6cb1f665cbd0804c3f38cc3
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2283968
Commit-Queue: Klaus Weidner <klausw@chromium.org>
Reviewed-by: default avatarPhilip Jägenstedt <foolip@chromium.org>
Reviewed-by: default avatarPhilip Rogers <pdr@chromium.org>
Reviewed-by: default avatarDaniel Cheng <dcheng@chromium.org>
Cr-Commit-Position: refs/heads/master@{#798382}
parent 2283f6d8
...@@ -16,5 +16,10 @@ struct FullscreenOptions { ...@@ -16,5 +16,10 @@ struct FullscreenOptions {
// True if this should be treated as a "webkit"-prefixed fullscreen request. // True if this should be treated as a "webkit"-prefixed fullscreen request.
// These don't return promises, and fire "webkit"-prefixed events. // These don't return promises, and fire "webkit"-prefixed events.
bool is_prefixed = false; bool is_prefixed = false;
// True if this fullscreen request is for WebXR DOM Overlay mode. In that case,
// if a subframe is requesting fullscreen mode, the parent frames need to use
// a transparent backdrop to keep content visible.
bool is_xr_overlay = false;
}; };
...@@ -160,13 +160,16 @@ void FullscreenController::EnterFullscreen(LocalFrame& frame, ...@@ -160,13 +160,16 @@ void FullscreenController::EnterFullscreen(LocalFrame& frame,
// fullscreen events. // fullscreen events.
fullscreen_options->is_prefixed = fullscreen_options->is_prefixed =
request_type & FullscreenRequestType::kPrefixed; request_type & FullscreenRequestType::kPrefixed;
fullscreen_options->is_xr_overlay =
request_type & FullscreenRequestType::kForXrOverlay;
#if DCHECK_IS_ON() #if DCHECK_IS_ON()
DVLOG(2) << __func__ << ": request_type=" DVLOG(2) << __func__ << ": request_type="
<< FullscreenRequestTypeToDebugString(request_type) << FullscreenRequestTypeToDebugString(request_type)
<< " fullscreen_options={display_id=" << " fullscreen_options={display_id="
<< fullscreen_options->display_id << fullscreen_options->display_id
<< ", is_prefixed=" << fullscreen_options->is_prefixed << "}"; << ", is_prefixed=" << fullscreen_options->is_prefixed
<< ", is_xr_overlay=" << fullscreen_options->is_xr_overlay << "}";
#endif #endif
// Don't send redundant EnterFullscreen message to the browser for the // Don't send redundant EnterFullscreen message to the browser for the
......
...@@ -4706,10 +4706,19 @@ static PaintLayer* GetXrOverlayLayer(Document& document) { ...@@ -4706,10 +4706,19 @@ static PaintLayer* GetXrOverlayLayer(Document& document) {
} }
PaintLayer* LocalFrameView::GetFullScreenOverlayLayer() const { PaintLayer* LocalFrameView::GetFullScreenOverlayLayer() const {
Document* doc = frame_->GetDocument();
DCHECK(doc);
// For WebXR DOM Overlay, the fullscreen overlay layer comes from either the
// overlay element itself, or from an iframe element if the overlay element is
// in an OOPIF. This layer is needed even for non-main-frame scenarios to
// ensure the background remains transparent.
if (doc->IsXrOverlay())
return GetXrOverlayLayer(*doc);
// Fullscreen overlay video layers are only used for the main frame.
DCHECK(frame_->IsMainFrame()); DCHECK(frame_->IsMainFrame());
if (auto* layer = GetXrOverlayLayer(*frame_->GetDocument())) return GetFullScreenOverlayVideoLayer(*doc);
return layer;
return GetFullScreenOverlayVideoLayer(*frame_->GetDocument());
} }
} // namespace blink } // namespace blink
...@@ -420,6 +420,8 @@ void RemoteFrame::WillEnterFullscreen( ...@@ -420,6 +420,8 @@ void RemoteFrame::WillEnterFullscreen(
FullscreenRequestType request_type = FullscreenRequestType request_type =
(request_options->is_prefixed ? FullscreenRequestType::kPrefixed (request_options->is_prefixed ? FullscreenRequestType::kPrefixed
: FullscreenRequestType::kUnprefixed) | : FullscreenRequestType::kUnprefixed) |
(request_options->is_xr_overlay ? FullscreenRequestType::kForXrOverlay
: FullscreenRequestType::kNull) |
FullscreenRequestType::kForCrossProcessDescendant; FullscreenRequestType::kForCrossProcessDescendant;
Fullscreen::RequestFullscreen(*owner_element, FullscreenOptions::Create(), Fullscreen::RequestFullscreen(*owner_element, FullscreenOptions::Create(),
......
...@@ -510,9 +510,14 @@ GraphicsLayer* PaintLayerCompositor::RootGraphicsLayer() const { ...@@ -510,9 +510,14 @@ GraphicsLayer* PaintLayerCompositor::RootGraphicsLayer() const {
} }
GraphicsLayer* PaintLayerCompositor::PaintRootGraphicsLayer() const { GraphicsLayer* PaintLayerCompositor::PaintRootGraphicsLayer() const {
if (layout_view_->GetDocument().GetPage()->GetChromeClient().IsPopup() || // Shortcut: skip the fullscreen checks for popups, and for not-main-frame
!IsMainFrame()) // ordinary fullscreen mode. Don't use the shortcut for WebXR DOM overlay mode
// since that requires ancestor frames to be rendered as transparent.
Document& doc = layout_view_->GetDocument();
if (doc.GetPage()->GetChromeClient().IsPopup() ||
(!IsMainFrame() && !doc.IsXrOverlay())) {
return RootGraphicsLayer(); return RootGraphicsLayer();
}
// Start from the full screen overlay layer if exists. Other layers will be // Start from the full screen overlay layer if exists. Other layers will be
// skipped during painting. // skipped during painting.
......
...@@ -63,6 +63,21 @@ class ChromeXRTest { ...@@ -63,6 +63,21 @@ class ChromeXRTest {
} }
simulateUserActivation(callback) { simulateUserActivation(callback) {
if (window.top !== window) {
// test_driver.click only works for the toplevel frame. This alternate
// Chrome-specific method is sufficient for starting an XR session in an
// iframe, and is used in platform-specific tests.
//
// TODO(https://github.com/web-platform-tests/wpt/issues/20282): use
// a cross-platform method if available.
xr_debug('simulateUserActivation', 'use eventSender');
document.addEventListener('click', callback);
eventSender.mouseMoveTo(0, 0);
eventSender.mouseDown();
eventSender.mouseUp();
document.removeEventListener('click', callback);
return;
}
const button = document.createElement('button'); const button = document.createElement('button');
button.textContent = 'click to continue test'; button.textContent = 'click to continue test';
button.style.display = 'block'; button.style.display = 'block';
......
<!DOCTYPE html>
<head>
<script src="/resources/testharness.js"></script>
<!-- testharnessreport.js is loaded dynamically in the parent only -->
<script src="/webxr/resources/webxr_util.js"></script>
<script src="/webxr/resources/webxr_test_constants.js"></script>
<script src="/webxr/resources/webxr_test_asserts.js"></script>
</head>
<body>
<script>
const fakeDeviceInitParams = {
supportedModes: ["immersive-ar"],
views: VALID_VIEWS,
viewerOrigin: IDENTITY_TRANSFORM,
supportedFeatures: ALL_FEATURES,
};
// Arbitrary message for notifying the parent frame
const FRAME_SESSION_COMPLETE = 'iframe done';
// This test runs inside the iframe, verifies that the session
// is working as expected, and sends a completion message to the parent.
const testBasicProperties = function(overlayElement, session,
fakeDeviceController, t) {
assert_equals(session.mode, 'immersive-ar');
assert_not_equals(session.environmentBlendMode, 'opaque');
assert_true(overlayElement != null);
assert_true(overlayElement instanceof Element);
// Verify that the DOM overlay type is one of the known types.
assert_in_array(session.domOverlayState.type,
["screen", "floating", "head-locked"]);
// Verify SameObject property for domOverlayState
assert_true(session.domOverlayState === session.domOverlayState);
// The overlay element should have a transparent background.
assert_equals(window.getComputedStyle(overlayElement).backgroundColor,
'rgba(0, 0, 0, 0)');
// Check that the pseudostyle is set.
assert_equals(document.querySelector(':xr-overlay'), overlayElement);
return new Promise((resolve) => {
// Wait for a couple of animation frames to give the optional fullscreen
// API integration test time to complete.
session.requestAnimationFrame(() => {
session.requestAnimationFrame(() => {
resolve();
parent.postMessage(FRAME_SESSION_COMPLETE, '*');
});
});
});
};
const run_parent = () => {
// Only the parent should use testharnessreport, load it dynamically.
const script = document.createElement('script');
script.src = '/resources/testharnessreport.js';
script.async = false;
document.head.append(script);
const frame = document.createElement('iframe');
// Optional test - if the WebXR DOM overlay is based on fullscreen
// mode (it doesn't have to be), resulting in a fullscreen event,
// check that the iframe has a transparent background.
const testFullscreen = async_test("Wait for fullscreenchange");
let gotFullscreenChange = false;
const onFullscreenChange = testFullscreen.step_func_done(() => {
gotFullscreenChange = true;
// The iframe element should have the :xr-overlay pseudostyle
// and a transparent background.
assert_equals(document.querySelector(':xr-overlay'), frame);
assert_equals(window.getComputedStyle(frame).backgroundColor,
'rgba(0, 0, 0, 0)');
});
document.addEventListener('fullscreenchange', onFullscreenChange);
// For the iframe, load the current file from an alternate domain.
frame.src = 'https://{{hosts[alt][www]}}:{{ports[https][1]}}' +
document.location.pathname;
frame.allow = 'xr-spatial-tracking; fullscreen';
document.body.appendChild(frame);
window.addEventListener('message', function(ev) {
if (ev.data == FRAME_SESSION_COMPLETE) {
// If there was no fullscreen change event, mark the optional
// fullscreen integration test as a failed optional test, reported
// as NOTRUN by the framework.
if (!gotFullscreenChange) {
testFullscreen.step(() => {
assert_implements_optional(
false, 'WebXR DOM overlay is not using fullscreen events');
testFullscreen.done();
});
}
}
});
// Add the child tests to the overall test suite, reporting results from them
// as part of the overall test result. Note that this call does not wait for
// these tests to complete, it just adds them to the list of pending tests.
fetch_tests_from_window(frame.contentWindow);
};
const run_iframe_child = () => {
// This is the child iframe. Set up and run the test, its result will
// be fetched by the parent.
document.body.appendChild(document.createElement('canvas'));
xr_session_promise_test(
"DOM Overlay in iframe",
testBasicProperties.bind(this, document.body),
fakeDeviceInitParams, 'immersive-ar',
{requiredFeatures: ['dom-overlay'],
domOverlay: { root: document.body } });
};
if (parent === self) {
run_parent();
} else {
run_iframe_child();
}
</script>
</body>
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