Commit 7d21d288 authored by Klaus Weidner's avatar Klaus Weidner Committed by Commit Bot

WebXR DOM overlay: compositing changes

This CL makes the needed paint / compositing changes to make the DOM overlay
element appear transparent.

Split out from https://crrev.com/c/1741008

Bug: 991747
Change-Id: I6461c095becb5b2712942521b33338bdaf5ccfff
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1802674
Commit-Queue: Klaus Weidner <klausw@chromium.org>
Reviewed-by: default avatarXianzhu Wang <wangxianzhu@chromium.org>
Cr-Commit-Position: refs/heads/master@{#703342}
parent eee0ea08
...@@ -3144,6 +3144,14 @@ void Document::SetIsImmersiveArOverlay(bool val) { ...@@ -3144,6 +3144,14 @@ void Document::SetIsImmersiveArOverlay(bool val) {
// the fullscreened element and its backdrop transparent. // the fullscreened element and its backdrop transparent.
documentElement()->PseudoStateChanged( documentElement()->PseudoStateChanged(
CSSSelector::kPseudoXrImmersiveDomOverlay); CSSSelector::kPseudoXrImmersiveDomOverlay);
// Ensure that the graphics layer tree gets fully rebuilt on changes,
// similar to HTMLVideoElement::DidEnterFullscreen(). This may not be
// strictly necessary if the compositing changes are based on visibility
// settings, but helps ensure consistency in case it's changed to
// detaching layers or re-rooting the graphics layer tree.
GetLayoutView()->Compositor()->SetNeedsCompositingUpdate(
kCompositingUpdateRebuildTree);
} }
} }
......
...@@ -8478,6 +8478,113 @@ TEST_F(WebFrameTest, OverlayFullscreenVideoInIframe) { ...@@ -8478,6 +8478,113 @@ TEST_F(WebFrameTest, OverlayFullscreenVideoInIframe) {
EXPECT_EQ(SkColorGetA(layer_tree_host->background_color()), SK_AlphaOPAQUE); EXPECT_EQ(SkColorGetA(layer_tree_host->background_color()), SK_AlphaOPAQUE);
} }
TEST_F(WebFrameTest, WebXrImmersiveOverlay) {
RegisterMockedHttpURLLoad("webxr_overlay.html");
frame_test_helpers::TestWebWidgetClient web_widget_client;
frame_test_helpers::WebViewHelper web_view_helper;
WebViewImpl* web_view_impl = web_view_helper.InitializeAndLoad(
base_url_ + "webxr_overlay.html", nullptr, nullptr, &web_widget_client);
web_view_helper.Resize(WebSize(640, 480));
// Ensure that the local frame view has a paint artifact compositor. It's
// created lazily, and doing so after entering fullscreen would undo the
// overlay layer modification.
UpdateAllLifecyclePhases(web_view_impl);
const cc::LayerTreeHost* layer_tree_host =
web_widget_client.layer_tree_host();
LocalFrame* frame = web_view_impl->MainFrameImpl()->GetFrame();
Document* document = frame->GetDocument();
EXPECT_FALSE(document->IsImmersiveArOverlay());
document->SetIsImmersiveArOverlay(true);
EXPECT_TRUE(document->IsImmersiveArOverlay());
Element* overlay = document->getElementById("overlay");
EXPECT_FALSE(Fullscreen::IsFullscreenElement(*overlay));
EXPECT_EQ(SkColorGetA(layer_tree_host->background_color()), SK_AlphaOPAQUE);
// Fullscreen should work without separate user activation while in ArOverlay
// mode.
Fullscreen::RequestFullscreen(*overlay);
web_view_impl->MainFrameWidget()->DidEnterFullscreen();
UpdateAllLifecyclePhases(web_view_impl);
EXPECT_TRUE(Fullscreen::IsFullscreenElement(*overlay));
EXPECT_EQ(SkColorGetA(layer_tree_host->background_color()),
SK_AlphaTRANSPARENT);
GraphicsLayer* inner_layer =
ToLayoutBoxModelObject(
frame->GetDocument()->getElementById("inner")->GetLayoutObject())
->Layer()
->GetCompositedLayerMapping()
->MainGraphicsLayer();
EXPECT_TRUE(inner_layer);
// Verify that the overlay layer and inner layers are present in the painted
// graphics layer tree and that the non-overlay graphics layers (if any) don't
// paint anything. The goal is that the body text and sibling div content
// should not be visible. This test should be independent of the mechanism
// used to accomplish this, i.e. it could be done by deleting layers, marking
// them as not drawing, or by overriding the paint root.
GraphicsLayer* overlay_layer =
ToLayoutBoxModelObject(overlay->GetLayoutObject())
->Layer()
->GetCompositedLayerMapping()
->MainGraphicsLayer();
EXPECT_TRUE(overlay_layer);
GraphicsLayer* root_layer =
frame->View()->GetLayoutView()->Compositor()->PaintRootGraphicsLayer();
EXPECT_TRUE(root_layer);
int actively_painting_layers = 0;
bool found_overlay_layer = false;
bool found_inner_layer = false;
ForAllGraphicsLayers(*root_layer, [&](GraphicsLayer& layer) -> bool {
// The overlay layers is expected to be present, but don't recurse into it.
// (This also skips the inner layer which is a child of the overlay layer.)
if (&layer == overlay_layer) {
found_overlay_layer = true;
return false;
}
if (&layer == inner_layer) {
// This shouldn't happen, the inner layer must remain inside the overlay
// layer.
found_inner_layer = true;
}
if (!layer.PaintsContentOrHitTest() || !layer.HasLayerState() ||
!layer.DrawsContent()) {
// Recurse into non-drawing layers, but don't check if they paint.
return true;
}
layer.CapturePaintRecord();
if (layer.GetPaintController().GetDisplayItemList().size() > 0 ||
layer.GetPaintController().PaintChunks().size() > 0)
++actively_painting_layers;
return true;
});
EXPECT_EQ(actively_painting_layers, 0);
EXPECT_TRUE(found_overlay_layer);
// Check for the inner layer separately, the previous recursion was supposed
// to skip it due to being a child of the overlay layer.
EXPECT_FALSE(found_inner_layer);
ForAllGraphicsLayers(*root_layer, [&](GraphicsLayer& layer) -> bool {
if (&layer == inner_layer) {
found_inner_layer = true;
return false;
}
return true;
});
EXPECT_TRUE(found_inner_layer);
web_view_impl->MainFrameWidget()->DidExitFullscreen();
UpdateAllLifecyclePhases(web_view_impl);
EXPECT_FALSE(Fullscreen::IsFullscreenElement(*overlay));
EXPECT_EQ(SkColorGetA(layer_tree_host->background_color()), SK_AlphaOPAQUE);
document->SetIsImmersiveArOverlay(false);
}
TEST_F(WebFrameTest, LayoutBlockPercentHeightDescendants) { TEST_F(WebFrameTest, LayoutBlockPercentHeightDescendants) {
RegisterMockedHttpURLLoad("percent-height-descendants.html"); RegisterMockedHttpURLLoad("percent-height-descendants.html");
frame_test_helpers::WebViewHelper web_view_helper; frame_test_helpers::WebViewHelper web_view_helper;
......
...@@ -6,8 +6,10 @@ ...@@ -6,8 +6,10 @@
#include "third_party/blink/renderer/core/css/css_property_names.h" #include "third_party/blink/renderer/core/css/css_property_names.h"
#include "third_party/blink/renderer/core/dom/document.h" #include "third_party/blink/renderer/core/dom/document.h"
#include "third_party/blink/renderer/core/dom/node.h"
#include "third_party/blink/renderer/core/frame/local_frame_view.h" #include "third_party/blink/renderer/core/frame/local_frame_view.h"
#include "third_party/blink/renderer/core/frame/settings.h" #include "third_party/blink/renderer/core/frame/settings.h"
#include "third_party/blink/renderer/core/fullscreen/fullscreen.h"
#include "third_party/blink/renderer/core/layout/layout_view.h" #include "third_party/blink/renderer/core/layout/layout_view.h"
#include "third_party/blink/renderer/core/page/page.h" #include "third_party/blink/renderer/core/page/page.h"
#include "third_party/blink/renderer/core/paint/paint_layer.h" #include "third_party/blink/renderer/core/paint/paint_layer.h"
...@@ -182,6 +184,15 @@ CompositingReasons CompositingReasonFinder::NonStyleDeterminedDirectReasons( ...@@ -182,6 +184,15 @@ CompositingReasons CompositingReasonFinder::NonStyleDeterminedDirectReasons(
layer.CompositingContainer()->GetLayoutObject().IsVideo()) layer.CompositingContainer()->GetLayoutObject().IsVideo())
direct_reasons |= CompositingReason::kVideoOverlay; direct_reasons |= CompositingReason::kVideoOverlay;
// Special case for immersive-ar DOM overlay mode, see also
// PaintLayerCompositor::ApplyXrImmersiveDomOverlayIfNeeded()
if (const Node* node = layer.GetLayoutObject().GetNode()) {
if (node->IsElementNode() && node->GetDocument().IsImmersiveArOverlay() &&
node == Fullscreen::FullscreenElementFrom(node->GetDocument())) {
direct_reasons |= CompositingReason::kImmersiveArOverlay;
}
}
if (layer.IsRootLayer() && if (layer.IsRootLayer() &&
(RequiresCompositingForScrollableFrame(*layout_object.View()) || (RequiresCompositingForScrollableFrame(*layout_object.View()) ||
layout_object.GetFrame()->IsLocalRoot())) { layout_object.GetFrame()->IsLocalRoot())) {
......
...@@ -725,10 +725,53 @@ GraphicsLayer* PaintLayerCompositor::RootGraphicsLayer() const { ...@@ -725,10 +725,53 @@ GraphicsLayer* PaintLayerCompositor::RootGraphicsLayer() const {
return nullptr; return nullptr;
} }
GraphicsLayer* PaintLayerCompositor::GetXrImmersiveDomOverlayLayer() const {
// immersive-ar DOM overlay mode is very similar to fullscreen video, using
// the AR camera image instead of a video element as a background that's
// separately composited in the browser. The fullscreened DOM content is shown
// on top of that, same as HTML video controls.
//
// The normal fullscreen mode assumes an opaque background, and this doesn't
// work for this use case since this mode uses a transparent background, so
// the non-fullscreened content would remain visible. Fix this by ensuring
// that the fullscreened element has its own layer and using that layer as the
// paint root. (This is different from the fullscreen video overlay which
// detaches layers other than the video layer. The overall effect is the same,
// but it seems safer since it avoids unattached leftover layers.)
DCHECK(IsMainFrame());
DCHECK(layout_view_.GetDocument().IsImmersiveArOverlay());
Element* fullscreen_element =
Fullscreen::FullscreenElementFrom(layout_view_.GetDocument());
if (!fullscreen_element)
return nullptr;
LayoutBoxModelObject* box = fullscreen_element->GetLayoutBoxModelObject();
if (!box) {
// Currently, only HTML fullscreen elements are supported for this mode,
// not others such as SVG or MathML.
DVLOG(1) << "no LayoutBoxModelObject for element " << fullscreen_element;
return nullptr;
}
// The fullscreen element will be in its own layer due to
// CompositingReasonFinder treating this scenario as a direct_reason.
PaintLayer* layer = box->Layer();
DCHECK(layer);
GraphicsLayer* full_screen_layer = layer->GraphicsLayerBacking(box);
return full_screen_layer;
}
GraphicsLayer* PaintLayerCompositor::PaintRootGraphicsLayer() const { GraphicsLayer* PaintLayerCompositor::PaintRootGraphicsLayer() const {
if (layout_view_.GetDocument().GetPage()->GetChromeClient().IsPopup()) if (layout_view_.GetDocument().GetPage()->GetChromeClient().IsPopup())
return RootGraphicsLayer(); return RootGraphicsLayer();
if (IsMainFrame() && layout_view_.GetDocument().IsImmersiveArOverlay()) {
GraphicsLayer* overlay_layer = GetXrImmersiveDomOverlayLayer();
if (overlay_layer)
return overlay_layer;
}
// Start painting at the root graphics layer of the inner viewport which is an // Start painting at the root graphics layer of the inner viewport which is an
// ancestor of both the main contents layers and the scrollbar layers. // ancestor of both the main contents layers and the scrollbar layers.
if (IsMainFrame() && GetVisualViewport().RootGraphicsLayer()) if (IsMainFrame() && GetVisualViewport().RootGraphicsLayer())
......
...@@ -129,7 +129,7 @@ class CORE_EXPORT PaintLayerCompositor { ...@@ -129,7 +129,7 @@ class CORE_EXPORT PaintLayerCompositor {
// Returns the GraphicsLayer we should start painting from. This can differ // Returns the GraphicsLayer we should start painting from. This can differ
// from above in some cases, e.g. when the RootGraphicsLayer is detached and // from above in some cases, e.g. when the RootGraphicsLayer is detached and
// swapped out for an overlay video layer. // swapped out for an overlay video or immersive-ar DOM overlay layer.
GraphicsLayer* PaintRootGraphicsLayer() const; GraphicsLayer* PaintRootGraphicsLayer() const;
static PaintLayerCompositor* FrameContentsCompositor(LayoutEmbeddedContent&); static PaintLayerCompositor* FrameContentsCompositor(LayoutEmbeddedContent&);
...@@ -203,6 +203,8 @@ class CORE_EXPORT PaintLayerCompositor { ...@@ -203,6 +203,8 @@ class CORE_EXPORT PaintLayerCompositor {
GraphicsLayer* ParentForContentLayers( GraphicsLayer* ParentForContentLayers(
GraphicsLayer* child_frame_parent_candidate = nullptr) const; GraphicsLayer* child_frame_parent_candidate = nullptr) const;
GraphicsLayer* GetXrImmersiveDomOverlayLayer() const;
LayoutView& layout_view_; LayoutView& layout_view_;
const bool has_accelerated_compositing_ = true; const bool has_accelerated_compositing_ = true;
......
<!DOCTYPE html>
<body style="border: 10px solid red">
"Body text"
<div id="overlay">
"Overlay text"
<div id="inner" style="will-change: transform;
transform: translate(20px, 20px);
background: blue;
width: 150px;
height: 50px">
"Inner text"
</div>
</div>
<div id="other" style="will-change: transform; transform: translate(1px, 1px);">
"Other text"
</div>
</body>
...@@ -36,6 +36,8 @@ constexpr CompositingReasonStringMap kCompositingReasonsStringMap[] = { ...@@ -36,6 +36,8 @@ constexpr CompositingReasonStringMap kCompositingReasonsStringMap[] = {
{CompositingReason::kActiveBackdropFilterAnimation, {CompositingReason::kActiveBackdropFilterAnimation,
"activeBackdropFilterAnimation", "activeBackdropFilterAnimation",
"Has an active accelerated backdrop filter animation or transition"}, "Has an active accelerated backdrop filter animation or transition"},
{CompositingReason::kImmersiveArOverlay, "immersiveArOverlay",
"Is DOM overlay for WebXR immersive-ar mode"},
{CompositingReason::kScrollDependentPosition, "scrollDependentPosition", {CompositingReason::kScrollDependentPosition, "scrollDependentPosition",
"Is fixed or sticky position"}, "Is fixed or sticky position"},
{CompositingReason::kOverflowScrollingTouch, "overflowScrollingTouch", {CompositingReason::kOverflowScrollingTouch, "overflowScrollingTouch",
......
...@@ -27,6 +27,7 @@ using CompositingReasons = uint64_t; ...@@ -27,6 +27,7 @@ using CompositingReasons = uint64_t;
V(ActiveOpacityAnimation) \ V(ActiveOpacityAnimation) \
V(ActiveFilterAnimation) \ V(ActiveFilterAnimation) \
V(ActiveBackdropFilterAnimation) \ V(ActiveBackdropFilterAnimation) \
V(ImmersiveArOverlay) \
V(ScrollDependentPosition) \ V(ScrollDependentPosition) \
V(OverflowScrollingTouch) \ V(OverflowScrollingTouch) \
V(OverflowScrollingParent) \ V(OverflowScrollingParent) \
...@@ -127,8 +128,8 @@ class PLATFORM_EXPORT CompositingReason { ...@@ -127,8 +128,8 @@ class PLATFORM_EXPORT CompositingReason {
kComboAllDirectNonStyleDeterminedReasons = kComboAllDirectNonStyleDeterminedReasons =
kVideo | kCanvas | kPlugin | kIFrame | kOverflowScrollingParent | kVideo | kCanvas | kPlugin | kIFrame | kOverflowScrollingParent |
kOutOfFlowClipping | kVideoOverlay | kRoot | kRootScroller | kOutOfFlowClipping | kVideoOverlay | kImmersiveArOverlay | kRoot |
kScrollDependentPosition, kRootScroller | kScrollDependentPosition,
kComboAllDirectReasons = kComboAllDirectStyleDeterminedReasons | kComboAllDirectReasons = kComboAllDirectStyleDeterminedReasons |
kComboAllDirectNonStyleDeterminedReasons, kComboAllDirectNonStyleDeterminedReasons,
......
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