Commit 396fda95 authored by Sam Fortiner's avatar Sam Fortiner Committed by Commit Bot

Update fixed-pos bounds expansion for overlap

The max overlap for fixed feature (crrev.com/766618) had an assumption
that the overlap testing rect, as computed by
BoundingBoxForCompositingOverlapTest, is in viewport space and includes
the area that a fixed-pos layer can occupy given any valid scroll
offset. This expansion was limited to just the fixed-pos layer and did
not include out-of-flow composited children of the fixed-pos layer. This
decision to use viewport space for the expanded rect caused an
unrecognized embedding of the scroll offset into the rect position at
compositing inputs time when the expanded bounding rect was computed.
The dependency on scroll offset could cause incorrect overlap test
results when the scroll offset changed and overlap testing re-ran
without a compositing inputs update. This was addressed by moving the
fixed-pos layer's expanded bounds to absolute space in crrev.com/807955.
However, this later revision also caused the children of a fixed-pos
element to change from storing their unexpanded overlap test bounding
rects in viewport space to storing them in absolute space without
expanding their bounds or otherwise compensating for no compositing
inputs update on scroll offset. This effectively introduced a dependency
on the scroll offset at bounds computation time for children of
fixed-pos, leading to crbug.com/1137974 due to incorrect overlap testing
results. This was addressed in m87 by reverting revision
crrev.com/807955.

This change addresses the issue in a different way. The new approach is
as follows:

1. Fixed-pos (and their children) compute non-expanded bounds instead of
expanded bounds in BoundingBoxForCompositingOverlapTest, now renamed to
LocalBoundingBoxForCompositingOverlapTest. These bounds, for fixed-pos
layers, are kept in viewport coords when stored on
AncestorDependentCompositingInputs, keeping them scrolloffset agnostics
as cached. Additionally, by avoiding expanding the bounds, the return
values from (un)ClippedAbsoluteBoundingBox are no longer expanded. These
return values are used by layer squashing sparsity calculations, meaning
this change reverts that calculation to its previous behavior before the
MaxOverlapBoundsForFixed feature was introduced.

2. Conversion to absolute coords and expansion happens at overlap
testing time, instead of compositing inputs update time, avoiding the
problem that was attempted to be addressed via crrev.com/807955.
Additionally, children of fixed-pos elements are also converted from
viewport to absolute space during overlap testing, avoiding the issue
found in crbug.com/1137974.

3. Only the top-most fixed-pos element expands its bounds. Without this
limitation, children of a fixed-pos element would also expand their
bounds and could detect overlap with siblings that they couldn't
actually overlap with. E.g. two absolutely positioned siblings with a
common fixed-pos ancestor do not need to expand their bounds to
determine if they can overlap.

4. Because of the change in number 3, it becomes important that the
fixed-pos layer's bound for overlap includes all that area covered by
the fixed-pos layer and any of its children that may have their own
composition layer. Without this consideration, it's possible for a child
to extend beyond the fixed-pos expanded bounds and not cause something
to composite due to overlap even though it intersects when scroll
offsets change. This would be fixed on a lifecycle update that re-ran
compositing with the new scroll offsets, but is still incorrect
behavior. To address this issue, the overlap testing bounds for the
top-most fixed-pos layer is expanded to include its composited stacking
children.

All of these cases also have tests added to validate them.

Bug: 1124753
Change-Id: I42b22892fb0040bde8cbabb7c85fe30f71f4ee57
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2522407Reviewed-by: default avatarXianzhu Wang <wangxianzhu@chromium.org>
Reviewed-by: default avatarChris Harrelson <chrishtr@chromium.org>
Commit-Queue: Sam Fortiner <samfort@microsoft.com>
Cr-Commit-Position: refs/heads/master@{#827547}
parent fb131969
......@@ -463,13 +463,17 @@ void CompositingInputsUpdater::UpdateAncestorDependentCompositingInputs(
properties.unclipped_absolute_bounding_box =
EnclosingIntRect(geometry_map_->AbsoluteRect(
layer->BoundingBoxForCompositingOverlapTest()));
layer->LocalBoundingBoxForCompositingOverlapTest()));
bool affected_by_scroll = root_layer_->GetScrollableArea() &&
layer->IsAffectedByScrollOf(root_layer_);
// At ths point, |unclipped_absolute_bounding_box| is in viewport space.
// At this point, |unclipped_absolute_bounding_box| is in viewport space.
// To convert to absolute space, add scroll offset for non-fixed layers.
// Content that is not affected by scroll, e.g. fixed-pos content and
// children of that content, stays in viewport space so we can expand its
// bounds during overlap testing without having a dependency on the scroll
// offset at the time these properties are calculated.
if (affected_by_scroll) {
properties.unclipped_absolute_bounding_box.Move(
RoundedIntSize(root_layer_->GetScrollableArea()->GetScrollOffset()));
......
......@@ -352,23 +352,8 @@ void CompositingRequirementsUpdater::UpdateRecursive(
unclipped_descendants.push_back(layer);
}
IntRect abs_bounds = use_clipped_bounding_rect
? layer->ClippedAbsoluteBoundingBox()
: layer->UnclippedAbsoluteBoundingBox();
if (!RuntimeEnabledFeatures::CompositingOptimizationsEnabled()) {
PaintLayer* root_layer = layout_view_.Layer();
// |abs_bounds| does not include root scroller offset. For the purposes
// of overlap, this only matters for fixed-position objects, and their
// relative position to other elements. Therefore, it's still correct to,
// instead of adding scroll to all non-fixed elements, add a reverse scroll
// to ones that are fixed.
if (root_layer->GetScrollableArea() &&
!layer->IsAffectedByScrollOf(root_layer)) {
abs_bounds.Move(
RoundedIntSize(root_layer->GetScrollableArea()->GetScrollOffset()));
}
}
IntRect abs_bounds = layer->ExpandedBoundingBoxForCompositingOverlapTest(
use_clipped_bounding_rect);
absolute_descendant_bounding_box = abs_bounds;
if (layer_can_be_composited && current_recursion_data.testing_overlap_ &&
......
......@@ -409,6 +409,30 @@ bool PaintLayer::IsAffectedByScrollOf(const PaintLayer* ancestor) const {
return current_layer == ancestor;
}
bool PaintLayer::IsTopMostNotAffectedByScrollOf(
const PaintLayer* ancestor) const {
// Returns true if |this| is the top-most fixed-pos layer between |this|
// (inclusive) and |ancestor.
// Should only call this method for layers that we already know are not
// affected by the scroll offset of the ancestor (implying this element or
// an ancestor must be fixed).
DCHECK(!IsAffectedByScrollOf(ancestor));
// Only fixed-pos elements can be top-most.
if (!GetLayoutObject().IsFixedPositioned())
return false;
PaintLayer* curr = Parent();
while (curr && curr != ancestor) {
if (curr->GetLayoutObject().IsFixedPositioned())
return false;
curr = curr->Parent();
}
return true;
}
void PaintLayer::UpdateTransformationMatrix() {
if (TransformationMatrix* transform = Transform()) {
LayoutBox* box = GetLayoutBox();
......@@ -1079,7 +1103,7 @@ void PaintLayer::SetChildNeedsCompositingInputsUpdateUpToAncestor(
const IntRect PaintLayer::ClippedAbsoluteBoundingBox() const {
if (RuntimeEnabledFeatures::CompositingOptimizationsEnabled()) {
PhysicalRect mapping_rect = BoundingBoxForCompositingOverlapTest();
PhysicalRect mapping_rect = LocalBoundingBoxForCompositingOverlapTest();
GetLayoutObject().MapToVisualRectInAncestorSpace(
GetLayoutObject().View(), mapping_rect, kUseGeometryMapper);
return EnclosingIntRect(mapping_rect);
......@@ -1091,7 +1115,7 @@ const IntRect PaintLayer::ClippedAbsoluteBoundingBox() const {
const IntRect PaintLayer::UnclippedAbsoluteBoundingBox() const {
if (RuntimeEnabledFeatures::CompositingOptimizationsEnabled()) {
return EnclosingIntRect(GetLayoutObject().LocalToAbsoluteRect(
BoundingBoxForCompositingOverlapTest(),
LocalBoundingBoxForCompositingOverlapTest(),
kUseGeometryMapperMode | kIgnoreScrollOffset));
} else {
return GetAncestorDependentCompositingInputs()
......@@ -2503,7 +2527,7 @@ void PaintLayer::UpdateFilterReferenceBox() {
return;
PhysicalRect result = LocalBoundingBox();
ExpandRectForStackingChildren(
*this, result, PaintLayer::kIncludeTransformsAndCompositedChildLayers);
*this, result, kIncludeTransforms | kIncludeCompositedChildLayers);
FloatRect reference_box = FloatRect(result);
float zoom = GetLayoutObject().StyleRef().EffectiveZoom();
......@@ -2645,7 +2669,15 @@ PhysicalRect PaintLayer::FragmentsBoundingBox(
return result;
}
PhysicalRect PaintLayer::BoundingBoxForCompositingOverlapTest() const {
PhysicalRect PaintLayer::LocalBoundingBoxForCompositingOverlapTest() const {
// Returns the bounding box, in the local coordinate system for this layer,
// for the content that this paint layer is responsible for compositing. This
// doesn't include the content painted by self-painting descendants such as
// composited absolute positioned children. But the bounds is suitable for
// calculations such as squashing sparsity. To get the bounds that includes
// the visible extent of this layer and its children for overlap testing, use
// ExpandedBoundingBoxForCompositingOverlapTest.
// Apply NeverIncludeTransformForAncestorLayer, because the geometry map in
// CompositingInputsUpdater will take care of applying the transform of |this|
// (== the ancestorLayer argument to boundingBoxForCompositing).
......@@ -2664,37 +2696,85 @@ PhysicalRect PaintLayer::BoundingBoxForCompositingOverlapTest() const {
style.BackdropFilter().MapRect(FloatRect(bounding_box)));
}
if (FixedToViewport() && !bounding_box.IsEmpty()) {
DCHECK_EQ(style.GetPosition(), EPosition::kFixed);
// Note that we only expand the bounding box for overlap testing when the
// fixed's containing block is the viewport. This keeps us from expanding
// the bounds when the fixed is a child of an ancestor with transform,
// filters, etc. and the fixed is no longer scroll position dependent.
// Expand the bounding box by the amount that scrolling the
// viewport can expand the area that this fixed-pos element could
// cover. Compute how much we could still scroll in each direction.
// |max_scroll_delta| is the amount we could still scroll in
// increasing offset direction. |min_scroll_delta| is the amount we
// can still scroll in a decreasing scroll offset direction.
PaintLayerScrollableArea* scrollable_area =
GetLayoutObject().View()->GetScrollableArea();
return bounding_box;
}
IntRect PaintLayer::ExpandedBoundingBoxForCompositingOverlapTest(
bool use_clipped_bounding_rect) const {
// Returns the bounding box for this layer and self-painted composited
// children which are otherwise not included in
// LocalBoundingBoxForCompositingOverlapTest. Use the bounds from this layer
// for overlap testing that cares about the bounds of this layer and all its
// children.
IntRect abs_bounds = use_clipped_bounding_rect
? ClippedAbsoluteBoundingBox()
: UnclippedAbsoluteBoundingBox();
PaintLayer* root_layer = GetLayoutObject().View()->Layer();
// |abs_bounds| does not include root scroller offset, as in it's in absolute
// coordinates, for everything but fixed-pos objects (and their children)
// which are in viewport coords. Adjusting these to all be in absolute coords
// happens here. This adjustment is delayed until this point in time as doing
// it during compositing inputs update would embed the scroll offset at the
// time the compositing inputs was ran when converting from viewport to
// absolute, making the resulting rects unusable for any other scroll offset.
if (root_layer->GetScrollableArea() && !abs_bounds.IsEmpty() &&
!IsAffectedByScrollOf(root_layer)) {
PaintLayerScrollableArea* scrollable_area = root_layer->GetScrollableArea();
ScrollOffset current_scroll_offset = scrollable_area->GetScrollOffset();
ScrollOffset max_scroll_delta =
scrollable_area->MaximumScrollOffset() - current_scroll_offset;
ScrollOffset min_scroll_delta =
current_scroll_offset - scrollable_area->MinimumScrollOffset();
bounding_box.Expand(
LayoutRectOutsets(min_scroll_delta.Height(), max_scroll_delta.Width(),
max_scroll_delta.Height(), min_scroll_delta.Width()));
// Move the bounds to absolute coords
if (!RuntimeEnabledFeatures::CompositingOptimizationsEnabled())
abs_bounds.Move(RoundedIntSize(current_scroll_offset));
if (IsTopMostNotAffectedByScrollOf(root_layer)) {
// For overlap testing, expand the rect used for fixed-pos content in
// two ways. First, include any children such that overlap testing
// against the top-most fixed-pos layer is guaranteed to detect any
// overlap where a self-painting composited child of the fixed-pos layer
// exceeds the fixed-pos layer's bounds. Second, expand the rect to
// include the area it could cover if the view were to be scrolled to
// its minimum and maximum extents. This allows skipping overlap testing
// on scroll offset changes. Note that bounds expansion does not happen
// for fixed under a non-view container (under xform or filter for
// example) as those fixed still are affected by the view's scroll offset.
// This is checked for with the IsAffectedByScrollOf call earlier.
// Expand the rect to include children that are not already included in
// |layer|'s bounds.
PhysicalRect children_bounds;
if (!GetLayoutObject().ChildPaintBlockedByDisplayLock()) {
PaintLayerPaintOrderIterator iterator(*this, kAllChildren);
while (PaintLayer* child_layer = iterator.Next()) {
// Note that we intentionally include children irrespective of if they
// are composited or not.
children_bounds.Unite(child_layer->BoundingBoxForCompositingInternal(
*this, this,
kIncludeClips | kIncludeTransforms |
kIncludeCompositedChildLayers));
}
if (!children_bounds.IsEmpty()) {
GetLayoutObject().MapToVisualRectInAncestorSpace(
GetLayoutObject().View(), children_bounds, kUseGeometryMapper);
abs_bounds.Unite(EnclosingIntRect(children_bounds));
}
}
// Expand bounds to include min/max scroll extents
ScrollOffset max_scroll_delta =
scrollable_area->MaximumScrollOffset() - current_scroll_offset;
ScrollOffset min_scroll_delta =
current_scroll_offset - scrollable_area->MinimumScrollOffset();
abs_bounds.Expand(
IntRectOutsets(min_scroll_delta.Height(), max_scroll_delta.Width(),
max_scroll_delta.Height(), min_scroll_delta.Width()));
}
}
return bounding_box;
return abs_bounds;
}
void PaintLayer::ExpandRectForStackingChildren(
const PaintLayer& composited_layer,
PhysicalRect& result,
PaintLayer::CalculateBoundsOptions options) const {
unsigned options) const {
// If we're locked, th en the subtree does not contribute painted output.
// Furthermore, we might not have up-to-date sizing and position information
// in the subtree, so skip recursing into the subtree.
......@@ -2708,31 +2788,32 @@ void PaintLayer::ExpandRectForStackingChildren(
// for this Layer. For example, the bounds of squashed Layers
// will be included in the computation of the appropriate squashing
// GraphicsLayer.
if (options != PaintLayer::CalculateBoundsOptions::
kIncludeTransformsAndCompositedChildLayers &&
child_layer->GetCompositingState() != kNotComposited)
continue;
result.Unite(child_layer->BoundingBoxForCompositingInternal(
composited_layer, this, options));
if ((options & kIncludeCompositedChildLayers) ||
child_layer->GetCompositingState() == kNotComposited) {
result.Unite(child_layer->BoundingBoxForCompositingInternal(
composited_layer, this, options));
}
}
}
PhysicalRect PaintLayer::BoundingBoxForCompositing() const {
return BoundingBoxForCompositingInternal(
*this, nullptr, kIncludeClipsAndMaybeIncludeTransformForAncestorLayer);
*this, nullptr, kIncludeClips | kMaybeIncludeTransformForAncestorLayer);
}
bool PaintLayer::ShouldApplyTransformToBoundingBox(
const PaintLayer& composited_layer,
CalculateBoundsOptions options) const {
unsigned options) const {
DCHECK(!(options & kIncludeTransforms) ||
!(options & kMaybeIncludeTransformForAncestorLayer));
if (!Transform())
return false;
if (options == kIncludeTransformsAndCompositedChildLayers)
if (options & kIncludeTransforms)
return true;
if (PaintsWithTransform(kGlobalPaintNormalPhase)) {
if (this != &composited_layer)
return true;
if (options == kIncludeClipsAndMaybeIncludeTransformForAncestorLayer)
if (options & kMaybeIncludeTransformForAncestorLayer)
return true;
}
return false;
......@@ -2741,7 +2822,7 @@ bool PaintLayer::ShouldApplyTransformToBoundingBox(
PhysicalRect PaintLayer::BoundingBoxForCompositingInternal(
const PaintLayer& composited_layer,
const PaintLayer* stacking_parent,
CalculateBoundsOptions options) const {
unsigned options) const {
DCHECK_GE(GetLayoutObject().GetDocument().Lifecycle().GetState(),
DocumentLifecycle::kInPrePaint);
if (!IsSelfPaintingLayer())
......@@ -2771,14 +2852,11 @@ PhysicalRect PaintLayer::BoundingBoxForCompositingInternal(
return PhysicalRect();
PhysicalRect result;
if (options == kIncludeClipsAndMaybeIncludeTransformForAncestorLayer) {
if (options & kIncludeClips) {
// If there is a clip applied by an ancestor to this PaintLayer but below or
// equal to |ancestorLayer|, apply that clip. This optimizes the size
// of the composited layer to exclude clipped-out regions of descendants.
result = Clipper((GetLayoutObject().GetDocument().Lifecycle().GetState() ==
DocumentLifecycle::kInCompositingAssignmentsUpdate)
? GeometryMapperOption::kUseGeometryMapper
: GeometryMapperOption::kUseGeometryMapper)
result = Clipper(GeometryMapperOption::kUseGeometryMapper)
.LocalClipRect(composited_layer);
result.Intersect(LocalBoundingBox());
......
......@@ -461,12 +461,17 @@ class CORE_EXPORT PaintLayer : public DisplayItemClient {
const PhysicalRect& damage_rect,
const PhysicalOffset& offset_from_root) const;
// MaybeIncludeTransformForAncestorLayer means that a transform on
// |ancestorLayer| may be applied to the bounding box, in particular if
// paintsWithTransform() is true.
enum CalculateBoundsOptions {
kIncludeClipsAndMaybeIncludeTransformForAncestorLayer,
kIncludeTransformsAndCompositedChildLayers,
// Include clips between this layer and its ancestor layer (inclusive).
kIncludeClips = 0x1,
// Include transforms, irrespective of if they are applied via composition
// or painting.
kIncludeTransforms = 0x2,
// Include child layers (recursive) whether composited or not.
kIncludeCompositedChildLayers = 0x4,
// Include transform for the ancestor layer (|composited_layer| in the
// initial call) if |PaintsWithTransform|
kMaybeIncludeTransformForAncestorLayer = 0x8
};
// Bounding box relative to some ancestor layer. Pass offsetFromRoot if known.
......@@ -475,10 +480,11 @@ class CORE_EXPORT PaintLayer : public DisplayItemClient {
PhysicalRect PhysicalBoundingBox(const PaintLayer* ancestor_layer) const;
PhysicalRect FragmentsBoundingBox(const PaintLayer* ancestor_layer) const;
PhysicalRect BoundingBoxForCompositingOverlapTest() const;
PhysicalRect LocalBoundingBoxForCompositingOverlapTest() const;
IntRect ExpandedBoundingBoxForCompositingOverlapTest(
bool use_clipped_bounding_rect) const;
PhysicalRect BoundingBoxForCompositing() const;
// Static position is set in parent's coordinate space.
LayoutUnit StaticInlinePosition() const { return static_inline_position_; }
LayoutUnit StaticBlockPosition() const { return static_block_position_; }
......@@ -804,7 +810,14 @@ class CORE_EXPORT PaintLayer : public DisplayItemClient {
const PaintLayer* nearest_contained_layout_layer = nullptr;
// These two boxes do not include any applicable scroll offset of the
// root PaintLayer.
// root PaintLayer. Note that 'absolute' here is potentially misleading as
// the actual coordinate system depends on if this layer is affected by the
// viewport's scroll offset or not. For content that is not affected by the
// viewport scroll offsets, this ends up being a rect in viewport coords.
// For content that is affected by the viewport's scroll offset this
// coordinate system is in absolute coords.
// Note: This stores LocalBoundingBoxForCompositingOverlapTest and not the
// expanded bounds (ExpandedBoundingBoxForCompositingOverlapTest).
IntRect clipped_absolute_bounding_box;
IntRect unclipped_absolute_bounding_box;
......@@ -1274,18 +1287,20 @@ class CORE_EXPORT PaintLayer : public DisplayItemClient {
needs_paint_phase_float_ |= layer.needs_paint_phase_float_;
}
bool IsTopMostNotAffectedByScrollOf(const PaintLayer* ancestor) const;
void ExpandRectForStackingChildren(const PaintLayer& composited_layer,
PhysicalRect& result,
PaintLayer::CalculateBoundsOptions) const;
unsigned options) const;
// The return value is in the space of |stackingParent|, if non-null, or
// |this| otherwise.
PhysicalRect BoundingBoxForCompositingInternal(
const PaintLayer& composited_layer,
const PaintLayer* stacking_parent,
CalculateBoundsOptions) const;
unsigned options) const;
bool ShouldApplyTransformToBoundingBox(const PaintLayer& composited_layer,
CalculateBoundsOptions) const;
unsigned options) const;
AncestorDependentCompositingInputs& EnsureAncestorDependentCompositingInputs()
const {
......
......@@ -2594,7 +2594,169 @@ TEST_P(PaintLayerTest, InlineWithBackdropFilterHasPaintLayer) {
EXPECT_NE(nullptr, paint_layer);
}
TEST_P(PaintLayerTest, FixedUsesExpandedBoundingBoxForOverlap) {
TEST_P(PaintLayerTest, DirectCompositingReasonsCrossingFrameBoundaries) {
if (RuntimeEnabledFeatures::CompositeAfterPaintEnabled())
return;
SetBodyInnerHTML(R"HTML(
<iframe></iframe>
)HTML");
SetChildFrameHTML(R"HTML(
<div id=target style="position: relative"></div>
)HTML");
UpdateAllLifecyclePhasesForTest();
PaintLayer* target =
To<LayoutBoxModelObject>(
ChildDocument().getElementById("target")->GetLayoutObject())
->Layer();
EXPECT_EQ(
GetDocument().View()->GetLayoutView()->Layer(),
target->EnclosingDirectlyCompositableLayerCrossingFrameBoundaries());
}
TEST_P(PaintLayerTest,
DirectCompositingReasonsCrossingFrameBoundariesCompositedIframe) {
if (RuntimeEnabledFeatures::CompositeAfterPaintEnabled())
return;
SetBodyInnerHTML(R"HTML(
<iframe id="iframe" style="will-change: transform";></iframe>
)HTML");
SetChildFrameHTML(R"HTML(
<div id=target style="position: relative"></div>
)HTML");
UpdateAllLifecyclePhasesForTest();
PaintLayer* target =
To<LayoutBoxModelObject>(
ChildDocument().getElementById("target")->GetLayoutObject())
->Layer();
PaintLayer* iframe =
To<LayoutBoxModelObject>(
GetDocument().getElementById("iframe")->GetLayoutObject())
->Layer();
EXPECT_EQ(
iframe,
target->EnclosingDirectlyCompositableLayerCrossingFrameBoundaries());
}
TEST_P(PaintLayerTest,
DirectCompositingReasonsCrossingFrameBoundariesCompositedParent) {
if (RuntimeEnabledFeatures::CompositeAfterPaintEnabled())
return;
SetBodyInnerHTML(R"HTML(
<iframe></iframe>
)HTML");
SetChildFrameHTML(R"HTML(
<div id="parent" style="will-change: transform">
<div id=target style="position: relative"></div>
</div>
)HTML");
UpdateAllLifecyclePhasesForTest();
PaintLayer* target =
To<LayoutBoxModelObject>(
ChildDocument().getElementById("target")->GetLayoutObject())
->Layer();
PaintLayer* parent =
To<LayoutBoxModelObject>(
ChildDocument().getElementById("parent")->GetLayoutObject())
->Layer();
EXPECT_EQ(
parent,
target->EnclosingDirectlyCompositableLayerCrossingFrameBoundaries());
}
TEST_P(PaintLayerTest, GlobalRootScrollerHitTest) {
SetBodyInnerHTML(R"HTML(
<style>
:root {
clip-path: circle(30%);
background:blue;
transform: rotate(30deg);
transform-style: preserve-3d;
}
#perspective {
perspective:100px;
}
#threedee {
transform: rotate3d(1, 1, 1, 45deg);
width:100px; height:200px;
}
</style>
<div id="perspective">
<div id="threedee"></div>
</div>
)HTML");
GetDocument().GetPage()->SetPageScaleFactor(2);
UpdateAllLifecyclePhasesForTest();
const HitTestRequest hit_request(HitTestRequest::kActive);
const HitTestLocation location(IntPoint(400, 300));
HitTestResult result;
GetLayoutView().HitTestNoLifecycleUpdate(location, result);
EXPECT_EQ(result.InnerNode(), GetDocument().documentElement());
EXPECT_EQ(result.GetScrollbar(), nullptr);
if (GetDocument().GetPage()->GetScrollbarTheme().AllowsHitTest()) {
const HitTestLocation location_scrollbar(IntPoint(790, 300));
HitTestResult result_scrollbar;
EXPECT_EQ(result_scrollbar.InnerNode(), &GetDocument());
EXPECT_NE(result_scrollbar.GetScrollbar(), nullptr);
}
}
TEST_P(PaintLayerTest, HasNonEmptyChildLayoutObjectsZeroSizeOverflowVisible) {
SetBodyInnerHTML(R"HTML(
<div id="layer" style="position: relative">
<div style="overflow: visible; height: 0; width: 0">text</div>
</div>
)HTML");
auto* layer = GetPaintLayerByElementId("layer");
EXPECT_TRUE(layer->HasVisibleContent());
EXPECT_FALSE(layer->HasVisibleDescendant());
EXPECT_TRUE(layer->HasNonEmptyChildLayoutObjects());
}
enum { kCompositingOptimizations = 1 << 0 };
// TODO(chrishtr): Remove this test configuration and keep the appropriate
// variants of the tests when CompositingOptimizations ships or is removed.
class PaintLayerOverlapTestConfigurations
: public testing::WithParamInterface<unsigned>,
private ScopedCompositingOptimizationsForTest {
public:
PaintLayerOverlapTestConfigurations()
: ScopedCompositingOptimizationsForTest(GetParam() &
kCompositingOptimizations) {}
~PaintLayerOverlapTestConfigurations() override {
// Must destruct all objects before toggling back feature flags.
WebHeap::CollectAllGarbageForTesting();
}
};
class PaintLayerOverlapTest : public PaintLayerOverlapTestConfigurations,
public RenderingTest {
public:
PaintLayerOverlapTest()
: RenderingTest(MakeGarbageCollected<SingleChildLocalFrameClient>()) {}
void SetUp() override {
EnableCompositing();
RenderingTest::SetUp();
}
};
INSTANTIATE_TEST_SUITE_P(All,
PaintLayerOverlapTest,
::testing::Values(0, kCompositingOptimizations));
TEST_P(PaintLayerOverlapTest, FixedUsesExpandedBoundingBoxForOverlap) {
SetBodyInnerHTML(R"HTML(
<style>
* {
......@@ -2616,8 +2778,12 @@ TEST_P(PaintLayerTest, FixedUsesExpandedBoundingBoxForOverlap) {
)HTML");
PaintLayer* fixed = GetPaintLayerByElementId("fixed");
EXPECT_EQ(fixed->BoundingBoxForCompositingOverlapTest(),
PhysicalRect(0, 0, 30, 20));
EXPECT_EQ(fixed->ExpandedBoundingBoxForCompositingOverlapTest(false),
IntRect(50, 50, 30, 20));
EXPECT_EQ(fixed->LocalBoundingBoxForCompositingOverlapTest(),
PhysicalRect(0, 0, 10, 10));
EXPECT_EQ(fixed->UnclippedAbsoluteBoundingBox(), IntRect(50, 50, 10, 10));
EXPECT_EQ(fixed->ClippedAbsoluteBoundingBox(), IntRect(50, 50, 10, 10));
// Modify the viewport scroll offset and ensure that the bounding box is still
// adjusted by the new amount the viewport can scroll in any direction.
......@@ -2625,11 +2791,21 @@ TEST_P(PaintLayerTest, FixedUsesExpandedBoundingBoxForOverlap) {
ScrollOffset(10, 10), mojom::blink::ScrollType::kProgrammatic);
UpdateAllLifecyclePhasesForTest();
EXPECT_EQ(fixed->BoundingBoxForCompositingOverlapTest(),
PhysicalRect(-10, -10, 30, 20));
EXPECT_EQ(fixed->ExpandedBoundingBoxForCompositingOverlapTest(false),
IntRect(50, 50, 30, 20));
EXPECT_EQ(fixed->LocalBoundingBoxForCompositingOverlapTest(),
PhysicalRect(0, 0, 10, 10));
if (!RuntimeEnabledFeatures::CompositingOptimizationsEnabled()) {
EXPECT_EQ(fixed->UnclippedAbsoluteBoundingBox(), IntRect(50, 50, 10, 10));
EXPECT_EQ(fixed->ClippedAbsoluteBoundingBox(), IntRect(50, 50, 10, 10));
} else {
EXPECT_EQ(fixed->UnclippedAbsoluteBoundingBox(), IntRect(60, 60, 10, 10));
EXPECT_EQ(fixed->ClippedAbsoluteBoundingBox(), IntRect(60, 60, 10, 10));
}
}
TEST_P(PaintLayerTest, FixedInScrollerUsesExpandedBoundingBoxForOverlap) {
TEST_P(PaintLayerOverlapTest,
FixedInScrollerUsesExpandedBoundingBoxForOverlap) {
SetBodyInnerHTML(R"HTML(
<style>
* {
......@@ -2665,8 +2841,12 @@ TEST_P(PaintLayerTest, FixedInScrollerUsesExpandedBoundingBoxForOverlap) {
)HTML");
PaintLayer* fixed = GetPaintLayerByElementId("fixed");
EXPECT_EQ(fixed->BoundingBoxForCompositingOverlapTest(),
PhysicalRect(0, 0, 30, 20));
EXPECT_EQ(fixed->ExpandedBoundingBoxForCompositingOverlapTest(false),
IntRect(50, 50, 30, 20));
EXPECT_EQ(fixed->LocalBoundingBoxForCompositingOverlapTest(),
PhysicalRect(0, 0, 10, 10));
EXPECT_EQ(fixed->UnclippedAbsoluteBoundingBox(), IntRect(50, 50, 10, 10));
EXPECT_EQ(fixed->ClippedAbsoluteBoundingBox(), IntRect(50, 50, 10, 10));
// Modify the inner scroll offset and ensure that the bounding box is still
// the same.
......@@ -2675,8 +2855,12 @@ TEST_P(PaintLayerTest, FixedInScrollerUsesExpandedBoundingBoxForOverlap) {
scrollable_area->ScrollToAbsolutePosition(FloatPoint(10, 10));
UpdateAllLifecyclePhasesForTest();
EXPECT_EQ(fixed->BoundingBoxForCompositingOverlapTest(),
PhysicalRect(0, 0, 30, 20));
EXPECT_EQ(fixed->ExpandedBoundingBoxForCompositingOverlapTest(false),
IntRect(50, 50, 30, 20));
EXPECT_EQ(fixed->LocalBoundingBoxForCompositingOverlapTest(),
PhysicalRect(0, 0, 10, 10));
EXPECT_EQ(fixed->UnclippedAbsoluteBoundingBox(), IntRect(50, 50, 10, 10));
EXPECT_EQ(fixed->ClippedAbsoluteBoundingBox(), IntRect(50, 50, 10, 10));
// Modify the viewport scroll offset and ensure that the bounding box is still
// adjusted by the newamount the viewport can scroll in any direction.
......@@ -2684,11 +2868,21 @@ TEST_P(PaintLayerTest, FixedInScrollerUsesExpandedBoundingBoxForOverlap) {
ScrollOffset(10, 10), mojom::blink::ScrollType::kProgrammatic);
UpdateAllLifecyclePhasesForTest();
EXPECT_EQ(fixed->BoundingBoxForCompositingOverlapTest(),
PhysicalRect(-10, -10, 30, 20));
EXPECT_EQ(fixed->ExpandedBoundingBoxForCompositingOverlapTest(false),
IntRect(50, 50, 30, 20));
EXPECT_EQ(fixed->LocalBoundingBoxForCompositingOverlapTest(),
PhysicalRect(0, 0, 10, 10));
if (!RuntimeEnabledFeatures::CompositingOptimizationsEnabled()) {
EXPECT_EQ(fixed->UnclippedAbsoluteBoundingBox(), IntRect(50, 50, 10, 10));
EXPECT_EQ(fixed->ClippedAbsoluteBoundingBox(), IntRect(50, 50, 10, 10));
} else {
EXPECT_EQ(fixed->UnclippedAbsoluteBoundingBox(), IntRect(60, 60, 10, 10));
EXPECT_EQ(fixed->ClippedAbsoluteBoundingBox(), IntRect(60, 60, 10, 10));
}
}
TEST_P(PaintLayerTest, FixedUnderTransformDoesNotExpandBoundingBoxForOverlap) {
TEST_P(PaintLayerOverlapTest,
FixedUnderTransformDoesNotExpandBoundingBoxForOverlap) {
SetBodyInnerHTML(R"HTML(
<style>
.anim {
......@@ -2727,11 +2921,15 @@ TEST_P(PaintLayerTest, FixedUnderTransformDoesNotExpandBoundingBoxForOverlap) {
// fixed composited, it shouldn't have expanded bounds because its containing
// block isn't the viewport.
PaintLayer* fixed = GetPaintLayerByElementId("fixed");
EXPECT_EQ(fixed->BoundingBoxForCompositingOverlapTest(),
EXPECT_EQ(fixed->ExpandedBoundingBoxForCompositingOverlapTest(false),
IntRect(117, 117, 66, 66));
EXPECT_EQ(fixed->LocalBoundingBoxForCompositingOverlapTest(),
PhysicalRect(0, 0, 50, 50));
EXPECT_EQ(fixed->UnclippedAbsoluteBoundingBox(), IntRect(117, 117, 66, 66));
EXPECT_EQ(fixed->ClippedAbsoluteBoundingBox(), IntRect(117, 117, 66, 66));
}
TEST_P(PaintLayerTest, NestedFixedUsesExpandedBoundingBoxForOverlap) {
TEST_P(PaintLayerOverlapTest, NestedFixedUsesExpandedBoundingBoxForOverlap) {
SetBodyInnerHTML(R"HTML(
<style>
* {
......@@ -2776,8 +2974,12 @@ TEST_P(PaintLayerTest, NestedFixedUsesExpandedBoundingBoxForOverlap) {
To<LayoutBoxModelObject>(
ChildDocument().getElementById("fixed")->GetLayoutObject())
->Layer();
EXPECT_EQ(fixed->BoundingBoxForCompositingOverlapTest(),
PhysicalRect(0, 0, 410, 410));
EXPECT_EQ(fixed->ExpandedBoundingBoxForCompositingOverlapTest(false),
IntRect(50, 50, 410, 410));
EXPECT_EQ(fixed->LocalBoundingBoxForCompositingOverlapTest(),
PhysicalRect(0, 0, 10, 10));
EXPECT_EQ(fixed->UnclippedAbsoluteBoundingBox(), IntRect(50, 50, 10, 10));
EXPECT_EQ(fixed->ClippedAbsoluteBoundingBox(), IntRect(50, 50, 10, 10));
// Modify the top-most viewport's scroll offset and ensure that the bounding
// box is still the same. This shows that we're not considering the wrong
......@@ -2786,8 +2988,12 @@ TEST_P(PaintLayerTest, NestedFixedUsesExpandedBoundingBoxForOverlap) {
ScrollOffset(10, 10), mojom::blink::ScrollType::kProgrammatic);
UpdateAllLifecyclePhasesForTest();
EXPECT_EQ(fixed->BoundingBoxForCompositingOverlapTest(),
PhysicalRect(0, 0, 410, 410));
EXPECT_EQ(fixed->ExpandedBoundingBoxForCompositingOverlapTest(false),
IntRect(50, 50, 410, 410));
EXPECT_EQ(fixed->LocalBoundingBoxForCompositingOverlapTest(),
PhysicalRect(0, 0, 10, 10));
EXPECT_EQ(fixed->UnclippedAbsoluteBoundingBox(), IntRect(50, 50, 10, 10));
EXPECT_EQ(fixed->ClippedAbsoluteBoundingBox(), IntRect(50, 50, 10, 10));
// Now modify the iframe's scroll offset. This one should affect the fixed's
// bounding box.
......@@ -2795,141 +3001,522 @@ TEST_P(PaintLayerTest, NestedFixedUsesExpandedBoundingBoxForOverlap) {
ScrollOffset(10, 10), mojom::blink::ScrollType::kProgrammatic);
UpdateAllLifecyclePhasesForTest();
EXPECT_EQ(fixed->BoundingBoxForCompositingOverlapTest(),
PhysicalRect(-10, -10, 410, 410));
EXPECT_EQ(fixed->ExpandedBoundingBoxForCompositingOverlapTest(false),
IntRect(50, 50, 410, 410));
EXPECT_EQ(fixed->LocalBoundingBoxForCompositingOverlapTest(),
PhysicalRect(0, 0, 10, 10));
if (!RuntimeEnabledFeatures::CompositingOptimizationsEnabled()) {
EXPECT_EQ(fixed->UnclippedAbsoluteBoundingBox(), IntRect(50, 50, 10, 10));
EXPECT_EQ(fixed->ClippedAbsoluteBoundingBox(), IntRect(50, 50, 10, 10));
} else {
EXPECT_EQ(fixed->UnclippedAbsoluteBoundingBox(), IntRect(60, 60, 10, 10));
EXPECT_EQ(fixed->ClippedAbsoluteBoundingBox(), IntRect(60, 60, 10, 10));
}
}
TEST_P(PaintLayerTest, DirectCompositingReasonsCrossingFrameBoundaries) {
if (RuntimeEnabledFeatures::CompositeAfterPaintEnabled())
return;
TEST_P(PaintLayerOverlapTest, FixedWithExpandedBoundsForChild) {
SetBodyInnerHTML(R"HTML(
<iframe></iframe>
)HTML");
SetChildFrameHTML(R"HTML(
<div id=target style="position: relative"></div>
<style>
* {
margin: 0;
}
body {
height: 1000px;
}
#fixed {
height: 50px;
left: 25px;
position: fixed;
top: 25px;
width: 50px;
}
#abs {
height: 25px;
left: 50px;
position: absolute;
top: 200px;
width: 25px;
will-change: transform;
}
</style>
<div id=fixed>
<div id=abs></div>
</div>
)HTML");
UpdateAllLifecyclePhasesForTest();
PaintLayer* target =
To<LayoutBoxModelObject>(
ChildDocument().getElementById("target")->GetLayoutObject())
->Layer();
// The fixed-pos layer should use bounds that have been expanded to include
// the absolutely positioned child. Without this expansion, overlap testing
// can miss overlap from that child leading to incorrect composition order.
PaintLayer* fixed = GetPaintLayerByElementId("fixed");
EXPECT_EQ(fixed->ExpandedBoundingBoxForCompositingOverlapTest(false),
IntRect(25, 25, 75, 625));
EXPECT_EQ(fixed->LocalBoundingBoxForCompositingOverlapTest(),
PhysicalRect(0, 0, 50, 50));
EXPECT_EQ(fixed->UnclippedAbsoluteBoundingBox(), IntRect(25, 25, 50, 50));
EXPECT_EQ(fixed->ClippedAbsoluteBoundingBox(), IntRect(25, 25, 50, 50));
// Verify that the abs bounds is not expanded even though it is a child of a
// fixed-pos layer. Expanding the abs bounds would mean that it could
// unnecessarily detect overlap with siblings that it doesn't ever actually
// overlap with.
PaintLayer* abs = GetPaintLayerByElementId("abs");
EXPECT_EQ(abs->ExpandedBoundingBoxForCompositingOverlapTest(false),
IntRect(75, 225, 25, 25));
EXPECT_EQ(abs->LocalBoundingBoxForCompositingOverlapTest(),
PhysicalRect(0, 0, 25, 25));
EXPECT_EQ(abs->UnclippedAbsoluteBoundingBox(), IntRect(75, 225, 25, 25));
EXPECT_EQ(abs->ClippedAbsoluteBoundingBox(), IntRect(75, 225, 25, 25));
// Modify the scroll offset and ensure that the bounding box is still the
// same. Note that if we get different expanded bounding boxes for overlap
// testing with different scroll offsets then it implies that scroll offset is
// a part of that calculation and we may get incorrect results as scroll
// offsets changes and partial updates happen.
GetDocument().View()->LayoutViewport()->SetScrollOffset(
ScrollOffset(0, 400), mojom::blink::ScrollType::kProgrammatic);
UpdateAllLifecyclePhasesForTest();
EXPECT_EQ(
GetDocument().View()->GetLayoutView()->Layer(),
target->EnclosingDirectlyCompositableLayerCrossingFrameBoundaries());
EXPECT_EQ(fixed->ExpandedBoundingBoxForCompositingOverlapTest(false),
IntRect(25, 25, 75, 625));
EXPECT_EQ(fixed->LocalBoundingBoxForCompositingOverlapTest(),
PhysicalRect(0, 0, 50, 50));
if (!RuntimeEnabledFeatures::CompositingOptimizationsEnabled()) {
EXPECT_EQ(fixed->UnclippedAbsoluteBoundingBox(), IntRect(25, 25, 50, 50));
EXPECT_EQ(fixed->ClippedAbsoluteBoundingBox(), IntRect(25, 25, 50, 50));
} else {
EXPECT_EQ(fixed->UnclippedAbsoluteBoundingBox(), IntRect(25, 425, 50, 50));
EXPECT_EQ(fixed->ClippedAbsoluteBoundingBox(), IntRect(25, 425, 50, 50));
}
EXPECT_EQ(abs->ExpandedBoundingBoxForCompositingOverlapTest(false),
IntRect(75, 625, 25, 25));
EXPECT_EQ(abs->LocalBoundingBoxForCompositingOverlapTest(),
PhysicalRect(0, 0, 25, 25));
if (!RuntimeEnabledFeatures::CompositingOptimizationsEnabled()) {
EXPECT_EQ(abs->UnclippedAbsoluteBoundingBox(), IntRect(75, 225, 25, 25));
EXPECT_EQ(abs->ClippedAbsoluteBoundingBox(), IntRect(75, 225, 25, 25));
} else {
EXPECT_EQ(abs->UnclippedAbsoluteBoundingBox(), IntRect(75, 625, 25, 25));
EXPECT_EQ(abs->ClippedAbsoluteBoundingBox(), IntRect(75, 625, 25, 25));
}
}
TEST_P(PaintLayerTest,
DirectCompositingReasonsCrossingFrameBoundariesCompositedIframe) {
if (RuntimeEnabledFeatures::CompositeAfterPaintEnabled())
return;
TEST_P(PaintLayerOverlapTest, FixedWithClippedExpandedBoundsForChild) {
SetBodyInnerHTML(R"HTML(
<iframe id="iframe" style="will-change: transform";></iframe>
)HTML");
SetChildFrameHTML(R"HTML(
<div id=target style="position: relative"></div>
<style>
* {
margin: 0;
}
body {
height: 1000px;
}
#fixed {
height: 50px;
left: 25px;
position: fixed;
top: 25px;
width: 50px;
overflow: hidden;
}
#abs {
height: 25px;
left: 50px;
position: absolute;
top: 200px;
width: 25px;
will-change: transform;
}
</style>
<div id=fixed>
<div id=abs></div>
</div>
)HTML");
UpdateAllLifecyclePhasesForTest();
PaintLayer* target =
To<LayoutBoxModelObject>(
ChildDocument().getElementById("target")->GetLayoutObject())
->Layer();
// The fixed-pos layer should use bounds that have been expanded to include
// the absolutely positioned child. However, the fixed-pos ancestor also has
// clipping which will limit the expansion.
PaintLayer* fixed = GetPaintLayerByElementId("fixed");
EXPECT_EQ(fixed->ExpandedBoundingBoxForCompositingOverlapTest(false),
IntRect(25, 25, 50, 450));
EXPECT_EQ(fixed->LocalBoundingBoxForCompositingOverlapTest(),
PhysicalRect(0, 0, 50, 50));
EXPECT_EQ(fixed->UnclippedAbsoluteBoundingBox(), IntRect(25, 25, 50, 50));
EXPECT_EQ(fixed->ClippedAbsoluteBoundingBox(), IntRect(25, 25, 50, 50));
// Verify that the abs bounds is not expanded even though it is a child of a
// fixed-pos layer. Expanding the abs bounds would mean that it could
// unnecessarily detect overlap with siblings that it doesn't ever actually
// overlap with. Note that the clipped bounds is an empty rect because of the
// clipping from the ancestor.
PaintLayer* abs = GetPaintLayerByElementId("abs");
EXPECT_EQ(abs->ExpandedBoundingBoxForCompositingOverlapTest(false),
IntRect(75, 225, 25, 25));
EXPECT_EQ(abs->LocalBoundingBoxForCompositingOverlapTest(),
PhysicalRect(0, 0, 25, 25));
EXPECT_EQ(abs->UnclippedAbsoluteBoundingBox(), IntRect(75, 225, 25, 25));
EXPECT_EQ(abs->ClippedAbsoluteBoundingBox(), IntRect(0, 0, 0, 0));
// Modify the scroll offset and ensure that the bounding box is still the
// same. Note that if we get different expanded bounding boxes for overlap
// testing with different scroll offsets then it implies that scroll offset is
// a part of that calculation and we may get incorrect results as scroll
// offsets changes and partial updates happen.
GetDocument().View()->LayoutViewport()->SetScrollOffset(
ScrollOffset(0, 400), mojom::blink::ScrollType::kProgrammatic);
UpdateAllLifecyclePhasesForTest();
PaintLayer* iframe =
To<LayoutBoxModelObject>(
GetDocument().getElementById("iframe")->GetLayoutObject())
->Layer();
EXPECT_EQ(fixed->ExpandedBoundingBoxForCompositingOverlapTest(false),
IntRect(25, 25, 50, 450));
EXPECT_EQ(fixed->LocalBoundingBoxForCompositingOverlapTest(),
PhysicalRect(0, 0, 50, 50));
if (!RuntimeEnabledFeatures::CompositingOptimizationsEnabled()) {
EXPECT_EQ(fixed->UnclippedAbsoluteBoundingBox(), IntRect(25, 25, 50, 50));
EXPECT_EQ(fixed->ClippedAbsoluteBoundingBox(), IntRect(25, 25, 50, 50));
} else {
EXPECT_EQ(fixed->UnclippedAbsoluteBoundingBox(), IntRect(25, 425, 50, 50));
EXPECT_EQ(fixed->ClippedAbsoluteBoundingBox(), IntRect(25, 425, 50, 50));
}
EXPECT_EQ(
iframe,
target->EnclosingDirectlyCompositableLayerCrossingFrameBoundaries());
EXPECT_EQ(abs->ExpandedBoundingBoxForCompositingOverlapTest(false),
IntRect(75, 625, 25, 25));
EXPECT_EQ(abs->LocalBoundingBoxForCompositingOverlapTest(),
PhysicalRect(0, 0, 25, 25));
if (!RuntimeEnabledFeatures::CompositingOptimizationsEnabled()) {
EXPECT_EQ(abs->UnclippedAbsoluteBoundingBox(), IntRect(75, 225, 25, 25));
EXPECT_EQ(abs->ClippedAbsoluteBoundingBox(), IntRect(0, 0, 0, 0));
} else {
EXPECT_EQ(abs->UnclippedAbsoluteBoundingBox(), IntRect(75, 625, 25, 25));
EXPECT_EQ(abs->ClippedAbsoluteBoundingBox(), IntRect(0, 0, 0, 0));
}
}
TEST_P(PaintLayerTest,
DirectCompositingReasonsCrossingFrameBoundariesCompositedParent) {
if (RuntimeEnabledFeatures::CompositeAfterPaintEnabled())
return;
TEST_P(PaintLayerOverlapTest, FixedWithExpandedBoundsForGrandChild) {
SetBodyInnerHTML(R"HTML(
<iframe></iframe>
)HTML");
SetChildFrameHTML(R"HTML(
<div id="parent" style="will-change: transform">
<div id=target style="position: relative"></div>
<style>
* {
margin: 0;
}
body {
height: 1000px;
}
#fixed {
height: 50px;
left: 25px;
position: fixed;
top: 25px;
width: 50px;
}
#abs {
height: 25px;
left: 50px;
position: absolute;
top: 200px;
width: 25px;
}
#abs2 {
height: 25px;
left: 50px;
position: absolute;
top: 100px;
width: 25px;
}
</style>
<div id=fixed>
<div id=abs>
<div id=abs2></div>
</div>
</div>
)HTML");
UpdateAllLifecyclePhasesForTest();
PaintLayer* target =
To<LayoutBoxModelObject>(
ChildDocument().getElementById("target")->GetLayoutObject())
->Layer();
// The fixed-pos layer should use bounds that have been expanded to include
// the absolutely positioned grandchild.
PaintLayer* fixed = GetPaintLayerByElementId("fixed");
EXPECT_EQ(fixed->ExpandedBoundingBoxForCompositingOverlapTest(false),
IntRect(25, 25, 125, 725));
EXPECT_EQ(fixed->LocalBoundingBoxForCompositingOverlapTest(),
PhysicalRect(0, 0, 50, 50));
EXPECT_EQ(fixed->UnclippedAbsoluteBoundingBox(), IntRect(25, 25, 50, 50));
EXPECT_EQ(fixed->ClippedAbsoluteBoundingBox(), IntRect(25, 25, 50, 50));
// Verify that the abs bounds is not expanded even though it is a child of a
// fixed-pos layer. Additionally, it shouldn't include its child as only
// fixed-pos expands to include descendants.
PaintLayer* abs = GetPaintLayerByElementId("abs");
EXPECT_EQ(abs->ExpandedBoundingBoxForCompositingOverlapTest(false),
IntRect(75, 225, 25, 25));
EXPECT_EQ(abs->LocalBoundingBoxForCompositingOverlapTest(),
PhysicalRect(0, 0, 25, 25));
EXPECT_EQ(abs->UnclippedAbsoluteBoundingBox(), IntRect(75, 225, 25, 25));
EXPECT_EQ(abs->ClippedAbsoluteBoundingBox(), IntRect(75, 225, 25, 25));
PaintLayer* abs2 = GetPaintLayerByElementId("abs2");
EXPECT_EQ(abs2->ExpandedBoundingBoxForCompositingOverlapTest(false),
IntRect(125, 325, 25, 25));
EXPECT_EQ(abs2->LocalBoundingBoxForCompositingOverlapTest(),
PhysicalRect(0, 0, 25, 25));
EXPECT_EQ(abs2->UnclippedAbsoluteBoundingBox(), IntRect(125, 325, 25, 25));
EXPECT_EQ(abs2->ClippedAbsoluteBoundingBox(), IntRect(125, 325, 25, 25));
// Modify the scroll offset and ensure that the bounding box is still the
// same. Note that if we get different expanded bounding boxes for overlap
// testing with different scroll offsets then it implies that scroll offset is
// a part of that calculation and we may get incorrect results as scroll
// offsets changes and partial updates happen.
GetDocument().View()->LayoutViewport()->SetScrollOffset(
ScrollOffset(0, 400), mojom::blink::ScrollType::kProgrammatic);
UpdateAllLifecyclePhasesForTest();
PaintLayer* parent =
To<LayoutBoxModelObject>(
ChildDocument().getElementById("parent")->GetLayoutObject())
->Layer();
EXPECT_EQ(fixed->ExpandedBoundingBoxForCompositingOverlapTest(false),
IntRect(25, 25, 125, 725));
EXPECT_EQ(fixed->LocalBoundingBoxForCompositingOverlapTest(),
PhysicalRect(0, 0, 50, 50));
if (!RuntimeEnabledFeatures::CompositingOptimizationsEnabled()) {
EXPECT_EQ(fixed->UnclippedAbsoluteBoundingBox(), IntRect(25, 25, 50, 50));
EXPECT_EQ(fixed->ClippedAbsoluteBoundingBox(), IntRect(25, 25, 50, 50));
} else {
EXPECT_EQ(fixed->UnclippedAbsoluteBoundingBox(), IntRect(25, 425, 50, 50));
EXPECT_EQ(fixed->ClippedAbsoluteBoundingBox(), IntRect(25, 425, 50, 50));
}
EXPECT_EQ(
parent,
target->EnclosingDirectlyCompositableLayerCrossingFrameBoundaries());
EXPECT_EQ(abs->ExpandedBoundingBoxForCompositingOverlapTest(false),
IntRect(75, 625, 25, 25));
EXPECT_EQ(abs->LocalBoundingBoxForCompositingOverlapTest(),
PhysicalRect(0, 0, 25, 25));
if (!RuntimeEnabledFeatures::CompositingOptimizationsEnabled()) {
EXPECT_EQ(abs->UnclippedAbsoluteBoundingBox(), IntRect(75, 225, 25, 25));
EXPECT_EQ(abs->ClippedAbsoluteBoundingBox(), IntRect(75, 225, 25, 25));
} else {
EXPECT_EQ(abs->UnclippedAbsoluteBoundingBox(), IntRect(75, 625, 25, 25));
EXPECT_EQ(abs->ClippedAbsoluteBoundingBox(), IntRect(75, 625, 25, 25));
}
EXPECT_EQ(abs2->ExpandedBoundingBoxForCompositingOverlapTest(false),
IntRect(125, 725, 25, 25));
EXPECT_EQ(abs2->LocalBoundingBoxForCompositingOverlapTest(),
PhysicalRect(0, 0, 25, 25));
if (!RuntimeEnabledFeatures::CompositingOptimizationsEnabled()) {
EXPECT_EQ(abs2->UnclippedAbsoluteBoundingBox(), IntRect(125, 325, 25, 25));
EXPECT_EQ(abs2->ClippedAbsoluteBoundingBox(), IntRect(125, 325, 25, 25));
} else {
EXPECT_EQ(abs2->UnclippedAbsoluteBoundingBox(), IntRect(125, 725, 25, 25));
EXPECT_EQ(abs2->ClippedAbsoluteBoundingBox(), IntRect(125, 725, 25, 25));
}
// Add will-change to the middle child to ensure the bounds are still the
// same. This helps confirm that the computation of the bounds is agnostic to
// if descendants are composited or not.
GetDocument().getElementById("abs")->setAttribute(html_names::kStyleAttr,
"will-change: transform");
UpdateAllLifecyclePhasesForTest();
EXPECT_EQ(fixed->ExpandedBoundingBoxForCompositingOverlapTest(false),
IntRect(25, 25, 125, 725));
EXPECT_EQ(fixed->LocalBoundingBoxForCompositingOverlapTest(),
PhysicalRect(0, 0, 50, 50));
if (!RuntimeEnabledFeatures::CompositingOptimizationsEnabled()) {
EXPECT_EQ(fixed->UnclippedAbsoluteBoundingBox(), IntRect(25, 25, 50, 50));
EXPECT_EQ(fixed->ClippedAbsoluteBoundingBox(), IntRect(25, 25, 50, 50));
} else {
EXPECT_EQ(fixed->UnclippedAbsoluteBoundingBox(), IntRect(25, 425, 50, 50));
EXPECT_EQ(fixed->ClippedAbsoluteBoundingBox(), IntRect(25, 425, 50, 50));
}
EXPECT_EQ(abs->ExpandedBoundingBoxForCompositingOverlapTest(false),
IntRect(75, 625, 25, 25));
EXPECT_EQ(abs->LocalBoundingBoxForCompositingOverlapTest(),
PhysicalRect(0, 0, 25, 25));
if (!RuntimeEnabledFeatures::CompositingOptimizationsEnabled()) {
EXPECT_EQ(abs->UnclippedAbsoluteBoundingBox(), IntRect(75, 225, 25, 25));
EXPECT_EQ(abs->ClippedAbsoluteBoundingBox(), IntRect(75, 225, 25, 25));
} else {
EXPECT_EQ(abs->UnclippedAbsoluteBoundingBox(), IntRect(75, 625, 25, 25));
EXPECT_EQ(abs->ClippedAbsoluteBoundingBox(), IntRect(75, 625, 25, 25));
}
EXPECT_EQ(abs2->ExpandedBoundingBoxForCompositingOverlapTest(false),
IntRect(125, 725, 25, 25));
EXPECT_EQ(abs2->LocalBoundingBoxForCompositingOverlapTest(),
PhysicalRect(0, 0, 25, 25));
if (!RuntimeEnabledFeatures::CompositingOptimizationsEnabled()) {
EXPECT_EQ(abs2->UnclippedAbsoluteBoundingBox(), IntRect(125, 325, 25, 25));
EXPECT_EQ(abs2->ClippedAbsoluteBoundingBox(), IntRect(125, 325, 25, 25));
} else {
EXPECT_EQ(abs2->UnclippedAbsoluteBoundingBox(), IntRect(125, 725, 25, 25));
EXPECT_EQ(abs2->ClippedAbsoluteBoundingBox(), IntRect(125, 725, 25, 25));
}
// Add will-change to the grandchild and ensure the bounds are still the same.
GetDocument().getElementById("abs2")->setAttribute(html_names::kStyleAttr,
"will-change: transform");
UpdateAllLifecyclePhasesForTest();
EXPECT_EQ(fixed->ExpandedBoundingBoxForCompositingOverlapTest(false),
IntRect(25, 25, 125, 725));
EXPECT_EQ(fixed->LocalBoundingBoxForCompositingOverlapTest(),
PhysicalRect(0, 0, 50, 50));
if (!RuntimeEnabledFeatures::CompositingOptimizationsEnabled()) {
EXPECT_EQ(fixed->UnclippedAbsoluteBoundingBox(), IntRect(25, 25, 50, 50));
EXPECT_EQ(fixed->ClippedAbsoluteBoundingBox(), IntRect(25, 25, 50, 50));
} else {
EXPECT_EQ(fixed->UnclippedAbsoluteBoundingBox(), IntRect(25, 425, 50, 50));
EXPECT_EQ(fixed->ClippedAbsoluteBoundingBox(), IntRect(25, 425, 50, 50));
}
EXPECT_EQ(abs->ExpandedBoundingBoxForCompositingOverlapTest(false),
IntRect(75, 625, 25, 25));
EXPECT_EQ(abs->LocalBoundingBoxForCompositingOverlapTest(),
PhysicalRect(0, 0, 25, 25));
if (!RuntimeEnabledFeatures::CompositingOptimizationsEnabled()) {
EXPECT_EQ(abs->UnclippedAbsoluteBoundingBox(), IntRect(75, 225, 25, 25));
EXPECT_EQ(abs->ClippedAbsoluteBoundingBox(), IntRect(75, 225, 25, 25));
} else {
EXPECT_EQ(abs->UnclippedAbsoluteBoundingBox(), IntRect(75, 625, 25, 25));
EXPECT_EQ(abs->ClippedAbsoluteBoundingBox(), IntRect(75, 625, 25, 25));
}
EXPECT_EQ(abs2->ExpandedBoundingBoxForCompositingOverlapTest(false),
IntRect(125, 725, 25, 25));
EXPECT_EQ(abs2->LocalBoundingBoxForCompositingOverlapTest(),
PhysicalRect(0, 0, 25, 25));
if (!RuntimeEnabledFeatures::CompositingOptimizationsEnabled()) {
EXPECT_EQ(abs2->UnclippedAbsoluteBoundingBox(), IntRect(125, 325, 25, 25));
EXPECT_EQ(abs2->ClippedAbsoluteBoundingBox(), IntRect(125, 325, 25, 25));
} else {
EXPECT_EQ(abs2->UnclippedAbsoluteBoundingBox(), IntRect(125, 725, 25, 25));
EXPECT_EQ(abs2->ClippedAbsoluteBoundingBox(), IntRect(125, 725, 25, 25));
}
// Remove will-change from the middle child and ensure the bounds are still
// the same.
GetDocument().getElementById("abs")->setAttribute(html_names::kStyleAttr, "");
UpdateAllLifecyclePhasesForTest();
EXPECT_EQ(fixed->ExpandedBoundingBoxForCompositingOverlapTest(false),
IntRect(25, 25, 125, 725));
EXPECT_EQ(fixed->LocalBoundingBoxForCompositingOverlapTest(),
PhysicalRect(0, 0, 50, 50));
if (!RuntimeEnabledFeatures::CompositingOptimizationsEnabled()) {
EXPECT_EQ(fixed->UnclippedAbsoluteBoundingBox(), IntRect(25, 25, 50, 50));
EXPECT_EQ(fixed->ClippedAbsoluteBoundingBox(), IntRect(25, 25, 50, 50));
} else {
EXPECT_EQ(fixed->UnclippedAbsoluteBoundingBox(), IntRect(25, 425, 50, 50));
EXPECT_EQ(fixed->ClippedAbsoluteBoundingBox(), IntRect(25, 425, 50, 50));
}
EXPECT_EQ(abs->ExpandedBoundingBoxForCompositingOverlapTest(false),
IntRect(75, 625, 25, 25));
EXPECT_EQ(abs->LocalBoundingBoxForCompositingOverlapTest(),
PhysicalRect(0, 0, 25, 25));
if (!RuntimeEnabledFeatures::CompositingOptimizationsEnabled()) {
EXPECT_EQ(abs->UnclippedAbsoluteBoundingBox(), IntRect(75, 225, 25, 25));
EXPECT_EQ(abs->ClippedAbsoluteBoundingBox(), IntRect(75, 225, 25, 25));
} else {
EXPECT_EQ(abs->UnclippedAbsoluteBoundingBox(), IntRect(75, 625, 25, 25));
EXPECT_EQ(abs->ClippedAbsoluteBoundingBox(), IntRect(75, 625, 25, 25));
}
EXPECT_EQ(abs2->ExpandedBoundingBoxForCompositingOverlapTest(false),
IntRect(125, 725, 25, 25));
EXPECT_EQ(abs2->LocalBoundingBoxForCompositingOverlapTest(),
PhysicalRect(0, 0, 25, 25));
if (!RuntimeEnabledFeatures::CompositingOptimizationsEnabled()) {
EXPECT_EQ(abs2->UnclippedAbsoluteBoundingBox(), IntRect(125, 325, 25, 25));
EXPECT_EQ(abs2->ClippedAbsoluteBoundingBox(), IntRect(125, 325, 25, 25));
} else {
EXPECT_EQ(abs2->UnclippedAbsoluteBoundingBox(), IntRect(125, 725, 25, 25));
EXPECT_EQ(abs2->ClippedAbsoluteBoundingBox(), IntRect(125, 725, 25, 25));
}
}
TEST_P(PaintLayerTest, GlobalRootScrollerHitTest) {
USE_NON_OVERLAY_SCROLLBARS();
TEST_P(PaintLayerOverlapTest, FixedWithExpandedBoundsForFixedChild) {
SetBodyInnerHTML(R"HTML(
<style>
:root {
clip-path: circle(30%);
background:blue;
transform: rotate(30deg);
transform-style: preserve-3d;
overflow-x: hidden;
* {
margin: 0;
}
#perspective {
perspective:100px;
body {
height: 1000px;
}
#threedee {
transform: rotate3d(1, 1, 1, 45deg);
width:100px; height:200px;
#fixed {
height: 50px;
left: 25px;
position: fixed;
top: 25px;
width: 50px;
}
#nestedFixed {
height: 25px;
left: 50px;
position: fixed;
top: 100px;
width: 25px;
}
</style>
<div id="perspective">
<div id="threedee"></div>
<div id=fixed>
<div id=nestedFixed></div>
</div>
<div style="height:1000px"></div>
)HTML");
GetDocument().GetPage()->SetPageScaleFactor(2);
UpdateAllLifecyclePhasesForTest();
const HitTestRequest hit_request(HitTestRequest::kActive);
const HitTestLocation location(IntPoint(400, 300));
HitTestResult result;
GetLayoutView().HitTestNoLifecycleUpdate(location, result);
EXPECT_EQ(result.InnerNode(), GetDocument().documentElement());
EXPECT_EQ(result.GetScrollbar(), nullptr);
const HitTestLocation location_scrollbar(IntPoint(790, 300));
HitTestResult result_scrollbar;
GetLayoutView().HitTestNoLifecycleUpdate(location_scrollbar,
result_scrollbar);
EXPECT_EQ(result_scrollbar.InnerNode(), GetDocument().documentElement());
EXPECT_NE(result_scrollbar.GetScrollbar(), nullptr);
EXPECT_EQ(result_scrollbar.LocalPoint(), location_scrollbar.Point());
}
// The fixed-pos layer should use bounds that have been expanded to include
// the absolutely positioned child. Without this expansion, overlap testing
// can miss overlap from that child leading to incorrect composition order.
PaintLayer* fixed = GetPaintLayerByElementId("fixed");
EXPECT_EQ(fixed->ExpandedBoundingBoxForCompositingOverlapTest(false),
IntRect(25, 25, 50, 500));
EXPECT_EQ(fixed->LocalBoundingBoxForCompositingOverlapTest(),
PhysicalRect(0, 0, 50, 50));
EXPECT_EQ(fixed->UnclippedAbsoluteBoundingBox(), IntRect(25, 25, 50, 50));
EXPECT_EQ(fixed->ClippedAbsoluteBoundingBox(), IntRect(25, 25, 50, 50));
// Note that the nested fixed should not expand its bounds as it doesn't move
// relative to its siblings, fixed-pos or not.
PaintLayer* nestedFixed = GetPaintLayerByElementId("nestedFixed");
EXPECT_EQ(nestedFixed->ExpandedBoundingBoxForCompositingOverlapTest(false),
IntRect(50, 100, 25, 25));
EXPECT_EQ(nestedFixed->LocalBoundingBoxForCompositingOverlapTest(),
PhysicalRect(0, 0, 25, 25));
EXPECT_EQ(nestedFixed->UnclippedAbsoluteBoundingBox(),
IntRect(50, 100, 25, 25));
EXPECT_EQ(nestedFixed->ClippedAbsoluteBoundingBox(),
IntRect(50, 100, 25, 25));
// Modify the scroll offset and ensure that the bounding box is still the
// same.
GetDocument().View()->LayoutViewport()->SetScrollOffset(
ScrollOffset(0, 400), mojom::blink::ScrollType::kProgrammatic);
UpdateAllLifecyclePhasesForTest();
TEST_P(PaintLayerTest, HasNonEmptyChildLayoutObjectsZeroSizeOverflowVisible) {
SetBodyInnerHTML(R"HTML(
<div id="layer" style="position: relative">
<div style="overflow: visible; height: 0; width: 0">text</div>
</div>
)HTML");
EXPECT_EQ(fixed->ExpandedBoundingBoxForCompositingOverlapTest(false),
IntRect(25, 25, 50, 500));
EXPECT_EQ(fixed->LocalBoundingBoxForCompositingOverlapTest(),
PhysicalRect(0, 0, 50, 50));
if (!RuntimeEnabledFeatures::CompositingOptimizationsEnabled()) {
EXPECT_EQ(fixed->UnclippedAbsoluteBoundingBox(), IntRect(25, 25, 50, 50));
EXPECT_EQ(fixed->ClippedAbsoluteBoundingBox(), IntRect(25, 25, 50, 50));
} else {
EXPECT_EQ(fixed->UnclippedAbsoluteBoundingBox(), IntRect(25, 425, 50, 50));
EXPECT_EQ(fixed->ClippedAbsoluteBoundingBox(), IntRect(25, 425, 50, 50));
}
auto* layer = GetPaintLayerByElementId("layer");
EXPECT_TRUE(layer->HasVisibleContent());
EXPECT_FALSE(layer->HasVisibleDescendant());
EXPECT_TRUE(layer->HasNonEmptyChildLayoutObjects());
EXPECT_EQ(nestedFixed->ExpandedBoundingBoxForCompositingOverlapTest(false),
IntRect(50, 500, 25, 25));
EXPECT_EQ(nestedFixed->LocalBoundingBoxForCompositingOverlapTest(),
PhysicalRect(0, 0, 25, 25));
if (!RuntimeEnabledFeatures::CompositingOptimizationsEnabled()) {
EXPECT_EQ(nestedFixed->UnclippedAbsoluteBoundingBox(),
IntRect(50, 100, 25, 25));
EXPECT_EQ(nestedFixed->ClippedAbsoluteBoundingBox(),
IntRect(50, 100, 25, 25));
} else {
EXPECT_EQ(nestedFixed->UnclippedAbsoluteBoundingBox(),
IntRect(50, 500, 25, 25));
EXPECT_EQ(nestedFixed->ClippedAbsoluteBoundingBox(),
IntRect(50, 500, 25, 25));
}
}
} // namespace blink
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