diff options
author | Allan Sandfeld Jensen <allan.jensen@qt.io> | 2019-02-13 15:05:36 +0100 |
---|---|---|
committer | Allan Sandfeld Jensen <allan.jensen@qt.io> | 2019-02-14 10:33:47 +0000 |
commit | e684a3455bcc29a6e3e66a004e352dea4e1141e7 (patch) | |
tree | d55b4003bde34d7d05f558f02cfd82b2a66a7aac /chromium/third_party/blink/renderer/core/paint | |
parent | 2b94bfe47ccb6c08047959d1c26e392919550e86 (diff) | |
download | qtwebengine-chromium-e684a3455bcc29a6e3e66a004e352dea4e1141e7.tar.gz |
BASELINE: Update Chromium to 72.0.3626.110 and Ninja to 1.9.0
Change-Id: Ic57220b00ecc929a893c91f5cc552f5d3e99e922
Reviewed-by: Michael BrĂ¼ning <michael.bruning@qt.io>
Diffstat (limited to 'chromium/third_party/blink/renderer/core/paint')
134 files changed, 5832 insertions, 3696 deletions
diff --git a/chromium/third_party/blink/renderer/core/paint/BUILD.gn b/chromium/third_party/blink/renderer/core/paint/BUILD.gn index e5bd01849d3..4a8d0dfea01 100644 --- a/chromium/third_party/blink/renderer/core/paint/BUILD.gn +++ b/chromium/third_party/blink/renderer/core/paint/BUILD.gn @@ -178,8 +178,8 @@ blink_core_sources("paint") { "paint_result.h", "paint_timing.cc", "paint_timing.h", - "paint_tracker.cc", - "paint_tracker.h", + "paint_timing_detector.cc", + "paint_timing_detector.h", "pre_paint_tree_walk.cc", "pre_paint_tree_walk.h", "replaced_painter.cc", diff --git a/chromium/third_party/blink/renderer/core/paint/README.md b/chromium/third_party/blink/renderer/core/paint/README.md index 970e193bac6..714aa95842a 100644 --- a/chromium/third_party/blink/renderer/core/paint/README.md +++ b/chromium/third_party/blink/renderer/core/paint/README.md @@ -102,7 +102,9 @@ are treated in different ways during painting: concept. * Visual rect: the bounding box of all pixels that will be painted by a - display item client. + [display item client](../../platform/graphics/paint/README.md#display-items). + It's in the space of the containing transform property node (see [Building + paint property trees](#building-paint-property-trees)). * Isolation nodes/boundary: In certain situations, it is possible to put in place a barrier that isolates a subtree from being affected by its @@ -376,125 +378,7 @@ Layerization | PLC/CLM | PLC/CLM | cc property tree builder | on | off | off ``` -## PaintInvalidation (Deprecated by [PrePaint](#PrePaint)) - -Paint invalidation marks anything that need to be painted differently from the -original cached painting. - -Paint invalidation is a document cycle stage after compositing update and before -paint. During the previous stages, objects are marked for needing paint -invalidation checking if needed by style change, layout change, compositing -change, etc. In paint invalidation stage, we traverse the layout tree in -pre-order, crossing frame boundaries, for marked subtrees and objects and send -the following information to `GraphicsLayer`s and `PaintController`s: - -* invalidated display item clients: must invalidate all display item clients - that will generate different display items. - -* paint invalidation rects: must cover all areas that will generate different - pixels. They are generated based on visual rects of invalidated display item - clients. - -### `PaintInvalidationState` - -`PaintInvalidationState` is an optimization used during the paint invalidation -phase. Before the paint invalidation tree walk, a root `PaintInvalidationState` -is created for the root `LayoutView`. During the tree walk, one -`PaintInvalidationState` is created for each visited object based on the -`PaintInvalidationState` passed from the parent object. It tracks the following -information to provide O(1) complexity access to them if possible: - -* Paint invalidation container: Since as indicated by the definitions in - [Glossaries](#other-glossaries), the paint invalidation container for - stacked objects can differ from normal objects, we have to track both - separately. Here is an example: - - <div style="overflow: scroll"> - <div id=A style="position: absolute"></div> - <div id=B></div> - </div> - - If the scroller is composited (for high-DPI screens for example), it is the - paint invalidation container for div B, but not A. - -* Paint offset and clip rect: if possible, `PaintInvalidationState` - accumulates paint offsets and overflow clipping rects from the paint - invalidation container to provide O(1) complexity to map a point or a rect - in current object's local space to paint invalidation container's space. - Because locations of objects are determined by their containing blocks, and - the containing block for absolute-position objects differs from - non-absolute, we track paint offsets and overflow clipping rects for - absolute-position objects separately. - -In cases that accurate accumulation of paint offsets and clipping rects is -impossible, we will fall back to slow-path using -`LayoutObject::localToAncestorPoint()` or -`LayoutObject::mapToVisualRectInAncestorSpace()`. This includes the following -cases: - -* An object has transform related property, is multi-column or has flipped - blocks writing-mode, causing we can't simply accumulate paint offset for - mapping a local rect to paint invalidation container; - -* An object has has filter (including filter induced by reflection), which - needs to expand visual rect for descendants, because currently we don't - include and filter extents into visual overflow; - -* For a fixed-position object we calculate its offset using - `LayoutObject::localToAncestorPoint()`, but map for its descendants in - fast-path if no other things prevent us from doing this; - -* Because we track paint offset from the normal paint invalidation container - only, if we are going to use - `m_paintInvalidationContainerForStackedContents` and it's different from the - normal paint invalidation container, we have to force slow-path because the - accumulated paint offset is not usable; - -* We also stop to track paint offset and clipping rect for absolute-position - objects when `m_paintInvalidationContainerForStackedContents` becomes - different from `m_paintInvalidationContainer`. - -### Paint invalidation of texts - -Texts are painted by `InlineTextBoxPainter` using `InlineTextBox` as display -item client. Text backgrounds and masks are painted by `InlineTextFlowPainter` -using `InlineFlowBox` as display item client. We should invalidate these display -item clients when their painting will change. - -`LayoutInline`s and `LayoutText`s are marked for full paint invalidation if -needed when new style is set on them. During paint invalidation, we invalidate -the `InlineFlowBox`s directly contained by the `LayoutInline` in -`LayoutInline::InvalidateDisplayItemClients()` and `InlineTextBox`s contained by -the `LayoutText` in `LayoutText::InvalidateDisplayItemClients()`. We don't need -to traverse into the subtree of `InlineFlowBox`s in -`LayoutInline::InvalidateDisplayItemClients()` because the descendant -`InlineFlowBox`s and `InlineTextBox`s will be handled by their owning -`LayoutInline`s and `LayoutText`s, respectively, when changed style is propagated. - -### Specialty of `::first-line` - -`::first-line` pseudo style dynamically applies to all `InlineBox`'s in the -first line in the block having `::first-line` style. The actual applied style is -computed from the `::first-line` style and other applicable styles. - -If the first line contains any `LayoutInline`, we compute the style from the -`::first-line` style and the style of the `LayoutInline` and apply the computed -style to the first line part of the `LayoutInline`. In Blink's style -implementation, the combined first line style of `LayoutInline` is identified -with `FIRST_LINE_INHERITED` pseudo ID. - -The normal paint invalidation of texts doesn't work for first line because -* `ComputedStyle::VisualInvalidationDiff()` can't detect first line style - changes; -* The normal paint invalidation is based on whole LayoutObject's, not aware of - the first line. - -We have a special path for first line style change: the style system informs the -layout system when the computed first-line style changes through -`LayoutObject::FirstLineStyleDidChange()`. When this happens, we invalidate all -`InlineBox`es in the first line. - -## PrePaint (Slimming paint invalidation/v2 only) +## PrePaint [`PrePaintTreeWalk`](pre_paint_tree_walk.h) During `InPrePaint` document lifecycle state, this class is called to walk the @@ -626,12 +510,82 @@ for a much more detail about multicolumn/pagination. ### Paint invalidation [`PaintInvalidator`](paint_invalidator.h) -This class replaces [`PaintInvalidationState`] for SlimmingPaintInvalidation. -The main difference is that in PaintInvalidator, visual rects and locations -are computed by `GeometryMapper`(../../platform/graphics/paint/geometry_mapper.h), -based on paint properties produced by `PaintPropertyTreeBuilder`. +Paint invalidator marks anything that need to be painted differently from the +original cached painting. + +During the document lifecycle stages prior to PrePaint, objects are marked for +needing paint invalidation checking if needed by style change, layout change, +compositing change, etc. In PrePaint stage, we traverse the layout tree in +pre-order, crossing frame boundaries, for marked subtrees and objects and +invalidate display item clients that will generate different display items. + +At the beginning of the PrePaint tree walk, a root `PaintInvalidatorContext` +is created for the root `LayoutView`. During the tree walk, one +`PaintInvalidatorContext` is created for each visited object based on the +`PaintInvalidatorContext` passed from the parent object. It tracks the following +information to provide O(1) complexity access to them if possible: + +* Paint invalidation container (Slimming Paint v1 only): Since as indicated by + the definitions in [Glossaries](#other-glossaries), the paint invalidation + container for stacked objects can differ from normal objects, we have to + track both separately. Here is an example: + + <div style="overflow: scroll"> + <div id=A style="position: absolute"></div> + <div id=B></div> + </div> + + If the scroller is composited (for high-DPI screens for example), it is the + paint invalidation container for div B, but not A. + +* Painting layer: the layer which will initiate painting of the current + object. It's the same value as `LayoutObject::PaintingLayer()`. + +`PaintInvalidator`[PaintInvalidator.h] initializes `PaintInvalidatorContext` +for the current object, then calls `LayoutObject::InvalidatePaint()` which +calls the object's paint invalidator (e.g. `BoxPaintInvalidator`) to complete +paint invalidation of the object. + +#### Paint invalidation of text + +Text is painted by `InlineTextBoxPainter` using `InlineTextBox` as display +item client. Text backgrounds and masks are painted by `InlineTextFlowPainter` +using `InlineFlowBox` as display item client. We should invalidate these display +item clients when their painting will change. + +`LayoutInline`s and `LayoutText`s are marked for full paint invalidation if +needed when new style is set on them. During paint invalidation, we invalidate +the `InlineFlowBox`s directly contained by the `LayoutInline` in +`LayoutInline::InvalidateDisplayItemClients()` and `InlineTextBox`s contained by +the `LayoutText` in `LayoutText::InvalidateDisplayItemClients()`. We don't need +to traverse into the subtree of `InlineFlowBox`s in +`LayoutInline::InvalidateDisplayItemClients()` because the descendant +`InlineFlowBox`s and `InlineTextBox`s will be handled by their owning +`LayoutInline`s and `LayoutText`s, respectively, when changed style is +propagated. + +#### Specialty of `::first-line` + +`::first-line` pseudo style dynamically applies to all `InlineBox`'s in the +first line in the block having `::first-line` style. The actual applied style is +computed from the `::first-line` style and other applicable styles. + +If the first line contains any `LayoutInline`, we compute the style from the +`::first-line` style and the style of the `LayoutInline` and apply the computed +style to the first line part of the `LayoutInline`. In Blink's style +implementation, the combined first line style of `LayoutInline` is identified +with `FIRST_LINE_INHERITED` pseudo ID. + +The normal paint invalidation of texts doesn't work for first line because +* `ComputedStyle::VisualInvalidationDiff()` can't detect first line style + changes; +* The normal paint invalidation is based on whole LayoutObject's, not aware of + the first line. -TODO(wangxianzhu): Combine documentation of PaintInvalidation phase into here. +We have a special path for first line style change: the style system informs the +layout system when the computed first-line style changes through +`LayoutObject::FirstLineStyleDidChange()`. When this happens, we invalidate all +`InlineBox`es in the first line. ## Paint diff --git a/chromium/third_party/blink/renderer/core/paint/background_image_geometry.cc b/chromium/third_party/blink/renderer/core/paint/background_image_geometry.cc index 53351c425ad..7533c6b7600 100644 --- a/chromium/third_party/blink/renderer/core/paint/background_image_geometry.cc +++ b/chromium/third_party/blink/renderer/core/paint/background_image_geometry.cc @@ -15,7 +15,7 @@ #include "third_party/blink/renderer/core/paint/paint_layer_scrollable_area.h" #include "third_party/blink/renderer/core/style/border_edge.h" #include "third_party/blink/renderer/platform/geometry/layout_rect.h" -#include "third_party/blink/renderer/platform/layout_unit.h" +#include "third_party/blink/renderer/platform/geometry/layout_unit.h" namespace blink { @@ -44,6 +44,13 @@ bool FixedBackgroundPaintsInLocalCoordinates( const LayoutView& view = ToLayoutView(obj); + // TODO(wangxianzhu): For SPv2, inline this function into + // FixedBackgroundPaintsInLocalCoordinates(). + if (RuntimeEnabledFeatures::SlimmingPaintV2Enabled()) { + return view.GetBackgroundPaintLocation() != + kBackgroundPaintInScrollingContents; + } + if (global_paint_flags & kGlobalPaintFlattenCompositingLayers) return false; @@ -368,9 +375,14 @@ LayoutRect FixedAttachmentPositioningArea(const LayoutBoxModelObject& obj, // The LayoutView is the only object that can paint a fixed background into // its scrolling contents layer, so it gets a special adjustment here. if (obj.IsLayoutView()) { - auto* mapping = obj.Layer()->GetCompositedLayerMapping(); - if (mapping && mapping->BackgroundPaintsOntoScrollingContentsLayer()) + if (RuntimeEnabledFeatures::SlimmingPaintV2Enabled()) { + DCHECK_EQ(obj.GetBackgroundPaintLocation(), + kBackgroundPaintInScrollingContents); rect.SetLocation(IntPoint(ToLayoutView(obj).ScrolledContentOffset())); + } else if (auto* mapping = obj.Layer()->GetCompositedLayerMapping()) { + if (mapping->BackgroundPaintsOntoScrollingContentsLayer()) + rect.SetLocation(IntPoint(ToLayoutView(obj).ScrolledContentOffset())); + } } rect.MoveBy(AccumulatedScrollOffsetForFixedBackground(obj, container)); @@ -665,9 +677,12 @@ void BackgroundImageGeometry::ComputePositioningArea( // Apply the adjustments. snapped_dest_rect_ = unsnapped_dest_rect_; snapped_dest_rect_.Contract(snapped_dest_adjust); + snapped_dest_rect_ = LayoutRect(PixelSnappedIntRect(snapped_dest_rect_)); unsnapped_dest_rect_.Contract(unsnapped_dest_adjust); snapped_positioning_area = unsnapped_positioning_area; snapped_positioning_area.Contract(snapped_box_outset); + snapped_positioning_area = + LayoutRect(PixelSnappedIntRect(snapped_positioning_area)); unsnapped_positioning_area.Contract(unsnapped_box_outset); // Offset of the positioning area from the corner of the @@ -738,12 +753,12 @@ void BackgroundImageGeometry::CalculateFillTileSize( // an intrinsic ratio or size. tile_size_.SetWidth(positioning_area_size.Width()); } else if (image_intrinsic_size.Height()) { - LayoutUnit adjusted_width = image_intrinsic_size.Width() * - tile_size_.Height() / - image_intrinsic_size.Height(); + float adjusted_width = image_intrinsic_size.Width().ToFloat() / + image_intrinsic_size.Height().ToFloat() * + tile_size_.Height().ToFloat(); if (image_intrinsic_size.Width() >= 1 && adjusted_width < 1) - adjusted_width = LayoutUnit(1); - tile_size_.SetWidth(adjusted_width); + adjusted_width = 1; + tile_size_.SetWidth(LayoutUnit(adjusted_width)); } } else if (!layer_width.IsAuto() && layer_height.IsAuto()) { if (image->ImageHasRelativeSize()) { @@ -751,12 +766,12 @@ void BackgroundImageGeometry::CalculateFillTileSize( // an intrinsic ratio or size. tile_size_.SetHeight(positioning_area_size.Height()); } else if (image_intrinsic_size.Width()) { - LayoutUnit adjusted_height = image_intrinsic_size.Height() * - tile_size_.Width() / - image_intrinsic_size.Width(); + float adjusted_height = image_intrinsic_size.Height().ToFloat() / + image_intrinsic_size.Width().ToFloat() * + tile_size_.Width().ToFloat(); if (image_intrinsic_size.Height() >= 1 && adjusted_height < 1) - adjusted_height = LayoutUnit(1); - tile_size_.SetHeight(adjusted_height); + adjusted_height = 1; + tile_size_.SetHeight(LayoutUnit(adjusted_height)); } } else if (layer_width.IsAuto() && layer_height.IsAuto()) { // If both width and height are auto, use the image's intrinsic size. diff --git a/chromium/third_party/blink/renderer/core/paint/block_painter.cc b/chromium/third_party/blink/renderer/core/paint/block_painter.cc index fe5ceef86f8..193441f7626 100644 --- a/chromium/third_party/blink/renderer/core/paint/block_painter.cc +++ b/chromium/third_party/blink/renderer/core/paint/block_painter.cc @@ -39,7 +39,32 @@ void BlockPainter::Paint(const PaintInfo& paint_info) { local_paint_info.phase = PaintPhase::kDescendantOutlinesOnly; } else if (ShouldPaintSelfBlockBackground(original_phase)) { local_paint_info.phase = PaintPhase::kSelfBlockBackgroundOnly; - layout_block_.PaintObject(local_paint_info, paint_offset); + // With SlimmingPaintV2 we need to call PaintObject twice: once for the + // background painting that does not scroll, and a second time for the + // background painting that scrolls. + // Without SlimmingPaintV2, this happens as the main graphics layer + // paints the background, and then the scrolling contents graphics layer + // paints the background. + if (RuntimeEnabledFeatures::SlimmingPaintV2Enabled()) { + auto paint_location = layout_block_.GetBackgroundPaintLocation(); + if (!(paint_location & kBackgroundPaintInGraphicsLayer)) + local_paint_info.SetSkipsBackground(true); + layout_block_.PaintObject(local_paint_info, paint_offset); + local_paint_info.SetSkipsBackground(false); + + // Record the scroll hit test after the non-scrolling background so + // background squashing is not affected. Hit test order would be + // equivalent if this were immediately before the background. + PaintScrollHitTestDisplayItem(paint_info); + + if (paint_location & kBackgroundPaintInScrollingContents) { + local_paint_info.SetIsPaintingScrollingBackground(true); + layout_block_.PaintObject(local_paint_info, paint_offset); + local_paint_info.SetIsPaintingScrollingBackground(false); + } + } else { + layout_block_.PaintObject(local_paint_info, paint_offset); + } if (ShouldPaintDescendantBlockBackgrounds(original_phase)) local_paint_info.phase = PaintPhase::kDescendantBlockBackgroundsOnly; } @@ -201,30 +226,6 @@ void BlockPainter::PaintScrollHitTestDisplayItem(const PaintInfo& paint_info) { } } -// TODO(pdr): Non-blocks also need to paint the hit test display item. Move this -// to a more central place such as BoxPainter. -void BlockPainter::RecordHitTestData(const PaintInfo& paint_info, - const LayoutPoint& paint_offset) { - // Hit test display items are only needed for compositing. This flag is used - // for for printing and drag images which do not need hit testing. - if (paint_info.GetGlobalPaintFlags() & kGlobalPaintFlattenCompositingLayers) - return; - - auto touch_action = layout_block_.EffectiveWhitelistedTouchAction(); - if (touch_action == TouchAction::kTouchActionAuto) - return; - - // TODO(pdr): If we are painting the background into the scrolling contents - // layer, we need to use the overflow rect instead of the border box rect. We - // may want to move the call to RecordHitTestRect into - // BoxPainter::PaintBoxDecorationBackgroundWithRect and share the logic - // the background painting code already uses. - auto rect = layout_block_.BorderBoxRect(); - rect.MoveBy(paint_offset); - HitTestData::RecordHitTestRect(paint_info.context, layout_block_, - HitTestRect(rect, touch_action)); -} - DISABLE_CFI_PERF void BlockPainter::PaintObject(const PaintInfo& paint_info, const LayoutPoint& paint_offset) { @@ -251,21 +252,8 @@ void BlockPainter::PaintObject(const PaintInfo& paint_info, // of the current object and non-self-painting descendants, or 2. // kSelfBlockBackgroundOnly - Paint background of the current object only), // paint those now. This is steps #1, 2, and 4 of the CSS spec (see above). - if (ShouldPaintSelfBlockBackground(paint_phase)) { - // Paint the background if we're visible and this block has a box decoration - // (background, border, appearance, or box shadow). - if (layout_block_.StyleRef().Visibility() == EVisibility::kVisible && - layout_block_.HasBoxDecorationBackground()) { - layout_block_.PaintBoxDecorationBackground(paint_info, paint_offset); - } - if (RuntimeEnabledFeatures::PaintTouchActionRectsEnabled()) - RecordHitTestData(paint_info, paint_offset); - // Record the scroll hit test after the background so background squashing - // is not affected. Hit test order would be equivalent if this were - // immediately before the background. - if (RuntimeEnabledFeatures::SlimmingPaintV2Enabled()) - PaintScrollHitTestDisplayItem(paint_info); - } + if (ShouldPaintSelfBlockBackground(paint_phase)) + layout_block_.PaintBoxDecorationBackground(paint_info, paint_offset); // If we're in any phase except *just* the self (outline or background) or a // mask, paint children now. This is step #5, 7, 8, and 9 of the CSS spec (see diff --git a/chromium/third_party/blink/renderer/core/paint/block_painter.h b/chromium/third_party/blink/renderer/core/paint/block_painter.h index 175e962d18c..e16f0be0a7a 100644 --- a/chromium/third_party/blink/renderer/core/paint/block_painter.h +++ b/chromium/third_party/blink/renderer/core/paint/block_painter.h @@ -40,10 +40,6 @@ class BlockPainter { // Paint scroll hit test placeholders in the correct paint order (see: // ScrollHitTestDisplayItem.h). void PaintScrollHitTestDisplayItem(const PaintInfo&); - // Paint a hit test display item and record hit test data. This should be - // called in the background paint phase even if there is no other painted - // content. - void RecordHitTestData(const PaintInfo&, const LayoutPoint& paint_offset); void PaintBlockFlowContents(const PaintInfo&, const LayoutPoint&); void PaintCarets(const PaintInfo&, const LayoutPoint& paint_offset); diff --git a/chromium/third_party/blink/renderer/core/paint/block_painter_test.cc b/chromium/third_party/blink/renderer/core/paint/block_painter_test.cc index ea569aa7e3c..180c207955b 100644 --- a/chromium/third_party/blink/renderer/core/paint/block_painter_test.cc +++ b/chromium/third_party/blink/renderer/core/paint/block_painter_test.cc @@ -4,14 +4,18 @@ #include "third_party/blink/renderer/core/paint/block_painter.h" -#include <gtest/gtest.h> +#include "testing/gmock/include/gmock/gmock.h" +#include "testing/gtest/include/gtest/gtest.h" #include "third_party/blink/renderer/core/dom/events/event_listener.h" #include "third_party/blink/renderer/core/frame/local_frame_view.h" +#include "third_party/blink/renderer/core/paint/compositing/composited_layer_mapping.h" #include "third_party/blink/renderer/core/paint/paint_controller_paint_test.h" #include "third_party/blink/renderer/platform/graphics/paint/drawing_display_item.h" #include "third_party/blink/renderer/platform/graphics/paint/paint_chunk.h" #include "third_party/blink/renderer/platform/graphics/paint/scroll_hit_test_display_item.h" +using testing::ElementsAre; + namespace blink { using BlockPainterTest = PaintControllerPaintTest; @@ -32,59 +36,72 @@ TEST_P(BlockPainterTest, ScrollHitTestProperties) { </div> )HTML"); - auto& container = *GetLayoutObjectByElementId("container"); + auto& container = ToLayoutBlock(*GetLayoutObjectByElementId("container")); auto& child = *GetLayoutObjectByElementId("child"); // The scroll hit test should be after the container background but before the // scrolled contents. - EXPECT_DISPLAY_LIST(RootPaintController().GetDisplayItemList(), 4, - TestDisplayItem(GetLayoutView(), kDocumentBackgroundType), - TestDisplayItem(container, kBackgroundType), - TestDisplayItem(container, kScrollHitTestType), - TestDisplayItem(child, kBackgroundType)); - const auto& paint_chunks = - RootPaintController().GetPaintArtifact().PaintChunks(); - EXPECT_EQ(4u, paint_chunks.size()); - const auto& root_chunk = RootPaintController().PaintChunks()[0]; - EXPECT_EQ(GetLayoutView().Layer(), &root_chunk.id.client); - const auto& container_chunk = RootPaintController().PaintChunks()[1]; - EXPECT_EQ(ToLayoutBoxModelObject(container).Layer(), - &container_chunk.id.client); - // The container's scroll hit test. - const auto& scroll_hit_test_chunk = RootPaintController().PaintChunks()[2]; - EXPECT_EQ(&container, &scroll_hit_test_chunk.id.client); - EXPECT_EQ(kScrollHitTestType, scroll_hit_test_chunk.id.type); - // The scrolled contents. - const auto& contents_chunk = RootPaintController().PaintChunks()[3]; - EXPECT_EQ(&container, &contents_chunk.id.client); + EXPECT_EQ( + kBackgroundPaintInGraphicsLayer | kBackgroundPaintInScrollingContents, + container.GetBackgroundPaintLocation()); + EXPECT_THAT( + RootPaintController().GetDisplayItemList(), + ElementsAre( + IsSameId(&ViewScrollingBackgroundClient(), kDocumentBackgroundType), + IsSameId(&container, kBackgroundType), + IsSameId(&container, kScrollHitTestType), + IsSameId(&container.GetScrollableArea() + ->GetScrollingBackgroundDisplayItemClient(), + kBackgroundType), + IsSameId(&child, kBackgroundType))); + + const auto& paint_chunks = RootPaintController().PaintChunks(); + EXPECT_THAT( + paint_chunks, + ElementsAre( + IsPaintChunk(0, 1, + PaintChunk::Id(ViewScrollingBackgroundClient(), + kDocumentBackgroundType), + GetLayoutView().FirstFragment().ContentsProperties()), + IsPaintChunk(1, 2, + PaintChunk::Id(*container.Layer(), + kNonScrollingBackgroundChunkType), + container.FirstFragment().LocalBorderBoxProperties()), + IsPaintChunk(2, 3, PaintChunk::Id(container, kScrollHitTestType), + container.FirstFragment().LocalBorderBoxProperties()), + IsPaintChunk(3, 5, + PaintChunk::Id(container, kScrollingBackgroundChunkType), + container.FirstFragment().ContentsProperties()))); // The document should not scroll so there should be no scroll offset // transform. - auto* root_transform = root_chunk.properties.Transform(); + const auto* root_transform = paint_chunks[0].properties.Transform(); EXPECT_EQ(nullptr, root_transform->ScrollNode()); // The container's background chunk should not scroll and therefore should use // the root transform. Its local transform is actually a paint offset // transform. - auto* container_transform = container_chunk.properties.Transform()->Parent(); - EXPECT_EQ(root_transform, container_transform); + const auto* container_transform = paint_chunks[1].properties.Transform(); + EXPECT_EQ(root_transform, container_transform->Parent()); EXPECT_EQ(nullptr, container_transform->ScrollNode()); // The scroll hit test should not be scrolled and should not be clipped. // Its local transform is actually a paint offset transform. - auto* scroll_hit_test_transform = - scroll_hit_test_chunk.properties.Transform()->Parent(); + const auto& scroll_hit_test_chunk = paint_chunks[2]; + const auto* scroll_hit_test_transform = + scroll_hit_test_chunk.properties.Transform(); EXPECT_EQ(nullptr, scroll_hit_test_transform->ScrollNode()); - EXPECT_EQ(root_transform, scroll_hit_test_transform); - auto* scroll_hit_test_clip = scroll_hit_test_chunk.properties.Clip(); + EXPECT_EQ(root_transform, scroll_hit_test_transform->Parent()); + const auto* scroll_hit_test_clip = scroll_hit_test_chunk.properties.Clip(); EXPECT_EQ(FloatRect(0, 0, 800, 600), scroll_hit_test_clip->ClipRect().Rect()); // The scrolled contents should be scrolled and clipped. - auto* contents_transform = contents_chunk.properties.Transform(); - auto* contents_scroll = contents_transform->ScrollNode(); + const auto& contents_chunk = RootPaintController().PaintChunks()[3]; + const auto* contents_transform = contents_chunk.properties.Transform(); + const auto* contents_scroll = contents_transform->ScrollNode(); EXPECT_EQ(IntSize(200, 300), contents_scroll->ContentsSize()); EXPECT_EQ(IntRect(0, 0, 200, 200), contents_scroll->ContainerRect()); - auto* contents_clip = contents_chunk.properties.Clip(); + const auto* contents_clip = contents_chunk.properties.Clip(); EXPECT_EQ(FloatRect(0, 0, 200, 200), contents_clip->ClipRect().Rect()); // The scroll hit test display item maintains a reference to a scroll offset @@ -107,42 +124,52 @@ TEST_P(BlockPainterTest, FrameScrollHitTestProperties) { <div id='child'></div> )HTML"); - auto& html = *GetDocument().documentElement()->GetLayoutObject(); + auto& html = + ToLayoutBlock(*GetDocument().documentElement()->GetLayoutObject()); auto& child = *GetLayoutObjectByElementId("child"); // The scroll hit test should be after the document background but before the // scrolled contents. - EXPECT_DISPLAY_LIST(RootPaintController().GetDisplayItemList(), 3, - TestDisplayItem(GetLayoutView(), kDocumentBackgroundType), - TestDisplayItem(GetLayoutView(), kScrollHitTestType), - TestDisplayItem(child, kBackgroundType)); - - const auto& paint_chunks = - RootPaintController().GetPaintArtifact().PaintChunks(); - EXPECT_EQ(3u, paint_chunks.size()); - const auto& root_chunk = RootPaintController().PaintChunks()[0]; - EXPECT_EQ(GetLayoutView().Layer(), &root_chunk.id.client); - const auto& scroll_hit_test_chunk = RootPaintController().PaintChunks()[1]; - EXPECT_EQ(&GetLayoutView(), &scroll_hit_test_chunk.id.client); - EXPECT_EQ(kScrollHitTestType, scroll_hit_test_chunk.id.type); - // The scrolled contents. - const auto& contents_chunk = RootPaintController().PaintChunks()[2]; - EXPECT_EQ(ToLayoutBoxModelObject(html).Layer(), &contents_chunk.id.client); + EXPECT_THAT(RootPaintController().GetDisplayItemList(), + ElementsAre(IsSameId(&GetLayoutView(), kScrollHitTestType), + IsSameId(&ViewScrollingBackgroundClient(), + kDocumentBackgroundType), + IsSameId(&child, kBackgroundType))); + + const auto& paint_chunks = RootPaintController().PaintChunks(); + EXPECT_THAT( + paint_chunks, + ElementsAre( + IsPaintChunk( + 0, 1, + PaintChunk::Id(*GetLayoutView().Layer(), + DisplayItem::kLayerChunkBackground), + GetLayoutView().FirstFragment().LocalBorderBoxProperties()), + IsPaintChunk(1, 2, + PaintChunk::Id(ViewScrollingBackgroundClient(), + kDocumentBackgroundType), + GetLayoutView().FirstFragment().ContentsProperties()), + IsPaintChunk(2, 3, + PaintChunk::Id(*html.Layer(), + kNonScrollingContentsBackgroundChunkType), + html.FirstFragment().ContentsProperties()))); // The scroll hit test should not be scrolled and should not be clipped. - auto* scroll_hit_test_transform = + const auto& scroll_hit_test_chunk = RootPaintController().PaintChunks()[0]; + const auto* scroll_hit_test_transform = scroll_hit_test_chunk.properties.Transform(); EXPECT_EQ(nullptr, scroll_hit_test_transform->ScrollNode()); - auto* scroll_hit_test_clip = scroll_hit_test_chunk.properties.Clip(); + const auto* scroll_hit_test_clip = scroll_hit_test_chunk.properties.Clip(); EXPECT_EQ(FloatRect(LayoutRect::InfiniteIntRect()), scroll_hit_test_clip->ClipRect().Rect()); // The scrolled contents should be scrolled and clipped. - auto* contents_transform = contents_chunk.properties.Transform(); - auto* contents_scroll = contents_transform->ScrollNode(); + const auto& contents_chunk = RootPaintController().PaintChunks()[2]; + const auto* contents_transform = contents_chunk.properties.Transform(); + const auto* contents_scroll = contents_transform->ScrollNode(); EXPECT_EQ(IntSize(800, 2000), contents_scroll->ContentsSize()); EXPECT_EQ(IntRect(0, 0, 800, 600), contents_scroll->ContainerRect()); - auto* contents_clip = contents_chunk.properties.Clip(); + const auto* contents_clip = contents_chunk.properties.Clip(); EXPECT_EQ(FloatRect(0, 0, 800, 600), contents_clip->ClipRect().Rect()); // The scroll hit test display item maintains a reference to a scroll offset @@ -173,6 +200,7 @@ TEST_F(BlockPainterTestWithPaintTouchAction, TouchActionRectsWithoutPaint) { .touchActionNone { touch-action: none; } #childVisible { width: 200px; height: 25px; } #childHidden { width: 200px; height: 30px; visibility: hidden; } + #childDisplayNone { width: 200px; height: 30px; display: none; } </style> <div id='parent'> <div id='childVisible'></div> @@ -182,33 +210,92 @@ TEST_F(BlockPainterTestWithPaintTouchAction, TouchActionRectsWithoutPaint) { // Initially there should be no hit test display items because there is no // touch action. - auto* scrolling_client = GetLayoutView().Layer()->GraphicsLayerBacking(); - EXPECT_DISPLAY_LIST( - RootPaintController().GetDisplayItemList(), 1, - TestDisplayItem(*scrolling_client, kDocumentBackgroundType)); + const auto& scrolling_client = ViewScrollingBackgroundClient(); + EXPECT_THAT( + RootPaintController().GetDisplayItemList(), + ElementsAre(IsSameId(&scrolling_client, kDocumentBackgroundType))); // Add a touch action to parent and ensure that hit test display items are - // created for both the parent and child. + // created for both the parent and the visible child. auto* parent_element = GetElementById("parent"); - parent_element->setAttribute(HTMLNames::classAttr, "touchActionNone"); - GetDocument().View()->UpdateAllLifecyclePhases(); + parent_element->setAttribute(html_names::kClassAttr, "touchActionNone"); + UpdateAllLifecyclePhasesForTest(); auto* parent = GetLayoutObjectByElementId("parent"); - auto* childVisible = GetLayoutObjectByElementId("childVisible"); - auto* childHidden = GetLayoutObjectByElementId("childHidden"); - EXPECT_DISPLAY_LIST( - RootPaintController().GetDisplayItemList(), 4, - TestDisplayItem(*scrolling_client, kDocumentBackgroundType), - TestDisplayItem(*parent, DisplayItem::kHitTest), - TestDisplayItem(*childVisible, DisplayItem::kHitTest), - TestDisplayItem(*childHidden, DisplayItem::kHitTest)); + auto* child_visible = GetLayoutObjectByElementId("childVisible"); + EXPECT_THAT(RootPaintController().GetDisplayItemList(), + ElementsAre(IsSameId(&scrolling_client, kDocumentBackgroundType), + IsSameId(parent, DisplayItem::kHitTest), + IsSameId(child_visible, DisplayItem::kHitTest))); // Remove the touch action from parent and ensure no hit test display items // are left. - parent_element->removeAttribute(HTMLNames::classAttr); - GetDocument().View()->UpdateAllLifecyclePhases(); - EXPECT_DISPLAY_LIST( - RootPaintController().GetDisplayItemList(), 1, - TestDisplayItem(*scrolling_client, kDocumentBackgroundType)); + parent_element->removeAttribute(html_names::kClassAttr); + UpdateAllLifecyclePhasesForTest(); + EXPECT_THAT( + RootPaintController().GetDisplayItemList(), + ElementsAre(IsSameId(&scrolling_client, kDocumentBackgroundType))); +} + +TEST_F(BlockPainterTestWithPaintTouchAction, + TouchActionRectSubsequenceCaching) { + SetBodyInnerHTML(R"HTML( + <style> + body { margin: 0; } + #touchaction { + width: 100px; + height: 100px; + touch-action: none; + } + #sibling { + width: 100px; + height: 100px; + background: blue; + } + </style> + <div id='touchaction'></div> + )HTML"); + + const auto& scrolling_client = ViewScrollingBackgroundClient(); + const auto* touchaction = GetLayoutObjectByElementId("touchaction"); + EXPECT_THAT(RootPaintController().GetDisplayItemList(), + ElementsAre(IsSameId(&scrolling_client, kDocumentBackgroundType), + IsSameId(touchaction, DisplayItem::kHitTest))); + + const auto& hit_test_client = *touchaction->EnclosingLayer(); + EXPECT_SUBSEQUENCE(hit_test_client, 1, 2); + + PaintChunk::Id root_chunk_id(scrolling_client, kDocumentBackgroundType); + auto root_chunk_properties = + GetLayoutView().FirstFragment().ContentsProperties(); + + PaintChunk::Id hit_test_chunk_id(hit_test_client, + kNonScrollingBackgroundChunkType); + auto hit_test_chunk_properties = touchaction->EnclosingLayer() + ->GetLayoutObject() + .FirstFragment() + .ContentsProperties(); + HitTestData hit_test_data; + hit_test_data.touch_action_rects.emplace_back(LayoutRect(0, 0, 100, 100)); + + EXPECT_THAT( + RootPaintController().PaintChunks(), + ElementsAre(IsPaintChunk(0, 1, root_chunk_id, root_chunk_properties), + IsPaintChunk(1, 2, hit_test_chunk_id, + hit_test_chunk_properties, hit_test_data))); + + // Trigger a repaint with the whole HTML subsequence cached. + GetLayoutView().Layer()->SetNeedsRepaint(); + EXPECT_TRUE(PaintWithoutCommit()); + EXPECT_EQ(2, NumCachedNewItems()); + CommitAndFinishCycle(); + + EXPECT_SUBSEQUENCE(hit_test_client, 1, 2); + + EXPECT_THAT( + RootPaintController().PaintChunks(), + ElementsAre(IsPaintChunk(0, 1, root_chunk_id, root_chunk_properties), + IsPaintChunk(1, 2, hit_test_chunk_id, + hit_test_chunk_properties, hit_test_data))); } TEST_F(BlockPainterTestWithPaintTouchAction, TouchActionRectPaintCaching) { @@ -230,55 +317,104 @@ TEST_F(BlockPainterTestWithPaintTouchAction, TouchActionRectPaintCaching) { <div id='sibling'></div> )HTML"); - auto* scrolling_client = GetLayoutView().Layer()->GraphicsLayerBacking(); - auto* touchaction_element = GetElementById("touchaction"); - auto* touchaction = touchaction_element->GetLayoutObject(); + const auto& scrolling_client = ViewScrollingBackgroundClient(); + const auto* touchaction = GetLayoutObjectByElementId("touchaction"); auto* sibling_element = GetElementById("sibling"); - auto* sibling = sibling_element->GetLayoutObject(); - EXPECT_DISPLAY_LIST( - RootPaintController().GetDisplayItemList(), 3, - TestDisplayItem(*scrolling_client, kDocumentBackgroundType), - TestDisplayItem(*touchaction, DisplayItem::kHitTest), - TestDisplayItem(*sibling, kBackgroundType)); - - { - const auto& paint_chunks = - RootPaintController().GetPaintArtifact().PaintChunks(); - EXPECT_EQ(paint_chunks.size(), 2u); - auto& background_chunk = paint_chunks[0]; - EXPECT_EQ(nullptr, background_chunk.GetHitTestData()); - auto& hit_test_chunk = paint_chunks[1]; - DCHECK(hit_test_chunk.GetHitTestData()); - EXPECT_EQ(1u, hit_test_chunk.GetHitTestData()->touch_action_rects.size()); - auto& touch_action_rect = - hit_test_chunk.GetHitTestData()->touch_action_rects[0]; - EXPECT_EQ(LayoutRect(0, 0, 100, 100), touch_action_rect.rect); - EXPECT_EQ(TouchAction::kTouchActionNone, - touch_action_rect.whitelisted_touch_action); - } - - sibling_element->setAttribute(HTMLNames::styleAttr, "background: green;"); + const auto* sibling = sibling_element->GetLayoutObject(); + EXPECT_THAT(RootPaintController().GetDisplayItemList(), + ElementsAre(IsSameId(&scrolling_client, kDocumentBackgroundType), + IsSameId(touchaction, DisplayItem::kHitTest), + IsSameId(sibling, kBackgroundType))); + + PaintChunk::Id root_chunk_id(scrolling_client, kDocumentBackgroundType); + auto root_chunk_properties = + GetLayoutView().FirstFragment().ContentsProperties(); + + PaintChunk::Id hit_test_chunk_id(*touchaction->EnclosingLayer(), + kNonScrollingBackgroundChunkType); + auto hit_test_chunk_properties = touchaction->EnclosingLayer() + ->GetLayoutObject() + .FirstFragment() + .ContentsProperties(); + HitTestData hit_test_data; + hit_test_data.touch_action_rects.emplace_back(LayoutRect(0, 0, 100, 100)); + + EXPECT_THAT( + RootPaintController().PaintChunks(), + ElementsAre(IsPaintChunk(0, 1, root_chunk_id, root_chunk_properties), + IsPaintChunk(1, 3, hit_test_chunk_id, + hit_test_chunk_properties, hit_test_data))); + + sibling_element->setAttribute(html_names::kStyleAttr, "background: green;"); GetDocument().View()->UpdateAllLifecyclePhasesExceptPaint(); EXPECT_TRUE(PaintWithoutCommit()); // Only the background display item of the sibling should be invalidated. EXPECT_EQ(2, NumCachedNewItems()); CommitAndFinishCycle(); - { - const auto& paint_chunks = - RootPaintController().GetPaintArtifact().PaintChunks(); - EXPECT_EQ(paint_chunks.size(), 2u); - auto& background_chunk = paint_chunks[0]; - EXPECT_EQ(nullptr, background_chunk.GetHitTestData()); - auto& hit_test_chunk = paint_chunks[1]; - DCHECK(hit_test_chunk.GetHitTestData()); - EXPECT_EQ(1u, hit_test_chunk.GetHitTestData()->touch_action_rects.size()); - auto& touch_action_rect = - hit_test_chunk.GetHitTestData()->touch_action_rects[0]; - EXPECT_EQ(LayoutRect(0, 0, 100, 100), touch_action_rect.rect); - EXPECT_EQ(TouchAction::kTouchActionNone, - touch_action_rect.whitelisted_touch_action); - } + EXPECT_THAT( + RootPaintController().PaintChunks(), + ElementsAre(IsPaintChunk(0, 1, root_chunk_id, root_chunk_properties), + IsPaintChunk(1, 3, hit_test_chunk_id, + hit_test_chunk_properties, hit_test_data))); +} + +TEST_F(BlockPainterTestWithPaintTouchAction, TouchActionRectScrollingContents) { + SetBodyInnerHTML(R"HTML( + <style> + ::-webkit-scrollbar { display: none; } + body { margin: 0; } + #scroller { + width: 100px; + height: 100px; + overflow: scroll; + touch-action: none; + will-change: transform; + background-color: blue; + } + #child { + width: 10px; + height: 400px; + } + </style> + <div id='scroller'> + <div id='child'></div> + </div> + )HTML"); + + const auto& root_client = GetLayoutView() + .GetScrollableArea() + ->GetScrollingBackgroundDisplayItemClient(); + auto* scroller_element = GetElementById("scroller"); + auto* scroller = ToLayoutBoxModelObject(scroller_element->GetLayoutObject()); + const auto& scroller_client = + scroller->GetScrollableArea()->GetScrollingBackgroundDisplayItemClient(); + auto* child_element = GetElementById("child"); + auto* child = child_element->GetLayoutObject(); + auto& non_scroller_paint_controller = RootPaintController(); + auto& scroller_paint_controller = scroller->GetScrollableArea() + ->Layer() + ->GraphicsLayerBacking() + ->GetPaintController(); + EXPECT_THAT(scroller_paint_controller.GetDisplayItemList(), + ElementsAre(IsSameId(&scroller_client, kBackgroundType), + IsSameId(&scroller_client, DisplayItem::kHitTest), + IsSameId(child, DisplayItem::kHitTest))); + HitTestData hit_test_data; + hit_test_data.touch_action_rects.emplace_back(LayoutRect(0, 0, 100, 400)); + hit_test_data.touch_action_rects.emplace_back(LayoutRect(0, 0, 10, 400)); + EXPECT_THAT( + scroller_paint_controller.PaintChunks(), + ElementsAre(IsPaintChunk( + 0, 3, PaintChunk::Id(*scroller, kScrollingBackgroundChunkType), + scroller->FirstFragment().ContentsProperties(), hit_test_data))); + + EXPECT_THAT(non_scroller_paint_controller.GetDisplayItemList(), + ElementsAre(IsSameId(&root_client, kDocumentBackgroundType))); + EXPECT_THAT(non_scroller_paint_controller.PaintChunks(), + ElementsAre(IsPaintChunk( + 0, 1, PaintChunk::Id(root_client, kDocumentBackgroundType), + GetLayoutView().FirstFragment().ContentsProperties()))); } TEST_F(BlockPainterTestWithPaintTouchAction, TouchActionRectPaintChunkChanges) { @@ -293,55 +429,51 @@ TEST_F(BlockPainterTestWithPaintTouchAction, TouchActionRectPaintChunkChanges) { <div id='touchaction'></div> )HTML"); - auto* scrolling_client = GetLayoutView().Layer()->GraphicsLayerBacking(); + const auto& scrolling_client = ViewScrollingBackgroundClient(); auto* touchaction_element = GetElementById("touchaction"); auto* touchaction = touchaction_element->GetLayoutObject(); - EXPECT_DISPLAY_LIST( - RootPaintController().GetDisplayItemList(), 1, - TestDisplayItem(*scrolling_client, kDocumentBackgroundType)); - - { - const auto& paint_chunks = - RootPaintController().GetPaintArtifact().PaintChunks(); - EXPECT_EQ(paint_chunks.size(), 1u); - EXPECT_EQ(nullptr, paint_chunks[0].GetHitTestData()); - } + EXPECT_THAT( + RootPaintController().GetDisplayItemList(), + ElementsAre(IsSameId(&scrolling_client, kDocumentBackgroundType))); - touchaction_element->setAttribute(HTMLNames::styleAttr, - "touch-action: none;"); - GetDocument().View()->UpdateAllLifecyclePhases(); - EXPECT_DISPLAY_LIST( - RootPaintController().GetDisplayItemList(), 2, - TestDisplayItem(*scrolling_client, kDocumentBackgroundType), - TestDisplayItem(*touchaction, DisplayItem::kHitTest)); - - { - const auto& paint_chunks = - RootPaintController().GetPaintArtifact().PaintChunks(); - EXPECT_EQ(paint_chunks.size(), 2u); - auto& background_chunk = paint_chunks[0]; - EXPECT_EQ(nullptr, background_chunk.GetHitTestData()); - auto& hit_test_chunk = paint_chunks[1]; - DCHECK(hit_test_chunk.GetHitTestData()); - EXPECT_EQ(1u, hit_test_chunk.GetHitTestData()->touch_action_rects.size()); - auto& touch_action_rect = - hit_test_chunk.GetHitTestData()->touch_action_rects[0]; - EXPECT_EQ(LayoutRect(0, 0, 100, 100), touch_action_rect.rect); - EXPECT_EQ(TouchAction::kTouchActionNone, - touch_action_rect.whitelisted_touch_action); - } + PaintChunk::Id root_chunk_id(scrolling_client, kDocumentBackgroundType); + auto root_chunk_properties = + GetLayoutView().FirstFragment().ContentsProperties(); - touchaction_element->removeAttribute(HTMLNames::styleAttr); - GetDocument().View()->UpdateAllLifecyclePhases(); - EXPECT_DISPLAY_LIST( - RootPaintController().GetDisplayItemList(), 1, - TestDisplayItem(*scrolling_client, kDocumentBackgroundType)); - { - const auto& paint_chunks = - RootPaintController().GetPaintArtifact().PaintChunks(); - EXPECT_EQ(paint_chunks.size(), 1u); - EXPECT_EQ(nullptr, paint_chunks[0].GetHitTestData()); - } + EXPECT_THAT( + RootPaintController().PaintChunks(), + ElementsAre(IsPaintChunk(0, 1, root_chunk_id, root_chunk_properties))); + + touchaction_element->setAttribute(html_names::kStyleAttr, + "touch-action: none;"); + UpdateAllLifecyclePhasesForTest(); + EXPECT_THAT(RootPaintController().GetDisplayItemList(), + ElementsAre(IsSameId(&scrolling_client, kDocumentBackgroundType), + IsSameId(touchaction, DisplayItem::kHitTest))); + + PaintChunk::Id hit_test_chunk_id(*touchaction->EnclosingLayer(), + kNonScrollingBackgroundChunkType); + auto hit_test_chunk_properties = touchaction->EnclosingLayer() + ->GetLayoutObject() + .FirstFragment() + .ContentsProperties(); + HitTestData hit_test_data; + hit_test_data.touch_action_rects.emplace_back(LayoutRect(0, 0, 100, 100)); + + EXPECT_THAT( + RootPaintController().PaintChunks(), + ElementsAre(IsPaintChunk(0, 1, root_chunk_id, root_chunk_properties), + IsPaintChunk(1, 2, hit_test_chunk_id, + hit_test_chunk_properties, hit_test_data))); + + touchaction_element->removeAttribute(html_names::kStyleAttr); + UpdateAllLifecyclePhasesForTest(); + EXPECT_THAT( + RootPaintController().GetDisplayItemList(), + ElementsAre(IsSameId(&scrolling_client, kDocumentBackgroundType))); + EXPECT_THAT( + RootPaintController().PaintChunks(), + ElementsAre(IsPaintChunk(0, 1, root_chunk_id, root_chunk_properties))); } namespace { @@ -353,7 +485,7 @@ class BlockPainterMockEventListener final : public EventListener { return this == &other; } - void handleEvent(ExecutionContext*, Event*) final {} + void Invoke(ExecutionContext*, Event*) final {} }; } // namespace @@ -372,32 +504,32 @@ TEST_F(BlockPainterTestWithPaintTouchAction, TouchHandlerRectsWithoutPaint) { // Initially there should be no hit test display items because there are no // event handlers. - auto* scrolling_client = GetLayoutView().Layer()->GraphicsLayerBacking(); - EXPECT_DISPLAY_LIST( - RootPaintController().GetDisplayItemList(), 1, - TestDisplayItem(*scrolling_client, kDocumentBackgroundType)); + const auto& scrolling_client = ViewScrollingBackgroundClient(); + EXPECT_THAT( + RootPaintController().GetDisplayItemList(), + ElementsAre(IsSameId(&scrolling_client, kDocumentBackgroundType))); // Add an event listener to parent and ensure that hit test display items are // created for both the parent and child. BlockPainterMockEventListener* callback = new BlockPainterMockEventListener(); auto* parent_element = GetElementById("parent"); - parent_element->addEventListener(EventTypeNames::touchstart, callback); - GetDocument().View()->UpdateAllLifecyclePhases(); + parent_element->addEventListener(event_type_names::kTouchstart, callback); + UpdateAllLifecyclePhasesForTest(); + auto* parent = GetLayoutObjectByElementId("parent"); auto* child = GetLayoutObjectByElementId("child"); - EXPECT_DISPLAY_LIST( - RootPaintController().GetDisplayItemList(), 3, - TestDisplayItem(*scrolling_client, kDocumentBackgroundType), - TestDisplayItem(*parent, DisplayItem::kHitTest), - TestDisplayItem(*child, DisplayItem::kHitTest)); + EXPECT_THAT(RootPaintController().GetDisplayItemList(), + ElementsAre(IsSameId(&scrolling_client, kDocumentBackgroundType), + IsSameId(parent, DisplayItem::kHitTest), + IsSameId(child, DisplayItem::kHitTest))); // Remove the event handler from parent and ensure no hit test display items // are left. parent_element->RemoveAllEventListeners(); - GetDocument().View()->UpdateAllLifecyclePhases(); - EXPECT_DISPLAY_LIST( - RootPaintController().GetDisplayItemList(), 1, - TestDisplayItem(*scrolling_client, kDocumentBackgroundType)); + UpdateAllLifecyclePhasesForTest(); + EXPECT_THAT( + RootPaintController().GetDisplayItemList(), + ElementsAre(IsSameId(&scrolling_client, kDocumentBackgroundType))); } TEST_F(BlockPainterTestWithPaintTouchAction, @@ -414,24 +546,22 @@ TEST_F(BlockPainterTestWithPaintTouchAction, </div> )HTML"); - auto* scrolling_client = GetLayoutView().Layer()->GraphicsLayerBacking(); + const auto& scrolling_client = ViewScrollingBackgroundClient(); auto* parent = GetLayoutObjectByElementId("parent"); auto* child = GetLayoutObjectByElementId("child"); - EXPECT_DISPLAY_LIST( - RootPaintController().GetDisplayItemList(), 3, - TestDisplayItem(*scrolling_client, kDocumentBackgroundType), - TestDisplayItem(*parent, DisplayItem::kHitTest), - TestDisplayItem(*child, DisplayItem::kHitTest)); + EXPECT_THAT(RootPaintController().GetDisplayItemList(), + ElementsAre(IsSameId(&scrolling_client, kDocumentBackgroundType), + IsSameId(parent, DisplayItem::kHitTest), + IsSameId(child, DisplayItem::kHitTest))); auto* child_element = GetElementById("parent"); child_element->setAttribute("style", "background: blue;"); - GetDocument().View()->UpdateAllLifecyclePhases(); - EXPECT_DISPLAY_LIST( - RootPaintController().GetDisplayItemList(), 4, - TestDisplayItem(*scrolling_client, kDocumentBackgroundType), - TestDisplayItem(*parent, kBackgroundType), - TestDisplayItem(*parent, DisplayItem::kHitTest), - TestDisplayItem(*child, DisplayItem::kHitTest)); + UpdateAllLifecyclePhasesForTest(); + EXPECT_THAT(RootPaintController().GetDisplayItemList(), + ElementsAre(IsSameId(&scrolling_client, kDocumentBackgroundType), + IsSameId(parent, kBackgroundType), + IsSameId(parent, DisplayItem::kHitTest), + IsSameId(child, DisplayItem::kHitTest))); } TEST_F(BlockPainterTestWithPaintTouchAction, ScrolledHitTestChunkProperties) { @@ -456,28 +586,45 @@ TEST_F(BlockPainterTestWithPaintTouchAction, ScrolledHitTestChunkProperties) { </div> )HTML"); - auto* scrolling_client = GetLayoutView().Layer()->GraphicsLayerBacking(); - auto* scroller = GetLayoutObjectByElementId("scroller"); - auto* child = GetLayoutObjectByElementId("child"); - EXPECT_DISPLAY_LIST( - RootPaintController().GetDisplayItemList(), 3, - TestDisplayItem(*scrolling_client, kDocumentBackgroundType), - TestDisplayItem(*scroller, DisplayItem::kHitTest), - TestDisplayItem(*child, DisplayItem::kHitTest)); - - const auto& paint_chunks = - RootPaintController().GetPaintArtifact().PaintChunks(); - EXPECT_EQ(3u, paint_chunks.size()); - - const auto& scroller_paint_chunk = RootPaintController().PaintChunks()[1]; - EXPECT_EQ(ToLayoutBoxModelObject(scroller)->Layer(), - &scroller_paint_chunk.id.client); + const auto& scrolling_client = ViewScrollingBackgroundClient(); + const auto* scroller = ToLayoutBlock(GetLayoutObjectByElementId("scroller")); + const auto* child = GetLayoutObjectByElementId("child"); + EXPECT_THAT(RootPaintController().GetDisplayItemList(), + ElementsAre(IsSameId(&scrolling_client, kDocumentBackgroundType), + IsSameId(scroller, DisplayItem::kHitTest), + IsSameId(child, DisplayItem::kHitTest))); + + HitTestData scroller_hit_test_data; + scroller_hit_test_data.touch_action_rects.emplace_back( + LayoutRect(0, 0, 100, 100)); + HitTestData scrolled_hit_test_data; + scrolled_hit_test_data.touch_action_rects.emplace_back( + LayoutRect(0, 0, 200, 50)); + + const auto& paint_chunks = RootPaintController().PaintChunks(); + EXPECT_THAT( + paint_chunks, + ElementsAre( + IsPaintChunk( + 0, 1, PaintChunk::Id(scrolling_client, kDocumentBackgroundType), + GetLayoutView().FirstFragment().ContentsProperties()), + IsPaintChunk(1, 2, + PaintChunk::Id(*scroller->Layer(), + kNonScrollingBackgroundChunkType), + scroller->FirstFragment().LocalBorderBoxProperties(), + scroller_hit_test_data), + IsPaintChunk( + 2, 3, + PaintChunk::Id(*scroller, kScrollingContentsBackgroundChunkType), + scroller->FirstFragment().ContentsProperties(), + scrolled_hit_test_data))); + + const auto& scroller_paint_chunk = paint_chunks[1]; EXPECT_EQ(FloatRect(0, 0, 100, 100), scroller_paint_chunk.bounds); // The hit test rect for the scroller itself should not be scrolled. EXPECT_FALSE(scroller_paint_chunk.properties.Transform()->ScrollNode()); - const auto& scrolled_paint_chunk = RootPaintController().PaintChunks()[2]; - EXPECT_EQ(scroller, &scrolled_paint_chunk.id.client); + const auto& scrolled_paint_chunk = paint_chunks[2]; EXPECT_EQ(FloatRect(0, 0, 200, 50), scrolled_paint_chunk.bounds); // The hit test rect for the scrolled contents should be scrolled. EXPECT_TRUE(scrolled_paint_chunk.properties.Transform()->ScrollNode()); diff --git a/chromium/third_party/blink/renderer/core/paint/box_model_object_painter.cc b/chromium/third_party/blink/renderer/core/paint/box_model_object_painter.cc index 268adbb68ee..7b320469781 100644 --- a/chromium/third_party/blink/renderer/core/paint/box_model_object_painter.cc +++ b/chromium/third_party/blink/renderer/core/paint/box_model_object_painter.cc @@ -56,10 +56,14 @@ BoxModelObjectPainter::BoxModelObjectPainter(const LayoutBoxModelObject& box, box_model_(box), flow_box_(flow_box) {} -bool BoxModelObjectPainter:: - IsPaintingBackgroundOfPaintContainerIntoScrollingContentsLayer( - const LayoutBoxModelObject* box_model_, - const PaintInfo& paint_info) { +bool BoxModelObjectPainter::IsPaintingScrollingBackground( + const LayoutBoxModelObject* box_model_, + const PaintInfo& paint_info) { + if (RuntimeEnabledFeatures::SlimmingPaintV2Enabled()) { + // TODO(wangxianzhu): For SPv2, remove this method and let callers use + // PaintInfo::IsPaintScrollingBackground() directly. + return paint_info.IsPaintingScrollingBackground(); + } return paint_info.PaintFlags() & kPaintLayerPaintingOverflowContents && !(paint_info.PaintFlags() & kPaintLayerPaintingCompositingBackgroundPhase) && @@ -99,8 +103,7 @@ LayoutRect BoxModelObjectPainter::AdjustRectForScrolledContent( LayoutRect scrolled_paint_rect = rect; GraphicsContext& context = paint_info.context; if (info.is_clipped_with_local_scrolling && - !IsPaintingBackgroundOfPaintContainerIntoScrollingContentsLayer( - &box_model_, paint_info)) { + !IsPaintingScrollingBackground(&box_model_, paint_info)) { // Clip to the overflow area. const LayoutBox& this_box = ToLayoutBox(box_model_); // TODO(chrishtr): this should be pixel-snapped. diff --git a/chromium/third_party/blink/renderer/core/paint/box_model_object_painter.h b/chromium/third_party/blink/renderer/core/paint/box_model_object_painter.h index c1ff417c07f..73775e86982 100644 --- a/chromium/third_party/blink/renderer/core/paint/box_model_object_painter.h +++ b/chromium/third_party/blink/renderer/core/paint/box_model_object_painter.h @@ -27,9 +27,8 @@ class BoxModelObjectPainter : public BoxPainterBase { BoxModelObjectPainter(const LayoutBoxModelObject&, const InlineFlowBox* = nullptr); - static bool IsPaintingBackgroundOfPaintContainerIntoScrollingContentsLayer( - const LayoutBoxModelObject*, - const PaintInfo&); + static bool IsPaintingScrollingBackground(const LayoutBoxModelObject*, + const PaintInfo&); protected: LayoutRectOutsets ComputeBorders() const override; diff --git a/chromium/third_party/blink/renderer/core/paint/box_paint_invalidator.cc b/chromium/third_party/blink/renderer/core/paint/box_paint_invalidator.cc index 7b903f55bc8..a9618ef19d3 100644 --- a/chromium/third_party/blink/renderer/core/paint/box_paint_invalidator.cc +++ b/chromium/third_party/blink/renderer/core/paint/box_paint_invalidator.cc @@ -15,6 +15,109 @@ namespace blink { +bool BoxPaintInvalidator::HasEffectiveBackground() { + // The view can paint background not from the style. + if (box_.IsLayoutView()) + return true; + return box_.StyleRef().HasBackground() && !box_.BackgroundTransfersToView(); +} + +// |width| is of the positioning area. +static bool ShouldFullyInvalidateFillLayersOnWidthChange( + const FillLayer& layer) { + // Nobody will use multiple layers without wanting fancy positioning. + if (layer.Next()) + return true; + + // The layer properties checked below apply only when there is a valid image. + StyleImage* img = layer.GetImage(); + if (!img || !img->CanRender()) + return false; + + if (layer.RepeatX() != EFillRepeat::kRepeatFill && + layer.RepeatX() != EFillRepeat::kNoRepeatFill) + return true; + + // TODO(alancutter): Make this work correctly for calc lengths. + if (layer.PositionX().IsPercentOrCalc() && !layer.PositionX().IsZero()) + return true; + + if (layer.BackgroundXOrigin() != BackgroundEdgeOrigin::kLeft) + return true; + + EFillSizeType size_type = layer.SizeType(); + + if (size_type == EFillSizeType::kContain || + size_type == EFillSizeType::kCover) + return true; + + if (size_type == EFillSizeType::kSizeLength) { + // TODO(alancutter): Make this work correctly for calc lengths. + if (layer.SizeLength().Width().IsPercentOrCalc() && + !layer.SizeLength().Width().IsZero()) + return true; + if (img->IsGeneratedImage() && layer.SizeLength().Width().IsAuto()) + return true; + } else if (img->UsesImageContainerSize()) { + return true; + } + + return false; +} + +// |height| is of the positioning area. +static bool ShouldFullyInvalidateFillLayersOnHeightChange( + const FillLayer& layer) { + // Nobody will use multiple layers without wanting fancy positioning. + if (layer.Next()) + return true; + + // The layer properties checked below apply only when there is a valid image. + StyleImage* img = layer.GetImage(); + if (!img || !img->CanRender()) + return false; + + if (layer.RepeatY() != EFillRepeat::kRepeatFill && + layer.RepeatY() != EFillRepeat::kNoRepeatFill) + return true; + + // TODO(alancutter): Make this work correctly for calc lengths. + if (layer.PositionY().IsPercentOrCalc() && !layer.PositionY().IsZero()) + return true; + + if (layer.BackgroundYOrigin() != BackgroundEdgeOrigin::kTop) + return true; + + EFillSizeType size_type = layer.SizeType(); + + if (size_type == EFillSizeType::kContain || + size_type == EFillSizeType::kCover) + return true; + + if (size_type == EFillSizeType::kSizeLength) { + // TODO(alancutter): Make this work correctly for calc lengths. + if (layer.SizeLength().Height().IsPercentOrCalc() && + !layer.SizeLength().Height().IsZero()) + return true; + if (img->IsGeneratedImage() && layer.SizeLength().Height().IsAuto()) + return true; + } else if (img->UsesImageContainerSize()) { + return true; + } + + return false; +} + +// old_size and new_size are the old and new sizes of the positioning area. +bool ShouldFullyInvalidateFillLayersOnSizeChange(const FillLayer& layer, + const LayoutSize& old_size, + const LayoutSize& new_size) { + return (old_size.Width() != new_size.Width() && + ShouldFullyInvalidateFillLayersOnWidthChange(layer)) || + (old_size.Height() != new_size.Height() && + ShouldFullyInvalidateFillLayersOnHeightChange(layer)); +} + PaintInvalidationReason BoxPaintInvalidator::ComputePaintInvalidationReason() { PaintInvalidationReason reason = ObjectPaintInvalidatorWithContext(box_, context_) @@ -25,16 +128,11 @@ PaintInvalidationReason BoxPaintInvalidator::ComputePaintInvalidationReason() { const ComputedStyle& style = box_.StyleRef(); - if ((style.BackgroundLayers().ThisOrNextLayersUseContentBox() || - style.MaskLayers().ThisOrNextLayersUseContentBox()) && - box_.PreviousPhysicalContentBoxRect() != box_.PhysicalContentBoxRect()) { + if (style.MaskLayers().AnyLayerUsesContentBox() && + box_.PreviousPhysicalContentBoxRect() != box_.PhysicalContentBoxRect()) return PaintInvalidationReason::kGeometry; - } - LayoutSize old_border_box_size = box_.PreviousSize(); - LayoutSize new_border_box_size = box_.Size(); - bool border_box_changed = old_border_box_size != new_border_box_size; - if (!border_box_changed && + if (box_.PreviousSize() == box_.Size() && context_.old_visual_rect == context_.fragment_data->VisualRect()) return PaintInvalidationReason::kNone; @@ -45,14 +143,13 @@ PaintInvalidationReason BoxPaintInvalidator::ComputePaintInvalidationReason() { // - scale, rotate, skew etc. transforms, // - visual (ink) overflows. if (context_.old_visual_rect != - LayoutRect(context_.old_paint_offset, old_border_box_size) || + LayoutRect(context_.old_paint_offset, box_.PreviousSize()) || context_.fragment_data->VisualRect() != - LayoutRect(context_.fragment_data->PaintOffset(), - new_border_box_size)) { + LayoutRect(context_.fragment_data->PaintOffset(), box_.Size())) { return PaintInvalidationReason::kGeometry; } - DCHECK(border_box_changed); + DCHECK_NE(box_.PreviousSize(), box_.Size()); // Incremental invalidation is not applicable if there is border in the // direction of border box size change because we don't know the border @@ -64,14 +161,7 @@ PaintInvalidationReason BoxPaintInvalidator::ComputePaintInvalidationReason() { style.HasFilterInducingProperty() || style.HasMask() || style.ClipPath()) return PaintInvalidationReason::kGeometry; - if (style.HasBorderRadius()) - return PaintInvalidationReason::kGeometry; - - if (old_border_box_size.Width() != new_border_box_size.Width() && - box_.MustInvalidateBackgroundOrBorderPaintOnWidthChange()) - return PaintInvalidationReason::kGeometry; - if (old_border_box_size.Height() != new_border_box_size.Height() && - box_.MustInvalidateBackgroundOrBorderPaintOnHeightChange()) + if (style.HasBorderRadius() || style.CanRenderBorderImage()) return PaintInvalidationReason::kGeometry; // Needs to repaint frame boundaries. @@ -82,25 +172,24 @@ PaintInvalidationReason BoxPaintInvalidator::ComputePaintInvalidationReason() { if (box_.IsLayoutMultiColumnSet()) return PaintInvalidationReason::kGeometry; + // Background invalidation has been done during InvalidateBackground(), so + // we don't need to check background in this function. + return PaintInvalidationReason::kIncremental; } -bool BoxPaintInvalidator::BackgroundGeometryDependsOnLayoutOverflowRect() - const { - return !box_.IsDocumentElement() && !box_.BackgroundStolenForBeingBody() && - box_.StyleRef() - .BackgroundLayers() - .ThisOrNextLayersHaveLocalAttachment(); +bool BoxPaintInvalidator::BackgroundGeometryDependsOnLayoutOverflowRect() { + return HasEffectiveBackground() && + box_.StyleRef().BackgroundLayers().AnyLayerHasLocalAttachmentImage(); } -// Background positioning in layout overflow rect doesn't mean it will -// paint onto the scrolling contents layer because some conditions prevent -// it from that. We may also treat non-local solid color backgrounds as local -// and paint onto the scrolling contents layer. -// See PaintLayer::canPaintBackgroundOntoScrollingContentsLayer(). bool BoxPaintInvalidator::BackgroundPaintsOntoScrollingContentsLayer() { - if (box_.IsDocumentElement() || box_.BackgroundStolenForBeingBody()) + if (!HasEffectiveBackground()) return false; + if (RuntimeEnabledFeatures::SlimmingPaintV2Enabled()) { + return box_.GetBackgroundPaintLocation() & + kBackgroundPaintInScrollingContents; + } if (!box_.HasLayer()) return false; if (auto* mapping = box_.Layer()->GetCompositedLayerMapping()) @@ -108,48 +197,65 @@ bool BoxPaintInvalidator::BackgroundPaintsOntoScrollingContentsLayer() { return false; } +bool BoxPaintInvalidator::BackgroundPaintsOntoMainGraphicsLayer() { + if (!HasEffectiveBackground()) + return false; + if (RuntimeEnabledFeatures::SlimmingPaintV2Enabled()) + return box_.GetBackgroundPaintLocation() & kBackgroundPaintInGraphicsLayer; + if (!box_.HasLayer()) + return true; + if (auto* mapping = box_.Layer()->GetCompositedLayerMapping()) + return mapping->BackgroundPaintsOntoGraphicsLayer(); + return true; +} + bool BoxPaintInvalidator::ShouldFullyInvalidateBackgroundOnLayoutOverflowChange( const LayoutRect& old_layout_overflow, - const LayoutRect& new_layout_overflow) const { + const LayoutRect& new_layout_overflow) { if (new_layout_overflow == old_layout_overflow) return false; - // TODO(pdr): This check can likely be removed because size changes are - // caught below. - if (new_layout_overflow.IsEmpty() || old_layout_overflow.IsEmpty()) - return true; - - if (new_layout_overflow.Location() != old_layout_overflow.Location()) { - auto& layers = box_.StyleRef().BackgroundLayers(); - // The background should invalidate on most location changes but we can - // avoid invalidation in a common case if the background is a single color - // that fully covers the overflow area. - // TODO(pdr): Check all background layers instead of skipping this if there - // are multiple backgrounds. - if (layers.Next() || layers.GetImage() || - layers.RepeatX() != EFillRepeat::kRepeatFill || - layers.RepeatY() != EFillRepeat::kRepeatFill) - return true; - } + if (!BackgroundGeometryDependsOnLayoutOverflowRect()) + return false; - if (new_layout_overflow.Width() != old_layout_overflow.Width() && - box_.MustInvalidateFillLayersPaintOnWidthChange( - box_.StyleRef().BackgroundLayers())) - return true; - if (new_layout_overflow.Height() != old_layout_overflow.Height() && - box_.MustInvalidateFillLayersPaintOnHeightChange( - box_.StyleRef().BackgroundLayers())) + // The background should invalidate on most location changes. + if (new_layout_overflow.Location() != old_layout_overflow.Location()) return true; - return false; + return ShouldFullyInvalidateFillLayersOnSizeChange( + box_.StyleRef().BackgroundLayers(), old_layout_overflow.Size(), + new_layout_overflow.Size()); } -bool BoxPaintInvalidator::ViewBackgroundShouldFullyInvalidate() const { +BoxPaintInvalidator::BackgroundInvalidationType +BoxPaintInvalidator::ComputeViewBackgroundInvalidation() { DCHECK(box_.IsLayoutView()); - // Fixed attachment background is handled in LayoutView::layout(). - // TODO(wangxianzhu): Combine code for fixed-attachment background. - if (box_.StyleRef().HasEntirelyFixedBackground()) - return false; + + const auto& layout_view = ToLayoutView(box_); + auto new_background_rect = layout_view.BackgroundRect(); + auto old_background_rect = layout_view.PreviousBackgroundRect(); + layout_view.SetPreviousBackgroundRect(new_background_rect); + + // BackgroundRect is the positioning area of all fixed attachment backgrounds, + // including the LayoutView's and descendants'. + bool background_location_changed = + new_background_rect.Location() != old_background_rect.Location(); + bool background_size_changed = + new_background_rect.Size() != old_background_rect.Size(); + if (background_location_changed || background_size_changed) { + for (auto* object : + layout_view.GetFrameView()->BackgroundAttachmentFixedObjects()) { + if (background_location_changed || + ShouldFullyInvalidateFillLayersOnSizeChange( + object->StyleRef().BackgroundLayers(), old_background_rect.Size(), + new_background_rect.Size())) + object->SetBackgroundNeedsFullPaintInvalidation(); + } + } + + if (background_location_changed || + layout_view.BackgroundNeedsFullPaintInvalidation()) + return BackgroundInvalidationType::kFull; // LayoutView's non-fixed-attachment background is positioned in the // document element and needs to invalidate if the size changes. @@ -163,20 +269,47 @@ bool BoxPaintInvalidator::ViewBackgroundShouldFullyInvalidate() const { if (ShouldFullyInvalidateBackgroundOnLayoutOverflowChange( document_background_box->PreviousPhysicalLayoutOverflowRect(), document_background_box->PhysicalLayoutOverflowRect())) { - return true; + return BackgroundInvalidationType::kFull; } } } } - return false; + + return background_size_changed ? BackgroundInvalidationType::kIncremental + : BackgroundInvalidationType::kNone; } BoxPaintInvalidator::BackgroundInvalidationType -BoxPaintInvalidator::ComputeBackgroundInvalidation() { - if (box_.BackgroundChangedSinceLastPaintInvalidation()) +BoxPaintInvalidator::ComputeBackgroundInvalidation( + bool& should_invalidate_all_layers) { + should_invalidate_all_layers = false; + + // If background changed, we may paint the background on different graphics + // layer, so we need to fully invalidate the background on all layers. + if (box_.BackgroundNeedsFullPaintInvalidation()) { + if (box_.HasLayer() && box_.Layer()->GetCompositedLayerMapping() && + box_.Layer()->GetCompositedLayerMapping()->ScrollingContentsLayer()) + should_invalidate_all_layers = true; + + if (RuntimeEnabledFeatures::SlimmingPaintV2Enabled() && + box_.FirstFragment().PaintProperties() && + box_.FirstFragment().PaintProperties()->ScrollTranslation()) + should_invalidate_all_layers = true; + + return BackgroundInvalidationType::kFull; + } + + if (!HasEffectiveBackground()) + return BackgroundInvalidationType::kNone; + + const auto& background_layers = box_.StyleRef().BackgroundLayers(); + if (background_layers.AnyLayerHasDefaultAttachmentImage() && + ShouldFullyInvalidateFillLayersOnSizeChange( + background_layers, box_.PreviousSize(), box_.Size())) return BackgroundInvalidationType::kFull; - if (box_.IsLayoutView() && ViewBackgroundShouldFullyInvalidate()) + if (background_layers.AnyLayerUsesContentBox() && + box_.PreviousPhysicalContentBoxRect() != box_.PhysicalContentBoxRect()) return BackgroundInvalidationType::kFull; bool layout_overflow_change_causes_invalidation = @@ -203,22 +336,30 @@ BoxPaintInvalidator::ComputeBackgroundInvalidation() { } void BoxPaintInvalidator::InvalidateBackground() { - BackgroundInvalidationType background_invalidation_type = - ComputeBackgroundInvalidation(); - - if (BackgroundPaintsOntoScrollingContentsLayer()) { - if (background_invalidation_type != BackgroundInvalidationType::kNone) { - auto reason = - background_invalidation_type == BackgroundInvalidationType::kFull - ? PaintInvalidationReason::kBackgroundOnScrollingContentsLayer - : PaintInvalidationReason::kIncremental; - context_.painting_layer->SetNeedsRepaint(); - ObjectPaintInvalidator(box_).InvalidateDisplayItemClient( - *box_.Layer()->GetCompositedLayerMapping()->ScrollingContentsLayer(), - reason); - } - } else if (background_invalidation_type == - BackgroundInvalidationType::kFull) { + bool should_invalidate_all_layers; + auto background_invalidation_type = + ComputeBackgroundInvalidation(should_invalidate_all_layers); + if (box_.IsLayoutView()) { + background_invalidation_type = std::max( + background_invalidation_type, ComputeViewBackgroundInvalidation()); + } + + if (should_invalidate_all_layers || + (BackgroundPaintsOntoScrollingContentsLayer() && + background_invalidation_type != BackgroundInvalidationType::kNone)) { + auto reason = + background_invalidation_type == BackgroundInvalidationType::kFull + ? PaintInvalidationReason::kBackground + : PaintInvalidationReason::kIncremental; + context_.painting_layer->SetNeedsRepaint(); + ObjectPaintInvalidator(box_).InvalidateDisplayItemClient( + box_.GetScrollableArea()->GetScrollingBackgroundDisplayItemClient(), + reason); + } + + if (should_invalidate_all_layers || + (BackgroundPaintsOntoMainGraphicsLayer() && + background_invalidation_type == BackgroundInvalidationType::kFull)) { box_.GetMutableForPainting() .SetShouldDoFullPaintInvalidationWithoutGeometryChange( PaintInvalidationReason::kBackground); @@ -241,7 +382,7 @@ void BoxPaintInvalidator::InvalidatePaint() { bool BoxPaintInvalidator:: NeedsToSavePreviousContentBoxRectOrLayoutOverflowRect() { // The LayoutView depends on the document element's layout overflow rect (see: - // ViewBackgroundShouldFullyInvalidate) and needs to invalidate before the + // ComputeViewBackgroundInvalidation) and needs to invalidate before the // document element invalidates. There are few document elements so the // previous layout overflow rect is always saved, rather than duplicating the // logic save-if-needed logic for this special case. @@ -265,8 +406,8 @@ bool BoxPaintInvalidator:: // Background and mask layers can depend on other boxes than border box. See // crbug.com/490533 - if ((style.BackgroundLayers().ThisOrNextLayersUseContentBox() || - style.MaskLayers().ThisOrNextLayersUseContentBox()) && + if ((style.BackgroundLayers().AnyLayerUsesContentBox() || + style.MaskLayers().AnyLayerUsesContentBox()) && box_.ContentSize() != box_.Size()) return true; if ((BackgroundGeometryDependsOnLayoutOverflowRect() || diff --git a/chromium/third_party/blink/renderer/core/paint/box_paint_invalidator.h b/chromium/third_party/blink/renderer/core/paint/box_paint_invalidator.h index 9cd71dc515f..2935f46bb40 100644 --- a/chromium/third_party/blink/renderer/core/paint/box_paint_invalidator.h +++ b/chromium/third_party/blink/renderer/core/paint/box_paint_invalidator.h @@ -30,15 +30,18 @@ class CORE_EXPORT BoxPaintInvalidator { private: friend class BoxPaintInvalidatorTest; - bool BackgroundGeometryDependsOnLayoutOverflowRect() const; + bool HasEffectiveBackground(); + bool BackgroundGeometryDependsOnLayoutOverflowRect(); bool BackgroundPaintsOntoScrollingContentsLayer(); + bool BackgroundPaintsOntoMainGraphicsLayer(); bool ShouldFullyInvalidateBackgroundOnLayoutOverflowChange( const LayoutRect& old_layout_overflow, - const LayoutRect& new_layout_overflow) const; - bool ViewBackgroundShouldFullyInvalidate() const; + const LayoutRect& new_layout_overflow); enum BackgroundInvalidationType { kNone = 0, kIncremental, kFull }; - BackgroundInvalidationType ComputeBackgroundInvalidation(); + BackgroundInvalidationType ComputeViewBackgroundInvalidation(); + BackgroundInvalidationType ComputeBackgroundInvalidation( + bool& should_invalidate_all_layers); void InvalidateBackground(); PaintInvalidationReason ComputePaintInvalidationReason(); diff --git a/chromium/third_party/blink/renderer/core/paint/box_paint_invalidator_test.cc b/chromium/third_party/blink/renderer/core/paint/box_paint_invalidator_test.cc index fd7efdadda3..e4296ac8448 100644 --- a/chromium/third_party/blink/renderer/core/paint/box_paint_invalidator_test.cc +++ b/chromium/third_party/blink/renderer/core/paint/box_paint_invalidator_test.cc @@ -44,7 +44,7 @@ class BoxPaintInvalidatorTest : public PaintControllerPaintTest { void ExpectFullPaintInvalidationOnGeometryChange(const char* test_title) { SCOPED_TRACE(test_title); - GetDocument().View()->UpdateAllLifecyclePhases(); + UpdateAllLifecyclePhasesForTest(); auto& target = *GetDocument().getElementById("target"); auto& box = *ToLayoutBox(target.GetLayoutObject()); LayoutRect visual_rect = box.FirstFragment().VisualRect(); @@ -55,8 +55,8 @@ class BoxPaintInvalidatorTest : public PaintControllerPaintTest { ComputePaintInvalidationReason(box, visual_rect, paint_offset)); target.setAttribute( - HTMLNames::styleAttr, - target.getAttribute(HTMLNames::styleAttr) + "; width: 200px"); + html_names::kStyleAttr, + target.getAttribute(html_names::kStyleAttr) + "; width: 200px"); GetDocument().View()->UpdateLifecycleToLayoutClean(); // Simulate that PaintInvalidator updates visual rect. box.GetMutableForPainting().SetVisualRect( @@ -103,8 +103,8 @@ TEST_P(BoxPaintInvalidatorTest, ComputePaintInvalidationReasonPaintingNothing) { auto& target = *GetDocument().getElementById("target"); auto& box = *ToLayoutBox(target.GetLayoutObject()); // Remove border. - target.setAttribute(HTMLNames::classAttr, ""); - GetDocument().View()->UpdateAllLifecyclePhases(); + target.setAttribute(html_names::kClassAttr, ""); + UpdateAllLifecyclePhasesForTest(); EXPECT_TRUE(box.PaintedOutputOfObjectHasNoEffectRegardlessOfSize()); LayoutRect visual_rect = box.FirstFragment().VisualRect(); @@ -121,7 +121,7 @@ TEST_P(BoxPaintInvalidatorTest, ComputePaintInvalidationReasonPaintingNothing) { // Visual rect size change. LayoutRect old_visual_rect = visual_rect; - target.setAttribute(HTMLNames::styleAttr, "width: 200px"); + target.setAttribute(html_names::kStyleAttr, "width: 200px"); GetDocument().View()->UpdateLifecycleToLayoutClean(); // Simulate that PaintInvalidator updates visual rect. box.GetMutableForPainting().SetVisualRect( @@ -137,9 +137,9 @@ TEST_P(BoxPaintInvalidatorTest, ComputePaintInvalidationReasonBasic) { auto& target = *GetDocument().getElementById("target"); auto& box = *ToLayoutBox(target.GetLayoutObject()); // Remove border. - target.setAttribute(HTMLNames::classAttr, ""); - target.setAttribute(HTMLNames::styleAttr, "background: blue"); - GetDocument().View()->UpdateAllLifecyclePhases(); + target.setAttribute(html_names::kClassAttr, ""); + target.setAttribute(html_names::kStyleAttr, "background: blue"); + UpdateAllLifecyclePhasesForTest(); box.SetShouldCheckForPaintInvalidation(); LayoutRect visual_rect = box.FirstFragment().VisualRect(); @@ -152,7 +152,7 @@ TEST_P(BoxPaintInvalidatorTest, ComputePaintInvalidationReasonBasic) { // Visual rect size change. LayoutRect old_visual_rect = visual_rect; - target.setAttribute(HTMLNames::styleAttr, "background: blue; width: 200px"); + target.setAttribute(html_names::kStyleAttr, "background: blue; width: 200px"); GetDocument().View()->UpdateLifecycleToLayoutClean(); // Simulate that PaintInvalidator updates visual rect. box.GetMutableForPainting().SetVisualRect( @@ -189,26 +189,27 @@ TEST_P(BoxPaintInvalidatorTest, ComputePaintInvalidationReasonOtherCases) { ExpectFullPaintInvalidationOnGeometryChange("With border"); // Clear border, set background. - target.setAttribute(HTMLNames::classAttr, "background"); - target.setAttribute(HTMLNames::styleAttr, "border-radius: 5px"); + target.setAttribute(html_names::kClassAttr, "background"); + target.setAttribute(html_names::kStyleAttr, "border-radius: 5px"); ExpectFullPaintInvalidationOnGeometryChange("With border-radius"); - target.setAttribute(HTMLNames::styleAttr, "-webkit-mask: url(#)"); + target.setAttribute(html_names::kStyleAttr, "-webkit-mask: url(#)"); ExpectFullPaintInvalidationOnGeometryChange("With mask"); - target.setAttribute(HTMLNames::styleAttr, "filter: blur(5px)"); + target.setAttribute(html_names::kStyleAttr, "filter: blur(5px)"); ExpectFullPaintInvalidationOnGeometryChange("With filter"); - target.setAttribute(HTMLNames::styleAttr, "outline: 2px solid blue"); + target.setAttribute(html_names::kStyleAttr, "outline: 2px solid blue"); ExpectFullPaintInvalidationOnGeometryChange("With outline"); - target.setAttribute(HTMLNames::styleAttr, "box-shadow: inset 3px 2px"); + target.setAttribute(html_names::kStyleAttr, "box-shadow: inset 3px 2px"); ExpectFullPaintInvalidationOnGeometryChange("With box-shadow"); - target.setAttribute(HTMLNames::styleAttr, "-webkit-appearance: button"); + target.setAttribute(html_names::kStyleAttr, "-webkit-appearance: button"); ExpectFullPaintInvalidationOnGeometryChange("With appearance"); - target.setAttribute(HTMLNames::styleAttr, "clip-path: circle(50% at 0 50%)"); + target.setAttribute(html_names::kStyleAttr, + "clip-path: circle(50% at 0 50%)"); ExpectFullPaintInvalidationOnGeometryChange("With clip-path"); } diff --git a/chromium/third_party/blink/renderer/core/paint/box_painter.cc b/chromium/third_party/blink/renderer/core/paint/box_painter.cc index 8b2dced6687..7c6f1ce6709 100644 --- a/chromium/third_party/blink/renderer/core/paint/box_painter.cc +++ b/chromium/third_party/blink/renderer/core/paint/box_painter.cc @@ -19,15 +19,17 @@ #include "third_party/blink/renderer/core/paint/nine_piece_image_painter.h" #include "third_party/blink/renderer/core/paint/object_painter.h" #include "third_party/blink/renderer/core/paint/paint_info.h" +#include "third_party/blink/renderer/core/paint/paint_layer_scrollable_area.h" #include "third_party/blink/renderer/core/paint/scoped_paint_state.h" #include "third_party/blink/renderer/core/paint/svg_foreign_object_painter.h" #include "third_party/blink/renderer/core/paint/theme_painter.h" #include "third_party/blink/renderer/platform/geometry/layout_point.h" +#include "third_party/blink/renderer/platform/geometry/length_functions.h" #include "third_party/blink/renderer/platform/graphics/graphics_context_state_saver.h" #include "third_party/blink/renderer/platform/graphics/paint/display_item_cache_skipper.h" #include "third_party/blink/renderer/platform/graphics/paint/drawing_recorder.h" +#include "third_party/blink/renderer/platform/graphics/paint/hit_test_display_item.h" #include "third_party/blink/renderer/platform/graphics/paint/scoped_paint_chunk_properties.h" -#include "third_party/blink/renderer/platform/length_functions.h" namespace blink { @@ -53,15 +55,14 @@ void BoxPainter::PaintChildren(const PaintInfo& paint_info) { void BoxPainter::PaintBoxDecorationBackground(const PaintInfo& paint_info, const LayoutPoint& paint_offset) { LayoutRect paint_rect; + const DisplayItemClient* background_client = nullptr; base::Optional<ScopedBoxContentsPaintState> contents_paint_state; - if (BoxModelObjectPainter:: - IsPaintingBackgroundOfPaintContainerIntoScrollingContentsLayer( - &layout_box_, paint_info)) { + if (BoxModelObjectPainter::IsPaintingScrollingBackground(&layout_box_, + paint_info)) { // For the case where we are painting the background into the scrolling // contents layer of a composited scroller we need to include the entire // overflow rect. paint_rect = layout_box_.PhysicalLayoutOverflowRect(); - contents_paint_state.emplace(paint_info, paint_offset, layout_box_); paint_rect.MoveBy(contents_paint_state->PaintOffset()); @@ -69,32 +70,45 @@ void BoxPainter::PaintBoxDecorationBackground(const PaintInfo& paint_info, // paint_rect so we expand the paint_rect by the border size when painting // the background into the scrolling contents layer. paint_rect.Expand(layout_box_.BorderBoxOutsets()); + + background_client = &layout_box_.GetScrollableArea() + ->GetScrollingBackgroundDisplayItemClient(); } else { paint_rect = layout_box_.BorderBoxRect(); paint_rect.MoveBy(paint_offset); + background_client = &layout_box_; + } + + // Paint the background if we're visible and this block has a box decoration + // (background, border, appearance, or box shadow). + const ComputedStyle& style = layout_box_.StyleRef(); + if (style.Visibility() == EVisibility::kVisible && + layout_box_.HasBoxDecorationBackground()) { + PaintBoxDecorationBackgroundWithRect( + contents_paint_state ? contents_paint_state->GetPaintInfo() + : paint_info, + paint_rect, *background_client); } - PaintBoxDecorationBackgroundWithRect( - contents_paint_state ? contents_paint_state->GetPaintInfo() : paint_info, - paint_rect); + if (RuntimeEnabledFeatures::PaintTouchActionRectsEnabled()) + RecordHitTestData(paint_info, paint_rect, *background_client); } bool BoxPainter::BackgroundIsKnownToBeOpaque(const PaintInfo& paint_info) { - LayoutRect bounds = - BoxModelObjectPainter:: - IsPaintingBackgroundOfPaintContainerIntoScrollingContentsLayer( - &layout_box_, paint_info) - ? layout_box_.LayoutOverflowRect() - : layout_box_.SelfVisualOverflowRect(); + LayoutRect bounds = BoxModelObjectPainter::IsPaintingScrollingBackground( + &layout_box_, paint_info) + ? layout_box_.LayoutOverflowRect() + : layout_box_.SelfVisualOverflowRect(); return layout_box_.BackgroundIsKnownToBeOpaqueInRect(bounds); } void BoxPainter::PaintBoxDecorationBackgroundWithRect( const PaintInfo& paint_info, - const LayoutRect& paint_rect) { - bool painting_overflow_contents = BoxModelObjectPainter:: - IsPaintingBackgroundOfPaintContainerIntoScrollingContentsLayer( - &layout_box_, paint_info); + const LayoutRect& paint_rect, + const DisplayItemClient& background_client) { + bool painting_scrolling_background = + BoxModelObjectPainter::IsPaintingScrollingBackground(&layout_box_, + paint_info); const ComputedStyle& style = layout_box_.StyleRef(); base::Optional<DisplayItemCacheSkipper> cache_skipper; @@ -110,18 +124,12 @@ void BoxPainter::PaintBoxDecorationBackgroundWithRect( cache_skipper.emplace(paint_info.context); } - const DisplayItemClient& display_item_client = - painting_overflow_contents ? static_cast<const DisplayItemClient&>( - *layout_box_.Layer() - ->GetCompositedLayerMapping() - ->ScrollingContentsLayer()) - : layout_box_; if (DrawingRecorder::UseCachedDrawingIfPossible( - paint_info.context, display_item_client, + paint_info.context, background_client, DisplayItem::kBoxDecorationBackground)) return; - DrawingRecorder recorder(paint_info.context, display_item_client, + DrawingRecorder recorder(paint_info.context, background_client, DisplayItem::kBoxDecorationBackground); BoxDecorationData box_decoration_data(layout_box_); GraphicsContextStateSaver state_saver(paint_info.context, false); @@ -131,12 +139,17 @@ void BoxPainter::PaintBoxDecorationBackgroundWithRect( BackgroundIsKnownToBeOpaque(paint_info)) recorder.SetKnownToBeOpaque(); + const auto skip_background = layout_box_.BackgroundTransfersToView() || + (paint_info.SkipRootBackground() && + paint_info.PaintContainer() == &layout_box_); + bool needs_end_layer = false; - if (!painting_overflow_contents) { + if (!painting_scrolling_background) { // FIXME: Should eventually give the theme control over whether the box // shadow should paint, since controls could have custom shadows of their // own. - BoxPainterBase::PaintNormalBoxShadow(paint_info, paint_rect, style); + BoxPainterBase::PaintNormalBoxShadow(paint_info, paint_rect, style, true, + true, skip_background); if (BleedAvoidanceIsClipping(box_decoration_data.bleed_avoidance)) { state_saver.Save(); @@ -158,10 +171,7 @@ void BoxPainter::PaintBoxDecorationBackgroundWithRect( bool theme_painted = box_decoration_data.has_appearance && !theme_painter.Paint(layout_box_, paint_info, snapped_paint_rect); - bool should_paint_background = - !theme_painted && (!paint_info.SkipRootBackground() || - paint_info.PaintContainer() != &layout_box_); - if (should_paint_background) { + if (!theme_painted && !skip_background) { PaintBackground(paint_info, paint_rect, box_decoration_data.background_color, box_decoration_data.bleed_avoidance); @@ -173,7 +183,7 @@ void BoxPainter::PaintBoxDecorationBackgroundWithRect( } } - if (!painting_overflow_contents) { + if (!painting_scrolling_background) { BoxPainterBase::PaintInsetBoxShadowWithBorderRect(paint_info, paint_rect, style); @@ -200,9 +210,7 @@ void BoxPainter::PaintBackground(const PaintInfo& paint_info, const LayoutRect& paint_rect, const Color& background_color, BackgroundBleedAvoidance bleed_avoidance) { - if (layout_box_.IsDocumentElement()) - return; - if (layout_box_.BackgroundStolenForBeingBody()) + if (layout_box_.BackgroundTransfersToView()) return; if (layout_box_.BackgroundIsKnownToBeObscured()) return; @@ -245,4 +253,24 @@ void BoxPainter::PaintMaskImages(const PaintInfo& paint_info, include_logical_right_edge); } +void BoxPainter::RecordHitTestData(const PaintInfo& paint_info, + const LayoutRect& paint_rect, + const DisplayItemClient& background_client) { + // Hit test display items are only needed for compositing. This flag is used + // for for printing and drag images which do not need hit testing. + if (paint_info.GetGlobalPaintFlags() & kGlobalPaintFlattenCompositingLayers) + return; + + // If an object is not visible, it does not participate in hit testing. + if (layout_box_.StyleRef().Visibility() != EVisibility::kVisible) + return; + + auto touch_action = layout_box_.EffectiveWhitelistedTouchAction(); + if (touch_action == TouchAction::kTouchActionAuto) + return; + + HitTestDisplayItem::Record(paint_info.context, background_client, + HitTestRect(paint_rect, touch_action)); +} + } // namespace blink diff --git a/chromium/third_party/blink/renderer/core/paint/box_painter.h b/chromium/third_party/blink/renderer/core/paint/box_painter.h index 4045538d6f6..89f67ee8793 100644 --- a/chromium/third_party/blink/renderer/core/paint/box_painter.h +++ b/chromium/third_party/blink/renderer/core/paint/box_painter.h @@ -15,6 +15,7 @@ namespace blink { struct PaintInfo; class Color; +class DisplayItemClient; class LayoutBox; class LayoutPoint; class LayoutRect; @@ -32,8 +33,18 @@ class BoxPainter { void PaintMask(const PaintInfo&, const LayoutPoint& paint_offset); void PaintMaskImages(const PaintInfo&, const LayoutRect&); - void PaintBoxDecorationBackgroundWithRect(const PaintInfo&, - const LayoutRect&); + void PaintBoxDecorationBackgroundWithRect( + const PaintInfo&, + const LayoutRect&, + const DisplayItemClient& background_client); + + // Paint a hit test display item and record hit test data. This should be + // called in the background paint phase even if there is no other painted + // content. + void RecordHitTestData(const PaintInfo&, + const LayoutRect& paint_rect, + const DisplayItemClient& background_client); + private: bool BackgroundIsKnownToBeOpaque(const PaintInfo&); void PaintBackground(const PaintInfo&, diff --git a/chromium/third_party/blink/renderer/core/paint/box_painter_base.cc b/chromium/third_party/blink/renderer/core/paint/box_painter_base.cc index 71efbe56c55..e0a47c38afa 100644 --- a/chromium/third_party/blink/renderer/core/paint/box_painter_base.cc +++ b/chromium/third_party/blink/renderer/core/paint/box_painter_base.cc @@ -54,7 +54,8 @@ void BoxPainterBase::PaintNormalBoxShadow(const PaintInfo& info, const LayoutRect& paint_rect, const ComputedStyle& style, bool include_logical_left_edge, - bool include_logical_right_edge) { + bool include_logical_right_edge, + bool background_is_skipped) { if (!style.BoxShadow()) return; GraphicsContext& context = info.context; @@ -64,8 +65,9 @@ void BoxPainterBase::PaintNormalBoxShadow(const PaintInfo& info, bool has_border_radius = style.HasBorderRadius(); bool has_opaque_background = + !background_is_skipped && style.VisitedDependentColor(GetCSSPropertyBackgroundColor()).Alpha() == - 255; + 255; GraphicsContextStateSaver state_saver(context, false); @@ -425,15 +427,19 @@ inline bool PaintFastBottomLayer(Node* node, // so snap to integers. This is particuarly important for sprite maps. // Calculation up to this point, in LayoutUnits, can lead to small variations // from integer size, so it is safe to round without introducing major issues. - const FloatRect src_rect = - FloatRect(RoundedIntRect(Image::ComputeSubsetForBackground( - image_tile, dest_rect_for_subset, intrinsic_tile_size))); + const FloatRect unrounded_subset = Image::ComputeSubsetForBackground( + image_tile, dest_rect_for_subset, intrinsic_tile_size); + FloatRect src_rect = FloatRect(RoundedIntRect(unrounded_subset)); + + // If we have rounded the image size to 0, revert the rounding. + if (src_rect.IsEmpty()) + src_rect = unrounded_subset; TRACE_EVENT1(TRACE_DISABLED_BY_DEFAULT("devtools.timeline"), "PaintImage", "data", - InspectorPaintImageEvent::Data(node, *info.image, - FloatRect(image->Rect()), - FloatRect(image_border.Rect()))); + inspector_paint_image_event::Data( + node, *info.image, FloatRect(image->Rect()), + FloatRect(image_border.Rect()))); // Since there is no way for the developer to specify decode behavior, use // kSync by default. @@ -548,7 +554,7 @@ void PaintFillLayerBackground(GraphicsContext& context, image) { TRACE_EVENT1(TRACE_DISABLED_BY_DEFAULT("devtools.timeline"), "PaintImage", "data", - InspectorPaintImageEvent::Data( + inspector_paint_image_event::Data( node, *info.image, FloatRect(image->Rect()), FloatRect(scrolled_paint_rect))); context.DrawTiledImage(image, diff --git a/chromium/third_party/blink/renderer/core/paint/box_painter_base.h b/chromium/third_party/blink/renderer/core/paint/box_painter_base.h index f0cbcbda06f..fb43c43caae 100644 --- a/chromium/third_party/blink/renderer/core/paint/box_painter_base.h +++ b/chromium/third_party/blink/renderer/core/paint/box_painter_base.h @@ -67,7 +67,8 @@ class BoxPainterBase { const LayoutRect&, const ComputedStyle&, bool include_logical_left_edge = true, - bool include_logical_right_edge = true); + bool include_logical_right_edge = true, + bool background_is_skipped = true); static void PaintInsetBoxShadowWithBorderRect( const PaintInfo&, diff --git a/chromium/third_party/blink/renderer/core/paint/box_reflection_utils.cc b/chromium/third_party/blink/renderer/core/paint/box_reflection_utils.cc index 218e92a8914..520c388b6de 100644 --- a/chromium/third_party/blink/renderer/core/paint/box_reflection_utils.cc +++ b/chromium/third_party/blink/renderer/core/paint/box_reflection_utils.cc @@ -10,10 +10,10 @@ #include "third_party/blink/renderer/platform/geometry/float_rect.h" #include "third_party/blink/renderer/platform/geometry/layout_point.h" #include "third_party/blink/renderer/platform/geometry/layout_rect.h" +#include "third_party/blink/renderer/platform/geometry/length_functions.h" #include "third_party/blink/renderer/platform/graphics/box_reflection.h" #include "third_party/blink/renderer/platform/graphics/paint/drawing_recorder.h" #include "third_party/blink/renderer/platform/graphics/paint/paint_record_builder.h" -#include "third_party/blink/renderer/platform/length_functions.h" namespace blink { diff --git a/chromium/third_party/blink/renderer/core/paint/compositing/composited_layer_mapping.cc b/chromium/third_party/blink/renderer/core/paint/compositing/composited_layer_mapping.cc index 35a6efa96c6..1806c4d07b5 100644 --- a/chromium/third_party/blink/renderer/core/paint/compositing/composited_layer_mapping.cc +++ b/chromium/third_party/blink/renderer/core/paint/compositing/composited_layer_mapping.cc @@ -68,13 +68,13 @@ #include "third_party/blink/renderer/core/paint/scrollable_area_painter.h" #include "third_party/blink/renderer/core/probe/core_probes.h" #include "third_party/blink/renderer/platform/fonts/font_cache.h" +#include "third_party/blink/renderer/platform/geometry/length_functions.h" #include "third_party/blink/renderer/platform/graphics/bitmap_image.h" #include "third_party/blink/renderer/platform/graphics/compositor_filter_operations.h" #include "third_party/blink/renderer/platform/graphics/graphics_context.h" #include "third_party/blink/renderer/platform/graphics/paint/cull_rect.h" #include "third_party/blink/renderer/platform/graphics/paint/drawing_recorder.h" #include "third_party/blink/renderer/platform/graphics/paint/paint_controller.h" -#include "third_party/blink/renderer/platform/length_functions.h" #include "third_party/blink/renderer/platform/runtime_enabled_features.h" #include "third_party/blink/renderer/platform/transforms/transform_state.h" #include "third_party/blink/renderer/platform/wtf/text/string_builder.h" @@ -82,30 +82,20 @@ namespace blink { -using namespace HTMLNames; - -static IntRect ContentsRect(const LayoutObject& layout_object) { +static LayoutRect ContentsRect(const LayoutObject& layout_object) { if (!layout_object.IsBox()) - return IntRect(); - if (layout_object.IsCanvas()) { - return PixelSnappedIntRect( - ToLayoutHTMLCanvas(layout_object).ReplacedContentRect()); - } - if (layout_object.IsVideo()) { - return PixelSnappedIntRect( - ToLayoutVideo(layout_object).ReplacedContentRect()); - } - - return PixelSnappedIntRect( - ToLayoutBox(layout_object).PhysicalContentBoxRect()); + return LayoutRect(); + if (layout_object.IsLayoutReplaced()) + return ToLayoutReplaced(layout_object).ReplacedContentRect(); + return ToLayoutBox(layout_object).PhysicalContentBoxRect(); } -static IntRect BackgroundRect(const LayoutObject& layout_object) { +static LayoutRect BackgroundRect(const LayoutObject& layout_object) { if (!layout_object.IsBox()) - return IntRect(); + return LayoutRect(); const LayoutBox& box = ToLayoutBox(layout_object); - return PixelSnappedIntRect(box.PhysicalBackgroundRect(kBackgroundClipRect)); + return box.PhysicalBackgroundRect(kBackgroundClipRect); } static inline bool IsTextureLayerCanvas(const LayoutObject& layout_object) { @@ -198,7 +188,6 @@ static FloatPoint StickyPositionOffsetForLayer(PaintLayer& layer) { CompositedLayerMapping::CompositedLayerMapping(PaintLayer& layer) : owning_layer_(layer), - content_offset_in_compositing_layer_dirty_(false), pending_update_scope_(kGraphicsLayerUpdateNone), is_main_frame_layout_view_layer_(false), scrolling_contents_are_empty_(false), @@ -322,12 +311,6 @@ void CompositedLayerMapping::UpdateFilters() { CompositorFilterOperations operations; OwningLayer().UpdateCompositorFilterOperationsForFilter(operations); - // If the image violates some feature policy optimized image policies, render - // with inverted color. - if (GetLayoutObject().IsLayoutImage() && - ToLayoutImage(GetLayoutObject()).ShouldInvertColor()) - operations.AppendInvertFilter(1.0f); - graphics_layer_->SetFilters(std::move(operations)); } @@ -418,7 +401,7 @@ void CompositedLayerMapping::UpdateBackgroundPaintsOntoScrollingContentsLayer( // it would be visually correct and we are using composited scrolling meaning // we have a scrolling contents layer to paint it into. BackgroundPaintLocation paint_location = - owning_layer_.GetBackgroundPaintLocation(); + GetLayoutObject().GetBackgroundPaintLocation(); bool should_paint_onto_scrolling_contents_layer = paint_location & kBackgroundPaintInScrollingContents && owning_layer_.GetScrollableArea()->UsesCompositedScrolling(); @@ -444,6 +427,10 @@ void CompositedLayerMapping::UpdateBackgroundPaintsOntoScrollingContentsLayer( } void CompositedLayerMapping::UpdateContentsOpaque() { + // If there is a foreground layer, children paint into that layer and + // not graphics_layer_, and so don't contribute to the opaqueness of the + // latter. + bool should_check_children = !foreground_layer_.get(); if (IsTextureLayerCanvas(GetLayoutObject())) { CanvasRenderingContext* context = ToHTMLCanvasElement(GetLayoutObject().GetNode())->RenderingContext(); @@ -480,13 +467,14 @@ void CompositedLayerMapping::UpdateContentsOpaque() { // this for solid color backgrounds the answer will be the same. scrolling_contents_layer_->SetContentsOpaque( owning_layer_.BackgroundIsKnownToBeOpaqueInRect( - ToLayoutBox(GetLayoutObject()).PhysicalPaddingBoxRect())); + ToLayoutBox(GetLayoutObject()).PhysicalPaddingBoxRect(), + should_check_children)); - if (owning_layer_.GetBackgroundPaintLocation() & + if (GetLayoutObject().GetBackgroundPaintLocation() & kBackgroundPaintInGraphicsLayer) { graphics_layer_->SetContentsOpaque( owning_layer_.BackgroundIsKnownToBeOpaqueInRect( - CompositedBounds())); + CompositedBounds(), should_check_children)); } else { // If we only paint the background onto the scrolling contents layer we // are going to leave a hole in the m_graphicsLayer where the background @@ -497,7 +485,8 @@ void CompositedLayerMapping::UpdateContentsOpaque() { if (HasScrollingLayer()) scrolling_contents_layer_->SetContentsOpaque(false); graphics_layer_->SetContentsOpaque( - owning_layer_.BackgroundIsKnownToBeOpaqueInRect(CompositedBounds())); + owning_layer_.BackgroundIsKnownToBeOpaqueInRect( + CompositedBounds(), should_check_children)); } } } @@ -506,10 +495,10 @@ void CompositedLayerMapping::UpdateRasterizationPolicy() { bool transformed_rasterization_allowed = !(owning_layer_.GetCompositingReasons() & CompositingReason::kComboAllDirectReasons); - graphics_layer_->ContentLayer()->SetTransformedRasterizationAllowed( + graphics_layer_->CcLayer()->SetTransformedRasterizationAllowed( transformed_rasterization_allowed); if (squashing_layer_) - squashing_layer_->ContentLayer()->SetTransformedRasterizationAllowed(true); + squashing_layer_->CcLayer()->SetTransformedRasterizationAllowed(true); } void CompositedLayerMapping::UpdateCompositedBounds() { @@ -518,7 +507,6 @@ void CompositedLayerMapping::UpdateCompositedBounds() { // FIXME: if this is really needed for performance, it would be better to // store it on Layer. composited_bounds_ = owning_layer_.BoundingBoxForCompositing(); - content_offset_in_compositing_layer_dirty_ = true; } GraphicsLayer* CompositedLayerMapping::FrameContentsGraphicsLayer() const { @@ -565,8 +553,8 @@ void CompositedLayerMapping::UpdateAfterPartResize() { child_containment_layer_ ? FloatPoint(child_containment_layer_->GetPosition()) : FloatPoint(); - document_layer->SetPosition(FloatPoint(FlooredIntPoint( - FloatPoint(ContentsBox().Location()) - parent_position))); + document_layer->SetPosition(FloatPoint(RoundedIntSize( + ContentsBox().Location() - LayoutPoint(parent_position)))); } } } @@ -1132,7 +1120,7 @@ void CompositedLayerMapping::UpdateSquashingLayerGeometry( .InvalidatePaintIncludingNonCompositingDescendants(); TRACE_LAYER_INVALIDATION(layers[i].paint_layer, - InspectorLayerInvalidationTrackingEvent:: + inspector_layer_invalidation_tracking_event:: kSquashingLayerGeometryWasUpdated); layers_needing_paint_invalidation.push_back(layers[i].paint_layer); } @@ -1218,8 +1206,6 @@ void CompositedLayerMapping::UpdateGraphicsLayerGeometry( UpdateOverflowControlsHostLayerGeometry(compositing_stacking_context, compositing_container, graphics_layer_parent_location); - UpdateContentsOffsetInCompositingLayer( - snapped_offset_from_composited_ancestor, graphics_layer_parent_location); UpdateStickyConstraints(GetLayoutObject().StyleRef()); UpdateSquashingLayerGeometry( graphics_layer_parent_location, compositing_container, @@ -1606,16 +1592,13 @@ void CompositedLayerMapping::UpdateChildContainmentLayerGeometry() { void CompositedLayerMapping::UpdateChildTransformLayerGeometry() { if (!child_transform_layer_) return; - const IntRect border_box = - ToLayoutBox(owning_layer_.GetLayoutObject()) - .PixelSnappedBorderBoxRect(SubpixelAccumulation()); - child_transform_layer_->SetSize(gfx::Size(border_box.Size())); - child_transform_layer_->SetOffsetFromLayoutObject( - ToIntSize(border_box.Location())); - IntPoint parent_location( - child_transform_layer_->Parent()->OffsetFromLayoutObject()); - child_transform_layer_->SetPosition( - FloatPoint(border_box.Location() - parent_location)); + + LayoutRect border_box = + ToLayoutBox(owning_layer_.GetLayoutObject()).BorderBoxRect(); + border_box.Move(ContentOffsetInCompositingLayer()); + child_transform_layer_->SetSize(gfx::Size(border_box.PixelSnappedSize())); + child_transform_layer_->SetOffsetFromLayoutObject(IntSize()); + child_transform_layer_->SetPosition(FloatPoint(border_box.Location())); } void CompositedLayerMapping::UpdateMaskLayerGeometry() { @@ -1913,59 +1896,6 @@ void CompositedLayerMapping::UpdateContentsRect() { graphics_layer_->SetContentsRect(PixelSnappedIntRect(ContentsBox())); } -void CompositedLayerMapping::UpdateContentsOffsetInCompositingLayer( - const IntPoint& snapped_offset_from_composited_ancestor, - const IntPoint& graphics_layer_parent_location) { - // m_graphicsLayer is positioned relative to our compositing ancestor - // PaintLayer, but it's not positioned at the origin of m_owningLayer, it's - // offset by m_contentBounds.location(). This is what - // contentOffsetInCompositingLayer is meant to capture, roughly speaking - // (ignoring rounding and subpixel accumulation). - // - // Our ancestor graphics layers in this CLM (m_graphicsLayer and potentially - // m_ancestorClippingLayer) have pixel snapped, so if we don't adjust this - // offset, we'll see accumulated rounding errors due to that snapping. - // - // In order to ensure that we account for this rounding, we compute - // contentsOffsetInCompositingLayer in a somewhat roundabout way. - // - // our position = (desired position) - (inherited graphics layer offset). - // - // Precisely, - // Offset = snappedOffsetFromCompositedAncestor - - // offsetDueToAncestorGraphicsLayers (See code below) - // = snappedOffsetFromCompositedAncestor - - // (m_graphicsLayer->position() + graphicsLayerParentLocation) - // = snappedOffsetFromCompositedAncestor - - // (relativeCompositingBounds.location() - - // graphicsLayerParentLocation + - // graphicsLayerParentLocation) - // (See updateMainGraphicsLayerGeometry) - // = snappedOffsetFromCompositedAncestor - - // relativeCompositingBounds.location() - // = snappedOffsetFromCompositedAncestor - - // (pixelSnappedIntRect(contentBounds.location()) + - // snappedOffsetFromCompositedAncestor) - // (See computeBoundsOfOwningLayer) - // = -pixelSnappedIntRect(contentBounds.location()) - // - // As you can see, we've ended up at the same spot - // (-contentBounds.location()), but by subtracting off our ancestor graphics - // layers positions, we can be sure we've accounted correctly for any pixel - // snapping due to ancestor graphics layers. - // - // And drawing of composited children takes into account the subpixel - // accumulation of this CLM already (through its own - // graphicsLayerParentLocation it appears). - FloatPoint offset_due_to_ancestor_graphics_layers = - FloatPoint(graphics_layer_->GetPosition()) + - graphics_layer_parent_location; - content_offset_in_compositing_layer_ = - LayoutSize(snapped_offset_from_composited_ancestor - - offset_due_to_ancestor_graphics_layers); - content_offset_in_compositing_layer_dirty_ = false; -} - void CompositedLayerMapping::UpdateDrawsContent() { bool in_overlay_fullscreen_video = false; if (GetLayoutObject().IsVideo()) { @@ -2958,12 +2888,12 @@ FloatPoint3D CompositedLayerMapping::ComputeTransformOrigin( // Return the offset from the top-left of this compositing layer at which the // LayoutObject's contents are painted. LayoutSize CompositedLayerMapping::ContentOffsetInCompositingLayer() const { - DCHECK(!content_offset_in_compositing_layer_dirty_); - return content_offset_in_compositing_layer_; + return owning_layer_.SubpixelAccumulation() - + LayoutSize(graphics_layer_->OffsetFromLayoutObject()); } LayoutRect CompositedLayerMapping::ContentsBox() const { - LayoutRect contents_box = LayoutRect(ContentsRect(GetLayoutObject())); + LayoutRect contents_box = ContentsRect(GetLayoutObject()); contents_box.Move(ContentOffsetInCompositingLayer()); return contents_box; } @@ -3183,7 +3113,7 @@ void CompositedLayerMapping::DoPaintTask( kPaintsIntoGroupedBacking) { // FIXME: GraphicsLayers need a way to split for multicol. PaintLayerPaintingInfo painting_info( - paint_info.paint_layer, LayoutRect(dirty_rect), kGlobalPaintNormalPhase, + paint_info.paint_layer, CullRect(dirty_rect), kGlobalPaintNormalPhase, paint_info.paint_layer->SubpixelAccumulation()); PaintLayerPainter(*paint_info.paint_layer) .PaintLayerContents(context, painting_info, paint_layer_flags); @@ -3196,7 +3126,7 @@ void CompositedLayerMapping::DoPaintTask( } } else { PaintLayerPaintingInfo painting_info( - paint_info.paint_layer, LayoutRect(dirty_rect), kGlobalPaintNormalPhase, + paint_info.paint_layer, CullRect(dirty_rect), kGlobalPaintNormalPhase, paint_info.paint_layer->SubpixelAccumulation()); PaintLayerPainter(*paint_info.paint_layer) .Paint(context, painting_info, paint_layer_flags); @@ -3443,8 +3373,8 @@ void CompositedLayerMapping::PaintContents( TRACE_EVENT1( "devtools.timeline,rail", "Paint", "data", - InspectorPaintEvent::Data(&owning_layer_.GetLayoutObject(), - LayoutRect(interest_rect), graphics_layer)); + inspector_paint_event::Data(&owning_layer_.GetLayoutObject(), + LayoutRect(interest_rect), graphics_layer)); PaintLayerFlags paint_layer_flags = 0; if (graphics_layer_painting_phase & kGraphicsLayerPaintBackground) @@ -3514,8 +3444,8 @@ void CompositedLayerMapping::PaintScrollableArea( const IntRect& interest_rect) const { // cull_rect is in the space of the containing scrollable area in which // Scrollbar::Paint() will paint the scrollbar. - CullRect cull_rect(CullRect(interest_rect), - graphics_layer->OffsetFromLayoutObject()); + CullRect cull_rect(interest_rect); + cull_rect.Move(graphics_layer->OffsetFromLayoutObject()); PaintLayerScrollableArea* scrollable_area = owning_layer_.GetScrollableArea(); if (graphics_layer == LayerForHorizontalScrollbar()) { if (const Scrollbar* scrollbar = scrollable_area->HorizontalScrollbar()) diff --git a/chromium/third_party/blink/renderer/core/paint/compositing/composited_layer_mapping.h b/chromium/third_party/blink/renderer/core/paint/compositing/composited_layer_mapping.h index 36c76840e91..c83e8cc41ff 100644 --- a/chromium/third_party/blink/renderer/core/paint/compositing/composited_layer_mapping.h +++ b/chromium/third_party/blink/renderer/core/paint/compositing/composited_layer_mapping.h @@ -300,11 +300,18 @@ class CORE_EXPORT CompositedLayerMapping final : public GraphicsLayerClient { // the background can scroll with the content). When the background is also // opaque this allows us to composite the scroller even on low DPI as we can // draw with subpixel anti-aliasing. - bool BackgroundPaintsOntoScrollingContentsLayer() { + bool BackgroundPaintsOntoScrollingContentsLayer() const { return background_paints_onto_scrolling_contents_layer_; } - bool DrawsBackgroundOntoContentLayer() { + // Returns true if the background paints onto the main graphics layer. + // In some situations, we may paint background on both the main graphics layer + // and the scrolling contents layer. + bool BackgroundPaintsOntoGraphicsLayer() const { + return background_paints_onto_graphics_layer_; + } + + bool DrawsBackgroundOntoContentLayer() const { return draws_background_onto_content_layer_; } @@ -455,9 +462,6 @@ class CORE_EXPORT CompositedLayerMapping final : public GraphicsLayerClient { Color LayoutObjectBackgroundColor() const; void UpdateBackgroundColor(); void UpdateContentsRect(); - void UpdateContentsOffsetInCompositingLayer( - const IntPoint& snapped_offset_from_composited_ancestor, - const IntPoint& graphics_layer_parent_location); void UpdateAfterPartResize(); void UpdateCompositingReasons(); @@ -666,8 +670,6 @@ class CORE_EXPORT CompositedLayerMapping final : public GraphicsLayerClient { LayoutRect composited_bounds_; - LayoutSize content_offset_in_compositing_layer_; - // We keep track of the scrolling contents offset, so that when it changes we // can notify the ScrollingCoordinator, which passes on main-thread scrolling // updates to the compositor. @@ -675,8 +677,6 @@ class CORE_EXPORT CompositedLayerMapping final : public GraphicsLayerClient { const PaintLayer* clip_inheritance_ancestor_; - unsigned content_offset_in_compositing_layer_dirty_ : 1; - unsigned pending_update_scope_ : 2; unsigned is_main_frame_layout_view_layer_ : 1; diff --git a/chromium/third_party/blink/renderer/core/paint/compositing/composited_layer_mapping_test.cc b/chromium/third_party/blink/renderer/core/paint/compositing/composited_layer_mapping_test.cc index 5947a838062..7d2b078394a 100644 --- a/chromium/third_party/blink/renderer/core/paint/compositing/composited_layer_mapping_test.cc +++ b/chromium/third_party/blink/renderer/core/paint/compositing/composited_layer_mapping_test.cc @@ -55,8 +55,17 @@ class CompositedLayerMappingTest : public RenderingTest { RenderingTest::SetUp(); EnableCompositing(); } +}; - void TearDown() override { RenderingTest::TearDown(); } +// Tests the pre-BlinkGenPropertyTrees composited layer mapping code. With BGPT, +// some layer updates are skipped (see: CLM::UpdateGraphicsLayerConfiguration +// and CLM::UpdateStickyConstraints). +class CompositedLayerMappingTestWithoutBGPT + : private ScopedBlinkGenPropertyTreesForTest, + public CompositedLayerMappingTest { + public: + CompositedLayerMappingTestWithoutBGPT() + : ScopedBlinkGenPropertyTreesForTest(false) {} }; TEST_F(CompositedLayerMappingTest, SubpixelAccumulationChange) { @@ -132,7 +141,7 @@ TEST_F(CompositedLayerMappingTest, SimpleInterestRect) { "<div id='target' style='width: 200px; height: 200px; will-change: " "transform'></div>"); - GetDocument().View()->UpdateAllLifecyclePhases(); + UpdateAllLifecyclePhasesForTest(); Element* element = GetDocument().getElementById("target"); PaintLayer* paint_layer = ToLayoutBoxModelObject(element->GetLayoutObject())->Layer(); @@ -147,7 +156,7 @@ TEST_F(CompositedLayerMappingTest, TallLayerInterestRect) { "<div id='target' style='width: 200px; height: 10000px; will-change: " "transform'></div>"); - GetDocument().View()->UpdateAllLifecyclePhases(); + UpdateAllLifecyclePhasesForTest(); Element* element = GetDocument().getElementById("target"); PaintLayer* paint_layer = ToLayoutBoxModelObject(element->GetLayoutObject())->Layer(); @@ -166,10 +175,10 @@ TEST_F(CompositedLayerMappingTest, TallCompositedScrolledLayerInterestRect) { </div> )HTML"); - GetDocument().View()->UpdateAllLifecyclePhases(); + UpdateAllLifecyclePhasesForTest(); GetDocument().View()->LayoutViewport()->SetScrollOffset(ScrollOffset(0, 8000), kProgrammaticScroll); - GetDocument().View()->UpdateAllLifecyclePhases(); + UpdateAllLifecyclePhasesForTest(); Element* element = GetDocument().getElementById("target"); PaintLayer* paint_layer = @@ -184,10 +193,10 @@ TEST_F(CompositedLayerMappingTest, TallNonCompositedScrolledLayerInterestRect) { <div style='width: 200px; height: 11000px;'></div> )HTML"); - GetDocument().View()->UpdateAllLifecyclePhases(); + UpdateAllLifecyclePhasesForTest(); GetDocument().View()->LayoutViewport()->SetScrollOffset(ScrollOffset(0, 8000), kProgrammaticScroll); - GetDocument().View()->UpdateAllLifecyclePhases(); + UpdateAllLifecyclePhasesForTest(); PaintLayer* paint_layer = GetDocument().GetLayoutView()->Layer(); ASSERT_TRUE(paint_layer->GraphicsLayerBacking()); @@ -202,7 +211,7 @@ TEST_F(CompositedLayerMappingTest, TallLayerWholeDocumentInterestRect) { GetDocument().GetSettings()->SetMainFrameClipsContent(false); - GetDocument().View()->UpdateAllLifecyclePhases(); + UpdateAllLifecyclePhasesForTest(); Element* element = GetDocument().getElementById("target"); PaintLayer* paint_layer = ToLayoutBoxModelObject(element->GetLayoutObject())->Layer(); @@ -223,10 +232,10 @@ TEST_F(CompositedLayerMappingTest, VerticalRightLeftWritingModeDocument) { 200px;'></div> )HTML"); - GetDocument().View()->UpdateAllLifecyclePhases(); + UpdateAllLifecyclePhasesForTest(); GetDocument().View()->LayoutViewport()->SetScrollOffset( ScrollOffset(-5000, 0), kProgrammaticScroll); - GetDocument().View()->UpdateAllLifecyclePhases(); + UpdateAllLifecyclePhasesForTest(); PaintLayer* paint_layer = GetDocument().GetLayoutView()->Layer(); ASSERT_TRUE(paint_layer->GraphicsLayerBacking()); @@ -243,7 +252,7 @@ TEST_F(CompositedLayerMappingTest, RotatedInterestRect) { "<div id='target' style='width: 200px; height: 200px; will-change: " "transform; transform: rotateZ(45deg)'></div>"); - GetDocument().View()->UpdateAllLifecyclePhases(); + UpdateAllLifecyclePhasesForTest(); Element* element = GetDocument().getElementById("target"); PaintLayer* paint_layer = ToLayoutBoxModelObject(element->GetLayoutObject())->Layer(); @@ -257,7 +266,7 @@ TEST_F(CompositedLayerMappingTest, RotatedInterestRectNear90Degrees) { "<div id='target' style='width: 10000px; height: 200px; will-change: " "transform; transform: rotateY(89.9999deg)'></div>"); - GetDocument().View()->UpdateAllLifecyclePhases(); + UpdateAllLifecyclePhasesForTest(); Element* element = GetDocument().getElementById("target"); PaintLayer* paint_layer = ToLayoutBoxModelObject(element->GetLayoutObject())->Layer(); @@ -304,7 +313,7 @@ TEST_F(CompositedLayerMappingTest, LargeScaleInterestRect) { </div> )HTML"); - GetDocument().View()->UpdateAllLifecyclePhases(); + UpdateAllLifecyclePhasesForTest(); Element* element = GetDocument().getElementById("target"); PaintLayer* paint_layer = ToLayoutBoxModelObject(element->GetLayoutObject())->Layer(); @@ -321,7 +330,7 @@ TEST_F(CompositedLayerMappingTest, PerspectiveInterestRect) { </div> )HTML"); - GetDocument().View()->UpdateAllLifecyclePhases(); + UpdateAllLifecyclePhasesForTest(); Element* element = GetDocument().getElementById("target"); PaintLayer* paint_layer = ToLayoutBoxModelObject(element->GetLayoutObject())->Layer(); @@ -338,7 +347,7 @@ TEST_F(CompositedLayerMappingTest, 3D90DegRotatedTallInterestRect) { "<div id='target' style='width: 200px; height: 10000px; will-change: " "transform; transform: rotateY(90deg)'></div>"); - GetDocument().View()->UpdateAllLifecyclePhases(); + UpdateAllLifecyclePhasesForTest(); Element* element = GetDocument().getElementById("target"); PaintLayer* paint_layer = ToLayoutBoxModelObject(element->GetLayoutObject())->Layer(); @@ -352,7 +361,7 @@ TEST_F(CompositedLayerMappingTest, 3D45DegRotatedTallInterestRect) { "<div id='target' style='width: 200px; height: 10000px; will-change: " "transform; transform: rotateY(45deg)'></div>"); - GetDocument().View()->UpdateAllLifecyclePhases(); + UpdateAllLifecyclePhasesForTest(); Element* element = GetDocument().getElementById("target"); PaintLayer* paint_layer = ToLayoutBoxModelObject(element->GetLayoutObject())->Layer(); @@ -366,7 +375,7 @@ TEST_F(CompositedLayerMappingTest, RotatedTallInterestRect) { "<div id='target' style='width: 200px; height: 10000px; will-change: " "transform; transform: rotateZ(45deg)'></div>"); - GetDocument().View()->UpdateAllLifecyclePhases(); + UpdateAllLifecyclePhasesForTest(); Element* element = GetDocument().getElementById("target"); PaintLayer* paint_layer = ToLayoutBoxModelObject(element->GetLayoutObject())->Layer(); @@ -380,7 +389,7 @@ TEST_F(CompositedLayerMappingTest, WideLayerInterestRect) { "<div id='target' style='width: 10000px; height: 200px; will-change: " "transform'></div>"); - GetDocument().View()->UpdateAllLifecyclePhases(); + UpdateAllLifecyclePhasesForTest(); Element* element = GetDocument().getElementById("target"); PaintLayer* paint_layer = ToLayoutBoxModelObject(element->GetLayoutObject())->Layer(); @@ -397,7 +406,7 @@ TEST_F(CompositedLayerMappingTest, FixedPositionInterestRect) { "<div id='target' style='width: 300px; height: 400px; will-change: " "transform; position: fixed; top: 100px; left: 200px;'></div>"); - GetDocument().View()->UpdateAllLifecyclePhases(); + UpdateAllLifecyclePhasesForTest(); Element* element = GetDocument().getElementById("target"); PaintLayer* paint_layer = ToLayoutBoxModelObject(element->GetLayoutObject())->Layer(); @@ -413,7 +422,7 @@ TEST_F(CompositedLayerMappingTest, LayerOffscreenInterestRect) { </div> )HTML"); - GetDocument().View()->UpdateAllLifecyclePhases(); + UpdateAllLifecyclePhasesForTest(); Element* element = GetDocument().getElementById("target"); PaintLayer* paint_layer = ToLayoutBoxModelObject(element->GetLayoutObject())->Layer(); @@ -433,7 +442,7 @@ TEST_F(CompositedLayerMappingTest, ScrollingLayerInterestRect) { <div style='width: 100px; height: 10000px'></div></div> )HTML"); - GetDocument().View()->UpdateAllLifecyclePhases(); + UpdateAllLifecyclePhasesForTest(); Element* element = GetDocument().getElementById("target"); PaintLayer* paint_layer = ToLayoutBoxModelObject(element->GetLayoutObject())->Layer(); @@ -457,7 +466,7 @@ TEST_F(CompositedLayerMappingTest, ClippedBigLayer) { transform'></div></div> )HTML"); - GetDocument().View()->UpdateAllLifecyclePhases(); + UpdateAllLifecyclePhasesForTest(); Element* element = GetDocument().getElementById("target"); PaintLayer* paint_layer = ToLayoutBoxModelObject(element->GetLayoutObject())->Layer(); @@ -467,7 +476,7 @@ TEST_F(CompositedLayerMappingTest, ClippedBigLayer) { RecomputeInterestRect(paint_layer->GraphicsLayerBacking())); } -TEST_F(CompositedLayerMappingTest, ClippingMaskLayer) { +TEST_F(CompositedLayerMappingTestWithoutBGPT, ClippingMaskLayer) { if (RuntimeEnabledFeatures::SlimmingPaintV2Enabled()) return; @@ -481,7 +490,7 @@ TEST_F(CompositedLayerMappingTest, ClippingMaskLayer) { SetBodyInnerHTML("<video id='video' src='x' style='" + style_without_clipping + "'></video>"); - GetDocument().View()->UpdateAllLifecyclePhases(); + UpdateAllLifecyclePhasesForTest(); Element* video_element = GetDocument().getElementById("video"); GraphicsLayer* graphics_layer = ToLayoutBoxModelObject(video_element->GetLayoutObject()) @@ -490,18 +499,18 @@ TEST_F(CompositedLayerMappingTest, ClippingMaskLayer) { EXPECT_FALSE(graphics_layer->MaskLayer()); EXPECT_FALSE(graphics_layer->ContentsClippingMaskLayer()); - video_element->setAttribute(HTMLNames::styleAttr, style_with_border_radius); - GetDocument().View()->UpdateAllLifecyclePhases(); + video_element->setAttribute(html_names::kStyleAttr, style_with_border_radius); + UpdateAllLifecyclePhasesForTest(); EXPECT_FALSE(graphics_layer->MaskLayer()); EXPECT_TRUE(graphics_layer->ContentsClippingMaskLayer()); - video_element->setAttribute(HTMLNames::styleAttr, style_with_clip_path); - GetDocument().View()->UpdateAllLifecyclePhases(); + video_element->setAttribute(html_names::kStyleAttr, style_with_clip_path); + UpdateAllLifecyclePhasesForTest(); EXPECT_TRUE(graphics_layer->MaskLayer()); EXPECT_FALSE(graphics_layer->ContentsClippingMaskLayer()); - video_element->setAttribute(HTMLNames::styleAttr, style_without_clipping); - GetDocument().View()->UpdateAllLifecyclePhases(); + video_element->setAttribute(html_names::kStyleAttr, style_without_clipping); + UpdateAllLifecyclePhasesForTest(); EXPECT_FALSE(graphics_layer->MaskLayer()); EXPECT_FALSE(graphics_layer->ContentsClippingMaskLayer()); } @@ -514,7 +523,7 @@ TEST_F(CompositedLayerMappingTest, ScrollContentsFlattenForScroller) { <div style='width: 1000px; height: 1000px;'>Foo</div>Foo</div> )HTML"); - GetDocument().View()->UpdateAllLifecyclePhases(); + UpdateAllLifecyclePhasesForTest(); Element* element = GetDocument().getElementById("scroller"); PaintLayer* paint_layer = ToLayoutBoxModelObject(element->GetLayoutObject())->Layer(); @@ -623,7 +632,7 @@ TEST_F(CompositedLayerMappingTest, InterestRectChangeOnViewportScroll) { <div id='div' style='width: 100px; height: 10000px'>Text</div> )HTML"); - GetDocument().View()->UpdateAllLifecyclePhases(); + UpdateAllLifecyclePhasesForTest(); GraphicsLayer* root_scrolling_layer = GetDocument().GetLayoutView()->Layer()->GraphicsLayerBacking(); EXPECT_EQ(IntRect(0, 0, 800, 4600), @@ -631,7 +640,7 @@ TEST_F(CompositedLayerMappingTest, InterestRectChangeOnViewportScroll) { GetDocument().View()->LayoutViewport()->SetScrollOffset(ScrollOffset(0, 300), kProgrammaticScroll); - GetDocument().View()->UpdateAllLifecyclePhases(); + UpdateAllLifecyclePhasesForTest(); // Still use the previous interest rect because the recomputed rect hasn't // changed enough. EXPECT_EQ(IntRect(0, 0, 800, 4900), @@ -641,7 +650,7 @@ TEST_F(CompositedLayerMappingTest, InterestRectChangeOnViewportScroll) { GetDocument().View()->LayoutViewport()->SetScrollOffset(ScrollOffset(0, 600), kProgrammaticScroll); - GetDocument().View()->UpdateAllLifecyclePhases(); + UpdateAllLifecyclePhasesForTest(); // Use recomputed interest rect because it changed enough. EXPECT_EQ(IntRect(0, 0, 800, 5200), RecomputeInterestRect(root_scrolling_layer)); @@ -650,7 +659,7 @@ TEST_F(CompositedLayerMappingTest, InterestRectChangeOnViewportScroll) { GetDocument().View()->LayoutViewport()->SetScrollOffset(ScrollOffset(0, 5400), kProgrammaticScroll); - GetDocument().View()->UpdateAllLifecyclePhases(); + UpdateAllLifecyclePhasesForTest(); EXPECT_EQ(IntRect(0, 1400, 800, 8600), RecomputeInterestRect(root_scrolling_layer)); EXPECT_EQ(IntRect(0, 1400, 800, 8600), @@ -658,7 +667,7 @@ TEST_F(CompositedLayerMappingTest, InterestRectChangeOnViewportScroll) { GetDocument().View()->LayoutViewport()->SetScrollOffset(ScrollOffset(0, 9000), kProgrammaticScroll); - GetDocument().View()->UpdateAllLifecyclePhases(); + UpdateAllLifecyclePhasesForTest(); // Still use the previous interest rect because it contains the recomputed // interest rect. EXPECT_EQ(IntRect(0, 5000, 800, 5000), @@ -669,7 +678,7 @@ TEST_F(CompositedLayerMappingTest, InterestRectChangeOnViewportScroll) { GetDocument().View()->LayoutViewport()->SetScrollOffset(ScrollOffset(0, 2000), kProgrammaticScroll); // Use recomputed interest rect because it changed enough. - GetDocument().View()->UpdateAllLifecyclePhases(); + UpdateAllLifecyclePhasesForTest(); EXPECT_EQ(IntRect(0, 0, 800, 6600), RecomputeInterestRect(root_scrolling_layer)); EXPECT_EQ(IntRect(0, 0, 800, 6600), @@ -685,14 +694,14 @@ TEST_F(CompositedLayerMappingTest, InterestRectChangeOnShrunkenViewport) { <div id='div' style='width: 100px; height: 10000px'>Text</div> )HTML"); - GetDocument().View()->UpdateAllLifecyclePhases(); + UpdateAllLifecyclePhasesForTest(); GraphicsLayer* root_scrolling_layer = GetDocument().GetLayoutView()->Layer()->GraphicsLayerBacking(); EXPECT_EQ(IntRect(0, 0, 800, 4600), PreviousInterestRect(root_scrolling_layer)); GetDocument().View()->SetFrameRect(IntRect(0, 0, 800, 60)); - GetDocument().View()->UpdateAllLifecyclePhases(); + UpdateAllLifecyclePhasesForTest(); // Repaint required, so interest rect should be updated to shrunken size. EXPECT_EQ(IntRect(0, 0, 800, 4060), RecomputeInterestRect(root_scrolling_layer)); @@ -715,33 +724,33 @@ TEST_F(CompositedLayerMappingTest, InterestRectChangeOnScroll) { </div )HTML"); - GetDocument().View()->UpdateAllLifecyclePhases(); + UpdateAllLifecyclePhasesForTest(); Element* scroller = GetDocument().getElementById("scroller"); GraphicsLayer* scrolling_layer = scroller->GetLayoutBox()->Layer()->GraphicsLayerBacking(); EXPECT_EQ(IntRect(0, 0, 400, 4400), PreviousInterestRect(scrolling_layer)); scroller->setScrollTop(300); - GetDocument().View()->UpdateAllLifecyclePhases(); + UpdateAllLifecyclePhasesForTest(); // Still use the previous interest rect because the recomputed rect hasn't // changed enough. EXPECT_EQ(IntRect(0, 0, 400, 4700), RecomputeInterestRect(scrolling_layer)); EXPECT_EQ(IntRect(0, 0, 400, 4400), PreviousInterestRect(scrolling_layer)); scroller->setScrollTop(600); - GetDocument().View()->UpdateAllLifecyclePhases(); + UpdateAllLifecyclePhasesForTest(); // Use recomputed interest rect because it changed enough. EXPECT_EQ(IntRect(0, 0, 400, 5000), RecomputeInterestRect(scrolling_layer)); EXPECT_EQ(IntRect(0, 0, 400, 5000), PreviousInterestRect(scrolling_layer)); scroller->setScrollTop(5600); - GetDocument().View()->UpdateAllLifecyclePhases(); + UpdateAllLifecyclePhasesForTest(); EXPECT_EQ(IntRect(0, 1600, 400, 8400), RecomputeInterestRect(scrolling_layer)); EXPECT_EQ(IntRect(0, 1600, 400, 8400), PreviousInterestRect(scrolling_layer)); scroller->setScrollTop(9000); - GetDocument().View()->UpdateAllLifecyclePhases(); + UpdateAllLifecyclePhasesForTest(); // Still use the previous interest rect because it contains the recomputed // interest rect. EXPECT_EQ(IntRect(0, 5000, 400, 5000), @@ -750,7 +759,7 @@ TEST_F(CompositedLayerMappingTest, InterestRectChangeOnScroll) { scroller->setScrollTop(2000); // Use recomputed interest rect because it changed enough. - GetDocument().View()->UpdateAllLifecyclePhases(); + UpdateAllLifecyclePhasesForTest(); EXPECT_EQ(IntRect(0, 0, 400, 6400), RecomputeInterestRect(scrolling_layer)); EXPECT_EQ(IntRect(0, 0, 400, 6400), PreviousInterestRect(scrolling_layer)); } @@ -771,22 +780,22 @@ TEST_F(CompositedLayerMappingTest, </div )HTML"); - GetDocument().View()->UpdateAllLifecyclePhases(); + UpdateAllLifecyclePhasesForTest(); Element* scroller = GetDocument().getElementById("scroller"); GraphicsLayer* scrolling_layer = scroller->GetLayoutBox()->Layer()->GraphicsLayerBacking(); scroller->setScrollTop(5400); - GetDocument().View()->UpdateAllLifecyclePhases(); + UpdateAllLifecyclePhasesForTest(); scroller->setScrollTop(9400); - GetDocument().View()->UpdateAllLifecyclePhases(); + UpdateAllLifecyclePhasesForTest(); EXPECT_EQ(IntRect(0, 5400, 400, 4600), RecomputeInterestRect(scrolling_layer)); EXPECT_EQ(IntRect(0, 5400, 400, 4600), PreviousInterestRect(scrolling_layer)); // Paint invalidation and repaint should change previous paint interest rect. GetDocument().getElementById("content")->setTextContent("Change"); - GetDocument().View()->UpdateAllLifecyclePhases(); + UpdateAllLifecyclePhasesForTest(); EXPECT_EQ(IntRect(0, 5400, 400, 4600), RecomputeInterestRect(scrolling_layer)); EXPECT_EQ(IntRect(0, 5400, 400, 4600), PreviousInterestRect(scrolling_layer)); @@ -868,7 +877,7 @@ TEST_F(CompositedLayerMappingTest, InterestRectOfIframeInScrolledDiv) { // Scroll 8000 pixels down to move the iframe into view. GetDocument().View()->LayoutViewport()->SetScrollOffset( ScrollOffset(0.0, 8000.0), kProgrammaticScroll); - GetDocument().View()->UpdateAllLifecyclePhases(); + UpdateAllLifecyclePhasesForTest(); Element* target = ChildDocument().getElementById("target"); ASSERT_TRUE(target); @@ -894,12 +903,12 @@ TEST_F(CompositedLayerMappingTest, InterestRectOfScrolledIframe) { "<style>body { margin: 0; } #target { width: 200px; " "height: 8000px;}</style><div id=target></div>"); - GetDocument().View()->UpdateAllLifecyclePhases(); + UpdateAllLifecyclePhasesForTest(); // Scroll 7500 pixels down to bring the scrollable area to the bottom. ChildDocument().View()->LayoutViewport()->SetScrollOffset( ScrollOffset(0.0, 7500.0), kProgrammaticScroll); - GetDocument().View()->UpdateAllLifecyclePhases(); + UpdateAllLifecyclePhasesForTest(); ASSERT_TRUE(ChildDocument().View()->GetLayoutView()->HasLayer()); EXPECT_EQ(IntRect(0, 3500, 500, 4500), @@ -927,13 +936,13 @@ TEST_F(CompositedLayerMappingTest, InterestRectOfIframeWithContentBoxOffset) { "<style>body { margin: 0; } #target { width: 200px; " "height: 8000px;}</style> <div id=target></div>"); - GetDocument().View()->UpdateAllLifecyclePhases(); + UpdateAllLifecyclePhasesForTest(); // Scroll 3000 pixels down to bring the scrollable area to somewhere in the // middle. ChildDocument().View()->LayoutViewport()->SetScrollOffset( ScrollOffset(0.0, 3000.0), kProgrammaticScroll); - GetDocument().View()->UpdateAllLifecyclePhases(); + UpdateAllLifecyclePhasesForTest(); ASSERT_TRUE(ChildDocument().View()->GetLayoutView()->HasLayer()); EXPECT_EQ(IntRect(0, 0, 500, 7500), @@ -965,7 +974,7 @@ TEST_F(CompositedLayerMappingTest, InterestRectOfIframeWithFixedContents) { </div> )HTML"); - GetDocument().View()->UpdateAllLifecyclePhases(); + UpdateAllLifecyclePhasesForTest(); auto* fixed = ChildDocument().getElementById("fixed")->GetLayoutObject(); auto* graphics_layer = fixed->EnclosingLayer()->GraphicsLayerBacking(fixed); @@ -975,7 +984,7 @@ TEST_F(CompositedLayerMappingTest, InterestRectOfIframeWithFixedContents) { ChildDocument().View()->LayoutViewport()->SetScrollOffset( ScrollOffset(0.0, 3000.0), kProgrammaticScroll); - GetDocument().View()->UpdateAllLifecyclePhases(); + UpdateAllLifecyclePhasesForTest(); // Because the fixed element does not scroll, the interest rect is unchanged. EXPECT_EQ(IntRect(1000, 0, 4400, 300), RecomputeInterestRect(graphics_layer)); @@ -994,14 +1003,14 @@ TEST_F(CompositedLayerMappingTest, ScrolledFixedPositionInterestRect) { <div id="forcescroll" style="height: 2000px;"></div> )HTML"); - GetDocument().View()->UpdateAllLifecyclePhases(); + UpdateAllLifecyclePhasesForTest(); auto* fixed = GetDocument().getElementById("fixed")->GetLayoutObject(); auto* graphics_layer = fixed->EnclosingLayer()->GraphicsLayerBacking(fixed); EXPECT_EQ(IntRect(0, 500, 100, 4030), RecomputeInterestRect(graphics_layer)); GetDocument().View()->LayoutViewport()->SetScrollOffset( ScrollOffset(0.0, 200.0), kProgrammaticScroll); - GetDocument().View()->UpdateAllLifecyclePhases(); + UpdateAllLifecyclePhasesForTest(); // Because the fixed element does not scroll, the interest rect is unchanged. EXPECT_EQ(IntRect(0, 500, 100, 4030), RecomputeInterestRect(graphics_layer)); @@ -1044,7 +1053,7 @@ TEST_F(CompositedLayerMappingTest, GetDocument().getElementById("negative-composited-child"); negative_composited_child->parentNode()->RemoveChild( negative_composited_child); - GetDocument().View()->UpdateAllLifecyclePhases(); + UpdateAllLifecyclePhasesForTest(); mapping = ToLayoutBlock(GetLayoutObjectByElementId("container")) ->Layer() @@ -1071,7 +1080,7 @@ TEST_F(CompositedLayerMappingTest, <div id="target"><div id="scrolled"></div></div> </div> )HTML"); - GetDocument().View()->UpdateAllLifecyclePhases(); + UpdateAllLifecyclePhasesForTest(); Element* element = GetDocument().getElementById("target"); PaintLayer* paint_layer = @@ -1086,8 +1095,8 @@ TEST_F(CompositedLayerMappingTest, EXPECT_TRUE(mapping->DecorationOutlineLayer()); // No decoration outline layer is created when not composited scrolling. - element->setAttribute(HTMLNames::styleAttr, "overflow: visible;"); - GetDocument().View()->UpdateAllLifecyclePhases(); + element->setAttribute(html_names::kStyleAttr, "overflow: visible;"); + UpdateAllLifecyclePhasesForTest(); paint_layer = ToLayoutBoxModelObject(element->GetLayoutObject())->Layer(); ASSERT_TRUE(paint_layer); @@ -1108,7 +1117,7 @@ TEST_F(CompositedLayerMappingTest, <div id="scroller"><div id="scrolled"></div></div> </div> )HTML"); - GetDocument().View()->UpdateAllLifecyclePhases(); + UpdateAllLifecyclePhasesForTest(); Element* scroller = GetDocument().getElementById("scroller"); PaintLayer* paint_layer = @@ -1120,8 +1129,8 @@ TEST_F(CompositedLayerMappingTest, // The decoration outline layer is created when composited scrolling // with an outline drawn over the composited scrolling region. - scroller->setAttribute(HTMLNames::styleAttr, "outline-offset: -2px;"); - GetDocument().View()->UpdateAllLifecyclePhases(); + scroller->setAttribute(html_names::kStyleAttr, "outline-offset: -2px;"); + UpdateAllLifecyclePhasesForTest(); paint_layer = ToLayoutBoxModelObject(scroller->GetLayoutObject())->Layer(); ASSERT_TRUE(paint_layer); @@ -1131,8 +1140,8 @@ TEST_F(CompositedLayerMappingTest, // The decoration outline layer is destroyed when the scrolling region // will not be covered up by the outline. - scroller->removeAttribute(HTMLNames::styleAttr); - GetDocument().View()->UpdateAllLifecyclePhases(); + scroller->removeAttribute(html_names::kStyleAttr); + UpdateAllLifecyclePhasesForTest(); paint_layer = ToLayoutBoxModelObject(scroller->GetLayoutObject())->Layer(); ASSERT_TRUE(paint_layer); @@ -1146,23 +1155,21 @@ TEST_F(CompositedLayerMappingTest, true); SetBodyInnerHTML(R"HTML( <div id='container' style='overflow: scroll; width: 300px; height: - 300px; background: white; will-change: transform;'> - <div style='background-color: blue; clip-path: circle(600px at 1000px 1000px); - width: 2000px; height: - 2000px;'></div> + 300px; background: white; will-change: transform;'> + <div style='background-color: blue; width: 2000px; height: 2000px; + clip-path: circle(600px at 1000px 1000px);'></div> </div> )HTML"); - PaintLayer* layer = - ToLayoutBlock(GetLayoutObjectByElementId("container"))->Layer(); + const auto* container = ToLayoutBox(GetLayoutObjectByElementId("container")); EXPECT_EQ(kBackgroundPaintInScrollingContents, - layer->GetBackgroundPaintLocation()); + container->GetBackgroundPaintLocation()); // We currently don't use composited scrolling when the container has a // border-radius so even though we can paint the background onto the scrolling // contents layer we don't have a scrolling contents layer to paint into in // this case. - CompositedLayerMapping* mapping = layer->GetCompositedLayerMapping(); + const auto* mapping = container->Layer()->GetCompositedLayerMapping(); EXPECT_FALSE(mapping->HasScrollingLayer()); EXPECT_FALSE(mapping->BackgroundPaintsOntoScrollingContentsLayer()); } @@ -1177,35 +1184,35 @@ TEST_F(CompositedLayerMappingTest, // direction to compensate. To make this a little clearer, for the first // example here the layer positions are calculated as: // - // m_graphicsLayer x = left_pos - shadow_spread + shadow_x_offset + // graphics_layer_ x = left_pos - shadow_spread + shadow_x_offset // = 50 - 10 - 10 // = 30 // - // m_graphicsLayer y = top_pos - shadow_spread + shadow_y_offset + // graphics_layer_ y = top_pos - shadow_spread + shadow_y_offset // = 50 - 10 + 0 // = 40 // - // contents x = 50 - m_graphicsLayer x = 50 - 30 = 20 - // contents y = 50 - m_graphicsLayer y = 50 - 40 = 10 + // contents x = 50 - graphics_layer_ x = 50 - 30 = 20 + // contents y = 50 - graphics_layer_ y = 50 - 40 = 10 // // The reason that perspective matters is that it affects which 'contents' - // layer is offset; m_childTransformLayer when using perspective, or - // m_scrollingLayer when there is no perspective. + // layer is offset; child_transform_layer_ when using perspective, or + // scrolling_layer_ when there is no perspective. - SetBodyInnerHTML( - "<div id='scroller' style='position: absolute; top: 50px; left: 50px; " - "width: 400px; height: 245px; overflow: auto; will-change: transform; " - "box-shadow: -10px 0 0 10px; perspective: 1px;'>" - " <div style='position: absolute; top: 50px; bottom: 0; width: 200px; " - "height: 200px;'></div>" - "</div>" - - "<div id='scroller2' style='position: absolute; top: 400px; left: 50px; " - "width: 400px; height: 245px; overflow: auto; will-change: transform; " - "box-shadow: -10px 0 0 10px;'>" - " <div style='position: absolute; top: 50px; bottom: 0; width: 200px; " - "height: 200px;'></div>" - "</div>"); + SetBodyInnerHTML(R"HTML( + <div id='scroller' style='position: absolute; top: 50px; left: 50px; + width: 400px; height: 245px; overflow: auto; will-change: transform; + box-shadow: -10px 0 0 10px; perspective: 1px;'> + <div style='position: absolute; top: 50px; bottom: 0; width: 200px; + height: 200px;'></div> + </div> + <div id='scroller2' style='position: absolute; top: 400px; left: 50px; + width: 400px; height: 245px; overflow: auto; will-change: transform; + box-shadow: -10px 0 0 10px;'> + <div style='position: absolute; top: 50px; bottom: 0; width: 200px; + height: 200px;'></div> + </div> + )HTML"); CompositedLayerMapping* mapping = ToLayoutBlock(GetLayoutObjectByElementId("scroller")) @@ -1252,7 +1259,8 @@ TEST_F(CompositedLayerMappingTest, EXPECT_FLOAT_EQ(10, scrolling_layer2->GetPosition().y()); } -TEST_F(CompositedLayerMappingTest, AncestorClippingMaskLayerUpdates) { +TEST_F(CompositedLayerMappingTestWithoutBGPT, + AncestorClippingMaskLayerUpdates) { SetBodyInnerHTML(R"HTML( <style> #ancestor { width: 100px; height: 100px; overflow: hidden; } @@ -1262,7 +1270,7 @@ TEST_F(CompositedLayerMappingTest, AncestorClippingMaskLayerUpdates) { <div id='child'></div> </div> )HTML"); - GetDocument().View()->UpdateAllLifecyclePhases(); + UpdateAllLifecyclePhasesForTest(); Element* ancestor = GetDocument().getElementById("ancestor"); ASSERT_TRUE(ancestor); @@ -1281,8 +1289,8 @@ TEST_F(CompositedLayerMappingTest, AncestorClippingMaskLayerUpdates) { ASSERT_FALSE(child_paint_layer); // Making the child conposited causes creation of an AncestorClippingLayer. - child->setAttribute(HTMLNames::styleAttr, "will-change: transform"); - GetDocument().View()->UpdateAllLifecyclePhases(); + child->setAttribute(html_names::kStyleAttr, "will-change: transform"); + UpdateAllLifecyclePhasesForTest(); child_paint_layer = ToLayoutBoxModelObject(child->GetLayoutObject())->Layer(); ASSERT_TRUE(child_paint_layer); CompositedLayerMapping* child_mapping = @@ -1294,8 +1302,8 @@ TEST_F(CompositedLayerMappingTest, AncestorClippingMaskLayerUpdates) { // Adding border radius to the ancestor requires an // ancestorClippingMaskLayer for the child - ancestor->setAttribute(HTMLNames::styleAttr, "border-radius: 40px;"); - GetDocument().View()->UpdateAllLifecyclePhases(); + ancestor->setAttribute(html_names::kStyleAttr, "border-radius: 40px;"); + UpdateAllLifecyclePhasesForTest(); child_paint_layer = ToLayoutBoxModelObject(child->GetLayoutObject())->Layer(); ASSERT_TRUE(child_paint_layer); child_mapping = child_paint_layer->GetCompositedLayerMapping(); @@ -1306,8 +1314,8 @@ TEST_F(CompositedLayerMappingTest, AncestorClippingMaskLayerUpdates) { // Removing the border radius should remove the ancestorClippingMaskLayer // for the child - ancestor->setAttribute(HTMLNames::styleAttr, "border-radius: 0px;"); - GetDocument().View()->UpdateAllLifecyclePhases(); + ancestor->setAttribute(html_names::kStyleAttr, "border-radius: 0px;"); + UpdateAllLifecyclePhasesForTest(); child_paint_layer = ToLayoutBoxModelObject(child->GetLayoutObject())->Layer(); ASSERT_TRUE(child_paint_layer); child_mapping = child_paint_layer->GetCompositedLayerMapping(); @@ -1317,13 +1325,13 @@ TEST_F(CompositedLayerMappingTest, AncestorClippingMaskLayerUpdates) { EXPECT_FALSE(child_mapping->AncestorClippingMaskLayer()); // Add border radius back so we can test one more case - ancestor->setAttribute(HTMLNames::styleAttr, "border-radius: 40px;"); - GetDocument().View()->UpdateAllLifecyclePhases(); + ancestor->setAttribute(html_names::kStyleAttr, "border-radius: 40px;"); + UpdateAllLifecyclePhasesForTest(); // Now change the overflow to remove the need for an ancestor clip // on the child - ancestor->setAttribute(HTMLNames::styleAttr, "overflow: visible"); - GetDocument().View()->UpdateAllLifecyclePhases(); + ancestor->setAttribute(html_names::kStyleAttr, "overflow: visible"); + UpdateAllLifecyclePhasesForTest(); child_paint_layer = ToLayoutBoxModelObject(child->GetLayoutObject())->Layer(); ASSERT_TRUE(child_paint_layer); child_mapping = child_paint_layer->GetCompositedLayerMapping(); @@ -1332,7 +1340,8 @@ TEST_F(CompositedLayerMappingTest, AncestorClippingMaskLayerUpdates) { EXPECT_FALSE(child_mapping->AncestorClippingMaskLayer()); } -TEST_F(CompositedLayerMappingTest, AncestorClippingMaskLayerSiblingUpdates) { +TEST_F(CompositedLayerMappingTestWithoutBGPT, + AncestorClippingMaskLayerSiblingUpdates) { SetBodyInnerHTML(R"HTML( <style> #ancestor { width: 200px; height: 200px; overflow: hidden; } @@ -1346,7 +1355,7 @@ TEST_F(CompositedLayerMappingTest, AncestorClippingMaskLayerSiblingUpdates) { <div id='child2'></div> </div> )HTML"); - GetDocument().View()->UpdateAllLifecyclePhases(); + UpdateAllLifecyclePhasesForTest(); Element* ancestor = GetDocument().getElementById("ancestor"); ASSERT_TRUE(ancestor); @@ -1377,8 +1386,8 @@ TEST_F(CompositedLayerMappingTest, AncestorClippingMaskLayerSiblingUpdates) { ASSERT_FALSE(child2_mapping); // Making child1 composited causes creation of an AncestorClippingLayer. - child1->setAttribute(HTMLNames::styleAttr, "will-change: transform"); - GetDocument().View()->UpdateAllLifecyclePhases(); + child1->setAttribute(html_names::kStyleAttr, "will-change: transform"); + UpdateAllLifecyclePhasesForTest(); child1_paint_layer = ToLayoutBoxModelObject(child1->GetLayoutObject())->Layer(); ASSERT_TRUE(child1_paint_layer); @@ -1395,8 +1404,8 @@ TEST_F(CompositedLayerMappingTest, AncestorClippingMaskLayerSiblingUpdates) { // Adding border radius to the ancestor requires an // ancestorClippingMaskLayer for child1 - ancestor->setAttribute(HTMLNames::styleAttr, "border-radius: 40px;"); - GetDocument().View()->UpdateAllLifecyclePhases(); + ancestor->setAttribute(html_names::kStyleAttr, "border-radius: 40px;"); + UpdateAllLifecyclePhasesForTest(); child1_paint_layer = ToLayoutBoxModelObject(child1->GetLayoutObject())->Layer(); ASSERT_TRUE(child1_paint_layer); @@ -1413,8 +1422,8 @@ TEST_F(CompositedLayerMappingTest, AncestorClippingMaskLayerSiblingUpdates) { // Making child2 composited causes creation of an AncestorClippingLayer // and a mask layer. - child2->setAttribute(HTMLNames::styleAttr, "will-change: transform"); - GetDocument().View()->UpdateAllLifecyclePhases(); + child2->setAttribute(html_names::kStyleAttr, "will-change: transform"); + UpdateAllLifecyclePhasesForTest(); child1_paint_layer = ToLayoutBoxModelObject(child1->GetLayoutObject())->Layer(); ASSERT_TRUE(child1_paint_layer); @@ -1434,8 +1443,8 @@ TEST_F(CompositedLayerMappingTest, AncestorClippingMaskLayerSiblingUpdates) { // Removing will-change: transform on child1 should result in the removal // of all clipping and masking layers - child1->setAttribute(HTMLNames::styleAttr, "will-change: none"); - GetDocument().View()->UpdateAllLifecyclePhases(); + child1->setAttribute(html_names::kStyleAttr, "will-change: none"); + UpdateAllLifecyclePhasesForTest(); child1_paint_layer = ToLayoutBoxModelObject(child1->GetLayoutObject())->Layer(); ASSERT_TRUE(child1_paint_layer); @@ -1452,8 +1461,8 @@ TEST_F(CompositedLayerMappingTest, AncestorClippingMaskLayerSiblingUpdates) { // Now change the overflow to remove the need for an ancestor clip // on the children - ancestor->setAttribute(HTMLNames::styleAttr, "overflow: visible"); - GetDocument().View()->UpdateAllLifecyclePhases(); + ancestor->setAttribute(html_names::kStyleAttr, "overflow: visible"); + UpdateAllLifecyclePhasesForTest(); child1_paint_layer = ToLayoutBoxModelObject(child1->GetLayoutObject())->Layer(); ASSERT_TRUE(child1_paint_layer); @@ -1468,7 +1477,8 @@ TEST_F(CompositedLayerMappingTest, AncestorClippingMaskLayerSiblingUpdates) { EXPECT_FALSE(child2_mapping->AncestorClippingMaskLayer()); } -TEST_F(CompositedLayerMappingTest, AncestorClippingMaskLayerGrandchildUpdates) { +TEST_F(CompositedLayerMappingTestWithoutBGPT, + AncestorClippingMaskLayerGrandchildUpdates) { SetBodyInnerHTML(R"HTML( <style> #ancestor { width: 200px; height: 200px; overflow: hidden; } @@ -1483,7 +1493,7 @@ TEST_F(CompositedLayerMappingTest, AncestorClippingMaskLayerGrandchildUpdates) { </div> </div> )HTML"); - GetDocument().View()->UpdateAllLifecyclePhases(); + UpdateAllLifecyclePhasesForTest(); Element* ancestor = GetDocument().getElementById("ancestor"); ASSERT_TRUE(ancestor); @@ -1514,8 +1524,8 @@ TEST_F(CompositedLayerMappingTest, AncestorClippingMaskLayerGrandchildUpdates) { ASSERT_FALSE(grandchild_mapping); // Making grandchild composited causes creation of an AncestorClippingLayer. - grandchild->setAttribute(HTMLNames::styleAttr, "will-change: transform"); - GetDocument().View()->UpdateAllLifecyclePhases(); + grandchild->setAttribute(html_names::kStyleAttr, "will-change: transform"); + UpdateAllLifecyclePhasesForTest(); child_paint_layer = ToLayoutBoxModelObject(child->GetLayoutObject())->Layer(); ASSERT_TRUE(child_paint_layer); child_mapping = child_paint_layer->GetCompositedLayerMapping(); @@ -1531,8 +1541,8 @@ TEST_F(CompositedLayerMappingTest, AncestorClippingMaskLayerGrandchildUpdates) { // Adding border radius to the ancestor requires an // ancestorClippingMaskLayer for grandchild - ancestor->setAttribute(HTMLNames::styleAttr, "border-radius: 40px;"); - GetDocument().View()->UpdateAllLifecyclePhases(); + ancestor->setAttribute(html_names::kStyleAttr, "border-radius: 40px;"); + UpdateAllLifecyclePhasesForTest(); child_paint_layer = ToLayoutBoxModelObject(child->GetLayoutObject())->Layer(); ASSERT_TRUE(child_paint_layer); child_mapping = child_paint_layer->GetCompositedLayerMapping(); @@ -1549,9 +1559,9 @@ TEST_F(CompositedLayerMappingTest, AncestorClippingMaskLayerGrandchildUpdates) { // Moving the grandchild out of the clip region should result in removal // of the mask layer. It also removes the grandchild from its own mapping // because it is now squashed. - grandchild->setAttribute(HTMLNames::styleAttr, + grandchild->setAttribute(html_names::kStyleAttr, "left: 250px; will-change: transform"); - GetDocument().View()->UpdateAllLifecyclePhases(); + UpdateAllLifecyclePhasesForTest(); child_paint_layer = ToLayoutBoxModelObject(child->GetLayoutObject())->Layer(); ASSERT_TRUE(child_paint_layer); child_mapping = child_paint_layer->GetCompositedLayerMapping(); @@ -1567,8 +1577,8 @@ TEST_F(CompositedLayerMappingTest, AncestorClippingMaskLayerGrandchildUpdates) { // Now change the overflow to remove the need for an ancestor clip // on the children - ancestor->setAttribute(HTMLNames::styleAttr, "overflow: visible"); - GetDocument().View()->UpdateAllLifecyclePhases(); + ancestor->setAttribute(html_names::kStyleAttr, "overflow: visible"); + UpdateAllLifecyclePhasesForTest(); child_paint_layer = ToLayoutBoxModelObject(child->GetLayoutObject())->Layer(); ASSERT_TRUE(child_paint_layer); child_mapping = child_paint_layer->GetCompositedLayerMapping(); @@ -1581,7 +1591,8 @@ TEST_F(CompositedLayerMappingTest, AncestorClippingMaskLayerGrandchildUpdates) { EXPECT_FALSE(grandchild_mapping->AncestorClippingLayer()); } -TEST_F(CompositedLayerMappingTest, AncestorClipMaskRequiredByBorderRadius) { +TEST_F(CompositedLayerMappingTestWithoutBGPT, + AncestorClipMaskRequiredByBorderRadius) { // Verify that we create the mask layer when the child is contained within // the rectangular clip but not contained within the rounded rect clip. SetBodyInnerHTML(R"HTML( @@ -1598,7 +1609,7 @@ TEST_F(CompositedLayerMappingTest, AncestorClipMaskRequiredByBorderRadius) { <div id='child'></div> </div> )HTML"); - GetDocument().View()->UpdateAllLifecyclePhases(); + UpdateAllLifecyclePhasesForTest(); Element* ancestor = GetDocument().getElementById("ancestor"); ASSERT_TRUE(ancestor); @@ -1623,7 +1634,7 @@ TEST_F(CompositedLayerMappingTest, AncestorClipMaskRequiredByBorderRadius) { EXPECT_TRUE(child_mapping->AncestorClippingMaskLayer()); } -TEST_F(CompositedLayerMappingTest, +TEST_F(CompositedLayerMappingTestWithoutBGPT, AncestorClipMaskNotRequiredByNestedBorderRadius) { // This case has the child within all ancestors and does not require a // mask. @@ -1646,7 +1657,7 @@ TEST_F(CompositedLayerMappingTest, </div> </div> )HTML"); - GetDocument().View()->UpdateAllLifecyclePhases(); + UpdateAllLifecyclePhasesForTest(); Element* child = GetDocument().getElementById("child"); ASSERT_TRUE(child); @@ -1661,7 +1672,7 @@ TEST_F(CompositedLayerMappingTest, EXPECT_FALSE(child_mapping->AncestorClippingMaskLayer()); } -TEST_F(CompositedLayerMappingTest, +TEST_F(CompositedLayerMappingTestWithoutBGPT, AncestorClipMaskRequiredByParentBorderRadius) { // This case has the child within the grandparent but not the parent, and does // require a mask so that the parent will clip the corners. @@ -1684,7 +1695,7 @@ TEST_F(CompositedLayerMappingTest, </div> </div> )HTML"); - GetDocument().View()->UpdateAllLifecyclePhases(); + UpdateAllLifecyclePhasesForTest(); Element* child = GetDocument().getElementById("child"); ASSERT_TRUE(child); @@ -1702,7 +1713,7 @@ TEST_F(CompositedLayerMappingTest, EXPECT_EQ(120, layer_size.height()); } -TEST_F(CompositedLayerMappingTest, +TEST_F(CompositedLayerMappingTestWithoutBGPT, AncestorClipMaskNotRequiredByParentBorderRadius) { // This case has the child within the grandparent but not the parent, and does // not require a mask because the parent does not have border radius @@ -1725,7 +1736,7 @@ TEST_F(CompositedLayerMappingTest, </div> </div> )HTML"); - GetDocument().View()->UpdateAllLifecyclePhases(); + UpdateAllLifecyclePhasesForTest(); Element* child = GetDocument().getElementById("child"); ASSERT_TRUE(child); @@ -1740,7 +1751,7 @@ TEST_F(CompositedLayerMappingTest, EXPECT_FALSE(child_mapping->AncestorClippingMaskLayer()); } -TEST_F(CompositedLayerMappingTest, +TEST_F(CompositedLayerMappingTestWithoutBGPT, AncestorClipMaskRequiredByGrandparentBorderRadius1) { // This case has the child clipped by the grandparent border radius but not // the parent, and requires a mask to clip to the grandparent. Although in @@ -1765,7 +1776,7 @@ TEST_F(CompositedLayerMappingTest, </div> </div> )HTML"); - GetDocument().View()->UpdateAllLifecyclePhases(); + UpdateAllLifecyclePhasesForTest(); Element* child = GetDocument().getElementById("child"); ASSERT_TRUE(child); @@ -1783,7 +1794,7 @@ TEST_F(CompositedLayerMappingTest, EXPECT_EQ(120, layer_size.height()); } -TEST_F(CompositedLayerMappingTest, +TEST_F(CompositedLayerMappingTestWithoutBGPT, AncestorClipMaskRequiredByGrandparentBorderRadius2) { // Similar to the previous case, but here we really do need the mask. SetBodyInnerHTML(R"HTML( @@ -1805,7 +1816,7 @@ TEST_F(CompositedLayerMappingTest, </div> </div> )HTML"); - GetDocument().View()->UpdateAllLifecyclePhases(); + UpdateAllLifecyclePhasesForTest(); Element* child = GetDocument().getElementById("child"); ASSERT_TRUE(child); @@ -1823,7 +1834,7 @@ TEST_F(CompositedLayerMappingTest, EXPECT_EQ(160, layer_size.height()); } -TEST_F(CompositedLayerMappingTest, +TEST_F(CompositedLayerMappingTestWithoutBGPT, AncestorClipMaskNotRequiredByBorderRadiusInside) { // Verify that we do not create the mask layer when the child is contained // within the rounded rect clip. @@ -1841,7 +1852,7 @@ TEST_F(CompositedLayerMappingTest, <div id='child'></div> </div> )HTML"); - GetDocument().View()->UpdateAllLifecyclePhases(); + UpdateAllLifecyclePhasesForTest(); Element* ancestor = GetDocument().getElementById("ancestor"); ASSERT_TRUE(ancestor); @@ -1866,7 +1877,7 @@ TEST_F(CompositedLayerMappingTest, EXPECT_FALSE(child_mapping->AncestorClippingMaskLayer()); } -TEST_F(CompositedLayerMappingTest, +TEST_F(CompositedLayerMappingTestWithoutBGPT, AncestorClipMaskNotRequiredByBorderRadiusOutside) { // Verify that we do not create the mask layer when the child is outside // the ancestors rectangular clip. @@ -1884,7 +1895,7 @@ TEST_F(CompositedLayerMappingTest, <div id='child'></div> </div> )HTML"); - GetDocument().View()->UpdateAllLifecyclePhases(); + UpdateAllLifecyclePhasesForTest(); Element* ancestor = GetDocument().getElementById("ancestor"); ASSERT_TRUE(ancestor); @@ -1909,7 +1920,8 @@ TEST_F(CompositedLayerMappingTest, EXPECT_FALSE(child_mapping->AncestorClippingMaskLayer()); } -TEST_F(CompositedLayerMappingTest, AncestorClipMaskRequiredDueToScaleUp) { +TEST_F(CompositedLayerMappingTestWithoutBGPT, + AncestorClipMaskRequiredDueToScaleUp) { // Verify that we include the mask when the untransformed child does not // intersect the border radius but the transformed child does. Here the // child is inside the parent and scaled to expand to be clipped. @@ -1928,7 +1940,7 @@ TEST_F(CompositedLayerMappingTest, AncestorClipMaskRequiredDueToScaleUp) { <div id='child'></div> </div> )HTML"); - GetDocument().View()->UpdateAllLifecyclePhases(); + UpdateAllLifecyclePhasesForTest(); Element* child = GetDocument().getElementById("child"); ASSERT_TRUE(child); @@ -1943,7 +1955,8 @@ TEST_F(CompositedLayerMappingTest, AncestorClipMaskRequiredDueToScaleUp) { EXPECT_TRUE(child_mapping->AncestorClippingMaskLayer()); } -TEST_F(CompositedLayerMappingTest, AncestorClipMaskNotRequiredDueToScaleDown) { +TEST_F(CompositedLayerMappingTestWithoutBGPT, + AncestorClipMaskNotRequiredDueToScaleDown) { // Verify that we exclude the mask when the untransformed child does // intersect the border radius but the transformed child does not. Here the // child is bigger than the parent and scaled down such that it does not @@ -1963,7 +1976,7 @@ TEST_F(CompositedLayerMappingTest, AncestorClipMaskNotRequiredDueToScaleDown) { <div id='child'></div> </div> )HTML"); - GetDocument().View()->UpdateAllLifecyclePhases(); + UpdateAllLifecyclePhasesForTest(); Element* child = GetDocument().getElementById("child"); ASSERT_TRUE(child); @@ -1978,7 +1991,8 @@ TEST_F(CompositedLayerMappingTest, AncestorClipMaskNotRequiredDueToScaleDown) { EXPECT_FALSE(child_mapping->AncestorClippingMaskLayer()); } -TEST_F(CompositedLayerMappingTest, AncestorClipMaskRequiredDueToTranslateInto) { +TEST_F(CompositedLayerMappingTestWithoutBGPT, + AncestorClipMaskRequiredDueToTranslateInto) { // Verify that we include the mask when the untransformed child does not // intersect the border radius but the transformed child does. Here the // child is outside the parent and translated to be clipped. @@ -1997,7 +2011,7 @@ TEST_F(CompositedLayerMappingTest, AncestorClipMaskRequiredDueToTranslateInto) { <div id='child'></div> </div> )HTML"); - GetDocument().View()->UpdateAllLifecyclePhases(); + UpdateAllLifecyclePhasesForTest(); Element* child = GetDocument().getElementById("child"); ASSERT_TRUE(child); @@ -2012,7 +2026,7 @@ TEST_F(CompositedLayerMappingTest, AncestorClipMaskRequiredDueToTranslateInto) { EXPECT_TRUE(child_mapping->AncestorClippingMaskLayer()); } -TEST_F(CompositedLayerMappingTest, +TEST_F(CompositedLayerMappingTestWithoutBGPT, AncestorClipMaskNotRequiredDueToTranslateOut) { // Verify that we exclude the mask when the untransformed child does // intersect the border radius but the transformed child does not. Here the @@ -2032,7 +2046,7 @@ TEST_F(CompositedLayerMappingTest, <div id='child'></div> </div> )HTML"); - GetDocument().View()->UpdateAllLifecyclePhases(); + UpdateAllLifecyclePhasesForTest(); Element* child = GetDocument().getElementById("child"); ASSERT_TRUE(child); @@ -2047,7 +2061,8 @@ TEST_F(CompositedLayerMappingTest, EXPECT_FALSE(child_mapping->AncestorClippingMaskLayer()); } -TEST_F(CompositedLayerMappingTest, AncestorClipMaskRequiredDueToRotation) { +TEST_F(CompositedLayerMappingTestWithoutBGPT, + AncestorClipMaskRequiredDueToRotation) { // Verify that we include the mask when the untransformed child does not // intersect the border radius but the transformed child does. Here the // child is just within the mask-not-required area but when rotated requires @@ -2067,7 +2082,7 @@ TEST_F(CompositedLayerMappingTest, AncestorClipMaskRequiredDueToRotation) { <div id='child'></div> </div> )HTML"); - GetDocument().View()->UpdateAllLifecyclePhases(); + UpdateAllLifecyclePhasesForTest(); Element* child = GetDocument().getElementById("child"); ASSERT_TRUE(child); @@ -2082,7 +2097,7 @@ TEST_F(CompositedLayerMappingTest, AncestorClipMaskRequiredDueToRotation) { EXPECT_TRUE(child_mapping->AncestorClippingMaskLayer()); } -TEST_F(CompositedLayerMappingTest, +TEST_F(CompositedLayerMappingTestWithoutBGPT, AncestorClipMaskRequiredByBorderRadiusWithCompositedDescendant) { // This case has the child and grandchild within the ancestors and would // in principle not need a mask, but does because we cannot efficiently @@ -2106,7 +2121,7 @@ TEST_F(CompositedLayerMappingTest, </div> </div> )HTML"); - GetDocument().View()->UpdateAllLifecyclePhases(); + UpdateAllLifecyclePhasesForTest(); Element* parent = GetDocument().getElementById("parent"); ASSERT_TRUE(parent); @@ -2121,7 +2136,7 @@ TEST_F(CompositedLayerMappingTest, EXPECT_TRUE(parent_mapping->AncestorClippingMaskLayer()); } -TEST_F(CompositedLayerMappingTest, +TEST_F(CompositedLayerMappingTestWithoutBGPT, AncestorClipMaskGrandparentBorderRadiusCompositedDescendant) { // This case has the child clipped by the grandparent border radius but not // the parent, and does not itself require a mask to clip to the grandparent. @@ -2150,7 +2165,7 @@ TEST_F(CompositedLayerMappingTest, </div> </div> )HTML"); - GetDocument().View()->UpdateAllLifecyclePhases(); + UpdateAllLifecyclePhasesForTest(); Element* child = GetDocument().getElementById("child"); ASSERT_TRUE(child); @@ -2232,7 +2247,7 @@ TEST_F(CompositedLayerMappingTest, StickyPositionNotSquashed) { PaintLayerScrollableArea* scrollable_area = scroller->GetScrollableArea(); scrollable_area->ScrollToAbsolutePosition( FloatPoint(scrollable_area->ScrollPosition().Y(), 100)); - GetDocument().View()->UpdateAllLifecyclePhases(); + UpdateAllLifecyclePhasesForTest(); // Now that sticky2 and sticky3 overlap sticky1 they will be promoted, but // they should not be squashed into the same layer because they scroll with @@ -2275,7 +2290,7 @@ TEST_F(CompositedLayerMappingTest, PaintLayerScrollableArea* scrollable_area = scroller->GetScrollableArea(); scrollable_area->ScrollToAbsolutePosition( FloatPoint(scrollable_area->ScrollPosition().Y(), 100)); - GetDocument().View()->UpdateAllLifecyclePhases(); + UpdateAllLifecyclePhasesForTest(); // On the blink side, a sticky offset of (0, 100) should have been applied to // the sticky element. @@ -2323,7 +2338,7 @@ TEST_F(CompositedLayerMappingTest, ASSERT_TRUE(scrollable_area); scrollable_area->ScrollToAbsolutePosition( FloatPoint(scrollable_area->ScrollPosition().Y(), 100)); - GetDocument().View()->UpdateAllLifecyclePhases(); + UpdateAllLifecyclePhasesForTest(); EXPECT_FLOAT_EQ(0, main_graphics_layer->GetPosition().x()); EXPECT_FLOAT_EQ(100, main_graphics_layer->GetPosition().y()); @@ -2346,8 +2361,8 @@ TEST_F(CompositedLayerMappingTest, GraphicsLayer* target_graphics_layer = target_layer ? target_layer->GraphicsLayerBacking() : nullptr; ASSERT_TRUE(target_graphics_layer); - EXPECT_FALSE(target_graphics_layer->ContentLayer() - ->transformed_rasterization_allowed()); + EXPECT_FALSE( + target_graphics_layer->CcLayer()->transformed_rasterization_allowed()); } { LayoutObject* target = GetLayoutObjectByElementId("target2"); @@ -2356,8 +2371,8 @@ TEST_F(CompositedLayerMappingTest, GraphicsLayer* target_graphics_layer = target_layer ? target_layer->GraphicsLayerBacking() : nullptr; ASSERT_TRUE(target_graphics_layer); - EXPECT_FALSE(target_graphics_layer->ContentLayer() - ->transformed_rasterization_allowed()); + EXPECT_FALSE( + target_graphics_layer->CcLayer()->transformed_rasterization_allowed()); } { LayoutObject* target = GetLayoutObjectByElementId("target3"); @@ -2366,8 +2381,8 @@ TEST_F(CompositedLayerMappingTest, GraphicsLayer* target_graphics_layer = target_layer ? target_layer->GraphicsLayerBacking() : nullptr; ASSERT_TRUE(target_graphics_layer); - EXPECT_FALSE(target_graphics_layer->ContentLayer() - ->transformed_rasterization_allowed()); + EXPECT_FALSE( + target_graphics_layer->CcLayer()->transformed_rasterization_allowed()); } } @@ -2389,14 +2404,15 @@ TEST_F(CompositedLayerMappingTest, TransformedRasterizationForInlineTransform) { GraphicsLayer* target_graphics_layer = target_layer ? target_layer->GraphicsLayerBacking() : nullptr; ASSERT_TRUE(target_graphics_layer); - EXPECT_TRUE(target_graphics_layer->ContentLayer() - ->transformed_rasterization_allowed()); + EXPECT_TRUE( + target_graphics_layer->CcLayer()->transformed_rasterization_allowed()); } // This tests that when the scroller becomes no longer scrollable if a sticky // element is promoted for another reason we do remove its composited sticky // constraint as it doesn't need to move on the compositor. -TEST_F(CompositedLayerMappingTest, CompositedStickyConstraintRemovedAndAdded) { +TEST_F(CompositedLayerMappingTestWithoutBGPT, + CompositedStickyConstraintRemovedAndAdded) { SetBodyInnerHTML(R"HTML( <style> .scroller { overflow: auto; height: 200px; } @@ -2408,7 +2424,7 @@ TEST_F(CompositedLayerMappingTest, CompositedStickyConstraintRemovedAndAdded) { <div id='spacer' style='height: 2000px;'></div> </div> )HTML"); - GetDocument().View()->UpdateAllLifecyclePhases(); + UpdateAllLifecyclePhasesForTest(); PaintLayer* sticky_layer = ToLayoutBoxModelObject(GetLayoutObjectByElementId("sticky"))->Layer(); EXPECT_TRUE(sticky_layer->GraphicsLayerBacking() @@ -2417,9 +2433,9 @@ TEST_F(CompositedLayerMappingTest, CompositedStickyConstraintRemovedAndAdded) { .is_sticky); // Make the scroller no longer scrollable. - GetDocument().getElementById("spacer")->setAttribute(HTMLNames::styleAttr, + GetDocument().getElementById("spacer")->setAttribute(html_names::kStyleAttr, "height: 0;"); - GetDocument().View()->UpdateAllLifecyclePhases(); + UpdateAllLifecyclePhasesForTest(); // The sticky position element is composited due to a compositing trigger but // should no longer have a sticky position constraint on the compositor. @@ -2431,9 +2447,9 @@ TEST_F(CompositedLayerMappingTest, CompositedStickyConstraintRemovedAndAdded) { .is_sticky); // Make the scroller scrollable again. - GetDocument().getElementById("spacer")->setAttribute(HTMLNames::styleAttr, + GetDocument().getElementById("spacer")->setAttribute(html_names::kStyleAttr, "height: 2000px;"); - GetDocument().View()->UpdateAllLifecyclePhases(); + UpdateAllLifecyclePhasesForTest(); sticky_layer = ToLayoutBoxModelObject(GetLayoutObjectByElementId("sticky"))->Layer(); @@ -2463,7 +2479,7 @@ TEST_F(CompositedLayerMappingTest, ScrollingContainerBoundsChange) { </div )HTML"); - GetDocument().View()->UpdateAllLifecyclePhases(); + UpdateAllLifecyclePhasesForTest(); Element* scrollerElement = GetDocument().getElementById("scroller"); LayoutBoxModelObject* scroller = ToLayoutBoxModelObject(GetLayoutObjectByElementId("scroller")); @@ -2475,21 +2491,21 @@ TEST_F(CompositedLayerMappingTest, ScrollingContainerBoundsChange) { EXPECT_EQ(100, scrolling_layer->scroll_container_bounds().height()); scrollerElement->setScrollTop(300); - scrollerElement->setAttribute(HTMLNames::styleAttr, "max-height: 25px;"); - GetDocument().View()->UpdateAllLifecyclePhases(); + scrollerElement->setAttribute(html_names::kStyleAttr, "max-height: 25px;"); + UpdateAllLifecyclePhasesForTest(); EXPECT_EQ(50, scrolling_layer->CurrentScrollOffset().y()); EXPECT_EQ(150, scrolling_layer->bounds().height()); EXPECT_EQ(25, scrolling_layer->scroll_container_bounds().height()); - scrollerElement->setAttribute(HTMLNames::styleAttr, "max-height: 300px;"); - GetDocument().View()->UpdateAllLifecyclePhases(); + scrollerElement->setAttribute(html_names::kStyleAttr, "max-height: 300px;"); + UpdateAllLifecyclePhasesForTest(); EXPECT_EQ(50, scrolling_layer->CurrentScrollOffset().y()); EXPECT_EQ(150, scrolling_layer->bounds().height()); EXPECT_EQ(100, scrolling_layer->scroll_container_bounds().height()); } TEST_F(CompositedLayerMappingTest, MainFrameLayerBackgroundColor) { - GetDocument().View()->UpdateAllLifecyclePhases(); + UpdateAllLifecyclePhasesForTest(); EXPECT_EQ(Color::kWhite, GetDocument().View()->BaseBackgroundColor()); auto* view_layer = GetDocument().GetLayoutView()->Layer()->GraphicsLayerBacking(); @@ -2497,9 +2513,9 @@ TEST_F(CompositedLayerMappingTest, MainFrameLayerBackgroundColor) { Color base_background(255, 0, 0); GetDocument().View()->SetBaseBackgroundColor(base_background); - GetDocument().body()->setAttribute(HTMLNames::styleAttr, + GetDocument().body()->setAttribute(html_names::kStyleAttr, "background: rgba(0, 255, 0, 0.5)"); - GetDocument().View()->UpdateAllLifecyclePhases(); + UpdateAllLifecyclePhasesForTest(); EXPECT_EQ(base_background, GetDocument().View()->BaseBackgroundColor()); EXPECT_EQ(Color(127, 128, 0, 255), view_layer->BackgroundColor()); } @@ -2524,8 +2540,8 @@ TEST_F(CompositedLayerMappingTest, ScrollingLayerBackgroundColor) { EXPECT_EQ(Color::kTransparent, graphics_layer->BackgroundColor()); EXPECT_EQ(Color::kTransparent, scrolling_contents_layer->BackgroundColor()); - target->setAttribute(HTMLNames::classAttr, "color"); - GetDocument().View()->UpdateAllLifecyclePhases(); + target->setAttribute(html_names::kClassAttr, "color"); + UpdateAllLifecyclePhasesForTest(); EXPECT_EQ(Color(0, 0, 255), graphics_layer->BackgroundColor()); EXPECT_EQ(Color(0, 0, 255), scrolling_contents_layer->BackgroundColor()); } @@ -2545,7 +2561,7 @@ TEST_F(CompositedLayerMappingTest, ClipPathNoChildContainmentLayer) { ASSERT_FALSE(mapping->ClippingLayer()); } -TEST_F(CompositedLayerMappingTest, ForegroundLayerSizing) { +TEST_F(CompositedLayerMappingTestWithoutBGPT, ForegroundLayerSizing) { // This test verifies the foreground layer is sized to the clip rect. SetBodyInnerHTML(R"HTML( <div id='target' style='position:relative; z-index:0; width:100px; @@ -2600,7 +2616,7 @@ TEST_F(CompositedLayerMappingTest, ScrollLayerSizingSubpixelAccumulation) { <div id="space"></div> </div> )HTML"); - GetDocument().View()->UpdateAllLifecyclePhases(); + UpdateAllLifecyclePhasesForTest(); auto* mapping = ToLayoutBoxModelObject(GetLayoutObjectByElementId("scroller")) ->Layer() ->GetCompositedLayerMapping(); @@ -2634,7 +2650,7 @@ TEST_F(CompositedLayerMappingTest, SquashingScroll) { GetDocument().View()->LayoutViewport()->ScrollBy(ScrollOffset(0, 25), kUserScroll); - GetDocument().View()->UpdateAllLifecyclePhases(); + UpdateAllLifecyclePhasesForTest(); EXPECT_EQ( LayoutPoint(), @@ -2660,7 +2676,7 @@ TEST_F(CompositedLayerMappingTest, SquashingScrollInterestRect) { GetDocument().View()->LayoutViewport()->ScrollBy(ScrollOffset(0, 5000), kUserScroll); - GetDocument().View()->UpdateAllLifecyclePhases(); + UpdateAllLifecyclePhasesForTest(); EXPECT_EQ(IntRect(0, 1000, 200, 5000), squashed->GroupedMapping()->SquashingLayer()->InterestRect()); @@ -2696,49 +2712,60 @@ transform'></div> scroller_element->setScrollTop(300); - GetDocument().View()->UpdateAllLifecyclePhases(); + UpdateAllLifecyclePhasesForTest(); // 100px down from squashing's main graphics layer. EXPECT_EQ(FloatPoint(0, 100), squashed->GraphicsLayerBacking()->GetPosition()); } -TEST_F(CompositedLayerMappingTest, ImageWithInvertFilterLayer) { - SetBodyInnerHTML("<img id='image' style='will-change: transform;' src='x'>"); - ToLayoutImage(GetLayoutObjectByElementId("image")) - ->UpdateShouldInvertColorForTest(true); - GetDocument().View()->UpdateAllLifecyclePhases(); - cc::FilterOperations filters; - filters.Append(cc::FilterOperation::CreateInvertFilter(1.0f)); - EXPECT_EQ(filters, ToLayoutBoxModelObject(GetLayoutObjectByElementId("image")) - ->Layer() - ->GraphicsLayerBacking() - ->CcLayer() - ->filters()); -} - -TEST_F(CompositedLayerMappingTest, ImageWithInvertFilterLayerUpdated) { - SetBodyInnerHTML("<img id='image' style='will-change: transform;' src='x'>"); - ToLayoutImage(GetLayoutObjectByElementId("image")) - ->UpdateShouldInvertColorForTest(true); - GetDocument().View()->UpdateAllLifecyclePhases(); - cc::FilterOperations filters0, filters1; - filters0.Append(cc::FilterOperation::CreateInvertFilter(1.0f)); - EXPECT_EQ(filters0, - ToLayoutBoxModelObject(GetLayoutObjectByElementId("image")) - ->Layer() - ->GraphicsLayerBacking() - ->CcLayer() - ->filters()); - ToLayoutImage(GetLayoutObjectByElementId("image")) - ->UpdateShouldInvertColorForTest(false); - GetDocument().View()->UpdateAllLifecyclePhases(); - EXPECT_EQ(filters1, - ToLayoutBoxModelObject(GetLayoutObjectByElementId("image")) - ->Layer() - ->GraphicsLayerBacking() - ->CcLayer() - ->filters()); +TEST_F(CompositedLayerMappingTest, ContentsNotOpaqueWithForegroundLayer) { + if (RuntimeEnabledFeatures::SlimmingPaintV2Enabled()) + return; + + SetHtmlInnerHTML(R"HTML( + <style> + div { + width: 100px; + height: 100px; + position: relative; + isolation: isolate; + } + </style> + <div id='target' style='will-change: transform'> + <div style='background: blue; z-index: -1; will-change: transform'></div> + <div style='background: blue'></div> + </div> + )HTML"); + PaintLayer* target_layer = + ToLayoutBoxModelObject(GetLayoutObjectByElementId("target"))->Layer(); + CompositedLayerMapping* mapping = target_layer->GetCompositedLayerMapping(); + EXPECT_TRUE(mapping->ForegroundLayer()); + EXPECT_FALSE(mapping->MainGraphicsLayer()->ContentsOpaque()); +} + +TEST_F(CompositedLayerMappingTest, ContentsOpaque) { + if (RuntimeEnabledFeatures::SlimmingPaintV2Enabled()) + return; + + SetHtmlInnerHTML(R"HTML( + <style> + div { + width: 100px; + height: 100px; + position: relative; + isolation: isolate; + } + </style> + <div id='target' style='will-change: transform'> + <div style='background: blue'></div> + </div> + )HTML"); + PaintLayer* target_layer = + ToLayoutBoxModelObject(GetLayoutObjectByElementId("target"))->Layer(); + CompositedLayerMapping* mapping = target_layer->GetCompositedLayerMapping(); + EXPECT_FALSE(mapping->ForegroundLayer()); + EXPECT_TRUE(mapping->MainGraphicsLayer()->ContentsOpaque()); } } // namespace blink diff --git a/chromium/third_party/blink/renderer/core/paint/compositing/compositing_inputs_updater.cc b/chromium/third_party/blink/renderer/core/paint/compositing/compositing_inputs_updater.cc index 60fcf54f708..0c7bdf68294 100644 --- a/chromium/third_party/blink/renderer/core/paint/compositing/compositing_inputs_updater.cc +++ b/chromium/third_party/blink/renderer/core/paint/compositing/compositing_inputs_updater.cc @@ -238,6 +238,9 @@ void CompositingInputsUpdater::UpdateRecursive(PaintLayer* layer, layer->DirectCompositingReasons()); } + if (layer->GetLayoutObject().IsVideo()) + info.is_under_video = true; + bool should_recurse = layer->ChildNeedsCompositingInputsUpdate() || update_type == kForceUpdate; @@ -289,46 +292,44 @@ void CompositingInputsUpdater::UpdateAncestorDependentCompositingInputs( PaintLayer::AncestorDependentCompositingInputs properties; LayoutBoxModelObject& layout_object = layer->GetLayoutObject(); - if (!RuntimeEnabledFeatures::SlimmingPaintV2Enabled()) { - // The final value for |unclipped_absolute_bounding_box| needs to be - // in absolute, unscrolled space, without any scroll applied. - properties.unclipped_absolute_bounding_box = - EnclosingIntRect(geometry_map_.AbsoluteRect( - FloatRect(layer->BoundingBoxForCompositingOverlapTest()))); - - bool affected_by_scroll = root_layer_->GetScrollableArea() && - layer->IsAffectedByScrollOf(root_layer_); - - // At ths point, |unclipped_absolute_bounding_box| is in viewport space. - // To convert to absolute space, add scroll offset for non-fixed layers. - if (affected_by_scroll) { - properties.unclipped_absolute_bounding_box.Move( - RoundedIntSize(root_layer_->GetScrollableArea()->GetScrollOffset())); - } + // The final value for |unclipped_absolute_bounding_box| needs to be + // in absolute, unscrolled space, without any scroll applied. + properties.unclipped_absolute_bounding_box = + EnclosingIntRect(geometry_map_.AbsoluteRect( + FloatRect(layer->BoundingBoxForCompositingOverlapTest()))); - ClipRect clip_rect; - layer->Clipper(PaintLayer::kDoNotUseGeometryMapper) - .CalculateBackgroundClipRect( - ClipRectsContext(root_layer_, - &root_layer_->GetLayoutObject().FirstFragment(), - kAbsoluteClipRectsIgnoringViewportClip, - kIgnorePlatformOverlayScrollbarSize, - kIgnoreOverflowClipAndScroll), - clip_rect); - IntRect snapped_clip_rect = PixelSnappedIntRect(clip_rect.Rect()); - // |snapped_clip_rect| is in absolute space space, but with scroll applied. - // To convert to absolute, unscrolled space, subtract scroll offsets for - // fixed layers. - if (root_layer_->GetScrollableArea() && !affected_by_scroll) { - snapped_clip_rect.Move( - RoundedIntSize(-root_layer_->GetScrollableArea()->GetScrollOffset())); - } + bool affected_by_scroll = root_layer_->GetScrollableArea() && + layer->IsAffectedByScrollOf(root_layer_); - properties.clipped_absolute_bounding_box = - properties.unclipped_absolute_bounding_box; - properties.clipped_absolute_bounding_box.Intersect(snapped_clip_rect); + // At ths point, |unclipped_absolute_bounding_box| is in viewport space. + // To convert to absolute space, add scroll offset for non-fixed layers. + if (affected_by_scroll) { + properties.unclipped_absolute_bounding_box.Move( + RoundedIntSize(root_layer_->GetScrollableArea()->GetScrollOffset())); } + ClipRect clip_rect; + layer->Clipper(PaintLayer::kDoNotUseGeometryMapper) + .CalculateBackgroundClipRect( + ClipRectsContext(root_layer_, + &root_layer_->GetLayoutObject().FirstFragment(), + kAbsoluteClipRectsIgnoringViewportClip, + kIgnorePlatformOverlayScrollbarSize, + kIgnoreOverflowClipAndScroll), + clip_rect); + IntRect snapped_clip_rect = PixelSnappedIntRect(clip_rect.Rect()); + // |snapped_clip_rect| is in absolute space space, but with scroll applied. + // To convert to absolute, unscrolled space, subtract scroll offsets for + // fixed layers. + if (root_layer_->GetScrollableArea() && !affected_by_scroll) { + snapped_clip_rect.Move( + RoundedIntSize(-root_layer_->GetScrollableArea()->GetScrollOffset())); + } + + properties.clipped_absolute_bounding_box = + properties.unclipped_absolute_bounding_box; + properties.clipped_absolute_bounding_box.Intersect(snapped_clip_rect); + const PaintLayer* parent = layer->Parent(); properties.opacity_ancestor = parent->IsTransparent() ? parent : parent->OpacityAncestor(); @@ -360,6 +361,8 @@ void CompositingInputsUpdater::UpdateAncestorDependentCompositingInputs( if (info.needs_reparent_scroll && layout_object.StyleRef().IsStacked()) properties.scroll_parent = info.scrolling_ancestor; + properties.is_under_video = info.is_under_video; + layer->UpdateAncestorDependentCompositingInputs(properties); } diff --git a/chromium/third_party/blink/renderer/core/paint/compositing/compositing_inputs_updater.h b/chromium/third_party/blink/renderer/core/paint/compositing/compositing_inputs_updater.h index 5303a1e9cc2..823f1569c12 100644 --- a/chromium/third_party/blink/renderer/core/paint/compositing/compositing_inputs_updater.h +++ b/chromium/third_party/blink/renderer/core/paint/compositing/compositing_inputs_updater.h @@ -61,6 +61,8 @@ class CompositingInputsUpdater { bool needs_reparent_scroll = false; bool needs_reparent_scroll_for_absolute = false; bool needs_reparent_scroll_for_fixed = false; + + bool is_under_video = false; }; void UpdateRecursive(PaintLayer*, UpdateType, AncestorInfo); diff --git a/chromium/third_party/blink/renderer/core/paint/compositing/compositing_inputs_updater_test.cc b/chromium/third_party/blink/renderer/core/paint/compositing/compositing_inputs_updater_test.cc index c979b7949e5..0f23c68dc3d 100644 --- a/chromium/third_party/blink/renderer/core/paint/compositing/compositing_inputs_updater_test.cc +++ b/chromium/third_party/blink/renderer/core/paint/compositing/compositing_inputs_updater_test.cc @@ -73,7 +73,7 @@ TEST_F(CompositingInputsUpdaterTest, EXPECT_FALSE(sticky->Layer()->AncestorOverflowLayer()->GetScrollableArea()); EXPECT_EQ(sticky->Layer()->AncestorOverflowLayer(), outer_scroller->Layer()); - GetDocument().View()->UpdateAllLifecyclePhases(); + UpdateAllLifecyclePhasesForTest(); // Both scrollers must still have a layer. EXPECT_TRUE(outer_scroller->Layer()); @@ -106,7 +106,7 @@ TEST_F(CompositingInputsUpdaterTest, UnclippedAndClippedRectsUnderScroll) { ->GetLayoutView() ->Layer() ->SetNeedsCompositingInputsUpdate(); - GetDocument().View()->UpdateAllLifecyclePhases(); + UpdateAllLifecyclePhasesForTest(); EXPECT_EQ(IntRect(8, 8, 200, 200), target->Layer()->ClippedAbsoluteBoundingBox()); EXPECT_EQ(IntRect(8, 8, 200, 200), @@ -133,7 +133,7 @@ TEST_F(CompositingInputsUpdaterTest, ->Layer() ->SetNeedsCompositingInputsUpdate(); - GetDocument().View()->UpdateAllLifecyclePhases(); + UpdateAllLifecyclePhasesForTest(); EXPECT_EQ(IntRect(8, 8, 200, 200), target->Layer()->ClippedAbsoluteBoundingBox()); EXPECT_EQ(IntRect(8, 8, 200, 200), diff --git a/chromium/third_party/blink/renderer/core/paint/compositing/compositing_layer_assigner.cc b/chromium/third_party/blink/renderer/core/paint/compositing/compositing_layer_assigner.cc index 1e011f6df64..a4e6f781c8d 100644 --- a/chromium/third_party/blink/renderer/core/paint/compositing/compositing_layer_assigner.cc +++ b/chromium/third_party/blink/renderer/core/paint/compositing/compositing_layer_assigner.cc @@ -135,15 +135,10 @@ CompositingLayerAssigner::GetReasonsPreventingSquashing( const PaintLayer& squashing_layer = squashing_state.most_recent_mapping->OwningLayer(); - // FIXME: this special case for video exists only to deal with corner cases - // where a LayoutVideo does not report that it needs to be directly - // composited. Video does not currently support sharing a backing, but this - // could be generalized in the future. The following layout tests fail if we - // permit the video to share a backing with other layers. - // - // compositing/video/video-controls-layer-creation.html - if (layer->GetLayoutObject().IsVideo() || - squashing_layer.GetLayoutObject().IsVideo()) + // Don't squash into or out of any thing underneath a video, including the + // user-agent shadow DOM for controls. This is is to work around a + // bug involving overflow clip of videos. See crbug.com/900602. + if (layer->IsUnderVideo() || squashing_layer.IsUnderVideo()) return SquashingDisallowedReason::kSquashingVideoIsDisallowed; // Don't squash iframes, frames or plugins. @@ -258,7 +253,8 @@ void CompositingLayerAssigner::UpdateSquashingAssignment( // Issue a paint invalidation, since |layer| may have been added to an // already-existing squashing layer. TRACE_LAYER_INVALIDATION( - layer, InspectorLayerInvalidationTrackingEvent::kAddedToSquashingLayer); + layer, + inspector_layer_invalidation_tracking_event::kAddedToSquashingLayer); layers_needing_paint_invalidation.push_back(layer); layers_changed_ = true; } else if (composited_layer_update == kRemoveFromSquashingLayer) { @@ -274,9 +270,9 @@ void CompositingLayerAssigner::UpdateSquashingAssignment( // If we need to issue paint invalidations, do so now that we've removed it // from a squashed layer. - TRACE_LAYER_INVALIDATION( - layer, - InspectorLayerInvalidationTrackingEvent::kRemovedFromSquashingLayer); + TRACE_LAYER_INVALIDATION(layer, + inspector_layer_invalidation_tracking_event:: + kRemovedFromSquashingLayer); layers_needing_paint_invalidation.push_back(layer); layers_changed_ = true; @@ -308,7 +304,8 @@ void CompositingLayerAssigner::AssignLayersToBackingsInternal( if (compositor_->AllocateOrClearCompositedLayerMapping( layer, composited_layer_update)) { TRACE_LAYER_INVALIDATION( - layer, InspectorLayerInvalidationTrackingEvent::kNewCompositedLayer); + layer, + inspector_layer_invalidation_tracking_event::kNewCompositedLayer); layers_needing_paint_invalidation.push_back(layer); layers_changed_ = true; if (ScrollingCoordinator* scrolling_coordinator = diff --git a/chromium/third_party/blink/renderer/core/paint/compositing/compositing_layer_property_updater.cc b/chromium/third_party/blink/renderer/core/paint/compositing/compositing_layer_property_updater.cc index 4c46a29c8de..d8f7858836e 100644 --- a/chromium/third_party/blink/renderer/core/paint/compositing/compositing_layer_property_updater.cc +++ b/chromium/third_party/blink/renderer/core/paint/compositing/compositing_layer_property_updater.cc @@ -66,10 +66,15 @@ void CompositingLayerPropertyUpdater::Update(const LayoutObject& object) { if (graphics_layer) { if (!container_layer_state) { container_layer_state = fragment_data.LocalBorderBoxProperties(); - if (const auto* properties = fragment_data.PaintProperties()) { - // CSS clip should be applied within the layer. - if (const auto* css_clip = properties->CssClip()) - container_layer_state->SetClip(css_clip->Parent()); + // Before BlinkGenPropertyTrees, CSS clip could not be composited so + // we should avoid setting it on the layer itself. + if (!RuntimeEnabledFeatures::BlinkGenPropertyTreesEnabled() && + !RuntimeEnabledFeatures::SlimmingPaintV2Enabled()) { + if (const auto* properties = fragment_data.PaintProperties()) { + if (const auto* css_clip = properties->CssClip()) { + container_layer_state->SetClip(css_clip->Parent()); + } + } } } graphics_layer->SetLayerState( diff --git a/chromium/third_party/blink/renderer/core/paint/compositing/compositing_reason_finder.cc b/chromium/third_party/blink/renderer/core/paint/compositing/compositing_reason_finder.cc index 877168d3999..bc5808dac28 100644 --- a/chromium/third_party/blink/renderer/core/paint/compositing/compositing_reason_finder.cc +++ b/chromium/third_party/blink/renderer/core/paint/compositing/compositing_reason_finder.cc @@ -5,7 +5,7 @@ #include "third_party/blink/renderer/core/paint/compositing/compositing_reason_finder.h" #include "third_party/blink/renderer/core/animation/scroll_timeline.h" -#include "third_party/blink/renderer/core/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/frame/local_frame_view.h" #include "third_party/blink/renderer/core/frame/settings.h" @@ -94,9 +94,6 @@ CompositingReasonFinder::PotentialCompositingReasonsFromStyle( !style.SubtreeWillChangeContents()) reasons |= CompositingReason::kWillChangeCompositingHint; - if (style.HasInlineTransform()) - reasons |= CompositingReason::kInlineTransform; - if (style.UsedTransformStyle3D() == ETransformStyle3D::kPreserve3d) reasons |= CompositingReason::kPreserve3DWith3DDescendants; @@ -257,7 +254,8 @@ bool CompositingReasonFinder::RequiresCompositingForRootScroller( const auto& settings = *layer.GetLayoutObject().GetDocument().GetSettings(); if (!settings.GetAcceleratedCompositingEnabled()) return false; - return RootScrollerUtil::IsGlobal(layer); + + return layer.GetLayoutObject().IsGlobalRootScroller(); } bool CompositingReasonFinder::RequiresCompositingForScrollDependentPosition( @@ -271,7 +269,7 @@ bool CompositingReasonFinder::RequiresCompositingForScrollDependentPosition( (compositing_triggers_ & kViewportConstrainedPositionedTrigger)) && (!RuntimeEnabledFeatures::CompositeOpaqueFixedPositionEnabled() || !layer->BackgroundIsKnownToBeOpaqueInRect( - LayoutRect(layer->BoundingBoxForCompositing())) || + LayoutRect(layer->BoundingBoxForCompositing()), true) || layer->CompositesWithTransform() || layer->CompositesWithOpacity())) { return false; } diff --git a/chromium/third_party/blink/renderer/core/paint/compositing/compositing_reason_finder_test.cc b/chromium/third_party/blink/renderer/core/paint/compositing/compositing_reason_finder_test.cc index e02efa25f26..3dd6e2789a9 100644 --- a/chromium/third_party/blink/renderer/core/paint/compositing/compositing_reason_finder_test.cc +++ b/chromium/third_party/blink/renderer/core/paint/compositing/compositing_reason_finder_test.cc @@ -205,23 +205,23 @@ TEST_F(CompositingReasonFinderTest, OnlyNonTransformedFixedLayersPromoted) { EXPECT_TRUE(paint_layer->GraphicsLayerBacking()->ContentsOpaque()); // Change the parent to have a transform. - parent->setAttribute(HTMLNames::styleAttr, "transform: translate(1px, 0);"); - GetDocument().View()->UpdateAllLifecyclePhases(); + parent->setAttribute(html_names::kStyleAttr, "transform: translate(1px, 0);"); + UpdateAllLifecyclePhasesForTest(); paint_layer = ToLayoutBoxModelObject(fixed->GetLayoutObject())->Layer(); ASSERT_TRUE(paint_layer); EXPECT_EQ(kNotComposited, paint_layer->GetCompositingState()); // Change the parent to have no transform again. - parent->removeAttribute(HTMLNames::styleAttr); - GetDocument().View()->UpdateAllLifecyclePhases(); + parent->removeAttribute(html_names::kStyleAttr); + UpdateAllLifecyclePhasesForTest(); paint_layer = ToLayoutBoxModelObject(fixed->GetLayoutObject())->Layer(); ASSERT_TRUE(paint_layer); EXPECT_EQ(kPaintsIntoOwnBacking, paint_layer->GetCompositingState()); EXPECT_TRUE(paint_layer->GraphicsLayerBacking()->ContentsOpaque()); // Apply a transform to the fixed directly. - fixed->setAttribute(HTMLNames::styleAttr, "transform: translate(1px, 0);"); - GetDocument().View()->UpdateAllLifecyclePhases(); + fixed->setAttribute(html_names::kStyleAttr, "transform: translate(1px, 0);"); + UpdateAllLifecyclePhasesForTest(); paint_layer = ToLayoutBoxModelObject(fixed->GetLayoutObject())->Layer(); ASSERT_TRUE(paint_layer); EXPECT_EQ(kNotComposited, paint_layer->GetCompositingState()); @@ -253,23 +253,23 @@ TEST_F(CompositingReasonFinderTest, OnlyOpaqueFixedLayersPromoted) { EXPECT_TRUE(paint_layer->GraphicsLayerBacking()->ContentsOpaque()); // Change the parent to be partially translucent. - parent->setAttribute(HTMLNames::styleAttr, "opacity: 0.5;"); - GetDocument().View()->UpdateAllLifecyclePhases(); + parent->setAttribute(html_names::kStyleAttr, "opacity: 0.5;"); + UpdateAllLifecyclePhasesForTest(); paint_layer = ToLayoutBoxModelObject(fixed->GetLayoutObject())->Layer(); ASSERT_TRUE(paint_layer); EXPECT_EQ(kNotComposited, paint_layer->GetCompositingState()); // Change the parent to be opaque again. - parent->setAttribute(HTMLNames::styleAttr, "opacity: 1;"); - GetDocument().View()->UpdateAllLifecyclePhases(); + parent->setAttribute(html_names::kStyleAttr, "opacity: 1;"); + UpdateAllLifecyclePhasesForTest(); paint_layer = ToLayoutBoxModelObject(fixed->GetLayoutObject())->Layer(); ASSERT_TRUE(paint_layer); EXPECT_EQ(kPaintsIntoOwnBacking, paint_layer->GetCompositingState()); EXPECT_TRUE(paint_layer->GraphicsLayerBacking()->ContentsOpaque()); // Make the fixed translucent. - fixed->setAttribute(HTMLNames::styleAttr, "opacity: 0.5"); - GetDocument().View()->UpdateAllLifecyclePhases(); + fixed->setAttribute(html_names::kStyleAttr, "opacity: 0.5"); + UpdateAllLifecyclePhasesForTest(); paint_layer = ToLayoutBoxModelObject(fixed->GetLayoutObject())->Layer(); ASSERT_TRUE(paint_layer); EXPECT_EQ(kNotComposited, paint_layer->GetCompositingState()); @@ -402,7 +402,7 @@ TEST_F(CompositingReasonFinderTest, DontPromoteEmptyIframe) { <!DOCTYPE html> <iframe style="width:0; height:0; border: 0;" srcdoc="<!DOCTYPE html>"></iframe> )HTML"); - GetDocument().View()->UpdateAllLifecyclePhases(); + UpdateAllLifecyclePhasesForTest(); LocalFrame* child_frame = ToLocalFrame(GetDocument().GetFrame()->Tree().FirstChild()); diff --git a/chromium/third_party/blink/renderer/core/paint/compositing/compositing_requirements_updater.cc b/chromium/third_party/blink/renderer/core/paint/compositing/compositing_requirements_updater.cc index a861ecdfa27..1fa0a141872 100644 --- a/chromium/third_party/blink/renderer/core/paint/compositing/compositing_requirements_updater.cc +++ b/chromium/third_party/blink/renderer/core/paint/compositing/compositing_requirements_updater.cc @@ -272,11 +272,18 @@ void CompositingRequirementsUpdater::UpdateRecursive( CompositingReasons reasons_to_composite = CompositingReason::kNone; CompositingReasons direct_reasons = CompositingReason::kNone; - bool has_non_root_composited_scrolling_ancestor = - layer->AncestorScrollingLayer() && - layer->AncestorScrollingLayer()->GetScrollableArea() && - layer->AncestorScrollingLayer()->NeedsCompositedScrolling() && - !layer->AncestorScrollingLayer()->IsRootLayer(); + bool has_non_root_composited_scrolling_ancestor = false; + const PaintLayer* ancestor_scrolling_layer = layer->AncestorScrollingLayer(); + while (ancestor_scrolling_layer && + ancestor_scrolling_layer->GetScrollableArea()) { + if (ancestor_scrolling_layer->NeedsCompositedScrolling() && + !ancestor_scrolling_layer->IsRootLayer()) { + has_non_root_composited_scrolling_ancestor = true; + break; + } + ancestor_scrolling_layer = + ancestor_scrolling_layer->AncestorScrollingLayer(); + } bool use_clipped_bounding_rect = !has_non_root_composited_scrolling_ancestor; @@ -601,11 +608,6 @@ void CompositingRequirementsUpdater::UpdateRecursive( will_be_composited_or_squashed = true; } - if (will_be_composited_or_squashed) { - reasons_to_composite |= layer->PotentialCompositingReasonsFromStyle() & - CompositingReason::kInlineTransform; - } - if (will_be_composited_or_squashed && layer->GetLayoutObject().StyleRef().HasBlendMode()) { current_recursion_data.has_unisolated_composited_blending_descendant_ = @@ -624,12 +626,9 @@ void CompositingRequirementsUpdater::UpdateRecursive( bool is_composited_clipping_layer = can_be_composited && (reasons_to_composite & CompositingReason::kClipsCompositingDescendants); - bool is_composited_with_inline_transform = - reasons_to_composite & CompositingReason::kInlineTransform; if ((!child_recursion_data.testing_overlap_ && !is_composited_clipping_layer) || - layer->GetLayoutObject().StyleRef().HasCurrentTransformAnimation() || - is_composited_with_inline_transform) + layer->GetLayoutObject().StyleRef().HasCurrentTransformAnimation()) current_recursion_data.testing_overlap_ = false; if (child_recursion_data.compositing_ancestor_ == layer) diff --git a/chromium/third_party/blink/renderer/core/paint/compositing/compositing_requirements_updater_test.cc b/chromium/third_party/blink/renderer/core/paint/compositing/compositing_requirements_updater_test.cc index db1715411de..5d3bedb33f0 100644 --- a/chromium/third_party/blink/renderer/core/paint/compositing/compositing_requirements_updater_test.cc +++ b/chromium/third_party/blink/renderer/core/paint/compositing/compositing_requirements_updater_test.cc @@ -42,7 +42,7 @@ TEST_F(CompositingRequirementsUpdaterTest, FixedPosOverlap) { GetDocument().View()->LayoutViewport()->ScrollBy(ScrollOffset(0, 100), kUserScroll); - GetDocument().View()->UpdateAllLifecyclePhases(); + UpdateAllLifecyclePhasesForTest(); // No longer overlaps the first div. EXPECT_EQ(CompositingReason::kNone, fixed->Layer()->GetCompositingReasons()); @@ -69,41 +69,14 @@ TEST_F(CompositingRequirementsUpdaterTest, EXPECT_FALSE(target->GetCompositingReasons()); // Now make |target| self-painting. - GetDocument().getElementById("target")->setAttribute(HTMLNames::styleAttr, + GetDocument().getElementById("target")->setAttribute(html_names::kStyleAttr, "position: relative"); - GetDocument().View()->UpdateAllLifecyclePhases(); + UpdateAllLifecyclePhasesForTest(); EXPECT_EQ(CompositingReason::kOverlap, target->GetCompositingReasons()); } TEST_F(CompositingRequirementsUpdaterTest, - NoAssumedOverlapReasonForNonSelfPaintingLayer) { - SetBodyInnerHTML(R"HTML( - <style> - #target { - overflow: auto; - width: 100px; - height: 100px; - } - </style> - <div style="position: relative; width: 500px; height: 300px; - transform: translateZ(0)"></div> - <div id=target></div> - )HTML"); - - PaintLayer* target = - ToLayoutBoxModelObject(GetLayoutObjectByElementId("target"))->Layer(); - EXPECT_FALSE(target->GetCompositingReasons()); - - // Now make |target| self-painting. - GetDocument().getElementById("target")->setAttribute(HTMLNames::styleAttr, - "position: relative"); - GetDocument().View()->UpdateAllLifecyclePhases(); - EXPECT_EQ(CompositingReason::kAssumedOverlap, - target->GetCompositingReasons()); -} - -TEST_F(CompositingRequirementsUpdaterTest, NoDescendantReasonForNonSelfPaintingLayer) { SetBodyInnerHTML(R"HTML( <style> @@ -123,9 +96,9 @@ TEST_F(CompositingRequirementsUpdaterTest, EXPECT_FALSE(target->GetCompositingReasons()); // Now make |target| self-painting. - GetDocument().getElementById("target")->setAttribute(HTMLNames::styleAttr, + GetDocument().getElementById("target")->setAttribute(html_names::kStyleAttr, "position: relative"); - GetDocument().View()->UpdateAllLifecyclePhases(); + UpdateAllLifecyclePhasesForTest(); EXPECT_EQ(CompositingReason::kClipsCompositingDescendants, target->GetCompositingReasons()); } @@ -160,9 +133,9 @@ TEST_F(CompositingRequirementsUpdaterTest, GetDocument().View()->SetTracksPaintInvalidations(true); - GetDocument().getElementById("target")->setAttribute(HTMLNames::styleAttr, + GetDocument().getElementById("target")->setAttribute(html_names::kStyleAttr, "display: none"); - GetDocument().View()->UpdateAllLifecyclePhases(); + UpdateAllLifecyclePhasesForTest(); EXPECT_EQ(kNotComposited, squashed->GetCompositingState()); auto* tracking = GetDocument() diff --git a/chromium/third_party/blink/renderer/core/paint/compositing/paint_layer_compositor_test.cc b/chromium/third_party/blink/renderer/core/paint/compositing/paint_layer_compositor_test.cc index 936bc1c905f..993d3993c13 100644 --- a/chromium/third_party/blink/renderer/core/paint/compositing/paint_layer_compositor_test.cc +++ b/chromium/third_party/blink/renderer/core/paint/compositing/paint_layer_compositor_test.cc @@ -73,7 +73,7 @@ TEST_F(PaintLayerCompositorTest, // given animations separated only by a lifecycle update to // CompositingInputsClean, they should both be started in the same lifecycle // and as such grouped together. - GetDocument().View()->UpdateAllLifecyclePhases(); + UpdateAllLifecyclePhasesForTest(); EXPECT_EQ(DocumentLifecycle::kPaintClean, GetDocument().Lifecycle().GetState()); @@ -104,8 +104,8 @@ TEST_F(PaintLayerCompositorTest, UpdateDoesNotOrphanMainGraphicsLayer) { // Force CompositedLayerMapping to update the internal layer hierarchy. auto* box = GetDocument().getElementById("box"); - box->setAttribute(HTMLNames::styleAttr, "height: 1000px;"); - GetDocument().View()->UpdateAllLifecyclePhases(); + box->setAttribute(html_names::kStyleAttr, "height: 1000px;"); + UpdateAllLifecyclePhasesForTest(); EXPECT_EQ(main_graphics_layer_parent, main_graphics_layer->Parent()); } diff --git a/chromium/third_party/blink/renderer/core/paint/decoration_info.h b/chromium/third_party/blink/renderer/core/paint/decoration_info.h index a3147cecdcd..6871a017ea9 100644 --- a/chromium/third_party/blink/renderer/core/paint/decoration_info.h +++ b/chromium/third_party/blink/renderer/core/paint/decoration_info.h @@ -7,7 +7,7 @@ #include "third_party/blink/renderer/platform/fonts/font_baseline.h" #include "third_party/blink/renderer/platform/geometry/float_point.h" -#include "third_party/blink/renderer/platform/layout_unit.h" +#include "third_party/blink/renderer/platform/geometry/layout_unit.h" #include "third_party/blink/renderer/platform/wtf/allocator.h" namespace blink { diff --git a/chromium/third_party/blink/renderer/core/paint/document_marker_painter.cc b/chromium/third_party/blink/renderer/core/paint/document_marker_painter.cc index 4777c420468..40063a379c1 100644 --- a/chromium/third_party/blink/renderer/core/paint/document_marker_painter.cc +++ b/chromium/third_party/blink/renderer/core/paint/document_marker_painter.cc @@ -10,8 +10,8 @@ #include "third_party/blink/renderer/core/paint/text_paint_style.h" #include "third_party/blink/renderer/core/style/computed_style.h" #include "third_party/blink/renderer/platform/fonts/simple_font_data.h" +#include "third_party/blink/renderer/platform/geometry/layout_unit.h" #include "third_party/blink/renderer/platform/graphics/graphics_context_state_saver.h" -#include "third_party/blink/renderer/platform/layout_unit.h" namespace blink { diff --git a/chromium/third_party/blink/renderer/core/paint/ellipsis_box_painter.cc b/chromium/third_party/blink/renderer/core/paint/ellipsis_box_painter.cc index ff4358b75cc..c1f28b7d83a 100644 --- a/chromium/third_party/blink/renderer/core/paint/ellipsis_box_painter.cc +++ b/chromium/third_party/blink/renderer/core/paint/ellipsis_box_painter.cc @@ -36,13 +36,11 @@ void EllipsisBoxPainter::PaintEllipsis(const PaintInfo& paint_info, box_origin.MoveBy(paint_offset); GraphicsContext& context = paint_info.context; - DisplayItem::Type display_item_type = - DisplayItem::PaintPhaseToDrawingType(paint_info.phase); if (DrawingRecorder::UseCachedDrawingIfPossible(context, ellipsis_box_, - display_item_type)) + paint_info.phase)) return; - DrawingRecorder recorder(context, ellipsis_box_, display_item_type); + DrawingRecorder recorder(context, ellipsis_box_, paint_info.phase); LayoutRect box_rect(box_origin, LayoutSize(ellipsis_box_.LogicalWidth(), diff --git a/chromium/third_party/blink/renderer/core/paint/embedded_content_painter.cc b/chromium/third_party/blink/renderer/core/paint/embedded_content_painter.cc index b957865f1fa..0d024b1f447 100644 --- a/chromium/third_party/blink/renderer/core/paint/embedded_content_painter.cc +++ b/chromium/third_party/blink/renderer/core/paint/embedded_content_painter.cc @@ -30,13 +30,10 @@ void EmbeddedContentPainter::PaintReplaced(const PaintInfo& paint_info, paint_offset + layout_embedded_content_.ReplacedContentRect().Location())); - // Views don't support painting with a paint offset, but instead - // offset themselves using the frame rect location. To paint Views at - // our desired location, we need to apply paint offset as a transform, with - // the frame rect neutralized. IntSize view_paint_offset = paint_location - embedded_content_view->FrameRect().Location(); - CullRect adjusted_cull_rect(paint_info.GetCullRect(), -view_paint_offset); + CullRect adjusted_cull_rect = paint_info.GetCullRect(); + adjusted_cull_rect.Move(-view_paint_offset); embedded_content_view->Paint(paint_info.context, paint_info.GetGlobalPaintFlags(), adjusted_cull_rect, view_paint_offset); diff --git a/chromium/third_party/blink/renderer/core/paint/fieldset_painter.cc b/chromium/third_party/blink/renderer/core/paint/fieldset_painter.cc index 5d762f2cd8c..744ebe0f011 100644 --- a/chromium/third_party/blink/renderer/core/paint/fieldset_painter.cc +++ b/chromium/third_party/blink/renderer/core/paint/fieldset_painter.cc @@ -39,46 +39,50 @@ void FieldsetPainter::PaintBoxDecorationBackground( return BoxPainter(layout_fieldset_) .PaintBoxDecorationBackground(paint_info, paint_offset); - if (DrawingRecorder::UseCachedDrawingIfPossible( - paint_info.context, layout_fieldset_, paint_info.phase)) - return; - - FieldsetPaintInfo fieldset_paint_info = - CreateFieldsetPaintInfo(layout_fieldset_, *legend); - paint_rect.Contract(fieldset_paint_info.border_outsets); - - DrawingRecorder recorder(paint_info.context, layout_fieldset_, - paint_info.phase); - BoxDecorationData box_decoration_data(layout_fieldset_); - - BoxPainterBase::PaintNormalBoxShadow(paint_info, paint_rect, - layout_fieldset_.StyleRef()); - BackgroundImageGeometry geometry(layout_fieldset_); - BoxModelObjectPainter(layout_fieldset_) - .PaintFillLayers(paint_info, box_decoration_data.background_color, - layout_fieldset_.StyleRef().BackgroundLayers(), - paint_rect, geometry); - BoxPainterBase::PaintInsetBoxShadowWithBorderRect( - paint_info, paint_rect, layout_fieldset_.StyleRef()); - - if (!box_decoration_data.has_border_decoration) - return; - - // Create a clipping region around the legend and paint the border as normal - GraphicsContext& graphics_context = paint_info.context; - GraphicsContextStateSaver state_saver(graphics_context); - - LayoutRect legend_cutout_rect = fieldset_paint_info.legend_cutout_rect; - legend_cutout_rect.MoveBy(paint_offset); - graphics_context.ClipOut(PixelSnappedIntRect(legend_cutout_rect)); - - Node* node = nullptr; - const LayoutObject* layout_object = &layout_fieldset_; - for (; layout_object && !node; layout_object = layout_object->Parent()) - node = layout_object->GeneratingNode(); - BoxPainterBase::PaintBorder(layout_fieldset_, layout_fieldset_.GetDocument(), - node, paint_info, paint_rect, - layout_fieldset_.StyleRef()); + if (!DrawingRecorder::UseCachedDrawingIfPossible( + paint_info.context, layout_fieldset_, paint_info.phase)) { + FieldsetPaintInfo fieldset_paint_info = + CreateFieldsetPaintInfo(layout_fieldset_, *legend); + paint_rect.Contract(fieldset_paint_info.border_outsets); + + DrawingRecorder recorder(paint_info.context, layout_fieldset_, + paint_info.phase); + BoxDecorationData box_decoration_data(layout_fieldset_); + + BoxPainterBase::PaintNormalBoxShadow(paint_info, paint_rect, + layout_fieldset_.StyleRef()); + BackgroundImageGeometry geometry(layout_fieldset_); + BoxModelObjectPainter(layout_fieldset_) + .PaintFillLayers(paint_info, box_decoration_data.background_color, + layout_fieldset_.StyleRef().BackgroundLayers(), + paint_rect, geometry); + BoxPainterBase::PaintInsetBoxShadowWithBorderRect( + paint_info, paint_rect, layout_fieldset_.StyleRef()); + + if (box_decoration_data.has_border_decoration) { + // Create a clipping region around the legend and paint the border as + // normal + GraphicsContext& graphics_context = paint_info.context; + GraphicsContextStateSaver state_saver(graphics_context); + + LayoutRect legend_cutout_rect = fieldset_paint_info.legend_cutout_rect; + legend_cutout_rect.MoveBy(paint_offset); + graphics_context.ClipOut(PixelSnappedIntRect(legend_cutout_rect)); + + Node* node = nullptr; + const LayoutObject* layout_object = &layout_fieldset_; + for (; layout_object && !node; layout_object = layout_object->Parent()) + node = layout_object->GeneratingNode(); + BoxPainterBase::PaintBorder( + layout_fieldset_, layout_fieldset_.GetDocument(), node, paint_info, + paint_rect, layout_fieldset_.StyleRef()); + } + } + + if (RuntimeEnabledFeatures::PaintTouchActionRectsEnabled()) { + BoxPainter(layout_fieldset_) + .RecordHitTestData(paint_info, paint_rect, layout_fieldset_); + } } void FieldsetPainter::PaintMask(const PaintInfo& paint_info, diff --git a/chromium/third_party/blink/renderer/core/paint/filter_effect_builder.cc b/chromium/third_party/blink/renderer/core/paint/filter_effect_builder.cc index e8d7fb75428..3dd4e661ecc 100644 --- a/chromium/third_party/blink/renderer/core/paint/filter_effect_builder.cc +++ b/chromium/third_party/blink/renderer/core/paint/filter_effect_builder.cc @@ -33,6 +33,7 @@ #include "third_party/blink/renderer/core/svg/svg_filter_element.h" #include "third_party/blink/renderer/core/svg/svg_length_context.h" #include "third_party/blink/renderer/core/svg/svg_resource.h" +#include "third_party/blink/renderer/platform/geometry/length_functions.h" #include "third_party/blink/renderer/platform/graphics/compositor_filter_operations.h" #include "third_party/blink/renderer/platform/graphics/filters/fe_box_reflect.h" #include "third_party/blink/renderer/platform/graphics/filters/fe_color_matrix.h" @@ -44,7 +45,6 @@ #include "third_party/blink/renderer/platform/graphics/filters/paint_filter_builder.h" #include "third_party/blink/renderer/platform/graphics/filters/source_graphic.h" #include "third_party/blink/renderer/platform/graphics/interpolation_space.h" -#include "third_party/blink/renderer/platform/length_functions.h" #include "third_party/blink/renderer/platform/wtf/math_extras.h" namespace blink { @@ -305,14 +305,14 @@ CompositorFilterOperations FilterEffectBuilder::BuildFilterOperations( Filter* reference_filter = BuildReferenceFilter(reference_operation, nullptr); if (reference_filter && reference_filter->LastEffect()) { - PaintFilterBuilder::PopulateSourceGraphicImageFilters( + paint_filter_builder::PopulateSourceGraphicImageFilters( reference_filter->GetSourceGraphic(), nullptr, current_interpolation_space); FilterEffect* filter_effect = reference_filter->LastEffect(); current_interpolation_space = filter_effect->OperatingInterpolationSpace(); - auto paint_filter = PaintFilterBuilder::Build( + auto paint_filter = paint_filter_builder::Build( filter_effect, current_interpolation_space); if (!paint_filter) continue; @@ -385,7 +385,7 @@ CompositorFilterOperations FilterEffectBuilder::BuildFilterOperations( // instead of calling this a "reference filter". const auto& reflection = ToBoxReflectFilterOperation(*op).Reflection(); filters.AppendReferenceFilter( - PaintFilterBuilder::BuildBoxReflectFilter(reflection, nullptr)); + paint_filter_builder::BuildBoxReflectFilter(reflection, nullptr)); break; } case FilterOperation::NONE: @@ -394,8 +394,9 @@ CompositorFilterOperations FilterEffectBuilder::BuildFilterOperations( } if (current_interpolation_space != kInterpolationSpaceSRGB) { // Transform to device color space at the end of processing, if required. - sk_sp<PaintFilter> filter = PaintFilterBuilder::TransformInterpolationSpace( - nullptr, current_interpolation_space, kInterpolationSpaceSRGB); + sk_sp<PaintFilter> filter = + paint_filter_builder::TransformInterpolationSpace( + nullptr, current_interpolation_space, kInterpolationSpaceSRGB); filters.AppendReferenceFilter(std::move(filter)); } diff --git a/chromium/third_party/blink/renderer/core/paint/fragment_data.h b/chromium/third_party/blink/renderer/core/paint/fragment_data.h index 3cbdc220f9e..e71fd303568 100644 --- a/chromium/third_party/blink/renderer/core/paint/fragment_data.h +++ b/chromium/third_party/blink/renderer/core/paint/fragment_data.h @@ -227,7 +227,7 @@ class CORE_EXPORT FragmentData { void DestroyTail(); // Contains rare data that that is not needed on all fragments. - struct RareData { + struct CORE_EXPORT RareData { USING_FAST_MALLOC(RareData); public: diff --git a/chromium/third_party/blink/renderer/core/paint/frame_painter.cc b/chromium/third_party/blink/renderer/core/paint/frame_painter.cc index 12b6df67135..52e06dff7a7 100644 --- a/chromium/third_party/blink/renderer/core/paint/frame_painter.cc +++ b/chromium/third_party/blink/renderer/core/paint/frame_painter.cc @@ -68,7 +68,7 @@ void FramePainter::PaintContents(GraphicsContext& context, FramePaintTiming frame_paint_timing(context, &GetFrameView().GetFrame()); TRACE_EVENT1( "devtools.timeline,rail", "Paint", "data", - InspectorPaintEvent::Data(layout_view, LayoutRect(rect), nullptr)); + inspector_paint_event::Data(layout_view, LayoutRect(rect), nullptr)); bool is_top_level_painter = !in_paint_contents_; in_paint_contents_ = true; @@ -101,11 +101,11 @@ void FramePainter::PaintContents(GraphicsContext& context, root_layer->GetLayoutObject().GetFrame()); context.SetDeviceScaleFactor(device_scale_factor); - layer_painter.Paint(context, LayoutRect(rect), updated_global_paint_flags, + layer_painter.Paint(context, CullRect(rect), updated_global_paint_flags, root_layer_paint_flags); if (root_layer->ContainsDirtyOverlayScrollbars()) { - layer_painter.PaintOverlayScrollbars(context, LayoutRect(rect), + layer_painter.PaintOverlayScrollbars(context, CullRect(rect), updated_global_paint_flags); } diff --git a/chromium/third_party/blink/renderer/core/paint/frame_set_painter.cc b/chromium/third_party/blink/renderer/core/paint/frame_set_painter.cc index 683c170db66..196fca5a046 100644 --- a/chromium/third_party/blink/renderer/core/paint/frame_set_painter.cc +++ b/chromium/third_party/blink/renderer/core/paint/frame_set_painter.cc @@ -26,7 +26,7 @@ static Color BorderFillColor() { void FrameSetPainter::PaintColumnBorder(const PaintInfo& paint_info, const IntRect& border_rect) { - if (!paint_info.GetCullRect().IntersectsCullRect(border_rect)) + if (!paint_info.GetCullRect().Intersects(border_rect)) return; // FIXME: We should do something clever when borders from distinct framesets diff --git a/chromium/third_party/blink/renderer/core/paint/html_canvas_painter.cc b/chromium/third_party/blink/renderer/core/paint/html_canvas_painter.cc index a5692927314..a9443b1220c 100644 --- a/chromium/third_party/blink/renderer/core/paint/html_canvas_painter.cc +++ b/chromium/third_party/blink/renderer/core/paint/html_canvas_painter.cc @@ -44,11 +44,11 @@ void HTMLCanvasPainter::PaintReplaced(const PaintInfo& paint_info, canvas->RenderingContext()->IsComposited()) { if (cc::Layer* layer = canvas->RenderingContext()->CcLayer()) { IntRect pixel_snapped_rect = PixelSnappedIntRect(paint_rect); - layer->SetBounds(static_cast<gfx::Size>(pixel_snapped_rect.Size())); + layer->SetOffsetToTransformParent( + gfx::Vector2dF(pixel_snapped_rect.X(), pixel_snapped_rect.Y())); + layer->SetBounds(gfx::Size(pixel_snapped_rect.Size())); layer->SetIsDrawable(true); - RecordForeignLayer( - context, layout_html_canvas_, DisplayItem::kForeignLayerCanvas, layer, - FloatPoint(pixel_snapped_rect.Location()), pixel_snapped_rect.Size()); + RecordForeignLayer(context, DisplayItem::kForeignLayerCanvas, layer); return; } } diff --git a/chromium/third_party/blink/renderer/core/paint/html_canvas_painter_test.cc b/chromium/third_party/blink/renderer/core/paint/html_canvas_painter_test.cc index 5f9839a5747..0e4681dfc81 100644 --- a/chromium/third_party/blink/renderer/core/paint/html_canvas_painter_test.cc +++ b/chromium/third_party/blink/renderer/core/paint/html_canvas_painter_test.cc @@ -98,7 +98,7 @@ TEST_P(HTMLCanvasPainterTestForSPv2, Canvas2DLayerAppearsInLayerTree) { // Force the page to paint. element->FinalizeFrame(); - GetDocument().View()->UpdateAllLifecyclePhases(); + UpdateAllLifecyclePhasesForTest(); // Fetch the layer associated with the <canvas>, and check that it was // correctly configured in the layer tree. diff --git a/chromium/third_party/blink/renderer/core/paint/image_element_timing.cc b/chromium/third_party/blink/renderer/core/paint/image_element_timing.cc index 3fd6520fc57..346e103cf35 100644 --- a/chromium/third_party/blink/renderer/core/paint/image_element_timing.cc +++ b/chromium/third_party/blink/renderer/core/paint/image_element_timing.cc @@ -16,6 +16,7 @@ #include "third_party/blink/renderer/core/timing/window_performance.h" #include "third_party/blink/renderer/platform/cross_thread_functional.h" #include "third_party/blink/renderer/platform/graphics/paint/geometry_mapper.h" +#include "third_party/blink/renderer/platform/weborigin/security_origin.h" #include "third_party/blink/renderer/platform/wtf/text/atomic_string.h" namespace blink { @@ -50,12 +51,22 @@ void ImageElementTiming::NotifyImagePainted(const HTMLImageElement* element, images_notified_.insert(layout_image); - // Compute the viewport rect. LocalFrame* frame = GetSupplementable()->GetFrame(); DCHECK(frame == layout_image->GetDocument().GetFrame()); if (!frame) return; + // Skip the computations below if the element is not same origin. + if (!layout_image->CachedImage()) + return; + + const KURL& url = layout_image->CachedImage()->Url(); + DCHECK(GetSupplementable()->document() == &layout_image->GetDocument()); + if (!SecurityOrigin::AreSameSchemeHostPort(layout_image->GetDocument().Url(), + url)) + return; + + // Compute the viewport rect. WebLayerTreeView* layerTreeView = frame->GetChromeClient().GetWebLayerTreeView(frame); if (!layerTreeView) @@ -81,7 +92,7 @@ void ImageElementTiming::NotifyImagePainted(const HTMLImageElement* element, visible_new_visual_rect.Intersect(viewport); const AtomicString attr = - element->FastGetAttribute(HTMLNames::elementtimingAttr); + element->FastGetAttribute(html_names::kElementtimingAttr); // Do not create an entry if 'elementtiming' is not present or the image is // below a certain size threshold. if (attr.IsEmpty() && @@ -94,7 +105,7 @@ void ImageElementTiming::NotifyImagePainted(const HTMLImageElement* element, // empty, use the ID. If empty, use 'img'. AtomicString name = attr; if (name.IsEmpty()) - name = element->FastGetAttribute(HTMLNames::idAttr); + name = element->FastGetAttribute(html_names::kIdAttr); if (name.IsEmpty()) name = "img"; element_timings_.emplace_back(name, visible_new_visual_rect); @@ -110,15 +121,29 @@ void ImageElementTiming::NotifyImagePainted(const HTMLImageElement* element, void ImageElementTiming::ReportImagePaintSwapTime(WebLayerTreeView::SwapResult, base::TimeTicks timestamp) { - WindowPerformance* performance = - DOMWindowPerformance::performance(*GetSupplementable()); - if (!performance || !performance->HasObserverFor(PerformanceEntry::kElement)) - return; - - for (const auto& element_timing : element_timings_) { - performance->AddElementTiming(element_timing.name, element_timing.rect, - timestamp); + Document* document = GetSupplementable()->document(); + DCHECK(document); + const SecurityOrigin* current_origin = document->GetSecurityOrigin(); + // It suffices to check the current origin against the parent origin since all + // origins stored in |element_timings_| have been checked against the current + // origin. + while (document && + current_origin->IsSameSchemeHostPort(document->GetSecurityOrigin())) { + DCHECK(document->domWindow()); + WindowPerformance* performance = + DOMWindowPerformance::performance(*document->domWindow()); + if (performance && + performance->HasObserverFor(PerformanceEntry::kElement)) { + for (const auto& element_timing : element_timings_) { + performance->AddElementTiming(element_timing.name, element_timing.rect, + timestamp); + } + } + // Provide the entry to the parent documents for as long as the origin check + // still holds. + document = document->ParentDocument(); } + element_timings_.clear(); } diff --git a/chromium/third_party/blink/renderer/core/paint/image_paint_timing_detector.cc b/chromium/third_party/blink/renderer/core/paint/image_paint_timing_detector.cc index fdeee5f4f9d..f0b454af11e 100644 --- a/chromium/third_party/blink/renderer/core/paint/image_paint_timing_detector.cc +++ b/chromium/third_party/blink/renderer/core/paint/image_paint_timing_detector.cc @@ -1,16 +1,21 @@ // 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. - #include "third_party/blink/renderer/core/paint/image_paint_timing_detector.h" #include "third_party/blink/renderer/core/frame/local_frame.h" #include "third_party/blink/renderer/core/frame/local_frame_view.h" #include "third_party/blink/renderer/core/inspector/identifiers_factory.h" +#include "third_party/blink/renderer/core/layout/layout_box_model_object.h" #include "third_party/blink/renderer/core/layout/layout_image.h" +#include "third_party/blink/renderer/core/layout/layout_image_resource.h" +#include "third_party/blink/renderer/core/layout/layout_video.h" #include "third_party/blink/renderer/core/layout/layout_view.h" +#include "third_party/blink/renderer/core/layout/svg/layout_svg_image.h" #include "third_party/blink/renderer/core/page/chrome_client.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_timing_detector.h" +#include "third_party/blink/renderer/core/style/style_fetched_image.h" #include "third_party/blink/renderer/platform/geometry/layout_rect.h" #include "third_party/blink/renderer/platform/graphics/paint/geometry_mapper.h" #include "third_party/blink/renderer/platform/instrumentation/tracing/trace_event.h" @@ -18,6 +23,78 @@ namespace blink { +namespace { +String GetImageUrl(const LayoutObject& object) { + if (object.IsImage()) { + const ImageResourceContent* cached_image = + ToLayoutImage(&object)->CachedImage(); + return cached_image ? cached_image->Url().StrippedForUseAsReferrer() : ""; + } + if (object.IsVideo()) { + const ImageResourceContent* cached_image = + ToLayoutVideo(&object)->CachedImage(); + return cached_image ? cached_image->Url().StrippedForUseAsReferrer() : ""; + } + if (object.IsSVGImage()) { + const LayoutImageResource* image_resource = + ToLayoutSVGImage(&object)->ImageResource(); + const ImageResourceContent* cached_image = image_resource->CachedImage(); + return cached_image ? cached_image->Url().StrippedForUseAsReferrer() : ""; + } + DCHECK(ImagePaintTimingDetector::HasContentfulBackgroundImage(object)); + const ComputedStyle* style = object.Style(); + StringBuilder concatenated_result; + for (const FillLayer* bg_layer = &style->BackgroundLayers(); bg_layer; + bg_layer = bg_layer->Next()) { + StyleImage* bg_image = bg_layer->GetImage(); + if (!bg_image || !bg_image->IsImageResource()) + continue; + const StyleFetchedImage* fetched_image = ToStyleFetchedImage(bg_image); + const String url = fetched_image->Url().StrippedForUseAsReferrer(); + concatenated_result.Append(url.Utf8().data(), url.length()); + } + return concatenated_result.ToString(); +} + +bool AttachedBackgroundImagesAllLoaded(const LayoutObject& object) { + DCHECK(ImagePaintTimingDetector::HasContentfulBackgroundImage(object)); + const ComputedStyle* style = object.Style(); + DCHECK(style); + for (const FillLayer* bg_layer = &style->BackgroundLayers(); bg_layer; + bg_layer = bg_layer->Next()) { + StyleImage* bg_image = bg_layer->GetImage(); + // A layout object with background images is not loaded until all of the + // background images are loaded. + if (!bg_image || !bg_image->IsImageResource()) + continue; + if (!bg_image->IsLoaded()) + return false; + } + return true; +} + +bool IsLoaded(const LayoutObject& object) { + if (object.IsImage()) { + const ImageResourceContent* cached_image = + ToLayoutImage(&object)->CachedImage(); + return cached_image ? cached_image->IsLoaded() : false; + } + if (object.IsVideo()) { + const ImageResourceContent* cached_image = + ToLayoutVideo(&object)->CachedImage(); + return cached_image ? cached_image->IsLoaded() : false; + } + if (object.IsSVGImage()) { + const LayoutImageResource* image_resource = + ToLayoutSVGImage(&object)->ImageResource(); + const ImageResourceContent* cached_image = image_resource->CachedImage(); + return cached_image ? cached_image->IsLoaded() : false; + } + DCHECK(ImagePaintTimingDetector::HasContentfulBackgroundImage(object)); + return AttachedBackgroundImagesAllLoaded(object); +} +} // namespace + // Set a big enough limit for the number of nodes to ensure memory usage is // capped. Exceeding such limit will deactivate the algorithm. constexpr size_t kImageNodeNumberLimit = 5000; @@ -57,57 +134,57 @@ void ImagePaintTimingDetector::PopulateTraceValue( IdentifiersFactory::FrameId(&frame_view_->GetFrame())); } -IntRect ImagePaintTimingDetector::CalculateTransformedRect( - LayoutRect& invalidated_rect, - const PaintLayer& painting_layer) const { - const auto* local_transform = painting_layer.GetLayoutObject() - .FirstFragment() - .LocalBorderBoxProperties() - .Transform(); - const auto* ancestor_transform = painting_layer.GetLayoutObject() - .View() - ->FirstFragment() - .LocalBorderBoxProperties() - .Transform(); - FloatRect invalidated_rect_abs = FloatRect(invalidated_rect); - if (invalidated_rect_abs.IsEmpty() || invalidated_rect_abs.IsZero()) - return IntRect(); - DCHECK(local_transform); - DCHECK(ancestor_transform); - GeometryMapper::SourceToDestinationRect(local_transform, ancestor_transform, - invalidated_rect_abs); - IntRect invalidated_rect_in_viewport = RoundedIntRect(invalidated_rect_abs); - ScrollableArea* scrollable_area = frame_view_->GetScrollableArea(); - DCHECK(scrollable_area); - IntRect viewport = scrollable_area->VisibleContentRect(); - invalidated_rect_in_viewport.Intersect(viewport); - return invalidated_rect_in_viewport; +void ImagePaintTimingDetector::OnLargestImagePaintDetected( + const ImageRecord& largest_image_record) { + largest_image_paint_ = largest_image_record.first_paint_time_after_loaded; + std::unique_ptr<TracedValue> value = TracedValue::Create(); + PopulateTraceValue(*value, largest_image_record, + ++largest_image_candidate_index_max_); + TRACE_EVENT_INSTANT_WITH_TIMESTAMP1( + "loading", "LargestImagePaint::Candidate", TRACE_EVENT_SCOPE_THREAD, + largest_image_record.first_paint_time_after_loaded, "data", + std::move(value)); + frame_view_->GetPaintTimingDetector().DidChangePerformanceTiming(); +} + +void ImagePaintTimingDetector::OnLastImagePaintDetected( + const ImageRecord& last_image_record) { + last_image_paint_ = last_image_record.first_paint_time_after_loaded; + std::unique_ptr<TracedValue> value = TracedValue::Create(); + PopulateTraceValue(*value, last_image_record, + ++last_image_candidate_index_max_); + TRACE_EVENT_INSTANT_WITH_TIMESTAMP1( + "loading", "LastImagePaint::Candidate", TRACE_EVENT_SCOPE_THREAD, + last_image_record.first_paint_time_after_loaded, "data", + std::move(value)); + frame_view_->GetPaintTimingDetector().DidChangePerformanceTiming(); } void ImagePaintTimingDetector::Analyze() { + // These conditions represents the following scenarios: + // 1. candiate being nullptr: no loaded image is found. + // 2. candidate's first paint being null: largest/last image is still pending + // for timing. We discard the candidate and wait for the next analysis. + // 3. new candidate equals to old candidate: we don't need to update the + // result unless it's a new candidate. ImageRecord* largest_image_record = FindLargestPaintCandidate(); - // In cases where largest/last image is still pending for timing, we discard - // the result and wait for the next analysis. + bool new_candidate_detected = false; if (largest_image_record && - !largest_image_record->first_paint_time_after_loaded.is_null()) { - std::unique_ptr<TracedValue> value = TracedValue::Create(); - PopulateTraceValue(*value, *largest_image_record, - ++largest_image_candidate_index_max_); - TRACE_EVENT_INSTANT_WITH_TIMESTAMP1( - "loading", "LargestImagePaint::Candidate", TRACE_EVENT_SCOPE_THREAD, - largest_image_record->first_paint_time_after_loaded, "data", - std::move(value)); + !largest_image_record->first_paint_time_after_loaded.is_null() && + largest_image_record->first_paint_time_after_loaded != + largest_image_paint_) { + new_candidate_detected = true; + OnLargestImagePaintDetected(*largest_image_record); } ImageRecord* last_image_record = FindLastPaintCandidate(); if (last_image_record && - !last_image_record->first_paint_time_after_loaded.is_null()) { - std::unique_ptr<TracedValue> value = TracedValue::Create(); - PopulateTraceValue(*value, *last_image_record, - ++last_image_candidate_index_max_); - TRACE_EVENT_INSTANT_WITH_TIMESTAMP1( - "loading", "LastImagePaint::Candidate", TRACE_EVENT_SCOPE_THREAD, - last_image_record->first_paint_time_after_loaded, "data", - std::move(value)); + !last_image_record->first_paint_time_after_loaded.is_null() && + last_image_record->first_paint_time_after_loaded != last_image_paint_) { + new_candidate_detected = true; + OnLastImagePaintDetected(*last_image_record); + } + if (new_candidate_detected) { + frame_view_->GetPaintTimingDetector().DidChangePerformanceTiming(); } } @@ -136,6 +213,20 @@ void ImagePaintTimingDetector::NotifyNodeRemoved(DOMNodeId node_id) { // bother to remove these records from largest_image_heap_ or // latest_image_heap_, to reduce computation. id_record_map_.erase(node_id); + + if (id_record_map_.size() == 0) { + const bool largest_image_paint_invalidated = + largest_image_paint_ != base::TimeTicks(); + const bool last_image_paint_invalidated = + last_image_paint_ != base::TimeTicks(); + if (largest_image_paint_invalidated) + largest_image_paint_ = base::TimeTicks(); + if (last_image_paint_invalidated) + last_image_paint_ = base::TimeTicks(); + if (largest_image_paint_invalidated || last_image_paint_invalidated) { + frame_view_->GetPaintTimingDetector().DidChangePerformanceTiming(); + } + } } } @@ -166,9 +257,8 @@ void ImagePaintTimingDetector::ReportSwapTime( base::TimeTicks timestamp) { // The callback is safe from race-condition only when running on main-thread. DCHECK(ThreadState::Current()->IsMainThread()); - // This callback is queued only when there are records in - // records_pending_timing_. - DCHECK_GT(records_pending_timing_.size(), 0UL); + // Not guranteed to be non-empty, because records can be removed between + // callback registration and invocation. while (records_pending_timing_.size() > 0) { DOMNodeId node_id = records_pending_timing_.front(); if (!id_record_map_.Contains(node_id)) { @@ -185,6 +275,48 @@ void ImagePaintTimingDetector::ReportSwapTime( Analyze(); } +// In the context of FCP++, we define contentful background image as one that +// satisfies all of the following conditions: +// * has image reources attached to style of the object, i.e., +// { background-image: url('example.gif') } +// * not attached to <body> or <html> +// +// static +bool ImagePaintTimingDetector::HasContentfulBackgroundImage( + const LayoutObject& object) { + Node* node = object.GetNode(); + if (!node) + return false; + // Background images attached to <body> or <html> are likely for background + // purpose, so we rule them out, according to the following rules: + // * Box model objects includes objects of box model, such as <div>, <body>, + // LayoutView, but not includes LayoutText. + // * BackgroundTransfersToView is true for the <body>, <html>, e.g., that + // have transferred their background to LayoutView. + // * LayoutView has the background transfered by <html> if <html> has + // background. + if (!object.IsBoxModelObject() || + ToLayoutBoxModelObject(object).BackgroundTransfersToView()) + return false; + if (object.IsLayoutView()) + return false; + const ComputedStyle* style = object.Style(); + if (!style) + return false; + if (!style->HasBackgroundImage()) + return false; + + for (const FillLayer* bg_layer = &style->BackgroundLayers(); bg_layer; + bg_layer = bg_layer->Next()) { + StyleImage* bg_image = bg_layer->GetImage(); + // Rule out images that doesn't load any image resources, e.g., a gradient. + if (!bg_image || !bg_image->IsImageResource()) + continue; + return true; + } + return false; +} + void ImagePaintTimingDetector::RecordImage(const LayoutObject& object, const PaintLayer& painting_layer) { Node* node = object.GetNode(); @@ -194,57 +326,60 @@ void ImagePaintTimingDetector::RecordImage(const LayoutObject& object, if (size_zero_ids_.Contains(node_id)) return; - const LayoutImage* image = ToLayoutImage(&object); - if (!id_record_map_.Contains(node_id)) { - recorded_node_count_++; - if (recorded_node_count_ < kImageNodeNumberLimit) { - LayoutRect invalidated_rect = image->FirstFragment().VisualRect(); - // Do not record first size until invalidated_rect's size becomes - // non-empty. - if (invalidated_rect.IsEmpty()) - return; - IntRect invalidated_rect_in_viewport = - CalculateTransformedRect(invalidated_rect, painting_layer); - int rect_size = invalidated_rect_in_viewport.Height() * - invalidated_rect_in_viewport.Width(); - if (rect_size == 0) { - // When rect_size == 0, it either means the image is size 0 or the image - // is out of viewport. Either way, we don't track this image anymore, to - // reduce computation. - size_zero_ids_.insert(node_id); - return; - } - std::unique_ptr<ImageRecord> record = std::make_unique<ImageRecord>(); - record->node_id = node_id; - record->frame_index = frame_index_; - record->first_size = rect_size; - record->first_paint_index = ++first_paint_index_max_; - ImageResourceContent* cachedImg = image->CachedImage(); - record->image_url = - !cachedImg ? "" : cachedImg->Url().StrippedForUseAsReferrer(); - largest_image_heap_.push(record->AsWeakPtr()); - latest_image_heap_.push(record->AsWeakPtr()); - id_record_map_.insert(node_id, std::move(record)); - } else { - // for assessing whether kImageNodeNumberLimit is large enough for all - // normal cases - TRACE_EVENT_INSTANT1("loading", "ImagePaintTimingDetector::OverNodeLimit", - TRACE_EVENT_SCOPE_THREAD, "recorded_node_count", - recorded_node_count_); + if (!id_record_map_.Contains(node_id) && is_recording_) { + LayoutRect invalidated_rect = object.FirstFragment().VisualRect(); + // Before the image resource is loaded, <img> has size 0, so we do not + // record the first size until the invalidated rect's size becomes + // non-empty. + if (invalidated_rect.IsEmpty()) + return; + uint64_t rect_size = + frame_view_->GetPaintTimingDetector().CalculateVisualSize( + invalidated_rect, painting_layer); + if (rect_size == 0) { + // When rect_size == 0, it either means the image is size 0 or the image + // is out of viewport. Either way, we don't track this image anymore, to + // reduce computation. + size_zero_ids_.insert(node_id); + return; } + // Non-trivial image is found. + std::unique_ptr<ImageRecord> record = std::make_unique<ImageRecord>(); + record->node_id = node_id; + record->image_url = GetImageUrl(object); + // Mind that first_size has to be assigned at the push of + // largest_image_heap_ since it's the sorting key. + record->first_size = rect_size; + largest_image_heap_.push(record->AsWeakPtr()); + id_record_map_.insert(node_id, std::move(record)); + if (id_record_map_.size() + size_zero_ids_.size() > kImageNodeNumberLimit) + Deactivate(); } - if (id_record_map_.Contains(node_id) && - IsJustLoaded(image, *id_record_map_.at(node_id))) { + if (id_record_map_.Contains(node_id) && !id_record_map_.at(node_id)->loaded && + IsLoaded(object)) { + // The image is just loaded. records_pending_timing_.push(node_id); - id_record_map_.at(node_id)->loaded = true; + ImageRecord* record = id_record_map_.at(node_id); + record->frame_index = frame_index_; + record->loaded = true; + // Latest image heap differs from largest image heap in that the former + // pushes a record when an image is loaded while the latter pushes when an + // image is attached to DOM. This causes last image paint to base its order + // on load time other than attachment time. + // Mind that first_paint_index has to be assigned at the push of + // latest_image_heap_ since it's the sorting key. + record->first_paint_index = ++first_paint_index_max_; + latest_image_heap_.push(record->AsWeakPtr()); } } -bool ImagePaintTimingDetector::IsJustLoaded(const LayoutImage* image, - const ImageRecord& record) const { - ImageResourceContent* cachedImg = image->CachedImage(); - return cachedImg && cachedImg->IsLoaded() && !record.loaded; +void ImagePaintTimingDetector::Deactivate() { + TRACE_EVENT_INSTANT2("loading", "ImagePaintTimingDetector::OverNodeLimit", + TRACE_EVENT_SCOPE_THREAD, "recorded_node_count", + id_record_map_.size(), "size_zero_node_count", + size_zero_ids_.size()); + is_recording_ = false; } ImageRecord* ImagePaintTimingDetector::FindLargestPaintCandidate() { diff --git a/chromium/third_party/blink/renderer/core/paint/image_paint_timing_detector.h b/chromium/third_party/blink/renderer/core/paint/image_paint_timing_detector.h index 0d933082422..9bcfd63aa45 100644 --- a/chromium/third_party/blink/renderer/core/paint/image_paint_timing_detector.h +++ b/chromium/third_party/blink/renderer/core/paint/image_paint_timing_detector.h @@ -17,16 +17,14 @@ namespace blink { class PaintLayer; -class IntRect; class LayoutObject; class TracedValue; class LocalFrameView; -class LayoutImage; class ImageRecord : public base::SupportsWeakPtr<ImageRecord> { public: DOMNodeId node_id = kInvalidDOMNodeId; - double first_size = 0.0; + uint64_t first_size = 0; // LastImagePaint uses the order of the first paints to determine the last // image. unsigned first_paint_index = 0; @@ -64,29 +62,30 @@ class CORE_EXPORT ImagePaintTimingDetector final friend class ImagePaintTimingDetectorTest; public: - ImagePaintTimingDetector(LocalFrameView* frame_view); - void RecordImage(const LayoutObject& object, - const PaintLayer& painting_layer); + ImagePaintTimingDetector(LocalFrameView*); + void RecordImage(const LayoutObject&, const PaintLayer&); + static bool HasContentfulBackgroundImage(const LayoutObject& object); void OnPrePaintFinished(); void NotifyNodeRemoved(DOMNodeId); + base::TimeTicks LargestImagePaint() const { return largest_image_paint_; } + base::TimeTicks LastImagePaint() const { return last_image_paint_; } void Trace(blink::Visitor*); private: ImageRecord* FindLargestPaintCandidate(); ImageRecord* FindLastPaintCandidate(); - void PopulateTraceValue(TracedValue& value, + void PopulateTraceValue(TracedValue&, const ImageRecord& first_image_paint, unsigned report_count) const; - IntRect CalculateTransformedRect(LayoutRect& visual_rect, - const PaintLayer& painting_layer) const; // This is provided for unit test to force invoking swap promise callback. void ReportSwapTime(unsigned max_frame_index_to_time, WebLayerTreeView::SwapResult, base::TimeTicks); void RegisterNotifySwapTime(); - void InvokeCallback(); + void OnLargestImagePaintDetected(const ImageRecord&); + void OnLastImagePaintDetected(const ImageRecord&); + void Deactivate(); - bool IsJustLoaded(const LayoutImage*, const ImageRecord&) const; void Analyze(); base::RepeatingCallback<void(WebLayerTreeView::ReportTimeCallback)> @@ -104,7 +103,6 @@ class CORE_EXPORT ImagePaintTimingDetector final bool (*)(const base::WeakPtr<ImageRecord>&, const base::WeakPtr<ImageRecord>&)> latest_image_heap_; - unsigned recorded_node_count_ = 0; // Node-ids of records pending swap time are stored in this queue until they // get a swap time. @@ -121,6 +119,10 @@ class CORE_EXPORT ImagePaintTimingDetector final unsigned frame_index_ = 1; unsigned last_frame_index_queued_for_timing_ = 0; + bool is_recording_ = true; + + base::TimeTicks largest_image_paint_; + base::TimeTicks last_image_paint_; Member<LocalFrameView> frame_view_; }; } // namespace blink diff --git a/chromium/third_party/blink/renderer/core/paint/image_paint_timing_detector_test.cc b/chromium/third_party/blink/renderer/core/paint/image_paint_timing_detector_test.cc index a48eaec6088..95ca64c2c98 100644 --- a/chromium/third_party/blink/renderer/core/paint/image_paint_timing_detector_test.cc +++ b/chromium/third_party/blink/renderer/core/paint/image_paint_timing_detector_test.cc @@ -4,27 +4,42 @@ #include "third_party/blink/renderer/core/paint/image_paint_timing_detector.h" #include "build/build_config.h" +#include "third_party/blink/public/platform/web_url_loader_mock_factory.h" #include "third_party/blink/renderer/core/html/html_image_element.h" -#include "third_party/blink/renderer/core/paint/paint_tracker.h" +#include "third_party/blink/renderer/core/html/media/html_video_element.h" +#include "third_party/blink/renderer/core/paint/paint_timing_detector.h" +#include "third_party/blink/renderer/core/svg/svg_image_element.h" #include "third_party/blink/renderer/core/testing/core_unit_test_helper.h" #include "third_party/blink/renderer/platform/graphics/static_bitmap_image.h" #include "third_party/blink/renderer/platform/runtime_enabled_features.h" #include "third_party/blink/renderer/platform/testing/runtime_enabled_features_test_helpers.h" +#include "third_party/blink/renderer/platform/testing/unit_test_helpers.h" +#include "third_party/blink/renderer/platform/testing/url_test_helpers.h" +#include "third_party/blink/renderer/platform/testing/wtf/scoped_mock_clock.h" #include "third_party/skia/include/core/SkImage.h" #include "third_party/skia/include/core/SkSurface.h" namespace blink { -class ImagePaintTimingDetectorTest : public PageTestBase, - private ScopedPaintTrackingForTest { +class ImagePaintTimingDetectorTest + : public PageTestBase, + private ScopedFirstContentfulPaintPlusPlusForTest { using CallbackQueue = std::queue<WebLayerTreeView::ReportTimeCallback>; public: - ImagePaintTimingDetectorTest() : ScopedPaintTrackingForTest(true){}; + ImagePaintTimingDetectorTest() + : ScopedFirstContentfulPaintPlusPlusForTest(true), + base_url_("http://www.test.com/"){}; + + ~ImagePaintTimingDetectorTest() override { + Platform::Current() + ->GetURLLoaderMockFactory() + ->UnregisterAllURLsAndClearMemoryCache(); + } void SetUp() override { PageTestBase::SetUp(); - GetPaintTracker() + GetPaintTimingDetector() .GetImagePaintTimingDetector() .notify_swap_time_override_for_testing_ = base::BindRepeating(&ImagePaintTimingDetectorTest::FakeNotifySwapTime, @@ -33,21 +48,41 @@ class ImagePaintTimingDetectorTest : public PageTestBase, protected: LocalFrameView& GetFrameView() { return *GetFrame().View(); } - PaintTracker& GetPaintTracker() { return GetFrameView().GetPaintTracker(); } + PaintTimingDetector& GetPaintTimingDetector() { + return GetFrameView().GetPaintTimingDetector(); + } ImageRecord* FindLargestPaintCandidate() { - return GetPaintTracker() + return GetPaintTimingDetector() .GetImagePaintTimingDetector() .FindLargestPaintCandidate(); } ImageRecord* FindLastPaintCandidate() { - return GetPaintTracker() + return GetPaintTimingDetector() .GetImagePaintTimingDetector() .FindLastPaintCandidate(); } + unsigned CountRecords() { + return GetPaintTimingDetector() + .GetImagePaintTimingDetector() + .id_record_map_.size(); + } + + TimeTicks LargestPaintStoredResult() { + return GetPaintTimingDetector() + .GetImagePaintTimingDetector() + .largest_image_paint_; + } + + TimeTicks LastPaintStoredResult() { + return GetPaintTimingDetector() + .GetImagePaintTimingDetector() + .last_image_paint_; + } + void UpdateAllLifecyclePhasesAndInvokeCallbackIfAny() { - GetFrameView().UpdateAllLifecyclePhases(); + UpdateAllLifecyclePhasesForTest(); if (callback_queue_.size() > 0) { InvokeCallback(); } @@ -63,14 +98,35 @@ class ImagePaintTimingDetectorTest : public PageTestBase, void SetImageAndPaint(AtomicString id, int width, int height) { Element* element = GetDocument().getElementById(id); // Set image and make it loaded. - SetImageForTest(ToHTMLImageElement(element), width, height); + ImageResourceContent* content = CreateImageForTest(width, height); + ToHTMLImageElement(element)->SetImageForTest(content); + } + + void SetVideoImageAndPaint(AtomicString id, int width, int height) { + Element* element = GetDocument().getElementById(id); + // Set image and make it loaded. + ImageResourceContent* content = CreateImageForTest(width, height); + ToHTMLVideoElement(element)->SetImageForTest(content); + } + + void SetSVGImageAndPaint(AtomicString id, int width, int height) { + Element* element = GetDocument().getElementById(id); + // Set image and make it loaded. + ImageResourceContent* content = CreateImageForTest(width, height); + ToSVGImageElement(element)->SetImageForTest(content); + } + + void RegisterMockedHttpURLLoad(const std::string& file_name) { + url_test_helpers::RegisterMockedURLLoadFromBase( + WebString::FromUTF8(base_url_), test::CoreTestDataPath(), + WebString::FromUTF8(file_name)); } private: void FakeNotifySwapTime(WebLayerTreeView::ReportTimeCallback callback) { callback_queue_.push(std::move(callback)); } - void SetImageForTest(HTMLImageElement* image_element, int width, int height) { + ImageResourceContent* CreateImageForTest(int width, int height) { sk_sp<SkColorSpace> src_rgb_color_space = SkColorSpace::MakeSRGB(); SkImageInfo raster_image_info = SkImageInfo::MakeN32Premul(width, height, src_rgb_color_space); @@ -79,17 +135,18 @@ class ImagePaintTimingDetectorTest : public PageTestBase, ImageResourceContent* original_image_resource = ImageResourceContent::CreateLoaded( StaticBitmapImage::Create(image).get()); - image_element->SetImageForTest(original_image_resource); + return original_image_resource; } CallbackQueue callback_queue_; + std::string base_url_; }; TEST_F(ImagePaintTimingDetectorTest, LargestImagePaint_NoImage) { SetBodyInnerHTML(R"HTML( <div></div> )HTML"); - GetFrameView().UpdateAllLifecyclePhases(); + UpdateAllLifecyclePhasesForTest(); ImageRecord* record = FindLargestPaintCandidate(); EXPECT_FALSE(record); } @@ -102,10 +159,24 @@ TEST_F(ImagePaintTimingDetectorTest, LargestImagePaint_OneImage) { UpdateAllLifecyclePhasesAndInvokeCallbackIfAny(); ImageRecord* record = FindLargestPaintCandidate(); EXPECT_TRUE(record); - EXPECT_EQ(record->first_size, 25); + EXPECT_EQ(record->first_size, 25ul); EXPECT_TRUE(record->loaded); } +TEST_F(ImagePaintTimingDetectorTest, + IgnoreImageUntilInvalidatedRectSizeNonZero) { + SetBodyInnerHTML(R"HTML( + <img id="target"></img> + )HTML"); + UpdateAllLifecyclePhasesAndInvokeCallbackIfAny(); + EXPECT_EQ(CountRecords(), 0u); + SetImageAndPaint("target", 5, 5); + UpdateAllLifecyclePhasesAndInvokeCallbackIfAny(); + ImageRecord* record = FindLargestPaintCandidate(); + EXPECT_TRUE(record); + EXPECT_EQ(CountRecords(), 1u); +} + TEST_F(ImagePaintTimingDetectorTest, LargestImagePaint_Largest) { SetBodyInnerHTML(R"HTML( <img id="smaller"></img> @@ -117,16 +188,16 @@ TEST_F(ImagePaintTimingDetectorTest, LargestImagePaint_Largest) { ImageRecord* record; record = FindLargestPaintCandidate(); EXPECT_TRUE(record); - EXPECT_EQ(record->first_size, 25); + EXPECT_EQ(record->first_size, 25ul); SetImageAndPaint("larger", 9, 9); UpdateAllLifecyclePhasesAndInvokeCallbackIfAny(); record = FindLargestPaintCandidate(); EXPECT_TRUE(record); #if defined(OS_MACOSX) - EXPECT_EQ(record->first_size, 90); + EXPECT_EQ(record->first_size, 90ul); #else - EXPECT_EQ(record->first_size, 81); + EXPECT_EQ(record->first_size, 81ul); #endif EXPECT_TRUE(record->loaded); @@ -135,9 +206,9 @@ TEST_F(ImagePaintTimingDetectorTest, LargestImagePaint_Largest) { record = FindLargestPaintCandidate(); EXPECT_TRUE(record); #if defined(OS_MACOSX) - EXPECT_EQ(record->first_size, 90); + EXPECT_EQ(record->first_size, 90ul); #else - EXPECT_EQ(record->first_size, 81); + EXPECT_EQ(record->first_size, 81ul); #endif EXPECT_TRUE(record->loaded); } @@ -170,55 +241,151 @@ TEST_F(ImagePaintTimingDetectorTest, LargestImagePaint_IgnoreTheRemoved) { ImageRecord* record; record = FindLargestPaintCandidate(); EXPECT_TRUE(record); + EXPECT_NE(LargestPaintStoredResult(), base::TimeTicks()); GetDocument().getElementById("parent")->RemoveChild( GetDocument().getElementById("target")); UpdateAllLifecyclePhasesAndInvokeCallbackIfAny(); record = FindLargestPaintCandidate(); EXPECT_FALSE(record); + EXPECT_EQ(LargestPaintStoredResult(), base::TimeTicks()); } TEST_F(ImagePaintTimingDetectorTest, - LargestImagePaint_OneSwapPromiseForOneFrame) { + LargestImagePaint_NodeRemovedBetweenRegistrationAndInvocation) { SetBodyInnerHTML(R"HTML( <div id="parent"> - <img id="1"></img> - <img id="2"></img> + <img id="target"></img> </div> )HTML"); - SetImageAndPaint("1", 5, 5); - GetFrameView().UpdateAllLifecyclePhases(); + SetImageAndPaint("target", 5, 5); + UpdateAllLifecyclePhasesForTest(); - SetImageAndPaint("2", 9, 9); - GetFrameView().UpdateAllLifecyclePhases(); + GetDocument().getElementById("parent")->RemoveChild( + GetDocument().getElementById("target")); InvokeCallback(); + + ImageRecord* record; + record = FindLargestPaintCandidate(); + EXPECT_FALSE(record); +} + +TEST_F(ImagePaintTimingDetectorTest, LargestImagePaint_IgnoreReAttached) { + SetBodyInnerHTML(R"HTML( + <div id="parent"> + </div> + )HTML"); + HTMLImageElement* image = HTMLImageElement::Create(GetDocument()); + image->setAttribute("id", "target"); + GetDocument().getElementById("parent")->AppendChild(image); + SetImageAndPaint("target", 5, 5); + UpdateAllLifecyclePhasesAndInvokeCallbackIfAny(); ImageRecord* record; record = FindLargestPaintCandidate(); EXPECT_TRUE(record); -#if defined(OS_MACOSX) - EXPECT_EQ(record->first_size, 90); -#else - EXPECT_EQ(record->first_size, 81); -#endif - EXPECT_TRUE(record->first_paint_time_after_loaded.is_null()); + GetDocument().getElementById("parent")->RemoveChild(image); + UpdateAllLifecyclePhasesAndInvokeCallbackIfAny(); + record = FindLargestPaintCandidate(); + EXPECT_FALSE(record); + + GetDocument().getElementById("parent")->AppendChild(image); + SetImageAndPaint("target", 5, 5); + UpdateAllLifecyclePhasesAndInvokeCallbackIfAny(); + record = FindLargestPaintCandidate(); + EXPECT_TRUE(record); +} + +// This test dipicts a situation when a smaller image has loaded, but a larger +// image is loading. When we call analyze, the result will be empty because +// we don't know when the largest image will finish loading. We wait until +// next analysis to make the judgement again. +// This bahavior is the same with Last Image Paint as well. +TEST_F(ImagePaintTimingDetectorTest, DiscardAnalysisWhenLargestIsLoading) { + SetBodyInnerHTML(R"HTML( + <div id="parent"> + <img height="5" width="5" id="1"></img> + <img height="9" width="9" id="2"></img> + </div> + )HTML"); + SetImageAndPaint("1", 5, 5); + UpdateAllLifecyclePhasesForTest(); + ImageRecord* record; + InvokeCallback(); + record = FindLargestPaintCandidate(); + EXPECT_FALSE(record); + + SetImageAndPaint("2", 9, 9); + UpdateAllLifecyclePhasesForTest(); InvokeCallback(); record = FindLargestPaintCandidate(); EXPECT_TRUE(record); #if defined(OS_MACOSX) - EXPECT_EQ(record->first_size, 90); + EXPECT_EQ(record->first_size, 90ul); #else - EXPECT_EQ(record->first_size, 81); + EXPECT_EQ(record->first_size, 81ul); #endif EXPECT_FALSE(record->first_paint_time_after_loaded.is_null()); } +// This is to prove that a swap time is assigned only to nodes of the frame who +// register the swap time. In other words, swap time A should match frame A; +// swap time B should match frame B. +TEST_F(ImagePaintTimingDetectorTest, MatchSwapTimeToNodesOfDifferentFrames) { + SetBodyInnerHTML(R"HTML( + <div id="parent"> + <img height="5" width="5" id="smaller"></img> + <img height="9" width="9" id="larger"></img> + </div> + )HTML"); + + SetImageAndPaint("larger", 9, 9); + UpdateAllLifecyclePhasesForTest(); + SetImageAndPaint("smaller", 5, 5); + UpdateAllLifecyclePhasesForTest(); + InvokeCallback(); + // record1 is the larger. + ImageRecord* record1 = FindLargestPaintCandidate(); + const base::TimeTicks record1Time = record1->first_paint_time_after_loaded; + GetDocument().getElementById("parent")->RemoveChild( + GetDocument().getElementById("larger")); + UpdateAllLifecyclePhasesForTest(); + InvokeCallback(); + // record2 is the smaller. + ImageRecord* record2 = FindLargestPaintCandidate(); + EXPECT_NE(record1Time, record2->first_paint_time_after_loaded); +} + +TEST_F(ImagePaintTimingDetectorTest, + LargestImagePaint_UpdateResultWhenLargestChanged) { + TimeTicks time1 = CurrentTimeTicks(); + SetBodyInnerHTML(R"HTML( + <div id="parent"> + <img id="target1"></img> + <img id="target2"></img> + </div> + )HTML"); + SetImageAndPaint("target1", 5, 5); + UpdateAllLifecyclePhasesAndInvokeCallbackIfAny(); + TimeTicks time2 = CurrentTimeTicks(); + TimeTicks result1 = LargestPaintStoredResult(); + EXPECT_GE(result1, time1); + EXPECT_GE(time2, result1); + + SetImageAndPaint("target2", 10, 10); + UpdateAllLifecyclePhasesAndInvokeCallbackIfAny(); + TimeTicks time3 = CurrentTimeTicks(); + TimeTicks result2 = LargestPaintStoredResult(); + EXPECT_GE(result2, time2); + EXPECT_GE(time3, result2); +} + TEST_F(ImagePaintTimingDetectorTest, LastImagePaint_NoImage) { SetBodyInnerHTML(R"HTML( <div></div> )HTML"); - GetFrameView().UpdateAllLifecyclePhases(); + UpdateAllLifecyclePhasesForTest(); ImageRecord* record = FindLastPaintCandidate(); EXPECT_FALSE(record); } @@ -233,46 +400,86 @@ TEST_F(ImagePaintTimingDetectorTest, LastImagePaint_OneImage) { UpdateAllLifecyclePhasesAndInvokeCallbackIfAny(); ImageRecord* record = FindLastPaintCandidate(); EXPECT_TRUE(record); - EXPECT_GT(record->first_size, 0); + EXPECT_GT(record->first_size, 0ul); EXPECT_TRUE(record->loaded); } TEST_F(ImagePaintTimingDetectorTest, LastImagePaint_Last) { + WTF::ScopedMockClock clock; SetBodyInnerHTML(R"HTML( <div id="parent"> - <img id="1"></img> - <img id="2"></img> - <img id="3"></img> + <img height="10" width="10" id="1"></img> + <img height="5" width="5" id="2"></img> + <img height="7" width="7" id="3"></img> </div> )HTML"); - TimeTicks time1 = CurrentTimeTicks(); + UpdateAllLifecyclePhasesForTest(); SetImageAndPaint("1", 10, 10); - UpdateAllLifecyclePhasesAndInvokeCallbackIfAny(); + UpdateAllLifecyclePhasesForTest(); + clock.Advance(TimeDelta::FromSecondsD(1)); + InvokeCallback(); + ImageRecord* record; record = FindLastPaintCandidate(); EXPECT_TRUE(record); - EXPECT_GE(record->first_paint_time_after_loaded, time1); + EXPECT_EQ(record->first_size, 100ul); + EXPECT_EQ(record->first_paint_time_after_loaded, + base::TimeTicks() + TimeDelta::FromSecondsD(1)); - TimeTicks time2 = CurrentTimeTicks(); SetImageAndPaint("2", 5, 5); - UpdateAllLifecyclePhasesAndInvokeCallbackIfAny(); + UpdateAllLifecyclePhasesForTest(); + clock.Advance(TimeDelta::FromSecondsD(1)); + InvokeCallback(); + record = FindLastPaintCandidate(); EXPECT_TRUE(record); - EXPECT_GE(record->first_paint_time_after_loaded, time2); +#if defined(OS_MACOSX) + EXPECT_EQ(record->first_size, 30ul); +#else + EXPECT_EQ(record->first_size, 25ul); +#endif + EXPECT_EQ(record->first_paint_time_after_loaded, + base::TimeTicks() + TimeDelta::FromSecondsD(2)); - TimeTicks time3 = CurrentTimeTicks(); SetImageAndPaint("3", 7, 7); - UpdateAllLifecyclePhasesAndInvokeCallbackIfAny(); + UpdateAllLifecyclePhasesForTest(); + clock.Advance(TimeDelta::FromSecondsD(1)); + // 6th s + InvokeCallback(); record = FindLastPaintCandidate(); EXPECT_TRUE(record); - EXPECT_GE(record->first_paint_time_after_loaded, time3); + EXPECT_GE(record->first_paint_time_after_loaded, + base::TimeTicks() + TimeDelta::FromSecondsD(3)); GetDocument().getElementById("parent")->RemoveChild( GetDocument().getElementById("3")); record = FindLastPaintCandidate(); EXPECT_TRUE(record); - EXPECT_GE(record->first_paint_time_after_loaded, time2); - EXPECT_LE(record->first_paint_time_after_loaded, time3); + EXPECT_GE(record->first_paint_time_after_loaded, + base::TimeTicks() + TimeDelta::FromSecondsD(2)); +} + +TEST_F(ImagePaintTimingDetectorTest, LastImagePaint_LastBasedOnLoadTime) { + SetBodyInnerHTML(R"HTML( + <div id="parent"> + <img height="5" width="5" id="1"></img> + </div> + )HTML"); + Element* image = GetDocument().CreateRawElement(html_names::kImgTag); + image->setAttribute(html_names::kIdAttr, "2"); + image->setAttribute(html_names::kHeightAttr, "10"); + image->setAttribute(html_names::kWidthAttr, "10"); + GetDocument().getElementById("parent")->appendChild(image); + SetImageAndPaint("2", 10, 10); + UpdateAllLifecyclePhasesAndInvokeCallbackIfAny(); + + SetImageAndPaint("1", 5, 5); + UpdateAllLifecyclePhasesAndInvokeCallbackIfAny(); + + ImageRecord* record; + record = FindLastPaintCandidate(); + EXPECT_TRUE(record); + EXPECT_EQ(record->first_size, 25ul); } TEST_F(ImagePaintTimingDetectorTest, LastImagePaint_IgnoreTheRemoved) { @@ -319,19 +526,19 @@ TEST_F(ImagePaintTimingDetectorTest, LastImagePaint_OneSwapPromiseForOneFrame) { </div> )HTML"); SetImageAndPaint("1", 5, 5); - GetFrameView().UpdateAllLifecyclePhases(); + UpdateAllLifecyclePhasesForTest(); SetImageAndPaint("2", 9, 9); - GetFrameView().UpdateAllLifecyclePhases(); + UpdateAllLifecyclePhasesForTest(); InvokeCallback(); ImageRecord* record; record = FindLastPaintCandidate(); EXPECT_TRUE(record); #if defined(OS_MACOSX) - EXPECT_EQ(record->first_size, 90); + EXPECT_EQ(record->first_size, 90ul); #else - EXPECT_EQ(record->first_size, 81); + EXPECT_EQ(record->first_size, 81ul); #endif EXPECT_TRUE(record->first_paint_time_after_loaded.is_null()); @@ -339,11 +546,141 @@ TEST_F(ImagePaintTimingDetectorTest, LastImagePaint_OneSwapPromiseForOneFrame) { record = FindLastPaintCandidate(); EXPECT_TRUE(record); #if defined(OS_MACOSX) - EXPECT_EQ(record->first_size, 90); + EXPECT_EQ(record->first_size, 90ul); #else - EXPECT_EQ(record->first_size, 81); + EXPECT_EQ(record->first_size, 81ul); #endif EXPECT_FALSE(record->first_paint_time_after_loaded.is_null()); } +TEST_F(ImagePaintTimingDetectorTest, + LastImagePaint_UpdateResultWhenLastChanged) { + TimeTicks time1 = CurrentTimeTicks(); + SetBodyInnerHTML(R"HTML( + <div id="parent"> + <img id="target1"></img> + </div> + )HTML"); + SetImageAndPaint("target1", 5, 5); + UpdateAllLifecyclePhasesAndInvokeCallbackIfAny(); + TimeTicks time2 = CurrentTimeTicks(); + TimeTicks result1 = LastPaintStoredResult(); + EXPECT_GE(result1, time1); + EXPECT_GE(time2, result1); + + Element* image = GetDocument().CreateRawElement(html_names::kImgTag); + image->setAttribute(html_names::kIdAttr, "target2"); + GetDocument().getElementById("parent")->appendChild(image); + SetImageAndPaint("target2", 2, 2); + UpdateAllLifecyclePhasesAndInvokeCallbackIfAny(); + TimeTicks time3 = CurrentTimeTicks(); + TimeTicks result2 = LastPaintStoredResult(); + EXPECT_GE(result2, time2); + EXPECT_GE(time3, result2); +} + +TEST_F(ImagePaintTimingDetectorTest, VideoImage) { + SetBodyInnerHTML(R"HTML( + <video id="target" poster="http://example.com/nonexistant.gif"></video> + )HTML"); + + SetVideoImageAndPaint("target", 5, 5); + + UpdateAllLifecyclePhasesAndInvokeCallbackIfAny(); + ImageRecord* record = FindLastPaintCandidate(); + EXPECT_TRUE(record); + EXPECT_GT(record->first_size, 0ul); + EXPECT_TRUE(record->loaded); +} + +TEST_F(ImagePaintTimingDetectorTest, VideoImage_ImageNotLoaded) { + SetBodyInnerHTML(R"HTML( + <video id="target" poster="http://example.com/nonexistant.gif"></video> + )HTML"); + + UpdateAllLifecyclePhasesAndInvokeCallbackIfAny(); + ImageRecord* record = FindLastPaintCandidate(); + EXPECT_FALSE(record); +} + +TEST_F(ImagePaintTimingDetectorTest, SVGImage) { + SetBodyInnerHTML(R"HTML( + <svg> + <image id="target" width="10" height="10" + xlink:href="http://example.com/nonexistant.jpg"/> + </svg> + )HTML"); + + SetSVGImageAndPaint("target", 5, 5); + + UpdateAllLifecyclePhasesAndInvokeCallbackIfAny(); + ImageRecord* record = FindLastPaintCandidate(); + EXPECT_TRUE(record); + EXPECT_GT(record->first_size, 0ul); + EXPECT_TRUE(record->loaded); +} + +TEST_F(ImagePaintTimingDetectorTest, BackgroundImage) { + RegisterMockedHttpURLLoad("white-1x1.png"); + SetBodyInnerHTML(R"HTML( + <style> + div { + background-image: url('white-1x1.png'); + } + </style> + <div> + place-holder + </div> + )HTML"); + UpdateAllLifecyclePhasesForTest(); + ImageRecord* record = FindLastPaintCandidate(); + EXPECT_TRUE(record); + EXPECT_EQ(CountRecords(), 1u); +} + +TEST_F(ImagePaintTimingDetectorTest, BackgroundImage_IgnoreBody) { + RegisterMockedHttpURLLoad("white-1x1.png"); + SetBodyInnerHTML(R"HTML( + <style> + body { + background-image: url('white-1x1.png'); + } + </style> + <body> + </body> + )HTML"); + UpdateAllLifecyclePhasesForTest(); + EXPECT_EQ(CountRecords(), 0u); +} + +TEST_F(ImagePaintTimingDetectorTest, BackgroundImage_IgnoreHtml) { + RegisterMockedHttpURLLoad("white-1x1.png"); + SetBodyInnerHTML(R"HTML( + <html> + <style> + html { + background-image: url('white-1x1.png'); + } + </style> + </html> + )HTML"); + UpdateAllLifecyclePhasesForTest(); + EXPECT_EQ(CountRecords(), 0u); +} + +TEST_F(ImagePaintTimingDetectorTest, BackgroundImage_IgnoreGradient) { + SetBodyInnerHTML(R"HTML( + <style> + div { + background-image: linear-gradient(blue, yellow); + } + </style> + <div> + place-holder + </div> + )HTML"); + UpdateAllLifecyclePhasesForTest(); + EXPECT_EQ(CountRecords(), 0u); +} + } // namespace blink diff --git a/chromium/third_party/blink/renderer/core/paint/image_painter.cc b/chromium/third_party/blink/renderer/core/paint/image_painter.cc index f9eeb52ca2e..c8c341eac76 100644 --- a/chromium/third_party/blink/renderer/core/paint/image_painter.cc +++ b/chromium/third_party/blink/renderer/core/paint/image_painter.cc @@ -22,6 +22,7 @@ #include "third_party/blink/renderer/platform/graphics/paint/display_item_cache_skipper.h" #include "third_party/blink/renderer/platform/graphics/paint/drawing_recorder.h" #include "third_party/blink/renderer/platform/graphics/path.h" +#include "third_party/blink/renderer/platform/graphics/placeholder_image.h" #include "third_party/blink/renderer/platform/graphics/scoped_interpolation_quality.h" namespace blink { @@ -52,6 +53,8 @@ void ImagePainter::PaintAreaElementFocusRing(const PaintInfo& paint_info) { // do it for an area within an image, so we don't call // LayoutTheme::themeDrawsFocusRing here. + // We use EnsureComputedStyle() instead of GetComputedStyle() here because + // <area> is used and its style applied even if it has display:none. const ComputedStyle& area_element_style = *area_element.EnsureComputedStyle(); // If the outline width is 0 we want to avoid drawing anything even if we // don't use the value directly. @@ -172,8 +175,8 @@ void ImagePainter::PaintIntoRect(GraphicsContext& context, TRACE_EVENT1(TRACE_DISABLED_BY_DEFAULT("devtools.timeline"), "PaintImage", "data", - InspectorPaintImageEvent::Data(layout_image_, src_rect, - FloatRect(dest_rect))); + inspector_paint_image_event::Data(layout_image_, src_rect, + FloatRect(dest_rect))); ScopedInterpolationQuality interpolation_quality_scope( context, layout_image_.StyleRef().GetInterpolationQuality()); @@ -184,6 +187,17 @@ void ImagePainter::PaintIntoRect(GraphicsContext& context, ? ToHTMLImageElement(node)->GetDecodingModeForPainting( image->paint_image_id()) : Image::kUnspecifiedDecode; + + if (layout_image_.IsImagePolicyViolated()) { + // Does not set an observer for the placeholder image, setting it to null. + scoped_refptr<PlaceholderImage> placeholder_image = + PlaceholderImage::Create(nullptr, image->Size(), + image->Data() ? image->Data()->size() : 0); + placeholder_image->SetIconAndTextScaleFactor( + layout_image_.GetFrame()->PageZoomFactor()); + image = std::move(placeholder_image); + } + context.DrawImage( image.get(), decode_mode, FloatRect(pixel_snapped_dest_rect), &src_rect, SkBlendMode::kSrcOver, diff --git a/chromium/third_party/blink/renderer/core/paint/inline_flow_box_painter.cc b/chromium/third_party/blink/renderer/core/paint/inline_flow_box_painter.cc index a5320c2361f..bd82f77257b 100644 --- a/chromium/third_party/blink/renderer/core/paint/inline_flow_box_painter.cc +++ b/chromium/third_party/blink/renderer/core/paint/inline_flow_box_painter.cc @@ -14,6 +14,7 @@ #include "third_party/blink/renderer/core/paint/paint_info.h" #include "third_party/blink/renderer/platform/graphics/graphics_context_state_saver.h" #include "third_party/blink/renderer/platform/graphics/paint/drawing_recorder.h" +#include "third_party/blink/renderer/platform/graphics/paint/hit_test_display_item.h" namespace blink { @@ -56,7 +57,7 @@ void InlineFlowBoxPainter::Paint(const PaintInfo& paint_info, inline_flow_box_.FlipForWritingMode(overflow_rect); overflow_rect.MoveBy(paint_offset); - if (!paint_info.GetCullRect().IntersectsCullRect(overflow_rect)) + if (!paint_info.GetCullRect().Intersects(overflow_rect)) return; if (paint_info.phase == PaintPhase::kMask) { @@ -239,12 +240,10 @@ void InlineFlowBoxPainter::PaintMask(const PaintInfo& paint_info, return; if (DrawingRecorder::UseCachedDrawingIfPossible( - paint_info.context, inline_flow_box_, - DisplayItem::PaintPhaseToDrawingType(paint_info.phase))) + paint_info.context, inline_flow_box_, paint_info.phase)) return; - DrawingRecorder recorder( - paint_info.context, inline_flow_box_, - DisplayItem::PaintPhaseToDrawingType(paint_info.phase)); + DrawingRecorder recorder(paint_info.context, inline_flow_box_, + paint_info.phase); LayoutRect paint_rect = AdjustedPaintRect(paint_offset); @@ -337,11 +336,15 @@ void InlineFlowBoxPainter::RecordHitTestData(const PaintInfo& paint_info, LayoutObject* layout_object = LineLayoutAPIShim::LayoutObjectFrom(inline_flow_box_.GetLineLayoutItem()); + // If an object is not visible, it does not participate in hit testing. + if (layout_object->StyleRef().Visibility() != EVisibility::kVisible) + return; + auto touch_action = layout_object->EffectiveWhitelistedTouchAction(); if (touch_action == TouchAction::kTouchActionAuto) return; - HitTestData::RecordHitTestRect( + HitTestDisplayItem::Record( paint_info.context, inline_flow_box_, HitTestRect(AdjustedPaintRect(paint_offset), touch_action)); } diff --git a/chromium/third_party/blink/renderer/core/paint/inline_painter.cc b/chromium/third_party/blink/renderer/core/paint/inline_painter.cc index b7b15f77950..c410d15e3a8 100644 --- a/chromium/third_party/blink/renderer/core/paint/inline_painter.cc +++ b/chromium/third_party/blink/renderer/core/paint/inline_painter.cc @@ -22,7 +22,7 @@ void InlinePainter::Paint(const PaintInfo& paint_info) { if (RuntimeEnabledFeatures::LayoutNGEnabled()) { // Inline box with self painting layer is painted in this code path. - if (auto* block_flow = layout_inline_.EnclosingNGBlockFlow()) { + if (auto* block_flow = layout_inline_.ContainingNGBlockFlow()) { if (auto* block_flow_fragment = block_flow->PaintFragment()) { block_flow_fragment->PaintInlineBoxForDescendants( local_paint_info, paint_offset, &layout_inline_); diff --git a/chromium/third_party/blink/renderer/core/paint/inline_text_box_painter.cc b/chromium/third_party/blink/renderer/core/paint/inline_text_box_painter.cc index f1cecb66571..d5abae00013 100644 --- a/chromium/third_party/blink/renderer/core/paint/inline_text_box_painter.cc +++ b/chromium/third_party/blink/renderer/core/paint/inline_text_box_painter.cc @@ -77,7 +77,7 @@ static LineLayoutItem EnclosingUnderlineObject( return current; if (Node* node = current.GetNode()) { - if (IsHTMLAnchorElement(node) || node->HasTagName(HTMLNames::fontTag)) + if (IsHTMLAnchorElement(node) || node->HasTagName(html_names::kFontTag)) return current; } } @@ -147,11 +147,9 @@ void InlineTextBoxPainter::Paint(const PaintInfo& paint_info, base::Optional<DrawingRecorder> recorder; if (paint_info.phase != PaintPhase::kTextClip) { if (DrawingRecorder::UseCachedDrawingIfPossible( - paint_info.context, inline_text_box_, - DisplayItem::PaintPhaseToDrawingType(paint_info.phase))) + paint_info.context, inline_text_box_, paint_info.phase)) return; - recorder.emplace(paint_info.context, inline_text_box_, - DisplayItem::PaintPhaseToDrawingType(paint_info.phase)); + recorder.emplace(paint_info.context, inline_text_box_, paint_info.phase); } GraphicsContext& context = paint_info.context; diff --git a/chromium/third_party/blink/renderer/core/paint/link_highlight_impl.cc b/chromium/third_party/blink/renderer/core/paint/link_highlight_impl.cc index a2bcd6715c6..b09c84aa9cb 100644 --- a/chromium/third_party/blink/renderer/core/paint/link_highlight_impl.cc +++ b/chromium/third_party/blink/renderer/core/paint/link_highlight_impl.cc @@ -59,7 +59,7 @@ #include "third_party/blink/renderer/platform/graphics/paint/drawing_recorder.h" #include "third_party/blink/renderer/platform/graphics/paint/paint_canvas.h" #include "third_party/blink/renderer/platform/graphics/paint/paint_recorder.h" -#include "third_party/blink/renderer/platform/layout_test_support.h" +#include "third_party/blink/renderer/platform/web_test_support.h" #include "third_party/blink/renderer/platform/wtf/vector.h" #include "third_party/skia/include/core/SkMatrix44.h" #include "ui/gfx/geometry/rect.h" @@ -246,6 +246,13 @@ bool LinkHighlightImpl::ComputeHighlightLayerPathAndPosition( content_layer_->SetPosition(bounding_rect.Location()); + if (RuntimeEnabledFeatures::BlinkGenPropertyTreesEnabled()) { + FloatPoint offset(current_graphics_layer_->GetOffsetFromTransformNode()); + offset.MoveBy(bounding_rect.Location()); + content_layer_->SetOffsetToTransformParent( + gfx::Vector2dF(offset.X(), offset.Y())); + } + return path_has_changed; } @@ -310,15 +317,14 @@ void LinkHighlightImpl::StartHighlightAnimationIfNeeded() { curve->AddKeyframe(CompositorFloatKeyframe( extra_duration_required.InSecondsF(), kStartOpacity, timing_function)); } - // For layout tests we don't fade out. + // For web tests we don't fade out. curve->AddKeyframe(CompositorFloatKeyframe( (kFadeDuration + extra_duration_required).InSecondsF(), - LayoutTestSupport::IsRunningLayoutTest() ? kStartOpacity : 0, - timing_function)); + WebTestSupport::IsRunningWebTest() ? kStartOpacity : 0, timing_function)); std::unique_ptr<CompositorKeyframeModel> keyframe_model = - CompositorKeyframeModel::Create(*curve, CompositorTargetProperty::OPACITY, - 0, 0); + CompositorKeyframeModel::Create( + *curve, compositor_target_property::OPACITY, 0, 0); content_layer_->SetIsDrawable(true); compositor_animation_->AddKeyframeModel(std::move(keyframe_model)); @@ -348,6 +354,12 @@ class LinkHighlightDisplayItemClientForTracking : public DisplayItemClient { }; void LinkHighlightImpl::UpdateGeometry() { + if (!node_ || !node_->GetLayoutObject()) { + ClearGraphicsLayerLinkHighlightPointer(); + ReleaseResources(); + return; + } + // To avoid unnecessary updates (e.g. other entities have requested animations // from our WebViewImpl), only proceed if we actually requested an update. if (!geometry_needs_update_) @@ -355,28 +367,22 @@ void LinkHighlightImpl::UpdateGeometry() { geometry_needs_update_ = false; - bool has_layout_object = node_ && node_->GetLayoutObject(); - if (has_layout_object) { - const LayoutBoxModelObject& paint_invalidation_container = - node_->GetLayoutObject()->ContainerForPaintInvalidation(); - AttachLinkHighlightToCompositingLayer(paint_invalidation_container); - if (ComputeHighlightLayerPathAndPosition(paint_invalidation_container)) { - // We only need to invalidate the layer if the highlight size has changed, - // otherwise we can just re-position the layer without needing to - // repaint. - content_layer_->SetNeedsDisplay(); - - if (current_graphics_layer_) { - gfx::Rect rect = gfx::ToEnclosingRect( - gfx::RectF(Layer()->position(), gfx::SizeF(Layer()->bounds()))); - current_graphics_layer_->TrackRasterInvalidation( - LinkHighlightDisplayItemClientForTracking(), IntRect(rect), - PaintInvalidationReason::kFullLayer); - } + const LayoutBoxModelObject& paint_invalidation_container = + node_->GetLayoutObject()->ContainerForPaintInvalidation(); + AttachLinkHighlightToCompositingLayer(paint_invalidation_container); + if (ComputeHighlightLayerPathAndPosition(paint_invalidation_container)) { + // We only need to invalidate the layer if the highlight size has changed, + // otherwise we can just re-position the layer without needing to + // repaint. + content_layer_->SetNeedsDisplay(); + + if (current_graphics_layer_) { + gfx::Rect rect = gfx::ToEnclosingRect( + gfx::RectF(Layer()->position(), gfx::SizeF(Layer()->bounds()))); + current_graphics_layer_->TrackRasterInvalidation( + LinkHighlightDisplayItemClientForTracking(), IntRect(rect), + PaintInvalidationReason::kFullLayer); } - } else { - ClearGraphicsLayerLinkHighlightPointer(); - ReleaseResources(); } } diff --git a/chromium/third_party/blink/renderer/core/paint/link_highlight_impl_test.cc b/chromium/third_party/blink/renderer/core/paint/link_highlight_impl_test.cc index 3282070f328..313cc562e8f 100644 --- a/chromium/third_party/blink/renderer/core/paint/link_highlight_impl_test.cc +++ b/chromium/third_party/blink/renderer/core/paint/link_highlight_impl_test.cc @@ -76,7 +76,7 @@ class LinkHighlightImplTest : public testing::Test, } void SetUp() override { - WebURL url = URLTestHelpers::RegisterMockedURLLoadFromBase( + WebURL url = url_test_helpers::RegisterMockedURLLoadFromBase( WebString::FromUTF8("http://www.test.com/"), test::CoreTestDataPath(), WebString::FromUTF8("test_touch_link_highlight.html")); web_view_helper_.InitializeAndLoad(url.GetString().Utf8()); @@ -108,7 +108,12 @@ class LinkHighlightImplTest : public testing::Test, return local_frame_view->GetPaintArtifactCompositorForTesting(); } - FrameTestHelpers::WebViewHelper web_view_helper_; + void UpdateAllLifecyclePhases() { + web_view_helper_.GetWebView()->MainFrameWidget()->UpdateAllLifecyclePhases( + WebWidget::LifecycleUpdateReason::kTest); + } + + frame_test_helpers::WebViewHelper web_view_helper_; }; INSTANTIATE_TEST_CASE_P(All, LinkHighlightImplTest, testing::Bool()); @@ -118,7 +123,7 @@ TEST_P(LinkHighlightImplTest, verifyWebViewImplIntegration) { int page_width = 640; int page_height = 480; web_view_impl->Resize(WebSize(page_width, page_height)); - web_view_impl->UpdateAllLifecyclePhases(); + UpdateAllLifecyclePhases(); WebGestureEvent touch_event(WebInputEvent::kGestureShowPress, WebInputEvent::kNoModifiers, @@ -171,7 +176,7 @@ TEST_P(LinkHighlightImplTest, resetDuringNodeRemoval) { int page_width = 640; int page_height = 480; web_view_impl->Resize(WebSize(page_width, page_height)); - web_view_impl->UpdateAllLifecyclePhases(); + UpdateAllLifecyclePhases(); WebGestureEvent touch_event(WebInputEvent::kGestureShowPress, WebInputEvent::kNoModifiers, @@ -193,7 +198,7 @@ TEST_P(LinkHighlightImplTest, resetDuringNodeRemoval) { EXPECT_TRUE(highlight_layer->GetLinkHighlights().at(0)); touch_node->remove(IGNORE_EXCEPTION_FOR_TESTING); - web_view_impl->UpdateAllLifecyclePhases(); + UpdateAllLifecyclePhases(); EXPECT_EQ(0U, highlight_layer->GetLinkHighlights().size()); } @@ -204,7 +209,7 @@ TEST_P(LinkHighlightImplTest, resetLayerTreeView) { int page_width = 640; int page_height = 480; web_view_impl->Resize(WebSize(page_width, page_height)); - web_view_impl->UpdateAllLifecyclePhases(); + UpdateAllLifecyclePhases(); WebGestureEvent touch_event(WebInputEvent::kGestureShowPress, WebInputEvent::kNoModifiers, @@ -238,7 +243,7 @@ TEST_P(LinkHighlightImplTest, HighlightLayerEffectNode) { web_view_impl->Resize(WebSize(page_width, page_height)); paint_artifact_compositor()->EnableExtraDataForTesting(); - web_view_impl->UpdateAllLifecyclePhases(); + UpdateAllLifecyclePhases(); size_t layer_count_before_highlight = ContentLayerCount(); WebGestureEvent touch_event(WebInputEvent::kGestureShowPress, @@ -272,7 +277,7 @@ TEST_P(LinkHighlightImplTest, HighlightLayerEffectNode) { EXPECT_TRUE(highlight->effect()->RequiresCompositingForAnimation()); touch_node->remove(IGNORE_EXCEPTION_FOR_TESTING); - web_view_impl->UpdateAllLifecyclePhases(); + UpdateAllLifecyclePhases(); // Removing the highlight layer should drop the cc layer count by one. EXPECT_EQ(layer_count_before_highlight, ContentLayerCount()); } diff --git a/chromium/third_party/blink/renderer/core/paint/list_marker_painter.cc b/chromium/third_party/blink/renderer/core/paint/list_marker_painter.cc index a8567658ffd..4653f90e542 100644 --- a/chromium/third_party/blink/renderer/core/paint/list_marker_painter.cc +++ b/chromium/third_party/blink/renderer/core/paint/list_marker_painter.cc @@ -144,8 +144,8 @@ void ListMarkerPainter::Paint(const PaintInfo& paint_info) { // Text is not arbitrary. We can judge whether it's RTL from the first // character, and we only need to handle the direction RightToLeft for now. bool text_needs_reversing = - WTF::Unicode::Direction(layout_list_marker_.GetText()[0]) == - WTF::Unicode::kRightToLeft; + WTF::unicode::Direction(layout_list_marker_.GetText()[0]) == + WTF::unicode::kRightToLeft; StringBuilder reversed_text; if (text_needs_reversing) { unsigned length = layout_list_marker_.GetText().length(); diff --git a/chromium/third_party/blink/renderer/core/paint/ng/ng_box_fragment_painter.cc b/chromium/third_party/blink/renderer/core/paint/ng/ng_box_fragment_painter.cc index 23ee4bd5b2b..226cd35199c 100644 --- a/chromium/third_party/blink/renderer/core/paint/ng/ng_box_fragment_painter.cc +++ b/chromium/third_party/blink/renderer/core/paint/ng/ng_box_fragment_painter.cc @@ -33,6 +33,7 @@ #include "third_party/blink/renderer/core/paint/object_painter.h" #include "third_party/blink/renderer/core/paint/paint_info.h" #include "third_party/blink/renderer/core/paint/paint_layer.h" +#include "third_party/blink/renderer/core/paint/paint_layer_scrollable_area.h" #include "third_party/blink/renderer/core/paint/paint_phase.h" #include "third_party/blink/renderer/core/paint/scoped_paint_state.h" #include "third_party/blink/renderer/core/paint/scrollable_area_painter.h" @@ -41,6 +42,7 @@ #include "third_party/blink/renderer/platform/graphics/graphics_context_state_saver.h" #include "third_party/blink/renderer/platform/graphics/paint/display_item_cache_skipper.h" #include "third_party/blink/renderer/platform/graphics/paint/drawing_recorder.h" +#include "third_party/blink/renderer/platform/graphics/paint/hit_test_display_item.h" #include "third_party/blink/renderer/platform/scroll/scroll_types.h" namespace blink { @@ -200,6 +202,10 @@ void NGBoxFragmentPainter::RecordHitTestData(const PaintInfo& paint_info, if (paint_info.GetGlobalPaintFlags() & kGlobalPaintFlattenCompositingLayers) return; + // If an object is not visible, it does not participate in hit testing. + if (box_fragment_.Style().Visibility() != EVisibility::kVisible) + return; + const NGPhysicalFragment& physical_fragment = PhysicalFragment(); auto touch_action = physical_fragment.EffectiveWhitelistedTouchAction(); if (touch_action == TouchAction::kTouchActionAuto) @@ -214,7 +220,7 @@ void NGBoxFragmentPainter::RecordHitTestData(const PaintInfo& paint_info, if (physical_fragment.IsInline()) border_box.offset += box_fragment_.InlineOffsetToContainerBox(); border_box.offset += NGPhysicalOffset(paint_offset); - HitTestData::RecordHitTestRect( + HitTestDisplayItem::Record( paint_info.context, box_fragment_, HitTestRect(border_box.ToLayoutRect(), touch_action)); } @@ -314,17 +320,15 @@ void NGBoxFragmentPainter::PaintBlockFlowContents( LayoutRect overflow_rect(box_fragment_.ChildrenInkOverflow()); overflow_rect.MoveBy(paint_offset); - if (!paint_info.GetCullRect().IntersectsCullRect(overflow_rect)) + if (!paint_info.GetCullRect().Intersects(overflow_rect)) return; if (paint_info.phase == PaintPhase::kMask) { if (DrawingRecorder::UseCachedDrawingIfPossible( - paint_info.context, box_fragment_, - DisplayItem::PaintPhaseToDrawingType(paint_info.phase))) + paint_info.context, box_fragment_, paint_info.phase)) return; - DrawingRecorder recorder( - paint_info.context, box_fragment_, - DisplayItem::PaintPhaseToDrawingType(paint_info.phase)); + DrawingRecorder recorder(paint_info.context, box_fragment_, + paint_info.phase); PaintMask(paint_info, paint_offset); return; } @@ -353,7 +357,7 @@ void NGBoxFragmentPainter::PaintInlineChild(const NGPaintFragment& child, } void NGBoxFragmentPainter::PaintBlockChildren(const PaintInfo& paint_info) { - for (const auto& child : box_fragment_.Children()) { + for (const NGPaintFragment* child : box_fragment_.Children()) { const NGPhysicalFragment& fragment = child->PhysicalFragment(); if (child->HasSelfPaintingLayer() || fragment.IsFloating()) continue; @@ -371,9 +375,9 @@ void NGBoxFragmentPainter::PaintBlockChildren(const PaintInfo& paint_info) { } void NGBoxFragmentPainter::PaintFloatingChildren( - const Vector<scoped_refptr<NGPaintFragment>>& children, + NGPaintFragment::ChildList children, const PaintInfo& paint_info) { - for (const auto& child : children) { + for (const NGPaintFragment* child : children) { const NGPhysicalFragment& fragment = child->PhysicalFragment(); if (child->HasSelfPaintingLayer()) continue; @@ -450,8 +454,7 @@ void NGBoxFragmentPainter::PaintBoxDecorationBackground( LayoutRect paint_rect; base::Optional<ScopedBoxContentsPaintState> contents_paint_state; - if (IsPaintingBackgroundOfPaintContainerIntoScrollingContentsLayer( - box_fragment_, paint_info)) { + if (IsPaintingScrollingBackground(box_fragment_, paint_info)) { // For the case where we are painting the background into the scrolling // contents layer of a composited scroller we need to include the entire // overflow rect. @@ -484,11 +487,9 @@ void NGBoxFragmentPainter::PaintBoxDecorationBackground( bool NGBoxFragmentPainter::BackgroundIsKnownToBeOpaque( const PaintInfo& paint_info) { const LayoutBox& layout_box = ToLayoutBox(*box_fragment_.GetLayoutObject()); - LayoutRect bounds = - IsPaintingBackgroundOfPaintContainerIntoScrollingContentsLayer( - box_fragment_, paint_info) - ? layout_box.LayoutOverflowRect() - : layout_box.SelfVisualOverflowRect(); + LayoutRect bounds = IsPaintingScrollingBackground(box_fragment_, paint_info) + ? layout_box.LayoutOverflowRect() + : layout_box.SelfVisualOverflowRect(); return layout_box.BackgroundIsKnownToBeOpaqueInRect(bounds); } @@ -501,8 +502,7 @@ void NGBoxFragmentPainter::PaintBoxDecorationBackgroundWithRect( const LayoutBox& layout_box = ToLayoutBox(layout_object); bool painting_overflow_contents = - IsPaintingBackgroundOfPaintContainerIntoScrollingContentsLayer( - box_fragment_, paint_info); + IsPaintingScrollingBackground(box_fragment_, paint_info); const ComputedStyle& style = box_fragment_.Style(); base::Optional<DisplayItemCacheSkipper> cache_skipper; @@ -519,11 +519,10 @@ void NGBoxFragmentPainter::PaintBoxDecorationBackgroundWithRect( } const DisplayItemClient& display_item_client = - painting_overflow_contents ? static_cast<const DisplayItemClient&>( - *layout_box.Layer() - ->GetCompositedLayerMapping() - ->ScrollingContentsLayer()) - : box_fragment_; + painting_overflow_contents + ? layout_box.GetScrollableArea() + ->GetScrollingBackgroundDisplayItemClient() + : box_fragment_; if (DrawingRecorder::UseCachedDrawingIfPossible( paint_info.context, display_item_client, DisplayItem::kBoxDecorationBackground)) @@ -544,7 +543,15 @@ void NGBoxFragmentPainter::PaintBoxDecorationBackgroundWithRect( PaintNormalBoxShadow(paint_info, paint_rect, style, border_edges_.line_left, border_edges_.line_right); - if (BleedAvoidanceIsClipping(box_decoration_data.bleed_avoidance)) { + if (box_fragment_.HasSelfPaintingLayer() && layout_box.IsTableCell() && + ToLayoutTableCell(layout_box).Table()->ShouldCollapseBorders()) { + // We have to clip here because the background would paint on top of the + // collapsed table borders otherwise, since this is a self-painting layer. + LayoutRect clip_rect = paint_rect; + clip_rect.Expand(ToLayoutTableCell(layout_box).BorderInsets()); + state_saver.Save(); + paint_info.context.Clip(PixelSnappedIntRect(clip_rect)); + } else if (BleedAvoidanceIsClipping(box_decoration_data.bleed_avoidance)) { state_saver.Save(); FloatRoundedRect border = style.GetRoundedBorderFor( paint_rect, border_edges_.line_left, border_edges_.line_right); @@ -611,9 +618,7 @@ void NGBoxFragmentPainter::PaintBackground( BackgroundBleedAvoidance bleed_avoidance) { const LayoutObject& layout_object = *box_fragment_.GetLayoutObject(); const LayoutBox& layout_box = ToLayoutBox(layout_object); - if (layout_box.IsDocumentElement()) - return; - if (layout_box.BackgroundStolenForBeingBody()) + if (layout_box.BackgroundTransfersToView()) return; if (layout_box.BackgroundIsKnownToBeObscured()) return; @@ -698,10 +703,9 @@ void NGBoxFragmentPainter::PaintAllPhasesAtomically( } void NGBoxFragmentPainter::PaintLineBoxChildren( - const Vector<scoped_refptr<NGPaintFragment>>& line_boxes, + NGPaintFragment::ChildList line_boxes, const PaintInfo& paint_info, const LayoutPoint& paint_offset) { - // Only paint during the foreground/selection phases. if (paint_info.phase != PaintPhase::kForeground && paint_info.phase != PaintPhase::kSelection && @@ -725,7 +729,7 @@ void NGBoxFragmentPainter::PaintLineBoxChildren( return; // TODO(layout-dev): Early return if no line intersects cull rect. - for (const auto& line : line_boxes) { + for (const NGPaintFragment* line : line_boxes) { if (line->PhysicalFragment().IsFloatingOrOutOfFlowPositioned()) continue; const LayoutPoint child_offset = @@ -741,10 +745,10 @@ void NGBoxFragmentPainter::PaintLineBoxChildren( } void NGBoxFragmentPainter::PaintInlineChildren( - const Vector<scoped_refptr<NGPaintFragment>>& inline_children, + NGPaintFragment::ChildList inline_children, const PaintInfo& paint_info, const LayoutPoint& paint_offset) { - for (const auto& child : inline_children) { + for (const NGPaintFragment* child : inline_children) { if (child->PhysicalFragment().IsFloating()) continue; if (child->PhysicalFragment().IsAtomicInline()) { @@ -756,7 +760,7 @@ void NGBoxFragmentPainter::PaintInlineChildren( } void NGBoxFragmentPainter::PaintInlineChildrenOutlines( - const Vector<scoped_refptr<NGPaintFragment>>& line_boxes, + NGPaintFragment::ChildList line_boxes, const PaintInfo& paint_info, const LayoutPoint& paint_offset) { // TODO(layout-dev): Implement. @@ -797,11 +801,9 @@ void NGBoxFragmentPainter::PaintTextChild(const NGPaintFragment& text_fragment, base::Optional<DrawingRecorder> recorder; if (paint_info.phase != PaintPhase::kTextClip) { if (DrawingRecorder::UseCachedDrawingIfPossible( - paint_info.context, text_fragment, - DisplayItem::PaintPhaseToDrawingType(paint_info.phase))) + paint_info.context, text_fragment, paint_info.phase)) return; - recorder.emplace(paint_info.context, text_fragment, - DisplayItem::PaintPhaseToDrawingType(paint_info.phase)); + recorder.emplace(paint_info.context, text_fragment, paint_info.phase); } const NGPhysicalTextFragment& physical_text_fragment = @@ -848,10 +850,9 @@ void NGBoxFragmentPainter::PaintAtomicInline(const PaintInfo& paint_info) { PaintAllPhasesAtomically(paint_info, is_self_painting); } -bool NGBoxFragmentPainter:: - IsPaintingBackgroundOfPaintContainerIntoScrollingContentsLayer( - const NGPaintFragment& fragment, - const PaintInfo& paint_info) { +bool NGBoxFragmentPainter::IsPaintingScrollingBackground( + const NGPaintFragment& fragment, + const PaintInfo& paint_info) { // TODO(layout-dev): Change paint_info.PaintContainer to accept fragments // once LayoutNG supports scrolling containers. return paint_info.PaintFlags() & kPaintLayerPaintingOverflowContents && @@ -916,8 +917,7 @@ LayoutRect NGBoxFragmentPainter::AdjustRectForScrolledContent( // Clip to the overflow area. if (info.is_clipped_with_local_scrolling && - !IsPaintingBackgroundOfPaintContainerIntoScrollingContentsLayer( - box_fragment_, paint_info)) { + !IsPaintingScrollingBackground(box_fragment_, paint_info)) { context.Clip(FloatRect(physical.OverflowClipRect(rect.Location()))); // Adjust the paint rect to reflect a scrolled content box with borders at @@ -1170,12 +1170,14 @@ bool NGBoxFragmentPainter::HitTestChildBoxFragment( bool NGBoxFragmentPainter::HitTestChildren( HitTestResult& result, - const Vector<scoped_refptr<NGPaintFragment>>& children, + NGPaintFragment::ChildList children, const HitTestLocation& location_in_container, const LayoutPoint& accumulated_offset, HitTestAction action) { - for (auto iter = children.rbegin(); iter != children.rend(); iter++) { - const scoped_refptr<NGPaintFragment>& child = *iter; + Vector<NGPaintFragment*, 16> child_vector; + children.ToList(&child_vector); + for (unsigned i = child_vector.size(); i;) { + const NGPaintFragment* child = child_vector[--i]; const NGPhysicalOffset offset = child->Offset(); if (child->HasSelfPaintingLayer()) continue; @@ -1206,8 +1208,7 @@ bool NGBoxFragmentPainter::HitTestChildren( continue; // Hit test culled inline boxes between |fragment| and its parent fragment. - const NGPaintFragment* previous_sibling = - std::next(iter) == children.rend() ? nullptr : std::next(iter)->get(); + const NGPaintFragment* previous_sibling = i ? child_vector[i - 1] : nullptr; if (HitTestCulledInlineAncestors(result, *child, previous_sibling, location_in_container, child_physical_offset)) diff --git a/chromium/third_party/blink/renderer/core/paint/ng/ng_box_fragment_painter.h b/chromium/third_party/blink/renderer/core/paint/ng/ng_box_fragment_painter.h index 269fbdd14ad..440d6ad9892 100644 --- a/chromium/third_party/blink/renderer/core/paint/ng/ng_box_fragment_painter.h +++ b/chromium/third_party/blink/renderer/core/paint/ng/ng_box_fragment_painter.h @@ -10,6 +10,7 @@ #include "third_party/blink/renderer/core/layout/ng/geometry/ng_border_edges.h" #include "third_party/blink/renderer/core/layout/ng/ng_physical_box_fragment.h" #include "third_party/blink/renderer/core/paint/box_painter_base.h" +#include "third_party/blink/renderer/core/paint/ng/ng_paint_fragment.h" #include "third_party/blink/renderer/platform/geometry/layout_point.h" #include "third_party/blink/renderer/platform/geometry/layout_size.h" #include "third_party/blink/renderer/platform/wtf/allocator.h" @@ -21,7 +22,6 @@ class HitTestLocation; class HitTestRequest; class HitTestResult; class LayoutRect; -class NGPaintFragment; class NGPhysicalFragment; class ScopedPaintState; struct PaintInfo; @@ -64,9 +64,7 @@ class NGBoxFragmentPainter : public BoxPainterBase { const LayoutRect&) override; private: - bool IsPaintingBackgroundOfPaintContainerIntoScrollingContentsLayer( - const NGPaintFragment&, - const PaintInfo&); + bool IsPaintingScrollingBackground(const NGPaintFragment&, const PaintInfo&); bool ShouldPaint(const ScopedPaintState&) const; void PaintBoxDecorationBackground(const PaintInfo&, @@ -78,16 +76,15 @@ class NGBoxFragmentPainter : public BoxPainterBase { void PaintAllPhasesAtomically(const PaintInfo&, bool is_self_painting); void PaintBlockChildren(const PaintInfo&); - void PaintLineBoxChildren(const Vector<scoped_refptr<NGPaintFragment>>&, + void PaintLineBoxChildren(NGPaintFragment::ChildList, const PaintInfo&, const LayoutPoint& paint_offset); - void PaintInlineChildren(const Vector<scoped_refptr<NGPaintFragment>>&, + void PaintInlineChildren(NGPaintFragment::ChildList, const PaintInfo&, const LayoutPoint& paint_offset); - void PaintInlineChildrenOutlines( - const Vector<scoped_refptr<NGPaintFragment>>&, - const PaintInfo&, - const LayoutPoint& paint_offset); + void PaintInlineChildrenOutlines(NGPaintFragment::ChildList, + const PaintInfo&, + const LayoutPoint& paint_offset); void PaintInlineChildBoxUsingLegacyFallback(const NGPhysicalFragment&, const PaintInfo&); void PaintBlockFlowContents(const PaintInfo&, @@ -99,8 +96,7 @@ class NGBoxFragmentPainter : public BoxPainterBase { void PaintTextChild(const NGPaintFragment&, const PaintInfo&, const LayoutPoint& paint_offset); - void PaintFloatingChildren(const Vector<scoped_refptr<NGPaintFragment>>&, - const PaintInfo&); + void PaintFloatingChildren(NGPaintFragment::ChildList, const PaintInfo&); void PaintFloats(const PaintInfo&); void PaintMask(const PaintInfo&, const LayoutPoint& paint_offset); void PaintOverflowControlsIfNeeded(const PaintInfo&, @@ -127,7 +123,7 @@ class NGBoxFragmentPainter : public BoxPainterBase { // box in paint layer. Note that this includes scrolling offset when the // container has 'overflow: scroll'. bool HitTestChildren(HitTestResult&, - const Vector<scoped_refptr<NGPaintFragment>>&, + NGPaintFragment::ChildList, const HitTestLocation& location_in_container, const LayoutPoint& physical_offset, HitTestAction); diff --git a/chromium/third_party/blink/renderer/core/paint/ng/ng_fieldset_painter.cc b/chromium/third_party/blink/renderer/core/paint/ng/ng_fieldset_painter.cc index edcc67c4e24..603f9a6837d 100644 --- a/chromium/third_party/blink/renderer/core/paint/ng/ng_fieldset_painter.cc +++ b/chromium/third_party/blink/renderer/core/paint/ng/ng_fieldset_painter.cc @@ -24,9 +24,9 @@ void NGFieldsetPainter::PaintBoxDecorationBackground( const LayoutPoint paint_offset) { const NGPaintFragment* legend = nullptr; if (fieldset_.Children().size()) { - const auto first_child = fieldset_.Children()[0]; - if (first_child->PhysicalFragment().IsRenderedLegend()) - legend = &(*first_child); + const auto& first_child = fieldset_.Children().front(); + if (first_child.PhysicalFragment().IsRenderedLegend()) + legend = &first_child; } // Paint the fieldset (background, other decorations, and) border, with the diff --git a/chromium/third_party/blink/renderer/core/paint/ng/ng_paint_fragment.cc b/chromium/third_party/blink/renderer/core/paint/ng/ng_paint_fragment.cc index 5881265aa5c..81a6fad320b 100644 --- a/chromium/third_party/blink/renderer/core/paint/ng/ng_paint_fragment.cc +++ b/chromium/third_party/blink/renderer/core/paint/ng/ng_paint_fragment.cc @@ -34,6 +34,18 @@ namespace blink { namespace { +struct SameSizeAsNGPaintFragment : public RefCounted<NGPaintFragment>, + public DisplayItemClient, + public ImageResourceObserver { + void* pointers[6]; + NGPhysicalOffset offsets[2]; + LayoutRect rects[1]; + unsigned flags; +}; + +static_assert(sizeof(NGPaintFragment) == sizeof(SameSizeAsNGPaintFragment), + "NGPaintFragment should stay small."); + NGLogicalRect ComputeLogicalRectFor(const NGPhysicalOffsetRect& physical_rect, const NGPaintFragment& paint_fragment) { const WritingMode writing_mode = paint_fragment.Style().GetWritingMode(); @@ -55,7 +67,7 @@ NGPhysicalOffsetRect ComputePhysicalRectFor( paint_fragment.PhysicalFragment().ResolvedDirection(); const NGPhysicalSize outer_size = paint_fragment.Size(); const NGPhysicalSize physical_size = - logical_rect.size.ConvertToPhysical(writing_mode); + ToNGPhysicalSize(logical_rect.size, writing_mode); const NGPhysicalOffset physical_offset = logical_rect.offset.ConvertToPhysical(writing_mode, text_direction, outer_size, physical_size); @@ -72,7 +84,7 @@ NGLogicalRect ExpandedSelectionRectForSoftLineBreakIfNeeded( if (selection_status.line_break == SelectSoftLineBreak::kNotSelected) return rect; if (paint_fragment.GetLayoutObject() - ->EnclosingNGBlockFlow() + ->ContainingNGBlockFlow() ->ShouldTruncateOverflowingText()) return rect; // Copy from InlineTextBoxPainter::PaintSelection. @@ -164,7 +176,57 @@ NGPaintFragment::NGPaintFragment( DCHECK(physical_fragment_); } -NGPaintFragment::~NGPaintFragment() = default; +NGPaintFragment::~NGPaintFragment() { + // The default destructor will deref |first_child_|, but because children are + // in a linked-list, it will call this destructor recursively. Remove children + // first non-recursively to avoid stack overflow when there are many chlidren. + RemoveChildren(); +} + +void NGPaintFragment::RemoveChildren() { + scoped_refptr<NGPaintFragment> child = std::move(first_child_); + DCHECK(!first_child_); + while (child) { + child = std::move(child->next_sibling_); + } +} + +template <typename Traverse> +NGPaintFragment& NGPaintFragment::List<Traverse>::front() const { + DCHECK(first_); + return *first_; +} + +template <typename Traverse> +NGPaintFragment& NGPaintFragment::List<Traverse>::back() const { + DCHECK(first_); + NGPaintFragment* last = first_; + for (NGPaintFragment* fragment : *this) + last = fragment; + return *last; +} + +template <typename Traverse> +wtf_size_t NGPaintFragment::List<Traverse>::size() const { + wtf_size_t size = 0; + for (NGPaintFragment* fragment : *this) { + ANALYZER_ALLOW_UNUSED(fragment); + ++size; + } + return size; +} + +template <typename Traverse> +void NGPaintFragment::List<Traverse>::ToList( + Vector<NGPaintFragment*, 16>* list) const { + if (UNLIKELY(!list->IsEmpty())) + list->Shrink(0); + if (IsEmpty()) + return; + list->ReserveCapacity(size()); + for (NGPaintFragment* fragment : *this) + list->push_back(fragment); +} void NGPaintFragment::SetShouldDoFullPaintInvalidation() { if (LayoutObject* layout_object = GetLayoutObject()) @@ -183,17 +245,23 @@ scoped_refptr<NGPaintFragment> NGPaintFragment::CreateOrReuse( // Re-using NGPaintFragment allows the paint system to identify objects. if (previous_instance) { DCHECK_EQ(previous_instance->parent_, parent); + DCHECK(!previous_instance->next_sibling_); +// TODO(kojii): This fails some tests when reusing line box was enabled. +// Investigate and re-enable. +#if 0 // If the physical fragment was re-used, re-use the paint fragment as well. if (&previous_instance->PhysicalFragment() == fragment.get()) { previous_instance->offset_ = offset; previous_instance->next_for_same_layout_object_ = nullptr; + previous_instance->is_dirty_inline_ = false; // No need to re-populate children because NGPhysicalFragment is // immutable and thus children should not have been changed. *populate_children = false; previous_instance->SetShouldDoFullPaintInvalidation(); return previous_instance; } +#endif // If the LayoutObject are the same, the new paint fragment should have the // same DisplayItemClient identity as the previous instance. @@ -201,8 +269,9 @@ scoped_refptr<NGPaintFragment> NGPaintFragment::CreateOrReuse( previous_instance->physical_fragment_ = std::move(fragment); previous_instance->offset_ = offset; previous_instance->next_for_same_layout_object_ = nullptr; + previous_instance->is_dirty_inline_ = false; if (!*populate_children) - previous_instance->children_.clear(); + previous_instance->first_child_ = nullptr; previous_instance->SetShouldDoFullPaintInvalidation(); return previous_instance; } @@ -233,6 +302,12 @@ scoped_refptr<NGPaintFragment> NGPaintFragment::Create( return paint_fragment; } +NGPaintFragment::RareData& NGPaintFragment::EnsureRareData() { + if (!rare_data_) + rare_data_ = std::make_unique<RareData>(); + return *rare_data_; +} + void NGPaintFragment::UpdateFromCachedLayoutResult( scoped_refptr<const NGPhysicalFragment> fragment, NGPhysicalOffset offset) { @@ -243,11 +318,14 @@ void NGPaintFragment::UpdateFromCachedLayoutResult( // children do not change. const NGPhysicalContainerFragment& container_fragment = ToNGPhysicalContainerFragment(*fragment); - DCHECK_EQ(Children().size(), container_fragment.Children().size()); - for (unsigned i = 0; i < container_fragment.Children().size(); i++) { - DCHECK_EQ(Children()[i]->physical_fragment_.get(), + NGPaintFragment* child = FirstChild(); + for (unsigned i = 0; i < container_fragment.Children().size(); + i++, child = child->NextSibling()) { + DCHECK(child); + DCHECK_EQ(child->physical_fragment_.get(), container_fragment.Children()[i].get()); } + DCHECK(!child); #endif DCHECK_EQ(physical_fragment_.get(), fragment.get()); @@ -257,16 +335,22 @@ void NGPaintFragment::UpdateFromCachedLayoutResult( NGPaintFragment* NGPaintFragment::Last(const NGBreakToken& break_token) { for (NGPaintFragment* fragment = this; fragment; - fragment = fragment->next_fragmented_.get()) { + fragment = fragment->Next()) { if (fragment->PhysicalFragment().BreakToken() == &break_token) return fragment; } return nullptr; } +NGPaintFragment* NGPaintFragment::Next() { + if (!rare_data_) + return nullptr; + return rare_data_->next_fragmented_.get(); +} + NGPaintFragment* NGPaintFragment::Last() { for (NGPaintFragment* fragment = this;;) { - NGPaintFragment* next = fragment->next_fragmented_.get(); + NGPaintFragment* next = fragment->Next(); if (!next) return fragment; fragment = next; @@ -288,7 +372,8 @@ scoped_refptr<NGPaintFragment>* NGPaintFragment::Find( if (!*fragment) return fragment; - scoped_refptr<NGPaintFragment>* next = &(*fragment)->next_fragmented_; + scoped_refptr<NGPaintFragment>* next = + &(*fragment)->EnsureRareData().next_fragmented_; if ((*fragment)->PhysicalFragment().BreakToken() == break_token) return next; fragment = next; @@ -297,7 +382,9 @@ scoped_refptr<NGPaintFragment>* NGPaintFragment::Find( } void NGPaintFragment::SetNext(scoped_refptr<NGPaintFragment> fragment) { - next_fragmented_ = std::move(fragment); + if (!rare_data_ && !fragment) + return; + EnsureRareData().next_fragmented_ = std::move(fragment); } bool NGPaintFragment::IsDescendantOfNotSelf( @@ -325,6 +412,18 @@ bool NGPaintFragment::ShouldClipOverflow() const { ToNGPhysicalBoxFragment(*physical_fragment_).ShouldClipOverflow(); } +LayoutRect NGPaintFragment::SelectionVisualRect() const { + if (!rare_data_) + return LayoutRect(); + return rare_data_->selection_visual_rect_; +} + +void NGPaintFragment::SetSelectionVisualRect(const LayoutRect& rect) { + if (!rare_data_ && rect.IsEmpty()) + return; + EnsureRareData().selection_visual_rect_ = rect; +} + LayoutRect NGPaintFragment::SelfInkOverflow() const { return physical_fragment_->InkOverflow().ToLayoutRect(); } @@ -341,20 +440,23 @@ void NGPaintFragment::PopulateDescendants( DCHECK(fragment.IsContainer()); const NGPhysicalContainerFragment& container = ToNGPhysicalContainerFragment(fragment); - children_.ReserveCapacity(container.Children().size()); + scoped_refptr<NGPaintFragment> previous_children = std::move(first_child_); + scoped_refptr<NGPaintFragment>* last_child_ptr = &first_child_; bool children_are_inline = !fragment.IsBox() || ToNGPhysicalBoxFragment(fragment).ChildrenInline(); - unsigned child_index = 0; for (const NGLink& child_fragment : container.Children()) { bool populate_children = child_fragment->IsContainer() && !child_fragment->IsBlockFormattingContextRoot(); - scoped_refptr<NGPaintFragment> child = CreateOrReuse( - child_fragment.get(), child_fragment.Offset(), this, - child_index < children_.size() ? std::move(children_[child_index]) - : nullptr, - &populate_children); + scoped_refptr<NGPaintFragment> previous_child; + if (previous_children) { + previous_child = std::move(previous_children); + previous_children = std::move(previous_child->next_sibling_); + } + scoped_refptr<NGPaintFragment> child = + CreateOrReuse(child_fragment.get(), child_fragment.Offset(), this, + std::move(previous_child), &populate_children); if (children_are_inline) { if (!child_fragment->IsFloating() && @@ -373,15 +475,10 @@ void NGPaintFragment::PopulateDescendants( } } - if (child_index < children_.size()) - children_[child_index] = std::move(child); - else - children_.push_back(std::move(child)); - ++child_index; + DCHECK(!*last_child_ptr); + *last_child_ptr = std::move(child); + last_child_ptr = &((*last_child_ptr)->next_sibling_); } - - if (child_index < children_.size()) - children_.resize(child_index); } // Add to a linked list for each LayoutObject. @@ -406,22 +503,16 @@ void NGPaintFragment::AssociateWithLayoutObject( NGPaintFragment* NGPaintFragment::GetForInlineContainer( const LayoutObject* layout_object) { DCHECK(layout_object && layout_object->IsInline()); - // Search from its parent because |EnclosingNGBlockFlow| returns itself when - // the LayoutObject is a box (i.e., atomic inline, including inline block and - // replaced elements.) - if (LayoutObject* parent = layout_object->Parent()) { - if (LayoutBlockFlow* block_flow = parent->EnclosingNGBlockFlow()) { - if (NGPaintFragment* fragment = block_flow->PaintFragment()) - return fragment; - - // TODO(kojii): IsLayoutFlowThread should probably be done in - // EnclosingNGBlockFlow(), but there seem to be both expectations today. - // This needs cleanup. - if (block_flow->IsLayoutFlowThread()) { - DCHECK(block_flow->Parent() && - block_flow->Parent()->IsLayoutBlockFlow()); - return ToLayoutBlockFlow(block_flow->Parent())->PaintFragment(); - } + if (LayoutBlockFlow* block_flow = layout_object->ContainingNGBlockFlow()) { + if (NGPaintFragment* fragment = block_flow->PaintFragment()) + return fragment; + + // TODO(kojii): IsLayoutFlowThread should probably be done in + // ContainingNGBlockFlow(), but there seem to be both expectations today. + // This needs cleanup. + if (block_flow->IsLayoutFlowThread()) { + DCHECK(block_flow->Parent() && block_flow->Parent()->IsLayoutBlockFlow()); + return ToLayoutBlockFlow(block_flow->Parent())->PaintFragment(); } } return nullptr; @@ -437,25 +528,20 @@ NGPaintFragment::FragmentRange NGPaintFragment::InlineFragmentsFor( return FragmentRange(nullptr, false); } +const NGPaintFragment* NGPaintFragment::LastForSameLayoutObject() const { + return const_cast<NGPaintFragment*>(this)->LastForSameLayoutObject(); +} + +NGPaintFragment* NGPaintFragment::LastForSameLayoutObject() { + NGPaintFragment* fragment = this; + while (fragment->next_for_same_layout_object_) + fragment = fragment->next_for_same_layout_object_; + return fragment; +} + void NGPaintFragment::DirtyLinesFromChangedChild(LayoutObject* child) { - if (child->IsInline()) { - LayoutBlockFlow* const block = child->EnclosingNGBlockFlow(); - if (block && block->PaintFragment()) - MarkLineBoxesDirtyFor(*child); - } - if (!child->IsInLayoutNGInlineFormattingContext()) - return; - // We should rest first inline fragment for following tests: - // * fast/dom/HTMLObjectElement/fallback-content-behaviour.html - // * fast/dom/shadow/exposed-object-within-shadow.html - // * fast/lists/inline-before-content-after-list-marker.html - // * fast/lists/list-with-image-display-changed.html - for (LayoutObject* runner = child; runner; - runner = runner->NextInPreOrder(child)) { - if (!runner->IsInLayoutNGInlineFormattingContext()) - continue; - runner->SetFirstInlineFragment(nullptr); - } + if (child->IsInline()) + MarkLineBoxesDirtyFor(*child); } bool NGPaintFragment::FlippedLocalVisualRectFor( @@ -483,11 +569,11 @@ bool NGPaintFragment::FlippedLocalVisualRectFor( void NGPaintFragment::UpdateVisualRectForNonLayoutObjectChildren() { // Scan direct children only beause line boxes are always direct children of // the inline formatting context. - for (auto& child : Children()) { + for (NGPaintFragment* child : Children()) { if (!child->PhysicalFragment().IsLineBox()) continue; LayoutRect union_of_children; - for (const auto& descendant : child->Children()) + for (const NGPaintFragment* descendant : child->Children()) union_of_children.Unite(descendant->VisualRect()); child->SetVisualRect(union_of_children); } @@ -512,7 +598,7 @@ void NGPaintFragment::PaintInlineBoxForDescendants( const LayoutInline* layout_object, NGPhysicalOffset offset) const { DCHECK(layout_object); - for (const auto& child : Children()) { + for (const NGPaintFragment* child : Children()) { if (child->GetLayoutObject() == layout_object) { NGInlineBoxFragmentPainter(*child).Paint( paint_info, paint_offset + offset.ToLayoutPoint() /*, paint_offset*/); @@ -536,43 +622,55 @@ const NGPaintFragment* NGPaintFragment::ContainerLineBox() const { } NGPaintFragment* NGPaintFragment::FirstLineBox() const { - for (auto& child : children_) { + for (NGPaintFragment* child : Children()) { if (child->PhysicalFragment().IsLineBox()) - return child.get(); + return child; } return nullptr; } void NGPaintFragment::MarkLineBoxesDirtyFor(const LayoutObject& layout_object) { DCHECK(layout_object.IsInline()) << layout_object; - if (TryMarkLineBoxDirtyFor(layout_object)) - return; + // Since |layout_object| isn't in fragment tree, check preceding siblings. // Note: Once we reuse lines below dirty lines, we should check next siblings. for (LayoutObject* previous = layout_object.PreviousSibling(); previous; previous = previous->PreviousSibling()) { + // If the previoius object had never been laid out, it should have already + // marked the line box dirty. + if (!previous->EverHadLayout()) + return; + if (previous->IsFloatingOrOutOfFlowPositioned()) continue; + // |previous| may not be in inline formatting context, e.g. <object>. - if (TryMarkLineBoxDirtyFor(*previous)) + if (TryMarkLastLineBoxDirtyFor(*previous)) return; } - // There is no siblings, try parent. + + // There is no siblings, try parent. If it's a non-atomic inline (e.g., span), + // mark dirty for it, but if it's an atomic inline (e.g., inline block), do + // not propagate across inline formatting context boundary. const LayoutObject& parent = *layout_object.Parent(); - if (parent.IsInline()) + if (parent.IsInline() && !parent.IsAtomicInlineLevel()) return MarkLineBoxesDirtyFor(parent); - if (!parent.IsLayoutNGMixin()) - return; - const LayoutBlockFlow& block = ToLayoutBlockFlow(parent); - if (!block.PaintFragment()) { - // We have not yet layout. - return; + + // The |layout_object| is inserted into an empty block. + // Mark the first line box dirty. + if (parent.IsLayoutNGMixin()) { + const LayoutBlockFlow& block = ToLayoutBlockFlow(parent); + if (NGPaintFragment* paint_fragment = block.PaintFragment()) { + if (NGPaintFragment* first_line = paint_fragment->FirstLineBox()) { + first_line->is_dirty_inline_ = true; + return; + } + } } - // We inserted |layout_object| into empty block. - block.PaintFragment()->FirstLineBox()->is_dirty_inline_ = true; } -void NGPaintFragment::MarkLineBoxDirty() { +void NGPaintFragment::MarkContainingLineBoxDirty() { + DCHECK(PhysicalFragment().IsInline() || PhysicalFragment().IsLineBox()); for (NGPaintFragment* fragment : NGPaintFragmentTraversal::InclusiveAncestorsOf(*this)) { if (fragment->is_dirty_inline_) @@ -584,13 +682,27 @@ void NGPaintFragment::MarkLineBoxDirty() { NOTREACHED() << this; // Should have a line box ancestor. } -bool NGPaintFragment::TryMarkLineBoxDirtyFor( +bool NGPaintFragment::TryMarkFirstLineBoxDirtyFor( + const LayoutObject& layout_object) { + if (!layout_object.IsInLayoutNGInlineFormattingContext()) + return false; + // Once we reuse lines below dirty lines, we should mark lines for all + // inline fragments. + if (NGPaintFragment* const fragment = layout_object.FirstInlineFragment()) { + fragment->MarkContainingLineBoxDirty(); + return true; + } + return false; +} + +bool NGPaintFragment::TryMarkLastLineBoxDirtyFor( const LayoutObject& layout_object) { + if (!layout_object.IsInLayoutNGInlineFormattingContext()) + return false; // Once we reuse lines below dirty lines, we should mark lines for all // inline fragments. - NGPaintFragment* const first_fragment = layout_object.FirstInlineFragment(); - if (first_fragment) { - first_fragment->MarkLineBoxDirty(); + if (NGPaintFragment* const fragment = layout_object.FirstInlineFragment()) { + fragment->LastForSameLayoutObject()->MarkContainingLineBoxDirty(); return true; } return false; @@ -600,7 +712,7 @@ void NGPaintFragment::SetShouldDoFullPaintInvalidationRecursively() { if (LayoutObject* layout_object = GetLayoutObject()) layout_object->SetShouldDoFullPaintInvalidation(); - for (auto& child : children_) + for (NGPaintFragment* child : Children()) child->SetShouldDoFullPaintInvalidationRecursively(); } @@ -660,14 +772,12 @@ PositionWithAffinity NGPaintFragment::PositionForPointInText( if (text_fragment.IsAnonymousText()) return PositionWithAffinity(); const unsigned text_offset = text_fragment.TextOffsetForPoint(point); + const NGCaretPosition unadjusted_position{ + this, NGCaretPositionType::kAtTextOffset, text_offset}; if (text_offset > text_fragment.StartOffset() && text_offset < text_fragment.EndOffset()) { - const Position position = NGOffsetMapping::GetFor(GetLayoutObject()) - ->GetFirstPosition(text_offset); - return PositionWithAffinity(position, TextAffinity::kDownstream); + return unadjusted_position.ToPositionInDOMTreeWithAffinity(); } - const NGCaretPosition unadjusted_position{ - this, NGCaretPositionType::kAtTextOffset, text_offset}; return BidiAdjustment::AdjustForHitTest(unadjusted_position) .ToPositionInDOMTreeWithAffinity(); } @@ -693,7 +803,7 @@ PositionWithAffinity NGPaintFragment::PositionForPointInInlineLevelBox( const NGPaintFragment* closest_child_after = nullptr; LayoutUnit closest_child_after_inline_offset = LayoutUnit::Max(); - for (const auto& child : Children()) { + for (const NGPaintFragment* child : Children()) { const LayoutUnit child_inline_min = ChildLogicalOffsetInParent(*child).inline_offset; const LayoutUnit child_inline_max = @@ -708,14 +818,14 @@ PositionWithAffinity NGPaintFragment::PositionForPointInInlineLevelBox( if (inline_point < child_inline_min) { if (child_inline_min < closest_child_after_inline_offset) { - closest_child_after = child.get(); + closest_child_after = child; closest_child_after_inline_offset = child_inline_min; } } if (inline_point > child_inline_max) { if (child_inline_max > closest_child_before_inline_offset) { - closest_child_before = child.get(); + closest_child_before = child; closest_child_before_inline_offset = child_inline_max; } } @@ -758,7 +868,7 @@ PositionWithAffinity NGPaintFragment::PositionForPointInInlineFormattingContext( const NGPaintFragment* closest_line_after = nullptr; LayoutUnit closest_line_after_block_offset = LayoutUnit::Max(); - for (const auto& child : Children()) { + for (const NGPaintFragment* child : Children()) { if (!child->PhysicalFragment().IsLineBox() || child->Children().IsEmpty()) continue; @@ -777,14 +887,14 @@ PositionWithAffinity NGPaintFragment::PositionForPointInInlineFormattingContext( if (block_point < line_min) { if (line_min < closest_line_after_block_offset) { - closest_line_after = child.get(); + closest_line_after = child; closest_line_after_block_offset = line_min; } } if (block_point >= line_max) { if (line_max > closest_line_before_block_offset) { - closest_line_before = child.get(); + closest_line_before = child; closest_line_before_block_offset = line_max; } } @@ -874,30 +984,6 @@ bool NGPaintFragment::ShouldPaintDragCaret() const { return ToLayoutBlock(GetLayoutObject())->ShouldPaintDragCaret(); } -// ---- - -NGPaintFragment& NGPaintFragment::FragmentRange::front() const { - DCHECK(first_); - return *first_; -} - -NGPaintFragment& NGPaintFragment::FragmentRange::back() const { - DCHECK(first_); - NGPaintFragment* last = first_; - for (NGPaintFragment* fragment : *this) - last = fragment; - return *last; -} - -wtf_size_t NGPaintFragment::FragmentRange::size() const { - wtf_size_t size = 0; - for (NGPaintFragment* fragment : *this) { - ANALYZER_ALLOW_UNUSED(fragment); - ++size; - } - return size; -} - String NGPaintFragment::DebugName() const { StringBuilder name; @@ -923,4 +1009,9 @@ String NGPaintFragment::DebugName() const { return name.ToString(); } +template class CORE_TEMPLATE_EXPORT + NGPaintFragment::List<NGPaintFragment::TraverseNextForSameLayoutObject>; +template class CORE_TEMPLATE_EXPORT + NGPaintFragment::List<NGPaintFragment::TraverseNextSibling>; + } // namespace blink diff --git a/chromium/third_party/blink/renderer/core/paint/ng/ng_paint_fragment.h b/chromium/third_party/blink/renderer/core/paint/ng/ng_paint_fragment.h index 631db023f44..7d331af509f 100644 --- a/chromium/third_party/blink/renderer/core/paint/ng/ng_paint_fragment.h +++ b/chromium/third_party/blink/renderer/core/paint/ng/ng_paint_fragment.h @@ -58,20 +58,81 @@ class CORE_EXPORT NGPaintFragment : public RefCounted<NGPaintFragment>, NGPhysicalOffset offset); // Next/last fragment for when this is fragmented. - NGPaintFragment* Next() { return next_fragmented_.get(); } + NGPaintFragment* Next(); void SetNext(scoped_refptr<NGPaintFragment>); NGPaintFragment* Last(); NGPaintFragment* Last(const NGBreakToken&); static scoped_refptr<NGPaintFragment>* Find(scoped_refptr<NGPaintFragment>*, const NGBreakToken*); + template <typename Traverse> + class List { + public: + explicit List(NGPaintFragment* first) : first_(first) {} + + class iterator final + : public std::iterator<std::forward_iterator_tag, NGPaintFragment*> { + public: + explicit iterator(NGPaintFragment* first) : current_(first) {} + + NGPaintFragment* operator*() const { return current_; } + NGPaintFragment* operator->() const { return current_; } + iterator& operator++() { + DCHECK(current_); + current_ = Traverse::Next(current_); + return *this; + } + bool operator==(const iterator& other) const { + return current_ == other.current_; + } + bool operator!=(const iterator& other) const { + return current_ != other.current_; + } + + private: + NGPaintFragment* current_; + }; + + CORE_EXPORT iterator begin() const { return iterator(first_); } + CORE_EXPORT iterator end() const { return iterator(nullptr); } + + // Returns the first |NGPaintFragment| in |FragmentRange| as STL container. + // It is error to call |front()| for empty range. + NGPaintFragment& front() const; + + // Returns the last |NGPaintFragment| in |FragmentRange| as STL container. + // It is error to call |back()| for empty range. + // Note: The complexity of |back()| is O(n) where n is number of elements + // in this |FragmentRange|. + NGPaintFragment& back() const; + + // Returns number of fragments in this range. The complexity is O(n) where n + // is number of elements. + wtf_size_t size() const; + CORE_EXPORT bool IsEmpty() const { return !first_; } + + void ToList(Vector<NGPaintFragment*, 16>*) const; + + private: + NGPaintFragment* first_; + }; + + class TraverseNextSibling { + public: + static NGPaintFragment* Next(NGPaintFragment* current) { + return current->next_sibling_.get(); + } + }; + using ChildList = List<TraverseNextSibling>; + // The parent NGPaintFragment. This is nullptr for a root; i.e., when parent // is not for NGPaint. In the first phase, this means that this is a root of // an inline formatting context. NGPaintFragment* Parent() const { return parent_; } - const Vector<scoped_refptr<NGPaintFragment>>& Children() const { - return children_; - } + NGPaintFragment* FirstChild() const { return first_child_.get(); } + NGPaintFragment* NextSibling() const { return next_sibling_.get(); } + ChildList Children() const { return ChildList(first_child_.get()); } + // Note, as the name implies, |IsDescendantOfNotSelf| returns false for the // same object. This is different from |LayoutObject::IsDescendant| but is // same as |Node::IsDescendant|. @@ -109,10 +170,8 @@ class CORE_EXPORT NGPaintFragment : public RefCounted<NGPaintFragment>, LayoutRect VisualRect() const override { return visual_rect_; } void SetVisualRect(const LayoutRect& rect) { visual_rect_ = rect; } - LayoutRect SelectionVisualRect() const { return selection_visual_rect_; } - void SetSelectionVisualRect(const LayoutRect& rect) { - selection_visual_rect_ = rect; - } + LayoutRect SelectionVisualRect() const; + void SetSelectionVisualRect(const LayoutRect& rect); // CSS ink overflow https://www.w3.org/TR/css-overflow-3/#ink // Encloses all pixels painted by self + children. @@ -171,15 +230,23 @@ class CORE_EXPORT NGPaintFragment : public RefCounted<NGPaintFragment>, } // Returns true when associated fragment of |layout_object| has line box. - static bool TryMarkLineBoxDirtyFor(const LayoutObject& layout_object); + static bool TryMarkFirstLineBoxDirtyFor(const LayoutObject& layout_object); + static bool TryMarkLastLineBoxDirtyFor(const LayoutObject& layout_object); // A range of fragments for |FragmentsFor()|. - class CORE_EXPORT FragmentRange { + class TraverseNextForSameLayoutObject { + public: + static NGPaintFragment* Next(NGPaintFragment* current) { + return current->next_for_same_layout_object_; + } + }; + class CORE_EXPORT FragmentRange + : public List<TraverseNextForSameLayoutObject> { public: explicit FragmentRange( NGPaintFragment* first, bool is_in_layout_ng_inline_formatting_context = true) - : first_(first), + : List(first), is_in_layout_ng_inline_formatting_context_( is_in_layout_ng_inline_formatting_context) {} @@ -187,50 +254,7 @@ class CORE_EXPORT NGPaintFragment : public RefCounted<NGPaintFragment>, return is_in_layout_ng_inline_formatting_context_; } - bool IsEmpty() const { return !first_; } - - class iterator final - : public std::iterator<std::forward_iterator_tag, NGPaintFragment*> { - public: - explicit iterator(NGPaintFragment* first) : current_(first) {} - - NGPaintFragment* operator*() const { return current_; } - NGPaintFragment* operator->() const { return current_; } - iterator& operator++() { - CHECK(current_); - current_ = current_->next_for_same_layout_object_; - return *this; - } - bool operator==(const iterator& other) const { - return current_ == other.current_; - } - bool operator!=(const iterator& other) const { - return current_ != other.current_; - } - - private: - NGPaintFragment* current_; - }; - - iterator begin() const { return iterator(first_); } - iterator end() const { return iterator(nullptr); } - - // Returns the first |NGPaintFragment| in |FragmentRange| as STL container. - // It is error to call |front()| for empty range. - NGPaintFragment& front() const; - - // Returns the last |NGPaintFragment| in |FragmentRange| as STL container. - // It is error to call |back()| for empty range. - // Note: The complexity of |back()| is O(n) where n is number of elements - // in this |FragmentRange|. - NGPaintFragment& back() const; - - // Returns number of fragments in this range. The complexity is O(n) where n - // is number of elements. - wtf_size_t size() const; - private: - NGPaintFragment* first_; bool is_in_layout_ng_inline_formatting_context_; }; @@ -246,9 +270,22 @@ class CORE_EXPORT NGPaintFragment : public RefCounted<NGPaintFragment>, // for a LayoutObject. static FragmentRange InlineFragmentsFor(const LayoutObject*); + const NGPaintFragment* LastForSameLayoutObject() const; + NGPaintFragment* LastForSameLayoutObject(); + // Called when lines containing |child| is dirty. static void DirtyLinesFromChangedChild(LayoutObject* child); + // Mark this line box was changed, in order to re-use part of an inline + // formatting context. + void MarkLineBoxDirty() { + DCHECK(PhysicalFragment().IsLineBox()); + is_dirty_inline_ = true; + } + + // Mark the line box that contains this fragment dirty. + void MarkContainingLineBoxDirty(); + // Computes LocalVisualRect for an inline LayoutObject in the // LayoutObject::LocalVisualRect semantics; i.e., physical coordinates with // flipped block-flow direction. See layout/README.md for the coordinate @@ -272,6 +309,8 @@ class CORE_EXPORT NGPaintFragment : public RefCounted<NGPaintFragment>, LayoutObject*, HashMap<const LayoutObject*, NGPaintFragment*>* last_fragment_map); + void RemoveChildren(); + // Helps for PositionForPoint() when |this| falls in different categories. PositionWithAffinity PositionForPointInText(const NGPhysicalOffset&) const; PositionWithAffinity PositionForPointInInlineFormattingContext( @@ -282,10 +321,6 @@ class CORE_EXPORT NGPaintFragment : public RefCounted<NGPaintFragment>, // Dirty line boxes containing |layout_object|. static void MarkLineBoxesDirtyFor(const LayoutObject& layout_object); - // Mark this line box was changed, in order to re-use part of an inline - // formatting context. - void MarkLineBoxDirty(); - // // Following fields are computed in the layout phase. // @@ -294,10 +329,21 @@ class CORE_EXPORT NGPaintFragment : public RefCounted<NGPaintFragment>, NGPhysicalOffset offset_; NGPaintFragment* parent_; - Vector<scoped_refptr<NGPaintFragment>> children_; + scoped_refptr<NGPaintFragment> first_child_; + scoped_refptr<NGPaintFragment> next_sibling_; + + struct RareData { + USING_FAST_MALLOC(RareData); + + public: + // The next fragment for when this is fragmented. + scoped_refptr<NGPaintFragment> next_fragmented_; - // The next fragment for when this is fragmented. - scoped_refptr<NGPaintFragment> next_fragmented_; + // Used for invalidating selected fragment. + LayoutRect selection_visual_rect_; + }; + RareData& EnsureRareData(); + std::unique_ptr<RareData> rare_data_; NGPaintFragment* next_for_same_layout_object_ = nullptr; NGPhysicalOffset inline_offset_to_container_box_; @@ -313,9 +359,13 @@ class CORE_EXPORT NGPaintFragment : public RefCounted<NGPaintFragment>, // LayoutRect visual_rect_; - LayoutRect selection_visual_rect_; }; +extern template class CORE_EXTERN_TEMPLATE_EXPORT + NGPaintFragment::List<NGPaintFragment::TraverseNextForSameLayoutObject>; +extern template class CORE_EXTERN_TEMPLATE_EXPORT + NGPaintFragment::List<NGPaintFragment::TraverseNextSibling>; + } // namespace blink #endif // THIRD_PARTY_BLINK_RENDERER_CORE_PAINT_NG_NG_PAINT_FRAGMENT_H_ diff --git a/chromium/third_party/blink/renderer/core/paint/ng/ng_paint_fragment_test.cc b/chromium/third_party/blink/renderer/core/paint/ng/ng_paint_fragment_test.cc index 33781b44cb3..c1a1fe50234 100644 --- a/chromium/third_party/blink/renderer/core/paint/ng/ng_paint_fragment_test.cc +++ b/chromium/third_party/blink/renderer/core/paint/ng/ng_paint_fragment_test.cc @@ -30,11 +30,18 @@ class NGPaintFragmentTest : public RenderingTest, const NGPaintFragment* root = GetPaintFragmentByElementId(id); EXPECT_TRUE(root); EXPECT_GE(1u, root->Children().size()); - const NGPaintFragment& line_box = *root->Children()[0]; + const NGPaintFragment& line_box = *root->FirstChild(); EXPECT_EQ(NGPhysicalFragment::kFragmentLineBox, line_box.PhysicalFragment().Type()); return line_box; } + + Vector<NGPaintFragment*, 16> ToList( + const NGPaintFragment::ChildList& children) { + Vector<NGPaintFragment*, 16> list; + children.ToList(&list); + return list; + } }; TEST_F(NGPaintFragmentTest, InlineFragmentsFor) { @@ -76,17 +83,17 @@ TEST_F(NGPaintFragmentTest, InlineFragmentsFor) { EXPECT_EQ(NGPhysicalOffset(LayoutUnit(60), LayoutUnit()), results[0]->InlineOffsetToContainerBox()); EXPECT_EQ("789", ToNGPhysicalTextFragment( - results[0]->Children()[0]->PhysicalFragment()) + results[0]->FirstChild()->PhysicalFragment()) .Text()); EXPECT_EQ(NGPhysicalOffset(LayoutUnit(), LayoutUnit(10)), results[1]->InlineOffsetToContainerBox()); EXPECT_EQ("123456789", ToNGPhysicalTextFragment( - results[1]->Children()[0]->PhysicalFragment()) + results[1]->FirstChild()->PhysicalFragment()) .Text()); EXPECT_EQ(NGPhysicalOffset(LayoutUnit(), LayoutUnit(20)), results[2]->InlineOffsetToContainerBox()); EXPECT_EQ("123", ToNGPhysicalTextFragment( - results[2]->Children()[0]->PhysicalFragment()) + results[2]->FirstChild()->PhysicalFragment()) .Text()); } @@ -104,25 +111,25 @@ TEST_F(NGPaintFragmentTest, InlineBox) { )HTML"); const NGPaintFragment* container = GetPaintFragmentByElementId("container"); EXPECT_EQ(2u, container->Children().size()); - const NGPaintFragment& line1 = *container->Children()[0]; + const NGPaintFragment& line1 = *container->FirstChild(); EXPECT_EQ(2u, line1.Children().size()); // Inline boxes without box decorations (border, background, etc.) do not // generate box fragments and that their child fragments are placed directly // under the line box. - const NGPaintFragment& outer_text = *line1.Children()[0]; + const NGPaintFragment& outer_text = *line1.FirstChild(); EXPECT_EQ(NGPhysicalFragment::kFragmentText, outer_text.PhysicalFragment().Type()); EXPECT_EQ(LayoutRect(0, 0, 60, 10), outer_text.VisualRect()); - const NGPaintFragment& inner_text1 = *line1.Children()[1]; + const NGPaintFragment& inner_text1 = *ToList(line1.Children())[1]; EXPECT_EQ(NGPhysicalFragment::kFragmentText, inner_text1.PhysicalFragment().Type()); EXPECT_EQ(LayoutRect(60, 0, 30, 10), inner_text1.VisualRect()); - const NGPaintFragment& line2 = *container->Children()[1]; + const NGPaintFragment& line2 = *ToList(container->Children())[1]; EXPECT_EQ(1u, line2.Children().size()); - const NGPaintFragment& inner_text2 = *line2.Children()[0]; + const NGPaintFragment& inner_text2 = *line2.FirstChild(); EXPECT_EQ(NGPhysicalFragment::kFragmentText, inner_text2.PhysicalFragment().Type()); EXPECT_EQ(LayoutRect(0, 10, 30, 10), inner_text2.VisualRect()); @@ -143,34 +150,34 @@ TEST_F(NGPaintFragmentTest, InlineBoxWithDecorations) { )HTML"); const NGPaintFragment* container = GetPaintFragmentByElementId("container"); EXPECT_EQ(2u, container->Children().size()); - const NGPaintFragment& line1 = *container->Children()[0]; + const NGPaintFragment& line1 = *container->FirstChild(); EXPECT_EQ(2u, line1.Children().size()); - const NGPaintFragment& outer_text = *line1.Children()[0]; + const NGPaintFragment& outer_text = *line1.FirstChild(); EXPECT_EQ(NGPhysicalFragment::kFragmentText, outer_text.PhysicalFragment().Type()); EXPECT_EQ(LayoutRect(0, 0, 60, 10), outer_text.VisualRect()); // Inline boxes with box decorations generate box fragments. - const NGPaintFragment& inline_box1 = *line1.Children()[1]; + const NGPaintFragment& inline_box1 = *ToList(line1.Children())[1]; EXPECT_EQ(NGPhysicalFragment::kFragmentBox, inline_box1.PhysicalFragment().Type()); EXPECT_EQ(LayoutRect(60, 0, 30, 10), inline_box1.VisualRect()); EXPECT_EQ(1u, inline_box1.Children().size()); - const NGPaintFragment& inner_text1 = *inline_box1.Children()[0]; + const NGPaintFragment& inner_text1 = *inline_box1.FirstChild(); EXPECT_EQ(NGPhysicalFragment::kFragmentText, inner_text1.PhysicalFragment().Type()); EXPECT_EQ(LayoutRect(60, 0, 30, 10), inner_text1.VisualRect()); - const NGPaintFragment& line2 = *container->Children()[1]; + const NGPaintFragment& line2 = *ToList(container->Children())[1]; EXPECT_EQ(1u, line2.Children().size()); - const NGPaintFragment& inline_box2 = *line2.Children()[0]; + const NGPaintFragment& inline_box2 = *line2.FirstChild(); EXPECT_EQ(NGPhysicalFragment::kFragmentBox, inline_box2.PhysicalFragment().Type()); EXPECT_EQ(LayoutRect(0, 10, 30, 10), inline_box2.VisualRect()); - const NGPaintFragment& inner_text2 = *inline_box2.Children()[0]; + const NGPaintFragment& inner_text2 = *inline_box2.FirstChild(); EXPECT_EQ(NGPhysicalFragment::kFragmentText, inner_text2.PhysicalFragment().Type()); EXPECT_EQ(LayoutRect(0, 10, 30, 10), inner_text2.VisualRect()); @@ -195,11 +202,11 @@ TEST_F(NGPaintFragmentTest, InlineBlock) { const NGPaintFragment* container = GetPaintFragmentByElementId("container"); EXPECT_TRUE(container); EXPECT_EQ(1u, container->Children().size()); - const NGPaintFragment& line1 = *container->Children()[0]; + const NGPaintFragment& line1 = *container->FirstChild(); EXPECT_EQ(3u, line1.Children().size()); // Test the outer text "12345". - const NGPaintFragment& outer_text = *line1.Children()[0]; + const NGPaintFragment& outer_text = *line1.FirstChild(); EXPECT_EQ(NGPhysicalFragment::kFragmentText, outer_text.PhysicalFragment().Type()); EXPECT_EQ("12345 ", ToNGPhysicalTextFragment(outer_text.PhysicalFragment()) @@ -219,7 +226,7 @@ TEST_F(NGPaintFragmentTest, InlineBlock) { EXPECT_EQ(&outer_text, *fragments.begin()); // Test the inline block "box1". - const NGPaintFragment& box1 = *line1.Children()[1]; + const NGPaintFragment& box1 = *ToList(line1.Children())[1]; EXPECT_EQ(NGPhysicalFragment::kFragmentBox, box1.PhysicalFragment().Type()); EXPECT_EQ(NGPhysicalFragment::kAtomicInline, box1.PhysicalFragment().BoxType()); @@ -240,8 +247,8 @@ TEST_F(NGPaintFragmentTest, InlineBlock) { EXPECT_EQ(box1_inner->GetLayoutObject(), box1.GetLayoutObject()); // Test the text fragment inside of the inline block. - const NGPaintFragment& inner_line_box = *box1_inner->Children()[0]; - const NGPaintFragment& inner_text = *inner_line_box.Children()[0]; + const NGPaintFragment& inner_line_box = *box1_inner->FirstChild(); + const NGPaintFragment& inner_text = *inner_line_box.FirstChild(); EXPECT_EQ(NGPhysicalFragment::kFragmentText, inner_text.PhysicalFragment().Type()); EXPECT_EQ(LayoutRect(60, 0, 10, 10), inner_text.VisualRect()); @@ -256,7 +263,7 @@ TEST_F(NGPaintFragmentTest, InlineBlock) { EXPECT_EQ(&inner_text, *fragments.begin()); // Test the inline block "box2". - const NGPaintFragment& box2 = *line1.Children()[2]; + const NGPaintFragment& box2 = *ToList(line1.Children())[2]; EXPECT_EQ(NGPhysicalFragment::kFragmentBox, box2.PhysicalFragment().Type()); EXPECT_EQ(NGPhysicalFragment::kAtomicInline, box2.PhysicalFragment().BoxType()); @@ -264,7 +271,7 @@ TEST_F(NGPaintFragmentTest, InlineBlock) { EXPECT_EQ(LayoutRect(), box2.SelectionVisualRect()); GetDocument().GetFrame()->Selection().SelectAll(); - GetDocument().View()->UpdateAllLifecyclePhases(); + UpdateAllLifecyclePhasesForTest(); EXPECT_EQ(LayoutRect(0, 0, 60, 10), outer_text.VisualRect()); EXPECT_EQ(LayoutRect(0, 0, 60, 10), outer_text.SelectionVisualRect()); EXPECT_EQ(LayoutRect(60, 0, 10, 10), box1.VisualRect()); @@ -290,22 +297,22 @@ TEST_F(NGPaintFragmentTest, RelativeBlock) { )HTML"); const NGPaintFragment* container = GetPaintFragmentByElementId("container"); EXPECT_EQ(2u, container->Children().size()); - const NGPaintFragment& line1 = *container->Children()[0]; + const NGPaintFragment& line1 = *container->FirstChild(); EXPECT_EQ(2u, line1.Children().size()); - const NGPaintFragment& outer_text = *line1.Children()[0]; + const NGPaintFragment& outer_text = *line1.FirstChild(); EXPECT_EQ(NGPhysicalFragment::kFragmentText, outer_text.PhysicalFragment().Type()); EXPECT_EQ(LayoutRect(0, 10, 60, 10), outer_text.VisualRect()); - const NGPaintFragment& inner_text1 = *line1.Children()[1]; + const NGPaintFragment& inner_text1 = *ToList(line1.Children())[1]; EXPECT_EQ(NGPhysicalFragment::kFragmentText, inner_text1.PhysicalFragment().Type()); EXPECT_EQ(LayoutRect(60, 10, 30, 10), inner_text1.VisualRect()); - const NGPaintFragment& line2 = *container->Children()[1]; + const NGPaintFragment& line2 = *ToList(container->Children())[1]; EXPECT_EQ(1u, line2.Children().size()); - const NGPaintFragment& inner_text2 = *line2.Children()[0]; + const NGPaintFragment& inner_text2 = *line2.FirstChild(); EXPECT_EQ(NGPhysicalFragment::kFragmentText, inner_text2.PhysicalFragment().Type()); EXPECT_EQ(LayoutRect(0, 20, 30, 10), inner_text2.VisualRect()); @@ -326,33 +333,33 @@ TEST_F(NGPaintFragmentTest, RelativeInline) { )HTML"); const NGPaintFragment* container = GetPaintFragmentByElementId("container"); EXPECT_EQ(2u, container->Children().size()); - const NGPaintFragment& line1 = *container->Children()[0]; + const NGPaintFragment& line1 = *container->FirstChild(); EXPECT_EQ(2u, line1.Children().size()); - const NGPaintFragment& outer_text = *line1.Children()[0]; + const NGPaintFragment& outer_text = *line1.FirstChild(); EXPECT_EQ(NGPhysicalFragment::kFragmentText, outer_text.PhysicalFragment().Type()); EXPECT_EQ(LayoutRect(0, 0, 60, 10), outer_text.VisualRect()); - const NGPaintFragment& inline_box1 = *line1.Children()[1]; + const NGPaintFragment& inline_box1 = *ToList(line1.Children())[1]; EXPECT_EQ(NGPhysicalFragment::kFragmentBox, inline_box1.PhysicalFragment().Type()); EXPECT_EQ(LayoutRect(60, 10, 30, 10), inline_box1.VisualRect()); EXPECT_EQ(1u, inline_box1.Children().size()); - const NGPaintFragment& inner_text1 = *inline_box1.Children()[0]; + const NGPaintFragment& inner_text1 = *inline_box1.FirstChild(); EXPECT_EQ(NGPhysicalFragment::kFragmentText, inner_text1.PhysicalFragment().Type()); EXPECT_EQ(LayoutRect(60, 10, 30, 10), inner_text1.VisualRect()); - const NGPaintFragment& line2 = *container->Children()[1]; + const NGPaintFragment& line2 = *ToList(container->Children())[1]; EXPECT_EQ(1u, line2.Children().size()); - const NGPaintFragment& inline_box2 = *line2.Children()[0]; + const NGPaintFragment& inline_box2 = *line2.FirstChild(); EXPECT_EQ(NGPhysicalFragment::kFragmentBox, inline_box2.PhysicalFragment().Type()); EXPECT_EQ(LayoutRect(0, 20, 30, 10), inline_box2.VisualRect()); - const NGPaintFragment& inner_text2 = *inline_box2.Children()[0]; + const NGPaintFragment& inner_text2 = *inline_box2.FirstChild(); EXPECT_EQ(NGPhysicalFragment::kFragmentText, inner_text2.PhysicalFragment().Type()); EXPECT_EQ(LayoutRect(0, 20, 30, 10), inner_text2.VisualRect()); @@ -373,33 +380,33 @@ TEST_F(NGPaintFragmentTest, RelativeBlockAndInline) { )HTML"); const NGPaintFragment* container = GetPaintFragmentByElementId("container"); EXPECT_EQ(2u, container->Children().size()); - const NGPaintFragment& line1 = *container->Children()[0]; + const NGPaintFragment& line1 = *container->FirstChild(); EXPECT_EQ(2u, line1.Children().size()); - const NGPaintFragment& outer_text = *line1.Children()[0]; + const NGPaintFragment& outer_text = *line1.FirstChild(); EXPECT_EQ(NGPhysicalFragment::kFragmentText, outer_text.PhysicalFragment().Type()); EXPECT_EQ(LayoutRect(0, 10, 60, 10), outer_text.VisualRect()); - const NGPaintFragment& inline_box1 = *line1.Children()[1]; + const NGPaintFragment& inline_box1 = *ToList(line1.Children())[1]; EXPECT_EQ(NGPhysicalFragment::kFragmentBox, inline_box1.PhysicalFragment().Type()); EXPECT_EQ(LayoutRect(60, 20, 30, 10), inline_box1.VisualRect()); EXPECT_EQ(1u, inline_box1.Children().size()); - const NGPaintFragment& inner_text1 = *inline_box1.Children()[0]; + const NGPaintFragment& inner_text1 = *inline_box1.FirstChild(); EXPECT_EQ(NGPhysicalFragment::kFragmentText, inner_text1.PhysicalFragment().Type()); EXPECT_EQ(LayoutRect(60, 20, 30, 10), inner_text1.VisualRect()); - const NGPaintFragment& line2 = *container->Children()[1]; + const NGPaintFragment& line2 = *ToList(container->Children())[1]; EXPECT_EQ(1u, line2.Children().size()); - const NGPaintFragment& inline_box2 = *line2.Children()[0]; + const NGPaintFragment& inline_box2 = *line2.FirstChild(); EXPECT_EQ(NGPhysicalFragment::kFragmentBox, inline_box2.PhysicalFragment().Type()); EXPECT_EQ(LayoutRect(0, 30, 30, 10), inline_box2.VisualRect()); - const NGPaintFragment& inner_text2 = *inline_box2.Children()[0]; + const NGPaintFragment& inner_text2 = *inline_box2.FirstChild(); EXPECT_EQ(NGPhysicalFragment::kFragmentText, inner_text2.PhysicalFragment().Type()); EXPECT_EQ(LayoutRect(0, 30, 30, 10), inner_text2.VisualRect()); @@ -426,32 +433,32 @@ TEST_F(NGPaintFragmentTest, FlippedBlock) { )HTML"); const NGPaintFragment* container = GetPaintFragmentByElementId("container"); EXPECT_EQ(2u, container->Children().size()); - const NGPaintFragment& line1 = *container->Children()[0]; + const NGPaintFragment& line1 = *container->FirstChild(); EXPECT_EQ(NGPhysicalFragment::kFragmentLineBox, line1.PhysicalFragment().Type()); EXPECT_EQ(LayoutRect(190, 0, 10, 100), line1.VisualRect()); EXPECT_EQ(1u, line1.Children().size()); - const NGPaintFragment& text1 = *line1.Children()[0]; + const NGPaintFragment& text1 = *line1.FirstChild(); EXPECT_EQ(NGPhysicalFragment::kFragmentText, text1.PhysicalFragment().Type()); EXPECT_EQ(LayoutRect(190, 0, 10, 100), text1.VisualRect()); - const NGPaintFragment& line2 = *container->Children()[1]; + const NGPaintFragment& line2 = *ToList(container->Children())[1]; EXPECT_EQ(NGPhysicalFragment::kFragmentLineBox, line2.PhysicalFragment().Type()); EXPECT_EQ(LayoutRect(180, 0, 10, 70), line2.VisualRect()); EXPECT_EQ(2u, line2.Children().size()); - const NGPaintFragment& text2 = *line2.Children()[0]; + const NGPaintFragment& text2 = *line2.FirstChild(); EXPECT_EQ(NGPhysicalFragment::kFragmentText, text2.PhysicalFragment().Type()); EXPECT_EQ(LayoutRect(180, 0, 10, 40), text2.VisualRect()); - const NGPaintFragment& box = *line2.Children()[1]; + const NGPaintFragment& box = *ToList(line2.Children())[1]; EXPECT_EQ(NGPhysicalFragment::kFragmentBox, box.PhysicalFragment().Type()); EXPECT_EQ(LayoutRect(180, 40, 10, 30), box.VisualRect()); EXPECT_EQ(1u, box.Children().size()); - const NGPaintFragment& text3 = *box.Children()[0]; + const NGPaintFragment& text3 = *box.FirstChild(); EXPECT_EQ(NGPhysicalFragment::kFragmentText, text3.PhysicalFragment().Type()); EXPECT_EQ(LayoutRect(180, 40, 10, 30), text3.VisualRect()); } @@ -463,9 +470,9 @@ TEST_F(NGPaintFragmentTest, MarkLineBoxesDirtyByRemoveBr) { Element& target = *GetDocument().getElementById("target"); target.remove(); const NGPaintFragment& container = *GetPaintFragmentByElementId("container"); - EXPECT_FALSE(container.Children()[0]->IsDirty()); - EXPECT_TRUE(container.Children()[1]->IsDirty()); - EXPECT_FALSE(container.Children()[2]->IsDirty()); + EXPECT_FALSE(container.FirstChild()->IsDirty()); + EXPECT_TRUE(ToList(container.Children())[1]->IsDirty()); + EXPECT_FALSE(ToList(container.Children())[2]->IsDirty()); } TEST_F(NGPaintFragmentTest, MarkLineBoxesDirtyByRemoveChild) { @@ -475,9 +482,9 @@ TEST_F(NGPaintFragmentTest, MarkLineBoxesDirtyByRemoveChild) { Element& target = *GetDocument().getElementById("target"); target.remove(); const NGPaintFragment& container = *GetPaintFragmentByElementId("container"); - EXPECT_TRUE(container.Children()[0]->IsDirty()); - EXPECT_TRUE(container.Children()[1]->IsDirty()); - EXPECT_FALSE(container.Children()[2]->IsDirty()); + EXPECT_TRUE(container.FirstChild()->IsDirty()); + EXPECT_TRUE(ToList(container.Children())[1]->IsDirty()); + EXPECT_FALSE(ToList(container.Children())[2]->IsDirty()); } TEST_F(NGPaintFragmentTest, MarkLineBoxesDirtyByRemoveSpanWithBr) { @@ -488,21 +495,26 @@ TEST_F(NGPaintFragmentTest, MarkLineBoxesDirtyByRemoveSpanWithBr) { Element& target = *GetDocument().getElementById("target"); target.remove(); const NGPaintFragment& container = *GetPaintFragmentByElementId("container"); - EXPECT_FALSE(container.Children()[0]->IsDirty()); - EXPECT_TRUE(container.Children()[1]->IsDirty()); - EXPECT_FALSE(container.Children()[2]->IsDirty()); + EXPECT_FALSE(container.FirstChild()->IsDirty()); + EXPECT_TRUE(ToList(container.Children())[1]->IsDirty()); + EXPECT_FALSE(ToList(container.Children())[2]->IsDirty()); } -TEST_F(NGPaintFragmentTest, MarkLineBoxesDirtyByInsertAtStart) { +// "ByInsert" tests are disabled, because they require |UpdateStyleAndLayout()| +// to update |IsDirty|, but NGPaintFragment maybe re-used during the layout. In +// such case, the result is not deterministic. +TEST_F(NGPaintFragmentTest, DISABLED_MarkLineBoxesDirtyByInsertAtStart) { SetBodyInnerHTML( "<div id=container>line 1<br><b id=target>line 2</b><br>line 3<br>" "</div>"); const NGPaintFragment& container = *GetPaintFragmentByElementId("container"); - const scoped_refptr<const NGPaintFragment> line1 = container.Children()[0]; + const scoped_refptr<const NGPaintFragment> line1 = container.FirstChild(); ASSERT_TRUE(line1->PhysicalFragment().IsLineBox()) << line1; - const scoped_refptr<const NGPaintFragment> line2 = container.Children()[1]; + const scoped_refptr<const NGPaintFragment> line2 = + ToList(container.Children())[1]; ASSERT_TRUE(line2->PhysicalFragment().IsLineBox()) << line2; - const scoped_refptr<const NGPaintFragment> line3 = container.Children()[2]; + const scoped_refptr<const NGPaintFragment> line3 = + ToList(container.Children())[2]; ASSERT_TRUE(line3->PhysicalFragment().IsLineBox()) << line3; Element& target = *GetDocument().getElementById("target"); target.parentNode()->insertBefore(Text::Create(GetDocument(), "XYZ"), @@ -514,16 +526,21 @@ TEST_F(NGPaintFragmentTest, MarkLineBoxesDirtyByInsertAtStart) { EXPECT_FALSE(line3->IsDirty()); } -TEST_F(NGPaintFragmentTest, MarkLineBoxesDirtyByInsertAtLast) { +// "ByInsert" tests are disabled, because they require |UpdateStyleAndLayout()| +// to update |IsDirty|, but NGPaintFragment maybe re-used during the layout. In +// such case, the result is not deterministic. +TEST_F(NGPaintFragmentTest, DISABLED_MarkLineBoxesDirtyByInsertAtLast) { SetBodyInnerHTML( "<div id=container>line 1<br><b id=target>line 2</b><br>line 3<br>" "</div>"); const NGPaintFragment& container = *GetPaintFragmentByElementId("container"); - const scoped_refptr<const NGPaintFragment> line1 = container.Children()[0]; + const scoped_refptr<const NGPaintFragment> line1 = container.FirstChild(); ASSERT_TRUE(line1->PhysicalFragment().IsLineBox()) << line1; - const scoped_refptr<const NGPaintFragment> line2 = container.Children()[1]; + const scoped_refptr<const NGPaintFragment> line2 = + ToList(container.Children())[1]; ASSERT_TRUE(line2->PhysicalFragment().IsLineBox()) << line2; - const scoped_refptr<const NGPaintFragment> line3 = container.Children()[2]; + const scoped_refptr<const NGPaintFragment> line3 = + ToList(container.Children())[2]; ASSERT_TRUE(line3->PhysicalFragment().IsLineBox()) << line3; Element& target = *GetDocument().getElementById("target"); target.parentNode()->appendChild(Text::Create(GetDocument(), "XYZ")); @@ -534,16 +551,21 @@ TEST_F(NGPaintFragmentTest, MarkLineBoxesDirtyByInsertAtLast) { EXPECT_TRUE(line3->IsDirty()); } -TEST_F(NGPaintFragmentTest, MarkLineBoxesDirtyByInsertAtMiddle) { +// "ByInsert" tests are disabled, because they require |UpdateStyleAndLayout()| +// to update |IsDirty|, but NGPaintFragment maybe re-used during the layout. In +// such case, the result is not deterministic. +TEST_F(NGPaintFragmentTest, DISABLED_MarkLineBoxesDirtyByInsertAtMiddle) { SetBodyInnerHTML( "<div id=container>line 1<br><b id=target>line 2</b><br>line 3<br>" "</div>"); const NGPaintFragment& container = *GetPaintFragmentByElementId("container"); - const scoped_refptr<const NGPaintFragment> line1 = container.Children()[0]; + const scoped_refptr<const NGPaintFragment> line1 = container.FirstChild(); ASSERT_TRUE(line1->PhysicalFragment().IsLineBox()) << line1; - const scoped_refptr<const NGPaintFragment> line2 = container.Children()[1]; + const scoped_refptr<const NGPaintFragment> line2 = + ToList(container.Children())[1]; ASSERT_TRUE(line2->PhysicalFragment().IsLineBox()) << line2; - const scoped_refptr<const NGPaintFragment> line3 = container.Children()[2]; + const scoped_refptr<const NGPaintFragment> line3 = + ToList(container.Children())[2]; ASSERT_TRUE(line3->PhysicalFragment().IsLineBox()) << line3; Element& target = *GetDocument().getElementById("target"); target.parentNode()->insertBefore(Text::Create(GetDocument(), "XYZ"), @@ -562,9 +584,52 @@ TEST_F(NGPaintFragmentTest, MarkLineBoxesDirtyByTextSetData) { Element& target = *GetDocument().getElementById("target"); ToText(*target.firstChild()).setData("abc"); const NGPaintFragment& container = *GetPaintFragmentByElementId("container"); - EXPECT_FALSE(container.Children()[0]->IsDirty()); - EXPECT_TRUE(container.Children()[1]->IsDirty()); - EXPECT_FALSE(container.Children()[2]->IsDirty()); + auto lines = ToList(container.Children()); + // TODO(kojii): Currently we don't optimzie for <br>. We can do this, then + // lines[0] should not be dirty. + EXPECT_TRUE(lines[0]->IsDirty()); +} + +TEST_F(NGPaintFragmentTest, MarkLineBoxesDirtyWrappedLine) { + SetBodyInnerHTML(R"HTML( + <style> + #container { + font-size: 10px; + width: 10ch; + } + </style> + <div id=container> + 1234567 + 123456<span id="target">7</span> + </div>)HTML"); + Element& target = *GetDocument().getElementById("target"); + target.remove(); + + const NGPaintFragment& container = *GetPaintFragmentByElementId("container"); + const NGPaintFragment& line0 = *container.FirstChild(); + const NGPaintFragment& line1 = *line0.NextSibling(); + EXPECT_FALSE(line0.IsDirty()); + EXPECT_TRUE(line1.IsDirty()); +} + +TEST_F(NGPaintFragmentTest, MarkLineBoxesDirtyInsideInlineBlock) { + SetBodyInnerHTML(R"HTML( + <div id=container> + <div id="inline-block" style="display: inline-block"> + <span id="target">DELETE ME</span> + </div> + </div>)HTML"); + Element& target = *GetDocument().getElementById("target"); + target.remove(); + + const NGPaintFragment& container = *GetPaintFragmentByElementId("container"); + const NGPaintFragment& line0 = *container.FirstChild(); + EXPECT_FALSE(line0.IsDirty()); + + const NGPaintFragment& inline_block = + *GetPaintFragmentByElementId("inline-block"); + const NGPaintFragment& inner_line0 = *inline_block.FirstChild(); + EXPECT_TRUE(inner_line0.IsDirty()); } } // namespace blink diff --git a/chromium/third_party/blink/renderer/core/paint/ng/ng_paint_fragment_traversal.cc b/chromium/third_party/blink/renderer/core/paint/ng/ng_paint_fragment_traversal.cc index 1aa3e390965..c7473b2fe3f 100644 --- a/chromium/third_party/blink/renderer/core/paint/ng/ng_paint_fragment_traversal.cc +++ b/chromium/third_party/blink/renderer/core/paint/ng/ng_paint_fragment_traversal.cc @@ -23,13 +23,13 @@ void CollectPaintFragments(const NGPaintFragment& container, NGPhysicalOffset offset_to_container_box, Filter& filter, Vector<NGPaintFragmentWithContainerOffset>* result) { - for (const auto& child : container.Children()) { + for (NGPaintFragment* child : container.Children()) { NGPaintFragmentWithContainerOffset fragment_with_offset{ - child.get(), child->Offset() + offset_to_container_box}; - if (filter.IsCollectible(child.get())) { + child, child->Offset() + offset_to_container_box}; + if (filter.IsCollectible(child)) { result->push_back(fragment_with_offset); } - if (filter.IsTraverse(child.get())) { + if (filter.IsTraverse(child)) { CollectPaintFragments(*child, fragment_with_offset.container_offset, filter, result); } @@ -143,22 +143,19 @@ NGPaintFragmentTraversalContext NextSiblingOf( return {fragment.parent, fragment.index + 1}; } -unsigned IndexOfChild(const NGPaintFragment& parent, - const NGPaintFragment& fragment) { - const auto& children = parent.Children(); - const auto* it = std::find_if( - children.begin(), children.end(), - [&fragment](const auto& child) { return &fragment == child.get(); }); - DCHECK(it != children.end()); - return static_cast<unsigned>(std::distance(children.begin(), it)); +unsigned IndexOf(const Vector<NGPaintFragment*, 16>& fragments, + const NGPaintFragment& fragment) { + auto* const* it = std::find_if( + fragments.begin(), fragments.end(), + [&fragment](const auto& child) { return &fragment == child; }); + DCHECK(it != fragments.end()); + return static_cast<unsigned>(std::distance(fragments.begin(), it)); } } // namespace NGPaintFragmentTraversal::NGPaintFragmentTraversal(const NGPaintFragment& root) - : root_(root) { - Push(root, 0); -} + : current_(root.FirstChild()), root_(root) {} NGPaintFragmentTraversal::NGPaintFragmentTraversal(const NGPaintFragment& root, const NGPaintFragment& start) @@ -166,34 +163,19 @@ NGPaintFragmentTraversal::NGPaintFragmentTraversal(const NGPaintFragment& root, MoveTo(start); } -void NGPaintFragmentTraversal::Push(const NGPaintFragment& parent, - unsigned index) { - stack_.push_back(ParentAndIndex{&parent, index}); - current_ = parent.Children()[index].get(); -} - -void NGPaintFragmentTraversal::Push(const NGPaintFragment& fragment) { - const NGPaintFragment* parent = fragment.Parent(); - DCHECK(parent); - Push(*parent, IndexOfChild(*parent, fragment)); -} - void NGPaintFragmentTraversal::MoveTo(const NGPaintFragment& fragment) { DCHECK(fragment.IsDescendantOfNotSelf(root_)); - - // Because we may not traverse all descendants of |root_|, just push the - // specified fragment. Computing its ancestors up to |root_| is deferred to - // |MoveToNextSiblingOrAncestor()|. - stack_.resize(0); - Push(fragment); + current_ = &fragment; } void NGPaintFragmentTraversal::MoveToNext() { if (IsAtEnd()) return; - if (!current_->Children().IsEmpty()) { - Push(*current_, 0); + if (const NGPaintFragment* first_child = current_->FirstChild()) { + current_ = first_child; + if (UNLIKELY(!siblings_.IsEmpty())) + siblings_.Shrink(0); return; } @@ -203,11 +185,12 @@ void NGPaintFragmentTraversal::MoveToNext() { void NGPaintFragmentTraversal::MoveToNextSiblingOrAncestor() { while (!IsAtEnd()) { // Check if we have a next sibling. - auto& stack_top = stack_.back(); - if (++stack_top.index < stack_top.parent->Children().size()) { - current_ = stack_top.parent->Children()[stack_top.index].get(); + if (const NGPaintFragment* next = current_->NextSibling()) { + current_ = next; + ++current_index_; return; } + MoveToParent(); } } @@ -215,39 +198,36 @@ void NGPaintFragmentTraversal::MoveToNextSiblingOrAncestor() { void NGPaintFragmentTraversal::MoveToParent() { if (IsAtEnd()) return; - DCHECK(!stack_.IsEmpty()); - const NGPaintFragment& parent = *stack_.back().parent; - stack_.pop_back(); - if (&parent == &root_) { - DCHECK(stack_.IsEmpty()); + + current_ = current_->Parent(); + if (current_ == &root_) current_ = nullptr; - return; - } - if (stack_.IsEmpty()) { - // We might have started with |MoveTo()|, and thus computing parent stack - // was deferred. - Push(parent); - return; - } - DCHECK_EQ(&parent, - stack_.back().parent->Children()[stack_.back().index].get()); - current_ = &parent; + if (UNLIKELY(!siblings_.IsEmpty())) + siblings_.Shrink(0); } void NGPaintFragmentTraversal::MoveToPrevious() { if (IsAtEnd()) return; - DCHECK(!stack_.IsEmpty()); - auto& stack_top = stack_.back(); - if (stack_top.index == 0) { + + if (siblings_.IsEmpty()) { + current_->Parent()->Children().ToList(&siblings_); + current_index_ = IndexOf(siblings_, *current_); + } + + if (!current_index_) { // There is no previous sibling of |current_|. We move to parent. MoveToParent(); return; } - --stack_top.index; - current_ = stack_top.parent->Children()[stack_top.index].get(); - while (!current_->Children().IsEmpty()) - Push(*current_, current_->Children().size() - 1); + + current_ = siblings_[--current_index_]; + while (current_->FirstChild()) { + current_->Children().ToList(&siblings_); + DCHECK(!siblings_.IsEmpty()); + current_index_ = siblings_.size() - 1; + current_ = siblings_[current_index_]; + } } NGPaintFragmentTraversal::AncestorRange @@ -287,11 +267,11 @@ NGPaintFragment* NGPaintFragmentTraversal::PreviousLineOf( NGPaintFragment* parent = line.Parent(); DCHECK(parent); NGPaintFragment* previous_line = nullptr; - for (const auto& sibling : parent->Children()) { - if (sibling.get() == &line) + for (NGPaintFragment* sibling : parent->Children()) { + if (sibling == &line) return previous_line; if (sibling->PhysicalFragment().IsLineBox()) - previous_line = sibling.get(); + previous_line = sibling; } NOTREACHED(); return nullptr; @@ -300,15 +280,32 @@ NGPaintFragment* NGPaintFragmentTraversal::PreviousLineOf( const NGPaintFragment* NGPaintFragmentTraversalContext::GetFragment() const { if (!parent) return nullptr; - return parent->Children()[index].get(); + return siblings[index]; +} + +NGPaintFragmentTraversalContext::NGPaintFragmentTraversalContext( + const NGPaintFragment* fragment) { + if (fragment) { + parent = fragment->Parent(); + parent->Children().ToList(&siblings); + index = IndexOf(siblings, *fragment); + } +} + +NGPaintFragmentTraversalContext::NGPaintFragmentTraversalContext( + const NGPaintFragment* parent, + unsigned index) + : parent(parent), index(index) { + DCHECK(parent); + parent->Children().ToList(&siblings); + DCHECK(index < siblings.size()); } // static NGPaintFragmentTraversalContext NGPaintFragmentTraversalContext::Create( const NGPaintFragment* fragment) { - if (!fragment) - return NGPaintFragmentTraversalContext(); - return {fragment->Parent(), IndexOfChild(*fragment->Parent(), *fragment)}; + return fragment ? NGPaintFragmentTraversalContext(fragment) + : NGPaintFragmentTraversalContext(); } NGPaintFragmentTraversalContext NGPaintFragmentTraversal::PreviousInlineLeafOf( diff --git a/chromium/third_party/blink/renderer/core/paint/ng/ng_paint_fragment_traversal.h b/chromium/third_party/blink/renderer/core/paint/ng/ng_paint_fragment_traversal.h index 5a79444e1dd..377e5466038 100644 --- a/chromium/third_party/blink/renderer/core/paint/ng/ng_paint_fragment_traversal.h +++ b/chromium/third_party/blink/renderer/core/paint/ng/ng_paint_fragment_traversal.h @@ -28,6 +28,12 @@ struct CORE_EXPORT NGPaintFragmentTraversalContext { STACK_ALLOCATED(); public: + NGPaintFragmentTraversalContext() = default; + explicit NGPaintFragmentTraversalContext(const NGPaintFragment* fragment); + NGPaintFragmentTraversalContext(const NGPaintFragment* parent, + unsigned index); + // TODO(kojii): deprecated, prefer constructors to avoid unexpected + // instantiation. static NGPaintFragmentTraversalContext Create(const NGPaintFragment*); bool IsNull() const { return !parent; } @@ -39,6 +45,7 @@ struct CORE_EXPORT NGPaintFragmentTraversalContext { const NGPaintFragment* parent = nullptr; unsigned index = 0; + Vector<NGPaintFragment*, 16> siblings; }; // Utility class for traversing the paint fragment tree. @@ -170,9 +177,6 @@ class CORE_EXPORT NGPaintFragmentTraversal { const NGPaintFragmentTraversalContext&); private: - void Push(const NGPaintFragment& parent, unsigned index); - void Push(const NGPaintFragment& fragment); - // |current_| holds a |NGPaintFragment| specified by |index|th child of // |parent| of the last element of |stack_|. const NGPaintFragment* current_ = nullptr; @@ -181,18 +185,12 @@ class CORE_EXPORT NGPaintFragmentTraversal { // from traversal. |current_| can't |root_|. const NGPaintFragment& root_; - // The stack of parent and its child index up to the root. Each stack entry - // represents the current node, and thus - // |stack_.back().parent->Children()[stack_.back().index] == current_|. - // - // Computing ancestors maybe deferred until |MoveToNextSiblingOrAncestor()| - // when |Moveto()| is used. In that case, the |stack_| does not contain all - // fragments to the |root_|. - struct ParentAndIndex { - const NGPaintFragment* parent; - unsigned index; - }; - Vector<ParentAndIndex, 4> stack_; + // Keep a list of siblings for MoveToPrevious(). + // TODO(kojii): We could keep a stack of this to avoid repetitive + // constructions of the list when we move up/down levels. Also can consider + // sharing with NGPaintFragmentTraversalContext. + unsigned current_index_ = 0; + Vector<NGPaintFragment*, 16> siblings_; DISALLOW_COPY_AND_ASSIGN(NGPaintFragmentTraversal); }; diff --git a/chromium/third_party/blink/renderer/core/paint/ng/ng_paint_fragment_traversal_test.cc b/chromium/third_party/blink/renderer/core/paint/ng/ng_paint_fragment_traversal_test.cc index e9a7f1b5242..79670ae17e1 100644 --- a/chromium/third_party/blink/renderer/core/paint/ng/ng_paint_fragment_traversal_test.cc +++ b/chromium/third_party/blink/renderer/core/paint/ng/ng_paint_fragment_traversal_test.cc @@ -27,7 +27,7 @@ class NGPaintFragmentTraversalTest : public RenderingTest, root_fragment_ = layout_block_flow_->PaintFragment(); } - const Vector<scoped_refptr<NGPaintFragment>>& RootChildren() const { + const NGPaintFragment::ChildList RootChildren() const { return root_fragment_->Children(); } @@ -51,6 +51,13 @@ class NGPaintFragmentTraversalTest : public RenderingTest, return results; } + Vector<NGPaintFragment*, 16> ToList( + const NGPaintFragment::ChildList& children) { + Vector<NGPaintFragment*, 16> list; + children.ToList(&list); + return list; + } + LayoutBlockFlow* layout_block_flow_; NGPaintFragment* root_fragment_; }; @@ -65,14 +72,14 @@ TEST_F(NGPaintFragmentTraversalTest, MoveToNext) { </div> )HTML"); NGPaintFragmentTraversal traversal(*root_fragment_); - NGPaintFragment* line0 = root_fragment_->Children()[0].get(); - NGPaintFragment* line1 = root_fragment_->Children()[1].get(); - NGPaintFragment* span = line0->Children()[1].get(); - NGPaintFragment* br = line0->Children()[2].get(); - EXPECT_THAT(ToDepthFirstList(&traversal), - ElementsAreArray({line0, line0->Children()[0].get(), span, - span->Children()[0].get(), br, line1, - line1->Children()[0].get()})); + NGPaintFragment* line0 = root_fragment_->FirstChild(); + NGPaintFragment* line1 = ToList(root_fragment_->Children())[1]; + NGPaintFragment* span = ToList(line0->Children())[1]; + NGPaintFragment* br = ToList(line0->Children())[2]; + EXPECT_THAT( + ToDepthFirstList(&traversal), + ElementsAreArray({line0, line0->FirstChild(), span, span->FirstChild(), + br, line1, line1->FirstChild()})); } TEST_F(NGPaintFragmentTraversalTest, MoveToNextWithRoot) { @@ -84,13 +91,13 @@ TEST_F(NGPaintFragmentTraversalTest, MoveToNextWithRoot) { line1 </div> )HTML"); - NGPaintFragment* line0 = root_fragment_->Children()[0].get(); - NGPaintFragment* span = line0->Children()[1].get(); - NGPaintFragment* br = line0->Children()[2].get(); + NGPaintFragment* line0 = root_fragment_->FirstChild(); + NGPaintFragment* span = ToList(line0->Children())[1]; + NGPaintFragment* br = ToList(line0->Children())[2]; NGPaintFragmentTraversal traversal(*line0); - EXPECT_THAT(ToDepthFirstList(&traversal), - ElementsAreArray({line0->Children()[0].get(), span, - span->Children()[0].get(), br})); + EXPECT_THAT( + ToDepthFirstList(&traversal), + ElementsAreArray({line0->FirstChild(), span, span->FirstChild(), br})); } TEST_F(NGPaintFragmentTraversalTest, MoveToPrevious) { @@ -103,15 +110,15 @@ TEST_F(NGPaintFragmentTraversalTest, MoveToPrevious) { </div> )HTML"); NGPaintFragmentTraversal traversal(*root_fragment_); - NGPaintFragment* line0 = root_fragment_->Children()[0].get(); - NGPaintFragment* line1 = root_fragment_->Children()[1].get(); - NGPaintFragment* span = line0->Children()[1].get(); - NGPaintFragment* br = line0->Children()[2].get(); - traversal.MoveTo(*line1->Children()[0].get()); - EXPECT_THAT(ToReverseDepthFirstList(&traversal), - ElementsAreArray({line1->Children()[0].get(), line1, br, - span->Children()[0].get(), span, - line0->Children()[0].get(), line0})); + NGPaintFragment* line0 = root_fragment_->FirstChild(); + NGPaintFragment* line1 = ToList(root_fragment_->Children())[1]; + NGPaintFragment* span = ToList(line0->Children())[1]; + NGPaintFragment* br = ToList(line0->Children())[2]; + traversal.MoveTo(*line1->FirstChild()); + EXPECT_THAT( + ToReverseDepthFirstList(&traversal), + ElementsAreArray({line1->FirstChild(), line1, br, span->FirstChild(), + span, line0->FirstChild(), line0})); } TEST_F(NGPaintFragmentTraversalTest, MoveToPreviousWithRoot) { @@ -123,14 +130,14 @@ TEST_F(NGPaintFragmentTraversalTest, MoveToPreviousWithRoot) { line1 </div> )HTML"); - NGPaintFragment* line0 = root_fragment_->Children()[0].get(); - NGPaintFragment* span = line0->Children()[1].get(); - NGPaintFragment* br = line0->Children()[2].get(); + NGPaintFragment* line0 = root_fragment_->FirstChild(); + NGPaintFragment* span = ToList(line0->Children())[1]; + NGPaintFragment* br = ToList(line0->Children())[2]; NGPaintFragmentTraversal traversal(*line0); traversal.MoveTo(*br); - EXPECT_THAT(ToReverseDepthFirstList(&traversal), - ElementsAreArray({br, span->Children()[0].get(), span, - line0->Children()[0].get()})); + EXPECT_THAT( + ToReverseDepthFirstList(&traversal), + ElementsAreArray({br, span->FirstChild(), span, line0->FirstChild()})); } TEST_F(NGPaintFragmentTraversalTest, MoveTo) { @@ -143,15 +150,15 @@ TEST_F(NGPaintFragmentTraversalTest, MoveTo) { </div> )HTML"); NGPaintFragmentTraversal traversal(*root_fragment_); - NGPaintFragment* line0 = root_fragment_->Children()[0].get(); - NGPaintFragment* line1 = root_fragment_->Children()[1].get(); - NGPaintFragment* span = line0->Children()[1].get(); - NGPaintFragment* br = line0->Children()[2].get(); + NGPaintFragment* line0 = root_fragment_->FirstChild(); + NGPaintFragment* line1 = ToList(root_fragment_->Children())[1]; + NGPaintFragment* span = ToList(line0->Children())[1]; + NGPaintFragment* br = ToList(line0->Children())[2]; traversal.MoveTo(*span); EXPECT_EQ(span, &*traversal); EXPECT_THAT(ToDepthFirstList(&traversal), - ElementsAreArray({span, span->Children()[0].get(), br, line1, - line1->Children()[0].get()})); + ElementsAreArray( + {span, span->FirstChild(), br, line1, line1->FirstChild()})); } TEST_F(NGPaintFragmentTraversalTest, MoveToWithRoot) { @@ -163,31 +170,31 @@ TEST_F(NGPaintFragmentTraversalTest, MoveToWithRoot) { line1 </div> )HTML"); - NGPaintFragment* line0 = root_fragment_->Children()[0].get(); - NGPaintFragment* span = line0->Children()[1].get(); - NGPaintFragment* br = line0->Children()[2].get(); + NGPaintFragment* line0 = root_fragment_->FirstChild(); + NGPaintFragment* span = ToList(line0->Children())[1]; + NGPaintFragment* br = ToList(line0->Children())[2]; NGPaintFragmentTraversal traversal(*line0); traversal.MoveTo(*span); EXPECT_EQ(span, &*traversal); EXPECT_THAT(ToDepthFirstList(&traversal), - ElementsAreArray({span, span->Children()[0].get(), br})); + ElementsAreArray({span, span->FirstChild(), br})); } TEST_F(NGPaintFragmentTraversalTest, PreviousLineOf) { SetUpHtml("t", "<div id=t>foo<br>bar</div>"); ASSERT_EQ(2u, RootChildren().size()); - EXPECT_EQ(nullptr, - NGPaintFragmentTraversal::PreviousLineOf(*RootChildren()[0])); - EXPECT_EQ(RootChildren()[0].get(), - NGPaintFragmentTraversal::PreviousLineOf(*RootChildren()[1])); + EXPECT_EQ(nullptr, NGPaintFragmentTraversal::PreviousLineOf( + *ToList(RootChildren())[0])); + EXPECT_EQ(ToList(RootChildren())[0], NGPaintFragmentTraversal::PreviousLineOf( + *ToList(RootChildren())[1])); } TEST_F(NGPaintFragmentTraversalTest, PreviousLineInListItem) { SetUpHtml("t", "<ul><li id=t>foo</li></ul>"); ASSERT_EQ(2u, RootChildren().size()); - ASSERT_TRUE(RootChildren()[0]->PhysicalFragment().IsListMarker()); - EXPECT_EQ(nullptr, - NGPaintFragmentTraversal::PreviousLineOf(*RootChildren()[1])); + ASSERT_TRUE(ToList(RootChildren())[0]->PhysicalFragment().IsListMarker()); + EXPECT_EQ(nullptr, NGPaintFragmentTraversal::PreviousLineOf( + *ToList(RootChildren())[1])); } TEST_F(NGPaintFragmentTraversalTest, InlineDescendantsOf) { diff --git a/chromium/third_party/blink/renderer/core/paint/ng/ng_text_fragment_painter_test.cc b/chromium/third_party/blink/renderer/core/paint/ng/ng_text_fragment_painter_test.cc index ccc822a59a5..8bbcb7b0c2b 100644 --- a/chromium/third_party/blink/renderer/core/paint/ng/ng_text_fragment_painter_test.cc +++ b/chromium/third_party/blink/renderer/core/paint/ng/ng_text_fragment_painter_test.cc @@ -19,6 +19,8 @@ #include "third_party/blink/renderer/platform/graphics/paint/paint_controller.h" #include "third_party/blink/renderer/platform/runtime_enabled_features.h" +using testing::ElementsAre; + namespace blink { class NGTextFragmentPainterTest : public PaintControllerPaintTest, @@ -50,14 +52,14 @@ TEST_P(NGTextFragmentPainterTest, TestTextStyle) { const NGPaintFragment& root_fragment = *block_flow.PaintFragment(); EXPECT_EQ(1u, root_fragment.Children().size()); - const NGPaintFragment& line_box_fragment = *root_fragment.Children()[0]; + const NGPaintFragment& line_box_fragment = *root_fragment.FirstChild(); EXPECT_EQ(1u, line_box_fragment.Children().size()); - const NGPaintFragment& text_fragment = *line_box_fragment.Children()[0]; + const NGPaintFragment& text_fragment = *line_box_fragment.FirstChild(); - EXPECT_DISPLAY_LIST( - RootPaintController().GetDisplayItemList(), 2, - TestDisplayItem(ViewBackgroundClient(), DisplayItem::kDocumentBackground), - TestDisplayItem(text_fragment, kForegroundType)); + EXPECT_THAT(RootPaintController().GetDisplayItemList(), + ElementsAre(IsSameId(&ViewScrollingBackgroundClient(), + DisplayItem::kDocumentBackground), + IsSameId(&text_fragment, kForegroundType))); } } // namespace blink diff --git a/chromium/third_party/blink/renderer/core/paint/ng/ng_text_painter.cc b/chromium/third_party/blink/renderer/core/paint/ng/ng_text_painter.cc index 86837c55665..d0588ee72f2 100644 --- a/chromium/third_party/blink/renderer/core/paint/ng/ng_text_painter.cc +++ b/chromium/third_party/blink/renderer/core/paint/ng/ng_text_painter.cc @@ -4,7 +4,7 @@ #include "third_party/blink/renderer/core/paint/ng/ng_text_painter.h" -#include "third_party/blink/renderer/core/css_property_names.h" +#include "third_party/blink/renderer/core/css/css_property_names.h" #include "third_party/blink/renderer/core/frame/settings.h" #include "third_party/blink/renderer/core/layout/ng/inline/ng_physical_text_fragment.h" #include "third_party/blink/renderer/core/layout/ng/ng_unpositioned_float.h" diff --git a/chromium/third_party/blink/renderer/core/paint/nine_piece_image_grid.cc b/chromium/third_party/blink/renderer/core/paint/nine_piece_image_grid.cc index 3cf8407a9bf..5544a45b57c 100644 --- a/chromium/third_party/blink/renderer/core/paint/nine_piece_image_grid.cc +++ b/chromium/third_party/blink/renderer/core/paint/nine_piece_image_grid.cc @@ -7,7 +7,7 @@ #include "third_party/blink/renderer/core/style/nine_piece_image.h" #include "third_party/blink/renderer/platform/geometry/float_size.h" #include "third_party/blink/renderer/platform/geometry/int_size.h" -#include "third_party/blink/renderer/platform/length_functions.h" +#include "third_party/blink/renderer/platform/geometry/length_functions.h" namespace blink { diff --git a/chromium/third_party/blink/renderer/core/paint/nine_piece_image_grid_test.cc b/chromium/third_party/blink/renderer/core/paint/nine_piece_image_grid_test.cc index ca9f272a56a..f4bc6c67e3b 100644 --- a/chromium/third_party/blink/renderer/core/paint/nine_piece_image_grid_test.cc +++ b/chromium/third_party/blink/renderer/core/paint/nine_piece_image_grid_test.cc @@ -23,9 +23,6 @@ class NinePieceImageGridTest : public RenderingTest { nullptr, nullptr, nullptr, nullptr, nullptr, cssvalue::kRepeating); return StyleGeneratedImage::Create(*gradient); } - - private: - void SetUp() override { RenderingTest::SetUp(); } }; TEST_F(NinePieceImageGridTest, NinePieceImagePainting_NoDrawables) { diff --git a/chromium/third_party/blink/renderer/core/paint/nine_piece_image_painter.cc b/chromium/third_party/blink/renderer/core/paint/nine_piece_image_painter.cc index aa56f62ea47..5c32e136170 100644 --- a/chromium/third_party/blink/renderer/core/paint/nine_piece_image_painter.cc +++ b/chromium/third_party/blink/renderer/core/paint/nine_piece_image_painter.cc @@ -100,9 +100,9 @@ bool NinePieceImagePainter::Paint(GraphicsContext& graphics_context, TRACE_EVENT1(TRACE_DISABLED_BY_DEFAULT("devtools.timeline"), "PaintImage", "data", - InspectorPaintImageEvent::Data(node, *style_image, - FloatRect(image->Rect()), - FloatRect(border_image_rect))); + inspector_paint_image_event::Data(node, *style_image, + FloatRect(image->Rect()), + FloatRect(border_image_rect))); ScopedInterpolationQuality interpolation_quality_scope( graphics_context, style.GetInterpolationQuality()); diff --git a/chromium/third_party/blink/renderer/core/paint/object_paint_invalidator.cc b/chromium/third_party/blink/renderer/core/paint/object_paint_invalidator.cc index 8592193d999..806caccaa65 100644 --- a/chromium/third_party/blink/renderer/core/paint/object_paint_invalidator.cc +++ b/chromium/third_party/blink/renderer/core/paint/object_paint_invalidator.cc @@ -199,7 +199,7 @@ void ObjectPaintInvalidator::InvalidateDisplayItemClient( TRACE_EVENT_INSTANT1( TRACE_DISABLED_BY_DEFAULT("devtools.timeline.invalidationTracking"), "PaintInvalidationTracking", TRACE_EVENT_SCOPE_THREAD, "data", - InspectorPaintInvalidationTrackingEvent::Data(object_)); + inspector_paint_invalidation_tracking_event::Data(object_)); } client.Invalidate(reason); @@ -336,7 +336,10 @@ PaintInvalidationReason ObjectPaintInvalidatorWithContext::InvalidateSelection( if (full_invalidation) return reason; - + // We should invalidate LayoutSVGText always. + // See layout_selection.cc SetShouldInvalidateIfNeeded for more detail. + if (object_.IsSVGText()) + return PaintInvalidationReason::kSelection; const LayoutRect invalidation_rect = UnionRect(new_selection_rect, old_selection_rect); if (invalidation_rect.IsEmpty()) diff --git a/chromium/third_party/blink/renderer/core/paint/object_paint_invalidator_test.cc b/chromium/third_party/blink/renderer/core/paint/object_paint_invalidator_test.cc index 88c8cfd8d3e..f538faee254 100644 --- a/chromium/third_party/blink/renderer/core/paint/object_paint_invalidator_test.cc +++ b/chromium/third_party/blink/renderer/core/paint/object_paint_invalidator_test.cc @@ -116,7 +116,7 @@ TEST_F(ObjectPaintInvalidatorTest, TraverseFloatUnderCompositedInline) { EXPECT_TRUE(composited_container_layer->NeedsRepaint()); EXPECT_FALSE(span_layer->NeedsRepaint()); - GetDocument().View()->UpdateAllLifecyclePhases(); + UpdateAllLifecyclePhasesForTest(); // Traversing from span should mark needsRepaint on correct layers for target. EXPECT_FALSE(containing_block_layer->NeedsRepaint()); @@ -128,7 +128,7 @@ TEST_F(ObjectPaintInvalidatorTest, TraverseFloatUnderCompositedInline) { EXPECT_TRUE(composited_container_layer->NeedsRepaint()); EXPECT_TRUE(span_layer->NeedsRepaint()); - GetDocument().View()->UpdateAllLifecyclePhases(); + UpdateAllLifecyclePhasesForTest(); // Traversing from compositedContainer should reach target. GetDocument().View()->SetTracksPaintInvalidations(true); @@ -286,7 +286,7 @@ TEST_F(ObjectPaintInvalidatorTest, InvalidatePaintRectangle) { EXPECT_EQ(LayoutRect(18, 18, 80, 100), target->PartialInvalidationVisualRect()); - GetDocument().View()->UpdateAllLifecyclePhases(); + UpdateAllLifecyclePhasesForTest(); EXPECT_EQ(LayoutRect(), target->PartialInvalidationLocalRect()); EXPECT_EQ(LayoutRect(), target->PartialInvalidationVisualRect()); @@ -319,7 +319,7 @@ TEST_F(ObjectPaintInvalidatorTest, Selection) { // Add selection. GetDocument().View()->SetTracksPaintInvalidations(true); GetDocument().GetFrame()->Selection().SelectAll(); - GetDocument().View()->UpdateAllLifecyclePhases(); + UpdateAllLifecyclePhasesForTest(); const auto* graphics_layer = GetLayoutView().Layer()->GraphicsLayerBacking(); const auto* invalidations = &graphics_layer->GetRasterInvalidationTracking()->Invalidations(); @@ -332,7 +332,7 @@ TEST_F(ObjectPaintInvalidatorTest, Selection) { // Simulate a change without full invalidation or selection change. GetDocument().View()->SetTracksPaintInvalidations(true); target->SetShouldCheckForPaintInvalidation(); - GetDocument().View()->UpdateAllLifecyclePhases(); + UpdateAllLifecyclePhasesForTest(); EXPECT_TRUE(graphics_layer->GetRasterInvalidationTracking() ->Invalidations() .IsEmpty()); @@ -342,7 +342,7 @@ TEST_F(ObjectPaintInvalidatorTest, Selection) { // Remove selection. GetDocument().View()->SetTracksPaintInvalidations(true); GetDocument().GetFrame()->Selection().Clear(); - GetDocument().View()->UpdateAllLifecyclePhases(); + UpdateAllLifecyclePhasesForTest(); invalidations = &graphics_layer->GetRasterInvalidationTracking()->Invalidations(); ASSERT_EQ(1u, invalidations->size()); diff --git a/chromium/third_party/blink/renderer/core/paint/paint_and_raster_invalidation_test.cc b/chromium/third_party/blink/renderer/core/paint/paint_and_raster_invalidation_test.cc index d444cab326f..96048abd457 100644 --- a/chromium/third_party/blink/renderer/core/paint/paint_and_raster_invalidation_test.cc +++ b/chromium/third_party/blink/renderer/core/paint/paint_and_raster_invalidation_test.cc @@ -6,6 +6,7 @@ #include "testing/gmock/include/gmock/gmock-matchers.h" #include "third_party/blink/renderer/core/frame/local_dom_window.h" +#include "third_party/blink/renderer/core/paint/compositing/composited_layer_mapping.h" namespace blink { @@ -25,26 +26,34 @@ void SetUpHTML(PaintAndRasterInvalidationTest& test) { height: 100px; transform-origin: 0 0; } - .background { + .solid { background: blue; } + .gradient { + background-image: linear-gradient(blue, yellow); + } + .scroll { + overflow: scroll; + } .solid-composited-scroller { overflow: scroll; will-change: transform; background: blue; } - .local-background { + .local-attachment { background-attachment: local; - overflow: scroll; - } - .gradient { - background-image: linear-gradient(blue, yellow); } .transform { transform: scale(2); } + .border { + border: 10px solid black; + } + .composited { + will-change: transform; + } </style> - <div id='target' class='background'></div> + <div id='target' class='solid'></div> )HTML"); } @@ -56,37 +65,50 @@ TEST_P(PaintAndRasterInvalidationTest, TrackingForTracing) { <div id="target"></div> )HTML"); auto* target = GetDocument().getElementById("target"); + auto get_debug_info = [&]() -> std::string { + auto* cc_layer = + RuntimeEnabledFeatures::SlimmingPaintV2Enabled() + ? GetDocument() + .View() + ->GetPaintArtifactCompositorForTesting() + ->RootLayer() + ->children()[0] + .get() + : GetLayoutView().Layer()->GraphicsLayerBacking()->CcLayer(); + return cc_layer->GetLayerClientForTesting() + ->TakeDebugInfo(cc_layer) + ->ToString(); + }; { // This is equivalent to enabling disabled-by-default-blink.invalidation // for tracing. ScopedPaintUnderInvalidationCheckingForTest checking(true); - target->setAttribute(HTMLNames::styleAttr, "height: 200px"); - GetDocument().View()->UpdateAllLifecyclePhases(); - EXPECT_THAT(GetCcLayerClient()->TakeDebugInfo(GetCcLayer())->ToString(), - MatchesRegex( - "\\{\"layer_name\":.*\"annotated_invalidation_rects\":\\[" - "\\{\"geometry_rect\":\\[8,108,100,100\\]," - "\"reason\":\"incremental\"," - "\"client\":\"LayoutBlockFlow DIV id='target'\"\\}\\]\\}")); - - target->setAttribute(HTMLNames::styleAttr, "height: 200px; width: 200px"); - GetDocument().View()->UpdateAllLifecyclePhases(); - EXPECT_THAT(GetCcLayerClient()->TakeDebugInfo(GetCcLayer())->ToString(), - MatchesRegex( - "\\{\"layer_name\":.*\"annotated_invalidation_rects\":\\[" - "\\{\"geometry_rect\":\\[108,8,100,200\\]," - "\"reason\":\"incremental\"," - "\"client\":\"LayoutBlockFlow DIV id='target'\"\\}\\]\\}")); + target->setAttribute(html_names::kStyleAttr, "height: 200px"); + UpdateAllLifecyclePhasesForTest(); + EXPECT_THAT( + get_debug_info(), + MatchesRegex( + "\\{\"layer_name\":.*\"annotated_invalidation_rects\":\\[" + "\\{\"geometry_rect\":\\[8,108,100,100\\]," + "\"reason\":\"incremental\"," + "\"client\":\"LayoutN?G?BlockFlow DIV id='target'\"\\}\\]\\}")); + + target->setAttribute(html_names::kStyleAttr, "height: 200px; width: 200px"); + UpdateAllLifecyclePhasesForTest(); + EXPECT_THAT( + get_debug_info(), + MatchesRegex( + "\\{\"layer_name\":.*\"annotated_invalidation_rects\":\\[" + "\\{\"geometry_rect\":\\[108,8,100,200\\]," + "\"reason\":\"incremental\"," + "\"client\":\"LayoutN?G?BlockFlow DIV id='target'\"\\}\\]\\}")); } - target->setAttribute(HTMLNames::styleAttr, "height: 300px; width: 300px"); - GetDocument().View()->UpdateAllLifecyclePhases(); - EXPECT_EQ(std::string::npos, GetCcLayerClient() - ->TakeDebugInfo(GetCcLayer()) - ->ToString() - .find("invalidation_rects")); + target->setAttribute(html_names::kStyleAttr, "height: 300px; width: 300px"); + UpdateAllLifecyclePhasesForTest(); + EXPECT_EQ(std::string::npos, get_debug_info().find("invalidation_rects")); } TEST_P(PaintAndRasterInvalidationTest, IncrementalInvalidationExpand) { @@ -95,8 +117,8 @@ TEST_P(PaintAndRasterInvalidationTest, IncrementalInvalidationExpand) { auto* object = target->GetLayoutObject(); GetDocument().View()->SetTracksPaintInvalidations(true); - target->setAttribute(HTMLNames::styleAttr, "width: 100px; height: 200px"); - GetDocument().View()->UpdateAllLifecyclePhases(); + target->setAttribute(html_names::kStyleAttr, "width: 100px; height: 200px"); + UpdateAllLifecyclePhasesForTest(); EXPECT_THAT(GetRasterInvalidationTracking()->Invalidations(), UnorderedElementsAre( RasterInvalidationInfo{object, object->DebugName(), @@ -114,8 +136,8 @@ TEST_P(PaintAndRasterInvalidationTest, IncrementalInvalidationShrink) { auto* object = target->GetLayoutObject(); GetDocument().View()->SetTracksPaintInvalidations(true); - target->setAttribute(HTMLNames::styleAttr, "width: 20px; height: 80px"); - GetDocument().View()->UpdateAllLifecyclePhases(); + target->setAttribute(html_names::kStyleAttr, "width: 20px; height: 80px"); + UpdateAllLifecyclePhasesForTest(); EXPECT_THAT(GetRasterInvalidationTracking()->Invalidations(), UnorderedElementsAre( RasterInvalidationInfo{object, object->DebugName(), @@ -133,8 +155,8 @@ TEST_P(PaintAndRasterInvalidationTest, IncrementalInvalidationMixed) { auto* object = target->GetLayoutObject(); GetDocument().View()->SetTracksPaintInvalidations(true); - target->setAttribute(HTMLNames::styleAttr, "width: 100px; height: 80px"); - GetDocument().View()->UpdateAllLifecyclePhases(); + target->setAttribute(html_names::kStyleAttr, "width: 100px; height: 80px"); + UpdateAllLifecyclePhasesForTest(); EXPECT_THAT(GetRasterInvalidationTracking()->Invalidations(), UnorderedElementsAre( RasterInvalidationInfo{object, object->DebugName(), @@ -152,8 +174,9 @@ TEST_P(PaintAndRasterInvalidationTest, SubpixelVisualRectChagne) { auto* object = target->GetLayoutObject(); GetDocument().View()->SetTracksPaintInvalidations(true); - target->setAttribute(HTMLNames::styleAttr, "width: 100.6px; height: 70.3px"); - GetDocument().View()->UpdateAllLifecyclePhases(); + target->setAttribute(html_names::kStyleAttr, + "width: 100.6px; height: 70.3px"); + UpdateAllLifecyclePhasesForTest(); EXPECT_THAT(GetRasterInvalidationTracking()->Invalidations(), UnorderedElementsAre( RasterInvalidationInfo{object, object->DebugName(), @@ -165,8 +188,8 @@ TEST_P(PaintAndRasterInvalidationTest, SubpixelVisualRectChagne) { GetDocument().View()->SetTracksPaintInvalidations(false); GetDocument().View()->SetTracksPaintInvalidations(true); - target->setAttribute(HTMLNames::styleAttr, "width: 50px; height: 100px"); - GetDocument().View()->UpdateAllLifecyclePhases(); + target->setAttribute(html_names::kStyleAttr, "width: 50px; height: 100px"); + UpdateAllLifecyclePhasesForTest(); EXPECT_THAT(GetRasterInvalidationTracking()->Invalidations(), UnorderedElementsAre( RasterInvalidationInfo{object, object->DebugName(), @@ -182,12 +205,13 @@ TEST_P(PaintAndRasterInvalidationTest, SubpixelVisualRectChangeWithTransform) { SetUpHTML(*this); Element* target = GetDocument().getElementById("target"); auto* object = target->GetLayoutObject(); - target->setAttribute(HTMLNames::classAttr, "background transform"); - GetDocument().View()->UpdateAllLifecyclePhases(); + target->setAttribute(html_names::kClassAttr, "solid transform"); + UpdateAllLifecyclePhasesForTest(); GetDocument().View()->SetTracksPaintInvalidations(true); - target->setAttribute(HTMLNames::styleAttr, "width: 100.6px; height: 70.3px"); - GetDocument().View()->UpdateAllLifecyclePhases(); + target->setAttribute(html_names::kStyleAttr, + "width: 100.6px; height: 70.3px"); + UpdateAllLifecyclePhasesForTest(); EXPECT_THAT(GetRasterInvalidationTracking()->Invalidations(), UnorderedElementsAre( RasterInvalidationInfo{object, object->DebugName(), @@ -199,8 +223,8 @@ TEST_P(PaintAndRasterInvalidationTest, SubpixelVisualRectChangeWithTransform) { GetDocument().View()->SetTracksPaintInvalidations(false); GetDocument().View()->SetTracksPaintInvalidations(true); - target->setAttribute(HTMLNames::styleAttr, "width: 50px; height: 100px"); - GetDocument().View()->UpdateAllLifecyclePhases(); + target->setAttribute(html_names::kStyleAttr, "width: 50px; height: 100px"); + UpdateAllLifecyclePhasesForTest(); EXPECT_THAT(GetRasterInvalidationTracking()->Invalidations(), UnorderedElementsAre( RasterInvalidationInfo{object, object->DebugName(), @@ -219,9 +243,9 @@ TEST_P(PaintAndRasterInvalidationTest, SubpixelWithinPixelsChange) { EXPECT_EQ(LayoutRect(0, 0, 50, 100), object->FirstFragment().VisualRect()); GetDocument().View()->SetTracksPaintInvalidations(true); - target->setAttribute(HTMLNames::styleAttr, + target->setAttribute(html_names::kStyleAttr, "margin-top: 0.6px; width: 50px; height: 99.3px"); - GetDocument().View()->UpdateAllLifecyclePhases(); + UpdateAllLifecyclePhasesForTest(); EXPECT_EQ(LayoutRect(0, 0, 50, 100), object->FirstFragment().VisualRect()); EXPECT_THAT(GetRasterInvalidationTracking()->Invalidations(), UnorderedElementsAre(RasterInvalidationInfo{ @@ -230,9 +254,9 @@ TEST_P(PaintAndRasterInvalidationTest, SubpixelWithinPixelsChange) { GetDocument().View()->SetTracksPaintInvalidations(false); GetDocument().View()->SetTracksPaintInvalidations(true); - target->setAttribute(HTMLNames::styleAttr, + target->setAttribute(html_names::kStyleAttr, "margin-top: 0.6px; width: 49.3px; height: 98.5px"); - GetDocument().View()->UpdateAllLifecyclePhases(); + UpdateAllLifecyclePhasesForTest(); EXPECT_EQ(LayoutRect(0, 0, 50, 100), object->FirstFragment().VisualRect()); EXPECT_THAT(GetRasterInvalidationTracking()->Invalidations(), UnorderedElementsAre(RasterInvalidationInfo{ @@ -245,13 +269,13 @@ TEST_P(PaintAndRasterInvalidationTest, ResizeRotated) { SetUpHTML(*this); Element* target = GetDocument().getElementById("target"); auto* object = target->GetLayoutObject(); - target->setAttribute(HTMLNames::styleAttr, "transform: rotate(45deg)"); - GetDocument().View()->UpdateAllLifecyclePhases(); + target->setAttribute(html_names::kStyleAttr, "transform: rotate(45deg)"); + UpdateAllLifecyclePhasesForTest(); GetDocument().View()->SetTracksPaintInvalidations(true); - target->setAttribute(HTMLNames::styleAttr, + target->setAttribute(html_names::kStyleAttr, "transform: rotate(45deg); width: 200px"); - GetDocument().View()->UpdateAllLifecyclePhases(); + UpdateAllLifecyclePhasesForTest(); auto expected_rect = EnclosingIntRect( TransformationMatrix().Rotate(45).MapRect(FloatRect(50, 0, 150, 100))); expected_rect.Intersect(IntRect(0, 0, 800, 600)); @@ -265,19 +289,19 @@ TEST_P(PaintAndRasterInvalidationTest, ResizeRotated) { TEST_P(PaintAndRasterInvalidationTest, ResizeRotatedChild) { SetUpHTML(*this); Element* target = GetDocument().getElementById("target"); - target->setAttribute(HTMLNames::styleAttr, + target->setAttribute(html_names::kStyleAttr, "transform: rotate(45deg); width: 200px"); target->SetInnerHTMLFromString( "<div id=child style='width: 50px; height: 50px; background: " "red'></div>"); - GetDocument().View()->UpdateAllLifecyclePhases(); + UpdateAllLifecyclePhasesForTest(); Element* child = GetDocument().getElementById("child"); auto* child_object = child->GetLayoutObject(); GetDocument().View()->SetTracksPaintInvalidations(true); - child->setAttribute(HTMLNames::styleAttr, + child->setAttribute(html_names::kStyleAttr, "width: 100px; height: 50px; background: red"); - GetDocument().View()->UpdateAllLifecyclePhases(); + UpdateAllLifecyclePhasesForTest(); auto expected_rect = EnclosingIntRect( TransformationMatrix().Rotate(45).MapRect(FloatRect(50, 0, 50, 50))); expected_rect.Intersect(IntRect(0, 0, 800, 600)); @@ -289,66 +313,71 @@ TEST_P(PaintAndRasterInvalidationTest, ResizeRotatedChild) { } TEST_P(PaintAndRasterInvalidationTest, CompositedLayoutViewResize) { - // TODO(crbug.com/732611): Fix SPv2 for scrolling contents layer. - if (RuntimeEnabledFeatures::SlimmingPaintV2Enabled()) - return; - SetUpHTML(*this); Element* target = GetDocument().getElementById("target"); - target->setAttribute(HTMLNames::classAttr, ""); - target->setAttribute(HTMLNames::styleAttr, "height: 2000px"); - GetDocument().View()->UpdateAllLifecyclePhases(); + target->setAttribute(html_names::kClassAttr, ""); + target->setAttribute(html_names::kStyleAttr, "height: 2000px"); + UpdateAllLifecyclePhasesForTest(); + EXPECT_EQ(kBackgroundPaintInScrollingContents, + GetLayoutView().GetBackgroundPaintLocation()); + if (!RuntimeEnabledFeatures::SlimmingPaintV2Enabled()) { + const auto* mapping = GetLayoutView().Layer()->GetCompositedLayerMapping(); + EXPECT_TRUE(mapping->BackgroundPaintsOntoScrollingContentsLayer()); + EXPECT_FALSE(mapping->BackgroundPaintsOntoGraphicsLayer()); + } // Resize the content. GetDocument().View()->SetTracksPaintInvalidations(true); - target->setAttribute(HTMLNames::styleAttr, "height: 3000px"); - GetDocument().View()->UpdateAllLifecyclePhases(); + target->setAttribute(html_names::kStyleAttr, "height: 3000px"); + UpdateAllLifecyclePhasesForTest(); EXPECT_THAT( GetRasterInvalidationTracking()->Invalidations(), UnorderedElementsAre(RasterInvalidationInfo{ - ViewScrollingContentsDisplayItemClient(), - ViewScrollingContentsDisplayItemClient()->DebugName(), + &ViewScrollingBackgroundClient(), + ViewScrollingBackgroundClient().DebugName(), IntRect(0, 2000, 800, 1000), PaintInvalidationReason::kIncremental})); GetDocument().View()->SetTracksPaintInvalidations(false); - // Resize the viewport. No paint invalidation. + // Resize the viewport. No invalidation. GetDocument().View()->SetTracksPaintInvalidations(true); GetDocument().View()->Resize(800, 1000); - GetDocument().View()->UpdateAllLifecyclePhases(); + UpdateAllLifecyclePhasesForTest(); EXPECT_FALSE(GetRasterInvalidationTracking()->HasInvalidations()); GetDocument().View()->SetTracksPaintInvalidations(false); } TEST_P(PaintAndRasterInvalidationTest, CompositedLayoutViewGradientResize) { - // TODO(crbug.com/732611): Fix SPv2 for scrolling contents layer. - if (RuntimeEnabledFeatures::SlimmingPaintV2Enabled()) - return; - SetUpHTML(*this); - GetDocument().body()->setAttribute(HTMLNames::classAttr, "gradient"); + GetDocument().body()->setAttribute(html_names::kClassAttr, "gradient"); Element* target = GetDocument().getElementById("target"); - target->setAttribute(HTMLNames::classAttr, ""); - target->setAttribute(HTMLNames::styleAttr, "height: 2000px"); - GetDocument().View()->UpdateAllLifecyclePhases(); + target->setAttribute(html_names::kClassAttr, ""); + target->setAttribute(html_names::kStyleAttr, "height: 2000px"); + UpdateAllLifecyclePhasesForTest(); + EXPECT_EQ(kBackgroundPaintInScrollingContents, + GetLayoutView().GetBackgroundPaintLocation()); + if (!RuntimeEnabledFeatures::SlimmingPaintV2Enabled()) { + const auto* mapping = GetLayoutView().Layer()->GetCompositedLayerMapping(); + EXPECT_TRUE(mapping->BackgroundPaintsOntoScrollingContentsLayer()); + EXPECT_FALSE(mapping->BackgroundPaintsOntoGraphicsLayer()); + } // Resize the content. GetDocument().View()->SetTracksPaintInvalidations(true); - target->setAttribute(HTMLNames::styleAttr, "height: 3000px"); - GetDocument().View()->UpdateAllLifecyclePhases(); + target->setAttribute(html_names::kStyleAttr, "height: 3000px"); + UpdateAllLifecyclePhasesForTest(); EXPECT_THAT( GetRasterInvalidationTracking()->Invalidations(), UnorderedElementsAre(RasterInvalidationInfo{ - ViewScrollingContentsDisplayItemClient(), - ViewScrollingContentsDisplayItemClient()->DebugName(), - IntRect(0, 0, 800, 3000), - PaintInvalidationReason::kBackgroundOnScrollingContentsLayer})); + &ViewScrollingBackgroundClient(), + ViewScrollingBackgroundClient().DebugName(), IntRect(0, 0, 800, 3000), + PaintInvalidationReason::kBackground})); GetDocument().View()->SetTracksPaintInvalidations(false); - // Resize the viewport. No paint invalidation. + // Resize the viewport. No invalidation. GetDocument().View()->SetTracksPaintInvalidations(true); GetDocument().View()->Resize(800, 1000); - GetDocument().View()->UpdateAllLifecyclePhases(); + UpdateAllLifecyclePhasesForTest(); EXPECT_FALSE(GetRasterInvalidationTracking()->HasInvalidations()); GetDocument().View()->SetTracksPaintInvalidations(false); } @@ -368,32 +397,43 @@ TEST_P(PaintAndRasterInvalidationTest, NonCompositedLayoutViewResize) { </style> <div id='content' style='width: 200px; height: 200px'></div> )HTML"); - GetDocument().View()->UpdateAllLifecyclePhases(); + UpdateAllLifecyclePhasesForTest(); Element* iframe = GetDocument().getElementById("iframe"); Element* content = ChildDocument().getElementById("content"); EXPECT_EQ(GetLayoutView(), content->GetLayoutObject()->ContainerForPaintInvalidation()); + EXPECT_EQ(kBackgroundPaintInScrollingContents, + content->GetLayoutObject() + ->View() + ->GetBackgroundPaintLocation()); // Resize the content. GetDocument().View()->SetTracksPaintInvalidations(true); - content->setAttribute(HTMLNames::styleAttr, "height: 500px"); - GetDocument().View()->UpdateAllLifecyclePhases(); + content->setAttribute(html_names::kStyleAttr, "height: 500px"); + UpdateAllLifecyclePhasesForTest(); // No invalidation because the changed part of layout overflow is clipped. EXPECT_FALSE(GetRasterInvalidationTracking()->HasInvalidations()); GetDocument().View()->SetTracksPaintInvalidations(false); // Resize the iframe. GetDocument().View()->SetTracksPaintInvalidations(true); - iframe->setAttribute(HTMLNames::styleAttr, "height: 200px"); - GetDocument().View()->UpdateAllLifecyclePhases(); - // The iframe doesn't have anything visible by itself, so we only issue raster - // invalidation for the frame contents. - EXPECT_THAT( - GetRasterInvalidationTracking()->Invalidations(), - UnorderedElementsAre(RasterInvalidationInfo{ - content->GetLayoutObject()->View(), - content->GetLayoutObject()->View()->DebugName(), - IntRect(0, 100, 100, 100), PaintInvalidationReason::kIncremental})); + iframe->setAttribute(html_names::kStyleAttr, "height: 200px"); + UpdateAllLifecyclePhasesForTest(); + if (RuntimeEnabledFeatures::SlimmingPaintV2Enabled()) { + // TODO(wangxianzhu): This is probably incorrect, but for now we assume + // any scrolling contents as composited during SPv2 painting. Perhaps we + // need some heuristic about composited scrolling during painting. + EXPECT_FALSE(GetRasterInvalidationTracking()->HasInvalidations()); + } else { + // The iframe doesn't have anything visible by itself, so we only issue + // raster invalidation for the frame contents. + EXPECT_THAT( + GetRasterInvalidationTracking()->Invalidations(), + UnorderedElementsAre(RasterInvalidationInfo{ + content->GetLayoutObject()->View(), + content->GetLayoutObject()->View()->DebugName(), + IntRect(0, 100, 100, 100), PaintInvalidationReason::kIncremental})); + } GetDocument().View()->SetTracksPaintInvalidations(false); } @@ -416,7 +456,7 @@ TEST_P(PaintAndRasterInvalidationTest, NonCompositedLayoutViewGradientResize) { </style> <div id='content' style='width: 200px; height: 200px'></div> )HTML"); - GetDocument().View()->UpdateAllLifecyclePhases(); + UpdateAllLifecyclePhasesForTest(); Element* iframe = GetDocument().getElementById("iframe"); Element* content = ChildDocument().getElementById("content"); LayoutView* frame_layout_view = content->GetLayoutObject()->View(); @@ -425,46 +465,64 @@ TEST_P(PaintAndRasterInvalidationTest, NonCompositedLayoutViewGradientResize) { // Resize the content. GetDocument().View()->SetTracksPaintInvalidations(true); - content->setAttribute(HTMLNames::styleAttr, "height: 500px"); - GetDocument().View()->UpdateAllLifecyclePhases(); - EXPECT_THAT( - GetRasterInvalidationTracking()->Invalidations(), - UnorderedElementsAre(RasterInvalidationInfo{ - frame_layout_view, frame_layout_view->DebugName(), - IntRect(0, 0, 100, 100), PaintInvalidationReason::kBackground})); + content->setAttribute(html_names::kStyleAttr, "height: 500px"); + UpdateAllLifecyclePhasesForTest(); + if (RuntimeEnabledFeatures::SlimmingPaintV2Enabled()) { + // TODO(wangxianzhu): This is probably incorrect, but for now we assume + // any scrolling contents as composited during SPv2 painting. Perhaps we + // need some heuristic about composited scrolling during painting. + EXPECT_FALSE(GetRasterInvalidationTracking()->HasInvalidations()); + } else { + EXPECT_THAT( + GetRasterInvalidationTracking()->Invalidations(), + UnorderedElementsAre(RasterInvalidationInfo{ + frame_layout_view, frame_layout_view->DebugName(), + IntRect(0, 0, 100, 100), PaintInvalidationReason::kBackground})); + } GetDocument().View()->SetTracksPaintInvalidations(false); // Resize the iframe. GetDocument().View()->SetTracksPaintInvalidations(true); - iframe->setAttribute(HTMLNames::styleAttr, "height: 200px"); - GetDocument().View()->UpdateAllLifecyclePhases(); - // The iframe doesn't have anything visible by itself, so we only issue raster - // invalidation for the frame contents. - EXPECT_THAT( - GetRasterInvalidationTracking()->Invalidations(), - UnorderedElementsAre(RasterInvalidationInfo{ - frame_layout_view, frame_layout_view->DebugName(), - IntRect(0, 0, 100, 200), PaintInvalidationReason::kGeometry})); + iframe->setAttribute(html_names::kStyleAttr, "height: 200px"); + UpdateAllLifecyclePhasesForTest(); + if (RuntimeEnabledFeatures::SlimmingPaintV2Enabled()) { + // TODO(wangxianzhu): This is probably incorrect, but for now we assume + // any scrolling contents as composited during SPv2 painting. Perhaps we + // need some heuristic about composited scrolling during painting. + EXPECT_FALSE(GetRasterInvalidationTracking()->HasInvalidations()); + } else { + // The iframe doesn't have anything visible by itself, so we only issue + // raster invalidation for the frame contents. + EXPECT_THAT( + GetRasterInvalidationTracking()->Invalidations(), + UnorderedElementsAre(RasterInvalidationInfo{ + frame_layout_view, frame_layout_view->DebugName(), + IntRect(0, 100, 100, 100), PaintInvalidationReason::kIncremental})); + } GetDocument().View()->SetTracksPaintInvalidations(false); } TEST_P(PaintAndRasterInvalidationTest, CompositedBackgroundAttachmentLocalResize) { - // TODO(crbug.com/732611): Fix SPv2 for scrolling contents layer. - if (RuntimeEnabledFeatures::SlimmingPaintV2Enabled()) - return; - SetUpHTML(*this); Element* target = GetDocument().getElementById("target"); - target->setAttribute(HTMLNames::classAttr, "background local-background"); - target->setAttribute(HTMLNames::styleAttr, "will-change: transform"); + target->setAttribute(html_names::kClassAttr, + "solid composited scroll local-attachment border"); target->SetInnerHTMLFromString( "<div id=child style='width: 500px; height: 500px'></div>", ASSERT_NO_EXCEPTION); Element* child = GetDocument().getElementById("child"); - GetDocument().View()->UpdateAllLifecyclePhases(); + UpdateAllLifecyclePhasesForTest(); auto* target_obj = ToLayoutBoxModelObject(target->GetLayoutObject()); + EXPECT_EQ(kBackgroundPaintInScrollingContents, + target_obj->GetBackgroundPaintLocation()); + if (!RuntimeEnabledFeatures::SlimmingPaintV2Enabled()) { + const auto* mapping = target_obj->Layer()->GetCompositedLayerMapping(); + EXPECT_TRUE(mapping->BackgroundPaintsOntoScrollingContentsLayer()); + EXPECT_FALSE(mapping->BackgroundPaintsOntoGraphicsLayer()); + } + auto container_raster_invalidation_tracking = [&]() -> const RasterInvalidationTracking* { if (RuntimeEnabledFeatures::SlimmingPaintV2Enabled()) @@ -484,76 +542,99 @@ TEST_P(PaintAndRasterInvalidationTest, // Resize the content. GetDocument().View()->SetTracksPaintInvalidations(true); - child->setAttribute(HTMLNames::styleAttr, "width: 500px; height: 1000px"); - GetDocument().View()->UpdateAllLifecyclePhases(); + child->setAttribute(html_names::kStyleAttr, "width: 500px; height: 1000px"); + UpdateAllLifecyclePhasesForTest(); // No invalidation on the container layer. EXPECT_FALSE(container_raster_invalidation_tracking()->HasInvalidations()); // Incremental invalidation of background on contents layer. - const auto* client = target_obj->Layer()->GraphicsLayerBacking(); + const auto& client = target_obj->GetScrollableArea() + ->GetScrollingBackgroundDisplayItemClient(); EXPECT_THAT(contents_raster_invalidation_tracking()->Invalidations(), UnorderedElementsAre(RasterInvalidationInfo{ - client, client->DebugName(), IntRect(0, 500, 500, 500), + &client, client.DebugName(), IntRect(0, 500, 500, 500), PaintInvalidationReason::kIncremental})); GetDocument().View()->SetTracksPaintInvalidations(false); // Resize the container. GetDocument().View()->SetTracksPaintInvalidations(true); - target->setAttribute(HTMLNames::styleAttr, - "will-change: transform; height: 200px"); - GetDocument().View()->UpdateAllLifecyclePhases(); - // No invalidation for composited layer resize. - EXPECT_FALSE(container_raster_invalidation_tracking()->HasInvalidations()); + target->setAttribute(html_names::kStyleAttr, "height: 200px"); + UpdateAllLifecyclePhasesForTest(); + // Border invalidated in the container layer. + EXPECT_THAT(container_raster_invalidation_tracking()->Invalidations(), + UnorderedElementsAre(RasterInvalidationInfo{ + target_obj, target_obj->DebugName(), IntRect(0, 0, 70, 220), + PaintInvalidationReason::kGeometry})); + // No invalidation on scrolling contents for container resize. EXPECT_FALSE(contents_raster_invalidation_tracking()->HasInvalidations()); GetDocument().View()->SetTracksPaintInvalidations(false); } TEST_P(PaintAndRasterInvalidationTest, CompositedBackgroundAttachmentLocalGradientResize) { - // TODO(crbug.com/732611): Fix SPv2 for scrolling contents layer. - if (RuntimeEnabledFeatures::SlimmingPaintV2Enabled()) - return; - SetUpHTML(*this); Element* target = GetDocument().getElementById("target"); - target->setAttribute(HTMLNames::classAttr, "local-background gradient"); - target->setAttribute(HTMLNames::styleAttr, "will-change: transform"); + target->setAttribute(html_names::kClassAttr, + "gradient composited scroll local-attachment border"); target->SetInnerHTMLFromString( "<div id='child' style='width: 500px; height: 500px'></div>", ASSERT_NO_EXCEPTION); Element* child = GetDocument().getElementById("child"); - GetDocument().View()->UpdateAllLifecyclePhases(); + UpdateAllLifecyclePhasesForTest(); - // Resize the content. - GetDocument().View()->SetTracksPaintInvalidations(true); - child->setAttribute(HTMLNames::styleAttr, "width: 500px; height: 1000px"); - GetDocument().View()->UpdateAllLifecyclePhases(); LayoutBoxModelObject* target_obj = ToLayoutBoxModelObject(target->GetLayoutObject()); - GraphicsLayer* container_layer = - target_obj->Layer()->GraphicsLayerBacking(target_obj); - GraphicsLayer* contents_layer = target_obj->Layer()->GraphicsLayerBacking(); + auto container_raster_invalidation_tracking = + [&]() -> const RasterInvalidationTracking* { + if (RuntimeEnabledFeatures::SlimmingPaintV2Enabled()) + return GetRasterInvalidationTracking(1); + return target_obj->Layer() + ->GraphicsLayerBacking(target_obj) + ->GetRasterInvalidationTracking(); + }; + auto contents_raster_invalidation_tracking = + [&]() -> const RasterInvalidationTracking* { + if (RuntimeEnabledFeatures::SlimmingPaintV2Enabled()) + return GetRasterInvalidationTracking(2); + return target_obj->Layer() + ->GraphicsLayerBacking() + ->GetRasterInvalidationTracking(); + }; + + // Resize the content. + GetDocument().View()->SetTracksPaintInvalidations(true); + child->setAttribute(html_names::kStyleAttr, "width: 500px; height: 1000px"); + UpdateAllLifecyclePhasesForTest(); + EXPECT_EQ(kBackgroundPaintInScrollingContents, + target_obj->GetBackgroundPaintLocation()); + if (!RuntimeEnabledFeatures::SlimmingPaintV2Enabled()) { + const auto* mapping = target_obj->Layer()->GetCompositedLayerMapping(); + EXPECT_TRUE(mapping->BackgroundPaintsOntoScrollingContentsLayer()); + EXPECT_FALSE(mapping->BackgroundPaintsOntoGraphicsLayer()); + } + // No invalidation on the container layer. - EXPECT_FALSE( - container_layer->GetRasterInvalidationTracking()->HasInvalidations()); + EXPECT_FALSE(container_raster_invalidation_tracking()->HasInvalidations()); // Full invalidation of background on contents layer because the gradient // background is resized. - EXPECT_THAT( - contents_layer->GetRasterInvalidationTracking()->Invalidations(), - UnorderedElementsAre(RasterInvalidationInfo{ - contents_layer, contents_layer->DebugName(), IntRect(0, 0, 500, 1000), - PaintInvalidationReason::kBackgroundOnScrollingContentsLayer})); + const auto& client = target_obj->GetScrollableArea() + ->GetScrollingBackgroundDisplayItemClient(); + EXPECT_THAT(contents_raster_invalidation_tracking()->Invalidations(), + UnorderedElementsAre(RasterInvalidationInfo{ + &client, client.DebugName(), IntRect(0, 0, 500, 1000), + PaintInvalidationReason::kBackground})); GetDocument().View()->SetTracksPaintInvalidations(false); // Resize the container. GetDocument().View()->SetTracksPaintInvalidations(true); - target->setAttribute(HTMLNames::styleAttr, - "will-change: transform; height: 200px"); - GetDocument().View()->UpdateAllLifecyclePhases(); - // No explicit raster invalidation for composited layer resize. - EXPECT_FALSE( - container_layer->GetRasterInvalidationTracking()->HasInvalidations()); - EXPECT_FALSE( - contents_layer->GetRasterInvalidationTracking()->HasInvalidations()); + target->setAttribute(html_names::kStyleAttr, "height: 200px"); + UpdateAllLifecyclePhasesForTest(); + // Border invalidated in the container layer. + EXPECT_THAT(container_raster_invalidation_tracking()->Invalidations(), + UnorderedElementsAre(RasterInvalidationInfo{ + target_obj, target_obj->DebugName(), IntRect(0, 0, 70, 220), + PaintInvalidationReason::kGeometry})); + // No invalidation on scrolling contents for container resize. + EXPECT_FALSE(contents_raster_invalidation_tracking()->HasInvalidations()); GetDocument().View()->SetTracksPaintInvalidations(false); } @@ -562,60 +643,93 @@ TEST_P(PaintAndRasterInvalidationTest, SetUpHTML(*this); Element* target = GetDocument().getElementById("target"); auto* object = target->GetLayoutObject(); - target->setAttribute(HTMLNames::classAttr, "background local-background"); + target->setAttribute(html_names::kClassAttr, "solid local-attachment scroll"); target->SetInnerHTMLFromString( "<div id=child style='width: 500px; height: 500px'></div>", ASSERT_NO_EXCEPTION); Element* child = GetDocument().getElementById("child"); - GetDocument().View()->UpdateAllLifecyclePhases(); - EXPECT_EQ(&GetLayoutView(), - &target->GetLayoutObject()->ContainerForPaintInvalidation()); + UpdateAllLifecyclePhasesForTest(); + EXPECT_EQ(&GetLayoutView(), object->ContainerForPaintInvalidation()); + EXPECT_EQ(kBackgroundPaintInScrollingContents, + ToLayoutBoxModelObject(object)->GetBackgroundPaintLocation()); // Resize the content. GetDocument().View()->SetTracksPaintInvalidations(true); - child->setAttribute(HTMLNames::styleAttr, "width: 500px; height: 1000px"); - GetDocument().View()->UpdateAllLifecyclePhases(); + child->setAttribute(html_names::kStyleAttr, "width: 500px; height: 1000px"); + UpdateAllLifecyclePhasesForTest(); // No invalidation because the changed part is invisible. EXPECT_FALSE(GetRasterInvalidationTracking()->HasInvalidations()); // Resize the container. GetDocument().View()->SetTracksPaintInvalidations(true); - target->setAttribute(HTMLNames::styleAttr, "height: 200px"); - GetDocument().View()->UpdateAllLifecyclePhases(); - EXPECT_THAT(GetRasterInvalidationTracking()->Invalidations(), - UnorderedElementsAre(RasterInvalidationInfo{ - object, object->DebugName(), IntRect(0, 100, 50, 100), - PaintInvalidationReason::kIncremental})); + target->setAttribute(html_names::kStyleAttr, "height: 200px"); + UpdateAllLifecyclePhasesForTest(); + if (RuntimeEnabledFeatures::SlimmingPaintV2Enabled()) { + // TODO(wangxianzhu): This is probably incorrect, but for now we assume + // any scrolling contents as composited during SPv2 painting. Perhaps we + // need some heuristic about composited scrolling during painting. + EXPECT_FALSE(GetRasterInvalidationTracking()->HasInvalidations()); + } else { + EXPECT_THAT(GetRasterInvalidationTracking()->Invalidations(), + UnorderedElementsAre(RasterInvalidationInfo{ + object, object->DebugName(), IntRect(0, 100, 50, 100), + PaintInvalidationReason::kIncremental})); + } GetDocument().View()->SetTracksPaintInvalidations(false); } TEST_P(PaintAndRasterInvalidationTest, CompositedSolidBackgroundResize) { - // TODO(crbug.com/732611): Fix SPv2 for scrolling contents layer. - if (RuntimeEnabledFeatures::SlimmingPaintV2Enabled()) - return; + // To trigger background painting on both container and contents layer. + // Note that the test may need update when we change the background paint + // location rules. + SetPreferCompositingToLCDText(false); SetUpHTML(*this); Element* target = GetDocument().getElementById("target"); - target->setAttribute(HTMLNames::classAttr, "solid-composited-scroller"); + target->setAttribute(html_names::kClassAttr, "solid composited scroll"); target->SetInnerHTMLFromString("<div style='height: 500px'></div>", ASSERT_NO_EXCEPTION); - GetDocument().View()->UpdateAllLifecyclePhases(); + UpdateAllLifecyclePhasesForTest(); // Resize the scroller. GetDocument().View()->SetTracksPaintInvalidations(true); - target->setAttribute(HTMLNames::styleAttr, "width: 100px"); - GetDocument().View()->UpdateAllLifecyclePhases(); + target->setAttribute(html_names::kStyleAttr, "width: 100px"); + UpdateAllLifecyclePhasesForTest(); LayoutBoxModelObject* target_object = ToLayoutBoxModelObject(target->GetLayoutObject()); - GraphicsLayer* scrolling_contents_layer = - target_object->Layer()->GraphicsLayerBacking(); + EXPECT_EQ( + kBackgroundPaintInScrollingContents | kBackgroundPaintInGraphicsLayer, + target_object->GetBackgroundPaintLocation()); + if (!RuntimeEnabledFeatures::SlimmingPaintV2Enabled()) { + const auto* mapping = target_object->Layer()->GetCompositedLayerMapping(); + EXPECT_TRUE(mapping->BackgroundPaintsOntoScrollingContentsLayer()); + EXPECT_TRUE(mapping->BackgroundPaintsOntoGraphicsLayer()); + } + + const auto* contents_raster_invalidation_tracking = + RuntimeEnabledFeatures::SlimmingPaintV2Enabled() + ? GetRasterInvalidationTracking(2) + : target_object->Layer() + ->GraphicsLayerBacking() + ->GetRasterInvalidationTracking(); + const auto& client = target_object->GetScrollableArea() + ->GetScrollingBackgroundDisplayItemClient(); + EXPECT_THAT(contents_raster_invalidation_tracking->Invalidations(), + UnorderedElementsAre(RasterInvalidationInfo{ + &client, client.DebugName(), IntRect(50, 0, 50, 500), + PaintInvalidationReason::kIncremental})); + const auto* container_raster_invalidation_tracking = + RuntimeEnabledFeatures::SlimmingPaintV2Enabled() + ? GetRasterInvalidationTracking(1) + : target_object->Layer() + ->GraphicsLayerBacking(target_object) + ->GetRasterInvalidationTracking(); EXPECT_THAT( - scrolling_contents_layer->GetRasterInvalidationTracking() - ->Invalidations(), + container_raster_invalidation_tracking->Invalidations(), UnorderedElementsAre(RasterInvalidationInfo{ - scrolling_contents_layer, scrolling_contents_layer->DebugName(), - IntRect(50, 0, 50, 500), PaintInvalidationReason::kIncremental})); + target_object, target_object->DebugName(), IntRect(50, 0, 50, 100), + PaintInvalidationReason::kIncremental})); GetDocument().View()->SetTracksPaintInvalidations(false); } @@ -641,7 +755,7 @@ TEST_P(PaintAndRasterInvalidationTest, RecalcOverflowInvalidatesBackground) { <div id='container'></div> )HTML"); - GetDocument().View()->UpdateAllLifecyclePhases(); + UpdateAllLifecyclePhasesForTest(); ScrollableArea* scrollable_area = GetDocument().View()->LayoutViewport(); ASSERT_EQ(scrollable_area->MaximumScrollOffset().Height(), 0); @@ -649,7 +763,7 @@ TEST_P(PaintAndRasterInvalidationTest, RecalcOverflowInvalidatesBackground) { GetDocument().GetLayoutView()->ShouldCheckForPaintInvalidation()); Element* container = GetDocument().getElementById("container"); - container->setAttribute(HTMLNames::styleAttr, + container->setAttribute(html_names::kStyleAttr, "transform: translateY(1000px);"); GetDocument().UpdateStyleAndLayoutTree(); @@ -674,8 +788,8 @@ TEST_P(PaintAndRasterInvalidationTest, EXPECT_EQ(LayoutRect(0, 0, 100, 100), child_layout_view->FirstFragment().VisualRect()); - iframe->setAttribute(HTMLNames::styleAttr, "border: 20px solid blue"); - GetDocument().View()->UpdateAllLifecyclePhases(); + iframe->setAttribute(html_names::kStyleAttr, "border: 20px solid blue"); + UpdateAllLifecyclePhasesForTest(); EXPECT_EQ(GetDocument().GetLayoutView(), &child_layout_view->ContainerForPaintInvalidation()); EXPECT_EQ(LayoutRect(0, 0, 100, 100), @@ -683,10 +797,6 @@ TEST_P(PaintAndRasterInvalidationTest, }; TEST_P(PaintAndRasterInvalidationTest, DelayedFullPaintInvalidation) { - // TODO(crbug.com/732611): Fix SPv2 for scrolling contents layer. - if (RuntimeEnabledFeatures::SlimmingPaintV2Enabled()) - return; - EnableCompositing(); SetBodyInnerHTML(R"HTML( <style>body { margin: 0 }</style> @@ -708,7 +818,7 @@ TEST_P(PaintAndRasterInvalidationTest, DelayedFullPaintInvalidation) { EXPECT_TRUE(target->Parent()->ShouldCheckForPaintInvalidation()); GetDocument().View()->SetTracksPaintInvalidations(true); - GetDocument().View()->UpdateAllLifecyclePhases(); + UpdateAllLifecyclePhasesForTest(); EXPECT_FALSE(GetRasterInvalidationTracking()->HasInvalidations()); EXPECT_FALSE(target->ShouldDoFullPaintInvalidation()); EXPECT_TRUE(target->ShouldDelayFullPaintInvalidation()); @@ -722,7 +832,7 @@ TEST_P(PaintAndRasterInvalidationTest, DelayedFullPaintInvalidation) { GetDocument().View()->SetTracksPaintInvalidations(true); // Scroll target into view. GetDocument().domWindow()->scrollTo(0, 4000); - GetDocument().View()->UpdateAllLifecyclePhases(); + UpdateAllLifecyclePhasesForTest(); EXPECT_THAT(GetRasterInvalidationTracking()->Invalidations(), UnorderedElementsAre(RasterInvalidationInfo{ target, target->DebugName(), IntRect(0, 4000, 100, 100), @@ -761,7 +871,7 @@ TEST_P(PaintAndRasterInvalidationTest, SVGHiddenContainer) { GetDocument().View()->SetTracksPaintInvalidations(true); ToElement(mask_rect->GetNode())->setAttribute("x", "20"); - GetDocument().View()->UpdateAllLifecyclePhases(); + UpdateAllLifecyclePhasesForTest(); EXPECT_EQ(LayoutRect(), mask_rect->FirstFragment().VisualRect()); EXPECT_EQ(LayoutRect(55, 66, 7, 8), real_rect->FirstFragment().VisualRect()); @@ -816,6 +926,10 @@ TEST_P(PaintAndRasterInvalidationTest, UpdateVisualRectWhenPrinting) { FloatSize page_size(400, 200); GetFrame().StartPrinting(page_size, page_size, 1); GetDocument().View()->UpdateLifecyclePhasesForPrinting(); + // In LayoutNG these may be different layout objects, so get them again + a = GetDocument().getElementById("a")->GetLayoutObject(); + b = GetDocument().getElementById("b")->GetLayoutObject(); + c = GetDocument().getElementById("c")->GetLayoutObject(); EXPECT_EQ(LayoutRect(0, 0, 150, 20), a->FirstFragment().VisualRect()); EXPECT_EQ(LayoutRect(150, 0, 150, 20), b->FirstFragment().VisualRect()); @@ -824,6 +938,9 @@ TEST_P(PaintAndRasterInvalidationTest, UpdateVisualRectWhenPrinting) { GetFrame().EndPrinting(); GetDocument().View()->UpdateLifecyclePhasesForPrinting(); + a = GetDocument().getElementById("a")->GetLayoutObject(); + b = GetDocument().getElementById("b")->GetLayoutObject(); + c = GetDocument().getElementById("c")->GetLayoutObject(); EXPECT_EQ(LayoutRect(0, 0, 150, 20), a->FirstFragment().VisualRect()); EXPECT_EQ(LayoutRect(150, 0, 150, 20), b->FirstFragment().VisualRect()); @@ -834,19 +951,19 @@ TEST_P(PaintAndRasterInvalidationTest, PaintPropertyChange) { SetUpHTML(*this); Element* target = GetDocument().getElementById("target"); auto* object = target->GetLayoutObject(); - target->setAttribute(HTMLNames::classAttr, "background transform"); - GetDocument().View()->UpdateAllLifecyclePhases(); + target->setAttribute(html_names::kClassAttr, "solid transform"); + UpdateAllLifecyclePhasesForTest(); auto* layer = ToLayoutBoxModelObject(object)->Layer(); GetDocument().View()->SetTracksPaintInvalidations(true); - target->setAttribute(HTMLNames::styleAttr, "transform: scale(3)"); + target->setAttribute(html_names::kStyleAttr, "transform: scale(3)"); GetDocument().View()->UpdateAllLifecyclePhasesExceptPaint(); EXPECT_FALSE(layer->NeedsRepaint()); const auto* transform = object->FirstFragment().PaintProperties()->Transform(); EXPECT_TRUE(transform->Changed(*transform->Parent())); - GetDocument().View()->UpdateAllLifecyclePhases(); + UpdateAllLifecyclePhasesForTest(); EXPECT_THAT(GetRasterInvalidationTracking()->Invalidations(), UnorderedElementsAre( RasterInvalidationInfo{ @@ -870,13 +987,12 @@ TEST_P(PaintAndRasterInvalidationTest, ResizeContainerOfFixedSizeSVG) { Element* target = GetDocument().getElementById("target"); GetDocument().View()->SetTracksPaintInvalidations(true); - target->setAttribute(HTMLNames::styleAttr, "width: 200px; height: 200px"); - GetDocument().View()->UpdateAllLifecyclePhases(); + target->setAttribute(html_names::kStyleAttr, "width: 200px; height: 200px"); + UpdateAllLifecyclePhasesForTest(); // No raster invalidations because the resized-div doesn't paint anything by // itself, and the svg is fixed sized. - EXPECT_THAT(GetRasterInvalidationTracking()->Invalidations(), - UnorderedElementsAre()); + EXPECT_FALSE(GetRasterInvalidationTracking()->HasInvalidations()); // At least we don't invalidate paint of the SVG rect. for (const auto& paint_invalidation : *GetDocument().View()->TrackedObjectPaintInvalidations()) { @@ -911,7 +1027,7 @@ TEST_P(PaintAndRasterInvalidationTest, ScrollingInvalidatesStickyOffset) { const auto* inner = GetLayoutObjectByElementId("inner"); EXPECT_EQ(LayoutPoint(0, 0), inner->FirstFragment().PaintOffset()); - GetDocument().View()->UpdateAllLifecyclePhases(); + UpdateAllLifecyclePhasesForTest(); EXPECT_FALSE(sticky->NeedsPaintPropertyUpdate()); EXPECT_EQ(LayoutPoint(0, 0), sticky->FirstFragment().PaintOffset()); @@ -968,8 +1084,8 @@ TEST_F(PaintInvalidatorCustomClientTest, ResetInvalidationRecorded(); - target->setAttribute(HTMLNames::styleAttr, "opacity: 0.98"); - GetDocument().View()->UpdateAllLifecyclePhases(); + target->setAttribute(html_names::kStyleAttr, "opacity: 0.98"); + UpdateAllLifecyclePhasesForTest(); EXPECT_TRUE(InvalidationRecorded()); } diff --git a/chromium/third_party/blink/renderer/core/paint/paint_and_raster_invalidation_test.h b/chromium/third_party/blink/renderer/core/paint/paint_and_raster_invalidation_test.h index c5ba86ed439..fd4ee35e43e 100644 --- a/chromium/third_party/blink/renderer/core/paint/paint_and_raster_invalidation_test.h +++ b/chromium/third_party/blink/renderer/core/paint/paint_and_raster_invalidation_test.h @@ -5,7 +5,6 @@ #ifndef THIRD_PARTY_BLINK_RENDERER_CORE_PAINT_PAINT_AND_RASTER_INVALIDATION_TEST_H_ #define THIRD_PARTY_BLINK_RENDERER_CORE_PAINT_PAINT_AND_RASTER_INVALIDATION_TEST_H_ -#include "cc/layers/picture_layer.h" #include "third_party/blink/renderer/core/paint/paint_controller_paint_test.h" #include "third_party/blink/renderer/platform/graphics/compositing/content_layer_client_impl.h" #include "third_party/blink/renderer/platform/graphics/compositing/paint_artifact_compositor.h" @@ -19,28 +18,21 @@ class PaintAndRasterInvalidationTest : public PaintControllerPaintTest { : PaintControllerPaintTest(SingleChildLocalFrameClient::Create()) {} protected: - cc::Layer* GetCcLayer(size_t index = 0) const { - if (RuntimeEnabledFeatures::SlimmingPaintV2Enabled()) { - return GetDocument() - .View() - ->GetPaintArtifactCompositorForTesting() - ->RootLayer() - ->children()[index] - .get(); - } - return GetLayoutView().Layer()->GraphicsLayerBacking()->ContentLayer(); - } - - cc::LayerClient* GetCcLayerClient(size_t index = 0) const { - return GetCcLayer(index)->GetLayerClientForTesting(); + ContentLayerClientImpl* GetContentLayerClient(size_t index = 0) const { + DCHECK(RuntimeEnabledFeatures::SlimmingPaintV2Enabled()); + const auto& clients = GetDocument() + .View() + ->GetPaintArtifactCompositorForTesting() + ->ContentLayerClientsForTesting(); + return index < clients.size() ? clients[index].get() : nullptr; } const RasterInvalidationTracking* GetRasterInvalidationTracking( size_t index = 0) const { if (RuntimeEnabledFeatures::SlimmingPaintV2Enabled()) { - return static_cast<ContentLayerClientImpl*>(GetCcLayerClient(index)) - ->GetRasterInvalidator() - .GetTracking(); + if (auto* client = GetContentLayerClient(index)) + return client->GetRasterInvalidator().GetTracking(); + return nullptr; } return GetLayoutView() .Layer() @@ -61,8 +53,11 @@ class PaintAndRasterInvalidationTest : public PaintControllerPaintTest { } } - const DisplayItemClient* ViewScrollingContentsDisplayItemClient() const { - return GetLayoutView().Layer()->GraphicsLayerBacking(); + void SetPreferCompositingToLCDText(bool enable) { + GetDocument() + .GetFrame() + ->GetSettings() + ->SetPreferCompositingToLCDTextEnabled(enable); } private: diff --git a/chromium/third_party/blink/renderer/core/paint/paint_controller_paint_test.cc b/chromium/third_party/blink/renderer/core/paint/paint_controller_paint_test.cc index f97a7e13108..40084431197 100644 --- a/chromium/third_party/blink/renderer/core/paint/paint_controller_paint_test.cc +++ b/chromium/third_party/blink/renderer/core/paint/paint_controller_paint_test.cc @@ -9,12 +9,15 @@ #include "third_party/blink/renderer/core/layout/layout_text.h" #include "third_party/blink/renderer/core/layout/line/inline_text_box.h" #include "third_party/blink/renderer/core/page/focus_controller.h" +#include "third_party/blink/renderer/core/paint/ng/ng_paint_fragment.h" #include "third_party/blink/renderer/core/paint/object_paint_properties.h" #include "third_party/blink/renderer/core/paint/paint_layer_painter.h" #include "third_party/blink/renderer/platform/graphics/graphics_context.h" #include "third_party/blink/renderer/platform/graphics/paint/drawing_display_item.h" #include "third_party/blink/renderer/platform/graphics/paint/drawing_recorder.h" +using testing::ElementsAre; + namespace blink { INSTANTIATE_PAINT_TEST_CASE_P(PaintControllerPaintTest); @@ -30,20 +33,21 @@ TEST_P(PaintControllerPaintTest, FullDocumentPaintingWithCaret) { Element& div = *ToElement(GetDocument().body()->firstChild()); InlineTextBox& text_inline_box = *ToLayoutText(div.firstChild()->GetLayoutObject())->FirstTextBox(); - EXPECT_DISPLAY_LIST( - RootPaintController().GetDisplayItemList(), 2, - TestDisplayItem(ViewBackgroundClient(), kDocumentBackgroundType), - TestDisplayItem(text_inline_box, kForegroundType)); + EXPECT_THAT(RootPaintController().GetDisplayItemList(), + ElementsAre(IsSameId(&ViewScrollingBackgroundClient(), + kDocumentBackgroundType), + IsSameId(&text_inline_box, kForegroundType))); div.focus(); - GetDocument().View()->UpdateAllLifecyclePhases(); - - EXPECT_DISPLAY_LIST( - RootPaintController().GetDisplayItemList(), 3, - TestDisplayItem(ViewBackgroundClient(), kDocumentBackgroundType), - TestDisplayItem(text_inline_box, kForegroundType), - TestDisplayItem(CaretDisplayItemClientForTesting(), - DisplayItem::kCaret)); // New! + UpdateAllLifecyclePhasesForTest(); + + EXPECT_THAT( + RootPaintController().GetDisplayItemList(), + ElementsAre( + IsSameId(&ViewScrollingBackgroundClient(), kDocumentBackgroundType), + IsSameId(&text_inline_box, kForegroundType), + // New! + IsSameId(&CaretDisplayItemClientForTesting(), DisplayItem::kCaret))); } TEST_P(PaintControllerPaintTest, InlineRelayout) { @@ -54,26 +58,37 @@ TEST_P(PaintControllerPaintTest, InlineRelayout) { LayoutBlock& div_block = *ToLayoutBlock(GetDocument().body()->firstChild()->GetLayoutObject()); LayoutText& text = *ToLayoutText(div_block.FirstChild()); - InlineTextBox& first_text_box = *text.FirstTextBox(); + DisplayItemClient& first_text_box = + text.FirstInlineFragment() + ? (DisplayItemClient&)*text.FirstInlineFragment() + : (DisplayItemClient&)*text.FirstTextBox(); - EXPECT_DISPLAY_LIST( - RootPaintController().GetDisplayItemList(), 2, - TestDisplayItem(ViewBackgroundClient(), kDocumentBackgroundType), - TestDisplayItem(first_text_box, kForegroundType)); + EXPECT_THAT(RootPaintController().GetDisplayItemList(), + ElementsAre(IsSameId(&ViewScrollingBackgroundClient(), + kDocumentBackgroundType), + IsSameId(&first_text_box, kForegroundType))); - div.setAttribute(HTMLNames::styleAttr, "width: 10px; height: 200px"); - GetDocument().View()->UpdateAllLifecyclePhases(); + div.setAttribute(html_names::kStyleAttr, "width: 10px; height: 200px"); + UpdateAllLifecyclePhasesForTest(); LayoutText& new_text = *ToLayoutText(div_block.FirstChild()); - InlineTextBox& new_first_text_box = *new_text.FirstTextBox(); - InlineTextBox& second_text_box = - *new_text.FirstTextBox()->NextForSameLayoutObject(); - - EXPECT_DISPLAY_LIST( - RootPaintController().GetDisplayItemList(), 3, - TestDisplayItem(ViewBackgroundClient(), kDocumentBackgroundType), - TestDisplayItem(new_first_text_box, kForegroundType), - TestDisplayItem(second_text_box, kForegroundType)); + DisplayItemClient& new_first_text_box = + new_text.FirstInlineFragment() + ? (DisplayItemClient&)*new_text.FirstInlineFragment() + : (DisplayItemClient&)*text.FirstTextBox(); + DisplayItemClient& second_text_box = + new_text.FirstInlineFragment() + ? (DisplayItemClient&)*NGPaintFragment:: + TraverseNextForSameLayoutObject::Next( + new_text.FirstInlineFragment()) + : (DisplayItemClient&)*new_text.FirstTextBox() + ->NextForSameLayoutObject(); + + EXPECT_THAT(RootPaintController().GetDisplayItemList(), + ElementsAre(IsSameId(&ViewScrollingBackgroundClient(), + kDocumentBackgroundType), + IsSameId(&new_first_text_box, kForegroundType), + IsSameId(&second_text_box, kForegroundType))); } TEST_P(PaintControllerPaintTest, ChunkIdClientCacheFlag) { @@ -89,29 +104,11 @@ TEST_P(PaintControllerPaintTest, ChunkIdClientCacheFlag) { LayoutObject& sub_div = *div.FirstChild(); LayoutObject& sub_div2 = *sub_div.NextSibling(); - EXPECT_DISPLAY_LIST( - RootPaintController().GetDisplayItemList(), 3, - TestDisplayItem(ViewBackgroundClient(), kDocumentBackgroundType), - TestDisplayItem(sub_div, kBackgroundType), - TestDisplayItem(sub_div2, kBackgroundType)); - - // Verify that the background does not scroll. - const PaintChunk& background_chunk = RootPaintController().PaintChunks()[0]; - auto* transform = background_chunk.properties.Transform(); - // TODO(crbug.com/732611): SPv2 invalidations are incorrect if there is - // scrolling. - if (RuntimeEnabledFeatures::SlimmingPaintV2Enabled()) - EXPECT_FALSE(transform->ScrollNode()); - else - EXPECT_TRUE(transform->ScrollNode()); - - const EffectPaintPropertyNode* effect_node = - div.FirstFragment().PaintProperties()->Effect(); - EXPECT_EQ(0.5f, effect_node->Opacity()); - - const PaintChunk& chunk = RootPaintController().PaintChunks()[1]; - EXPECT_EQ(*div.Layer(), chunk.id.client); - EXPECT_EQ(effect_node, chunk.properties.Effect()); + EXPECT_THAT(RootPaintController().GetDisplayItemList(), + ElementsAre(IsSameId(&ViewScrollingBackgroundClient(), + kDocumentBackgroundType), + IsSameId(&sub_div, kBackgroundType), + IsSameId(&sub_div2, kBackgroundType))); EXPECT_FALSE(div.Layer()->IsJustCreated()); // Client used by only paint chunks and non-cachaeable display items but not @@ -131,10 +128,10 @@ TEST_P(PaintControllerPaintTest, CompositingNoFold) { LayoutBlock& div = *ToLayoutBlock(GetLayoutObjectByElementId("div")); LayoutObject& sub_div = *div.FirstChild(); - EXPECT_DISPLAY_LIST( - RootPaintController().GetDisplayItemList(), 2, - TestDisplayItem(ViewBackgroundClient(), kDocumentBackgroundType), - TestDisplayItem(sub_div, kBackgroundType)); + EXPECT_THAT(RootPaintController().GetDisplayItemList(), + ElementsAre(IsSameId(&ViewScrollingBackgroundClient(), + kDocumentBackgroundType), + IsSameId(&sub_div, kBackgroundType))); } TEST_P(PaintControllerPaintTestForSPv2, FrameScrollingContents) { @@ -151,24 +148,29 @@ TEST_P(PaintControllerPaintTestForSPv2, FrameScrollingContents) { <div id='div4' style='top: 9000px; left: 9000px'></div> )HTML"); - auto& div1 = *GetLayoutObjectByElementId("div1"); + const auto& div1 = *GetLayoutObjectByElementId("div1"); + const auto& div2 = *GetLayoutObjectByElementId("div2"); + const auto& div3 = *GetLayoutObjectByElementId("div3"); + const auto& div4 = *GetLayoutObjectByElementId("div4"); - // TODO(crbug.com/792577): Cull rect for frame scrolling contents is too - // small? - EXPECT_DISPLAY_LIST(RootPaintController().GetDisplayItemList(), 3, - TestDisplayItem(GetLayoutView(), kDocumentBackgroundType), - TestDisplayItem(GetLayoutView(), kScrollHitTestType), - TestDisplayItem(div1, kBackgroundType)); + EXPECT_THAT( + RootPaintController().GetDisplayItemList(), + ElementsAre( + IsSameId(&GetLayoutView(), kScrollHitTestType), + IsSameId(&ViewScrollingBackgroundClient(), kDocumentBackgroundType), + IsSameId(&div1, kBackgroundType), IsSameId(&div2, kBackgroundType))); GetDocument().View()->LayoutViewport()->SetScrollOffset( ScrollOffset(5000, 5000), kProgrammaticScroll); - GetDocument().View()->UpdateAllLifecyclePhases(); - - // TODO(crbug.com/792577): Cull rect for frame scrolling contents is too - // small? - EXPECT_DISPLAY_LIST(RootPaintController().GetDisplayItemList(), 2, - TestDisplayItem(GetLayoutView(), kDocumentBackgroundType), - TestDisplayItem(GetLayoutView(), kScrollHitTestType)); + UpdateAllLifecyclePhasesForTest(); + + EXPECT_THAT( + RootPaintController().GetDisplayItemList(), + ElementsAre( + IsSameId(&GetLayoutView(), kScrollHitTestType), + IsSameId(&ViewScrollingBackgroundClient(), kDocumentBackgroundType), + IsSameId(&div2, kBackgroundType), IsSameId(&div3, kBackgroundType), + IsSameId(&div4, kBackgroundType))); } TEST_P(PaintControllerPaintTestForSPv2, BlockScrollingNonLayeredContents) { @@ -195,23 +197,25 @@ TEST_P(PaintControllerPaintTestForSPv2, BlockScrollingNonLayeredContents) { auto& div4 = *GetLayoutObjectByElementId("div4"); // Initial cull rect: (0,0 4200x4200) - EXPECT_DISPLAY_LIST(RootPaintController().GetDisplayItemList(), 4, - TestDisplayItem(GetLayoutView(), kDocumentBackgroundType), - TestDisplayItem(container, kScrollHitTestType), - TestDisplayItem(div1, kBackgroundType), - TestDisplayItem(div2, kBackgroundType)); + EXPECT_THAT( + RootPaintController().GetDisplayItemList(), + ElementsAre( + IsSameId(&ViewScrollingBackgroundClient(), kDocumentBackgroundType), + IsSameId(&container, kScrollHitTestType), + IsSameId(&div1, kBackgroundType), IsSameId(&div2, kBackgroundType))); container.GetScrollableArea()->SetScrollOffset(ScrollOffset(5000, 5000), kProgrammaticScroll); - GetDocument().View()->UpdateAllLifecyclePhases(); + UpdateAllLifecyclePhasesForTest(); // Cull rect after scroll: (1000,1000 8100x8100) - EXPECT_DISPLAY_LIST(RootPaintController().GetDisplayItemList(), 5, - TestDisplayItem(GetLayoutView(), kDocumentBackgroundType), - TestDisplayItem(container, kScrollHitTestType), - TestDisplayItem(div2, kBackgroundType), - TestDisplayItem(div3, kBackgroundType), - TestDisplayItem(div4, kBackgroundType)); + EXPECT_THAT( + RootPaintController().GetDisplayItemList(), + ElementsAre( + IsSameId(&ViewScrollingBackgroundClient(), kDocumentBackgroundType), + IsSameId(&container, kScrollHitTestType), + IsSameId(&div2, kBackgroundType), IsSameId(&div3, kBackgroundType), + IsSameId(&div4, kBackgroundType))); } TEST_P(PaintControllerPaintTestForSPv2, ScrollHitTestOrder) { @@ -236,12 +240,17 @@ TEST_P(PaintControllerPaintTestForSPv2, ScrollHitTestOrder) { // The container's items should all be after the document's scroll hit test // to ensure the container is hit before the document. Similarly, the child's // items should all be after the container's scroll hit test. - EXPECT_DISPLAY_LIST(RootPaintController().GetDisplayItemList(), 5, - TestDisplayItem(GetLayoutView(), kDocumentBackgroundType), - TestDisplayItem(GetLayoutView(), kScrollHitTestType), - TestDisplayItem(container, kBackgroundType), - TestDisplayItem(container, kScrollHitTestType), - TestDisplayItem(child, kBackgroundType)); + EXPECT_THAT( + RootPaintController().GetDisplayItemList(), + ElementsAre( + IsSameId(&GetLayoutView(), kScrollHitTestType), + IsSameId(&ViewScrollingBackgroundClient(), kDocumentBackgroundType), + IsSameId(&container, kBackgroundType), + IsSameId(&container, kScrollHitTestType), + IsSameId(&container.GetScrollableArea() + ->GetScrollingBackgroundDisplayItemClient(), + kBackgroundType), + IsSameId(&child, kBackgroundType))); } TEST_P(PaintControllerPaintTestForSPv2, NonStackingScrollHitTestOrder) { @@ -276,13 +285,18 @@ TEST_P(PaintControllerPaintTestForSPv2, NonStackingScrollHitTestOrder) { // testing should hit positive descendants, the container, and then negative // descendants so the ScrollHitTest item should be immediately after the // background. - EXPECT_DISPLAY_LIST(RootPaintController().GetDisplayItemList(), 6, - TestDisplayItem(GetLayoutView(), kDocumentBackgroundType), - TestDisplayItem(neg_z_child, kBackgroundType), - TestDisplayItem(container, kBackgroundType), - TestDisplayItem(container, kScrollHitTestType), - TestDisplayItem(child, kBackgroundType), - TestDisplayItem(pos_z_child, kBackgroundType)); + EXPECT_THAT( + RootPaintController().GetDisplayItemList(), + ElementsAre( + IsSameId(&ViewScrollingBackgroundClient(), kDocumentBackgroundType), + IsSameId(&neg_z_child, kBackgroundType), + IsSameId(&container, kBackgroundType), + IsSameId(&container, kScrollHitTestType), + IsSameId(&container.GetScrollableArea() + ->GetScrollingBackgroundDisplayItemClient(), + kBackgroundType), + IsSameId(&child, kBackgroundType), + IsSameId(&pos_z_child, kBackgroundType))); } TEST_P(PaintControllerPaintTestForSPv2, StackingScrollHitTestOrder) { @@ -315,13 +329,18 @@ TEST_P(PaintControllerPaintTestForSPv2, StackingScrollHitTestOrder) { // Both positive and negative z-index descendants are painted after the // background. The scroll hit test should be after the background but before // the z-index descendants to ensure hit test order is correct. - EXPECT_DISPLAY_LIST(RootPaintController().GetDisplayItemList(), 6, - TestDisplayItem(GetLayoutView(), kDocumentBackgroundType), - TestDisplayItem(container, kBackgroundType), - TestDisplayItem(container, kScrollHitTestType), - TestDisplayItem(neg_z_child, kBackgroundType), - TestDisplayItem(child, kBackgroundType), - TestDisplayItem(pos_z_child, kBackgroundType)); + EXPECT_THAT( + RootPaintController().GetDisplayItemList(), + ElementsAre( + IsSameId(&ViewScrollingBackgroundClient(), kDocumentBackgroundType), + IsSameId(&container, kBackgroundType), + IsSameId(&container, kScrollHitTestType), + IsSameId(&container.GetScrollableArea() + ->GetScrollingBackgroundDisplayItemClient(), + kBackgroundType), + IsSameId(&neg_z_child, kBackgroundType), + IsSameId(&child, kBackgroundType), + IsSameId(&pos_z_child, kBackgroundType))); } TEST_P(PaintControllerPaintTestForSPv2, @@ -353,12 +372,13 @@ TEST_P(PaintControllerPaintTestForSPv2, // Even though container does not paint a background, the scroll hit test item // should still be between the negative z-index child and the regular child. - EXPECT_DISPLAY_LIST(RootPaintController().GetDisplayItemList(), 5, - TestDisplayItem(GetLayoutView(), kDocumentBackgroundType), - TestDisplayItem(neg_z_child, kBackgroundType), - TestDisplayItem(container, kScrollHitTestType), - TestDisplayItem(child, kBackgroundType), - TestDisplayItem(pos_z_child, kBackgroundType)); + EXPECT_THAT(RootPaintController().GetDisplayItemList(), + ElementsAre(IsSameId(&ViewScrollingBackgroundClient(), + kDocumentBackgroundType), + IsSameId(&neg_z_child, kBackgroundType), + IsSameId(&container, kScrollHitTestType), + IsSameId(&child, kBackgroundType), + IsSameId(&pos_z_child, kBackgroundType))); } } // namespace blink diff --git a/chromium/third_party/blink/renderer/core/paint/paint_controller_paint_test.h b/chromium/third_party/blink/renderer/core/paint/paint_controller_paint_test.h index 9b284a22450..10a82a00786 100644 --- a/chromium/third_party/blink/renderer/core/paint/paint_controller_paint_test.h +++ b/chromium/third_party/blink/renderer/core/paint/paint_controller_paint_test.h @@ -47,8 +47,7 @@ class PaintControllerPaintTestBase : public RenderingTest { GraphicsContext graphics_context(RootPaintController()); GetDocument().View()->Paint( graphics_context, kGlobalPaintNormalPhase, - interest_rect ? CullRect(*interest_rect) - : CullRect(LayoutRect::InfiniteIntRect())); + interest_rect ? CullRect(*interest_rect) : CullRect::Infinite()); return true; } GetDocument().View()->Lifecycle().AdvanceTo( @@ -65,13 +64,10 @@ class PaintControllerPaintTestBase : public RenderingTest { return true; } - const DisplayItemClient& ViewBackgroundClient() { - if (!RuntimeEnabledFeatures::SlimmingPaintV2Enabled()) { - // With SPv1, the document background uses the scrolling contents - // layer as its DisplayItemClient. - return *GetLayoutView().Layer()->GraphicsLayerBacking(); - } - return GetLayoutView(); + const DisplayItemClient& ViewScrollingBackgroundClient() { + return GetLayoutView() + .GetScrollableArea() + ->GetScrollingBackgroundDisplayItemClient(); } void CommitAndFinishCycle() { @@ -119,6 +115,11 @@ class PaintControllerPaintTestBase : public RenderingTest { bool ClientCacheIsValid(const DisplayItemClient& client) { return RootPaintController().ClientCacheIsValid(client); } + + using SubsequenceMarkers = PaintController::SubsequenceMarkers; + SubsequenceMarkers* GetSubsequenceMarkers(const DisplayItemClient& client) { + return RootPaintController().GetSubsequenceMarkers(client); + } }; class PaintControllerPaintTest : public PaintTestConfigurations, @@ -128,6 +129,29 @@ class PaintControllerPaintTest : public PaintTestConfigurations, : PaintControllerPaintTestBase(local_frame_client) {} }; +// Shorter names for frequently used display item types in core/ tests. +const DisplayItem::Type kNonScrollingBackgroundChunkType = + DisplayItem::PaintPhaseToDrawingType(PaintPhase::kSelfBlockBackgroundOnly); +const DisplayItem::Type kScrollingBackgroundChunkType = + DisplayItem::PaintPhaseToClipType(PaintPhase::kSelfBlockBackgroundOnly); +const DisplayItem::Type kNonScrollingContentsBackgroundChunkType = + DisplayItem::PaintPhaseToDrawingType( + PaintPhase::kDescendantBlockBackgroundsOnly); +const DisplayItem::Type kScrollingContentsBackgroundChunkType = + DisplayItem::PaintPhaseToClipType( + PaintPhase::kDescendantBlockBackgroundsOnly); + +#define EXPECT_SUBSEQUENCE(client, expected_start, expected_end) \ + do { \ + auto* subsequence = GetSubsequenceMarkers(client); \ + ASSERT_NE(nullptr, subsequence); \ + EXPECT_EQ(static_cast<size_t>(expected_start), subsequence->start); \ + EXPECT_EQ(static_cast<size_t>(expected_end), subsequence->end); \ + } while (false) + +#define EXPECT_NO_SUBSEQUENCE(client) \ + EXPECT_EQ(nullptr, GetSubsequenceMarkers(client) + } // namespace blink #endif // THIRD_PARTY_BLINK_RENDERER_CORE_PAINT_PAINT_CONTROLLER_PAINT_TEST_H_ diff --git a/chromium/third_party/blink/renderer/core/paint/paint_info.h b/chromium/third_party/blink/renderer/core/paint/paint_info.h index 5ec583786b9..fa4838be50b 100644 --- a/chromium/third_party/blink/renderer/core/paint/paint_info.h +++ b/chromium/third_party/blink/renderer/core/paint/paint_info.h @@ -71,7 +71,8 @@ struct CORE_EXPORT PaintInfo { fragment_logical_top_in_flow_thread), paint_flags_(paint_flags), global_paint_flags_(global_paint_flags), - suppress_painting_descendants_(suppress_painting_descendants) {} + suppress_painting_descendants_(suppress_painting_descendants), + is_painting_scrolling_background_(false) {} PaintInfo(GraphicsContext& new_context, const PaintInfo& copy_other_fields_from) @@ -84,12 +85,20 @@ struct CORE_EXPORT PaintInfo { paint_flags_(copy_other_fields_from.paint_flags_), global_paint_flags_(copy_other_fields_from.global_paint_flags_), suppress_painting_descendants_( - copy_other_fields_from.suppress_painting_descendants_) {} + copy_other_fields_from.suppress_painting_descendants_), + is_painting_scrolling_background_(false) { + // We should never pass is_painting_scrolling_background_ other PaintInfo. + DCHECK(!copy_other_fields_from.is_painting_scrolling_background_); + } // Creates a PaintInfo for painting descendants. See comments about the paint // phases in PaintPhase.h for details. PaintInfo ForDescendants() const { PaintInfo result(*this); + + // We should never start to paint descendant when the flag is set. + DCHECK(!result.is_painting_scrolling_background_); + if (phase == PaintPhase::kDescendantOutlinesOnly) result.phase = PaintPhase::kOutline; else if (phase == PaintPhase::kDescendantBlockBackgroundsOnly) @@ -104,9 +113,17 @@ struct CORE_EXPORT PaintInfo { return paint_flags_ & kPaintLayerPaintingRenderingResourceSubtree; } + // TODO(wangxianzhu): Rename this function to SkipBackground() for SPv2. bool SkipRootBackground() const { return paint_flags_ & kPaintLayerPaintingSkipRootBackground; } + void SetSkipsBackground(bool b) { + DCHECK(RuntimeEnabledFeatures::SlimmingPaintV2Enabled()); + if (b) + paint_flags_ |= kPaintLayerPaintingSkipRootBackground; + else + paint_flags_ &= ~kPaintLayerPaintingSkipRootBackground; + } bool IsPrinting() const { return global_paint_flags_ & kGlobalPaintPrinting; } @@ -128,19 +145,10 @@ struct CORE_EXPORT PaintInfo { const CullRect& GetCullRect() const { return cull_rect_; } - void ApplyInfiniteCullRect() { - cull_rect_ = CullRect(LayoutRect::InfiniteIntRect()); - } - - void UpdateCullRect(const AffineTransform& local_to_parent_transform) { - cull_rect_.UpdateCullRect(local_to_parent_transform); - } + void ApplyInfiniteCullRect() { cull_rect_ = CullRect::Infinite(); } - void UpdateCullRectForScrollingContents( - const IntRect& overflow_clip_rect, - const AffineTransform& local_to_parent_transform) { - cull_rect_.UpdateForScrollingContents(overflow_clip_rect, - local_to_parent_transform); + void TransformCullRect(const TransformPaintPropertyNode* transform) { + cull_rect_.ApplyTransform(transform); } // Returns the fragment of the current painting object matching the current @@ -161,8 +169,17 @@ struct CORE_EXPORT PaintInfo { fragment_logical_top_in_flow_thread_ = fragment_logical_top; } + bool IsPaintingScrollingBackground() const { + DCHECK(RuntimeEnabledFeatures::SlimmingPaintV2Enabled()); + return is_painting_scrolling_background_; + } + void SetIsPaintingScrollingBackground(bool b) { + DCHECK(RuntimeEnabledFeatures::SlimmingPaintV2Enabled()); + is_painting_scrolling_background_ = b; + } + // FIXME: Introduce setters/getters at some point. Requires a lot of changes - // throughout layout/. + // throughout paint/. GraphicsContext& context; PaintPhase phase; @@ -176,13 +193,12 @@ struct CORE_EXPORT PaintInfo { // which initiated the current painting, in the containing flow thread. LayoutUnit fragment_logical_top_in_flow_thread_; - const PaintLayerFlags paint_flags_; + PaintLayerFlags paint_flags_; const GlobalPaintFlags global_paint_flags_; const bool suppress_painting_descendants_; - // TODO(chrishtr): temporary while we implement CullRect everywhere. - friend class ScopedSVGPaintState; - friend class SVGShapePainter; + // For SPv2 only. + bool is_painting_scrolling_background_; }; Image::ImageDecodingMode GetImageDecodingMode(Node*); diff --git a/chromium/third_party/blink/renderer/core/paint/paint_invalidator.cc b/chromium/third_party/blink/renderer/core/paint/paint_invalidator.cc index 7a9f4cab984..83e098a5dc3 100644 --- a/chromium/third_party/blink/renderer/core/paint/paint_invalidator.cc +++ b/chromium/third_party/blink/renderer/core/paint/paint_invalidator.cc @@ -226,10 +226,8 @@ void PaintInvalidator::UpdatePaintingLayer(const LayoutObject& object, context.painting_layer->SetNeedsPaintPhaseDescendantBlockBackgrounds(); } else if (RuntimeEnabledFeatures::PaintTouchActionRectsEnabled()) { // Hit testing rects for touch action paint in the background phase. - if (object.EffectiveWhitelistedTouchAction() != - TouchAction::kTouchActionAuto) { + if (object.HasEffectiveWhitelistedTouchAction()) context.painting_layer->SetNeedsPaintPhaseDescendantBlockBackgrounds(); - } } } @@ -490,7 +488,9 @@ void PaintInvalidator::InvalidatePaint( auto reason = static_cast<const DisplayItemClient&>(object) .GetPaintInvalidationReason(); if (object.ShouldDelayFullPaintInvalidation() && - !IsFullPaintInvalidationReason(reason)) + (!IsFullPaintInvalidationReason(reason) || + // Delay invalidation if the client has never been painted. + reason == PaintInvalidationReason::kJustCreated)) pending_delayed_paint_invalidations_.push_back(&object); if (object.SubtreeShouldDoFullPaintInvalidation()) { diff --git a/chromium/third_party/blink/renderer/core/paint/paint_layer.cc b/chromium/third_party/blink/renderer/core/paint/paint_layer.cc index 134aa1e493e..90810d02437 100644 --- a/chromium/third_party/blink/renderer/core/paint/paint_layer.cc +++ b/chromium/third_party/blink/renderer/core/paint/paint_layer.cc @@ -48,8 +48,8 @@ #include "third_party/blink/public/platform/task_type.h" #include "third_party/blink/renderer/core/animation/scroll_timeline.h" +#include "third_party/blink/renderer/core/css/css_property_names.h" #include "third_party/blink/renderer/core/css/pseudo_style_request.h" -#include "third_party/blink/renderer/core/css_property_names.h" #include "third_party/blink/renderer/core/dom/document.h" #include "third_party/blink/renderer/core/dom/shadow_root.h" #include "third_party/blink/renderer/core/frame/local_frame.h" @@ -85,11 +85,11 @@ #include "third_party/blink/renderer/platform/bindings/v8_per_isolate_data.h" #include "third_party/blink/renderer/platform/geometry/float_point_3d.h" #include "third_party/blink/renderer/platform/geometry/float_rect.h" +#include "third_party/blink/renderer/platform/geometry/length_functions.h" #include "third_party/blink/renderer/platform/graphics/compositor_filter_operations.h" #include "third_party/blink/renderer/platform/graphics/filters/filter.h" #include "third_party/blink/renderer/platform/graphics/paint/geometry_mapper.h" #include "third_party/blink/renderer/platform/instrumentation/tracing/trace_event.h" -#include "third_party/blink/renderer/platform/length_functions.h" #include "third_party/blink/renderer/platform/runtime_enabled_features.h" #include "third_party/blink/renderer/platform/transforms/transform_state.h" #include "third_party/blink/renderer/platform/transforms/transformation_matrix.h" @@ -117,7 +117,7 @@ struct SameSizeAsPaintLayer : DisplayItemClient { LayoutUnit layout_units[4]; IntSize size; Persistent<PaintLayerScrollableArea> scrollable_area; - LayoutRect previous_dirty_rect; + CullRect previous_cull_rect; }; static_assert(sizeof(PaintLayer) == sizeof(SameSizeAsPaintLayer), @@ -126,8 +126,6 @@ static_assert(sizeof(PaintLayer) == sizeof(SameSizeAsPaintLayer), } // namespace -using namespace HTMLNames; - PaintLayerRareData::PaintLayerRareData() : enclosing_pagination_layer(nullptr), potential_compositing_reasons_from_style(CompositingReason::kNone), @@ -247,7 +245,7 @@ PaintLayerCompositor* PaintLayer::Compositor() const { void PaintLayer::ContentChanged(ContentChangeType change_type) { // updateLayerCompositingState will query compositingReasons for accelerated // overflow scrolling. This is tripped by - // LayoutTests/compositing/content-changed-chicken-egg.html + // web_tests/compositing/content-changed-chicken-egg.html DisableCompositingQueryAsserts disabler; if (Compositor()) { @@ -282,15 +280,6 @@ bool PaintLayer::PaintsWithFilters() const { GetCompositingState() != kPaintsIntoOwnBacking; } -bool PaintLayer::PaintsWithBackdropFilters() const { - if (!GetLayoutObject().HasBackdropFilter()) - return false; - - // https://code.google.com/p/chromium/issues/detail?id=343759 - DisableCompositingQueryAsserts disabler; - return !GetCompositedLayerMapping() || - GetCompositingState() != kPaintsIntoOwnBacking; -} LayoutSize PaintLayer::SubpixelAccumulation() const { return rare_data_ ? rare_data_->subpixel_accumulation : LayoutSize(); @@ -1673,7 +1662,7 @@ bool PaintLayer::HasOverflowControls() const { void PaintLayer::AppendSingleFragmentIgnoringPagination( PaintLayerFragments& fragments, const PaintLayer* root_layer, - const LayoutRect* dirty_rect, + const CullRect* cull_rect, OverlayScrollbarClipBehavior overlay_scrollbar_clip_behavior, ShouldRespectOverflowClipType respect_overflow_clip, const LayoutPoint* offset_from_root, @@ -1685,7 +1674,7 @@ void PaintLayer::AppendSingleFragmentIgnoringPagination( respect_overflow_clip, sub_pixel_accumulation); Clipper(kUseGeometryMapper) .CalculateRects(clip_rects_context, &GetLayoutObject().FirstFragment(), - dirty_rect, fragment.layer_bounds, + cull_rect, fragment.layer_bounds, fragment.background_rect, fragment.foreground_rect, offset_from_root); fragment.root_fragment_data = &root_layer->GetLayoutObject().FirstFragment(); @@ -1714,7 +1703,7 @@ bool PaintLayer::ShouldFragmentCompositedBounds( void PaintLayer::CollectFragments( PaintLayerFragments& fragments, const PaintLayer* root_layer, - const LayoutRect* dirty_rect, + const CullRect* cull_rect, OverlayScrollbarClipBehavior overlay_scrollbar_clip_behavior, ShouldRespectOverflowClipType respect_overflow_clip, const LayoutPoint* offset_from_root, @@ -1771,20 +1760,20 @@ void PaintLayer::CollectFragments( overlay_scrollbar_clip_behavior, respect_overflow_clip, sub_pixel_accumulation); - base::Optional<LayoutRect> fragment_dirty_rect; - if (dirty_rect) { - // |dirty_rect| is in the coordinate space of |root_layer| (i.e. the + base::Optional<CullRect> fragment_cull_rect; + if (cull_rect) { + // |cull_rect| is in the coordinate space of |root_layer| (i.e. the // space of |root_layer|'s first fragment). Map the rect to the space of // the current root fragment. - fragment_dirty_rect = *dirty_rect; - first_root_fragment_data.MapRectToFragment(*root_fragment_data, - *fragment_dirty_rect); + auto rect = cull_rect->Rect(); + first_root_fragment_data.MapRectToFragment(*root_fragment_data, rect); + fragment_cull_rect.emplace(rect); } Clipper(kUseGeometryMapper) .CalculateRects( clip_rects_context, fragment_data, - fragment_dirty_rect ? &*fragment_dirty_rect : nullptr, + fragment_cull_rect ? &*fragment_cull_rect : nullptr, fragment.layer_bounds, fragment.background_rect, fragment.foreground_rect, offset_from_root_can_be_used ? offset_from_root : nullptr); @@ -2509,7 +2498,8 @@ bool PaintLayer::HitTestClippedOutByClipPath( float inverse_zoom = 1 / GetLayoutObject().StyleRef().EffectiveZoom(); point.Scale(inverse_zoom, inverse_zoom); reference_box.Scale(inverse_zoom); - return !clipper->HitTestClipContent(reference_box, point); + HitTestLocation location(point); + return !clipper->HitTestClipContent(reference_box, location); } bool PaintLayer::IntersectsDamageRect( @@ -2755,25 +2745,6 @@ GraphicsLayer* PaintLayer::GraphicsLayerBacking(const LayoutObject* obj) const { } } -BackgroundPaintLocation PaintLayer::GetBackgroundPaintLocation( - uint32_t* reasons) const { - BackgroundPaintLocation location; - bool may_have_scrolling_layers_without_scrolling = IsRootLayer(); - if (!ScrollsOverflow() && !may_have_scrolling_layers_without_scrolling) { - location = kBackgroundPaintInGraphicsLayer; - } else { - // If we care about LCD text, paint root backgrounds into scrolling contents - // layer even if style suggests otherwise. (For non-root scrollers, we just - // avoid compositing - see PLSA::ComputeNeedsCompositedScrolling.) - DCHECK(Compositor()); - if (IsRootLayer() && !Compositor()->PreferCompositingToLCDTextEnabled()) - location = kBackgroundPaintInScrollingContents; - else - location = GetLayoutObject().GetBackgroundPaintLocation(reasons); - } - return location; -} - void PaintLayer::EnsureCompositedLayerMapping() { if (rare_data_ && rare_data_->composited_layer_mapping) return; @@ -2877,7 +2848,8 @@ bool PaintLayer::CompositesWithOpacity() const { } bool PaintLayer::BackgroundIsKnownToBeOpaqueInRect( - const LayoutRect& local_rect) const { + const LayoutRect& local_rect, + bool should_check_children) const { if (PaintsWithTransparency(kGlobalPaintNormalPhase)) return false; @@ -2909,6 +2881,9 @@ bool PaintLayer::BackgroundIsKnownToBeOpaqueInRect( if (GetLayoutObject().BackgroundIsKnownToBeOpaqueInRect(local_rect)) return true; + if (!should_check_children) + return false; + // We can't consult child layers if we clip, since they might cover // parts of the rect that are clipped out. if (GetLayoutObject().HasClipRelatedProperty()) @@ -2942,7 +2917,7 @@ bool PaintLayer::ChildBackgroundIsKnownToBeOpaqueInRect( child_layer->ConvertToLayerCoords(this, child_offset); child_local_rect.MoveBy(-child_offset); - if (child_layer->BackgroundIsKnownToBeOpaqueInRect(child_local_rect)) + if (child_layer->BackgroundIsKnownToBeOpaqueInRect(child_local_rect, true)) return true; } return false; @@ -3089,15 +3064,8 @@ bool PaintLayer::AttemptDirectCompositingUpdate( if (!rare_data_ || !rare_data_->composited_layer_mapping) return false; - // To cut off almost all the work in the compositing update for - // this case, we treat inline transforms has having assumed overlap - // (similar to how we treat animated transforms). Notice that we read - // CompositingReasonInlineTransform from the m_compositingReasons, which - // means that the inline transform actually triggered assumed overlap in - // the overlap map. - if (diff.TransformChanged() && - (!rare_data_ || !(rare_data_->compositing_reasons & - CompositingReason::kInlineTransform))) + // If a transform changed, we can't use the fast path. + if (diff.TransformChanged()) return false; // We composite transparent Layers differently from non-transparent @@ -3173,7 +3141,8 @@ void PaintLayer::StyleDidChange(StyleDifference diff, SetNeedsCompositingInputsUpdate(); } - if (diff.NeedsLayout()) + // HasAlphaChanged can affect whether a composited layer is opaque. + if (diff.NeedsLayout() || diff.HasAlphaChanged()) SetNeedsCompositingInputsUpdate(); // A scroller that changes background color might become opaque or not @@ -3263,11 +3232,21 @@ void PaintLayer::UpdateCompositorFilterOperationsForFilter( if (!operations.IsEmpty() && !filter_on_effect_node_dirty_ && reference_box == operations.ReferenceBox()) return; - operations = FilterEffectBuilder(reference_box, zoom).BuildFilterOperations(filter); } +void PaintLayer::UpdateCompositorFilterOperationsForBackdropFilter( + CompositorFilterOperations& operations) const { + const auto& style = GetLayoutObject().StyleRef(); + float zoom = style.EffectiveZoom(); + auto filter = FilterOperationsIncludingReflection(); + FloatRect reference_box = FilterReferenceBox(filter, zoom); + if (!operations.IsEmpty() && reference_box == operations.ReferenceBox()) + return; + operations = CreateCompositorFilterOperationsForBackdropFilter(); +} + CompositorFilterOperations PaintLayer::CreateCompositorFilterOperationsForBackdropFilter() const { const auto& style = GetLayoutObject().StyleRef(); @@ -3279,8 +3258,10 @@ PaintLayer::CreateCompositorFilterOperationsForBackdropFilter() const { PaintLayerResourceInfo& PaintLayer::EnsureResourceInfo() { PaintLayerRareData& rare_data = EnsureRareData(); - if (!rare_data.resource_info) - rare_data.resource_info = new PaintLayerResourceInfo(this); + if (!rare_data.resource_info) { + rare_data.resource_info = + MakeGarbageCollected<PaintLayerResourceInfo>(this); + } return *rare_data.resource_info; } diff --git a/chromium/third_party/blink/renderer/core/paint/paint_layer.h b/chromium/third_party/blink/renderer/core/paint/paint_layer.h index 43e1ef79a57..232b549fab4 100644 --- a/chromium/third_party/blink/renderer/core/paint/paint_layer.h +++ b/chromium/third_party/blink/renderer/core/paint/paint_layer.h @@ -59,6 +59,7 @@ #include "third_party/blink/renderer/core/paint/paint_layer_stacking_node_iterator.h" #include "third_party/blink/renderer/core/paint/paint_result.h" #include "third_party/blink/renderer/platform/graphics/compositing_reasons.h" +#include "third_party/blink/renderer/platform/graphics/paint/cull_rect.h" #include "third_party/blink/renderer/platform/graphics/squashing_disallowed_reasons.h" #include "third_party/blink/renderer/platform/wtf/allocator.h" @@ -292,10 +293,6 @@ class CORE_EXPORT PaintLayer : public DisplayItemClient { void SetSizeHackForLayoutTreeAsText(const LayoutSize& size) { size_ = size; } - // For LayoutTreeAsText - LayoutRect RectIgnoringNeedsPositionUpdate() const { - return LayoutRect(LocationInternal(), size_); - } #if DCHECK_IS_ON() bool NeedsPositionUpdate() const { return needs_position_update_; } #endif @@ -531,12 +528,6 @@ class CORE_EXPORT PaintLayer : public DisplayItemClient { // Returns nullptr if this PaintLayer is not composited. GraphicsLayer* GraphicsLayerBacking(const LayoutObject* = nullptr) const; - // TODO(yigu): PaintLayerScrollableArea::computeNeedsCompositedScrolling - // calls this method to obtain main thread scrolling reasons due to - // background paint location. Once the cases get handled on compositor the - // parameter "reasons" could be removed. - BackgroundPaintLocation GetBackgroundPaintLocation( - uint32_t* reasons = nullptr) const; // NOTE: If you are using hasCompositedLayerMapping to determine the state of // compositing for this layer, (and not just to do bookkeeping related to the // mapping like, say, allocating or deallocating a mapping), then you may have @@ -607,7 +598,10 @@ class CORE_EXPORT PaintLayer : public DisplayItemClient { // Returns true if background phase is painted opaque in the given rect. // The query rect is given in local coordinates. - bool BackgroundIsKnownToBeOpaqueInRect(const LayoutRect&) const; + // if |should_check_children| is true, checks non-composited stacking children + // recursively to see if they paint opaquely over the rect. + bool BackgroundIsKnownToBeOpaqueInRect(const LayoutRect&, + bool should_check_children) const; bool ContainsDirtyOverlayScrollbars() const { return contains_dirty_overlay_scrollbars_; @@ -624,6 +618,9 @@ class CORE_EXPORT PaintLayer : public DisplayItemClient { void SetFilterOnEffectNodeDirty() { filter_on_effect_node_dirty_ = true; } void ClearFilterOnEffectNodeDirty() { filter_on_effect_node_dirty_ = false; } + void UpdateCompositorFilterOperationsForBackdropFilter( + CompositorFilterOperations&) const; + void SetIsUnderSVGHiddenContainer(bool value) { is_under_svg_hidden_container_ = value; } @@ -633,7 +630,6 @@ class CORE_EXPORT PaintLayer : public DisplayItemClient { const; bool PaintsWithFilters() const; - bool PaintsWithBackdropFilters() const; FilterEffect* LastFilterEffect() const; // Maps "forward" to determine which pixels in a destination rect are @@ -758,6 +754,8 @@ class CORE_EXPORT PaintLayer : public DisplayItemClient { IntRect unclipped_absolute_bounding_box; const LayoutBoxModelObject* clipping_container = nullptr; + + bool is_under_video = false; }; void SetNeedsCompositingInputsUpdate(); @@ -826,6 +824,9 @@ class CORE_EXPORT PaintLayer : public DisplayItemClient { const PaintLayer* MaskAncestor() const { return GetAncestorDependentCompositingInputs().mask_ancestor; } + bool IsUnderVideo() const { + return GetAncestorDependentCompositingInputs().is_under_video; + } bool HasDescendantWithClipPath() const { DCHECK(!needs_descendant_dependent_flags_update_); return has_descendant_with_clip_path_; @@ -917,7 +918,7 @@ class CORE_EXPORT PaintLayer : public DisplayItemClient { void AppendSingleFragmentIgnoringPagination( PaintLayerFragments&, const PaintLayer* root_layer, - const LayoutRect* dirty_rect, + const CullRect* cull_rect, OverlayScrollbarClipBehavior = kIgnorePlatformOverlayScrollbarSize, ShouldRespectOverflowClipType = kRespectOverflowClip, const LayoutPoint* offset_from_root = nullptr, @@ -926,7 +927,7 @@ class CORE_EXPORT PaintLayer : public DisplayItemClient { void CollectFragments( PaintLayerFragments&, const PaintLayer* root_layer, - const LayoutRect* dirty_rect, + const CullRect* cull_rect, OverlayScrollbarClipBehavior = kIgnorePlatformOverlayScrollbarSize, ShouldRespectOverflowClipType = kRespectOverflowClip, const LayoutPoint* offset_from_root = nullptr, @@ -965,12 +966,8 @@ class CORE_EXPORT PaintLayer : public DisplayItemClient { // shouldCreateSubsequence() in PaintLayerPainter.cpp for the cases we use // subsequence when painting a PaintLayer. - LayoutRect PreviousPaintDirtyRect() const { - return previous_paint_dirty_rect_; - } - void SetPreviousPaintDirtyRect(const LayoutRect& rect) { - previous_paint_dirty_rect_ = rect; - } + CullRect PreviousCullRect() const { return previous_cull_rect_; } + void SetPreviousCullRect(const CullRect& rect) { previous_cull_rect_ = rect; } PaintResult PreviousPaintResult() const { return static_cast<PaintResult>(previous_paint_result_); @@ -1341,7 +1338,7 @@ class CORE_EXPORT PaintLayer : public DisplayItemClient { std::unique_ptr<PaintLayerStackingNode> stacking_node_; - LayoutRect previous_paint_dirty_rect_; + CullRect previous_cull_rect_; std::unique_ptr<PaintLayerRareData> rare_data_; diff --git a/chromium/third_party/blink/renderer/core/paint/paint_layer_clipper.cc b/chromium/third_party/blink/renderer/core/paint/paint_layer_clipper.cc index d9408bf5d5c..5c2b8267cc7 100644 --- a/chromium/third_party/blink/renderer/core/paint/paint_layer_clipper.cc +++ b/chromium/third_party/blink/renderer/core/paint/paint_layer_clipper.cc @@ -48,6 +48,7 @@ #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/layout/layout_view.h" +#include "third_party/blink/renderer/core/page/scrolling/root_scroller_util.h" #include "third_party/blink/renderer/core/paint/compositing/compositing_reason_finder.h" #include "third_party/blink/renderer/core/paint/object_paint_properties.h" #include "third_party/blink/renderer/core/paint/paint_layer.h" @@ -270,7 +271,7 @@ LayoutRect PaintLayerClipper::LocalClipRect( void PaintLayerClipper::CalculateRectsWithGeometryMapper( const ClipRectsContext& context, const FragmentData& fragment_data, - const LayoutRect* paint_dirty_rect, + const CullRect* cull_rect, LayoutRect& layer_bounds, ClipRect& background_rect, ClipRect& foreground_rect, @@ -280,20 +281,24 @@ void PaintLayerClipper::CalculateRectsWithGeometryMapper( layer_bounds.SetLocation(*offset_from_root); } else { layer_bounds.SetLocation(LayoutPoint(context.sub_pixel_accumulation)); - layer_bounds.MoveBy(fragment_data.PaintOffset()); - GeometryMapper::SourceToDestinationRect( - fragment_data.PreTransform(), - context.root_fragment->LocalBorderBoxProperties().Transform(), - layer_bounds); - layer_bounds.MoveBy(-context.root_fragment->PaintOffset()); + if (&layer_ == context.root_layer) { + DCHECK_EQ(&fragment_data, context.root_fragment); + } else { + layer_bounds.MoveBy(fragment_data.PaintOffset()); + GeometryMapper::SourceToDestinationRect( + fragment_data.PreTransform(), + context.root_fragment->LocalBorderBoxProperties().Transform(), + layer_bounds); + layer_bounds.MoveBy(-context.root_fragment->PaintOffset()); + } } CalculateBackgroundClipRectWithGeometryMapper( context, fragment_data, kRespectOverflowClip, background_rect); foreground_rect.Reset(); - if (paint_dirty_rect) - background_rect.Intersect(*paint_dirty_rect); + if (cull_rect) + background_rect.Intersect(LayoutRect(cull_rect->Rect())); if (ShouldClipOverflow(context)) { LayoutBoxModelObject& layout_object = layer_.GetLayoutObject(); @@ -312,7 +317,7 @@ void PaintLayerClipper::CalculateRectsWithGeometryMapper( void PaintLayerClipper::CalculateRects( const ClipRectsContext& context, const FragmentData* fragment_data, - const LayoutRect* paint_dirty_rect, + const CullRect* cull_rect, LayoutRect& layer_bounds, ClipRect& background_rect, ClipRect& foreground_rect, @@ -325,7 +330,7 @@ void PaintLayerClipper::CalculateRects( // TODO(chrishtr): find the root cause of not having a fragment and fix it. if (!fragment_data->HasLocalBorderBoxProperties()) return; - CalculateRectsWithGeometryMapper(context, *fragment_data, paint_dirty_rect, + CalculateRectsWithGeometryMapper(context, *fragment_data, cull_rect, layer_bounds, background_rect, foreground_rect, offset_from_root); return; @@ -339,8 +344,8 @@ void PaintLayerClipper::CalculateRects( CalculateBackgroundClipRect(context, background_rect); background_rect.Move(context.sub_pixel_accumulation); } - if (paint_dirty_rect) - background_rect.Intersect(*paint_dirty_rect); + if (cull_rect) + background_rect.Intersect(LayoutRect(cull_rect->Rect())); foreground_rect = background_rect; @@ -524,12 +529,12 @@ void PaintLayerClipper::InitializeCommonClipRectState( LayoutRect PaintLayerClipper::LocalVisualRect( const ClipRectsContext& context) const { const LayoutObject& layout_object = layer_.GetLayoutObject(); - // The LayoutView is special since its overflow clipping rect may be larger - // than its box rect (crbug.com/492871). + // The LayoutView or Global Root Scroller is special since its overflow + // clipping rect may be larger than its box rect (crbug.com/492871). + bool affected_by_url_bar = layout_object.IsGlobalRootScroller(); LayoutRect layer_bounds_with_visual_overflow = - layout_object.IsLayoutView() - ? ToLayoutView(layout_object).ViewRect() - : ToLayoutBox(layout_object).VisualOverflowRect(); + affected_by_url_bar ? layout_object.View()->ViewRect() + : ToLayoutBox(layout_object).VisualOverflowRect(); ToLayoutBox(layout_object) .FlipForWritingMode( // PaintLayer are in physical coordinates, so the overflow has to be diff --git a/chromium/third_party/blink/renderer/core/paint/paint_layer_clipper.h b/chromium/third_party/blink/renderer/core/paint/paint_layer_clipper.h index 50bde01ed1e..dc5b2f7fd76 100644 --- a/chromium/third_party/blink/renderer/core/paint/paint_layer_clipper.h +++ b/chromium/third_party/blink/renderer/core/paint/paint_layer_clipper.h @@ -47,9 +47,8 @@ #include "third_party/blink/renderer/core/core_export.h" #include "third_party/blink/renderer/core/paint/clip_rects_cache.h" - +#include "third_party/blink/renderer/platform/graphics/paint/cull_rect.h" #include "third_party/blink/renderer/platform/scroll/scroll_types.h" - #include "third_party/blink/renderer/platform/wtf/allocator.h" namespace blink { @@ -191,11 +190,11 @@ class CORE_EXPORT PaintLayerClipper { // include subpixel accumualation. Otherwise it is set to the offset from // |layer_| to |root_layer|, plus |context.sub_pixel_accumuation|. // |fragment_data| is only used in kUseGeometryMapper mode. - // If |paint_dirty_rect| is provided, intersects |background_rect| - // and |foreground_rect| with it. + // If |cull_rect| is provided, intersects |background_rect| and + // |foreground_rect| with it. void CalculateRects(const ClipRectsContext&, const FragmentData*, - const LayoutRect* paint_dirty_rect, + const CullRect* cull_rect, LayoutRect& layer_bounds, ClipRect& background_rect, ClipRect& foreground_rect, @@ -232,7 +231,7 @@ class CORE_EXPORT PaintLayerClipper { ALWAYS_INLINE void CalculateRectsWithGeometryMapper( const ClipRectsContext&, const FragmentData&, - const LayoutRect* paint_dirty_rect, + const CullRect* cull_rect, LayoutRect& layer_bounds, ClipRect& background_rect, ClipRect& foreground_rect, diff --git a/chromium/third_party/blink/renderer/core/paint/paint_layer_clipper_test.cc b/chromium/third_party/blink/renderer/core/paint/paint_layer_clipper_test.cc index df01f76103e..31723b02f8d 100644 --- a/chromium/third_party/blink/renderer/core/paint/paint_layer_clipper_test.cc +++ b/chromium/third_party/blink/renderer/core/paint/paint_layer_clipper_test.cc @@ -10,8 +10,8 @@ #include "third_party/blink/renderer/core/layout/layout_view.h" #include "third_party/blink/renderer/core/paint/paint_layer.h" #include "third_party/blink/renderer/core/testing/core_unit_test_helper.h" -#include "third_party/blink/renderer/platform/layout_test_support.h" #include "third_party/blink/renderer/platform/testing/unit_test_helpers.h" +#include "third_party/blink/renderer/platform/web_test_support.h" namespace blink { @@ -20,12 +20,12 @@ class PaintLayerClipperTest : public RenderingTest { PaintLayerClipperTest() : RenderingTest(EmptyLocalFrameClient::Create()) {} void SetUp() override { - LayoutTestSupport::SetMockThemeEnabledForTest(true); + WebTestSupport::SetMockThemeEnabledForTest(true); RenderingTest::SetUp(); } void TearDown() override { - LayoutTestSupport::SetMockThemeEnabledForTest(false); + WebTestSupport::SetMockThemeEnabledForTest(false); RenderingTest::TearDown(); } }; @@ -877,10 +877,6 @@ TEST_F(PaintLayerClipperTest, ScrollbarClipBehaviorParent) { PaintLayer* parent_paint_layer = ToLayoutBoxModelObject(parent->GetLayoutObject())->Layer(); - Element* child = GetDocument().getElementById("child"); - PaintLayer* child_paint_layer = - ToLayoutBoxModelObject(child->GetLayoutObject())->Layer(); - ClipRectsContext context( parent_paint_layer, &parent_paint_layer->GetLayoutObject().FirstFragment(), @@ -890,7 +886,7 @@ TEST_F(PaintLayerClipperTest, ScrollbarClipBehaviorParent) { ClipRect background_rect, foreground_rect; parent_paint_layer->Clipper(PaintLayer::kUseGeometryMapper) .CalculateRects(context, - &child_paint_layer->GetLayoutObject().FirstFragment(), + &parent_paint_layer->GetLayoutObject().FirstFragment(), nullptr, layer_bounds, background_rect, foreground_rect); // Only the foreground is clipped by the scrollbar size, because we diff --git a/chromium/third_party/blink/renderer/core/paint/paint_layer_painter.cc b/chromium/third_party/blink/renderer/core/paint/paint_layer_painter.cc index fe82fe74207..aaca470e4d1 100644 --- a/chromium/third_party/blink/renderer/core/paint/paint_layer_painter.cc +++ b/chromium/third_party/blink/renderer/core/paint/paint_layer_painter.cc @@ -6,6 +6,7 @@ #include "base/optional.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/layout/layout_view.h" #include "third_party/blink/renderer/core/paint/clip_path_clipper.h" #include "third_party/blink/renderer/core/paint/compositing/composited_layer_mapping.h" @@ -38,12 +39,11 @@ static inline bool ShouldSuppressPaintingLayer(const PaintLayer& layer) { } void PaintLayerPainter::Paint(GraphicsContext& context, - const LayoutRect& damage_rect, + const CullRect& cull_rect, const GlobalPaintFlags global_paint_flags, PaintLayerFlags paint_flags) { - PaintLayerPaintingInfo painting_info( - &paint_layer_, LayoutRect(EnclosingIntRect(damage_rect)), - global_paint_flags, LayoutSize()); + PaintLayerPaintingInfo painting_info(&paint_layer_, cull_rect, + global_paint_flags, LayoutSize()); if (!paint_layer_.PaintsIntoOwnOrGroupedBacking(global_paint_flags)) Paint(context, painting_info, paint_flags); } @@ -100,6 +100,12 @@ PaintResult PaintLayerPainter::Paint( GraphicsContext& context, const PaintLayerPaintingInfo& painting_info, PaintLayerFlags paint_flags) { + if (paint_layer_.GetLayoutObject().PaintBlockedByDisplayLock()) + return kFullyPainted; + // TODO(vmpstr): This should be called after paint succeeds, but due to + // multiple early outs this is more convenient. We should use RAII here. + paint_layer_.GetLayoutObject().NotifyDisplayLockDidPaint(); + if (paint_layer_.GetLayoutObject().GetFrameView()->ShouldThrottleRendering()) return kFullyPainted; @@ -195,41 +201,28 @@ static bool ShouldCreateSubsequence(const PaintLayer& paint_layer, static bool ShouldRepaintSubsequence( PaintLayer& paint_layer, - const PaintLayerPaintingInfo& painting_info, - ShouldRespectOverflowClipType respect_overflow_clip) { - bool needs_repaint = false; - - // We should set shouldResetEmptyPaintPhaseFlags if some previously unpainted - // objects may begin to be painted, causing a previously empty paint phase to - // become non-empty. - + const PaintLayerPaintingInfo& painting_info) { // Repaint subsequence if the layer is marked for needing repaint. - // We don't set needsResetEmptyPaintPhase here, but clear the empty paint - // phase flags in PaintLayer::setNeedsPaintPhaseXXX(), to ensure that we won't - // clear previousPaintPhaseXXXEmpty flags when unrelated things changed which - // won't cause the paint phases to become non-empty. if (paint_layer.NeedsRepaint()) - needs_repaint = true; + return true; - // Repaint if previously the layer might be clipped by paintDirtyRect and - // paintDirtyRect changes. - if ((paint_layer.PreviousPaintResult() == kMayBeClippedByPaintDirtyRect || + // Repaint if previously the layer may be clipped by cull rect, and cull rect + // changes. + if ((paint_layer.PreviousPaintResult() == kMayBeClippedByCullRect || // When PaintUnderInvalidationChecking is enabled, always repaint the // subsequence when the paint rect changes because we will strictly match // new and cached subsequences. Normally we can reuse the cached fully // painted subsequence even if we would partially paint this time. RuntimeEnabledFeatures::PaintUnderInvalidationCheckingEnabled()) && - paint_layer.PreviousPaintDirtyRect() != painting_info.paint_dirty_rect) { - needs_repaint = true; - } - paint_layer.SetPreviousPaintDirtyRect(painting_info.paint_dirty_rect); + paint_layer.PreviousCullRect() != painting_info.cull_rect) + return true; - return needs_repaint; + return false; } -static bool ShouldUseInfiniteDirtyRect(const GraphicsContext& context, - const PaintLayer& layer, - PaintLayerPaintingInfo& painting_info) { +static bool ShouldUseInfiniteCullRect(const GraphicsContext& context, + const PaintLayer& layer, + PaintLayerPaintingInfo& painting_info) { // Cull rects and clips can't be propagated across a filter which moves // pixels, since the input of the filter may be outside the cull rect / // clips yet still result in painted output. @@ -247,12 +240,25 @@ static bool ShouldUseInfiniteDirtyRect(const GraphicsContext& context, // 2) Complexity: Difficulty updating clips when ancestor transforms // change. // For these reasons, we use an infinite dirty rect here. - if (layer.PaintsWithTransform(painting_info.GetGlobalPaintFlags())) { - // The reasons don't apply for printing though, because when we enter and - // leaving printing mode, full invalidations occur. - return !context.Printing(); - } + if (layer.PaintsWithTransform(painting_info.GetGlobalPaintFlags()) && + // The reasons don't apply for printing though, because when we enter and + // leaving printing mode, full invalidations occur. + !context.Printing()) + return true; + + return false; +} +static bool IsMainFrameNotClippingContents(const PaintLayer& layer) { + // If MainFrameClipsContent is false which means that WebPreferences:: + // record_whole_document is true, we should not cull the scrolling contents + // of the main frame. + if (layer.GetLayoutObject().IsLayoutView()) { + const auto* frame = layer.GetLayoutObject().GetFrame(); + if (frame && frame->IsMainFrame() && + !frame->GetSettings()->GetMainFrameClipsContent()) + return true; + } return false; } @@ -262,44 +268,70 @@ void PaintLayerPainter::AdjustForPaintProperties( PaintLayerFlags& paint_flags) { const auto& first_fragment = paint_layer_.GetLayoutObject().FirstFragment(); - bool is_using_infinite_dirty_rect = painting_info.paint_dirty_rect == - LayoutRect(LayoutRect::InfiniteIntRect()); - bool should_use_infinite_dirty_rect = - ShouldUseInfiniteDirtyRect(context, paint_layer_, painting_info); - if (!is_using_infinite_dirty_rect && should_use_infinite_dirty_rect) { - painting_info.paint_dirty_rect = LayoutRect(LayoutRect::InfiniteIntRect()); - is_using_infinite_dirty_rect = true; + bool is_main_frame_not_clipping_contents = + IsMainFrameNotClippingContents(paint_layer_); + bool should_use_infinite_cull_rect = + is_main_frame_not_clipping_contents || + ShouldUseInfiniteCullRect(context, paint_layer_, painting_info); + if (should_use_infinite_cull_rect) { + painting_info.cull_rect = CullRect::Infinite(); + // Avoid clipping during CollectFragments. + if (is_main_frame_not_clipping_contents) + paint_flags |= kPaintLayerPaintingOverflowContents; } if (painting_info.root_layer == &paint_layer_) return; - const auto& first_root_fragment = - painting_info.root_layer->GetLayoutObject().FirstFragment(); - bool transform_changed = - first_root_fragment.LocalBorderBoxProperties().Transform() != - first_fragment.LocalBorderBoxProperties().Transform(); - - // Will use the current layer as the new root layer if the layer requires - // infinite dirty rect or has different transform space from the current - // root layer. - if (!should_use_infinite_dirty_rect && !transform_changed) - return; - - if (!is_using_infinite_dirty_rect && transform_changed) { - // painting_info.paint_dirty_rect is currently in - // |painting_info.root_layer|'s pixel-snapped border box space. We need to - // adjust it into |paint_layer_|'s space. - // This handles the following cases: + if (!should_use_infinite_cull_rect) { + // painting_info.cull_rect is currently in |painting_info.root_layer|'s + // pixel-snapped border box space. We need to adjust it into + // |paint_layer_|'s space. This handles the following cases: // - The current layer has PaintOffsetTranslation; // - The current layer's transform state escapes the root layers contents // transform, e.g. a fixed-position layer; // - Scroll offsets. - first_root_fragment.MapRectToFragment(first_fragment, - painting_info.paint_dirty_rect); + const auto& first_root_fragment = + painting_info.root_layer->GetLayoutObject().FirstFragment(); + const auto* source_transform = + first_root_fragment.LocalBorderBoxProperties().Transform(); + if (RuntimeEnabledFeatures::SlimmingPaintV2Enabled() && + IsMainFrameNotClippingContents(*painting_info.root_layer)) { + // Use PostScrollTranslation as the source transform to avoid clipping of + // the scrolling contents in CullRect::ApplyTransforms(). + source_transform = first_root_fragment.PostScrollTranslation(); + } + const auto* destination_transform = + first_fragment.LocalBorderBoxProperties().Transform(); + if (source_transform == destination_transform) + return; + + if (RuntimeEnabledFeatures::SlimmingPaintV2Enabled()) { + auto& cull_rect = painting_info.cull_rect; + // CullRect::ApplyTransforms() requires the cull rect in the source + // transform space. Convert cull_rect from the root layer's local space. + cull_rect.MoveBy(RoundedIntPoint(first_root_fragment.PaintOffset())); + base::Optional<CullRect> old_cull_rect; + if (!paint_layer_.NeedsRepaint()) { + old_cull_rect = paint_layer_.PreviousCullRect(); + // Convert old_cull_rect into the layer's transform space. + old_cull_rect->MoveBy(RoundedIntPoint(first_fragment.PaintOffset())); + } + cull_rect.ApplyTransforms(source_transform, destination_transform, + old_cull_rect); + // Convert cull_rect from the layer's transform space to the layer's local + // space. + cull_rect.MoveBy(-RoundedIntPoint(first_fragment.PaintOffset())); + } else if (!painting_info.cull_rect.IsInfinite()) { + auto rect = painting_info.cull_rect.Rect(); + first_root_fragment.MapRectToFragment(first_fragment, rect); + painting_info.cull_rect = CullRect(rect); + } } - // Make the current layer the new root layer. + // We reach here if the layer requires infinite cull rect or has different + // transform space from the current root layer. Use the current layer as + // the new root layer. painting_info.root_layer = &paint_layer_; // These flags no longer apply for the new root layer. paint_flags &= ~kPaintLayerPaintingSkipRootBackground; @@ -317,7 +349,9 @@ PaintResult PaintLayerPainter::PaintLayerContents( GraphicsContext& context, const PaintLayerPaintingInfo& painting_info_arg, PaintLayerFlags paint_flags_arg) { - PaintLayerFlags paint_flags = paint_flags_arg; + DCHECK(paint_layer_.IsSelfPaintingLayer() || + paint_layer_.HasSelfPaintingLayerDescendant()); + PaintResult result = kFullyPainted; if (paint_layer_.GetLayoutObject().GetFrameView()->ShouldThrottleRendering()) @@ -331,12 +365,13 @@ PaintResult PaintLayerPainter::PaintLayerContents( // TODO(crbug.com/848056): This can happen e.g. when we paint a filter // referencing a SVG foreign object through feImage, especially when there // is circular references. Should find a better solution. - paint_layer_.SetPreviousPaintDirtyRect(LayoutRect()); - return kMayBeClippedByPaintDirtyRect; + paint_layer_.SetPreviousCullRect(CullRect()); + return kMayBeClippedByCullRect; } - DCHECK(paint_layer_.IsSelfPaintingLayer() || - paint_layer_.HasSelfPaintingLayerDescendant()); + PaintLayerFlags paint_flags = paint_flags_arg; + PaintLayerPaintingInfo painting_info = painting_info_arg; + AdjustForPaintProperties(context, painting_info, paint_flags); bool is_self_painting_layer = paint_layer_.IsSelfPaintingLayer(); bool is_painting_overlay_scrollbars = @@ -365,21 +400,17 @@ PaintResult PaintLayerPainter::PaintLayerContents( LayoutSize subpixel_accumulation = paint_layer_.GetCompositingState() == kPaintsIntoOwnBacking ? paint_layer_.SubpixelAccumulation() - : painting_info_arg.sub_pixel_accumulation; - - PaintLayerPaintingInfo painting_info = painting_info_arg; - AdjustForPaintProperties(context, painting_info, paint_flags); + : painting_info.sub_pixel_accumulation; ShouldRespectOverflowClipType respect_overflow_clip = ShouldRespectOverflowClip(paint_flags, paint_layer_.GetLayoutObject()); bool should_create_subsequence = ShouldCreateSubsequence( - paint_layer_, context, painting_info_arg, paint_flags); + paint_layer_, context, painting_info, paint_flags); base::Optional<SubsequenceRecorder> subsequence_recorder; if (should_create_subsequence) { - if (!ShouldRepaintSubsequence(paint_layer_, painting_info, - respect_overflow_clip) && + if (!ShouldRepaintSubsequence(paint_layer_, painting_info) && SubsequenceRecorder::UseCachedSubsequenceIfPossible(context, paint_layer_)) { return paint_layer_.PreviousPaintResult(); @@ -393,8 +424,8 @@ PaintResult PaintLayerPainter::PaintLayerContents( offset_from_root.Move(subpixel_accumulation); LayoutRect bounds = paint_layer_.PhysicalBoundingBox(offset_from_root); - if (!painting_info.paint_dirty_rect.Contains(bounds)) - result = kMayBeClippedByPaintDirtyRect; + if (!LayoutRect(painting_info.cull_rect.Rect()).Contains(bounds)) + result = kMayBeClippedByCullRect; // These helpers output clip and compositing operations using a RAII pattern. // Stack-allocated-varibles are destructed in the reverse order of @@ -436,7 +467,7 @@ PaintResult PaintLayerPainter::PaintLayerContents( // clipping container. This handles nested border radius by including // all of them in the mask. // - // The paint rect is in this layer's space, so convert it to the clipper's + // The cull rect is in this layer's space, so convert it to the clipper's // layer's space. The root_layer is also changed to the clipper's layer to // simplify coordinate system adjustments. The change to root_layer must // persist to correctly record the clips. @@ -445,22 +476,24 @@ PaintResult PaintLayerPainter::PaintLayerContents( local_painting_info.root_layer = paint_layer_for_fragments; paint_layer_.ConvertToLayerCoords(local_painting_info.root_layer, offset_to_clipper); - local_painting_info.paint_dirty_rect.MoveBy(offset_to_clipper); + LayoutRect new_cull_rect(local_painting_info.cull_rect.Rect()); + new_cull_rect.MoveBy(offset_to_clipper); + local_painting_info.cull_rect = CullRect(EnclosingIntRect(new_cull_rect)); // Overflow clip of the compositing container is irrelevant. respect_overflow_clip = kIgnoreOverflowClip; } paint_layer_for_fragments->CollectFragments( layer_fragments, local_painting_info.root_layer, - &local_painting_info.paint_dirty_rect, - kIgnorePlatformOverlayScrollbarSize, respect_overflow_clip, - &offset_from_root, local_painting_info.sub_pixel_accumulation); + &local_painting_info.cull_rect, kIgnorePlatformOverlayScrollbarSize, + respect_overflow_clip, &offset_from_root, + local_painting_info.sub_pixel_accumulation); // PaintLayer::CollectFragments depends on the paint dirty rect in // complicated ways. For now, always assume a partially painted output // for fragmented content. if (layer_fragments.size() > 1) - result = kMayBeClippedByPaintDirtyRect; + result = kMayBeClippedByCullRect; if (paint_flags & kPaintLayerPaintingAncestorClippingMaskPhase) { // Fragment offsets have been computed in the clipping container's @@ -477,7 +510,7 @@ PaintResult PaintLayerPainter::PaintLayerContents( should_paint_content = AtLeastOneFragmentIntersectsDamageRect( layer_fragments, local_painting_info, paint_flags, offset_from_root); if (!should_paint_content) - result = kMayBeClippedByPaintDirtyRect; + result = kMayBeClippedByCullRect; } } @@ -533,8 +566,8 @@ PaintResult PaintLayerPainter::PaintLayerContents( paint_layer_, DisplayItem::kLayerChunkNegativeZOrderChildren); } if (PaintChildren(kNegativeZOrderChildren, context, painting_info, - paint_flags) == kMayBeClippedByPaintDirtyRect) - result = kMayBeClippedByPaintDirtyRect; + paint_flags) == kMayBeClippedByCullRect) + result = kMayBeClippedByCullRect; } if (should_paint_own_contents) { @@ -555,9 +588,8 @@ PaintResult PaintLayerPainter::PaintLayerContents( DisplayItem::kLayerChunkNormalFlowAndPositiveZOrderChildren); } if (PaintChildren(kNormalFlowChildren | kPositiveZOrderChildren, context, - painting_info, - paint_flags) == kMayBeClippedByPaintDirtyRect) - result = kMayBeClippedByPaintDirtyRect; + painting_info, paint_flags) == kMayBeClippedByCullRect) + result = kMayBeClippedByCullRect; } if (should_paint_overlay_scrollbars) { @@ -629,8 +661,8 @@ PaintResult PaintLayerPainter::PaintLayerContents( } } - if (subsequence_recorder) - paint_layer_.SetPreviousPaintResult(result); + paint_layer_.SetPreviousPaintResult(result); + paint_layer_.SetPreviousCullRect(local_painting_info.cull_rect); return result; } @@ -698,8 +730,8 @@ PaintResult PaintLayerPainter::PaintChildren( continue; if (PaintLayerPainter(*child).Paint(context, painting_info, paint_flags) == - kMayBeClippedByPaintDirtyRect) - result = kMayBeClippedByPaintDirtyRect; + kMayBeClippedByCullRect) + result = kMayBeClippedByCullRect; } return result; @@ -947,14 +979,13 @@ void PaintLayerPainter::PaintChildClippingMaskForFragments( void PaintLayerPainter::PaintOverlayScrollbars( GraphicsContext& context, - const LayoutRect& damage_rect, + const CullRect& cull_rect, const GlobalPaintFlags paint_flags) { if (!paint_layer_.ContainsDirtyOverlayScrollbars()) return; - PaintLayerPaintingInfo painting_info( - &paint_layer_, LayoutRect(EnclosingIntRect(damage_rect)), paint_flags, - LayoutSize()); + PaintLayerPaintingInfo painting_info(&paint_layer_, cull_rect, paint_flags, + LayoutSize()); Paint(context, painting_info, kPaintLayerPaintingOverlayScrollbars); paint_layer_.SetContainsDirtyOverlayScrollbars(false); diff --git a/chromium/third_party/blink/renderer/core/paint/paint_layer_painter.h b/chromium/third_party/blink/renderer/core/paint/paint_layer_painter.h index e1ca59231fb..4c39e007f2b 100644 --- a/chromium/third_party/blink/renderer/core/paint/paint_layer_painter.h +++ b/chromium/third_party/blink/renderer/core/paint/paint_layer_painter.h @@ -13,6 +13,7 @@ namespace blink { +class CullRect; class ClipRect; class ComputedStyle; class DisplayItemClient; @@ -30,11 +31,11 @@ class CORE_EXPORT PaintLayerPainter { public: PaintLayerPainter(PaintLayer& paint_layer) : paint_layer_(paint_layer) {} - // The Paint() method paints the layers that intersect the damage rect from + // The Paint() method paints the layers that intersect the cull rect from // back to front. paint() assumes that the caller will clip to the bounds of // damageRect if necessary. void Paint(GraphicsContext&, - const LayoutRect& damage_rect, + const CullRect&, const GlobalPaintFlags = kGlobalPaintNormalPhase, PaintLayerFlags = 0); // Paint() assumes that the caller will clip to the bounds of the painting @@ -49,7 +50,7 @@ class CORE_EXPORT PaintLayerPainter { PaintLayerFlags); void PaintOverlayScrollbars(GraphicsContext&, - const LayoutRect& damage_rect, + const CullRect&, const GlobalPaintFlags); // Returns true if the painted output of this PaintLayer and its children is diff --git a/chromium/third_party/blink/renderer/core/paint/paint_layer_painter_test.cc b/chromium/third_party/blink/renderer/core/paint/paint_layer_painter_test.cc index 7c9d48f0e23..ccafe4bd544 100644 --- a/chromium/third_party/blink/renderer/core/paint/paint_layer_painter_test.cc +++ b/chromium/third_party/blink/renderer/core/paint/paint_layer_painter_test.cc @@ -12,6 +12,7 @@ #include "third_party/blink/renderer/platform/testing/runtime_enabled_features_test_helpers.h" using testing::ElementsAre; +using testing::UnorderedElementsAre; namespace blink { @@ -28,7 +29,7 @@ class PaintLayerPainterTest : public PaintControllerPaintTest { PaintLayer* target_layer = ToLayoutBox(GetLayoutObjectByElementId(element_name))->Layer(); - PaintLayerPaintingInfo painting_info(nullptr, LayoutRect(), + PaintLayerPaintingInfo painting_info(nullptr, CullRect(), kGlobalPaintNormalPhase, LayoutSize()); bool invisible = PaintLayerPainter(*target_layer) @@ -80,36 +81,30 @@ TEST_P(PaintLayerPainterTest, CachedSubsequence) { auto& content2 = *GetLayoutObjectByElementId("content2"); auto& filler2 = *GetLayoutObjectByElementId("filler2"); - const auto& view_display_item_client = ViewBackgroundClient(); - const auto& view_chunk_client = - RuntimeEnabledFeatures::SlimmingPaintV2Enabled() - ? *GetLayoutView().Layer() - : view_display_item_client; - - EXPECT_DISPLAY_LIST( - RootPaintController().GetDisplayItemList(), 7, - TestDisplayItem(view_display_item_client, kDocumentBackgroundType), - TestDisplayItem(container1, kBackgroundType), - TestDisplayItem(content1, kBackgroundType), - TestDisplayItem(filler1, kBackgroundType), - TestDisplayItem(container2, kBackgroundType), - TestDisplayItem(content2, kBackgroundType), - TestDisplayItem(filler2, kBackgroundType)); + const auto& view_client = ViewScrollingBackgroundClient(); + EXPECT_THAT( + RootPaintController().GetDisplayItemList(), + ElementsAre(IsSameId(&view_client, kDocumentBackgroundType), + IsSameId(GetDisplayItemClientFromLayoutObject(&container1), + kBackgroundType), + IsSameId(GetDisplayItemClientFromLayoutObject(&content1), + kBackgroundType), + IsSameId(GetDisplayItemClientFromLayoutObject(&filler1), + kBackgroundType), + IsSameId(GetDisplayItemClientFromLayoutObject(&container2), + kBackgroundType), + IsSameId(GetDisplayItemClientFromLayoutObject(&content2), + kBackgroundType), + IsSameId(GetDisplayItemClientFromLayoutObject(&filler2), + kBackgroundType))); auto* container1_layer = ToLayoutBoxModelObject(container1).Layer(); auto* filler1_layer = ToLayoutBoxModelObject(filler1).Layer(); auto* container2_layer = ToLayoutBoxModelObject(container2).Layer(); auto* filler2_layer = ToLayoutBoxModelObject(filler2).Layer(); - auto other_chunk_state = GetLayoutView().FirstFragment().ContentsProperties(); - auto view_chunk_state = other_chunk_state; - if (RuntimeEnabledFeatures::SlimmingPaintV2Enabled()) { - view_chunk_state = - GetLayoutView().FirstFragment().LocalBorderBoxProperties(); - } + auto chunk_state = GetLayoutView().FirstFragment().ContentsProperties(); - auto view_chunk_type = RuntimeEnabledFeatures::SlimmingPaintV2Enabled() - ? DisplayItem::kLayerChunkBackground - : kDocumentBackgroundType; + auto view_chunk_type = kDocumentBackgroundType; auto chunk_background_type = DisplayItem::kLayerChunkBackground; auto chunk_foreground_type = DisplayItem::kLayerChunkNormalFlowAndPositiveZOrderChildren; @@ -119,43 +114,35 @@ TEST_P(PaintLayerPainterTest, CachedSubsequence) { auto check_chunks = [&]() { // Check that new paint chunks were forced for |container1| and // |container2|. - const auto& paint_chunks = - RootPaintController().GetPaintArtifact().PaintChunks(); - EXPECT_EQ(paint_chunks.size(), 7u); - EXPECT_EQ( - paint_chunks[0], - PaintChunk(0, 1, PaintChunk::Id(view_chunk_client, view_chunk_type), - view_chunk_state)); - EXPECT_EQ(paint_chunks[1], PaintChunk(1, 2, - PaintChunk::Id(*container1_layer, - chunk_background_type), - other_chunk_state)); - EXPECT_EQ(paint_chunks[2], PaintChunk(2, 3, - PaintChunk::Id(*container1_layer, - chunk_foreground_type), - other_chunk_state)); - EXPECT_EQ( - paint_chunks[3], - PaintChunk(3, 4, PaintChunk::Id(*filler1_layer, filler_chunk_type), - other_chunk_state)); - EXPECT_EQ(paint_chunks[4], PaintChunk(4, 5, - PaintChunk::Id(*container2_layer, - chunk_background_type), - other_chunk_state)); - EXPECT_EQ(paint_chunks[5], PaintChunk(5, 6, - PaintChunk::Id(*container2_layer, - chunk_foreground_type), - other_chunk_state)); - EXPECT_EQ( - paint_chunks[6], - PaintChunk(6, 7, PaintChunk::Id(*filler2_layer, filler_chunk_type), - other_chunk_state)); + EXPECT_THAT( + RootPaintController().PaintChunks(), + ElementsAre( + IsPaintChunk(0, 1, PaintChunk::Id(view_client, view_chunk_type), + chunk_state), + IsPaintChunk( + 1, 2, PaintChunk::Id(*container1_layer, chunk_background_type), + chunk_state), + IsPaintChunk( + 2, 3, PaintChunk::Id(*container1_layer, chunk_foreground_type), + chunk_state), + IsPaintChunk(3, 4, + PaintChunk::Id(*filler1_layer, filler_chunk_type), + chunk_state), + IsPaintChunk( + 4, 5, PaintChunk::Id(*container2_layer, chunk_background_type), + chunk_state), + IsPaintChunk( + 5, 6, PaintChunk::Id(*container2_layer, chunk_foreground_type), + chunk_state), + IsPaintChunk(6, 7, + PaintChunk::Id(*filler2_layer, filler_chunk_type), + chunk_state))); }; check_chunks(); ToHTMLElement(content1.GetNode()) - ->setAttribute(HTMLNames::styleAttr, + ->setAttribute(html_names::kStyleAttr, "position: absolute; width: 100px; height: 100px; " "background-color: green"); GetDocument().View()->UpdateAllLifecyclePhasesExceptPaint(); @@ -164,26 +151,27 @@ TEST_P(PaintLayerPainterTest, CachedSubsequence) { CommitAndFinishCycle(); - EXPECT_DISPLAY_LIST( - RootPaintController().GetDisplayItemList(), 7, - TestDisplayItem(view_display_item_client, kDocumentBackgroundType), - TestDisplayItem(container1, kBackgroundType), - TestDisplayItem(content1, kBackgroundType), - TestDisplayItem(filler1, kBackgroundType), - TestDisplayItem(container2, kBackgroundType), - TestDisplayItem(content2, kBackgroundType), - TestDisplayItem(filler2, kBackgroundType)); + EXPECT_THAT( + RootPaintController().GetDisplayItemList(), + ElementsAre(IsSameId(&view_client, kDocumentBackgroundType), + IsSameId(GetDisplayItemClientFromLayoutObject(&container1), + kBackgroundType), + IsSameId(GetDisplayItemClientFromLayoutObject(&content1), + kBackgroundType), + IsSameId(GetDisplayItemClientFromLayoutObject(&filler1), + kBackgroundType), + IsSameId(GetDisplayItemClientFromLayoutObject(&container2), + kBackgroundType), + IsSameId(GetDisplayItemClientFromLayoutObject(&content2), + kBackgroundType), + IsSameId(GetDisplayItemClientFromLayoutObject(&filler2), + kBackgroundType))); // We should still have the paint chunks forced by the cached subsequences. check_chunks(); } -TEST_P(PaintLayerPainterTest, CachedSubsequenceOnInterestRectChange) { - // TODO(wangxianzhu): SPv2 deals with interest rect differently, so disable - // this test for SPv2 temporarily. - if (RuntimeEnabledFeatures::SlimmingPaintV2Enabled()) - return; - +TEST_P(PaintLayerPainterTest, CachedSubsequenceOnCullRectChange) { SetBodyInnerHTML(R"HTML( <div id='container1' style='position: relative; z-index: 1; width: 200px; height: 200px; background-color: blue'> @@ -206,44 +194,42 @@ TEST_P(PaintLayerPainterTest, CachedSubsequenceOnInterestRectChange) { )HTML"); InvalidateAll(RootPaintController()); - LayoutObject& container1 = - *GetDocument().getElementById("container1")->GetLayoutObject(); - LayoutObject& content1 = - *GetDocument().getElementById("content1")->GetLayoutObject(); - LayoutObject& container2 = - *GetDocument().getElementById("container2")->GetLayoutObject(); - LayoutObject& content2a = - *GetDocument().getElementById("content2a")->GetLayoutObject(); - LayoutObject& content2b = - *GetDocument().getElementById("content2b")->GetLayoutObject(); - LayoutObject& container3 = - *GetDocument().getElementById("container3")->GetLayoutObject(); - LayoutObject& content3 = - *GetDocument().getElementById("content3")->GetLayoutObject(); + DisplayItemClient& container1 = + *GetDisplayItemClientFromElementId("container1"); + DisplayItemClient& content1 = *GetDisplayItemClientFromElementId("content1"); + DisplayItemClient& container2 = + *GetDisplayItemClientFromElementId("container2"); + DisplayItemClient& content2a = + *GetDisplayItemClientFromElementId("content2a"); + DisplayItemClient& content2b = + *GetDisplayItemClientFromElementId("content2b"); + DisplayItemClient& container3 = + *GetDisplayItemClientFromElementId("container3"); + DisplayItemClient& content3 = *GetDisplayItemClientFromElementId("content3"); GetDocument().View()->UpdateAllLifecyclePhasesExceptPaint(); - IntRect interest_rect(0, 0, 400, 300); - Paint(&interest_rect); + IntRect cull_rect(0, 0, 400, 300); + Paint(&cull_rect); - const auto& background_display_item_client = ViewBackgroundClient(); + const auto& background_display_item_client = ViewScrollingBackgroundClient(); // Container1 is fully in the interest rect; // Container2 is partly (including its stacking chidren) in the interest rect; // Content2b is out of the interest rect and output nothing; // Container3 is partly in the interest rect. - EXPECT_DISPLAY_LIST( - RootPaintController().GetDisplayItemList(), 7, - TestDisplayItem(background_display_item_client, kDocumentBackgroundType), - TestDisplayItem(container1, kBackgroundType), - TestDisplayItem(content1, kBackgroundType), - TestDisplayItem(container2, kBackgroundType), - TestDisplayItem(content2a, kBackgroundType), - TestDisplayItem(container3, kBackgroundType), - TestDisplayItem(content3, kBackgroundType)); + EXPECT_THAT(RootPaintController().GetDisplayItemList(), + ElementsAre(IsSameId(&background_display_item_client, + kDocumentBackgroundType), + IsSameId(&container1, kBackgroundType), + IsSameId(&content1, kBackgroundType), + IsSameId(&container2, kBackgroundType), + IsSameId(&content2a, kBackgroundType), + IsSameId(&container3, kBackgroundType), + IsSameId(&content3, kBackgroundType))); GetDocument().View()->UpdateAllLifecyclePhasesExceptPaint(); - IntRect new_interest_rect(0, 100, 300, 1000); - EXPECT_TRUE(PaintWithoutCommit(&new_interest_rect)); + IntRect new_cull_rect(0, 100, 300, 1000); + EXPECT_TRUE(PaintWithoutCommit(&new_cull_rect)); // Container1 becomes partly in the interest rect, but uses cached subsequence // because it was fully painted before; @@ -255,18 +241,18 @@ TEST_P(PaintLayerPainterTest, CachedSubsequenceOnInterestRectChange) { CommitAndFinishCycle(); - EXPECT_DISPLAY_LIST( - RootPaintController().GetDisplayItemList(), 6, - TestDisplayItem(background_display_item_client, kDocumentBackgroundType), - TestDisplayItem(container1, kBackgroundType), - TestDisplayItem(content1, kBackgroundType), - TestDisplayItem(container2, kBackgroundType), - TestDisplayItem(content2a, kBackgroundType), - TestDisplayItem(content2b, kBackgroundType)); + EXPECT_THAT(RootPaintController().GetDisplayItemList(), + ElementsAre(IsSameId(&background_display_item_client, + kDocumentBackgroundType), + IsSameId(&container1, kBackgroundType), + IsSameId(&content1, kBackgroundType), + IsSameId(&container2, kBackgroundType), + IsSameId(&content2a, kBackgroundType), + IsSameId(&content2b, kBackgroundType))); } TEST_P(PaintLayerPainterTest, - CachedSubsequenceOnInterestRectChangeUnderInvalidationChecking) { + CachedSubsequenceOnCullRectChangeUnderInvalidationChecking) { ScopedPaintUnderInvalidationCheckingForTest under_invalidation_checking(true); SetBodyInnerHTML(R"HTML( @@ -279,18 +265,18 @@ TEST_P(PaintLayerPainterTest, // |target| will be fully painted. GetDocument().View()->UpdateAllLifecyclePhasesExceptPaint(); - IntRect interest_rect(0, 0, 400, 300); - Paint(&interest_rect); + IntRect cull_rect(0, 0, 400, 300); + Paint(&cull_rect); // |target| will be partially painted. Should not trigger under-invalidation // checking DCHECKs. GetDocument().View()->UpdateAllLifecyclePhasesExceptPaint(); - IntRect new_interest_rect(0, 100, 300, 1000); - Paint(&new_interest_rect); + IntRect new_cull_rect(0, 100, 300, 1000); + Paint(&new_cull_rect); } TEST_P(PaintLayerPainterTest, - CachedSubsequenceOnStyleChangeWithInterestRectClipping) { + CachedSubsequenceOnStyleChangeWithCullRectClipping) { SetBodyInnerHTML(R"HTML( <div id='container1' style='position: relative; z-index: 1; width: 200px; height: 200px; background-color: blue'> @@ -304,45 +290,163 @@ TEST_P(PaintLayerPainterTest, </div> )HTML"); GetDocument().View()->UpdateAllLifecyclePhasesExceptPaint(); - // PaintResult of all subsequences will be MayBeClippedByPaintDirtyRect. - IntRect interest_rect(0, 0, 50, 300); - Paint(&interest_rect); - - LayoutObject& container1 = - *GetDocument().getElementById("container1")->GetLayoutObject(); - LayoutObject& content1 = - *GetDocument().getElementById("content1")->GetLayoutObject(); - LayoutObject& container2 = - *GetDocument().getElementById("container2")->GetLayoutObject(); - LayoutObject& content2 = - *GetDocument().getElementById("content2")->GetLayoutObject(); - - const auto& background_display_item_client = ViewBackgroundClient(); - EXPECT_DISPLAY_LIST( - RootPaintController().GetDisplayItemList(), 5, - TestDisplayItem(background_display_item_client, kDocumentBackgroundType), - TestDisplayItem(container1, kBackgroundType), - TestDisplayItem(content1, kBackgroundType), - TestDisplayItem(container2, kBackgroundType), - TestDisplayItem(content2, kBackgroundType)); - - ToHTMLElement(content1.GetNode()) - ->setAttribute(HTMLNames::styleAttr, + // PaintResult of all subsequences will be MayBeClippedByCullRect. + IntRect cull_rect(0, 0, 50, 300); + Paint(&cull_rect); + + DisplayItemClient& container1 = + *GetDisplayItemClientFromElementId("container1"); + DisplayItemClient& content1 = *GetDisplayItemClientFromElementId("content1"); + DisplayItemClient& container2 = + *GetDisplayItemClientFromElementId("container2"); + DisplayItemClient& content2 = *GetDisplayItemClientFromElementId("content2"); + + const auto& background_display_item_client = ViewScrollingBackgroundClient(); + EXPECT_THAT(RootPaintController().GetDisplayItemList(), + ElementsAre(IsSameId(&background_display_item_client, + kDocumentBackgroundType), + IsSameId(&container1, kBackgroundType), + IsSameId(&content1, kBackgroundType), + IsSameId(&container2, kBackgroundType), + IsSameId(&content2, kBackgroundType))); + + ToHTMLElement(GetElementById("content1")) + ->setAttribute(html_names::kStyleAttr, "position: absolute; width: 100px; height: 100px; " "background-color: green"); GetDocument().View()->UpdateAllLifecyclePhasesExceptPaint(); - EXPECT_TRUE(PaintWithoutCommit(&interest_rect)); + EXPECT_TRUE(PaintWithoutCommit(&cull_rect)); EXPECT_EQ(4, NumCachedNewItems()); CommitAndFinishCycle(); - EXPECT_DISPLAY_LIST( - RootPaintController().GetDisplayItemList(), 5, - TestDisplayItem(background_display_item_client, kDocumentBackgroundType), - TestDisplayItem(container1, kBackgroundType), - TestDisplayItem(content1, kBackgroundType), - TestDisplayItem(container2, kBackgroundType), - TestDisplayItem(content2, kBackgroundType)); + EXPECT_THAT(RootPaintController().GetDisplayItemList(), + ElementsAre(IsSameId(&background_display_item_client, + kDocumentBackgroundType), + IsSameId(&container1, kBackgroundType), + IsSameId(&content1, kBackgroundType), + IsSameId(&container2, kBackgroundType), + IsSameId(&content2, kBackgroundType))); +} + +TEST_P(PaintLayerPainterTest, CachedSubsequenceRetainsPreviousPaintResult) { + SetBodyInnerHTML(R"HTML( + <style> + html, body { height: 100%; margin: 0 } + ::-webkit-scrollbar { display:none } + </style> + <div id="target" style="height: 8000px; contain: paint"> + <div id="content1" style="height: 100px; background: blue"></div> + <div style="height: 6000px"></div> + <div id="content2" style="height: 100px; background: blue"></div> + </div> + <div id="change" style="display: none"></div> + )HTML"); + + const auto* target_layer = + ToLayoutBoxModelObject(GetLayoutObjectByElementId("target"))->Layer(); + const auto* content1 = GetLayoutObjectByElementId("content1"); + const auto* content2 = GetLayoutObjectByElementId("content2"); + const auto& view_client = ViewScrollingBackgroundClient(); + // |target| is partially painted. + EXPECT_EQ(kMayBeClippedByCullRect, target_layer->PreviousPaintResult()); + if (RuntimeEnabledFeatures::SlimmingPaintV2Enabled()) { + // SPv2 doesn't clip the cull rect by the scrolling contents rect, which + // doesn't affect painted results. + EXPECT_EQ(CullRect(IntRect(-4000, -4000, 8800, 8600)), + target_layer->PreviousCullRect()); + // |content2| is out of the cull rect. + EXPECT_THAT( + RootPaintController().GetDisplayItemList(), + ElementsAre(IsSameId(&GetLayoutView(), DisplayItem::kScrollHitTest), + IsSameId(&view_client, kDocumentBackgroundType), + IsSameId(content1, kBackgroundType))); + // |target| created subsequence. + EXPECT_SUBSEQUENCE(*target_layer, 2, 3); + } else { + EXPECT_EQ(CullRect(IntRect(0, 0, 800, 4600)), + target_layer->PreviousCullRect()); + // |content2| is out of the cull rect. + EXPECT_THAT(RootPaintController().GetDisplayItemList(), + ElementsAre(IsSameId(&view_client, kDocumentBackgroundType), + IsSameId(content1, kBackgroundType))); + // |target| created subsequence. + EXPECT_SUBSEQUENCE(*target_layer, 1, 2); + } + + // Change something that triggers a repaint but |target| should use cached + // subsequence. + GetDocument().getElementById("change")->setAttribute(html_names::kStyleAttr, + "display: block"); + GetDocument().View()->UpdateAllLifecyclePhasesExceptPaint(); + EXPECT_FALSE(target_layer->NeedsRepaint()); + EXPECT_TRUE(PaintWithoutCommit()); + EXPECT_EQ(2, NumCachedNewItems()); + CommitAndFinishCycle(); + + // |target| is still partially painted. + EXPECT_EQ(kMayBeClippedByCullRect, target_layer->PreviousPaintResult()); + if (RuntimeEnabledFeatures::SlimmingPaintV2Enabled()) { + // SPv2 doens't clip the cull rect by the scrolling contents rect, which + // doesn't affect painted results. + EXPECT_EQ(CullRect(IntRect(-4000, -4000, 8800, 8600)), + target_layer->PreviousCullRect()); + EXPECT_THAT( + RootPaintController().GetDisplayItemList(), + ElementsAre(IsSameId(&GetLayoutView(), DisplayItem::kScrollHitTest), + IsSameId(&view_client, kDocumentBackgroundType), + IsSameId(content1, kBackgroundType))); + // |target| still created subsequence (cached). + EXPECT_SUBSEQUENCE(*target_layer, 2, 3); + } else { + EXPECT_EQ(CullRect(IntRect(0, 0, 800, 4600)), + target_layer->PreviousCullRect()); + EXPECT_THAT(RootPaintController().GetDisplayItemList(), + ElementsAre(IsSameId(&view_client, kDocumentBackgroundType), + IsSameId(content1, kBackgroundType))); + // |target| still created subsequence (cached). + EXPECT_SUBSEQUENCE(*target_layer, 1, 2); + } + + // Scroll the view so that both |content1| and |content2| are in the interest + // rect. + GetLayoutView().GetScrollableArea()->SetScrollOffset(ScrollOffset(0, 3000), + kProgrammaticScroll); + GetDocument().View()->UpdateAllLifecyclePhasesExceptPaint(); + // Scrolling doesn't set NeedsRepaint flag. Change of paint dirty rect of + // a partially painted layer will trigger repaint. + EXPECT_FALSE(target_layer->NeedsRepaint()); + EXPECT_TRUE(PaintWithoutCommit()); + EXPECT_EQ(2, NumCachedNewItems()); + CommitAndFinishCycle(); + + // |target| is still partially painted. + EXPECT_EQ(kMayBeClippedByCullRect, target_layer->PreviousPaintResult()); + if (RuntimeEnabledFeatures::SlimmingPaintV2Enabled()) { + // SPv2 doens't clip the cull rect by the scrolling contents rect, which + // doesn't affect painted results. + EXPECT_EQ(CullRect(IntRect(-4000, -1000, 8800, 8600)), + target_layer->PreviousCullRect()); + // Painted result should include both |content1| and |content2|. + EXPECT_THAT( + RootPaintController().GetDisplayItemList(), + ElementsAre(IsSameId(&GetLayoutView(), DisplayItem::kScrollHitTest), + IsSameId(&view_client, kDocumentBackgroundType), + IsSameId(content1, kBackgroundType), + IsSameId(content2, kBackgroundType))); + // |target| still created subsequence (repainted). + EXPECT_SUBSEQUENCE(*target_layer, 2, 4); + } else { + EXPECT_EQ(CullRect(IntRect(0, 0, 800, 7600)), + target_layer->PreviousCullRect()); + // Painted result should include both |content1| and |content2|. + EXPECT_THAT(RootPaintController().GetDisplayItemList(), + ElementsAre(IsSameId(&view_client, kDocumentBackgroundType), + IsSameId(content1, kBackgroundType), + IsSameId(content2, kBackgroundType))); + // |target| still created subsequence (repainted). + EXPECT_SUBSEQUENCE(*target_layer, 1, 3); + } } TEST_P(PaintLayerPainterTest, PaintPhaseOutline) { @@ -362,8 +466,8 @@ TEST_P(PaintLayerPainterTest, PaintPhaseOutline) { LayoutObject& outline_div = *GetDocument().getElementById("outline")->GetLayoutObject(); ToHTMLElement(outline_div.GetNode()) - ->setAttribute(HTMLNames::styleAttr, style_without_outline); - GetDocument().View()->UpdateAllLifecyclePhases(); + ->setAttribute(html_names::kStyleAttr, style_without_outline); + UpdateAllLifecyclePhasesForTest(); LayoutBoxModelObject& self_painting_layer_object = *ToLayoutBoxModelObject( GetDocument().getElementById("self-painting-layer")->GetLayoutObject()); @@ -383,9 +487,9 @@ TEST_P(PaintLayerPainterTest, PaintPhaseOutline) { // Outline on the self-painting-layer node itself doesn't affect // PaintPhaseDescendantOutlines. ToHTMLElement(self_painting_layer_object.GetNode()) - ->setAttribute(HTMLNames::styleAttr, + ->setAttribute(html_names::kStyleAttr, "position: absolute; outline: 1px solid green"); - GetDocument().View()->UpdateAllLifecyclePhases(); + UpdateAllLifecyclePhasesForTest(); EXPECT_FALSE(self_painting_layer.NeedsPaintPhaseDescendantOutlines()); EXPECT_FALSE(non_self_painting_layer.NeedsPaintPhaseDescendantOutlines()); EXPECT_TRUE(DisplayItemListContains( @@ -395,7 +499,7 @@ TEST_P(PaintLayerPainterTest, PaintPhaseOutline) { // needsPaintPhaseDescendantOutlines should be set when any descendant on the // same layer has outline. ToHTMLElement(outline_div.GetNode()) - ->setAttribute(HTMLNames::styleAttr, style_with_outline); + ->setAttribute(html_names::kStyleAttr, style_with_outline); GetDocument().View()->UpdateAllLifecyclePhasesExceptPaint(); EXPECT_TRUE(self_painting_layer.NeedsPaintPhaseDescendantOutlines()); EXPECT_FALSE(non_self_painting_layer.NeedsPaintPhaseDescendantOutlines()); @@ -407,8 +511,8 @@ TEST_P(PaintLayerPainterTest, PaintPhaseOutline) { // needsPaintPhaseDescendantOutlines should be reset when no outline is // actually painted. ToHTMLElement(outline_div.GetNode()) - ->setAttribute(HTMLNames::styleAttr, style_without_outline); - GetDocument().View()->UpdateAllLifecyclePhases(); + ->setAttribute(html_names::kStyleAttr, style_without_outline); + UpdateAllLifecyclePhasesForTest(); EXPECT_TRUE(self_painting_layer.NeedsPaintPhaseDescendantOutlines()); } @@ -429,8 +533,8 @@ TEST_P(PaintLayerPainterTest, PaintPhaseFloat) { LayoutObject& float_div = *GetDocument().getElementById("float")->GetLayoutObject(); ToHTMLElement(float_div.GetNode()) - ->setAttribute(HTMLNames::styleAttr, style_without_float); - GetDocument().View()->UpdateAllLifecyclePhases(); + ->setAttribute(html_names::kStyleAttr, style_without_float); + UpdateAllLifecyclePhasesForTest(); LayoutBoxModelObject& self_painting_layer_object = *ToLayoutBoxModelObject( GetDocument().getElementById("self-painting-layer")->GetLayoutObject()); @@ -450,7 +554,7 @@ TEST_P(PaintLayerPainterTest, PaintPhaseFloat) { // needsPaintPhaseFloat should be set when any descendant on the same layer // has float. ToHTMLElement(float_div.GetNode()) - ->setAttribute(HTMLNames::styleAttr, style_with_float); + ->setAttribute(html_names::kStyleAttr, style_with_float); GetDocument().View()->UpdateAllLifecyclePhasesExceptPaint(); EXPECT_TRUE(self_painting_layer.NeedsPaintPhaseFloat()); EXPECT_FALSE(non_self_painting_layer.NeedsPaintPhaseFloat()); @@ -462,8 +566,8 @@ TEST_P(PaintLayerPainterTest, PaintPhaseFloat) { // needsPaintPhaseFloat should be reset when there is no float actually // painted. ToHTMLElement(float_div.GetNode()) - ->setAttribute(HTMLNames::styleAttr, style_without_float); - GetDocument().View()->UpdateAllLifecyclePhases(); + ->setAttribute(html_names::kStyleAttr, style_without_float); + UpdateAllLifecyclePhasesForTest(); EXPECT_TRUE(self_painting_layer.NeedsPaintPhaseFloat()); } @@ -478,7 +582,7 @@ TEST_P(PaintLayerPainterTest, PaintPhaseFloatUnderInlineLayer) { </div> </div> )HTML"); - GetDocument().View()->UpdateAllLifecyclePhases(); + UpdateAllLifecyclePhasesForTest(); LayoutObject& float_div = *GetDocument().getElementById("float")->GetLayoutObject(); @@ -522,8 +626,8 @@ TEST_P(PaintLayerPainterTest, PaintPhaseBlockBackground) { LayoutObject& background_div = *GetDocument().getElementById("background")->GetLayoutObject(); ToHTMLElement(background_div.GetNode()) - ->setAttribute(HTMLNames::styleAttr, style_without_background); - GetDocument().View()->UpdateAllLifecyclePhases(); + ->setAttribute(html_names::kStyleAttr, style_without_background); + UpdateAllLifecyclePhasesForTest(); LayoutBoxModelObject& self_painting_layer_object = *ToLayoutBoxModelObject( GetDocument().getElementById("self-painting-layer")->GetLayoutObject()); @@ -544,9 +648,9 @@ TEST_P(PaintLayerPainterTest, PaintPhaseBlockBackground) { // Background on the self-painting-layer node itself doesn't affect // PaintPhaseDescendantBlockBackgrounds. ToHTMLElement(self_painting_layer_object.GetNode()) - ->setAttribute(HTMLNames::styleAttr, + ->setAttribute(html_names::kStyleAttr, "position: absolute; background: green"); - GetDocument().View()->UpdateAllLifecyclePhases(); + UpdateAllLifecyclePhasesForTest(); EXPECT_FALSE(self_painting_layer.NeedsPaintPhaseDescendantBlockBackgrounds()); EXPECT_FALSE( non_self_painting_layer.NeedsPaintPhaseDescendantBlockBackgrounds()); @@ -557,7 +661,7 @@ TEST_P(PaintLayerPainterTest, PaintPhaseBlockBackground) { // needsPaintPhaseDescendantBlockBackgrounds should be set when any descendant // on the same layer has Background. ToHTMLElement(background_div.GetNode()) - ->setAttribute(HTMLNames::styleAttr, style_with_background); + ->setAttribute(html_names::kStyleAttr, style_with_background); GetDocument().View()->UpdateAllLifecyclePhasesExceptPaint(); EXPECT_TRUE(self_painting_layer.NeedsPaintPhaseDescendantBlockBackgrounds()); EXPECT_FALSE( @@ -570,8 +674,8 @@ TEST_P(PaintLayerPainterTest, PaintPhaseBlockBackground) { // needsPaintPhaseDescendantBlockBackgrounds should be reset when no outline // is actually painted. ToHTMLElement(background_div.GetNode()) - ->setAttribute(HTMLNames::styleAttr, style_without_background); - GetDocument().View()->UpdateAllLifecyclePhases(); + ->setAttribute(html_names::kStyleAttr, style_without_background); + UpdateAllLifecyclePhasesForTest(); EXPECT_TRUE(self_painting_layer.NeedsPaintPhaseDescendantBlockBackgrounds()); } @@ -599,8 +703,8 @@ TEST_P(PaintLayerPainterTest, PaintPhasesUpdateOnLayerAddition) { EXPECT_TRUE(html_layer.NeedsPaintPhaseDescendantBlockBackgrounds()); ToHTMLElement(layer_div.GetNode()) - ->setAttribute(HTMLNames::styleAttr, "position: relative"); - GetDocument().View()->UpdateAllLifecyclePhases(); + ->setAttribute(html_names::kStyleAttr, "position: relative"); + UpdateAllLifecyclePhasesForTest(); ASSERT_TRUE(layer_div.HasLayer()); PaintLayer& layer = *layer_div.Layer(); ASSERT_TRUE(layer.IsSelfPaintingLayer()); @@ -635,9 +739,9 @@ TEST_P(PaintLayerPainterTest, PaintPhasesUpdateOnBecomingSelfPainting) { ToHTMLElement(layer_div.GetNode()) ->setAttribute( - HTMLNames::styleAttr, + html_names::kStyleAttr, "width: 100px; height: 100px; overflow: hidden; position: relative"); - GetDocument().View()->UpdateAllLifecyclePhases(); + UpdateAllLifecyclePhasesForTest(); PaintLayer& layer = *layer_div.Layer(); ASSERT_TRUE(layer.IsSelfPaintingLayer()); EXPECT_TRUE(layer.NeedsPaintPhaseDescendantOutlines()); @@ -674,9 +778,9 @@ TEST_P(PaintLayerPainterTest, PaintPhasesUpdateOnBecomingNonSelfPainting) { EXPECT_FALSE(html_layer.NeedsPaintPhaseDescendantBlockBackgrounds()); ToHTMLElement(layer_div.GetNode()) - ->setAttribute(HTMLNames::styleAttr, + ->setAttribute(html_names::kStyleAttr, "width: 100px; height: 100px; overflow: hidden"); - GetDocument().View()->UpdateAllLifecyclePhases(); + UpdateAllLifecyclePhasesForTest(); EXPECT_FALSE(layer.IsSelfPaintingLayer()); EXPECT_TRUE(html_layer.NeedsPaintPhaseDescendantOutlines()); EXPECT_TRUE(html_layer.NeedsPaintPhaseDescendantBlockBackgrounds()); @@ -721,9 +825,9 @@ TEST_P(PaintLayerPainterTest, EXPECT_FALSE(layer.NeedsPaintPhaseDescendantBlockBackgrounds()); ToHTMLElement(table.GetNode()) - ->setAttribute(HTMLNames::styleAttr, + ->setAttribute(html_names::kStyleAttr, "position: relative; border-collapse: collapse"); - GetDocument().View()->UpdateAllLifecyclePhases(); + UpdateAllLifecyclePhasesForTest(); EXPECT_TRUE(layer.NeedsPaintPhaseDescendantBlockBackgrounds()); } @@ -870,4 +974,303 @@ TEST_P(PaintLayerPainterTest, ExpectPaintedOutputInvisible("target", false); } +using PaintLayerPainterTestSPv2 = PaintLayerPainterTest; + +INSTANTIATE_SPV2_TEST_CASE_P(PaintLayerPainterTestSPv2); + +TEST_P(PaintLayerPainterTestSPv2, SimpleCullRect) { + SetBodyInnerHTML(R"HTML( + <div id='target' + style='width: 200px; height: 200px; position: relative'> + </div> + )HTML"); + + EXPECT_EQ(IntRect(0, 0, 800, 600), + GetPaintLayerByElementId("target")->PreviousCullRect().Rect()); +} + +TEST_P(PaintLayerPainterTestSPv2, TallLayerCullRect) { + SetBodyInnerHTML(R"HTML( + <div id='target' + style='width: 200px; height: 10000px; position: relative'> + </div> + )HTML"); + + // Viewport rect (0, 0, 800, 600) expanded by 4000 for scrolling. + EXPECT_EQ(IntRect(-4000, -4000, 8800, 8600), + GetPaintLayerByElementId("target")->PreviousCullRect().Rect()); +} + +TEST_P(PaintLayerPainterTestSPv2, WideLayerCullRect) { + SetBodyInnerHTML(R"HTML( + <div id='target' + style='width: 10000px; height: 200px; position: relative'> + </div> + )HTML"); + + // Same as TallLayerCullRect. + EXPECT_EQ(IntRect(-4000, -4000, 8800, 8600), + GetPaintLayerByElementId("target")->PreviousCullRect().Rect()); +} + +TEST_P(PaintLayerPainterTestSPv2, TallScrolledLayerCullRect) { + SetBodyInnerHTML(R"HTML( + <div id='target' style='width: 200px; height: 10000px; position: relative'> + </div> + )HTML"); + + // Viewport rect (0, 0, 800, 600) expanded by 4000. + EXPECT_EQ(IntRect(-4000, -4000, 8800, 8600), + GetPaintLayerByElementId("target")->PreviousCullRect().Rect()); + + GetDocument().View()->LayoutViewport()->SetScrollOffset(ScrollOffset(0, 6000), + kProgrammaticScroll); + UpdateAllLifecyclePhasesForTest(); + EXPECT_EQ(IntRect(-4000, 2000, 8800, 8600), + GetPaintLayerByElementId("target")->PreviousCullRect().Rect()); + + GetDocument().View()->LayoutViewport()->SetScrollOffset(ScrollOffset(0, 6500), + kProgrammaticScroll); + UpdateAllLifecyclePhasesForTest(); + // Used the previous cull rect because the scroll amount is small. + EXPECT_EQ(IntRect(-4000, 2000, 8800, 8600), + GetPaintLayerByElementId("target")->PreviousCullRect().Rect()); + + GetDocument().View()->LayoutViewport()->SetScrollOffset(ScrollOffset(0, 6600), + kProgrammaticScroll); + UpdateAllLifecyclePhasesForTest(); + // Used new cull rect. + EXPECT_EQ(IntRect(-4000, 2600, 8800, 8600), + GetPaintLayerByElementId("target")->PreviousCullRect().Rect()); +} + +TEST_P(PaintLayerPainterTestSPv2, WholeDocumentCullRect) { + GetDocument().GetSettings()->SetMainFrameClipsContent(false); + SetBodyInnerHTML(R"HTML( + <style> + div { background: blue; } + ::-webkit-scrollbar { display: none; } + </style> + <div id='relative' + style='width: 200px; height: 10000px; position: relative'> + </div> + <div id='fixed' style='width: 200px; height: 200px; position: fixed'> + </div> + <div id='scroll' style='width: 200px; height: 200px; overflow: scroll'> + <div id='below-scroll' style='height: 5000px; position: relative'></div> + <div style='height: 200px'>Should not paint</div> + </div> + <div id='normal' style='width: 200px; height: 200px'></div> + )HTML"); + + // Viewport clipping is disabled. + EXPECT_TRUE(GetLayoutView().Layer()->PreviousCullRect().IsInfinite()); + EXPECT_TRUE( + GetPaintLayerByElementId("relative")->PreviousCullRect().IsInfinite()); + EXPECT_TRUE( + GetPaintLayerByElementId("fixed")->PreviousCullRect().IsInfinite()); + EXPECT_TRUE( + GetPaintLayerByElementId("scroll")->PreviousCullRect().IsInfinite()); + + // Cull rect is normal for contents below scroll other than the viewport. + EXPECT_EQ( + IntRect(-4000, -4000, 8200, 8200), + GetPaintLayerByElementId("below-scroll")->PreviousCullRect().Rect()); + + EXPECT_THAT( + RootPaintController().GetDisplayItemList(), + UnorderedElementsAre( + IsSameId(&GetLayoutView(), DisplayItem::kScrollHitTest), + IsSameId(&ViewScrollingBackgroundClient(), kDocumentBackgroundType), + IsSameId(GetDisplayItemClientFromElementId("relative"), + kBackgroundType), + IsSameId(GetDisplayItemClientFromElementId("normal"), + kBackgroundType), + IsSameId(GetLayoutObjectByElementId("scroll"), + DisplayItem::kScrollHitTest), + IsSameId(GetDisplayItemClientFromElementId("scroll"), + kBackgroundType), + IsSameId(&ToLayoutBox(GetLayoutObjectByElementId("scroll")) + ->GetScrollableArea() + ->GetScrollingBackgroundDisplayItemClient(), + kBackgroundType), + IsSameId(GetDisplayItemClientFromElementId("below-scroll"), + kBackgroundType), + IsSameId(GetDisplayItemClientFromElementId("fixed"), + kBackgroundType))); +} + +TEST_P(PaintLayerPainterTestSPv2, VerticalRightLeftWritingModeDocument) { + SetBodyInnerHTML(R"HTML( + <style> + html { writing-mode: vertical-rl; } + body { margin: 0; } + </style> + <div id='target' style='width: 10000px; height: 200px; position: relative'> + </div> + )HTML"); + + GetDocument().View()->LayoutViewport()->SetScrollOffset( + ScrollOffset(-5000, 0), kProgrammaticScroll); + UpdateAllLifecyclePhasesForTest(); + + // A scroll by -5000px is equivalent to a scroll by (10000 - 5000 - 800)px = + // 4200px in non-RTL mode. Expanding the resulting rect by 4000px in each + // direction yields this result. + EXPECT_EQ(IntRect(200, -4000, 8800, 8600), + GetPaintLayerByElementId("target")->PreviousCullRect().Rect()); +} + +TEST_P(PaintLayerPainterTestSPv2, ScaledCullRect) { + SetBodyInnerHTML(R"HTML( + <div style='width: 200px; height: 300px; overflow: scroll; + transform: scaleX(2) scaleY(0.5)'> + <div id='target' style='height: 400px; position: relative'></div> + </div> + )HTML"); + + // The scale doesn't affect the cull rect. + EXPECT_EQ(IntRect(-4000, -4000, 8200, 8300), + GetPaintLayerByElementId("target")->PreviousCullRect().Rect()); +} + +TEST_P(PaintLayerPainterTestSPv2, ScaledAndRotatedCullRect) { + SetBodyInnerHTML(R"HTML( + <div style='width: 200px; height: 300px; overflow: scroll; + transform: scaleX(2) scaleY(0.5) rotateZ(45deg)'> + <div id='target' style='height: 400px; position: relative'></div> + </div> + )HTML"); + + // The scale and the rotation don't affect the cull rect. + EXPECT_EQ(IntRect(-4000, -4000, 8200, 8300), + GetPaintLayerByElementId("target")->PreviousCullRect().Rect()); +} + +TEST_P(PaintLayerPainterTestSPv2, 3DRotated90DegreesCullRect) { + SetBodyInnerHTML(R"HTML( + <div style='width: 200px; height: 300px; overflow: scroll; + transform: rotateY(90deg)'> + <div id='target' style='height: 400px; position: relative'></div> + </div> + )HTML"); + + // It's rotated 90 degrees about the X axis, which means its visual content + // rect is empty, we fall back to the 4000px cull rect padding amount. + EXPECT_EQ(IntRect(-4000, -4000, 8200, 8300), + GetPaintLayerByElementId("target")->PreviousCullRect().Rect()); +} + +TEST_P(PaintLayerPainterTestSPv2, 3DRotatedNear90DegreesCullRect) { + SetBodyInnerHTML(R"HTML( + <div style='width: 200px; height: 300px; overflow: scroll; + transform: rotateY(89.9999deg)'> + <div id='target' style='height: 400px; position: relative'></div> + </div> + )HTML"); + + // Because the layer is rotated to almost 90 degrees, floating-point error + // leads to a reverse-projected rect that is much much larger than the + // original layer size in certain dimensions. In such cases, we often fall + // back to the 4000px cull rect padding amount. + EXPECT_EQ(IntRect(-4000, -4000, 8200, 8300), + GetPaintLayerByElementId("target")->PreviousCullRect().Rect()); +} + +TEST_P(PaintLayerPainterTestSPv2, PerspectiveCullRect) { + SetBodyInnerHTML(R"HTML( + <div id='target' + style='width: 100px; height: 100px; transform: perspective(1000px)'> + </div> + )HTML"); + + // Use infinite cull rect with perspective. + EXPECT_TRUE( + GetPaintLayerByElementId("target")->PreviousCullRect().IsInfinite()); +} + +TEST_P(PaintLayerPainterTestSPv2, 3D45DegRotatedTallCullRect) { + SetBodyInnerHTML(R"HTML( + <div id='target' + style='width: 200px; height: 10000px; transform: rotateY(45deg)'> + </div> + )HTML"); + + // Use infinite cull rect with 3d transform. + EXPECT_TRUE( + GetPaintLayerByElementId("target")->PreviousCullRect().IsInfinite()); +} + +TEST_P(PaintLayerPainterTestSPv2, FixedPositionCullRect) { + SetBodyInnerHTML(R"HTML( + <div id='target' style='width: 1000px; height: 2000px; + position: fixed; top: 100px; left: 200px;'> + </div> + )HTML"); + + EXPECT_EQ(IntRect(0, 0, 800, 600), + GetPaintLayerByElementId("target")->PreviousCullRect().Rect()); +} + +TEST_P(PaintLayerPainterTestSPv2, LayerOffscreenNearCullRect) { + SetBodyInnerHTML(R"HTML( + <div style='width: 200px; height: 300px; overflow: scroll; + position: absolute; top: 3000px; left: 0px;'> + <div id='target' style='height: 500px; position: relative'></div> + </div> + )HTML"); + + EXPECT_EQ(IntRect(-4000, -4000, 8200, 8300), + GetPaintLayerByElementId("target")->PreviousCullRect().Rect()); +} + +TEST_P(PaintLayerPainterTestSPv2, LayerOffscreenFarCullRect) { + SetBodyInnerHTML(R"HTML( + <div style='width: 200px; height: 300px; overflow: scroll; + position: absolute; top: 9000px'> + <div id='target' style='height: 500px; position: relative'></div> + </div> + )HTML"); + + // The layer is too far away from the viewport. + EXPECT_EQ(IntRect(), + GetPaintLayerByElementId("target")->PreviousCullRect().Rect()); +} + +TEST_P(PaintLayerPainterTestSPv2, ScrollingLayerCullRect) { + SetBodyInnerHTML(R"HTML( + <style> + div::-webkit-scrollbar { width: 5px; } + </style> + <div style='width: 200px; height: 200px; overflow: scroll'> + <div id='target' + style='width: 100px; height: 10000px; position: relative'> + </div> + </div> + )HTML"); + + // In screen space, the scroller is (8, 8, 195, 193) (because of overflow clip + // of 'target', scrollbar and root margin). + // Applying the viewport clip of the root has no effect because + // the clip is already small. Mapping it down into the graphics layer + // space yields (0, 0, 195, 193). This is then expanded by 4000px. + EXPECT_EQ(IntRect(-4000, -4000, 8195, 8193), + GetPaintLayerByElementId("target")->PreviousCullRect().Rect()); +} + +TEST_P(PaintLayerPainterTestSPv2, ClippedBigLayer) { + SetBodyInnerHTML(R"HTML( + <div style='width: 1px; height: 1px; overflow: hidden'> + <div id='target' + style='width: 10000px; height: 10000px; position: relative'> + </div> + </div> + )HTML"); + + // The viewport is not scrollable because of the clip, so the cull rect is + // just the viewport rect. + EXPECT_EQ(IntRect(0, 0, 800, 600), + GetPaintLayerByElementId("target")->PreviousCullRect().Rect()); +} + } // namespace blink diff --git a/chromium/third_party/blink/renderer/core/paint/paint_layer_painting_info.h b/chromium/third_party/blink/renderer/core/paint/paint_layer_painting_info.h index 3c90598c7fe..bee6fa8e2eb 100644 --- a/chromium/third_party/blink/renderer/core/paint/paint_layer_painting_info.h +++ b/chromium/third_party/blink/renderer/core/paint/paint_layer_painting_info.h @@ -47,7 +47,7 @@ #include "base/logging.h" #include "third_party/blink/renderer/core/paint/paint_phase.h" -#include "third_party/blink/renderer/platform/geometry/layout_rect.h" +#include "third_party/blink/renderer/platform/graphics/paint/cull_rect.h" #include "third_party/blink/renderer/platform/wtf/allocator.h" #if DCHECK_IS_ON() @@ -88,20 +88,20 @@ struct PaintLayerPaintingInfo { STACK_ALLOCATED(); public: - PaintLayerPaintingInfo(PaintLayer* in_root_layer, - const LayoutRect& in_dirty_rect, + PaintLayerPaintingInfo(PaintLayer* root_layer, + const CullRect& cull_rect, GlobalPaintFlags global_paint_flags, - const LayoutSize& in_sub_pixel_accumulation) - : root_layer(in_root_layer), - paint_dirty_rect(in_dirty_rect), - sub_pixel_accumulation(in_sub_pixel_accumulation), + const LayoutSize& sub_pixel_accumulation) + : root_layer(root_layer), + cull_rect(cull_rect), + sub_pixel_accumulation(sub_pixel_accumulation), global_paint_flags_(global_paint_flags) {} GlobalPaintFlags GetGlobalPaintFlags() const { return global_paint_flags_; } // TODO(jchaffraix): We should encapsulate all these fields. const PaintLayer* root_layer; - LayoutRect paint_dirty_rect; // relative to rootLayer; + CullRect cull_rect; // relative to rootLayer; LayoutSize sub_pixel_accumulation; private: diff --git a/chromium/third_party/blink/renderer/core/paint/paint_layer_scrollable_area.cc b/chromium/third_party/blink/renderer/core/paint/paint_layer_scrollable_area.cc index fe5b25522fa..5d9cb4586e2 100644 --- a/chromium/third_party/blink/renderer/core/paint/paint_layer_scrollable_area.cc +++ b/chromium/third_party/blink/renderer/core/paint/paint_layer_scrollable_area.cc @@ -74,6 +74,7 @@ #include "third_party/blink/renderer/core/layout/layout_scrollbar_part.h" #include "third_party/blink/renderer/core/layout/layout_theme.h" #include "third_party/blink/renderer/core/layout/layout_view.h" +#include "third_party/blink/renderer/core/layout/logical_values.h" #include "third_party/blink/renderer/core/layout/ng/legacy_layout_tree_walking.h" #include "third_party/blink/renderer/core/loader/document_loader.h" #include "third_party/blink/renderer/core/page/chrome_client.h" @@ -143,7 +144,8 @@ PaintLayerScrollableArea::PaintLayerScrollableArea(PaintLayer& layer) scroll_anchor_(this), non_composited_main_thread_scrolling_reasons_(0), horizontal_scrollbar_previously_was_overlay_(false), - vertical_scrollbar_previously_was_overlay_(false) { + vertical_scrollbar_previously_was_overlay_(false), + scrolling_background_display_item_client_(*this) { Node* node = GetLayoutBox()->GetNode(); if (node && node->IsElementNode()) { // We save and restore only the scrollOffset as the other scroll values are @@ -231,6 +233,7 @@ bool PaintLayerScrollableArea::HasBeenDisposed() const { void PaintLayerScrollableArea::Trace(blink::Visitor* visitor) { visitor->Trace(scrollbar_manager_); visitor->Trace(scroll_anchor_); + visitor->Trace(scrolling_background_display_item_client_); ScrollableArea::Trace(visitor); } @@ -289,17 +292,6 @@ GraphicsLayer* PaintLayerScrollableArea::LayerForScrollCorner() const { : nullptr; } -bool PaintLayerScrollableArea::ShouldUseIntegerScrollOffset() const { - if (!HasBeenDisposed()) { - Frame* frame = GetLayoutBox()->GetFrame(); - if (frame->GetSettings() && - !frame->GetSettings()->GetPreferCompositingToLCDTextEnabled()) - return true; - } - - return ScrollableArea::ShouldUseIntegerScrollOffset(); -} - bool PaintLayerScrollableArea::IsActive() const { Page* page = GetLayoutBox()->GetFrame()->GetPage(); return page && page->GetFocusController().IsActive(); @@ -427,7 +419,6 @@ void PaintLayerScrollableArea::UpdateScrollOffset( if (HasBeenDisposed() || GetScrollOffset() == new_offset) return; - bool offset_was_zero = scroll_offset_.IsZero(); scroll_offset_ = new_offset; LocalFrame* frame = GetLayoutBox()->GetFrame(); @@ -437,7 +428,7 @@ void PaintLayerScrollableArea::UpdateScrollOffset( bool is_root_layer = Layer()->IsRootLayer(); TRACE_EVENT1("devtools.timeline", "ScrollLayer", "data", - InspectorScrollLayerEvent::Data(GetLayoutBox())); + inspector_scroll_layer_event::Data(GetLayoutBox())); // FIXME(420741): Resolve circular dependency between scroll offset and // compositing state, and remove this disabler. @@ -471,7 +462,7 @@ void PaintLayerScrollableArea::UpdateScrollOffset( page->GetChromeClient().ClearToolTip(*frame); } - InvalidatePaintForScrollOffsetChange(offset_was_zero); + InvalidatePaintForScrollOffsetChange(); // The scrollOffsetTranslation paint property depends on the scroll offset. // (see: PaintPropertyTreeBuilder::UpdateScrollAndScrollTranslation). @@ -508,71 +499,55 @@ void PaintLayerScrollableArea::UpdateScrollOffset( cache->HandleScrollPositionChanged(GetLayoutBox()); } -void PaintLayerScrollableArea::InvalidatePaintForScrollOffsetChange( - bool offset_was_zero) { +void PaintLayerScrollableArea::InvalidatePaintForScrollOffsetChange() { InvalidatePaintForStickyDescendants(); - bool requires_paint_invalidation = false; - - // "background-attachment: local" causes the background of this element to - // change position due to scroll so a paint invalidation is needed. - // TODO(pdr): This invalidation can be removed if the local background - // attachment is painted into the scrolling contents. - if (ScrollsOverflow() && - GetLayoutBox()->StyleRef().BackgroundLayers().Attachment() == - EFillAttachment::kLocal) { - if (!UsesCompositedScrolling()) - requires_paint_invalidation = true; - - if (RuntimeEnabledFeatures::SlimmingPaintV2Enabled()) { - GetLayoutBox()->SetShouldDoFullPaintInvalidation(); - return; - } - } + auto* box = GetLayoutBox(); + auto* frame_view = box->GetFrameView(); + frame_view->InvalidateBackgroundAttachmentFixedDescendantsOnScroll(*box); - if (RuntimeEnabledFeatures::SlimmingPaintV2Enabled()) { - // TODO(pdr): If this is the root frame, descendants with fixed background - // attachments need to be invalidated. - - // A scroll offset translation is still needed for overflow:hidden and there - // is an optimization to only create this translation node when scroll - // offset is non-zero (see: NeedsScrollOrScrollTranslation in - // PaintPropertyTreeBuilder.cpp). Because of this optimization, gaining or - // losing scroll offset can change whether a property exists and we have to - // invalidate paint to ensure this property gets picked up in BlockPainter. - bool needs_repaint_for_overflow_hidden = - !ScrollsOverflow() && (offset_was_zero || GetScrollOffset().IsZero()); - // An invalidation is needed to ensure the interest rect is recalculated - // so newly-scrolled-to items are repainted. We may want to set a flag on - // PaintLayer to just check for interest rect changes instead of doing a - // full repaint. - bool needs_repaint_for_interest_rect = true; - if (needs_repaint_for_overflow_hidden || needs_repaint_for_interest_rect) - Layer()->SetNeedsRepaint(); - - return; - } - - LocalFrameView* frame_view = GetLayoutBox()->GetFrameView(); - bool is_root_layer = Layer()->IsRootLayer(); - frame_view->InvalidateBackgroundAttachmentFixedDescendants(*GetLayoutBox()); - - if (is_root_layer && frame_view->HasViewportConstrainedObjects() && + if (box->IsLayoutView() && frame_view->HasViewportConstrainedObjects() && !frame_view->InvalidateViewportConstrainedObjects()) { - requires_paint_invalidation = true; - } - - if (requires_paint_invalidation) { - GetLayoutBox()->SetShouldDoFullPaintInvalidation(); - GetLayoutBox()->SetSubtreeShouldCheckForPaintInvalidation(); - } else if (!UsesCompositedScrolling()) { - // If any scrolling content might have ben clipped by a cull rect, then - // that cull rect could be affected by scroll offset. For composited - // scrollers, this will be taken care of by the interest rect computation - // in CompositedLayerMapping. - // TODO(chrishtr): replace this shortcut with interest rects. + box->SetShouldDoFullPaintInvalidation(); + box->SetSubtreeShouldCheckForPaintInvalidation(); + } + + // TODO(chrishtr): remove this slow path once crbug.com/906885 is fixed. + // See also https://bugs.chromium.org/p/chromium/issues/detail?id=903287#c10. + if (Layer()->EnclosingPaginationLayer()) + box->SetSubtreeShouldCheckForPaintInvalidation(); + + // If not composited, background always paints into the main graphics layer. + bool background_paint_in_graphics_layer = true; + bool background_paint_in_scrolling_contents = false; + if (RuntimeEnabledFeatures::SlimmingPaintV2Enabled() || + UsesCompositedScrolling()) { + auto background_paint_location = box->GetBackgroundPaintLocation(); + background_paint_in_graphics_layer = + background_paint_location & kBackgroundPaintInGraphicsLayer; + background_paint_in_scrolling_contents = + background_paint_location & kBackgroundPaintInScrollingContents; + } + + // Both local attachment background painted in graphics layer and normal + // attachment background painted in scrolling contents require paint + // invalidation. Fixed attachment background has been dealt with in + // frame_view->InvalidateBackgroundAttachmentFixedDescendantsOnScroll(). + auto background_layers = box->StyleRef().BackgroundLayers(); + if ((background_layers.AnyLayerHasLocalAttachmentImage() && + background_paint_in_graphics_layer) || + (background_layers.AnyLayerHasDefaultAttachmentImage() && + background_paint_in_scrolling_contents)) + box->SetBackgroundNeedsFullPaintInvalidation(); + + // If any scrolling content might have been clipped by a cull rect, then + // that cull rect could be affected by scroll offset. For composited + // scrollers, this will be taken care of by the interest rect computation + // in CompositedLayerMapping. + // TODO(wangxianzhu): replace this shortcut with interest rects. + if (RuntimeEnabledFeatures::SlimmingPaintV2Enabled() || + !UsesCompositedScrolling()) Layer()->SetNeedsRepaint(); - } } IntSize PaintLayerScrollableArea::ScrollOffsetInt() const { @@ -588,7 +563,7 @@ IntSize PaintLayerScrollableArea::MinimumScrollOffsetInt() const { } IntSize PaintLayerScrollableArea::MaximumScrollOffsetInt() const { - if (!GetLayoutBox()->HasOverflowClip()) + if (!GetLayoutBox() || !GetLayoutBox()->HasOverflowClip()) return ToIntSize(-ScrollOrigin()); IntSize content_size = ContentsSize(); @@ -834,7 +809,7 @@ int PaintLayerScrollableArea::PageStep(ScrollbarOrientation orientation) const { } LayoutBox* PaintLayerScrollableArea::GetLayoutBox() const { - return layer_->GetLayoutBox(); + return layer_ ? layer_->GetLayoutBox() : nullptr; } PaintLayer* PaintLayerScrollableArea::Layer() const { @@ -984,9 +959,13 @@ void PaintLayerScrollableArea::UpdateAfterLayout() { !GetLayoutBox()->IsHorizontalWritingMode())) { GetLayoutBox()->SetPreferredLogicalWidthsDirty(); } - // If the box is managed by LayoutNG, don't go here. We don't want to - // re-enter the NG layout algorithm for this box from here. - if (!IsManagedByLayoutNG(*GetLayoutBox())) { + if (IsManagedByLayoutNG(*GetLayoutBox())) { + // If the box is managed by LayoutNG, don't go here. We don't want to + // re-enter the NG layout algorithm for this box from here. Just update + // the rectangles, in case scrollbars were added or removed. LayoutNG + // has its own scrollbar change detection mechanism. + UpdateScrollDimensions(); + } else { if (PreventRelayoutScope::RelayoutIsPrevented()) { // We're not doing re-layout right now, but we still want to // add the scrollbar to the logical width now, to facilitate parent @@ -998,7 +977,7 @@ void PaintLayerScrollableArea::UpdateAfterLayout() { in_overflow_relayout_ = true; SubtreeLayoutScope layout_scope(*GetLayoutBox()); layout_scope.SetNeedsLayout( - GetLayoutBox(), LayoutInvalidationReason::kScrollbarChanged); + GetLayoutBox(), layout_invalidation_reason::kScrollbarChanged); if (GetLayoutBox()->IsLayoutBlock()) { LayoutBlock* block = ToLayoutBlock(GetLayoutBox()); block->ScrollbarsChanged(horizontal_scrollbar_should_change, @@ -1260,7 +1239,7 @@ void PaintLayerScrollableArea::UpdateAfterOverflowRecalc() { (GetLayoutBox()->HasAutoVerticalScrollbar() && vertical_scrollbar_should_change)) { GetLayoutBox()->SetNeedsLayoutAndFullPaintInvalidation( - LayoutInvalidationReason::kUnknown); + layout_invalidation_reason::kUnknown); } ClampScrollOffsetAfterOverflowChange(); @@ -1571,6 +1550,13 @@ bool PaintLayerScrollableArea::SetHasVerticalScrollbar(bool has_scrollbar) { if (FreezeScrollbarsScope::ScrollbarsAreFrozen()) return false; + if (GetLayoutBox()->GetDocument().IsVerticalScrollEnforced()) { + // When the policy is enforced the contents of document cannot be scrolled. + // This would make rendering a scrollbar look strange + // (https://crbug.com/898151). + return false; + } + if (has_scrollbar == HasVerticalScrollbar()) return false; @@ -1639,9 +1625,10 @@ void PaintLayerScrollableArea::SnapAfterScrollbarScrolling( GetLayoutBox()->GetDocument().GetSnapCoordinator(); if (!snap_coordinator) return; - snap_coordinator->PerformSnapping(*GetLayoutBox(), - orientation == kHorizontalScrollbar, - orientation == kVerticalScrollbar); + + snap_coordinator->SnapForEndPosition(*GetLayoutBox(), + orientation == kHorizontalScrollbar, + orientation == kVerticalScrollbar); } void PaintLayerScrollableArea::PositionOverflowControls() { @@ -1983,7 +1970,9 @@ void PaintLayerScrollableArea::Resize(const IntPoint& pos, bool is_box_sizing_border = GetLayoutBox()->StyleRef().BoxSizing() == EBoxSizing::kBorderBox; - EResize resize = GetLayoutBox()->StyleRef().Resize(); + EResize resize = + ResolvedResize(GetLayoutBox()->StyleRef(), + GetLayoutBox()->ContainingBlock()->StyleRef()); if (resize != EResize::kVertical && difference.Width()) { if (element->IsFormControlElement()) { // Make implicit margins from the theme explicit (see @@ -2057,6 +2046,18 @@ LayoutRect PaintLayerScrollableArea::ScrollIntoView( if (!UserInputScrollable(kVerticalScrollbar)) new_scroll_offset.SetHeight(old_scroll_offset.Height()); } + + FloatPoint end_point = ScrollOffsetToPosition(new_scroll_offset); + std::unique_ptr<SnapSelectionStrategy> strategy = + SnapSelectionStrategy::CreateForEndPosition(gfx::ScrollOffset(end_point), + true, true); + end_point = GetLayoutBox() + ->GetDocument() + .GetSnapCoordinator() + ->GetSnapPosition(*GetLayoutBox(), *strategy) + .value_or(end_point); + new_scroll_offset = ScrollPositionToOffset(end_point); + if (params.is_for_scroll_sequence) { DCHECK(params.GetScrollType() == kProgrammaticScroll || params.GetScrollType() == kUserScroll); @@ -2165,11 +2166,6 @@ void PaintLayerScrollableArea::UpdateCompositingLayersAfterScroll() { kCompositingUpdateAfterGeometryChange); } - // Sticky constraints and paint property nodes need to be updated - // to the new sticky locations. - if (HasStickyDescendants()) - InvalidateAllStickyConstraints(); - // If we have fixed elements and we scroll the root layer we might // change compositing since the fixed elements might now overlap a // composited layer. @@ -2253,12 +2249,13 @@ bool PaintLayerScrollableArea::ComputeNeedsCompositedScrolling( // TODO(flackr): Allow integer transforms as long as all of the ancestor // transforms are also integer. bool background_supports_lcd_text = - layer->GetLayoutObject().StyleRef().IsStackingContext() && - layer->GetBackgroundPaintLocation( + GetLayoutBox()->StyleRef().IsStackingContext() && + GetLayoutBox()->GetBackgroundPaintLocation( &non_composited_main_thread_scrolling_reasons_) & kBackgroundPaintInScrollingContents && layer->BackgroundIsKnownToBeOpaqueInRect( - ToLayoutBox(layer->GetLayoutObject()).PhysicalPaddingBoxRect()) && + ToLayoutBox(layer->GetLayoutObject()).PhysicalPaddingBoxRect(), + true) && !layer->CompositesWithTransform() && !layer->CompositesWithOpacity(); // TODO(crbug.com/839341): Remove ScrollTimeline check once we support @@ -2276,7 +2273,8 @@ bool PaintLayerScrollableArea::ComputeNeedsCompositedScrolling( MainThreadScrollingReason::kHasTransformAndLCDText; } if (!layer->BackgroundIsKnownToBeOpaqueInRect( - ToLayoutBox(layer->GetLayoutObject()).PhysicalPaddingBoxRect())) { + ToLayoutBox(layer->GetLayoutObject()).PhysicalPaddingBoxRect(), + true)) { non_composited_main_thread_scrolling_reasons_ |= MainThreadScrollingReason::kBackgroundNotOpaqueInRectAndLCDText; } @@ -2322,9 +2320,7 @@ bool PaintLayerScrollableArea::VisualViewportSuppliesScrollbars() const { const TopDocumentRootScrollerController& controller = GetLayoutBox()->GetDocument().GetPage()->GlobalRootScrollerController(); - - return RootScrollerUtil::ScrollableAreaForRootScroller( - controller.GlobalRootScroller()) == this; + return controller.RootScrollerArea() == this; } bool PaintLayerScrollableArea::ScheduleAnimation() { @@ -2500,7 +2496,7 @@ PaintLayerScrollableArea::PreventRelayoutScope::~PreventRelayoutScope() { DCHECK(scrollable_area->NeedsRelayout()); LayoutBox* box = scrollable_area->GetLayoutBox(); layout_scope_->SetNeedsLayout( - box, LayoutInvalidationReason::kScrollbarChanged); + box, layout_invalidation_reason::kScrollbarChanged); if (box->IsLayoutBlock()) { bool horizontal_scrollbar_changed = scrollable_area->HasHorizontalScrollbar() != @@ -2547,9 +2543,10 @@ void PaintLayerScrollableArea::PreventRelayoutScope::ResetRelayoutNeeded() { HeapVector<Member<PaintLayerScrollableArea>>& PaintLayerScrollableArea::PreventRelayoutScope::NeedsRelayoutList() { - DEFINE_STATIC_LOCAL(Persistent<HeapVector<Member<PaintLayerScrollableArea>>>, - needs_relayout_list, - (new HeapVector<Member<PaintLayerScrollableArea>>)); + DEFINE_STATIC_LOCAL( + Persistent<HeapVector<Member<PaintLayerScrollableArea>>>, + needs_relayout_list, + (MakeGarbageCollected<HeapVector<Member<PaintLayerScrollableArea>>>())); return *needs_relayout_list; } @@ -2586,9 +2583,10 @@ void PaintLayerScrollableArea::DelayScrollOffsetClampScope:: HeapVector<Member<PaintLayerScrollableArea>>& PaintLayerScrollableArea::DelayScrollOffsetClampScope::NeedsClampList() { - DEFINE_STATIC_LOCAL(Persistent<HeapVector<Member<PaintLayerScrollableArea>>>, - needs_clamp_list, - (new HeapVector<Member<PaintLayerScrollableArea>>)); + DEFINE_STATIC_LOCAL( + Persistent<HeapVector<Member<PaintLayerScrollableArea>>>, + needs_clamp_list, + (MakeGarbageCollected<HeapVector<Member<PaintLayerScrollableArea>>>())); return *needs_clamp_list; } @@ -2845,4 +2843,37 @@ CompositorElementId PaintLayerScrollableArea::GetCompositorElementId() const { GetLayoutBox()->UniqueId(), CompositorElementIdNamespace::kScroll); } +LayoutRect +PaintLayerScrollableArea::ScrollingBackgroundDisplayItemClient::VisualRect() + const { + const auto* box = scrollable_area_->GetLayoutBox(); + auto overflow_clip_rect = box->OverflowClipRect(LayoutPoint()); + auto scroll_size = scrollable_area_->overflow_rect_.Size(); + // Ensure scrolling contents are at least as large as the scroll clip + scroll_size = scroll_size.ExpandedTo(overflow_clip_rect.Size()); + LayoutRect result(overflow_clip_rect.Location(), scroll_size); + result.MoveBy(box->FirstFragment().PaintOffset()); + result = LayoutRect(PixelSnappedIntRect(result)); +#if DCHECK_IS_ON() + if (!RuntimeEnabledFeatures::SlimmingPaintV2Enabled()) { + DCHECK_EQ(result, + scrollable_area_->layer_->GraphicsLayerBacking()->VisualRect()); + } +#endif + return result; +} + +String +PaintLayerScrollableArea::ScrollingBackgroundDisplayItemClient::DebugName() + const { + return "Scrolling background of " + + scrollable_area_->GetLayoutBox()->DebugName(); +} + +bool PaintLayerScrollableArea::ScrollingBackgroundDisplayItemClient:: + PaintedOutputOfObjectHasNoEffectRegardlessOfSize() const { + return scrollable_area_->GetLayoutBox() + ->PaintedOutputOfObjectHasNoEffectRegardlessOfSize(); +} + } // namespace blink diff --git a/chromium/third_party/blink/renderer/core/paint/paint_layer_scrollable_area.h b/chromium/third_party/blink/renderer/core/paint/paint_layer_scrollable_area.h index ec116bef8a6..48def2b72e9 100644 --- a/chromium/third_party/blink/renderer/core/paint/paint_layer_scrollable_area.h +++ b/chromium/third_party/blink/renderer/core/paint/paint_layer_scrollable_area.h @@ -244,9 +244,10 @@ class CORE_EXPORT PaintLayerScrollableArea final // FIXME: We should pass in the LayoutBox but this opens a window // for crashers during PaintLayer setup (see crbug.com/368062). static PaintLayerScrollableArea* Create(PaintLayer& layer) { - return new PaintLayerScrollableArea(layer); + return MakeGarbageCollected<PaintLayerScrollableArea>(layer); } + explicit PaintLayerScrollableArea(PaintLayer&); ~PaintLayerScrollableArea() override; void Dispose(); bool HasBeenDisposed() const override; @@ -284,7 +285,6 @@ class CORE_EXPORT PaintLayerScrollableArea final GraphicsLayer* LayerForScrollCorner() const override; bool ShouldScrollOnMainThread() const override; - bool ShouldUseIntegerScrollOffset() const override; bool IsActive() const override; bool IsScrollCornerVisible() const override; IntRect ScrollCornerRect() const override; @@ -548,9 +548,11 @@ class CORE_EXPORT PaintLayerScrollableArea final void Trace(blink::Visitor*) override; - private: - explicit PaintLayerScrollableArea(PaintLayer&); + const DisplayItemClient& GetScrollingBackgroundDisplayItemClient() const { + return scrolling_background_display_item_client_; + } + private: bool NeedsScrollbarReconstruction() const; void ResetScrollOriginChanged() { scroll_origin_changed_ = false; } @@ -562,7 +564,7 @@ class CORE_EXPORT PaintLayerScrollableArea final void UpdateScrollbarProportions(); void UpdateScrollOffset(const ScrollOffset&, ScrollType) override; - void InvalidatePaintForScrollOffsetChange(bool offset_was_zero); + void InvalidatePaintForScrollOffsetChange(); int VerticalScrollbarStart(int min_x, int max_x) const; int HorizontalScrollbarStart(int min_x) const; @@ -697,6 +699,27 @@ class CORE_EXPORT PaintLayerScrollableArea final LayoutRect horizontal_scrollbar_visual_rect_; LayoutRect vertical_scrollbar_visual_rect_; LayoutRect scroll_corner_and_resizer_visual_rect_; + + class ScrollingBackgroundDisplayItemClient : public DisplayItemClient { + DISALLOW_NEW(); + + public: + ScrollingBackgroundDisplayItemClient( + const PaintLayerScrollableArea& scrollable_area) + : scrollable_area_(&scrollable_area) {} + + LayoutRect VisualRect() const override; + String DebugName() const override; + bool PaintedOutputOfObjectHasNoEffectRegardlessOfSize() const override; + + void Trace(Visitor* visitor) { visitor->Trace(scrollable_area_); } + + private: + Member<const PaintLayerScrollableArea> scrollable_area_; + }; + + ScrollingBackgroundDisplayItemClient + scrolling_background_display_item_client_; }; DEFINE_TYPE_CASTS(PaintLayerScrollableArea, diff --git a/chromium/third_party/blink/renderer/core/paint/paint_layer_scrollable_area_test.cc b/chromium/third_party/blink/renderer/core/paint/paint_layer_scrollable_area_test.cc index ea6ccd73737..5a3f1d6c312 100644 --- a/chromium/third_party/blink/renderer/core/paint/paint_layer_scrollable_area_test.cc +++ b/chromium/third_party/blink/renderer/core/paint/paint_layer_scrollable_area_test.cc @@ -12,6 +12,7 @@ #include "third_party/blink/renderer/core/testing/core_unit_test_helper.h" #include "third_party/blink/renderer/platform/graphics/graphics_layer.h" #include "third_party/blink/renderer/platform/scroll/scroll_types.h" +#include "third_party/blink/renderer/platform/testing/paint_test_configurations.h" using testing::_; @@ -30,13 +31,13 @@ class ScrollableAreaMockChromeClient : public EmptyChromeClient { } // namespace { -class PaintLayerScrollableAreaTest : public RenderingTest { +class PaintLayerScrollableAreaTestBase : public RenderingTest { public: - PaintLayerScrollableAreaTest() + PaintLayerScrollableAreaTestBase() : RenderingTest(EmptyLocalFrameClient::Create()), chrome_client_(new ScrollableAreaMockChromeClient) {} - ~PaintLayerScrollableAreaTest() override { + ~PaintLayerScrollableAreaTestBase() override { testing::Mock::VerifyAndClearExpectations(&GetChromeClient()); } @@ -45,9 +46,8 @@ class PaintLayerScrollableAreaTest : public RenderingTest { } BackgroundPaintLocation GetBackgroundPaintLocation(const char* element_id) { - PaintLayer* paint_layer = - ToLayoutBoxModelObject(GetLayoutObjectByElementId(element_id))->Layer(); - return paint_layer->GetBackgroundPaintLocation(); + return ToLayoutBoxModelObject(GetLayoutObjectByElementId(element_id)) + ->GetBackgroundPaintLocation(); } private: @@ -59,7 +59,13 @@ class PaintLayerScrollableAreaTest : public RenderingTest { Persistent<ScrollableAreaMockChromeClient> chrome_client_; }; -TEST_F(PaintLayerScrollableAreaTest, +class PaintLayerScrollableAreaTest : public PaintLayerScrollableAreaTestBase, + public PaintTestConfigurations {}; + +INSTANTIATE_PAINT_TEST_CASE_P(PaintLayerScrollableAreaTest); +using PaintLayerScrollableAreaTestSPv1 = PaintLayerScrollableAreaTestBase; + +TEST_P(PaintLayerScrollableAreaTest, CanPaintBackgroundOntoScrollingContentsLayer) { GetDocument().GetFrame()->GetSettings()->SetPreferCompositingToLCDTextEnabled( true); @@ -152,6 +158,11 @@ TEST_F(PaintLayerScrollableAreaTest, border: 5px solid rgba(0, 0, 0, 0.5);'> <div class='spacer'></div> </div> + <div id='scroller18' class='scroller' + style='background: white; + border: 5px dashed black;'> + <div class='spacer'></div> + </div> )HTML"); // #scroller1 can paint background into scrolling contents layer even with a @@ -246,9 +257,16 @@ TEST_F(PaintLayerScrollableAreaTest, // be painted in the graphics layer to be under the translucent border. EXPECT_EQ(kBackgroundPaintInGraphicsLayer, GetBackgroundPaintLocation("scroller17")); + + // #scroller18 can be painted in both layers because the background is a + // solid color, it must be because the dashed border reveals the background + // underneath it. + EXPECT_EQ( + kBackgroundPaintInGraphicsLayer | kBackgroundPaintInScrollingContents, + GetBackgroundPaintLocation("scroller18")); } -TEST_F(PaintLayerScrollableAreaTest, OpaqueContainedLayersPromoted) { +TEST_F(PaintLayerScrollableAreaTestSPv1, OpaqueContainedLayersPromoted) { SetBodyInnerHTML(R"HTML( <style> #scroller { overflow: scroll; height: 200px; width: 200px; @@ -258,7 +276,6 @@ TEST_F(PaintLayerScrollableAreaTest, OpaqueContainedLayersPromoted) { </style> <div id="scroller"><div id="scrolled"></div></div> )HTML"); - GetDocument().View()->UpdateAllLifecyclePhases(); Element* scroller = GetDocument().getElementById("scroller"); PaintLayer* paint_layer = @@ -272,7 +289,7 @@ TEST_F(PaintLayerScrollableAreaTest, OpaqueContainedLayersPromoted) { // Tests that we don't promote scrolling content which would not be contained. // Promoting the scroller would also require promoting the positioned div // which would lose subpixel anti-aliasing due to its transparent background. -TEST_F(PaintLayerScrollableAreaTest, NonContainedLayersNotPromoted) { +TEST_F(PaintLayerScrollableAreaTestSPv1, NonContainedLayersNotPromoted) { SetBodyInnerHTML(R"HTML( <style> #scroller { overflow: scroll; height: 200px; width: 200px; @@ -286,7 +303,6 @@ TEST_F(PaintLayerScrollableAreaTest, NonContainedLayersNotPromoted) { <div id="scrolled"></div> </div> )HTML"); - GetDocument().View()->UpdateAllLifecyclePhases(); Element* scroller = GetDocument().getElementById("scroller"); PaintLayer* paint_layer = @@ -296,7 +312,7 @@ TEST_F(PaintLayerScrollableAreaTest, NonContainedLayersNotPromoted) { EXPECT_FALSE(paint_layer->GraphicsLayerBacking()); } -TEST_F(PaintLayerScrollableAreaTest, TransparentLayersNotPromoted) { +TEST_F(PaintLayerScrollableAreaTestSPv1, TransparentLayersNotPromoted) { SetBodyInnerHTML(R"HTML( <style> #scroller { overflow: scroll; height: 200px; width: 200px; background: @@ -306,7 +322,6 @@ TEST_F(PaintLayerScrollableAreaTest, TransparentLayersNotPromoted) { </style> <div id="scroller"><div id="scrolled"></div></div> )HTML"); - GetDocument().View()->UpdateAllLifecyclePhases(); Element* scroller = GetDocument().getElementById("scroller"); PaintLayer* paint_layer = @@ -316,7 +331,7 @@ TEST_F(PaintLayerScrollableAreaTest, TransparentLayersNotPromoted) { EXPECT_FALSE(paint_layer->GraphicsLayerBacking()); } -TEST_F(PaintLayerScrollableAreaTest, OpaqueLayersDepromotedOnStyleChange) { +TEST_F(PaintLayerScrollableAreaTestSPv1, OpaqueLayersDepromotedOnStyleChange) { SetBodyInnerHTML(R"HTML( <style> #scroller { overflow: scroll; height: 200px; width: 200px; background: @@ -325,7 +340,6 @@ TEST_F(PaintLayerScrollableAreaTest, OpaqueLayersDepromotedOnStyleChange) { </style> <div id="scroller"><div id="scrolled"></div></div> )HTML"); - GetDocument().View()->UpdateAllLifecyclePhases(); Element* scroller = GetDocument().getElementById("scroller"); PaintLayer* paint_layer = @@ -335,16 +349,16 @@ TEST_F(PaintLayerScrollableAreaTest, OpaqueLayersDepromotedOnStyleChange) { // Change the background to transparent scroller->setAttribute( - HTMLNames::styleAttr, + html_names::kStyleAttr, "background: rgba(255,255,255,0.5) local content-box;"); - GetDocument().View()->UpdateAllLifecyclePhases(); + UpdateAllLifecyclePhasesForTest(); paint_layer = ToLayoutBoxModelObject(scroller->GetLayoutObject())->Layer(); ASSERT_TRUE(paint_layer); EXPECT_FALSE(paint_layer->NeedsCompositedScrolling()); EXPECT_FALSE(paint_layer->GraphicsLayerBacking()); } -TEST_F(PaintLayerScrollableAreaTest, OpaqueLayersPromotedOnStyleChange) { +TEST_F(PaintLayerScrollableAreaTestSPv1, OpaqueLayersPromotedOnStyleChange) { SetBodyInnerHTML(R"HTML( <style> #scroller { overflow: scroll; height: 200px; width: 200px; background: @@ -353,7 +367,6 @@ TEST_F(PaintLayerScrollableAreaTest, OpaqueLayersPromotedOnStyleChange) { </style> <div id="scroller"><div id="scrolled"></div></div> )HTML"); - GetDocument().View()->UpdateAllLifecyclePhases(); Element* scroller = GetDocument().getElementById("scroller"); PaintLayer* paint_layer = @@ -362,9 +375,9 @@ TEST_F(PaintLayerScrollableAreaTest, OpaqueLayersPromotedOnStyleChange) { EXPECT_FALSE(paint_layer->NeedsCompositedScrolling()); // Change the background to opaque - scroller->setAttribute(HTMLNames::styleAttr, + scroller->setAttribute(html_names::kStyleAttr, "background: white local content-box;"); - GetDocument().View()->UpdateAllLifecyclePhases(); + UpdateAllLifecyclePhasesForTest(); paint_layer = ToLayoutBoxModelObject(scroller->GetLayoutObject())->Layer(); ASSERT_TRUE(paint_layer); EXPECT_TRUE(paint_layer->NeedsCompositedScrolling()); @@ -376,7 +389,8 @@ TEST_F(PaintLayerScrollableAreaTest, OpaqueLayersPromotedOnStyleChange) { // Tests that a transform on the scroller or an ancestor will prevent promotion // TODO(flackr): Allow integer transforms as long as all of the ancestor // transforms are also integer. -TEST_F(PaintLayerScrollableAreaTest, OnlyNonTransformedOpaqueLayersPromoted) { +TEST_F(PaintLayerScrollableAreaTestSPv1, + OnlyNonTransformedOpaqueLayersPromoted) { SetBodyInnerHTML(R"HTML( <style> #scroller { overflow: scroll; height: 200px; width: 200px; background: @@ -387,7 +401,6 @@ TEST_F(PaintLayerScrollableAreaTest, OnlyNonTransformedOpaqueLayersPromoted) { <div id="scroller"><div id="scrolled"></div></div> </div> )HTML"); - GetDocument().View()->UpdateAllLifecyclePhases(); Element* parent = GetDocument().getElementById("parent"); Element* scroller = GetDocument().getElementById("scroller"); @@ -400,16 +413,16 @@ TEST_F(PaintLayerScrollableAreaTest, OnlyNonTransformedOpaqueLayersPromoted) { EXPECT_TRUE(paint_layer->GraphicsLayerBacking()->ContentsOpaque()); // Change the parent to have a transform. - parent->setAttribute(HTMLNames::styleAttr, "transform: translate(1px, 0);"); - GetDocument().View()->UpdateAllLifecyclePhases(); + parent->setAttribute(html_names::kStyleAttr, "transform: translate(1px, 0);"); + UpdateAllLifecyclePhasesForTest(); paint_layer = ToLayoutBoxModelObject(scroller->GetLayoutObject())->Layer(); ASSERT_TRUE(paint_layer); EXPECT_FALSE(paint_layer->NeedsCompositedScrolling()); EXPECT_FALSE(paint_layer->GraphicsLayerBacking()); // Change the parent to have no transform again. - parent->removeAttribute(HTMLNames::styleAttr); - GetDocument().View()->UpdateAllLifecyclePhases(); + parent->removeAttribute(html_names::kStyleAttr); + UpdateAllLifecyclePhasesForTest(); paint_layer = ToLayoutBoxModelObject(scroller->GetLayoutObject())->Layer(); ASSERT_TRUE(paint_layer); EXPECT_TRUE(paint_layer->NeedsCompositedScrolling()); @@ -418,8 +431,9 @@ TEST_F(PaintLayerScrollableAreaTest, OnlyNonTransformedOpaqueLayersPromoted) { EXPECT_TRUE(paint_layer->GraphicsLayerBacking()->ContentsOpaque()); // Apply a transform to the scroller directly. - scroller->setAttribute(HTMLNames::styleAttr, "transform: translate(1px, 0);"); - GetDocument().View()->UpdateAllLifecyclePhases(); + scroller->setAttribute(html_names::kStyleAttr, + "transform: translate(1px, 0);"); + UpdateAllLifecyclePhasesForTest(); paint_layer = ToLayoutBoxModelObject(scroller->GetLayoutObject())->Layer(); ASSERT_TRUE(paint_layer); EXPECT_FALSE(paint_layer->NeedsCompositedScrolling()); @@ -428,7 +442,7 @@ TEST_F(PaintLayerScrollableAreaTest, OnlyNonTransformedOpaqueLayersPromoted) { // Test that opacity applied to the scroller or an ancestor will cause the // scrolling contents layer to not be promoted. -TEST_F(PaintLayerScrollableAreaTest, OnlyOpaqueLayersPromoted) { +TEST_F(PaintLayerScrollableAreaTestSPv1, OnlyOpaqueLayersPromoted) { SetBodyInnerHTML(R"HTML( <style> #scroller { overflow: scroll; height: 200px; width: 200px; background: @@ -439,7 +453,6 @@ TEST_F(PaintLayerScrollableAreaTest, OnlyOpaqueLayersPromoted) { <div id="scroller"><div id="scrolled"></div></div> </div> )HTML"); - GetDocument().View()->UpdateAllLifecyclePhases(); Element* parent = GetDocument().getElementById("parent"); Element* scroller = GetDocument().getElementById("scroller"); @@ -452,16 +465,16 @@ TEST_F(PaintLayerScrollableAreaTest, OnlyOpaqueLayersPromoted) { EXPECT_TRUE(paint_layer->GraphicsLayerBacking()->ContentsOpaque()); // Change the parent to be partially translucent. - parent->setAttribute(HTMLNames::styleAttr, "opacity: 0.5;"); - GetDocument().View()->UpdateAllLifecyclePhases(); + parent->setAttribute(html_names::kStyleAttr, "opacity: 0.5;"); + UpdateAllLifecyclePhasesForTest(); paint_layer = ToLayoutBoxModelObject(scroller->GetLayoutObject())->Layer(); ASSERT_TRUE(paint_layer); EXPECT_FALSE(paint_layer->NeedsCompositedScrolling()); EXPECT_FALSE(paint_layer->GraphicsLayerBacking()); // Change the parent to be opaque again. - parent->setAttribute(HTMLNames::styleAttr, "opacity: 1;"); - GetDocument().View()->UpdateAllLifecyclePhases(); + parent->setAttribute(html_names::kStyleAttr, "opacity: 1;"); + UpdateAllLifecyclePhasesForTest(); paint_layer = ToLayoutBoxModelObject(scroller->GetLayoutObject())->Layer(); ASSERT_TRUE(paint_layer); EXPECT_TRUE(paint_layer->NeedsCompositedScrolling()); @@ -470,8 +483,8 @@ TEST_F(PaintLayerScrollableAreaTest, OnlyOpaqueLayersPromoted) { EXPECT_TRUE(paint_layer->GraphicsLayerBacking()->ContentsOpaque()); // Make the scroller translucent. - scroller->setAttribute(HTMLNames::styleAttr, "opacity: 0.5"); - GetDocument().View()->UpdateAllLifecyclePhases(); + scroller->setAttribute(html_names::kStyleAttr, "opacity: 0.5"); + UpdateAllLifecyclePhasesForTest(); paint_layer = ToLayoutBoxModelObject(scroller->GetLayoutObject())->Layer(); ASSERT_TRUE(paint_layer); EXPECT_FALSE(paint_layer->NeedsCompositedScrolling()); @@ -479,7 +492,7 @@ TEST_F(PaintLayerScrollableAreaTest, OnlyOpaqueLayersPromoted) { } // Test that <input> elements get promoted with "will-change:transform". -TEST_F(PaintLayerScrollableAreaTest, InputElementPromotionTest) { +TEST_F(PaintLayerScrollableAreaTestSPv1, InputElementPromotionTest) { SetBodyInnerHTML(R"HTML( <!DOCTYPE html> <style> @@ -487,7 +500,6 @@ TEST_F(PaintLayerScrollableAreaTest, InputElementPromotionTest) { </style> <input id='input' width=10 style='font-size:40pt;'/> )HTML"); - GetDocument().View()->UpdateAllLifecyclePhases(); Element* element = GetDocument().getElementById("input"); PaintLayer* paint_layer = @@ -495,14 +507,14 @@ TEST_F(PaintLayerScrollableAreaTest, InputElementPromotionTest) { ASSERT_FALSE(paint_layer); element->setAttribute("class", "composited"); - GetDocument().View()->UpdateAllLifecyclePhases(); + UpdateAllLifecyclePhasesForTest(); paint_layer = ToLayoutBoxModelObject(element->GetLayoutObject())->Layer(); ASSERT_TRUE(paint_layer); ASSERT_TRUE(paint_layer->HasCompositedLayerMapping()); } // Test that <select> elements get promoted with "will-change:transform". -TEST_F(PaintLayerScrollableAreaTest, SelectElementPromotionTest) { +TEST_F(PaintLayerScrollableAreaTestSPv1, SelectElementPromotionTest) { SetBodyInnerHTML(R"HTML( <!DOCTYPE html> <style> @@ -515,7 +527,6 @@ TEST_F(PaintLayerScrollableAreaTest, SelectElementPromotionTest) { <option> value 4</option> </select> )HTML"); - GetDocument().View()->UpdateAllLifecyclePhases(); Element* element = GetDocument().getElementById("select"); PaintLayer* paint_layer = @@ -525,14 +536,14 @@ TEST_F(PaintLayerScrollableAreaTest, SelectElementPromotionTest) { ASSERT_TRUE(!paint_layer || !paint_layer->HasCompositedLayerMapping()); element->setAttribute("class", "composited"); - GetDocument().View()->UpdateAllLifecyclePhases(); + UpdateAllLifecyclePhasesForTest(); paint_layer = ToLayoutBoxModelObject(element->GetLayoutObject())->Layer(); ASSERT_TRUE(paint_layer); ASSERT_TRUE(paint_layer->HasCompositedLayerMapping()); } // Ensure OverlayScrollbarColorTheme get updated when page load -TEST_F(PaintLayerScrollableAreaTest, OverlayScrollbarColorThemeUpdated) { +TEST_P(PaintLayerScrollableAreaTest, OverlayScrollbarColorThemeUpdated) { SetBodyInnerHTML(R"HTML( <style> div { overflow: scroll; } @@ -543,7 +554,6 @@ TEST_F(PaintLayerScrollableAreaTest, OverlayScrollbarColorThemeUpdated) { <div id="white">b</div> <div id="black">c</div> )HTML"); - GetDocument().View()->UpdateAllLifecyclePhases(); Element* none = GetDocument().getElementById("none"); Element* white = GetDocument().getElementById("white"); @@ -570,7 +580,7 @@ TEST_F(PaintLayerScrollableAreaTest, OverlayScrollbarColorThemeUpdated) { // Test that css clip applied to the scroller will cause the // scrolling contents layer to not be promoted. -TEST_F(PaintLayerScrollableAreaTest, +TEST_F(PaintLayerScrollableAreaTestSPv1, OnlyAutoClippedScrollingContentsLayerPromoted) { SetBodyInnerHTML(R"HTML( <style> @@ -582,7 +592,6 @@ TEST_F(PaintLayerScrollableAreaTest, </style> <div id="scroller"><div id="scrolled"></div></div> )HTML"); - GetDocument().View()->UpdateAllLifecyclePhases(); Element* scroller = GetDocument().getElementById("scroller"); PaintLayer* paint_layer = @@ -591,21 +600,21 @@ TEST_F(PaintLayerScrollableAreaTest, EXPECT_TRUE(paint_layer->NeedsCompositedScrolling()); // Add clip to scroller. - scroller->setAttribute(HTMLNames::classAttr, "clip"); - GetDocument().View()->UpdateAllLifecyclePhases(); + scroller->setAttribute(html_names::kClassAttr, "clip"); + UpdateAllLifecyclePhasesForTest(); paint_layer = ToLayoutBoxModelObject(scroller->GetLayoutObject())->Layer(); ASSERT_TRUE(paint_layer); EXPECT_FALSE(paint_layer->NeedsCompositedScrolling()); // Change the scroller to be auto clipped again. scroller->removeAttribute("class"); - GetDocument().View()->UpdateAllLifecyclePhases(); + UpdateAllLifecyclePhasesForTest(); paint_layer = ToLayoutBoxModelObject(scroller->GetLayoutObject())->Layer(); ASSERT_TRUE(paint_layer); EXPECT_TRUE(paint_layer->NeedsCompositedScrolling()); } -TEST_F(PaintLayerScrollableAreaTest, HideTooltipWhenScrollPositionChanges) { +TEST_P(PaintLayerScrollableAreaTest, HideTooltipWhenScrollPositionChanges) { SetBodyInnerHTML(R"HTML( <style> #scroller { width: 100px; height: 100px; overflow: scroll; } @@ -613,7 +622,6 @@ TEST_F(PaintLayerScrollableAreaTest, HideTooltipWhenScrollPositionChanges) { </style> <div id="scroller"><div id="scrolled"></div></div> )HTML"); - GetDocument().View()->UpdateAllLifecyclePhases(); Element* scroller = GetDocument().getElementById("scroller"); PaintLayerScrollableArea* scrollable_area = @@ -633,7 +641,7 @@ TEST_F(PaintLayerScrollableAreaTest, HideTooltipWhenScrollPositionChanges) { scrollable_area->SetScrollOffset(ScrollOffset(2, 2), kProgrammaticScroll); } -TEST_F(PaintLayerScrollableAreaTest, IncludeOverlayScrollbarsInVisibleWidth) { +TEST_P(PaintLayerScrollableAreaTest, IncludeOverlayScrollbarsInVisibleWidth) { ScopedOverlayScrollbarsForTest overlay_scrollbars(false); SetBodyInnerHTML(R"HTML( <style> @@ -642,7 +650,7 @@ TEST_F(PaintLayerScrollableAreaTest, IncludeOverlayScrollbarsInVisibleWidth) { </style> <div id="scroller"><div id="scrolled"></div></div> )HTML"); - GetDocument().View()->UpdateAllLifecyclePhases(); + Element* scroller = GetDocument().getElementById("scroller"); ASSERT_TRUE(scroller); PaintLayerScrollableArea* scrollable_area = @@ -652,7 +660,7 @@ TEST_F(PaintLayerScrollableAreaTest, IncludeOverlayScrollbarsInVisibleWidth) { EXPECT_EQ(scrollable_area->GetScrollOffset().Width(), 0); } -TEST_F(PaintLayerScrollableAreaTest, ShowAutoScrollbarsForVisibleContent) { +TEST_P(PaintLayerScrollableAreaTest, ShowAutoScrollbarsForVisibleContent) { ScopedOverlayScrollbarsForTest overlay_scrollbars(false); SetBodyInnerHTML(R"HTML( <style> @@ -671,18 +679,18 @@ TEST_F(PaintLayerScrollableAreaTest, ShowAutoScrollbarsForVisibleContent) { <div id='innerDiv'></div> </div> )HTML"); - GetDocument().View()->UpdateAllLifecyclePhases(); + Element* outer_div = GetDocument().getElementById("outerDiv"); ASSERT_TRUE(outer_div); outer_div->GetLayoutObject()->SetNeedsLayout("test"); - GetDocument().View()->UpdateAllLifecyclePhases(); + UpdateAllLifecyclePhasesForTest(); PaintLayerScrollableArea* scrollable_area = ToLayoutBoxModelObject(outer_div->GetLayoutObject())->GetScrollableArea(); ASSERT_TRUE(scrollable_area); EXPECT_TRUE(scrollable_area->HasVerticalScrollbar()); } -TEST_F(PaintLayerScrollableAreaTest, FloatOverflowInRtlContainer) { +TEST_P(PaintLayerScrollableAreaTest, FloatOverflowInRtlContainer) { ScopedOverlayScrollbarsForTest overlay_scrollbars(false); SetBodyInnerHTML(R"HTML( <!DOCTYPE html> @@ -700,7 +708,7 @@ TEST_F(PaintLayerScrollableAreaTest, FloatOverflowInRtlContainer) { </div> </div> )HTML"); - GetDocument().View()->UpdateAllLifecyclePhases(); + Element* container = GetDocument().getElementById("container"); ASSERT_TRUE(container); PaintLayerScrollableArea* scrollable_area = @@ -709,7 +717,7 @@ TEST_F(PaintLayerScrollableAreaTest, FloatOverflowInRtlContainer) { EXPECT_FALSE(scrollable_area->HasHorizontalScrollbar()); } -TEST_F(PaintLayerScrollableAreaTest, ScrollOriginInRtlContainer) { +TEST_P(PaintLayerScrollableAreaTest, ScrollOriginInRtlContainer) { SetBodyInnerHTML(R"HTML( <!DOCTYPE html> <style> @@ -728,7 +736,7 @@ TEST_F(PaintLayerScrollableAreaTest, ScrollOriginInRtlContainer) { <div> </div> )HTML"); - GetDocument().View()->UpdateAllLifecyclePhases(); + Element* container = GetDocument().getElementById("container"); ASSERT_TRUE(container); PaintLayerScrollableArea* scrollable_area = @@ -737,10 +745,7 @@ TEST_F(PaintLayerScrollableAreaTest, ScrollOriginInRtlContainer) { EXPECT_EQ(scrollable_area->ScrollOrigin().X(), 100); } -TEST_F(PaintLayerScrollableAreaTest, - SlimmingPaintV2OverflowHiddenScrollOffsetInvalidation) { - ScopedSlimmingPaintV2ForTest enabler(true); - +TEST_P(PaintLayerScrollableAreaTest, OverflowHiddenScrollOffsetInvalidation) { SetBodyInnerHTML(R"HTML( <style> #scroller { @@ -753,7 +758,6 @@ TEST_F(PaintLayerScrollableAreaTest, <div id='forceScroll' style='height: 2000px;'></div> </div> )HTML"); - GetDocument().View()->UpdateAllLifecyclePhases(); auto* scroller = GetLayoutObjectByElementId("scroller"); auto* scrollable_area = ToLayoutBoxModelObject(scroller)->GetScrollableArea(); @@ -769,7 +773,7 @@ TEST_F(PaintLayerScrollableAreaTest, scrollable_area->SetScrollOffset(ScrollOffset(0, 1), kProgrammaticScroll); EXPECT_TRUE(scroller->PaintingLayer()->NeedsRepaint()); EXPECT_TRUE(scroller->NeedsPaintPropertyUpdate()); - GetDocument().View()->UpdateAllLifecyclePhases(); + UpdateAllLifecyclePhasesForTest(); // A scroll offset translation is needed when scroll offset is non-zero. EXPECT_EQ(FloatSize(0, 1), scrollable_area->GetScrollOffset()); @@ -778,7 +782,7 @@ TEST_F(PaintLayerScrollableAreaTest, // A property update is needed when scroll offset changes. scrollable_area->SetScrollOffset(ScrollOffset(0, 2), kProgrammaticScroll); EXPECT_TRUE(scroller->NeedsPaintPropertyUpdate()); - GetDocument().View()->UpdateAllLifecyclePhases(); + UpdateAllLifecyclePhasesForTest(); // A scroll offset translation is still needed when scroll offset is non-zero. EXPECT_EQ(FloatSize(0, 2), scrollable_area->GetScrollOffset()); @@ -789,16 +793,14 @@ TEST_F(PaintLayerScrollableAreaTest, scrollable_area->SetScrollOffset(ScrollOffset(0, 0), kProgrammaticScroll); EXPECT_TRUE(scroller->PaintingLayer()->NeedsRepaint()); EXPECT_TRUE(scroller->NeedsPaintPropertyUpdate()); - GetDocument().View()->UpdateAllLifecyclePhases(); + UpdateAllLifecyclePhasesForTest(); // No scroll offset translation is needed when scroll offset is zero. EXPECT_EQ(nullptr, properties->ScrollTranslation()); EXPECT_EQ(FloatSize(0, 0), scrollable_area->GetScrollOffset()); } -TEST_F(PaintLayerScrollableAreaTest, SlimmingPaintV2ScrollDoesNotInvalidate) { - ScopedSlimmingPaintV2ForTest enabler(true); - +TEST_P(PaintLayerScrollableAreaTest, ScrollDoesNotInvalidate) { SetBodyInnerHTML(R"HTML( <style> #scroller { @@ -812,7 +814,6 @@ TEST_F(PaintLayerScrollableAreaTest, SlimmingPaintV2ScrollDoesNotInvalidate) { <div id='forceScroll' style='height: 2000px;'></div> </div> )HTML"); - GetDocument().View()->UpdateAllLifecyclePhases(); auto* scroller = GetLayoutObjectByElementId("scroller"); auto* scrollable_area = ToLayoutBoxModelObject(scroller)->GetScrollableArea(); @@ -826,15 +827,13 @@ TEST_F(PaintLayerScrollableAreaTest, SlimmingPaintV2ScrollDoesNotInvalidate) { scrollable_area->SetScrollOffset(ScrollOffset(0, 1), kProgrammaticScroll); EXPECT_FALSE(scroller->ShouldDoFullPaintInvalidation()); EXPECT_TRUE(scroller->NeedsPaintPropertyUpdate()); - GetDocument().View()->UpdateAllLifecyclePhases(); + UpdateAllLifecyclePhasesForTest(); EXPECT_EQ(FloatSize(0, 1), scrollable_area->GetScrollOffset()); EXPECT_NE(nullptr, properties->ScrollTranslation()); } -TEST_F(PaintLayerScrollableAreaTest, - SlimmingPaintV2ScrollWithLocalBackgroundAttachment) { - ScopedSlimmingPaintV2ForTest enabler(true); - +TEST_P(PaintLayerScrollableAreaTest, + ScrollWithLocalAttachmentBackgroundInScrollingContents) { SetBodyInnerHTML(R"HTML( <style> #scroller { @@ -849,23 +848,119 @@ TEST_F(PaintLayerScrollableAreaTest, <div id='forceScroll' style='height: 2000px;'></div> </div> )HTML"); - GetDocument().View()->UpdateAllLifecyclePhases(); - auto* scroller = GetLayoutObjectByElementId("scroller"); - auto* scrollable_area = ToLayoutBoxModelObject(scroller)->GetScrollableArea(); + auto* scroller = ToLayoutBox(GetLayoutObjectByElementId("scroller")); + auto* scrollable_area = scroller->GetScrollableArea(); + EXPECT_EQ(kBackgroundPaintInScrollingContents, + scroller->GetBackgroundPaintLocation()); + + // Programmatically changing the scroll offset. + scrollable_area->SetScrollOffset(ScrollOffset(0, 1), kProgrammaticScroll); + if (RuntimeEnabledFeatures::SlimmingPaintV2Enabled()) { + // No invalidation because the background paints into scrolling contents. + EXPECT_FALSE(scroller->ShouldDoFullPaintInvalidation()); + EXPECT_FALSE(scroller->BackgroundNeedsFullPaintInvalidation()); + } else { + // Full invalidation because there is no separate scrolling contents layer. + EXPECT_TRUE(scroller->ShouldDoFullPaintInvalidation()); + EXPECT_TRUE(scroller->BackgroundNeedsFullPaintInvalidation()); + } + EXPECT_TRUE(scroller->NeedsPaintPropertyUpdate()); + UpdateAllLifecyclePhasesForTest(); + EXPECT_EQ(FloatSize(0, 1), scrollable_area->GetScrollOffset()); + const auto* properties = scroller->FirstFragment().PaintProperties(); + EXPECT_NE(nullptr, properties->ScrollTranslation()); +} + +TEST_P(PaintLayerScrollableAreaTest, + ScrollWithLocalAttachmentBackgroundInMainLayer) { + SetBodyInnerHTML(R"HTML( + <style> + #scroller { + overflow: scroll; + height: 200px; + width: 200px; + border: 10px dashed black; + background: linear-gradient(black, white) local, yellow; + } + </style> + <div id='scroller'> + <div id='forceScroll' style='height: 2000px;'></div> + </div> + )HTML"); - // Programmatically changing the scroll offset should require paint - // invalidation due to background attachment. + auto* scroller = ToLayoutBox(GetLayoutObjectByElementId("scroller")); + auto* scrollable_area = scroller->GetScrollableArea(); + EXPECT_EQ( + kBackgroundPaintInGraphicsLayer | kBackgroundPaintInScrollingContents, + scroller->GetBackgroundPaintLocation()); + + // Programmatically changing the scroll offset. scrollable_area->SetScrollOffset(ScrollOffset(0, 1), kProgrammaticScroll); + // No invalidation because the background paints into the main layer. EXPECT_TRUE(scroller->ShouldDoFullPaintInvalidation()); + EXPECT_TRUE(scroller->BackgroundNeedsFullPaintInvalidation()); EXPECT_TRUE(scroller->NeedsPaintPropertyUpdate()); - GetDocument().View()->UpdateAllLifecyclePhases(); + UpdateAllLifecyclePhasesForTest(); EXPECT_EQ(FloatSize(0, 1), scrollable_area->GetScrollOffset()); const auto* properties = scroller->FirstFragment().PaintProperties(); EXPECT_NE(nullptr, properties->ScrollTranslation()); } -TEST_F(PaintLayerScrollableAreaTest, HitTestOverlayScrollbars) { +TEST_P(PaintLayerScrollableAreaTest, ViewScrollWithFixedAttachmentBackground) { + SetBodyInnerHTML(R"HTML( + <style> + html, #fixed-background { + background: linear-gradient(black, white) fixed; + } + #fixed-background { + width: 200px; + height: 200px; + overflow: scroll; + } + </style> + <div id="fixed-background"> + <div style="height: 3000px"></div> + </div> + <div style="height: 3000px"></div> + )HTML"); + + auto* fixed_background_div = + ToLayoutBox(GetLayoutObjectByElementId("fixed-background")); + auto* div_scrollable_area = fixed_background_div->GetScrollableArea(); + auto* view_scrollable_area = GetLayoutView().GetScrollableArea(); + + // Programmatically changing the view's scroll offset. Should invalidate all + // objects with fixed attachment background. + view_scrollable_area->SetScrollOffset(ScrollOffset(0, 1), + kProgrammaticScroll); + EXPECT_TRUE(fixed_background_div->ShouldDoFullPaintInvalidation()); + EXPECT_TRUE(fixed_background_div->BackgroundNeedsFullPaintInvalidation()); + EXPECT_FALSE(fixed_background_div->NeedsPaintPropertyUpdate()); + if (RuntimeEnabledFeatures::SlimmingPaintV2Enabled()) { + // In SPv2, we assume the view's fixed attachment background is composited + // at this time and doesn't need paint invalidation on view scroll. + EXPECT_FALSE(GetLayoutView().ShouldDoFullPaintInvalidation()); + EXPECT_FALSE(GetLayoutView().BackgroundNeedsFullPaintInvalidation()); + } else { + EXPECT_TRUE(GetLayoutView().ShouldDoFullPaintInvalidation()); + EXPECT_TRUE(GetLayoutView().BackgroundNeedsFullPaintInvalidation()); + } + EXPECT_TRUE(GetLayoutView().NeedsPaintPropertyUpdate()); + UpdateAllLifecyclePhasesForTest(); + + // Programmatically changing the div's scroll offset. Should invalidate the + // scrolled div with fixed attachment background. + div_scrollable_area->SetScrollOffset(ScrollOffset(0, 1), kProgrammaticScroll); + EXPECT_TRUE(fixed_background_div->ShouldDoFullPaintInvalidation()); + EXPECT_TRUE(fixed_background_div->BackgroundNeedsFullPaintInvalidation()); + EXPECT_TRUE(fixed_background_div->NeedsPaintPropertyUpdate()); + EXPECT_FALSE(GetLayoutView().ShouldDoFullPaintInvalidation()); + EXPECT_FALSE(GetLayoutView().BackgroundNeedsFullPaintInvalidation()); + EXPECT_TRUE(GetLayoutView().NeedsPaintPropertyUpdate()); +} + +TEST_P(PaintLayerScrollableAreaTest, HitTestOverlayScrollbars) { SetBodyInnerHTML(R"HTML( <style> html, body { @@ -883,7 +978,7 @@ TEST_F(PaintLayerScrollableAreaTest, HitTestOverlayScrollbars) { </style> <div id='scroller'><div id='scrolled'></div></div> )HTML"); - GetDocument().View()->UpdateAllLifecyclePhases(); + auto* scroller = GetLayoutObjectByElementId("scroller"); auto* scrollable_area = ToLayoutBoxModelObject(scroller)->GetScrollableArea(); @@ -911,7 +1006,7 @@ TEST_F(PaintLayerScrollableAreaTest, HitTestOverlayScrollbars) { EXPECT_EQ(hit_result.GetScrollbar(), scrollable_area->HorizontalScrollbar()); } -TEST_F(PaintLayerScrollableAreaTest, CompositedStickyDescendant) { +TEST_P(PaintLayerScrollableAreaTest, CompositedStickyDescendant) { SetBodyInnerHTML(R"HTML( <div id=scroller style="overflow: scroll; width: 500px; height: 300px; will-change: transform"> @@ -923,7 +1018,8 @@ TEST_F(PaintLayerScrollableAreaTest, CompositedStickyDescendant) { auto* scroller = ToLayoutBoxModelObject(GetLayoutObjectByElementId("scroller")); auto* scrollable_area = scroller->GetScrollableArea(); - EXPECT_EQ(kPaintsIntoOwnBacking, scroller->Layer()->GetCompositingState()); + if (!RuntimeEnabledFeatures::SlimmingPaintV2Enabled()) + EXPECT_EQ(kPaintsIntoOwnBacking, scroller->Layer()->GetCompositingState()); auto* sticky = ToLayoutBoxModelObject(GetLayoutObjectByElementId("sticky")); EXPECT_EQ(FloatSize(0, 0), sticky->FirstFragment() @@ -933,7 +1029,7 @@ TEST_F(PaintLayerScrollableAreaTest, CompositedStickyDescendant) { .To2DTranslation()); scrollable_area->SetScrollOffset(ScrollOffset(0, 50), kUserScroll); - GetDocument().View()->UpdateAllLifecyclePhases(); + UpdateAllLifecyclePhasesForTest(); EXPECT_EQ(FloatSize(0, 50), sticky->FirstFragment() .LocalBorderBoxProperties() @@ -943,7 +1039,7 @@ TEST_F(PaintLayerScrollableAreaTest, CompositedStickyDescendant) { } // Delayed scroll offset clamping should not crash. https://crbug.com/842495 -TEST_F(PaintLayerScrollableAreaTest, IgnoreDelayedScrollOnDestroyedLayer) { +TEST_P(PaintLayerScrollableAreaTest, IgnoreDelayedScrollOnDestroyedLayer) { SetBodyInnerHTML(R"HTML( <div id=scroller style="overflow: scroll; width: 200px; height: 200px;"> <div style="height: 1000px;"></div> @@ -955,11 +1051,11 @@ TEST_F(PaintLayerScrollableAreaTest, IgnoreDelayedScrollOnDestroyedLayer) { PaintLayerScrollableArea::DelayScrollOffsetClampScope::SetNeedsClamp( scroller->GetLayoutBox()->GetScrollableArea()); scroller->SetInlineStyleProperty(CSSPropertyDisplay, CSSValueNone); - GetDocument().View()->UpdateAllLifecyclePhases(); + UpdateAllLifecyclePhasesForTest(); } } -TEST_F(PaintLayerScrollableAreaTest, ScrollbarMaximum) { +TEST_P(PaintLayerScrollableAreaTest, ScrollbarMaximum) { SetBodyInnerHTML(R"HTML( <style> #spacer { @@ -987,7 +1083,7 @@ TEST_F(PaintLayerScrollableAreaTest, ScrollbarMaximum) { Scrollbar* scrollbar = scrollable_area->VerticalScrollbar(); scrollable_area->ScrollBy(ScrollOffset(0, 1000), kProgrammaticScroll); - GetDocument().View()->UpdateAllLifecyclePhases(); + UpdateAllLifecyclePhasesForTest(); EXPECT_EQ(scrollbar->CurrentPos(), scrollbar->Maximum()); } diff --git a/chromium/third_party/blink/renderer/core/paint/paint_layer_stacking_node.cc b/chromium/third_party/blink/renderer/core/paint/paint_layer_stacking_node.cc index a38e9efbc1d..fd5c1149ce0 100644 --- a/chromium/third_party/blink/renderer/core/paint/paint_layer_stacking_node.cc +++ b/chromium/third_party/blink/renderer/core/paint/paint_layer_stacking_node.cc @@ -50,6 +50,7 @@ #include "third_party/blink/public/platform/platform.h" #include "third_party/blink/renderer/core/layout/layout_multi_column_flow_thread.h" #include "third_party/blink/renderer/core/layout/layout_view.h" +#include "third_party/blink/renderer/core/paint/compositing/composited_layer_mapping.h" #include "third_party/blink/renderer/core/paint/compositing/paint_layer_compositor.h" #include "third_party/blink/renderer/core/paint/paint_layer.h" @@ -129,8 +130,19 @@ void PaintLayerStackingNode::DirtyZOrderLists() { void PaintLayerStackingNode::DirtyStackingContextZOrderLists( PaintLayer* layer) { if (PaintLayerStackingNode* stacking_node = - AncestorStackingContextNode(layer)) + AncestorStackingContextNode(layer)) { + // This invalidation code intentionally refers to stale state. + DisableCompositingQueryAsserts disabler; + + // Changes of stacking may result in graphics layers changing size + // due to new contents painting into them. + PaintLayer* ancestor_layer = stacking_node->Layer(); + if (auto* mapping = ancestor_layer->GetCompositedLayerMapping()) { + mapping->SetNeedsGraphicsLayerUpdate(kGraphicsLayerUpdateSubtree); + } + stacking_node->DirtyZOrderLists(); + } } void PaintLayerStackingNode::RebuildZOrderLists() { diff --git a/chromium/third_party/blink/renderer/core/paint/paint_layer_test.cc b/chromium/third_party/blink/renderer/core/paint/paint_layer_test.cc index a56863c2650..115dc3c2ba2 100644 --- a/chromium/third_party/blink/renderer/core/paint/paint_layer_test.cc +++ b/chromium/third_party/blink/renderer/core/paint/paint_layer_test.cc @@ -23,11 +23,6 @@ class PaintLayerTest : public PaintTestConfigurations, public RenderingTest { RenderingTest::SetUp(); EnableCompositing(); } - - protected: - PaintLayer* GetPaintLayerByElementId(const char* id) { - return ToLayoutBoxModelObject(GetLayoutObjectByElementId(id))->Layer(); - } }; INSTANTIATE_PAINT_TEST_CASE_P(PaintLayerTest); @@ -255,7 +250,7 @@ TEST_P(PaintLayerTest, CompositedScrollingNoNeedsRepaint) { EXPECT_EQ(LayoutPoint(-1000, -1000), content_layer->Location()); EXPECT_FALSE(content_layer->NeedsRepaint()); EXPECT_FALSE(scroll_layer->NeedsRepaint()); - GetDocument().View()->UpdateAllLifecyclePhases(); + UpdateAllLifecyclePhasesForTest(); } TEST_P(PaintLayerTest, NonCompositedScrollingNeedsRepaint) { @@ -285,7 +280,7 @@ TEST_P(PaintLayerTest, NonCompositedScrollingNeedsRepaint) { EXPECT_EQ(LayoutPoint(-1000, -1000), content_layer->Location()); EXPECT_TRUE(scroll_layer->NeedsRepaint()); EXPECT_FALSE(content_layer->NeedsRepaint()); - GetDocument().View()->UpdateAllLifecyclePhases(); + UpdateAllLifecyclePhasesForTest(); } TEST_P(PaintLayerTest, HasNonIsolatedDescendantWithBlendMode) { @@ -324,9 +319,9 @@ TEST_P(PaintLayerTest, HasStickyPositionDescendant) { EXPECT_TRUE(parent->HasStickyPositionDescendant()); EXPECT_FALSE(child->HasStickyPositionDescendant()); - GetDocument().getElementById("child")->setAttribute(HTMLNames::styleAttr, + GetDocument().getElementById("child")->setAttribute(html_names::kStyleAttr, "position: relative"); - GetDocument().View()->UpdateAllLifecyclePhases(); + UpdateAllLifecyclePhasesForTest(); EXPECT_FALSE(parent->HasStickyPositionDescendant()); EXPECT_FALSE(child->HasStickyPositionDescendant()); @@ -344,9 +339,9 @@ TEST_P(PaintLayerTest, HasFixedPositionDescendant) { EXPECT_TRUE(parent->HasFixedPositionDescendant()); EXPECT_FALSE(child->HasFixedPositionDescendant()); - GetDocument().getElementById("child")->setAttribute(HTMLNames::styleAttr, + GetDocument().getElementById("child")->setAttribute(html_names::kStyleAttr, "position: relative"); - GetDocument().View()->UpdateAllLifecyclePhases(); + UpdateAllLifecyclePhasesForTest(); EXPECT_FALSE(parent->HasFixedPositionDescendant()); EXPECT_FALSE(child->HasFixedPositionDescendant()); @@ -371,9 +366,9 @@ TEST_P(PaintLayerTest, HasFixedAndStickyPositionDescendant) { EXPECT_FALSE(child1->HasStickyPositionDescendant()); EXPECT_FALSE(child2->HasStickyPositionDescendant()); - GetDocument().getElementById("child1")->setAttribute(HTMLNames::styleAttr, + GetDocument().getElementById("child1")->setAttribute(html_names::kStyleAttr, "position: relative"); - GetDocument().View()->UpdateAllLifecyclePhases(); + UpdateAllLifecyclePhasesForTest(); EXPECT_TRUE(parent->HasFixedPositionDescendant()); EXPECT_FALSE(child1->HasFixedPositionDescendant()); @@ -382,9 +377,9 @@ TEST_P(PaintLayerTest, HasFixedAndStickyPositionDescendant) { EXPECT_FALSE(child1->HasStickyPositionDescendant()); EXPECT_FALSE(child2->HasStickyPositionDescendant()); - GetDocument().getElementById("child2")->setAttribute(HTMLNames::styleAttr, + GetDocument().getElementById("child2")->setAttribute(html_names::kStyleAttr, "position: relative"); - GetDocument().View()->UpdateAllLifecyclePhases(); + UpdateAllLifecyclePhasesForTest(); EXPECT_FALSE(parent->HasFixedPositionDescendant()); EXPECT_FALSE(child1->HasFixedPositionDescendant()); @@ -406,16 +401,16 @@ TEST_P(PaintLayerTest, HasNonContainedAbsolutePositionDescendant) { EXPECT_FALSE(parent->HasNonContainedAbsolutePositionDescendant()); EXPECT_FALSE(child->HasNonContainedAbsolutePositionDescendant()); - GetDocument().getElementById("child")->setAttribute(HTMLNames::styleAttr, + GetDocument().getElementById("child")->setAttribute(html_names::kStyleAttr, "position: absolute"); - GetDocument().View()->UpdateAllLifecyclePhases(); + UpdateAllLifecyclePhasesForTest(); EXPECT_TRUE(parent->HasNonContainedAbsolutePositionDescendant()); EXPECT_FALSE(child->HasNonContainedAbsolutePositionDescendant()); - GetDocument().getElementById("parent")->setAttribute(HTMLNames::styleAttr, + GetDocument().getElementById("parent")->setAttribute(html_names::kStyleAttr, "position: relative"); - GetDocument().View()->UpdateAllLifecyclePhases(); + UpdateAllLifecyclePhasesForTest(); EXPECT_FALSE(parent->HasNonContainedAbsolutePositionDescendant()); EXPECT_FALSE(child->HasNonContainedAbsolutePositionDescendant()); } @@ -565,8 +560,8 @@ TEST_P(PaintLayerTest, SubsequenceCachingStackingContexts) { GetDocument() .getElementById("grandchild1") - ->setAttribute(HTMLNames::styleAttr, "isolation: isolate"); - GetDocument().View()->UpdateAllLifecyclePhases(); + ->setAttribute(html_names::kStyleAttr, "isolation: isolate"); + UpdateAllLifecyclePhasesForTest(); EXPECT_FALSE(parent->SupportsSubsequenceCaching()); EXPECT_FALSE(child1->SupportsSubsequenceCaching()); @@ -612,9 +607,9 @@ TEST_P(PaintLayerTest, NegativeZIndexChangeToPositive) { EXPECT_TRUE(target->StackingNode()->HasNegativeZOrderList()); EXPECT_FALSE(target->StackingNode()->HasPositiveZOrderList()); - GetDocument().getElementById("child")->setAttribute(HTMLNames::styleAttr, + GetDocument().getElementById("child")->setAttribute(html_names::kStyleAttr, "z-index: 1"); - GetDocument().View()->UpdateAllLifecyclePhases(); + UpdateAllLifecyclePhasesForTest(); EXPECT_FALSE(target->StackingNode()->HasNegativeZOrderList()); EXPECT_TRUE(target->StackingNode()->HasPositiveZOrderList()); @@ -682,8 +677,8 @@ TEST_P(PaintLayerTest, Has3DTransformedDescendantChangeStyle) { EXPECT_FALSE(child->Has3DTransformedDescendant()); GetDocument().getElementById("child")->setAttribute( - HTMLNames::styleAttr, "transform: translateZ(1px)"); - GetDocument().View()->UpdateAllLifecyclePhases(); + html_names::kStyleAttr, "transform: translateZ(1px)"); + UpdateAllLifecyclePhasesForTest(); EXPECT_TRUE(parent->Has3DTransformedDescendant()); EXPECT_FALSE(child->Has3DTransformedDescendant()); @@ -738,8 +733,8 @@ TEST_P(PaintLayerTest, DescendantDependentFlagsStopsAtThrottledFrames) { // Move the child frame offscreen so it becomes available for throttling. auto* iframe = ToHTMLIFrameElement(GetDocument().getElementById("iframe")); - iframe->setAttribute(HTMLNames::styleAttr, "transform: translateY(5555px)"); - GetDocument().View()->UpdateAllLifecyclePhases(); + iframe->setAttribute(html_names::kStyleAttr, "transform: translateY(5555px)"); + UpdateAllLifecyclePhasesForTest(); // Ensure intersection observer notifications get delivered. test::RunPendingTasks(); EXPECT_FALSE(GetDocument().View()->IsHiddenForThrottling()); @@ -765,7 +760,7 @@ TEST_P(PaintLayerTest, DescendantDependentFlagsStopsAtThrottledFrames) { // Also check that the rest of the lifecycle succeeds without crashing due // to a stale m_needsDescendantDependentFlagsUpdate. - GetDocument().View()->UpdateAllLifecyclePhases(); + UpdateAllLifecyclePhasesForTest(); // Still dirty, because the frame was throttled. EXPECT_TRUE(ChildDocument() @@ -775,7 +770,7 @@ TEST_P(PaintLayerTest, DescendantDependentFlagsStopsAtThrottledFrames) { ->needs_descendant_dependent_flags_update_); } - GetDocument().View()->UpdateAllLifecyclePhases(); + UpdateAllLifecyclePhasesForTest(); EXPECT_FALSE(ChildDocument() .View() ->GetLayoutView() @@ -808,7 +803,7 @@ TEST_P(PaintLayerTest, PaintInvalidationOnNonCompositedScroll) { scroller->GetScrollableArea()->SetScrollOffset(ScrollOffset(0, 20), kProgrammaticScroll); - GetDocument().View()->UpdateAllLifecyclePhases(); + UpdateAllLifecyclePhasesForTest(); EXPECT_EQ(LayoutRect(0, 30, 50, 10), content_layer->FirstFragment().VisualRect()); EXPECT_EQ(LayoutRect(0, 30, 50, 5), content->FirstFragment().VisualRect()); @@ -837,7 +832,7 @@ TEST_P(PaintLayerTest, PaintInvalidationOnCompositedScroll) { scroller->GetScrollableArea()->SetScrollOffset(ScrollOffset(0, 20), kProgrammaticScroll); - GetDocument().View()->UpdateAllLifecyclePhases(); + UpdateAllLifecyclePhasesForTest(); EXPECT_EQ(LayoutRect(0, 30, 50, 10), content_layer->FirstFragment().VisualRect()); EXPECT_EQ(LayoutRect(0, 30, 50, 5), content->FirstFragment().VisualRect()); @@ -1353,7 +1348,7 @@ TEST_P(PaintLayerTest, NeedsRepaintOnSelfPaintingStatusChange) { // Removing column-width: 10px makes target layer no longer self-painting, // and change its compositing container. The original compositing container // span_layer should be marked NeedsRepaint. - target_element->setAttribute(HTMLNames::styleAttr, + target_element->setAttribute(html_names::kStyleAttr, "overflow: hidden; float: left"); GetDocument().View()->UpdateAllLifecyclePhasesExceptPaint(); EXPECT_FALSE(target_layer->IsSelfPaintingLayer()); @@ -1361,7 +1356,7 @@ TEST_P(PaintLayerTest, NeedsRepaintOnSelfPaintingStatusChange) { EXPECT_TRUE(target_layer->NeedsRepaint()); EXPECT_TRUE(target_layer->CompositingContainer()->NeedsRepaint()); EXPECT_TRUE(span_layer->NeedsRepaint()); - GetDocument().View()->UpdateAllLifecyclePhases(); + UpdateAllLifecyclePhasesForTest(); } TEST_P(PaintLayerTest, NeedsRepaintOnRemovingStackedLayer) { @@ -1381,15 +1376,15 @@ TEST_P(PaintLayerTest, NeedsRepaintOnRemovingStackedLayer) { EXPECT_NE(body_layer, target_layer->CompositingContainer()); auto* old_compositing_container = target_layer->CompositingContainer(); - body->setAttribute(HTMLNames::styleAttr, "margin-top: 0"); - target_element->setAttribute(HTMLNames::styleAttr, "top: 0"); + body->setAttribute(html_names::kStyleAttr, "margin-top: 0"); + target_element->setAttribute(html_names::kStyleAttr, "top: 0"); GetDocument().View()->UpdateAllLifecyclePhasesExceptPaint(); EXPECT_FALSE(target_object->HasLayer()); EXPECT_TRUE(body_layer->NeedsRepaint()); EXPECT_TRUE(old_compositing_container->NeedsRepaint()); - GetDocument().View()->UpdateAllLifecyclePhases(); + UpdateAllLifecyclePhasesForTest(); } TEST_P(PaintLayerTest, FrameViewContentSize) { @@ -1488,7 +1483,7 @@ TEST_P(PaintLayerTest, SquashingOffsets) { GetDocument().View()->LayoutViewport()->ScrollBy(ScrollOffset(0, 25), kUserScroll); - GetDocument().View()->UpdateAllLifecyclePhases(); + UpdateAllLifecyclePhasesForTest(); PaintLayer::MapPointInPaintInvalidationContainerToBacking( squashed->GetLayoutObject(), point); @@ -1742,4 +1737,46 @@ TEST_P(PaintLayerTest, HitTestFirstLetterPseudoElementDisplayContents) { result.InnerPossiblyPseudoNode()); } +TEST_P(PaintLayerTest, BackgroundIsKnownToBeOpaqueInRectChildren) { + SetBodyInnerHTML(R"HTML( + <style> + div { + width: 100px; + height: 100px; + position: relative; + isolation: isolate; + } + </style> + <div id='target'> + <div style='background: blue'></div> + </div> + )HTML"); + + PaintLayer* target_layer = GetPaintLayerByElementId("target"); + EXPECT_TRUE(target_layer->BackgroundIsKnownToBeOpaqueInRect( + LayoutRect(0, 0, 100, 100), true)); + EXPECT_FALSE(target_layer->BackgroundIsKnownToBeOpaqueInRect( + LayoutRect(0, 0, 100, 100), false)); +} + +TEST_P(PaintLayerTest, ChangeAlphaNeedsCompositingInputs) { + SetBodyInnerHTML(R"HTML( + <style> + #target { + background: white; + width: 100px; + height: 100px; + position: relative; + } + </style> + <div id='target'> + </div> + )HTML"); + PaintLayer* target = GetPaintLayerByElementId("target"); + StyleDifference diff; + diff.SetHasAlphaChanged(); + target->StyleDidChange(diff, target->GetLayoutObject().Style()); + EXPECT_TRUE(target->NeedsCompositingInputsUpdate()); +} + } // namespace blink diff --git a/chromium/third_party/blink/renderer/core/paint/paint_property_tree_builder.cc b/chromium/third_party/blink/renderer/core/paint/paint_property_tree_builder.cc index 5142a25165c..1c65f407cff 100644 --- a/chromium/third_party/blink/renderer/core/paint/paint_property_tree_builder.cc +++ b/chromium/third_party/blink/renderer/core/paint/paint_property_tree_builder.cc @@ -98,7 +98,7 @@ void VisualViewportPaintPropertyTreeBuilder::Update( context.fixed_position.scroll = visual_viewport.GetScrollNode(); #if DCHECK_IS_ON() - PaintPropertyTreePrinter::UpdateDebugNames(visual_viewport); + paint_property_tree_printer::UpdateDebugNames(visual_viewport); #endif } @@ -142,7 +142,7 @@ class FragmentPaintPropertyTreeBuilder { } #if DCHECK_IS_ON() if (properties_) - PaintPropertyTreePrinter::UpdateDebugNames(object_, *properties_); + paint_property_tree_printer::UpdateDebugNames(object_, *properties_); #endif } @@ -322,7 +322,8 @@ static bool NeedsIsolationNodes(const LayoutObject& object) { // Layout view establishes isolation with the exception of local roots (since // they are already essentially isolated). - if (object.IsLayoutView()) { + if (RuntimeEnabledFeatures::LayoutViewIsolationNodesEnabled() && + object.IsLayoutView()) { const auto* parent_frame = object.GetFrame()->Tree().Parent(); return parent_frame && parent_frame->IsLocalFrame(); } @@ -588,8 +589,14 @@ static CompositingReasons CompositingReasonsForTransform(const LayoutBox& box) { if (CompositingReasonFinder::RequiresCompositingForTransform(box)) compositing_reasons |= CompositingReason::k3DTransform; - if (CompositingReasonFinder::RequiresCompositingForTransformAnimation(style)) - compositing_reasons |= CompositingReason::kActiveTransformAnimation; + // Currently, we create transform nodes for an element whenever any property + // is being animated so that the existence of the effect node implies the + // existence of all nodes. + // TODO(flackr): Check for nodes for each KeyframeModel target + // property instead of creating all nodes and only create a transform node + // if needed, https://crbug.com/900241 + compositing_reasons |= + CompositingReasonFinder::CompositingReasonsForAnimation(style); if (style.HasWillChangeCompositingHint() && !style.SubtreeWillChangeContents()) @@ -681,7 +688,8 @@ void FragmentPaintPropertyTreeBuilder::UpdateTransform() { ? TransformPaintPropertyNode::BackfaceVisibility::kHidden : TransformPaintPropertyNode::BackfaceVisibility::kVisible; state.compositor_element_id = CompositorElementIdFromUniqueObjectId( - object_.UniqueId(), CompositorElementIdNamespace::kPrimary); + object_.UniqueId(), + CompositorElementIdNamespace::kPrimaryTransform); } OnUpdate(properties_->UpdateTransform(*context_.current.transform, @@ -769,7 +777,13 @@ static bool NeedsEffect(const LayoutObject& object) { if (style.Opacity() != 1.0f || style.HasWillChangeOpacityHint()) return true; - if (CompositingReasonFinder::RequiresCompositingForOpacityAnimation(style)) + // Currently, we create effect nodes for an element whenever any property + // is being animated so that the existence of the effect node implies the + // existence of all nodes. + // TODO(flackr): Check for nodes for each KeyframeModel target + // property instead of creating all nodes and only create an effect node + // if needed, https://crbug.com/900241 + if (CompositingReasonFinder::CompositingReasonsForAnimation(style)) return true; if (object.StyleRef().HasMask()) @@ -883,13 +897,25 @@ void FragmentPaintPropertyTreeBuilder::UpdateEffect() { // We may begin to composite our subtree prior to an animation starts, // but a compositor element ID is only needed when an animation is // current. - if (CompositingReasonFinder::RequiresCompositingForOpacityAnimation( - style)) { - state.direct_compositing_reasons = - CompositingReason::kActiveOpacityAnimation; + // + // Currently, we use the existence of this id to check if effect nodes + // have been created for animations on this element. + // TODO(flackr): Check for nodes for each KeyframeModel target + // property instead of creating all nodes and create each type of + // node as needed, https://crbug.com/900241 + state.direct_compositing_reasons = + CompositingReasonFinder::CompositingReasonsForAnimation(style); + if (state.direct_compositing_reasons) { + state.compositor_element_id = CompositorElementIdFromUniqueObjectId( + object_.UniqueId(), CompositorElementIdNamespace::kPrimaryEffect); + } else { + // The effect node CompositorElementId is used to uniquely identify + // renderpasses so even if we don't need one for animations we still + // need to set an id. Using kPrimary avoids confusing cc::Animation + // into thinking the element has been composited for animations. + state.compositor_element_id = CompositorElementIdFromUniqueObjectId( + object_.UniqueId(), CompositorElementIdNamespace::kPrimary); } - state.compositor_element_id = CompositorElementIdFromUniqueObjectId( - object_.UniqueId(), CompositorElementIdNamespace::kPrimary); } OnUpdate(properties_->UpdateEffect(*context_.current_effect, std::move(state))); @@ -984,15 +1010,18 @@ void FragmentPaintPropertyTreeBuilder::UpdateLinkHighlightEffect() { } static bool NeedsFilter(const LayoutObject& object) { + // Currently, we create filter nodes for an element whenever any property + // is being animated so that the existence of the effect node implies the + // existence of all animation nodes. + // TODO(flackr): Check for nodes for each KeyframeModel target + // property instead of creating all nodes and only create a filter node + // if needed, https://crbug.com/900241 // TODO(trchen): SVG caches filters in SVGResources. Implement it. - if (object.IsBoxModelObject() && ToLayoutBoxModelObject(object).Layer() && - (object.StyleRef().HasFilter() || object.HasReflection() || - CompositingReasonFinder::RequiresCompositingForFilterAnimation( - object.StyleRef()))) - return true; - if (object.IsLayoutImage() && ToLayoutImage(object).ShouldInvertColor()) - return true; - return false; + return (object.IsBoxModelObject() && ToLayoutBoxModelObject(object).Layer() && + (object.StyleRef().HasFilter() || object.HasReflection() || + object.HasBackdropFilter() || + CompositingReasonFinder::CompositingReasonsForAnimation( + object.StyleRef()))); } void FragmentPaintPropertyTreeBuilder::UpdateFilter() { @@ -1005,23 +1034,15 @@ void FragmentPaintPropertyTreeBuilder::UpdateFilter() { state.local_transform_space = context_.current.transform; state.filters_origin = FloatPoint(context_.current.paint_offset); - auto* layer = ToLayoutBoxModelObject(object_).Layer(); - if (layer) { + if (auto* layer = ToLayoutBoxModelObject(object_).Layer()) { // Try to use the cached filter. if (properties_->Filter()) state.filter = properties_->Filter()->Filter(); - if (object_.IsLayoutImage() && - ToLayoutImage(object_).ShouldInvertColor()) - state.filter.AppendInvertFilter(1.0f); - layer->UpdateCompositorFilterOperationsForFilter(state.filter); + layer->UpdateCompositorFilterOperationsForBackdropFilter( + state.backdrop_filter); layer->ClearFilterOnEffectNodeDirty(); - } else { - DCHECK(object_.IsLayoutImage() && - ToLayoutImage(object_).ShouldInvertColor()); - state.filter = CompositorFilterOperations(); - state.filter.AppendInvertFilter(1.0f); } // The CSS filter spec didn't specify how filters interact with overflow @@ -1053,11 +1074,10 @@ void FragmentPaintPropertyTreeBuilder::UpdateFilter() { // We may begin to composite our subtree prior to an animation starts, // but a compositor element ID is only needed when an animation is // current. + // TODO(flackr): Only set a compositing reason for filter animation + // once we no longer need to create all nodes, https://crbug.com/900241 state.direct_compositing_reasons = - CompositingReasonFinder::RequiresCompositingForFilterAnimation( - style) - ? CompositingReason::kActiveFilterAnimation - : CompositingReason::kNone; + CompositingReasonFinder::CompositingReasonsForAnimation(style); DCHECK(!style.HasCurrentFilterAnimation() || state.direct_compositing_reasons != CompositingReason::kNone); @@ -1227,10 +1247,7 @@ void FragmentPaintPropertyTreeBuilder::UpdateLocalBorderBoxContext() { if (!NeedsPaintPropertyUpdate()) return; - if (!object_.HasLayer() && !NeedsPaintOffsetTranslation(object_) && - !NeedsFilter(object_) && !NeedsOverflowClip(object_)) { - fragment_data_.ClearLocalBorderBoxProperties(); - } else { + if (object_.HasLayer() || properties_) { PropertyTreeState local_border_box = PropertyTreeState(context_.current.transform, context_.current.clip, context_.current_effect); @@ -1240,6 +1257,8 @@ void FragmentPaintPropertyTreeBuilder::UpdateLocalBorderBoxContext() { property_added_or_removed_ = true; fragment_data_.SetLocalBorderBoxProperties(std::move(local_border_box)); + } else { + fragment_data_.ClearLocalBorderBoxProperties(); } } diff --git a/chromium/third_party/blink/renderer/core/paint/paint_property_tree_builder_test.cc b/chromium/third_party/blink/renderer/core/paint/paint_property_tree_builder_test.cc index 44122e77bcd..69b19f58c75 100644 --- a/chromium/third_party/blink/renderer/core/paint/paint_property_tree_builder_test.cc +++ b/chromium/third_party/blink/renderer/core/paint/paint_property_tree_builder_test.cc @@ -126,7 +126,8 @@ TEST_P(PaintPropertyTreeBuilderTest, FixedPosition) { transformed_scroll->setScrollTop(5); LocalFrameView* frame_view = GetDocument().View(); - frame_view->UpdateAllLifecyclePhases(); + frame_view->UpdateAllLifecyclePhases( + DocumentLifecycle::LifecycleUpdateReason::kTest); // target1 is a fixed-position element inside an absolute-position scrolling // element. It should be attached under the viewport to skip scrolling and @@ -212,7 +213,8 @@ TEST_P(PaintPropertyTreeBuilderTest, PositionAndScroll) { Element* scroller = GetDocument().getElementById("scroller"); scroller->scrollTo(0, 100); LocalFrameView* frame_view = GetDocument().View(); - frame_view->UpdateAllLifecyclePhases(); + frame_view->UpdateAllLifecyclePhases( + DocumentLifecycle::LifecycleUpdateReason::kTest); const ObjectPaintProperties* scroller_properties = scroller->GetLayoutObject()->FirstFragment().PaintProperties(); EXPECT_EQ(TransformationMatrix().Translate(0, -100), @@ -366,7 +368,7 @@ TEST_P(PaintPropertyTreeBuilderTest, OverflowScrollVerticalRL) { EXPECT_EQ(FloatRoundedRect(10, 10, 85, 85), overflow_clip->ClipRect()); scroller->GetScrollableArea()->ScrollBy(ScrollOffset(-100, 0), kUserScroll); - GetDocument().View()->UpdateAllLifecyclePhases(); + UpdateAllLifecyclePhasesForTest(); // Only scroll_translation is affected by scrolling. EXPECT_EQ(TransformationMatrix().Translate(-215, 0), @@ -420,7 +422,7 @@ TEST_P(PaintPropertyTreeBuilderTest, OverflowScrollRTL) { EXPECT_EQ(FloatRoundedRect(25, 10, 85, 85), overflow_clip->ClipRect()); scroller->GetScrollableArea()->ScrollBy(ScrollOffset(-100, 0), kUserScroll); - GetDocument().View()->UpdateAllLifecyclePhases(); + UpdateAllLifecyclePhasesForTest(); // Only scroll_translation is affected by scrolling. EXPECT_EQ(TransformationMatrix().Translate(-215, 0), @@ -478,7 +480,7 @@ TEST_P(PaintPropertyTreeBuilderTest, OverflowScrollVerticalRLMulticol) { ToLayoutBox(GetLayoutObjectByElementId("scroller")) ->GetScrollableArea() ->ScrollBy(ScrollOffset(-100, 200), kUserScroll); - GetDocument().View()->UpdateAllLifecyclePhases(); + UpdateAllLifecyclePhasesForTest(); check_fragments(); } @@ -488,7 +490,8 @@ TEST_P(PaintPropertyTreeBuilderTest, DocScrollingTraditional) { GetDocument().domWindow()->scrollTo(0, 100); LocalFrameView* frame_view = GetDocument().View(); - frame_view->UpdateAllLifecyclePhases(); + frame_view->UpdateAllLifecyclePhases( + DocumentLifecycle::LifecycleUpdateReason::kTest); EXPECT_EQ(TransformationMatrix(), DocPreTranslation()->Matrix()); EXPECT_EQ( GetDocument().GetPage()->GetVisualViewport().GetScrollTranslationNode(), @@ -558,8 +561,8 @@ TEST_P(PaintPropertyTreeBuilderTest, Perspective) { inner->GetLayoutObject(), GetDocument().View()->GetLayoutView()); - perspective->setAttribute(HTMLNames::styleAttr, "perspective: 200px"); - GetDocument().View()->UpdateAllLifecyclePhases(); + perspective->setAttribute(html_names::kStyleAttr, "perspective: 200px"); + UpdateAllLifecyclePhasesForTest(); EXPECT_EQ(TransformationMatrix().ApplyPerspective(200), perspective_properties->Perspective()->Matrix()); EXPECT_EQ(FloatPoint3D(250, 250, 0), @@ -574,8 +577,9 @@ TEST_P(PaintPropertyTreeBuilderTest, Perspective) { perspective_properties->Perspective()->Parent()); } - perspective->setAttribute(HTMLNames::styleAttr, "perspective-origin: 5% 20%"); - GetDocument().View()->UpdateAllLifecyclePhases(); + perspective->setAttribute(html_names::kStyleAttr, + "perspective-origin: 5% 20%"); + UpdateAllLifecyclePhasesForTest(); EXPECT_EQ(TransformationMatrix().ApplyPerspective(100), perspective_properties->Perspective()->Matrix()); EXPECT_EQ(FloatPoint3D(70, 160, 0), @@ -625,17 +629,17 @@ TEST_P(PaintPropertyTreeBuilderTest, Transform) { GetDocument().View()->GetLayoutView()); transform->setAttribute( - HTMLNames::styleAttr, + html_names::kStyleAttr, "margin-left: 50px; margin-top: 100px; width: 400px; height: 300px;"); - GetDocument().View()->UpdateAllLifecyclePhases(); + UpdateAllLifecyclePhasesForTest(); EXPECT_EQ(nullptr, transform->GetLayoutObject()->FirstFragment().PaintProperties()); transform->setAttribute( - HTMLNames::styleAttr, + html_names::kStyleAttr, "margin-left: 50px; margin-top: 100px; width: 400px; height: 300px; " "transform: translate3d(123px, 456px, 789px)"); - GetDocument().View()->UpdateAllLifecyclePhases(); + UpdateAllLifecyclePhasesForTest(); EXPECT_EQ(TransformationMatrix().Translate3d(123, 456, 789), transform->GetLayoutObject() ->FirstFragment() @@ -700,9 +704,12 @@ TEST_P(PaintPropertyTreeBuilderTest, } TEST_P(PaintPropertyTreeBuilderTest, - OpacityAnimationDoesNotCreateTransformNode) { + OpacityAnimationCreatesTransformAndFilterNodes) { LoadTestData("opacity-animation.html"); - EXPECT_EQ(nullptr, PaintPropertiesForElement("target")->Transform()); + // TODO(flackr): Verify that after https://crbug.com/900241 is fixed we no + // longer create transform or filter nodes for opacity animations. + EXPECT_NE(nullptr, PaintPropertiesForElement("target")->Transform()); + EXPECT_NE(nullptr, PaintPropertiesForElement("target")->Filter()); } TEST_P(PaintPropertyTreeBuilderTest, @@ -752,17 +759,17 @@ TEST_P(PaintPropertyTreeBuilderTest, WillChangeTransform) { GetDocument().View()->GetLayoutView()); transform->setAttribute( - HTMLNames::styleAttr, + html_names::kStyleAttr, "margin-left: 50px; margin-top: 100px; width: 400px; height: 300px;"); - GetDocument().View()->UpdateAllLifecyclePhases(); + UpdateAllLifecyclePhasesForTest(); EXPECT_EQ(nullptr, transform->GetLayoutObject()->FirstFragment().PaintProperties()); transform->setAttribute( - HTMLNames::styleAttr, + html_names::kStyleAttr, "margin-left: 50px; margin-top: 100px; width: 400px; height: 300px; " "will-change: transform"); - GetDocument().View()->UpdateAllLifecyclePhases(); + UpdateAllLifecyclePhasesForTest(); EXPECT_EQ(TransformationMatrix(), transform->GetLayoutObject() ->FirstFragment() .PaintProperties() @@ -1061,7 +1068,7 @@ TEST_P(PaintPropertyTreeBuilderTest, EffectNodesAcrossHTMLSVGBoundary) { TEST_P(PaintPropertyTreeBuilderTest, EffectNodesAcrossSVGHTMLBoundary) { SetBodyInnerHTML(R"HTML( <svg id='svgRootWithOpacity' style='opacity: 0.3;'> - <foreignObject id='foreignObjectWithOpacity' opacity='0.4'> + <foreignObject id='foreignObjectWithOpacity' opacity='0.4' style='overflow: visible;'> <body> <span id='spanWithOpacity' style='opacity: 0.5'/> </body> @@ -1624,7 +1631,7 @@ TEST_P(PaintPropertyTreeBuilderTest, ControlClipInsideForeignObject) { <div style='column-count:2;'> <div style='columns: 2'> <svg style='width: 500px; height: 500px;'> - <foreignObject> + <foreignObject style='overflow: visible;'> <input id='button' style='width:345px; height:123px' value='some text'/> </foreignObject> @@ -1751,7 +1758,8 @@ TEST_P(PaintPropertyTreeBuilderTest, TransformNodesAcrossSubframes) { )HTML"); LocalFrameView* frame_view = GetDocument().View(); - frame_view->UpdateAllLifecyclePhases(); + frame_view->UpdateAllLifecyclePhases( + DocumentLifecycle::LifecycleUpdateReason::kTest); LayoutObject* div_with_transform = GetLayoutObjectByElementId("divWithTransform"); @@ -1812,6 +1820,9 @@ TEST_P(PaintPropertyTreeBuilderTest, TransformNodesAcrossSubframes) { } TEST_P(PaintPropertyTreeBuilderTest, FramesEstablishIsolation) { + if (!RuntimeEnabledFeatures::LayoutViewIsolationNodesEnabled()) + return; + SetBodyInnerHTML(R"HTML( <style> body { margin: 0; } @@ -1842,7 +1853,8 @@ TEST_P(PaintPropertyTreeBuilderTest, FramesEstablishIsolation) { )HTML"); LocalFrameView* frame_view = GetDocument().View(); - frame_view->UpdateAllLifecyclePhases(); + frame_view->UpdateAllLifecyclePhases( + DocumentLifecycle::LifecycleUpdateReason::kTest); LayoutObject* frame = ChildFrame().View()->GetLayoutView(); const auto& frame_contents_properties = @@ -1899,9 +1911,10 @@ TEST_P(PaintPropertyTreeBuilderTest, FramesEstablishIsolation) { // This causes a tree topology change which forces the subtree to be updated. // However, isolation stops this recursion. - GetDocument().getElementById("parent")->setAttribute(HTMLNames::classAttr, + GetDocument().getElementById("parent")->setAttribute(html_names::kClassAttr, "transformed"); - frame_view->UpdateAllLifecyclePhases(); + frame_view->UpdateAllLifecyclePhases( + DocumentLifecycle::LifecycleUpdateReason::kTest); // Verify that our clobbered state is still clobbered. EXPECT_EQ(TransformationMatrix().Translate(123, 321), @@ -1938,7 +1951,8 @@ TEST_P(PaintPropertyTreeBuilderTest, TransformNodesInTransformedSubframes) { <div id='transform'></div> )HTML"); LocalFrameView* frame_view = GetDocument().View(); - frame_view->UpdateAllLifecyclePhases(); + frame_view->UpdateAllLifecyclePhases( + DocumentLifecycle::LifecycleUpdateReason::kTest); // Assert that we have the following tree structure: // ... @@ -3251,8 +3265,8 @@ TEST_P(PaintPropertyTreeBuilderTest, CachedProperties) { // Change transform of b. B's transform node should be a new node with the new // value, and a and c's transform nodes should be unchanged (with c's parent // adjusted). - b->setAttribute(HTMLNames::styleAttr, "transform: translate(111px, 222px)"); - GetDocument().View()->UpdateAllLifecyclePhases(); + b->setAttribute(html_names::kStyleAttr, "transform: translate(111px, 222px)"); + UpdateAllLifecyclePhasesForTest(); EXPECT_EQ(a_properties, a->GetLayoutObject()->FirstFragment().PaintProperties()); @@ -3280,8 +3294,8 @@ TEST_P(PaintPropertyTreeBuilderTest, CachedProperties) { // Remove transform from b. B's transform node should be removed from the // tree, and a and c's transform nodes should be unchanged (with c's parent // adjusted). - b->setAttribute(HTMLNames::styleAttr, ""); - GetDocument().View()->UpdateAllLifecyclePhases(); + b->setAttribute(html_names::kStyleAttr, ""); + UpdateAllLifecyclePhasesForTest(); EXPECT_EQ(a_properties, a->GetLayoutObject()->FirstFragment().PaintProperties()); @@ -3304,8 +3318,8 @@ TEST_P(PaintPropertyTreeBuilderTest, CachedProperties) { // Re-add transform to b. B's transform node should be inserted into the tree, // and a and c's transform nodes should be unchanged (with c's parent // adjusted). - b->setAttribute(HTMLNames::styleAttr, "transform: translate(4px, 5px)"); - GetDocument().View()->UpdateAllLifecyclePhases(); + b->setAttribute(html_names::kStyleAttr, "transform: translate(4px, 5px)"); + UpdateAllLifecyclePhasesForTest(); EXPECT_EQ(a_properties, a->GetLayoutObject()->FirstFragment().PaintProperties()); @@ -3714,7 +3728,7 @@ TEST_P(PaintPropertyTreeBuilderTest, OverflowHiddenScrollProperties) { Element* overflow_hidden = GetDocument().getElementById("overflowHidden"); overflow_hidden->setScrollTop(37); - GetDocument().View()->UpdateAllLifecyclePhases(); + UpdateAllLifecyclePhasesForTest(); const ObjectPaintProperties* overflow_hidden_scroll_properties = overflow_hidden->GetLayoutObject()->FirstFragment().PaintProperties(); @@ -3747,7 +3761,7 @@ TEST_P(PaintPropertyTreeBuilderTest, FrameOverflowHiddenScrollProperties) { GetDocument().domWindow()->scrollTo(0, 37); - GetDocument().View()->UpdateAllLifecyclePhases(); + UpdateAllLifecyclePhasesForTest(); EXPECT_EQ(TransformationMatrix().Translate(0, -37), DocScrollTranslation()->Matrix()); @@ -3796,7 +3810,7 @@ TEST_P(PaintPropertyTreeBuilderTest, NestedScrollProperties) { Element* overflow_b = GetDocument().getElementById("overflowB"); overflow_b->setScrollTop(41); - GetDocument().View()->UpdateAllLifecyclePhases(); + UpdateAllLifecyclePhasesForTest(); const ObjectPaintProperties* overflow_a_scroll_properties = overflow_a->GetLayoutObject()->FirstFragment().PaintProperties(); @@ -3892,7 +3906,7 @@ TEST_P(PaintPropertyTreeBuilderTest, PositionedScrollerIsNotNested) { Element* fixed_overflow = GetDocument().getElementById("fixedOverflow"); fixed_overflow->setScrollTop(43); - GetDocument().View()->UpdateAllLifecyclePhases(); + UpdateAllLifecyclePhasesForTest(); // The frame should scroll due to the "forceScroll" element. EXPECT_NE(nullptr, DocScroll()); @@ -3983,7 +3997,7 @@ TEST_P(PaintPropertyTreeBuilderTest, NestedPositionedScrollProperties) { Element* overflow_b = GetDocument().getElementById("overflowB"); overflow_b->setScrollTop(41); - GetDocument().View()->UpdateAllLifecyclePhases(); + UpdateAllLifecyclePhasesForTest(); const ObjectPaintProperties* overflow_a_scroll_properties = overflow_a->GetLayoutObject()->FirstFragment().PaintProperties(); @@ -4111,7 +4125,7 @@ TEST_P(PaintPropertyTreeBuilderTest, PaintOffsetsUnderMultiColumnScrolled) { LayoutObject* scroller = GetLayoutObjectByElementId("scroller"); ToLayoutBox(scroller)->GetScrollableArea()->ScrollBy(ScrollOffset(0, 300), kUserScroll); - GetDocument().View()->UpdateAllLifecyclePhases(); + UpdateAllLifecyclePhasesForTest(); EXPECT_EQ(FloatSize(8, 8), scroller->FirstFragment() .PaintProperties() @@ -4202,7 +4216,7 @@ TEST_P(PaintPropertyTreeBuilderTest, GetDocument().View()->LayoutViewport()->ScrollBy(ScrollOffset(0, 25), kUserScroll); - GetDocument().View()->UpdateAllLifecyclePhases(); + UpdateAllLifecyclePhasesForTest(); ASSERT_TRUE(multicol_container->FirstFragment().NextFragment()); ASSERT_FALSE( @@ -4386,12 +4400,6 @@ TEST_P(PaintPropertyTreeBuilderTest, } TEST_P(PaintPropertyTreeBuilderTest, CompositedUnderMultiColumn) { - // TODO(crbug.com/796768): Currently this test crashes for SPv2 when mapping - // layer clip rects from one fragment to another. May need to adjust fragment - // clip hierarchy to fix the crash. - if (RuntimeEnabledFeatures::SlimmingPaintV2Enabled()) - return; - SetBodyInnerHTML(R"HTML( <style>body { margin: 0; }</style> <div id='multicol' style='columns:3; column-fill:auto; column-gap: 0; @@ -4430,31 +4438,32 @@ TEST_P(PaintPropertyTreeBuilderTest, CompositedUnderMultiColumn) { EXPECT_EQ(LayoutPoint(100, 100), FragmentAt(composited, 0).PaintOffset()); EXPECT_EQ(LayoutPoint(100, -200), FragmentAt(composited, 0).PaginationOffset()); - EXPECT_EQ(LayoutUnit(), FragmentAt(composited, 0).LogicalTopInFlowThread()); + EXPECT_EQ(LayoutUnit(200), + FragmentAt(composited, 0).LogicalTopInFlowThread()); EXPECT_EQ(LayoutPoint(200, -100), FragmentAt(composited, 1).PaintOffset()); EXPECT_EQ(LayoutPoint(200, -400), FragmentAt(composited, 1).PaginationOffset()); - EXPECT_EQ(LayoutUnit(200), + EXPECT_EQ(LayoutUnit(400), FragmentAt(composited, 1).LogicalTopInFlowThread()); EXPECT_EQ(2u, NumFragments(non_composited_child)); EXPECT_EQ(LayoutPoint(100, 100), FragmentAt(non_composited_child, 0).PaintOffset()); EXPECT_EQ(LayoutPoint(100, -200), FragmentAt(non_composited_child, 0).PaginationOffset()); - EXPECT_EQ(LayoutUnit(), + EXPECT_EQ(LayoutUnit(200), FragmentAt(non_composited_child, 0).LogicalTopInFlowThread()); EXPECT_EQ(LayoutPoint(200, -100), FragmentAt(non_composited_child, 1).PaintOffset()); EXPECT_EQ(LayoutPoint(200, -400), FragmentAt(non_composited_child, 1).PaginationOffset()); - EXPECT_EQ(LayoutUnit(200), + EXPECT_EQ(LayoutUnit(400), FragmentAt(non_composited_child, 1).LogicalTopInFlowThread()); EXPECT_EQ(1u, NumFragments(composited_child)); EXPECT_EQ(LayoutPoint(200, 50), FragmentAt(composited_child, 0).PaintOffset()); EXPECT_EQ(LayoutPoint(200, -400), FragmentAt(composited_child, 0).PaginationOffset()); - EXPECT_EQ(LayoutUnit(), + EXPECT_EQ(LayoutUnit(400), FragmentAt(composited_child, 0).LogicalTopInFlowThread()); } else { // SPv1 forces single fragment for composited layers. @@ -4482,12 +4491,6 @@ TEST_P(PaintPropertyTreeBuilderTest, CompositedUnderMultiColumn) { } TEST_P(PaintPropertyTreeBuilderTest, FragmentsInPagedY) { - // TODO(crbug.com/796768): Currently this test crashes for SPv2 when mapping - // layer clip rects from one fragment to another. May need to adjust fragment - // clip hierarchy to fix the crash. - if (RuntimeEnabledFeatures::SlimmingPaintV2Enabled()) - return; - SetBodyInnerHTML(R"HTML( <div id='paged' style='overflow: -webkit-paged-y; column-gap: 0; width: 100px; height: 100px'> @@ -4509,12 +4512,6 @@ TEST_P(PaintPropertyTreeBuilderTest, FragmentsInPagedY) { } TEST_P(PaintPropertyTreeBuilderTest, FragmentsInPagedYWithGap) { - // TODO(crbug.com/796768): Currently this test crashes for SPv2 when mapping - // layer clip rects from one fragment to another. May need to adjust fragment - // clip hierarchy to fix the crash. - if (RuntimeEnabledFeatures::SlimmingPaintV2Enabled()) - return; - SetBodyInnerHTML(R"HTML( <div id='paged' style='overflow: -webkit-paged-y; column-gap: 10px; width: 100px; height: 100px'> @@ -4536,12 +4533,6 @@ TEST_P(PaintPropertyTreeBuilderTest, FragmentsInPagedYWithGap) { } TEST_P(PaintPropertyTreeBuilderTest, FragmentsInPagedX) { - // TODO(crbug.com/796768): Currently this test crashes for SPv2 when mapping - // layer clip rects from one fragment to another. May need to adjust fragment - // clip hierarchy to fix the crash. - if (RuntimeEnabledFeatures::SlimmingPaintV2Enabled()) - return; - SetBodyInnerHTML(R"HTML( <div id='paged' style='overflow: -webkit-paged-x; column-gap: 0; width: 100px; height: 100px'> @@ -4563,12 +4554,6 @@ TEST_P(PaintPropertyTreeBuilderTest, FragmentsInPagedX) { } TEST_P(PaintPropertyTreeBuilderTest, FragmentsInPagedYVerticalRL) { - // TODO(crbug.com/796768): Currently this test crashes for SPv2 when mapping - // layer clip rects from one fragment to another. May need to adjust fragment - // clip hierarchy to fix the crash. - if (RuntimeEnabledFeatures::SlimmingPaintV2Enabled()) - return; - SetBodyInnerHTML(R"HTML( <div id='paged' style='overflow: -webkit-paged-y; column-gap: 0; width: 100px; height: 100px; writing-mode: vertical-rl'> @@ -4636,18 +4621,12 @@ TEST_P(PaintPropertyTreeBuilderTest, FrameUnderMulticol) { )HTML"); // This should not crash on duplicated subsequences in the iframe. - GetDocument().View()->UpdateAllLifecyclePhases(); + UpdateAllLifecyclePhasesForTest(); // TODO(crbug.com/797779): Add code to verify fragments under the iframe. } TEST_P(PaintPropertyTreeBuilderTest, CompositedMulticolFrameUnderMulticol) { - // TODO(crbug.com/796768): Currently this test crashes for SPv2 when mapping - // layer clip rects from one fragment to another. May need to adjust fragment - // clip hierarchy to fix the crash. - if (RuntimeEnabledFeatures::SlimmingPaintV2Enabled()) - return; - SetBodyInnerHTML(R"HTML( <style>body { margin: 0 }</style> <div style='columns: 3; column-gap: 0; column-fill: auto; @@ -4666,7 +4645,7 @@ TEST_P(PaintPropertyTreeBuilderTest, CompositedMulticolFrameUnderMulticol) { )HTML"); // This should not crash on duplicated subsequences in the iframe. - GetDocument().View()->UpdateAllLifecyclePhases(); + UpdateAllLifecyclePhasesForTest(); // TODO(crbug.com/797779): Add code to verify fragments under the iframe. } @@ -4692,8 +4671,8 @@ TEST_P(PaintPropertyTreeBuilderTest, EXPECT_EQ(LayoutUnit(20), target->FirstFragment().LogicalTopInFlowThread()); Element* target_element = GetDocument().getElementById("target"); - target_element->setAttribute(HTMLNames::styleAttr, "position: absolute"); - GetDocument().View()->UpdateAllLifecyclePhases(); + target_element->setAttribute(html_names::kStyleAttr, "position: absolute"); + UpdateAllLifecyclePhasesForTest(); EXPECT_EQ(LayoutPoint(0, 0), target->FirstFragment().PaginationOffset()); EXPECT_EQ(LayoutUnit(), target->FirstFragment().LogicalTopInFlowThread()); } @@ -4851,8 +4830,8 @@ TEST_P(PaintPropertyTreeBuilderTest, ChangePositionUpdateDescendantProperties) { descendant->FirstFragment().LocalBorderBoxProperties().Clip()); ToElement(ancestor->GetNode()) - ->setAttribute(HTMLNames::styleAttr, "position: static"); - GetDocument().View()->UpdateAllLifecyclePhases(); + ->setAttribute(html_names::kStyleAttr, "position: static"); + UpdateAllLifecyclePhasesForTest(); EXPECT_NE(ancestor->FirstFragment().PaintProperties()->OverflowClip(), descendant->FirstFragment().LocalBorderBoxProperties().Clip()); } @@ -4877,6 +4856,9 @@ TEST_P(PaintPropertyTreeBuilderTest, SetBodyInnerHTML("<div id='target' style='opacity: 0.5'></div"); const ObjectPaintProperties* properties = PaintPropertiesForElement("target"); EXPECT_TRUE(properties->Effect()); + // TODO(flackr): Revisit whether effect ElementId should still exist when + // animations are no longer keyed off of the existence it: + // https://crbug.com/900241 EXPECT_NE(CompositorElementId(), properties->Effect()->GetCompositorElementId()); } @@ -5277,7 +5259,7 @@ TEST_P(PaintPropertyTreeBuilderTest, ScrollBoundsOffset) { Element* scroller = GetDocument().getElementById("scroller"); scroller->setScrollTop(42); - GetDocument().View()->UpdateAllLifecyclePhases(); + UpdateAllLifecyclePhasesForTest(); const ObjectPaintProperties* scroll_properties = scroller->GetLayoutObject()->FirstFragment().PaintProperties(); @@ -5306,18 +5288,18 @@ TEST_P(PaintPropertyTreeBuilderTest, ScrollBoundsOffset) { // And the scroll node should not. EXPECT_EQ(IntRect(0, 0, 100, 100), scroll_node->ContainerRect()); - scroller->setAttribute(HTMLNames::styleAttr, "border: 20px solid black;"); - GetDocument().View()->UpdateAllLifecyclePhases(); + scroller->setAttribute(html_names::kStyleAttr, "border: 20px solid black;"); + UpdateAllLifecyclePhasesForTest(); // The paint offset node should be offset by the margin. EXPECT_EQ(FloatSize(7, 11), paint_offset_translation->Matrix().To2DTranslation()); // The scroll node should be offset by the border. EXPECT_EQ(IntRect(20, 20, 100, 100), scroll_node->ContainerRect()); - scroller->setAttribute(HTMLNames::styleAttr, + scroller->setAttribute(html_names::kStyleAttr, "border: 20px solid black;" "transform: translate(20px, 30px);"); - GetDocument().View()->UpdateAllLifecyclePhases(); + UpdateAllLifecyclePhasesForTest(); // The scroll node's offset should not include margin if it has already been // included in a paint offset node. EXPECT_EQ(IntRect(20, 20, 100, 100), scroll_node->ContainerRect()); @@ -5360,8 +5342,8 @@ TEST_P(PaintPropertyTreeBuilderTest, BackfaceHidden) { EXPECT_EQ(nullptr, transform); } - ToElement(target->GetNode())->setAttribute(HTMLNames::styleAttr, ""); - GetDocument().View()->UpdateAllLifecyclePhases(); + ToElement(target->GetNode())->setAttribute(html_names::kStyleAttr, ""); + UpdateAllLifecyclePhasesForTest(); EXPECT_EQ(LayoutPoint(60, 50), target->FirstFragment().PaintOffset()); EXPECT_EQ(nullptr, target->FirstFragment().PaintProperties()); } @@ -5436,7 +5418,7 @@ TEST_P(PaintPropertyTreeBuilderTest, ImageBorderRadius) { TEST_P(PaintPropertyTreeBuilderTest, FrameClipWhenPrinting) { SetBodyInnerHTML("<iframe></iframe>"); SetChildFrameHTML(""); - GetDocument().View()->UpdateAllLifecyclePhases(); + UpdateAllLifecyclePhasesForTest(); // When not printing, both main and child frame views have content clip. auto* const main_frame_doc = &GetDocument(); @@ -5458,7 +5440,7 @@ TEST_P(PaintPropertyTreeBuilderTest, FrameClipWhenPrinting) { DocContentClip(child_frame_doc)->ClipRect().Rect()); GetFrame().EndPrinting(); - GetDocument().View()->UpdateAllLifecyclePhases(); + UpdateAllLifecyclePhasesForTest(); // When only the child frame is printing, it should not have content clip but // the main frame still have (which doesn't matter though). @@ -5585,7 +5567,7 @@ TEST_P(PaintPropertyTreeBuilderTest, EXPECT_FALSE(ToLayoutBoxModelObject(target)->Layer()->NeedsRepaint()); - opacity_element->setAttribute(HTMLNames::styleAttr, "opacity: 0.5"); + opacity_element->setAttribute(html_names::kStyleAttr, "opacity: 0.5"); GetDocument().View()->UpdateAllLifecyclePhasesExceptPaint(); // All paint chunks contained by the new opacity effect node need to be @@ -5664,7 +5646,7 @@ TEST_P(PaintPropertyTreeBuilderTest, RootHasCompositedScrolling) { // Remove scrolling from the root. Element* force_scroll_element = GetDocument().getElementById("forceScroll"); - force_scroll_element->setAttribute(HTMLNames::styleAttr, ""); + force_scroll_element->setAttribute(html_names::kStyleAttr, ""); GetDocument().View()->UpdateAllLifecyclePhasesExceptPaint(); // TODO(crbug.com/732611): SPv2 invalidations are incorrect if there is // scrolling. @@ -5685,7 +5667,7 @@ TEST_P(PaintPropertyTreeBuilderTest, IframeDoesNotRequireCompositedScrolling) { SetChildFrameHTML(R"HTML( <div id='forceInnerScroll' style='height: 2000px'></div> )HTML"); - GetDocument().View()->UpdateAllLifecyclePhases(); + UpdateAllLifecyclePhasesForTest(); if (RuntimeEnabledFeatures::SlimmingPaintV2Enabled()) EXPECT_TRUE(DocScrollTranslation()->HasDirectCompositingReasons()); @@ -5776,12 +5758,12 @@ TEST_P(PaintPropertyTreeBuilderTest, ClipHitTestChangeDoesNotCauseFullRepaint) { </html> )HTML"); CHECK(GetDocument().GetPage()->GetScrollbarTheme().UsesOverlayScrollbars()); - GetDocument().View()->UpdateAllLifecyclePhases(); + UpdateAllLifecyclePhasesForTest(); auto* child_layer = ToLayoutBox(GetLayoutObjectByElementId("child"))->Layer(); EXPECT_FALSE(child_layer->NeedsRepaint()); - GetDocument().body()->setAttribute(HTMLNames::classAttr, "noscrollbars"); + GetDocument().body()->setAttribute(html_names::kClassAttr, "noscrollbars"); GetDocument().View()->UpdateAllLifecyclePhasesExceptPaint(); EXPECT_FALSE(child_layer->NeedsRepaint()); } @@ -5801,7 +5783,7 @@ TEST_P(PaintPropertyTreeBuilderTest, ClipPathInheritanceWithoutMutation) { child->FirstFragment().LocalBorderBoxProperties().Clip(); child->SetNeedsPaintPropertyUpdate(); - GetDocument().View()->UpdateAllLifecyclePhases(); + UpdateAllLifecyclePhasesForTest(); const auto* new_clip_state = child->FirstFragment().LocalBorderBoxProperties().Clip(); @@ -5855,7 +5837,7 @@ TEST_P(PaintPropertyTreeBuilderTest, RepeatingFixedPositionInPagedMedia) { <div id="normal" style="height: 1000px"></div> )HTML"); GetDocument().domWindow()->scrollTo(0, 200); - GetDocument().View()->UpdateAllLifecyclePhases(); + UpdateAllLifecyclePhasesForTest(); const auto* fixed = GetLayoutObjectByElementId("fixed"); EXPECT_FALSE(fixed->IsFixedPositionObjectInPagedMedia()); @@ -5872,6 +5854,9 @@ TEST_P(PaintPropertyTreeBuilderTest, RepeatingFixedPositionInPagedMedia) { FloatSize page_size(300, 400); GetFrame().StartPrinting(page_size, page_size, 1); GetDocument().View()->UpdateLifecyclePhasesForPrinting(); + fixed = GetLayoutObjectByElementId("fixed"); + fixed_child = GetLayoutObjectByElementId("fixed-child"); + normal = GetLayoutObjectByElementId("normal"); // "fixed" should create fragments to repeat in each printed page. EXPECT_TRUE(fixed->IsFixedPositionObjectInPagedMedia()); @@ -5894,7 +5879,10 @@ TEST_P(PaintPropertyTreeBuilderTest, RepeatingFixedPositionInPagedMedia) { EXPECT_EQ(1u, NumFragments(normal)); GetFrame().EndPrinting(); - GetDocument().View()->UpdateAllLifecyclePhases(); + UpdateAllLifecyclePhasesForTest(); + fixed = GetLayoutObjectByElementId("fixed"); + fixed_child = GetLayoutObjectByElementId("fixed-child"); + normal = GetLayoutObjectByElementId("normal"); EXPECT_EQ(1u, NumFragments(fixed)); EXPECT_FALSE(fixed_child->IsFixedPositionObjectInPagedMedia()); EXPECT_EQ(1u, NumFragments(fixed_child)); @@ -5912,7 +5900,7 @@ TEST_P(PaintPropertyTreeBuilderTest, <div id="normal" style="height: 1000px"></div> )HTML"); GetDocument().domWindow()->scrollTo(0, 200); - GetDocument().View()->UpdateAllLifecyclePhases(); + UpdateAllLifecyclePhasesForTest(); const auto* fixed = GetLayoutObjectByElementId("fixed"); EXPECT_FALSE(fixed->IsFixedPositionObjectInPagedMedia()); @@ -5925,6 +5913,8 @@ TEST_P(PaintPropertyTreeBuilderTest, FloatSize page_size(300, 400); GetFrame().StartPrinting(page_size, page_size, 1); GetDocument().View()->UpdateLifecyclePhasesForPrinting(); + fixed = GetLayoutObjectByElementId("fixed"); + fixed_child = GetLayoutObjectByElementId("fixed-child"); // "fixed" should create fragments to repeat in each printed page. EXPECT_TRUE(fixed->IsFixedPositionObjectInPagedMedia()); @@ -5952,7 +5942,9 @@ TEST_P(PaintPropertyTreeBuilderTest, } GetFrame().EndPrinting(); - GetDocument().View()->UpdateAllLifecyclePhases(); + UpdateAllLifecyclePhasesForTest(); + fixed = GetLayoutObjectByElementId("fixed"); + fixed_child = GetLayoutObjectByElementId("fixed-child"); EXPECT_EQ(1u, NumFragments(fixed)); EXPECT_FALSE(fixed_child->IsFixedPositionObjectInPagedMedia()); EXPECT_EQ(1u, NumFragments(fixed_child)); @@ -5993,6 +5985,9 @@ TEST_P(PaintPropertyTreeBuilderTest, RepeatingTableSectionInPagedMedia) { FloatSize page_size(300, 400); GetFrame().StartPrinting(page_size, page_size, 1); GetDocument().View()->UpdateLifecyclePhasesForPrinting(); + // In LayoutNG, these may be different objects + head = ToLayoutTableSection(GetLayoutObjectByElementId("head")); + foot = ToLayoutTableSection(GetLayoutObjectByElementId("foot")); // "fixed" should create fragments to repeat in each printed page. EXPECT_TRUE(head->IsRepeatingHeaderGroup()); @@ -6026,7 +6021,9 @@ TEST_P(PaintPropertyTreeBuilderTest, RepeatingTableSectionInPagedMedia) { ASSERT_EQ(1u, NumFragments(&painting_layer_object)); GetFrame().EndPrinting(); - GetDocument().View()->UpdateAllLifecyclePhases(); + UpdateAllLifecyclePhasesForTest(); + head = ToLayoutTableSection(GetLayoutObjectByElementId("head")); + foot = ToLayoutTableSection(GetLayoutObjectByElementId("foot")); EXPECT_FALSE(head->IsRepeatingHeaderGroup()); EXPECT_EQ(1u, NumFragments(head)); EXPECT_EQ(1u, NumFragments(head->FirstRow())); @@ -6037,72 +6034,6 @@ TEST_P(PaintPropertyTreeBuilderTest, RepeatingTableSectionInPagedMedia) { EXPECT_EQ(1u, NumFragments(foot->FirstRow()->FirstCell())); } -TEST_P(PaintPropertyTreeBuilderTest, ImageWithInvertFilter) { - SetBodyInnerHTML(R"HTML( - <img id='img' src='x'> - )HTML"); - ToLayoutImage(GetLayoutObjectByElementId("img")) - ->UpdateShouldInvertColorForTest(true); - GetDocument().View()->UpdateAllLifecyclePhases(); - const auto* filters = PaintPropertiesForElement("img")->Filter(); - ASSERT_NE(nullptr, filters); - CompositorFilterOperations filters_expect; - filters_expect.AppendInvertFilter(1.0f); - EXPECT_EQ(filters_expect, filters->Filter()); -} - -TEST_P(PaintPropertyTreeBuilderTest, ImageWithInvertFilterUpdated) { - SetBodyInnerHTML(R"HTML( - <img id='img' src='x'> - )HTML"); - - ToLayoutImage(GetLayoutObjectByElementId("img")) - ->UpdateShouldInvertColorForTest(true); - GetDocument().View()->UpdateAllLifecyclePhases(); - const auto* filters = PaintPropertiesForElement("img")->Filter(); - ASSERT_NE(nullptr, filters); - CompositorFilterOperations filters_expect; - filters_expect.AppendInvertFilter(1.0f); - EXPECT_EQ(filters_expect, filters->Filter()); - ToLayoutImage(GetLayoutObjectByElementId("img")) - ->UpdateShouldInvertColorForTest(false); - GetDocument().View()->UpdateAllLifecyclePhases(); - EXPECT_FALSE(PaintPropertiesForElement("img")); -} - -TEST_P(PaintPropertyTreeBuilderTest, LayeredImageWithInvertFilter) { - SetBodyInnerHTML(R"HTML( - <img id='img' style='position: relative;' src='x'> - )HTML"); - ToLayoutImage(GetLayoutObjectByElementId("img")) - ->UpdateShouldInvertColorForTest(true); - GetDocument().View()->UpdateAllLifecyclePhases(); - const auto* filters = PaintPropertiesForElement("img")->Filter(); - ASSERT_NE(nullptr, filters); - CompositorFilterOperations filters_expect; - filters_expect.AppendInvertFilter(1.0f); - EXPECT_EQ(filters_expect, filters->Filter()); -} - -TEST_P(PaintPropertyTreeBuilderTest, LayeredImageWithInvertFilterUpdated) { - SetBodyInnerHTML(R"HTML( - <img id='img' style='position: relative;' src='x'> - )HTML"); - - ToLayoutImage(GetLayoutObjectByElementId("img")) - ->UpdateShouldInvertColorForTest(true); - GetDocument().View()->UpdateAllLifecyclePhases(); - const auto* filters = PaintPropertiesForElement("img")->Filter(); - ASSERT_NE(nullptr, filters); - CompositorFilterOperations filters_expect; - filters_expect.AppendInvertFilter(1.0f); - EXPECT_EQ(filters_expect, filters->Filter()); - ToLayoutImage(GetLayoutObjectByElementId("img")) - ->UpdateShouldInvertColorForTest(false); - GetDocument().View()->UpdateAllLifecyclePhases(); - EXPECT_FALSE(PaintPropertiesForElement("img")); -} - TEST_P(PaintPropertyTreeBuilderTest, FloatPaintOffsetInContainerWithScrollbars) { SetBodyInnerHTML(R"HTML( @@ -6191,8 +6122,8 @@ TEST_P(PaintPropertyTreeBuilderTest, ClipInvalidationForReplacedElement) { } GetDocument().getElementById("target")->setAttribute( - HTMLNames::styleAttr, "padding: 1px 2px 3px 4px;"); - GetDocument().View()->UpdateAllLifecyclePhases(); + html_names::kStyleAttr, "padding: 1px 2px 3px 4px;"); + UpdateAllLifecyclePhasesForTest(); { const auto* properties = PaintPropertiesForElement("target"); @@ -6302,7 +6233,7 @@ TEST_P(PaintPropertyTreeBuilderTest, StickyConstraintChain) { </div> )HTML"); GetDocument().getElementById("scroller")->setScrollTop(50); - GetDocument().View()->UpdateAllLifecyclePhases(); + UpdateAllLifecyclePhasesForTest(); const auto* outer_properties = PaintPropertiesForElement("outer"); ASSERT_TRUE(outer_properties && outer_properties->StickyTranslation()); @@ -6371,7 +6302,7 @@ TEST_P(PaintPropertyTreeBuilderTest, NonScrollableSticky) { </div> )HTML"); GetDocument().getElementById("scroller")->setScrollTop(50); - GetDocument().View()->UpdateAllLifecyclePhases(); + UpdateAllLifecyclePhasesForTest(); const auto* outer_properties = PaintPropertiesForElement("outer"); ASSERT_TRUE(outer_properties && outer_properties->StickyTranslation()); @@ -6407,7 +6338,7 @@ TEST_P(PaintPropertyTreeBuilderTest, WillChangeOpacityInducesAnEffectNode) { EXPECT_FLOAT_EQ(properties->Effect()->Opacity(), 1.f); auto* div = GetDocument().getElementById("div"); - div->setAttribute(HTMLNames::classAttr, "transluscent"); + div->setAttribute(html_names::kClassAttr, "transluscent"); GetDocument().View()->UpdateAllLifecyclePhasesExceptPaint(); EXPECT_FALSE(ToLayoutBox(div->GetLayoutObject())->Layer()->NeedsRepaint()); diff --git a/chromium/third_party/blink/renderer/core/paint/paint_property_tree_printer.cc b/chromium/third_party/blink/renderer/core/paint/paint_property_tree_printer.cc index ea825229b7b..06c43070593 100644 --- a/chromium/third_party/blink/renderer/core/paint/paint_property_tree_printer.cc +++ b/chromium/third_party/blink/renderer/core/paint/paint_property_tree_printer.cc @@ -163,7 +163,7 @@ void SetDebugName(const PropertyTreeNode* node, } // namespace -namespace PaintPropertyTreePrinter { +namespace paint_property_tree_printer { void UpdateDebugNames(const VisualViewport& viewport) { viewport.GetPageScaleNode()->SetDebugName("VisualViewport Scale Node"); @@ -212,7 +212,7 @@ void UpdateDebugNames(const LayoutObject& object, SetDebugName(properties.Scroll(), "Scroll", object); } -} // namespace PaintPropertyTreePrinter +} // namespace paint_property_tree_printer } // namespace blink diff --git a/chromium/third_party/blink/renderer/core/paint/paint_property_tree_printer.h b/chromium/third_party/blink/renderer/core/paint/paint_property_tree_printer.h index e7341612aa2..9f289b5850a 100644 --- a/chromium/third_party/blink/renderer/core/paint/paint_property_tree_printer.h +++ b/chromium/third_party/blink/renderer/core/paint/paint_property_tree_printer.h @@ -17,12 +17,12 @@ class LayoutObject; class ObjectPaintProperties; class VisualViewport; -namespace PaintPropertyTreePrinter { +namespace paint_property_tree_printer { void UpdateDebugNames(const VisualViewport&); void UpdateDebugNames(const LayoutObject&, ObjectPaintProperties&); -} // namespace PaintPropertyTreePrinter +} // namespace paint_property_tree_printer } // namespace blink diff --git a/chromium/third_party/blink/renderer/core/paint/paint_property_tree_printer_test.cc b/chromium/third_party/blink/renderer/core/paint/paint_property_tree_printer_test.cc index 190f7f4743e..2702524bfc6 100644 --- a/chromium/third_party/blink/renderer/core/paint/paint_property_tree_printer_test.cc +++ b/chromium/third_party/blink/renderer/core/paint/paint_property_tree_printer_test.cc @@ -49,9 +49,10 @@ TEST_P(PaintPropertyTreePrinterTest, SimpleEffectTree) { SetBodyInnerHTML("<div style='opacity: 0.9;'>hello world</div>"); String effect_tree_as_string = effectPropertyTreeAsString(*GetDocument().View()); - EXPECT_THAT(effect_tree_as_string.Ascii().data(), - testing::MatchesRegex("root .*" - " Effect \\(LayoutBlockFlow DIV\\) .*")); + EXPECT_THAT( + effect_tree_as_string.Ascii().data(), + testing::MatchesRegex("root .*" + " Effect \\(LayoutN?G?BlockFlow DIV\\) .*")); } TEST_P(PaintPropertyTreePrinterTest, SimpleScrollTree) { diff --git a/chromium/third_party/blink/renderer/core/paint/paint_property_tree_update_tests.cc b/chromium/third_party/blink/renderer/core/paint/paint_property_tree_update_tests.cc index 15c553caedc..9938a031839 100644 --- a/chromium/third_party/blink/renderer/core/paint/paint_property_tree_update_tests.cc +++ b/chromium/third_party/blink/renderer/core/paint/paint_property_tree_update_tests.cc @@ -49,7 +49,7 @@ TEST_P(PaintPropertyTreeUpdateTest, // TODO(pdr): The main thread scrolling setting should invalidate properties. GetDocument().View()->SetNeedsPaintPropertyUpdate(); overflow_a->GetLayoutObject()->SetNeedsPaintPropertyUpdate(); - GetDocument().View()->UpdateAllLifecyclePhases(); + UpdateAllLifecyclePhasesForTest(); EXPECT_TRUE(DocScroll()->ThreadedScrollingDisabled()); EXPECT_TRUE(overflow_a->GetLayoutObject() @@ -111,7 +111,7 @@ TEST_P(PaintPropertyTreeUpdateTest, // Removing a main thread scrolling reason should update the entire tree. overflow_b->removeAttribute("class"); - GetDocument().View()->UpdateAllLifecyclePhases(); + UpdateAllLifecyclePhasesForTest(); EXPECT_FALSE(DocScroll()->HasBackgroundAttachmentFixedDescendants()); EXPECT_FALSE(overflow_a->GetLayoutObject() ->FirstFragment() @@ -127,8 +127,8 @@ TEST_P(PaintPropertyTreeUpdateTest, ->HasBackgroundAttachmentFixedDescendants()); // Adding a main thread scrolling reason should update the entire tree. - overflow_b->setAttribute(HTMLNames::classAttr, "backgroundAttachmentFixed"); - GetDocument().View()->UpdateAllLifecyclePhases(); + overflow_b->setAttribute(html_names::kClassAttr, "backgroundAttachmentFixed"); + UpdateAllLifecyclePhasesForTest(); EXPECT_TRUE(DocScroll()->HasBackgroundAttachmentFixedDescendants()); EXPECT_TRUE(overflow_a->GetLayoutObject() ->FirstFragment() @@ -160,7 +160,7 @@ TEST_P(PaintPropertyTreeUpdateTest, ParentFrameMainThreadScrollReasons) { SetChildFrameHTML( "<style>body { margin: 0; }</style>" "<div id='forceScroll' style='height: 8888px;'></div>"); - GetDocument().View()->UpdateAllLifecyclePhases(); + UpdateAllLifecyclePhasesForTest(); Document* parent = &GetDocument(); EXPECT_TRUE(DocScroll(parent)->HasBackgroundAttachmentFixedDescendants()); Document* child = &ChildDocument(); @@ -168,14 +168,14 @@ TEST_P(PaintPropertyTreeUpdateTest, ParentFrameMainThreadScrollReasons) { // Removing a main thread scrolling reason should update the entire tree. auto* fixed_background = GetDocument().getElementById("fixedBackground"); - fixed_background->removeAttribute(HTMLNames::classAttr); - GetDocument().View()->UpdateAllLifecyclePhases(); + fixed_background->removeAttribute(html_names::kClassAttr); + UpdateAllLifecyclePhasesForTest(); EXPECT_FALSE(DocScroll(parent)->HasBackgroundAttachmentFixedDescendants()); EXPECT_FALSE(DocScroll(child)->HasBackgroundAttachmentFixedDescendants()); // Adding a main thread scrolling reason should update the entire tree. - fixed_background->setAttribute(HTMLNames::classAttr, "fixedBackground"); - GetDocument().View()->UpdateAllLifecyclePhases(); + fixed_background->setAttribute(html_names::kClassAttr, "fixedBackground"); + UpdateAllLifecyclePhasesForTest(); EXPECT_TRUE(DocScroll(parent)->HasBackgroundAttachmentFixedDescendants()); EXPECT_TRUE(DocScroll(child)->HasBackgroundAttachmentFixedDescendants()); } @@ -197,7 +197,7 @@ TEST_P(PaintPropertyTreeUpdateTest, ChildFrameMainThreadScrollReasons) { <div id='fixedBackground' class='fixedBackground'></div> <div id='forceScroll' style='height: 8888px;'></div> )HTML"); - GetDocument().View()->UpdateAllLifecyclePhases(); + UpdateAllLifecyclePhasesForTest(); Document* parent = &GetDocument(); EXPECT_FALSE(DocScroll(parent)->HasBackgroundAttachmentFixedDescendants()); @@ -206,14 +206,14 @@ TEST_P(PaintPropertyTreeUpdateTest, ChildFrameMainThreadScrollReasons) { // Removing a main thread scrolling reason should update the entire tree. auto* fixed_background = ChildDocument().getElementById("fixedBackground"); - fixed_background->removeAttribute(HTMLNames::classAttr); - GetDocument().View()->UpdateAllLifecyclePhases(); + fixed_background->removeAttribute(html_names::kClassAttr); + UpdateAllLifecyclePhasesForTest(); EXPECT_FALSE(DocScroll(parent)->HasBackgroundAttachmentFixedDescendants()); EXPECT_FALSE(DocScroll(child)->HasBackgroundAttachmentFixedDescendants()); // Adding a main thread scrolling reason should update the entire tree. - fixed_background->setAttribute(HTMLNames::classAttr, "fixedBackground"); - GetDocument().View()->UpdateAllLifecyclePhases(); + fixed_background->setAttribute(html_names::kClassAttr, "fixedBackground"); + UpdateAllLifecyclePhasesForTest(); EXPECT_FALSE(DocScroll(parent)->HasBackgroundAttachmentFixedDescendants()); EXPECT_TRUE(DocScroll(child)->HasBackgroundAttachmentFixedDescendants()); } @@ -278,7 +278,7 @@ TEST_P(PaintPropertyTreeUpdateTest, // Removing a main thread scrolling reason should update the entire tree. overflow_b->removeAttribute("class"); - GetDocument().View()->UpdateAllLifecyclePhases(); + UpdateAllLifecyclePhasesForTest(); EXPECT_FALSE(overflow_a->GetLayoutObject() ->FirstFragment() .PaintProperties() @@ -312,7 +312,8 @@ TEST_P(PaintPropertyTreeUpdateTest, DescendantNeedsUpdateAcrossFrames) { "translate3d(4px, 5px, 6px); width: 100px; height: 200px'></div>"); LocalFrameView* frame_view = GetDocument().View(); - frame_view->UpdateAllLifecyclePhases(); + frame_view->UpdateAllLifecyclePhases( + DocumentLifecycle::LifecycleUpdateReason::kTest); LayoutObject* div_with_transform = GetLayoutObjectByElementId("divWithTransform"); @@ -338,7 +339,8 @@ TEST_P(PaintPropertyTreeUpdateTest, DescendantNeedsUpdateAcrossFrames) { EXPECT_FALSE(inner_div_with_transform->DescendantNeedsPaintPropertyUpdate()); // After a lifecycle update, no nodes should need a descendant update. - frame_view->UpdateAllLifecyclePhases(); + frame_view->UpdateAllLifecyclePhases( + DocumentLifecycle::LifecycleUpdateReason::kTest); EXPECT_FALSE( GetDocument().GetLayoutView()->DescendantNeedsPaintPropertyUpdate()); EXPECT_FALSE(div_with_transform->DescendantNeedsPaintPropertyUpdate()); @@ -351,7 +353,8 @@ TEST_P(PaintPropertyTreeUpdateTest, DescendantNeedsUpdateAcrossFrames) { child_frame_view->SetNeedsPaintPropertyUpdate(); EXPECT_TRUE( GetDocument().GetLayoutView()->DescendantNeedsPaintPropertyUpdate()); - frame_view->UpdateAllLifecyclePhases(); + frame_view->UpdateAllLifecyclePhases( + DocumentLifecycle::LifecycleUpdateReason::kTest); EXPECT_FALSE( GetDocument().GetLayoutView()->DescendantNeedsPaintPropertyUpdate()); EXPECT_FALSE(GetDocument().GetLayoutView()->NeedsPaintPropertyUpdate()); @@ -364,13 +367,13 @@ TEST_P(PaintPropertyTreeUpdateTest, UpdatingFrameViewContentClip) { SetBodyInnerHTML("hello world."); EXPECT_EQ(FloatRoundedRect(0, 0, 800, 600), DocContentClip()->ClipRect()); GetDocument().View()->Resize(800, 599); - GetDocument().View()->UpdateAllLifecyclePhases(); + UpdateAllLifecyclePhasesForTest(); EXPECT_EQ(FloatRoundedRect(0, 0, 800, 599), DocContentClip()->ClipRect()); GetDocument().View()->Resize(800, 600); - GetDocument().View()->UpdateAllLifecyclePhases(); + UpdateAllLifecyclePhasesForTest(); EXPECT_EQ(FloatRoundedRect(0, 0, 800, 600), DocContentClip()->ClipRect()); GetDocument().View()->Resize(5, 5); - GetDocument().View()->UpdateAllLifecyclePhases(); + UpdateAllLifecyclePhasesForTest(); EXPECT_EQ(FloatRoundedRect(0, 0, 5, 5), DocContentClip()->ClipRect()); } @@ -392,8 +395,8 @@ TEST_P(PaintPropertyTreeUpdateTest, BuildingStopsAtThrottledFrames) { // Move the child frame offscreen so it becomes available for throttling. auto* iframe = ToHTMLIFrameElement(GetDocument().getElementById("iframe")); - iframe->setAttribute(HTMLNames::styleAttr, "transform: translateY(5555px)"); - GetDocument().View()->UpdateAllLifecyclePhases(); + iframe->setAttribute(html_names::kStyleAttr, "transform: translateY(5555px)"); + UpdateAllLifecyclePhasesForTest(); // Ensure intersection observer notifications get delivered. test::RunPendingTasks(); EXPECT_FALSE(GetDocument().View()->IsHiddenForThrottling()); @@ -428,7 +431,7 @@ TEST_P(PaintPropertyTreeUpdateTest, BuildingStopsAtThrottledFrames) { // A lifecycle update should update all properties except those with // actively throttled descendants. - GetDocument().View()->UpdateAllLifecyclePhases(); + UpdateAllLifecyclePhasesForTest(); EXPECT_FALSE(GetDocument().GetLayoutView()->NeedsPaintPropertyUpdate()); EXPECT_FALSE( GetDocument().GetLayoutView()->DescendantNeedsPaintPropertyUpdate()); @@ -443,7 +446,7 @@ TEST_P(PaintPropertyTreeUpdateTest, BuildingStopsAtThrottledFrames) { EXPECT_FALSE(GetDocument().View()->ShouldThrottleRendering()); EXPECT_FALSE(ChildDocument().View()->ShouldThrottleRendering()); // Once unthrottled, a lifecycel update should update all properties. - GetDocument().View()->UpdateAllLifecyclePhases(); + UpdateAllLifecyclePhasesForTest(); EXPECT_FALSE(GetDocument().GetLayoutView()->NeedsPaintPropertyUpdate()); EXPECT_FALSE( GetDocument().GetLayoutView()->DescendantNeedsPaintPropertyUpdate()); @@ -467,58 +470,58 @@ TEST_P(PaintPropertyTreeUpdateTest, ClipChangesUpdateOverflowClip) { </div> )HTML"); auto* div = GetDocument().getElementById("div"); - div->setAttribute(HTMLNames::styleAttr, "display:inline-block; width:7px;"); - GetDocument().View()->UpdateAllLifecyclePhases(); + div->setAttribute(html_names::kStyleAttr, "display:inline-block; width:7px;"); + UpdateAllLifecyclePhasesForTest(); auto* clip_properties = div->GetLayoutObject()->FirstFragment().PaintProperties()->OverflowClip(); EXPECT_EQ(FloatRect(0, 0, 7, 0), clip_properties->ClipRect().Rect()); // Width changes should update the overflow clip. - div->setAttribute(HTMLNames::styleAttr, "display:inline-block; width:7px;"); - GetDocument().View()->UpdateAllLifecyclePhases(); + div->setAttribute(html_names::kStyleAttr, "display:inline-block; width:7px;"); + UpdateAllLifecyclePhasesForTest(); clip_properties = div->GetLayoutObject()->FirstFragment().PaintProperties()->OverflowClip(); EXPECT_EQ(FloatRect(0, 0, 7, 0), clip_properties->ClipRect().Rect()); - div->setAttribute(HTMLNames::styleAttr, "display:inline-block; width:9px;"); - GetDocument().View()->UpdateAllLifecyclePhases(); + div->setAttribute(html_names::kStyleAttr, "display:inline-block; width:9px;"); + UpdateAllLifecyclePhasesForTest(); EXPECT_EQ(FloatRect(0, 0, 9, 0), clip_properties->ClipRect().Rect()); // An inline block's overflow clip should be updated when padding changes, // even if the border box remains unchanged. - div->setAttribute(HTMLNames::styleAttr, + div->setAttribute(html_names::kStyleAttr, "display:inline-block; width:7px; padding-right:3px;"); - GetDocument().View()->UpdateAllLifecyclePhases(); + UpdateAllLifecyclePhasesForTest(); clip_properties = div->GetLayoutObject()->FirstFragment().PaintProperties()->OverflowClip(); EXPECT_EQ(FloatRect(0, 0, 10, 0), clip_properties->ClipRect().Rect()); - div->setAttribute(HTMLNames::styleAttr, + div->setAttribute(html_names::kStyleAttr, "display:inline-block; width:8px; padding-right:2px;"); - GetDocument().View()->UpdateAllLifecyclePhases(); + UpdateAllLifecyclePhasesForTest(); EXPECT_EQ(FloatRect(0, 0, 10, 0), clip_properties->ClipRect().Rect()); - div->setAttribute(HTMLNames::styleAttr, + div->setAttribute(html_names::kStyleAttr, "display:inline-block; width:8px;" "padding-right:1px; padding-left:1px;"); - GetDocument().View()->UpdateAllLifecyclePhases(); + UpdateAllLifecyclePhasesForTest(); EXPECT_EQ(FloatRect(0, 0, 10, 0), clip_properties->ClipRect().Rect()); // An block's overflow clip should be updated when borders change. - div->setAttribute(HTMLNames::styleAttr, "border-right:3px solid red;"); - GetDocument().View()->UpdateAllLifecyclePhases(); + div->setAttribute(html_names::kStyleAttr, "border-right:3px solid red;"); + UpdateAllLifecyclePhasesForTest(); clip_properties = div->GetLayoutObject()->FirstFragment().PaintProperties()->OverflowClip(); EXPECT_EQ(FloatRect(0, 0, 797, 0), clip_properties->ClipRect().Rect()); - div->setAttribute(HTMLNames::styleAttr, "border-right:5px solid red;"); - GetDocument().View()->UpdateAllLifecyclePhases(); + div->setAttribute(html_names::kStyleAttr, "border-right:5px solid red;"); + UpdateAllLifecyclePhasesForTest(); EXPECT_EQ(FloatRect(0, 0, 795, 0), clip_properties->ClipRect().Rect()); // Removing overflow clip should remove the property. - div->setAttribute(HTMLNames::styleAttr, "overflow:hidden;"); - GetDocument().View()->UpdateAllLifecyclePhases(); + div->setAttribute(html_names::kStyleAttr, "overflow:hidden;"); + UpdateAllLifecyclePhasesForTest(); clip_properties = div->GetLayoutObject()->FirstFragment().PaintProperties()->OverflowClip(); EXPECT_EQ(FloatRect(0, 0, 800, 0), clip_properties->ClipRect().Rect()); - div->setAttribute(HTMLNames::styleAttr, "overflow:visible;"); - GetDocument().View()->UpdateAllLifecyclePhases(); + div->setAttribute(html_names::kStyleAttr, "overflow:visible;"); + UpdateAllLifecyclePhasesForTest(); EXPECT_TRUE(!div->GetLayoutObject()->FirstFragment().PaintProperties() || !div->GetLayoutObject() ->FirstFragment() @@ -536,14 +539,14 @@ TEST_P(PaintPropertyTreeUpdateTest, ContainPaintChangesUpdateOverflowClip) { <div style='width: 100px; height: 100px'></div> </div> )HTML"); - GetDocument().View()->UpdateAllLifecyclePhases(); + UpdateAllLifecyclePhasesForTest(); auto* div = GetDocument().getElementById("div"); auto* properties = div->GetLayoutObject()->FirstFragment().PaintProperties()->OverflowClip(); EXPECT_EQ(FloatRect(0, 0, 7, 6), properties->ClipRect().Rect()); - div->setAttribute(HTMLNames::styleAttr, ""); - GetDocument().View()->UpdateAllLifecyclePhases(); + div->setAttribute(html_names::kStyleAttr, ""); + UpdateAllLifecyclePhasesForTest(); EXPECT_TRUE(!div->GetLayoutObject()->FirstFragment().PaintProperties() || !div->GetLayoutObject() ->FirstFragment() @@ -556,8 +559,8 @@ TEST_P(PaintPropertyTreeUpdateTest, NoPaintPropertyUpdateOnBackgroundChange) { SetBodyInnerHTML("<div id='div' style='background-color: blue'>DIV</div>"); auto* div = GetDocument().getElementById("div"); - GetDocument().View()->UpdateAllLifecyclePhases(); - div->setAttribute(HTMLNames::styleAttr, "background-color: green"); + UpdateAllLifecyclePhasesForTest(); + div->setAttribute(html_names::kStyleAttr, "background-color: green"); GetDocument().View()->UpdateLifecycleToLayoutClean(); EXPECT_FALSE(div->GetLayoutObject()->NeedsPaintPropertyUpdate()); } @@ -576,14 +579,16 @@ TEST_P(PaintPropertyTreeUpdateTest, "<div id='forceScroll' style='height: 3000px;'></div>"); LocalFrameView* frame_view = GetDocument().View(); - frame_view->UpdateAllLifecyclePhases(); + frame_view->UpdateAllLifecyclePhases( + DocumentLifecycle::LifecycleUpdateReason::kTest); EXPECT_EQ(nullptr, DocScroll()); Document* child_doc = &ChildDocument(); EXPECT_NE(nullptr, DocScroll(child_doc)); auto* iframe_container = GetDocument().getElementById("iframeContainer"); - iframe_container->setAttribute(HTMLNames::styleAttr, "visibility: hidden;"); - frame_view->UpdateAllLifecyclePhases(); + iframe_container->setAttribute(html_names::kStyleAttr, "visibility: hidden;"); + frame_view->UpdateAllLifecyclePhases( + DocumentLifecycle::LifecycleUpdateReason::kTest); EXPECT_EQ(nullptr, DocScroll()); EXPECT_EQ(nullptr, DocScroll(child_doc)); @@ -599,8 +604,8 @@ TEST_P(PaintPropertyTreeUpdateTest, EXPECT_TRUE(properties->Transform()->HasDirectCompositingReasons()); // Removing the animation should remove the transform node. - target->removeAttribute(HTMLNames::classAttr); - GetDocument().View()->UpdateAllLifecyclePhases(); + target->removeAttribute(html_names::kClassAttr); + UpdateAllLifecyclePhasesForTest(); // Ensure the paint properties object was cleared as it is no longer needed. EXPECT_EQ(nullptr, target->GetLayoutObject()->FirstFragment().PaintProperties()); @@ -616,8 +621,8 @@ TEST_P(PaintPropertyTreeUpdateTest, EXPECT_TRUE(properties->Effect()->HasDirectCompositingReasons()); // Removing the animation should remove the effect node. - target->removeAttribute(HTMLNames::classAttr); - GetDocument().View()->UpdateAllLifecyclePhases(); + target->removeAttribute(html_names::kClassAttr); + UpdateAllLifecyclePhasesForTest(); EXPECT_EQ(nullptr, target->GetLayoutObject()->FirstFragment().PaintProperties()); } @@ -630,8 +635,8 @@ TEST_P(PaintPropertyTreeUpdateTest, LoadTestData("transform-animation.html"); Element* target = GetDocument().getElementById("target"); - target->setAttribute(HTMLNames::styleAttr, "transform: translateX(2em)"); - GetDocument().View()->UpdateAllLifecyclePhases(); + target->setAttribute(html_names::kStyleAttr, "transform: translateX(2em)"); + UpdateAllLifecyclePhasesForTest(); const ObjectPaintProperties* properties = target->GetLayoutObject()->FirstFragment().PaintProperties(); @@ -639,8 +644,8 @@ TEST_P(PaintPropertyTreeUpdateTest, properties->Transform()->GetCompositorElementId()); // Remove the animation but keep the transform on the element. - target->removeAttribute(HTMLNames::classAttr); - GetDocument().View()->UpdateAllLifecyclePhases(); + target->removeAttribute(html_names::kClassAttr); + UpdateAllLifecyclePhasesForTest(); EXPECT_NE(CompositorElementId(), properties->Transform()->GetCompositorElementId()); } @@ -653,16 +658,16 @@ TEST_P(PaintPropertyTreeUpdateTest, LoadTestData("opacity-animation.html"); Element* target = GetDocument().getElementById("target"); - target->setAttribute(HTMLNames::styleAttr, "opacity: 0.2"); - GetDocument().View()->UpdateAllLifecyclePhases(); + target->setAttribute(html_names::kStyleAttr, "opacity: 0.2"); + UpdateAllLifecyclePhasesForTest(); const ObjectPaintProperties* properties = target->GetLayoutObject()->FirstFragment().PaintProperties(); EXPECT_NE(CompositorElementId(), properties->Effect()->GetCompositorElementId()); - target->removeAttribute(HTMLNames::classAttr); - GetDocument().View()->UpdateAllLifecyclePhases(); + target->removeAttribute(html_names::kClassAttr); + UpdateAllLifecyclePhasesForTest(); EXPECT_NE(CompositorElementId(), properties->Effect()->GetCompositorElementId()); } @@ -692,8 +697,8 @@ TEST_P(PaintPropertyTreeUpdateTest, PerspectiveOriginUpdatesOnSizeChanges) { perspective->FirstFragment().PaintProperties()->Perspective()->Origin()); auto* contents = GetDocument().getElementById("contents"); - contents->setAttribute(HTMLNames::styleAttr, "height: 200px;"); - GetDocument().View()->UpdateAllLifecyclePhases(); + contents->setAttribute(html_names::kStyleAttr, "height: 200px;"); + UpdateAllLifecyclePhasesForTest(); EXPECT_EQ( TransformationMatrix().ApplyPerspective(100), perspective->FirstFragment().PaintProperties()->Perspective()->Matrix()); @@ -723,8 +728,9 @@ TEST_P(PaintPropertyTreeUpdateTest, TransformUpdatesOnRelativeLengthChanges) { ->Transform() ->Matrix()); - transform->setAttribute(HTMLNames::styleAttr, "width: 200px; height: 300px;"); - GetDocument().View()->UpdateAllLifecyclePhases(); + transform->setAttribute(html_names::kStyleAttr, + "width: 200px; height: 300px;"); + UpdateAllLifecyclePhasesForTest(); EXPECT_EQ(TransformationMatrix().Translate3d(100, 150, 0), transform_object->FirstFragment() .PaintProperties() @@ -757,8 +763,8 @@ TEST_P(PaintPropertyTreeUpdateTest, CSSClipDependingOnSize) { FloatRect(45, 50, 105, 100), clip->FirstFragment().PaintProperties()->CssClip()->ClipRect().Rect()); - outer->setAttribute(HTMLNames::styleAttr, "height: 200px"); - GetDocument().View()->UpdateAllLifecyclePhases(); + outer->setAttribute(html_names::kStyleAttr, "height: 200px"); + UpdateAllLifecyclePhasesForTest(); EXPECT_EQ( FloatRect(45, 50, 105, 200), clip->FirstFragment().PaintProperties()->CssClip()->ClipRect().Rect()); @@ -781,8 +787,8 @@ TEST_P(PaintPropertyTreeUpdateTest, ScrollBoundsChange) { EXPECT_EQ(IntSize(200, 200), scroll_node->ContentsSize()); GetDocument().getElementById("content")->setAttribute( - HTMLNames::styleAttr, "width: 200px; height: 300px"); - GetDocument().View()->UpdateAllLifecyclePhases(); + html_names::kStyleAttr, "width: 200px; height: 300px"); + UpdateAllLifecyclePhasesForTest(); EXPECT_EQ(scroll_node, container->FirstFragment() .PaintProperties() ->ScrollTranslation() @@ -827,11 +833,11 @@ TEST_P(PaintPropertyTreeUpdateTest, ScrollbarWidthChange) { container->FirstFragment().PaintProperties()->OverflowClip(); EXPECT_EQ(FloatSize(80, 80), overflow_clip->ClipRect().Rect().Size()); - auto* new_style = GetDocument().CreateRawElement(HTMLNames::styleTag); + auto* new_style = GetDocument().CreateRawElement(html_names::kStyleTag); new_style->setTextContent("::-webkit-scrollbar {width: 40px; height: 40px}"); GetDocument().body()->AppendChild(new_style); - GetDocument().View()->UpdateAllLifecyclePhases(); + UpdateAllLifecyclePhasesForTest(); EXPECT_EQ(overflow_clip, container->FirstFragment().PaintProperties()->OverflowClip()); EXPECT_EQ(FloatSize(60, 60), overflow_clip->ClipRect().Rect().Size()); @@ -849,8 +855,8 @@ TEST_P(PaintPropertyTreeUpdateTest, Preserve3DChange) { EXPECT_TRUE(transform->FlattensInheritedTransform()); GetDocument().getElementById("parent")->setAttribute( - HTMLNames::styleAttr, "transform-style: preserve-3d"); - GetDocument().View()->UpdateAllLifecyclePhases(); + html_names::kStyleAttr, "transform-style: preserve-3d"); + UpdateAllLifecyclePhasesForTest(); EXPECT_EQ(transform, child->FirstFragment().PaintProperties()->Transform()); EXPECT_FALSE(transform->FlattensInheritedTransform()); } @@ -868,7 +874,7 @@ TEST_P(PaintPropertyTreeUpdateTest, MenuListControlClipChange) { // Should not assert in FindPropertiesNeedingUpdate. ToHTMLSelectElement(select->GetNode())->setSelectedIndex(1); - GetDocument().View()->UpdateAllLifecyclePhases(); + UpdateAllLifecyclePhasesForTest(); EXPECT_NE(nullptr, select->FirstFragment().PaintProperties()->OverflowClip()); } @@ -883,9 +889,9 @@ TEST_P(PaintPropertyTreeUpdateTest, BoxAddRemoveMask) { EXPECT_EQ(nullptr, PaintPropertiesForElement("target")); auto* target = GetDocument().getElementById("target"); - target->setAttribute(HTMLNames::styleAttr, + target->setAttribute(html_names::kStyleAttr, "-webkit-mask: linear-gradient(red, blue)"); - GetDocument().View()->UpdateAllLifecyclePhases(); + UpdateAllLifecyclePhasesForTest(); const auto* properties = PaintPropertiesForElement("target"); ASSERT_NE(nullptr, properties); @@ -895,8 +901,8 @@ TEST_P(PaintPropertyTreeUpdateTest, BoxAddRemoveMask) { ASSERT_NE(nullptr, mask_clip); EXPECT_EQ(FloatRoundedRect(8, 8, 100, 100), mask_clip->ClipRect()); - target->setAttribute(HTMLNames::styleAttr, ""); - GetDocument().View()->UpdateAllLifecyclePhases(); + target->setAttribute(html_names::kStyleAttr, ""); + UpdateAllLifecyclePhasesForTest(); EXPECT_EQ(nullptr, PaintPropertiesForElement("target")); } @@ -920,9 +926,9 @@ TEST_P(PaintPropertyTreeUpdateTest, MaskClipNodeBoxSizeChange) { ASSERT_NE(nullptr, mask_clip); EXPECT_EQ(FloatRoundedRect(8, 8, 100, 100), mask_clip->ClipRect()); - GetDocument().getElementById("target")->setAttribute(HTMLNames::styleAttr, + GetDocument().getElementById("target")->setAttribute(html_names::kStyleAttr, "height: 200px"); - GetDocument().View()->UpdateAllLifecyclePhases(); + UpdateAllLifecyclePhasesForTest(); ASSERT_EQ(mask_clip, properties->MaskClip()); EXPECT_EQ(FloatRoundedRect(8, 8, 100, 200), mask_clip->ClipRect()); @@ -935,9 +941,9 @@ TEST_P(PaintPropertyTreeUpdateTest, InlineAddRemoveMask) { EXPECT_EQ(nullptr, PaintPropertiesForElement("target")); auto* target = GetDocument().getElementById("target"); - target->setAttribute(HTMLNames::styleAttr, + target->setAttribute(html_names::kStyleAttr, "-webkit-mask: linear-gradient(red, blue)"); - GetDocument().View()->UpdateAllLifecyclePhases(); + UpdateAllLifecyclePhasesForTest(); const auto* properties = PaintPropertiesForElement("target"); ASSERT_NE(nullptr, properties); @@ -947,8 +953,8 @@ TEST_P(PaintPropertyTreeUpdateTest, InlineAddRemoveMask) { ASSERT_NE(nullptr, mask_clip); EXPECT_EQ(50, mask_clip->ClipRect().Rect().Width()); - target->setAttribute(HTMLNames::styleAttr, ""); - GetDocument().View()->UpdateAllLifecyclePhases(); + target->setAttribute(html_names::kStyleAttr, ""); + UpdateAllLifecyclePhasesForTest(); EXPECT_EQ(nullptr, PaintPropertiesForElement("target")); } @@ -965,9 +971,9 @@ TEST_P(PaintPropertyTreeUpdateTest, MaskClipNodeInlineBoundsChange) { ASSERT_NE(nullptr, mask_clip); EXPECT_EQ(50, mask_clip->ClipRect().Rect().Width()); - GetDocument().getElementById("img")->setAttribute(HTMLNames::styleAttr, + GetDocument().getElementById("img")->setAttribute(html_names::kStyleAttr, "width: 100px"); - GetDocument().View()->UpdateAllLifecyclePhases(); + UpdateAllLifecyclePhasesForTest(); ASSERT_EQ(mask_clip, properties->MaskClip()); EXPECT_EQ(100, mask_clip->ClipRect().Rect().Width()); @@ -988,7 +994,7 @@ TEST_P(PaintPropertyTreeUpdateTest, AddRemoveSVGMask) { EXPECT_EQ(nullptr, PaintPropertiesForElement("rect")); GetDocument().getElementById("rect")->setAttribute("mask", "url(#mask)"); - GetDocument().View()->UpdateAllLifecyclePhases(); + UpdateAllLifecyclePhasesForTest(); const auto* properties = PaintPropertiesForElement("rect"); ASSERT_NE(nullptr, properties); EXPECT_NE(nullptr, properties->Effect()); @@ -998,7 +1004,7 @@ TEST_P(PaintPropertyTreeUpdateTest, AddRemoveSVGMask) { EXPECT_EQ(FloatRoundedRect(0, 100, 100, 100), mask_clip->ClipRect()); GetDocument().getElementById("rect")->removeAttribute("mask"); - GetDocument().View()->UpdateAllLifecyclePhases(); + UpdateAllLifecyclePhasesForTest(); EXPECT_EQ(nullptr, PaintPropertiesForElement("rect")); } @@ -1025,7 +1031,7 @@ TEST_P(PaintPropertyTreeUpdateTest, SVGMaskTargetBoundsChange) { EXPECT_EQ(FloatRoundedRect(0, 50, 100, 150), mask_clip->ClipRect()); GetDocument().getElementById("rect")->setAttribute("width", "200"); - GetDocument().View()->UpdateAllLifecyclePhases(); + UpdateAllLifecyclePhasesForTest(); EXPECT_NE(nullptr, properties->Effect()); EXPECT_NE(nullptr, properties->Mask()); EXPECT_EQ(FloatRoundedRect(0, 50, 100, 150), mask_clip->ClipRect()); @@ -1047,15 +1053,15 @@ TEST_P(PaintPropertyTreeUpdateTest, WillTransformChangeAboveFixed) { fixed->FirstFragment().LocalBorderBoxProperties().Transform()); ToElement(container->GetNode()) - ->setAttribute(HTMLNames::styleAttr, "will-change: top"); - GetDocument().View()->UpdateAllLifecyclePhases(); + ->setAttribute(html_names::kStyleAttr, "will-change: top"); + UpdateAllLifecyclePhasesForTest(); EXPECT_EQ( GetLayoutView().FirstFragment().LocalBorderBoxProperties().Transform(), fixed->FirstFragment().LocalBorderBoxProperties().Transform()); ToElement(container->GetNode()) - ->setAttribute(HTMLNames::styleAttr, "will-change: transform"); - GetDocument().View()->UpdateAllLifecyclePhases(); + ->setAttribute(html_names::kStyleAttr, "will-change: transform"); + UpdateAllLifecyclePhasesForTest(); EXPECT_EQ(container->FirstFragment().PaintProperties()->Transform(), fixed->FirstFragment().LocalBorderBoxProperties().Transform()); } @@ -1087,17 +1093,19 @@ TEST_P(PaintPropertyTreeUpdateTest, CompositingReasonForAnimation) { ASSERT_TRUE(filter); EXPECT_FALSE(filter->HasDirectCompositingReasons()); - target->setAttribute(HTMLNames::styleAttr, "transform: translateX(11px)"); - GetDocument().View()->UpdateAllLifecyclePhases(); + target->setAttribute(html_names::kStyleAttr, "transform: translateX(11px)"); + UpdateAllLifecyclePhasesForTest(); if (RuntimeEnabledFeatures::SlimmingPaintV2Enabled()) { EXPECT_TRUE(transform->HasDirectCompositingReasons()); EXPECT_TRUE(transform->RequiresCompositingForAnimation()); } - EXPECT_FALSE(filter->HasDirectCompositingReasons()); + // TODO(flackr): After https://crbug.com/900241 is fixed the filter effect + // should no longer have direct compositing reasons due to the animation. + EXPECT_TRUE(filter->HasDirectCompositingReasons()); - target->setAttribute(HTMLNames::styleAttr, + target->setAttribute(html_names::kStyleAttr, "transform: translateX(11px); filter: opacity(40%)"); - GetDocument().View()->UpdateAllLifecyclePhases(); + UpdateAllLifecyclePhasesForTest(); // The transform animation still continues. if (RuntimeEnabledFeatures::SlimmingPaintV2Enabled()) { EXPECT_TRUE(transform->HasDirectCompositingReasons()); @@ -1122,11 +1130,11 @@ TEST_P(PaintPropertyTreeUpdateTest, SVGViewportContainerOverflowChange) { properties->OverflowClip()->ClipRect().Rect()); GetDocument().getElementById("target")->setAttribute("overflow", "visible"); - GetDocument().View()->UpdateAllLifecyclePhases(); + UpdateAllLifecyclePhasesForTest(); EXPECT_EQ(nullptr, PaintPropertiesForElement("target")); GetDocument().getElementById("target")->setAttribute("overflow", "hidden"); - GetDocument().View()->UpdateAllLifecyclePhases(); + UpdateAllLifecyclePhasesForTest(); properties = PaintPropertiesForElement("target"); ASSERT_NE(nullptr, properties); EXPECT_EQ(FloatRect(0, 0, 30, 40), @@ -1148,11 +1156,11 @@ TEST_P(PaintPropertyTreeUpdateTest, SVGForeignObjectOverflowChange) { properties->OverflowClip()->ClipRect().Rect()); GetDocument().getElementById("target")->setAttribute("overflow", "visible"); - GetDocument().View()->UpdateAllLifecyclePhases(); + UpdateAllLifecyclePhasesForTest(); EXPECT_EQ(nullptr, PaintPropertiesForElement("target")); GetDocument().getElementById("target")->setAttribute("overflow", "hidden"); - GetDocument().View()->UpdateAllLifecyclePhases(); + UpdateAllLifecyclePhasesForTest(); properties = PaintPropertiesForElement("target"); ASSERT_NE(nullptr, properties); EXPECT_EQ(FloatRect(10, 20, 30, 40), @@ -1169,11 +1177,11 @@ TEST_P(PaintPropertyTreeBuilderTest, OmitOverflowClipOnSelectionChange) { EXPECT_FALSE(PaintPropertiesForElement("target")->OverflowClip()); GetDocument().GetFrame()->Selection().SelectAll(); - GetDocument().View()->UpdateAllLifecyclePhases(); + UpdateAllLifecyclePhasesForTest(); EXPECT_TRUE(PaintPropertiesForElement("target")->OverflowClip()); GetDocument().GetFrame()->Selection().Clear(); - GetDocument().View()->UpdateAllLifecyclePhases(); + UpdateAllLifecyclePhasesForTest(); EXPECT_FALSE(PaintPropertiesForElement("target")->OverflowClip()); } @@ -1190,11 +1198,11 @@ TEST_P(PaintPropertyTreeBuilderTest, OmitOverflowClipOnCaretChange) { EXPECT_FALSE(PaintPropertiesForElement("target")->OverflowClip()); target->focus(); - GetDocument().View()->UpdateAllLifecyclePhases(); + UpdateAllLifecyclePhasesForTest(); EXPECT_TRUE(PaintPropertiesForElement("target")->OverflowClip()); target->blur(); - GetDocument().View()->UpdateAllLifecyclePhases(); + UpdateAllLifecyclePhasesForTest(); EXPECT_FALSE(PaintPropertiesForElement("target")->OverflowClip()); } @@ -1227,8 +1235,8 @@ TEST_P(PaintPropertyTreeUpdateTest, GetDocument() .getElementById("container") - ->setAttribute(HTMLNames::styleAttr, "width: 500px"); - GetDocument().View()->UpdateAllLifecyclePhases(); + ->setAttribute(html_names::kStyleAttr, "width: 500px"); + UpdateAllLifecyclePhasesForTest(); ASSERT_EQ(2u, NumFragments(flow_thread)); EXPECT_EQ(1000000, FragmentAt(flow_thread, 0) .PaintProperties() @@ -1265,9 +1273,9 @@ TEST_P(PaintPropertyTreeUpdateTest, ASSERT_TRUE(props->Effect()); EXPECT_EQ(props->Effect()->BlendMode(), SkBlendMode::kDarken); - blended_element->setAttribute(HTMLNames::styleAttr, + blended_element->setAttribute(html_names::kStyleAttr, "mix-blend-mode: lighten;"); - GetDocument().View()->UpdateAllLifecyclePhases(); + UpdateAllLifecyclePhasesForTest(); props = blended_element->GetLayoutObject()->FirstFragment().PaintProperties(); ASSERT_TRUE(props->Effect()); @@ -1309,7 +1317,7 @@ TEST_P(PaintPropertyTreeUpdateTest, EnsureSnapContainerData) { )HTML"); GetDocument().View()->Resize(300, 300); - GetDocument().View()->UpdateAllLifecyclePhases(); + UpdateAllLifecyclePhasesForTest(); auto doc_snap_container_data = DocScroll()->GetSnapContainerData(); ASSERT_TRUE(doc_snap_container_data); @@ -1340,13 +1348,13 @@ TEST_P(PaintPropertyTreeUpdateTest, EXPECT_EQ(nullptr, effect_properties->Effect()->OutputClip()); auto* descendant = GetDocument().getElementById("descendant"); - descendant->setAttribute(HTMLNames::styleAttr, "position: relative"); - GetDocument().View()->UpdateAllLifecyclePhases(); + descendant->setAttribute(html_names::kStyleAttr, "position: relative"); + UpdateAllLifecyclePhasesForTest(); EXPECT_EQ(clip_properties->OverflowClip(), effect_properties->Effect()->OutputClip()); - descendant->setAttribute(HTMLNames::styleAttr, "position: absolute"); - GetDocument().View()->UpdateAllLifecyclePhases(); + descendant->setAttribute(html_names::kStyleAttr, "position: absolute"); + UpdateAllLifecyclePhasesForTest(); // The effect's OutputClip is nullptr because of the absolute descendant. EXPECT_EQ(nullptr, effect_properties->Effect()->OutputClip()); } @@ -1384,7 +1392,7 @@ TEST_P(PaintPropertyTreeUpdateTest, ForwardReferencedSVGElementUpdate) { GetDocument().getElementById("filter")->setAttribute("width", "20"); GetDocument().getElementById("svg2")->setAttribute("transform", "translate(2)"); - UpdateAllLifecyclePhases(); + UpdateAllLifecyclePhasesForTest(); EXPECT_NE(nullptr, svg2_properties->Transform()); EXPECT_EQ(svg2_properties->PaintOffsetTranslation(), diff --git a/chromium/third_party/blink/renderer/core/paint/paint_result.h b/chromium/third_party/blink/renderer/core/paint/paint_result.h index 5bad181944c..7f11d3ee920 100644 --- a/chromium/third_party/blink/renderer/core/paint/paint_result.h +++ b/chromium/third_party/blink/renderer/core/paint/paint_result.h @@ -12,12 +12,12 @@ enum PaintResult { // The layer/object is fully painted. This includes cases that nothing needs // painting regardless of the paint rect. kFullyPainted, - // Some part of the layer/object is out of the paint rect and may be not fully - // painted. The results cannot be cached because they may change when paint + // Some part of the layer/object is out of the cull rect and may be not fully + // painted. The results cannot be cached because they may change when cull // rect changes. - kMayBeClippedByPaintDirtyRect, + kMayBeClippedByCullRect, - kMaxPaintResult = kMayBeClippedByPaintDirtyRect, + kMaxPaintResult = kMayBeClippedByCullRect, }; } // namespace blink diff --git a/chromium/third_party/blink/renderer/core/paint/paint_timing.cc b/chromium/third_party/blink/renderer/core/paint/paint_timing.cc index b9963652687..5cd87b85cec 100644 --- a/chromium/third_party/blink/renderer/core/paint/paint_timing.cc +++ b/chromium/third_party/blink/renderer/core/paint/paint_timing.cc @@ -47,7 +47,7 @@ const char PaintTiming::kSupplementName[] = "PaintTiming"; PaintTiming& PaintTiming::From(Document& document) { PaintTiming* timing = Supplement<Document>::From<PaintTiming>(document); if (!timing) { - timing = new PaintTiming(document); + timing = MakeGarbageCollected<PaintTiming>(document); ProvideTo(document, timing); } return *timing; @@ -157,7 +157,7 @@ void PaintTiming::Trace(blink::Visitor* visitor) { PaintTiming::PaintTiming(Document& document) : Supplement<Document>(document), - fmp_detector_(new FirstMeaningfulPaintDetector(this)) {} + fmp_detector_(MakeGarbageCollected<FirstMeaningfulPaintDetector>(this)) {} LocalFrame* PaintTiming::GetFrame() const { return GetSupplementable()->GetFrame(); diff --git a/chromium/third_party/blink/renderer/core/paint/paint_timing.h b/chromium/third_party/blink/renderer/core/paint/paint_timing.h index 7990b7b8e21..ea77cacbba4 100644 --- a/chromium/third_party/blink/renderer/core/paint/paint_timing.h +++ b/chromium/third_party/blink/renderer/core/paint/paint_timing.h @@ -35,6 +35,7 @@ class CORE_EXPORT PaintTiming final public: static const char kSupplementName[]; + explicit PaintTiming(Document&); virtual ~PaintTiming() = default; static PaintTiming& From(Document&); @@ -110,7 +111,6 @@ class CORE_EXPORT PaintTiming final void Trace(blink::Visitor*) override; private: - explicit PaintTiming(Document&); LocalFrame* GetFrame() const; void NotifyPaintTimingChanged(); diff --git a/chromium/third_party/blink/renderer/core/paint/paint_timing_detector.cc b/chromium/third_party/blink/renderer/core/paint/paint_timing_detector.cc new file mode 100644 index 00000000000..7b07edff362 --- /dev/null +++ b/chromium/third_party/blink/renderer/core/paint/paint_timing_detector.cc @@ -0,0 +1,95 @@ +// 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. +#include "third_party/blink/renderer/core/paint/paint_timing_detector.h" +#include "third_party/blink/renderer/core/dom/document.h" +#include "third_party/blink/renderer/core/frame/local_frame.h" +#include "third_party/blink/renderer/core/frame/local_frame_view.h" +#include "third_party/blink/renderer/core/layout/layout_object.h" +#include "third_party/blink/renderer/core/layout/layout_view.h" +#include "third_party/blink/renderer/core/loader/document_loader.h" +#include "third_party/blink/renderer/core/paint/image_paint_timing_detector.h" +#include "third_party/blink/renderer/core/paint/paint_layer.h" +#include "third_party/blink/renderer/core/paint/text_paint_timing_detector.h" +#include "third_party/blink/renderer/platform/geometry/int_rect.h" + +namespace blink { + +PaintTimingDetector::PaintTimingDetector(LocalFrameView* frame_view) + : frame_view_(frame_view), + text_paint_timing_detector_(new TextPaintTimingDetector(frame_view)), + image_paint_timing_detector_(new ImagePaintTimingDetector(frame_view)){}; + +void PaintTimingDetector::NotifyPrePaintFinished() { + text_paint_timing_detector_->OnPrePaintFinished(); + image_paint_timing_detector_->OnPrePaintFinished(); +} + +void PaintTimingDetector::NotifyObjectPrePaint( + const LayoutObject& object, + const PaintLayer& painting_layer) { + // Todo(maxlg): incoperate iframe's statistics + if (!frame_view_->GetFrame().IsMainFrame()) + return; + + if (object.IsText()) { + text_paint_timing_detector_->RecordText(object, painting_layer); + } + if (object.IsImage() || object.IsVideo() || object.IsSVGImage() || + ImagePaintTimingDetector::HasContentfulBackgroundImage(object)) { + image_paint_timing_detector_->RecordImage(object, painting_layer); + } + // Todo(maxlg): add other detectors here. +} + +void PaintTimingDetector::NotifyNodeRemoved(const LayoutObject& object) { + if (!object.GetNode()) + return; + text_paint_timing_detector_->NotifyNodeRemoved( + DOMNodeIds::IdForNode(object.GetNode())); + image_paint_timing_detector_->NotifyNodeRemoved( + DOMNodeIds::IdForNode(object.GetNode())); +} + +void PaintTimingDetector::DidChangePerformanceTiming() { + Document* document = frame_view_->GetFrame().GetDocument(); + if (!document) + return; + DocumentLoader* loader = document->Loader(); + if (!loader) + return; + loader->DidChangePerformanceTiming(); +} + +uint64_t PaintTimingDetector::CalculateVisualSize( + const LayoutRect& invalidated_rect, + const PaintLayer& painting_layer) const { + // This case should be dealt with outside the function. + DCHECK(!invalidated_rect.IsEmpty()); + + // As Layout objects live in different transform spaces, the object's rect + // should be projected to the viewport's transform space. + IntRect visual_rect = SaturatedRect(EnclosedIntRect(invalidated_rect)); + painting_layer.GetLayoutObject().FirstFragment().MapRectToFragment( + painting_layer.GetLayoutObject().View()->FirstFragment(), visual_rect); + + // A visual rect means the part of the rect that's visible within + // the viewport. We define the size of it as visual size. + ScrollableArea* scrollable_area = frame_view_->GetScrollableArea(); + DCHECK(scrollable_area); + IntRect viewport = scrollable_area->VisibleContentRect(); + // Use saturated rect to avoid integer-overflow. + visual_rect.Intersect(SaturatedRect(viewport)); + return visual_rect.Size().Area(); +} + +void PaintTimingDetector::Dispose() { + text_paint_timing_detector_->Dispose(); +} + +void PaintTimingDetector::Trace(Visitor* visitor) { + visitor->Trace(text_paint_timing_detector_); + visitor->Trace(image_paint_timing_detector_); + visitor->Trace(frame_view_); +} +} // namespace blink diff --git a/chromium/third_party/blink/renderer/core/paint/paint_tracker.h b/chromium/third_party/blink/renderer/core/paint/paint_timing_detector.h index 25b52224581..4002f0873b2 100644 --- a/chromium/third_party/blink/renderer/core/paint/paint_tracker.h +++ b/chromium/third_party/blink/renderer/core/paint/paint_timing_detector.h @@ -2,8 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#ifndef THIRD_PARTY_BLINK_RENDERER_CORE_PAINT_PAINT_TRACKER_H_ -#define THIRD_PARTY_BLINK_RENDERER_CORE_PAINT_PAINT_TRACKER_H_ +#ifndef THIRD_PARTY_BLINK_RENDERER_CORE_PAINT_PAINT_TIMING_DETECTOR_H_ +#define THIRD_PARTY_BLINK_RENDERER_CORE_PAINT_PAINT_TIMING_DETECTOR_H_ #include "third_party/blink/renderer/core/core_export.h" #include "third_party/blink/renderer/platform/heap/member.h" @@ -13,23 +13,28 @@ namespace blink { class LayoutObject; class LocalFrameView; class PaintLayer; +class LayoutRect; class TextPaintTimingDetector; class ImagePaintTimingDetector; -// PaintTracker contains some of paint metric detectors, providing common -// infrastructure for these detectors. +// PaintTimingDetector contains some of paint metric detectors, +// providing common infrastructure for these detectors. // // Users has to enable 'loading' trace category to enable the metrics. // // See also: // https://docs.google.com/document/d/1DRVd4a2VU8-yyWftgOparZF-sf16daf0vfbsHuz2rws/edit -class CORE_EXPORT PaintTracker : public GarbageCollected<PaintTracker> { +class CORE_EXPORT PaintTimingDetector + : public GarbageCollected<PaintTimingDetector> { public: - PaintTracker(LocalFrameView*); + PaintTimingDetector(LocalFrameView*); void NotifyObjectPrePaint(const LayoutObject& object, const PaintLayer& painting_layer); void NotifyNodeRemoved(const LayoutObject& object); void NotifyPrePaintFinished(); + void DidChangePerformanceTiming(); + uint64_t CalculateVisualSize(const LayoutRect& invalidated_rect, + const PaintLayer& painting_layer) const; void Dispose(); TextPaintTimingDetector& GetTextPaintTimingDetector() { @@ -48,4 +53,4 @@ class CORE_EXPORT PaintTracker : public GarbageCollected<PaintTracker> { } // namespace blink -#endif // THIRD_PARTY_BLINK_RENDERER_CORE_PAINT_PAINT_TRACKER_H_ +#endif // THIRD_PARTY_BLINK_RENDERER_CORE_PAINT_PAINT_TIMING_DETECTOR_H_ diff --git a/chromium/third_party/blink/renderer/core/paint/paint_tracker.cc b/chromium/third_party/blink/renderer/core/paint/paint_tracker.cc deleted file mode 100644 index a6bccff1c63..00000000000 --- a/chromium/third_party/blink/renderer/core/paint/paint_tracker.cc +++ /dev/null @@ -1,57 +0,0 @@ -// 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. -#include "third_party/blink/renderer/core/paint/paint_tracker.h" -#include "third_party/blink/renderer/core/frame/local_frame.h" -#include "third_party/blink/renderer/core/frame/local_frame_view.h" -#include "third_party/blink/renderer/core/layout/layout_object.h" -#include "third_party/blink/renderer/core/paint/image_paint_timing_detector.h" -#include "third_party/blink/renderer/core/paint/paint_layer.h" -#include "third_party/blink/renderer/core/paint/text_paint_timing_detector.h" - -namespace blink { - -PaintTracker::PaintTracker(LocalFrameView* frame_view) - : frame_view_(frame_view), - text_paint_timing_detector_(new TextPaintTimingDetector(frame_view)), - image_paint_timing_detector_(new ImagePaintTimingDetector(frame_view)){}; - -void PaintTracker::NotifyPrePaintFinished() { - text_paint_timing_detector_->OnPrePaintFinished(); - image_paint_timing_detector_->OnPrePaintFinished(); -} - -void PaintTracker::NotifyObjectPrePaint(const LayoutObject& object, - const PaintLayer& painting_layer) { - // Todo(maxlg): incoperate iframe's statistics - if (!frame_view_->GetFrame().IsMainFrame()) - return; - - if (object.IsText()) { - text_paint_timing_detector_->RecordText(object, painting_layer); - } - if (object.IsImage()) { - image_paint_timing_detector_->RecordImage(object, painting_layer); - } - // Todo(maxlg): add other detectors here. -} - -void PaintTracker::NotifyNodeRemoved(const LayoutObject& object) { - if (!object.GetNode()) - return; - text_paint_timing_detector_->NotifyNodeRemoved( - DOMNodeIds::IdForNode(object.GetNode())); - image_paint_timing_detector_->NotifyNodeRemoved( - DOMNodeIds::IdForNode(object.GetNode())); -} - -void PaintTracker::Dispose() { - text_paint_timing_detector_->Dispose(); -} - -void PaintTracker::Trace(Visitor* visitor) { - visitor->Trace(text_paint_timing_detector_); - visitor->Trace(image_paint_timing_detector_); - visitor->Trace(frame_view_); -} -} // namespace blink diff --git a/chromium/third_party/blink/renderer/core/paint/pre_paint_tree_walk.cc b/chromium/third_party/blink/renderer/core/paint/pre_paint_tree_walk.cc index 8948ebaed15..d535454c767 100644 --- a/chromium/third_party/blink/renderer/core/paint/pre_paint_tree_walk.cc +++ b/chromium/third_party/blink/renderer/core/paint/pre_paint_tree_walk.cc @@ -22,7 +22,7 @@ #include "third_party/blink/renderer/core/paint/ng/ng_paint_fragment.h" #include "third_party/blink/renderer/core/paint/paint_layer.h" #include "third_party/blink/renderer/core/paint/paint_property_tree_printer.h" -#include "third_party/blink/renderer/core/paint/paint_tracker.h" +#include "third_party/blink/renderer/core/paint/paint_timing_detector.h" #include "third_party/blink/renderer/platform/graphics/paint/geometry_mapper.h" namespace blink { @@ -133,8 +133,9 @@ void PrePaintTreeWalk::Walk(LocalFrameView& frame_view) { if (RuntimeEnabledFeatures::JankTrackingEnabled()) frame_view.GetJankTracker().NotifyPrePaintFinished(); - if (RuntimeEnabledFeatures::PaintTrackingEnabled()) - frame_view.GetPaintTracker().NotifyPrePaintFinished(); + if (RuntimeEnabledFeatures::FirstContentfulPaintPlusPlusEnabled()) { + frame_view.GetPaintTimingDetector().NotifyPrePaintFinished(); + } context_storage_.pop_back(); } @@ -158,7 +159,7 @@ bool HasBlockingTouchEventHandler(const LocalFrame& frame, const auto* blocking = registry.EventHandlerTargets( EventHandlerRegistry::kTouchStartOrMoveEventBlocking); const auto* blocking_low_latency = registry.EventHandlerTargets( - EventHandlerRegistry::kTouchStartOrMoveEventBlocking); + EventHandlerRegistry::kTouchStartOrMoveEventBlockingLowLatency); return blocking->Contains(&target) || blocking_low_latency->Contains(&target); } @@ -327,9 +328,8 @@ void PrePaintTreeWalk::WalkInternal(const LayoutObject& object, property_tree_builder.emplace(object, *context.tree_builder_context); property_changed = property_tree_builder->UpdateForSelf(); - if (property_changed && - !context.tree_builder_context - ->supports_composited_raster_invalidation) { + if (property_changed && !context.tree_builder_context + ->supports_composited_raster_invalidation) { paint_invalidator_context.subtree_flags |= PaintInvalidatorContext::kSubtreeFullInvalidation; } @@ -375,13 +375,20 @@ void PrePaintTreeWalk::WalkInternal(const LayoutObject& object, object, paint_invalidator_context.old_visual_rect, *paint_invalidator_context.painting_layer); } - if (RuntimeEnabledFeatures::PaintTrackingEnabled()) { - object.GetFrameView()->GetPaintTracker().NotifyObjectPrePaint( + if (RuntimeEnabledFeatures::FirstContentfulPaintPlusPlusEnabled()) { + object.GetFrameView()->GetPaintTimingDetector().NotifyObjectPrePaint( object, *paint_invalidator_context.painting_layer); } } void PrePaintTreeWalk::Walk(const LayoutObject& object) { + if (object.PrePaintBlockedByDisplayLock()) + return; + // TODO(vmpstr): Technically we should do this after prepaint finishes, but + // due to a possible early out this is more convenient. We should change this + // to RAII. + object.NotifyDisplayLockDidPrePaint(); + // We need to be careful not to have a reference to the parent context, since // this reference will be to the context_storage_ memory which may be // reallocated during this function call. diff --git a/chromium/third_party/blink/renderer/core/paint/pre_paint_tree_walk_test.cc b/chromium/third_party/blink/renderer/core/paint/pre_paint_tree_walk_test.cc index cca36abecfc..9e7fd97d66f 100644 --- a/chromium/third_party/blink/renderer/core/paint/pre_paint_tree_walk_test.cc +++ b/chromium/third_party/blink/renderer/core/paint/pre_paint_tree_walk_test.cc @@ -77,8 +77,8 @@ TEST_P(PrePaintTreeWalkTest, PropertyTreesRebuiltWithBorderInvalidation) { EXPECT_EQ(nullptr, transformed_properties->Transform()); // Cause a paint invalidation. - transformed_element->setAttribute(HTMLNames::classAttr, "border"); - GetDocument().View()->UpdateAllLifecyclePhases(); + transformed_element->setAttribute(html_names::kClassAttr, "border"); + UpdateAllLifecyclePhasesForTest(); // Should have changed back. EXPECT_EQ(TransformationMatrix().Translate(100, 100), @@ -92,7 +92,7 @@ TEST_P(PrePaintTreeWalkTest, PropertyTreesRebuiltWithFrameScroll) { // Cause a scroll invalidation and ensure the translation is updated. GetDocument().domWindow()->scrollTo(0, 100); - GetDocument().View()->UpdateAllLifecyclePhases(); + UpdateAllLifecyclePhasesForTest(); EXPECT_EQ(TransformationMatrix().Translate(0, -100), FrameScrollTranslation()->Matrix()); @@ -115,8 +115,8 @@ TEST_P(PrePaintTreeWalkTest, PropertyTreesRebuiltWithCSSTransformInvalidation) { transformed_properties->Transform()->Matrix()); // Invalidate the CSS transform property. - transformed_element->setAttribute(HTMLNames::classAttr, "transformB"); - GetDocument().View()->UpdateAllLifecyclePhases(); + transformed_element->setAttribute(html_names::kClassAttr, "transformB"); + UpdateAllLifecyclePhasesForTest(); // The transform should have changed. EXPECT_EQ(TransformationMatrix().Translate(200, 200), @@ -138,8 +138,8 @@ TEST_P(PrePaintTreeWalkTest, PropertyTreesRebuiltWithOpacityInvalidation) { EXPECT_EQ(0.9f, transparent_properties->Effect()->Opacity()); // Invalidate the opacity property. - transparent_element->setAttribute(HTMLNames::classAttr, "opacityB"); - GetDocument().View()->UpdateAllLifecyclePhases(); + transparent_element->setAttribute(html_names::kClassAttr, "opacityB"); + UpdateAllLifecyclePhasesForTest(); // The opacity should have changed. EXPECT_EQ(0.4f, transparent_properties->Effect()->Opacity()); @@ -165,7 +165,7 @@ TEST_P(PrePaintTreeWalkTest, ClearSubsequenceCachingClipChange) { EXPECT_FALSE(child_paint_layer->NeedsRepaint()); EXPECT_FALSE(child_paint_layer->NeedsPaintPhaseFloat()); - parent->setAttribute(HTMLNames::classAttr, "clip"); + parent->setAttribute(html_names::kClassAttr, "clip"); GetDocument().View()->UpdateAllLifecyclePhasesExceptPaint(); EXPECT_TRUE(child_paint_layer->NeedsRepaint()); @@ -191,7 +191,7 @@ TEST_P(PrePaintTreeWalkTest, ClearSubsequenceCachingClipChange2DTransform) { EXPECT_FALSE(child_paint_layer->NeedsRepaint()); EXPECT_FALSE(child_paint_layer->NeedsPaintPhaseFloat()); - parent->setAttribute(HTMLNames::classAttr, "clip"); + parent->setAttribute(html_names::kClassAttr, "clip"); GetDocument().View()->UpdateAllLifecyclePhasesExceptPaint(); EXPECT_TRUE(child_paint_layer->NeedsRepaint()); @@ -220,7 +220,7 @@ TEST_P(PrePaintTreeWalkTest, ClearSubsequenceCachingClipChangePosAbs) { // This changes clips for absolute-positioned descendants of "child" but not // normal-position ones, which are already clipped to 50x50. - parent->setAttribute(HTMLNames::classAttr, "clip"); + parent->setAttribute(html_names::kClassAttr, "clip"); GetDocument().View()->UpdateAllLifecyclePhasesExceptPaint(); EXPECT_TRUE(child_paint_layer->NeedsRepaint()); @@ -249,7 +249,7 @@ TEST_P(PrePaintTreeWalkTest, ClearSubsequenceCachingClipChangePosFixed) { // This changes clips for absolute-positioned descendants of "child" but not // normal-position ones, which are already clipped to 50x50. - parent->setAttribute(HTMLNames::classAttr, "clip"); + parent->setAttribute(html_names::kClassAttr, "clip"); GetDocument().View()->UpdateAllLifecyclePhasesExceptPaint(); EXPECT_TRUE(child_paint_layer->NeedsRepaint()); @@ -274,7 +274,7 @@ TEST_P(PrePaintTreeWalkTest, VisualRectClipForceSubtree) { auto* grandchild = GetLayoutObjectByElementId("grandchild"); GetDocument().getElementById("parent")->removeAttribute("style"); - GetDocument().View()->UpdateAllLifecyclePhases(); + UpdateAllLifecyclePhasesForTest(); EXPECT_EQ(200, grandchild->FirstFragment().VisualRect().Height()); } @@ -295,11 +295,11 @@ TEST_P(PrePaintTreeWalkTest, ClipChangeHasRadius) { auto* target = GetDocument().getElementById("target"); auto* target_object = ToLayoutBoxModelObject(target->GetLayoutObject()); - target->setAttribute(HTMLNames::styleAttr, "border-radius: 5px"); + target->setAttribute(html_names::kStyleAttr, "border-radius: 5px"); GetDocument().View()->UpdateAllLifecyclePhasesExceptPaint(); EXPECT_TRUE(target_object->Layer()->NeedsRepaint()); // And should not trigger any assert failure. - GetDocument().View()->UpdateAllLifecyclePhases(); + UpdateAllLifecyclePhasesForTest(); } namespace { @@ -311,7 +311,7 @@ class PrePaintTreeWalkMockEventListener final : public EventListener { return this == &other; } - void handleEvent(ExecutionContext*, Event*) final {} + void Invoke(ExecutionContext*, Event*) final {} }; } // namespace @@ -326,7 +326,7 @@ TEST_P(PrePaintTreeWalkTest, InsideBlockingTouchEventHandlerUpdate) { </div> )HTML"); - GetDocument().View()->UpdateAllLifecyclePhases(); + UpdateAllLifecyclePhasesForTest(); auto& ancestor = *GetLayoutObjectByElementId("ancestor"); auto& handler = *GetLayoutObjectByElementId("handler"); auto& descendant = *GetLayoutObjectByElementId("descendant"); @@ -346,7 +346,7 @@ TEST_P(PrePaintTreeWalkTest, InsideBlockingTouchEventHandlerUpdate) { PrePaintTreeWalkMockEventListener* callback = new PrePaintTreeWalkMockEventListener(); auto* handler_element = GetDocument().getElementById("handler"); - handler_element->addEventListener(EventTypeNames::touchstart, callback); + handler_element->addEventListener(event_type_names::kTouchstart, callback); EXPECT_FALSE(ancestor.EffectiveWhitelistedTouchActionChanged()); EXPECT_TRUE(handler.EffectiveWhitelistedTouchActionChanged()); @@ -356,7 +356,7 @@ TEST_P(PrePaintTreeWalkTest, InsideBlockingTouchEventHandlerUpdate) { EXPECT_FALSE(handler.DescendantEffectiveWhitelistedTouchActionChanged()); EXPECT_FALSE(descendant.DescendantEffectiveWhitelistedTouchActionChanged()); - GetDocument().View()->UpdateAllLifecyclePhases(); + UpdateAllLifecyclePhasesForTest(); EXPECT_FALSE(ancestor.EffectiveWhitelistedTouchActionChanged()); EXPECT_FALSE(handler.EffectiveWhitelistedTouchActionChanged()); EXPECT_FALSE(descendant.EffectiveWhitelistedTouchActionChanged()); @@ -382,7 +382,7 @@ TEST_P(PrePaintTreeWalkTest, EffectiveTouchActionStyleUpdate) { </div> )HTML"); - GetDocument().View()->UpdateAllLifecyclePhases(); + UpdateAllLifecyclePhasesForTest(); auto& ancestor = *GetLayoutObjectByElementId("ancestor"); auto& touchaction = *GetLayoutObjectByElementId("touchaction"); auto& descendant = *GetLayoutObjectByElementId("descendant"); @@ -396,7 +396,7 @@ TEST_P(PrePaintTreeWalkTest, EffectiveTouchActionStyleUpdate) { GetDocument() .getElementById("touchaction") - ->setAttribute(HTMLNames::classAttr, "touchaction"); + ->setAttribute(html_names::kClassAttr, "touchaction"); GetDocument().View()->UpdateLifecycleToLayoutClean(); EXPECT_FALSE(ancestor.EffectiveWhitelistedTouchActionChanged()); EXPECT_TRUE(touchaction.EffectiveWhitelistedTouchActionChanged()); @@ -405,7 +405,7 @@ TEST_P(PrePaintTreeWalkTest, EffectiveTouchActionStyleUpdate) { EXPECT_FALSE(touchaction.DescendantEffectiveWhitelistedTouchActionChanged()); EXPECT_FALSE(descendant.DescendantEffectiveWhitelistedTouchActionChanged()); - GetDocument().View()->UpdateAllLifecyclePhases(); + UpdateAllLifecyclePhasesForTest(); EXPECT_FALSE(ancestor.EffectiveWhitelistedTouchActionChanged()); EXPECT_FALSE(touchaction.EffectiveWhitelistedTouchActionChanged()); EXPECT_FALSE(descendant.EffectiveWhitelistedTouchActionChanged()); @@ -424,10 +424,10 @@ TEST_P(PrePaintTreeWalkTest, ClipChangesDoNotCauseVisualRectUpdates) { </div> )HTML"); - GetDocument().getElementById("parent")->setAttribute(HTMLNames::styleAttr, + GetDocument().getElementById("parent")->setAttribute(html_names::kStyleAttr, "border-radius: 5px"); - GetDocument().View()->UpdateAllLifecyclePhases(); + UpdateAllLifecyclePhasesForTest(); auto& parent = *GetLayoutObjectByElementId("parent"); auto& child = *GetLayoutObjectByElementId("child"); @@ -442,10 +442,10 @@ TEST_P(PrePaintTreeWalkTest, ClipChangesDoNotCauseVisualRectUpdates) { // Cause the child clip to change without changing paint property tree // topology. - GetDocument().getElementById("parent")->setAttribute(HTMLNames::styleAttr, + GetDocument().getElementById("parent")->setAttribute(html_names::kStyleAttr, "border-radius: 6px"); - GetDocument().View()->UpdateAllLifecyclePhases(); + UpdateAllLifecyclePhasesForTest(); EXPECT_EQ(100, parent.FirstFragment().VisualRect().Width()); EXPECT_EQ(100, parent.FirstFragment().VisualRect().Height()); EXPECT_EQ(100, child.FirstFragment().VisualRect().Width()); diff --git a/chromium/third_party/blink/renderer/core/paint/replaced_painter.cc b/chromium/third_party/blink/renderer/core/paint/replaced_painter.cc index 9e07504eb60..c94a14b0101 100644 --- a/chromium/third_party/blink/renderer/core/paint/replaced_painter.cc +++ b/chromium/third_party/blink/renderer/core/paint/replaced_painter.cc @@ -12,6 +12,7 @@ #include "third_party/blink/renderer/core/paint/object_painter.h" #include "third_party/blink/renderer/core/paint/paint_info.h" #include "third_party/blink/renderer/core/paint/paint_layer.h" +#include "third_party/blink/renderer/core/paint/paint_layer_scrollable_area.h" #include "third_party/blink/renderer/core/paint/scoped_paint_state.h" #include "third_party/blink/renderer/core/paint/scrollable_area_painter.h" #include "third_party/blink/renderer/core/paint/selection_painting_utils.h" @@ -51,9 +52,7 @@ ScopedReplacedContentPaintState::ScopedReplacedContentPaintState( if (content_transform && replaced.IsSVGRoot()) { new_properties.SetTransform(content_transform); adjusted_paint_info_.emplace(input_paint_info_); - DCHECK(content_transform->Matrix().IsAffine()); - adjusted_paint_info_->UpdateCullRect( - content_transform->Matrix().ToAffineTransform()); + adjusted_paint_info_->TransformCullRect(content_transform); property_changed = true; } @@ -65,14 +64,6 @@ ScopedReplacedContentPaintState::ScopedReplacedContentPaintState( property_changed = true; } - // Check filter for optimized image policy violation highlights, which - // may be applied locally. - if (paint_properties->Filter() && - (!replaced.HasLayer() || !replaced.Layer()->IsSelfPaintingLayer())) { - new_properties.SetEffect(paint_properties->Filter()); - property_changed = true; - } - if (property_changed) { chunk_properties_.emplace(input_paint_info_.context.GetPaintController(), new_properties, replaced, @@ -82,6 +73,15 @@ ScopedReplacedContentPaintState::ScopedReplacedContentPaintState( } // anonymous namespace +bool ReplacedPainter::ShouldPaintBoxDecorationBackground( + const PaintInfo& paint_info) { + // LayoutFrameSet paints everything in the foreground phase. + if (layout_replaced_.IsLayoutEmbeddedContent() && + layout_replaced_.Parent()->IsFrameSet()) + return paint_info.phase == PaintPhase::kForeground; + return ShouldPaintSelfBlockBackground(paint_info.phase); +} + void ReplacedPainter::Paint(const PaintInfo& paint_info) { // TODO(crbug.com/797779): For now embedded contents don't know whether // they are painted in a fragmented context and may do something bad in a @@ -102,16 +102,32 @@ void ReplacedPainter::Paint(const PaintInfo& paint_info) { auto paint_offset = paint_state.PaintOffset(); LayoutRect border_rect(paint_offset, layout_replaced_.Size()); - if (ShouldPaintSelfBlockBackground(local_paint_info.phase)) { - if (layout_replaced_.StyleRef().Visibility() == EVisibility::kVisible && - layout_replaced_.HasBoxDecorationBackground()) { + if (ShouldPaintBoxDecorationBackground(local_paint_info)) { + bool should_paint_background = false; + if (layout_replaced_.StyleRef().Visibility() == EVisibility::kVisible) { + if (layout_replaced_.HasBoxDecorationBackground()) + should_paint_background = true; + if (RuntimeEnabledFeatures::PaintTouchActionRectsEnabled() && + layout_replaced_.HasEffectiveWhitelistedTouchAction()) { + should_paint_background = true; + } + } + if (should_paint_background) { if (layout_replaced_.HasLayer() && layout_replaced_.Layer()->GetCompositingState() == kPaintsIntoOwnBacking && layout_replaced_.Layer() ->GetCompositedLayerMapping() - ->DrawsBackgroundOntoContentLayer()) + ->DrawsBackgroundOntoContentLayer()) { + // If the background paints into the content layer, we can skip painting + // the background but still need to paint the touch action rects. + if (RuntimeEnabledFeatures::PaintTouchActionRectsEnabled()) { + BoxPainter(layout_replaced_) + .RecordHitTestData(local_paint_info, border_rect, + layout_replaced_); + } return; + } BoxPainter(layout_replaced_) .PaintBoxDecorationBackground(local_paint_info, paint_offset); @@ -146,18 +162,19 @@ void ReplacedPainter::Paint(const PaintInfo& paint_info) { if (skip_clip || !layout_replaced_.PhysicalContentBoxRect().IsEmpty()) { ScopedReplacedContentPaintState content_paint_state(paint_state, layout_replaced_); - if (RuntimeEnabledFeatures::PaintTouchActionRectsEnabled()) { - RecordHitTestData(content_paint_state.GetPaintInfo(), - content_paint_state.PaintOffset()); - } layout_replaced_.PaintReplaced(content_paint_state.GetPaintInfo(), content_paint_state.PaintOffset()); } if (layout_replaced_.CanResize()) { - ScrollableAreaPainter(*layout_replaced_.Layer()->GetScrollableArea()) - .PaintResizer(local_paint_info.context, RoundedIntPoint(paint_offset), - local_paint_info.GetCullRect()); + auto* scrollable_area = layout_replaced_.GetScrollableArea(); + DCHECK(scrollable_area); + if (!scrollable_area->HasLayerForScrollCorner()) { + ScrollableAreaPainter(*scrollable_area) + .PaintResizer(local_paint_info.context, RoundedIntPoint(paint_offset), + local_paint_info.GetCullRect()); + } + // Otherwise the resizer will be painted by the scroll corner layer. } // The selection tint never gets clipped by border-radius rounding, since we @@ -184,26 +201,6 @@ void ReplacedPainter::Paint(const PaintInfo& paint_info) { } } -void ReplacedPainter::RecordHitTestData(const PaintInfo& paint_info, - const LayoutPoint& paint_offset) { - // Hit test display items are only needed for compositing. This flag is used - // for for printing and drag images which do not need hit testing. - if (paint_info.GetGlobalPaintFlags() & kGlobalPaintFlattenCompositingLayers) - return; - - if (paint_info.phase != PaintPhase::kForeground) - return; - - auto touch_action = layout_replaced_.EffectiveWhitelistedTouchAction(); - if (touch_action == TouchAction::kTouchActionAuto) - return; - - auto rect = layout_replaced_.VisualOverflowRect(); - rect.MoveBy(paint_offset); - HitTestData::RecordHitTestRect(paint_info.context, layout_replaced_, - HitTestRect(rect, touch_action)); -} - bool ReplacedPainter::ShouldPaint(const ScopedPaintState& paint_state) const { const auto& paint_info = paint_state.GetPaintInfo(); if (paint_info.phase != PaintPhase::kForeground && diff --git a/chromium/third_party/blink/renderer/core/paint/replaced_painter.h b/chromium/third_party/blink/renderer/core/paint/replaced_painter.h index 6da46de130f..59a32561175 100644 --- a/chromium/third_party/blink/renderer/core/paint/replaced_painter.h +++ b/chromium/third_party/blink/renderer/core/paint/replaced_painter.h @@ -26,10 +26,8 @@ class ReplacedPainter { bool ShouldPaint(const ScopedPaintState&) const; private: - // Paint a hit test display item and record hit test data. This should be - // called in the background paint phase even if there is no other painted - // content. - void RecordHitTestData(const PaintInfo&, const LayoutPoint& paint_offset); + bool ShouldPaintBoxDecorationBackground(const PaintInfo&); + const LayoutReplaced& layout_replaced_; }; diff --git a/chromium/third_party/blink/renderer/core/paint/scoped_paint_state.cc b/chromium/third_party/blink/renderer/core/paint/scoped_paint_state.cc index 7884fe3e955..2f48a9aae32 100644 --- a/chromium/third_party/blink/renderer/core/paint/scoped_paint_state.cc +++ b/chromium/third_party/blink/renderer/core/paint/scoped_paint_state.cc @@ -32,9 +32,7 @@ void ScopedPaintState::AdjustForPaintOffsetTranslation( } adjusted_paint_info_.emplace(input_paint_info_); - DCHECK(paint_offset_translation->Matrix().IsAffine()); - adjusted_paint_info_->UpdateCullRect( - paint_offset_translation->Matrix().ToAffineTransform()); + adjusted_paint_info_->TransformCullRect(paint_offset_translation); } void ScopedPaintState::FinishPaintOffsetTranslationAsDrawing() { @@ -45,11 +43,7 @@ void ScopedPaintState::FinishPaintOffsetTranslationAsDrawing() { } void ScopedBoxContentsPaintState::AdjustForBoxContents(const LayoutBox& box) { - DCHECK((input_paint_info_.phase != PaintPhase::kSelfBlockBackgroundOnly || - BoxModelObjectPainter:: - IsPaintingBackgroundOfPaintContainerIntoScrollingContentsLayer( - &box, input_paint_info_)) && - input_paint_info_.phase != PaintPhase::kSelfOutlineOnly && + DCHECK(input_paint_info_.phase != PaintPhase::kSelfOutlineOnly && input_paint_info_.phase != PaintPhase::kMask); if (!fragment_to_paint_ || !fragment_to_paint_->HasLocalBorderBoxProperties()) @@ -75,16 +69,17 @@ void ScopedBoxContentsPaintState::AdjustForBoxContents(const LayoutBox& box) { // descendant objects' Paint() method, e.g. inline boxes. paint_offset_ += box.ScrollOrigin(); + // If a LayoutView is using infinite cull rect, we are painting with viewport + // clip disabled, so don't cull the scrolling contents. This is just for + // completeness because we always paint the whole scrolling background even + // with a smaller cull rect, and the scrolling document contents are under the + // layer of document element which will use infinite cull rect calculated in + // PaintLayerPainter::AdjustForPaintProperties(). + if (box.IsLayoutView() && input_paint_info_.GetCullRect().IsInfinite()) + return; + adjusted_paint_info_.emplace(input_paint_info_); - DCHECK(scroll_translation->Matrix().IsAffine()); - if (RuntimeEnabledFeatures::SlimmingPaintV2Enabled()) { - adjusted_paint_info_->UpdateCullRectForScrollingContents( - EnclosingIntRect(box.OverflowClipRect(paint_offset_)), - scroll_translation->Matrix().ToAffineTransform()); - } else { - adjusted_paint_info_->UpdateCullRect( - scroll_translation->Matrix().ToAffineTransform()); - } + adjusted_paint_info_->TransformCullRect(scroll_translation); } } // namespace blink diff --git a/chromium/third_party/blink/renderer/core/paint/scoped_paint_state.h b/chromium/third_party/blink/renderer/core/paint/scoped_paint_state.h index a19873d93cd..c2551fc99b1 100644 --- a/chromium/third_party/blink/renderer/core/paint/scoped_paint_state.h +++ b/chromium/third_party/blink/renderer/core/paint/scoped_paint_state.h @@ -86,8 +86,7 @@ class ScopedPaintState { bool LocalRectIntersectsCullRect(const LayoutRect& local_rect) const { LayoutRect rect_in_paint_info_space = local_rect; rect_in_paint_info_space.MoveBy(PaintOffset()); - return GetPaintInfo().GetCullRect().IntersectsCullRect( - rect_in_paint_info_space); + return GetPaintInfo().GetCullRect().Intersects(rect_in_paint_info_space); } protected: diff --git a/chromium/third_party/blink/renderer/core/paint/scoped_svg_paint_state.cc b/chromium/third_party/blink/renderer/core/paint/scoped_svg_paint_state.cc index 553dc23786b..5c97cec87a4 100644 --- a/chromium/third_party/blink/renderer/core/paint/scoped_svg_paint_state.cc +++ b/chromium/third_party/blink/renderer/core/paint/scoped_svg_paint_state.cc @@ -177,7 +177,7 @@ bool ScopedSVGPaintState::ApplyFilterIfNecessary(SVGResources* resources) { // Because we cache the filter contents and do not invalidate on paint // invalidation rect changes, we need to paint the entire filter region // so elements outside the initial paint (due to scrolling, etc) paint. - filter_paint_info_->cull_rect_ = CullRect(LayoutRect::InfiniteIntRect()); + filter_paint_info_->ApplyInfiniteCullRect(); return true; } diff --git a/chromium/third_party/blink/renderer/core/paint/scrollable_area_painter.cc b/chromium/third_party/blink/renderer/core/paint/scrollable_area_painter.cc index 13aa91a200f..f2f01a0e491 100644 --- a/chromium/third_party/blink/renderer/core/paint/scrollable_area_painter.cc +++ b/chromium/third_party/blink/renderer/core/paint/scrollable_area_painter.cc @@ -36,7 +36,7 @@ void ScrollableAreaPainter::PaintResizer(GraphicsContext& context, abs_rect.MoveBy(paint_offset); if (const auto* resizer = GetScrollableArea().Resizer()) { - if (!cull_rect.IntersectsCullRect(abs_rect)) + if (!cull_rect.Intersects(abs_rect)) return; ScrollbarPainter::PaintIntoRect(*resizer, context, paint_offset, LayoutRect(abs_rect)); @@ -133,7 +133,8 @@ void ScrollableAreaPainter::PaintOverflowControls( if (painting_overlay_controls) adjusted_paint_offset = GetScrollableArea().CachedOverlayScrollbarOffset(); - CullRect adjusted_cull_rect(paint_info.GetCullRect(), -adjusted_paint_offset); + CullRect adjusted_cull_rect = paint_info.GetCullRect(); + adjusted_cull_rect.MoveBy(-adjusted_paint_offset); // Overlay scrollbars paint in a second pass through the layer tree so that // they will paint on top of everything else. If this is the normal painting // pass, paintingOverlayControls will be false, and we should just tell the @@ -214,18 +215,18 @@ bool ScrollableAreaPainter::OverflowControlsIntersectRect( GetScrollableArea().GetLayoutBox()->PixelSnappedBorderBoxRect( GetScrollableArea().Layer()->SubpixelAccumulation()); - if (cull_rect.IntersectsCullRect( + if (cull_rect.Intersects( GetScrollableArea().RectForHorizontalScrollbar(border_box))) return true; - if (cull_rect.IntersectsCullRect( + if (cull_rect.Intersects( GetScrollableArea().RectForVerticalScrollbar(border_box))) return true; - if (cull_rect.IntersectsCullRect(GetScrollableArea().ScrollCornerRect())) + if (cull_rect.Intersects(GetScrollableArea().ScrollCornerRect())) return true; - if (cull_rect.IntersectsCullRect(GetScrollableArea().ResizerCornerRect( + if (cull_rect.Intersects(GetScrollableArea().ResizerCornerRect( border_box, kResizerForPointer))) return true; @@ -242,7 +243,7 @@ void ScrollableAreaPainter::PaintScrollCorner( abs_rect.MoveBy(paint_offset); if (const auto* scroll_corner = GetScrollableArea().ScrollCorner()) { - if (!adjusted_cull_rect.IntersectsCullRect(abs_rect)) + if (!adjusted_cull_rect.Intersects(abs_rect)) return; ScrollbarPainter::PaintIntoRect(*scroll_corner, context, paint_offset, LayoutRect(abs_rect)); diff --git a/chromium/third_party/blink/renderer/core/paint/svg_container_painter.cc b/chromium/third_party/blink/renderer/core/paint/svg_container_painter.cc index b2b3450bab2..7abb5388cbc 100644 --- a/chromium/third_party/blink/renderer/core/paint/svg_container_painter.cc +++ b/chromium/third_party/blink/renderer/core/paint/svg_container_painter.cc @@ -45,9 +45,10 @@ void SVGContainerPainter::Paint(const PaintInfo& paint_info) { // content, does this in PaintLayerPainter::PaintSingleFragment. if (layout_svg_container_.StyleRef().HasTransform()) { paint_info_before_filtering.ApplyInfiniteCullRect(); - } else { - paint_info_before_filtering.UpdateCullRect( - layout_svg_container_.LocalToSVGParentTransform()); + } else if (const auto* properties = + layout_svg_container_.FirstFragment().PaintProperties()) { + if (const auto* transform = properties->Transform()) + paint_info_before_filtering.TransformCullRect(transform); } ScopedSVGTransformState transform_state( diff --git a/chromium/third_party/blink/renderer/core/paint/svg_filter_painter.cc b/chromium/third_party/blink/renderer/core/paint/svg_filter_painter.cc index 738109459c7..4937fa5bcf4 100644 --- a/chromium/third_party/blink/renderer/core/paint/svg_filter_painter.cc +++ b/chromium/third_party/blink/renderer/core/paint/svg_filter_painter.cc @@ -67,7 +67,7 @@ static void PaintFilteredContent(GraphicsContext& context, DrawingRecorder recorder(context, object, DisplayItem::kSVGFilter); sk_sp<PaintFilter> image_filter = - PaintFilterBuilder::Build(effect, kInterpolationSpaceSRGB); + paint_filter_builder::Build(effect, kInterpolationSpaceSRGB); context.Save(); // Clip drawing of filtered image to the minimum required paint rect. @@ -153,8 +153,8 @@ void SVGFilterPainter::FinishEffect( if (filter_data->state_ == FilterData::kRecordingContent) { DCHECK(filter->GetSourceGraphic()); sk_sp<PaintRecord> content = recording_context.EndContent(bounds); - PaintFilterBuilder::BuildSourceGraphic(filter->GetSourceGraphic(), - std::move(content), bounds); + paint_filter_builder::BuildSourceGraphic(filter->GetSourceGraphic(), + std::move(content), bounds); filter_data->state_ = FilterData::kReadyToPaint; } diff --git a/chromium/third_party/blink/renderer/core/paint/svg_foreign_object_painter.cc b/chromium/third_party/blink/renderer/core/paint/svg_foreign_object_painter.cc index 495ecd4a339..cb53167f8bf 100644 --- a/chromium/third_party/blink/renderer/core/paint/svg_foreign_object_painter.cc +++ b/chromium/third_party/blink/renderer/core/paint/svg_foreign_object_painter.cc @@ -45,8 +45,7 @@ void SVGForeignObjectPainter::PaintLayer(const PaintInfo& paint_info) { // cull rects under transform are intentionally reset to infinity, // to improve cache invalidation performance in the pre-paint tree // walk (see https://http://crrev.com/482854). - LayoutRect(LayoutRect::InfiniteIntRect()), - paint_info.GetGlobalPaintFlags(), LayoutSize()); + CullRect::Infinite(), paint_info.GetGlobalPaintFlags(), LayoutSize()); PaintLayerPainter(*layout_svg_foreign_object_.Layer()) .Paint(paint_info.context, layer_painting_info, paint_info.PaintFlags()); } diff --git a/chromium/third_party/blink/renderer/core/paint/svg_image_painter.cc b/chromium/third_party/blink/renderer/core/paint/svg_image_painter.cc index c387cfecba2..d077d2e646e 100644 --- a/chromium/third_party/blink/renderer/core/paint/svg_image_painter.cc +++ b/chromium/third_party/blink/renderer/core/paint/svg_image_painter.cc @@ -29,7 +29,7 @@ void SVGImagePainter::Paint(const PaintInfo& paint_info) { .CullRectSkipsPainting(paint_info_before_filtering)) { return; } - // Images cannot have children so do not call UpdateCullRect. + // Images cannot have children so do not call TransformCullRect. ScopedSVGTransformState transform_state( paint_info_before_filtering, layout_svg_image_, diff --git a/chromium/third_party/blink/renderer/core/paint/svg_inline_text_box_painter.cc b/chromium/third_party/blink/renderer/core/paint/svg_inline_text_box_painter.cc index 34e32f500a8..e93430f93d2 100644 --- a/chromium/third_party/blink/renderer/core/paint/svg_inline_text_box_painter.cc +++ b/chromium/third_party/blink/renderer/core/paint/svg_inline_text_box_painter.cc @@ -93,15 +93,13 @@ void SVGInlineTextBoxPainter::Paint(const PaintInfo& paint_info, if (!TextShouldBePainted(text_layout_object)) return; - DisplayItem::Type display_item_type = - DisplayItem::PaintPhaseToDrawingType(paint_info.phase); if (!DrawingRecorder::UseCachedDrawingIfPossible( - paint_info.context, svg_inline_text_box_, display_item_type)) { + paint_info.context, svg_inline_text_box_, paint_info.phase)) { LayoutObject& parent_layout_object = ParentInlineLayoutObject(); const ComputedStyle& style = parent_layout_object.StyleRef(); DrawingRecorder recorder(paint_info.context, svg_inline_text_box_, - display_item_type); + paint_info.phase); InlineTextBoxPainter text_painter(svg_inline_text_box_); const DocumentMarkerVector& markers_to_paint = text_painter.ComputeMarkersToPaint(); diff --git a/chromium/third_party/blink/renderer/core/paint/svg_model_object_painter.cc b/chromium/third_party/blink/renderer/core/paint/svg_model_object_painter.cc index e41966fb3df..d92f8d07706 100644 --- a/chromium/third_party/blink/renderer/core/paint/svg_model_object_painter.cc +++ b/chromium/third_party/blink/renderer/core/paint/svg_model_object_painter.cc @@ -7,7 +7,7 @@ #include "third_party/blink/renderer/core/layout/svg/layout_svg_model_object.h" #include "third_party/blink/renderer/core/paint/object_painter.h" #include "third_party/blink/renderer/core/paint/paint_info.h" -#include "third_party/blink/renderer/platform/graphics/paint/hit_test_data.h" +#include "third_party/blink/renderer/platform/graphics/paint/hit_test_display_item.h" namespace blink { @@ -24,7 +24,7 @@ bool SVGModelObjectPainter::CullRectSkipsPainting(const PaintInfo& paint_info) { if (layout_svg_model_object_.IsSVGHiddenContainer()) return false; - return !paint_info.GetCullRect().IntersectsCullRect( + return !paint_info.GetCullRect().IntersectsTransformed( layout_svg_model_object_.LocalToSVGParentTransform(), layout_svg_model_object_.VisualRectInLocalSVGCoordinates()); } @@ -44,8 +44,8 @@ void SVGModelObjectPainter::RecordHitTestData( auto rect = LayoutRect(layout_svg_model_object.VisualRectInLocalSVGCoordinates()); - HitTestData::RecordHitTestRect(paint_info.context, layout_svg_model_object, - HitTestRect(rect, touch_action)); + HitTestDisplayItem::Record(paint_info.context, layout_svg_model_object, + HitTestRect(rect, touch_action)); } void SVGModelObjectPainter::PaintOutline(const PaintInfo& paint_info) { diff --git a/chromium/third_party/blink/renderer/core/paint/svg_shape_painter.cc b/chromium/third_party/blink/renderer/core/paint/svg_shape_painter.cc index 05f9ec19ba9..edd2bf774eb 100644 --- a/chromium/third_party/blink/renderer/core/paint/svg_shape_painter.cc +++ b/chromium/third_party/blink/renderer/core/paint/svg_shape_painter.cc @@ -53,7 +53,7 @@ void SVGShapePainter::Paint(const PaintInfo& paint_info) { .CullRectSkipsPainting(paint_info_before_filtering)) { return; } - // Shapes cannot have children so do not call UpdateCullRect. + // Shapes cannot have children so do not call TransformCullRect. ScopedSVGTransformState transform_state( paint_info_before_filtering, layout_svg_shape_, @@ -249,7 +249,7 @@ void SVGShapePainter::PaintMarker(const PaintInfo& paint_info, // It's expensive to track the transformed paint cull rect for each // marker so just disable culling. The shape paint call will already // be culled if it is outside the paint info cull rect. - marker_paint_info.cull_rect_ = CullRect(LayoutRect::InfiniteIntRect()); + marker_paint_info.ApplyInfiniteCullRect(); SVGContainerPainter(marker).Paint(marker_paint_info); builder.EndRecording(*canvas); diff --git a/chromium/third_party/blink/renderer/core/paint/svg_text_painter.cc b/chromium/third_party/blink/renderer/core/paint/svg_text_painter.cc index d0065f25c7a..d645002252b 100644 --- a/chromium/third_party/blink/renderer/core/paint/svg_text_painter.cc +++ b/chromium/third_party/blink/renderer/core/paint/svg_text_painter.cc @@ -8,6 +8,7 @@ #include "third_party/blink/renderer/core/paint/block_painter.h" #include "third_party/blink/renderer/core/paint/paint_info.h" #include "third_party/blink/renderer/core/paint/scoped_svg_paint_state.h" +#include "third_party/blink/renderer/platform/graphics/paint/hit_test_display_item.h" namespace blink { @@ -17,7 +18,11 @@ void SVGTextPainter::Paint(const PaintInfo& paint_info) { return; PaintInfo block_info(paint_info); - block_info.UpdateCullRect(layout_svg_text_.LocalToSVGParentTransform()); + if (const auto* properties = + layout_svg_text_.FirstFragment().PaintProperties()) { + if (const auto* transform = properties->Transform()) + block_info.TransformCullRect(transform); + } ScopedSVGTransformState transform_state( block_info, layout_svg_text_, layout_svg_text_.LocalToSVGParentTransform()); @@ -47,8 +52,8 @@ void SVGTextPainter::RecordHitTestData(const PaintInfo& paint_info) { return; auto rect = LayoutRect(layout_svg_text_.VisualRectInLocalSVGCoordinates()); - HitTestData::RecordHitTestRect(paint_info.context, layout_svg_text_, - HitTestRect(rect, touch_action)); + HitTestDisplayItem::Record(paint_info.context, layout_svg_text_, + HitTestRect(rect, touch_action)); } } // namespace blink diff --git a/chromium/third_party/blink/renderer/core/paint/table_cell_painter.cc b/chromium/third_party/blink/renderer/core/paint/table_cell_painter.cc index 02479d6a7c6..e655bdb0c9b 100644 --- a/chromium/third_party/blink/renderer/core/paint/table_cell_painter.cc +++ b/chromium/third_party/blink/renderer/core/paint/table_cell_painter.cc @@ -41,12 +41,12 @@ void TableCellPainter::PaintContainerBackgroundBehindCell( void TableCellPainter::PaintBackground(const PaintInfo& paint_info, const LayoutRect& paint_rect, const LayoutObject& background_object) { - if (layout_table_cell_.BackgroundStolenForBeingBody()) + if (layout_table_cell_.BackgroundTransfersToView()) return; Color c = background_object.ResolveColor(GetCSSPropertyBackgroundColor()); const FillLayer& bg_layer = background_object.StyleRef().BackgroundLayers(); - if (bg_layer.HasImage() || c.Alpha()) { + if (bg_layer.AnyLayerHasImage() || c.Alpha()) { // We have to clip here because the background would paint // on top of the borders otherwise. This only matters for cells and rows. bool should_clip = background_object.HasLayer() && @@ -80,45 +80,48 @@ void TableCellPainter::PaintBoxDecorationBackground( bool has_box_shadow = style.BoxShadow(); bool needs_to_paint_border = style.HasBorderDecoration() && !table->ShouldCollapseBorders(); - if (!has_background && !has_box_shadow && !needs_to_paint_border) - return; - - if (DrawingRecorder::UseCachedDrawingIfPossible( - paint_info.context, layout_table_cell_, - DisplayItem::kBoxDecorationBackground)) - return; - - // TODO(chrishtr): the pixel-snapping here is likely incorrect. - DrawingRecorder recorder(paint_info.context, layout_table_cell_, - DisplayItem::kBoxDecorationBackground); - - LayoutRect paint_rect = PaintRectNotIncludingVisualOverflow(paint_offset); - - if (has_box_shadow) - BoxPainterBase::PaintNormalBoxShadow(paint_info, paint_rect, style); - - if (has_background) - PaintBackground(paint_info, paint_rect, layout_table_cell_); - - if (has_box_shadow) { - // If the table collapses borders, the inner rect is the border box rect - // inset by inner half widths of collapsed borders (which are returned - // from the overriden BorderXXX() methods). Otherwise the following code is - // equivalent to BoxPainterBase::PaintInsetBoxShadowWithBorderRect(). - auto inner_rect = paint_rect; - inner_rect.ContractEdges( - layout_table_cell_.BorderTop(), layout_table_cell_.BorderRight(), - layout_table_cell_.BorderBottom(), layout_table_cell_.BorderLeft()); - BoxPainterBase::PaintInsetBoxShadowWithInnerRect( - paint_info, inner_rect, layout_table_cell_.StyleRef()); + if (has_background || has_box_shadow || needs_to_paint_border) { + if (!DrawingRecorder::UseCachedDrawingIfPossible( + paint_info.context, layout_table_cell_, + DisplayItem::kBoxDecorationBackground)) { + // TODO(chrishtr): the pixel-snapping here is likely incorrect. + DrawingRecorder recorder(paint_info.context, layout_table_cell_, + DisplayItem::kBoxDecorationBackground); + + LayoutRect paint_rect = PaintRectNotIncludingVisualOverflow(paint_offset); + + if (has_box_shadow) + BoxPainterBase::PaintNormalBoxShadow(paint_info, paint_rect, style); + + if (has_background) + PaintBackground(paint_info, paint_rect, layout_table_cell_); + + if (has_box_shadow) { + // If the table collapses borders, the inner rect is the border box rect + // inset by inner half widths of collapsed borders (which are returned + // from the overriden BorderXXX() methods). Otherwise the following code + // is equivalent to BoxPainterBase::PaintInsetBoxShadowWithBorderRect(). + auto inner_rect = paint_rect; + inner_rect.ContractEdges( + layout_table_cell_.BorderTop(), layout_table_cell_.BorderRight(), + layout_table_cell_.BorderBottom(), layout_table_cell_.BorderLeft()); + BoxPainterBase::PaintInsetBoxShadowWithInnerRect( + paint_info, inner_rect, layout_table_cell_.StyleRef()); + } + + if (needs_to_paint_border) { + BoxPainterBase::PaintBorder( + layout_table_cell_, layout_table_cell_.GetDocument(), + layout_table_cell_.GeneratingNode(), paint_info, paint_rect, style); + } + } } - if (!needs_to_paint_border) - return; - - BoxPainterBase::PaintBorder( - layout_table_cell_, layout_table_cell_.GetDocument(), - layout_table_cell_.GeneratingNode(), paint_info, paint_rect, style); + if (RuntimeEnabledFeatures::PaintTouchActionRectsEnabled()) { + LayoutRect rect = PaintRectNotIncludingVisualOverflow(paint_offset); + BoxPainter(layout_table_cell_) + .RecordHitTestData(paint_info, rect, layout_table_cell_); + } } void TableCellPainter::PaintMask(const PaintInfo& paint_info, diff --git a/chromium/third_party/blink/renderer/core/paint/table_paint_invalidator.cc b/chromium/third_party/blink/renderer/core/paint/table_paint_invalidator.cc index 515292251b0..f1a16a2c0b5 100644 --- a/chromium/third_party/blink/renderer/core/paint/table_paint_invalidator.cc +++ b/chromium/third_party/blink/renderer/core/paint/table_paint_invalidator.cc @@ -25,14 +25,14 @@ void TablePaintInvalidator::InvalidatePaint() { context_.old_visual_rect != context_.fragment_data->VisualRect(); for (LayoutTableCol* col = table_.FirstColumn(); col; col = col->NextColumn()) { - // LayoutTableCol uses the table's localVisualRect(). Should check column + // LayoutTableCol uses the table's LocalVisualRect(). Should check column // for paint invalidation when table's visual rect changed. if (visual_rect_changed) col->SetShouldCheckForPaintInvalidation(); - // This ensures that the backgroundChangedSinceLastPaintInvalidation flag - // is up-to-date. + // This ensures that the BackgroundNeedsFullPaintInvalidation flag is + // up-to-date. col->EnsureIsReadyForPaintInvalidation(); - if (col->BackgroundChangedSinceLastPaintInvalidation()) { + if (col->BackgroundNeedsFullPaintInvalidation()) { has_col_changed_background = true; break; } diff --git a/chromium/third_party/blink/renderer/core/paint/table_painter.cc b/chromium/third_party/blink/renderer/core/paint/table_painter.cc index 5508fb24de2..bde9eb4092b 100644 --- a/chromium/third_party/blink/renderer/core/paint/table_painter.cc +++ b/chromium/third_party/blink/renderer/core/paint/table_painter.cc @@ -52,37 +52,22 @@ void TablePainter::PaintObject(const PaintInfo& paint_info, ObjectPainter(layout_table_).PaintOutline(paint_info, paint_offset); } -void TablePainter::RecordHitTestData(const PaintInfo& paint_info, - const LayoutPoint& paint_offset) { - // Hit test display items are only needed for compositing. This flag is used - // for for printing and drag images which do not need hit testing. - if (paint_info.GetGlobalPaintFlags() & kGlobalPaintFlattenCompositingLayers) - return; - - auto touch_action = layout_table_.EffectiveWhitelistedTouchAction(); - if (touch_action == TouchAction::kTouchActionAuto) - return; - - auto rect = layout_table_.BorderBoxRect(); - rect.MoveBy(paint_offset); - HitTestData::RecordHitTestRect(paint_info.context, layout_table_, - HitTestRect(rect, touch_action)); -} - void TablePainter::PaintBoxDecorationBackground( const PaintInfo& paint_info, const LayoutPoint& paint_offset) { - if (RuntimeEnabledFeatures::PaintTouchActionRectsEnabled()) - RecordHitTestData(paint_info, paint_offset); - - if (!layout_table_.HasBoxDecorationBackground() || - layout_table_.StyleRef().Visibility() != EVisibility::kVisible) - return; - LayoutRect rect(paint_offset, layout_table_.Size()); layout_table_.SubtractCaptionRect(rect); - BoxPainter(layout_table_) - .PaintBoxDecorationBackgroundWithRect(paint_info, rect); + + if (layout_table_.HasBoxDecorationBackground() && + layout_table_.StyleRef().Visibility() == EVisibility::kVisible) { + BoxPainter(layout_table_) + .PaintBoxDecorationBackgroundWithRect(paint_info, rect, layout_table_); + } + + if (RuntimeEnabledFeatures::PaintTouchActionRectsEnabled()) { + BoxPainter(layout_table_) + .RecordHitTestData(paint_info, rect, layout_table_); + } } void TablePainter::PaintMask(const PaintInfo& paint_info, diff --git a/chromium/third_party/blink/renderer/core/paint/table_painter.h b/chromium/third_party/blink/renderer/core/paint/table_painter.h index 7578ce44655..3cea620b7dc 100644 --- a/chromium/third_party/blink/renderer/core/paint/table_painter.h +++ b/chromium/third_party/blink/renderer/core/paint/table_painter.h @@ -26,10 +26,6 @@ class TablePainter { private: void PaintCollapsedBorders(const PaintInfo&); - // Paint a hit test display item and record hit test data. This should be - // called in the background paint phase even if there is no other painted - // content. - void RecordHitTestData(const PaintInfo&, const LayoutPoint& paint_offset); const LayoutTable& layout_table_; }; diff --git a/chromium/third_party/blink/renderer/core/paint/table_painter_test.cc b/chromium/third_party/blink/renderer/core/paint/table_painter_test.cc index 7075a87248b..200f2aca79f 100644 --- a/chromium/third_party/blink/renderer/core/paint/table_painter_test.cc +++ b/chromium/third_party/blink/renderer/core/paint/table_painter_test.cc @@ -11,6 +11,8 @@ // TableRowPainter and TableCellPainter. It's difficult to separate the tests // into individual files because of dependencies among the painter classes. +using testing::ElementsAre; + namespace blink { using TablePainterTest = PaintControllerPaintTest; @@ -37,19 +39,21 @@ TEST_P(TablePainterTest, Background) { IntRect interest_rect(0, 0, 200, 200); Paint(&interest_rect); - EXPECT_DISPLAY_LIST( - RootPaintController().GetDisplayItemList(), 2, - TestDisplayItem(ViewBackgroundClient(), DisplayItem::kDocumentBackground), - TestDisplayItem(row1, DisplayItem::kBoxDecorationBackground)); + EXPECT_THAT( + RootPaintController().GetDisplayItemList(), + ElementsAre(IsSameId(&ViewScrollingBackgroundClient(), + DisplayItem::kDocumentBackground), + IsSameId(&row1, DisplayItem::kBoxDecorationBackground))); GetDocument().View()->UpdateAllLifecyclePhasesExceptPaint(); interest_rect = IntRect(0, 300, 200, 1000); Paint(&interest_rect); - EXPECT_DISPLAY_LIST( - RootPaintController().GetDisplayItemList(), 2, - TestDisplayItem(ViewBackgroundClient(), DisplayItem::kDocumentBackground), - TestDisplayItem(row2, DisplayItem::kBoxDecorationBackground)); + EXPECT_THAT( + RootPaintController().GetDisplayItemList(), + ElementsAre(IsSameId(&ViewScrollingBackgroundClient(), + DisplayItem::kDocumentBackground), + IsSameId(&row2, DisplayItem::kBoxDecorationBackground))); } TEST_P(TablePainterTest, BackgroundWithCellSpacing) { @@ -80,32 +84,35 @@ TEST_P(TablePainterTest, BackgroundWithCellSpacing) { IntRect interest_rect(0, 200, 200, 150); Paint(&interest_rect); - EXPECT_DISPLAY_LIST( - RootPaintController().GetDisplayItemList(), 3, - TestDisplayItem(ViewBackgroundClient(), DisplayItem::kDocumentBackground), - TestDisplayItem(row1, DisplayItem::kBoxDecorationBackground), - TestDisplayItem(cell1, DisplayItem::kBoxDecorationBackground)); + EXPECT_THAT( + RootPaintController().GetDisplayItemList(), + ElementsAre(IsSameId(&ViewScrollingBackgroundClient(), + DisplayItem::kDocumentBackground), + IsSameId(&row1, DisplayItem::kBoxDecorationBackground), + IsSameId(&cell1, DisplayItem::kBoxDecorationBackground))); GetDocument().View()->UpdateAllLifecyclePhasesExceptPaint(); // Intersects the spacing only. interest_rect = IntRect(0, 250, 100, 100); Paint(&interest_rect); - EXPECT_DISPLAY_LIST( - RootPaintController().GetDisplayItemList(), 2, - TestDisplayItem(ViewBackgroundClient(), DisplayItem::kDocumentBackground), - TestDisplayItem(row1, DisplayItem::kBoxDecorationBackground)); + EXPECT_THAT( + RootPaintController().GetDisplayItemList(), + ElementsAre(IsSameId(&ViewScrollingBackgroundClient(), + DisplayItem::kDocumentBackground), + IsSameId(&row1, DisplayItem::kBoxDecorationBackground))); GetDocument().View()->UpdateAllLifecyclePhasesExceptPaint(); // Intersects cell2 only. interest_rect = IntRect(0, 350, 200, 150); Paint(&interest_rect); - EXPECT_DISPLAY_LIST( - RootPaintController().GetDisplayItemList(), 3, - TestDisplayItem(ViewBackgroundClient(), DisplayItem::kDocumentBackground), - TestDisplayItem(row2, DisplayItem::kBoxDecorationBackground), - TestDisplayItem(cell2, DisplayItem::kBoxDecorationBackground)); + EXPECT_THAT( + RootPaintController().GetDisplayItemList(), + ElementsAre(IsSameId(&ViewScrollingBackgroundClient(), + DisplayItem::kDocumentBackground), + IsSameId(&row2, DisplayItem::kBoxDecorationBackground), + IsSameId(&cell2, DisplayItem::kBoxDecorationBackground))); } TEST_P(TablePainterTest, BackgroundInSelfPaintingRow) { @@ -133,32 +140,33 @@ TEST_P(TablePainterTest, BackgroundInSelfPaintingRow) { IntRect interest_rect(200, 0, 200, 200); Paint(&interest_rect); - EXPECT_DISPLAY_LIST( - RootPaintController().GetDisplayItemList(), 3, - TestDisplayItem(ViewBackgroundClient(), DisplayItem::kDocumentBackground), - TestDisplayItem(row, DisplayItem::kBoxDecorationBackground), - TestDisplayItem(cell1, DisplayItem::kBoxDecorationBackground)); + EXPECT_THAT( + RootPaintController().GetDisplayItemList(), + ElementsAre(IsSameId(&ViewScrollingBackgroundClient(), + DisplayItem::kDocumentBackground), + IsSameId(&row, DisplayItem::kBoxDecorationBackground), + IsSameId(&cell1, DisplayItem::kBoxDecorationBackground))); GetDocument().View()->UpdateAllLifecyclePhasesExceptPaint(); // Intersects the spacing only. interest_rect = IntRect(300, 0, 100, 100); Paint(&interest_rect); - EXPECT_DISPLAY_LIST(RootPaintController().GetDisplayItemList(), 1, - TestDisplayItem(ViewBackgroundClient(), - DisplayItem::kDocumentBackground)); + EXPECT_THAT(RootPaintController().GetDisplayItemList(), + ElementsAre(IsSameId(&ViewScrollingBackgroundClient(), + DisplayItem::kDocumentBackground))); GetDocument().View()->UpdateAllLifecyclePhasesExceptPaint(); // Intersects cell2 only. interest_rect = IntRect(450, 0, 200, 200); Paint(&interest_rect); - EXPECT_DISPLAY_LIST( - RootPaintController().GetDisplayItemList(), 3, - TestDisplayItem(ViewBackgroundClient(), - DisplayItem::kDocumentBackground), - TestDisplayItem(row, DisplayItem::kBoxDecorationBackground), - TestDisplayItem(cell2, DisplayItem::kBoxDecorationBackground)); + EXPECT_THAT( + RootPaintController().GetDisplayItemList(), + ElementsAre(IsSameId(&ViewScrollingBackgroundClient(), + DisplayItem::kDocumentBackground), + IsSameId(&row, DisplayItem::kBoxDecorationBackground), + IsSameId(&cell2, DisplayItem::kBoxDecorationBackground))); } TEST_P(TablePainterTest, CollapsedBorderAndOverflow) { @@ -183,13 +191,14 @@ TEST_P(TablePainterTest, CollapsedBorderAndOverflow) { Paint(&interest_rect); // We should paint all display items of cell. - EXPECT_DISPLAY_LIST( - RootPaintController().GetDisplayItemList(), 4, - TestDisplayItem(ViewBackgroundClient(), DisplayItem::kDocumentBackground), - TestDisplayItem(cell, DisplayItem::kBoxDecorationBackground), - TestDisplayItem(*cell.Row(), DisplayItem::kTableCollapsedBorders), - TestDisplayItem(cell, DisplayItem::PaintPhaseToDrawingType( - PaintPhase::kSelfOutlineOnly))); + EXPECT_THAT( + RootPaintController().GetDisplayItemList(), + ElementsAre(IsSameId(&ViewScrollingBackgroundClient(), + DisplayItem::kDocumentBackground), + IsSameId(&cell, DisplayItem::kBoxDecorationBackground), + IsSameId(cell.Row(), DisplayItem::kTableCollapsedBorders), + IsSameId(&cell, DisplayItem::PaintPhaseToDrawingType( + PaintPhase::kSelfOutlineOnly)))); } } // namespace blink diff --git a/chromium/third_party/blink/renderer/core/paint/table_row_painter.cc b/chromium/third_party/blink/renderer/core/paint/table_row_painter.cc index 30b2a0585aa..9ba2b745488 100644 --- a/chromium/third_party/blink/renderer/core/paint/table_row_painter.cc +++ b/chromium/third_party/blink/renderer/core/paint/table_row_painter.cc @@ -14,6 +14,7 @@ #include "third_party/blink/renderer/core/paint/scoped_paint_state.h" #include "third_party/blink/renderer/core/paint/table_cell_painter.h" #include "third_party/blink/renderer/platform/graphics/paint/drawing_recorder.h" +#include "third_party/blink/renderer/platform/graphics/paint/hit_test_display_item.h" namespace blink { @@ -62,7 +63,7 @@ void TableRowPainter::HandleChangedPartialPaint( dirtied_columns == layout_table_row_.Section()->FullTableEffectiveColumnSpan() ? kFullyPainted - : kMayBeClippedByPaintDirtyRect; + : kMayBeClippedByCullRect; layout_table_row_.GetMutableForPainting().UpdatePaintResult( paint_result, paint_info.GetCullRect()); } @@ -74,14 +75,18 @@ void TableRowPainter::RecordHitTestData(const PaintInfo& paint_info, if (paint_info.GetGlobalPaintFlags() & kGlobalPaintFlattenCompositingLayers) return; + // If an object is not visible, it does not participate in hit testing. + if (layout_table_row_.StyleRef().Visibility() != EVisibility::kVisible) + return; + auto touch_action = layout_table_row_.EffectiveWhitelistedTouchAction(); if (touch_action == TouchAction::kTouchActionAuto) return; auto rect = layout_table_row_.BorderBoxRect(); rect.MoveBy(paint_offset); - HitTestData::RecordHitTestRect(paint_info.context, layout_table_row_, - HitTestRect(rect, touch_action)); + HitTestDisplayItem::Record(paint_info.context, layout_table_row_, + HitTestRect(rect, touch_action)); } void TableRowPainter::PaintBoxDecorationBackground( diff --git a/chromium/third_party/blink/renderer/core/paint/table_section_painter.cc b/chromium/third_party/blink/renderer/core/paint/table_section_painter.cc index a9a4b7d7707..78f6d2826a8 100644 --- a/chromium/third_party/blink/renderer/core/paint/table_section_painter.cc +++ b/chromium/third_party/blink/renderer/core/paint/table_section_painter.cc @@ -278,7 +278,7 @@ void TableSectionPainter::PaintBoxDecorationBackground( dirtied_columns == layout_table_section_.FullTableEffectiveColumnSpan() && dirtied_rows == layout_table_section_.FullSectionRowSpan() ? kFullyPainted - : kMayBeClippedByPaintDirtyRect; + : kMayBeClippedByCullRect; layout_table_section_.GetMutableForPainting().UpdatePaintResult( paint_result, paint_info.GetCullRect()); diff --git a/chromium/third_party/blink/renderer/core/paint/text_paint_timing_detector.cc b/chromium/third_party/blink/renderer/core/paint/text_paint_timing_detector.cc index 6d0cd0b73ea..88adcefd481 100644 --- a/chromium/third_party/blink/renderer/core/paint/text_paint_timing_detector.cc +++ b/chromium/third_party/blink/renderer/core/paint/text_paint_timing_detector.cc @@ -11,6 +11,7 @@ #include "third_party/blink/renderer/core/page/chrome_client.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_timing_detector.h" #include "third_party/blink/renderer/platform/geometry/layout_rect.h" #include "third_party/blink/renderer/platform/graphics/paint/geometry_mapper.h" #include "third_party/blink/renderer/platform/instrumentation/tracing/trace_event.h" @@ -52,45 +53,52 @@ void TextPaintTimingDetector::PopulateTraceValue( IdentifiersFactory::FrameId(&frame_view_->GetFrame())); } -IntRect TextPaintTimingDetector::CalculateTransformedRect( - LayoutRect& invalidated_rect, - const PaintLayer& painting_layer) const { - const auto* local_transform = painting_layer.GetLayoutObject() - .FirstFragment() - .LocalBorderBoxProperties() - .Transform(); - const auto* ancestor_transform = painting_layer.GetLayoutObject() - .View() - ->FirstFragment() - .LocalBorderBoxProperties() - .Transform(); - FloatRect invalidated_rect_abs = FloatRect(invalidated_rect); - if (invalidated_rect_abs.IsEmpty() || invalidated_rect_abs.IsZero()) - return IntRect(); - GeometryMapper::SourceToDestinationRect(local_transform, ancestor_transform, - invalidated_rect_abs); - IntRect invalidated_rect_in_viewport = RoundedIntRect(invalidated_rect_abs); - invalidated_rect_in_viewport.Intersect( - frame_view_->GetScrollableArea()->VisibleContentRect()); - return invalidated_rect_in_viewport; -} - -void TextPaintTimingDetector::TimerFired(TimerBase* timer) { - if (TextRecord* largest_text_first_paint = FindLargestPaintCandidate()) { - std::unique_ptr<TracedValue> value = TracedValue::Create(); - PopulateTraceValue(*value, *largest_text_first_paint, - largest_text_candidate_index_max_++); - TRACE_EVENT_INSTANT_WITH_TIMESTAMP1( - "loading", "LargestTextPaint::Candidate", TRACE_EVENT_SCOPE_THREAD, - largest_text_first_paint->first_paint_time, "data", std::move(value)); +void TextPaintTimingDetector::OnLargestTextDetected( + const TextRecord& largest_text_record) { + largest_text_paint_ = largest_text_record.first_paint_time; + + std::unique_ptr<TracedValue> value = TracedValue::Create(); + PopulateTraceValue(*value, largest_text_record, + largest_text_candidate_index_max_++); + TRACE_EVENT_INSTANT_WITH_TIMESTAMP1( + "loading", "LargestTextPaint::Candidate", TRACE_EVENT_SCOPE_THREAD, + largest_text_paint_, "data", std::move(value)); +} + +void TextPaintTimingDetector::OnLastTextDetected( + const TextRecord& last_text_record) { + last_text_paint_ = last_text_record.first_paint_time; + + std::unique_ptr<TracedValue> value = TracedValue::Create(); + PopulateTraceValue(*value, last_text_record, + last_text_candidate_index_max_++); + TRACE_EVENT_INSTANT_WITH_TIMESTAMP1( + "loading", "LastTextPaint::Candidate", TRACE_EVENT_SCOPE_THREAD, + last_text_paint_, "data", std::move(value)); +} + +void TextPaintTimingDetector::TimerFired(TimerBase* time) { + // Wrap Analyze method in TimerFired so that we can drop |time| for Analyze + // in testing. + Analyze(); +} + +void TextPaintTimingDetector::Analyze() { + TextRecord* largest_text_first_paint = FindLargestPaintCandidate(); + bool new_candidate_detected = false; + if (largest_text_first_paint && + largest_text_first_paint->first_paint_time != largest_text_paint_) { + OnLargestTextDetected(*largest_text_first_paint); + new_candidate_detected = true; } - if (TextRecord* last_text_first_paint = FindLastPaintCandidate()) { - std::unique_ptr<TracedValue> value = TracedValue::Create(); - PopulateTraceValue(*value, *last_text_first_paint, - last_text_candidate_index_max_++); - TRACE_EVENT_INSTANT_WITH_TIMESTAMP1( - "loading", "LastTextPaint::Candidate", TRACE_EVENT_SCOPE_THREAD, - last_text_first_paint->first_paint_time, "data", std::move(value)); + TextRecord* last_text_first_paint = FindLastPaintCandidate(); + if (last_text_first_paint && + last_text_first_paint->first_paint_time != last_text_paint_) { + OnLastTextDetected(*last_text_first_paint); + new_candidate_detected = true; + } + if (new_candidate_detected) { + frame_view_->GetPaintTimingDetector().DidChangePerformanceTiming(); } } @@ -109,11 +117,25 @@ void TextPaintTimingDetector::OnPrePaintFinished() { } void TextPaintTimingDetector::NotifyNodeRemoved(DOMNodeId node_id) { - if (recorded_text_node_ids_.find(node_id) != recorded_text_node_ids_.end()) { - // We assume that the removed node's id wouldn't be recycled, so we don't - // bother to remove these records from largest_text_heap_ or - // latest_text_heap_, to reduce computation. - recorded_text_node_ids_.erase(node_id); + if (recorded_text_node_ids_.find(node_id) == recorded_text_node_ids_.end()) + return; + // We assume that removed nodes' id would not be recycled, and it's expensive + // to remove records from largest_text_heap_ and latest_text_heap_, so we + // intentionally keep the records of removed nodes in largest_text_heap_ and + // latest_text_heap_. + recorded_text_node_ids_.erase(node_id); + if (recorded_text_node_ids_.size() == 0) { + const bool largest_text_paint_invalidated = + largest_text_paint_ != base::TimeTicks(); + const bool last_text_paint_invalidated = + last_text_paint_ != base::TimeTicks(); + if (largest_text_paint_invalidated) + largest_text_paint_ = base::TimeTicks(); + if (last_text_paint_invalidated) + last_text_paint_ = base::TimeTicks(); + if (largest_text_paint_invalidated || last_text_paint_invalidated) { + frame_view_->GetPaintTimingDetector().DidChangePerformanceTiming(); + } } } @@ -150,11 +172,15 @@ void TextPaintTimingDetector::ReportSwapTime( void TextPaintTimingDetector::RecordText(const LayoutObject& object, const PaintLayer& painting_layer) { + if (!is_recording_) + return; Node* node = object.GetNode(); if (!node) return; DOMNodeId node_id = DOMNodeIds::IdForNode(node); + // This metric defines the size of a text by its first size. So it + // early-returns if the text has been recorded. if (size_zero_node_ids_.find(node_id) != size_zero_node_ids_.end()) return; if (recorded_text_node_ids_.find(node_id) != recorded_text_node_ids_.end()) @@ -162,34 +188,38 @@ void TextPaintTimingDetector::RecordText(const LayoutObject& object, // When node_id is not found in recorded_text_node_ids_, this invalidation is // the text's first invalidation. - // We deactivate the algorithm if the number of nodes exceeds limitation. - recorded_node_count_++; - if (recorded_node_count_ > kTextNodeNumberLimit) { - // for assessing whether kTextNodeNumberLimit is large enough for all - // normal cases - TRACE_EVENT_INSTANT1("loading", "TextPaintTimingDetector::OverNodeLimit", - TRACE_EVENT_SCOPE_THREAD, "recorded_node_count", - recorded_node_count_); - return; - } + uint64_t rect_size = 0; LayoutRect invalidated_rect = object.FirstFragment().VisualRect(); - int rect_size = 0; if (!invalidated_rect.IsEmpty()) { - IntRect invalidated_rect_in_viewport = - CalculateTransformedRect(invalidated_rect, painting_layer); - rect_size = invalidated_rect_in_viewport.Height() * - invalidated_rect_in_viewport.Width(); + rect_size = frame_view_->GetPaintTimingDetector().CalculateVisualSize( + invalidated_rect, painting_layer); } - // When rect_size == 0, it either means invalidated_rect.IsEmpty() or - // the text is size 0 or the text is out of viewport. Either way, we don't - // record their time, to reduce computation. + + // When rect_size == 0, it either means the text size is 0 or the text is out + // of viewport. In either case, we don't record their time for efficiency. if (rect_size == 0) { size_zero_node_ids_.insert(node_id); } else { + // Non-trivial text is found. TextRecord record = {node_id, rect_size, base::TimeTicks(), ToLayoutText(&object)->GetText()}; texts_to_record_swap_time_.push_back(record); } + + if (recorded_text_node_ids_.size() + size_zero_node_ids_.size() + + texts_to_record_swap_time_.size() >= + kTextNodeNumberLimit) { + Deactivate(); + } +} + +void TextPaintTimingDetector::Deactivate() { + TRACE_EVENT_INSTANT2("loading", "TextPaintTimingDetector::OverNodeLimit", + TRACE_EVENT_SCOPE_THREAD, "recorded_node_count", + recorded_text_node_ids_.size(), "size_zero_node_count", + size_zero_node_ids_.size()); + timer_.Stop(); + is_recording_ = false; } TextRecord* TextPaintTimingDetector::FindLargestPaintCandidate() { diff --git a/chromium/third_party/blink/renderer/core/paint/text_paint_timing_detector.h b/chromium/third_party/blink/renderer/core/paint/text_paint_timing_detector.h index 2e518a09e03..d009f65a73c 100644 --- a/chromium/third_party/blink/renderer/core/paint/text_paint_timing_detector.h +++ b/chromium/third_party/blink/renderer/core/paint/text_paint_timing_detector.h @@ -14,14 +14,13 @@ namespace blink { class PaintLayer; -class IntRect; class LayoutObject; class TracedValue; class LocalFrameView; struct TextRecord { DOMNodeId node_id = kInvalidDOMNodeId; - double first_size = 0.0; + uint64_t first_size = 0; base::TimeTicks first_paint_time = base::TimeTicks(); String text = ""; }; @@ -62,19 +61,23 @@ class CORE_EXPORT TextPaintTimingDetector final void OnPrePaintFinished(); void NotifyNodeRemoved(DOMNodeId); void Dispose() { timer_.Stop(); } + base::TimeTicks LargestTextPaint() const { return largest_text_paint_; } + base::TimeTicks LastTextPaint() const { return last_text_paint_; } void Trace(blink::Visitor*); private: void PopulateTraceValue(TracedValue& value, const TextRecord& first_text_paint, unsigned candidate_index) const; - IntRect CalculateTransformedRect(LayoutRect& visual_rect, - const PaintLayer& painting_layer) const; void TimerFired(TimerBase*); + void Analyze(); void ReportSwapTime(WebLayerTreeView::SwapResult result, base::TimeTicks timestamp); void RegisterNotifySwapTime(ReportTimeCallback callback); + void OnLargestTextDetected(const TextRecord&); + void OnLastTextDetected(const TextRecord&); + void Deactivate(); HashSet<DOMNodeId> recorded_text_node_ids_; HashSet<DOMNodeId> size_zero_node_ids_; @@ -92,9 +95,12 @@ class CORE_EXPORT TextPaintTimingDetector final // Make sure that at most one swap promise is ongoing. bool awaiting_swap_promise_ = false; - unsigned recorded_node_count_ = 0; unsigned largest_text_candidate_index_max_ = 0; unsigned last_text_candidate_index_max_ = 0; + bool is_recording_ = true; + + base::TimeTicks largest_text_paint_; + base::TimeTicks last_text_paint_; TaskRunnerTimer<TextPaintTimingDetector> timer_; Member<LocalFrameView> frame_view_; }; diff --git a/chromium/third_party/blink/renderer/core/paint/text_paint_timing_detector_test.cc b/chromium/third_party/blink/renderer/core/paint/text_paint_timing_detector_test.cc index 573d019dd72..2b2f395d437 100644 --- a/chromium/third_party/blink/renderer/core/paint/text_paint_timing_detector_test.cc +++ b/chromium/third_party/blink/renderer/core/paint/text_paint_timing_detector_test.cc @@ -3,32 +3,53 @@ // found in the LICENSE file. #include "third_party/blink/renderer/core/paint/text_paint_timing_detector.h" -#include "third_party/blink/renderer/core/paint/paint_tracker.h" +#include "third_party/blink/renderer/core/paint/paint_timing_detector.h" #include "third_party/blink/renderer/core/testing/core_unit_test_helper.h" #include "third_party/blink/renderer/platform/runtime_enabled_features.h" +#include "third_party/blink/renderer/platform/testing/runtime_enabled_features_test_helpers.h" namespace blink { -class TextPaintTimingDetectorTest : public RenderingTest { +class TextPaintTimingDetectorTest + : public RenderingTest, + private ScopedFirstContentfulPaintPlusPlusForTest { public: - void SetUp() override { - RenderingTest::SetUp(); - RuntimeEnabledFeatures::SetPaintTrackingEnabled(true); - } + TextPaintTimingDetectorTest() + : ScopedFirstContentfulPaintPlusPlusForTest(true) {} + void SetUp() override { RenderingTest::SetUp(); } protected: LocalFrameView& GetFrameView() { return *GetFrame().View(); } - PaintTracker& GetPaintTracker() { return GetFrameView().GetPaintTracker(); } + PaintTimingDetector& GetPaintTimingDetector() { + return GetFrameView().GetPaintTimingDetector(); + } + + TimeTicks LargestPaintStoredResult() { + return GetPaintTimingDetector() + .GetTextPaintTimingDetector() + .largest_text_paint_; + } + + TimeTicks LastPaintStoredResult() { + return GetPaintTimingDetector() + .GetTextPaintTimingDetector() + .last_text_paint_; + } void UpdateAllLifecyclePhasesAndSimulateSwapTime() { - GetFrameView().UpdateAllLifecyclePhases(); + GetFrameView().UpdateAllLifecyclePhases( + DocumentLifecycle::LifecycleUpdateReason::kTest); TextPaintTimingDetector& detector = - GetPaintTracker().GetTextPaintTimingDetector(); + GetPaintTimingDetector().GetTextPaintTimingDetector(); if (detector.texts_to_record_swap_time_.size() > 0) { detector.ReportSwapTime(WebLayerTreeView::SwapResult::kDidSwap, CurrentTimeTicks()); } } + + void SimulateAnalyze() { + GetPaintTimingDetector().GetTextPaintTimingDetector().Analyze(); + } }; TEST_F(TextPaintTimingDetectorTest, LargestTextPaint_NoText) { @@ -36,7 +57,7 @@ TEST_F(TextPaintTimingDetectorTest, LargestTextPaint_NoText) { <div></div> )HTML"); UpdateAllLifecyclePhasesAndSimulateSwapTime(); - TextRecord* record = GetPaintTracker() + TextRecord* record = GetPaintTimingDetector() .GetTextPaintTimingDetector() .FindLargestPaintCandidate(); EXPECT_FALSE(record); @@ -47,7 +68,7 @@ TEST_F(TextPaintTimingDetectorTest, LargestTextPaint_OneText) { <div>The only text</div> )HTML"); UpdateAllLifecyclePhasesAndSimulateSwapTime(); - TextRecord* record = GetPaintTracker() + TextRecord* record = GetPaintTimingDetector() .GetTextPaintTimingDetector() .FindLargestPaintCandidate(); EXPECT_TRUE(record); @@ -68,12 +89,40 @@ TEST_F(TextPaintTimingDetectorTest, LargestTextPaint_LargestText) { GetDocument().body()->AppendChild(tiny_text); UpdateAllLifecyclePhasesAndSimulateSwapTime(); - TextRecord* record = GetPaintTracker() + TextRecord* record = GetPaintTimingDetector() .GetTextPaintTimingDetector() .FindLargestPaintCandidate(); EXPECT_EQ(record->text, "a long-long-long text"); } +TEST_F(TextPaintTimingDetectorTest, UpdateResultWhenCandidateChanged) { + TimeTicks time1 = CurrentTimeTicks(); + SetBodyInnerHTML(R"HTML( + <div>small text</div> + )HTML"); + UpdateAllLifecyclePhasesAndSimulateSwapTime(); + SimulateAnalyze(); + TimeTicks time2 = CurrentTimeTicks(); + TimeTicks first_largest = LargestPaintStoredResult(); + TimeTicks first_last = LastPaintStoredResult(); + EXPECT_GE(first_largest, time1); + EXPECT_GE(time2, first_largest); + EXPECT_GE(first_last, time1); + EXPECT_GE(time2, first_last); + + Text* larger_text = GetDocument().createTextNode("a long-long-long text"); + GetDocument().body()->AppendChild(larger_text); + UpdateAllLifecyclePhasesAndSimulateSwapTime(); + SimulateAnalyze(); + TimeTicks time3 = CurrentTimeTicks(); + TimeTicks second_largest = LargestPaintStoredResult(); + TimeTicks second_last = LastPaintStoredResult(); + EXPECT_GE(second_largest, time2); + EXPECT_GE(time3, second_largest); + EXPECT_GE(second_last, time2); + EXPECT_GE(time3, second_last); +} + TEST_F(TextPaintTimingDetectorTest, LargestTextPaint_ReportFirstPaintTime) { TimeTicks time1 = CurrentTimeTicks(); SetBodyInnerHTML(R"HTML( @@ -85,15 +134,15 @@ TEST_F(TextPaintTimingDetectorTest, LargestTextPaint_ReportFirstPaintTime) { UpdateAllLifecyclePhasesAndSimulateSwapTime(); TimeTicks time2 = CurrentTimeTicks(); - GetDocument().getElementById("b")->setAttribute(HTMLNames::styleAttr, + GetDocument().getElementById("b")->setAttribute(html_names::kStyleAttr, AtomicString("height:50px")); UpdateAllLifecyclePhasesAndSimulateSwapTime(); - GetDocument().getElementById("b")->setAttribute(HTMLNames::styleAttr, + GetDocument().getElementById("b")->setAttribute(html_names::kStyleAttr, AtomicString("height:100px")); UpdateAllLifecyclePhasesAndSimulateSwapTime(); - TextRecord* record = GetPaintTracker() + TextRecord* record = GetPaintTimingDetector() .GetTextPaintTimingDetector() .FindLargestPaintCandidate(); EXPECT_EQ(record->text, "a long-long-long-long moving text"); @@ -114,11 +163,12 @@ TEST_F(TextPaintTimingDetectorTest, <div class='out'>text outside of viewport</div> )HTML"); UpdateAllLifecyclePhasesAndSimulateSwapTime(); - EXPECT_FALSE(GetPaintTracker() + EXPECT_FALSE(GetPaintTimingDetector() .GetTextPaintTimingDetector() .FindLargestPaintCandidate()); - EXPECT_FALSE( - GetPaintTracker().GetTextPaintTimingDetector().FindLastPaintCandidate()); + EXPECT_FALSE(GetPaintTimingDetector() + .GetTextPaintTimingDetector() + .FindLastPaintCandidate()); } TEST_F(TextPaintTimingDetectorTest, LargestTextPaint_IgnoreRemovedText) { @@ -129,13 +179,47 @@ TEST_F(TextPaintTimingDetectorTest, LargestTextPaint_IgnoreRemovedText) { </div> )HTML"); UpdateAllLifecyclePhasesAndSimulateSwapTime(); + TextRecord* record = GetPaintTimingDetector() + .GetTextPaintTimingDetector() + .FindLargestPaintCandidate(); + EXPECT_TRUE(record); + EXPECT_EQ(record->text, + "(large text)(large text)(large text)(large text)(large " + "text)(large text)"); + GetDocument().getElementById("parent")->RemoveChild( GetDocument().getElementById("earlyLargeText")); UpdateAllLifecyclePhasesAndSimulateSwapTime(); - TextRecord* record = GetPaintTracker() + record = GetPaintTimingDetector() + .GetTextPaintTimingDetector() + .FindLargestPaintCandidate(); + EXPECT_EQ(record->text, "small text"); +} + +TEST_F(TextPaintTimingDetectorTest, LargestTextPaint_ReportLastNullCandidate) { + SetBodyInnerHTML(R"HTML( + <div id='parent'> + <div id='remove'>text</div> + </div> + )HTML"); + UpdateAllLifecyclePhasesAndSimulateSwapTime(); + SimulateAnalyze(); + TextRecord* record = GetPaintTimingDetector() .GetTextPaintTimingDetector() .FindLargestPaintCandidate(); - EXPECT_EQ(record->text, "small text"); + EXPECT_TRUE(record); + EXPECT_EQ(record->text, "text"); + EXPECT_NE(LargestPaintStoredResult(), base::TimeTicks()); + + GetDocument().getElementById("parent")->RemoveChild( + GetDocument().getElementById("remove")); + UpdateAllLifecyclePhasesAndSimulateSwapTime(); + SimulateAnalyze(); + record = GetPaintTimingDetector() + .GetTextPaintTimingDetector() + .FindLargestPaintCandidate(); + EXPECT_FALSE(record); + EXPECT_EQ(LargestPaintStoredResult(), base::TimeTicks()); } TEST_F(TextPaintTimingDetectorTest, @@ -147,7 +231,7 @@ TEST_F(TextPaintTimingDetectorTest, </div> )HTML"); UpdateAllLifecyclePhasesAndSimulateSwapTime(); - TextRecord* record = GetPaintTracker() + TextRecord* record = GetPaintTimingDetector() .GetTextPaintTimingDetector() .FindLargestPaintCandidate(); EXPECT_EQ(record->text, "short"); @@ -165,10 +249,10 @@ TEST_F(TextPaintTimingDetectorTest, LargestTextPaint_CompareSizesAtFirstPaint) { // viewport. GetDocument() .getElementById("shorteningText") - ->setAttribute(HTMLNames::styleAttr, + ->setAttribute(html_names::kStyleAttr, AtomicString("position:fixed;left:-10px")); UpdateAllLifecyclePhasesAndSimulateSwapTime(); - TextRecord* record = GetPaintTracker() + TextRecord* record = GetPaintTimingDetector() .GetTextPaintTimingDetector() .FindLargestPaintCandidate(); EXPECT_EQ(record->text, "large-to-small text"); @@ -179,8 +263,9 @@ TEST_F(TextPaintTimingDetectorTest, LastTextPaint_NoText) { <div></div> )HTML"); UpdateAllLifecyclePhasesAndSimulateSwapTime(); - TextRecord* record = - GetPaintTracker().GetTextPaintTimingDetector().FindLastPaintCandidate(); + TextRecord* record = GetPaintTimingDetector() + .GetTextPaintTimingDetector() + .FindLastPaintCandidate(); EXPECT_FALSE(record); } @@ -189,8 +274,9 @@ TEST_F(TextPaintTimingDetectorTest, LastTextPaint_OneText) { <div>The only text</div> )HTML"); UpdateAllLifecyclePhasesAndSimulateSwapTime(); - TextRecord* record = - GetPaintTracker().GetTextPaintTimingDetector().FindLastPaintCandidate(); + TextRecord* record = GetPaintTimingDetector() + .GetTextPaintTimingDetector() + .FindLastPaintCandidate(); EXPECT_EQ(record->text, "The only text"); } @@ -208,8 +294,9 @@ TEST_F(TextPaintTimingDetectorTest, LastTextPaint_LastText) { GetDocument().body()->AppendChild(tiny_text); UpdateAllLifecyclePhasesAndSimulateSwapTime(); - TextRecord* record = - GetPaintTracker().GetTextPaintTimingDetector().FindLastPaintCandidate(); + TextRecord* record = GetPaintTimingDetector() + .GetTextPaintTimingDetector() + .FindLastPaintCandidate(); EXPECT_EQ(record->text, "3rd text"); } @@ -227,16 +314,17 @@ TEST_F(TextPaintTimingDetectorTest, LastTextPaint_ReportFirstPaintTime) { UpdateAllLifecyclePhasesAndSimulateSwapTime(); TimeTicks time2 = CurrentTimeTicks(); - GetDocument().getElementById("b")->setAttribute(HTMLNames::styleAttr, + GetDocument().getElementById("b")->setAttribute(html_names::kStyleAttr, AtomicString("height:50px")); UpdateAllLifecyclePhasesAndSimulateSwapTime(); - GetDocument().getElementById("b")->setAttribute(HTMLNames::styleAttr, + GetDocument().getElementById("b")->setAttribute(html_names::kStyleAttr, AtomicString("height:100px")); UpdateAllLifecyclePhasesAndSimulateSwapTime(); - TextRecord* record = - GetPaintTracker().GetTextPaintTimingDetector().FindLastPaintCandidate(); + TextRecord* record = GetPaintTimingDetector() + .GetTextPaintTimingDetector() + .FindLastPaintCandidate(); EXPECT_EQ(record->text, "latest text"); TimeTicks firing_time = record->first_paint_time; EXPECT_GE(firing_time, time1); @@ -257,8 +345,9 @@ TEST_F(TextPaintTimingDetectorTest, LastTextPaint_IgnoreRemovedText) { GetDocument().body()->RemoveChild(GetDocument().body()->lastChild()); UpdateAllLifecyclePhasesAndSimulateSwapTime(); - TextRecord* record = - GetPaintTracker().GetTextPaintTimingDetector().FindLastPaintCandidate(); + TextRecord* record = GetPaintTimingDetector() + .GetTextPaintTimingDetector() + .FindLastPaintCandidate(); EXPECT_EQ(record->text, "earliest text"); } @@ -270,9 +359,9 @@ TEST_F(TextPaintTimingDetectorTest, LastTextPaint_StopRecordingOverNodeLimit) { UpdateAllLifecyclePhasesAndSimulateSwapTime(); for (int i = 1; i <= 4999; i++) { - Element* div = GetDocument().CreateRawElement(HTMLNames::divTag); + Element* div = GetDocument().CreateRawElement(html_names::kDivTag); div->appendChild(GetDocument().createTextNode(WTF::String::Number(i))); - div->setAttribute(HTMLNames::styleAttr, + div->setAttribute(html_names::kStyleAttr, AtomicString("position:fixed;left:0px")); GetDocument().body()->AppendChild(div); } @@ -284,16 +373,44 @@ TEST_F(TextPaintTimingDetectorTest, LastTextPaint_StopRecordingOverNodeLimit) { text = GetDocument().createTextNode(WTF::String::Number(5000)); GetDocument().body()->AppendChild(text); UpdateAllLifecyclePhasesAndSimulateSwapTime(); - record = - GetPaintTracker().GetTextPaintTimingDetector().FindLastPaintCandidate(); + record = GetPaintTimingDetector() + .GetTextPaintTimingDetector() + .FindLastPaintCandidate(); EXPECT_EQ(record->text, "5000"); text = GetDocument().createTextNode(WTF::String::Number(5001)); GetDocument().body()->AppendChild(text); UpdateAllLifecyclePhasesAndSimulateSwapTime(); - record = - GetPaintTracker().GetTextPaintTimingDetector().FindLastPaintCandidate(); + record = GetPaintTimingDetector() + .GetTextPaintTimingDetector() + .FindLastPaintCandidate(); EXPECT_EQ(record->text, "5000"); } +TEST_F(TextPaintTimingDetectorTest, LastTextPaint_ReportLastNullCandidate) { + SetBodyInnerHTML(R"HTML( + <div id='parent'> + <div id='remove'>text</div> + </div> + )HTML"); + UpdateAllLifecyclePhasesAndSimulateSwapTime(); + SimulateAnalyze(); + TextRecord* record = GetPaintTimingDetector() + .GetTextPaintTimingDetector() + .FindLargestPaintCandidate(); + EXPECT_TRUE(record); + EXPECT_EQ(record->text, "text"); + EXPECT_NE(LastPaintStoredResult(), base::TimeTicks()); + + GetDocument().getElementById("parent")->RemoveChild( + GetDocument().getElementById("remove")); + UpdateAllLifecyclePhasesAndSimulateSwapTime(); + SimulateAnalyze(); + record = GetPaintTimingDetector() + .GetTextPaintTimingDetector() + .FindLargestPaintCandidate(); + EXPECT_FALSE(record); + EXPECT_EQ(LastPaintStoredResult(), base::TimeTicks()); +} + } // namespace blink diff --git a/chromium/third_party/blink/renderer/core/paint/text_painter.cc b/chromium/third_party/blink/renderer/core/paint/text_painter.cc index 65369c3d025..1e91b6dbe9c 100644 --- a/chromium/third_party/blink/renderer/core/paint/text_painter.cc +++ b/chromium/third_party/blink/renderer/core/paint/text_painter.cc @@ -4,7 +4,7 @@ #include "third_party/blink/renderer/core/paint/text_painter.h" -#include "third_party/blink/renderer/core/css_property_names.h" +#include "third_party/blink/renderer/core/css/css_property_names.h" #include "third_party/blink/renderer/core/frame/settings.h" #include "third_party/blink/renderer/core/layout/api/line_layout_api_shim.h" #include "third_party/blink/renderer/core/layout/api/line_layout_item.h" diff --git a/chromium/third_party/blink/renderer/core/paint/text_painter_test.cc b/chromium/third_party/blink/renderer/core/paint/text_painter_test.cc index 31319930c46..99a269a730d 100644 --- a/chromium/third_party/blink/renderer/core/paint/text_painter_test.cc +++ b/chromium/third_party/blink/renderer/core/paint/text_painter_test.cc @@ -7,7 +7,7 @@ #include <memory> #include "testing/gtest/include/gtest/gtest.h" #include "third_party/blink/renderer/core/css/css_primitive_value.h" -#include "third_party/blink/renderer/core/css_property_names.h" +#include "third_party/blink/renderer/core/css/css_property_names.h" #include "third_party/blink/renderer/core/css_value_keywords.h" #include "third_party/blink/renderer/core/frame/settings.h" #include "third_party/blink/renderer/core/layout/api/line_layout_text.h" @@ -38,10 +38,13 @@ class TextPainterTest : public RenderingTest { is_printing ? kGlobalPaintPrinting : kGlobalPaintNormalPhase, 0); } - private: + protected: void SetUp() override { RenderingTest::SetUp(); SetBodyInnerHTML("Hello world"); + UpdateLayoutText(); + } + void UpdateLayoutText() { layout_text_ = ToLayoutText(GetDocument().body()->firstChild()->GetLayoutObject()); ASSERT_TRUE(layout_text_); @@ -55,7 +58,7 @@ class TextPainterTest : public RenderingTest { TEST_F(TextPainterTest, TextPaintingStyle_Simple) { GetDocument().body()->SetInlineStyleProperty(CSSPropertyColor, CSSValueBlue); - GetDocument().View()->UpdateAllLifecyclePhases(); + UpdateAllLifecyclePhasesForTest(); TextPaintStyle text_style = TextPainter::TextPaintingStyle( GetLineLayoutText().GetDocument(), GetLineLayoutText().StyleRef(), @@ -79,7 +82,7 @@ TEST_F(TextPainterTest, TextPaintingStyle_AllProperties) { CSSPrimitiveValue::UnitType::kPixels); GetDocument().body()->SetInlineStyleProperty(CSSPropertyTextShadow, "1px 2px 3px yellow"); - GetDocument().View()->UpdateAllLifecyclePhases(); + UpdateAllLifecyclePhasesForTest(); TextPaintStyle text_style = TextPainter::TextPaintingStyle( GetLineLayoutText().GetDocument(), GetLineLayoutText().StyleRef(), @@ -109,7 +112,7 @@ TEST_F(TextPainterTest, TextPaintingStyle_UsesTextAsClip) { CSSPrimitiveValue::UnitType::kPixels); GetDocument().body()->SetInlineStyleProperty(CSSPropertyTextShadow, "1px 2px 3px yellow"); - GetDocument().View()->UpdateAllLifecyclePhases(); + UpdateAllLifecyclePhasesForTest(); TextPaintStyle text_style = TextPainter::TextPaintingStyle( GetLineLayoutText().GetDocument(), GetLineLayoutText().StyleRef(), @@ -134,7 +137,10 @@ TEST_F(TextPainterTest, GetDocument().GetSettings()->SetShouldPrintBackgrounds(false); FloatSize page_size(500, 800); GetFrame().StartPrinting(page_size, page_size, 1); - GetDocument().View()->UpdateAllLifecyclePhases(); + UpdateAllLifecyclePhasesForTest(); + // In LayoutNG, printing currently forces layout tree reattachment, + // so we need to re-get layout_text_. + UpdateLayoutText(); TextPaintStyle text_style = TextPainter::TextPaintingStyle( GetLineLayoutText().GetDocument(), GetLineLayoutText().StyleRef(), @@ -157,6 +163,9 @@ TEST_F(TextPainterTest, TextPaintingStyle_ForceBackgroundToWhite_Darkened) { FloatSize page_size(500, 800); GetFrame().StartPrinting(page_size, page_size, 1); GetDocument().View()->UpdateLifecyclePhasesForPrinting(); + // In LayoutNG, printing currently forces layout tree reattachment, + // so we need to re-get layout_text_. + UpdateLayoutText(); TextPaintStyle text_style = TextPainter::TextPaintingStyle( GetLineLayoutText().GetDocument(), GetLineLayoutText().StyleRef(), diff --git a/chromium/third_party/blink/renderer/core/paint/theme_painter.cc b/chromium/third_party/blink/renderer/core/paint/theme_painter.cc index bdf67c30e1b..b6eae64342d 100644 --- a/chromium/third_party/blink/renderer/core/paint/theme_painter.cc +++ b/chromium/third_party/blink/renderer/core/paint/theme_painter.cc @@ -68,6 +68,7 @@ ThemePainter::ThemePainter() = default; #define COUNT_APPEARANCE(doc, feature) \ UseCounter::Count(doc, WebFeature::kCSSValueAppearance##feature##Rendered) +// Returns true; Needs CSS painting and/or PaintBorderOnly(). bool ThemePainter::Paint(const LayoutObject& o, const PaintInfo& paint_info, const IntRect& r) { @@ -99,14 +100,14 @@ bool ThemePainter::Paint(const LayoutObject& o, case kCheckboxPart: { COUNT_APPEARANCE(doc, Checkbox); auto* input = ToHTMLInputElementOrNull(node); - if (!input || input->type() != InputTypeNames::checkbox) + if (!input || input->type() != input_type_names::kCheckbox) COUNT_APPEARANCE(doc, CheckboxForOthers); return PaintCheckbox(node, o.GetDocument(), style, paint_info, r); } case kRadioPart: { COUNT_APPEARANCE(doc, Radio); auto* input = ToHTMLInputElementOrNull(node); - if (!input || input->type() != InputTypeNames::radio) + if (!input || input->type() != input_type_names::kRadio) COUNT_APPEARANCE(doc, RadioForOthers); return PaintRadio(node, o.GetDocument(), style, paint_info, r); } @@ -120,7 +121,7 @@ bool ThemePainter::Paint(const LayoutObject& o, case kSquareButtonPart: { COUNT_APPEARANCE(doc, SquareButton); auto* input = ToHTMLInputElementOrNull(node); - if (!input || input->type() != InputTypeNames::color) + if (!input || input->type() != input_type_names::kColor) COUNT_APPEARANCE(doc, SquareButtonForOthers); return PaintButton(node, o.GetDocument(), style, paint_info, r); } @@ -148,14 +149,14 @@ bool ThemePainter::Paint(const LayoutObject& o, case kSliderHorizontalPart: { COUNT_APPEARANCE(doc, SliderHorizontal); auto* input = ToHTMLInputElementOrNull(node); - if (!input || input->type() != InputTypeNames::range) + if (!input || input->type() != input_type_names::kRange) COUNT_APPEARANCE(doc, SliderHorizontalForOthers); return PaintSliderTrack(o, paint_info, r); } case kSliderVerticalPart: { COUNT_APPEARANCE(doc, SliderVertical); auto* input = ToHTMLInputElementOrNull(node); - if (!input || input->type() != InputTypeNames::range) + if (!input || input->type() != input_type_names::kRange) COUNT_APPEARANCE(doc, SliderVerticalForOthers); return PaintSliderTrack(o, paint_info, r); } @@ -163,7 +164,7 @@ bool ThemePainter::Paint(const LayoutObject& o, COUNT_APPEARANCE(doc, SliderThumbHorizontal); auto* input = ToHTMLInputElementOrNull(node ? node->OwnerShadowHost() : nullptr); - if (!input || input->type() != InputTypeNames::range) + if (!input || input->type() != input_type_names::kRange) COUNT_APPEARANCE(doc, SliderThumbHorizontalForOthers); return PaintSliderThumb(node, style, paint_info, r); } @@ -171,7 +172,7 @@ bool ThemePainter::Paint(const LayoutObject& o, COUNT_APPEARANCE(doc, SliderThumbVertical); auto* input = ToHTMLInputElementOrNull(node ? node->OwnerShadowHost() : nullptr); - if (!input || input->type() != InputTypeNames::range) + if (!input || input->type() != input_type_names::kRange) COUNT_APPEARANCE(doc, SliderThumbVerticalForOthers); return PaintSliderThumb(node, style, paint_info, r); } @@ -205,7 +206,7 @@ bool ThemePainter::Paint(const LayoutObject& o, case kSearchFieldPart: { COUNT_APPEARANCE(doc, SearchField); auto* input = ToHTMLInputElementOrNull(node); - if (!input || input->type() != InputTypeNames::search) + if (!input || input->type() != input_type_names::kSearch) COUNT_APPEARANCE(doc, SearchFieldForOthers); return PaintSearchField(node, style, paint_info, r); } @@ -213,8 +214,8 @@ bool ThemePainter::Paint(const LayoutObject& o, COUNT_APPEARANCE(doc, SearchCancel); auto* element = ToElementOrNull(node); if (!element || !element->OwnerShadowHost() || - element->FastGetAttribute(HTMLNames::idAttr) != - ShadowElementNames::SearchClearButton()) + element->FastGetAttribute(html_names::kIdAttr) != + shadow_element_names::SearchClearButton()) COUNT_APPEARANCE(doc, SearchCancelForOthers); return PaintSearchFieldCancelButton(o, paint_info, r); } @@ -226,6 +227,7 @@ bool ThemePainter::Paint(const LayoutObject& o, return true; } +// Returns true; Needs CSS border painting. bool ThemePainter::PaintBorderOnly(const Node* node, const ComputedStyle& style, const PaintInfo& paint_info, @@ -237,7 +239,7 @@ bool ThemePainter::PaintBorderOnly(const Node* node, UseCounter::Count(node->GetDocument(), WebFeature::kCSSValueAppearanceTextFieldRendered); if (auto* input = ToHTMLInputElementOrNull(node)) { - if (input->type() == InputTypeNames::search) { + if (input->type() == input_type_names::kSearch) { UseCounter::Count( node->GetDocument(), WebFeature::kCSSValueAppearanceTextFieldForSearch); @@ -261,21 +263,30 @@ bool ThemePainter::PaintBorderOnly(const Node* node, case kSearchFieldPart: case kListboxPart: return true; - case kCheckboxPart: - case kRadioPart: - case kPushButtonPart: - case kSquareButtonPart: case kButtonPart: + case kCheckboxPart: + case kInnerSpinButtonPart: case kMenulistPart: - case kMeterPart: case kProgressBarPart: + case kPushButtonPart: + case kRadioPart: + case kSearchFieldCancelButtonPart: case kSliderHorizontalPart: - case kSliderVerticalPart: case kSliderThumbHorizontalPart: case kSliderThumbVerticalPart: - case kSearchFieldCancelButtonPart: + case kSliderVerticalPart: + case kSquareButtonPart: + // Supported appearance values don't need CSS border painting. + return false; default: - break; + if (node) { + UseCounter::Count( + node->GetDocument(), + WebFeature::kCSSValueAppearanceNoImplementationSkipBorder); + } + // TODO(tkent): Should do CSS border painting for non-supported + // appearance values. + return false; } return false; @@ -326,7 +337,7 @@ void ThemePainter::PaintSliderTicks(const LayoutObject& o, return; HTMLInputElement* input = ToHTMLInputElement(node); - if (input->type() != InputTypeNames::range || + if (input->type() != input_type_names::kRange || !input->UserAgentShadowRoot()->HasChildren()) return; @@ -345,7 +356,7 @@ void ThemePainter::PaintSliderTicks(const LayoutObject& o, IntSize thumb_size; LayoutObject* thumb_layout_object = input->UserAgentShadowRoot() - ->getElementById(ShadowElementNames::SliderThumb()) + ->getElementById(shadow_element_names::SliderThumb()) ->GetLayoutObject(); if (thumb_layout_object) { const ComputedStyle& thumb_style = thumb_layout_object->StyleRef(); @@ -363,7 +374,7 @@ void ThemePainter::PaintSliderTicks(const LayoutObject& o, IntRect track_bounds; LayoutObject* track_layout_object = input->UserAgentShadowRoot() - ->getElementById(ShadowElementNames::SliderTrack()) + ->getElementById(shadow_element_names::SliderTrack()) ->GetLayoutObject(); // We can ignoring transforms because transform is handled by the graphics // context. diff --git a/chromium/third_party/blink/renderer/core/paint/theme_painter_default.cc b/chromium/third_party/blink/renderer/core/paint/theme_painter_default.cc index ed220102bb9..d4f4e51b646 100644 --- a/chromium/third_party/blink/renderer/core/paint/theme_painter_default.cc +++ b/chromium/third_party/blink/renderer/core/paint/theme_painter_default.cc @@ -35,7 +35,7 @@ #include "third_party/blink/renderer/platform/graphics/color.h" #include "third_party/blink/renderer/platform/graphics/graphics_context.h" #include "third_party/blink/renderer/platform/graphics/graphics_context_state_saver.h" -#include "third_party/blink/renderer/platform/layout_test_support.h" +#include "third_party/blink/renderer/platform/web_test_support.h" namespace blink { @@ -44,7 +44,7 @@ namespace { const unsigned kDefaultButtonBackgroundColor = 0xffdddddd; bool UseMockTheme() { - return LayoutTestSupport::IsMockThemeEnabledForTest(); + return WebTestSupport::IsMockThemeEnabledForTest(); } WebThemeEngine::State GetWebThemeState(const Node* node) { diff --git a/chromium/third_party/blink/renderer/core/paint/video_painter.cc b/chromium/third_party/blink/renderer/core/paint/video_painter.cc index 97152a71e8b..4ae8687d6eb 100644 --- a/chromium/third_party/blink/renderer/core/paint/video_painter.cc +++ b/chromium/third_party/blink/renderer/core/paint/video_painter.cc @@ -52,11 +52,11 @@ void VideoPainter::PaintReplaced(const PaintInfo& paint_info, if (paint_with_foreign_layer) { if (cc::Layer* layer = layout_video_.MediaElement()->CcLayer()) { IntRect pixel_snapped_rect = PixelSnappedIntRect(content_rect); - layer->SetBounds(static_cast<gfx::Size>(pixel_snapped_rect.Size())); + layer->SetOffsetToTransformParent( + gfx::Vector2dF(pixel_snapped_rect.X(), pixel_snapped_rect.Y())); + layer->SetBounds(gfx::Size(pixel_snapped_rect.Size())); layer->SetIsDrawable(true); - RecordForeignLayer( - context, layout_video_, DisplayItem::kForeignLayerVideo, layer, - FloatPoint(pixel_snapped_rect.Location()), pixel_snapped_rect.Size()); + RecordForeignLayer(context, DisplayItem::kForeignLayerVideo, layer); return; } } diff --git a/chromium/third_party/blink/renderer/core/paint/video_painter_test.cc b/chromium/third_party/blink/renderer/core/paint/video_painter_test.cc index 998110e4ae1..7a445402b80 100644 --- a/chromium/third_party/blink/renderer/core/paint/video_painter_test.cc +++ b/chromium/third_party/blink/renderer/core/paint/video_painter_test.cc @@ -26,7 +26,7 @@ class StubWebMediaPlayer : public EmptyWebMediaPlayer { const cc::Layer* GetCcLayer() { return layer_.get(); } // WebMediaPlayer - LoadTiming Load(LoadType, const WebMediaPlayerSource&, CORSMode) override { + LoadTiming Load(LoadType, const WebMediaPlayerSource&, CorsMode) override { network_state_ = kNetworkStateLoaded; client_->NetworkStateChanged(); ready_state_ = kReadyStateHaveEnoughData; @@ -88,7 +88,7 @@ TEST_F(VideoPainterTestForSPv2, VideoLayerAppearsInLayerTree) { test::RunPendingTasks(); // Force the page to paint. - GetDocument().View()->UpdateAllLifecyclePhases(); + UpdateAllLifecyclePhasesForTest(); // Fetch the layer associated with the <video>, and check that it was // correctly configured in the layer tree. diff --git a/chromium/third_party/blink/renderer/core/paint/view_painter.cc b/chromium/third_party/blink/renderer/core/paint/view_painter.cc index eeef54c1fa2..c81a3325fb5 100644 --- a/chromium/third_party/blink/renderer/core/paint/view_painter.cc +++ b/chromium/third_party/blink/renderer/core/paint/view_painter.cc @@ -15,6 +15,7 @@ #include "third_party/blink/renderer/core/paint/compositing/composited_layer_mapping.h" #include "third_party/blink/renderer/core/paint/paint_info.h" #include "third_party/blink/renderer/core/paint/paint_layer.h" +#include "third_party/blink/renderer/core/paint/paint_layer_scrollable_area.h" #include "third_party/blink/renderer/platform/graphics/paint/drawing_recorder.h" #include "third_party/blink/renderer/platform/graphics/paint/scoped_paint_chunk_properties.h" #include "third_party/blink/renderer/platform/runtime_enabled_features.h" @@ -31,38 +32,27 @@ void ViewPainter::Paint(const PaintInfo& paint_info) { } void ViewPainter::PaintBoxDecorationBackground(const PaintInfo& paint_info) { - if (paint_info.SkipRootBackground()) + if (layout_view_.StyleRef().Visibility() != EVisibility::kVisible) return; - // This function overrides background painting for the LayoutView. - // View background painting is special in the following ways: - // 1. The view paints background for the root element, the background - // positioning respects the positioning and transformation of the root - // element. - // 2. CSS background-clip is ignored, the background layers always expand to - // cover the whole canvas. None of the stacking context effects (except - // transformation) on the root element affects the background. - // 3. The main frame is also responsible for painting the user-agent-defined - // base background color. Conceptually it should be painted by the embedder - // but painting it here allows culling and pre-blending optimization when - // possible. - - GraphicsContext& context = paint_info.context; + bool has_touch_action_rect = + RuntimeEnabledFeatures::PaintTouchActionRectsEnabled() && + (layout_view_.HasEffectiveWhitelistedTouchAction()); + if (!layout_view_.HasBoxDecorationBackground() && !has_touch_action_rect) + return; // The background rect always includes at least the visible content size. - IntRect background_rect( - PixelSnappedIntRect(layout_view_.OverflowClipRect(LayoutPoint()))); + IntRect background_rect(PixelSnappedIntRect(layout_view_.BackgroundRect())); // When printing, paint the entire unclipped scrolling content area. if (paint_info.IsPrinting()) background_rect.Unite(layout_view_.DocumentRect()); - const DisplayItemClient* display_item_client = &layout_view_; + const DisplayItemClient* background_client = &layout_view_; base::Optional<ScopedPaintChunkProperties> scoped_scroll_property; - if (BoxModelObjectPainter:: - IsPaintingBackgroundOfPaintContainerIntoScrollingContentsLayer( - &layout_view_, paint_info)) { + if (BoxModelObjectPainter::IsPaintingScrollingBackground(&layout_view_, + paint_info)) { // Layout overflow, combined with the visible content size. auto document_rect = layout_view_.DocumentRect(); // DocumentRect is relative to ScrollOrigin. Add ScrollOrigin to let it be @@ -70,26 +60,57 @@ void ViewPainter::PaintBoxDecorationBackground(const PaintInfo& paint_info) { // object_paint_properties.h for details. document_rect.MoveBy(layout_view_.ScrollOrigin()); background_rect.Unite(document_rect); - display_item_client = layout_view_.Layer()->GraphicsLayerBacking(); + background_client = &layout_view_.GetScrollableArea() + ->GetScrollingBackgroundDisplayItemClient(); scoped_scroll_property.emplace( paint_info.context.GetPaintController(), - layout_view_.FirstFragment().ContentsProperties(), *display_item_client, + layout_view_.FirstFragment().ContentsProperties(), *background_client, DisplayItem::kDocumentBackground); } + if (layout_view_.HasBoxDecorationBackground()) { + PaintBoxDecorationBackgroundInternal(paint_info, background_rect, + *background_client); + } + if (has_touch_action_rect) { + BoxPainter(layout_view_) + .RecordHitTestData(paint_info, LayoutRect(background_rect), + *background_client); + } +} + +// This function handles background painting for the LayoutView. +// View background painting is special in the following ways: +// 1. The view paints background for the root element, the background +// positioning respects the positioning and transformation of the root +// element. +// 2. CSS background-clip is ignored, the background layers always expand to +// cover the whole canvas. None of the stacking context effects (except +// transformation) on the root element affects the background. +// 3. The main frame is also responsible for painting the user-agent-defined +// base background color. Conceptually it should be painted by the embedder +// but painting it here allows culling and pre-blending optimization when +// possible. +void ViewPainter::PaintBoxDecorationBackgroundInternal( + const PaintInfo& paint_info, + const IntRect& background_rect, + const DisplayItemClient& background_client) { + // TODO(pdr): Can this check be removed? It is not hit in any test. + if (paint_info.SkipRootBackground()) + return; + + GraphicsContext& context = paint_info.context; if (DrawingRecorder::UseCachedDrawingIfPossible( - context, *display_item_client, DisplayItem::kDocumentBackground)) + context, background_client, DisplayItem::kDocumentBackground)) { return; + } + DrawingRecorder recorder(context, background_client, + DisplayItem::kDocumentBackground); const Document& document = layout_view_.GetDocument(); const LocalFrameView& frame_view = *layout_view_.GetFrameView(); - bool is_main_frame = document.IsInMainFrame(); - bool paints_base_background = - is_main_frame && (frame_view.BaseBackgroundColor().Alpha() > 0); - bool should_clear_canvas = - paints_base_background && - (document.GetSettings() && - document.GetSettings()->GetShouldClearDocumentBackground()); + bool paints_base_background = document.IsInMainFrame() && + (frame_view.BaseBackgroundColor().Alpha() > 0); Color base_background_color = paints_base_background ? frame_view.BaseBackgroundColor() : Color(); Color root_background_color = layout_view_.StyleRef().VisitedDependentColor( @@ -98,9 +119,6 @@ void ViewPainter::PaintBoxDecorationBackground(const PaintInfo& paint_info) { document.documentElement() ? document.documentElement()->GetLayoutObject() : nullptr; - DrawingRecorder recorder(context, *display_item_client, - DisplayItem::kDocumentBackground); - // Special handling for print economy mode. bool force_background_to_white = BoxModelObjectPainter::ShouldForceWhiteBackgroundForPrintEconomy( @@ -109,7 +127,7 @@ void ViewPainter::PaintBoxDecorationBackground(const PaintInfo& paint_info) { // If for any reason the view background is not transparent, paint white // instead, otherwise keep transparent as is. if (paints_base_background || root_background_color.Alpha() || - layout_view_.StyleRef().BackgroundLayers().GetImage()) + layout_view_.StyleRef().BackgroundLayers().AnyLayerHasImage()) context.FillRect(background_rect, Color::kWhite, SkBlendMode::kSrc); return; } @@ -130,9 +148,8 @@ void ViewPainter::PaintBoxDecorationBackground(const PaintInfo& paint_info) { if (!root_object || !root_object->IsBox()) { background_renderable = false; } else if (root_object->HasLayer()) { - if (BoxModelObjectPainter:: - IsPaintingBackgroundOfPaintContainerIntoScrollingContentsLayer( - &layout_view_, paint_info)) { + if (BoxModelObjectPainter::IsPaintingScrollingBackground(&layout_view_, + paint_info)) { transform.Translate(layout_view_.ScrolledContentOffset().Width(), layout_view_.ScrolledContentOffset().Height()); } @@ -155,6 +172,10 @@ void ViewPainter::PaintBoxDecorationBackground(const PaintInfo& paint_info) { } } + bool should_clear_canvas = + paints_base_background && + (document.GetSettings() && + document.GetSettings()->GetShouldClearDocumentBackground()); if (!background_renderable) { if (base_background_color.Alpha()) { context.FillRect( diff --git a/chromium/third_party/blink/renderer/core/paint/view_painter.h b/chromium/third_party/blink/renderer/core/paint/view_painter.h index a5fcfbe03b1..36563c5a9c3 100644 --- a/chromium/third_party/blink/renderer/core/paint/view_painter.h +++ b/chromium/third_party/blink/renderer/core/paint/view_painter.h @@ -10,6 +10,8 @@ namespace blink { struct PaintInfo; +class DisplayItemClient; +class IntRect; class LayoutView; class ViewPainter { @@ -23,6 +25,11 @@ class ViewPainter { private: const LayoutView& layout_view_; + + void PaintBoxDecorationBackgroundInternal( + const PaintInfo&, + const IntRect& background_rect, + const DisplayItemClient& background_client); }; } // namespace blink diff --git a/chromium/third_party/blink/renderer/core/paint/view_painter_test.cc b/chromium/third_party/blink/renderer/core/paint/view_painter_test.cc index ce4b14e41ab..f217354b9a0 100644 --- a/chromium/third_party/blink/renderer/core/paint/view_painter_test.cc +++ b/chromium/third_party/blink/renderer/core/paint/view_painter_test.cc @@ -5,10 +5,13 @@ #include "third_party/blink/renderer/core/paint/view_painter.h" #include <gtest/gtest.h> +#include "third_party/blink/renderer/core/frame/local_dom_window.h" #include "third_party/blink/renderer/core/paint/compositing/composited_layer_mapping.h" #include "third_party/blink/renderer/core/paint/paint_controller_paint_test.h" #include "third_party/blink/renderer/platform/graphics/paint/drawing_display_item.h" +using testing::ElementsAre; + namespace blink { class ViewPainterTest : public PaintControllerPaintTest { @@ -20,11 +23,6 @@ INSTANTIATE_PAINT_TEST_CASE_P(ViewPainterTest); void ViewPainterTest::RunFixedBackgroundTest( bool prefer_compositing_to_lcd_text) { - // TODO(crbug.com/792577): Cull rect for frame scrolling contents is too - // small. - if (RuntimeEnabledFeatures::SlimmingPaintV2Enabled()) - return; - if (prefer_compositing_to_lcd_text) { Settings* settings = GetDocument().GetFrame()->GetSettings(); settings->SetPreferCompositingToLCDTextEnabled(true); @@ -47,42 +45,74 @@ void ViewPainterTest::RunFixedBackgroundTest( ScrollOffset scroll_offset(200, 150); layout_viewport->SetScrollOffset(scroll_offset, kUserScroll); - frame_view->UpdateAllLifecyclePhases(); + frame_view->UpdateAllLifecyclePhases( + DocumentLifecycle::LifecycleUpdateReason::kTest); - CompositedLayerMapping* clm = - GetLayoutView().Layer()->GetCompositedLayerMapping(); - - // If we prefer compositing to LCD text, the fixed background should go in a - // different layer from the scrolling content; otherwise, it should go in the - // same layer (i.e., the scrolling contents layer). - GraphicsLayer* layer_for_background; - if (prefer_compositing_to_lcd_text) { - layer_for_background = clm->MainGraphicsLayer(); + const DisplayItem* background_display_item = nullptr; + if (RuntimeEnabledFeatures::SlimmingPaintV2Enabled()) { + const auto& display_items = RootPaintController().GetDisplayItemList(); + if (prefer_compositing_to_lcd_text) { + EXPECT_THAT( + display_items, + ElementsAre(IsSameId(&GetLayoutView(), kDocumentBackgroundType), + IsSameId(&GetLayoutView(), DisplayItem::kScrollHitTest), + IsSameId(GetDocument().body()->GetLayoutObject(), + kBackgroundType))); + background_display_item = &display_items[0]; + } else { + EXPECT_THAT( + display_items, + ElementsAre(IsSameId(&GetLayoutView(), DisplayItem::kScrollHitTest), + IsSameId(&ViewScrollingBackgroundClient(), + kDocumentBackgroundType), + IsSameId(GetDocument().body()->GetLayoutObject(), + kBackgroundType))); + background_display_item = &display_items[1]; + } } else { - layer_for_background = clm->ScrollingContentsLayer(); + // If we prefer compositing to LCD text, the fixed background should go in a + // different layer from the scrolling content; otherwise, it should go in + // the same layer (i.e., the scrolling contents layer). + if (prefer_compositing_to_lcd_text) { + const auto& display_items = GetLayoutView() + .Layer() + ->GraphicsLayerBacking(&GetLayoutView()) + ->GetPaintController() + .GetDisplayItemList(); + EXPECT_THAT( + display_items, + ElementsAre(IsSameId(&GetLayoutView(), kDocumentBackgroundType))); + background_display_item = &display_items[0]; + } else { + const auto& display_items = RootPaintController().GetDisplayItemList(); + EXPECT_THAT(display_items, + ElementsAre(IsSameId(&ViewScrollingBackgroundClient(), + kDocumentBackgroundType), + IsSameId(GetDocument().body()->GetLayoutObject(), + kBackgroundType))); + background_display_item = &display_items[0]; + } } - const DisplayItemList& display_items = - layer_for_background->GetPaintController().GetDisplayItemList(); - const DisplayItem& background = display_items[0]; - EXPECT_EQ(background.GetType(), kDocumentBackgroundType); - DisplayItemClient* expected_client; - if (!prefer_compositing_to_lcd_text) - expected_client = GetLayoutView().Layer()->GraphicsLayerBacking(); - else - expected_client = &GetLayoutView(); - EXPECT_EQ(&background.Client(), expected_client); sk_sp<const PaintRecord> record = - static_cast<const DrawingDisplayItem&>(background).GetPaintRecord(); + static_cast<const DrawingDisplayItem*>(background_display_item) + ->GetPaintRecord(); ASSERT_EQ(record->size(), 2u); cc::PaintOpBuffer::Iterator it(record.get()); ASSERT_EQ((*++it)->GetType(), cc::PaintOpType::DrawRect); - // This is the dest_rect_ calculated by BackgroundImageGeometry. For a fixed + // This is the dest_rect_ calculated by BackgroundImageGeometry. For a fixed // background in scrolling contents layer, its location is the scroll offset. SkRect rect = static_cast<const cc::DrawRectOp*>(*it)->rect; - ASSERT_EQ(prefer_compositing_to_lcd_text ? ScrollOffset() : scroll_offset, - ScrollOffset(rect.fLeft, rect.fTop)); + if (prefer_compositing_to_lcd_text) { + EXPECT_EQ(SkRect::MakeXYWH(0, 0, 800, 600), rect); + } else if (RuntimeEnabledFeatures::SlimmingPaintV2Enabled()) { + EXPECT_EQ(SkRect::MakeXYWH(0, 0, 800, 600), rect); + } else { + EXPECT_EQ(SkRect::MakeXYWH(scroll_offset.Width(), scroll_offset.Height(), + 800, 600), + rect); + } } TEST_P(ViewPainterTest, DocumentFixedBackgroundLowDPI) { @@ -94,32 +124,195 @@ TEST_P(ViewPainterTest, DocumentFixedBackgroundHighDPI) { } TEST_P(ViewPainterTest, DocumentBackgroundWithScroll) { - // TODO(crbug.com/792577): Cull rect for frame scrolling contents is too - // small. - if (RuntimeEnabledFeatures::SlimmingPaintV2Enabled()) - return; - - SetBodyInnerHTML("<div style='height: 5000px'></div>"); - - const DisplayItemClient* background_item_client; - const DisplayItemClient* background_chunk_client; - background_item_client = GetLayoutView().Layer()->GraphicsLayerBacking(); - background_chunk_client = background_item_client; - - EXPECT_DISPLAY_LIST( - RootPaintController().GetDisplayItemList(), 1, - TestDisplayItem(*background_item_client, kDocumentBackgroundType)); - - const auto& chunks = RootPaintController().GetPaintArtifact().PaintChunks(); - EXPECT_EQ(1u, chunks.size()); - const auto& chunk = chunks[0]; - EXPECT_EQ(background_chunk_client, &chunk.id.client); - - const auto& tree_state = chunk.properties; - EXPECT_EQ(&EffectPaintPropertyNode::Root(), tree_state.Effect()); - const auto* properties = GetLayoutView().FirstFragment().PaintProperties(); - EXPECT_EQ(properties->ScrollTranslation(), tree_state.Transform()); - EXPECT_EQ(properties->OverflowClip(), tree_state.Clip()); + SetBodyInnerHTML(R"HTML( + <style>::-webkit-scrollbar { display: none }</style> + <div style='height: 5000px'></div> + )HTML"); + + if (RuntimeEnabledFeatures::SlimmingPaintV2Enabled()) { + EXPECT_THAT( + RootPaintController().GetDisplayItemList(), + ElementsAre(IsSameId(&GetLayoutView(), DisplayItem::kScrollHitTest), + IsSameId(&ViewScrollingBackgroundClient(), + kDocumentBackgroundType))); + EXPECT_THAT( + RootPaintController().PaintChunks(), + ElementsAre( + IsPaintChunk( + 0, 1, + PaintChunk::Id(*GetLayoutView().Layer(), + DisplayItem::kLayerChunkBackground), + GetLayoutView().FirstFragment().LocalBorderBoxProperties()), + IsPaintChunk( + 1, 2, + PaintChunk::Id(ViewScrollingBackgroundClient(), + kDocumentBackgroundType), + GetLayoutView().FirstFragment().ContentsProperties()))); + } else { + EXPECT_THAT(RootPaintController().GetDisplayItemList(), + ElementsAre(IsSameId(&ViewScrollingBackgroundClient(), + kDocumentBackgroundType))); + EXPECT_THAT(RootPaintController().PaintChunks(), + ElementsAre(IsPaintChunk( + 0, 1, + PaintChunk::Id(ViewScrollingBackgroundClient(), + kDocumentBackgroundType), + GetLayoutView().FirstFragment().ContentsProperties()))); + } +} + +class ViewPainterTestWithPaintTouchAction + : public ViewPainterTest, + private ScopedPaintTouchActionRectsForTest { + public: + ViewPainterTestWithPaintTouchAction() + : ViewPainterTest(), ScopedPaintTouchActionRectsForTest(true) {} + + void SetUp() override { + ViewPainterTest::SetUp(); + Settings* settings = GetDocument().GetFrame()->GetSettings(); + settings->SetPreferCompositingToLCDTextEnabled(true); + } +}; + +INSTANTIATE_PAINT_TEST_CASE_P(ViewPainterTestWithPaintTouchAction); + +TEST_P(ViewPainterTestWithPaintTouchAction, TouchActionRectScrollingContents) { + SetBodyInnerHTML(R"HTML( + <style> + ::-webkit-scrollbar { display: none; } + html { + background: lightblue; + touch-action: none; + } + body { + margin: 0; + } + </style> + <div id='forcescroll' style='width: 0; height: 3000px;'></div> + )HTML"); + + GetFrame().DomWindow()->scrollBy(0, 100); + UpdateAllLifecyclePhasesForTest(); + + const auto& scrolling_client = ViewScrollingBackgroundClient(); + auto scrolling_properties = + GetLayoutView().FirstFragment().ContentsProperties(); + HitTestData view_hit_test_data; + view_hit_test_data.touch_action_rects.emplace_back( + LayoutRect(0, 0, 800, 3000)); + + auto* html = + ToLayoutBlock(GetDocument().documentElement()->GetLayoutObject()); + HitTestData html_hit_test_data; + html_hit_test_data.touch_action_rects.emplace_back( + LayoutRect(0, 0, 800, 3000)); + html_hit_test_data.touch_action_rects.emplace_back( + LayoutRect(0, 0, 800, 3000)); + + if (RuntimeEnabledFeatures::SlimmingPaintV2Enabled()) { + HitTestData non_scrolling_hit_test_data; + non_scrolling_hit_test_data.touch_action_rects.emplace_back( + LayoutRect(0, 0, 800, 600)); + EXPECT_THAT( + RootPaintController().PaintChunks(), + ElementsAre( + IsPaintChunk( + 0, 1, + PaintChunk::Id(*GetLayoutView().Layer(), + DisplayItem::kLayerChunkBackground), + GetLayoutView().FirstFragment().LocalBorderBoxProperties(), + non_scrolling_hit_test_data), + IsPaintChunk( + 1, 2, + PaintChunk::Id(GetLayoutView(), DisplayItem::kScrollHitTest), + GetLayoutView().FirstFragment().LocalBorderBoxProperties()), + IsPaintChunk( + 2, 4, PaintChunk::Id(scrolling_client, kDocumentBackgroundType), + scrolling_properties, view_hit_test_data), + IsPaintChunk(4, 7, + PaintChunk::Id(*html->Layer(), + kNonScrollingBackgroundChunkType), + scrolling_properties, html_hit_test_data))); + } else { + EXPECT_THAT( + RootPaintController().PaintChunks(), + ElementsAre( + IsPaintChunk( + 0, 2, PaintChunk::Id(scrolling_client, kDocumentBackgroundType), + scrolling_properties, view_hit_test_data), + IsPaintChunk(2, 5, + PaintChunk::Id(*html->Layer(), + kNonScrollingBackgroundChunkType), + scrolling_properties, html_hit_test_data))); + } +} + +TEST_P(ViewPainterTestWithPaintTouchAction, + TouchActionRectNonScrollingContents) { + SetBodyInnerHTML(R"HTML( + <style> + ::-webkit-scrollbar { display: none; } + html { + background: radial-gradient( + circle at 100px 100px, blue, transparent 200px) fixed; + touch-action: none; + } + body { + margin: 0; + } + </style> + <div id='forcescroll' style='width: 0; height: 3000px;'></div> + )HTML"); + + GetFrame().DomWindow()->scrollBy(0, 100); + UpdateAllLifecyclePhasesForTest(); + + auto* view = &GetLayoutView(); + auto non_scrolling_properties = + view->FirstFragment().LocalBorderBoxProperties(); + HitTestData view_hit_test_data; + view_hit_test_data.touch_action_rects.emplace_back( + LayoutRect(0, 0, 800, 600)); + auto* html = + ToLayoutBlock(GetDocument().documentElement()->GetLayoutObject()); + auto scrolling_properties = view->FirstFragment().ContentsProperties(); + HitTestData scrolling_hit_test_data; + scrolling_hit_test_data.touch_action_rects.emplace_back( + LayoutRect(0, 0, 800, 3000)); + scrolling_hit_test_data.touch_action_rects.emplace_back( + LayoutRect(0, 0, 800, 3000)); + if (RuntimeEnabledFeatures::SlimmingPaintV2Enabled()) { + EXPECT_THAT( + RootPaintController().PaintChunks(), + ElementsAre( + IsPaintChunk(0, 2, + PaintChunk::Id(*view->Layer(), + DisplayItem::kLayerChunkBackground), + non_scrolling_properties, view_hit_test_data), + IsPaintChunk(2, 3, + PaintChunk::Id(*view, DisplayItem::kScrollHitTest), + non_scrolling_properties), + IsPaintChunk(3, 6, + PaintChunk::Id(*html->Layer(), + kNonScrollingBackgroundChunkType), + scrolling_properties, scrolling_hit_test_data))); + } else { + auto& non_scrolling_paint_controller = + view->Layer()->GraphicsLayerBacking(view)->GetPaintController(); + EXPECT_THAT( + non_scrolling_paint_controller.PaintChunks(), + ElementsAre(IsPaintChunk( + 0, 2, + PaintChunk::Id(*view->Layer(), kNonScrollingBackgroundChunkType), + non_scrolling_properties, view_hit_test_data))); + EXPECT_THAT( + RootPaintController().PaintChunks(), + ElementsAre(IsPaintChunk( + 0, 3, + PaintChunk::Id(*html->Layer(), kNonScrollingBackgroundChunkType), + scrolling_properties, scrolling_hit_test_data))); + } } } // namespace blink |