diff options
Diffstat (limited to 'chromium/third_party/blink/renderer/core/paint')
176 files changed, 7115 insertions, 4786 deletions
diff --git a/chromium/third_party/blink/renderer/core/paint/BUILD.gn b/chromium/third_party/blink/renderer/core/paint/BUILD.gn index 335341d4569..98c25ea6a92 100644 --- a/chromium/third_party/blink/renderer/core/paint/BUILD.gn +++ b/chromium/third_party/blink/renderer/core/paint/BUILD.gn @@ -134,6 +134,8 @@ blink_core_sources("paint") { "ng/ng_fragment_painter.h", "ng/ng_inline_box_fragment_painter.cc", "ng/ng_inline_box_fragment_painter.h", + "ng/ng_mathml_painter.cc", + "ng/ng_mathml_painter.h", "ng/ng_paint_fragment.cc", "ng/ng_paint_fragment.h", "ng/ng_paint_fragment_traversal.cc", diff --git a/chromium/third_party/blink/renderer/core/paint/DEPS b/chromium/third_party/blink/renderer/core/paint/DEPS index 25664f0f1f8..75b3e64e39c 100644 --- a/chromium/third_party/blink/renderer/core/paint/DEPS +++ b/chromium/third_party/blink/renderer/core/paint/DEPS @@ -7,11 +7,14 @@ include_rules = [ ] specific_include_rules = { - "(theme_painter|fallback_theme)\.cc": [ + "(theme_painter|fallback_theme|object_painter_base)\.cc": [ "+ui/native_theme/native_theme.h", "+ui/native_theme/native_theme_base.h", ], ".*test\.cc": [ "+base/test/trace_event_analyzer.h", - ] + ], + "video_painter_test.cc": [ + "+components/paint_preview/common/paint_preview_tracker.h", + ], } diff --git a/chromium/third_party/blink/renderer/core/paint/README.md b/chromium/third_party/blink/renderer/core/paint/README.md index 7dd05de5f52..6d5696496b2 100644 --- a/chromium/third_party/blink/renderer/core/paint/README.md +++ b/chromium/third_party/blink/renderer/core/paint/README.md @@ -165,12 +165,11 @@ most recent step towards CompositeAfterPaint was a project called which uses the compositing decisions from the current compositor (PaintLayerCompositor, which produces GraphicsLayers) with the new CompositeAfterPaint compositor (PaintArtifactCompositor). This is done by a step -at the end of paint which collects all painted GraphicsLayers (and their -associated cc::Layers) as a list of -[ForeignLayerDisplayItem](../../platform/graphics/paint/foreign_layer_display_item.h)s. -Foreign layers are typically used for cc::Layers managed outside blink (e.g., +at the end of paint which collects all painted GraphicsLayers as a list of +[GraphicsLayerDisplayItem](../../platform/graphics/paint/graphics_layer_display_item.h)s. +Additionaly, [ForeignLayerDisplayItem](../../platform/graphics/paint/foreign_layer_display_item.h)s are used for cc::Layers managed outside blink (e.g., video layers, plugin layers) and are treated as opaque composited content by -the PaintArtifactCompositor. This approach of using foreign layers starts using +the PaintArtifactCompositor. This approach starts using much of the new PaintArtifactCompositor logic (e.g., converting blink property trees to cc property trees) without changing how compositing decisions are made. @@ -292,9 +291,9 @@ from layout |<--------------------------------------------------+ | PaintChunksToCcLayer::Convert() | v | -+----------------+ | -| Foreign layers | | -+----------------+ | ++--------------------------------------------------+ | +| GraphicsLayerDisplayItem/ForeignLayerDisplayItem | | ++--------------------------------------------------+ | | | | LocalFrameView::PushPaintArtifactToCompositor() | | PaintArtifactCompositor::Update() | @@ -647,8 +646,7 @@ a PaintLayer and whether we can use cached subsequence for a PaintLayer. See During painting, we walk the layout tree multiple times for multiple paint phases. Sometimes a layer contain nothing needing a certain paint phase and we can skip tree walk for such empty phases. Now we have optimized -`PaintPhaseDescendantBlockBackgroundsOnly`, `PaintPhaseDescendantOutlinesOnly` -and `PaintPhaseFloat` for empty paint phases. +`PaintPhaseDescendantOutlinesOnly` and `PaintPhaseFloat` for empty paint phases. During paint invalidation, we set the containing self-painting layer's `NeedsPaintPhaseXXX` flag if the object has something needing to be painted in @@ -663,36 +661,40 @@ if an object changes style and creates a self-painting-layer, we copy the flags from its containing self-painting layer to this layer, assuming that this layer needs all paint phases that its container self-painting layer needs. -We could update the `NeedsPaintPhaseXXX` flags in a separate tree walk, but that -would regress performance of the first paint. For CompositeAfterPaint, we can -update the flags during the pre-painting tree walk to simplify the logic. - -### Hit test painting +### Hit test information recording Hit testing is done in paint-order, and to preserve this information the paint -system is re-used to paint hit test display items in the background phase of -painting. This information is then used in the compositor to implement cc-side -hit testing. Hit test display items are produced even if there is no painted -content. +system is re-used to record hit test information when painting the background. +This information is then used in the compositor to implement cc-side hit +testing. Hit test information is recorded even if there is no painted content. + +We record different types of hit test information in the following data +structures: + +1. Paint chunk bounds -There are two types of hit test painting: + The bounds of the current paint chunk are expanded to ensure the bounds + contain the hit testable area. -1. [HitTestDisplayItem](../../platform/graphics/paint/hit_test_display_item.h) +2. [`HitTestData::touch_action_rects`](../../platform/graphics/paint/hit_test_data.h) - Used for [touch action rects](http://docs.google.com/document/u/1/d/1ksiqEPkDeDuI_l5HvWlq1MfzFyDxSnsNB8YXIaXa3sE/view) - which are areas of the page that allow certain gesture effects, as well as - areas of the page that disallow touch events due to blocking touch event - handlers. + Used for [touch action rects](http://docs.google.com/document/u/1/d/1ksiqEPkDeDuI_l5HvWlq1MfzFyDxSnsNB8YXIaXa3sE/view) + which are areas of the page that allow certain gesture effects, as well as + areas of the page that disallow touch events due to blocking touch event + handlers. -2. [ScrollHitTestDisplayItem](../../platform/graphics/paint/scroll_hit_test_display_item.h) +3. [`HitTestData::scroll_translation`](../../platform/graphics/paint/hit_test_data.h) + and + [`HitTestData::scroll_hit_test_rect`](../../platform/graphics/paint/hit_test_data.h) - Used to create - [non-fast scrollable regions](https://docs.google.com/document/d/1IyYJ6bVF7KZq96b_s5NrAzGtVoBXn_LQnya9y4yT3iw/view) - to prevent compositor scrolling of non-composited scrollers, plugins with - blocking scroll event handlers, and resize handles. + Used to create + [non-fast scrollable regions](https://docs.google.com/document/d/1IyYJ6bVF7KZq96b_s5NrAzGtVoBXn_LQnya9y4yT3iw/view) + to prevent compositor scrolling of non-composited scrollers, plugins with + blocking scroll event handlers, and resize handles. - This is also used for CompositeAfterPaint to force a special cc::Layer that - is marked as being scrollable. + If `scroll_translation` is not null, this is also used for + CompositeAfterPaint to force a special cc::Layer that is marked as being + scrollable when composited scrolling is needed for the scroller. ### Scrollbar painting 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 a92ade16bda..eadaebbe0fc 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 @@ -39,27 +39,12 @@ inline LayoutUnit GetSpaceBetweenImageTiles(LayoutUnit area_size, bool FixedBackgroundPaintsInLocalCoordinates( const LayoutObject& obj, const GlobalPaintFlags global_paint_flags) { - if (!obj.IsLayoutView()) + const auto* view = DynamicTo<LayoutView>(obj); + if (!view) return false; - const LayoutView& view = ToLayoutView(obj); - - // TODO(wangxianzhu): For CAP, inline this function into - // FixedBackgroundPaintsInLocalCoordinates(). - if (RuntimeEnabledFeatures::CompositeAfterPaintEnabled()) { - return view.GetBackgroundPaintLocation() != - kBackgroundPaintInScrollingContents; - } - - if (global_paint_flags & kGlobalPaintFlattenCompositingLayers) - return false; - - PaintLayer* root_layer = view.Layer(); - if (!root_layer || root_layer->GetCompositingState() == kNotComposited) - return false; - - CompositedLayerMapping* mapping = root_layer->GetCompositedLayerMapping(); - return !mapping->BackgroundPaintsOntoScrollingContentsLayer(); + return !(view->GetBackgroundPaintLocation() & + kBackgroundPaintInScrollingContents); } LayoutPoint AccumulatedScrollOffsetForFixedBackground( @@ -103,15 +88,17 @@ void BackgroundImageGeometry::SetNoRepeatX(const FillLayer& fill_layer, return; } - // The snapped offset may not yet be snapped, so make sure it is an integer. - snapped_x_offset = LayoutUnit(RoundToInt(snapped_x_offset)); - if (x_offset > 0) { DCHECK(snapped_x_offset >= LayoutUnit()); // Move the dest rect if the offset is positive. The image "stays" where // it is over the dest rect, so this effectively modifies the phase. unsnapped_dest_rect_.Move(x_offset, LayoutUnit()); - snapped_dest_rect_.Move(snapped_x_offset, LayoutUnit()); + + // For the snapped geometry, note that negative x_offsets typically + // arise when using positive offsets from the bottom of the background + // rect. We try to move the snapped dest rect to give the same offset. + LayoutUnit dx = snapped_dest_rect_.Width() - unsnapped_dest_rect_.Width(); + snapped_dest_rect_.Move(x_offset + dx, LayoutUnit()); // Make the dest as wide as a tile, which will reduce the dest // rect if the tile is too small to fill the paint_rect. If not, @@ -147,16 +134,17 @@ void BackgroundImageGeometry::SetNoRepeatY(const FillLayer& fill_layer, LayoutSize(SpaceSize().Width(), unsnapped_dest_rect_.Height())); return; } - - // The snapped offset may not yet be snapped, so make sure it is an integer. - snapped_y_offset = LayoutUnit(RoundToInt(snapped_y_offset)); - if (y_offset > 0) { DCHECK(snapped_y_offset >= LayoutUnit()); // Move the dest rect if the offset is positive. The image "stays" where // it is in the paint rect, so this effectively modifies the phase. unsnapped_dest_rect_.Move(LayoutUnit(), y_offset); - snapped_dest_rect_.Move(LayoutUnit(), snapped_y_offset); + + // For the snapped geometry, note that negative y_offsets typically + // arise when using positive offsets from the bottom of the background + // rect. We try to move the snapped dest rect to give the same offset. + LayoutUnit dy = snapped_dest_rect_.Height() - unsnapped_dest_rect_.Height(); + snapped_dest_rect_.Move(LayoutUnit(), y_offset + dy); // Make the dest as wide as a tile, which will reduce the dest // rect if the tile is too small to fill the paint_rect. If not, @@ -402,17 +390,9 @@ 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()) { - if (RuntimeEnabledFeatures::CompositeAfterPaintEnabled()) { - DCHECK_EQ(obj.GetBackgroundPaintLocation(), - kBackgroundPaintInScrollingContents); - rect.SetLocation(LayoutPoint(ToLayoutView(obj).ScrolledContentOffset())); - } else if (auto* mapping = obj.Layer()->GetCompositedLayerMapping()) { - if (mapping->BackgroundPaintsOntoScrollingContentsLayer()) { - rect.SetLocation( - LayoutPoint(ToLayoutView(obj).ScrolledContentOffset())); - } - } + if (auto* layout_view = DynamicTo<LayoutView>(obj)) { + if (obj.GetBackgroundPaintLocation() & kBackgroundPaintInScrollingContents) + rect.SetLocation(LayoutPoint(layout_view->ScrolledContentOffset())); } rect.MoveBy(AccumulatedScrollOffsetForFixedBackground(obj, container)); @@ -442,13 +422,13 @@ LayoutRect FixedAttachmentPositioningArea(const LayoutBoxModelObject& obj, } // Anonymous namespace -BackgroundImageGeometry::BackgroundImageGeometry(const LayoutView& view) +BackgroundImageGeometry::BackgroundImageGeometry( + const LayoutView& view, + bool root_elemnet_has_transform) : box_(view), positioning_box_(view.RootBox()), - has_non_local_geometry_(false), painting_view_(true), - painting_table_cell_(false), - cell_using_container_background_(false) { + root_element_has_transform_(root_elemnet_has_transform) { // The background of the box generated by the root element covers the // entire canvas and will be painted by the view object, but the we should // still use the root element box for positioning. @@ -457,14 +437,9 @@ BackgroundImageGeometry::BackgroundImageGeometry(const LayoutView& view) BackgroundImageGeometry::BackgroundImageGeometry( const LayoutBoxModelObject& obj) - : box_(obj), - positioning_box_(obj), - has_non_local_geometry_(false), - painting_view_(false), - painting_table_cell_(false), - cell_using_container_background_(false) { + : box_(obj), positioning_box_(obj) { // Specialized constructor should be used for LayoutView. - DCHECK(!obj.IsLayoutView()); + DCHECK(!IsA<LayoutView>(obj)); } BackgroundImageGeometry::BackgroundImageGeometry( @@ -474,8 +449,6 @@ BackgroundImageGeometry::BackgroundImageGeometry( positioning_box_(background_object && !background_object->IsTableCell() ? ToLayoutBoxModelObject(*background_object) : cell), - has_non_local_geometry_(false), - painting_view_(false), painting_table_cell_(true) { cell_using_container_background_ = background_object && !background_object->IsTableCell(); @@ -729,11 +702,14 @@ void BackgroundImageGeometry::ComputePositioningArea( snapped_box_offset = LayoutPoint(snapped_box_outset.Left() - snapped_dest_adjust.Left(), snapped_box_outset.Top() - snapped_dest_adjust.Top()); - // For view backgrounds, the input paint rect is specified in root element - // local coordinate (i.e. a transform is applied on the context for - // painting), and is expanded to cover the whole canvas. Since left/top is - // relative to the paint rect, we need to offset them back. - if (painting_view_) { + + // |paint_rect|'s location is usually assumed by BackgroundImageGeometry + // to encode paint offset in the local transform space. The one case in + // which this is not true is painting the background of the LayoutView + // canvas when the HTML element has a transform. In that case, the + // paint offset is zero, and the offset gets applied later by a + // PaintOffsetTranslation. + if (painting_view_ && root_element_has_transform_) { unsnapped_box_offset -= paint_rect.Location(); snapped_box_offset -= paint_rect.Location(); } @@ -756,7 +732,8 @@ void BackgroundImageGeometry::CalculateFillTileSize( : unsnapped_positioning_area_size; LayoutSize image_intrinsic_size(image->ImageSize( positioning_box_.GetDocument(), - positioning_box_.StyleRef().EffectiveZoom(), positioning_area_size)); + positioning_box_.StyleRef().EffectiveZoom(), positioning_area_size, + LayoutObject::ShouldRespectImageOrientation(&box_))); switch (type) { case EFillSizeType::kSizeLength: { tile_size_ = positioning_area_size; @@ -1039,9 +1016,13 @@ void BackgroundImageGeometry::Calculate(const LayoutBoxModelObject* container, if (ShouldUseFixedAttachment(fill_layer)) UseFixedAttachment(paint_rect.Location()); - // Clip the final output rect to the paint rect, maintaining snapping. + // Clip the final output rect to the paint rect. unsnapped_dest_rect_.Intersect(paint_rect); - snapped_dest_rect_.Intersect(LayoutRect(PixelSnappedIntRect(paint_rect))); + + // Clip the snapped rect, and re-snap the dest rect as we may have + // adjusted it with unsnapped values. + snapped_dest_rect_.Intersect(paint_rect); + snapped_dest_rect_ = LayoutRect(PixelSnappedIntRect(snapped_dest_rect_)); } const ImageResourceObserver& BackgroundImageGeometry::ImageClient() const { diff --git a/chromium/third_party/blink/renderer/core/paint/background_image_geometry.h b/chromium/third_party/blink/renderer/core/paint/background_image_geometry.h index cac45a7592c..02c1f2eb794 100644 --- a/chromium/third_party/blink/renderer/core/paint/background_image_geometry.h +++ b/chromium/third_party/blink/renderer/core/paint/background_image_geometry.h @@ -31,7 +31,7 @@ class BackgroundImageGeometry { public: // Constructor for LayoutView where the coordinate space is different. - BackgroundImageGeometry(const LayoutView&); + BackgroundImageGeometry(const LayoutView&, bool root_elemnet_has_transform); // Constructor for table cells where background_object may be the row or // column the background image is attached to. @@ -181,10 +181,11 @@ class BackgroundImageGeometry { FloatPoint phase_; LayoutSize tile_size_; LayoutSize repeat_spacing_; - bool has_non_local_geometry_; - bool painting_view_; - bool painting_table_cell_; - bool cell_using_container_background_; + bool has_non_local_geometry_ = false; + bool painting_view_ = false; + bool painting_table_cell_ = false; + bool cell_using_container_background_ = false; + bool root_element_has_transform_ = false; }; } // namespace blink diff --git a/chromium/third_party/blink/renderer/core/paint/block_flow_paint_invalidator.cc b/chromium/third_party/blink/renderer/core/paint/block_flow_paint_invalidator.cc index 680513699b2..ca0d9bfebb3 100644 --- a/chromium/third_party/blink/renderer/core/paint/block_flow_paint_invalidator.cc +++ b/chromium/third_party/blink/renderer/core/paint/block_flow_paint_invalidator.cc @@ -6,6 +6,8 @@ #include "third_party/blink/renderer/core/layout/layout_block_flow.h" #include "third_party/blink/renderer/core/layout/layout_inline.h" +#include "third_party/blink/renderer/core/layout/ng/inline/ng_fragment_items.h" +#include "third_party/blink/renderer/core/layout/ng/inline/ng_inline_cursor.h" #include "third_party/blink/renderer/core/paint/box_paint_invalidator.h" #include "third_party/blink/renderer/core/paint/ng/ng_paint_fragment.h" #include "third_party/blink/renderer/core/paint/paint_invalidator.h" @@ -58,17 +60,31 @@ void BlockFlowPaintInvalidator::InvalidateDisplayItemClients( reason == PaintInvalidationReason::kSelection) return; - // It's the RootInlineBox that paints the ::first-line background. Note that - // since it may be expensive to figure out if the first line is affected by - // any ::first-line selectors at all, we just invalidate it unconditionally - // which is typically cheaper. - if (RootInlineBox* line = block_flow_.FirstRootBox()) { - if (line->IsFirstLineStyle()) { - object_paint_invalidator.InvalidateDisplayItemClient(*line, reason); + NGInlineCursor cursor(block_flow_); + if (cursor) { + // Line boxes record hit test data (see NGBoxFragmentPainter::PaintLineBox) + // and should be invalidated if they change. + bool invalidate_all_lines = block_flow_.HasEffectiveAllowedTouchAction(); + + for (cursor.MoveToFirstLine(); cursor; cursor.MoveToNextLine()) { + // The first line NGLineBoxFragment paints the ::first-line background. + // Because it may be expensive to figure out if the first line is affected + // by any ::first-line selectors at all, we just invalidate + // unconditionally which is typically cheaper. + if (invalidate_all_lines || cursor.Current().UsesFirstLineStyle()) { + DCHECK(cursor.Current().GetDisplayItemClient()); + object_paint_invalidator.InvalidateDisplayItemClient( + *cursor.Current().GetDisplayItemClient(), reason); + } + if (!invalidate_all_lines) + break; } - } else if (paint_fragment) { - NGPaintFragment* line = paint_fragment->FirstLineBox(); - if (line && line->PhysicalFragment().UsesFirstLineStyle()) { + } else if (RootInlineBox* line = block_flow_.FirstRootBox()) { + // It's the RootInlineBox that paints the ::first-line background. Note that + // since it may be expensive to figure out if the first line is affected by + // any ::first-line selectors at all, we just invalidate it unconditionally + // which is typically cheaper. + if (line->IsFirstLineStyle()) { object_paint_invalidator.InvalidateDisplayItemClient(*line, reason); } } 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 4364b0e0342..60a7b22aa39 100644 --- a/chromium/third_party/blink/renderer/core/paint/block_painter.cc +++ b/chromium/third_party/blink/renderer/core/paint/block_painter.cc @@ -146,7 +146,7 @@ void BlockPainter::PaintChild(const LayoutBox& child, // paints floats in regular tree order (the FloatingObjects list is only used // by legacy layout). if (paint_info.phase != PaintPhase::kFloat && - paint_info.phase != PaintPhase::kSelection && + paint_info.phase != PaintPhase::kSelectionDragImage && paint_info.phase != PaintPhase::kTextClip) return; @@ -182,7 +182,7 @@ void BlockPainter::PaintInlineBox(const InlineBox& inline_box, const PaintInfo& paint_info) { if (paint_info.phase != PaintPhase::kForeground && paint_info.phase != PaintPhase::kForcedColorsModeBackplate && - paint_info.phase != PaintPhase::kSelection) + paint_info.phase != PaintPhase::kSelectionDragImage) return; // Text clips are painted only for the direct inline children of the object @@ -287,7 +287,7 @@ void BlockPainter::PaintBlockFlowContents(const PaintInfo& paint_info, To<LayoutBlockFlow>(layout_block_).GetFloatingObjects(); const PaintPhase paint_phase = paint_info.phase; if (!floating_objects || !(paint_phase == PaintPhase::kFloat || - paint_phase == PaintPhase::kSelection || + paint_phase == PaintPhase::kSelectionDragImage || paint_phase == PaintPhase::kTextClip)) { return; } 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 00021a4679e..070d7578f99 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 @@ -12,7 +12,6 @@ #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; @@ -71,93 +70,107 @@ TEST_F(BlockPainterTouchActionTest, TouchActionRectsWithoutPaint) { </div> )HTML"); - // Initially there should be no hit test display items because there is no - // touch action. + // Initially there should be no hit test data because there is no touch + // action. const auto& scrolling_client = ViewScrollingBackgroundClient(); EXPECT_THAT( RootPaintController().GetDisplayItemList(), ElementsAre(IsSameId(&scrolling_client, kDocumentBackgroundType))); + PaintChunk::Id root_chunk_id(scrolling_client, kDocumentBackgroundType); + auto root_chunk_properties = + GetLayoutView().FirstFragment().ContentsProperties(); + EXPECT_THAT( + RootPaintController().PaintChunks(), + ElementsAre(IsPaintChunk(0, 1, root_chunk_id, root_chunk_properties))); - // Add a touch action to parent and ensure that hit test display items are - // created for both the parent and the visible child. + // Add a touch action to parent and ensure that hit test data are created + // for both the parent and the visible child. auto* parent_element = GetElementById("parent"); parent_element->setAttribute(html_names::kClassAttr, "touchActionNone"); UpdateAllLifecyclePhasesForTest(); - auto* parent = GetLayoutObjectByElementId("parent"); - auto* child_visible = GetLayoutObjectByElementId("childVisible"); - EXPECT_THAT(RootPaintController().GetDisplayItemList(), - ElementsAre(IsSameId(&scrolling_client, kDocumentBackgroundType), - IsSameId(parent, DisplayItem::kHitTest), - IsSameId(child_visible, DisplayItem::kHitTest))); + EXPECT_THAT( + RootPaintController().GetDisplayItemList(), + ElementsAre(IsSameId(&scrolling_client, kDocumentBackgroundType))); + HitTestData hit_test_data; + hit_test_data.touch_action_rects = {{IntRect(0, 0, 100, 100)}, + {IntRect(0, 0, 200, 25)}}; + EXPECT_THAT(RootPaintController().PaintChunks(), + ElementsAre(IsPaintChunk(0, 1, root_chunk_id, + root_chunk_properties, &hit_test_data))); - // Remove the touch action from parent and ensure no hit test display items - // are left. + // Remove the touch action from parent and ensure no hit test data are left. parent_element->removeAttribute(html_names::kClassAttr); UpdateAllLifecyclePhasesForTest(); EXPECT_THAT( RootPaintController().GetDisplayItemList(), ElementsAre(IsSameId(&scrolling_client, kDocumentBackgroundType))); + EXPECT_THAT( + RootPaintController().PaintChunks(), + ElementsAre(IsPaintChunk(0, 1, root_chunk_id, root_chunk_properties))); } TEST_F(BlockPainterTouchActionTest, TouchActionRectSubsequenceCaching) { SetBodyInnerHTML(R"HTML( <style> body { margin: 0; } + #stacking-context { + position: absolute; + z-index: 1; + } #touchaction { width: 100px; height: 100px; touch-action: none; } - #sibling { - width: 100px; - height: 100px; - background: blue; - } </style> - <div id='touchaction'></div> + <div id='stacking-context'> + <div id='touchaction'></div> + </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))); + EXPECT_THAT( + RootPaintController().GetDisplayItemList(), + ElementsAre(IsSameId(&scrolling_client, kDocumentBackgroundType))); - const auto& hit_test_client = *touchaction->EnclosingLayer(); + const auto& hit_test_client = + *ToLayoutBox(GetLayoutObjectByElementId("stacking-context"))->Layer(); 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); + PaintChunk::Id hit_test_chunk_id(hit_test_client, DisplayItem::kLayerChunk); 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)); + hit_test_data.touch_action_rects = {{IntRect(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))); + ElementsAre( + IsPaintChunk(0, 1, root_chunk_id, root_chunk_properties), + IsPaintChunk(1, 1, hit_test_chunk_id, hit_test_chunk_properties, + &hit_test_data, IntRect(0, 0, 100, 100)))); // Trigger a repaint with the whole HTML subsequence cached. GetLayoutView().Layer()->SetNeedsRepaint(); EXPECT_TRUE(PaintWithoutCommit()); - EXPECT_EQ(2, NumCachedNewItems()); + EXPECT_EQ(1, 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))); + ElementsAre( + IsPaintChunk(0, 1, root_chunk_id, root_chunk_properties), + IsPaintChunk(1, 1, hit_test_chunk_id, hit_test_chunk_properties, + &hit_test_data, IntRect(0, 0, 100, 100)))); } TEST_F(BlockPainterTouchActionTest, TouchActionRectPaintCaching) { @@ -180,45 +193,34 @@ TEST_F(BlockPainterTouchActionTest, TouchActionRectPaintCaching) { )HTML"); const auto& scrolling_client = ViewScrollingBackgroundClient(); - const auto* touchaction = GetLayoutObjectByElementId("touchaction"); auto* sibling_element = GetElementById("sibling"); 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)); + hit_test_data.touch_action_rects = {{IntRect(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))); + EXPECT_THAT(RootPaintController().PaintChunks(), + ElementsAre(IsPaintChunk(0, 2, root_chunk_id, + root_chunk_properties, &hit_test_data))); sibling_element->setAttribute(html_names::kStyleAttr, "background: green;"); - GetDocument().View()->UpdateAllLifecyclePhasesExceptPaint(); + GetDocument().View()->UpdateAllLifecyclePhasesExceptPaint( + DocumentUpdateReason::kTest); EXPECT_TRUE(PaintWithoutCommit()); // Only the background display item of the sibling should be invalidated. - EXPECT_EQ(2, NumCachedNewItems()); + EXPECT_EQ(1, NumCachedNewItems()); CommitAndFinishCycle(); - 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))); + EXPECT_THAT(RootPaintController().PaintChunks(), + ElementsAre(IsPaintChunk(0, 2, root_chunk_id, + root_chunk_properties, &hit_test_data))); } TEST_F(BlockPainterTouchActionTest, TouchActionRectScrollingContents) { @@ -251,25 +253,21 @@ TEST_F(BlockPainterTouchActionTest, TouchActionRectScrollingContents) { 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))); + ElementsAre(IsSameId(&scroller_client, kBackgroundType))); 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)); + hit_test_data.touch_action_rects = {{IntRect(0, 0, 100, 400)}, + {IntRect(0, 0, 10, 400)}}; EXPECT_THAT( scroller_paint_controller.PaintChunks(), ElementsAre(IsPaintChunk( - 0, 3, PaintChunk::Id(*scroller, kScrollingBackgroundChunkType), - scroller->FirstFragment().ContentsProperties(), hit_test_data))); + 0, 1, PaintChunk::Id(*scroller, kScrollingBackgroundChunkType), + scroller->FirstFragment().ContentsProperties(), &hit_test_data))); EXPECT_THAT(non_scroller_paint_controller.GetDisplayItemList(), ElementsAre(IsSameId(&root_client, kDocumentBackgroundType))); @@ -309,24 +307,18 @@ TEST_F(BlockPainterTouchActionTest, TouchActionRectPaintChunkChanges) { touchaction_element->setAttribute(html_names::kStyleAttr, "touch-action: none;"); UpdateAllLifecyclePhasesForTest(); - EXPECT_THAT(RootPaintController().GetDisplayItemList(), - ElementsAre(IsSameId(&scrolling_client, kDocumentBackgroundType), - IsSameId(touchaction, DisplayItem::kHitTest))); + EXPECT_THAT( + RootPaintController().GetDisplayItemList(), + ElementsAre(IsSameId(&scrolling_client, kDocumentBackgroundType))); 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)); + hit_test_data.touch_action_rects = {{IntRect(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))); + EXPECT_THAT(RootPaintController().PaintChunks(), + ElementsAre(IsPaintChunk(0, 1, root_chunk_id, + root_chunk_properties, &hit_test_data))); touchaction_element->removeAttribute(html_names::kStyleAttr); UpdateAllLifecyclePhasesForTest(); @@ -358,35 +350,46 @@ TEST_F(BlockPainterTouchActionTest, TouchHandlerRectsWithoutPaint) { </div> )HTML"); - // Initially there should be no hit test display items because there are no - // event handlers. + // Initially there should be no hit test data because there are no event + // handlers. 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. + // Add an event listener to parent and ensure that hit test data are created + // for both the parent and child. BlockPainterMockEventListener* callback = MakeGarbageCollected<BlockPainterMockEventListener>(); auto* parent_element = GetElementById("parent"); parent_element->addEventListener(event_type_names::kTouchstart, callback); UpdateAllLifecyclePhasesForTest(); - auto* parent = GetLayoutObjectByElementId("parent"); - auto* child = GetLayoutObjectByElementId("child"); - EXPECT_THAT(RootPaintController().GetDisplayItemList(), - ElementsAre(IsSameId(&scrolling_client, kDocumentBackgroundType), - IsSameId(parent, DisplayItem::kHitTest), - IsSameId(child, DisplayItem::kHitTest))); + EXPECT_THAT( + RootPaintController().GetDisplayItemList(), + ElementsAre(IsSameId(&scrolling_client, kDocumentBackgroundType))); + HitTestData hit_test_data; + hit_test_data.touch_action_rects = {{IntRect(0, 0, 100, 100)}, + {IntRect(0, 0, 200, 50)}}; + EXPECT_THAT( + RootPaintController().PaintChunks(), + ElementsAre(IsPaintChunk( + 0, 1, PaintChunk::Id(scrolling_client, kDocumentBackgroundType), + GetLayoutView().FirstFragment().ContentsProperties(), &hit_test_data, + IntRect(0, 0, 800, 600)))); - // Remove the event handler from parent and ensure no hit test display items - // are left. + // Remove the event handler from parent and ensure no hit test data are left. parent_element->RemoveAllEventListeners(); UpdateAllLifecyclePhasesForTest(); EXPECT_THAT( RootPaintController().GetDisplayItemList(), ElementsAre(IsSameId(&scrolling_client, kDocumentBackgroundType))); + EXPECT_THAT( + RootPaintController().PaintChunks(), + ElementsAre(IsPaintChunk( + 0, 1, PaintChunk::Id(scrolling_client, kDocumentBackgroundType), + GetLayoutView().FirstFragment().ContentsProperties(), nullptr, + IntRect(0, 0, 800, 600)))); } TEST_F(BlockPainterTouchActionTest, TouchActionRectsAcrossPaintChanges) { @@ -403,21 +406,32 @@ TEST_F(BlockPainterTouchActionTest, TouchActionRectsAcrossPaintChanges) { )HTML"); const auto& scrolling_client = ViewScrollingBackgroundClient(); - auto* parent = GetLayoutObjectByElementId("parent"); - auto* child = GetLayoutObjectByElementId("child"); - EXPECT_THAT(RootPaintController().GetDisplayItemList(), - ElementsAre(IsSameId(&scrolling_client, kDocumentBackgroundType), - IsSameId(parent, DisplayItem::kHitTest), - IsSameId(child, DisplayItem::kHitTest))); + EXPECT_THAT( + RootPaintController().GetDisplayItemList(), + ElementsAre(IsSameId(&scrolling_client, kDocumentBackgroundType))); + HitTestData hit_test_data; + hit_test_data.touch_action_rects = {{IntRect(0, 0, 100, 100)}, + {IntRect(0, 0, 200, 50)}}; + EXPECT_THAT( + RootPaintController().PaintChunks(), + ElementsAre(IsPaintChunk( + 0, 1, PaintChunk::Id(scrolling_client, kDocumentBackgroundType), + GetLayoutView().FirstFragment().ContentsProperties(), &hit_test_data, + IntRect(0, 0, 800, 600)))); - auto* child_element = GetElementById("parent"); + auto* child_element = GetElementById("child"); child_element->setAttribute("style", "background: blue;"); UpdateAllLifecyclePhasesForTest(); - EXPECT_THAT(RootPaintController().GetDisplayItemList(), - ElementsAre(IsSameId(&scrolling_client, kDocumentBackgroundType), - IsSameId(parent, kBackgroundType), - IsSameId(parent, DisplayItem::kHitTest), - IsSameId(child, DisplayItem::kHitTest))); + EXPECT_THAT( + RootPaintController().GetDisplayItemList(), + ElementsAre(IsSameId(&scrolling_client, kDocumentBackgroundType), + IsSameId(child_element->GetLayoutObject(), kBackgroundType))); + EXPECT_THAT( + RootPaintController().PaintChunks(), + ElementsAre(IsPaintChunk( + 0, 2, PaintChunk::Id(scrolling_client, kDocumentBackgroundType), + GetLayoutView().FirstFragment().ContentsProperties(), &hit_test_data, + IntRect(0, 0, 800, 600)))); } TEST_F(BlockPainterTouchActionTest, ScrolledHitTestChunkProperties) { @@ -445,24 +459,21 @@ TEST_F(BlockPainterTouchActionTest, ScrolledHitTestChunkProperties) { const auto& scrolling_client = ViewScrollingBackgroundClient(); const auto* scroller = To<LayoutBlock>(GetLayoutObjectByElementId("scroller")); - const auto* child = GetLayoutObjectByElementId("child"); - EXPECT_THAT(RootPaintController().GetDisplayItemList(), - ElementsAre(IsSameId(&scrolling_client, kDocumentBackgroundType), - IsSameId(scroller, DisplayItem::kHitTest), - IsSameId(scroller, DisplayItem::kScrollHitTest), - IsSameId(child, DisplayItem::kHitTest))); + EXPECT_THAT( + RootPaintController().GetDisplayItemList(), + ElementsAre(IsSameId(&scrolling_client, kDocumentBackgroundType))); HitTestData scroller_touch_action_hit_test_data; - scroller_touch_action_hit_test_data.touch_action_rects.emplace_back( - LayoutRect(0, 0, 100, 100)); + scroller_touch_action_hit_test_data.touch_action_rects = { + {IntRect(0, 0, 100, 100)}}; const auto& scrolling_contents_properties = scroller->FirstFragment().ContentsProperties(); HitTestData scroll_hit_test_data; - scroll_hit_test_data.SetScrollHitTest( - &scrolling_contents_properties.Transform(), IntRect(0, 0, 100, 100)); + scroll_hit_test_data.scroll_translation = + &scrolling_contents_properties.Transform(); + scroll_hit_test_data.scroll_hit_test_rect = IntRect(0, 0, 100, 100); HitTestData scrolled_hit_test_data; - scrolled_hit_test_data.touch_action_rects.emplace_back( - LayoutRect(0, 0, 200, 50)); + scrolled_hit_test_data.touch_action_rects = {{IntRect(0, 0, 200, 50)}}; const auto& paint_chunks = RootPaintController().PaintChunks(); EXPECT_THAT( @@ -470,29 +481,28 @@ TEST_F(BlockPainterTouchActionTest, ScrolledHitTestChunkProperties) { ElementsAre( IsPaintChunk( 0, 1, PaintChunk::Id(scrolling_client, kDocumentBackgroundType), - GetLayoutView().FirstFragment().ContentsProperties()), - IsPaintChunk(1, 2, - PaintChunk::Id(*scroller->Layer(), - kNonScrollingBackgroundChunkType), - scroller->FirstFragment().LocalBorderBoxProperties(), - scroller_touch_action_hit_test_data), - IsPaintChunk(2, 3, + GetLayoutView().FirstFragment().ContentsProperties(), nullptr, + IntRect(0, 0, 800, 600)), + IsPaintChunk( + 1, 1, + PaintChunk::Id(*scroller->Layer(), DisplayItem::kLayerChunk), + scroller->FirstFragment().LocalBorderBoxProperties(), + &scroller_touch_action_hit_test_data, IntRect(0, 0, 100, 100)), + IsPaintChunk(1, 1, PaintChunk::Id(*scroller, DisplayItem::kScrollHitTest), scroller->FirstFragment().LocalBorderBoxProperties(), - scroll_hit_test_data), + &scroll_hit_test_data, IntRect(0, 0, 100, 100)), IsPaintChunk( - 3, 4, - PaintChunk::Id(*scroller, kScrollingContentsBackgroundChunkType), + 1, 1, + PaintChunk::Id(*scroller, kClippedContentsBackgroundChunkType), scroller->FirstFragment().ContentsProperties(), - scrolled_hit_test_data))); + &scrolled_hit_test_data, IntRect(0, 0, 200, 50)))); const auto& scroller_paint_chunk = paint_chunks[1]; - EXPECT_EQ(IntRect(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 = paint_chunks[3]; - EXPECT_EQ(IntRect(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_border_painter.cc b/chromium/third_party/blink/renderer/core/paint/box_border_painter.cc index eace1c00a23..49772004f87 100644 --- a/chromium/third_party/blink/renderer/core/paint/box_border_painter.cc +++ b/chromium/third_party/blink/renderer/core/paint/box_border_painter.cc @@ -289,20 +289,12 @@ LayoutRectOutsets DoubleStripeInsets(const BorderEdge edges[], stripe)); } -float ClampOrRound(float border_width) { - // Make sure non-zero borders never disappear - if (border_width > 0.0f && border_width <= 1.0f) - return 1.0f; - - return roundf(border_width); -} - void DrawSolidBorderRect(GraphicsContext& context, const FloatRect& border_rect, float border_width, const Color& color) { FloatRect stroke_rect(border_rect); - border_width = ClampOrRound(border_width); + border_width = roundf(border_width); stroke_rect.Inflate(-border_width / 2); bool was_antialias = context.ShouldAntialias(); @@ -854,7 +846,7 @@ void BoxBorderPainter::PaintSide(GraphicsContext& context, if (use_path) path = &border_info.rounded_border_path; else - side_rect.SetHeight(ClampOrRound(edge.Width())); + side_rect.SetHeight(roundf(edge.Width())); PaintOneBorderSide(context, side_rect, BoxSide::kTop, BoxSide::kLeft, BoxSide::kRight, path, border_info.anti_alias, color, @@ -869,7 +861,7 @@ void BoxBorderPainter::PaintSide(GraphicsContext& context, if (use_path) path = &border_info.rounded_border_path; else - side_rect.ShiftYEdgeTo(side_rect.MaxY() - ClampOrRound(edge.Width())); + side_rect.ShiftYEdgeTo(side_rect.MaxY() - roundf(edge.Width())); PaintOneBorderSide(context, side_rect, BoxSide::kBottom, BoxSide::kLeft, BoxSide::kRight, path, border_info.anti_alias, color, @@ -884,7 +876,7 @@ void BoxBorderPainter::PaintSide(GraphicsContext& context, if (use_path) path = &border_info.rounded_border_path; else - side_rect.SetWidth(ClampOrRound(edge.Width())); + side_rect.SetWidth(roundf(edge.Width())); PaintOneBorderSide(context, side_rect, BoxSide::kLeft, BoxSide::kTop, BoxSide::kBottom, path, border_info.anti_alias, color, @@ -899,7 +891,7 @@ void BoxBorderPainter::PaintSide(GraphicsContext& context, if (use_path) path = &border_info.rounded_border_path; else - side_rect.ShiftXEdgeTo(side_rect.MaxX() - ClampOrRound(edge.Width())); + side_rect.ShiftXEdgeTo(side_rect.MaxX() - roundf(edge.Width())); PaintOneBorderSide(context, side_rect, BoxSide::kRight, BoxSide::kTop, BoxSide::kBottom, path, border_info.anti_alias, color, @@ -1017,9 +1009,8 @@ void BoxBorderPainter::PaintOneBorderSide( ObjectPainter::DrawLineForBoxSide( graphics_context, side_rect.X(), side_rect.Y(), side_rect.MaxX(), side_rect.MaxY(), side, color, edge_to_render.BorderStyle(), - miter1 != kNoMiter ? ClampOrRound(adjacent_edge1.Width()) : 0, - miter2 != kNoMiter ? ClampOrRound(adjacent_edge2.Width()) : 0, - antialias); + miter1 != kNoMiter ? roundf(adjacent_edge1.Width()) : 0, + miter2 != kNoMiter ? roundf(adjacent_edge2.Width()) : 0, antialias); } } 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 20136ea1566..d9462947cb3 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 @@ -124,13 +124,24 @@ LayoutRectOutsets BoxModelObjectPainter::ComputePadding() const { BoxPainterBase::FillLayerInfo BoxModelObjectPainter::GetFillLayerInfo( const Color& color, const FillLayer& bg_layer, - BackgroundBleedAvoidance bleed_avoidance) const { + BackgroundBleedAvoidance bleed_avoidance, + bool is_painting_scrolling_background) const { return BoxPainterBase::FillLayerInfo( box_model_.GetDocument(), box_model_.StyleRef(), box_model_.HasOverflowClip(), color, bg_layer, bleed_avoidance, + LayoutObject::ShouldRespectImageOrientation(&box_model_), (flow_box_ ? flow_box_->IncludeLogicalLeftEdge() : true), (flow_box_ ? flow_box_->IncludeLogicalRightEdge() : true), - box_model_.IsInline()); + box_model_.IsLayoutInline(), is_painting_scrolling_background); +} + +bool BoxModelObjectPainter::IsPaintingScrollingBackground( + const PaintInfo& paint_info) const { + if (!box_model_.IsBox()) + return false; + + const auto& this_box = ToLayoutBox(box_model_); + return BoxDecorationData::IsPaintingScrollingBackground(paint_info, this_box); } } // namespace blink 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 eed39f2c998..03f15100911 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 @@ -33,7 +33,9 @@ class BoxModelObjectPainter : public BoxPainterBase { BoxPainterBase::FillLayerInfo GetFillLayerInfo( const Color&, const FillLayer&, - BackgroundBleedAvoidance) const override; + BackgroundBleedAvoidance, + bool is_painting_scrolling_background) const override; + bool IsPaintingScrollingBackground(const PaintInfo&) const override; void PaintTextClipMask(GraphicsContext&, const IntRect& mask_rect, 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 985e5a237f4..53a09c5d9e4 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 @@ -17,7 +17,7 @@ namespace blink { bool BoxPaintInvalidator::HasEffectiveBackground() { // The view can paint background not from the style. - if (box_.IsLayoutView()) + if (IsA<LayoutView>(box_)) return true; return box_.StyleRef().HasBackground() && !box_.BackgroundTransfersToView(); } @@ -184,27 +184,14 @@ bool BoxPaintInvalidator::BackgroundGeometryDependsOnLayoutOverflowRect() { bool BoxPaintInvalidator::BackgroundPaintsOntoScrollingContentsLayer() { if (!HasEffectiveBackground()) return false; - if (RuntimeEnabledFeatures::CompositeAfterPaintEnabled()) { - return box_.GetBackgroundPaintLocation() & - kBackgroundPaintInScrollingContents; - } - if (!box_.HasLayer()) - return false; - if (auto* mapping = box_.Layer()->GetCompositedLayerMapping()) - return mapping->BackgroundPaintsOntoScrollingContentsLayer(); - return false; + return box_.GetBackgroundPaintLocation() & + kBackgroundPaintInScrollingContents; } bool BoxPaintInvalidator::BackgroundPaintsOntoMainGraphicsLayer() { if (!HasEffectiveBackground()) return false; - if (RuntimeEnabledFeatures::CompositeAfterPaintEnabled()) - return box_.GetBackgroundPaintLocation() & kBackgroundPaintInGraphicsLayer; - if (!box_.HasLayer()) - return true; - if (auto* mapping = box_.Layer()->GetCompositedLayerMapping()) - return mapping->BackgroundPaintsOntoGraphicsLayer(); - return true; + return box_.GetBackgroundPaintLocation() & kBackgroundPaintInGraphicsLayer; } bool BoxPaintInvalidator::ShouldFullyInvalidateBackgroundOnLayoutOverflowChange( @@ -227,9 +214,9 @@ bool BoxPaintInvalidator::ShouldFullyInvalidateBackgroundOnLayoutOverflowChange( BoxPaintInvalidator::BackgroundInvalidationType BoxPaintInvalidator::ComputeViewBackgroundInvalidation() { - DCHECK(box_.IsLayoutView()); + DCHECK(IsA<LayoutView>(box_)); - const auto& layout_view = ToLayoutView(box_); + const auto& layout_view = To<LayoutView>(box_); auto new_background_rect = layout_view.BackgroundRect(); auto old_background_rect = layout_view.PreviousBackgroundRect(); layout_view.SetPreviousBackgroundRect(new_background_rect); @@ -250,18 +237,31 @@ BoxPaintInvalidator::ComputeViewBackgroundInvalidation() { 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. - // See: https://drafts.csswg.org/css-backgrounds-3/#root-background. - if (BackgroundGeometryDependsOnLayoutOverflowRect()) { - Element* document_element = box_.GetDocument().documentElement(); + if (Element* document_element = box_.GetDocument().documentElement()) { if (document_element) { - const auto* document_background = document_element->GetLayoutObject(); - if (document_background && document_background->IsBox()) { - const auto* document_background_box = ToLayoutBox(document_background); - if (ShouldFullyInvalidateBackgroundOnLayoutOverflowChange( - document_background_box->PreviousPhysicalLayoutOverflowRect(), - document_background_box->PhysicalLayoutOverflowRect())) { + if (const auto* document_element_object = + document_element->GetLayoutObject()) { + // LayoutView's non-fixed-attachment background is positioned in the + // document element and needs to invalidate if the size changes. + // See: https://drafts.csswg.org/css-backgrounds-3/#root-background. + if (BackgroundGeometryDependsOnLayoutOverflowRect()) { + if (document_element_object->IsBox()) { + const auto* document_background_box = + ToLayoutBox(document_element_object); + if (ShouldFullyInvalidateBackgroundOnLayoutOverflowChange( + document_background_box + ->PreviousPhysicalLayoutOverflowRect(), + document_background_box->PhysicalLayoutOverflowRect())) { + return BackgroundInvalidationType::kFull; + } + } + } + + // The document background paints with a transform but nevertheless + // extended onto an infinite canvas. In cases where it has a transform + // we cna't apply incremental invalidation, because the visual rect is + // no longer axis-aligned to the LayoutView. + if (document_element_object->StyleRef().HasTransform()) { return BackgroundInvalidationType::kFull; } } @@ -275,16 +275,6 @@ BoxPaintInvalidator::ComputeViewBackgroundInvalidation() { BoxPaintInvalidator::BackgroundInvalidationType BoxPaintInvalidator::ComputeBackgroundInvalidation( bool& should_invalidate_all_layers) { - // Need to fully invalidate the background on all layers if background paint - // location changed. - auto new_background_location = box_.GetBackgroundPaintLocation(); - if (new_background_location != box_.PreviousBackgroundPaintLocation()) { - should_invalidate_all_layers = true; - box_.GetMutableForPainting().SetPreviousBackgroundPaintLocation( - new_background_location); - return BackgroundInvalidationType::kFull; - } - // 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()) { @@ -332,7 +322,7 @@ void BoxPaintInvalidator::InvalidateBackground() { bool should_invalidate_all_layers = false; auto background_invalidation_type = ComputeBackgroundInvalidation(should_invalidate_all_layers); - if (box_.IsLayoutView()) { + if (IsA<LayoutView>(box_)) { background_invalidation_type = std::max( background_invalidation_type, ComputeViewBackgroundInvalidation()); } @@ -394,9 +384,6 @@ bool BoxPaintInvalidator:: if (context_.fragment_data->VisualRect().IsEmpty()) return false; - if (box_.PaintedOutputOfObjectHasNoEffectRegardlessOfSize()) - return false; - const ComputedStyle& style = box_.StyleRef(); // Background and mask layers can depend on other boxes than border box. See 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 714a3777769..c6456900950 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 @@ -8,6 +8,7 @@ #include "third_party/blink/renderer/core/html/html_frame_owner_element.h" #include "third_party/blink/renderer/core/html_names.h" #include "third_party/blink/renderer/core/layout/layout_view.h" +#include "third_party/blink/renderer/core/paint/paint_and_raster_invalidation_test.h" #include "third_party/blink/renderer/core/paint/paint_controller_paint_test.h" #include "third_party/blink/renderer/core/paint/paint_invalidator.h" #include "third_party/blink/renderer/core/paint/paint_layer.h" @@ -18,11 +19,11 @@ namespace blink { -class BoxPaintInvalidatorTest : public PaintControllerPaintTest { +using ::testing::UnorderedElementsAre; + +class BoxPaintInvalidatorTest : public PaintAndRasterInvalidationTest { public: - BoxPaintInvalidatorTest() - : PaintControllerPaintTest( - MakeGarbageCollected<SingleChildLocalFrameClient>()) {} + BoxPaintInvalidatorTest() = default; protected: PaintInvalidationReason ComputePaintInvalidationReason( @@ -60,6 +61,7 @@ class BoxPaintInvalidatorTest : public PaintControllerPaintTest { auto& box = *ToLayoutBox(target.GetLayoutObject()); auto visual_rect = box.FirstFragment().VisualRect(); auto paint_offset = box.FirstFragment().PaintOffset(); + box.SetShouldCheckForPaintInvalidation(); // No geometry change. EXPECT_EQ(PaintInvalidationReason::kNone, @@ -68,7 +70,13 @@ class BoxPaintInvalidatorTest : public PaintControllerPaintTest { target.setAttribute( html_names::kStyleAttr, target.getAttribute(html_names::kStyleAttr) + "; width: 200px"); - GetDocument().View()->UpdateLifecycleToCompositingInputsClean(); + if (RuntimeEnabledFeatures::CompositeAfterPaintEnabled()) { + GetDocument().View()->UpdateLifecycleToLayoutClean( + DocumentUpdateReason::kTest); + } else { + GetDocument().View()->UpdateLifecycleToCompositingInputsClean( + DocumentUpdateReason::kTest); + } // Simulate that PaintInvalidator updates visual rect. box.GetMutableForPainting().SetVisualRect( IntRect(visual_rect.Location(), RoundedIntSize(box.Size()))); @@ -109,7 +117,10 @@ class BoxPaintInvalidatorTest : public PaintControllerPaintTest { INSTANTIATE_PAINT_TEST_SUITE_P(BoxPaintInvalidatorTest); -TEST_P(BoxPaintInvalidatorTest, ComputePaintInvalidationReasonPaintingNothing) { +// Paint invalidation for empty content is needed for updating composited layer +// bounds for correct composited hit testing. It won't cause raster invalidation +// (tested in paint_and_raster_invalidation_test.cc). +TEST_P(BoxPaintInvalidatorTest, ComputePaintInvalidationReasonEmptyContent) { SetUpHTML(); auto& target = *GetDocument().getElementById("target"); auto& box = *ToLayoutBox(target.GetLayoutObject()); @@ -117,7 +128,7 @@ TEST_P(BoxPaintInvalidatorTest, ComputePaintInvalidationReasonPaintingNothing) { target.setAttribute(html_names::kClassAttr, ""); UpdateAllLifecyclePhasesForTest(); - EXPECT_TRUE(box.PaintedOutputOfObjectHasNoEffectRegardlessOfSize()); + box.SetShouldCheckForPaintInvalidation(); auto visual_rect = box.FirstFragment().VisualRect(); // No geometry change. @@ -126,19 +137,22 @@ TEST_P(BoxPaintInvalidatorTest, ComputePaintInvalidationReasonPaintingNothing) { ComputePaintInvalidationReason(box, visual_rect, visual_rect.Location())); // Paint offset change. - EXPECT_EQ(PaintInvalidationReason::kNone, - ComputePaintInvalidationReason( - box, visual_rect, visual_rect.Location() + IntSize(10, 20))); + auto old_visual_rect = visual_rect; + old_visual_rect.Move(IntSize(10, 20)); + EXPECT_EQ(PaintInvalidationReason::kGeometry, + ComputePaintInvalidationReason(box, old_visual_rect, + old_visual_rect.Location())); // Visual rect size change. - auto old_visual_rect = visual_rect; + old_visual_rect = visual_rect; target.setAttribute(html_names::kStyleAttr, "width: 200px"); - GetDocument().View()->UpdateLifecycleToLayoutClean(); + GetDocument().View()->UpdateLifecycleToLayoutClean( + DocumentUpdateReason::kTest); // Simulate that PaintInvalidator updates visual rect. box.GetMutableForPainting().SetVisualRect( IntRect(visual_rect.Location(), RoundedIntSize(box.Size()))); - EXPECT_EQ(PaintInvalidationReason::kNone, + EXPECT_EQ(PaintInvalidationReason::kIncremental, ComputePaintInvalidationReason(box, old_visual_rect, old_visual_rect.Location())); } @@ -164,7 +178,8 @@ TEST_P(BoxPaintInvalidatorTest, ComputePaintInvalidationReasonBasic) { // Visual rect size change. auto old_visual_rect = visual_rect; target.setAttribute(html_names::kStyleAttr, "background: blue; width: 200px"); - GetDocument().View()->UpdateLifecycleToLayoutClean(); + GetDocument().View()->UpdateLifecycleToLayoutClean( + DocumentUpdateReason::kTest); // Simulate that PaintInvalidator updates visual rect. box.GetMutableForPainting().SetVisualRect( IntRect(visual_rect.Location(), RoundedIntSize(box.Size()))); @@ -193,6 +208,27 @@ TEST_P(BoxPaintInvalidatorTest, ComputePaintInvalidationReasonBasic) { box, visual_rect, visual_rect.Location() + IntSize(10, 20))); } +TEST_P(BoxPaintInvalidatorTest, + InvalidateLineBoxHitTestOnCompositingStyleChange) { + ScopedPaintUnderInvalidationCheckingForTest under_invalidation_checking(true); + SetBodyInnerHTML(R"HTML( + <style> + #target { + width: 100px; + height: 100px; + touch-action: none; + } + </style> + <div id="target" style="will-change: transform;">a<br>b</div> + )HTML"); + + UpdateAllLifecyclePhasesForTest(); + auto& target = *GetDocument().getElementById("target"); + target.setAttribute(html_names::kStyleAttr, ""); + UpdateAllLifecyclePhasesForTest(); + // This test passes if no underinvalidation occurs. +} + TEST_P(BoxPaintInvalidatorTest, ComputePaintInvalidationReasonOtherCases) { SetUpHTML(); auto& target = *GetDocument().getElementById("target"); @@ -211,9 +247,6 @@ TEST_P(BoxPaintInvalidatorTest, ComputePaintInvalidationReasonOtherCases) { target.setAttribute(html_names::kStyleAttr, "filter: blur(5px)"); ExpectFullPaintInvalidationOnGeometryChange("With filter"); - target.setAttribute(html_names::kStyleAttr, "outline: 2px solid blue"); - ExpectFullPaintInvalidationOnGeometryChange("With outline"); - target.setAttribute(html_names::kStyleAttr, "box-shadow: inset 3px 2px"); ExpectFullPaintInvalidationOnGeometryChange("With box-shadow"); @@ -222,4 +255,50 @@ TEST_P(BoxPaintInvalidatorTest, ComputePaintInvalidationReasonOtherCases) { ExpectFullPaintInvalidationOnGeometryChange("With clip-path"); } +TEST_P(BoxPaintInvalidatorTest, ComputePaintInvalidationReasonOutline) { + SetUpHTML(); + auto& target = *GetDocument().getElementById("target"); + auto* object = target.GetLayoutObject(); + + GetDocument().View()->SetTracksRasterInvalidations(true); + target.setAttribute(html_names::kStyleAttr, "outline: 2px solid blue;"); + UpdateAllLifecyclePhasesForTest(); + EXPECT_THAT(GetRasterInvalidationTracking()->Invalidations(), + UnorderedElementsAre(RasterInvalidationInfo{ + object, object->DebugName(), IntRect(0, 0, 72, 142), + PaintInvalidationReason::kStyle})); + GetDocument().View()->SetTracksRasterInvalidations(false); + + GetDocument().View()->SetTracksRasterInvalidations(true); + target.setAttribute(html_names::kStyleAttr, + "outline: 2px solid blue; width: 100px;"); + UpdateAllLifecyclePhasesForTest(); + EXPECT_THAT(GetRasterInvalidationTracking()->Invalidations(), + UnorderedElementsAre(RasterInvalidationInfo{ + object, object->DebugName(), IntRect(0, 0, 122, 142), + PaintInvalidationReason::kGeometry})); + GetDocument().View()->SetTracksRasterInvalidations(false); +} + +TEST_P(BoxPaintInvalidatorTest, InvalidateHitTestOnCompositingStyleChange) { + ScopedPaintUnderInvalidationCheckingForTest under_invalidation_checking(true); + SetBodyInnerHTML(R"HTML( + <style> + #target { + width: 400px; + height: 300px; + overflow: hidden; + touch-action: none; + } + </style> + <div id="target" style="will-change: transform;"></div> + )HTML"); + + UpdateAllLifecyclePhasesForTest(); + auto& target = *GetDocument().getElementById("target"); + target.setAttribute(html_names::kStyleAttr, ""); + UpdateAllLifecyclePhasesForTest(); + // This test passes if no underinvalidation occurs. +} + } // namespace blink 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 56c647e4f24..be14cf89e05 100644 --- a/chromium/third_party/blink/renderer/core/paint/box_painter.cc +++ b/chromium/third_party/blink/renderer/core/paint/box_painter.cc @@ -29,9 +29,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/graphics/paint/scoped_paint_chunk_properties.h" -#include "third_party/blink/renderer/platform/graphics/paint/scroll_hit_test_display_item.h" namespace blink { @@ -48,9 +46,8 @@ void BoxPainter::PaintChildren(const PaintInfo& paint_info) { PaintInfo child_info(paint_info); for (LayoutObject* child = layout_box_.SlowFirstChild(); child; child = child->NextSibling()) { - if (child->IsSVGForeignObject()) { - SVGForeignObjectPainter(ToLayoutSVGForeignObject(*child)) - .PaintLayer(paint_info); + if (auto* foreign_object = DynamicTo<LayoutSVGForeignObject>(*child)) { + SVGForeignObjectPainter(*foreign_object).PaintLayer(paint_info); } else { child->Paint(child_info); } @@ -121,20 +118,6 @@ void BoxPainter::PaintBoxDecorationBackground( RecordScrollHitTestData(paint_info, *background_client); } -bool BoxPainter::BackgroundIsKnownToBeOpaque(const PaintInfo& paint_info) { - // If the box has multiple fragments, its VisualRect is the bounding box of - // all fragments' visual rects, which is likely to cover areas that are not - // covered by painted background. - if (layout_box_.FirstFragment().NextFragment()) - return false; - - PhysicalRect bounds = - BoxDecorationData::IsPaintingScrollingBackground(paint_info, layout_box_) - ? layout_box_.PhysicalLayoutOverflowRect() - : layout_box_.PhysicalSelfVisualOverflowRect(); - return layout_box_.BackgroundIsKnownToBeOpaqueInRect(bounds); -} - void BoxPainter::PaintBoxDecorationBackgroundWithRect( const PaintInfo& paint_info, const PhysicalRect& paint_rect, @@ -142,17 +125,9 @@ void BoxPainter::PaintBoxDecorationBackgroundWithRect( const ComputedStyle& style = layout_box_.StyleRef(); base::Optional<DisplayItemCacheSkipper> cache_skipper; - // Disable cache in under-invalidation checking mode for MediaSliderPart - // because we always paint using the latest data (buffered ranges, current - // time and duration) which may be different from the cached data, and for - // delayed-invalidation object because it may change before it's actually - // invalidated. Note that we still report harmless under-invalidation of - // non-delayed-invalidation animated background, which should be ignored. if (RuntimeEnabledFeatures::PaintUnderInvalidationCheckingEnabled() && - (style.EffectiveAppearance() == kMediaSliderPart || - layout_box_.ShouldDelayFullPaintInvalidation())) { + BoxPainterBase::ShouldSkipPaintUnderInvalidationChecking(layout_box_)) cache_skipper.emplace(paint_info.context); - } BoxDecorationData box_decoration_data(paint_info, layout_box_); if (!box_decoration_data.ShouldPaint()) @@ -167,11 +142,6 @@ void BoxPainter::PaintBoxDecorationBackgroundWithRect( DisplayItem::kBoxDecorationBackground); GraphicsContextStateSaver state_saver(paint_info.context, false); - if (RuntimeEnabledFeatures::CompositeAfterPaintEnabled() && - paint_rect.EdgesOnPixelBoundaries() && - BackgroundIsKnownToBeOpaque(paint_info)) - recorder.SetKnownToBeOpaque(); - bool needs_end_layer = false; // FIXME: Should eventually give the theme control over whether the box // shadow should paint, since controls could have custom shadows of their @@ -293,8 +263,8 @@ void BoxPainter::PaintMaskImages(const PaintInfo& paint_info, void BoxPainter::RecordHitTestData(const PaintInfo& paint_info, const PhysicalRect& 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. + // Hit test data 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; @@ -302,20 +272,19 @@ void BoxPainter::RecordHitTestData(const PaintInfo& paint_info, if (layout_box_.StyleRef().Visibility() != EVisibility::kVisible) return; - auto touch_action = layout_box_.EffectiveAllowedTouchAction(); - if (touch_action == TouchAction::kTouchActionAuto) + if (!paint_info.FragmentToPaint(layout_box_)) return; - HitTestDisplayItem::Record( - paint_info.context, background_client, - HitTestRect(paint_rect.ToLayoutRect(), touch_action)); + paint_info.context.GetPaintController().RecordHitTestData( + background_client, PixelSnappedIntRect(paint_rect), + layout_box_.EffectiveAllowedTouchAction()); } void BoxPainter::RecordScrollHitTestData( const PaintInfo& paint_info, 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. + // Scroll hit test data are only needed for compositing. This flag is used for + // printing and drag images which do not need hit testing. if (paint_info.GetGlobalPaintFlags() & kGlobalPaintFlattenCompositingLayers) return; @@ -334,25 +303,24 @@ void BoxPainter::RecordScrollHitTestData( if (layout_box_.GetScrollableArea()->ScrollsOverflow()) { const auto* properties = fragment->PaintProperties(); - // If there is an associated scroll node, emit a scroll hit test display - // item. + // If there is an associated scroll node, emit scroll hit test data. if (properties && properties->Scroll()) { DCHECK(properties->ScrollTranslation()); - // The local border box properties are used instead of the contents - // properties so that the scroll hit test is not clipped or scrolled. - ScopedPaintChunkProperties scroll_hit_test_properties( - paint_info.context.GetPaintController(), - fragment->LocalBorderBoxProperties(), background_client, - DisplayItem::kScrollHitTest); - ScrollHitTestDisplayItem::Record( - paint_info.context, background_client, DisplayItem::kScrollHitTest, + // We record scroll hit test data in the local border box properties + // instead of the contents properties so that the scroll hit test is not + // clipped or scrolled. + auto& paint_controller = paint_info.context.GetPaintController(); + DCHECK_EQ(fragment->LocalBorderBoxProperties(), + paint_controller.CurrentPaintChunkProperties()); + paint_controller.RecordScrollHitTestData( + background_client, DisplayItem::kScrollHitTest, properties->ScrollTranslation(), fragment->VisualRect()); } } ScrollableAreaPainter(*layout_box_.GetScrollableArea()) - .RecordResizerScrollHitTestData( - paint_info.context, fragment->PaintOffset(), background_client); + .RecordResizerScrollHitTestData(paint_info.context, + fragment->PaintOffset()); } } // 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 0b79434cf20..18322986bea 100644 --- a/chromium/third_party/blink/renderer/core/paint/box_painter.h +++ b/chromium/third_party/blink/renderer/core/paint/box_painter.h @@ -38,21 +38,19 @@ class BoxPainter { const PhysicalRect&, 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. + // Expands the bounds of the current paint chunk for hit test, and records + // special touch action if any. This should be called in the background paint + // phase even if there is no other painted content. void RecordHitTestData(const PaintInfo&, const PhysicalRect& paint_rect, const DisplayItemClient& background_client); - // Paint a scroll hit test display item and record scroll hit test data. This - // should be called in the background paint phase even if there is no other - // painted content. + // This should be called in the background paint phase even if there is no + // other painted content. void RecordScrollHitTestData(const PaintInfo&, const DisplayItemClient& background_client); private: - bool BackgroundIsKnownToBeOpaque(const PaintInfo&); void PaintBackground(const PaintInfo&, const PhysicalRect&, const Color& background_color, 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 597dbee509e..660f62adc5b 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 @@ -9,6 +9,7 @@ #include "third_party/blink/renderer/core/dom/node_computed_style.h" #include "third_party/blink/renderer/core/frame/settings.h" #include "third_party/blink/renderer/core/inspector/inspector_trace_events.h" +#include "third_party/blink/renderer/core/layout/layout_progress.h" #include "third_party/blink/renderer/core/paint/background_image_geometry.h" #include "third_party/blink/renderer/core/paint/box_border_painter.h" #include "third_party/blink/renderer/core/paint/image_element_timing.h" @@ -22,6 +23,7 @@ #include "third_party/blink/renderer/core/style/shadow_list.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/bitmap_image.h" #include "third_party/blink/renderer/platform/graphics/graphics_context_state_saver.h" #include "third_party/blink/renderer/platform/graphics/paint/paint_controller.h" #include "third_party/blink/renderer/platform/graphics/scoped_interpolation_quality.h" @@ -274,11 +276,14 @@ BoxPainterBase::FillLayerInfo::FillLayerInfo( Color bg_color, const FillLayer& layer, BackgroundBleedAvoidance bleed_avoidance, + RespectImageOrientationEnum respect_image_orientation, bool include_left, bool include_right, - bool is_inline) + bool is_inline, + bool is_painting_scrolling_background) : image(layer.GetImage()), color(bg_color), + respect_image_orientation(respect_image_orientation), include_left_edge(include_left), include_right_edge(include_right), is_bottom_layer(!layer.Next()), @@ -315,7 +320,7 @@ BoxPainterBase::FillLayerInfo::FillLayerInfo( // BorderFillBox radius clipping is taken care of by // BackgroundBleedClip{Only,Layer} is_rounded_fill = - has_rounded_border && + has_rounded_border && !is_painting_scrolling_background && !(is_border_fill && BleedAvoidanceIsClipping(bleed_avoidance)); should_paint_image = image && image->CanRender(); @@ -369,6 +374,25 @@ FloatRect ComputeSubsetForBackground(const FloatRect& phase_and_size, subset.Height() / scale.Height()); } +FloatRect SnapSourceRectIfNearIntegral(const FloatRect src_rect) { + // Round to avoid filtering pulling in neighboring pixels, for the + // common case of sprite maps, but only if we're close to an integral size. + // "Close" in this context means we will allow floating point inaccuracy, + // when converted to layout units, to be at most one LayoutUnit::Epsilon and + // still snap. + if (std::abs(std::round(src_rect.X()) - src_rect.X()) <= + LayoutUnit::Epsilon() && + std::abs(std::round(src_rect.Y()) - src_rect.Y()) <= + LayoutUnit::Epsilon() && + std::abs(std::round(src_rect.MaxX()) - src_rect.MaxX()) <= + LayoutUnit::Epsilon() && + std::abs(std::round(src_rect.MaxY()) - src_rect.MaxY()) <= + LayoutUnit::Epsilon()) { + return FloatRect(RoundedIntRect(src_rect)); + } + return src_rect; +} + // The unsnapped_subset_size should be the target painting area implied by the // content, without any snapping applied. It is necessary to correctly // compute the subset of the source image to paint into the destination. @@ -385,14 +409,21 @@ void DrawTiledBackground(GraphicsContext& context, const FloatSize& tile_size, SkBlendMode op, const FloatSize& repeat_spacing, - bool has_filter_property) { + bool has_filter_property, + RespectImageOrientationEnum respect_orientation) { DCHECK(!tile_size.IsEmpty()); // Use the intrinsic size of the image if it has one, otherwise force the // generated image to be the tile size. FloatSize intrinsic_tile_size(image->Size()); FloatSize scale(1, 1); - if (!image->HasIntrinsicSize()) { + if (!image->HasIntrinsicSize() || + // TODO(crbug.com/1042783): This is not checking for real empty image + // (for which we have checked and skipped the whole FillLayer), but for + // that a subpixel image size is rounded to empty, to avoid infinite tile + // scale that would be calculated in the |else| part. + // We should probably support subpixel size here. + intrinsic_tile_size.IsEmpty()) { intrinsic_tile_size = tile_size; } else { scale = FloatSize(tile_size.Width() / intrinsic_tile_size.Width(), @@ -412,26 +443,48 @@ void DrawTiledBackground(GraphicsContext& context, if (one_tile_rect.Contains(dest_rect_for_subset)) { FloatRect visible_src_rect = ComputeSubsetForBackground( one_tile_rect, dest_rect_for_subset, intrinsic_tile_size); - // Round to avoid filtering pulling in neighboring pixels, for the - // common case of sprite maps. - // TODO(schenney): Snapping at this level is a problem for cases where we - // might be animating background-position to pan over an image. Ideally we - // would either snap only if close to integral, or move snapping - // calculations up the stack. - visible_src_rect = FloatRect(RoundedIntRect(visible_src_rect)); + visible_src_rect = SnapSourceRectIfNearIntegral(visible_src_rect); + + // When respecting image orientation, the drawing code expects the source + // rect to be in the unrotated image space, but we have computed it here in + // the rotated space in order to position and size the background. Undo the + // src rect rotation if necessary. + if (respect_orientation && !image->HasDefaultOrientation()) { + visible_src_rect = image->CorrectSrcRectForImageOrientation( + visible_src_rect.Size(), visible_src_rect); + } + context.DrawImage(image, Image::kSyncDecode, snapped_paint_rect, &visible_src_rect, has_filter_property, op, - kDoNotRespectImageOrientation); + respect_orientation); return; } + // At this point we have decided to tile the image to fill the dest rect. // Note that this tile rect the image's pre-scaled size. FloatRect tile_rect(FloatPoint(), intrinsic_tile_size); + + // Farther down the pipeline we will use the scaled tile size to determine + // which dimensions to clamp or repeat in. We do not want to repeat when the + // tile size rounds to match the dest in a given dimension, to avoid having + // a single row or column repeated when the developer almost certainly + // intended the image to not repeat (this generally occurs under zoom). + // + // So detect when we do not want to repeat and set the scale to round the + // values in that dimension. + if (fabs(tile_size.Width() - snapped_paint_rect.Width()) <= 0.5) { + scale.SetWidth(snapped_paint_rect.Width() / intrinsic_tile_size.Width()); + } + if (fabs(tile_size.Height() - snapped_paint_rect.Height()) <= 0.5) { + scale.SetHeight(snapped_paint_rect.Height() / intrinsic_tile_size.Height()); + } + // This call takes the unscaled image, applies the given scale, and paints // it into the snapped_dest_rect using phase from one_tile_rect and the // given repeat spacing. Note the phase is already scaled. context.DrawImageTiled(image, snapped_paint_rect, tile_rect, scale, - one_tile_rect.Location(), repeat_spacing, op); + one_tile_rect.Location(), repeat_spacing, op, + respect_orientation); } inline bool PaintFastBottomLayer(Node* node, @@ -528,7 +581,10 @@ inline bool PaintFastBottomLayer(Node* node, // intrinsic size is the requested tile size. bool has_intrinsic_size = image->HasIntrinsicSize(); const FloatSize intrinsic_tile_size = - !has_intrinsic_size ? image_tile.Size() : FloatSize(image->Size()); + !has_intrinsic_size + ? image_tile.Size() + : FloatSize(image->Size(info.respect_image_orientation)); + // Subset computation needs the same location as was used with // ComputePhaseForBackground above, but needs the unsnapped destination // size to correctly calculate sprite subsets in the presence of zoom. But if @@ -544,12 +600,21 @@ inline bool PaintFastBottomLayer(Node* node, // from integer size, so it is safe to round without introducing major issues. const FloatRect unrounded_subset = ComputeSubsetForBackground( image_tile, dest_rect_for_subset, intrinsic_tile_size); - FloatRect src_rect = FloatRect(RoundedIntRect(unrounded_subset)); + FloatRect src_rect = SnapSourceRectIfNearIntegral(unrounded_subset); - // If we have rounded the image size to 0, revert the rounding. + // If we have snapped the image size to 0, revert the rounding. if (src_rect.IsEmpty()) src_rect = unrounded_subset; + // When respecting image orientation, the drawing code expects the source rect + // to be in the unrotated image space, but we have computed it here in the + // rotated space in order to position and size the background. Undo the src + // rect rotation if necessaary. + if (info.respect_image_orientation && !image->HasDefaultOrientation()) { + src_rect = + image->CorrectSrcRectForImageOrientation(src_rect.Size(), src_rect); + } + TRACE_EVENT1(TRACE_DISABLED_BY_DEFAULT("devtools.timeline"), "PaintImage", "data", inspector_paint_image_event::Data( @@ -561,19 +626,21 @@ inline bool PaintFastBottomLayer(Node* node, context.DrawImageRRect( image, Image::kSyncDecode, image_border, src_rect, node && node->ComputedStyleRef().HasFilterInducingProperty(), - composite_op); + composite_op, info.respect_image_orientation); if (info.image && info.image->IsImageResource()) { PaintTimingDetector::NotifyBackgroundImagePaint( - node, image, To<StyleFetchedImage>(info.image.Get()), - paint_info.context.GetPaintController().CurrentPaintChunkProperties()); + node, image, To<StyleFetchedImage>(info.image), + paint_info.context.GetPaintController().CurrentPaintChunkProperties(), + RoundedIntRect(image_border.Rect())); } if (node && info.image && info.image->IsImageResource()) { LocalDOMWindow* window = node->GetDocument().domWindow(); DCHECK(window); ImageElementTiming::From(*window).NotifyBackgroundImagePainted( - node, To<StyleFetchedImage>(info.image.Get()), - context.GetPaintController().CurrentPaintChunkProperties()); + node, To<StyleFetchedImage>(info.image), + context.GetPaintController().CurrentPaintChunkProperties(), + RoundedIntRect(image_border.Rect())); } return true; } @@ -691,18 +758,21 @@ void PaintFillLayerBackground(GraphicsContext& context, FloatRect(geometry.SnappedDestRect()), geometry.Phase(), FloatSize(geometry.TileSize()), composite_op, FloatSize(geometry.SpaceSize()), - node && node->ComputedStyleRef().HasFilterInducingProperty()); + node && node->ComputedStyleRef().HasFilterInducingProperty(), + info.respect_image_orientation); if (info.image && info.image->IsImageResource()) { PaintTimingDetector::NotifyBackgroundImagePaint( - node, image, To<StyleFetchedImage>(info.image.Get()), - context.GetPaintController().CurrentPaintChunkProperties()); + node, image, To<StyleFetchedImage>(info.image), + context.GetPaintController().CurrentPaintChunkProperties(), + EnclosingIntRect(geometry.SnappedDestRect())); } if (node && info.image && info.image->IsImageResource()) { LocalDOMWindow* window = node->GetDocument().domWindow(); DCHECK(window); ImageElementTiming::From(*window).NotifyBackgroundImagePainted( - node, To<StyleFetchedImage>(info.image.Get()), - context.GetPaintController().CurrentPaintChunkProperties()); + node, To<StyleFetchedImage>(info.image), + context.GetPaintController().CurrentPaintChunkProperties(), + EnclosingIntRect(geometry.SnappedDestRect())); } } } @@ -743,7 +813,9 @@ void BoxPainterBase::PaintFillLayer(const PaintInfo& paint_info, if (rect.IsEmpty()) return; - const FillLayerInfo info = GetFillLayerInfo(color, bg_layer, bleed_avoidance); + const FillLayerInfo info = + GetFillLayerInfo(color, bg_layer, bleed_avoidance, + IsPaintingScrollingBackground(paint_info)); // If we're not actually going to paint anything, abort early. if (!info.should_paint_image && !info.should_paint_color) return; @@ -913,4 +985,31 @@ void BoxPainterBase::PaintMaskImages(const PaintInfo& paint_info, include_logical_right_edge); } +bool BoxPainterBase::ShouldSkipPaintUnderInvalidationChecking( + const LayoutBox& box) { + DCHECK(RuntimeEnabledFeatures::PaintUnderInvalidationCheckingEnabled()); + + // Disable paint under-invalidation checking for cases that under-invalidation + // is intensional and/or harmless. + + // A box having delayed-invalidation may change before it's actually + // invalidated. Note that we still report harmless under-invalidation of + // non-delayed-invalidation animated background, which should be ignored. + if (box.ShouldDelayFullPaintInvalidation()) + return true; + + // We always paint a MediaSliderPart using the latest data (buffered ranges, + // current time and duration) which may be different from the cached data. + if (box.StyleRef().EffectiveAppearance() == kMediaSliderPart) + return true; + + // We paint an indeterminate progress based on the position calculated from + // the animation progress. Harmless under-invalidatoin may happen during a + // paint that is not scheduled for animation. + if (box.IsProgress() && !ToLayoutProgress(box).IsDeterminate()) + return true; + + return false; +} + } // namespace blink 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 146d65ceb78..7b5bbf22c58 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 @@ -10,6 +10,7 @@ #include "third_party/blink/renderer/core/style/style_image.h" #include "third_party/blink/renderer/platform/geometry/layout_rect_outsets.h" #include "third_party/blink/renderer/platform/graphics/color.h" +#include "third_party/blink/renderer/platform/graphics/image_orientation.h" #include "third_party/blink/renderer/platform/wtf/allocator/allocator.h" #include "third_party/skia/include/core/SkBlendMode.h" @@ -23,6 +24,7 @@ class FloatRoundedRect; class GraphicsContext; class ImageResourceObserver; class IntRect; +class LayoutBox; struct PaintInfo; struct PhysicalOffset; struct PhysicalRect; @@ -102,6 +104,8 @@ class BoxPainterBase { FillLayerOcclusionOutputList& reversed_paint_list, const FillLayer&); + static bool ShouldSkipPaintUnderInvalidationChecking(const LayoutBox&); + struct FillLayerInfo { STACK_ALLOCATED(); @@ -112,16 +116,19 @@ class BoxPainterBase { Color bg_color, const FillLayer&, BackgroundBleedAvoidance, + RespectImageOrientationEnum, bool include_left_edge, bool include_right_edge, - bool is_inline); + bool is_inline, + bool is_painting_scrolling_background); // FillLayerInfo is a temporary, stack-allocated container which cannot // outlive the StyleImage. This would normally be a raw pointer, if not for // the Oilpan tooling complaints. - Member<StyleImage> image; + StyleImage* image; Color color; + RespectImageOrientationEnum respect_image_orientation; bool include_left_edge; bool include_right_edge; bool is_bottom_layer; @@ -152,9 +159,12 @@ class BoxPainterBase { virtual PhysicalRect AdjustRectForScrolledContent(const PaintInfo&, const FillLayerInfo&, const PhysicalRect&) = 0; - virtual FillLayerInfo GetFillLayerInfo(const Color&, - const FillLayer&, - BackgroundBleedAvoidance) const = 0; + virtual FillLayerInfo GetFillLayerInfo( + const Color&, + const FillLayer&, + BackgroundBleedAvoidance, + bool is_painting_scrolling_background) const = 0; + virtual bool IsPaintingScrollingBackground(const PaintInfo&) const = 0; static void PaintInsetBoxShadow(const PaintInfo&, const FloatRoundedRect&, const ComputedStyle&, @@ -162,9 +172,9 @@ class BoxPainterBase { bool include_logical_right_edge = true); private: - Member<const Document> document_; + const Document* document_; const ComputedStyle& style_; - Member<Node> node_; + Node* node_; }; } // namespace blink diff --git a/chromium/third_party/blink/renderer/core/paint/box_painter_test.cc b/chromium/third_party/blink/renderer/core/paint/box_painter_test.cc index d651e69e97d..5a929df6094 100644 --- a/chromium/third_party/blink/renderer/core/paint/box_painter_test.cc +++ b/chromium/third_party/blink/renderer/core/paint/box_painter_test.cc @@ -7,7 +7,6 @@ #include "testing/gmock/include/gmock/gmock.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/scroll_hit_test_display_item.h" using testing::ElementsAre; @@ -17,30 +16,50 @@ using BoxPainterTest = PaintControllerPaintTest; INSTANTIATE_PAINT_TEST_SUITE_P(BoxPainterTest); -TEST_P(BoxPainterTest, DontPaintEmptyDecorationBackground) { +TEST_P(BoxPainterTest, EmptyDecorationBackground) { SetBodyInnerHTML(R"HTML( + <style> + body { + margin: 0; + /* to force a subsequene and paint chunk */ + opacity: 0.5; + /* to verify child empty backgrounds expand chunk bounds */ + height: 0; + } + </style> <div id="div1" style="width: 100px; height: 100px; background: green"> </div> <div id="div2" style="width: 100px; height: 100px; outline: 2px solid blue"> </div> + <div id="div3" style="width: 200px; height: 150px"></div> )HTML"); auto* div1 = GetLayoutObjectByElementId("div1"); auto* div2 = GetLayoutObjectByElementId("div2"); + auto* body = GetDocument().body()->GetLayoutBox(); + // Empty backgrounds don't generate display items. EXPECT_THAT(RootPaintController().GetDisplayItemList(), ElementsAre(IsSameId(&ViewScrollingBackgroundClient(), kDocumentBackgroundType), IsSameId(div1, kBackgroundType), IsSameId(div2, DisplayItem::PaintPhaseToDrawingType( PaintPhase::kSelfOutlineOnly)))); + EXPECT_THAT( + RootPaintController().PaintChunks(), + ElementsAre( + IsPaintChunk(0, 1, + PaintChunk::Id(ViewScrollingBackgroundClient(), + kDocumentBackgroundType), + GetLayoutView().FirstFragment().ContentsProperties(), + nullptr, IntRect(0, 0, 800, 600)), + // Empty backgrounds contribute to bounds of paint chunks. + IsPaintChunk(1, 3, + PaintChunk::Id(*body->Layer(), DisplayItem::kLayerChunk), + body->FirstFragment().LocalBorderBoxProperties(), + nullptr, IntRect(-2, 0, 202, 350)))); } -using BoxPainterScrollHitTestTest = PaintControllerPaintTest; - -INSTANTIATE_SCROLL_HIT_TEST_SUITE_P(BoxPainterScrollHitTestTest); - -TEST_P(BoxPainterScrollHitTestTest, - ScrollHitTestOrderWithScrollBackgroundAttachment) { +TEST_P(BoxPainterTest, ScrollHitTestOrderWithScrollBackgroundAttachment) { SetBodyInnerHTML(R"HTML( <style> ::-webkit-scrollbar { display: none; } @@ -60,7 +79,7 @@ TEST_P(BoxPainterScrollHitTestTest, </div> )HTML"); - auto& container = *GetLayoutObjectByElementId("container"); + auto& container = ToLayoutBox(*GetLayoutObjectByElementId("container")); auto& child = *GetLayoutObjectByElementId("child"); // As a reminder, "background-attachment: scroll" does not move when the @@ -74,11 +93,29 @@ TEST_P(BoxPainterScrollHitTestTest, ElementsAre(IsSameId(&ViewScrollingBackgroundClient(), kDocumentBackgroundType), IsSameId(&container, kBackgroundType), - IsSameId(&container, kScrollHitTestType), IsSameId(&child, kBackgroundType))); + HitTestData scroll_hit_test; + scroll_hit_test.scroll_translation = + &container.FirstFragment().ContentsProperties().Transform(); + scroll_hit_test.scroll_hit_test_rect = IntRect(0, 0, 200, 200); + EXPECT_THAT( + RootPaintController().PaintChunks(), + ElementsAre( + IsPaintChunk(0, 1, + PaintChunk::Id(ViewScrollingBackgroundClient(), + kDocumentBackgroundType), + GetLayoutView().FirstFragment().ContentsProperties()), + IsPaintChunk( + 1, 2, + PaintChunk::Id(*container.Layer(), DisplayItem::kLayerChunk), + container.FirstFragment().LocalBorderBoxProperties()), + IsPaintChunk(2, 2, + PaintChunk::Id(container, DisplayItem::kScrollHitTest), + container.FirstFragment().LocalBorderBoxProperties(), + &scroll_hit_test, IntRect(0, 0, 200, 200)), + IsPaintChunk(2, 3))); } else { - // Because the frame composited scrolls, no scroll hit test display item is - // needed. + // Because the frame composited scrolls, no scroll hit test is needed. const auto* non_scrolling_layer = To<LayoutBlock>(container) .Layer() ->GetCompositedLayerMapping() @@ -94,8 +131,7 @@ TEST_P(BoxPainterScrollHitTestTest, } } -TEST_P(BoxPainterScrollHitTestTest, - ScrollHitTestOrderWithLocalBackgroundAttachment) { +TEST_P(BoxPainterTest, ScrollHitTestOrderWithLocalBackgroundAttachment) { SetBodyInnerHTML(R"HTML( <style> ::-webkit-scrollbar { display: none; } @@ -131,12 +167,32 @@ TEST_P(BoxPainterScrollHitTestTest, RootPaintController().GetDisplayItemList(), ElementsAre( IsSameId(&ViewScrollingBackgroundClient(), kDocumentBackgroundType), - IsSameId(&container, kScrollHitTestType), IsSameId(container_scrolling_client, kBackgroundType), IsSameId(&child, kBackgroundType))); + HitTestData scroll_hit_test; + scroll_hit_test.scroll_translation = + &container.FirstFragment().ContentsProperties().Transform(); + scroll_hit_test.scroll_hit_test_rect = IntRect(0, 0, 200, 200); + EXPECT_THAT( + RootPaintController().PaintChunks(), + ElementsAre( + IsPaintChunk(0, 1, + PaintChunk::Id(ViewScrollingBackgroundClient(), + kDocumentBackgroundType), + GetLayoutView().FirstFragment().ContentsProperties()), + IsPaintChunk( + 1, 1, + PaintChunk::Id(*container.Layer(), DisplayItem::kLayerChunk), + container.FirstFragment().LocalBorderBoxProperties()), + IsPaintChunk(1, 1, + PaintChunk::Id(container, DisplayItem::kScrollHitTest), + container.FirstFragment().LocalBorderBoxProperties(), + &scroll_hit_test, IntRect(0, 0, 200, 200)), + IsPaintChunk( + 1, 3, PaintChunk::Id(container, kScrollingBackgroundChunkType), + container.FirstFragment().ContentsProperties()))); } else { - // Because the frame composited scrolls, no scroll hit test display item is - // needed. + // Because the frame composited scrolls, no scroll hit test is needed. const auto* non_scrolling_layer = container.Layer()->GetCompositedLayerMapping()->MainGraphicsLayer(); EXPECT_TRUE(non_scrolling_layer->GetPaintController() @@ -152,12 +208,7 @@ TEST_P(BoxPainterScrollHitTestTest, } } -TEST_P(BoxPainterScrollHitTestTest, ScrollHitTestProperties) { - // This test depends on the CompositeAfterPaint behavior of painting solid - // color backgrounds into both the non-scrolled and scrolled spaces. - if (!RuntimeEnabledFeatures::CompositeAfterPaintEnabled()) - return; - +TEST_P(BoxPainterTest, ScrollHitTestProperties) { SetBodyInnerHTML(R"HTML( <style> ::-webkit-scrollbar { display: none; } @@ -183,23 +234,22 @@ TEST_P(BoxPainterScrollHitTestTest, ScrollHitTestProperties) { // scrolled contents. EXPECT_EQ( kBackgroundPaintInGraphicsLayer | kBackgroundPaintInScrollingContents, - container.GetBackgroundPaintLocation()); + container.ComputeBackgroundPaintLocationIfComposited()); + EXPECT_EQ(kBackgroundPaintInGraphicsLayer, + container.GetBackgroundPaintLocation()); EXPECT_THAT( RootPaintController().GetDisplayItemList(), ElementsAre( IsSameId(&ViewScrollingBackgroundClient(), kDocumentBackgroundType), IsSameId(&container, kBackgroundType), - IsSameId(&container, kScrollHitTestType), - IsSameId(&container.GetScrollableArea() - ->GetScrollingBackgroundDisplayItemClient(), - kBackgroundType), IsSameId(&child, kBackgroundType))); HitTestData scroll_hit_test_data; const auto& scrolling_contents_properties = container.FirstFragment().ContentsProperties(); - scroll_hit_test_data.SetScrollHitTest( - &scrolling_contents_properties.Transform(), IntRect(0, 0, 200, 200)); + scroll_hit_test_data.scroll_translation = + &scrolling_contents_properties.Transform(); + scroll_hit_test_data.scroll_hit_test_rect = IntRect(0, 0, 200, 200); EXPECT_THAT( paint_chunks, ElementsAre( @@ -207,16 +257,18 @@ TEST_P(BoxPainterScrollHitTestTest, ScrollHitTestProperties) { 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), + IsPaintChunk( + 1, 2, + PaintChunk::Id(*container.Layer(), DisplayItem::kLayerChunk), + container.FirstFragment().LocalBorderBoxProperties()), + IsPaintChunk(2, 2, + PaintChunk::Id(container, DisplayItem::kScrollHitTest), container.FirstFragment().LocalBorderBoxProperties(), - scroll_hit_test_data), - IsPaintChunk(3, 5, - PaintChunk::Id(container, kScrollingBackgroundChunkType), - scrolling_contents_properties))); + &scroll_hit_test_data, IntRect(0, 0, 200, 200)), + IsPaintChunk( + 2, 3, + PaintChunk::Id(container, kClippedContentsBackgroundChunkType), + scrolling_contents_properties))); // We always create scroll node for the root layer. const auto& root_transform = paint_chunks[0].properties.Transform(); @@ -237,25 +289,23 @@ TEST_P(BoxPainterScrollHitTestTest, ScrollHitTestProperties) { EXPECT_EQ(nullptr, scroll_hit_test_transform.ScrollNode()); 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()); + EXPECT_EQ(FloatRect(0, 0, 800, 600), + scroll_hit_test_clip.UnsnappedClipRect().Rect()); // The scrolled contents should be scrolled and clipped. - const auto& contents_chunk = RootPaintController().PaintChunks()[3]; + const auto& contents_chunk = paint_chunks[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()); const auto& contents_clip = contents_chunk.properties.Clip(); - EXPECT_EQ(FloatRect(0, 0, 200, 200), contents_clip.ClipRect().Rect()); + EXPECT_EQ(FloatRect(0, 0, 200, 200), + contents_clip.UnsnappedClipRect().Rect()); - // The scroll hit test display item maintains a reference to a scroll offset - // translation node and the contents should be scrolled by this node. - const auto& scroll_hit_test_display_item = - static_cast<const ScrollHitTestDisplayItem&>( - RootPaintController() - .GetDisplayItemList()[scroll_hit_test_chunk.begin_index]); + // The scroll paint chunk maintains a reference to a scroll translation node + // and the contents should be scrolled by this node. EXPECT_EQ(&contents_transform, - scroll_hit_test_display_item.scroll_offset_node()); + scroll_hit_test_chunk.hit_test_data->scroll_translation); } } // namespace blink diff --git a/chromium/third_party/blink/renderer/core/paint/clip_path_clipper.cc b/chromium/third_party/blink/renderer/core/paint/clip_path_clipper.cc index 9e1eed19de9..2091d948454 100644 --- a/chromium/third_party/blink/renderer/core/paint/clip_path_clipper.cc +++ b/chromium/third_party/blink/renderer/core/paint/clip_path_clipper.cc @@ -25,22 +25,6 @@ namespace blink { namespace { -class SVGClipExpansionCycleHelper { - public: - void Lock(LayoutSVGResourceClipper& clipper) { - DCHECK(!clipper.HasCycle()); - clipper.BeginClipExpansion(); - clippers_.push_back(&clipper); - } - ~SVGClipExpansionCycleHelper() { - for (auto* clipper : clippers_) - clipper->EndClipExpansion(); - } - - private: - Vector<LayoutSVGResourceClipper*, 1> clippers_; -}; - LayoutSVGResourceClipper* ResolveElementReference( const LayoutObject& layout_object, const ReferenceClipPathOperation& reference_clip_path_operation) { @@ -65,7 +49,7 @@ LayoutSVGResourceClipper* ResolveElementReference( FloatRect ClipPathClipper::LocalReferenceBox(const LayoutObject& object) { if (object.IsSVGChild()) - return object.ObjectBoundingBox(); + return SVGResources::ReferenceBoxForEffects(object); if (object.IsBox()) return FloatRect(ToLayoutBox(object).BorderBoxRect()); @@ -127,17 +111,17 @@ static bool IsClipPathOperationValid( return false; SECURITY_DCHECK(!resource_clipper->NeedsLayout()); resource_clipper->ClearInvalidationMask(); - if (resource_clipper->HasCycle()) - return false; } return true; } ClipPathClipper::ClipPathClipper(GraphicsContext& context, const LayoutObject& layout_object, + const DisplayItemClient& display_item_client, const PhysicalOffset& paint_offset) : context_(context), layout_object_(layout_object), + display_item_client_(display_item_client), paint_offset_(paint_offset) { DCHECK(layout_object.StyleRef().ClipPath()); } @@ -166,20 +150,20 @@ ClipPathClipper::~ClipPathClipper() { return; ScopedPaintChunkProperties scoped_properties( context_.GetPaintController(), - layout_object_.FirstFragment().ClipPathProperties(), layout_object_, + layout_object_.FirstFragment().ClipPathProperties(), display_item_client_, DisplayItem::kSVGClip); bool is_svg_child = layout_object_.IsSVGChild(); FloatRect reference_box = LocalReferenceBox(layout_object_); - if (DrawingRecorder::UseCachedDrawingIfPossible(context_, layout_object_, - DisplayItem::kSVGClip)) + if (DrawingRecorder::UseCachedDrawingIfPossible( + context_, display_item_client_, DisplayItem::kSVGClip)) return; - DrawingRecorder recorder(context_, layout_object_, DisplayItem::kSVGClip); + DrawingRecorder recorder(context_, display_item_client_, + DisplayItem::kSVGClip); context_.Save(); context_.Translate(paint_offset_.left, paint_offset_.top); - SVGClipExpansionCycleHelper locks; bool is_first = true; bool rest_of_the_chain_already_appled = false; const LayoutObject* current_object = &layout_object_; @@ -201,7 +185,6 @@ ClipPathClipper::~ClipPathClipper() { // because it would have been applied as path-based clip already. DCHECK(resource_clipper); DCHECK_EQ(clip_path->GetType(), ClipPathOperation::REFERENCE); - locks.Lock(*resource_clipper); if (resource_clipper->StyleRef().ClipPath()) { // Try to apply nested clip-path as path-based clip. bool unused; diff --git a/chromium/third_party/blink/renderer/core/paint/clip_path_clipper.h b/chromium/third_party/blink/renderer/core/paint/clip_path_clipper.h index c852040eb9a..a52caedba03 100644 --- a/chromium/third_party/blink/renderer/core/paint/clip_path_clipper.h +++ b/chromium/third_party/blink/renderer/core/paint/clip_path_clipper.h @@ -13,6 +13,7 @@ namespace blink { +class DisplayItemClient; class GraphicsContext; class LayoutObject; @@ -22,6 +23,7 @@ class CORE_EXPORT ClipPathClipper { public: ClipPathClipper(GraphicsContext&, const LayoutObject&, + const DisplayItemClient&, const PhysicalOffset& paint_offset); ~ClipPathClipper(); @@ -52,6 +54,7 @@ class CORE_EXPORT ClipPathClipper { private: GraphicsContext& context_; const LayoutObject& layout_object_; + const DisplayItemClient& display_item_client_; PhysicalOffset paint_offset_; }; 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 0927a3d005f..4a32393fafc 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 @@ -28,7 +28,6 @@ #include <memory> #include "cc/layers/picture_layer.h" -#include "third_party/blink/public/common/features.h" #include "third_party/blink/renderer/core/accessibility/apply_dark_mode.h" #include "third_party/blink/renderer/core/display_lock/display_lock_utilities.h" #include "third_party/blink/renderer/core/dom/dom_node_ids.h" @@ -45,6 +44,7 @@ #include "third_party/blink/renderer/core/html/media/html_video_element.h" #include "third_party/blink/renderer/core/html_names.h" #include "third_party/blink/renderer/core/layout/geometry/transform_state.h" +#include "third_party/blink/renderer/core/layout/layout_box_model_object.h" #include "third_party/blink/renderer/core/layout/layout_embedded_content.h" #include "third_party/blink/renderer/core/layout/layout_embedded_object.h" #include "third_party/blink/renderer/core/layout/layout_html_canvas.h" @@ -180,7 +180,7 @@ static bool NeedsDecorationOutlineLayer(const PaintLayer& paint_layer, bool could_obscure_decorations = (paint_layer.GetScrollableArea() && paint_layer.GetScrollableArea()->UsesCompositedScrolling()) || - layout_object.IsCanvas() || layout_object.IsVideo(); + layout_object.IsCanvas() || IsA<LayoutVideo>(layout_object); return could_obscure_decorations && layout_object.StyleRef().HasOutline() && layout_object.StyleRef().OutlineOffset() < -min_border_width; @@ -189,21 +189,12 @@ static bool NeedsDecorationOutlineLayer(const PaintLayer& paint_layer, CompositedLayerMapping::CompositedLayerMapping(PaintLayer& layer) : owning_layer_(layer), pending_update_scope_(kGraphicsLayerUpdateNone), - is_main_frame_layout_view_layer_(false), scrolling_contents_are_empty_(false), - background_paints_onto_scrolling_contents_layer_(false), - background_paints_onto_graphics_layer_(false), draws_background_onto_content_layer_(false) { - if (layer.IsRootLayer() && GetLayoutObject().GetFrame()->IsMainFrame()) - is_main_frame_layout_view_layer_ = true; - CreatePrimaryGraphicsLayer(); } CompositedLayerMapping::~CompositedLayerMapping() { - // Hits in compositing/squashing/squash-onto-nephew.html. - DisableCompositingQueryAsserts disabler; - // Do not leave the destroyed pointer dangling on any Layers that painted to // this mapping's squashing layer. for (wtf_size_t i = 0; i < squashed_layers_.size(); ++i) { @@ -222,7 +213,6 @@ CompositedLayerMapping::~CompositedLayerMapping() { UpdateMaskLayer(false); UpdateScrollingLayers(false); UpdateSquashingLayers(false); - DestroyGraphicsLayers(); } std::unique_ptr<GraphicsLayer> CompositedLayerMapping::CreateGraphicsLayer( @@ -237,6 +227,26 @@ std::unique_ptr<GraphicsLayer> CompositedLayerMapping::CreateGraphicsLayer( static_cast<int>(DOMNodeIds::IdForNode(owning_node))); } + // Attempt to associate each layer with the frame owner's element ID. + Document* owner = nullptr; + if (GetLayoutObject().IsLayoutEmbeddedContent()) { + auto& embedded = ToLayoutEmbeddedContent(GetLayoutObject()); + if (auto* frame_view = + DynamicTo<LocalFrameView>(embedded.GetEmbeddedContentView())) { + owner = frame_view->GetFrame().GetDocument(); + } else { + // Ignore remote and plugin frames. + } + } else { + owner = &GetLayoutObject().GetDocument(); + } + if (owner) { + graphics_layer->CcLayer()->SetFrameElementId( + CompositorElementIdFromUniqueObjectId( + DOMNodeIds::IdForNode(owner), + CompositorElementIdNamespace::kDOMNodeId)); + } + return graphics_layer; } @@ -248,52 +258,6 @@ void CompositedLayerMapping::CreatePrimaryGraphicsLayer() { graphics_layer_->SetHitTestable(true); } -void CompositedLayerMapping::DestroyGraphicsLayers() { - if (graphics_layer_) - graphics_layer_->RemoveFromParent(); - - graphics_layer_ = nullptr; - foreground_layer_ = nullptr; - mask_layer_ = nullptr; - - scrolling_layer_ = nullptr; - scrolling_contents_layer_ = nullptr; -} - -void CompositedLayerMapping::UpdateBackgroundPaintsOntoScrollingContentsLayer( - bool& invalidate_graphics_layer, - bool& invalidate_scrolling_contents_layer) { - invalidate_graphics_layer = false; - invalidate_scrolling_contents_layer = false; - // We can only paint the background onto the scrolling contents layer if - // 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 = - GetLayoutObject().GetBackgroundPaintLocation(); - bool should_paint_onto_scrolling_contents_layer = - paint_location & kBackgroundPaintInScrollingContents && - owning_layer_.GetScrollableArea()->UsesCompositedScrolling(); - if (should_paint_onto_scrolling_contents_layer != - BackgroundPaintsOntoScrollingContentsLayer()) { - background_paints_onto_scrolling_contents_layer_ = - should_paint_onto_scrolling_contents_layer; - // The scrolling contents layer needs to be updated for changed - // m_backgroundPaintsOntoScrollingContentsLayer. - if (HasScrollingLayer()) - invalidate_scrolling_contents_layer = true; - } - bool should_paint_onto_graphics_layer = - !background_paints_onto_scrolling_contents_layer_ || - paint_location & kBackgroundPaintInGraphicsLayer; - if (should_paint_onto_graphics_layer != - !!background_paints_onto_graphics_layer_) { - background_paints_onto_graphics_layer_ = should_paint_onto_graphics_layer; - // The graphics layer needs to be updated for changed - // m_backgroundPaintsOntoGraphicsLayer. - invalidate_graphics_layer = true; - } -} - 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 @@ -322,40 +286,35 @@ void CompositedLayerMapping::UpdateContentsOpaque() { // TODO(crbug.com/705019): Contents could be opaque, but that cannot be // determined from the main thread. Or can it? graphics_layer_->SetContentsOpaque(false); - } else { - // For non-root layers, background is painted by the scrolling contents - // layer if all backgrounds are background attachment local, otherwise - // background is painted by the primary graphics layer. - if (HasScrollingLayer() && - background_paints_onto_scrolling_contents_layer_) { - // Backgrounds painted onto the foreground are clipped by the padding box - // rect. - // TODO(flackr): This should actually check the entire overflow rect - // within the scrolling contents layer but since we currently only trigger - // this for solid color backgrounds the answer will be the same. - scrolling_contents_layer_->SetContentsOpaque( - owning_layer_.BackgroundIsKnownToBeOpaqueInRect( - ToLayoutBox(GetLayoutObject()).PhysicalPaddingBoxRect(), - should_check_children)); - - if (GetLayoutObject().GetBackgroundPaintLocation() & - kBackgroundPaintInGraphicsLayer) { - graphics_layer_->SetContentsOpaque( - owning_layer_.BackgroundIsKnownToBeOpaqueInRect( - 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 - // is so it is not opaque. - graphics_layer_->SetContentsOpaque(false); - } - } else { - if (HasScrollingLayer()) - scrolling_contents_layer_->SetContentsOpaque(false); + } else if (BackgroundPaintsOntoScrollingContentsLayer()) { + DCHECK(HasScrollingLayer()); + // Backgrounds painted onto the foreground are clipped by the padding box + // rect. + // TODO(flackr): This should actually check the entire overflow rect + // within the scrolling contents layer but since we currently only trigger + // this for solid color backgrounds the answer will be the same. + scrolling_contents_layer_->SetContentsOpaque( + owning_layer_.BackgroundIsKnownToBeOpaqueInRect( + ToLayoutBox(GetLayoutObject()).PhysicalPaddingBoxRect(), + should_check_children)); + + if (BackgroundPaintsOntoGraphicsLayer()) { graphics_layer_->SetContentsOpaque( owning_layer_.BackgroundIsKnownToBeOpaqueInRect( 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 + // is so it is not opaque. + graphics_layer_->SetContentsOpaque(false); } + } else { + DCHECK(BackgroundPaintsOntoGraphicsLayer()); + if (HasScrollingLayer()) + scrolling_contents_layer_->SetContentsOpaque(false); + graphics_layer_->SetContentsOpaque( + owning_layer_.BackgroundIsKnownToBeOpaqueInRect(CompositedBounds(), + should_check_children)); } } @@ -452,36 +411,30 @@ bool CompositedLayerMapping::UpdateGraphicsLayerConfiguration( } } - if (WebPluginContainerImpl* plugin = GetPluginContainer(layout_object)) { - graphics_layer_->SetContentsToCcLayer( - plugin->CcLayer(), plugin->PreventContentsOpaqueChangesToCcLayer()); - } else { - auto* frame_owner = - DynamicTo<HTMLFrameOwnerElement>(layout_object.GetNode()); - if (frame_owner && frame_owner->ContentFrame()) { - Frame* frame = frame_owner->ContentFrame(); - if (auto* remote = DynamicTo<RemoteFrame>(frame)) { - cc::Layer* layer = remote->GetCcLayer(); + if (layout_object.IsLayoutEmbeddedContent()) { + if (WebPluginContainerImpl* plugin = GetPluginContainer(layout_object)) { + graphics_layer_->SetContentsToCcLayer( + plugin->CcLayer(), plugin->PreventContentsOpaqueChangesToCcLayer()); + } else if (auto* frame_owner = + DynamicTo<HTMLFrameOwnerElement>(layout_object.GetNode())) { + if (auto* remote = DynamicTo<RemoteFrame>(frame_owner->ContentFrame())) { graphics_layer_->SetContentsToCcLayer( - layer, remote->WebLayerHasFixedContentsOpaque()); + remote->GetCcLayer(), remote->WebLayerHasFixedContentsOpaque()); } - } else if (layout_object.IsVideo()) { - HTMLMediaElement* media_element = - ToHTMLMediaElement(layout_object.GetNode()); - graphics_layer_->SetContentsToCcLayer( - media_element->CcLayer(), - /*prevent_contents_opaque_changes=*/true); - } else if (layout_object.IsCanvas()) { - graphics_layer_->SetContentsToCcLayer( - To<HTMLCanvasElement>(layout_object.GetNode())->ContentsCcLayer(), - /*prevent_contents_opaque_changes=*/false); - layer_config_changed = true; } - } - if (layout_object.IsLayoutEmbeddedContent()) { if (PaintLayerCompositor::AttachFrameContentLayersToIframeLayer( ToLayoutEmbeddedContent(layout_object))) layer_config_changed = true; + } else if (IsA<LayoutVideo>(layout_object)) { + auto* media_element = To<HTMLMediaElement>(layout_object.GetNode()); + graphics_layer_->SetContentsToCcLayer( + media_element->CcLayer(), + /*prevent_contents_opaque_changes=*/true); + } else if (layout_object.IsCanvas()) { + graphics_layer_->SetContentsToCcLayer( + To<HTMLCanvasElement>(layout_object.GetNode())->ContentsCcLayer(), + /*prevent_contents_opaque_changes=*/false); + layer_config_changed = true; } if (layer_config_changed) { @@ -769,22 +722,7 @@ void CompositedLayerMapping::UpdateGraphicsLayerGeometry( UpdateContentsRect(); UpdateBackgroundColor(); - - bool invalidate_graphics_layer; - bool invalidate_scrolling_contents_layer; - UpdateBackgroundPaintsOntoScrollingContentsLayer( - invalidate_graphics_layer, invalidate_scrolling_contents_layer); - - // This depends on background_paints_onto_graphics_layer_. UpdateDrawsContentAndPaintsHitTest(); - - // These invalidations need to happen after - // |UpdateDrawsContentAndPaintsHitTest|. - if (invalidate_graphics_layer) - graphics_layer_->SetNeedsDisplay(); - if (invalidate_scrolling_contents_layer) - scrolling_contents_layer_->SetNeedsDisplay(); - UpdateElementId(); UpdateContentsOpaque(); UpdateRasterizationPolicy(); @@ -846,11 +784,11 @@ void CompositedLayerMapping::UpdateOverflowControlsHostLayerGeometry( // To clip the scrollbars correctly, overflow_controls_host_layer_ should // match our border box size. - const IntRect border_box = - owning_layer_.GetLayoutBox()->PixelSnappedBorderBoxRect( - owning_layer_.SubpixelAccumulation()); - overflow_controls_host_layer_->SetSize(gfx::Size(border_box.Size())); - overflow_controls_host_layer_->SetMasksToBounds(true); + const IntSize border_box_size = + owning_layer_.GetLayoutBox()->PixelSnappedBorderBoxSize( + PhysicalOffset(owning_layer_.SubpixelAccumulation() + + owning_layer_.GetLayoutBox()->PhysicalLocation())); + overflow_controls_host_layer_->SetSize(gfx::Size(border_box_size)); } void CompositedLayerMapping::UpdateMaskLayerGeometry() { @@ -890,7 +828,8 @@ void CompositedLayerMapping::UpdateScrollingLayerGeometry() { scroll_size = scroll_size.ExpandedTo(overflow_clip_rect.Size()); auto* scrolling_coordinator = owning_layer_.GetScrollingCoordinator(); - scrolling_coordinator->UpdateCompositedScrollOffset(scrollable_area); + scrolling_coordinator->UpdateCompositorScrollOffset(*layout_box.GetFrame(), + *scrollable_area); if (gfx::Size(scroll_size) != scrolling_contents_layer_->Size() || scroll_container_size_changed) { @@ -1028,7 +967,7 @@ void CompositedLayerMapping::UpdateContentsRect() { void CompositedLayerMapping::UpdateDrawsContentAndPaintsHitTest() { bool in_overlay_fullscreen_video = false; - if (GetLayoutObject().IsVideo()) { + if (IsA<LayoutVideo>(GetLayoutObject())) { auto* video_element = To<HTMLVideoElement>(GetLayoutObject().GetNode()); if (video_element->IsFullscreen() && video_element->UsesOverlayFullscreenVideo()) @@ -1393,7 +1332,6 @@ bool CompositedLayerMapping::UpdateScrollingLayers( CreateGraphicsLayer(CompositingReason::kLayerForScrollingContainer); scrolling_layer_->SetDrawsContent(false); scrolling_layer_->SetHitTestable(false); - scrolling_layer_->SetMasksToBounds(true); // Inner layer which renders the content that scrolls. scrolling_contents_layer_ = @@ -1410,8 +1348,8 @@ bool CompositedLayerMapping::UpdateScrollingLayers( scrolling_coordinator->ScrollableAreaScrollLayerDidChange( scrollable_area); const auto& object = GetLayoutObject(); - if (object.IsLayoutView()) - ToLayoutView(object).GetFrameView()->ScrollableAreasDidChange(); + if (auto* layout_view = DynamicTo<LayoutView>(object)) + layout_view->GetFrameView()->ScrollableAreasDidChange(); } } } else if (scrolling_layer_) { @@ -1422,8 +1360,8 @@ bool CompositedLayerMapping::UpdateScrollingLayers( scrolling_coordinator->ScrollableAreaScrollLayerDidChange( scrollable_area); const auto& object = GetLayoutObject(); - if (object.IsLayoutView()) - ToLayoutView(object).GetFrameView()->ScrollableAreasDidChange(); + if (auto* layout_view = DynamicTo<LayoutView>(object)) + layout_view->GetFrameView()->ScrollableAreasDidChange(); } } @@ -1487,8 +1425,9 @@ CompositedLayerMapping::PaintingPhaseForPrimaryLayer() const { Color CompositedLayerMapping::LayoutObjectBackgroundColor() const { const auto& object = GetLayoutObject(); auto background_color = object.ResolveColor(GetCSSPropertyBackgroundColor()); - if (object.IsLayoutView() && object.GetDocument().IsInMainFrame()) { - return ToLayoutView(object).GetFrameView()->BaseBackgroundColor().Blend( + auto* layout_view = DynamicTo<LayoutView>(object); + if (layout_view && object.GetDocument().IsInMainFrame()) { + return layout_view->GetFrameView()->BaseBackgroundColor().Blend( background_color); } return background_color; @@ -1545,13 +1484,13 @@ bool CompositedLayerMapping::ContainsPaintedContent() const { // FIXME: we could optimize cases where the image, video or canvas is known to // fill the border box entirely, and set background color on the layer in that // case, instead of allocating backing store and painting. - if (layout_object.IsVideo() && - ToLayoutVideo(layout_object).ShouldDisplayVideo()) + auto* layout_video = DynamicTo<LayoutVideo>(layout_object); + if (layout_video && layout_video->ShouldDisplayVideo()) return owning_layer_.HasBoxDecorationsOrBackground(); if (layout_object.GetNode() && layout_object.GetNode()->IsDocumentNode()) { if (owning_layer_.NeedsCompositedScrolling()) - return background_paints_onto_graphics_layer_; + return BackgroundPaintsOntoGraphicsLayer(); // Look to see if the root object has a non-simple background LayoutObject* root_object = @@ -1595,9 +1534,6 @@ bool CompositedLayerMapping::ContainsPaintedContent() const { bool CompositedLayerMapping::IsDirectlyCompositedImage() const { DCHECK(GetLayoutObject().IsImage()); - if (base::FeatureList::IsEnabled(features::kDisableDirectlyCompositedImages)) - return false; - LayoutImage& image_layout_object = ToLayoutImage(GetLayoutObject()); if (owning_layer_.HasBoxDecorationsOrBackground() || @@ -1609,8 +1545,7 @@ bool CompositedLayerMapping::IsDirectlyCompositedImage() const { if (!cached_image->HasImage()) return false; - Image* image = cached_image->GetImage(); - if (!image->IsBitmapImage()) + if (!IsA<BitmapImage>(cached_image->GetImage())) return false; UseCounter::Count(GetLayoutObject().GetDocument(), @@ -1944,7 +1879,7 @@ IntRect CompositedLayerMapping::RecomputeInterestRect( GeometryMapper::LocalToAncestorVisualRect( source_state, root_view_contents_state, mapping_rect); - FloatRect visible_content_rect = mapping_rect.Rect(); + FloatRect visible_content_rect(EnclosingIntRect(mapping_rect.Rect())); // 3. Move into local border box transform space of the root LayoutView. // Note that the overflow clip has *not* been applied. @@ -2175,10 +2110,10 @@ void CompositedLayerMapping::PaintContents( graphics_layer == mask_layer_.get() || graphics_layer == scrolling_contents_layer_.get() || graphics_layer == decoration_outline_layer_.get()) { - if (background_paints_onto_scrolling_contents_layer_) { + if (BackgroundPaintsOntoScrollingContentsLayer()) { if (graphics_layer == scrolling_contents_layer_.get()) paint_layer_flags &= ~kPaintLayerPaintingSkipRootBackground; - else if (!background_paints_onto_graphics_layer_) + else if (!BackgroundPaintsOntoGraphicsLayer()) paint_layer_flags |= kPaintLayerPaintingSkipRootBackground; } @@ -2221,12 +2156,12 @@ void CompositedLayerMapping::PaintScrollableArea( if (graphics_layer == LayerForHorizontalScrollbar()) { if (const Scrollbar* scrollbar = scrollable_area->HorizontalScrollbar()) { if (cull_rect.Intersects(scrollbar->FrameRect())) - scrollbar->Paint(context); + scrollbar->Paint(context, IntPoint()); } } else if (graphics_layer == LayerForVerticalScrollbar()) { if (const Scrollbar* scrollbar = scrollable_area->VerticalScrollbar()) { if (cull_rect.Intersects(scrollbar->FrameRect())) - scrollbar->Paint(context); + scrollbar->Paint(context, IntPoint()); } } else if (graphics_layer == LayerForScrollCorner()) { ScrollableAreaPainter painter(*scrollable_area); @@ -2309,33 +2244,6 @@ void CompositedLayerMapping::VerifyNotPainting() { } #endif -// Only used for performance benchmark testing. Intended to be a -// sufficiently-unique element id name to allow picking out the target element -// for invalidation. -static const char kTestPaintInvalidationTargetName[] = - "blinkPaintInvalidationTarget"; - -void CompositedLayerMapping::InvalidateTargetElementForTesting() { - // The below is an artificial construct formed intentionally to focus a - // microbenchmark on the cost of paint with a partial invalidation. - Element* target_element = - owning_layer_.GetLayoutObject().GetDocument().getElementById( - AtomicString(kTestPaintInvalidationTargetName)); - // TODO(wkorman): If we don't find the expected target element, we could - // consider walking to the first leaf node so that the partial-invalidation - // benchmark mode still provides some value when running on generic pages. - if (!target_element) - return; - LayoutObject* target_object = target_element->GetLayoutObject(); - if (!target_object) - return; - target_object->EnclosingLayer()->SetNeedsRepaint(); - // TODO(wkorman): Consider revising the below to invalidate all - // non-compositing descendants as well. - target_object->InvalidateDisplayItemClients( - PaintInvalidationReason::kForTesting); -} - bool CompositedLayerMapping::InvalidateLayerIfNoPrecedingEntry( wtf_size_t index_to_clear) { PaintLayer* layer_to_remove = squashed_layers_[index_to_clear].paint_layer; 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 4d96ad6ac59..bb399d8b1d5 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 @@ -100,12 +100,6 @@ class CORE_EXPORT CompositedLayerMapping final : public GraphicsLayerClient { const PaintLayer* compositing_container, Vector<PaintLayer*>& layers_needing_paint_invalidation); - // Update whether background paints onto scrolling contents layer. - // Returns (through the reference params) what invalidations are needed. - void UpdateBackgroundPaintsOntoScrollingContentsLayer( - bool& invalidate_graphics_layer, - bool& invalidate_scrolling_contents_layer); - // Update whether layer needs blending. void UpdateContentsOpaque(); @@ -169,7 +163,6 @@ class CORE_EXPORT CompositedLayerMapping final : public GraphicsLayerClient { void UpdateElementId(); // GraphicsLayerClient interface - void InvalidateTargetElementForTesting() override; IntRect ComputeInterestRect( const GraphicsLayer*, const IntRect& previous_interest_rect) const override; @@ -258,27 +251,29 @@ class CORE_EXPORT CompositedLayerMapping final : public GraphicsLayerClient { bool AdjustForCompositedScrolling(const GraphicsLayer*, IntSize& offset) const; + bool DrawsBackgroundOntoContentLayer() const { + return draws_background_onto_content_layer_; + } + + private: // Returns true for layers with scrollable overflow which have a background // that can be painted into the composited scrolling contents layer (i.e. // 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() const { - return background_paints_onto_scrolling_contents_layer_; + return GetLayoutObject().GetBackgroundPaintLocation() & + kBackgroundPaintInScrollingContents; } // 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_; + return GetLayoutObject().GetBackgroundPaintLocation() & + kBackgroundPaintInGraphicsLayer; } - bool DrawsBackgroundOntoContentLayer() const { - return draws_background_onto_content_layer_; - } - - private: IntRect RecomputeInterestRect(const GraphicsLayer*) const; static bool InterestRectChangedEnoughToRepaint( const IntRect& previous_interest_rect, @@ -323,7 +318,6 @@ class CORE_EXPORT CompositedLayerMapping final : public GraphicsLayerClient { void UpdateScrollingLayerGeometry(); void CreatePrimaryGraphicsLayer(); - void DestroyGraphicsLayers(); std::unique_ptr<GraphicsLayer> CreateGraphicsLayer( CompositingReasons, @@ -483,19 +477,9 @@ class CORE_EXPORT CompositedLayerMapping final : public GraphicsLayerClient { PhysicalRect composited_bounds_; unsigned pending_update_scope_ : 2; - unsigned is_main_frame_layout_view_layer_ : 1; unsigned scrolling_contents_are_empty_ : 1; - // Keep track of whether the background is painted onto the scrolling contents - // layer for invalidations. - unsigned background_paints_onto_scrolling_contents_layer_ : 1; - - // Solid color border boxes may be painted into both the scrolling contents - // layer and the graphics layer because the scrolling contents layer is - // clipped by the padding box. - unsigned background_paints_onto_graphics_layer_ : 1; - bool draws_background_onto_content_layer_; friend class CompositedLayerMappingTest; 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 311dcbe9b6c..b43664bfed7 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 @@ -6,8 +6,13 @@ #include "cc/layers/layer.h" #include "cc/layers/picture_layer.h" +#include "cc/trees/layer_tree_host.h" +#include "cc/trees/property_tree.h" +#include "cc/trees/scroll_node.h" #include "testing/gtest/include/gtest/gtest.h" +#include "third_party/blink/renderer/core/dom/dom_node_ids.h" #include "third_party/blink/renderer/core/frame/local_frame_view.h" +#include "third_party/blink/renderer/core/html/html_frame_owner_element.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_view.h" @@ -64,7 +69,8 @@ TEST_F(CompositedLayerMappingTest, SubpixelAccumulationChange) { Element* target = GetDocument().getElementById("target"); target->SetInlineStyleProperty(CSSPropertyID::kLeft, "0.6px"); - GetDocument().View()->UpdateAllLifecyclePhasesExceptPaint(); + GetDocument().View()->UpdateAllLifecyclePhasesExceptPaint( + DocumentUpdateReason::kTest); PaintLayer* paint_layer = ToLayoutBoxModelObject(target->GetLayoutObject())->Layer(); @@ -86,7 +92,8 @@ TEST_F(CompositedLayerMappingTest, Element* target = GetDocument().getElementById("target"); target->SetInlineStyleProperty(CSSPropertyID::kLeft, "0.6px"); - GetDocument().View()->UpdateAllLifecyclePhasesExceptPaint(); + GetDocument().View()->UpdateAllLifecyclePhasesExceptPaint( + DocumentUpdateReason::kTest); PaintLayer* paint_layer = ToLayoutBoxModelObject(target->GetLayoutObject())->Layer(); @@ -129,7 +136,8 @@ TEST_F(CompositedLayerMappingTest, Element* target = GetDocument().getElementById("target"); target->SetInlineStyleProperty(CSSPropertyID::kLeft, "0.6px"); - GetDocument().View()->UpdateAllLifecyclePhasesExceptPaint(); + GetDocument().View()->UpdateAllLifecyclePhasesExceptPaint( + DocumentUpdateReason::kTest); PaintLayer* paint_layer = ToLayoutBoxModelObject(target->GetLayoutObject())->Layer(); @@ -181,8 +189,8 @@ TEST_F(CompositedLayerMappingTest, TallCompositedScrolledLayerInterestRect) { )HTML"); UpdateAllLifecyclePhasesForTest(); - GetDocument().View()->LayoutViewport()->SetScrollOffset(ScrollOffset(0, 8000), - kProgrammaticScroll); + GetDocument().View()->LayoutViewport()->SetScrollOffset( + ScrollOffset(0, 8000), mojom::blink::ScrollType::kProgrammatic); UpdateAllLifecyclePhasesForTest(); Element* element = GetDocument().getElementById("target"); @@ -199,8 +207,8 @@ TEST_F(CompositedLayerMappingTest, TallNonCompositedScrolledLayerInterestRect) { )HTML"); UpdateAllLifecyclePhasesForTest(); - GetDocument().View()->LayoutViewport()->SetScrollOffset(ScrollOffset(0, 8000), - kProgrammaticScroll); + GetDocument().View()->LayoutViewport()->SetScrollOffset( + ScrollOffset(0, 8000), mojom::blink::ScrollType::kProgrammatic); UpdateAllLifecyclePhasesForTest(); PaintLayer* paint_layer = GetDocument().GetLayoutView()->Layer(); @@ -239,7 +247,7 @@ TEST_F(CompositedLayerMappingTest, VerticalRightLeftWritingModeDocument) { UpdateAllLifecyclePhasesForTest(); GetDocument().View()->LayoutViewport()->SetScrollOffset( - ScrollOffset(-5000, 0), kProgrammaticScroll); + ScrollOffset(-5000, 0), mojom::blink::ScrollType::kProgrammatic); UpdateAllLifecyclePhasesForTest(); PaintLayer* paint_layer = GetDocument().GetLayoutView()->Layer(); @@ -410,7 +418,7 @@ TEST_F(CompositedLayerMappingTest, 3D45DegRotatedTallInterestRect) { PaintLayer* paint_layer = ToLayoutBoxModelObject(element->GetLayoutObject())->Layer(); ASSERT_TRUE(!!paint_layer->GraphicsLayerBacking()); - EXPECT_EQ(IntRect(0, 0, 200, 6249), + EXPECT_EQ(IntRect(0, 0, 200, 6226), RecomputeInterestRect(paint_layer->GraphicsLayerBacking())); } @@ -618,8 +626,8 @@ TEST_F(CompositedLayerMappingTest, InterestRectChangeOnViewportScroll) { EXPECT_EQ(IntRect(0, 0, 800, 4600), PreviousInterestRect(root_scrolling_layer)); - GetDocument().View()->LayoutViewport()->SetScrollOffset(ScrollOffset(0, 300), - kProgrammaticScroll); + GetDocument().View()->LayoutViewport()->SetScrollOffset( + ScrollOffset(0, 300), mojom::blink::ScrollType::kProgrammatic); UpdateAllLifecyclePhasesForTest(); // Still use the previous interest rect because the recomputed rect hasn't // changed enough. @@ -628,8 +636,8 @@ TEST_F(CompositedLayerMappingTest, InterestRectChangeOnViewportScroll) { EXPECT_EQ(IntRect(0, 0, 800, 4600), PreviousInterestRect(root_scrolling_layer)); - GetDocument().View()->LayoutViewport()->SetScrollOffset(ScrollOffset(0, 600), - kProgrammaticScroll); + GetDocument().View()->LayoutViewport()->SetScrollOffset( + ScrollOffset(0, 600), mojom::blink::ScrollType::kProgrammatic); UpdateAllLifecyclePhasesForTest(); // Use recomputed interest rect because it changed enough. EXPECT_EQ(IntRect(0, 0, 800, 5200), @@ -637,16 +645,16 @@ TEST_F(CompositedLayerMappingTest, InterestRectChangeOnViewportScroll) { EXPECT_EQ(IntRect(0, 0, 800, 5200), PreviousInterestRect(root_scrolling_layer)); - GetDocument().View()->LayoutViewport()->SetScrollOffset(ScrollOffset(0, 5400), - kProgrammaticScroll); + GetDocument().View()->LayoutViewport()->SetScrollOffset( + ScrollOffset(0, 5400), mojom::blink::ScrollType::kProgrammatic); UpdateAllLifecyclePhasesForTest(); EXPECT_EQ(IntRect(0, 1400, 800, 8600), RecomputeInterestRect(root_scrolling_layer)); EXPECT_EQ(IntRect(0, 1400, 800, 8600), PreviousInterestRect(root_scrolling_layer)); - GetDocument().View()->LayoutViewport()->SetScrollOffset(ScrollOffset(0, 9000), - kProgrammaticScroll); + GetDocument().View()->LayoutViewport()->SetScrollOffset( + ScrollOffset(0, 9000), mojom::blink::ScrollType::kProgrammatic); UpdateAllLifecyclePhasesForTest(); // Still use the previous interest rect because it contains the recomputed // interest rect. @@ -655,8 +663,8 @@ TEST_F(CompositedLayerMappingTest, InterestRectChangeOnViewportScroll) { EXPECT_EQ(IntRect(0, 1400, 800, 8600), PreviousInterestRect(root_scrolling_layer)); - GetDocument().View()->LayoutViewport()->SetScrollOffset(ScrollOffset(0, 2000), - kProgrammaticScroll); + GetDocument().View()->LayoutViewport()->SetScrollOffset( + ScrollOffset(0, 2000), mojom::blink::ScrollType::kProgrammatic); // Use recomputed interest rect because it changed enough. UpdateAllLifecyclePhasesForTest(); EXPECT_EQ(IntRect(0, 0, 800, 6600), @@ -856,7 +864,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); + ScrollOffset(0.0, 8000.0), mojom::blink::ScrollType::kProgrammatic); UpdateAllLifecyclePhasesForTest(); Element* target = ChildDocument().getElementById("target"); @@ -887,7 +895,7 @@ TEST_F(CompositedLayerMappingTest, InterestRectOfScrolledIframe) { // Scroll 7500 pixels down to bring the scrollable area to the bottom. ChildDocument().View()->LayoutViewport()->SetScrollOffset( - ScrollOffset(0.0, 7500.0), kProgrammaticScroll); + ScrollOffset(0.0, 7500.0), mojom::blink::ScrollType::kProgrammatic); UpdateAllLifecyclePhasesForTest(); ASSERT_TRUE(ChildDocument().View()->GetLayoutView()->HasLayer()); @@ -921,7 +929,7 @@ TEST_F(CompositedLayerMappingTest, InterestRectOfIframeWithContentBoxOffset) { // Scroll 3000 pixels down to bring the scrollable area to somewhere in the // middle. ChildDocument().View()->LayoutViewport()->SetScrollOffset( - ScrollOffset(0.0, 3000.0), kProgrammaticScroll); + ScrollOffset(0.0, 3000.0), mojom::blink::ScrollType::kProgrammatic); UpdateAllLifecyclePhasesForTest(); ASSERT_TRUE(ChildDocument().View()->GetLayoutView()->HasLayer()); @@ -963,7 +971,7 @@ TEST_F(CompositedLayerMappingTest, InterestRectOfIframeWithFixedContents) { EXPECT_EQ(IntRect(1000, 0, 4400, 300), RecomputeInterestRect(graphics_layer)); ChildDocument().View()->LayoutViewport()->SetScrollOffset( - ScrollOffset(0.0, 3000.0), kProgrammaticScroll); + ScrollOffset(0.0, 3000.0), mojom::blink::ScrollType::kProgrammatic); UpdateAllLifecyclePhasesForTest(); // Because the fixed element does not scroll, the interest rect is unchanged. @@ -989,7 +997,7 @@ TEST_F(CompositedLayerMappingTest, ScrolledFixedPositionInterestRect) { EXPECT_EQ(IntRect(0, 500, 100, 4030), RecomputeInterestRect(graphics_layer)); GetDocument().View()->LayoutViewport()->SetScrollOffset( - ScrollOffset(0.0, 200.0), kProgrammaticScroll); + ScrollOffset(0.0, 200.0), mojom::blink::ScrollType::kProgrammatic); UpdateAllLifecyclePhasesForTest(); // Because the fixed element does not scroll, the interest rect is unchanged. @@ -1142,7 +1150,7 @@ TEST_F(CompositedLayerMappingTest, const auto* container = ToLayoutBox(GetLayoutObjectByElementId("container")); EXPECT_EQ(kBackgroundPaintInScrollingContents, - container->GetBackgroundPaintLocation()); + container->ComputeBackgroundPaintLocationIfComposited()); // 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 @@ -1150,7 +1158,8 @@ TEST_F(CompositedLayerMappingTest, // this case. const auto* mapping = container->Layer()->GetCompositedLayerMapping(); EXPECT_FALSE(mapping->HasScrollingLayer()); - EXPECT_FALSE(mapping->BackgroundPaintsOntoScrollingContentsLayer()); + EXPECT_EQ(kBackgroundPaintInGraphicsLayer, + container->GetBackgroundPaintLocation()); } TEST_F(CompositedLayerMappingTest, StickyPositionMainThreadOffset) { @@ -1184,7 +1193,8 @@ TEST_F(CompositedLayerMappingTest, StickyPositionMainThreadOffset) { sticky_layer->SetNeedsCompositingInputsUpdate(); EXPECT_TRUE(sticky_layer->NeedsCompositingInputsUpdate()); - GetDocument().View()->UpdateLifecycleToCompositingCleanPlusScrolling(); + GetDocument().View()->UpdateLifecycleToCompositingCleanPlusScrolling( + DocumentUpdateReason::kTest); EXPECT_FALSE(sticky_layer->NeedsCompositingInputsUpdate()); } @@ -1412,22 +1422,28 @@ TEST_F(CompositedLayerMappingTest, ScrollingContainerBoundsChange) { PaintLayerScrollableArea* scrollable_area = scroller->GetScrollableArea(); cc::Layer* scrolling_layer = scrollable_area->LayerForScrolling(); - EXPECT_EQ(0, scrolling_layer->CurrentScrollOffset().y()); + auto element_id = scrollable_area->GetScrollElementId(); + auto& scroll_tree = + scrolling_layer->layer_tree_host()->property_trees()->scroll_tree; + EXPECT_EQ(0, scroll_tree.current_scroll_offset(element_id).y()); EXPECT_EQ(150, scrolling_layer->bounds().height()); - EXPECT_EQ(100, scrolling_layer->scroll_container_bounds().height()); + auto* scroll_node = scroll_tree.FindNodeFromElementId(element_id); + EXPECT_EQ(100, scroll_node->container_bounds.height()); scrollerElement->setScrollTop(300); scrollerElement->setAttribute(html_names::kStyleAttr, "max-height: 25px;"); UpdateAllLifecyclePhasesForTest(); - EXPECT_EQ(50, scrolling_layer->CurrentScrollOffset().y()); + EXPECT_EQ(50, scroll_tree.current_scroll_offset(element_id).y()); EXPECT_EQ(150, scrolling_layer->bounds().height()); - EXPECT_EQ(25, scrolling_layer->scroll_container_bounds().height()); + scroll_node = scroll_tree.FindNodeFromElementId(element_id); + EXPECT_EQ(25, scroll_node->container_bounds.height()); scrollerElement->setAttribute(html_names::kStyleAttr, "max-height: 300px;"); UpdateAllLifecyclePhasesForTest(); - EXPECT_EQ(50, scrolling_layer->CurrentScrollOffset().y()); + EXPECT_EQ(50, scroll_tree.current_scroll_offset(element_id).y()); EXPECT_EQ(150, scrolling_layer->bounds().height()); - EXPECT_EQ(100, scrolling_layer->scroll_container_bounds().height()); + scroll_node = scroll_tree.FindNodeFromElementId(element_id); + EXPECT_EQ(100, scroll_node->container_bounds.height()); } TEST_F(CompositedLayerMappingTest, MainFrameLayerBackgroundColor) { @@ -1515,27 +1531,6 @@ TEST_F(CompositedLayerMappingTest, ScrollLayerSizingSubpixelAccumulation) { mapping->ScrollingContentsLayer()->Size().height()); } -TEST_F(CompositedLayerMappingTest, SquashingScroll) { - SetHtmlInnerHTML(R"HTML( - <style> - * { margin: 0 } - </style> - <div id=target - style='width: 200px; height: 200px; position: relative; will-change: transform'></div> - <div id=squashed - style='width: 200px; height: 200px; top: -200px; position: relative;'></div> - <div style='width: 10px; height: 3000px'></div> - )HTML"); - - auto* squashed = - ToLayoutBoxModelObject(GetLayoutObjectByElementId("squashed"))->Layer(); - EXPECT_EQ(kPaintsIntoGroupedBacking, squashed->GetCompositingState()); - - GetDocument().View()->LayoutViewport()->ScrollBy(ScrollOffset(0, 25), - kUserScroll); - UpdateAllLifecyclePhasesForTest(); -} - TEST_F(CompositedLayerMappingTest, SquashingScrollInterestRect) { SetHtmlInnerHTML(R"HTML( <style> @@ -1551,8 +1546,8 @@ TEST_F(CompositedLayerMappingTest, SquashingScrollInterestRect) { ToLayoutBoxModelObject(GetLayoutObjectByElementId("squashed"))->Layer(); EXPECT_EQ(kPaintsIntoGroupedBacking, squashed->GetCompositingState()); - GetDocument().View()->LayoutViewport()->ScrollBy(ScrollOffset(0, 5000), - kUserScroll); + GetDocument().View()->LayoutViewport()->ScrollBy( + ScrollOffset(0, 5000), mojom::blink::ScrollType::kUser); UpdateAllLifecyclePhasesForTest(); EXPECT_EQ(IntRect(0, 1000, 200, 5000), @@ -1776,13 +1771,101 @@ TEST_F(CompositedLayerMappingTest, GetDocument() .getElementById("uncorrelated") ->setAttribute(html_names::kStyleAttr, "width: 200px"); - GetDocument().View()->UpdateAllLifecyclePhasesExceptPaint(); + GetDocument().View()->UpdateAllLifecyclePhasesExceptPaint( + DocumentUpdateReason::kTest); EXPECT_FALSE(mapping->NeedsRepaint(*vertical_scrollbar_layer)); GetDocument().getElementById("child")->setAttribute(html_names::kStyleAttr, - "height: 300px"); - GetDocument().View()->UpdateAllLifecyclePhasesExceptPaint(); + "height: 50px"); + GetDocument().View()->UpdateAllLifecyclePhasesExceptPaint( + DocumentUpdateReason::kTest); EXPECT_TRUE(mapping->NeedsRepaint(*vertical_scrollbar_layer)); } +TEST_F(CompositedLayerMappingTest, IsolationClippingContainer) { + SetBodyInnerHTML(R"HTML( + <style> + #hideable { + overflow: hidden; + height: 10px; + } + .isolation { + contain: style layout; + height: 100px; + } + .squash-container { + will-change: transform; + } + .squashed { + position: absolute; + top: 0; + left: 0; + width: 100px; + height: 100px; + } + </style> + <div id="hideable"> + <div class="isolation" id="isolation_a"> + <div class="squash-container" id="squash_container_a">a</div> + <div class="squashed"></div> + </div> + <div class="isolation"> + <div class="squash-container">b</div> + <div class="squashed"></div> + </div> + </div> + )HTML"); + + Element* hideable = GetDocument().getElementById("hideable"); + hideable->SetInlineStyleProperty(CSSPropertyID::kOverflow, "visible"); + + UpdateAllLifecyclePhasesForTest(); + + auto* isolation_a = GetDocument().getElementById("isolation_a"); + auto* isolation_a_object = isolation_a->GetLayoutObject(); + + auto* squash_container_a = GetDocument().getElementById("squash_container_a"); + PaintLayer* squash_container_a_layer = + ToLayoutBoxModelObject(squash_container_a->GetLayoutObject())->Layer(); + EXPECT_EQ(squash_container_a_layer->ClippingContainer(), isolation_a_object); +} + +TEST_F(CompositedLayerMappingTest, FrameAttribution) { + SetBodyInnerHTML(R"HTML( + <div id='child' style='will-change: transform;'></div> + <iframe id='subframe' style='will-change: transform;'></iframe> + )HTML"); + + UpdateAllLifecyclePhasesForTest(); + + // Ensure that we correctly attribute child layers in the main frame to their + // containing document. + Element* child = GetDocument().getElementById("child"); + PaintLayer* child_paint_layer = + ToLayoutBoxModelObject(child->GetLayoutObject())->Layer(); + auto* child_layer = child_paint_layer->GraphicsLayerBacking()->CcLayer(); + EXPECT_TRUE(child_layer->frame_element_id()); + + EXPECT_EQ(child_layer->frame_element_id(), + CompositorElementIdFromUniqueObjectId( + DOMNodeIds::IdForNode(&GetDocument()), + CompositorElementIdNamespace::kDOMNodeId)); + + // Test that a layerized subframe's element ID is that of its containing + // document. + auto* subframe = + To<HTMLFrameOwnerElement>(GetDocument().getElementById("subframe")); + EXPECT_TRUE(subframe); + PaintLayer* subframe_paint_layer = + ToLayoutBoxModelObject(subframe->GetLayoutObject())->Layer(); + auto* subframe_layer = + subframe_paint_layer->GraphicsLayerBacking()->CcLayer(); + EXPECT_TRUE(subframe_layer->frame_element_id()); + + EXPECT_EQ(subframe_layer->frame_element_id(), + CompositorElementIdFromUniqueObjectId( + DOMNodeIds::IdForNode(subframe->contentDocument()), + CompositorElementIdNamespace::kDOMNodeId)); +} + } // 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 65440c163bd..d745fb4a8fa 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 @@ -4,6 +4,7 @@ #include "third_party/blink/renderer/core/paint/compositing/compositing_inputs_updater.h" +#include "third_party/blink/renderer/core/display_lock/display_lock_utilities.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/layout/layout_block.h" @@ -48,6 +49,13 @@ void CompositingInputsUpdater::Update() { UpdateType update_type = kDoNotForceUpdate; PaintLayer* layer = compositing_inputs_root_ ? compositing_inputs_root_ : root_layer_; + + if (DisplayLockUtilities::NearestLockedExclusiveAncestor( + layer->GetLayoutObject())) { + compositing_inputs_root_ = nullptr; + return; + } + CompositingReasons initial_compositing_reasons = layer->DirectCompositingReasons(); ApplyAncestorInfoToSelfAndAncestorsRecursively(layer, update_type, info); @@ -219,6 +227,22 @@ void CompositingInputsUpdater::UpdateAncestorInfo(PaintLayer* const layer, info.enclosing_stacking_composited_layer; PaintLayer* enclosing_squashing_composited_layer = info.enclosing_squashing_composited_layer; + + if (layer->NeedsCompositingInputsUpdate()) { + if (enclosing_stacking_composited_layer) { + enclosing_stacking_composited_layer->GetCompositedLayerMapping() + ->SetNeedsGraphicsLayerUpdate(kGraphicsLayerUpdateSubtree); + } + + if (enclosing_squashing_composited_layer) { + enclosing_squashing_composited_layer->GetCompositedLayerMapping() + ->SetNeedsGraphicsLayerUpdate(kGraphicsLayerUpdateSubtree); + } + + update_type = kForceUpdate; + } + + switch (layer->GetCompositingState()) { case kNotComposited: break; @@ -232,17 +256,19 @@ void CompositingInputsUpdater::UpdateAncestorInfo(PaintLayer* const layer, break; } + // invalidate again after the switch, in case + // enclosing_stacking_composited_layer or + // enclosing_squashing_composited_layer was previously null. if (layer->NeedsCompositingInputsUpdate()) { if (enclosing_stacking_composited_layer) { enclosing_stacking_composited_layer->GetCompositedLayerMapping() ->SetNeedsGraphicsLayerUpdate(kGraphicsLayerUpdateSubtree); } + if (enclosing_squashing_composited_layer) { enclosing_squashing_composited_layer->GetCompositedLayerMapping() ->SetNeedsGraphicsLayerUpdate(kGraphicsLayerUpdateSubtree); } - - update_type = kForceUpdate; } if (style.GetPosition() == EPosition::kAbsolute) { @@ -287,13 +313,13 @@ void CompositingInputsUpdater::UpdateAncestorInfo(PaintLayer* const layer, // in the sense that they don't scroll along with its in-flow contents. // However LayoutView does clip them. if (layout_object.CanContainFixedPositionObjects() && - !layout_object.IsLayoutView()) { + !IsA<LayoutView>(layout_object)) { info.clip_chain_parent_for_fixed = layer; info.escape_clip_to_for_fixed = info.escape_clip_to; info.scrolling_ancestor_for_fixed = info.scrolling_ancestor; info.needs_reparent_scroll_for_fixed = info.needs_reparent_scroll; } - if (layout_object.IsLayoutView()) + if (IsA<LayoutView>(layout_object)) info.clip_chain_parent_for_fixed = layer; // CSS clip affects all descendants, not just containing-block descendants. 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 3d43a923ee3..4f66645c2eb 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 @@ -69,7 +69,8 @@ TEST_F(CompositingInputsUpdaterTest, // Before we update compositing inputs, validate that the current ancestor // overflow no longer has a scrollable area. - GetDocument().View()->UpdateLifecycleToLayoutClean(); + GetDocument().View()->UpdateLifecycleToLayoutClean( + DocumentUpdateReason::kTest); EXPECT_FALSE(sticky->Layer()->AncestorOverflowLayer()->GetScrollableArea()); EXPECT_EQ(sticky->Layer()->AncestorOverflowLayer(), outer_scroller->Layer()); @@ -99,8 +100,8 @@ TEST_F(CompositingInputsUpdaterTest, UnclippedAndClippedRectsUnderScroll) { LayoutBoxModelObject* target = ToLayoutBoxModelObject(GetLayoutObjectByElementId("target")); - GetDocument().View()->LayoutViewport()->ScrollBy(ScrollOffset(0, 25), - kUserScroll); + GetDocument().View()->LayoutViewport()->ScrollBy( + ScrollOffset(0, 25), mojom::blink::ScrollType::kUser); GetDocument() .View() ->GetLayoutView() @@ -125,8 +126,8 @@ TEST_F(CompositingInputsUpdaterTest, LayoutBoxModelObject* target = ToLayoutBoxModelObject(GetLayoutObjectByElementId("target")); - GetDocument().View()->LayoutViewport()->ScrollBy(ScrollOffset(0, 25), - kUserScroll); + GetDocument().View()->LayoutViewport()->ScrollBy( + ScrollOffset(0, 25), mojom::blink::ScrollType::kUser); GetDocument() .View() ->GetLayoutView() 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 d31bd3e8a32..81eb11a2ed2 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 @@ -28,6 +28,7 @@ #include "third_party/blink/renderer/core/animation/scroll_timeline.h" #include "third_party/blink/renderer/core/animation/worklet_animation_controller.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/page/page.h" #include "third_party/blink/renderer/core/page/scrolling/scrolling_coordinator.h" @@ -45,8 +46,6 @@ CompositingLayerAssigner::CompositingLayerAssigner( PaintLayerCompositor* compositor) : compositor_(compositor), layers_changed_(false) {} -CompositingLayerAssigner::~CompositingLayerAssigner() = default; - void CompositingLayerAssigner::Assign( PaintLayer* update_root, Vector<PaintLayer*>& layers_needing_paint_invalidation) { @@ -135,8 +134,8 @@ CompositingLayerAssigner::GetReasonsPreventingSquashing( const PaintLayer& squashing_layer = squashing_state.most_recent_mapping->OwningLayer(); - if (layer->GetLayoutObject().IsVideo() || - squashing_layer.GetLayoutObject().IsVideo()) + if (IsA<LayoutVideo>(layer->GetLayoutObject()) || + IsA<LayoutVideo>(squashing_layer.GetLayoutObject())) return SquashingDisallowedReason::kSquashingVideoIsDisallowed; // Don't squash iframes, frames or plugins. @@ -164,9 +163,6 @@ CompositingLayerAssigner::GetReasonsPreventingSquashing( if (layer->ScrollsWithRespectTo(&squashing_layer)) return SquashingDisallowedReason::kScrollsWithRespectToSquashingLayer; - if (layer->ScrollParent() && layer->HasCompositingDescendant()) - return SquashingDisallowedReason::kScrollChildWithCompositedDescendants; - if (layer->OpacityAncestor() != squashing_layer.OpacityAncestor()) return SquashingDisallowedReason::kOpacityAncestorMismatch; @@ -292,15 +288,6 @@ void CompositingLayerAssigner::AssignLayersToBackingsInternal( layer, composited_layer_update)) { layers_needing_paint_invalidation.push_back(layer); layers_changed_ = true; - if (ScrollingCoordinator* scrolling_coordinator = - layer->GetScrollingCoordinator()) { - if (layer->GetLayoutObject() - .StyleRef() - .HasViewportConstrainedPosition()) { - scrolling_coordinator->FrameViewFixedObjectsDidChange( - layer->GetLayoutObject().View()->GetFrameView()); - } - } } if (composited_layer_update != kNoCompositingStateChange) { diff --git a/chromium/third_party/blink/renderer/core/paint/compositing/compositing_layer_assigner.h b/chromium/third_party/blink/renderer/core/paint/compositing/compositing_layer_assigner.h index e4e34b370fd..557f55726ee 100644 --- a/chromium/third_party/blink/renderer/core/paint/compositing/compositing_layer_assigner.h +++ b/chromium/third_party/blink/renderer/core/paint/compositing/compositing_layer_assigner.h @@ -43,7 +43,6 @@ class CompositingLayerAssigner { public: explicit CompositingLayerAssigner(PaintLayerCompositor*); - ~CompositingLayerAssigner(); void Assign(PaintLayer* update_root, Vector<PaintLayer*>& layers_needing_paint_invalidation); 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 0ee97e10d65..3570a6d184e 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 @@ -4,22 +4,20 @@ #include "third_party/blink/renderer/core/paint/compositing/compositing_reason_finder.h" -#include "base/feature_list.h" -#include "third_party/blink/public/common/features.h" #include "third_party/blink/renderer/core/css/css_property_names.h" #include "third_party/blink/renderer/core/dom/document.h" #include "third_party/blink/renderer/core/dom/node.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/fullscreen/fullscreen.h" -#include "third_party/blink/renderer/core/html/html_frame_owner_element.h" +#include "third_party/blink/renderer/core/html/html_body_element.h" +#include "third_party/blink/renderer/core/layout/layout_embedded_content.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/page/page.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/public/common/features.h" - namespace blink { CompositingReasons CompositingReasonFinder::DirectReasons( @@ -70,7 +68,7 @@ CompositingReasonFinder::PotentialCompositingReasonsFromStyle( if (style.BackfaceVisibility() == EBackfaceVisibility::kHidden) reasons |= CompositingReason::kBackfaceVisibilityHidden; - reasons |= CompositingReasonsForAnimation(style); + reasons |= CompositingReasonsForAnimation(layout_object); reasons |= CompositingReasonsForWillChange(style); // If the implementation of CreatesGroup changes, we need to be aware of that @@ -106,6 +104,26 @@ CompositingReasonFinder::PotentialCompositingReasonsFromStyle( return reasons; } +static bool ShouldPreferCompositingForLayoutView( + const LayoutView& layout_view) { + auto has_direct_compositing_reasons = [](const LayoutObject* object) -> bool { + return object && CompositingReasonFinder::DirectReasonsForPaintProperties( + *object) != CompositingReason::kNone; + }; + if (has_direct_compositing_reasons( + layout_view.GetFrame()->OwnerLayoutObject())) + return true; + if (auto* document_element = layout_view.GetDocument().documentElement()) { + if (has_direct_compositing_reasons(document_element->GetLayoutObject())) + return true; + } + if (auto* body = layout_view.GetDocument().FirstBodyElement()) { + if (has_direct_compositing_reasons(body->GetLayoutObject())) + return true; + } + return false; +} + CompositingReasons CompositingReasonFinder::DirectReasonsForPaintProperties( const LayoutObject& object) { // TODO(wangxianzhu): Don't depend on PaintLayer for CompositeAfterPaint. @@ -113,7 +131,7 @@ CompositingReasons CompositingReasonFinder::DirectReasonsForPaintProperties( return CompositingReason::kNone; const ComputedStyle& style = object.StyleRef(); - auto reasons = CompositingReasonsForAnimation(style) | + auto reasons = CompositingReasonsForAnimation(object) | CompositingReasonsForWillChange(style); if (RequiresCompositingFor3DTransform(object)) @@ -141,7 +159,15 @@ CompositingReasons CompositingReasonFinder::DirectReasonsForPaintProperties( if (auto* scrollable_area = layer->GetScrollableArea()) { if (RuntimeEnabledFeatures::CompositeAfterPaintEnabled()) { bool force_prefer_compositing_to_lcd_text = - reasons != CompositingReason::kNone; + reasons != CompositingReason::kNone || + // In CompositeAfterPaint though we don't treat hidden backface as + // a direct compositing reason, it's very likely that the object will + // be composited, and it also indicates preference of compositing, + // so we prefer composited scrolling here. + style.BackfaceVisibility() == EBackfaceVisibility::kHidden || + (object.IsLayoutView() && + ShouldPreferCompositingForLayoutView(To<LayoutView>(object))); + if (scrollable_area->ComputeNeedsCompositedScrolling( force_prefer_compositing_to_lcd_text)) { reasons |= CompositingReason::kOverflowScrolling; @@ -153,6 +179,9 @@ CompositingReasons CompositingReasonFinder::DirectReasonsForPaintProperties( } } + if (object.CanHaveAdditionalCompositingReasons()) + reasons |= object.AdditionalCompositingReasons(); + return reasons; } @@ -165,10 +194,8 @@ bool CompositingReasonFinder::RequiresCompositingFor3DTransform( return false; // Don't composite "trivial" 3D transforms such as translateZ(0). - if (Platform::Current()->IsLowEndDevice() || - base::FeatureList::IsEnabled(blink::features::kDoNotCompositeTrivial3D)) { + if (Platform::Current()->IsLowEndDevice()) return layout_object.StyleRef().HasNonTrivial3DTransformOperation(); - } return layout_object.StyleRef().Has3DTransformOperation(); } @@ -202,17 +229,16 @@ CompositingReasons CompositingReasonFinder::NonStyleDeterminedDirectReasons( // into. These children (the controls) always need to be promoted into their // own layers to draw on top of the accelerated video. if (layer.CompositingContainer() && - layer.CompositingContainer()->GetLayoutObject().IsVideo()) + IsA<LayoutVideo>(layer.CompositingContainer()->GetLayoutObject())) direct_reasons |= CompositingReason::kVideoOverlay; - const Node* node = layer.GetLayoutObject().GetNode(); - // Special case for immersive-ar DOM overlay mode, see also - // PaintLayerCompositor::ApplyXrImmersiveDomOverlayIfNeeded() - if (node && node->IsElementNode() && - node->GetDocument().IsImmersiveArOverlay() && - node == Fullscreen::FullscreenElementFrom(node->GetDocument())) { - direct_reasons |= CompositingReason::kImmersiveArOverlay; + // PaintLayerCompositor::GetXrOverlayLayer() + if (const Node* node = layer.GetLayoutObject().GetNode()) { + if (node->IsElementNode() && node->GetDocument().IsXrOverlay() && + node == Fullscreen::FullscreenElementFrom(node->GetDocument())) { + direct_reasons |= CompositingReason::kXrOverlay; + } } if (layer.IsRootLayer() && @@ -221,20 +247,8 @@ CompositingReasons CompositingReasonFinder::NonStyleDeterminedDirectReasons( direct_reasons |= CompositingReason::kRoot; } - // Composite all cross-origin iframes, to improve compositor hit testing for - // input event targeting. crbug.com/1014273 - if (node && node->IsFrameOwnerElement() && - base::FeatureList::IsEnabled( - blink::features::kCompositeCrossOriginIframes)) { - if (Frame* iframe_frame = To<HTMLFrameOwnerElement>(node)->ContentFrame()) { - if (!iframe_frame->GetSecurityContext()->GetSecurityOrigin()->CanAccess( - node->GetDocument().GetSecurityOrigin())) { - direct_reasons |= CompositingReason::kCrossOriginIframe; - } - } - } - - direct_reasons |= layout_object.AdditionalCompositingReasons(); + if (layout_object.CanHaveAdditionalCompositingReasons()) + direct_reasons |= layout_object.AdditionalCompositingReasons(); DCHECK( !(direct_reasons & CompositingReason::kComboAllStyleDeterminedReasons)); @@ -242,12 +256,15 @@ CompositingReasons CompositingReasonFinder::NonStyleDeterminedDirectReasons( } CompositingReasons CompositingReasonFinder::CompositingReasonsForAnimation( - const ComputedStyle& style) { + const LayoutObject& object) { CompositingReasons reasons = CompositingReason::kNone; + const auto& style = object.StyleRef(); if (style.SubtreeWillChangeContents()) return reasons; - if (style.HasCurrentTransformAnimation()) + // Transforms don't apply on non-replaced inline elements. + // TODO(crbug.com/666244): Support composited transform animation for SVG. + if (object.IsBox() && style.HasCurrentTransformAnimation()) reasons |= CompositingReason::kActiveTransformAnimation; if (style.HasCurrentOpacityAnimation()) reasons |= CompositingReason::kActiveOpacityAnimation; diff --git a/chromium/third_party/blink/renderer/core/paint/compositing/compositing_reason_finder.h b/chromium/third_party/blink/renderer/core/paint/compositing/compositing_reason_finder.h index 11e4288a051..212d9ef4c33 100644 --- a/chromium/third_party/blink/renderer/core/paint/compositing/compositing_reason_finder.h +++ b/chromium/third_party/blink/renderer/core/paint/compositing/compositing_reason_finder.h @@ -35,8 +35,7 @@ class CORE_EXPORT CompositingReasonFinder { const LayoutObject&); static bool RequiresCompositingForScrollableFrame(const LayoutView&); - static CompositingReasons CompositingReasonsForAnimation( - const ComputedStyle&); + static CompositingReasons CompositingReasonsForAnimation(const LayoutObject&); static CompositingReasons CompositingReasonsForWillChange( const ComputedStyle&); static bool RequiresCompositingFor3DTransform(const LayoutObject&); 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 670ba475352..ec556f3f98d 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 @@ -23,11 +23,13 @@ class CompositingReasonFinderTest : public RenderingTest { CompositingReasonFinderTest() : RenderingTest(MakeGarbageCollected<SingleChildLocalFrameClient>()) {} - private: + protected: void SetUp() override { EnableCompositing(); RenderingTest::SetUp(); } + + void CheckCompositingReasonsForAnimation(bool supports_transform_animation); }; TEST_F(CompositingReasonFinderTest, CompositingReasonDependencies) { @@ -40,42 +42,7 @@ TEST_F(CompositingReasonFinderTest, CompositingReasonDependencies) { CompositingReason::kComboAllStyleDeterminedReasons); } -class CompositingReasonFinderTestWithDoNotCompositeTrivial3D - : public CompositingReasonFinderTest { - public: - CompositingReasonFinderTestWithDoNotCompositeTrivial3D() { - scoped_feature_list_.InitAndEnableFeature( - blink::features::kDoNotCompositeTrivial3D); - } - - base::test::ScopedFeatureList scoped_feature_list_; -}; - -TEST_F(CompositingReasonFinderTestWithDoNotCompositeTrivial3D, - DontPromoteTrivial3D) { - SetBodyInnerHTML(R"HTML( - <div id='target' - style='width: 100px; height: 100px; transform: translateZ(0)'></div> - )HTML"); - - Element* target = GetDocument().getElementById("target"); - PaintLayer* paint_layer = - ToLayoutBoxModelObject(target->GetLayoutObject())->Layer(); - EXPECT_EQ(kNotComposited, paint_layer->GetCompositingState()); -} - -class CompositingReasonFinderTestWithCompositeTrivial3D - : public CompositingReasonFinderTest { - public: - CompositingReasonFinderTestWithCompositeTrivial3D() { - scoped_feature_list_.InitAndDisableFeature( - blink::features::kDoNotCompositeTrivial3D); - } - - base::test::ScopedFeatureList scoped_feature_list_; -}; - -TEST_F(CompositingReasonFinderTestWithCompositeTrivial3D, PromoteTrivial3D) { +TEST_F(CompositingReasonFinderTest, PromoteTrivial3D) { SetBodyInnerHTML(R"HTML( <div id='target' style='width: 100px; height: 100px; transform: translateZ(0)'></div> @@ -142,6 +109,42 @@ TEST_F(CompositingReasonFinderTest, OnlyAnchoredStickyPositionPromoted) { ->GetCompositingState()); } +TEST_F(CompositingReasonFinderTest, + OnlyAnchoredStickyPositionPromotedAssumeOverlap) { + base::test::ScopedFeatureList feature_list; + feature_list.InitWithFeatureState( + blink::features::kAssumeOverlapAfterFixedOrStickyPosition, true); + SetBodyInnerHTML(R"HTML( + <style> + .scroller {contain: paint; width: 400px; height: 400px; overflow: auto; + will-change: transform;} + .sticky { position: sticky; width: 10px; height: 10px;}</style> + <div class='scroller'> + <div id='sticky-top' class='sticky' style='top: 0px;'></div> + <div id='sticky-no-anchor' class='sticky'></div> + <div style='height: 2000px;'></div> + </div> + )HTML"); + + EXPECT_EQ(kPaintsIntoOwnBacking, + ToLayoutBoxModelObject(GetLayoutObjectByElementId("sticky-top")) + ->Layer() + ->GetCompositingState()); + // Any scroll dependent layer, such as sticky-top, assumes that it overlaps + // anything which draws after it. + EXPECT_EQ( + kPaintsIntoOwnBacking, + ToLayoutBoxModelObject(GetLayoutObjectByElementId("sticky-no-anchor")) + ->Layer() + ->GetCompositingState()); + EXPECT_EQ( + CompositingReason::kAssumedOverlap, + ToLayoutBoxModelObject(GetLayoutObjectByElementId("sticky-no-anchor")) + ->Layer() + ->GetCompositingReasons() & + CompositingReason::kAssumedOverlap); +} + TEST_F(CompositingReasonFinderTest, OnlyScrollingStickyPositionPromoted) { SetBodyInnerHTML(R"HTML( <style>.scroller {width: 400px; height: 400px; overflow: auto; @@ -169,7 +172,9 @@ TEST_F(CompositingReasonFinderTest, OnlyScrollingStickyPositionPromoted) { ->GetCompositingState()); } -TEST_F(CompositingReasonFinderTest, CompositingReasonsForAnimation) { +void CompositingReasonFinderTest::CheckCompositingReasonsForAnimation( + bool supports_transform_animation) { + auto* object = GetLayoutObjectByElementId("target"); scoped_refptr<ComputedStyle> style = ComputedStyle::Create(); style->SetSubtreeWillChangeContents(false); @@ -177,32 +182,47 @@ TEST_F(CompositingReasonFinderTest, CompositingReasonsForAnimation) { style->SetHasCurrentOpacityAnimation(false); style->SetHasCurrentFilterAnimation(false); style->SetHasCurrentBackdropFilterAnimation(false); + object->SetStyle(style); + EXPECT_EQ(CompositingReason::kNone, - CompositingReasonFinder::CompositingReasonsForAnimation(*style)); + CompositingReasonFinder::CompositingReasonsForAnimation(*object)); + + CompositingReasons expected_compositing_reason_for_transform_animation = + supports_transform_animation + ? CompositingReason::kActiveTransformAnimation + : CompositingReason::kNone; style->SetHasCurrentTransformAnimation(true); - EXPECT_EQ(CompositingReason::kActiveTransformAnimation, - CompositingReasonFinder::CompositingReasonsForAnimation(*style)); + EXPECT_EQ(expected_compositing_reason_for_transform_animation, + CompositingReasonFinder::CompositingReasonsForAnimation(*object)); style->SetHasCurrentOpacityAnimation(true); - EXPECT_EQ(CompositingReason::kActiveTransformAnimation | + EXPECT_EQ(expected_compositing_reason_for_transform_animation | CompositingReason::kActiveOpacityAnimation, - CompositingReasonFinder::CompositingReasonsForAnimation(*style)); + CompositingReasonFinder::CompositingReasonsForAnimation(*object)); style->SetHasCurrentFilterAnimation(true); - EXPECT_EQ(CompositingReason::kActiveTransformAnimation | + EXPECT_EQ(expected_compositing_reason_for_transform_animation | CompositingReason::kActiveOpacityAnimation | CompositingReason::kActiveFilterAnimation, - CompositingReasonFinder::CompositingReasonsForAnimation(*style)); + CompositingReasonFinder::CompositingReasonsForAnimation(*object)); style->SetHasCurrentBackdropFilterAnimation(true); - EXPECT_EQ(CompositingReason::kActiveTransformAnimation | + EXPECT_EQ(expected_compositing_reason_for_transform_animation | CompositingReason::kActiveOpacityAnimation | CompositingReason::kActiveFilterAnimation | CompositingReason::kActiveBackdropFilterAnimation, - CompositingReasonFinder::CompositingReasonsForAnimation(*style)); - EXPECT_EQ(CompositingReason::kComboActiveAnimation, - CompositingReasonFinder::CompositingReasonsForAnimation(*style)); + CompositingReasonFinder::CompositingReasonsForAnimation(*object)); +} + +TEST_F(CompositingReasonFinderTest, CompositingReasonsForAnimationBox) { + SetBodyInnerHTML("<div id='target'>Target</div>"); + CheckCompositingReasonsForAnimation(/*supports_transform_animation*/ true); +} + +TEST_F(CompositingReasonFinderTest, CompositingReasonsForAnimationInline) { + SetBodyInnerHTML("<span id='target'>Target</span>"); + CheckCompositingReasonsForAnimation(/*supports_transform_animation*/ false); } TEST_F(CompositingReasonFinderTest, DontPromoteEmptyIframe) { @@ -241,7 +261,7 @@ TEST_F(CompositingReasonFinderTest, PromoteCrossOriginIframe) { ASSERT_TRUE(iframe_layer); ASSERT_FALSE(To<HTMLFrameOwnerElement>(iframe) ->ContentFrame() - ->IsCrossOriginSubframe()); + ->IsCrossOriginToMainFrame()); EXPECT_EQ(kNotComposited, iframe_layer->DirectCompositingReasons()); SetBodyInnerHTML(R"HTML( @@ -256,8 +276,8 @@ TEST_F(CompositingReasonFinderTest, PromoteCrossOriginIframe) { ASSERT_TRUE(iframe_layer); ASSERT_TRUE(To<HTMLFrameOwnerElement>(iframe) ->ContentFrame() - ->IsCrossOriginSubframe()); - EXPECT_EQ(CompositingReason::kCrossOriginIframe, + ->IsCrossOriginToMainFrame()); + EXPECT_EQ(CompositingReason::kIFrame, iframe_layer->DirectCompositingReasons()); } 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 966c367af23..0b9f70b8b36 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 @@ -27,6 +27,7 @@ #include "third_party/blink/renderer/core/paint/compositing/compositing_requirements_updater.h" #include "base/macros.h" +#include "third_party/blink/public/common/features.h" #include "third_party/blink/renderer/core/layout/layout_embedded_content.h" #include "third_party/blink/renderer/core/layout/layout_view.h" #include "third_party/blink/renderer/core/paint/compositing/paint_layer_compositor.h" @@ -209,8 +210,6 @@ CompositingRequirementsUpdater::CompositingRequirementsUpdater( LayoutView& layout_view) : layout_view_(layout_view) {} -CompositingRequirementsUpdater::~CompositingRequirementsUpdater() = default; - void CompositingRequirementsUpdater::Update( PaintLayer* root, CompositingReasonsStats& compositing_reasons_stats) { @@ -597,7 +596,10 @@ void CompositingRequirementsUpdater::UpdateRecursive( CompositingReason::kClipsCompositingDescendants); if ((!child_recursion_data.testing_overlap_ && !is_composited_clipping_layer) || - layer->GetLayoutObject().StyleRef().HasCurrentTransformAnimation()) + layer->GetLayoutObject().StyleRef().HasCurrentTransformAnimation() || + ((direct_reasons & CompositingReason::kScrollDependentPosition) && + base::FeatureList::IsEnabled( + features::kAssumeOverlapAfterFixedOrStickyPosition))) 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.h b/chromium/third_party/blink/renderer/core/paint/compositing/compositing_requirements_updater.h index dcb9464a6d9..6f1d051d9da 100644 --- a/chromium/third_party/blink/renderer/core/paint/compositing/compositing_requirements_updater.h +++ b/chromium/third_party/blink/renderer/core/paint/compositing/compositing_requirements_updater.h @@ -42,7 +42,6 @@ class CompositingRequirementsUpdater { public: CompositingRequirementsUpdater(LayoutView&); - ~CompositingRequirementsUpdater(); // Recurse through the layers in z-index and overflow order (which is // equivalent to painting order) 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 8100621f58d..15bb8e48d76 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 @@ -4,13 +4,12 @@ #include "third_party/blink/renderer/core/paint/compositing/compositing_requirements_updater.h" -#include "base/test/scoped_feature_list.h" #include "testing/gtest/include/gtest/gtest.h" -#include "third_party/blink/public/common/features.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/testing/core_unit_test_helper.h" #include "third_party/blink/renderer/platform/graphics/graphics_layer.h" +#include "third_party/blink/renderer/platform/testing/testing_platform_support.h" namespace blink { @@ -128,19 +127,7 @@ TEST_F(CompositingRequirementsUpdaterTest, EXPECT_EQ(IntRect(0, 0, 100, 100), tracking->Invalidations()[0].rect); } -class CompositingRequirementsUpdaterTestWithDoNotCompositeTrivial3D - : public CompositingRequirementsUpdaterTest { - public: - CompositingRequirementsUpdaterTestWithDoNotCompositeTrivial3D() { - scoped_feature_list_.InitAndEnableFeature( - blink::features::kDoNotCompositeTrivial3D); - } - - base::test::ScopedFeatureList scoped_feature_list_; -}; - -TEST_F(CompositingRequirementsUpdaterTestWithDoNotCompositeTrivial3D, - NonTrivial3DTransforms) { +TEST_F(CompositingRequirementsUpdaterTest, NonTrivial3DTransforms) { ScopedCSSIndependentTransformPropertiesForTest feature_scope(true); SetBodyInnerHTML(R"HTML( @@ -169,7 +156,7 @@ TEST_F(CompositingRequirementsUpdaterTestWithDoNotCompositeTrivial3D, ToLayoutBox(transform_3d)->Layer()->GetCompositingReasons()); const auto* transform_2d = GetLayoutObjectByElementId("2d-transform"); EXPECT_FALSE(transform_2d->StyleRef().HasNonTrivial3DTransformOperation()); - EXPECT_FALSE(ToLayoutBox(transform_2d)->Layer()->GetCompositingReasons()); + EXPECT_TRUE(ToLayoutBox(transform_2d)->Layer()->GetCompositingReasons()); const auto* transform_3d_translate_z = GetLayoutObjectByElementId("3d-transform-translate-z"); @@ -182,7 +169,7 @@ TEST_F(CompositingRequirementsUpdaterTestWithDoNotCompositeTrivial3D, GetLayoutObjectByElementId("2d-transform-translate-z"); EXPECT_FALSE( transform_2d_translate_z->StyleRef().HasNonTrivial3DTransformOperation()); - EXPECT_FALSE( + EXPECT_TRUE( ToLayoutBox(transform_2d_translate_z)->Layer()->GetCompositingReasons()); const auto* transform_2d_translate_x = GetLayoutObjectByElementId("2d-transform-translate-x"); @@ -197,7 +184,7 @@ TEST_F(CompositingRequirementsUpdaterTestWithDoNotCompositeTrivial3D, ToLayoutBox(xform_rot_x_3d)->Layer()->GetCompositingReasons()); const auto* xform_rot_x_2d = GetLayoutObjectByElementId("2d-transform-rot-x"); EXPECT_FALSE(xform_rot_x_2d->StyleRef().HasNonTrivial3DTransformOperation()); - EXPECT_FALSE(ToLayoutBox(xform_rot_x_2d)->Layer()->GetCompositingReasons()); + EXPECT_TRUE(ToLayoutBox(xform_rot_x_2d)->Layer()->GetCompositingReasons()); const auto* xform_rot_z_2d = GetLayoutObjectByElementId("2d-transform-rot-z"); EXPECT_FALSE(xform_rot_z_2d->StyleRef().HasNonTrivial3DTransformOperation()); EXPECT_FALSE(ToLayoutBox(xform_rot_z_2d)->Layer()->GetCompositingReasons()); @@ -208,7 +195,7 @@ TEST_F(CompositingRequirementsUpdaterTestWithDoNotCompositeTrivial3D, ToLayoutBox(rotation_y_3d)->Layer()->GetCompositingReasons()); const auto* rotation_y_2d = GetLayoutObjectByElementId("2d-rotation-y"); EXPECT_FALSE(rotation_y_2d->StyleRef().HasNonTrivial3DTransformOperation()); - EXPECT_FALSE(ToLayoutBox(rotation_y_2d)->Layer()->GetCompositingReasons()); + EXPECT_TRUE(ToLayoutBox(rotation_y_2d)->Layer()->GetCompositingReasons()); const auto* rotation_z_2d = GetLayoutObjectByElementId("2d-rotation-z"); EXPECT_FALSE(rotation_z_2d->StyleRef().HasNonTrivial3DTransformOperation()); EXPECT_FALSE(ToLayoutBox(rotation_z_2d)->Layer()->GetCompositingReasons()); diff --git a/chromium/third_party/blink/renderer/core/paint/compositing/compositing_test.cc b/chromium/third_party/blink/renderer/core/paint/compositing/compositing_test.cc index 55dfee22796..cefe3499d81 100644 --- a/chromium/third_party/blink/renderer/core/paint/compositing/compositing_test.cc +++ b/chromium/third_party/blink/renderer/core/paint/compositing/compositing_test.cc @@ -2,18 +2,22 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +#include "base/test/scoped_feature_list.h" #include "build/build_config.h" #include "cc/layers/picture_layer.h" #include "cc/trees/effect_node.h" #include "cc/trees/layer_tree_host.h" #include "cc/trees/scroll_and_scale_set.h" +#include "cc/trees/scroll_node.h" #include "cc/trees/transform_node.h" #include "testing/gtest/include/gtest/gtest.h" +#include "third_party/blink/public/common/features.h" #include "third_party/blink/public/web/web_script_source.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/frame/web_local_frame_impl.h" #include "third_party/blink/renderer/core/html/html_element.h" +#include "third_party/blink/renderer/core/html/html_iframe_element.h" #include "third_party/blink/renderer/core/layout/layout_box.h" #include "third_party/blink/renderer/core/paint/compositing/composited_layer_mapping.h" #include "third_party/blink/renderer/core/paint/paint_layer.h" @@ -48,9 +52,9 @@ class CompositingTest : public PaintTestConfigurations, public testing::Test { // Both sets the inner html and runs the document lifecycle. void InitializeWithHTML(LocalFrame& frame, const String& html_content) { - frame.GetDocument()->body()->SetInnerHTMLFromString(html_content); + frame.GetDocument()->body()->setInnerHTML(html_content); frame.GetDocument()->View()->UpdateAllLifecyclePhases( - DocumentLifecycle::LifecycleUpdateReason::kTest); + DocumentUpdateReason::kTest); } WebLocalFrame* LocalMainFrame() { return web_view_helper_->LocalMainFrame(); } @@ -66,7 +70,8 @@ class CompositingTest : public PaintTestConfigurations, public testing::Test { } const cc::Layer* CcLayerByDOMElementId(const char* id) { - return CcLayersByDOMElementId(RootCcLayer(), id)[0]; + auto layers = CcLayersByDOMElementId(RootCcLayer(), id); + return layers.IsEmpty() ? nullptr : layers[0]; } cc::LayerTreeHost* LayerTreeHost() { @@ -80,7 +85,7 @@ class CompositingTest : public PaintTestConfigurations, public testing::Test { void UpdateAllLifecyclePhases() { WebView()->MainFrameWidget()->UpdateAllLifecyclePhases( - WebWidget::LifecycleUpdateReason::kTest); + DocumentUpdateReason::kTest); } private: @@ -92,7 +97,7 @@ class CompositingTest : public PaintTestConfigurations, public testing::Test { std::unique_ptr<frame_test_helpers::WebViewHelper> web_view_helper_; }; -INSTANTIATE_LAYER_LIST_TEST_SUITE_P(CompositingTest); +INSTANTIATE_PAINT_TEST_SUITE_P(CompositingTest); TEST_P(CompositingTest, DidScrollCallbackAfterScrollableAreaChanges) { InitializeWithHTML(*WebView()->MainFrameImpl()->GetFrame(), @@ -121,9 +126,13 @@ TEST_P(CompositingTest, DidScrollCallbackAfterScrollableAreaChanges) { CompositorElementId scroll_element_id = scrollable_area->GetScrollElementId(); const auto* overflow_scroll_layer = CcLayerByCcElementId(RootCcLayer(), scroll_element_id); - EXPECT_TRUE(overflow_scroll_layer->scrollable()); - EXPECT_EQ(overflow_scroll_layer->scroll_container_bounds(), - gfx::Size(100, 100)); + const auto* scroll_node = + RootCcLayer() + ->layer_tree_host() + ->property_trees() + ->scroll_tree.FindNodeFromElementId(scroll_element_id); + EXPECT_TRUE(scroll_node->scrollable); + EXPECT_EQ(scroll_node->container_bounds, gfx::Size(100, 100)); // Ensure a synthetic impl-side scroll offset propagates to the scrollable // area using the DidScroll callback. @@ -151,8 +160,10 @@ TEST_P(CompositingTest, DidScrollCallbackAfterScrollableAreaChanges) { // apply impl-side offsets without crashing. ASSERT_EQ(overflow_scroll_layer, CcLayerByCcElementId(RootCcLayer(), scroll_element_id)); - const_cast<cc::Layer*>(overflow_scroll_layer) - ->SetScrollOffsetFromImplSide(gfx::ScrollOffset(0, 3)); + scroll_and_scale_set.scrolls[0] = {scroll_element_id, gfx::ScrollOffset(0, 1), + base::nullopt}; + overflow_scroll_layer->layer_tree_host()->ApplyScrollAndScale( + &scroll_and_scale_set); UpdateAllLifecyclePhases(); EXPECT_FALSE(CcLayerByCcElementId(RootCcLayer(), scroll_element_id)); @@ -173,9 +184,13 @@ TEST_P(CompositingTest, FrameViewScroll) { auto* scrollable_area = GetLocalFrameView()->LayoutViewport(); EXPECT_NE(nullptr, scrollable_area); - const auto* scroll_layer = CcLayerByCcElementId( - RootCcLayer(), scrollable_area->GetScrollElementId()); - EXPECT_TRUE(scroll_layer->scrollable()); + const auto* scroll_node = RootCcLayer() + ->layer_tree_host() + ->property_trees() + ->scroll_tree.FindNodeFromElementId( + scrollable_area->GetScrollElementId()); + ASSERT_TRUE(scroll_node); + EXPECT_TRUE(scroll_node->scrollable); // Ensure a synthetic impl-side scroll offset propagates to the scrollable // area using the DidScroll callback. @@ -184,16 +199,31 @@ TEST_P(CompositingTest, FrameViewScroll) { scroll_and_scale_set.scrolls.push_back({scrollable_area->GetScrollElementId(), gfx::ScrollOffset(0, 1), base::nullopt}); - scroll_layer->layer_tree_host()->ApplyScrollAndScale(&scroll_and_scale_set); + RootCcLayer()->layer_tree_host()->ApplyScrollAndScale(&scroll_and_scale_set); UpdateAllLifecyclePhases(); EXPECT_EQ(ScrollOffset(0, 1), scrollable_area->GetScrollOffset()); } +TEST_P(CompositingTest, WillChangeTransformHint) { + InitializeWithHTML(*WebView()->MainFrameImpl()->GetFrame(), R"HTML( + <style> + #willChange { + width: 100px; + height: 100px; + will-change: transform; + background: blue; + } + </style> + <div id="willChange"></div> + )HTML"); + UpdateAllLifecyclePhases(); + auto* layer = CcLayerByDOMElementId("willChange"); + EXPECT_TRUE(layer->has_will_change_transform_hint()); +} + class CompositingSimTest : public PaintTestConfigurations, public SimTest { public: void InitializeWithHTML(const String& html) { - WebView().MainFrameWidget()->Resize(WebSize(800, 600)); - SimRequest request("https://example.com/test.html", "text/html"); LoadURL("https://example.com/test.html"); request.Complete(html); @@ -206,7 +236,8 @@ class CompositingSimTest : public PaintTestConfigurations, public SimTest { } const cc::Layer* CcLayerByDOMElementId(const char* id) { - return CcLayersByDOMElementId(RootCcLayer(), id)[0]; + auto layers = CcLayersByDOMElementId(RootCcLayer(), id); + return layers.IsEmpty() ? nullptr : layers[0]; } Element* GetElementById(const AtomicString& id) { @@ -215,17 +246,16 @@ class CompositingSimTest : public PaintTestConfigurations, public SimTest { void UpdateAllLifecyclePhases() { WebView().MainFrameWidget()->UpdateAllLifecyclePhases( - WebWidget::LifecycleUpdateReason::kTest); + DocumentUpdateReason::kTest); } void UpdateAllLifecyclePhasesExceptPaint() { - WebView().MainFrameWidget()->UpdateLifecycle( - WebWidget::LifecycleUpdate::kPrePaint, - WebWidget::LifecycleUpdateReason::kTest); + WebView().MainFrameWidget()->UpdateLifecycle(WebLifecycleUpdate::kPrePaint, + DocumentUpdateReason::kTest); } cc::PropertyTrees* GetPropertyTrees() { - return Compositor().layer_tree_host().property_trees(); + return Compositor().layer_tree_host()->property_trees(); } cc::TransformNode* GetTransformNode(const cc::Layer* layer) { @@ -240,17 +270,18 @@ class CompositingSimTest : public PaintTestConfigurations, public SimTest { PaintArtifactCompositor* paint_artifact_compositor() { return MainFrame().GetFrameView()->GetPaintArtifactCompositor(); } + + private: + void SetUp() override { + SimTest::SetUp(); + // Ensure a non-empty size so painting does not early-out. + WebView().Resize(WebSize(800, 600)); + } }; -INSTANTIATE_LAYER_LIST_TEST_SUITE_P(CompositingSimTest); +INSTANTIATE_PAINT_TEST_SUITE_P(CompositingSimTest); TEST_P(CompositingSimTest, LayerUpdatesDoNotInvalidateEarlierLayers) { - // TODO(crbug.com/765003): CAP may make different layerization decisions and - // we cannot guarantee that both divs will be composited in this test. When - // CAP gets closer to launch, this test should be updated to pass. - if (RuntimeEnabledFeatures::CompositeAfterPaintEnabled()) - return; - InitializeWithHTML(R"HTML( <!DOCTYPE html> <style> @@ -259,6 +290,7 @@ TEST_P(CompositingSimTest, LayerUpdatesDoNotInvalidateEarlierLayers) { width: 100px; height: 100px; will-change: transform; + background: lightblue; } </style> <div id='a'></div> @@ -267,19 +299,12 @@ TEST_P(CompositingSimTest, LayerUpdatesDoNotInvalidateEarlierLayers) { Compositor().BeginFrame(); - auto* a_element = GetElementById("a"); auto* a_layer = CcLayerByDOMElementId("a"); - DCHECK_EQ(a_layer->element_id(), CompositorElementIdFromUniqueObjectId( - a_element->GetLayoutObject()->UniqueId(), - CompositorElementIdNamespace::kPrimary)); auto* b_element = GetElementById("b"); auto* b_layer = CcLayerByDOMElementId("b"); - DCHECK_EQ(b_layer->element_id(), CompositorElementIdFromUniqueObjectId( - b_element->GetLayoutObject()->UniqueId(), - CompositorElementIdNamespace::kPrimary)); // Initially, neither a nor b should have a layer that should push properties. - cc::LayerTreeHost& host = Compositor().layer_tree_host(); + cc::LayerTreeHost& host = *Compositor().layer_tree_host(); EXPECT_FALSE(host.LayersThatShouldPushProperties().count(a_layer)); EXPECT_FALSE(host.LayersThatShouldPushProperties().count(b_layer)); @@ -296,12 +321,6 @@ TEST_P(CompositingSimTest, LayerUpdatesDoNotInvalidateEarlierLayers) { } TEST_P(CompositingSimTest, LayerUpdatesDoNotInvalidateLaterLayers) { - // TODO(crbug.com/765003): CAP may make different layerization decisions and - // we cannot guarantee that both divs will be composited in this test. When - // CAP gets closer to launch, this test should be updated to pass. - if (RuntimeEnabledFeatures::CompositeAfterPaintEnabled()) - return; - InitializeWithHTML(R"HTML( <!DOCTYPE html> <style> @@ -310,6 +329,7 @@ TEST_P(CompositingSimTest, LayerUpdatesDoNotInvalidateLaterLayers) { width: 100px; height: 100px; will-change: transform; + background: lightblue; } </style> <div id='a'></div> @@ -321,22 +341,12 @@ TEST_P(CompositingSimTest, LayerUpdatesDoNotInvalidateLaterLayers) { auto* a_element = GetElementById("a"); auto* a_layer = CcLayerByDOMElementId("a"); - DCHECK_EQ(a_layer->element_id(), CompositorElementIdFromUniqueObjectId( - a_element->GetLayoutObject()->UniqueId(), - CompositorElementIdNamespace::kPrimary)); auto* b_element = GetElementById("b"); auto* b_layer = CcLayerByDOMElementId("b"); - DCHECK_EQ(b_layer->element_id(), CompositorElementIdFromUniqueObjectId( - b_element->GetLayoutObject()->UniqueId(), - CompositorElementIdNamespace::kPrimary)); - auto* c_element = GetElementById("c"); auto* c_layer = CcLayerByDOMElementId("c"); - DCHECK_EQ(c_layer->element_id(), CompositorElementIdFromUniqueObjectId( - c_element->GetLayoutObject()->UniqueId(), - CompositorElementIdNamespace::kPrimary)); // Initially, no layer should need to push properties. - cc::LayerTreeHost& host = Compositor().layer_tree_host(); + cc::LayerTreeHost& host = *Compositor().layer_tree_host(); EXPECT_FALSE(host.LayersThatShouldPushProperties().count(a_layer)); EXPECT_FALSE(host.LayersThatShouldPushProperties().count(b_layer)); EXPECT_FALSE(host.LayersThatShouldPushProperties().count(c_layer)); @@ -374,7 +384,7 @@ TEST_P(CompositingSimTest, Compositor().BeginFrame(); // Initially the host should not need to sync. - cc::LayerTreeHost& layer_tree_host = Compositor().layer_tree_host(); + cc::LayerTreeHost& layer_tree_host = *Compositor().layer_tree_host(); EXPECT_FALSE(layer_tree_host.needs_full_tree_sync()); int sequence_number = GetPropertyTrees()->sequence_number; EXPECT_GT(sequence_number, 0); @@ -394,12 +404,6 @@ TEST_P(CompositingSimTest, // non-layer-list mode, this occurs in BuildPropertyTreesInternal (see: // SetLayerPropertyChangedForChild). TEST_P(CompositingSimTest, LayerSubtreeTransformPropertyChanged) { - // TODO(crbug.com/765003): CAP may make different layerization decisions and - // we cannot guarantee that both divs will be composited in this test. When - // CAP gets closer to launch, this test should be updated to pass. - if (RuntimeEnabledFeatures::CompositeAfterPaintEnabled()) - return; - InitializeWithHTML(R"HTML( <!DOCTYPE html> <style> @@ -409,6 +413,7 @@ TEST_P(CompositingSimTest, LayerSubtreeTransformPropertyChanged) { height: 100px; will-change: transform; transform: translate(10px, 10px); + background: lightgreen; } #inner { width: 100px; @@ -426,16 +431,7 @@ TEST_P(CompositingSimTest, LayerSubtreeTransformPropertyChanged) { auto* outer_element = GetElementById("outer"); auto* outer_element_layer = CcLayerByDOMElementId("outer"); - DCHECK_EQ(outer_element_layer->element_id(), - CompositorElementIdFromUniqueObjectId( - outer_element->GetLayoutObject()->UniqueId(), - CompositorElementIdNamespace::kPrimary)); - auto* inner_element = GetElementById("inner"); auto* inner_element_layer = CcLayerByDOMElementId("inner"); - DCHECK_EQ(inner_element_layer->element_id(), - CompositorElementIdFromUniqueObjectId( - inner_element->GetLayoutObject()->UniqueId(), - CompositorElementIdNamespace::kPrimary)); // Initially, no layer should have |subtree_property_changed| set. EXPECT_FALSE(outer_element_layer->subtree_property_changed()); @@ -471,12 +467,6 @@ TEST_P(CompositingSimTest, LayerSubtreeTransformPropertyChanged) { // |transform_changed| set. In non-layer-list mode, this occurs in // cc::TransformTree::OnTransformAnimated and cc::Layer::SetTransform. TEST_P(CompositingSimTest, DirectTransformPropertyUpdate) { - // TODO(crbug.com/765003): CAP may make different layerization decisions and - // we cannot guarantee that both divs will be composited in this test. When - // CAP gets closer to launch, this test should be updated to pass. - if (RuntimeEnabledFeatures::CompositeAfterPaintEnabled()) - return; - InitializeWithHTML(R"HTML( <!DOCTYPE html> <style> @@ -486,6 +476,7 @@ TEST_P(CompositingSimTest, DirectTransformPropertyUpdate) { height: 100px; will-change: transform; transform: translate(10px, 10px) scale(1, 2); + background: lightgreen; } #inner { width: 100px; @@ -503,10 +494,6 @@ TEST_P(CompositingSimTest, DirectTransformPropertyUpdate) { auto* outer_element = GetElementById("outer"); auto* outer_element_layer = CcLayerByDOMElementId("outer"); - DCHECK_EQ(outer_element_layer->element_id(), - CompositorElementIdFromUniqueObjectId( - outer_element->GetLayoutObject()->UniqueId(), - CompositorElementIdNamespace::kPrimary)); auto transform_tree_index = outer_element_layer->transform_tree_index(); auto* transform_node = GetPropertyTrees()->transform_tree.Node(transform_tree_index); @@ -531,12 +518,6 @@ TEST_P(CompositingSimTest, DirectTransformPropertyUpdate) { // the changed value of a directly updated transform is still set if some other // change causes PaintArtifactCompositor to run and do non-direct updates. TEST_P(CompositingSimTest, DirectTransformPropertyUpdateCausesChange) { - // TODO(crbug.com/765003): CAP may make different layerization decisions and - // we cannot guarantee that both divs will be composited in this test. When - // CAP gets closer to launch, this test should be updated to pass. - if (RuntimeEnabledFeatures::CompositeAfterPaintEnabled()) - return; - InitializeWithHTML(R"HTML( <!DOCTYPE html> <style> @@ -546,6 +527,7 @@ TEST_P(CompositingSimTest, DirectTransformPropertyUpdateCausesChange) { height: 100px; will-change: transform; transform: translate(1px, 2px); + background: lightgreen; } #inner { width: 100px; @@ -564,20 +546,12 @@ TEST_P(CompositingSimTest, DirectTransformPropertyUpdateCausesChange) { auto* outer_element = GetElementById("outer"); auto* outer_element_layer = CcLayerByDOMElementId("outer"); - DCHECK_EQ(outer_element_layer->element_id(), - CompositorElementIdFromUniqueObjectId( - outer_element->GetLayoutObject()->UniqueId(), - CompositorElementIdNamespace::kPrimary)); auto outer_transform_tree_index = outer_element_layer->transform_tree_index(); auto* outer_transform_node = GetPropertyTrees()->transform_tree.Node(outer_transform_tree_index); auto* inner_element = GetElementById("inner"); auto* inner_element_layer = CcLayerByDOMElementId("inner"); - DCHECK_EQ(inner_element_layer->element_id(), - CompositorElementIdFromUniqueObjectId( - inner_element->GetLayoutObject()->UniqueId(), - CompositorElementIdNamespace::kPrimary)); auto inner_transform_tree_index = inner_element_layer->transform_tree_index(); auto* inner_transform_node = GetPropertyTrees()->transform_tree.Node(inner_transform_tree_index); @@ -680,12 +654,6 @@ TEST_P(CompositingSimTest, AffectedByOuterViewportBoundsDelta) { // |transform_changed| set. In non-layer-list mode, this occurs in // cc::Layer::SetTransformOrigin. TEST_P(CompositingSimTest, DirectTransformOriginPropertyUpdate) { - // TODO(crbug.com/765003): CAP may make different layerization decisions and - // we cannot guarantee that both divs will be composited in this test. When - // CAP gets closer to launch, this test should be updated to pass. - if (RuntimeEnabledFeatures::CompositeAfterPaintEnabled()) - return; - InitializeWithHTML(R"HTML( <!DOCTYPE html> <style> @@ -695,6 +663,7 @@ TEST_P(CompositingSimTest, DirectTransformOriginPropertyUpdate) { height: 100px; transform: rotate3d(3, 2, 1, 45deg); transform-origin: 10px 10px 100px; + background: lightblue; } </style> <div id='box'></div> @@ -704,10 +673,6 @@ TEST_P(CompositingSimTest, DirectTransformOriginPropertyUpdate) { auto* box_element = GetElementById("box"); auto* box_element_layer = CcLayerByDOMElementId("box"); - DCHECK_EQ(box_element_layer->element_id(), - CompositorElementIdFromUniqueObjectId( - box_element->GetLayoutObject()->UniqueId(), - CompositorElementIdNamespace::kPrimary)); auto transform_tree_index = box_element_layer->transform_tree_index(); auto* transform_node = GetPropertyTrees()->transform_tree.Node(transform_tree_index); @@ -803,12 +768,6 @@ TEST_P(CompositingSimTest, LayerSubtreeEffectPropertyChanged) { // This test is similar to |LayerSubtreeTransformPropertyChanged| but for // clip property node changes. TEST_P(CompositingSimTest, LayerSubtreeClipPropertyChanged) { - // TODO(crbug.com/765003): CAP may make different layerization decisions and - // we cannot guarantee that both divs will be composited in this test. When - // CAP gets closer to launch, this test should be updated to pass. - if (RuntimeEnabledFeatures::CompositeAfterPaintEnabled()) - return; - InitializeWithHTML(R"HTML( <!DOCTYPE html> <style> @@ -819,6 +778,7 @@ TEST_P(CompositingSimTest, LayerSubtreeClipPropertyChanged) { will-change: transform; position: absolute; clip: rect(10px, 80px, 70px, 40px); + background: lightgreen; } #inner { width: 100px; @@ -836,12 +796,7 @@ TEST_P(CompositingSimTest, LayerSubtreeClipPropertyChanged) { auto* outer_element = GetElementById("outer"); auto* outer_element_layer = CcLayerByDOMElementId("outer"); - auto* inner_element = GetElementById("inner"); auto* inner_element_layer = CcLayerByDOMElementId("inner"); - DCHECK_EQ(inner_element_layer->element_id(), - CompositorElementIdFromUniqueObjectId( - inner_element->GetLayoutObject()->UniqueId(), - CompositorElementIdNamespace::kPrimary)); // Initially, no layer should have |subtree_property_changed| set. EXPECT_FALSE(outer_element_layer->subtree_property_changed()); @@ -1118,4 +1073,143 @@ TEST_P(CompositingSimTest, NoRenderSurfaceWithAxisAlignedTransformAnimation) { } } +TEST_P(CompositingSimTest, PromoteCrossOriginIframe) { + base::test::ScopedFeatureList feature_list; + feature_list.InitWithFeatureState( + blink::features::kCompositeCrossOriginIframes, true); + InitializeWithHTML("<!DOCTYPE html><iframe id=iframe sandbox></iframe>"); + Compositor().BeginFrame(); + EXPECT_TRUE(CcLayerByDOMElementId("iframe")); +} + +// On initial layout, the iframe is not yet loaded and is not considered +// cross origin. This test ensures the iframe is promoted due to being cross +// origin after the iframe loads. +TEST_P(CompositingSimTest, PromoteCrossOriginIframeAfterLoading) { + base::test::ScopedFeatureList feature_list; + feature_list.InitWithFeatureState( + blink::features::kCompositeCrossOriginIframes, true); + + SimRequest main_resource("https://origin-a.com/a.html", "text/html"); + SimRequest frame_resource("https://origin-b.com/b.html", "text/html"); + + LoadURL("https://origin-a.com/a.html"); + main_resource.Complete(R"HTML( + <!DOCTYPE html> + <iframe id="iframe" src="https://origin-b.com/b.html"></iframe> + )HTML"); + frame_resource.Complete("<!DOCTYPE html>"); + Compositor().BeginFrame(); + + EXPECT_TRUE(CcLayerByDOMElementId("iframe")); +} + +// An iframe that is cross-origin to the parent should be composited. This test +// sets up nested frames with domains A -> B -> A. Both the child and grandchild +// frames should be composited because they are cross-origin to their parent. +TEST_P(CompositingSimTest, PromoteCrossOriginToParent) { + base::test::ScopedFeatureList feature_list; + feature_list.InitWithFeatureState( + blink::features::kCompositeCrossOriginIframes, true); + + SimRequest main_resource("https://origin-a.com/a.html", "text/html"); + SimRequest child_resource("https://origin-b.com/b.html", "text/html"); + SimRequest grandchild_resource("https://origin-a.com/c.html", "text/html"); + + LoadURL("https://origin-a.com/a.html"); + main_resource.Complete(R"HTML( + <!DOCTYPE html> + <iframe id="main_iframe" src="https://origin-b.com/b.html"></iframe> + )HTML"); + child_resource.Complete(R"HTML( + <!DOCTYPE html> + <iframe id="child_iframe" src="https://origin-a.com/c.html"></iframe> + )HTML"); + grandchild_resource.Complete("<!DOCTYPE html>"); + Compositor().BeginFrame(); + + EXPECT_TRUE(CcLayerByDOMElementId("main_iframe")); + EXPECT_TRUE(CcLayerByDOMElementId("child_iframe")); +} + +// Initially the iframe is cross-origin and should be composited. After changing +// to same-origin, the frame should no longer be composited. +TEST_P(CompositingSimTest, PromoteCrossOriginIframeAfterDomainChange) { + base::test::ScopedFeatureList feature_list; + feature_list.InitWithFeatureState( + blink::features::kCompositeCrossOriginIframes, true); + + SimRequest main_resource("https://origin-a.com/a.html", "text/html"); + SimRequest frame_resource("https://sub.origin-a.com/b.html", "text/html"); + + LoadURL("https://origin-a.com/a.html"); + main_resource.Complete(R"HTML( + <!DOCTYPE html> + <iframe id="iframe" src="https://sub.origin-a.com/b.html"></iframe> + )HTML"); + frame_resource.Complete("<!DOCTYPE html>"); + Compositor().BeginFrame(); + + EXPECT_TRUE(CcLayerByDOMElementId("iframe")); + + auto* iframe_element = + To<HTMLIFrameElement>(GetDocument().getElementById("iframe")); + NonThrowableExceptionState exception_state; + GetDocument().setDomain(String("origin-a.com"), exception_state); + iframe_element->contentDocument()->setDomain(String("origin-a.com"), + exception_state); + // We may not have scheduled a visual update so force an update instead of + // using BeginFrame. + UpdateAllLifecyclePhases(); + + EXPECT_FALSE(CcLayerByDOMElementId("iframe")); +} + +// This test sets up nested frames with domains A -> B -> A. Initially, the +// child frame and grandchild frame should be composited. After changing the +// child frame to A (same-origin), both child and grandchild frames should no +// longer be composited. +TEST_P(CompositingSimTest, PromoteCrossOriginToParentIframeAfterDomainChange) { + base::test::ScopedFeatureList feature_list; + feature_list.InitWithFeatureState( + blink::features::kCompositeCrossOriginIframes, true); + + SimRequest main_resource("https://origin-a.com/a.html", "text/html"); + SimRequest child_resource("https://sub.origin-a.com/b.html", "text/html"); + SimRequest grandchild_resource("https://origin-a.com/c.html", "text/html"); + + LoadURL("https://origin-a.com/a.html"); + main_resource.Complete(R"HTML( + <!DOCTYPE html> + <iframe id="main_iframe" src="https://sub.origin-a.com/b.html"></iframe> + )HTML"); + child_resource.Complete(R"HTML( + <!DOCTYPE html> + <iframe id="child_iframe" src="https://origin-a.com/c.html"></iframe> + )HTML"); + grandchild_resource.Complete("<!DOCTYPE html>"); + Compositor().BeginFrame(); + + EXPECT_TRUE(CcLayerByDOMElementId("main_iframe")); + EXPECT_TRUE(CcLayerByDOMElementId("child_iframe")); + + auto* main_iframe_element = + To<HTMLIFrameElement>(GetDocument().getElementById("main_iframe")); + NonThrowableExceptionState exception_state; + GetDocument().setDomain(String("origin-a.com"), exception_state); + auto* child_iframe_element = To<HTMLIFrameElement>( + main_iframe_element->contentDocument()->getElementById("child_iframe")); + child_iframe_element->contentDocument()->setDomain(String("origin-a.com"), + exception_state); + main_iframe_element->contentDocument()->setDomain(String("origin-a.com"), + exception_state); + + // We may not have scheduled a visual update so force an update instead of + // using BeginFrame. + UpdateAllLifecyclePhases(); + + EXPECT_FALSE(CcLayerByDOMElementId("main_iframe")); + EXPECT_FALSE(CcLayerByDOMElementId("child_iframe")); +} + } // namespace blink diff --git a/chromium/third_party/blink/renderer/core/paint/compositing/graphics_layer_tree_as_text.cc b/chromium/third_party/blink/renderer/core/paint/compositing/graphics_layer_tree_as_text.cc index 8be2ec4a1b5..fce32e76d78 100644 --- a/chromium/third_party/blink/renderer/core/paint/compositing/graphics_layer_tree_as_text.cc +++ b/chromium/third_party/blink/renderer/core/paint/compositing/graphics_layer_tree_as_text.cc @@ -15,9 +15,7 @@ namespace { std::unique_ptr<JSONObject> GraphicsLayerAsJSON(const GraphicsLayer* layer, LayerTreeFlags flags) { - // Intentionally passing through 0, 0 for the offset from the transform node - // as this dump implementation doesn't support transform/position information. - auto json = CCLayerAsJSON(layer->CcLayer(), flags, FloatPoint()); + auto json = CCLayerAsJSON(layer->CcLayer(), flags); // Content dumped after this point, down to AppendAdditionalInfoAsJSON, is // specific to GraphicsLayer tree dumping when called from one of the methods diff --git a/chromium/third_party/blink/renderer/core/paint/compositing/graphics_layer_tree_builder.cc b/chromium/third_party/blink/renderer/core/paint/compositing/graphics_layer_tree_builder.cc index bceebbc05cd..6e5eaa164fe 100644 --- a/chromium/third_party/blink/renderer/core/paint/compositing/graphics_layer_tree_builder.cc +++ b/chromium/third_party/blink/renderer/core/paint/compositing/graphics_layer_tree_builder.cc @@ -38,8 +38,6 @@ namespace blink { GraphicsLayerTreeBuilder::GraphicsLayerTreeBuilder() = default; -GraphicsLayerTreeBuilder::~GraphicsLayerTreeBuilder() = default; - static bool ShouldAppendLayer(const PaintLayer& layer) { auto* video_element = DynamicTo<HTMLVideoElement>(layer.GetLayoutObject().GetNode()); diff --git a/chromium/third_party/blink/renderer/core/paint/compositing/graphics_layer_tree_builder.h b/chromium/third_party/blink/renderer/core/paint/compositing/graphics_layer_tree_builder.h index 88e607b9442..b1ef0a4f661 100644 --- a/chromium/third_party/blink/renderer/core/paint/compositing/graphics_layer_tree_builder.h +++ b/chromium/third_party/blink/renderer/core/paint/compositing/graphics_layer_tree_builder.h @@ -39,7 +39,6 @@ class GraphicsLayerTreeBuilder { public: GraphicsLayerTreeBuilder(); - ~GraphicsLayerTreeBuilder(); void Rebuild(PaintLayer&, GraphicsLayerVector&); diff --git a/chromium/third_party/blink/renderer/core/paint/compositing/graphics_layer_updater.cc b/chromium/third_party/blink/renderer/core/paint/compositing/graphics_layer_updater.cc index e1df9970626..3026c276c50 100644 --- a/chromium/third_party/blink/renderer/core/paint/compositing/graphics_layer_updater.cc +++ b/chromium/third_party/blink/renderer/core/paint/compositing/graphics_layer_updater.cc @@ -104,8 +104,6 @@ GraphicsLayerUpdater::UpdateContext::CompositingStackingContext() const { GraphicsLayerUpdater::GraphicsLayerUpdater() : needs_rebuild_tree_(false) {} -GraphicsLayerUpdater::~GraphicsLayerUpdater() = default; - void GraphicsLayerUpdater::Update( PaintLayer& layer, Vector<PaintLayer*>& layers_needing_paint_invalidation) { diff --git a/chromium/third_party/blink/renderer/core/paint/compositing/graphics_layer_updater.h b/chromium/third_party/blink/renderer/core/paint/compositing/graphics_layer_updater.h index 9a98a937df3..54c765cc6db 100644 --- a/chromium/third_party/blink/renderer/core/paint/compositing/graphics_layer_updater.h +++ b/chromium/third_party/blink/renderer/core/paint/compositing/graphics_layer_updater.h @@ -39,7 +39,6 @@ class GraphicsLayerUpdater { public: GraphicsLayerUpdater(); - ~GraphicsLayerUpdater(); enum UpdateType { kDoNotForceUpdate, diff --git a/chromium/third_party/blink/renderer/core/paint/compositing/paint_layer_compositor.cc b/chromium/third_party/blink/renderer/core/paint/compositing/paint_layer_compositor.cc index 6fcd60085aa..3f7a2b6c166 100644 --- a/chromium/third_party/blink/renderer/core/paint/compositing/paint_layer_compositor.cc +++ b/chromium/third_party/blink/renderer/core/paint/compositing/paint_layer_compositor.cc @@ -66,10 +66,9 @@ namespace blink { PaintLayerCompositor::PaintLayerCompositor(LayoutView& layout_view) - : layout_view_(layout_view), - has_accelerated_compositing_(layout_view.GetDocument() - .GetSettings() - ->GetAcceleratedCompositingEnabled()) {} + : layout_view_(layout_view) { + DCHECK(!RuntimeEnabledFeatures::CompositeAfterPaintEnabled()); +} PaintLayerCompositor::~PaintLayerCompositor() { DCHECK_EQ(root_layer_attachment_, kRootLayerUnattached); @@ -135,7 +134,9 @@ void PaintLayerCompositor::EnableCompositingModeIfNeeded() { bool PaintLayerCompositor::RootShouldAlwaysComposite() const { // If compositing is disabled for the WebView, then nothing composites. - if (!has_accelerated_compositing_) + if (!layout_view_.GetDocument() + .GetSettings() + ->GetAcceleratedCompositingEnabled()) return false; // Local roots composite always, when compositing is enabled globally. if (layout_view_.GetFrame()->IsLocalRoot()) @@ -150,23 +151,11 @@ bool PaintLayerCompositor::RootShouldAlwaysComposite() const { } void PaintLayerCompositor::UpdateAcceleratedCompositingSettings() { - // AcceleratedCompositing setting does not change after initialization. - DCHECK_EQ(has_accelerated_compositing_, - layout_view_.GetDocument() - .GetSettings() - ->GetAcceleratedCompositingEnabled()); - root_should_always_composite_dirty_ = true; if (root_layer_attachment_ != kRootLayerUnattached) RootLayer()->SetNeedsCompositingInputsUpdate(); } -bool PaintLayerCompositor::PreferCompositingToLCDTextEnabled() const { - return layout_view_.GetDocument() - .GetSettings() - ->GetPreferCompositingToLCDTextEnabled(); -} - static LayoutVideo* FindFullscreenVideoLayoutObject(Document& document) { // Recursively find the document that is in fullscreen. Element* fullscreen_element = Fullscreen::FullscreenElementFrom(document); @@ -181,9 +170,7 @@ static LayoutVideo* FindFullscreenVideoLayoutObject(Document& document) { if (!IsA<HTMLVideoElement>(fullscreen_element)) return nullptr; LayoutObject* layout_object = fullscreen_element->GetLayoutObject(); - if (!layout_object) - return nullptr; - return ToLayoutVideo(layout_object); + return To<LayoutVideo>(layout_object); } void PaintLayerCompositor::UpdateIfNeededRecursive( @@ -321,7 +308,9 @@ GraphicsLayer* PaintLayerCompositor::OverlayFullscreenVideoGraphicsLayer() void PaintLayerCompositor::UpdateWithoutAcceleratedCompositing( CompositingUpdateType update_type) { - DCHECK(!HasAcceleratedCompositing()); + DCHECK(!layout_view_.GetDocument() + .GetSettings() + ->GetAcceleratedCompositingEnabled()); if (update_type >= kCompositingUpdateAfterCompositingInputChange) { CompositingInputsUpdater(RootLayer(), GetCompositingInputsRoot()).Update(); @@ -379,7 +368,9 @@ void PaintLayerCompositor::UpdateIfNeeded( CompositingUpdateType update_type = pending_update_type_; pending_update_type_ = kCompositingUpdateNone; - if (!HasAcceleratedCompositing()) { + if (!layout_view_.GetDocument() + .GetSettings() + ->GetAcceleratedCompositingEnabled()) { UpdateWithoutAcceleratedCompositing(update_type); Lifecycle().AdvanceTo( std::min(DocumentLifecycle::kCompositingClean, target_state)); @@ -563,14 +554,6 @@ bool PaintLayerCompositor::AllocateOrClearCompositedLayerMapping( layer->ClearClipRects(kPaintingClipRects); - // If a fixed position layer gained/lost a compositedLayerMapping or the - // reason not compositing it changed, the scrolling coordinator needs to - // recalculate whether it can do fast scrolling. - if (ScrollingCoordinator* scrolling_coordinator = GetScrollingCoordinator()) { - scrolling_coordinator->FrameViewFixedObjectsDidChange( - layout_view_.GetFrameView()); - } - // Compositing state affects whether to create paint offset translation of // this layer, and amount of paint offset translation of descendants. layer->GetLayoutObject().SetNeedsPaintPropertyUpdate(); @@ -658,13 +641,13 @@ GraphicsLayer* PaintLayerCompositor::RootGraphicsLayer() const { return nullptr; } -GraphicsLayer* PaintLayerCompositor::GetXrImmersiveDomOverlayLayer() const { +GraphicsLayer* PaintLayerCompositor::GetXrOverlayLayer() const { // immersive-ar DOM overlay mode is very similar to fullscreen video, using // the AR camera image instead of a video element as a background that's // separately composited in the browser. The fullscreened DOM content is shown // on top of that, same as HTML video controls. DCHECK(IsMainFrame()); - if (!layout_view_.GetDocument().IsImmersiveArOverlay()) + if (!layout_view_.GetDocument().IsXrOverlay()) return nullptr; Element* fullscreen_element = @@ -695,7 +678,7 @@ GraphicsLayer* PaintLayerCompositor::PaintRootGraphicsLayer() const { // Start from the full screen overlay layer if exists. Other layers will be // skipped during painting. - if (auto* layer = GetXrImmersiveDomOverlayLayer()) + if (auto* layer = GetXrOverlayLayer()) return layer; if (auto* layer = OverlayFullscreenVideoGraphicsLayer()) return layer; @@ -719,8 +702,10 @@ bool PaintLayerCompositor::CanBeComposited(const PaintLayer* layer) const { const bool has_compositor_animation = CompositingReasonFinder::CompositingReasonsForAnimation( - *layer->GetLayoutObject().Style()) != CompositingReason::kNone; - return has_accelerated_compositing_ && + layer->GetLayoutObject()) != CompositingReason::kNone; + return layout_view_.GetDocument() + .GetSettings() + ->GetAcceleratedCompositingEnabled() && (has_compositor_animation || !layer->SubtreeIsInvisible()) && layer->IsSelfPaintingLayer() && !layer->GetLayoutObject().IsLayoutFlowThread() && diff --git a/chromium/third_party/blink/renderer/core/paint/compositing/paint_layer_compositor.h b/chromium/third_party/blink/renderer/core/paint/compositing/paint_layer_compositor.h index b54493ba61e..e747e628c87 100644 --- a/chromium/third_party/blink/renderer/core/paint/compositing/paint_layer_compositor.h +++ b/chromium/third_party/blink/renderer/core/paint/compositing/paint_layer_compositor.h @@ -95,13 +95,6 @@ class CORE_EXPORT PaintLayerCompositor { // to the native view/window system. void SetCompositingModeEnabled(bool); - // Returns true if the accelerated compositing is enabled - bool HasAcceleratedCompositing() const { - return has_accelerated_compositing_; - } - - bool PreferCompositingToLCDTextEnabled() const; - bool RootShouldAlwaysComposite() const; // Notifies about changes to PreferCompositingToLCDText or @@ -195,10 +188,9 @@ class CORE_EXPORT PaintLayerCompositor { bool IsMainFrame() const; - GraphicsLayer* GetXrImmersiveDomOverlayLayer() const; + GraphicsLayer* GetXrOverlayLayer() const; LayoutView& layout_view_; - const bool has_accelerated_compositing_ = true; bool compositing_ = false; 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 f79b67a6769..669148c9936 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 @@ -36,7 +36,8 @@ TEST_F(PaintLayerCompositorTest, AdvancingToCompositingInputsClean) { box_layer->SetNeedsCompositingInputsUpdate(); - GetDocument().View()->UpdateLifecycleToCompositingInputsClean(); + GetDocument().View()->UpdateLifecycleToCompositingInputsClean( + DocumentUpdateReason::kTest); EXPECT_EQ(DocumentLifecycle::kCompositingInputsClean, GetDocument().Lifecycle().GetState()); EXPECT_FALSE(box_layer->NeedsCompositingInputsUpdate()); @@ -63,7 +64,8 @@ TEST_F(PaintLayerCompositorTest, // Update the lifecycle to CompositingInputsClean. This should not start the // animation lifecycle. - GetDocument().View()->UpdateLifecycleToCompositingInputsClean(); + GetDocument().View()->UpdateLifecycleToCompositingInputsClean( + DocumentUpdateReason::kTest); EXPECT_EQ(DocumentLifecycle::kCompositingInputsClean, GetDocument().Lifecycle().GetState()); @@ -110,7 +112,8 @@ TEST_F(PaintLayerCompositorTest, CompositingInputsUpdateStopsContainStrict) { EXPECT_FALSE(wrapper->NeedsCompositingInputsUpdate()); EXPECT_TRUE(target->NeedsCompositingInputsUpdate()); - GetDocument().View()->UpdateLifecycleToCompositingInputsClean(); + GetDocument().View()->UpdateLifecycleToCompositingInputsClean( + DocumentUpdateReason::kTest); EXPECT_EQ(DocumentLifecycle::kCompositingInputsClean, GetDocument().Lifecycle().GetState()); EXPECT_FALSE(wrapper->NeedsCompositingInputsUpdate()); diff --git a/chromium/third_party/blink/renderer/core/paint/css_mask_painter.cc b/chromium/third_party/blink/renderer/core/paint/css_mask_painter.cc index 25432d2d1ea..269bc6bc6df 100644 --- a/chromium/third_party/blink/renderer/core/paint/css_mask_painter.cc +++ b/chromium/third_party/blink/renderer/core/paint/css_mask_painter.cc @@ -23,8 +23,9 @@ base::Optional<IntRect> CSSMaskPainter::MaskBoundingBox( SVGResourcesCache::CachedResourcesForLayoutObject(object); LayoutSVGResourceMasker* masker = resources ? resources->Masker() : nullptr; if (masker) { - return EnclosingIntRect( - masker->ResourceBoundingBox(object.ObjectBoundingBox())); + const FloatRect reference_box = + SVGResources::ReferenceBoxForEffects(object); + return EnclosingIntRect(masker->ResourceBoundingBox(reference_box)); } } diff --git a/chromium/third_party/blink/renderer/core/paint/custom_scrollbar_theme.cc b/chromium/third_party/blink/renderer/core/paint/custom_scrollbar_theme.cc index 2e1600aba58..75934b0f5c3 100644 --- a/chromium/third_party/blink/renderer/core/paint/custom_scrollbar_theme.cc +++ b/chromium/third_party/blink/renderer/core/paint/custom_scrollbar_theme.cc @@ -41,13 +41,26 @@ CustomScrollbarTheme* CustomScrollbarTheme::GetCustomScrollbarTheme() { return &theme; } +ScrollbarPart CustomScrollbarTheme::HitTest(const Scrollbar& scrollbar, + const IntPoint& test_position) { + auto result = ScrollbarTheme::HitTest(scrollbar, test_position); + if (result == kScrollbarBGPart) { + // The ScrollbarTheme knows nothing about the double buttons. + if (ButtonRect(scrollbar, kBackButtonEndPart).Contains(test_position)) + return kBackButtonEndPart; + if (ButtonRect(scrollbar, kForwardButtonStartPart).Contains(test_position)) + return kForwardButtonStartPart; + } + return result; +} + void CustomScrollbarTheme::ButtonSizesAlongTrackAxis(const Scrollbar& scrollbar, int& before_size, int& after_size) { - IntRect first_button = BackButtonRect(scrollbar, kBackButtonStartPart); - IntRect second_button = ForwardButtonRect(scrollbar, kForwardButtonStartPart); - IntRect third_button = BackButtonRect(scrollbar, kBackButtonEndPart); - IntRect fourth_button = ForwardButtonRect(scrollbar, kForwardButtonEndPart); + IntRect first_button = ButtonRect(scrollbar, kBackButtonStartPart); + IntRect second_button = ButtonRect(scrollbar, kForwardButtonStartPart); + IntRect third_button = ButtonRect(scrollbar, kBackButtonEndPart); + IntRect fourth_button = ButtonRect(scrollbar, kForwardButtonEndPart); if (scrollbar.Orientation() == kHorizontalScrollbar) { before_size = first_button.Width() + second_button.Width(); after_size = third_button.Width() + fourth_button.Width(); @@ -74,14 +87,17 @@ int CustomScrollbarTheme::MinimumThumbLength(const Scrollbar& scrollbar) { return To<CustomScrollbar>(scrollbar).MinimumThumbLength(); } -IntRect CustomScrollbarTheme::BackButtonRect(const Scrollbar& scrollbar, - ScrollbarPart part_type) { +IntRect CustomScrollbarTheme::ButtonRect(const Scrollbar& scrollbar, + ScrollbarPart part_type) { return To<CustomScrollbar>(scrollbar).ButtonRect(part_type); } -IntRect CustomScrollbarTheme::ForwardButtonRect(const Scrollbar& scrollbar, - ScrollbarPart part_type) { - return To<CustomScrollbar>(scrollbar).ButtonRect(part_type); +IntRect CustomScrollbarTheme::BackButtonRect(const Scrollbar& scrollbar) { + return ButtonRect(scrollbar, kBackButtonStartPart); +} + +IntRect CustomScrollbarTheme::ForwardButtonRect(const Scrollbar& scrollbar) { + return ButtonRect(scrollbar, kForwardButtonEndPart); } IntRect CustomScrollbarTheme::TrackRect(const Scrollbar& scrollbar) { @@ -141,17 +157,15 @@ void CustomScrollbarTheme::PaintTrackAndButtons(GraphicsContext& context, PaintPart(context, scrollbar, scrollbar.FrameRect(), kScrollbarBGPart); if (HasButtons(scrollbar)) { - PaintButton(context, scrollbar, - BackButtonRect(scrollbar, kBackButtonStartPart), + PaintButton(context, scrollbar, ButtonRect(scrollbar, kBackButtonStartPart), kBackButtonStartPart); - PaintButton(context, scrollbar, - BackButtonRect(scrollbar, kBackButtonEndPart), + PaintButton(context, scrollbar, ButtonRect(scrollbar, kBackButtonEndPart), kBackButtonEndPart); PaintButton(context, scrollbar, - ForwardButtonRect(scrollbar, kForwardButtonStartPart), + ButtonRect(scrollbar, kForwardButtonStartPart), kForwardButtonStartPart); PaintButton(context, scrollbar, - ForwardButtonRect(scrollbar, kForwardButtonEndPart), + ButtonRect(scrollbar, kForwardButtonEndPart), kForwardButtonEndPart); } diff --git a/chromium/third_party/blink/renderer/core/paint/custom_scrollbar_theme.h b/chromium/third_party/blink/renderer/core/paint/custom_scrollbar_theme.h index 7b72703ac91..eecbdda3cd1 100644 --- a/chromium/third_party/blink/renderer/core/paint/custom_scrollbar_theme.h +++ b/chromium/third_party/blink/renderer/core/paint/custom_scrollbar_theme.h @@ -44,8 +44,8 @@ class CustomScrollbarTheme final : public ScrollbarTheme { return GetTheme().ScrollbarThickness(control_size); } - WebScrollbarButtonsPlacement ButtonsPlacement() const override { - return GetTheme().ButtonsPlacement(); + bool NativeThemeHasButtons() override { + return GetTheme().NativeThemeHasButtons(); } void PaintScrollCorner(GraphicsContext&, @@ -89,11 +89,13 @@ class CustomScrollbarTheme final : public ScrollbarTheme { const CustomScrollbar* = nullptr); protected: + ScrollbarPart HitTest(const Scrollbar&, const IntPoint&) override; + bool HasButtons(const Scrollbar&) override; bool HasThumb(const Scrollbar&) override; - IntRect BackButtonRect(const Scrollbar&, ScrollbarPart) override; - IntRect ForwardButtonRect(const Scrollbar&, ScrollbarPart) override; + IntRect BackButtonRect(const Scrollbar&) override; + IntRect ForwardButtonRect(const Scrollbar&) override; IntRect TrackRect(const Scrollbar&) override; void PaintTrackAndButtons(GraphicsContext&, @@ -112,6 +114,7 @@ class CustomScrollbarTheme final : public ScrollbarTheme { const IntRect&) override; private: + IntRect ButtonRect(const Scrollbar&, ScrollbarPart); void PaintScrollbarBackground(GraphicsContext&, const Scrollbar&); void PaintTrackBackground(GraphicsContext&, const Scrollbar&, const IntRect&); void PaintTrackPiece(GraphicsContext&, 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 41bd8afeb85..0d0459e9d09 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 @@ -95,25 +95,8 @@ sk_sp<PaintRecord> RecordMarker(Color blink_color) { void DrawDocumentMarker(GraphicsContext& context, const FloatPoint& pt, float width, - DocumentMarker::MarkerType marker_type, - float zoom) { - DCHECK(marker_type == DocumentMarker::kSpelling || - marker_type == DocumentMarker::kGrammar); - - DEFINE_STATIC_LOCAL( - PaintRecord*, spelling_marker, - (RecordMarker( - LayoutTheme::GetTheme().PlatformSpellingMarkerUnderlineColor()) - .release())); - DEFINE_STATIC_LOCAL( - PaintRecord*, grammar_marker, - (RecordMarker( - LayoutTheme::GetTheme().PlatformGrammarMarkerUnderlineColor()) - .release())); - auto* const marker = marker_type == DocumentMarker::kSpelling - ? spelling_marker - : grammar_marker; - + float zoom, + PaintRecord* const marker) { // Position already includes zoom and device scale factor. SkScalar origin_x = WebCoreFloatToSkScalar(pt.X()); SkScalar origin_y = WebCoreFloatToSkScalar(pt.Y()); @@ -185,14 +168,49 @@ void DocumentMarkerPainter::PaintStyleableMarkerUnderline( marker.UseTextColor() ? style.VisitedDependentColor(GetCSSPropertyWebkitTextFillColor()) : marker.UnderlineColor(); - context.SetStrokeColor(marker_color); - - context.SetStrokeThickness(line_thickness); - context.DrawLineForText( - FloatPoint( - box_origin.left + start, - (box_origin.top + logical_height.ToInt() - line_thickness).ToFloat()), - width); + if (marker.UnderlineStyle() != + ui::mojom::ImeTextSpanUnderlineStyle::kSquiggle) { + context.SetStrokeColor(marker_color); + context.SetStrokeThickness(line_thickness); + // Set the style of the underline if there is any. + switch (marker.UnderlineStyle()) { + case ui::mojom::ImeTextSpanUnderlineStyle::kDash: + context.SetStrokeStyle(StrokeStyle::kDashedStroke); + break; + case ui::mojom::ImeTextSpanUnderlineStyle::kDot: + context.SetStrokeStyle(StrokeStyle::kDottedStroke); + break; + case ui::mojom::ImeTextSpanUnderlineStyle::kSolid: + context.SetStrokeStyle(StrokeStyle::kSolidStroke); + break; + case ui::mojom::ImeTextSpanUnderlineStyle::kNone: + context.SetStrokeStyle(StrokeStyle::kNoStroke); + break; + case ui::mojom::ImeTextSpanUnderlineStyle::kSquiggle: + // Wavy stroke style is not implemented in DrawLineForText so we handle + // it specially in the else condition below only for composition + // markers. + break; + } + context.DrawLineForText( + FloatPoint(box_origin.left + start, + (box_origin.top + logical_height.ToInt() - line_thickness) + .ToFloat()), + width); + } else { + // For wavy underline format we use this logic that is very similar to + // spelling/grammar squiggles format. Only applicable for composition + // markers for now. + if (marker.GetType() == DocumentMarker::kComposition) { + sk_sp<PaintRecord> composition_marker = (RecordMarker(marker_color)); + DrawDocumentMarker( + context, + FloatPoint((box_origin.left + start).ToFloat(), + (box_origin.top + logical_height.ToInt() - line_thickness) + .ToFloat()), + width, line_thickness, composition_marker.get()); + } + } } void DocumentMarkerPainter::PaintDocumentMarker( @@ -210,7 +228,7 @@ void DocumentMarkerPainter::PaintDocumentMarker( // place the underline at the bottom of the text, but in larger fonts that's // not so good so we pin to two pixels under the baseline. float zoom = style.EffectiveZoom(); - int line_thickness = kMarkerHeight * zoom; + int line_thickness = static_cast<int>(ceilf(kMarkerHeight * zoom)); const SimpleFontData* font_data = style.GetFont().PrimaryFont(); DCHECK(font_data); @@ -227,10 +245,24 @@ void DocumentMarkerPainter::PaintDocumentMarker( // prevent a big gap. underline_offset = baseline + 2 * zoom; } + DEFINE_STATIC_LOCAL( + PaintRecord*, spelling_marker, + (RecordMarker( + LayoutTheme::GetTheme().PlatformSpellingMarkerUnderlineColor()) + .release())); + DEFINE_STATIC_LOCAL( + PaintRecord*, grammar_marker, + (RecordMarker( + LayoutTheme::GetTheme().PlatformGrammarMarkerUnderlineColor()) + .release())); + + auto* const marker = marker_type == DocumentMarker::kSpelling + ? spelling_marker + : grammar_marker; DrawDocumentMarker(context, FloatPoint((box_origin.left + local_rect.X()).ToFloat(), (box_origin.top + underline_offset).ToFloat()), - local_rect.Width().ToFloat(), marker_type, zoom); + local_rect.Width().ToFloat(), zoom, marker); } TextPaintStyle DocumentMarkerPainter::ComputeTextPaintStyleFrom( 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 df0263b1c2c..d4b65ec7134 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 @@ -22,7 +22,7 @@ void EllipsisBoxPainter::Paint(const PaintInfo& paint_info, const LayoutPoint& paint_offset, LayoutUnit line_top, LayoutUnit line_bottom) { - if (paint_info.phase == PaintPhase::kSelection) + if (paint_info.phase == PaintPhase::kSelectionDragImage) return; const ComputedStyle& style = ellipsis_box_.GetLineLayoutItem().StyleRef( diff --git a/chromium/third_party/blink/renderer/core/paint/embedded_object_painter.cc b/chromium/third_party/blink/renderer/core/paint/embedded_object_painter.cc index 43663833e74..9b93bd124de 100644 --- a/chromium/third_party/blink/renderer/core/paint/embedded_object_painter.cc +++ b/chromium/third_party/blink/renderer/core/paint/embedded_object_painter.cc @@ -31,7 +31,6 @@ static Font ReplacementTextFont() { font_description.SetWeight(BoldWeightValue()); font_description.SetComputedSize(font_description.SpecifiedSize()); Font font(font_description); - font.Update(nullptr); return font; } @@ -43,7 +42,7 @@ void EmbeddedObjectPainter::PaintReplaced(const PaintInfo& paint_info, return; } - if (paint_info.phase == PaintPhase::kSelection) + if (paint_info.phase == PaintPhase::kSelectionDragImage) return; GraphicsContext& context = paint_info.context; 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 f6fa95b1c13..900b20688cf 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 @@ -27,7 +27,6 @@ #include "third_party/blink/renderer/core/paint/filter_effect_builder.h" #include <algorithm> -#include "third_party/blink/public/platform/web_point.h" #include "third_party/blink/renderer/core/style/filter_operations.h" #include "third_party/blink/renderer/core/svg/graphics/filters/svg_filter_builder.h" #include "third_party/blink/renderer/core/svg/svg_filter_element.h" @@ -165,14 +164,16 @@ FilterEffect* FilterEffectBuilder::BuildFilterEffect( Vector<float> input_parameters = GrayscaleMatrix( To<BasicColorMatrixFilterOperation>(filter_operation)->Amount()); effect = MakeGarbageCollected<FEColorMatrix>( - parent_filter, FECOLORMATRIX_TYPE_MATRIX, input_parameters); + parent_filter, FECOLORMATRIX_TYPE_MATRIX, + std::move(input_parameters)); break; } case FilterOperation::SEPIA: { Vector<float> input_parameters = SepiaMatrix( To<BasicColorMatrixFilterOperation>(filter_operation)->Amount()); effect = MakeGarbageCollected<FEColorMatrix>( - parent_filter, FECOLORMATRIX_TYPE_MATRIX, input_parameters); + parent_filter, FECOLORMATRIX_TYPE_MATRIX, + std::move(input_parameters)); break; } case FilterOperation::SATURATE: { @@ -180,7 +181,8 @@ FilterEffect* FilterEffectBuilder::BuildFilterEffect( input_parameters.push_back(clampTo<float>( To<BasicColorMatrixFilterOperation>(filter_operation)->Amount())); effect = MakeGarbageCollected<FEColorMatrix>( - parent_filter, FECOLORMATRIX_TYPE_SATURATE, input_parameters); + parent_filter, FECOLORMATRIX_TYPE_SATURATE, + std::move(input_parameters)); break; } case FilterOperation::HUE_ROTATE: { @@ -188,7 +190,8 @@ FilterEffect* FilterEffectBuilder::BuildFilterEffect( input_parameters.push_back(clampTo<float>( To<BasicColorMatrixFilterOperation>(filter_operation)->Amount())); effect = MakeGarbageCollected<FEColorMatrix>( - parent_filter, FECOLORMATRIX_TYPE_HUEROTATE, input_parameters); + parent_filter, FECOLORMATRIX_TYPE_HUEROTATE, + std::move(input_parameters)); break; } case FilterOperation::INVERT: { diff --git a/chromium/third_party/blink/renderer/core/paint/first_meaningful_paint_detector.cc b/chromium/third_party/blink/renderer/core/paint/first_meaningful_paint_detector.cc index 3eaac6137e5..97c06979db0 100644 --- a/chromium/third_party/blink/renderer/core/paint/first_meaningful_paint_detector.cc +++ b/chromium/third_party/blink/renderer/core/paint/first_meaningful_paint_detector.cc @@ -151,16 +151,15 @@ void FirstMeaningfulPaintDetector::RegisterNotifySwapTime(PaintEvent event) { WrapCrossThreadWeakPersistent(this), event)); } -void FirstMeaningfulPaintDetector::ReportSwapTime( - PaintEvent event, - WebWidgetClient::SwapResult result, - base::TimeTicks timestamp) { +void FirstMeaningfulPaintDetector::ReportSwapTime(PaintEvent event, + WebSwapResult result, + base::TimeTicks timestamp) { DCHECK(event == PaintEvent::kProvisionalFirstMeaningfulPaint); DCHECK_GT(outstanding_swap_promise_count_, 0U); --outstanding_swap_promise_count_; // If the swap fails for any reason, we use the timestamp when the SwapPromise - // was broken. |result| == WebWidgetClient::SwapResult::kDidNotSwapSwapFails + // was broken. |result| == WebSwapResult::kDidNotSwapSwapFails // usually means the compositor decided not swap because there was no actual // damage, which can happen when what's being painted isn't visible. In this // case, the timestamp will be consistent with the case where the swap @@ -224,7 +223,7 @@ void FirstMeaningfulPaintDetector::SetTickClockForTesting( g_clock = clock; } -void FirstMeaningfulPaintDetector::Trace(blink::Visitor* visitor) { +void FirstMeaningfulPaintDetector::Trace(Visitor* visitor) { visitor->Trace(paint_timing_); } diff --git a/chromium/third_party/blink/renderer/core/paint/first_meaningful_paint_detector.h b/chromium/third_party/blink/renderer/core/paint/first_meaningful_paint_detector.h index a0c9db2c3f2..a093acfcf0b 100644 --- a/chromium/third_party/blink/renderer/core/paint/first_meaningful_paint_detector.h +++ b/chromium/third_party/blink/renderer/core/paint/first_meaningful_paint_detector.h @@ -6,7 +6,7 @@ #define THIRD_PARTY_BLINK_RENDERER_CORE_PAINT_FIRST_MEANINGFUL_PAINT_DETECTOR_H_ #include "base/macros.h" -#include "third_party/blink/public/web/web_widget_client.h" +#include "third_party/blink/public/web/web_swap_result.h" #include "third_party/blink/renderer/core/core_export.h" #include "third_party/blink/renderer/core/paint/paint_event.h" #include "third_party/blink/renderer/platform/heap/handle.h" @@ -39,14 +39,14 @@ class CORE_EXPORT FirstMeaningfulPaintDetector int visible_height); void NotifyInputEvent(); void NotifyPaint(); - void ReportSwapTime(PaintEvent, WebWidgetClient::SwapResult, base::TimeTicks); + void ReportSwapTime(PaintEvent, WebSwapResult, base::TimeTicks); void NotifyFirstContentfulPaint(base::TimeTicks swap_stamp); void OnNetwork2Quiet(); // The caller owns the |clock| which must outlive the paint detector. static void SetTickClockForTesting(const base::TickClock* clock); - void Trace(blink::Visitor*); + void Trace(Visitor*); enum HadUserInput { kNoUserInput, kHadUserInput, kHadUserInputEnumMax }; diff --git a/chromium/third_party/blink/renderer/core/paint/first_meaningful_paint_detector_test.cc b/chromium/third_party/blink/renderer/core/paint/first_meaningful_paint_detector_test.cc index b4b2344a792..b448877213e 100644 --- a/chromium/third_party/blink/renderer/core/paint/first_meaningful_paint_detector_test.cc +++ b/chromium/third_party/blink/renderer/core/paint/first_meaningful_paint_detector_test.cc @@ -49,7 +49,7 @@ class FirstMeaningfulPaintDetectorTest : public PageTestBase { for (int i = 0; i < new_elements; i++) builder.Append("<span>a</span>"); GetDocument().write(builder.ToString()); - GetDocument().UpdateStyleAndLayout(); + GetDocument().UpdateStyleAndLayout(DocumentUpdateReason::kTest); Detector().NotifyPaint(); } @@ -62,15 +62,14 @@ class FirstMeaningfulPaintDetectorTest : public PageTestBase { void ClearFirstPaintSwapPromise() { platform()->AdvanceClock(base::TimeDelta::FromMilliseconds(1)); - GetPaintTiming().ReportSwapTime( - PaintEvent::kFirstPaint, WebWidgetClient::SwapResult::kDidSwap, Now()); + GetPaintTiming().ReportSwapTime(PaintEvent::kFirstPaint, + WebSwapResult::kDidSwap, Now()); } void ClearFirstContentfulPaintSwapPromise() { platform()->AdvanceClock(base::TimeDelta::FromMilliseconds(1)); GetPaintTiming().ReportSwapTime(PaintEvent::kFirstContentfulPaint, - WebWidgetClient::SwapResult::kDidSwap, - Now()); + WebSwapResult::kDidSwap, Now()); } void ClearProvisionalFirstMeaningfulPaintSwapPromise() { @@ -81,7 +80,7 @@ class FirstMeaningfulPaintDetectorTest : public PageTestBase { void ClearProvisionalFirstMeaningfulPaintSwapPromise( base::TimeTicks timestamp) { Detector().ReportSwapTime(PaintEvent::kProvisionalFirstMeaningfulPaint, - WebWidgetClient::SwapResult::kDidSwap, timestamp); + WebSwapResult::kDidSwap, timestamp); } unsigned OutstandingDetectorSwapPromiseCount() { 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 c0b43ea28f1..9ae6c02600d 100644 --- a/chromium/third_party/blink/renderer/core/paint/fragment_data.h +++ b/chromium/third_party/blink/renderer/core/paint/fragment_data.h @@ -100,15 +100,16 @@ class CORE_EXPORT FragmentData { EnsureRareData().logical_top_in_flow_thread = top; } - // The pagination offset is the additional factor to add in to map - // from flow thread coordinates relative to the enclosing pagination - // layer, to visual coordiantes relative to that pagination layer. - PhysicalOffset PaginationOffset() const { - return rare_data_ ? rare_data_->pagination_offset : PhysicalOffset(); + // The pagination offset is the additional factor to add in to map from flow + // thread coordinates relative to the enclosing pagination layer, to visual + // coordinates relative to that pagination layer. Not to be used in LayoutNG + // fragment painting. + PhysicalOffset LegacyPaginationOffset() const { + return rare_data_ ? rare_data_->legacy_pagination_offset : PhysicalOffset(); } - void SetPaginationOffset(const PhysicalOffset& pagination_offset) { + void SetLegacyPaginationOffset(const PhysicalOffset& pagination_offset) { if (rare_data_ || pagination_offset != PhysicalOffset()) - EnsureRareData().pagination_offset = pagination_offset; + EnsureRareData().legacy_pagination_offset = pagination_offset; } bool IsClipPathCacheValid() const { @@ -256,7 +257,7 @@ class CORE_EXPORT FragmentData { IntRect partial_invalidation_visual_rect; // Fragment specific data. - PhysicalOffset pagination_offset; + PhysicalOffset legacy_pagination_offset; LayoutUnit logical_top_in_flow_thread; std::unique_ptr<ObjectPaintProperties> paint_properties; std::unique_ptr<RefCountedPropertyTreeState> local_border_box_properties; diff --git a/chromium/third_party/blink/renderer/core/paint/frame_paint_timing.h b/chromium/third_party/blink/renderer/core/paint/frame_paint_timing.h index 0416f1138b9..1d6d98f6f76 100644 --- a/chromium/third_party/blink/renderer/core/paint/frame_paint_timing.h +++ b/chromium/third_party/blink/renderer/core/paint/frame_paint_timing.h @@ -33,7 +33,7 @@ class FramePaintTiming { private: GraphicsContext& context_; - Member<const LocalFrame> frame_; + const LocalFrame* frame_; DISALLOW_COPY_AND_ASSIGN(FramePaintTiming); }; diff --git a/chromium/third_party/blink/renderer/core/paint/frame_painter.h b/chromium/third_party/blink/renderer/core/paint/frame_painter.h index 05c33f8fba5..ee2368328ee 100644 --- a/chromium/third_party/blink/renderer/core/paint/frame_painter.h +++ b/chromium/third_party/blink/renderer/core/paint/frame_painter.h @@ -28,7 +28,7 @@ class FramePainter { private: const LocalFrameView& GetFrameView(); - Member<const LocalFrameView> frame_view_; + const LocalFrameView* frame_view_; static bool in_paint_contents_; DISALLOW_COPY_AND_ASSIGN(FramePainter); 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 dc2531bcd1b..c3b20fb9d6d 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 @@ -71,7 +71,7 @@ TEST_P(HTMLCanvasPainterTestForCAP, Canvas2DLayerAppearsInLayerTree) { // Insert a <canvas> and force it into accelerated mode. // Not using SetBodyInnerHTML() because we need to test before document // lifecyle update. - GetDocument().body()->SetInnerHTMLFromString("<canvas width=300 height=200>"); + GetDocument().body()->setInnerHTML("<canvas width=300 height=200>"); auto* element = To<HTMLCanvasElement>(GetDocument().body()->firstChild()); CanvasContextCreationAttributesCore attributes; attributes.alpha = true; 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 47b371c6cfa..4a824465ff5 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 @@ -101,7 +101,7 @@ void ImageElementTiming::NotifyImagePainted( it->value.is_painted_ = true; NotifyImagePaintedInternal(layout_object->GetNode(), *layout_object, *cached_image, current_paint_chunk_properties, - it->value.load_time_); + it->value.load_time_, nullptr); } } @@ -110,7 +110,8 @@ void ImageElementTiming::NotifyImagePaintedInternal( const LayoutObject& layout_object, const ImageResourceContent& cached_image, const PropertyTreeState& current_paint_chunk_properties, - base::TimeTicks load_time) { + base::TimeTicks load_time, + const IntRect* image_border) { LocalFrame* frame = GetSupplementable()->GetFrame(); DCHECK(frame == layout_object.GetDocument().GetFrame()); DCHECK(node); @@ -134,8 +135,13 @@ void ImageElementTiming::NotifyImagePaintedInternal( if (!layout_object.HasNonZeroEffectiveOpacity()) return; + RespectImageOrientationEnum respect_orientation = + LayoutObject::ShouldRespectImageOrientation(&layout_object); + FloatRect intersection_rect = ElementTimingUtils::ComputeIntersectionRect( - frame, layout_object.FirstFragment().VisualRect(), + frame, + image_border ? *image_border + : layout_object.FragmentsVisualRectBoundingBox(), current_paint_chunk_properties); const AtomicString attr = element->FastGetAttribute(html_names::kElementtimingAttr); @@ -147,25 +153,35 @@ void ImageElementTiming::NotifyImagePaintedInternal( DCHECK(layout_object.GetDocument().GetSecurityOrigin()); // It's ok to expose rendering timestamp for data URIs so exclude those from // the Timing-Allow-Origin check. - bool response_tainting_not_basic = false; - bool tainted_origin_flag = false; - if (!url.ProtocolIsData() && - !Performance::PassesTimingAllowCheck( + if (!url.ProtocolIsData()) { + bool timing_allow_check = false; + // Use the TimingAllowPassed() check from the response if OutOfBlinkCors is + // enabled. If it is not enabled then that flag is not computed, so use to + // the single PassesTimingAllowCheck(), which is incorrect because it does + // not check the full redirect chain. See crbug.com/1003943. + if (RuntimeEnabledFeatures::OutOfBlinkCorsEnabled()) { + timing_allow_check = cached_image.GetResponse().TimingAllowPassed(); + } else { + bool response_tainting_not_basic = false; + bool tainted_origin_flag = false; + timing_allow_check = Performance::PassesTimingAllowCheck( cached_image.GetResponse(), cached_image.GetResponse(), *layout_object.GetDocument().GetSecurityOrigin(), - &layout_object.GetDocument(), &response_tainting_not_basic, - &tainted_origin_flag)) { - WindowPerformance* performance = - DOMWindowPerformance::performance(*GetSupplementable()); - if (performance) { - // Create an entry with a |startTime| of 0. - performance->AddElementTiming( - ImagePaintString(), url.GetString(), intersection_rect, - base::TimeTicks(), load_time, attr, - cached_image.IntrinsicSize(kDoNotRespectImageOrientation), id, - element); + layout_object.GetDocument().ToExecutionContext(), + &response_tainting_not_basic, &tainted_origin_flag); + } + if (!timing_allow_check) { + WindowPerformance* performance = + DOMWindowPerformance::performance(*GetSupplementable()); + if (performance) { + // Create an entry with a |startTime| of 0. + performance->AddElementTiming( + ImagePaintString(), url.GetString(), intersection_rect, + base::TimeTicks(), load_time, attr, + cached_image.IntrinsicSize(respect_orientation), id, element); + } + return; } - return; } // If the image URL is a data URL ("data:image/..."), then the |name| of the @@ -177,7 +193,7 @@ void ImageElementTiming::NotifyImagePaintedInternal( : url.GetString(); element_timings_.emplace_back(MakeGarbageCollected<ElementTimingInfo>( image_url, intersection_rect, load_time, attr, - cached_image.IntrinsicSize(kDoNotRespectImageOrientation), id, element)); + cached_image.IntrinsicSize(respect_orientation), id, element)); // Only queue a swap promise when |element_timings_| was empty. All of the // records in |element_timings_| will be processed when the promise succeeds // or fails, and at that time the vector is cleared. @@ -192,7 +208,8 @@ void ImageElementTiming::NotifyImagePaintedInternal( void ImageElementTiming::NotifyBackgroundImagePainted( Node* node, const StyleFetchedImage* background_image, - const PropertyTreeState& current_paint_chunk_properties) { + const PropertyTreeState& current_paint_chunk_properties, + const IntRect& image_border) { DCHECK(node); DCHECK(background_image); @@ -218,11 +235,11 @@ void ImageElementTiming::NotifyBackgroundImagePainted( info.is_painted_ = true; NotifyImagePaintedInternal(layout_object->GetNode(), *layout_object, *cached_image, current_paint_chunk_properties, - it->value); + it->value, &image_border); } } -void ImageElementTiming::ReportImagePaintSwapTime(WebWidgetClient::SwapResult, +void ImageElementTiming::ReportImagePaintSwapTime(WebSwapResult, base::TimeTicks timestamp) { WindowPerformance* performance = DOMWindowPerformance::performance(*GetSupplementable()); @@ -243,7 +260,7 @@ void ImageElementTiming::NotifyImageRemoved(const LayoutObject* layout_object, images_notified_.erase(std::make_pair(layout_object, image)); } -void ImageElementTiming::Trace(blink::Visitor* visitor) { +void ImageElementTiming::Trace(Visitor* visitor) { visitor->Trace(element_timings_); visitor->Trace(background_image_timestamps_); Supplement<LocalDOMWindow>::Trace(visitor); diff --git a/chromium/third_party/blink/renderer/core/paint/image_element_timing.h b/chromium/third_party/blink/renderer/core/paint/image_element_timing.h index 1f6ac8fbcd0..18db86a4047 100644 --- a/chromium/third_party/blink/renderer/core/paint/image_element_timing.h +++ b/chromium/third_party/blink/renderer/core/paint/image_element_timing.h @@ -7,7 +7,7 @@ #include <utility> -#include "third_party/blink/public/web/web_widget_client.h" +#include "third_party/blink/public/web/web_swap_result.h" #include "third_party/blink/renderer/core/dom/element.h" #include "third_party/blink/renderer/core/frame/local_dom_window.h" #include "third_party/blink/renderer/platform/heap/heap_allocator.h" @@ -57,12 +57,13 @@ class CORE_EXPORT ImageElementTiming final void NotifyBackgroundImagePainted( Node*, const StyleFetchedImage* background_image, - const PropertyTreeState& current_paint_chunk_properties); + const PropertyTreeState& current_paint_chunk_properties, + const IntRect& image_border); void NotifyImageRemoved(const LayoutObject*, const ImageResourceContent* image); - void Trace(blink::Visitor*) override; + void Trace(Visitor*) override; private: friend class ImageElementTimingTest; @@ -72,11 +73,11 @@ class CORE_EXPORT ImageElementTiming final const LayoutObject&, const ImageResourceContent& cached_image, const PropertyTreeState& current_paint_chunk_properties, - base::TimeTicks load_time); + base::TimeTicks load_time, + const IntRect* image_border); // Callback for the swap promise. Reports paint timestamps. - void ReportImagePaintSwapTime(WebWidgetClient::SwapResult, - base::TimeTicks timestamp); + void ReportImagePaintSwapTime(WebSwapResult, base::TimeTicks timestamp); // Class containing information about image element timing. class ElementTimingInfo final : public GarbageCollected<ElementTimingInfo> { @@ -97,7 +98,7 @@ class CORE_EXPORT ImageElementTiming final element(element) {} ~ElementTimingInfo() = default; - void Trace(blink::Visitor* visitor) { visitor->Trace(element); } + void Trace(Visitor* visitor) { visitor->Trace(element); } String url; FloatRect rect; diff --git a/chromium/third_party/blink/renderer/core/paint/image_element_timing_test.cc b/chromium/third_party/blink/renderer/core/paint/image_element_timing_test.cc index 1349b35b968..c39619a98a3 100644 --- a/chromium/third_party/blink/renderer/core/paint/image_element_timing_test.cc +++ b/chromium/third_party/blink/renderer/core/paint/image_element_timing_test.cc @@ -10,6 +10,7 @@ #include "third_party/blink/renderer/core/layout/svg/layout_svg_image.h" #include "third_party/blink/renderer/core/loader/resource/image_resource_content.h" #include "third_party/blink/renderer/platform/graphics/unaccelerated_static_bitmap_image.h" +#include "third_party/blink/renderer/platform/testing/paint_test_configurations.h" #include "third_party/blink/renderer/platform/testing/url_test_helpers.h" #include "third_party/skia/include/core/SkImage.h" #include "third_party/skia/include/core/SkSurface.h" @@ -22,7 +23,8 @@ extern bool IsExplicitlyRegisteredForTiming(const LayoutObject* layout_object); } -class ImageElementTimingTest : public testing::Test { +class ImageElementTimingTest : public testing::Test, + public PaintTestConfigurations { protected: void SetUp() override { web_view_helper_.Initialize(); @@ -87,8 +89,7 @@ class ImageElementTimingTest : public testing::Test { ->MainFrameImpl() ->GetFrame() ->View() - ->UpdateAllLifecyclePhases( - DocumentLifecycle::LifecycleUpdateReason::kTest); + ->UpdateAllLifecyclePhases(DocumentUpdateReason::kTest); } frame_test_helpers::WebViewHelper web_view_helper_; @@ -108,13 +109,18 @@ class ImageElementTimingTest : public testing::Test { } }; -TEST_F(ImageElementTimingTest, TestIsExplicitlyRegisteredForTiming) { +INSTANTIATE_PAINT_TEST_SUITE_P(ImageElementTimingTest); + +TEST_P(ImageElementTimingTest, TestIsExplicitlyRegisteredForTiming) { frame_test_helpers::LoadHTMLString( web_view_helper_.GetWebView()->MainFrameImpl(), R"HTML( <img id="missing-attribute" style='width: 100px; height: 100px;'/> - <img id="unset-attribute" elementtiming style='width: 100px; height: 100px;'/> - <img id="empty-attribute" elementtiming="" style='width: 100px; height: 100px;'/> - <img id="valid-attribute" elementtiming="valid-id" style='width: 100px; height: 100px;'/> + <img id="unset-attribute" elementtiming + style='width: 100px; height: 100px;'/> + <img id="empty-attribute" elementtiming="" + style='width: 100px; height: 100px;'/> + <img id="valid-attribute" elementtiming="valid-id" + style='width: 100px; height: 100px;'/> )HTML", base_url_); @@ -142,7 +148,7 @@ TEST_F(ImageElementTimingTest, TestIsExplicitlyRegisteredForTiming) { "should be explicitly registered."; } -TEST_F(ImageElementTimingTest, IgnoresUnmarkedElement) { +TEST_P(ImageElementTimingTest, IgnoresUnmarkedElement) { // Tests that, if the 'elementtiming' attribute is missing, the element isn't // considered by ImageElementTiming. frame_test_helpers::LoadHTMLString( @@ -157,12 +163,13 @@ TEST_F(ImageElementTimingTest, IgnoresUnmarkedElement) { std::make_pair(layout_image, layout_image->CachedImage()))); } -TEST_F(ImageElementTimingTest, ImageInsideSVG) { +TEST_P(ImageElementTimingTest, ImageInsideSVG) { frame_test_helpers::LoadHTMLString( web_view_helper_.GetWebView()->MainFrameImpl(), R"HTML( <svg> <foreignObject width="100" height="100"> - <img elementtiming="image-inside-svg" id="target" style='width: 100px; height: 100px;'/> + <img elementtiming="image-inside-svg" id="target" + style='width: 100px; height: 100px;'/> </foreignObject> </svg> )HTML", @@ -176,13 +183,14 @@ TEST_F(ImageElementTimingTest, ImageInsideSVG) { std::make_pair(layout_image, layout_image->CachedImage()))); } -TEST_F(ImageElementTimingTest, ImageInsideNonRenderedSVG) { +TEST_P(ImageElementTimingTest, ImageInsideNonRenderedSVG) { frame_test_helpers::LoadHTMLString( web_view_helper_.GetWebView()->MainFrameImpl(), R"HTML( <svg mask="url(#mask)"> <mask id="mask"> <foreignObject width="100" height="100"> - <img elementtiming="image-inside-svg" id="target" style='width: 100px; height: 100px;'/> + <img elementtiming="image-inside-svg" id="target" + style='width: 100px; height: 100px;'/> </foreignObject> </mask> <rect width="100" height="100" fill="green"/> @@ -196,10 +204,11 @@ TEST_F(ImageElementTimingTest, ImageInsideNonRenderedSVG) { EXPECT_FALSE(GetLayoutObjectById("target")); } -TEST_F(ImageElementTimingTest, ImageRemoved) { +TEST_P(ImageElementTimingTest, ImageRemoved) { frame_test_helpers::LoadHTMLString( web_view_helper_.GetWebView()->MainFrameImpl(), R"HTML( - <img elementtiming="will-be-removed" id="target" style='width: 100px; height: 100px;'/> + <img elementtiming="will-be-removed" id="target" + style='width: 100px; height: 100px;'/> )HTML", base_url_); LayoutImage* layout_image = SetImageResource("target", 5, 5); @@ -214,11 +223,12 @@ TEST_F(ImageElementTimingTest, ImageRemoved) { EXPECT_EQ(ImagesNotifiedSize(), 0u); } -TEST_F(ImageElementTimingTest, SVGImageRemoved) { +TEST_P(ImageElementTimingTest, SVGImageRemoved) { frame_test_helpers::LoadHTMLString( web_view_helper_.GetWebView()->MainFrameImpl(), R"HTML( <svg> - <image elementtiming="svg-will-be-removed" id="target" style='width: 100px; height: 100px;'/> + <image elementtiming="svg-will-be-removed" id="target" + style='width: 100px; height: 100px;'/> </svg> )HTML", base_url_); @@ -234,7 +244,7 @@ TEST_F(ImageElementTimingTest, SVGImageRemoved) { EXPECT_EQ(ImagesNotifiedSize(), 0u); } -TEST_F(ImageElementTimingTest, BackgroundImageRemoved) { +TEST_P(ImageElementTimingTest, BackgroundImageRemoved) { frame_test_helpers::LoadHTMLString( web_view_helper_.GetWebView()->MainFrameImpl(), R"HTML( <style> 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 6f8d03d3cda..eea55437e84 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 @@ -3,6 +3,7 @@ // found in the LICENSE file. #include "third_party/blink/renderer/core/paint/image_paint_timing_detector.h" +#include "third_party/blink/public/platform/web_float_rect.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_image_resource.h" @@ -215,7 +216,8 @@ void ImagePaintTimingDetector::RecordImage( const IntSize& intrinsic_size, const ImageResourceContent& cached_image, const PropertyTreeState& current_paint_chunk_properties, - const StyleFetchedImage* style_image) { + const StyleFetchedImage* style_image, + const IntRect* image_border) { Node* node = object.GetNode(); if (!node) return; @@ -234,7 +236,8 @@ void ImagePaintTimingDetector::RecordImage( frame_view_->GetPaintTimingDetector().Visualizer()) { FloatRect mapped_visual_rect = frame_view_->GetPaintTimingDetector().CalculateVisualRect( - object.FragmentsVisualRectBoundingBox(), + image_border ? *image_border + : object.FragmentsVisualRectBoundingBox(), current_paint_chunk_properties); visualizer->DumpImageDebuggingRect(object, mapped_visual_rect, cached_image); @@ -244,7 +247,8 @@ void ImagePaintTimingDetector::RecordImage( if (is_recored_visible_image || !is_recording_) return; - IntRect visual_rect = object.FragmentsVisualRectBoundingBox(); + IntRect visual_rect = + image_border ? *image_border : object.FragmentsVisualRectBoundingBox(); // Before the image resource starts loading, <img> has no size info. We wait // until the size is known. if (visual_rect.IsEmpty()) @@ -338,7 +342,7 @@ ImageRecord* ImageRecordsManager::FindLargestPaintCandidate() const { return size_ordered_set_.begin()->get(); } -void ImagePaintTimingDetector::Trace(blink::Visitor* visitor) { +void ImagePaintTimingDetector::Trace(Visitor* visitor) { visitor->Trace(frame_view_); visitor->Trace(callback_manager_); } 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 b18203fdb9e..29c490a6481 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 @@ -213,7 +213,8 @@ class CORE_EXPORT ImagePaintTimingDetector final const IntSize& intrinsic_size, const ImageResourceContent&, const PropertyTreeState& current_paint_chunk_properties, - const StyleFetchedImage*); + const StyleFetchedImage*, + const IntRect* image_border); void NotifyImageFinished(const LayoutObject&, const ImageResourceContent*); void OnPaintFinished(); void LayoutObjectWillBeDestroyed(const LayoutObject&); @@ -236,7 +237,7 @@ class CORE_EXPORT ImagePaintTimingDetector final // Return the candidate. ImageRecord* UpdateCandidate(); - void Trace(blink::Visitor*); + void Trace(Visitor*); private: friend class LargestContentfulPaintCalculatorTest; 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 6b606789d33..66125f8a9c2 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 @@ -25,7 +25,7 @@ #include "third_party/blink/renderer/platform/graphics/unaccelerated_static_bitmap_image.h" #include "third_party/blink/renderer/platform/heap/heap.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/paint_test_configurations.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/skia/include/core/SkImage.h" @@ -33,7 +33,25 @@ namespace blink { -class ImagePaintTimingDetectorTest : public testing::Test { +#define SIMPLE_IMAGE \ + "url(data:image/gif;base64," \ + "R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==)" + +#define LARGE_IMAGE \ + "url(data:image/gif;base64," \ + "iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABHNCSVQICAgIfAhkiAAAAAlwSF" \ + "lzAAAN1wAADdcBQiibeAAAAb5JREFUOMulkr1KA0EQgGdvTwwnYmER0gQsrFKmSy+pLESw9Qm0" \ + "F/ICNnba+h6iEOuAEWslKJKTOyJJvIT72d1xZuOFC0giOLA77O7Mt/PnNptN+I+49Xr9GhH3f3" \ + "mb0v1ht9vtLAUYYw5ItkgDL3KyD8PhcLvdbl/WarXT3DjLMnAcR/f7/YfxeKwtgC5RKQVhGILW" \ + "eg4hQ6hUKjWyucmhLFEUuWR3QYBWAZABQ9i5CCmXy16pVALP80BKaaG+70MQBLvzFMjRKKXh8j" \ + "6FSYKF7ITdEWLa4/ktokN74wiqjSMpnVcbQZqmEJHz+ckeCPFjWKwULpyspAqhdXVXdcnZcPjs" \ + "Ign+2BsVA8jVYuWlgJ3yBj0icgq2uoK+lg4t+ZvLomSKamSQ4AI5BcMADtMhyNoSgNIISUaFNt" \ + "wlazcDcBc4gjjVwCWid2usCWroYEhnaqbzFJLUzAHIXRDChXCcQP8zhkSZ5eNLgHAUzwDcRu4C" \ + "oIRn/wsGUQIIy4Vr9TH6SYFCNzw4nALn5627K4vIttOUOwfa5YnrDYzt/9OLv9I5l8kk5hZ3XL" \ + "O20b7tbR7zHLy/BX8G0IeBEM7ZN1NGIaFUaKLgAAAAAElFTkSuQmCC)" + +class ImagePaintTimingDetectorTest : public testing::Test, + public PaintTestConfigurations { public: ImagePaintTimingDetectorTest() : test_task_runner_( @@ -152,8 +170,7 @@ class ImagePaintTimingDetectorTest : public testing::Test { } void UpdateAllLifecyclePhases() { - GetDocument().View()->UpdateAllLifecyclePhases( - DocumentLifecycle::LifecycleUpdateReason::kTest); + GetDocument().View()->UpdateAllLifecyclePhases(DocumentUpdateReason::kTest); } void UpdateAllLifecyclePhasesAndInvokeCallbackIfAny() { @@ -177,8 +194,7 @@ class ImagePaintTimingDetectorTest : public testing::Test { void SetChildBodyInnerHTML(const String& content) { GetChildDocument()->SetBaseURLOverride(KURL("http://test.com")); - GetChildDocument()->body()->SetInnerHTMLFromString(content, - ASSERT_NO_EXCEPTION); + GetChildDocument()->body()->setInnerHTML(content, ASSERT_NO_EXCEPTION); child_mock_callback_manager_ = MakeGarbageCollected<MockPaintTimingCallbackManager>(); GetChildPaintTimingDetector() @@ -235,7 +251,13 @@ class ImagePaintTimingDetectorTest : public testing::Test { To<SVGImageElement>(element)->SetImageForTest(content); } - void SimulateScroll() { GetPaintTimingDetector().NotifyScroll(kUserScroll); } + void SimulateScroll() { + GetPaintTimingDetector().NotifyScroll(mojom::blink::ScrollType::kUser); + } + + void SimulateKeyUp() { + GetPaintTimingDetector().NotifyInputEvent(WebInputEvent::kKeyUp); + } scoped_refptr<base::TestMockTimeTaskRunner> test_task_runner_; frame_test_helpers::WebViewHelper web_view_helper_; @@ -266,7 +288,9 @@ class ImagePaintTimingDetectorTest : public testing::Test { constexpr base::TimeDelta ImagePaintTimingDetectorTest::kQuantumOfTime; -TEST_F(ImagePaintTimingDetectorTest, LargestImagePaint_NoImage) { +INSTANTIATE_PAINT_TEST_SUITE_P(ImagePaintTimingDetectorTest); + +TEST_P(ImagePaintTimingDetectorTest, LargestImagePaint_NoImage) { SetBodyInnerHTML(R"HTML( <div></div> )HTML"); @@ -274,7 +298,7 @@ TEST_F(ImagePaintTimingDetectorTest, LargestImagePaint_NoImage) { EXPECT_FALSE(record); } -TEST_F(ImagePaintTimingDetectorTest, LargestImagePaint_OneImage) { +TEST_P(ImagePaintTimingDetectorTest, LargestImagePaint_OneImage) { SetBodyInnerHTML(R"HTML( <img id="target"></img> )HTML"); @@ -286,7 +310,7 @@ TEST_F(ImagePaintTimingDetectorTest, LargestImagePaint_OneImage) { EXPECT_TRUE(record->loaded); } -TEST_F(ImagePaintTimingDetectorTest, InsertionOrderIsSecondaryRankingKey) { +TEST_P(ImagePaintTimingDetectorTest, InsertionOrderIsSecondaryRankingKey) { SetBodyInnerHTML(R"HTML( )HTML"); @@ -311,7 +335,7 @@ TEST_F(ImagePaintTimingDetectorTest, InsertionOrderIsSecondaryRankingKey) { DOMNodeIds::ExistingIdForNode(image1)); } -TEST_F(ImagePaintTimingDetectorTest, LargestImagePaint_TraceEvent_Candidate) { +TEST_P(ImagePaintTimingDetectorTest, LargestImagePaint_TraceEvent_Candidate) { using trace_analyzer::Query; trace_analyzer::Start("loading"); { @@ -352,7 +376,7 @@ TEST_F(ImagePaintTimingDetectorTest, LargestImagePaint_TraceEvent_Candidate) { EXPECT_EQ(false, isOOPIF); } -TEST_F(ImagePaintTimingDetectorTest, LargestImagePaint_TraceEvent_NoCandidate) { +TEST_P(ImagePaintTimingDetectorTest, LargestImagePaint_TraceEvent_NoCandidate) { using trace_analyzer::Query; trace_analyzer::Start("*"); { @@ -402,7 +426,7 @@ TEST_F(ImagePaintTimingDetectorTest, LargestImagePaint_TraceEvent_NoCandidate) { } } -TEST_F(ImagePaintTimingDetectorTest, UpdatePerformanceTiming) { +TEST_P(ImagePaintTimingDetectorTest, UpdatePerformanceTiming) { EXPECT_EQ(GetPerformanceTiming().LargestImagePaintSize(), 0u); EXPECT_EQ(GetPerformanceTiming().LargestImagePaint(), 0u); SetBodyInnerHTML(R"HTML( @@ -414,7 +438,7 @@ TEST_F(ImagePaintTimingDetectorTest, UpdatePerformanceTiming) { EXPECT_GT(GetPerformanceTiming().LargestImagePaint(), 0u); } -TEST_F(ImagePaintTimingDetectorTest, +TEST_P(ImagePaintTimingDetectorTest, PerformanceTimingHasZeroTimeNonZeroSizeWhenTheLargestIsNotPainted) { EXPECT_EQ(GetPerformanceTiming().LargestImagePaintSize(), 0u); EXPECT_EQ(GetPerformanceTiming().LargestImagePaint(), 0u); @@ -427,7 +451,7 @@ TEST_F(ImagePaintTimingDetectorTest, EXPECT_EQ(GetPerformanceTiming().LargestImagePaint(), 0u); } -TEST_F(ImagePaintTimingDetectorTest, UpdatePerformanceTimingToZero) { +TEST_P(ImagePaintTimingDetectorTest, UpdatePerformanceTimingToZero) { SetBodyInnerHTML(R"HTML( <img id="target"></img> )HTML"); @@ -441,7 +465,7 @@ TEST_F(ImagePaintTimingDetectorTest, UpdatePerformanceTimingToZero) { EXPECT_EQ(GetPerformanceTiming().LargestImagePaint(), 0u); } -TEST_F(ImagePaintTimingDetectorTest, LargestImagePaint_OpacityZero) { +TEST_P(ImagePaintTimingDetectorTest, LargestImagePaint_OpacityZero) { SetBodyInnerHTML(R"HTML( <style> img { @@ -457,7 +481,7 @@ TEST_F(ImagePaintTimingDetectorTest, LargestImagePaint_OpacityZero) { EXPECT_FALSE(record); } -TEST_F(ImagePaintTimingDetectorTest, LargestImagePaint_VisibilityHidden) { +TEST_P(ImagePaintTimingDetectorTest, LargestImagePaint_VisibilityHidden) { SetBodyInnerHTML(R"HTML( <style> img { @@ -473,7 +497,7 @@ TEST_F(ImagePaintTimingDetectorTest, LargestImagePaint_VisibilityHidden) { EXPECT_FALSE(record); } -TEST_F(ImagePaintTimingDetectorTest, LargestImagePaint_DisplayNone) { +TEST_P(ImagePaintTimingDetectorTest, LargestImagePaint_DisplayNone) { SetBodyInnerHTML(R"HTML( <style> img { @@ -489,7 +513,7 @@ TEST_F(ImagePaintTimingDetectorTest, LargestImagePaint_DisplayNone) { EXPECT_FALSE(record); } -TEST_F(ImagePaintTimingDetectorTest, LargestImagePaint_OpacityNonZero) { +TEST_P(ImagePaintTimingDetectorTest, LargestImagePaint_OpacityNonZero) { SetBodyInnerHTML(R"HTML( <style> img { @@ -505,7 +529,7 @@ TEST_F(ImagePaintTimingDetectorTest, LargestImagePaint_OpacityNonZero) { EXPECT_TRUE(record); } -TEST_F(ImagePaintTimingDetectorTest, +TEST_P(ImagePaintTimingDetectorTest, IgnoreImageUntilInvalidatedRectSizeNonZero) { SetBodyInnerHTML(R"HTML( <img id="target"></img> @@ -519,7 +543,7 @@ TEST_F(ImagePaintTimingDetectorTest, EXPECT_EQ(CountVisibleImageRecords(), 1u); } -TEST_F(ImagePaintTimingDetectorTest, LargestImagePaint_Largest) { +TEST_P(ImagePaintTimingDetectorTest, LargestImagePaint_Largest) { SetBodyInnerHTML(R"HTML( <style>img { display:block }</style> <img id="smaller"></img> @@ -537,7 +561,7 @@ TEST_F(ImagePaintTimingDetectorTest, LargestImagePaint_Largest) { UpdateAllLifecyclePhasesAndInvokeCallbackIfAny(); } -TEST_F(ImagePaintTimingDetectorTest, +TEST_P(ImagePaintTimingDetectorTest, LargestImagePaint_IgnoreThoseOutsideViewport) { SetBodyInnerHTML(R"HTML( <style> @@ -554,7 +578,7 @@ TEST_F(ImagePaintTimingDetectorTest, EXPECT_FALSE(record); } -TEST_F(ImagePaintTimingDetectorTest, +TEST_P(ImagePaintTimingDetectorTest, LargestImagePaint_UpdateOnRemovingTheLastImage) { SetBodyInnerHTML(R"HTML( <div id="parent"> @@ -576,7 +600,7 @@ TEST_F(ImagePaintTimingDetectorTest, EXPECT_EQ(LargestPaintStoredResult(), base::TimeTicks()); } -TEST_F(ImagePaintTimingDetectorTest, LargestImagePaint_UpdateOnRemoving) { +TEST_P(ImagePaintTimingDetectorTest, LargestImagePaint_UpdateOnRemoving) { SetBodyInnerHTML(R"HTML( <div id="parent"> <img id="target1"></img> @@ -608,7 +632,7 @@ TEST_F(ImagePaintTimingDetectorTest, LargestImagePaint_UpdateOnRemoving) { EXPECT_EQ(first_largest_image_paint, LargestPaintStoredResult()); } -TEST_F(ImagePaintTimingDetectorTest, +TEST_P(ImagePaintTimingDetectorTest, LargestImagePaint_NodeRemovedBetweenRegistrationAndInvocation) { SetBodyInnerHTML(R"HTML( <div id="parent"> @@ -628,7 +652,7 @@ TEST_F(ImagePaintTimingDetectorTest, EXPECT_FALSE(record); } -TEST_F(ImagePaintTimingDetectorTest, +TEST_P(ImagePaintTimingDetectorTest, RemoveRecordFromAllContainersAfterImageRemoval) { SetBodyInnerHTML(R"HTML( <div id="parent"> @@ -644,8 +668,12 @@ TEST_F(ImagePaintTimingDetectorTest, EXPECT_EQ(ContainerTotalSize(), 0u); } -TEST_F(ImagePaintTimingDetectorTest, +TEST_P(ImagePaintTimingDetectorTest, RemoveRecordFromAllContainersAfterInvisibleImageRemoved) { + // TODO(wangxianzhu): Fix this test for CompositeAfterPaint. + if (RuntimeEnabledFeatures::CompositeAfterPaintEnabled()) + return; + SetBodyInnerHTML(R"HTML( <style> #target { @@ -673,12 +701,12 @@ TEST_F(ImagePaintTimingDetectorTest, EXPECT_EQ(CountInvisibleRecords(), 0u); } -TEST_F(ImagePaintTimingDetectorTest, +TEST_P(ImagePaintTimingDetectorTest, RemoveRecordFromAllContainersAfterBackgroundImageRemoval) { SetBodyInnerHTML(R"HTML( <style> #target { - background-image: url(data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==); + background-image: )HTML" SIMPLE_IMAGE R"HTML(; } </style> <div id="parent"> @@ -695,7 +723,7 @@ TEST_F(ImagePaintTimingDetectorTest, EXPECT_EQ(ContainerTotalSize(), 0u); } -TEST_F(ImagePaintTimingDetectorTest, +TEST_P(ImagePaintTimingDetectorTest, RemoveRecordFromAllContainersAfterImageRemovedAndCallbackInvoked) { SetBodyInnerHTML(R"HTML( <div id="parent"> @@ -713,7 +741,7 @@ TEST_F(ImagePaintTimingDetectorTest, EXPECT_EQ(ContainerTotalSize(), 0u); } -TEST_F(ImagePaintTimingDetectorTest, +TEST_P(ImagePaintTimingDetectorTest, LargestImagePaint_ReattachedNodeTreatedAsNew) { SetBodyInnerHTML(R"HTML( <div id="parent"> @@ -756,7 +784,7 @@ TEST_F(ImagePaintTimingDetectorTest, // 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) { +TEST_P(ImagePaintTimingDetectorTest, MatchSwapTimeToNodesOfDifferentFrames) { SetBodyInnerHTML(R"HTML( <div id="parent"> <img height="5" width="5" id="smaller"></img> @@ -784,7 +812,7 @@ TEST_F(ImagePaintTimingDetectorTest, MatchSwapTimeToNodesOfDifferentFrames) { EXPECT_NE(record1Time, record2->paint_time); } -TEST_F(ImagePaintTimingDetectorTest, +TEST_P(ImagePaintTimingDetectorTest, LargestImagePaint_UpdateResultWhenLargestChanged) { base::TimeTicks time1 = test_task_runner_->NowTicks(); SetBodyInnerHTML(R"HTML( @@ -808,7 +836,7 @@ TEST_F(ImagePaintTimingDetectorTest, EXPECT_GE(time3, result2); } -TEST_F(ImagePaintTimingDetectorTest, OneSwapPromiseForOneFrame) { +TEST_P(ImagePaintTimingDetectorTest, OneSwapPromiseForOneFrame) { SetBodyInnerHTML(R"HTML( <style>img { display:block }</style> <div id="parent"> @@ -840,7 +868,7 @@ TEST_F(ImagePaintTimingDetectorTest, OneSwapPromiseForOneFrame) { EXPECT_FALSE(record->paint_time.is_null()); } -TEST_F(ImagePaintTimingDetectorTest, VideoImage) { +TEST_P(ImagePaintTimingDetectorTest, VideoImage) { SetBodyInnerHTML(R"HTML( <video id="target"></video> )HTML"); @@ -854,17 +882,15 @@ TEST_F(ImagePaintTimingDetectorTest, VideoImage) { EXPECT_TRUE(record->loaded); } -TEST_F(ImagePaintTimingDetectorTest, VideoImage_ImageNotLoaded) { - SetBodyInnerHTML(R"HTML( - <video id="target"></video> - )HTML"); +TEST_P(ImagePaintTimingDetectorTest, VideoImage_ImageNotLoaded) { + SetBodyInnerHTML("<video id='target'></video>"); UpdateAllLifecyclePhasesAndInvokeCallbackIfAny(); ImageRecord* record = FindLargestPaintCandidate(); EXPECT_FALSE(record); } -TEST_F(ImagePaintTimingDetectorTest, SVGImage) { +TEST_P(ImagePaintTimingDetectorTest, SVGImage) { SetBodyInnerHTML(R"HTML( <svg> <image id="target" width="10" height="10"/> @@ -880,28 +906,26 @@ TEST_F(ImagePaintTimingDetectorTest, SVGImage) { EXPECT_TRUE(record->loaded); } -TEST_F(ImagePaintTimingDetectorTest, BackgroundImage) { +TEST_P(ImagePaintTimingDetectorTest, BackgroundImage) { SetBodyInnerHTML(R"HTML( <style> div { - background-image: url(data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==); + background-image: )HTML" SIMPLE_IMAGE R"HTML(; } </style> - <div> - place-holder - </div> + <div>place-holder</div> )HTML"); ImageRecord* record = FindLargestPaintCandidate(); EXPECT_TRUE(record); EXPECT_EQ(CountVisibleImageRecords(), 1u); } -TEST_F(ImagePaintTimingDetectorTest, +TEST_P(ImagePaintTimingDetectorTest, BackgroundImageAndLayoutImageTrackedDifferently) { SetBodyInnerHTML(R"HTML( <style> img { - background-image: url(data:image/gif;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAAN1wAADdcBQiibeAAAAb5JREFUOMulkr1KA0EQgGdvTwwnYmER0gQsrFKmSy+pLESw9Qm0F/ICNnba+h6iEOuAEWslKJKTOyJJvIT72d1xZuOFC0giOLA77O7Mt/PnNptN+I+49Xr9GhH3f3mb0v1ht9vtLAUYYw5ItkgDL3KyD8PhcLvdbl/WarXT3DjLMnAcR/f7/YfxeKwtgC5RKQVhGILWeg4hQ6hUKjWyucmhLFEUuWR3QYBWAZABQ9i5CCmXy16pVALP80BKaaG+70MQBLvzFMjRKKXh8j6FSYKF7ITdEWLa4/ktokN74wiqjSMpnVcbQZqmEJHz+ckeCPFjWKwULpyspAqhdXVXdcnZcPjsIgn+2BsVA8jVYuWlgJ3yBj0icgq2uoK+lg4t+ZvLomSKamSQ4AI5BcMADtMhyNoSgNIISUaFNtwlazcDcBc4gjjVwCWid2usCWroYEhnaqbzFJLUzAHIXRDChXCcQP8zhkSZ5eNLgHAUzwDcRu4CoIRn/wsGUQIIy4Vr9TH6SYFCNzw4nALn5627K4vIttOUOwfa5YnrDYzt/9OLv9I5l8kk5hZ3XLO20b7tbR7zHLy/BX8G0IeBEM7ZN1NGIaFUaKLgAAAAAElFTkSuQmCC); + background-image: )HTML" LARGE_IMAGE R"HTML(; } </style> <img id="target"> @@ -916,31 +940,17 @@ TEST_F(ImagePaintTimingDetectorTest, EXPECT_EQ(record->first_size, 1u); } -TEST_F(ImagePaintTimingDetectorTest, BackgroundImage_IgnoreBody) { - SetBodyInnerHTML(R"HTML( - <style> - body { - background-image: url(data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==); - } - </style> - )HTML"); +TEST_P(ImagePaintTimingDetectorTest, BackgroundImage_IgnoreBody) { + SetBodyInnerHTML("<style>body { background-image: " SIMPLE_IMAGE "}</style>"); EXPECT_EQ(CountVisibleImageRecords(), 0u); } -TEST_F(ImagePaintTimingDetectorTest, BackgroundImage_IgnoreHtml) { - SetBodyInnerHTML(R"HTML( - <html> - <style> - html { - background-image: url(data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==); - } - </style> - </html> - )HTML"); +TEST_P(ImagePaintTimingDetectorTest, BackgroundImage_IgnoreHtml) { + SetBodyInnerHTML("<style>html { background-image: " SIMPLE_IMAGE "}</style>"); EXPECT_EQ(CountVisibleImageRecords(), 0u); } -TEST_F(ImagePaintTimingDetectorTest, BackgroundImage_IgnoreGradient) { +TEST_P(ImagePaintTimingDetectorTest, BackgroundImage_IgnoreGradient) { SetBodyInnerHTML(R"HTML( <style> div { @@ -956,15 +966,14 @@ TEST_F(ImagePaintTimingDetectorTest, BackgroundImage_IgnoreGradient) { // We put two background images in the same object, and test whether FCP++ can // find two different images. -TEST_F(ImagePaintTimingDetectorTest, BackgroundImageTrackedDifferently) { +TEST_P(ImagePaintTimingDetectorTest, BackgroundImageTrackedDifferently) { SetBodyInnerHTML(R"HTML( <style> #d { width: 50px; height: 50px; background-image: - url("data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw=="), - url("data:image/gif;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAAN1wAADdcBQiibeAAAAb5JREFUOMulkr1KA0EQgGdvTwwnYmER0gQsrFKmSy+pLESw9Qm0F/ICNnba+h6iEOuAEWslKJKTOyJJvIT72d1xZuOFC0giOLA77O7Mt/PnNptN+I+49Xr9GhH3f3mb0v1ht9vtLAUYYw5ItkgDL3KyD8PhcLvdbl/WarXT3DjLMnAcR/f7/YfxeKwtgC5RKQVhGILWeg4hQ6hUKjWyucmhLFEUuWR3QYBWAZABQ9i5CCmXy16pVALP80BKaaG+70MQBLvzFMjRKKXh8j6FSYKF7ITdEWLa4/ktokN74wiqjSMpnVcbQZqmEJHz+ckeCPFjWKwULpyspAqhdXVXdcnZcPjsIgn+2BsVA8jVYuWlgJ3yBj0icgq2uoK+lg4t+ZvLomSKamSQ4AI5BcMADtMhyNoSgNIISUaFNtwlazcDcBc4gjjVwCWid2usCWroYEhnaqbzFJLUzAHIXRDChXCcQP8zhkSZ5eNLgHAUzwDcRu4CoIRn/wsGUQIIy4Vr9TH6SYFCNzw4nALn5627K4vIttOUOwfa5YnrDYzt/9OLv9I5l8kk5hZ3XLO20b7tbR7zHLy/BX8G0IeBEM7ZN1NGIaFUaKLgAAAAAElFTkSuQmCC"); + )HTML" SIMPLE_IMAGE "," LARGE_IMAGE R"HTML(; } </style> <div id="d"></div> @@ -972,7 +981,7 @@ TEST_F(ImagePaintTimingDetectorTest, BackgroundImageTrackedDifferently) { EXPECT_EQ(CountVisibleImageRecords(), 2u); } -TEST_F(ImagePaintTimingDetectorTest, DeactivateAfterUserInput) { +TEST_P(ImagePaintTimingDetectorTest, DeactivateAfterUserInput) { SetBodyInnerHTML(R"HTML( <div id="parent"> <img id="target"></img> @@ -984,7 +993,19 @@ TEST_F(ImagePaintTimingDetectorTest, DeactivateAfterUserInput) { EXPECT_FALSE(GetPaintTimingDetector().GetImagePaintTimingDetector()); } -TEST_F(ImagePaintTimingDetectorTest, NullTimeNoCrash) { +TEST_P(ImagePaintTimingDetectorTest, ContinueAfterKeyUp) { + SetBodyInnerHTML(R"HTML( + <div id="parent"> + <img id="target"></img> + </div> + )HTML"); + SimulateKeyUp(); + SetImageAndPaint("target", 5, 5); + UpdateAllLifecyclePhasesAndInvokeCallbackIfAny(); + EXPECT_TRUE(GetPaintTimingDetector().GetImagePaintTimingDetector()); +} + +TEST_P(ImagePaintTimingDetectorTest, NullTimeNoCrash) { SetBodyInnerHTML(R"HTML( <img id="target"></img> )HTML"); @@ -993,7 +1014,7 @@ TEST_F(ImagePaintTimingDetectorTest, NullTimeNoCrash) { UpdateCandidate(); } -TEST_F(ImagePaintTimingDetectorTest, Iframe) { +TEST_P(ImagePaintTimingDetectorTest, Iframe) { SetBodyInnerHTML(R"HTML( <iframe width=100px height=100px></iframe> )HTML"); @@ -1013,7 +1034,7 @@ TEST_F(ImagePaintTimingDetectorTest, Iframe) { EXPECT_EQ(image->first_size, 25ul); } -TEST_F(ImagePaintTimingDetectorTest, Iframe_ClippedByMainFrameViewport) { +TEST_P(ImagePaintTimingDetectorTest, Iframe_ClippedByMainFrameViewport) { SetBodyInnerHTML(R"HTML( <style> #f { margin-top: 1234567px } @@ -1031,7 +1052,7 @@ TEST_F(ImagePaintTimingDetectorTest, Iframe_ClippedByMainFrameViewport) { EXPECT_EQ(CountVisibleImageRecords(), 0u); } -TEST_F(ImagePaintTimingDetectorTest, Iframe_HalfClippedByMainFrameViewport) { +TEST_P(ImagePaintTimingDetectorTest, Iframe_HalfClippedByMainFrameViewport) { SetBodyInnerHTML(R"HTML( <style> #f { margin-left: -5px; } @@ -1052,7 +1073,7 @@ TEST_F(ImagePaintTimingDetectorTest, Iframe_HalfClippedByMainFrameViewport) { EXPECT_LT(image->first_size, 100ul); } -TEST_F(ImagePaintTimingDetectorTest, SameSizeShouldNotBeIgnored) { +TEST_P(ImagePaintTimingDetectorTest, SameSizeShouldNotBeIgnored) { SetBodyInnerHTML(R"HTML( <style>img { display:block }</style> <img id='1'></img> @@ -1066,7 +1087,7 @@ TEST_F(ImagePaintTimingDetectorTest, SameSizeShouldNotBeIgnored) { EXPECT_EQ(CountRankingSetRecords(), 3u); } -TEST_F(ImagePaintTimingDetectorTest, UseIntrinsicSizeIfSmaller_Image) { +TEST_P(ImagePaintTimingDetectorTest, UseIntrinsicSizeIfSmaller_Image) { SetBodyInnerHTML(R"HTML( <img height="300" width="300" display="block" id="target"> </img> @@ -1078,7 +1099,7 @@ TEST_F(ImagePaintTimingDetectorTest, UseIntrinsicSizeIfSmaller_Image) { EXPECT_EQ(record->first_size, 25u); } -TEST_F(ImagePaintTimingDetectorTest, NotUseIntrinsicSizeIfLarger_Image) { +TEST_P(ImagePaintTimingDetectorTest, NotUseIntrinsicSizeIfLarger_Image) { SetBodyInnerHTML(R"HTML( <img height="1" width="1" display="block" id="target"> </img> @@ -1090,14 +1111,14 @@ TEST_F(ImagePaintTimingDetectorTest, NotUseIntrinsicSizeIfLarger_Image) { EXPECT_EQ(record->first_size, 1u); } -TEST_F(ImagePaintTimingDetectorTest, +TEST_P(ImagePaintTimingDetectorTest, UseIntrinsicSizeIfSmaller_BackgroundImage) { SetBodyInnerHTML(R"HTML( <style> #d { width: 50px; height: 50px; - background-image: url("data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw=="); + background-image: )HTML" SIMPLE_IMAGE R"HTML(; } </style> <div id="d"></div> @@ -1107,7 +1128,7 @@ TEST_F(ImagePaintTimingDetectorTest, EXPECT_EQ(record->first_size, 1u); } -TEST_F(ImagePaintTimingDetectorTest, +TEST_P(ImagePaintTimingDetectorTest, NotUseIntrinsicSizeIfLarger_BackgroundImage) { // The image is in 16x16. SetBodyInnerHTML(R"HTML( @@ -1115,7 +1136,7 @@ TEST_F(ImagePaintTimingDetectorTest, #d { width: 5px; height: 5px; - background-image: url("data:image/gif;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAAN1wAADdcBQiibeAAAAb5JREFUOMulkr1KA0EQgGdvTwwnYmER0gQsrFKmSy+pLESw9Qm0F/ICNnba+h6iEOuAEWslKJKTOyJJvIT72d1xZuOFC0giOLA77O7Mt/PnNptN+I+49Xr9GhH3f3mb0v1ht9vtLAUYYw5ItkgDL3KyD8PhcLvdbl/WarXT3DjLMnAcR/f7/YfxeKwtgC5RKQVhGILWeg4hQ6hUKjWyucmhLFEUuWR3QYBWAZABQ9i5CCmXy16pVALP80BKaaG+70MQBLvzFMjRKKXh8j6FSYKF7ITdEWLa4/ktokN74wiqjSMpnVcbQZqmEJHz+ckeCPFjWKwULpyspAqhdXVXdcnZcPjsIgn+2BsVA8jVYuWlgJ3yBj0icgq2uoK+lg4t+ZvLomSKamSQ4AI5BcMADtMhyNoSgNIISUaFNtwlazcDcBc4gjjVwCWid2usCWroYEhnaqbzFJLUzAHIXRDChXCcQP8zhkSZ5eNLgHAUzwDcRu4CoIRn/wsGUQIIy4Vr9TH6SYFCNzw4nALn5627K4vIttOUOwfa5YnrDYzt/9OLv9I5l8kk5hZ3XLO20b7tbR7zHLy/BX8G0IeBEM7ZN1NGIaFUaKLgAAAAAElFTkSuQmCC"); + background-image: )HTML" LARGE_IMAGE R"HTML(; } </style> <div id="d"></div> 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 be0a21026fb..86470ade240 100644 --- a/chromium/third_party/blink/renderer/core/paint/image_painter.cc +++ b/chromium/third_party/blink/renderer/core/paint/image_painter.cc @@ -61,7 +61,7 @@ bool CheckForOversizedImagesPolicy(const LayoutImage& layout_image, cached_image ? cached_image->Url().GetString() : g_empty_string; return !layout_image.GetDocument().IsFeatureEnabled( - mojom::FeaturePolicyFeature::kOversizedImages, + mojom::blink::DocumentPolicyFeature::kOversizedImages, blink::PolicyValue( std::max(downscale_ratio_width, downscale_ratio_height), blink::mojom::PolicyValueType::kDecDouble), @@ -142,7 +142,7 @@ void ImagePainter::PaintReplaced(const PaintInfo& paint_info, if (content_size.IsEmpty()) return; } else { - if (paint_info.phase == PaintPhase::kSelection) + if (paint_info.phase == PaintPhase::kSelectionDragImage) return; if (content_size.Width() <= 2 || content_size.Height() <= 2) return; @@ -187,8 +187,8 @@ void ImagePainter::PaintReplaced(const PaintInfo& paint_info, void ImagePainter::PaintIntoRect(GraphicsContext& context, const PhysicalRect& dest_rect, const PhysicalRect& content_rect) { - if (!layout_image_.ImageResource()->HasImage() || - layout_image_.ImageResource()->ErrorOccurred()) + const LayoutImageResource& image_resource = *layout_image_.ImageResource(); + if (!image_resource.HasImage() || image_resource.ErrorOccurred()) return; // FIXME: should we just ASSERT these conditions? (audit all // callers). @@ -197,11 +197,14 @@ void ImagePainter::PaintIntoRect(GraphicsContext& context, return; scoped_refptr<Image> image = - layout_image_.ImageResource()->GetImage(pixel_snapped_dest_rect.Size()); + image_resource.GetImage(FloatSize(dest_rect.size)); if (!image || image->IsNull()) return; - FloatRect src_rect = FloatRect(image->Rect()); + // Do not respect the image orientation when computing the source rect. It is + // in the un-orientated dimensions. + FloatRect src_rect(FloatPoint(), + image->SizeAsFloat(kDoNotRespectImageOrientation)); // If the content rect requires clipping, adjust |srcRect| and // |pixelSnappedDestRect| over using a clip. if (!content_rect.Contains(dest_rect)) { @@ -249,17 +252,18 @@ void ImagePainter::PaintIntoRect(GraphicsContext& context, layout_image_.StyleRef().HasFilterInducingProperty(), SkBlendMode::kSrcOver, LayoutObject::ShouldRespectImageOrientation(&layout_image_)); + + ImageResourceContent* image_content = image_resource.CachedImage(); if ((IsA<HTMLImageElement>(node) || IsA<HTMLVideoElement>(node)) && - !context.ContextDisabled() && layout_image_.CachedImage() && - layout_image_.CachedImage()->IsLoaded()) { + image_content && image_content->IsLoaded()) { LocalDOMWindow* window = layout_image_.GetDocument().domWindow(); DCHECK(window); ImageElementTiming::From(*window).NotifyImagePainted( - &layout_image_, layout_image_.CachedImage(), + &layout_image_, image_content, context.GetPaintController().CurrentPaintChunkProperties()); } PaintTimingDetector::NotifyImagePaint( - layout_image_, image->Size(), layout_image_.CachedImage(), + layout_image_, image->Size(), image_content, context.GetPaintController().CurrentPaintChunkProperties()); } diff --git a/chromium/third_party/blink/renderer/core/paint/inline_box_painter_base.h b/chromium/third_party/blink/renderer/core/paint/inline_box_painter_base.h index 0dc82e10ad0..4c826eadfa4 100644 --- a/chromium/third_party/blink/renderer/core/paint/inline_box_painter_base.h +++ b/chromium/third_party/blink/renderer/core/paint/inline_box_painter_base.h @@ -84,8 +84,8 @@ class InlineBoxPainterBase { bool object_has_multiple_boxes) const = 0; const ImageResourceObserver& image_observer_; - Member<const Document> document_; - Member<Node> node_; + const Document* document_; + Node* node_; // Style for the corresponding node. const ComputedStyle& style_; 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 4c14b9f3a1c..36a2191b612 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 @@ -15,7 +15,6 @@ #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" #include "third_party/blink/renderer/platform/instrumentation/use_counter.h" namespace blink { @@ -186,12 +185,12 @@ void InlineFlowBoxPainter::PaintBackgroundBorderShadow( const LayoutPoint& paint_offset) { DCHECK(paint_info.phase == PaintPhase::kForeground); - RecordHitTestData(paint_info, paint_offset); - if (inline_flow_box_.GetLineLayoutItem().StyleRef().Visibility() != EVisibility::kVisible) return; + RecordHitTestData(paint_info, paint_offset); + // You can use p::first-line to specify a background. If so, the root line // boxes for a line may actually have to paint a background. LayoutObject* inline_flow_box_layout_object = @@ -340,17 +339,11 @@ 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->EffectiveAllowedTouchAction(); - if (touch_action == TouchAction::kTouchActionAuto) - return; + DCHECK_EQ(layout_object->StyleRef().Visibility(), EVisibility::kVisible); - HitTestDisplayItem::Record( - paint_info.context, inline_flow_box_, - HitTestRect(AdjustedPaintRect(paint_offset), touch_action)); + paint_info.context.GetPaintController().RecordHitTestData( + inline_flow_box_, PixelSnappedIntRect(AdjustedPaintRect(paint_offset)), + layout_object->EffectiveAllowedTouchAction()); } void InlineFlowBoxPainter::PaintNormalBoxShadow(const PaintInfo& info, diff --git a/chromium/third_party/blink/renderer/core/paint/inline_flow_box_painter.h b/chromium/third_party/blink/renderer/core/paint/inline_flow_box_painter.h index 9b239f13267..f4d6a2831bd 100644 --- a/chromium/third_party/blink/renderer/core/paint/inline_flow_box_painter.h +++ b/chromium/third_party/blink/renderer/core/paint/inline_flow_box_painter.h @@ -77,9 +77,9 @@ class InlineFlowBoxPainter : public InlineBoxPainterBase { LayoutRect AdjustedPaintRect(const LayoutPoint& paint_offset) const; - // Paint a hit test display item and record hit test data. This should be - // called when painting the background even if there is no other painted - // content. + // Expands the bounds of the current paint chunk for hit test, and records + // special touch action if any. 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 InlineFlowBox& inline_flow_box_; 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 8f5cb403a0f..065c32ba2e8 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 @@ -149,7 +149,7 @@ void InlineTextBoxPainter::Paint(const PaintInfo& paint_info, bool have_selection = !is_printing && paint_info.phase != PaintPhase::kTextClip && inline_text_box_.IsSelected(); - if (!have_selection && paint_info.phase == PaintPhase::kSelection) { + if (!have_selection && paint_info.phase == PaintPhase::kSelectionDragImage) { // When only painting the selection, don't bother to paint if there is none. return; } @@ -264,7 +264,8 @@ void InlineTextBoxPainter::Paint(const PaintInfo& paint_info, inline_text_box_.GetLineLayoutItem().GetDocument(), style_to_use, inline_text_box_.GetLineLayoutItem().GetNode(), have_selection, paint_info, text_style); - bool paint_selected_text_only = (paint_info.phase == PaintPhase::kSelection); + bool paint_selected_text_only = + (paint_info.phase == PaintPhase::kSelectionDragImage); bool paint_selected_text_separately = !paint_selected_text_only && text_style != selection_style; @@ -280,7 +281,7 @@ void InlineTextBoxPainter::Paint(const PaintInfo& paint_info, // 1. Paint backgrounds behind text if needed. Examples of such backgrounds // include selection and composition highlights. - if (paint_info.phase != PaintPhase::kSelection && + if (paint_info.phase != PaintPhase::kSelectionDragImage && paint_info.phase != PaintPhase::kTextClip && !is_printing) { PaintDocumentMarkers(markers_to_paint, paint_info, box_origin, style_to_use, font, DocumentMarkerPaintPhase::kBackground); diff --git a/chromium/third_party/blink/renderer/core/paint/largest_contentful_paint_calculator.cc b/chromium/third_party/blink/renderer/core/paint/largest_contentful_paint_calculator.cc index d5ab8898d63..dd110c79c0b 100644 --- a/chromium/third_party/blink/renderer/core/paint/largest_contentful_paint_calculator.cc +++ b/chromium/third_party/blink/renderer/core/paint/largest_contentful_paint_calculator.cc @@ -68,11 +68,13 @@ void LargestContentfulPaintCalculator::UpdateLargestContentPaintIfNeeded( bool text_has_changed = false; if (largest_image.has_value()) { image_has_changed = HasLargestImageChanged(largest_image_, *largest_image); - OnLargestImageUpdated(*largest_image); + if (image_has_changed) + OnLargestImageUpdated(*largest_image); } if (largest_text.has_value()) { text_has_changed = HasLargestTextChanged(largest_text_, *largest_text); - OnLargestTextUpdated(*largest_text); + if (text_has_changed) + OnLargestTextUpdated(*largest_text); } // If |largest_image| does not have value, the detector may have been // destroyed. In this case, keep using its last candidate for comparison with @@ -121,17 +123,23 @@ void LargestContentfulPaintCalculator::UpdateLargestContentfulPaint( return; const KURL& url = cached_image->Url(); - auto* document = window_performance_->GetExecutionContext(); - bool expose_paint_time_to_api = true; - bool response_tainting_not_basic = false; - bool tainted_origin_flag = false; - if (!url.ProtocolIsData() && - (!document || - !Performance::PassesTimingAllowCheck( - cached_image->GetResponse(), cached_image->GetResponse(), - *document->GetSecurityOrigin(), document, - &response_tainting_not_basic, &tainted_origin_flag))) { - expose_paint_time_to_api = false; + bool expose_paint_time_to_api = url.ProtocolIsData(); + // Use TimingAllowPassed() if possible, see comment in ImageElementTiming. + if (!expose_paint_time_to_api) { + if (RuntimeEnabledFeatures::OutOfBlinkCorsEnabled()) { + expose_paint_time_to_api = + cached_image->GetResponse().TimingAllowPassed(); + } else { + auto* document = window_performance_->GetExecutionContext(); + bool response_tainting_not_basic = false; + bool tainted_origin_flag = false; + expose_paint_time_to_api = + document && + Performance::PassesTimingAllowCheck( + cached_image->GetResponse(), cached_image->GetResponse(), + *document->GetSecurityOrigin(), document, + &response_tainting_not_basic, &tainted_origin_flag); + } } const String& image_url = url.ProtocolIsData() diff --git a/chromium/third_party/blink/renderer/core/paint/largest_contentful_paint_calculator.h b/chromium/third_party/blink/renderer/core/paint/largest_contentful_paint_calculator.h index 72de5f84b10..45d35a591a0 100644 --- a/chromium/third_party/blink/renderer/core/paint/largest_contentful_paint_calculator.h +++ b/chromium/third_party/blink/renderer/core/paint/largest_contentful_paint_calculator.h @@ -23,7 +23,7 @@ class CORE_EXPORT LargestContentfulPaintCalculator final base::Optional<base::WeakPtr<TextRecord>> largest_text, base::Optional<const ImageRecord*> largest_image); - void Trace(blink::Visitor* visitor); + void Trace(Visitor* visitor); private: friend class LargestContentfulPaintCalculatorTest; diff --git a/chromium/third_party/blink/renderer/core/paint/line_box_list_painter.cc b/chromium/third_party/blink/renderer/core/paint/line_box_list_painter.cc index 3df1a8f2a29..1bbdc7dcc45 100644 --- a/chromium/third_party/blink/renderer/core/paint/line_box_list_painter.cc +++ b/chromium/third_party/blink/renderer/core/paint/line_box_list_painter.cc @@ -74,9 +74,7 @@ void BuildBackplate(const InlineFlowBox* box, } } -} // anonymous namespace - -static void AddURLRectsForInlineChildrenRecursively( +void AddURLRectsForInlineChildrenRecursively( const LayoutObject& layout_object, const PaintInfo& paint_info, const PhysicalOffset& paint_offset) { @@ -90,6 +88,8 @@ static void AddURLRectsForInlineChildrenRecursively( } } +} // anonymous namespace + bool LineBoxListPainter::ShouldPaint(const LayoutBoxModelObject& layout_object, const PaintInfo& paint_info, const PhysicalOffset& paint_offset) const { @@ -100,12 +100,6 @@ bool LineBoxListPainter::ShouldPaint(const LayoutBoxModelObject& layout_object, DCHECK(layout_object.IsLayoutBlock() || (layout_object.IsLayoutInline() && layout_object.HasLayer())); - if (paint_info.phase == PaintPhase::kForeground && - paint_info.ShouldAddUrlMetadata()) { - AddURLRectsForInlineChildrenRecursively(layout_object, paint_info, - paint_offset); - } - // If we have no lines then we have no work to do. if (!line_box_list_.First()) return false; @@ -123,11 +117,17 @@ void LineBoxListPainter::Paint(const LayoutBoxModelObject& layout_object, const PhysicalOffset& paint_offset) const { // Only paint during the foreground/selection phases. if (paint_info.phase != PaintPhase::kForeground && - paint_info.phase != PaintPhase::kSelection && + paint_info.phase != PaintPhase::kSelectionDragImage && paint_info.phase != PaintPhase::kTextClip && paint_info.phase != PaintPhase::kMask) return; + if (paint_info.phase == PaintPhase::kForeground && + paint_info.ShouldAddUrlMetadata()) { + AddURLRectsForInlineChildrenRecursively(layout_object, paint_info, + paint_offset); + } + if (!ShouldPaint(layout_object, paint_info, paint_offset)) return; @@ -158,8 +158,9 @@ void LineBoxListPainter::PaintBackplate( const LayoutBoxModelObject& layout_object, const PaintInfo& paint_info, const PhysicalOffset& paint_offset) const { - if (paint_info.phase != PaintPhase::kForcedColorsModeBackplate || - !ShouldPaint(layout_object, paint_info, paint_offset)) + DCHECK_EQ(paint_info.phase, PaintPhase::kForcedColorsModeBackplate); + + if (!ShouldPaint(layout_object, paint_info, paint_offset)) return; // Only paint backplates behind text when forced-color-adjust is auto. 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 78ef41eddaa..c67909af9aa 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 @@ -33,7 +33,6 @@ #include "cc/layers/picture_layer.h" #include "cc/paint/display_item_list.h" #include "third_party/blink/public/platform/platform.h" -#include "third_party/blink/public/platform/web_float_point.h" #include "third_party/blink/public/platform/web_rect.h" #include "third_party/blink/public/platform/web_size.h" #include "third_party/blink/public/web/blink.h" @@ -47,6 +46,8 @@ #include "third_party/blink/renderer/core/frame/web_local_frame_impl.h" #include "third_party/blink/renderer/core/layout/layout_box_model_object.h" #include "third_party/blink/renderer/core/layout/layout_object.h" +#include "third_party/blink/renderer/core/layout/ng/inline/ng_fragment_item.h" +#include "third_party/blink/renderer/core/layout/ng/inline/ng_inline_cursor.h" #include "third_party/blink/renderer/core/paint/compositing/composited_layer_mapping.h" #include "third_party/blink/renderer/core/paint/paint_layer.h" #include "third_party/blink/renderer/core/paint/paint_layer_scrollable_area.h" @@ -112,6 +113,8 @@ LinkHighlightImpl::LinkHighlightImpl(Node* node) EffectPaintPropertyNode::Root(), LinkHighlightEffectNodeState(kStartOpacity, element_id_)); + DCHECK(GetLayoutObject()); + GetLayoutObject()->SetNeedsPaintPropertyUpdate(); SetPaintArtifactCompositorNeedsUpdate(); #if DCHECK_IS_ON() @@ -132,7 +135,7 @@ void LinkHighlightImpl::ReleaseResources() { if (!node_) return; - if (auto* layout_object = node_->GetLayoutObject()) + if (auto* layout_object = GetLayoutObject()) layout_object->SetNeedsPaintPropertyUpdate(); SetPaintArtifactCompositorNeedsUpdate(); @@ -142,7 +145,6 @@ void LinkHighlightImpl::ReleaseResources() { LinkHighlightImpl::LinkHighlightFragment::LinkHighlightFragment() { layer_ = cc::PictureLayer::Create(this); - layer_->SetTransformOrigin(FloatPoint3D()); layer_->SetIsDrawable(true); layer_->SetOpacity(kStartOpacity); } @@ -233,17 +235,15 @@ void LinkHighlightImpl::NotifyAnimationFinished(double, int) { } void LinkHighlightImpl::UpdateBeforePrePaint() { - if (!node_ || !node_->GetLayoutObject() || - node_->GetLayoutObject()->GetFrameView()->ShouldThrottleRendering()) + auto* object = GetLayoutObject(); + if (!object || object->GetFrameView()->ShouldThrottleRendering()) ReleaseResources(); } void LinkHighlightImpl::UpdateAfterPrePaint() { - if (!node_) + auto* object = GetLayoutObject(); + if (!object) return; - - const auto* object = node_->GetLayoutObject(); - DCHECK(object); DCHECK(!object->GetFrameView()->ShouldThrottleRendering()); size_t fragment_count = 0; @@ -262,15 +262,12 @@ CompositorAnimation* LinkHighlightImpl::GetCompositorAnimation() const { } void LinkHighlightImpl::Paint(GraphicsContext& context) { - if (!node_) + auto* object = GetLayoutObject(); + if (!object) return; - const auto* object = node_->GetLayoutObject(); - // TODO(crbug.com/1016587): Change the CHECKs to DCHECKs after we address - // the cause of the bug. - CHECK(object); - CHECK(object->GetFrameView()); - CHECK(!object->GetFrameView()->ShouldThrottleRendering()); + DCHECK(object->GetFrameView()); + DCHECK(!object->GetFrameView()->ShouldThrottleRendering()); static const FloatSize rect_rounding_radii(3, 3); auto color = object->StyleRef().TapHighlightColor(); @@ -279,7 +276,6 @@ void LinkHighlightImpl::Paint(GraphicsContext& context) { // otherwise we may sometimes get a chain of adjacent boxes (e.g. for text // nodes) which end up looking like sausage links: these should ideally be // merged into a single rect before creating the path. - CHECK(node_->GetDocument().GetSettings()); bool use_rounded_rects = !node_->GetDocument() .GetSettings() ->GetMockGestureTapHighlightsEnabled() && @@ -293,6 +289,20 @@ void LinkHighlightImpl::Paint(GraphicsContext& context) { if (rects.size() > 1) use_rounded_rects = false; + // TODO(yosin): We should remove following if-statement once we release + // NGFragmentItem to renderer rounded rect even if nested inline, e.g. + // <a>ABC<b>DEF</b>GHI</a>. + // See gesture-tapHighlight-simple-nested.html + if (RuntimeEnabledFeatures::LayoutNGFragmentItemEnabled() && + use_rounded_rects && object->IsLayoutInline()) { + NGInlineCursor cursor; + cursor.MoveTo(*object); + // When |LayoutInline| has more than one children, we render square + // rectangle as |NGPaintFragment|. + if (cursor && cursor.CurrentItem()->DescendantsCount() > 2) + use_rounded_rects = false; + } + Path new_path; for (auto& rect : rects) { FloatRect snapped_rect(PixelSnappedIntRect(rect)); @@ -302,7 +312,7 @@ void LinkHighlightImpl::Paint(GraphicsContext& context) { new_path.AddRect(snapped_rect); } - CHECK_LT(index, fragments_.size()); + DCHECK_LT(index, fragments_.size()); auto& link_highlight_fragment = fragments_[index]; link_highlight_fragment.SetColor(color); @@ -310,7 +320,7 @@ void LinkHighlightImpl::Paint(GraphicsContext& context) { new_path.Translate(-ToFloatSize(bounding_rect.Location())); auto* layer = link_highlight_fragment.Layer(); - CHECK(layer); + DCHECK(layer); if (link_highlight_fragment.GetPath() != new_path) { link_highlight_fragment.SetPath(new_path); layer->SetBounds(gfx::Size(EnclosingIntRect(bounding_rect).Size())); @@ -324,7 +334,7 @@ void LinkHighlightImpl::Paint(GraphicsContext& context) { property_tree_state.SetEffect(Effect()); RecordForeignLayer(context, debug_name_client, DisplayItem::kForeignLayerLinkHighlight, layer, - bounding_rect.Location(), property_tree_state); + bounding_rect.Location(), &property_tree_state); } DCHECK_EQ(index, fragments_.size()); diff --git a/chromium/third_party/blink/renderer/core/paint/link_highlight_impl.h b/chromium/third_party/blink/renderer/core/paint/link_highlight_impl.h index 7d32caec420..d1acdedd830 100644 --- a/chromium/third_party/blink/renderer/core/paint/link_highlight_impl.h +++ b/chromium/third_party/blink/renderer/core/paint/link_highlight_impl.h @@ -30,6 +30,7 @@ #include "cc/layers/content_layer_client.h" #include "third_party/blink/renderer/core/core_export.h" +#include "third_party/blink/renderer/core/dom/node.h" #include "third_party/blink/renderer/platform/animation/compositor_animation.h" #include "third_party/blink/renderer/platform/animation/compositor_animation_client.h" #include "third_party/blink/renderer/platform/animation/compositor_animation_delegate.h" @@ -48,7 +49,6 @@ namespace blink { class EffectPaintPropertyNode; class GraphicsContext; -class Node; class CORE_EXPORT LinkHighlightImpl final : public CompositorAnimationDelegate, public CompositorAnimationClient { @@ -66,7 +66,9 @@ class CORE_EXPORT LinkHighlightImpl final : public CompositorAnimationDelegate, // CompositorAnimationClient implementation. CompositorAnimation* GetCompositorAnimation() const override; - Node* GetNode() const { return node_; } + LayoutObject* GetLayoutObject() const { + return node_ ? node_->GetLayoutObject() : nullptr; + } CompositorElementId ElementIdForTesting() const { return element_id_; } @@ -111,7 +113,7 @@ class CORE_EXPORT LinkHighlightImpl final : public CompositorAnimationDelegate, }; Vector<LinkHighlightFragment> fragments_; - Persistent<Node> node_; + WeakPersistent<Node> node_; std::unique_ptr<CompositorAnimation> compositor_animation_; scoped_refptr<EffectPaintPropertyNode> effect_; 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 3b0ff3bb636..c7cb2458cb9 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 @@ -30,8 +30,7 @@ #include "cc/layers/picture_layer.h" #include "cc/trees/layer_tree_host.h" #include "testing/gtest/include/gtest/gtest.h" -#include "third_party/blink/public/platform/web_float_point.h" -#include "third_party/blink/public/platform/web_input_event.h" +#include "third_party/blink/public/common/input/web_input_event.h" #include "third_party/blink/public/platform/web_size.h" #include "third_party/blink/public/platform/web_url_loader_mock_factory.h" #include "third_party/blink/public/web/web_frame.h" @@ -104,7 +103,7 @@ class LinkHighlightImplTest : public testing::Test, void UpdateAllLifecyclePhases() { web_view_helper_.GetWebView()->MainFrameWidget()->UpdateAllLifecyclePhases( - WebWidget::LifecycleUpdateReason::kTest); + DocumentUpdateReason::kTest); } LinkHighlight& GetLinkHighlight() { @@ -141,14 +140,14 @@ TEST_P(LinkHighlightImplTest, verifyWebViewImplIntegration) { // The coordinates below are linked to absolute positions in the referenced // .html file. - touch_event.SetPositionInWidget(WebFloatPoint(20, 20)); + touch_event.SetPositionInWidget(gfx::PointF(20, 20)); ASSERT_TRUE(web_view_impl->BestTapNode(GetTargetedEvent(touch_event))); - touch_event.SetPositionInWidget(WebFloatPoint(20, 40)); + touch_event.SetPositionInWidget(gfx::PointF(20, 40)); EXPECT_FALSE(web_view_impl->BestTapNode(GetTargetedEvent(touch_event))); - touch_event.SetPositionInWidget(WebFloatPoint(20, 20)); + touch_event.SetPositionInWidget(gfx::PointF(20, 20)); // Shouldn't crash. web_view_impl->EnableTapHighlightAtPoint(GetTargetedEvent(touch_event)); @@ -158,7 +157,7 @@ TEST_P(LinkHighlightImplTest, verifyWebViewImplIntegration) { EXPECT_TRUE(highlight->LayerForTesting(0)); // Find a target inside a scrollable div - touch_event.SetPositionInWidget(WebFloatPoint(20, 100)); + touch_event.SetPositionInWidget(gfx::PointF(20, 100)); web_view_impl->EnableTapHighlightAtPoint(GetTargetedEvent(touch_event)); ASSERT_TRUE(highlight); @@ -167,11 +166,11 @@ TEST_P(LinkHighlightImplTest, verifyWebViewImplIntegration) { // Don't highlight if no "hand cursor" touch_event.SetPositionInWidget( - WebFloatPoint(20, 220)); // An A-link with cross-hair cursor. + gfx::PointF(20, 220)); // An A-link with cross-hair cursor. web_view_impl->EnableTapHighlightAtPoint(GetTargetedEvent(touch_event)); EXPECT_FALSE(GetLinkHighlightImpl()); - touch_event.SetPositionInWidget(WebFloatPoint(20, 260)); // A text input box. + touch_event.SetPositionInWidget(gfx::PointF(20, 260)); // A text input box. web_view_impl->EnableTapHighlightAtPoint(GetTargetedEvent(touch_event)); EXPECT_FALSE(GetLinkHighlightImpl()); } @@ -188,7 +187,7 @@ TEST_P(LinkHighlightImplTest, resetDuringNodeRemoval) { WebInputEvent::kNoModifiers, WebInputEvent::GetStaticTimeStampForTests(), WebGestureDevice::kTouchscreen); - touch_event.SetPositionInWidget(WebFloatPoint(20, 20)); + touch_event.SetPositionInWidget(gfx::PointF(20, 20)); GestureEventWithHitTestResults targeted_event = GetTargetedEvent(touch_event); Node* touch_node = web_view_impl->BestTapNode(targeted_event); @@ -197,14 +196,14 @@ TEST_P(LinkHighlightImplTest, resetDuringNodeRemoval) { web_view_impl->EnableTapHighlightAtPoint(targeted_event); const auto* highlight = GetLinkHighlightImpl(); ASSERT_TRUE(highlight); - EXPECT_EQ(touch_node, highlight->GetNode()); + EXPECT_EQ(touch_node->GetLayoutObject(), highlight->GetLayoutObject()); touch_node->remove(IGNORE_EXCEPTION_FOR_TESTING); UpdateAllLifecyclePhases(); ASSERT_EQ(highlight, GetLinkHighlightImpl()); ASSERT_TRUE(highlight); - EXPECT_FALSE(highlight->GetNode()); + EXPECT_FALSE(highlight->GetLayoutObject()); } // A lifetime test: delete LayerTreeView while running LinkHighlights. @@ -220,7 +219,7 @@ TEST_P(LinkHighlightImplTest, resetLayerTreeView) { WebInputEvent::kNoModifiers, WebInputEvent::GetStaticTimeStampForTests(), WebGestureDevice::kTouchscreen); - touch_event.SetPositionInWidget(WebFloatPoint(20, 20)); + touch_event.SetPositionInWidget(gfx::PointF(20, 20)); GestureEventWithHitTestResults targeted_event = GetTargetedEvent(touch_event); Node* touch_node = web_view_impl->BestTapNode(targeted_event); @@ -245,7 +244,7 @@ TEST_P(LinkHighlightImplTest, HighlightLayerEffectNode) { WebInputEvent::kNoModifiers, WebInputEvent::GetStaticTimeStampForTests(), WebGestureDevice::kTouchscreen); - touch_event.SetPositionInWidget(WebFloatPoint(20, 20)); + touch_event.SetPositionInWidget(gfx::PointF(20, 20)); GestureEventWithHitTestResults targeted_event = GetTargetedEvent(touch_event); Node* touch_node = web_view_impl->BestTapNode(targeted_event); @@ -309,7 +308,7 @@ TEST_P(LinkHighlightImplTest, MultiColumn) { WebInputEvent::GetStaticTimeStampForTests(), WebGestureDevice::kTouchscreen); // This will touch the link under multicol. - touch_event.SetPositionInWidget(WebFloatPoint(20, 300)); + touch_event.SetPositionInWidget(gfx::PointF(20, 300)); GestureEventWithHitTestResults targeted_event = GetTargetedEvent(touch_event); Node* touch_node = web_view_impl->BestTapNode(targeted_event); @@ -380,4 +379,28 @@ TEST_P(LinkHighlightImplTest, MultiColumn) { EXPECT_EQ(layer_count_before_highlight, LayerCount()); } +TEST_P(LinkHighlightImplTest, DisplayContents) { + WebViewImpl* web_view_impl = web_view_helper_.GetWebView(); + + int page_width = 640; + int page_height = 480; + web_view_impl->MainFrameWidget()->Resize(WebSize(page_width, page_height)); + UpdateAllLifecyclePhases(); + + WebGestureEvent touch_event(WebInputEvent::kGestureShowPress, + WebInputEvent::kNoModifiers, + WebInputEvent::GetStaticTimeStampForTests(), + WebGestureDevice::kTouchscreen); + // This will touch the div with display:contents and cursor:pointer. + touch_event.SetPositionInWidget(gfx::PointF(20, 400)); + + GestureEventWithHitTestResults targeted_event = GetTargetedEvent(touch_event); + const Node* touched_node = targeted_event.GetHitTestResult().InnerNode(); + EXPECT_TRUE(touched_node->IsTextNode()); + EXPECT_FALSE(web_view_impl->BestTapNode(targeted_event)); + + web_view_impl->EnableTapHighlightAtPoint(targeted_event); + EXPECT_FALSE(GetLinkHighlightImpl()); +} + } // namespace blink 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 3d0509ef618..129ae0e73ca 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 @@ -25,6 +25,8 @@ void ListMarkerPainter::PaintSymbol(const PaintInfo& paint_info, const IntRect& marker) { DCHECK(object); GraphicsContext& context = paint_info.context; + ScopedDarkModeElementRoleOverride list_symbol( + &context, DarkModeFilter::ElementRole::kListSymbol); Color color(object->ResolveColor(GetCSSPropertyColor())); if (BoxModelObjectPainter::ShouldForceWhiteBackgroundForPrintEconomy( object->GetDocument(), style)) @@ -108,8 +110,7 @@ void ListMarkerPainter::Paint(const PaintInfo& paint_info) { Color color(layout_list_marker_.ResolveColor(GetCSSPropertyColor())); if (BoxModelObjectPainter::ShouldForceWhiteBackgroundForPrintEconomy( - layout_list_marker_.ListItem()->GetDocument(), - layout_list_marker_.StyleRef())) + layout_list_marker_.GetDocument(), layout_list_marker_.StyleRef())) color = TextPainter::TextColorForWhiteBackground(color); // Apply the color to the list marker text. diff --git a/chromium/third_party/blink/renderer/core/paint/multi_column_set_painter.cc b/chromium/third_party/blink/renderer/core/paint/multi_column_set_painter.cc index c9a4348d6f1..f16be7a8e54 100644 --- a/chromium/third_party/blink/renderer/core/paint/multi_column_set_painter.cc +++ b/chromium/third_party/blink/renderer/core/paint/multi_column_set_painter.cc @@ -29,7 +29,7 @@ void MultiColumnSetPainter::PaintObject(const PaintInfo& paint_info, // It's also really unlikely that the columns would overlap another block. if (!layout_multi_column_set_.FlowThread() || (paint_info.phase != PaintPhase::kForeground && - paint_info.phase != PaintPhase::kSelection)) + paint_info.phase != PaintPhase::kSelectionDragImage)) return; PaintColumnRules(paint_info, paint_offset); diff --git a/chromium/third_party/blink/renderer/core/paint/ng/README.md b/chromium/third_party/blink/renderer/core/paint/ng/README.md index 70c1fb9bb76..31778f46d11 100644 --- a/chromium/third_party/blink/renderer/core/paint/ng/README.md +++ b/chromium/third_party/blink/renderer/core/paint/ng/README.md @@ -59,6 +59,25 @@ even though the inline block LayoutObject has children. If the inline block has another inline block which LayoutNG can handle, another NGPaintFragment tree is created for the inner inline block. +### NGPhysicalFragment traversal ### + +When possible (when sufficiently transitioned to LayoutNG), we'll paint and +hit-test by traversing the physical fragment tree, rather than traversing the +LayoutObject tree. This is important for block fragmentation, where a CSS layout +box (LayoutObject) may be split into multiple fragments, and it's the +relationship between the fragments (not the layout objects) that determines the +offsets. In LayoutNG, there are also fragments that have no corresponding layout +object - e.g. a column (or other types of [fragmentainer]s too). + +Traditionally, when doing block fragmentation (multicol) in legacy layout, we +have to perform some complicated calculations, where we map and slice layout +objects into fragments during pre-paint. In LayoutNG this job is now as a +natural part of layout. So, all we have to do for painting and hit-testing, is +traverse the fragments. A fragment holds a list of child fragments and their +offsets. The offsets are relative to the parent fragment. As such, it's a rather +straight-forward job for pre-paint to calculate the offsets and bounding box. + [LayoutNG]: ../../layout/ng/README.md [NGPaintFragment]: ng_paint_fragment.h [NGPhysicalFragment]: ../../layout/ng/ng_physical_fragment.h +[fragmentainer]: https://drafts.csswg.org/css-break/#fragmentation-container 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 6260eae3666..e8ff0f43f2f 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 @@ -4,6 +4,7 @@ #include "third_party/blink/renderer/core/paint/ng/ng_box_fragment_painter.h" +#include "base/containers/adapters.h" #include "third_party/blink/renderer/core/editing/drag_caret.h" #include "third_party/blink/renderer/core/editing/frame_selection.h" #include "third_party/blink/renderer/core/frame/local_frame.h" @@ -28,6 +29,7 @@ #include "third_party/blink/renderer/core/paint/ng/ng_fieldset_painter.h" #include "third_party/blink/renderer/core/paint/ng/ng_fragment_painter.h" #include "third_party/blink/renderer/core/paint/ng/ng_inline_box_fragment_painter.h" +#include "third_party/blink/renderer/core/paint/ng/ng_mathml_painter.h" #include "third_party/blink/renderer/core/paint/ng/ng_paint_fragment.h" #include "third_party/blink/renderer/core/paint/ng/ng_text_fragment_painter.h" #include "third_party/blink/renderer/core/paint/object_painter.h" @@ -44,8 +46,6 @@ #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/scroll_hit_test_display_item.h" namespace blink { @@ -103,7 +103,7 @@ bool HitTestCulledInlineAncestors(HitTestResult& result, // To be passed as |accumulated_offset| to LayoutInline::HitTestCulledInline, // where it equals the physical offset of the containing block in paint layer. const PhysicalOffset fallback_accumulated_offset = - physical_offset - fragment.InlineOffsetToContainerBox(); + physical_offset - fragment.OffsetInContainerBlock(); const LayoutObject* limit_layout_object = parent.PhysicalFragment().IsLineBox() ? parent.Parent()->GetLayoutObject() : parent.GetLayoutObject(); @@ -156,71 +156,122 @@ bool FragmentRequiresLegacyFallback(const NGPhysicalFragment& fragment) { // Fallback to LayoutObject if this is a root of NG block layout. // If this box is for this painter, LayoutNGBlockFlow will call this back. // Otherwise it calls legacy painters. - return fragment.IsBlockFormattingContextRoot(); + return fragment.IsFormattingContextRoot(); } -// Recursively build up backplates behind inline text boxes, each split at the -// paragraph level. Store the results in paragraph_backplates. -void BuildBackplate(const NGPaintFragment* line, - const PhysicalOffset& paint_offset, - PhysicalRect* current_backplate, - int* consecutive_line_breaks, - Vector<PhysicalRect>* paragraph_backplates) { - DCHECK(current_backplate && consecutive_line_breaks && paragraph_backplates); - +// Returns a vector of backplates that surround the paragraphs of text within +// line_boxes. +// +// This function traverses descendants of an inline formatting context in +// pre-order DFS and build up backplates behind inline text boxes, each split at +// the paragraph level. Store the results in paragraph_backplates. +Vector<PhysicalRect> BuildBackplate(NGInlineCursor* descendants, + const PhysicalOffset& paint_offset) { // The number of consecutive forced breaks that split the backplate by // paragraph. static constexpr int kMaxConsecutiveLineBreaks = 2; + struct Backplates { + STACK_ALLOCATED(); + + public: + void AddTextRect(const PhysicalRect& box_rect) { + if (consecutive_line_breaks >= kMaxConsecutiveLineBreaks) { + // This is a paragraph point. + paragraph_backplates.push_back(current_backplate); + current_backplate = PhysicalRect(); + } + consecutive_line_breaks = 0; + + current_backplate.Unite(box_rect); + } + + void AddLineBreak() { consecutive_line_breaks++; } + + Vector<PhysicalRect> paragraph_backplates; + PhysicalRect current_backplate; + int consecutive_line_breaks = 0; + } backplates; + // Build up and paint backplates of all child inline text boxes. We are not // able to simply use the linebox rect to compute the backplate because the // backplate should only be painted for inline text and not for atomic // inlines. - for (const NGPaintFragment* child : line->Children()) { - const NGPhysicalFragment& child_fragment = child->PhysicalFragment(); - if (child_fragment.IsHiddenForPaint() || child_fragment.IsFloating()) - continue; - if (auto* text_fragment = - DynamicTo<NGPhysicalTextFragment>(child_fragment)) { - if (text_fragment->IsLineBreak()) { - (*consecutive_line_breaks)++; + for (; *descendants; descendants->MoveToNext()) { + if (const NGPaintFragment* child = descendants->CurrentPaintFragment()) { + const NGPhysicalFragment& child_fragment = child->PhysicalFragment(); + if (child_fragment.IsHiddenForPaint() || child_fragment.IsFloating()) continue; - } + if (auto* text_fragment = + DynamicTo<NGPhysicalTextFragment>(child_fragment)) { + if (text_fragment->IsLineBreak()) { + backplates.AddLineBreak(); + continue; + } - if (*consecutive_line_breaks >= kMaxConsecutiveLineBreaks) { - // This is a paragraph point. - paragraph_backplates->push_back(*current_backplate); - *current_backplate = PhysicalRect(); + PhysicalRect box_rect(child->OffsetInContainerBlock() + paint_offset, + child->Size()); + backplates.AddTextRect(box_rect); } - *consecutive_line_breaks = 0; - PhysicalRect box_rect(child->InlineOffsetToContainerBox() + paint_offset, - child->Size()); - current_backplate->Unite(box_rect); + continue; } - if (child_fragment.Type() == NGPhysicalFragment::kFragmentBox) { - // If a fragment box was reached, continue to recursively build - // up the backplate. - BuildBackplate(child, paint_offset, current_backplate, - consecutive_line_breaks, paragraph_backplates); + if (const NGFragmentItem* child_item = descendants->CurrentItem()) { + if (child_item->IsHiddenForPaint()) + continue; + if (child_item->IsText()) { + if (child_item->IsLineBreak()) { + backplates.AddLineBreak(); + continue; + } + + PhysicalRect box_rect( + child_item->OffsetInContainerBlock() + paint_offset, + child_item->Size()); + backplates.AddTextRect(box_rect); + } + continue; } + NOTREACHED(); } + + if (!backplates.current_backplate.IsEmpty()) + backplates.paragraph_backplates.push_back(backplates.current_backplate); + return backplates.paragraph_backplates; } -// Returns a vector of backplates that surround the paragraphs of text within -// line_boxes. -Vector<PhysicalRect> BuildBackplate(const NGPaintFragment::ChildList line_boxes, - const PhysicalOffset& paint_offset) { - Vector<PhysicalRect> paragraph_backplates; - PhysicalRect current_backplate; - int consecutive_line_breaks = 0; - for (const NGPaintFragment* line : line_boxes) { - // Recursively build up and paint backplates for line boxes containing text. - BuildBackplate(line, paint_offset, ¤t_backplate, - &consecutive_line_breaks, ¶graph_backplates); +bool HitTestAllPhasesInFragment(const NGPhysicalBoxFragment& fragment, + const HitTestLocation& hit_test_location, + PhysicalOffset accumulated_offset, + HitTestResult* result) { + // Hit test all phases of inline blocks, inline tables, replaced elements and + // non-positioned floats as if they created their own (pseudo- [1]) stacking + // context. https://www.w3.org/TR/CSS22/zindex.html#painting-order + // + // [1] As if it creates a new stacking context, but any positioned descendants + // and descendants which actually create a new stacking context should be + // considered part of the parent stacking context, not this new one. + + if (!fragment.CanTraverse()) { + return fragment.GetMutableLayoutObject()->HitTestAllPhases( + *result, hit_test_location, accumulated_offset); } - if (!current_backplate.IsEmpty()) - paragraph_backplates.push_back(current_backplate); - return paragraph_backplates; + + return NGBoxFragmentPainter(To<NGPhysicalBoxFragment>(fragment)) + .HitTestAllPhases(*result, hit_test_location, accumulated_offset); +} + +bool NodeAtPointInFragment(const NGPhysicalBoxFragment& fragment, + const HitTestLocation& hit_test_location, + PhysicalOffset accumulated_offset, + HitTestAction action, + HitTestResult* result) { + if (!fragment.CanTraverse()) { + return fragment.GetMutableLayoutObject()->NodeAtPoint( + *result, hit_test_location, accumulated_offset, action); + } + + return NGBoxFragmentPainter(fragment).NodeAtPoint(*result, hit_test_location, + accumulated_offset, action); } } // anonymous namespace @@ -234,16 +285,35 @@ const NGBorderEdges& NGBoxFragmentPainter::BorderEdges() const { return *border_edges_; } +PhysicalRect NGBoxFragmentPainter::SelfInkOverflow() const { + if (paint_fragment_) + return paint_fragment_->SelfInkOverflow(); + if (box_item_) + return box_item_->SelfInkOverflow(); + const NGPhysicalFragment& fragment = PhysicalFragment(); + DCHECK(fragment.IsBox() && !fragment.IsInlineBox()); + return ToLayoutBox(fragment.GetLayoutObject()) + ->PhysicalSelfVisualOverflowRect(); +} + +PhysicalRect NGBoxFragmentPainter::ContentsInkOverflow() const { + if (const LayoutObject* layout_object = box_fragment_.GetLayoutObject()) + return ToLayoutBox(layout_object)->PhysicalContentsVisualOverflowRect(); + return box_fragment_.ContentsInkOverflow(); +} + void NGBoxFragmentPainter::Paint(const PaintInfo& paint_info) { - if (PhysicalFragment().IsAtomicInline() && + if (PhysicalFragment().IsPaintedAtomically() && !box_fragment_.HasSelfPaintingLayer()) - PaintAtomicInline(paint_info); + PaintAllPhasesAtomically(paint_info); else PaintInternal(paint_info); } void NGBoxFragmentPainter::PaintInternal(const PaintInfo& paint_info) { - ScopedPaintState paint_state(box_fragment_, paint_info); + // Avoid initialization of Optional ScopedPaintState::chunk_properties_ + // and ScopedPaintState::adjusted_paint_info_. + STACK_UNINITIALIZED ScopedPaintState paint_state(box_fragment_, paint_info); if (!ShouldPaint(paint_state)) return; @@ -251,6 +321,15 @@ void NGBoxFragmentPainter::PaintInternal(const PaintInfo& paint_info) { PhysicalOffset paint_offset = paint_state.PaintOffset(); PaintPhase original_phase = info.phase; + ScopedPaintTimingDetectorBlockPaintHook + scoped_paint_timing_detector_block_paint_hook; + if (original_phase == PaintPhase::kForeground && + box_fragment_.GetLayoutObject()->IsBox()) { + scoped_paint_timing_detector_block_paint_hook.EmplaceIfNeeded( + ToLayoutBox(*box_fragment_.GetLayoutObject()), + paint_info.context.GetPaintController().CurrentPaintChunkProperties()); + } + if (original_phase == PaintPhase::kOutline) { info.phase = PaintPhase::kDescendantOutlinesOnly; } else if (ShouldPaintSelfBlockBackground(original_phase)) { @@ -318,17 +397,18 @@ void NGBoxFragmentPainter::RecordScrollHitTestData( .RecordScrollHitTestData(paint_info, background_client); } -void NGBoxFragmentPainter::RecordHitTestDataForLine( - const PaintInfo& paint_info, - const PhysicalOffset& paint_offset, - const NGPhysicalFragment& line, - const DisplayItemClient& display_item_client) { - PhysicalRect border_box = line.LocalRect(); - border_box.offset += paint_offset; - HitTestDisplayItem::Record( - paint_info.context, display_item_client, - HitTestRect(border_box.ToLayoutRect(), - PhysicalFragment().EffectiveAllowedTouchAction())); +bool NGBoxFragmentPainter::ShouldRecordHitTestData( + const PaintInfo& paint_info) { + // Hit test data 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 false; + + // If an object is not visible, it does not participate in hit testing. + if (PhysicalFragment().Style().Visibility() != EVisibility::kVisible) + return false; + + return true; } void NGBoxFragmentPainter::PaintObject( @@ -355,39 +435,46 @@ void NGBoxFragmentPainter::PaintObject( return; } - if (paint_phase == PaintPhase::kForeground && - paint_info.ShouldAddUrlMetadata()) { - NGFragmentPainter(box_fragment_, paint_fragment_) - .AddURLRectIfNeeded(paint_info, paint_offset); + if (paint_phase == PaintPhase::kForeground) { + if (paint_info.ShouldAddUrlMetadata()) { + NGFragmentPainter(box_fragment_, GetDisplayItemClient()) + .AddURLRectIfNeeded(paint_info, paint_offset); + } + if (is_visible && box_fragment_.IsMathMLFraction()) + NGMathMLPainter(box_fragment_).PaintFractionBar(paint_info, paint_offset); } if (paint_phase != PaintPhase::kSelfOutlineOnly && (!physical_box_fragment.Children().empty() || - physical_box_fragment.HasItems() || descendants_) && + physical_box_fragment.HasItems() || inline_box_cursor_) && !paint_info.DescendantPaintingBlocked()) { - if (RuntimeEnabledFeatures::LayoutNGFragmentPaintEnabled()) { - if (UNLIKELY(paint_phase == PaintPhase::kForeground && - box_fragment_.Style().HasColumnRule())) - PaintColumnRules(paint_info, paint_offset); - } + if (UNLIKELY(paint_phase == PaintPhase::kForeground && + box_fragment_.Style().HasColumnRule())) + PaintColumnRules(paint_info, paint_offset); if (paint_phase != PaintPhase::kFloat) { - if (UNLIKELY(descendants_)) { + if (UNLIKELY(inline_box_cursor_)) { // Use the descendants cursor for this painter if it is given. // Self-painting inline box paints only parts of the container block. // Adjust |paint_offset| because it is the offset of the inline box, but // |descendants_| has offsets to the contaiing block. - DCHECK(box_item_ && box_item_->HasSelfPaintingLayer()); + DCHECK(box_item_); + NGInlineCursor descendants = inline_box_cursor_->CursorForDescendants(); const PhysicalOffset paint_offset_to_inline_formatting_context = - paint_offset - box_item_->Offset(); + paint_offset - box_item_->OffsetInContainerBlock(); PaintInlineItems(paint_info.ForDescendants(), paint_offset_to_inline_formatting_context, - descendants_); + box_item_->OffsetInContainerBlock(), &descendants); } else if (items_) { - // Paint |NGFragmentItems| for this block if we have one. - NGInlineCursor cursor(*items_); - PaintInlineItems(paint_info.ForDescendants(), paint_offset, &cursor); - } else if (physical_box_fragment.ChildrenInline()) { + if (physical_box_fragment.IsBlockFlow()) { + PaintBlockFlowContents(paint_info, paint_offset); + } else { + DCHECK(physical_box_fragment.IsInlineBox()); + NGInlineCursor cursor(*items_); + PaintInlineItems(paint_info.ForDescendants(), paint_offset, + PhysicalOffset(), &cursor); + } + } else if (physical_box_fragment.IsInlineFormattingContext()) { DCHECK(!RuntimeEnabledFeatures::LayoutNGFragmentItemEnabled()); DCHECK(paint_fragment_); if (physical_box_fragment.IsBlockFlow()) { @@ -403,12 +490,12 @@ void NGBoxFragmentPainter::PaintObject( paint_offset); } } else { - PaintBlockChildren(paint_info); + PaintBlockChildren(paint_info, paint_offset); } } if (paint_phase == PaintPhase::kFloat || - paint_phase == PaintPhase::kSelection || + paint_phase == PaintPhase::kSelectionDragImage || paint_phase == PaintPhase::kTextClip) { if (physical_box_fragment.HasFloatingDescendantsForPaint()) PaintFloats(paint_info); @@ -416,7 +503,7 @@ void NGBoxFragmentPainter::PaintObject( } if (ShouldPaintSelfOutline(paint_phase)) { - NGFragmentPainter(box_fragment_, paint_fragment_) + NGFragmentPainter(box_fragment_, GetDisplayItemClient()) .PaintOutline(paint_info, paint_offset); } @@ -445,18 +532,20 @@ void NGBoxFragmentPainter::PaintBlockFlowContents( const PhysicalOffset& paint_offset) { const NGPhysicalBoxFragment& fragment = PhysicalFragment(); const LayoutObject* layout_object = fragment.GetLayoutObject(); - - DCHECK(fragment.ChildrenInline()); - DCHECK(paint_fragment_); + DCHECK(fragment.IsInlineFormattingContext()); // When the layout-tree gets into a bad state, we can end up trying to paint // a fragment with inline children, without a paint fragment. See: // http://crbug.com/1022545 - if (!paint_fragment_) { + if ((!paint_fragment_ && !items_) || + (layout_object && layout_object->NeedsLayout())) { NOTREACHED(); return; } + // Trying to rule out a null GraphicsContext, see: https://crbug.com/1040298 + CHECK(&paint_info.context); + // Check if there were contents to be painted and return early if none. // The union of |ContentsInkOverflow()| and |LocalRect()| covers the rect to // check, in both cases of: @@ -467,37 +556,73 @@ void NGBoxFragmentPainter::PaintBlockFlowContents( // |LayoutOverflow()|, but this can be approximiated with // |ContentsInkOverflow()|. PhysicalRect content_ink_rect = fragment.LocalRect(); - content_ink_rect.Unite(paint_fragment_->ContentsInkOverflow()); + content_ink_rect.Unite(ContentsInkOverflow()); content_ink_rect.offset += PhysicalOffset(paint_offset); if (!paint_info.GetCullRect().Intersects(content_ink_rect.ToLayoutRect())) return; - DCHECK(layout_object->IsLayoutBlockFlow()); - const auto& layout_block = To<LayoutBlock>(*layout_object); - DCHECK(layout_block.ChildrenInline()); - PaintLineBoxChildren(paint_fragment_->Children(), paint_info.ForDescendants(), - paint_offset); + if (paint_fragment_) { + NGInlineCursor children(*paint_fragment_); + PaintLineBoxChildren(&children, paint_info.ForDescendants(), paint_offset); + return; + } + DCHECK(items_); + NGInlineCursor children(*items_); + PaintLineBoxChildren(&children, paint_info.ForDescendants(), paint_offset); } -void NGBoxFragmentPainter::PaintBlockChildren(const PaintInfo& paint_info) { - DCHECK(!box_fragment_.ChildrenInline()); - DCHECK(!box_fragment_.GetLayoutObject()->ChildrenInline()); +void NGBoxFragmentPainter::PaintBlockChildren(const PaintInfo& paint_info, + PhysicalOffset paint_offset) { + DCHECK(!box_fragment_.IsInlineFormattingContext()); PaintInfo paint_info_for_descendants = paint_info.ForDescendants(); for (const NGLink& child : box_fragment_.Children()) { const NGPhysicalFragment& child_fragment = *child; + DCHECK(child_fragment.IsBox()); if (child_fragment.HasSelfPaintingLayer() || child_fragment.IsFloating() || child_fragment.IsColumnBox()) continue; - if (child_fragment.Type() == NGPhysicalFragment::kFragmentBox) { - // TODO(kojii): We could skip going through |LayoutObject| when we know - // children are always laid out by NG. See - // |FragmentRequiresLegacyFallback|. - child_fragment.GetLayoutObject()->Paint(paint_info_for_descendants); - } else { - DCHECK_EQ(child_fragment.Type(), - NGPhysicalFragment::kFragmentRenderedLegend); + const auto& box_child_fragment = To<NGPhysicalBoxFragment>(child_fragment); + if (box_child_fragment.CanTraverse()) { + if (!box_child_fragment.GetLayoutObject()) { + // It's normally FragmentData that provides us with the paint offset. + // FragmentData is (at least currently) associated with a LayoutObject. + // If we have no LayoutObject, we have no FragmentData, so we need to + // calculate the offset on our own (which is very simple, anyway). + // Bypass Paint() and jump directly to PaintObject(), to skip the code + // that assumes that we have a LayoutObject (and FragmentData). + PhysicalOffset child_offset = paint_offset + child.offset; + NGBoxFragmentPainter(box_child_fragment) + .PaintObject(paint_info, child_offset); + continue; + } + + NGBoxFragmentPainter(box_child_fragment) + .Paint(paint_info_for_descendants); + continue; } + child_fragment.GetLayoutObject()->Paint(paint_info_for_descendants); + } +} + +void NGBoxFragmentPainter::PaintFloatingItems(const PaintInfo& paint_info, + NGInlineCursor* cursor) { + for (; *cursor; cursor->MoveToNext()) { + const NGFragmentItem* item = cursor->Current().Item(); + DCHECK(item); + const NGPhysicalBoxFragment* child_fragment = item->BoxFragment(); + if (!child_fragment || child_fragment->HasSelfPaintingLayer() || + !child_fragment->IsFloating()) + continue; + // TODO(kojii): The float is outside of the inline formatting context and + // that it maybe another NG inline formatting context, NG block layout, or + // legacy. NGBoxFragmentPainter can handle only the first case. In order + // to cover more tests for other two cases, we always fallback to legacy, + // which will forward back to NGBoxFragmentPainter if the float is for + // NGBoxFragmentPainter. We can shortcut this for the first case when + // we're more stable. + ObjectPainter(*child_fragment->GetLayoutObject()) + .PaintAllPhasesAtomically(paint_info); } } @@ -505,60 +630,93 @@ void NGBoxFragmentPainter::PaintFloatingChildren( const NGPhysicalContainerFragment& container, const PaintInfo& paint_info, const PaintInfo& float_paint_info) { -#if DCHECK_IS_ON() - // Floats are in the fragment tree, not in the fragment item list. - if (const NGPhysicalBoxFragment* box_fragment = + DCHECK(container.HasFloatingDescendantsForPaint()); + + if (const NGPhysicalBoxFragment* box = DynamicTo<NGPhysicalBoxFragment>(&container)) { - if (const NGFragmentItems* items = box_fragment->Items()) { - DCHECK(std::none_of( - items->Items().begin(), items->Items().end(), [](const auto& item) { - return item->BoxFragment() && item->BoxFragment()->IsFloating(); - })); + if (const NGFragmentItems* items = box->Items()) { + NGInlineCursor cursor(*items); + PaintFloatingItems(float_paint_info, &cursor); + return; + } + if (inline_box_cursor_) { + DCHECK(box->IsInlineBox()); + NGInlineCursor descendants = inline_box_cursor_->CursorForDescendants(); + PaintFloatingItems(float_paint_info, &descendants); + return; } + DCHECK(!RuntimeEnabledFeatures::LayoutNGFragmentItemEnabled() || + !box->IsInlineBox()); } -#endif for (const NGLink& child : container.Children()) { const NGPhysicalFragment& child_fragment = *child; if (child_fragment.HasSelfPaintingLayer() || child_fragment.IsColumnBox()) continue; - // Atomic-inlines paint atomically, and shouldn't be traversed. - // TODO(layout-dev): This check should include all children which paint - // atomically. - if (child_fragment.IsAtomicInline()) + if (child_fragment.CanTraverse()) { + if (child_fragment.IsFloating()) { + NGBoxFragmentPainter(To<NGPhysicalBoxFragment>(child_fragment)) + .Paint(float_paint_info); + continue; + } + + // Any non-floated children which paint atomically shouldn't be traversed. + if (child_fragment.IsPaintedAtomically()) + continue; + } else { + if (child_fragment.IsFloating()) { + // TODO(kojii): The float is outside of the inline formatting context + // and that it maybe another NG inline formatting context, NG block + // layout, or legacy. NGBoxFragmentPainter can handle only the first + // case. In order to cover more tests for other two cases, we always + // fallback to legacy, which will forward back to NGBoxFragmentPainter + // if the float is for NGBoxFragmentPainter. We can shortcut this for + // the first case when we're more stable. + + ObjectPainter(*child_fragment.GetLayoutObject()) + .PaintAllPhasesAtomically(float_paint_info); + continue; + } + + // Any children which paint atomically shouldn't be traversed. + if (child_fragment.IsPaintedAtomically()) + continue; + + if (child_fragment.Type() == NGPhysicalFragment::kFragmentBox && + FragmentRequiresLegacyFallback(child_fragment)) { + child_fragment.GetLayoutObject()->Paint(paint_info); + continue; + } + } + + // The selection paint traversal is special. We will visit all fragments + // (including floats) in the normal paint traversal. There isn't any point + // performing the special float traversal here. + if (paint_info.phase == PaintPhase::kSelectionDragImage) continue; - if (child_fragment.IsFloating()) { - // TODO(kojii): The float is outside of the inline formatting context and - // that it maybe another NG inline formatting context, NG block layout, or - // legacy. NGBoxFragmentPainter can handle only the first case. In order - // to cover more tests for other two cases, we always fallback to legacy, - // which will forward back to NGBoxFragmentPainter if the float is for - // NGBoxFragmentPainter. We can shortcut this for the first case when - // we're more stable. - ObjectPainter(*child_fragment.GetLayoutObject()) - .PaintAllPhasesAtomically(float_paint_info); + const auto* child_container = + DynamicTo<NGPhysicalContainerFragment>(&child_fragment); + if (!child_container || !child_container->HasFloatingDescendantsForPaint()) continue; - } - if (child_fragment.Type() == NGPhysicalFragment::kFragmentBox && - FragmentRequiresLegacyFallback(child_fragment)) { - child_fragment.GetLayoutObject()->Paint(paint_info); + if (child_container->HasOverflowClip()) { + // We need to properly visit this fragment for painting, rather than + // jumping directly to its children (which is what we normally do when + // looking for floats), in order to set up the clip rectangle. + NGBoxFragmentPainter(To<NGPhysicalBoxFragment>(*child_container)) + .Paint(paint_info); continue; } - if (const auto* child_container = - DynamicTo<NGPhysicalContainerFragment>(&child_fragment)) { - if (child_container->HasFloatingDescendantsForPaint()) - PaintFloatingChildren(*child_container, paint_info, float_paint_info); - } + PaintFloatingChildren(*child_container, paint_info, float_paint_info); } } void NGBoxFragmentPainter::PaintFloats(const PaintInfo& paint_info) { DCHECK(PhysicalFragment().HasFloatingDescendantsForPaint() || - !PhysicalFragment().ChildrenInline()); + !PhysicalFragment().IsInlineFormattingContext()); PaintInfo float_paint_info(paint_info); if (paint_info.phase == PaintPhase::kFloat) @@ -645,12 +803,10 @@ void NGBoxFragmentPainter::PaintBoxDecorationBackground( } } - if (NGFragmentPainter::ShouldRecordHitTestData(paint_info, - PhysicalFragment())) { - HitTestDisplayItem::Record( - paint_info.context, *background_client, - HitTestRect(paint_rect.ToLayoutRect(), - PhysicalFragment().EffectiveAllowedTouchAction())); + if (ShouldRecordHitTestData(paint_info)) { + paint_info.context.GetPaintController().RecordHitTestData( + *background_client, PixelSnappedIntRect(paint_rect), + PhysicalFragment().EffectiveAllowedTouchAction()); } bool needs_scroll_hit_test = true; @@ -677,24 +833,6 @@ void NGBoxFragmentPainter::PaintBoxDecorationBackground( // TODO(kojii): This logic is kept in sync with BoxPainter. Not much efforts to // eliminate LayoutObject dependency were done yet. -bool NGBoxFragmentPainter::BackgroundIsKnownToBeOpaque( - const PaintInfo& paint_info) { - const LayoutBox& layout_box = ToLayoutBox(*box_fragment_.GetLayoutObject()); - - // If the box has multiple fragments, its VisualRect is the bounding box of - // all fragments' visual rects, which is likely to cover areas that are not - // covered by painted background. - if (layout_box.FirstFragment().NextFragment()) - return false; - - PhysicalRect bounds = IsPaintingScrollingBackground(paint_info) - ? layout_box.PhysicalLayoutOverflowRect() - : layout_box.PhysicalSelfVisualOverflowRect(); - return layout_box.BackgroundIsKnownToBeOpaqueInRect(bounds); -} - -// TODO(kojii): This logic is kept in sync with BoxPainter. Not much efforts to -// eliminate LayoutObject dependency were done yet. void NGBoxFragmentPainter::PaintBoxDecorationBackgroundWithRect( const PaintInfo& paint_info, const PhysicalRect& paint_rect, @@ -705,17 +843,9 @@ void NGBoxFragmentPainter::PaintBoxDecorationBackgroundWithRect( const ComputedStyle& style = box_fragment_.Style(); base::Optional<DisplayItemCacheSkipper> cache_skipper; - // Disable cache in under-invalidation checking mode for MediaSliderPart - // because we always paint using the latest data (buffered ranges, current - // time and duration) which may be different from the cached data, and for - // delayed-invalidation object because it may change before it's actually - // invalidated. Note that we still report harmless under-invalidation of - // non-delayed-invalidation animated background, which should be ignored. if (RuntimeEnabledFeatures::PaintUnderInvalidationCheckingEnabled() && - (style.EffectiveAppearance() == kMediaSliderPart || - layout_box.ShouldDelayFullPaintInvalidation())) { + ShouldSkipPaintUnderInvalidationChecking(layout_box)) cache_skipper.emplace(paint_info.context); - } BoxDecorationData box_decoration_data(paint_info, PhysicalFragment()); if (!box_decoration_data.ShouldPaint()) @@ -730,11 +860,6 @@ void NGBoxFragmentPainter::PaintBoxDecorationBackgroundWithRect( DisplayItem::kBoxDecorationBackground); GraphicsContextStateSaver state_saver(paint_info.context, false); - if (RuntimeEnabledFeatures::CompositeAfterPaintEnabled() && - paint_rect.EdgesOnPixelBoundaries() && - BackgroundIsKnownToBeOpaque(paint_info)) - recorder.SetKnownToBeOpaque(); - const NGBorderEdges& border_edges = BorderEdges(); if (box_decoration_data.ShouldPaintShadow()) { PaintNormalBoxShadow(paint_info, paint_rect, style, border_edges.line_left, @@ -971,14 +1096,15 @@ void NGBoxFragmentPainter::PaintInlineChildBoxUsingLegacyFallback( void NGBoxFragmentPainter::PaintAllPhasesAtomically( const PaintInfo& paint_info) { // Self-painting AtomicInlines should go to normal paint logic. - DCHECK(!(PhysicalFragment().IsAtomicInline() && + DCHECK(!(PhysicalFragment().IsPaintedAtomically() && box_fragment_.HasSelfPaintingLayer())); // Pass PaintPhaseSelection and PaintPhaseTextClip is handled by the regular // foreground paint implementation. We don't need complete painting for these // phases. PaintPhase phase = paint_info.phase; - if (phase == PaintPhase::kSelection || phase == PaintPhase::kTextClip) + if (phase == PaintPhase::kSelectionDragImage || + phase == PaintPhase::kTextClip) return PaintInternal(paint_info); if (phase != PaintPhase::kForeground) @@ -1003,39 +1129,33 @@ void NGBoxFragmentPainter::PaintAllPhasesAtomically( void NGBoxFragmentPainter::PaintInlineItems(const PaintInfo& paint_info, const PhysicalOffset& paint_offset, + const PhysicalOffset& parent_offset, NGInlineCursor* cursor) { - ScopedPaintTimingDetectorBlockPaintHook - scoped_paint_timing_detector_block_paint_hook; - // TODO(kojii): Copy more from |PaintLineBoxChildren|. - while (*cursor) { const NGFragmentItem* item = cursor->CurrentItem(); DCHECK(item); switch (item->Type()) { case NGFragmentItem::kText: case NGFragmentItem::kGeneratedText: - PaintTextItem(*cursor, paint_info, paint_offset); - break; - case NGFragmentItem::kLine: - if (PaintLineBoxItem(*item, paint_info, paint_offset) == - kSkipChildren) { - cursor->MoveToNextSkippingChildren(); - continue; - } + if (!item->IsHiddenForPaint()) + PaintTextItem(*cursor, paint_info, paint_offset, parent_offset); + cursor->MoveToNext(); break; case NGFragmentItem::kBox: - if (PaintBoxItem(*item, paint_info, paint_offset) == kSkipChildren) { - cursor->MoveToNextSkippingChildren(); - continue; - } + if (!item->IsHiddenForPaint()) + PaintBoxItem(*item, *cursor, paint_info, paint_offset, parent_offset); + cursor->MoveToNextSkippingChildren(); + break; + case NGFragmentItem::kLine: + NOTREACHED(); + cursor->MoveToNext(); break; } - cursor->MoveToNext(); } } -// Paint a line box. This function paints only background of `::first-line`. In -// all other cases, the container box paints background. +// Paint a line box. This function paints hit tests and backgrounds of +// `::first-line`. In all other cases, the container box paints background. inline void NGBoxFragmentPainter::PaintLineBox( const NGPhysicalFragment& line_box_fragment, const DisplayItemClient& display_item_client, @@ -1046,30 +1166,29 @@ inline void NGBoxFragmentPainter::PaintLineBox( if (paint_info.phase != PaintPhase::kForeground) return; - if (NGFragmentPainter::ShouldRecordHitTestData(paint_info, - PhysicalFragment())) { - RecordHitTestDataForLine(paint_info, child_offset, line_box_fragment, - display_item_client); + if (ShouldRecordHitTestData(paint_info)) { + PhysicalRect border_box = line_box_fragment.LocalRect(); + border_box.offset += child_offset; + paint_info.context.GetPaintController().RecordHitTestData( + display_item_client, PixelSnappedIntRect(border_box), + PhysicalFragment().EffectiveAllowedTouchAction()); + } + if (NGLineBoxFragmentPainter::NeedsPaint(line_box_fragment)) { + NGLineBoxFragmentPainter line_box_painter( + line_box_fragment, line_box_paint_fragment, line_box_item, + PhysicalFragment(), paint_fragment_); + line_box_painter.PaintBackgroundBorderShadow(paint_info, child_offset); } - - // Line boxes don't paint anything, except when its ::first-line style has - // a background. - if (!NGLineBoxFragmentPainter::NeedsPaint(line_box_fragment)) - return; - NGLineBoxFragmentPainter line_box_painter( - line_box_fragment, line_box_paint_fragment, line_box_item, - PhysicalFragment(), paint_fragment_); - line_box_painter.PaintBackgroundBorderShadow(paint_info, child_offset); } void NGBoxFragmentPainter::PaintLineBoxChildren( - NGPaintFragment::ChildList line_boxes, + NGInlineCursor* children, const PaintInfo& paint_info, const PhysicalOffset& paint_offset) { // Only paint during the foreground/selection phases. if (paint_info.phase != PaintPhase::kForeground && paint_info.phase != PaintPhase::kForcedColorsModeBackplate && - paint_info.phase != PaintPhase::kSelection && + paint_info.phase != PaintPhase::kSelectionDragImage && paint_info.phase != PaintPhase::kTextClip && paint_info.phase != PaintPhase::kMask && paint_info.phase != PaintPhase::kDescendantOutlinesOnly && @@ -1078,7 +1197,7 @@ void NGBoxFragmentPainter::PaintLineBoxChildren( // The only way an inline could paint like this is if it has a layer. const auto* layout_object = box_fragment_.GetLayoutObject(); - DCHECK(layout_object->IsLayoutBlock() || + DCHECK(!layout_object || layout_object->IsLayoutBlock() || (layout_object->IsLayoutInline() && layout_object->HasLayer())); // if (paint_info.phase == PaintPhase::kForeground && paint_info.IsPrinting()) @@ -1086,27 +1205,24 @@ void NGBoxFragmentPainter::PaintLineBoxChildren( // paint_offset); // If we have no lines then we have no work to do. - if (line_boxes.IsEmpty()) + if (!*children) return; - ScopedPaintTimingDetectorBlockPaintHook - scoped_paint_timing_detector_block_paint_hook; - const auto& layout_block = To<LayoutBlock>(*layout_object); - if (paint_info.phase == PaintPhase::kForeground) { - scoped_paint_timing_detector_block_paint_hook.EmplaceIfNeeded( - layout_block, - paint_info.context.GetPaintController().CurrentPaintChunkProperties()); + if (paint_info.phase == PaintPhase::kForcedColorsModeBackplate && + box_fragment_.GetDocument().InForcedColorsMode()) { + PaintBackplate(children, paint_info, paint_offset); + return; } - if (paint_info.phase == PaintPhase::kForcedColorsModeBackplate && - layout_block.GetDocument().InForcedColorsMode()) { - PaintBackplate(line_boxes, paint_info, paint_offset); + if (UNLIKELY(children->IsItemCursor())) { + PaintLineBoxChildItems(children, paint_info, paint_offset); return; } const bool is_horizontal = box_fragment_.Style().IsHorizontalWritingMode(); - - for (const NGPaintFragment* line : line_boxes) { + for (; *children; children->MoveToNextSkippingChildren()) { + const NGPaintFragment* line = children->CurrentPaintFragment(); + DCHECK(line); const NGPhysicalFragment& child_fragment = line->PhysicalFragment(); DCHECK(!child_fragment.IsOutOfFlowPositioned()); if (child_fragment.IsFloating()) @@ -1140,14 +1256,67 @@ void NGBoxFragmentPainter::PaintLineBoxChildren( } } -void NGBoxFragmentPainter::PaintBackplate(NGPaintFragment::ChildList line_boxes, +void NGBoxFragmentPainter::PaintLineBoxChildItems( + NGInlineCursor* children, + const PaintInfo& paint_info, + const PhysicalOffset& paint_offset) { + const bool is_horizontal = box_fragment_.Style().IsHorizontalWritingMode(); + for (; *children; children->MoveToNextSkippingChildren()) { + const NGFragmentItem* child_item = children->CurrentItem(); + DCHECK(child_item); + + // Check if CullRect intersects with this child, only in block direction + // because soft-wrap and <br> needs to paint outside of InkOverflow() in + // inline direction. + const PhysicalOffset& child_offset = + paint_offset + child_item->OffsetInContainerBlock(); + const PhysicalRect child_rect = child_item->InkOverflow(); + if (is_horizontal) { + LayoutUnit y = child_rect.offset.top + child_offset.top; + if (!paint_info.GetCullRect().IntersectsVerticalRange( + y, y + child_rect.size.height)) + continue; + } else { + LayoutUnit x = child_rect.offset.left + child_offset.left; + if (!paint_info.GetCullRect().IntersectsHorizontalRange( + x, x + child_rect.size.width)) + continue; + } + + if (child_item->Type() == NGFragmentItem::kLine) { + const NGPhysicalLineBoxFragment* line_box_fragment = + child_item->LineBoxFragment(); + DCHECK(line_box_fragment); + PaintLineBox(*line_box_fragment, *child_item, + /* line_box_paint_fragment */ nullptr, child_item, + paint_info, child_offset); + NGInlineCursor line_box_cursor = children->CursorForDescendants(); + PaintInlineItems(paint_info, paint_offset, + child_item->OffsetInContainerBlock(), &line_box_cursor); + continue; + } + + if (const NGPhysicalBoxFragment* child_fragment = + child_item->BoxFragment()) { + if (child_fragment->IsListMarker()) { + PaintBoxItem(*child_item, *child_fragment, *children, paint_info, + paint_offset); + continue; + } + } + + NOTREACHED(); + } +} + +void NGBoxFragmentPainter::PaintBackplate(NGInlineCursor* line_boxes, const PaintInfo& paint_info, const PhysicalOffset& paint_offset) { if (paint_info.phase != PaintPhase::kForcedColorsModeBackplate) return; // Only paint backplates behind text when forced-color-adjust is auto. - const ComputedStyle& style = line_boxes.front().Style(); + const ComputedStyle& style = PhysicalFragment().Style(); if (style.ForcedColorAdjust() == EForcedColorAdjust::kNone) return; @@ -1229,7 +1398,7 @@ void NGBoxFragmentPainter::PaintTextChild(const NGPaintFragment& paint_fragment, // Only paint during the foreground/selection phases. if (paint_info.phase != PaintPhase::kForeground && - paint_info.phase != PaintPhase::kSelection && + paint_info.phase != PaintPhase::kSelectionDragImage && paint_info.phase != PaintPhase::kTextClip && paint_info.phase != PaintPhase::kMask) return; @@ -1241,24 +1410,20 @@ void NGBoxFragmentPainter::PaintTextChild(const NGPaintFragment& paint_fragment, void NGBoxFragmentPainter::PaintTextItem(const NGInlineCursor& cursor, const PaintInfo& paint_info, - const PhysicalOffset& paint_offset) { + const PhysicalOffset& paint_offset, + const PhysicalOffset& parent_offset) { DCHECK(cursor.CurrentItem()); const NGFragmentItem& item = *cursor.CurrentItem(); DCHECK(item.IsText()) << item; // Only paint during the foreground/selection phases. if (paint_info.phase != PaintPhase::kForeground && - paint_info.phase != PaintPhase::kSelection && + paint_info.phase != PaintPhase::kSelectionDragImage && paint_info.phase != PaintPhase::kTextClip && paint_info.phase != PaintPhase::kMask) return; - // Need to check the style of each text items because they can have different - // styles than its siblings if inline boxes are culled. - if (UNLIKELY(!IsVisibleToPaint(item, item.Style()))) - return; - - NGTextFragmentPainter<NGInlineCursor> text_painter(cursor); + NGTextFragmentPainter<NGInlineCursor> text_painter(cursor, parent_offset); text_painter.Paint(paint_info, paint_offset); } @@ -1269,7 +1434,8 @@ NGBoxFragmentPainter::MoveTo NGBoxFragmentPainter::PaintLineBoxItem( DCHECK_EQ(item.Type(), NGFragmentItem::kLine); DCHECK(items_); // TODO(kojii): Check CullRect. - const PhysicalOffset line_box__offset = paint_offset + item.Offset(); + const PhysicalOffset line_box__offset = + paint_offset + item.OffsetInContainerBlock(); const NGPhysicalLineBoxFragment* line_box_fragment = item.LineBoxFragment(); DCHECK(line_box_fragment); PaintLineBox(*line_box_fragment, item, /* line_box_paint_fragment */ nullptr, @@ -1277,57 +1443,60 @@ NGBoxFragmentPainter::MoveTo NGBoxFragmentPainter::PaintLineBoxItem( return kDontSkipChildren; } -NGBoxFragmentPainter::MoveTo NGBoxFragmentPainter::PaintBoxItem( +// Paint non-culled box item. +void NGBoxFragmentPainter::PaintBoxItem( const NGFragmentItem& item, + const NGPhysicalBoxFragment& child_fragment, + const NGInlineCursor& cursor, const PaintInfo& paint_info, const PhysicalOffset& paint_offset) { DCHECK_EQ(item.Type(), NGFragmentItem::kBox); - - const ComputedStyle& style = item.Style(); - if (UNLIKELY(!IsVisibleToPaint(item, style))) - return kSkipChildren; - - // Nothing to paint if this is a culled inline box. Proceed to its - // descendants. - const NGPhysicalBoxFragment* child_fragment = item.BoxFragment(); - if (!child_fragment) - return kDontSkipChildren; - - DCHECK(!child_fragment->IsHiddenForPaint()); - if (child_fragment->HasSelfPaintingLayer() || child_fragment->IsFloating()) - return kSkipChildren; + DCHECK_EQ(&item, cursor.Current().Item()); + DCHECK_EQ(item.BoxFragment(), &child_fragment); + DCHECK(!child_fragment.IsHiddenForPaint()); + if (child_fragment.HasSelfPaintingLayer() || child_fragment.IsFloating()) + return; // TODO(kojii): Check CullRect. - if (child_fragment->IsAtomicInline() || child_fragment->IsListMarker()) { - if (FragmentRequiresLegacyFallback(*child_fragment)) { - PaintInlineChildBoxUsingLegacyFallback(*child_fragment, paint_info); - return kDontSkipChildren; + if (child_fragment.IsAtomicInline() || child_fragment.IsListMarker()) { + if (FragmentRequiresLegacyFallback(child_fragment)) { + PaintInlineChildBoxUsingLegacyFallback(child_fragment, paint_info); + return; } - NGBoxFragmentPainter(*child_fragment).PaintAllPhasesAtomically(paint_info); - return kDontSkipChildren; + NGBoxFragmentPainter(child_fragment).PaintAllPhasesAtomically(paint_info); + return; } - NGInlineBoxFragmentPainter(item, *child_fragment) + DCHECK(child_fragment.IsInlineBox()); + NGInlineBoxFragmentPainter(cursor, item, child_fragment) .Paint(paint_info, paint_offset); - return kDontSkipChildren; } -void NGBoxFragmentPainter::PaintAtomicInline(const PaintInfo& paint_info) { - DCHECK(PhysicalFragment().IsAtomicInline()); - // Self-painting AtomicInlines should go to normal paint logic. - DCHECK(!box_fragment_.HasSelfPaintingLayer()); +void NGBoxFragmentPainter::PaintBoxItem(const NGFragmentItem& item, + const NGInlineCursor& cursor, + const PaintInfo& paint_info, + const PhysicalOffset& paint_offset, + const PhysicalOffset& parent_offset) { + DCHECK_EQ(item.Type(), NGFragmentItem::kBox); + DCHECK_EQ(&item, cursor.Current().Item()); - // Text clips are painted only for the direct inline children of the object - // that has a text clip style on it, not block children. - if (paint_info.phase == PaintPhase::kTextClip) + if (const NGPhysicalBoxFragment* child_fragment = item.BoxFragment()) { + PaintBoxItem(item, *child_fragment, cursor, paint_info, paint_offset); return; + } - PaintAllPhasesAtomically(paint_info); + // This |item| is a culled inline box. + DCHECK(item.GetLayoutObject()->IsLayoutInline()); + NGInlineCursor children = cursor.CursorForDescendants(); + // Pass the given |parent_offset| because culled inline boxes do not affect + // the sub-pixel snapping behavior. TODO(kojii): This is for the + // compatibility, we may want to revisit in future. + PaintInlineItems(paint_info, paint_offset, parent_offset, &children); } bool NGBoxFragmentPainter::IsPaintingScrollingBackground( - const PaintInfo& paint_info) { + const PaintInfo& paint_info) const { if (RuntimeEnabledFeatures::CompositeAfterPaintEnabled()) return paint_info.IsPaintingScrollingBackground(); @@ -1361,27 +1530,43 @@ void NGBoxFragmentPainter::PaintTextClipMask(GraphicsContext& context, bool object_has_multiple_boxes) { PaintInfo paint_info(context, mask_rect, PaintPhase::kTextClip, kGlobalPaintNormalPhase, 0); - if (object_has_multiple_boxes) { - DCHECK(paint_fragment_); - PhysicalOffset local_offset = paint_fragment_->Offset(); - DCHECK(paint_fragment_); - NGInlineBoxFragmentPainter inline_box_painter(*paint_fragment_); - if (box_fragment_.Style().BoxDecorationBreak() == - EBoxDecorationBreak::kSlice) { - LayoutUnit offset_on_line; - LayoutUnit total_width; - inline_box_painter.ComputeFragmentOffsetOnLine( - box_fragment_.Style().Direction(), &offset_on_line, &total_width); - LayoutSize line_offset(offset_on_line, LayoutUnit()); - local_offset -= - PhysicalOffset(box_fragment_.Style().IsHorizontalWritingMode() - ? line_offset - : line_offset.TransposedSize()); - } - inline_box_painter.Paint(paint_info, paint_offset - local_offset); - } else { + if (!object_has_multiple_boxes) { PaintObject(paint_info, paint_offset); + return; } + + if (paint_fragment_) { + NGInlineBoxFragmentPainter inline_box_painter(*paint_fragment_); + PaintTextClipMask(paint_info, paint_offset - paint_fragment_->Offset(), + &inline_box_painter); + return; + } + + DCHECK(inline_box_cursor_); + DCHECK(box_item_); + NGInlineBoxFragmentPainter inline_box_painter(*inline_box_cursor_, + *box_item_); + PaintTextClipMask(paint_info, + paint_offset - box_item_->OffsetInContainerBlock(), + &inline_box_painter); +} + +void NGBoxFragmentPainter::PaintTextClipMask( + const PaintInfo& paint_info, + PhysicalOffset paint_offset, + NGInlineBoxFragmentPainter* inline_box_painter) { + const ComputedStyle& style = box_fragment_.Style(); + if (style.BoxDecorationBreak() == EBoxDecorationBreak::kSlice) { + LayoutUnit offset_on_line; + LayoutUnit total_width; + inline_box_painter->ComputeFragmentOffsetOnLine( + style.Direction(), &offset_on_line, &total_width); + if (style.IsHorizontalWritingMode()) + paint_offset.left += offset_on_line; + else + paint_offset.top += offset_on_line; + } + inline_box_painter->Paint(paint_info, paint_offset); } PhysicalRect NGBoxFragmentPainter::AdjustRectForScrolledContent( @@ -1421,219 +1606,249 @@ LayoutRectOutsets NGBoxFragmentPainter::ComputePadding() const { BoxPainterBase::FillLayerInfo NGBoxFragmentPainter::GetFillLayerInfo( const Color& color, const FillLayer& bg_layer, - BackgroundBleedAvoidance bleed_avoidance) const { + BackgroundBleedAvoidance bleed_avoidance, + bool is_painting_scrolling_background) const { const NGBorderEdges& border_edges = BorderEdges(); const NGPhysicalBoxFragment& fragment = PhysicalFragment(); return BoxPainterBase::FillLayerInfo( fragment.GetLayoutObject()->GetDocument(), fragment.Style(), fragment.HasOverflowClip(), color, bg_layer, bleed_avoidance, + LayoutObject::ShouldRespectImageOrientation(fragment.GetLayoutObject()), border_edges.line_left, border_edges.line_right, - fragment.GetLayoutObject()->IsInline()); + fragment.GetLayoutObject()->IsLayoutInline(), + is_painting_scrolling_background); +} + +bool NGBoxFragmentPainter::HitTestContext::AddNodeToResult( + Node* node, + const PhysicalRect& bounds_rect, + const PhysicalOffset& offset) const { + if (node && !result->InnerNode()) + result->SetNodeAndPosition(node, location.Point() - offset); + return result->AddNodeToListBasedTestResult(node, location, bounds_rect) == + kStopHitTesting; } -bool NGBoxFragmentPainter::IsInSelfHitTestingPhase(HitTestAction action) const { - // TODO(layout-dev): We should set an IsContainingBlock flag on - // NGPhysicalBoxFragment, instead of routing back to LayoutObject. - if (const auto* box = ToLayoutBoxOrNull(PhysicalFragment().GetLayoutObject())) - return box->IsInSelfHitTestingPhase(action); - return action == kHitTestForeground; +bool NGBoxFragmentPainter::NodeAtPoint(HitTestResult& result, + const HitTestLocation& hit_test_location, + const PhysicalOffset& physical_offset, + HitTestAction action) { + HitTestContext hit_test(action, hit_test_location, physical_offset, &result); + return NodeAtPoint(hit_test, physical_offset); } bool NGBoxFragmentPainter::NodeAtPoint(HitTestResult& result, const HitTestLocation& hit_test_location, const PhysicalOffset& physical_offset, + const PhysicalOffset& inline_root_offset, HitTestAction action) { + HitTestContext hit_test(action, hit_test_location, inline_root_offset, + &result); + return NodeAtPoint(hit_test, physical_offset); +} + +bool NGBoxFragmentPainter::NodeAtPoint(const HitTestContext& hit_test, + const PhysicalOffset& physical_offset) { const NGPhysicalBoxFragment& fragment = PhysicalFragment(); const PhysicalSize& size = box_fragment_.Size(); const ComputedStyle& style = box_fragment_.Style(); - bool hit_test_self = IsInSelfHitTestingPhase(action); + bool hit_test_self = fragment.IsInSelfHitTestingPhase(hit_test.action); - // TODO(layout-dev): Add support for hit testing overflow controls once we - // overflow has been implemented. - // if (hit_test_self && HasOverflowClip() && - // HitTestOverflowControl(result, hit_test_location, physical_offset)) - // return true; + if (hit_test_self && box_fragment_.HasOverflowClip() && + HitTestOverflowControl(hit_test, physical_offset)) + return true; - bool skip_children = result.GetHitTestRequest().GetStopNode() == - PhysicalFragment().GetLayoutObject(); + const LayoutObject* layout_object = PhysicalFragment().GetLayoutObject(); + bool skip_children = + layout_object && + layout_object == hit_test.result->GetHitTestRequest().GetStopNode(); if (!skip_children && box_fragment_.ShouldClipOverflow()) { // PaintLayer::HitTestContentsForFragments checked the fragments' // foreground rect for intersection if a layer is self painting, // so only do the overflow clip check here for non-self-painting layers. if (!box_fragment_.HasSelfPaintingLayer() && - !hit_test_location.Intersects(PhysicalFragment().OverflowClipRect( + !hit_test.location.Intersects(PhysicalFragment().OverflowClipRect( physical_offset, kExcludeOverlayScrollbarSizeForHitTesting))) { skip_children = true; } if (!skip_children && style.HasBorderRadius()) { PhysicalRect bounds_rect(physical_offset, size); - skip_children = !hit_test_location.Intersects( + skip_children = !hit_test.location.Intersects( style.GetRoundedInnerBorderFor(bounds_rect.ToLayoutRect())); } } if (!skip_children) { - PhysicalOffset scrolled_offset = physical_offset; - if (box_fragment_.HasOverflowClip()) { - scrolled_offset -= PhysicalOffset( - PhysicalFragment().PixelSnappedScrolledContentOffset()); + if (!box_fragment_.HasOverflowClip()) { + if (HitTestChildren(hit_test, physical_offset)) + return true; + } else { + const PhysicalOffset scrolled_offset = + physical_offset - + PhysicalOffset( + PhysicalFragment().PixelSnappedScrolledContentOffset()); + HitTestContext adjusted_hit_test(hit_test.action, hit_test.location, + scrolled_offset, hit_test.result); + if (HitTestChildren(adjusted_hit_test, scrolled_offset)) + return true; } - if (HitTestChildren(result, hit_test_location, scrolled_offset, action)) - return true; } if (style.HasBorderRadius() && - HitTestClippedOutByBorder(hit_test_location, physical_offset)) + HitTestClippedOutByBorder(hit_test.location, physical_offset)) return false; // Now hit test ourselves. - if (hit_test_self && VisibleToHitTestRequest(result.GetHitTestRequest())) { + if (hit_test_self && + VisibleToHitTestRequest(hit_test.result->GetHitTestRequest())) { PhysicalRect bounds_rect(physical_offset, size); - if (UNLIKELY(result.GetHitTestRequest().GetType() & + if (UNLIKELY(hit_test.result->GetHitTestRequest().GetType() & HitTestRequest::kHitTestVisualOverflow)) { - bounds_rect = paint_fragment_->SelfInkOverflow(); + bounds_rect = SelfInkOverflow(); bounds_rect.Move(physical_offset); } // TODO(kojii): Don't have good explanation why only inline box needs to // snap, but matches to legacy and fixes crbug.com/976606. if (fragment.IsInlineBox()) bounds_rect = PhysicalRect(PixelSnappedIntRect(bounds_rect)); - if (hit_test_location.Intersects(bounds_rect)) { - Node* node = fragment.NodeForHitTest(); - if (!result.InnerNode() && node) { - PhysicalOffset point = hit_test_location.Point() - physical_offset; - result.SetNodeAndPosition(node, point); - } - if (result.AddNodeToListBasedTestResult(node, hit_test_location, - bounds_rect) == kStopHitTesting) { + if (hit_test.location.Intersects(bounds_rect)) { + if (hit_test.AddNodeToResult(fragment.NodeForHitTest(), bounds_rect, + physical_offset)) return true; - } } } return false; } +bool NGBoxFragmentPainter::HitTestAllPhases( + HitTestResult& result, + const HitTestLocation& hit_test_location, + const PhysicalOffset& accumulated_offset, + HitTestFilter hit_test_filter) { + // Logic taken from LayoutObject::HitTestAllPhases(). + HitTestContext hit_test(kHitTestForeground, hit_test_location, + accumulated_offset, &result); + bool inside = false; + if (hit_test_filter != kHitTestSelf) { + // First test the foreground layer (lines and inlines). + inside = NodeAtPoint(hit_test, accumulated_offset); + + // Test floats next. + if (!inside) { + hit_test.action = kHitTestFloat; + inside = NodeAtPoint(hit_test, accumulated_offset); + } + + // Finally test to see if the mouse is in the background (within a child + // block's background). + if (!inside) { + hit_test.action = kHitTestChildBlockBackgrounds; + inside = NodeAtPoint(hit_test, accumulated_offset); + } + } + + // See if the pointer is inside us but not any of our descendants. + if (hit_test_filter != kHitTestDescendants && !inside) { + hit_test.action = kHitTestChildBlockBackground; + inside = NodeAtPoint(hit_test, accumulated_offset); + } + + return inside; +} + bool NGBoxFragmentPainter::VisibleToHitTestRequest( const HitTestRequest& request) const { return FragmentVisibleToHitTestRequest(box_fragment_, request); } bool NGBoxFragmentPainter::HitTestTextFragment( - HitTestResult& result, + const HitTestContext& hit_test, const NGInlineBackwardCursor& cursor, - const HitTestLocation& hit_test_location, - const PhysicalOffset& physical_offset, - HitTestAction action) { - if (action != kHitTestForeground) + const PhysicalOffset& physical_offset) { + if (hit_test.action != kHitTestForeground) return false; - const NGPaintFragment* text_paint_fragment = cursor.CurrentPaintFragment(); + const NGPaintFragment* text_paint_fragment = cursor.Current().PaintFragment(); DCHECK(text_paint_fragment); const auto& text_fragment = To<NGPhysicalTextFragment>(text_paint_fragment->PhysicalFragment()); - PhysicalRect border_rect(physical_offset, text_fragment.Size()); + if (!FragmentVisibleToHitTestRequest(text_fragment, + hit_test.result->GetHitTestRequest())) + return false; // TODO(layout-dev): Clip to line-top/bottom. + PhysicalRect border_rect(physical_offset, text_fragment.Size()); PhysicalRect rect(PixelSnappedIntRect(border_rect)); - if (UNLIKELY(result.GetHitTestRequest().GetType() & + if (UNLIKELY(hit_test.result->GetHitTestRequest().GetType() & HitTestRequest::kHitTestVisualOverflow)) { rect = text_fragment.SelfInkOverflow(); rect.Move(border_rect.offset); } + if (!hit_test.location.Intersects(rect)) + return false; - if (FragmentVisibleToHitTestRequest(text_fragment, - result.GetHitTestRequest()) && - hit_test_location.Intersects(rect)) { - Node* node = text_fragment.NodeForHitTest(); - if (!result.InnerNode() && node) { - PhysicalOffset point = hit_test_location.Point() - physical_offset + - text_paint_fragment->InlineOffsetToContainerBox(); - result.SetNodeAndPosition(node, point); - } - - if (result.AddNodeToListBasedTestResult(node, hit_test_location, rect) == - kStopHitTesting) { - return true; - } - } - - return false; + return hit_test.AddNodeToResult( + text_fragment.NodeForHitTest(), rect, + physical_offset - text_paint_fragment->OffsetInContainerBlock()); } -bool NGBoxFragmentPainter::HitTestTextItem( - HitTestResult& result, - const NGFragmentItem& text_item, - const HitTestLocation& hit_test_location, - const PhysicalOffset& physical_offset, - HitTestAction action) { +bool NGBoxFragmentPainter::HitTestTextItem(const HitTestContext& hit_test, + const NGFragmentItem& text_item) { DCHECK(text_item.IsText()); - if (action != kHitTestForeground) + if (hit_test.action != kHitTestForeground) + return false; + if (!IsVisibleToHitTest(text_item, hit_test.result->GetHitTestRequest())) return false; - - PhysicalRect border_rect(physical_offset, text_item.Size()); // TODO(layout-dev): Clip to line-top/bottom. + const PhysicalOffset offset = + hit_test.inline_root_offset + text_item.OffsetInContainerBlock(); + PhysicalRect border_rect(offset, text_item.Size()); PhysicalRect rect(PixelSnappedIntRect(border_rect)); - if (UNLIKELY(result.GetHitTestRequest().GetType() & + if (UNLIKELY(hit_test.result->GetHitTestRequest().GetType() & HitTestRequest::kHitTestVisualOverflow)) { rect = text_item.SelfInkOverflow(); rect.Move(border_rect.offset); } + if (!hit_test.location.Intersects(rect)) + return false; - if (IsVisibleToHitTest(text_item, result.GetHitTestRequest()) && - hit_test_location.Intersects(rect)) { - Node* node = text_item.NodeForHitTest(); - if (!result.InnerNode() && node) { - PhysicalOffset point = - hit_test_location.Point() - physical_offset + text_item.Offset(); - result.SetNodeAndPosition(node, point); - } - - if (result.AddNodeToListBasedTestResult(node, hit_test_location, rect) == - kStopHitTesting) { - return true; - } - } - - return false; + return hit_test.AddNodeToResult(text_item.NodeForHitTest(), rect, + hit_test.inline_root_offset); } // Replicates logic in legacy InlineFlowBox::NodeAtPoint(). bool NGBoxFragmentPainter::HitTestLineBoxFragment( - HitTestResult& result, + const HitTestContext& hit_test, const NGPhysicalLineBoxFragment& fragment, const NGInlineBackwardCursor& cursor, - const HitTestLocation& hit_test_location, - const PhysicalOffset& physical_offset, - HitTestAction action) { - if (HitTestChildren(result, cursor.CursorForDescendants(), hit_test_location, - physical_offset, action)) + const PhysicalOffset& physical_offset) { + if (HitTestChildren(hit_test, cursor.CursorForDescendants(), physical_offset)) return true; - if (action != kHitTestForeground) + if (hit_test.action != kHitTestForeground) return false; - if (!VisibleToHitTestRequest(result.GetHitTestRequest())) + if (!VisibleToHitTestRequest(hit_test.result->GetHitTestRequest())) return false; const PhysicalOffset overflow_location = - cursor.CurrentSelfInkOverflow().offset + physical_offset; - if (HitTestClippedOutByBorder(hit_test_location, overflow_location)) + cursor.Current().SelfInkOverflow().offset + physical_offset; + if (HitTestClippedOutByBorder(hit_test.location, overflow_location)) return false; const PhysicalRect bounds_rect(physical_offset, fragment.Size()); const ComputedStyle& containing_box_style = box_fragment_.Style(); if (containing_box_style.HasBorderRadius() && - !hit_test_location.Intersects(containing_box_style.GetRoundedBorderFor( - bounds_rect.ToLayoutRect()))) { + !hit_test.location.Intersects( + containing_box_style.GetRoundedBorderFor(bounds_rect.ToLayoutRect()))) return false; - } // Now hit test ourselves. - if (!hit_test_location.Intersects(bounds_rect)) + if (!hit_test.location.Intersects(bounds_rect)) return false; // Floats will be hit-tested in |kHitTestFloat| phase, but @@ -1643,123 +1858,207 @@ bool NGBoxFragmentPainter::HitTestLineBoxFragment( // restructuring. Changing the caller logic isn't easy because currently // floats are in the bounds of line boxes only in NG. if (fragment.HasFloatingDescendantsForPaint()) { - DCHECK_NE(action, kHitTestFloat); - if (HitTestChildren(result, cursor.CursorForDescendants(), - hit_test_location, physical_offset, kHitTestFloat)) { + DCHECK_NE(hit_test.action, kHitTestFloat); + HitTestContext hit_test_float = hit_test; + hit_test_float.action = kHitTestFloat; + if (HitTestChildren(hit_test_float, cursor.CursorForDescendants(), + physical_offset)) return false; - } } - Node* node = fragment.NodeForHitTest(); - if (!result.InnerNode() && node) { - const PhysicalOffset point = - hit_test_location.Point() - physical_offset + cursor.CurrentOffset(); - result.SetNodeAndPosition(node, point); - } - return result.AddNodeToListBasedTestResult(node, hit_test_location, - bounds_rect) == kStopHitTesting; + return hit_test.AddNodeToResult( + fragment.NodeForHitTest(), bounds_rect, + physical_offset - cursor.Current().OffsetInContainerBlock()); } bool NGBoxFragmentPainter::HitTestChildBoxFragment( - HitTestResult& result, + const HitTestContext& hit_test, const NGPhysicalBoxFragment& fragment, - const NGInlineBackwardCursor& cursor, - const HitTestLocation& hit_test_location, - const PhysicalOffset& physical_offset, - HitTestAction action) { + const NGInlineBackwardCursor& backward_cursor, + const PhysicalOffset& physical_offset) { // Note: Floats should only be hit tested in the |kHitTestFloat| phase, so we // shouldn't enter a float when |action| doesn't match. However, as floats may // scatter around in the entire inline formatting context, we should always // enter non-floating inline child boxes to search for floats in the // |kHitTestFloat| phase, unless the child box forms another context. - if (fragment.IsFloating() && action != kHitTestFloat) + if (fragment.IsFloating() && hit_test.action != kHitTestFloat) return false; if (!FragmentRequiresLegacyFallback(fragment)) { - // TODO(layout-dev): Implement HitTestAllPhases in NG after we stop - // falling back to legacy for child atomic inlines and floats. DCHECK(!fragment.IsAtomicInline()); DCHECK(!fragment.IsFloating()); - if (const NGPaintFragment* paint_fragment = cursor.CurrentPaintFragment()) { + if (const NGPaintFragment* paint_fragment = + backward_cursor.Current().PaintFragment()) { + if (fragment.IsInlineBox()) { + return NGBoxFragmentPainter(*paint_fragment) + .NodeAtPoint(hit_test, physical_offset); + } + // When traversing into a different inline formatting context, + // |inline_root_offset| needs to be updated. return NGBoxFragmentPainter(*paint_fragment) - .NodeAtPoint(result, hit_test_location, physical_offset, action); + .NodeAtPoint(*hit_test.result, hit_test.location, physical_offset, + hit_test.action); } - const NGFragmentItem* item = cursor.CurrentItem(); + NGInlineCursor cursor(backward_cursor); + const NGFragmentItem* item = cursor.Current().Item(); DCHECK(item); DCHECK_EQ(item->BoxFragment(), &fragment); - NGInlineCursor descendants = cursor.CursorForDescendants(); - return NGBoxFragmentPainter(*item, fragment, &descendants) - .NodeAtPoint(result, hit_test_location, physical_offset, action); + if (fragment.IsInlineBox()) { + return NGBoxFragmentPainter(cursor, *item, fragment) + .NodeAtPoint(hit_test, physical_offset); + } + // When traversing into a different inline formatting context, + // |inline_root_offset| needs to be updated. + return NGBoxFragmentPainter(cursor, *item, fragment) + .NodeAtPoint(*hit_test.result, hit_test.location, physical_offset, + hit_test.action); } - if (fragment.IsInline() && action != kHitTestForeground) + if (fragment.IsInline() && hit_test.action != kHitTestForeground) return false; - LayoutBox* const layout_box = ToLayoutBox(fragment.GetMutableLayoutObject()); + if (fragment.IsPaintedAtomically()) { + return HitTestAllPhasesInFragment(fragment, hit_test.location, + physical_offset, hit_test.result); + } - // https://www.w3.org/TR/CSS22/zindex.html#painting-order - // Hit test all phases of inline blocks, inline tables, replaced elements and - // non-positioned floats as if they created their own stacking contexts. - if (fragment.IsAtomicInline() || fragment.IsFloating()) { - return layout_box->HitTestAllPhases(result, hit_test_location, - physical_offset); + return fragment.GetMutableLayoutObject()->NodeAtPoint( + *hit_test.result, hit_test.location, physical_offset, hit_test.action); +} + +bool NGBoxFragmentPainter::HitTestChildBoxItem( + const HitTestContext& hit_test, + const NGFragmentItem& item, + const NGInlineBackwardCursor& cursor) { + DCHECK_EQ(&item, cursor.Current().Item()); + + if (const NGPhysicalBoxFragment* child_fragment = item.BoxFragment()) { + const PhysicalOffset child_offset = + hit_test.inline_root_offset + item.OffsetInContainerBlock(); + return HitTestChildBoxFragment(hit_test, *child_fragment, cursor, + child_offset); + } + + DCHECK(item.GetLayoutObject()->IsLayoutInline()); + DCHECK(!ToLayoutInline(item.GetLayoutObject())->ShouldCreateBoxFragment()); + if (NGInlineCursor descendants = cursor.CursorForDescendants()) { + if (HitTestItemsChildren(hit_test, descendants)) + return true; } - return layout_box->NodeAtPoint(result, hit_test_location, physical_offset, - action); + + // Now hit test ourselves. + if (hit_test.action == kHitTestForeground && + IsVisibleToHitTest(item, hit_test.result->GetHitTestRequest())) { + const PhysicalOffset child_offset = + hit_test.inline_root_offset + item.OffsetInContainerBlock(); + PhysicalRect bounds_rect(child_offset, item.Size()); + if (UNLIKELY(hit_test.result->GetHitTestRequest().GetType() & + HitTestRequest::kHitTestVisualOverflow)) { + bounds_rect = item.SelfInkOverflow(); + bounds_rect.Move(child_offset); + } + // TODO(kojii): Don't have good explanation why only inline box needs to + // snap, but matches to legacy and fixes crbug.com/976606. + bounds_rect = PhysicalRect(PixelSnappedIntRect(bounds_rect)); + if (hit_test.location.Intersects(bounds_rect)) { + if (hit_test.AddNodeToResult(item.NodeForHitTest(), bounds_rect, + child_offset)) + return true; + } + } + + return false; } bool NGBoxFragmentPainter::HitTestChildren( - HitTestResult& result, - const HitTestLocation& hit_test_location, - const PhysicalOffset& accumulated_offset, - HitTestAction action) { + const HitTestContext& hit_test, + const PhysicalOffset& accumulated_offset) { if (paint_fragment_) { NGInlineCursor cursor(*paint_fragment_); - return HitTestChildren(result, cursor, hit_test_location, - accumulated_offset, action); + return HitTestChildren(hit_test, cursor, accumulated_offset); } - if (UNLIKELY(descendants_)) { - if (!*descendants_) - return false; - return HitTestChildren(result, *descendants_, hit_test_location, - accumulated_offset, action); + if (UNLIKELY(inline_box_cursor_)) { + NGInlineCursor descendants = inline_box_cursor_->CursorForDescendants(); + if (descendants) + return HitTestChildren(hit_test, descendants, accumulated_offset); + return false; } if (items_) { NGInlineCursor cursor(*items_); - return HitTestChildren(result, cursor, hit_test_location, - accumulated_offset, action); + return HitTestChildren(hit_test, cursor, accumulated_offset); } - NOTREACHED(); - return false; + // Check descendants of this fragment because floats may be in the + // |NGFragmentItems| of the descendants. + if (hit_test.action == kHitTestFloat && + box_fragment_.HasFloatingDescendantsForPaint() && + RuntimeEnabledFeatures::LayoutNGFragmentItemEnabled()) { + return HitTestFloatingChildren(hit_test, box_fragment_, accumulated_offset); + } + + if (hit_test.action == kHitTestFloat) { + return box_fragment_.HasFloatingDescendantsForPaint() && + HitTestFloatingChildren(hit_test, box_fragment_, accumulated_offset); + } + return HitTestBlockChildren(*hit_test.result, hit_test.location, + accumulated_offset, hit_test.action); } bool NGBoxFragmentPainter::HitTestChildren( - HitTestResult& result, + const HitTestContext& hit_test, const NGInlineCursor& children, + const PhysicalOffset& accumulated_offset) { + if (children.IsPaintFragmentCursor()) + return HitTestPaintFragmentChildren(hit_test, children, accumulated_offset); + if (children.IsItemCursor()) + return HitTestItemsChildren(hit_test, children); + // Hits nothing if there were no children. + return false; +} + +bool NGBoxFragmentPainter::HitTestBlockChildren( + HitTestResult& result, const HitTestLocation& hit_test_location, - const PhysicalOffset& accumulated_offset, + PhysicalOffset accumulated_offset, HitTestAction action) { - if (children.IsPaintFragmentCursor()) { - return HitTestPaintFragmentChildren(result, children, hit_test_location, - accumulated_offset, action); - } - if (children.IsItemCursor()) { - return HitTestItemsChildren(result, children, hit_test_location, - accumulated_offset, action); + if (action == kHitTestChildBlockBackgrounds) + action = kHitTestChildBlockBackground; + auto children = box_fragment_.Children(); + for (const NGLink& child : base::Reversed(children)) { + const auto& block_child = To<NGPhysicalBoxFragment>(*child); + if (block_child.HasSelfPaintingLayer() || block_child.IsFloating()) + continue; + + const PhysicalOffset child_offset = accumulated_offset + child.offset; + + if (block_child.IsPaintedAtomically()) { + if (HitTestAllPhasesInFragment(block_child, hit_test_location, + child_offset, &result)) + return true; + + continue; + } + + if (NodeAtPointInFragment(block_child, hit_test_location, child_offset, + action, &result)) { + if (const LayoutObject* child_object = block_child.GetLayoutObject()) { + child_object->UpdateHitTestResult( + result, hit_test_location.Point() - accumulated_offset); + } + return true; + } } - // Hits nothing if there were no children. + return false; } bool NGBoxFragmentPainter::HitTestPaintFragmentChildren( - HitTestResult& result, + const HitTestContext& hit_test, const NGInlineCursor& children, - const HitTestLocation& hit_test_location, - const PhysicalOffset& accumulated_offset, - HitTestAction action) { + const PhysicalOffset& accumulated_offset) { DCHECK(children.IsPaintFragmentCursor()); for (NGInlineBackwardCursor cursor(children); cursor;) { - const NGPaintFragment* child_paint_fragment = cursor.CurrentPaintFragment(); + const NGPaintFragment* child_paint_fragment = + cursor.Current().PaintFragment(); DCHECK(child_paint_fragment); const NGPhysicalFragment& child_fragment = child_paint_fragment->PhysicalFragment(); @@ -1771,30 +2070,29 @@ bool NGBoxFragmentPainter::HitTestPaintFragmentChildren( const PhysicalOffset child_offset = child_paint_fragment->Offset() + accumulated_offset; if (child_fragment.Type() == NGPhysicalFragment::kFragmentBox) { - if (HitTestChildBoxFragment( - result, To<NGPhysicalBoxFragment>(child_fragment), cursor, - hit_test_location, child_offset, action)) + if (HitTestChildBoxFragment(hit_test, + To<NGPhysicalBoxFragment>(child_fragment), + cursor, child_offset)) return true; } else if (child_fragment.Type() == NGPhysicalFragment::kFragmentLineBox) { - if (HitTestLineBoxFragment( - result, To<NGPhysicalLineBoxFragment>(child_fragment), cursor, - hit_test_location, child_offset, action)) + if (HitTestLineBoxFragment(hit_test, + To<NGPhysicalLineBoxFragment>(child_fragment), + cursor, child_offset)) return true; } else if (child_fragment.Type() == NGPhysicalFragment::kFragmentText) { - if (HitTestTextFragment(result, cursor, hit_test_location, child_offset, - action)) + if (HitTestTextFragment(hit_test, cursor, child_offset)) return true; } cursor.MoveToPreviousSibling(); - if (child_fragment.IsInline() && action == kHitTestForeground) { + if (child_fragment.IsInline() && hit_test.action == kHitTestForeground) { // Hit test culled inline boxes between |fragment| and its parent // fragment. const NGPaintFragment* previous_sibling = - cursor ? cursor.CurrentPaintFragment() : nullptr; - if (HitTestCulledInlineAncestors(result, *child_paint_fragment, - previous_sibling, hit_test_location, + cursor ? cursor.Current().PaintFragment() : nullptr; + if (HitTestCulledInlineAncestors(*hit_test.result, *child_paint_fragment, + previous_sibling, hit_test.location, child_offset)) return true; } @@ -1804,44 +2102,142 @@ bool NGBoxFragmentPainter::HitTestPaintFragmentChildren( } bool NGBoxFragmentPainter::HitTestItemsChildren( - HitTestResult& result, - const NGInlineCursor& children, - const HitTestLocation& hit_test_location, - const PhysicalOffset& accumulated_offset, - HitTestAction action) { + const HitTestContext& hit_test, + const NGInlineCursor& children) { DCHECK(children.IsItemCursor()); for (NGInlineBackwardCursor cursor(children); cursor;) { - const NGFragmentItem* item = cursor.CurrentItem(); + const NGFragmentItem* item = cursor.Current().Item(); DCHECK(item); if (item->HasSelfPaintingLayer()) { cursor.MoveToPreviousSibling(); continue; } - const PhysicalOffset child_offset = item->Offset() + accumulated_offset; if (item->IsText()) { - if (HitTestTextItem(result, *item, hit_test_location, child_offset, - action)) + if (HitTestTextItem(hit_test, *item)) return true; } else if (item->Type() == NGFragmentItem::kLine) { const NGPhysicalLineBoxFragment* child_fragment = item->LineBoxFragment(); DCHECK(child_fragment); - if (HitTestLineBoxFragment(result, *child_fragment, cursor, - hit_test_location, child_offset, action)) + const PhysicalOffset child_offset = + hit_test.inline_root_offset + item->OffsetInContainerBlock(); + if (HitTestLineBoxFragment(hit_test, *child_fragment, cursor, + child_offset)) return true; } else if (item->Type() == NGFragmentItem::kBox) { - if (const NGPhysicalBoxFragment* child_fragment = item->BoxFragment()) { - if (HitTestChildBoxFragment(result, *child_fragment, cursor, - hit_test_location, child_offset, action)) - return true; - } + if (HitTestChildBoxItem(hit_test, *item, cursor)) + return true; } else { NOTREACHED(); } cursor.MoveToPreviousSibling(); + } + + return false; +} - // TODO(kojii): Implement hit-testing culled inline box. +bool NGBoxFragmentPainter::HitTestFloatingChildren( + const HitTestContext& hit_test, + const NGPhysicalContainerFragment& container, + const PhysicalOffset& accumulated_offset) { + DCHECK_EQ(hit_test.action, kHitTestFloat); + DCHECK(container.HasFloatingDescendantsForPaint()); + + if (const auto* box = DynamicTo<NGPhysicalBoxFragment>(&container)) { + if (const NGFragmentItems* items = box->Items()) { + NGInlineCursor children(*items); + return HitTestFloatingChildItems(hit_test, children, accumulated_offset); + } + } + + auto children = container.Children(); + for (const NGLink& child : base::Reversed(children)) { + const NGPhysicalFragment& child_fragment = *child.fragment; + if (child_fragment.HasSelfPaintingLayer()) + continue; + + const PhysicalOffset child_offset = accumulated_offset + child.offset; + + if (child_fragment.IsFloating()) { + if (HitTestAllPhasesInFragment(To<NGPhysicalBoxFragment>(child_fragment), + hit_test.location, child_offset, + hit_test.result)) + return true; + continue; + } + + if (child_fragment.IsPaintedAtomically()) + continue; + + const auto* child_container = + DynamicTo<NGPhysicalContainerFragment>(&child_fragment); + if (!child_container || !child_container->HasFloatingDescendantsForPaint()) + continue; + + if (child_container->HasOverflowClip()) { + // We need to properly visit this fragment for hit-testing, rather than + // jumping directly to its children (which is what we normally do when + // looking for floats), in order to set up the clip rectangle. + if (child_container->CanTraverse()) { + if (NGBoxFragmentPainter(*To<NGPhysicalBoxFragment>(child_container)) + .NodeAtPoint(*hit_test.result, hit_test.location, child_offset, + kHitTestFloat)) + return true; + } else if (child_fragment.GetMutableLayoutObject()->NodeAtPoint( + *hit_test.result, hit_test.location, child_offset, + kHitTestFloat)) { + return true; + } + continue; + } + + if (HitTestFloatingChildren(hit_test, *child_container, child_offset)) + return true; + } + return false; +} + +bool NGBoxFragmentPainter::HitTestFloatingChildItems( + const HitTestContext& hit_test, + const NGInlineCursor& children, + const PhysicalOffset& accumulated_offset) { + for (NGInlineBackwardCursor cursor(children); cursor; + cursor.MoveToPreviousSibling()) { + const NGFragmentItem* item = cursor.Current().Item(); + DCHECK(item); + if (item->Type() == NGFragmentItem::kBox) { + if (const NGPhysicalBoxFragment* child_box = item->BoxFragment()) { + if (child_box->HasSelfPaintingLayer()) + continue; + + const PhysicalOffset child_offset = + accumulated_offset + item->OffsetInContainerBlock(); + if (child_box->IsFloating()) { + if (HitTestAllPhasesInFragment(*child_box, hit_test.location, + child_offset, hit_test.result)) + return true; + continue; + } + + // Look into descendants of all inline boxes because inline boxes do not + // have |HasFloatingDescendantsForPaint()| flag. + if (!child_box->IsInlineBox()) + continue; + } + DCHECK(item->GetLayoutObject()->IsLayoutInline()); + } else if (item->Type() == NGFragmentItem::kLine) { + const NGPhysicalLineBoxFragment* child_line = item->LineBoxFragment(); + DCHECK(child_line); + if (!child_line->HasFloatingDescendantsForPaint()) + continue; + } else { + continue; + } + + NGInlineCursor descendants = cursor.CursorForDescendants(); + if (HitTestFloatingChildItems(hit_test, descendants, accumulated_offset)) + return true; } return false; @@ -1858,4 +2254,13 @@ bool NGBoxFragmentPainter::HitTestClippedOutByBorder( rect.ToLayoutRect(), border_edges.line_left, border_edges.line_right)); } +bool NGBoxFragmentPainter::HitTestOverflowControl( + const HitTestContext& hit_test, + PhysicalOffset accumulated_offset) { + const auto* layout_box = ToLayoutBoxOrNull(box_fragment_.GetLayoutObject()); + return layout_box && + layout_box->HitTestOverflowControl(*hit_test.result, hit_test.location, + accumulated_offset); +} + } // namespace blink 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 08fd0e73eda..5fe1f5fe9a1 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 @@ -8,6 +8,7 @@ #include "third_party/blink/renderer/core/layout/api/hit_test_action.h" #include "third_party/blink/renderer/core/layout/background_bleed_avoidance.h" #include "third_party/blink/renderer/core/layout/ng/geometry/ng_border_edges.h" +#include "third_party/blink/renderer/core/layout/ng/inline/ng_inline_cursor.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" @@ -24,6 +25,7 @@ class HitTestResult; class NGFragmentItems; class NGInlineCursor; class NGInlineBackwardCursor; +class NGInlineBoxFragmentPainter; class NGPhysicalFragment; class ScopedPaintState; struct PaintInfo; @@ -41,9 +43,9 @@ class NGBoxFragmentPainter : public BoxPainterBase { // Construct for an inline formatting context. NGBoxFragmentPainter(const NGPaintFragment&); // Construct for an inline box. - NGBoxFragmentPainter(const NGFragmentItem& item, - const NGPhysicalBoxFragment& fragment, - NGInlineCursor* descendants); + NGBoxFragmentPainter(const NGInlineCursor& inline_box_cursor, + const NGFragmentItem& item, + const NGPhysicalBoxFragment& fragment); void Paint(const PaintInfo&); void PaintObject(const PaintInfo&, @@ -58,6 +60,16 @@ class NGBoxFragmentPainter : public BoxPainterBase { const HitTestLocation& hit_test_location, const PhysicalOffset& physical_offset, HitTestAction); + bool NodeAtPoint(HitTestResult&, + const HitTestLocation& hit_test_location, + const PhysicalOffset& physical_offset, + const PhysicalOffset& inline_root_offset, + HitTestAction); + + bool HitTestAllPhases(HitTestResult&, + const HitTestLocation&, + const PhysicalOffset& accumulated_offset, + HitTestFilter = kHitTestAll); protected: LayoutRectOutsets ComputeBorders() const override; @@ -65,12 +77,17 @@ class NGBoxFragmentPainter : public BoxPainterBase { BoxPainterBase::FillLayerInfo GetFillLayerInfo( const Color&, const FillLayer&, - BackgroundBleedAvoidance) const override; + BackgroundBleedAvoidance, + bool is_painting_scrolling_background) const override; + bool IsPaintingScrollingBackground(const PaintInfo&) const override; void PaintTextClipMask(GraphicsContext&, const IntRect& mask_rect, const PhysicalOffset& paint_offset, bool object_has_multiple_boxes) override; + void PaintTextClipMask(const PaintInfo& paint_info, + PhysicalOffset paint_offset, + NGInlineBoxFragmentPainter* inline_box_painter); PhysicalRect AdjustRectForScrolledContent( const PaintInfo&, const BoxPainterBase::FillLayerInfo&, @@ -80,10 +97,10 @@ class NGBoxFragmentPainter : public BoxPainterBase { NGBoxFragmentPainter(const NGPhysicalBoxFragment&, const DisplayItemClient& display_item_client, const NGPaintFragment* = nullptr, - NGInlineCursor* descendants = nullptr); + const NGInlineCursor* inline_box_cursor = nullptr, + const NGFragmentItem* = nullptr); enum MoveTo { kDontSkipChildren, kSkipChildren }; - bool IsPaintingScrollingBackground(const PaintInfo&); bool ShouldPaint(const ScopedPaintState&) const; void PaintBoxDecorationBackground(const PaintInfo&, @@ -93,24 +110,27 @@ class NGBoxFragmentPainter : public BoxPainterBase { const PhysicalRect&, const DisplayItemClient&); void PaintColumnRules(const PaintInfo&, const PhysicalOffset& paint_offset); - bool BackgroundIsKnownToBeOpaque(const PaintInfo&); void PaintInternal(const PaintInfo&); void PaintAllPhasesAtomically(const PaintInfo&); - void PaintBlockChildren(const PaintInfo&); + void PaintBlockChildren(const PaintInfo&, PhysicalOffset); void PaintInlineItems(const PaintInfo&, const PhysicalOffset& paint_offset, + const PhysicalOffset& parent_offset, NGInlineCursor* cursor); - void PaintLineBoxChildren(NGPaintFragment::ChildList, + void PaintLineBoxChildren(NGInlineCursor* children, const PaintInfo&, const PhysicalOffset& paint_offset); + void PaintLineBoxChildItems(NGInlineCursor* children, + const PaintInfo&, + const PhysicalOffset& paint_offset); void PaintLineBox(const NGPhysicalFragment& line_box_fragment, const DisplayItemClient& display_item_client, const NGPaintFragment* line_box_paint_fragment, const NGFragmentItem* line_box_item, const PaintInfo&, const PhysicalOffset& paint_offset); - void PaintBackplate(NGPaintFragment::ChildList, + void PaintBackplate(NGInlineCursor* descendants, const PaintInfo&, const PhysicalOffset& paint_offset); void PaintInlineChildren(NGPaintFragment::ChildList, @@ -126,97 +146,128 @@ class NGBoxFragmentPainter : public BoxPainterBase { const PhysicalOffset& paint_offset); void PaintTextItem(const NGInlineCursor& cursor, const PaintInfo&, - const PhysicalOffset& paint_offset); + const PhysicalOffset& paint_offset, + const PhysicalOffset& parent_offset); MoveTo PaintLineBoxItem(const NGFragmentItem& item, const PaintInfo& paint_info, const PhysicalOffset& paint_offset); - MoveTo PaintBoxItem(const NGFragmentItem& item, - const PaintInfo& paint_info, - const PhysicalOffset& paint_offset); + void PaintBoxItem(const NGFragmentItem& item, + const NGPhysicalBoxFragment& child_fragment, + const NGInlineCursor& cursor, + const PaintInfo& paint_info, + const PhysicalOffset& paint_offset); + void PaintBoxItem(const NGFragmentItem& item, + const NGInlineCursor& cursor, + const PaintInfo& paint_info, + const PhysicalOffset& paint_offset, + const PhysicalOffset& parent_offset); + void PaintFloatingItems(const PaintInfo&, NGInlineCursor* cursor); void PaintFloatingChildren(const NGPhysicalContainerFragment&, const PaintInfo& paint_info, const PaintInfo& float_paint_info); void PaintFloats(const PaintInfo&); void PaintMask(const PaintInfo&, const PhysicalOffset& paint_offset); - void PaintAtomicInline(const PaintInfo&); void PaintBackground(const PaintInfo&, const PhysicalRect&, const Color& background_color, BackgroundBleedAvoidance = kBackgroundBleedNone); void PaintCarets(const PaintInfo&, const PhysicalOffset& paint_offset); - // Paint a scroll hit test display item and record scroll hit test data. This - // should be called in the background paint phase even if there is no other - // painted content. + // This should be called in the background paint phase even if there is no + // other painted content. void RecordScrollHitTestData(const PaintInfo&, const DisplayItemClient& background_client); - void RecordHitTestDataForLine(const PaintInfo& paint_info, - const PhysicalOffset& paint_offset, - const NGPhysicalFragment& line, - const DisplayItemClient& display_item_client); + bool ShouldRecordHitTestData(const PaintInfo&); - bool IsInSelfHitTestingPhase(HitTestAction) const; bool VisibleToHitTestRequest(const HitTestRequest&) const; + // This struct has common data needed while traversing trees for the hit + // testing. + struct HitTestContext { + STACK_ALLOCATED(); + + public: + HitTestContext(HitTestAction action, + const HitTestLocation& location, + const PhysicalOffset& inline_root_offset, + HitTestResult* result) + : action(action), + location(location), + inline_root_offset(inline_root_offset), + result(result) {} + + // Add |node| to |HitTestResult|. Returns true if the hit-testing should + // stop. + bool AddNodeToResult(Node* node, + const PhysicalRect& bounds_rect, + const PhysicalOffset& offset) const; + + HitTestAction action; + const HitTestLocation& location; + // When traversing within an inline formatting context, this member + // represents the offset of the root of the inline formatting context. + PhysicalOffset inline_root_offset; + // The result is set to this member, but its address does not change during + // the traversal. + HitTestResult* result; + }; + // Hit tests the children of a container fragment, which is either // |box_fragment_|, or one of its child line box fragments. // @param physical_offset Physical offset of the container fragment's content // box in paint layer. Note that this includes scrolling offset when the // container has 'overflow: scroll'. - bool HitTestChildren(HitTestResult&, - const HitTestLocation& hit_test_location, - const PhysicalOffset& physical_offset, - HitTestAction); - bool HitTestChildren(HitTestResult&, + bool NodeAtPoint(const HitTestContext& hit_test, + const PhysicalOffset& physical_offset); + bool HitTestChildren(const HitTestContext& hit_test, + const PhysicalOffset& physical_offset); + bool HitTestChildren(const HitTestContext& hit_test, const NGInlineCursor& children, - const HitTestLocation& hit_test_location, - const PhysicalOffset& physical_offset, - HitTestAction); - bool HitTestPaintFragmentChildren(HitTestResult&, - const NGInlineCursor& children, - const HitTestLocation& hit_test_location, - const PhysicalOffset& physical_offset, - HitTestAction); - bool HitTestItemsChildren(HitTestResult&, - const NGInlineCursor& children, - const HitTestLocation& hit_test_location, - const PhysicalOffset& physical_offset, + const PhysicalOffset& physical_offset); + bool HitTestBlockChildren(HitTestResult&, + const HitTestLocation&, + PhysicalOffset, HitTestAction); + bool HitTestPaintFragmentChildren(const HitTestContext& hit_test, + const NGInlineCursor& children, + const PhysicalOffset& physical_offset); + bool HitTestItemsChildren(const HitTestContext& hit_test, + const NGInlineCursor& children); + bool HitTestFloatingChildren(const HitTestContext& hit_test, + const NGPhysicalContainerFragment& container, + const PhysicalOffset& accumulated_offset); + bool HitTestFloatingChildItems(const HitTestContext& hit_test, + const NGInlineCursor& children, + const PhysicalOffset& accumulated_offset); // Hit tests a box fragment, which is a child of either |box_fragment_|, or // one of its child line box fragments. // @param physical_offset Physical offset of the given box fragment in the // paint layer. - bool HitTestChildBoxFragment(HitTestResult&, + bool HitTestChildBoxFragment(const HitTestContext& hit_test, const NGPhysicalBoxFragment& fragment, const NGInlineBackwardCursor& cursor, - const HitTestLocation& hit_test_location, - const PhysicalOffset& physical_offset, - HitTestAction); + const PhysicalOffset& physical_offset); + bool HitTestChildBoxItem(const HitTestContext& hit_test, + const NGFragmentItem& item, + const NGInlineBackwardCursor& cursor); // Hit tests the given text fragment. // @param physical_offset Physical offset of the text fragment in paint layer. - bool HitTestTextFragment(HitTestResult&, + bool HitTestTextFragment(const HitTestContext& hit_test, const NGInlineBackwardCursor& cursor, - const HitTestLocation& hit_test_location, - const PhysicalOffset& physical_offset, - HitTestAction); - bool HitTestTextItem(HitTestResult& result, - const NGFragmentItem& text_item, - const HitTestLocation& hit_test_location, - const PhysicalOffset& physical_offset, - HitTestAction action); + const PhysicalOffset& physical_offset); + bool HitTestTextItem(const HitTestContext& hit_test, + const NGFragmentItem& text_item); // Hit tests the given line box fragment. // @param physical_offset Physical offset of the line box fragment in paint // layer. - bool HitTestLineBoxFragment(HitTestResult&, + bool HitTestLineBoxFragment(const HitTestContext& hit_test, const NGPhysicalLineBoxFragment& fragment, const NGInlineBackwardCursor& cursor, - const HitTestLocation& hit_test_location, - const PhysicalOffset& physical_offset, - HitTestAction); + const PhysicalOffset& physical_offset); // Returns whether the hit test location is completely outside the border box, // which possibly has rounded corners. @@ -224,6 +275,9 @@ class NGBoxFragmentPainter : public BoxPainterBase { const HitTestLocation&, const PhysicalOffset& border_box_location) const; + bool HitTestOverflowControl(const HitTestContext&, + PhysicalOffset accumulated_offset); + const NGPhysicalBoxFragment& PhysicalFragment() const { return box_fragment_; } @@ -231,6 +285,8 @@ class NGBoxFragmentPainter : public BoxPainterBase { return display_item_client_; } const NGBorderEdges& BorderEdges() const; + PhysicalRect SelfInkOverflow() const; + PhysicalRect ContentsInkOverflow() const; const NGPhysicalBoxFragment& box_fragment_; const DisplayItemClient& display_item_client_; @@ -240,7 +296,7 @@ class NGBoxFragmentPainter : public BoxPainterBase { const NGPaintFragment* paint_fragment_; const NGFragmentItems* items_; const NGFragmentItem* box_item_ = nullptr; - NGInlineCursor* descendants_ = nullptr; + const NGInlineCursor* inline_box_cursor_ = nullptr; mutable base::Optional<NGBorderEdges> border_edges_; }; @@ -248,34 +304,45 @@ inline NGBoxFragmentPainter::NGBoxFragmentPainter( const NGPhysicalBoxFragment& box, const DisplayItemClient& display_item_client, const NGPaintFragment* paint_fragment, - NGInlineCursor* descendants) + const NGInlineCursor* inline_box_cursor, + const NGFragmentItem* box_item) : BoxPainterBase(&box.GetDocument(), box.Style(), box.GeneratingNode()), box_fragment_(box), display_item_client_(display_item_client), paint_fragment_(paint_fragment), items_(box.Items()), - descendants_(descendants) { + box_item_(box_item), + inline_box_cursor_(inline_box_cursor) { DCHECK(box.IsBox() || box.IsRenderedLegend()); - DCHECK(!paint_fragment || !descendants); #if DCHECK_IS_ON() - if (box.IsInlineBox()) { + if (RuntimeEnabledFeatures::LayoutNGFragmentItemEnabled()) { + DCHECK(!paint_fragment_); + if (inline_box_cursor_) + DCHECK_EQ(inline_box_cursor_->Current().Item(), box_item_); + if (box_item_) + DCHECK_EQ(box_item_->BoxFragment(), &box); + DCHECK_EQ(box.IsInlineBox(), !!inline_box_cursor_); + DCHECK_EQ(box.IsInlineBox(), !!box_item_); + } else { + DCHECK(!inline_box_cursor_); + DCHECK(!box_item_); if (paint_fragment) DCHECK_EQ(&paint_fragment->PhysicalFragment(), &box); - } else if (box.ChildrenInline()) { - // If no children, there maybe or may not be NGPaintFragment. - // TODO(kojii): To be investigated if this correct or should be fixed. - if (!box.Children().empty()) { - DCHECK(paint_fragment || box.HasItems()); - if (paint_fragment) - DCHECK_EQ(&paint_fragment->PhysicalFragment(), &box); + if (box.IsInlineBox()) { + DCHECK(paint_fragment_); + } else if (box.IsInlineFormattingContext()) { + // If no children, there maybe or may not be NGPaintFragment. + // TODO(kojii): To be investigated if this correct or should be fixed. + if (!box.Children().empty()) { + if (!box.GetLayoutObject() || + !box.GetLayoutObject()->PaintBlockedByDisplayLock( + DisplayLockLifecycleTarget::kChildren)) { + DCHECK(paint_fragment); + } + } + } else { + // We may not have |paint_fragment_| nor |box_item_|. } - } else if (box.IsColumnBox() || - (box.GetLayoutObject()->SlowFirstChild() && - box.GetLayoutObject()->SlowFirstChild()->IsLayoutFlowThread())) { - // TODO(kojii): NGPaintFragment for multicol has non-inline children - // (kColumnBox). Could this be regular box fragments? - } else { - DCHECK(!paint_fragment); } #endif } @@ -285,7 +352,8 @@ inline NGBoxFragmentPainter::NGBoxFragmentPainter( : NGBoxFragmentPainter(fragment, *fragment.GetLayoutObject(), /* paint_fragment */ nullptr, - /* descendants */ nullptr) {} + /* inline_box_cursor */ nullptr, + /* box_item */ nullptr) {} inline NGBoxFragmentPainter::NGBoxFragmentPainter( const NGPhysicalBoxFragment& fragment, @@ -297,7 +365,8 @@ inline NGBoxFragmentPainter::NGBoxFragmentPainter( : *static_cast<const DisplayItemClient*>( fragment.GetLayoutObject()), paint_fragment, - /* descendants */ nullptr) {} + /* inline_box_cursor */ nullptr, + /* box_item */ nullptr) {} inline NGBoxFragmentPainter::NGBoxFragmentPainter( const NGPaintFragment& paint_fragment) @@ -307,16 +376,16 @@ inline NGBoxFragmentPainter::NGBoxFragmentPainter( &paint_fragment) {} inline NGBoxFragmentPainter::NGBoxFragmentPainter( + const NGInlineCursor& inline_box_cursor, const NGFragmentItem& item, - const NGPhysicalBoxFragment& fragment, - NGInlineCursor* descendants) + const NGPhysicalBoxFragment& fragment) : NGBoxFragmentPainter(fragment, item, /* paint_fragment */ nullptr, - descendants) { + &inline_box_cursor, + &item) { DCHECK_EQ(item.BoxFragment(), &fragment); DCHECK(fragment.IsInlineBox()); - box_item_ = &item; } } // namespace blink diff --git a/chromium/third_party/blink/renderer/core/paint/ng/ng_box_fragment_painter_test.cc b/chromium/third_party/blink/renderer/core/paint/ng/ng_box_fragment_painter_test.cc index 813071ca277..ccaf7fcbf22 100644 --- a/chromium/third_party/blink/renderer/core/paint/ng/ng_box_fragment_painter_test.cc +++ b/chromium/third_party/blink/renderer/core/paint/ng/ng_box_fragment_painter_test.cc @@ -6,6 +6,7 @@ #include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" +#include "third_party/blink/renderer/core/layout/ng/inline/ng_inline_cursor.h" #include "third_party/blink/renderer/core/layout/ng/layout_ng_block_flow.h" #include "third_party/blink/renderer/core/layout/ng/ng_block_node.h" #include "third_party/blink/renderer/core/paint/ng/ng_paint_fragment.h" @@ -24,10 +25,9 @@ class NGBoxFragmentPainterTest : public PaintControllerPaintTest, ScopedLayoutNGForTest(true) {} }; -using NGBoxFragmentPainterScrollHitTestTest = NGBoxFragmentPainterTest; -INSTANTIATE_SCROLL_HIT_TEST_SUITE_P(NGBoxFragmentPainterScrollHitTestTest); +INSTANTIATE_PAINT_TEST_SUITE_P(NGBoxFragmentPainterTest); -TEST_P(NGBoxFragmentPainterScrollHitTestTest, ScrollHitTestOrder) { +TEST_P(NGBoxFragmentPainterTest, ScrollHitTestOrder) { GetPage().GetSettings().SetPreferCompositingToLCDTextEnabled(false); SetBodyInnerHTML(R"HTML( <!DOCTYPE html> @@ -43,17 +43,59 @@ TEST_P(NGBoxFragmentPainterScrollHitTestTest, ScrollHitTestOrder) { </style> <div id='scroller'>TEXT</div> )HTML"); - auto& scroller = *GetLayoutObjectByElementId("scroller"); + auto& scroller = ToLayoutBox(*GetLayoutObjectByElementId("scroller")); - const NGPaintFragment& root_fragment = *scroller.PaintFragment(); - const NGPaintFragment& line_box_fragment = *root_fragment.FirstChild(); - const NGPaintFragment& text_fragment = *line_box_fragment.FirstChild(); + const DisplayItemClient& root_fragment = + scroller.PaintFragment() + ? static_cast<const DisplayItemClient&>(*scroller.PaintFragment()) + : static_cast<const DisplayItemClient&>(scroller); + + NGInlineCursor cursor; + cursor.MoveTo(*scroller.SlowFirstChild()); + const DisplayItemClient& text_fragment = + *cursor.Current().GetDisplayItemClient(); EXPECT_THAT(RootPaintController().GetDisplayItemList(), ElementsAre(IsSameId(&ViewScrollingBackgroundClient(), DisplayItem::kDocumentBackground), - IsSameId(&root_fragment, DisplayItem::kScrollHitTest), IsSameId(&text_fragment, kForegroundType))); + HitTestData scroll_hit_test; + scroll_hit_test.scroll_translation = + &scroller.FirstFragment().ContentsProperties().Transform(); + scroll_hit_test.scroll_hit_test_rect = IntRect(0, 0, 40, 40); + if (RuntimeEnabledFeatures::CompositeAfterPaintEnabled()) { + EXPECT_THAT( + RootPaintController().PaintChunks(), + ElementsAre( + IsPaintChunk(0, 1, + PaintChunk::Id(ViewScrollingBackgroundClient(), + DisplayItem::kDocumentBackground), + GetLayoutView().FirstFragment().ContentsProperties()), + IsPaintChunk( + 1, 1, + PaintChunk::Id(*scroller.Layer(), DisplayItem::kLayerChunk), + scroller.FirstFragment().LocalBorderBoxProperties()), + IsPaintChunk( + 1, 1, + PaintChunk::Id(root_fragment, DisplayItem::kScrollHitTest), + scroller.FirstFragment().LocalBorderBoxProperties(), + &scroll_hit_test, IntRect(0, 0, 40, 40)), + IsPaintChunk(1, 2))); + } else { + EXPECT_THAT( + RootPaintController().PaintChunks(), + ElementsAre( + IsPaintChunk(0, 1, + PaintChunk::Id(ViewScrollingBackgroundClient(), + DisplayItem::kDocumentBackground), + GetLayoutView().FirstFragment().ContentsProperties()), + IsPaintChunk( + 1, 1, + PaintChunk::Id(root_fragment, DisplayItem::kScrollHitTest), + scroller.FirstFragment().LocalBorderBoxProperties(), + &scroll_hit_test, IntRect(0, 0, 40, 40)), + IsPaintChunk(1, 2))); + } } } // namespace blink 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 a79eee1c887..fe8baf7fa64 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 @@ -32,11 +32,6 @@ void NGFieldsetPainter::PaintBoxDecorationBackground( // Paint the fieldset (background, other decorations, and) border, with the // cutout hole for the legend. PaintFieldsetDecorationBackground(legend, paint_info, paint_offset); - - // Proceed to painting the legend. According to the spec, it should be done as - // part of the border phase. - if (legend) - PaintLegend(To<NGPhysicalBoxFragment>(**legend), paint_info); } void NGFieldsetPainter::PaintFieldsetDecorationBackground( @@ -103,16 +98,4 @@ void NGFieldsetPainter::PaintFieldsetDecorationBackground( } } -void NGFieldsetPainter::PaintLegend(const NGPhysicalBoxFragment& legend, - const PaintInfo& paint_info) { - // Unless the legend establishes its own self-painting layer, paint the legend - // as part of the border phase, according to spec. - const LayoutObject* legend_object = legend.GetLayoutObject(); - if (ToLayoutBox(legend_object)->HasSelfPaintingLayer()) - return; - PaintInfo legend_paint_info = paint_info; - legend_paint_info.phase = PaintPhase::kForeground; - ObjectPainter(*legend_object).PaintAllPhasesAtomically(legend_paint_info); -} - } // namespace blink diff --git a/chromium/third_party/blink/renderer/core/paint/ng/ng_fieldset_painter.h b/chromium/third_party/blink/renderer/core/paint/ng/ng_fieldset_painter.h index ce6012566e4..5a5f601b09d 100644 --- a/chromium/third_party/blink/renderer/core/paint/ng/ng_fieldset_painter.h +++ b/chromium/third_party/blink/renderer/core/paint/ng/ng_fieldset_painter.h @@ -27,7 +27,6 @@ class NGFieldsetPainter { void PaintFieldsetDecorationBackground(const NGLink* legend, const PaintInfo&, const PhysicalOffset&); - void PaintLegend(const NGPhysicalBoxFragment& legend, const PaintInfo&); const NGPhysicalBoxFragment& fieldset_; }; diff --git a/chromium/third_party/blink/renderer/core/paint/ng/ng_fragment_painter.cc b/chromium/third_party/blink/renderer/core/paint/ng/ng_fragment_painter.cc index 3fcbfa66af4..c27547d8ed0 100644 --- a/chromium/third_party/blink/renderer/core/paint/ng/ng_fragment_painter.cc +++ b/chromium/third_party/blink/renderer/core/paint/ng/ng_fragment_painter.cc @@ -83,23 +83,4 @@ void NGFragmentPainter::AddURLRectIfNeeded(const PaintInfo& paint_info, paint_info.context.SetURLForRect(url, rect); } -bool NGFragmentPainter::ShouldRecordHitTestData( - const PaintInfo& paint_info, - const NGPhysicalBoxFragment& fragment) { - // 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 false; - - // If an object is not visible, it does not participate in hit testing. - if (fragment.Style().Visibility() != EVisibility::kVisible) - return false; - - auto touch_action = fragment.EffectiveAllowedTouchAction(); - if (touch_action == TouchAction::kTouchActionAuto) - return false; - - return true; -} - } // namespace blink diff --git a/chromium/third_party/blink/renderer/core/paint/ng/ng_fragment_painter.h b/chromium/third_party/blink/renderer/core/paint/ng/ng_fragment_painter.h index 5d7cee1a9ed..60a21cf012d 100644 --- a/chromium/third_party/blink/renderer/core/paint/ng/ng_fragment_painter.h +++ b/chromium/third_party/blink/renderer/core/paint/ng/ng_fragment_painter.h @@ -22,28 +22,23 @@ class NGFragmentPainter : public ObjectPainterBase { public: NGFragmentPainter(const NGPhysicalBoxFragment& box, - const NGPaintFragment* paint_fragment) - : box_fragment_(box), paint_fragment_(paint_fragment) {} + const DisplayItemClient& display_item_client) + : box_fragment_(box), display_item_client_(display_item_client) {} void PaintOutline(const PaintInfo&, const PhysicalOffset& paint_offset); void AddURLRectIfNeeded(const PaintInfo&, const PhysicalOffset& paint_offset); - static bool ShouldRecordHitTestData(const PaintInfo&, - const NGPhysicalBoxFragment&); - private: const NGPhysicalBoxFragment& PhysicalFragment() const { return box_fragment_; } const DisplayItemClient& GetDisplayItemClient() const { - if (paint_fragment_) - return *paint_fragment_; - return *PhysicalFragment().GetLayoutObject(); + return display_item_client_; } const NGPhysicalBoxFragment& box_fragment_; - const NGPaintFragment* paint_fragment_; + const DisplayItemClient& display_item_client_; }; } // namespace blink diff --git a/chromium/third_party/blink/renderer/core/paint/ng/ng_inline_box_fragment_painter.cc b/chromium/third_party/blink/renderer/core/paint/ng/ng_inline_box_fragment_painter.cc index dda54a466e9..15aa6b95573 100644 --- a/chromium/third_party/blink/renderer/core/paint/ng/ng_inline_box_fragment_painter.cc +++ b/chromium/third_party/blink/renderer/core/paint/ng/ng_inline_box_fragment_painter.cc @@ -34,7 +34,14 @@ inline bool HasMultiplePaintFragments(const LayoutObject& layout_object) { } inline bool HasMultipleFragmentItems(const LayoutObject& layout_object) { - return HasMultipleItems(NGFragmentItem::ItemsFor(layout_object)); + NGInlineCursor cursor; + cursor.MoveTo(layout_object); + DCHECK(cursor); + if (cursor) { + cursor.MoveToNextForSameLayoutObject(); + return cursor; + } + return false; } } // namespace @@ -52,7 +59,7 @@ void NGInlineBoxFragmentPainter::Paint(const PaintInfo& paint_info, const PhysicalOffset adjusted_paint_offset = paint_offset + (inline_box_paint_fragment_ ? inline_box_paint_fragment_->Offset() - : inline_box_item_->Offset()); + : inline_box_item_->OffsetInContainerBlock()); if (paint_info.phase == PaintPhase::kForeground) PaintBackgroundBorderShadow(paint_info, adjusted_paint_offset); @@ -64,9 +71,10 @@ void NGInlineBoxFragmentPainter::Paint(const PaintInfo& paint_info, suppress_box_decoration_background); return; } + DCHECK(inline_box_cursor_); DCHECK(inline_box_item_); - NGBoxFragmentPainter box_painter(*inline_box_item_, PhysicalFragment(), - descendants_); + NGBoxFragmentPainter box_painter(*inline_box_cursor_, *inline_box_item_, + PhysicalFragment()); box_painter.PaintObject(paint_info, adjusted_paint_offset, suppress_box_decoration_background); } @@ -112,11 +120,21 @@ void NGInlineBoxFragmentPainterBase::PaintBackgroundBorderShadow( // TODO(eae): Switch to LayoutNG version of BackgroundImageGeometry. BackgroundImageGeometry geometry(*static_cast<const LayoutBoxModelObject*>( inline_box_fragment_.GetLayoutObject())); - // TODO(kojii): not applicable for line box - NGBoxFragmentPainter box_painter( - To<NGPhysicalBoxFragment>(inline_box_fragment_), - inline_box_paint_fragment_); const NGBorderEdges& border_edges = BorderEdges(); + if (inline_box_paint_fragment_) { + NGBoxFragmentPainter box_painter( + To<NGPhysicalBoxFragment>(inline_box_fragment_), + inline_box_paint_fragment_); + PaintBoxDecorationBackground( + box_painter, paint_info, paint_offset, adjusted_frame_rect, geometry, + object_has_multiple_boxes, border_edges.line_left, + border_edges.line_right); + return; + } + DCHECK(inline_box_cursor_); + NGBoxFragmentPainter box_painter( + *inline_box_cursor_, *inline_box_item_, + To<NGPhysicalBoxFragment>(inline_box_fragment_)); PaintBoxDecorationBackground(box_painter, paint_info, paint_offset, adjusted_frame_rect, geometry, object_has_multiple_boxes, @@ -178,22 +196,34 @@ void NGInlineBoxFragmentPainterBase::ComputeFragmentOffsetOnLine( LayoutUnit* offset_on_line, LayoutUnit* total_width) const { WritingMode writing_mode = inline_box_fragment_.Style().GetWritingMode(); - NGPaintFragment::FragmentRange fragments = - inline_box_paint_fragment_->InlineFragmentsFor( - inline_box_fragment_.GetLayoutObject()); + NGInlineCursor cursor; + DCHECK(inline_box_fragment_.GetLayoutObject()); + cursor.MoveTo(*inline_box_fragment_.GetLayoutObject()); LayoutUnit before; LayoutUnit after; bool before_self = true; - for (auto iter = fragments.begin(); iter != fragments.end(); ++iter) { - if (*iter == inline_box_paint_fragment_) { - before_self = false; - continue; + for (; cursor; cursor.MoveToNextForSameLayoutObject()) { + if (inline_box_paint_fragment_) { + DCHECK(cursor.CurrentPaintFragment()); + if (cursor.CurrentPaintFragment() == inline_box_paint_fragment_) { + before_self = false; + continue; + } + } else { + DCHECK(inline_box_item_); + DCHECK(cursor.CurrentItem()); + if (cursor.CurrentItem() == inline_box_item_) { + before_self = false; + continue; + } } + const NGPhysicalBoxFragment* box_fragment = cursor.Current().BoxFragment(); + DCHECK(box_fragment); if (before_self) - before += NGFragment(writing_mode, iter->PhysicalFragment()).InlineSize(); + before += NGFragment(writing_mode, *box_fragment).InlineSize(); else - after += NGFragment(writing_mode, iter->PhysicalFragment()).InlineSize(); + after += NGFragment(writing_mode, *box_fragment).InlineSize(); } NGFragment logical_fragment(writing_mode, inline_box_fragment_); @@ -326,7 +356,7 @@ void NGInlineBoxFragmentPainter::PaintAllFragments( for (const NGPaintFragment* fragment : fragments) { PhysicalOffset child_offset = paint_offset + - fragment->InlineOffsetToContainerBox() - + fragment->OffsetInContainerBlock() - fragment->Offset(); DCHECK(fragment->PhysicalFragment().IsBox()); NGInlineBoxFragmentPainter(*fragment).Paint(paint_info, child_offset); @@ -334,7 +364,6 @@ void NGInlineBoxFragmentPainter::PaintAllFragments( return; } - DCHECK(layout_inline.ShouldCreateBoxFragment()); NGInlineCursor cursor(*block_flow); cursor.MoveTo(layout_inline); for (; cursor; cursor.MoveToNextForSameLayoutObject()) { @@ -342,10 +371,23 @@ void NGInlineBoxFragmentPainter::PaintAllFragments( DCHECK(item); const NGPhysicalBoxFragment* box_fragment = item->BoxFragment(); DCHECK(box_fragment); - NGInlineCursor descendants = cursor.CursorForDescendants(); - NGInlineBoxFragmentPainter(*item, *box_fragment, &descendants) + NGInlineBoxFragmentPainter(cursor, *item, *box_fragment) .Paint(paint_info, paint_offset); } } +#if DCHECK_IS_ON() +void NGInlineBoxFragmentPainter::CheckValid() const { + if (inline_box_item_) { + DCHECK(inline_box_cursor_); + DCHECK_EQ(inline_box_cursor_->Current().Item(), inline_box_item_); + } + + DCHECK_EQ(inline_box_fragment_.Type(), + NGPhysicalFragment::NGFragmentType::kFragmentBox); + DCHECK_EQ(inline_box_fragment_.BoxType(), + NGPhysicalFragment::NGBoxType::kInlineBox); +} +#endif + } // namespace blink diff --git a/chromium/third_party/blink/renderer/core/paint/ng/ng_inline_box_fragment_painter.h b/chromium/third_party/blink/renderer/core/paint/ng/ng_inline_box_fragment_painter.h index 478e697015a..5b2529a74ae 100644 --- a/chromium/third_party/blink/renderer/core/paint/ng/ng_inline_box_fragment_painter.h +++ b/chromium/third_party/blink/renderer/core/paint/ng/ng_inline_box_fragment_painter.h @@ -6,6 +6,7 @@ #define THIRD_PARTY_BLINK_RENDERER_CORE_PAINT_NG_NG_INLINE_BOX_FRAGMENT_PAINTER_H_ #include "third_party/blink/renderer/core/layout/ng/geometry/ng_border_edges.h" +#include "third_party/blink/renderer/core/layout/ng/inline/ng_inline_cursor.h" #include "third_party/blink/renderer/core/layout/ng/inline/ng_physical_line_box_fragment.h" #include "third_party/blink/renderer/core/paint/inline_box_painter_base.h" #include "third_party/blink/renderer/core/paint/ng/ng_box_fragment_painter.h" @@ -30,6 +31,7 @@ class NGInlineBoxFragmentPainterBase : public InlineBoxPainterBase { protected: NGInlineBoxFragmentPainterBase( const NGPhysicalFragment& inline_box_fragment, + const NGInlineCursor* inline_box_cursor, const NGPaintFragment* inline_box_paint_fragment, const NGFragmentItem* inline_box_item, const LayoutObject& layout_object, @@ -42,8 +44,13 @@ class NGInlineBoxFragmentPainterBase : public InlineBoxPainterBase { line_style), inline_box_fragment_(inline_box_fragment), inline_box_paint_fragment_(inline_box_paint_fragment), - inline_box_item_(inline_box_item) { + inline_box_item_(inline_box_item), + inline_box_cursor_(inline_box_cursor) { #if DCHECK_IS_ON() + if (inline_box_cursor) { + DCHECK(inline_box_item); + DCHECK_EQ(inline_box_cursor->Current().Item(), inline_box_item); + } if (inline_box_paint_fragment) { DCHECK_EQ(&inline_box_paint_fragment->PhysicalFragment(), &inline_box_fragment); @@ -65,6 +72,7 @@ class NGInlineBoxFragmentPainterBase : public InlineBoxPainterBase { const ComputedStyle& style, const ComputedStyle& line_style) : NGInlineBoxFragmentPainterBase(inline_box_fragment.PhysicalFragment(), + /* inline_box_cursor */ nullptr, &inline_box_fragment, /* inline_box_item */ nullptr, layout_object, @@ -73,12 +81,14 @@ class NGInlineBoxFragmentPainterBase : public InlineBoxPainterBase { // Constructor for |NGFragmentItem|. NGInlineBoxFragmentPainterBase( + const NGInlineCursor& inline_box_cursor, const NGFragmentItem& inline_box_item, const NGPhysicalBoxFragment& inline_box_fragment, const LayoutObject& layout_object, const ComputedStyle& style, const ComputedStyle& line_style) : NGInlineBoxFragmentPainterBase(inline_box_fragment, + &inline_box_cursor, /* inline_box_paint_fragment */ nullptr, &inline_box_item, layout_object, @@ -114,6 +124,7 @@ class NGInlineBoxFragmentPainterBase : public InlineBoxPainterBase { const NGPhysicalFragment& inline_box_fragment_; const NGPaintFragment* inline_box_paint_fragment_ = nullptr; const NGFragmentItem* inline_box_item_ = nullptr; + const NGInlineCursor* inline_box_cursor_ = nullptr; }; // Painter for LayoutNG inline box fragments. Delegates to NGBoxFragmentPainter @@ -135,19 +146,23 @@ class NGInlineBoxFragmentPainter : public NGInlineBoxFragmentPainterBase { } // Constructor for |NGFragmentItem|. - NGInlineBoxFragmentPainter(const NGFragmentItem& inline_box_item, - const NGPhysicalBoxFragment& inline_box_fragment, - NGInlineCursor* descendants = nullptr) - : NGInlineBoxFragmentPainterBase(inline_box_item, + NGInlineBoxFragmentPainter(const NGInlineCursor& inline_box_cursor, + const NGFragmentItem& inline_box_item, + const NGPhysicalBoxFragment& inline_box_fragment) + : NGInlineBoxFragmentPainterBase(inline_box_cursor, + inline_box_item, inline_box_fragment, *inline_box_fragment.GetLayoutObject(), inline_box_fragment.Style(), - inline_box_fragment.Style()), - descendants_(descendants) { - DCHECK_EQ(inline_box_fragment.Type(), - NGPhysicalFragment::NGFragmentType::kFragmentBox); - DCHECK_EQ(inline_box_fragment.BoxType(), - NGPhysicalFragment::NGBoxType::kInlineBox); + inline_box_fragment.Style()) { + CheckValid(); + } + NGInlineBoxFragmentPainter(const NGInlineCursor& inline_box_cursor, + const NGFragmentItem& inline_box_item) + : NGInlineBoxFragmentPainter(inline_box_cursor, + inline_box_item, + *inline_box_item.BoxFragment()) { + DCHECK(inline_box_item.BoxFragment()); } void Paint(const PaintInfo&, const PhysicalOffset& paint_offset); @@ -163,8 +178,13 @@ class NGInlineBoxFragmentPainter : public NGInlineBoxFragmentPainterBase { const NGBorderEdges BorderEdges() const final; +#if DCHECK_IS_ON() + void CheckValid() const; +#else + void CheckValid() const {} +#endif + mutable base::Optional<NGBorderEdges> border_edges_; - NGInlineCursor* descendants_ = nullptr; }; // Painter for LayoutNG line box fragments. Line boxes don't paint anything, @@ -206,6 +226,7 @@ class NGLineBoxFragmentPainter : public NGInlineBoxFragmentPainterBase { const LayoutObject& layout_block_flow) : NGInlineBoxFragmentPainterBase( line_box_fragment, + /* inline_box_cursor */ nullptr, line_box_paint_fragment, line_box_item, layout_block_flow, diff --git a/chromium/third_party/blink/renderer/core/paint/ng/ng_mathml_painter.cc b/chromium/third_party/blink/renderer/core/paint/ng/ng_mathml_painter.cc new file mode 100644 index 00000000000..68e778f333d --- /dev/null +++ b/chromium/third_party/blink/renderer/core/paint/ng/ng_mathml_painter.cc @@ -0,0 +1,58 @@ +// Copyright 2020 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/ng/ng_mathml_painter.h" + +#include "third_party/blink/renderer/core/layout/ng/mathml/ng_math_layout_utils.h" +#include "third_party/blink/renderer/core/paint/ng/ng_box_fragment_painter.h" +#include "third_party/blink/renderer/core/paint/paint_info.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/graphics/paint/drawing_recorder.h" + +namespace blink { + +void NGMathMLPainter::PaintBar(const PaintInfo& info, const IntRect& bar) { + if (bar.IsEmpty()) + return; + + GraphicsContextStateSaver state_saver(info.context); + info.context.SetStrokeThickness(bar.Height()); + info.context.SetStrokeStyle(kSolidStroke); + info.context.SetStrokeColor( + box_fragment_.Style().VisitedDependentColor(GetCSSPropertyColor())); + IntPoint line_end_point = {bar.Width(), 0}; + info.context.DrawLine(bar.Location(), bar.Location() + line_end_point); +} + +void NGMathMLPainter::PaintFractionBar(const PaintInfo& info, + PhysicalOffset paint_offset) { + const DisplayItemClient& display_item_client = + *box_fragment_.GetLayoutObject(); + if (DrawingRecorder::UseCachedDrawingIfPossible( + info.context, display_item_client, info.phase)) + return; + + DrawingRecorder recorder(info.context, display_item_client, info.phase); + + DCHECK(box_fragment_.Style().IsHorizontalWritingMode()); + const ComputedStyle& style = box_fragment_.Style(); + LayoutUnit line_thickness = FractionLineThickness(style); + if (!line_thickness) + return; + LayoutUnit axis_height = MathAxisHeight(style); + if (auto baseline = box_fragment_.Baseline()) { + auto borders = box_fragment_.Borders(); + auto padding = box_fragment_.Padding(); + PhysicalRect bar_rect = { + borders.left + padding.left, *baseline - axis_height, + box_fragment_.Size().width - borders.HorizontalSum() - + padding.HorizontalSum(), + line_thickness}; + bar_rect.Move(paint_offset); + PaintBar(info, PixelSnappedIntRect(bar_rect)); + } +} + +} // namespace blink diff --git a/chromium/third_party/blink/renderer/core/paint/ng/ng_mathml_painter.h b/chromium/third_party/blink/renderer/core/paint/ng/ng_mathml_painter.h new file mode 100644 index 00000000000..70233e8017f --- /dev/null +++ b/chromium/third_party/blink/renderer/core/paint/ng/ng_mathml_painter.h @@ -0,0 +1,33 @@ +// Copyright 2020 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. + +#ifndef THIRD_PARTY_BLINK_RENDERER_CORE_PAINT_NG_NG_MATHML_PAINTER_H_ +#define THIRD_PARTY_BLINK_RENDERER_CORE_PAINT_NG_NG_MATHML_PAINTER_H_ + +#include "third_party/blink/renderer/platform/wtf/allocator/allocator.h" + +namespace blink { + +struct PaintInfo; +struct PhysicalOffset; +class IntRect; +class NGPhysicalBoxFragment; + +class NGMathMLPainter { + STACK_ALLOCATED(); + + public: + explicit NGMathMLPainter(const NGPhysicalBoxFragment& box_fragment) + : box_fragment_(box_fragment) {} + void PaintFractionBar(const PaintInfo&, PhysicalOffset); + + private: + void PaintBar(const PaintInfo&, const IntRect&); + + const NGPhysicalBoxFragment& box_fragment_; +}; + +} // namespace blink + +#endif // THIRD_PARTY_BLINK_RENDERER_CORE_PAINT_NG_NG_MATHML_PAINTER_H_ 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 8d5848d2cd6..9cd79dd07b2 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 @@ -21,7 +21,6 @@ #include "third_party/blink/renderer/core/layout/ng/inline/ng_physical_text_fragment.h" #include "third_party/blink/renderer/core/layout/ng/layout_ng_block_flow.h" #include "third_party/blink/renderer/core/layout/ng/list/layout_ng_list_item.h" -#include "third_party/blink/renderer/core/layout/ng/list/layout_ng_list_marker.h" #include "third_party/blink/renderer/core/layout/ng/ng_block_break_token.h" #include "third_party/blink/renderer/core/layout/ng/ng_fragment.h" #include "third_party/blink/renderer/core/layout/ng/ng_ink_overflow.h" @@ -116,7 +115,7 @@ LogicalRect ExpandedSelectionRectForSoftLineBreakIfNeeded( if (layout_block_flow && layout_block_flow->ShouldTruncateOverflowingText()) return rect; // Copy from InlineTextBoxPainter::PaintSelection. - const LayoutUnit space_width(cursor.CurrentStyle().GetFont().SpaceWidth()); + const LayoutUnit space_width(cursor.Current().Style().GetFont().SpaceWidth()); return {rect.offset, {rect.size.inline_size + space_width, rect.size.block_size}}; } @@ -139,7 +138,9 @@ LogicalRect ExpandSelectionRectToLineHeight(const LogicalRect& rect, NGInlineCursor line(cursor); line.MoveToContainingLine(); const PhysicalRect line_physical_rect( - line.CurrentOffset() - cursor.CurrentOffset(), line.CurrentSize()); + line.Current().OffsetInContainerBlock() - + cursor.Current().OffsetInContainerBlock(), + line.Current().Size()); return ExpandSelectionRectToLineHeight( rect, ComputeLogicalRectFor(line_physical_rect, cursor)); } @@ -432,8 +433,8 @@ void NGPaintFragment::PopulateDescendants(CreateContext* parent_context) { scoped_refptr<NGPaintFragment>* last_child_ptr = &first_child_; auto* box_physical_fragment = DynamicTo<NGPhysicalBoxFragment>(fragment); - bool children_are_inline = - !box_physical_fragment || box_physical_fragment->ChildrenInline(); + bool is_inline_fc = !box_physical_fragment || + box_physical_fragment->IsInlineFormattingContext(); for (const NGLink& child_fragment : container.Children()) { child_fragment->CheckType(); @@ -444,11 +445,11 @@ void NGPaintFragment::PopulateDescendants(CreateContext* parent_context) { child_context.populate_children = child_fragment->IsContainer() && - !child_fragment->IsBlockFormattingContextRoot(); + !child_fragment->IsFormattingContextRoot(); scoped_refptr<NGPaintFragment> child = CreateOrReuse( child_fragment.get(), child_fragment.Offset(), &child_context); - if (children_are_inline) { + if (is_inline_fc) { DCHECK(!child_fragment->IsOutOfFlowPositioned()); if (child_fragment->IsText() || child_fragment->IsInlineBox() || child_fragment->IsAtomicInline()) { @@ -506,12 +507,15 @@ void NGPaintFragment::AssociateWithLayoutObject( return; } // This |layout_object| was fragmented across multiple blocks. + DCHECK_EQ(layout_object, first_fragment->GetLayoutObject()); NGPaintFragment* last_fragment = first_fragment->LastForSameLayoutObject(); last_fragment->next_for_same_layout_object_ = this; return; } - DCHECK(add_result.stored_value->value); - add_result.stored_value->value->next_for_same_layout_object_ = this; + NGPaintFragment* last_fragment = add_result.stored_value->value; + DCHECK(last_fragment) << layout_object; + DCHECK_EQ(layout_object, last_fragment->GetLayoutObject()); + last_fragment->next_for_same_layout_object_ = this; add_result.stored_value->value = this; } @@ -537,7 +541,7 @@ void NGPaintFragment::ClearAssociationWithLayoutObject() { fragment.IsColumnBox()) { child->ClearAssociationWithLayoutObject(); } else { - DCHECK(fragment.IsText() || fragment.IsBlockFormattingContextRoot()); + DCHECK(fragment.IsText() || fragment.IsFormattingContextRoot()); DCHECK(child->Children().IsEmpty()); } } @@ -640,7 +644,7 @@ PhysicalRect NGPaintFragment::RecalcContentsInkOverflow() const { // A BFC root establishes a separate NGPaintFragment tree. Re-compute the // child tree using its LayoutObject, because it may not be NG. - if (child_fragment.IsBlockFormattingContextRoot()) { + if (child_fragment.IsFormattingContextRoot()) { LayoutBox* layout_box = ToLayoutBox(child_fragment.GetMutableLayoutObject()); layout_box->RecalcVisualOverflow(); @@ -661,7 +665,7 @@ PhysicalRect NGPaintFragment::RecalcContentsInkOverflow() const { PhysicalRect NGPaintFragment::RecalcInkOverflow() { const NGPhysicalFragment& fragment = PhysicalFragment(); fragment.CheckCanUpdateInkOverflow(); - DCHECK(!fragment.IsBlockFormattingContextRoot()); + DCHECK(!fragment.IsFormattingContextRoot()); // NGPhysicalTextFragment caches ink overflow in layout. No need to recalc nor // to store in NGPaintFragment. @@ -755,7 +759,7 @@ base::Optional<PhysicalRect> NGPaintFragment::LocalVisualRectFor( if (fragment->PhysicalFragment().IsHiddenForPaint()) continue; PhysicalRect child_visual_rect = fragment->SelfInkOverflow(); - child_visual_rect.offset += fragment->InlineOffsetToContainerBox(); + child_visual_rect.offset += fragment->OffsetInContainerBlock(); visual_rect.Unite(child_visual_rect); } return visual_rect; @@ -781,7 +785,9 @@ NGPaintFragment* NGPaintFragment::FirstLineBox() const { } const NGPaintFragment* NGPaintFragment::Root() const { - DCHECK(PhysicalFragment().IsInline()); + // Because of this function can be called during |LayoutObject::Destroy()|, + // we use |physical_fragment_| to avoid calling |IsAlive()|. + DCHECK(physical_fragment_->IsInline()); const NGPaintFragment* root = this; for (const NGPaintFragment* fragment : NGPaintFragmentTraversal::InclusiveAncestorsOf(*this)) { @@ -915,17 +921,19 @@ PhysicalRect ComputeLocalSelectionRectForText( LogicalRect logical_rect = ComputeLogicalRectFor(selection_rect, cursor); // Let LocalRect for line break have a space width to paint line break // when it is only character in a line or only selected in a line. - if (selection_status.start != selection_status.end && cursor.IsLineBreak() && + if (selection_status.start != selection_status.end && + cursor.Current().IsLineBreak() && // This is for old compatible that old doesn't paint last br in a page. - !IsLastBRInPage(*cursor.CurrentLayoutObject())) { + !IsLastBRInPage(*cursor.Current().GetLayoutObject())) { DCHECK(!logical_rect.size.inline_size); logical_rect.size.inline_size = - LayoutUnit(cursor.CurrentStyle().GetFont().SpaceWidth()); + LayoutUnit(cursor.Current().Style().GetFont().SpaceWidth()); } const LogicalRect line_break_extended_rect = - cursor.IsLineBreak() ? logical_rect - : ExpandedSelectionRectForSoftLineBreakIfNeeded( - logical_rect, cursor, selection_status); + cursor.Current().IsLineBreak() + ? logical_rect + : ExpandedSelectionRectForSoftLineBreakIfNeeded(logical_rect, cursor, + selection_status); const LogicalRect line_height_expanded_rect = ExpandSelectionRectToLineHeight(line_break_extended_rect, cursor); const PhysicalRect physical_rect = @@ -937,8 +945,8 @@ PhysicalRect ComputeLocalSelectionRectForText( // "ng_selection_painter.cc". PhysicalRect ComputeLocalSelectionRectForReplaced( const NGInlineCursor& cursor) { - DCHECK(cursor.CurrentLayoutObject()->IsLayoutReplaced()); - const PhysicalRect selection_rect = PhysicalRect({}, cursor.CurrentSize()); + DCHECK(cursor.Current().GetLayoutObject()->IsLayoutReplaced()); + const PhysicalRect selection_rect = PhysicalRect({}, cursor.Current().Size()); LogicalRect logical_rect = ComputeLogicalRectFor(selection_rect, cursor); const LogicalRect line_height_expanded_rect = ExpandSelectionRectToLineHeight(logical_rect, cursor); @@ -1038,7 +1046,9 @@ PositionWithAffinity NGPaintFragment::PositionForPointInInlineFormattingContext( const PhysicalOffset& point) const { DCHECK(PhysicalFragment().IsBlockFlow()); DCHECK(PhysicalFragment().IsBox()); - DCHECK(To<NGPhysicalBoxFragment>(PhysicalFragment()).ChildrenInline()); + DCHECK(To<NGPhysicalBoxFragment>(PhysicalFragment()) + .GetLayoutObject() + ->ChildrenInline()); const LogicalOffset logical_point = point.ConvertToLogical( Style().GetWritingMode(), Style().Direction(), Size(), @@ -1117,7 +1127,6 @@ PositionWithAffinity NGPaintFragment::PositionForPoint( // We current fall back to legacy for block formatting contexts, so we // should reach here only for inline formatting contexts. // TODO(xiaochengh): Do not fall back. - DCHECK(To<NGPhysicalBoxFragment>(PhysicalFragment()).ChildrenInline()); return PositionForPointInInlineFormattingContext(point); } @@ -1131,13 +1140,16 @@ String NGPaintFragment::DebugName() const { DCHECK(physical_fragment_); const NGPhysicalFragment& physical_fragment = *physical_fragment_; if (physical_fragment.IsBox()) { + const LayoutObject* layout_object = physical_fragment.GetLayoutObject(); + if (!layout_object) + return "NGPhysicalBoxFragment"; + // For the root |NGPaintFragment|, return the name of the |LayoutObject| to + // ease the transition to |NGFragmentItem|. + if (!Parent()) + return layout_object->DebugName(); name.Append("NGPhysicalBoxFragment"); - if (const LayoutObject* layout_object = - physical_fragment.GetLayoutObject()) { - DCHECK(physical_fragment.IsBox()); - name.Append(' '); - name.Append(layout_object->DebugName()); - } + name.Append(' '); + name.Append(layout_object->DebugName()); } else if (physical_fragment.IsText()) { name.Append("NGPhysicalTextFragment '"); name.Append(To<NGPhysicalTextFragment>(physical_fragment).Text()); 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 6ec7dadfd82..fd197b7cd86 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 @@ -155,10 +155,13 @@ class CORE_EXPORT NGPaintFragment : public RefCounted<NGPaintFragment>, bool IsDirty() const { return is_dirty_inline_; } // Returns offset to its container box for inline and line box fragments. - const PhysicalOffset& InlineOffsetToContainerBox() const { + const PhysicalOffset& OffsetInContainerBlock() const { DCHECK(PhysicalFragment().IsInline() || PhysicalFragment().IsLineBox()); return inline_offset_to_container_box_; } + const PhysicalRect RectInContainerBlock() const { + return PhysicalRect(OffsetInContainerBlock(), Size()); + } // InkOverflow of itself, not including contents, in the local coordinate. PhysicalRect SelfInkOverflow() const; 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 c301ed5cc5c..d778705c23d 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 @@ -45,6 +45,8 @@ class NGPaintFragmentTest : public RenderingTest, }; TEST_F(NGPaintFragmentTest, InlineFragmentsFor) { + if (RuntimeEnabledFeatures::LayoutNGFragmentItemEnabled()) + return; LoadAhem(); SetBodyInnerHTML(R"HTML( <!DOCTYPE html> @@ -70,7 +72,7 @@ TEST_F(NGPaintFragmentTest, InlineFragmentsFor) { results.AppendRange(it.begin(), it.end()); EXPECT_EQ(1u, results.size()); EXPECT_EQ(text1, results[0]->GetLayoutObject()); - EXPECT_EQ(PhysicalOffset(), results[0]->InlineOffsetToContainerBox()); + EXPECT_EQ(PhysicalOffset(), results[0]->OffsetInContainerBlock()); results.clear(); it = NGPaintFragment::InlineFragmentsFor(box); @@ -80,15 +82,15 @@ TEST_F(NGPaintFragmentTest, InlineFragmentsFor) { EXPECT_EQ(box, results[1]->GetLayoutObject()); EXPECT_EQ(box, results[2]->GetLayoutObject()); - EXPECT_EQ(PhysicalOffset(60, 0), results[0]->InlineOffsetToContainerBox()); + EXPECT_EQ(PhysicalOffset(60, 0), results[0]->OffsetInContainerBlock()); EXPECT_EQ("789", To<NGPhysicalTextFragment>( results[0]->FirstChild()->PhysicalFragment()) .Text()); - EXPECT_EQ(PhysicalOffset(0, 10), results[1]->InlineOffsetToContainerBox()); + EXPECT_EQ(PhysicalOffset(0, 10), results[1]->OffsetInContainerBlock()); EXPECT_EQ("123456789", To<NGPhysicalTextFragment>( results[1]->FirstChild()->PhysicalFragment()) .Text()); - EXPECT_EQ(PhysicalOffset(0, 20), results[2]->InlineOffsetToContainerBox()); + EXPECT_EQ(PhysicalOffset(0, 20), results[2]->OffsetInContainerBlock()); EXPECT_EQ("123", To<NGPhysicalTextFragment>( results[2]->FirstChild()->PhysicalFragment()) .Text()); @@ -102,6 +104,8 @@ TEST_F(NGPaintFragmentTest, InlineFragmentsFor) { } while (false) TEST_F(NGPaintFragmentTest, InlineBox) { + if (RuntimeEnabledFeatures::LayoutNGFragmentItemEnabled()) + return; LoadAhem(); SetBodyInnerHTML(R"HTML( <!DOCTYPE html> @@ -145,6 +149,8 @@ TEST_F(NGPaintFragmentTest, InlineBox) { } TEST_F(NGPaintFragmentTest, InlineBoxVerticalRL) { + if (RuntimeEnabledFeatures::LayoutNGFragmentItemEnabled()) + return; LoadAhem(); SetBodyInnerHTML(R"HTML( <!DOCTYPE html> @@ -189,6 +195,8 @@ TEST_F(NGPaintFragmentTest, InlineBoxVerticalRL) { } TEST_F(NGPaintFragmentTest, InlineBoxWithDecorations) { + if (RuntimeEnabledFeatures::LayoutNGFragmentItemEnabled()) + return; LoadAhem(); SetBodyInnerHTML(R"HTML( <!DOCTYPE html> @@ -244,6 +252,8 @@ TEST_F(NGPaintFragmentTest, InlineBoxWithDecorations) { } TEST_F(NGPaintFragmentTest, InlineBoxWithDecorationsVerticalRL) { + if (RuntimeEnabledFeatures::LayoutNGFragmentItemEnabled()) + return; LoadAhem(); SetBodyInnerHTML(R"HTML( <!DOCTYPE html> @@ -300,6 +310,8 @@ TEST_F(NGPaintFragmentTest, InlineBoxWithDecorationsVerticalRL) { } TEST_F(NGPaintFragmentTest, InlineBlock) { + if (RuntimeEnabledFeatures::LayoutNGFragmentItemEnabled()) + return; LoadAhem(); SetBodyInnerHTML(R"HTML( <!DOCTYPE html> @@ -404,6 +416,8 @@ TEST_F(NGPaintFragmentTest, InlineBlock) { } TEST_F(NGPaintFragmentTest, InlineBlockVerticalRL) { + if (RuntimeEnabledFeatures::LayoutNGFragmentItemEnabled()) + return; LoadAhem(); SetBodyInnerHTML(R"HTML( <!DOCTYPE html> @@ -511,6 +525,8 @@ TEST_F(NGPaintFragmentTest, InlineBlockVerticalRL) { } TEST_F(NGPaintFragmentTest, RelativeBlock) { + if (RuntimeEnabledFeatures::LayoutNGFragmentItemEnabled()) + return; LoadAhem(); SetBodyInnerHTML(R"HTML( <!DOCTYPE html> @@ -550,6 +566,8 @@ TEST_F(NGPaintFragmentTest, RelativeBlock) { } TEST_F(NGPaintFragmentTest, RelativeInline) { + if (RuntimeEnabledFeatures::LayoutNGFragmentItemEnabled()) + return; LoadAhem(); SetBodyInnerHTML(R"HTML( <!DOCTYPE html> @@ -604,6 +622,8 @@ TEST_F(NGPaintFragmentTest, RelativeInline) { } TEST_F(NGPaintFragmentTest, RelativeBlockAndInline) { + if (RuntimeEnabledFeatures::LayoutNGFragmentItemEnabled()) + return; LoadAhem(); SetBodyInnerHTML(R"HTML( <!DOCTYPE html> @@ -659,6 +679,8 @@ TEST_F(NGPaintFragmentTest, RelativeBlockAndInline) { // Test that OOF should not create a NGPaintFragment. TEST_F(NGPaintFragmentTest, OutOfFlow) { + if (RuntimeEnabledFeatures::LayoutNGFragmentItemEnabled()) + return; SetBodyInnerHTML(R"HTML( <!DOCTYPE html> <style> @@ -683,6 +705,8 @@ TEST_F(NGPaintFragmentTest, OutOfFlow) { } TEST_F(NGPaintFragmentTest, MarkLineBoxesDirtyByRemoveBr) { + if (RuntimeEnabledFeatures::LayoutNGFragmentItemEnabled()) + return; if (!RuntimeEnabledFeatures::LayoutNGLineCacheEnabled()) return; SetBodyInnerHTML( @@ -710,6 +734,8 @@ INSTANTIATE_TEST_SUITE_P(NGPaintFragmentTest, testing::ValuesIn(inline_child_data)); TEST_P(InlineChildTest, RemoveInlineChild) { + if (RuntimeEnabledFeatures::LayoutNGFragmentItemEnabled()) + return; SetBodyInnerHTML(String(R"HTML( <!DOCTYPE html> <style> @@ -735,6 +761,8 @@ TEST_P(InlineChildTest, RemoveInlineChild) { } TEST_F(NGPaintFragmentTest, MarkLineBoxesDirtyByRemoveChild) { + if (RuntimeEnabledFeatures::LayoutNGFragmentItemEnabled()) + return; if (!RuntimeEnabledFeatures::LayoutNGLineCacheEnabled()) return; SetBodyInnerHTML( @@ -750,6 +778,8 @@ TEST_F(NGPaintFragmentTest, MarkLineBoxesDirtyByRemoveChild) { } TEST_F(NGPaintFragmentTest, MarkLineBoxesDirtyByRemoveSpanWithBr) { + if (RuntimeEnabledFeatures::LayoutNGFragmentItemEnabled()) + return; if (!RuntimeEnabledFeatures::LayoutNGLineCacheEnabled()) return; SetBodyInnerHTML( @@ -768,6 +798,8 @@ TEST_F(NGPaintFragmentTest, MarkLineBoxesDirtyByRemoveSpanWithBr) { // to update |IsDirty|, but NGPaintFragment maybe re-used during the layout. In // such case, the result is not deterministic. TEST_F(NGPaintFragmentTest, DISABLED_MarkLineBoxesDirtyByInsertAtStart) { + if (RuntimeEnabledFeatures::LayoutNGFragmentItemEnabled()) + return; if (!RuntimeEnabledFeatures::LayoutNGLineCacheEnabled()) return; SetBodyInnerHTML( @@ -785,7 +817,7 @@ TEST_F(NGPaintFragmentTest, DISABLED_MarkLineBoxesDirtyByInsertAtStart) { Element& target = *GetDocument().getElementById("target"); target.parentNode()->insertBefore(Text::Create(GetDocument(), "XYZ"), &target); - GetDocument().UpdateStyleAndLayout(); + GetDocument().UpdateStyleAndLayout(DocumentUpdateReason::kTest); EXPECT_TRUE(line1->IsDirty()); EXPECT_FALSE(line2->IsDirty()); @@ -796,6 +828,8 @@ TEST_F(NGPaintFragmentTest, DISABLED_MarkLineBoxesDirtyByInsertAtStart) { // to update |IsDirty|, but NGPaintFragment maybe re-used during the layout. In // such case, the result is not deterministic. TEST_F(NGPaintFragmentTest, DISABLED_MarkLineBoxesDirtyByInsertAtLast) { + if (RuntimeEnabledFeatures::LayoutNGFragmentItemEnabled()) + return; if (!RuntimeEnabledFeatures::LayoutNGLineCacheEnabled()) return; SetBodyInnerHTML( @@ -812,7 +846,7 @@ TEST_F(NGPaintFragmentTest, DISABLED_MarkLineBoxesDirtyByInsertAtLast) { ASSERT_TRUE(line3->PhysicalFragment().IsLineBox()) << line3; Element& target = *GetDocument().getElementById("target"); target.parentNode()->appendChild(Text::Create(GetDocument(), "XYZ")); - GetDocument().UpdateStyleAndLayout(); + GetDocument().UpdateStyleAndLayout(DocumentUpdateReason::kTest); EXPECT_FALSE(line1->IsDirty()); EXPECT_FALSE(line2->IsDirty()); @@ -823,6 +857,8 @@ TEST_F(NGPaintFragmentTest, DISABLED_MarkLineBoxesDirtyByInsertAtLast) { // to update |IsDirty|, but NGPaintFragment maybe re-used during the layout. In // such case, the result is not deterministic. TEST_F(NGPaintFragmentTest, DISABLED_MarkLineBoxesDirtyByInsertAtMiddle) { + if (RuntimeEnabledFeatures::LayoutNGFragmentItemEnabled()) + return; if (!RuntimeEnabledFeatures::LayoutNGLineCacheEnabled()) return; SetBodyInnerHTML( @@ -840,7 +876,7 @@ TEST_F(NGPaintFragmentTest, DISABLED_MarkLineBoxesDirtyByInsertAtMiddle) { Element& target = *GetDocument().getElementById("target"); target.parentNode()->insertBefore(Text::Create(GetDocument(), "XYZ"), target.nextSibling()); - GetDocument().UpdateStyleAndLayout(); + GetDocument().UpdateStyleAndLayout(DocumentUpdateReason::kTest); EXPECT_TRUE(line1->IsDirty()); EXPECT_FALSE(line2->IsDirty()); @@ -848,6 +884,8 @@ TEST_F(NGPaintFragmentTest, DISABLED_MarkLineBoxesDirtyByInsertAtMiddle) { } TEST_F(NGPaintFragmentTest, MarkLineBoxesDirtyByTextSetData) { + if (RuntimeEnabledFeatures::LayoutNGFragmentItemEnabled()) + return; if (!RuntimeEnabledFeatures::LayoutNGLineCacheEnabled()) return; SetBodyInnerHTML( @@ -863,6 +901,8 @@ TEST_F(NGPaintFragmentTest, MarkLineBoxesDirtyByTextSetData) { } TEST_F(NGPaintFragmentTest, MarkLineBoxesDirtyWrappedLine) { + if (RuntimeEnabledFeatures::LayoutNGFragmentItemEnabled()) + return; if (!RuntimeEnabledFeatures::LayoutNGLineCacheEnabled()) return; SetBodyInnerHTML(R"HTML( @@ -887,6 +927,8 @@ TEST_F(NGPaintFragmentTest, MarkLineBoxesDirtyWrappedLine) { } TEST_F(NGPaintFragmentTest, MarkLineBoxesDirtyInsideInlineBlock) { + if (RuntimeEnabledFeatures::LayoutNGFragmentItemEnabled()) + return; if (!RuntimeEnabledFeatures::LayoutNGLineCacheEnabled()) return; SetBodyInnerHTML(R"HTML( 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 9714f1cc1cc..e5c4b783bc7 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 @@ -272,7 +272,7 @@ bool NGPaintFragmentTraversal::InlineDescendantsRange::Iterator:: bool NGPaintFragmentTraversal::InlineDescendantsRange::Iterator::ShouldTraverse( const NGPaintFragment& fragment) { return fragment.PhysicalFragment().IsContainer() && - !fragment.PhysicalFragment().IsBlockFormattingContextRoot(); + !fragment.PhysicalFragment().IsFormattingContextRoot(); } } // namespace blink 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 e4e8110f82c..78d645533bf 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 @@ -63,6 +63,8 @@ class NGPaintFragmentTraversalTest : public RenderingTest, }; TEST_F(NGPaintFragmentTraversalTest, MoveToNext) { + if (RuntimeEnabledFeatures::LayoutNGFragmentItemEnabled()) + return; SetUpHtml("t", R"HTML( <div id=t> line0 @@ -83,6 +85,8 @@ TEST_F(NGPaintFragmentTraversalTest, MoveToNext) { } TEST_F(NGPaintFragmentTraversalTest, MoveToNextWithRoot) { + if (RuntimeEnabledFeatures::LayoutNGFragmentItemEnabled()) + return; SetUpHtml("t", R"HTML( <div id=t> line0 @@ -101,6 +105,8 @@ TEST_F(NGPaintFragmentTraversalTest, MoveToNextWithRoot) { } TEST_F(NGPaintFragmentTraversalTest, MoveToPrevious) { + if (RuntimeEnabledFeatures::LayoutNGFragmentItemEnabled()) + return; SetUpHtml("t", R"HTML( <div id=t> line0 @@ -122,6 +128,8 @@ TEST_F(NGPaintFragmentTraversalTest, MoveToPrevious) { } TEST_F(NGPaintFragmentTraversalTest, MoveToPreviousWithRoot) { + if (RuntimeEnabledFeatures::LayoutNGFragmentItemEnabled()) + return; SetUpHtml("t", R"HTML( <div id=t> line0 @@ -141,6 +149,8 @@ TEST_F(NGPaintFragmentTraversalTest, MoveToPreviousWithRoot) { } TEST_F(NGPaintFragmentTraversalTest, MoveTo) { + if (RuntimeEnabledFeatures::LayoutNGFragmentItemEnabled()) + return; SetUpHtml("t", R"HTML( <div id=t> line0 @@ -162,6 +172,8 @@ TEST_F(NGPaintFragmentTraversalTest, MoveTo) { } TEST_F(NGPaintFragmentTraversalTest, MoveToWithRoot) { + if (RuntimeEnabledFeatures::LayoutNGFragmentItemEnabled()) + return; SetUpHtml("t", R"HTML( <div id=t> line0 @@ -181,6 +193,8 @@ TEST_F(NGPaintFragmentTraversalTest, MoveToWithRoot) { } TEST_F(NGPaintFragmentTraversalTest, InlineDescendantsOf) { + if (RuntimeEnabledFeatures::LayoutNGFragmentItemEnabled()) + return; SetUpHtml("t", "<ul>" "<li id=t style='position: absolute'>" diff --git a/chromium/third_party/blink/renderer/core/paint/ng/ng_text_fragment_painter.cc b/chromium/third_party/blink/renderer/core/paint/ng/ng_text_fragment_painter.cc index 4ecd342ed01..23d62f21cb8 100644 --- a/chromium/third_party/blink/renderer/core/paint/ng/ng_text_fragment_painter.cc +++ b/chromium/third_party/blink/renderer/core/paint/ng/ng_text_fragment_painter.cc @@ -64,6 +64,49 @@ inline const NGPaintFragment& AsDisplayItemClient( return cursor.PaintFragment(); } +inline PhysicalRect ComputeBoxRect(const NGInlineCursor& cursor, + const PhysicalOffset& paint_offset, + const PhysicalOffset& parent_offset) { + PhysicalRect box_rect = cursor.CurrentItem()->RectInContainerBlock(); + box_rect.offset.left += paint_offset.left; + // We round the y-axis to ensure consistent line heights. + box_rect.offset.top = + LayoutUnit((paint_offset.top + parent_offset.top).Round()) + + (box_rect.offset.top - parent_offset.top); + return box_rect; +} + +inline PhysicalRect ComputeBoxRect(const NGTextPainterCursor& cursor, + const PhysicalOffset& paint_offset, + const PhysicalOffset& parent_offset) { + PhysicalRect box_rect = cursor.PaintFragment().Rect(); + // We round the y-axis to ensure consistent line heights. + PhysicalOffset adjusted_paint_offset(paint_offset.left, + LayoutUnit(paint_offset.top.Round())); + box_rect.offset += adjusted_paint_offset; + return box_rect; +} + +inline const NGInlineCursor& InlineCursorForBlockFlow( + const NGInlineCursor& cursor, + base::Optional<NGInlineCursor>* storage) { + if (*storage) + return **storage; + *storage = cursor; + (*storage)->ExpandRootToContainingBlock(); + return **storage; +} + +inline const NGInlineCursor& InlineCursorForBlockFlow( + const NGTextPainterCursor& cursor, + base::Optional<NGInlineCursor>* storage) { + if (*storage) + return **storage; + storage->emplace(cursor.RootPaintFragment()); + (*storage)->MoveTo(cursor.PaintFragment()); + return **storage; +} + // TODO(yosin): Remove |GetTextFragmentPaintInfo| once the transition to // |NGFragmentItem| is done. http://crbug.com/982194 inline NGTextFragmentPaintInfo GetTextFragmentPaintInfo( @@ -98,28 +141,14 @@ inline std::pair<LayoutUnit, LayoutUnit> GetLineLeftAndRightForOffsets( // |NGFragmentItem| is done. http://crbug.com/982194 inline LayoutSelectionStatus ComputeLayoutSelectionStatus( const NGInlineCursor& cursor) { - return cursor.CurrentItem() - ->GetLayoutObject() + return cursor.Current() + .GetLayoutObject() ->GetDocument() .GetFrame() ->Selection() .ComputeLayoutSelectionStatus(cursor); } -inline LayoutSelectionStatus ComputeLayoutSelectionStatus( - const NGTextPainterCursor& cursor) { - // Note:: Because of this function is hot, we should not use |NGInlineCursor| - // on paint fragment which requires traversing ancestors to root. - NGInlineCursor inline_cursor(cursor.RootPaintFragment()); - inline_cursor.MoveTo(cursor.PaintFragment()); - return cursor.CurrentItem() - ->GetLayoutObject() - ->GetDocument() - .GetFrame() - ->Selection() - .ComputeLayoutSelectionStatus(inline_cursor); -} - // TODO(yosin): Remove |ComputeLocalRect| once the transition to // |NGFragmentItem| is done. http://crbug.com/982194 inline PhysicalRect ComputeLocalRect(const NGFragmentItem& text_item, @@ -181,19 +210,24 @@ unsigned ClampOffset(unsigned offset, const TextItem& text_fragment) { } void PaintRect(GraphicsContext& context, - const PhysicalOffset& location, const PhysicalRect& rect, const Color color) { if (!color.Alpha()) return; if (rect.size.IsEmpty()) return; - const IntRect pixel_snapped_rect = - PixelSnappedIntRect(PhysicalRect(rect.offset + location, rect.size)); + const IntRect pixel_snapped_rect = PixelSnappedIntRect(rect); if (!pixel_snapped_rect.IsEmpty()) context.FillRect(pixel_snapped_rect, color); } +void PaintRect(GraphicsContext& context, + const PhysicalOffset& location, + const PhysicalRect& rect, + const Color color) { + PaintRect(context, PhysicalRect(rect.offset + location, rect.size), color); +} + template <typename TextItem> PhysicalRect MarkerRectForForeground(const TextItem& text_fragment, StringView text, @@ -310,6 +344,92 @@ void PaintDocumentMarkers(GraphicsContext& context, } } +class SelectionPaintState { + STACK_ALLOCATED(); + + public: + explicit SelectionPaintState(const NGInlineCursor& containing_block) + : selection_status_(ComputeLayoutSelectionStatus(containing_block)), + containing_block_(containing_block) {} + + const LayoutSelectionStatus& Status() const { return selection_status_; } + + bool ShouldPaintSelectedTextOnly() const { return paint_selected_text_only_; } + + bool ShouldPaintSelectedTextSeparately() const { + return paint_selected_text_separately_; + } + + bool IsSelectionRectComputed() const { return selection_rect_.has_value(); } + + void ComputeSelectionStyle(const Document& document, + const ComputedStyle& style, + Node* node, + const PaintInfo& paint_info, + const TextPaintStyle& text_style) { + selection_style_ = TextPainterBase::SelectionPaintingStyle( + document, style, node, /*have_selection*/ true, paint_info, text_style); + paint_selected_text_only_ = + (paint_info.phase == PaintPhase::kSelectionDragImage); + paint_selected_text_separately_ = + !paint_selected_text_only_ && text_style != selection_style_; + } + + void ComputeSelectionRect(const PhysicalOffset& box_offset) { + DCHECK(!selection_rect_); + selection_rect_ = + ComputeLocalSelectionRectForText(containing_block_, selection_status_); + selection_rect_->offset += box_offset; + } + + // Logic is copied from InlineTextBoxPainter::PaintSelection. + // |selection_start| and |selection_end| should be between + // [text_fragment.StartOffset(), text_fragment.EndOffset()]. + void PaintSelectionBackground(GraphicsContext& context, + Node* node, + const Document& document, + const ComputedStyle& style) { + const Color color = SelectionBackgroundColor(document, style, node, + selection_style_.fill_color); + PaintRect(context, *selection_rect_, color); + } + + // Paint the selected text only. + void PaintSelectedText(NGTextPainter& text_painter, + unsigned length, + const TextPaintStyle& text_style, + DOMNodeId node_id) { + text_painter.PaintSelectedText(selection_status_.start, + selection_status_.end, length, text_style, + selection_style_, *selection_rect_, node_id); + } + + // Paint the text except selected parts. Does nothing if all is selected. + void PaintBeforeAndAfterSelectedText(NGTextPainter& text_painter, + unsigned start_offset, + unsigned end_offset, + unsigned length, + const TextPaintStyle& text_style, + DOMNodeId node_id) { + if (start_offset < selection_status_.start) { + text_painter.Paint(start_offset, selection_status_.start, length, + text_style, node_id); + } + if (selection_status_.end < end_offset) { + text_painter.Paint(selection_status_.end, end_offset, length, text_style, + node_id); + } + } + + private: + LayoutSelectionStatus selection_status_; + TextPaintStyle selection_style_; + base::Optional<PhysicalRect> selection_rect_; + const NGInlineCursor& containing_block_; + bool paint_selected_text_only_; + bool paint_selected_text_separately_; +}; + } // namespace StringView NGTextPainterCursor::CurrentText() const { @@ -323,37 +443,6 @@ const NGPaintFragment& NGTextPainterCursor::RootPaintFragment() const { } template <typename Cursor> -NGTextFragmentPainter<Cursor>::NGTextFragmentPainter(const Cursor& cursor) - : cursor_(cursor) {} - -static PhysicalRect ComputeLocalSelectionRectForText( - const NGTextPainterCursor& cursor, - const LayoutSelectionStatus& selection_status) { - NGInlineCursor inline_cursor(cursor.RootPaintFragment()); - inline_cursor.MoveTo(cursor.PaintFragment()); - return ComputeLocalSelectionRectForText(inline_cursor, selection_status); -} - -// Logic is copied from InlineTextBoxPainter::PaintSelection. -// |selection_start| and |selection_end| should be between -// [text_fragment.StartOffset(), text_fragment.EndOffset()]. -template <typename Cursor> -static void PaintSelection(GraphicsContext& context, - const Cursor& cursor, - Node* node, - const Document& document, - const ComputedStyle& style, - Color text_color, - const PhysicalRect& box_rect, - const LayoutSelectionStatus& selection_status) { - const Color color = - SelectionBackgroundColor(document, style, node, text_color); - const PhysicalRect selection_rect = - ComputeLocalSelectionRectForText(cursor, selection_status); - PaintRect(context, box_rect.offset, selection_rect, color); -} - -template <typename Cursor> void NGTextFragmentPainter<Cursor>::PaintSymbol( const LayoutObject* layout_object, const ComputedStyle& style, @@ -390,23 +479,23 @@ void NGTextFragmentPainter<Cursor>::Paint(const PaintInfo& paint_info, GetTextFragmentPaintInfo(cursor_); const LayoutObject* layout_object = text_item.GetLayoutObject(); const ComputedStyle& style = text_item.Style(); - PhysicalRect box_rect = AsDisplayItemClient(cursor_).Rect(); const Document& document = layout_object->GetDocument(); const bool is_printing = paint_info.IsPrinting(); // Determine whether or not we're selected. - bool have_selection = !is_printing && - paint_info.phase != PaintPhase::kTextClip && - layout_object->IsSelected(); - base::Optional<LayoutSelectionStatus> selection_status; - if (have_selection) { - selection_status = ComputeLayoutSelectionStatus(cursor_); - DCHECK_LE(selection_status->start, selection_status->end); - have_selection = selection_status->start < selection_status->end; + base::Optional<SelectionPaintState> selection; + if (UNLIKELY(!is_printing && paint_info.phase != PaintPhase::kTextClip && + layout_object->IsSelected())) { + const NGInlineCursor& root_inline_cursor = + InlineCursorForBlockFlow(cursor_, &inline_cursor_for_block_flow_); + selection.emplace(root_inline_cursor); + if (!selection->Status().HasValidRange()) + selection.reset(); } - if (!have_selection) { - // When only painting the selection, don't bother to paint if there is none. - if (paint_info.phase == PaintPhase::kSelection) + if (!selection) { + // When only painting the selection drag image, don't bother to paint if + // there is none. + if (paint_info.phase == PaintPhase::kSelectionDragImage) return; // Flow controls (line break, tab, <wbr>) need only selection painting. @@ -426,6 +515,8 @@ void NGTextFragmentPainter<Cursor>::Paint(const PaintInfo& paint_info, paint_info.phase); } + PhysicalRect box_rect = ComputeBoxRect(cursor_, paint_offset, parent_offset_); + if (UNLIKELY(text_item.IsSymbolMarker())) { // The NGInlineItem of marker might be Split(). To avoid calling PaintSymbol // multiple times, only call it the first time. For an outside marker, this @@ -438,15 +529,10 @@ void NGTextFragmentPainter<Cursor>::Paint(const PaintInfo& paint_info, return; } PaintSymbol(layout_object, style, box_rect.size, paint_info, - paint_offset + box_rect.offset); + box_rect.offset); return; } - // We round the y-axis to ensure consistent line heights. - PhysicalOffset adjusted_paint_offset(paint_offset.left, - LayoutUnit(paint_offset.top.Round())); - box_rect.offset += adjusted_paint_offset; - GraphicsContext& context = paint_info.context; // Determine text colors. @@ -454,11 +540,10 @@ void NGTextFragmentPainter<Cursor>::Paint(const PaintInfo& paint_info, Node* node = layout_object->GetNode(); TextPaintStyle text_style = TextPainterBase::TextPaintingStyle(document, style, paint_info); - TextPaintStyle selection_style = TextPainterBase::SelectionPaintingStyle( - document, style, node, have_selection, paint_info, text_style); - bool paint_selected_text_only = (paint_info.phase == PaintPhase::kSelection); - bool paint_selected_text_separately = - !paint_selected_text_only && text_style != selection_style; + if (UNLIKELY(selection)) { + selection->ComputeSelectionStyle(document, style, node, paint_info, + text_style); + } // Set our font. const Font& font = style.GetFont(); @@ -474,17 +559,17 @@ void NGTextFragmentPainter<Cursor>::Paint(const PaintInfo& paint_info, // before GraphicsContext flip. // TODO(yoichio): Make NGPhysicalTextFragment::LocalRect and // NGPaintFragment::ComputeLocalSelectionRectForText logical so that we can - // paint selection in same fliped dimention as NGTextPainter. + // paint selection in same flipped dimension as NGTextPainter. const DocumentMarkerVector& markers_to_paint = ComputeMarkersToPaint(node, text_item.IsEllipsis()); - if (paint_info.phase != PaintPhase::kSelection && + if (paint_info.phase != PaintPhase::kSelectionDragImage && paint_info.phase != PaintPhase::kTextClip && !is_printing) { PaintDocumentMarkers(context, text_item, cursor_.CurrentText(), markers_to_paint, box_rect.offset, style, DocumentMarkerPaintPhase::kBackground, nullptr); - if (have_selection) { - PaintSelection(context, cursor_, node, document, style, - selection_style.fill_color, box_rect, *selection_status); + if (UNLIKELY(selection)) { + selection->ComputeSelectionRect(box_rect.offset); + selection->PaintSelectionBackground(context, node, document, style); } } @@ -520,7 +605,7 @@ void NGTextFragmentPainter<Cursor>::Paint(const PaintInfo& paint_info, } const unsigned length = fragment_paint_info.to - fragment_paint_info.from; - if (!paint_selected_text_only) { + if (!selection || !selection->ShouldPaintSelectedTextOnly()) { // Paint text decorations except line-through. DecorationInfo decoration_info; bool has_line_through_decoration = false; @@ -549,16 +634,9 @@ void NGTextFragmentPainter<Cursor>::Paint(const PaintInfo& paint_info, unsigned start_offset = fragment_paint_info.from; unsigned end_offset = fragment_paint_info.to; - if (have_selection && paint_selected_text_separately) { - // Paint only the text that is not selected. - if (start_offset < selection_status->start) { - text_painter.Paint(start_offset, selection_status->start, length, - text_style, node_id); - } - if (selection_status->end < end_offset) { - text_painter.Paint(selection_status->end, end_offset, length, - text_style, node_id); - } + if (UNLIKELY(selection && selection->ShouldPaintSelectedTextSeparately())) { + selection->PaintBeforeAndAfterSelectedText( + text_painter, start_offset, end_offset, length, text_style, node_id); } else { text_painter.Paint(start_offset, end_offset, length, text_style, node_id); } @@ -571,11 +649,12 @@ void NGTextFragmentPainter<Cursor>::Paint(const PaintInfo& paint_info, } } - if (have_selection && - (paint_selected_text_only || paint_selected_text_separately)) { + if (UNLIKELY(selection && (selection->ShouldPaintSelectedTextOnly() || + selection->ShouldPaintSelectedTextSeparately()))) { // Paint only the text that is selected. - text_painter.Paint(selection_status->start, selection_status->end, length, - selection_style, node_id); + if (!selection->IsSelectionRectComputed()) + selection->ComputeSelectionRect(box_rect.offset); + selection->PaintSelectedText(text_painter, length, text_style, node_id); } if (paint_info.phase != PaintPhase::kForeground) diff --git a/chromium/third_party/blink/renderer/core/paint/ng/ng_text_fragment_painter.h b/chromium/third_party/blink/renderer/core/paint/ng/ng_text_fragment_painter.h index d1c4ab8c517..beddc4dc958 100644 --- a/chromium/third_party/blink/renderer/core/paint/ng/ng_text_fragment_painter.h +++ b/chromium/third_party/blink/renderer/core/paint/ng/ng_text_fragment_painter.h @@ -5,6 +5,7 @@ #ifndef THIRD_PARTY_BLINK_RENDERER_CORE_PAINT_NG_NG_TEXT_FRAGMENT_PAINTER_H_ #define THIRD_PARTY_BLINK_RENDERER_CORE_PAINT_NG_NG_TEXT_FRAGMENT_PAINTER_H_ +#include "third_party/blink/renderer/core/layout/ng/inline/ng_inline_cursor.h" #include "third_party/blink/renderer/core/layout/ng/inline/ng_physical_text_fragment.h" #include "third_party/blink/renderer/core/paint/ng/ng_paint_fragment.h" #include "third_party/blink/renderer/core/style/computed_style_constants.h" @@ -57,7 +58,10 @@ class NGTextFragmentPainter { STACK_ALLOCATED(); public: - explicit NGTextFragmentPainter(const Cursor&); + explicit NGTextFragmentPainter(const Cursor& cursor) : cursor_(cursor) {} + NGTextFragmentPainter(const Cursor& cursor, + const PhysicalOffset& parent_offset) + : cursor_(cursor), parent_offset_(parent_offset) {} void Paint(const PaintInfo&, const PhysicalOffset& paint_offset); @@ -80,6 +84,8 @@ class NGTextFragmentPainter { const PhysicalOffset& paint_offset); const Cursor& cursor_; + PhysicalOffset parent_offset_; + base::Optional<NGInlineCursor> inline_cursor_for_block_flow_; }; extern template class NGTextFragmentPainter<NGTextPainterCursor>; 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 0dfa168b059..e1d5ac5fa23 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 @@ -6,6 +6,7 @@ #include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" +#include "third_party/blink/renderer/core/layout/ng/inline/ng_inline_cursor.h" #include "third_party/blink/renderer/core/layout/ng/layout_ng_block_flow.h" #include "third_party/blink/renderer/core/layout/ng/ng_block_node.h" #include "third_party/blink/renderer/core/paint/ng/ng_paint_fragment.h" @@ -39,14 +40,14 @@ TEST_P(NGTextFragmentPainterTest, TestTextStyle) { const LayoutNGBlockFlow& block_flow = ToLayoutNGBlockFlow(container); InvalidateAll(RootPaintController()); - GetDocument().View()->UpdateAllLifecyclePhasesExceptPaint(); + GetDocument().View()->UpdateAllLifecyclePhasesExceptPaint( + DocumentUpdateReason::kTest); Paint(IntRect(0, 0, 640, 480)); - const NGPaintFragment& root_fragment = *block_flow.PaintFragment(); - EXPECT_EQ(1u, root_fragment.Children().size()); - const NGPaintFragment& line_box_fragment = *root_fragment.FirstChild(); - EXPECT_EQ(1u, line_box_fragment.Children().size()); - const NGPaintFragment& text_fragment = *line_box_fragment.FirstChild(); + NGInlineCursor cursor; + cursor.MoveTo(*block_flow.FirstChild()); + const DisplayItemClient& text_fragment = + *cursor.Current().GetDisplayItemClient(); EXPECT_THAT(RootPaintController().GetDisplayItemList(), ElementsAre(IsSameId(&ViewScrollingBackgroundClient(), @@ -65,4 +66,21 @@ TEST_P(NGTextFragmentPainterTest, LineBreak) { EXPECT_EQ(6u, RootPaintController().GetDisplayItemList().size()); } +TEST_P(NGTextFragmentPainterTest, DegenerateUnderlineIntercepts) { + SetBodyInnerHTML(R"HTML( + <!DOCTYPE html> + <style> + span { + font-size: 20px; + text-decoration: underline; + } + </style> + <span style="letter-spacing: -1e9999em;">a|b|c d{e{f{</span> + <span style="letter-spacing: 1e9999em;">a|b|c d{e{f{</span> + )HTML"); + UpdateAllLifecyclePhasesForTest(); + // Test for https://crbug.com/1043753: the underline intercepts are infinite + // due to letter spacing and this test passes if that does not cause a crash. +} + } // 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 431ff2304ba..bb35f02d633 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 @@ -40,6 +40,52 @@ void NGTextPainter::Paint(unsigned start_offset, } } +// This function paints text twice with different styles in order to: +// 1. Paint glyphs inside of |selection_rect| using |selection_style|, and +// outside using |text_style|. +// 2. Paint parts of a ligature glyph. +void NGTextPainter::PaintSelectedText(unsigned start_offset, + unsigned end_offset, + unsigned length, + const TextPaintStyle& text_style, + const TextPaintStyle& selection_style, + const PhysicalRect& selection_rect, + DOMNodeId node_id) { + if (!fragment_paint_info_.shape_result) + return; + + // Use fast path if all glyphs fit in |selection_rect|. Note |text_bounds_| is + // the bounds of all glyphs of this text fragment, including characters before + // |start_offset| or after |end_offset|. Computing exact bounds is expensive + // that this code only checks bounds of all glyphs. + if (selection_rect.Contains(text_bounds_)) { + Paint(start_offset, end_offset, length, selection_style, node_id); + return; + } + + // Adjust start/end offset when they are in the middle of a ligature. e.g., + // when |start_offset| is between a ligature of "fi", it needs to be adjusted + // to before "f". + fragment_paint_info_.shape_result->ExpandRangeToIncludePartialGlyphs( + &start_offset, &end_offset); + + // Because only a part of the text glyph can be selected, we need to draw + // the selection twice. First, draw the glyphs outside the selection area, + // with the original style. + FloatRect float_selection_rect(selection_rect); + { + GraphicsContextStateSaver state_saver(graphics_context_); + graphics_context_.ClipOut(float_selection_rect); + Paint(start_offset, end_offset, length, text_style, node_id); + } + // Then draw the glyphs inside the selection area, with the selection style. + { + GraphicsContextStateSaver state_saver(graphics_context_); + graphics_context_.Clip(float_selection_rect); + Paint(start_offset, end_offset, length, selection_style, node_id); + } +} + template <NGTextPainter::PaintInternalStep step> void NGTextPainter::PaintInternalFragment( unsigned from, diff --git a/chromium/third_party/blink/renderer/core/paint/ng/ng_text_painter.h b/chromium/third_party/blink/renderer/core/paint/ng/ng_text_painter.h index dc8c2bd5f19..b9b96936f54 100644 --- a/chromium/third_party/blink/renderer/core/paint/ng/ng_text_painter.h +++ b/chromium/third_party/blink/renderer/core/paint/ng/ng_text_painter.h @@ -44,6 +44,14 @@ class CORE_EXPORT NGTextPainter : public TextPainterBase { const TextPaintStyle&, DOMNodeId); + void PaintSelectedText(unsigned start_offset, + unsigned end_offset, + unsigned length, + const TextPaintStyle& text_style, + const TextPaintStyle& selection_style, + const PhysicalRect& selection_rect, + DOMNodeId node_id); + private: template <PaintInternalStep step> void PaintInternalFragment(unsigned from, unsigned to, DOMNodeId node_id); 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 b903af4b71d..ca91cbaff74 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 @@ -177,8 +177,9 @@ bool NinePieceImagePainter::Paint(GraphicsContext& graphics_context, // is one. For generated images, the actual image data (gradient stops, etc.) // are scaled to effective zoom instead so we must take care not to cause // scale of them again. - IntSize image_size = RoundedIntSize(style_image->ImageSize( - document, 1, border_image_rect.size.ToLayoutSize())); + IntSize image_size = RoundedIntSize( + style_image->ImageSize(document, 1, border_image_rect.size.ToLayoutSize(), + kRespectImageOrientation)); scoped_refptr<Image> image = style_image->GetImage(observer, document, style, FloatSize(image_size)); if (!image) 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 d7e46c2cda9..e3f8aeb9891 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 @@ -228,9 +228,6 @@ ObjectPaintInvalidatorWithContext::ComputePaintInvalidationReason() { context_.fragment_data->VisualRect().IsEmpty()) return PaintInvalidationReason::kNone; - if (object_.PaintedOutputOfObjectHasNoEffectRegardlessOfSize()) - return PaintInvalidationReason::kNone; - // Force full paint invalidation if the outline may be affected by descendants // and this object is marked for checking paint invalidation for any reason. if (object_.OutlineMayBeAffectedByDescendants() || 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 179caace7b3..a84527533ee 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 @@ -56,7 +56,9 @@ TEST_F(ObjectPaintInvalidatorTest, style='position: relative'></div> <div id='non-stacked-layered-child-of-composited-non-stacking-context' - style='overflow: scroll'></div> + style='overflow: scroll'> + <div style="height:40px"></div> + </div> </div> </div> )HTML"); @@ -326,7 +328,8 @@ TEST_F(ObjectPaintInvalidatorTest, InvalidatePaintRectangle) { EXPECT_TRUE(target->ShouldCheckForPaintInvalidation()); EXPECT_TRUE(IsValidDisplayItemClient(target)); - GetDocument().View()->UpdateAllLifecyclePhasesExceptPaint(); + GetDocument().View()->UpdateAllLifecyclePhasesExceptPaint( + DocumentUpdateReason::kTest); EXPECT_EQ(PhysicalRect(), target->PartialInvalidationLocalRect()); EXPECT_EQ(IntRect(18, 18, 80, 80), target->PartialInvalidationVisualRect()); EXPECT_FALSE(IsValidDisplayItemClient(target)); @@ -334,7 +337,8 @@ TEST_F(ObjectPaintInvalidatorTest, InvalidatePaintRectangle) { target->InvalidatePaintRectangle(PhysicalRect(30, 30, 50, 80)); EXPECT_EQ(PhysicalRect(30, 30, 50, 80), target->PartialInvalidationLocalRect()); - GetDocument().View()->UpdateAllLifecyclePhasesExceptPaint(); + GetDocument().View()->UpdateAllLifecyclePhasesExceptPaint( + DocumentUpdateReason::kTest); // PartialInvalidationVisualRect should accumulate until painting. EXPECT_EQ(IntRect(18, 18, 80, 100), target->PartialInvalidationVisualRect()); diff --git a/chromium/third_party/blink/renderer/core/paint/object_painter.cc b/chromium/third_party/blink/renderer/core/paint/object_painter.cc index 42c8caf0167..633e6518e9e 100644 --- a/chromium/third_party/blink/renderer/core/paint/object_painter.cc +++ b/chromium/third_party/blink/renderer/core/paint/object_painter.cc @@ -99,10 +99,10 @@ void ObjectPainter::AddURLRectIfNeeded(const PaintInfo& paint_info, } void ObjectPainter::PaintAllPhasesAtomically(const PaintInfo& paint_info) { - // Pass kSelection and kTextClip to the descendants so that + // Pass kSelectionDragImage and kTextClip to the descendants so that // they will paint for selection and text clip respectively. We don't need // complete painting for these phases. - if (paint_info.phase == PaintPhase::kSelection || + if (paint_info.phase == PaintPhase::kSelectionDragImage || paint_info.phase == PaintPhase::kTextClip) { layout_object_.Paint(paint_info); return; diff --git a/chromium/third_party/blink/renderer/core/paint/object_painter_base.cc b/chromium/third_party/blink/renderer/core/paint/object_painter_base.cc index f165ceeecf8..096fd2913e1 100644 --- a/chromium/third_party/blink/renderer/core/paint/object_painter_base.cc +++ b/chromium/third_party/blink/renderer/core/paint/object_painter_base.cc @@ -4,6 +4,7 @@ #include "third_party/blink/renderer/core/paint/object_painter_base.h" +#include "base/optional.h" #include "third_party/blink/renderer/core/paint/box_border_painter.h" #include "third_party/blink/renderer/core/paint/paint_info.h" #include "third_party/blink/renderer/core/style/border_edge.h" @@ -12,6 +13,8 @@ #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/graphics/skia/skia_utils.h" +#include "ui/base/ui_base_features.h" +#include "ui/native_theme/native_theme.h" namespace blink { @@ -486,6 +489,46 @@ void DrawSolidBoxSide(GraphicsContext& graphics_context, FillQuad(graphics_context, quad, color, antialias); } +float GetFocusRingBorderRadius(const ComputedStyle& style) { + // Default style is border-radius equal to outline width. + float border_radius = style.GetOutlineStrokeWidthForFocusRing(); + + if (::features::IsFormControlsRefreshEnabled() && !style.HasAuthorBorder() && + style.HasEffectiveAppearance()) { + // For the elements that have not been styled and that have an appearance, + // the focus ring should use the same border radius as the one used for + // drawing the element. + base::Optional<ui::NativeTheme::Part> part; + switch (style.EffectiveAppearance()) { + case kCheckboxPart: + part = ui::NativeTheme::kCheckbox; + break; + case kRadioPart: + part = ui::NativeTheme::kRadio; + break; + case kPushButtonPart: + case kSquareButtonPart: + case kButtonPart: + part = ui::NativeTheme::kPushButton; + break; + case kTextFieldPart: + case kTextAreaPart: + case kSearchFieldPart: + part = ui::NativeTheme::kTextField; + break; + default: + break; + } + if (part) { + return ui::NativeTheme::GetInstanceForWeb()->GetBorderRadiusForPart( + part.value(), style.Width().GetFloatValue(), + style.Height().GetFloatValue(), style.EffectiveZoom()); + } + } + + return border_radius; +} + } // anonymous namespace void ObjectPainterBase::PaintOutlineRects( @@ -498,9 +541,17 @@ void ObjectPainterBase::PaintOutlineRects( Color color = style.VisitedDependentColor(GetCSSPropertyOutlineColor()); if (style.OutlineStyleIsAuto()) { + // Logic in draw focus ring is dependent on whether the border is large + // enough to have an inset outline. Use the smallest border edge for that + // test. + float min_border_width = + std::min(std::min(style.BorderTopWidth(), style.BorderBottomWidth()), + std::min(style.BorderLeftWidth(), style.BorderRightWidth())); + float border_radius = GetFocusRingBorderRadius(style); paint_info.context.DrawFocusRing( pixel_snapped_outline_rects, style.GetOutlineStrokeWidthForFocusRing(), - style.OutlineOffset(), color, + style.OutlineOffset(), style.GetDefaultOffsetForFocusRing(), + border_radius, min_border_width, color, LayoutTheme::GetTheme().IsFocusRingOutset()); return; } 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 c0b3b43e62d..773e223519d 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 @@ -163,6 +163,20 @@ TEST_P(PaintAndRasterInvalidationTest, IncrementalInvalidationMixed) { GetDocument().View()->SetTracksRasterInvalidations(false); } +TEST_P(PaintAndRasterInvalidationTest, ResizeEmptyContent) { + SetUpHTML(*this); + Element* target = GetDocument().getElementById("target"); + // Make the box empty. + target->setAttribute(html_names::kClassAttr, ""); + UpdateAllLifecyclePhasesForTest(); + + GetDocument().View()->SetTracksRasterInvalidations(true); + target->setAttribute(html_names::kStyleAttr, "width: 100px; height: 80px"); + UpdateAllLifecyclePhasesForTest(); + EXPECT_FALSE(GetRasterInvalidationTracking()->HasInvalidations()); + GetDocument().View()->SetTracksRasterInvalidations(false); +} + TEST_P(PaintAndRasterInvalidationTest, SubpixelVisualRectChagne) { SetUpHTML(*this); Element* target = GetDocument().getElementById("target"); @@ -286,7 +300,7 @@ TEST_P(PaintAndRasterInvalidationTest, ResizeRotatedChild) { Element* target = GetDocument().getElementById("target"); target->setAttribute(html_names::kStyleAttr, "transform: rotate(45deg); width: 200px"); - target->SetInnerHTMLFromString( + target->setInnerHTML( "<div id=child style='width: 50px; height: 50px; background: " "red'></div>"); UpdateAllLifecyclePhasesForTest(); @@ -315,11 +329,6 @@ TEST_P(PaintAndRasterInvalidationTest, CompositedLayoutViewResize) { UpdateAllLifecyclePhasesForTest(); EXPECT_EQ(kBackgroundPaintInScrollingContents, GetLayoutView().GetBackgroundPaintLocation()); - if (!RuntimeEnabledFeatures::CompositeAfterPaintEnabled()) { - const auto* mapping = GetLayoutView().Layer()->GetCompositedLayerMapping(); - EXPECT_TRUE(mapping->BackgroundPaintsOntoScrollingContentsLayer()); - EXPECT_FALSE(mapping->BackgroundPaintsOntoGraphicsLayer()); - } // Resize the content. GetDocument().View()->SetTracksRasterInvalidations(true); @@ -350,11 +359,6 @@ TEST_P(PaintAndRasterInvalidationTest, CompositedLayoutViewGradientResize) { UpdateAllLifecyclePhasesForTest(); EXPECT_EQ(kBackgroundPaintInScrollingContents, GetLayoutView().GetBackgroundPaintLocation()); - if (!RuntimeEnabledFeatures::CompositeAfterPaintEnabled()) { - const auto* mapping = GetLayoutView().Layer()->GetCompositedLayerMapping(); - EXPECT_TRUE(mapping->BackgroundPaintsOntoScrollingContentsLayer()); - EXPECT_FALSE(mapping->BackgroundPaintsOntoGraphicsLayer()); - } // Resize the content. GetDocument().View()->SetTracksRasterInvalidations(true); @@ -395,9 +399,15 @@ TEST_P(PaintAndRasterInvalidationTest, NonCompositedLayoutViewResize) { UpdateAllLifecyclePhasesForTest(); Element* iframe = GetDocument().getElementById("iframe"); Element* content = ChildDocument().getElementById("content"); - EXPECT_EQ(GetLayoutView(), - content->GetLayoutObject()->ContainerForPaintInvalidation()); + if (!RuntimeEnabledFeatures::CompositeAfterPaintEnabled()) { + EXPECT_EQ(GetLayoutView(), + content->GetLayoutObject()->ContainerForPaintInvalidation()); + } EXPECT_EQ(kBackgroundPaintInScrollingContents, + content->GetLayoutObject() + ->View() + ->ComputeBackgroundPaintLocationIfComposited()); + EXPECT_EQ(kBackgroundPaintInGraphicsLayer, content->GetLayoutObject()->View()->GetBackgroundPaintLocation()); // Resize the content. @@ -414,11 +424,7 @@ TEST_P(PaintAndRasterInvalidationTest, NonCompositedLayoutViewResize) { UpdateAllLifecyclePhasesForTest(); // The iframe doesn't have anything visible by itself, so we only issue // raster invalidation for the frame contents. - const auto* iframe_layout_view = content->GetLayoutObject()->View(); - const auto* client = RuntimeEnabledFeatures::CompositeAfterPaintEnabled() - ? &iframe_layout_view->GetScrollableArea() - ->GetScrollingBackgroundDisplayItemClient() - : iframe_layout_view; + const auto* client = content->GetLayoutObject()->View(); EXPECT_THAT(GetRasterInvalidationTracking()->Invalidations(), UnorderedElementsAre(RasterInvalidationInfo{ client, client->DebugName(), IntRect(0, 100, 100, 100), @@ -426,6 +432,31 @@ TEST_P(PaintAndRasterInvalidationTest, NonCompositedLayoutViewResize) { GetDocument().View()->SetTracksRasterInvalidations(false); } +TEST_P(PaintAndRasterInvalidationTest, FullInvalidationWithHTMLTransform) { + GetDocument().documentElement()->setAttribute(html_names::kStyleAttr, + "transform: scale(0.5)"); + const DisplayItemClient* client = + &GetDocument() + .View() + ->GetLayoutView() + ->GetScrollableArea() + ->GetScrollingBackgroundDisplayItemClient(); + UpdateAllLifecyclePhasesForTest(); + + GetDocument().View()->SetTracksRasterInvalidations(true); + GetDocument().View()->Resize(WebSize(500, 500)); + UpdateAllLifecyclePhasesForTest(); + + EXPECT_THAT(GetRasterInvalidationTracking()->Invalidations(), + UnorderedElementsAre( + RasterInvalidationInfo{client, client->DebugName(), + IntRect(0, 0, 500, 500), + PaintInvalidationReason::kBackground}, + RasterInvalidationInfo{ + client, client->DebugName(), IntRect(0, 0, 500, 500), + PaintInvalidationReason::kPaintProperty})); +} + TEST_P(PaintAndRasterInvalidationTest, NonCompositedLayoutViewGradientResize) { SetBodyInnerHTML(R"HTML( <style> @@ -448,36 +479,20 @@ TEST_P(PaintAndRasterInvalidationTest, NonCompositedLayoutViewGradientResize) { UpdateAllLifecyclePhasesForTest(); Element* iframe = GetDocument().getElementById("iframe"); Element* content = ChildDocument().getElementById("content"); - LayoutView* frame_layout_view = content->GetLayoutObject()->View(); - EXPECT_EQ(GetLayoutView(), - content->GetLayoutObject()->ContainerForPaintInvalidation()); + if (!RuntimeEnabledFeatures::CompositeAfterPaintEnabled()) { + EXPECT_EQ(GetLayoutView(), + content->GetLayoutObject()->ContainerForPaintInvalidation()); + } // Resize the content. GetDocument().View()->SetTracksRasterInvalidations(true); content->setAttribute(html_names::kStyleAttr, "height: 500px"); UpdateAllLifecyclePhasesForTest(); - const auto* client = RuntimeEnabledFeatures::CompositeAfterPaintEnabled() - ? &frame_layout_view->GetScrollableArea() - ->GetScrollingBackgroundDisplayItemClient() - : frame_layout_view; - if (RuntimeEnabledFeatures::CompositeAfterPaintEnabled()) { - // The duplication is because we invalidated both the old visual rect and - // the new visual rect of the scrolling background display item which - // changed size, and then both mapped to the same rect in the layer. - EXPECT_THAT(GetRasterInvalidationTracking()->Invalidations(), - UnorderedElementsAre( - RasterInvalidationInfo{ - client, client->DebugName(), IntRect(0, 0, 100, 100), - PaintInvalidationReason::kBackground}, - RasterInvalidationInfo{ - client, client->DebugName(), IntRect(0, 0, 100, 100), - PaintInvalidationReason::kBackground})); - } else { - EXPECT_THAT(GetRasterInvalidationTracking()->Invalidations(), - UnorderedElementsAre(RasterInvalidationInfo{ - client, client->DebugName(), IntRect(0, 0, 100, 100), - PaintInvalidationReason::kBackground})); - } + const auto* client = content->GetLayoutObject()->View(); + EXPECT_THAT(GetRasterInvalidationTracking()->Invalidations(), + UnorderedElementsAre(RasterInvalidationInfo{ + client, client->DebugName(), IntRect(0, 0, 100, 100), + PaintInvalidationReason::kBackground})); GetDocument().View()->SetTracksRasterInvalidations(false); // Resize the iframe. @@ -499,7 +514,8 @@ TEST_P(PaintAndRasterInvalidationTest, Element* target = GetDocument().getElementById("target"); target->setAttribute(html_names::kClassAttr, "solid composited scroll local-attachment border"); - target->SetInnerHTMLFromString( + UpdateAllLifecyclePhasesForTest(); + target->setInnerHTML( "<div id=child style='width: 500px; height: 500px'></div>", ASSERT_NO_EXCEPTION); Element* child = GetDocument().getElementById("child"); @@ -508,11 +524,6 @@ TEST_P(PaintAndRasterInvalidationTest, auto* target_obj = ToLayoutBoxModelObject(target->GetLayoutObject()); EXPECT_EQ(kBackgroundPaintInScrollingContents, target_obj->GetBackgroundPaintLocation()); - if (!RuntimeEnabledFeatures::CompositeAfterPaintEnabled()) { - const auto* mapping = target_obj->Layer()->GetCompositedLayerMapping(); - EXPECT_TRUE(mapping->BackgroundPaintsOntoScrollingContentsLayer()); - EXPECT_FALSE(mapping->BackgroundPaintsOntoGraphicsLayer()); - } auto container_raster_invalidation_tracking = [&]() -> const RasterInvalidationTracking* { @@ -566,7 +577,7 @@ TEST_P(PaintAndRasterInvalidationTest, Element* target = GetDocument().getElementById("target"); target->setAttribute(html_names::kClassAttr, "gradient composited scroll local-attachment border"); - target->SetInnerHTMLFromString( + target->setInnerHTML( "<div id='child' style='width: 500px; height: 500px'></div>", ASSERT_NO_EXCEPTION); Element* child = GetDocument().getElementById("child"); @@ -597,11 +608,6 @@ TEST_P(PaintAndRasterInvalidationTest, UpdateAllLifecyclePhasesForTest(); EXPECT_EQ(kBackgroundPaintInScrollingContents, target_obj->GetBackgroundPaintLocation()); - if (!RuntimeEnabledFeatures::CompositeAfterPaintEnabled()) { - const auto* mapping = target_obj->Layer()->GetCompositedLayerMapping(); - EXPECT_TRUE(mapping->BackgroundPaintsOntoScrollingContentsLayer()); - EXPECT_FALSE(mapping->BackgroundPaintsOntoGraphicsLayer()); - } // No invalidation on the container layer. EXPECT_FALSE(container_raster_invalidation_tracking()->HasInvalidations()); @@ -635,14 +641,18 @@ TEST_P(PaintAndRasterInvalidationTest, Element* target = GetDocument().getElementById("target"); auto* object = target->GetLayoutObject(); target->setAttribute(html_names::kClassAttr, "solid local-attachment scroll"); - target->SetInnerHTMLFromString( + target->setInnerHTML( "<div id=child style='width: 500px; height: 500px'></div>", ASSERT_NO_EXCEPTION); Element* child = GetDocument().getElementById("child"); UpdateAllLifecyclePhasesForTest(); - EXPECT_EQ(&GetLayoutView(), object->ContainerForPaintInvalidation()); + if (!RuntimeEnabledFeatures::CompositeAfterPaintEnabled()) + EXPECT_EQ(&GetLayoutView(), object->ContainerForPaintInvalidation()); EXPECT_EQ(kBackgroundPaintInScrollingContents, - ToLayoutBoxModelObject(object)->GetBackgroundPaintLocation()); + ToLayoutBoxModelObject(object) + ->ComputeBackgroundPaintLocationIfComposited()); + EXPECT_EQ(kBackgroundPaintInGraphicsLayer, + object->GetBackgroundPaintLocation()); // Resize the content. GetDocument().View()->SetTracksRasterInvalidations(true); @@ -671,8 +681,8 @@ TEST_P(PaintAndRasterInvalidationTest, CompositedSolidBackgroundResize) { SetUpHTML(*this); Element* target = GetDocument().getElementById("target"); target->setAttribute(html_names::kClassAttr, "solid composited scroll"); - target->SetInnerHTMLFromString("<div style='height: 500px'></div>", - ASSERT_NO_EXCEPTION); + target->setInnerHTML("<div style='height: 500px'></div>", + ASSERT_NO_EXCEPTION); UpdateAllLifecyclePhasesForTest(); // Resize the scroller. @@ -685,11 +695,6 @@ TEST_P(PaintAndRasterInvalidationTest, CompositedSolidBackgroundResize) { EXPECT_EQ( kBackgroundPaintInScrollingContents | kBackgroundPaintInGraphicsLayer, target_object->GetBackgroundPaintLocation()); - if (!RuntimeEnabledFeatures::CompositeAfterPaintEnabled()) { - const auto* mapping = target_object->Layer()->GetCompositedLayerMapping(); - EXPECT_TRUE(mapping->BackgroundPaintsOntoScrollingContentsLayer()); - EXPECT_TRUE(mapping->BackgroundPaintsOntoGraphicsLayer()); - } const auto* contents_raster_invalidation_tracking = RuntimeEnabledFeatures::CompositeAfterPaintEnabled() @@ -767,15 +772,19 @@ TEST_P(PaintAndRasterInvalidationTest, Element* iframe = GetDocument().getElementById("iframe"); LayoutView* child_layout_view = ChildDocument().GetLayoutView(); - EXPECT_EQ(GetDocument().GetLayoutView(), - &child_layout_view->ContainerForPaintInvalidation()); + if (!RuntimeEnabledFeatures::CompositeAfterPaintEnabled()) { + EXPECT_EQ(GetDocument().GetLayoutView(), + &child_layout_view->ContainerForPaintInvalidation()); + } EXPECT_EQ(IntRect(0, 0, 100, 100), child_layout_view->FirstFragment().VisualRect()); iframe->setAttribute(html_names::kStyleAttr, "border: 20px solid blue"); UpdateAllLifecyclePhasesForTest(); - EXPECT_EQ(GetDocument().GetLayoutView(), - &child_layout_view->ContainerForPaintInvalidation()); + if (!RuntimeEnabledFeatures::CompositeAfterPaintEnabled()) { + EXPECT_EQ(GetDocument().GetLayoutView(), + &child_layout_view->ContainerForPaintInvalidation()); + } EXPECT_EQ(IntRect(0, 0, 100, 100), child_layout_view->FirstFragment().VisualRect()); } @@ -927,7 +936,8 @@ TEST_P(PaintAndRasterInvalidationTest, PaintPropertyChange) { auto* layer = ToLayoutBoxModelObject(object)->Layer(); GetDocument().View()->SetTracksRasterInvalidations(true); target->setAttribute(html_names::kStyleAttr, "transform: scale(3)"); - GetDocument().View()->UpdateAllLifecyclePhasesExceptPaint(); + GetDocument().View()->UpdateAllLifecyclePhasesExceptPaint( + DocumentUpdateReason::kTest); EXPECT_FALSE(layer->SelfNeedsRepaint()); const auto* transform = object->FirstFragment().PaintProperties()->Transform(); @@ -963,7 +973,8 @@ TEST_P(PaintAndRasterInvalidationTest, ResizeContainerOfFixedSizeSVG) { GetDocument().View()->SetTracksRasterInvalidations(true); target->setAttribute(html_names::kStyleAttr, "width: 200px; height: 200px"); - GetDocument().View()->UpdateAllLifecyclePhasesExceptPaint(); + GetDocument().View()->UpdateAllLifecyclePhasesExceptPaint( + DocumentUpdateReason::kTest); // We don't invalidate paint of the SVG rect. EXPECT_TRUE(static_cast<const DisplayItemClient*>(rect)->IsValid()); @@ -1115,7 +1126,8 @@ TEST_F(PaintInvalidatorCustomClientTest, ResetInvalidationRecorded(); target->setAttribute(html_names::kStyleAttr, "opacity: 0.98"); - GetDocument().View()->UpdateAllLifecyclePhasesExceptPaint(); + GetDocument().View()->UpdateAllLifecyclePhasesExceptPaint( + DocumentUpdateReason::kTest); EXPECT_TRUE( GetDocument().View()->GetLayoutView()->Layer()->DescendantNeedsRepaint()); EXPECT_TRUE(InvalidationRecorded()); @@ -1123,7 +1135,8 @@ TEST_F(PaintInvalidatorCustomClientTest, ResetInvalidationRecorded(); // Let PrePaintTreeWalk do something instead of no-op. GetDocument().View()->SetNeedsPaintPropertyUpdate(); - GetDocument().View()->UpdateAllLifecyclePhasesExceptPaint(); + GetDocument().View()->UpdateAllLifecyclePhasesExceptPaint( + DocumentUpdateReason::kTest); // The layer DescendantNeedsRepaint flag is only cleared after paint. EXPECT_TRUE( GetDocument().View()->GetLayoutView()->Layer()->DescendantNeedsRepaint()); 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 d9c7752ecf8..cafc50592fd 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 @@ -8,6 +8,7 @@ #include "third_party/blink/renderer/core/editing/frame_selection.h" #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/layout/ng/inline/ng_inline_cursor.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" @@ -58,37 +59,39 @@ TEST_P(PaintControllerPaintTest, InlineRelayout) { auto& div_block = *To<LayoutBlock>(GetDocument().body()->firstChild()->GetLayoutObject()); LayoutText& text = *ToLayoutText(div_block.FirstChild()); - DisplayItemClient& first_text_box = - text.FirstInlineFragment() - ? (DisplayItemClient&)*text.FirstInlineFragment() - : (DisplayItemClient&)*text.FirstTextBox(); + const DisplayItemClient* first_text_box = text.FirstTextBox(); + if (text.IsInLayoutNGInlineFormattingContext()) { + NGInlineCursor cursor; + cursor.MoveTo(text); + first_text_box = cursor.Current().GetDisplayItemClient(); + } EXPECT_THAT(RootPaintController().GetDisplayItemList(), ElementsAre(IsSameId(&ViewScrollingBackgroundClient(), kDocumentBackgroundType), - IsSameId(&first_text_box, kForegroundType))); + IsSameId(first_text_box, kForegroundType))); div.setAttribute(html_names::kStyleAttr, "width: 10px; height: 200px"); UpdateAllLifecyclePhasesForTest(); LayoutText& new_text = *ToLayoutText(div_block.FirstChild()); - 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(); + const DisplayItemClient* new_first_text_box = text.FirstTextBox(); + const DisplayItemClient* second_text_box = nullptr; + if (!text.IsInLayoutNGInlineFormattingContext()) { + second_text_box = new_text.FirstTextBox()->NextForSameLayoutObject(); + } else { + NGInlineCursor cursor; + cursor.MoveTo(text); + new_first_text_box = cursor.Current().GetDisplayItemClient(); + cursor.MoveToNextForSameLayoutObject(); + second_text_box = cursor.Current().GetDisplayItemClient(); + } EXPECT_THAT(RootPaintController().GetDisplayItemList(), ElementsAre(IsSameId(&ViewScrollingBackgroundClient(), kDocumentBackgroundType), - IsSameId(&new_first_text_box, kForegroundType), - IsSameId(&second_text_box, kForegroundType))); + IsSameId(new_first_text_box, kForegroundType), + IsSameId(second_text_box, kForegroundType))); } TEST_P(PaintControllerPaintTest, ChunkIdClientCacheFlag) { @@ -156,21 +159,47 @@ TEST_P(PaintControllerPaintTestForCAP, FrameScrollingContents) { EXPECT_THAT( RootPaintController().GetDisplayItemList(), ElementsAre( - IsSameId(&GetLayoutView(), kScrollHitTestType), IsSameId(&ViewScrollingBackgroundClient(), kDocumentBackgroundType), IsSameId(&div1, kBackgroundType), IsSameId(&div2, kBackgroundType))); + HitTestData view_scroll_hit_test; + view_scroll_hit_test.scroll_translation = + &GetLayoutView().FirstFragment().ContentsProperties().Transform(); + view_scroll_hit_test.scroll_hit_test_rect = IntRect(0, 0, 800, 600); + EXPECT_THAT( + RootPaintController().PaintChunks(), + ElementsAre( + IsPaintChunk( + 0, 0, + PaintChunk::Id(GetLayoutView(), DisplayItem::kScrollHitTest), + GetLayoutView().FirstFragment().LocalBorderBoxProperties(), + &view_scroll_hit_test, IntRect(0, 0, 800, 600)), + IsPaintChunk(0, 3, + PaintChunk::Id(ViewScrollingBackgroundClient(), + DisplayItem::kDocumentBackground), + GetLayoutView().FirstFragment().ContentsProperties()))); GetDocument().View()->LayoutViewport()->SetScrollOffset( - ScrollOffset(5000, 5000), kProgrammaticScroll); + ScrollOffset(5000, 5000), mojom::blink::ScrollType::kProgrammatic); UpdateAllLifecyclePhasesForTest(); EXPECT_THAT( RootPaintController().GetDisplayItemList(), ElementsAre( - IsSameId(&GetLayoutView(), kScrollHitTestType), IsSameId(&ViewScrollingBackgroundClient(), kDocumentBackgroundType), IsSameId(&div2, kBackgroundType), IsSameId(&div3, kBackgroundType), IsSameId(&div4, kBackgroundType))); + EXPECT_THAT( + RootPaintController().PaintChunks(), + ElementsAre( + IsPaintChunk( + 0, 0, + PaintChunk::Id(GetLayoutView(), DisplayItem::kScrollHitTest), + GetLayoutView().FirstFragment().LocalBorderBoxProperties(), + &view_scroll_hit_test, IntRect(0, 0, 800, 600)), + IsPaintChunk(0, 4, + PaintChunk::Id(ViewScrollingBackgroundClient(), + DisplayItem::kDocumentBackground), + GetLayoutView().FirstFragment().ContentsProperties()))); } TEST_P(PaintControllerPaintTestForCAP, BlockScrollingNonLayeredContents) { @@ -201,11 +230,34 @@ TEST_P(PaintControllerPaintTestForCAP, BlockScrollingNonLayeredContents) { RootPaintController().GetDisplayItemList(), ElementsAre( IsSameId(&ViewScrollingBackgroundClient(), kDocumentBackgroundType), - IsSameId(&container, kScrollHitTestType), IsSameId(&div1, kBackgroundType), IsSameId(&div2, kBackgroundType))); - - container.GetScrollableArea()->SetScrollOffset(ScrollOffset(5000, 5000), - kProgrammaticScroll); + HitTestData container_scroll_hit_test; + container_scroll_hit_test.scroll_translation = + &container.FirstFragment().ContentsProperties().Transform(); + container_scroll_hit_test.scroll_hit_test_rect = IntRect(0, 0, 200, 200); + EXPECT_THAT( + RootPaintController().PaintChunks(), + ElementsAre( + IsPaintChunk(0, 1, + PaintChunk::Id(ViewScrollingBackgroundClient(), + DisplayItem::kDocumentBackground), + GetLayoutView().FirstFragment().ContentsProperties()), + IsPaintChunk( + 1, 1, + PaintChunk::Id(*container.Layer(), DisplayItem::kLayerChunk), + container.FirstFragment().LocalBorderBoxProperties(), nullptr, + IntRect(0, 0, 200, 200)), + IsPaintChunk(1, 1, + PaintChunk::Id(container, DisplayItem::kScrollHitTest), + container.FirstFragment().LocalBorderBoxProperties(), + &container_scroll_hit_test, IntRect(0, 0, 200, 200)), + IsPaintChunk( + 1, 3, + PaintChunk::Id(container, kClippedContentsBackgroundChunkType), + container.FirstFragment().ContentsProperties()))); + + container.GetScrollableArea()->SetScrollOffset( + ScrollOffset(5000, 5000), mojom::blink::ScrollType::kProgrammatic); UpdateAllLifecyclePhasesForTest(); // Cull rect after scroll: (1000,1000 8100x8100) @@ -213,9 +265,28 @@ TEST_P(PaintControllerPaintTestForCAP, BlockScrollingNonLayeredContents) { RootPaintController().GetDisplayItemList(), ElementsAre( IsSameId(&ViewScrollingBackgroundClient(), kDocumentBackgroundType), - IsSameId(&container, kScrollHitTestType), IsSameId(&div2, kBackgroundType), IsSameId(&div3, kBackgroundType), IsSameId(&div4, kBackgroundType))); + EXPECT_THAT( + RootPaintController().PaintChunks(), + ElementsAre( + IsPaintChunk(0, 1, + PaintChunk::Id(ViewScrollingBackgroundClient(), + DisplayItem::kDocumentBackground), + GetLayoutView().FirstFragment().ContentsProperties()), + IsPaintChunk( + 1, 1, + PaintChunk::Id(*container.Layer(), DisplayItem::kLayerChunk), + container.FirstFragment().LocalBorderBoxProperties(), nullptr, + IntRect(0, 0, 200, 200)), + IsPaintChunk(1, 1, + PaintChunk::Id(container, DisplayItem::kScrollHitTest), + container.FirstFragment().LocalBorderBoxProperties(), + &container_scroll_hit_test, IntRect(0, 0, 200, 200)), + IsPaintChunk( + 1, 4, + PaintChunk::Id(container, kClippedContentsBackgroundChunkType), + container.FirstFragment().ContentsProperties()))); } TEST_P(PaintControllerPaintTestForCAP, ScrollHitTestOrder) { @@ -243,14 +314,42 @@ TEST_P(PaintControllerPaintTestForCAP, ScrollHitTestOrder) { 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))); + HitTestData view_scroll_hit_test; + view_scroll_hit_test.scroll_translation = + &GetLayoutView().FirstFragment().ContentsProperties().Transform(); + view_scroll_hit_test.scroll_hit_test_rect = IntRect(0, 0, 800, 600); + HitTestData container_scroll_hit_test; + container_scroll_hit_test.scroll_translation = + &container.FirstFragment().ContentsProperties().Transform(); + container_scroll_hit_test.scroll_hit_test_rect = IntRect(0, 0, 200, 200); + EXPECT_THAT( + RootPaintController().PaintChunks(), + ElementsAre( + IsPaintChunk( + 0, 0, + PaintChunk::Id(GetLayoutView(), DisplayItem::kScrollHitTest), + GetLayoutView().FirstFragment().LocalBorderBoxProperties(), + &view_scroll_hit_test, IntRect(0, 0, 800, 600)), + IsPaintChunk(0, 1, + PaintChunk::Id(ViewScrollingBackgroundClient(), + DisplayItem::kDocumentBackground), + GetLayoutView().FirstFragment().ContentsProperties()), + IsPaintChunk( + 1, 2, + PaintChunk::Id(*container.Layer(), DisplayItem::kLayerChunk), + container.FirstFragment().LocalBorderBoxProperties(), nullptr, + IntRect(0, 0, 200, 200)), + IsPaintChunk(2, 2, + PaintChunk::Id(container, DisplayItem::kScrollHitTest), + container.FirstFragment().LocalBorderBoxProperties(), + &container_scroll_hit_test, IntRect(0, 0, 200, 200)), + IsPaintChunk( + 2, 3, + PaintChunk::Id(container, kClippedContentsBackgroundChunkType), + container.FirstFragment().ContentsProperties()))); } TEST_P(PaintControllerPaintTestForCAP, NonStackingScrollHitTestOrder) { @@ -274,16 +373,17 @@ TEST_P(PaintControllerPaintTestForCAP, NonStackingScrollHitTestOrder) { </div> )HTML"); - auto& container = *To<LayoutBlock>(GetLayoutObjectByElementId("container")); + auto& html = *GetDocument().documentElement()->GetLayoutBox(); + auto& container = *ToLayoutBox(GetLayoutObjectByElementId("container")); auto& child = *GetLayoutObjectByElementId("child"); - auto& neg_z_child = *GetLayoutObjectByElementId("negZChild"); - auto& pos_z_child = *GetLayoutObjectByElementId("posZChild"); + auto& neg_z_child = *ToLayoutBox(GetLayoutObjectByElementId("negZChild")); + auto& pos_z_child = *ToLayoutBox(GetLayoutObjectByElementId("posZChild")); // Container is not a stacking context because no z-index is auto. // Negative z-index descendants are painted before the background and // positive z-index descendants are painted after the background. Scroll hit // testing should hit positive descendants, the container, and then negative - // descendants so the ScrollHitTest item should be immediately after the + // descendants so the scroll hit test should be immediately after the // background. EXPECT_THAT( RootPaintController().GetDisplayItemList(), @@ -291,12 +391,45 @@ TEST_P(PaintControllerPaintTestForCAP, NonStackingScrollHitTestOrder) { 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))); + HitTestData container_scroll_hit_test; + container_scroll_hit_test.scroll_translation = + &container.FirstFragment().ContentsProperties().Transform(); + container_scroll_hit_test.scroll_hit_test_rect = IntRect(0, 0, 200, 200); + EXPECT_THAT( + RootPaintController().PaintChunks(), + ElementsAre( + IsPaintChunk(0, 1, + PaintChunk::Id(ViewScrollingBackgroundClient(), + DisplayItem::kDocumentBackground), + GetLayoutView().FirstFragment().ContentsProperties()), + IsPaintChunk( + 1, 2, + PaintChunk::Id(*neg_z_child.Layer(), DisplayItem::kLayerChunk), + neg_z_child.FirstFragment().LocalBorderBoxProperties()), + IsPaintChunk( + 2, 2, + PaintChunk::Id(*html.Layer(), DisplayItem::kLayerChunkForeground), + html.FirstFragment().LocalBorderBoxProperties(), nullptr, + IntRect(0, 0, 800, 200)), + IsPaintChunk( + 2, 3, + PaintChunk::Id(*container.Layer(), DisplayItem::kLayerChunk), + container.FirstFragment().LocalBorderBoxProperties(), nullptr, + IntRect(0, 0, 200, 200)), + IsPaintChunk(3, 3, + PaintChunk::Id(container, DisplayItem::kScrollHitTest), + container.FirstFragment().LocalBorderBoxProperties(), + &container_scroll_hit_test, IntRect(0, 0, 200, 200)), + IsPaintChunk( + 3, 4, + PaintChunk::Id(container, kClippedContentsBackgroundChunkType), + container.FirstFragment().ContentsProperties()), + IsPaintChunk( + 4, 5, + PaintChunk::Id(*pos_z_child.Layer(), DisplayItem::kLayerChunk), + pos_z_child.FirstFragment().LocalBorderBoxProperties()))); } TEST_P(PaintControllerPaintTestForCAP, StackingScrollHitTestOrder) { @@ -320,10 +453,10 @@ TEST_P(PaintControllerPaintTestForCAP, StackingScrollHitTestOrder) { </div> )HTML"); - auto& container = *To<LayoutBlock>(GetLayoutObjectByElementId("container")); + auto& container = *ToLayoutBox(GetLayoutObjectByElementId("container")); auto& child = *GetLayoutObjectByElementId("child"); - auto& neg_z_child = *GetLayoutObjectByElementId("negZChild"); - auto& pos_z_child = *GetLayoutObjectByElementId("posZChild"); + auto& neg_z_child = *ToLayoutBox(GetLayoutObjectByElementId("negZChild")); + auto& pos_z_child = *ToLayoutBox(GetLayoutObjectByElementId("posZChild")); // Container is a stacking context because z-index is non-auto. // Both positive and negative z-index descendants are painted after the @@ -334,13 +467,47 @@ TEST_P(PaintControllerPaintTestForCAP, StackingScrollHitTestOrder) { 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))); + HitTestData container_scroll_hit_test; + container_scroll_hit_test.scroll_translation = + &container.FirstFragment().ContentsProperties().Transform(); + container_scroll_hit_test.scroll_hit_test_rect = IntRect(0, 0, 200, 200); + EXPECT_THAT( + RootPaintController().PaintChunks(), + ElementsAre( + IsPaintChunk(0, 1, + PaintChunk::Id(ViewScrollingBackgroundClient(), + DisplayItem::kDocumentBackground), + GetLayoutView().FirstFragment().ContentsProperties()), + IsPaintChunk( + 1, 2, + PaintChunk::Id(*container.Layer(), DisplayItem::kLayerChunk), + container.FirstFragment().LocalBorderBoxProperties(), nullptr, + IntRect(0, 0, 200, 200)), + IsPaintChunk(2, 2, + PaintChunk::Id(container, DisplayItem::kScrollHitTest), + container.FirstFragment().LocalBorderBoxProperties(), + &container_scroll_hit_test, IntRect(0, 0, 200, 200)), + IsPaintChunk(2, 3, + PaintChunk::Id(container, kScrollingBackgroundChunkType), + container.FirstFragment().ContentsProperties()), + IsPaintChunk( + 3, 4, + PaintChunk::Id(*neg_z_child.Layer(), DisplayItem::kLayerChunk), + neg_z_child.FirstFragment().LocalBorderBoxProperties()), + IsPaintChunk( + 4, 5, + PaintChunk::Id(container, kClippedContentsBackgroundChunkType), + container.FirstFragment().ContentsProperties()), + IsPaintChunk( + 5, 6, + PaintChunk::Id(*pos_z_child.Layer(), DisplayItem::kLayerChunk), + pos_z_child.FirstFragment().LocalBorderBoxProperties()))); } TEST_P(PaintControllerPaintTestForCAP, @@ -365,20 +532,57 @@ TEST_P(PaintControllerPaintTestForCAP, </div> )HTML"); - auto& container = *To<LayoutBlock>(GetLayoutObjectByElementId("container")); + auto& html = *GetDocument().documentElement()->GetLayoutBox(); + auto& container = *ToLayoutBox(GetLayoutObjectByElementId("container")); auto& child = *GetLayoutObjectByElementId("child"); - auto& neg_z_child = *GetLayoutObjectByElementId("negZChild"); - auto& pos_z_child = *GetLayoutObjectByElementId("posZChild"); + auto& neg_z_child = *ToLayoutBox(GetLayoutObjectByElementId("negZChild")); + auto& pos_z_child = *ToLayoutBox(GetLayoutObjectByElementId("posZChild")); - // Even though container does not paint a background, the scroll hit test item + // Even though container does not paint a background, the scroll hit test // should still be between the negative z-index child and the regular child. EXPECT_THAT(RootPaintController().GetDisplayItemList(), ElementsAre(IsSameId(&ViewScrollingBackgroundClient(), kDocumentBackgroundType), IsSameId(&neg_z_child, kBackgroundType), - IsSameId(&container, kScrollHitTestType), IsSameId(&child, kBackgroundType), IsSameId(&pos_z_child, kBackgroundType))); + HitTestData container_scroll_hit_test; + container_scroll_hit_test.scroll_translation = + &container.FirstFragment().ContentsProperties().Transform(); + container_scroll_hit_test.scroll_hit_test_rect = IntRect(0, 0, 200, 200); + EXPECT_THAT( + RootPaintController().PaintChunks(), + ElementsAre( + IsPaintChunk(0, 1, + PaintChunk::Id(ViewScrollingBackgroundClient(), + DisplayItem::kDocumentBackground), + GetLayoutView().FirstFragment().ContentsProperties()), + IsPaintChunk( + 1, 2, + PaintChunk::Id(*neg_z_child.Layer(), DisplayItem::kLayerChunk), + neg_z_child.FirstFragment().LocalBorderBoxProperties()), + IsPaintChunk( + 2, 2, + PaintChunk::Id(*html.Layer(), DisplayItem::kLayerChunkForeground), + html.FirstFragment().LocalBorderBoxProperties(), nullptr, + IntRect(0, 0, 800, 200)), + IsPaintChunk( + 2, 2, + PaintChunk::Id(*container.Layer(), DisplayItem::kLayerChunk), + container.FirstFragment().LocalBorderBoxProperties(), nullptr, + IntRect(0, 0, 200, 200)), + IsPaintChunk(2, 2, + PaintChunk::Id(container, DisplayItem::kScrollHitTest), + container.FirstFragment().LocalBorderBoxProperties(), + &container_scroll_hit_test, IntRect(0, 0, 200, 200)), + IsPaintChunk( + 2, 3, + PaintChunk::Id(container, kClippedContentsBackgroundChunkType), + container.FirstFragment().ContentsProperties()), + IsPaintChunk( + 3, 4, + PaintChunk::Id(*pos_z_child.Layer(), DisplayItem::kLayerChunk), + pos_z_child.FirstFragment().LocalBorderBoxProperties()))); } } // 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 c88fa5805fe..f87c116d72f 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 @@ -137,24 +137,10 @@ 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 = +const DisplayItem::Type kClippedContentsBackgroundChunkType = 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 6927f0aed5a..7716d6e8cc0 100644 --- a/chromium/third_party/blink/renderer/core/paint/paint_info.h +++ b/chromium/third_party/blink/renderer/core/paint/paint_info.h @@ -29,6 +29,7 @@ #include "third_party/blink/renderer/core/core_export.h" #include "third_party/blink/renderer/core/layout/layout_object.h" +#include "third_party/blink/renderer/core/layout/ng/ng_physical_fragment.h" // TODO(jchaffraix): Once we unify PaintBehavior and PaintLayerFlags, we should // move PaintLayerFlags to PaintPhase and rename it. Thus removing the need for // this #include @@ -168,6 +169,17 @@ struct CORE_EXPORT PaintInfo { return nullptr; } + // Returns the FragmentData of the specified physical fragment. If fragment + // traversal is supported, it will map directly to the right FragmentData. + // Otherwise we'll fall back to matching against the current + // PaintLayerFragment. + const FragmentData* FragmentToPaint( + const NGPhysicalFragment& fragment) const { + if (fragment.CanTraverse()) + return fragment.GetFragmentData(); + return FragmentToPaint(*fragment.GetLayoutObject()); + } + void SetFragmentLogicalTopInFlowThread(LayoutUnit fragment_logical_top) { fragment_logical_top_in_flow_thread_ = fragment_logical_top; } 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 358f04b3639..12031a700f8 100644 --- a/chromium/third_party/blink/renderer/core/paint/paint_invalidator.cc +++ b/chromium/third_party/blink/renderer/core/paint/paint_invalidator.cc @@ -14,7 +14,10 @@ #include "third_party/blink/renderer/core/layout/layout_table.h" #include "third_party/blink/renderer/core/layout/layout_table_section.h" #include "third_party/blink/renderer/core/layout/layout_view.h" +#include "third_party/blink/renderer/core/layout/ng/inline/ng_fragment_item.h" #include "third_party/blink/renderer/core/layout/ng/legacy_layout_tree_walking.h" +#include "third_party/blink/renderer/core/layout/ng/ng_fragment_child_iterator.h" +#include "third_party/blink/renderer/core/layout/ng/ng_physical_box_fragment.h" #include "third_party/blink/renderer/core/layout/svg/svg_layout_support.h" #include "third_party/blink/renderer/core/page/link_highlight.h" #include "third_party/blink/renderer/core/page/page.h" @@ -131,22 +134,43 @@ PaintInvalidatorContext::ParentContextAccessor::ParentContext() const { IntRect PaintInvalidator::ComputeVisualRect( const LayoutObject& object, + const NGPrePaintInfo* pre_paint_info, const PaintInvalidatorContext& context) { if (object.IsSVGChild()) { return context.MapLocalRectToVisualRectForSVGChild( object, SVGLayoutSupport::LocalVisualRect(object)); } - return context.MapLocalRectToVisualRect(object, object.LocalVisualRect()); + PhysicalRect rect; + if (pre_paint_info) { + const NGFragmentChildIterator& iterator = pre_paint_info->iterator; + DCHECK(iterator->BoxFragment() || iterator->FragmentItem()); + if (object.StyleRef().Visibility() == EVisibility::kVisible) { + if (const auto* box_fragment = iterator->BoxFragment()) + rect = box_fragment->InkOverflow(); + else + rect = iterator->FragmentItem()->InkOverflow(); + // The paint offset of text and non-atomic inlines are special; they are + // set to the paint offset of their container. Add the offset to the + // fragment now, to set the correct visual rectangle. + if (!object.IsBox()) + rect.Move(iterator->Link().offset); + } + } else { + rect = object.LocalVisualRect(); + } + return context.MapLocalRectToVisualRect(object, rect); } void PaintInvalidator::UpdatePaintingLayer(const LayoutObject& object, - PaintInvalidatorContext& context) { + PaintInvalidatorContext& context, + bool is_ng_painting) { if (object.HasLayer() && ToLayoutBoxModelObject(object).HasSelfPaintingLayer()) { context.painting_layer = ToLayoutBoxModelObject(object).Layer(); - } else if (object.IsColumnSpanAll() || - object.IsFloatingWithNonContainingBlockParent()) { + } else if (!is_ng_painting && + (object.IsColumnSpanAll() || + object.IsFloatingWithNonContainingBlockParent())) { // See |LayoutObject::PaintingLayer| for the special-cases of floating under // inline and multicolumn. // Post LayoutNG the |LayoutObject::IsFloatingWithNonContainingBlockParent| @@ -164,47 +188,24 @@ void PaintInvalidator::UpdatePaintingLayer(const LayoutObject& object, IsLayoutNGContainingBlock(object.ContainingBlock()))) context.painting_layer->SetNeedsPaintPhaseFloat(); - // Table collapsed borders are painted in PaintPhaseDescendantBlockBackgrounds - // on the table's layer. - if (object.IsTable() && - ToInterface<LayoutNGTableInterface>(object).HasCollapsedBorders()) - context.painting_layer->SetNeedsPaintPhaseDescendantBlockBackgrounds(); - - // The following flags are for descendants of the layer object only. - if (object == context.painting_layer->GetLayoutObject()) - return; - - if (object.IsTableSection()) { - const auto& section = ToInterface<LayoutNGTableSectionInterface>(object); - if (section.TableInterface()->HasColElements()) - context.painting_layer->SetNeedsPaintPhaseDescendantBlockBackgrounds(); - } - - if (object.StyleRef().HasOutline()) + if (object != context.painting_layer->GetLayoutObject() && + object.StyleRef().HasOutline()) context.painting_layer->SetNeedsPaintPhaseDescendantOutlines(); - - if (object.HasBoxDecorationBackground() - // We also paint non-overlay overflow controls in background phase. - || (object.HasOverflowClip() && ToLayoutBox(object) - .GetScrollableArea() - ->HasNonOverlayOverflowControls())) { - context.painting_layer->SetNeedsPaintPhaseDescendantBlockBackgrounds(); - } else { - // Hit testing rects for touch action paint in the background phase. - if (object.HasEffectiveAllowedTouchAction()) - context.painting_layer->SetNeedsPaintPhaseDescendantBlockBackgrounds(); - } } void PaintInvalidator::UpdatePaintInvalidationContainer( const LayoutObject& object, - PaintInvalidatorContext& context) { + PaintInvalidatorContext& context, + bool is_ng_painting) { + if (RuntimeEnabledFeatures::CompositeAfterPaintEnabled()) + return; + if (object.IsPaintInvalidationContainer()) { context.paint_invalidation_container = ToLayoutBoxModelObject(&object); if (object.StyleRef().IsStackingContext() || object.IsSVGRoot()) context.paint_invalidation_container_for_stacked_contents = ToLayoutBoxModelObject(&object); - } else if (object.IsLayoutView()) { + } else if (IsA<LayoutView>(object)) { // paint_invalidation_container_for_stacked_contents is only for stacked // descendants in its own frame, because it doesn't establish stacking // context for stacked contents in sub-frames. @@ -213,8 +214,9 @@ void PaintInvalidator::UpdatePaintInvalidationContainer( context.paint_invalidation_container_for_stacked_contents = context.paint_invalidation_container = &object.ContainerForPaintInvalidation(); - } else if (object.IsColumnSpanAll() || - object.IsFloatingWithNonContainingBlockParent()) { + } else if (!is_ng_painting && + (object.IsColumnSpanAll() || + object.IsFloatingWithNonContainingBlockParent())) { // In these cases, the object may belong to an ancestor of the current // paint invalidation container, in paint order. // Post LayoutNG the |LayoutObject::IsFloatingWithNonContainingBlockParent| @@ -265,16 +267,27 @@ void PaintInvalidator::UpdatePaintInvalidationContainer( } void PaintInvalidator::UpdateVisualRect(const LayoutObject& object, + const NGPrePaintInfo* pre_paint_info, FragmentData& fragment_data, PaintInvalidatorContext& context) { if (!context.NeedsVisualRectUpdate(object)) return; DCHECK(context.tree_builder_context_); - DCHECK(context.tree_builder_context_->current.paint_offset == - fragment_data.PaintOffset()); - - fragment_data.SetVisualRect(ComputeVisualRect(object, context)); + DCHECK(!pre_paint_info || &fragment_data == &pre_paint_info->fragment_data); + + IntRect visual_rect = ComputeVisualRect(object, pre_paint_info, context); + if (pre_paint_info && !object.IsBox()) { + DCHECK(object.IsInline()); + // Text and non-atomic inlines share the same FragmentData object per block + // fragment, and their FragmentData objects are reset when visiting their + // first fragment. So just add to the visual rectangle. + visual_rect.Unite(fragment_data.VisualRect()); + } else { + DCHECK_EQ(context.tree_builder_context_->current.paint_offset, + fragment_data.PaintOffset()); + } + fragment_data.SetVisualRect(visual_rect); object.GetFrameView()->GetLayoutShiftTracker().NotifyObjectPrePaint( object, @@ -286,7 +299,7 @@ void PaintInvalidator::UpdateVisualRect(const LayoutObject& object, // it has was inherited from the parent frame, and movements of a // frame relative to its parent are tracked in the parent frame's // LayoutShiftTracker, not the child frame's. - object.IsLayoutView() + IsA<LayoutView>(object) ? FloatSize() : context.tree_builder_context_->paint_offset_delta); } @@ -315,13 +328,14 @@ void PaintInvalidator::UpdateEmptyVisualRectFlag( bool PaintInvalidator::InvalidatePaint( const LayoutObject& object, + const NGPrePaintInfo* pre_paint_info, const PaintPropertyTreeBuilderContext* tree_builder_context, PaintInvalidatorContext& context) { TRACE_EVENT1(TRACE_DISABLED_BY_DEFAULT("blink.invalidation"), "PaintInvalidator::InvalidatePaint()", "object", object.DebugName().Ascii()); - if (object.IsSVGHiddenContainer()) + if (object.IsSVGHiddenContainer() || object.IsLayoutTableCol()) context.subtree_flags |= PaintInvalidatorContext::kSubtreeNoInvalidation; if (context.subtree_flags & PaintInvalidatorContext::kSubtreeNoInvalidation) @@ -329,8 +343,9 @@ bool PaintInvalidator::InvalidatePaint( object.GetMutableForPainting().EnsureIsReadyForPaintInvalidation(); - UpdatePaintingLayer(object, context); - UpdatePaintInvalidationContainer(object, context); + UpdatePaintingLayer(object, context, /* is_ng_painting */ !!pre_paint_info); + UpdatePaintInvalidationContainer(object, context, + /* is_ng_painting */ !!pre_paint_info); UpdateEmptyVisualRectFlag(object, context); if (!object.ShouldCheckForPaintInvalidation() && !context.NeedsSubtreeWalk()) @@ -346,38 +361,72 @@ bool PaintInvalidator::InvalidatePaint( object.GetFrameView()->SetForeignLayerListNeedsUpdate(); } - unsigned tree_builder_index = 0; + if (pre_paint_info) { + FragmentData& fragment_data = pre_paint_info->fragment_data; + if (!object.IsBox()) { + // Text and non-atomic inlines may generate multiple physical fragments, + // and we're updating the VisualRect in the FragmentData as we visit each + // of them. As such, the current VisualRect in FragmentData is possibly + // incomplete, so letting anyone use it for comparisons is meaningless. + // TODO(crbug.com/1043787): Fix this. The way we use FragmentData for + // non-atomic inlines is not ideal for how LayoutNG works. + context.old_visual_rect = IntRect(); + } else { + context.old_visual_rect = fragment_data.VisualRect(); + } + context.fragment_data = &fragment_data; - for (auto* fragment_data = &object.GetMutableForPainting().FirstFragment(); - fragment_data; - fragment_data = fragment_data->NextFragment(), tree_builder_index++) { - context.old_visual_rect = fragment_data->VisualRect(); - context.fragment_data = fragment_data; +#if DCHECK_IS_ON() + context.tree_builder_context_actually_needed_ = + tree_builder_context && tree_builder_context->is_actually_needed; +#endif + if (tree_builder_context) { + DCHECK_EQ(tree_builder_context->fragments.size(), 1u); + context.tree_builder_context_ = &tree_builder_context->fragments[0]; + context.old_paint_offset = + context.tree_builder_context_->old_paint_offset; + } else { + context.tree_builder_context_ = nullptr; + context.old_paint_offset = fragment_data.PaintOffset(); + } + + UpdateVisualRect(object, pre_paint_info, fragment_data, context); + object.InvalidatePaint(context); + } else { + unsigned tree_builder_index = 0; - DCHECK(!tree_builder_context || - tree_builder_index < tree_builder_context->fragments.size()); + for (auto* fragment_data = &object.GetMutableForPainting().FirstFragment(); + fragment_data; + fragment_data = fragment_data->NextFragment(), tree_builder_index++) { + context.old_visual_rect = fragment_data->VisualRect(); + context.fragment_data = fragment_data; - { + DCHECK(!tree_builder_context || + tree_builder_index < tree_builder_context->fragments.size()); + + { #if DCHECK_IS_ON() - context.tree_builder_context_actually_needed_ = - tree_builder_context && tree_builder_context->is_actually_needed; - FindObjectVisualRectNeedingUpdateScope finder(object, *fragment_data, - context); + context.tree_builder_context_actually_needed_ = + tree_builder_context && tree_builder_context->is_actually_needed; + FindObjectVisualRectNeedingUpdateScope finder(object, *fragment_data, + context); #endif - if (tree_builder_context) { - context.tree_builder_context_ = - &tree_builder_context->fragments[tree_builder_index]; - context.old_paint_offset = - context.tree_builder_context_->old_paint_offset; - } else { - context.tree_builder_context_ = nullptr; - context.old_paint_offset = fragment_data->PaintOffset(); + if (tree_builder_context) { + context.tree_builder_context_ = + &tree_builder_context->fragments[tree_builder_index]; + context.old_paint_offset = + context.tree_builder_context_->old_paint_offset; + } else { + context.tree_builder_context_ = nullptr; + context.old_paint_offset = fragment_data->PaintOffset(); + } + + UpdateVisualRect(object, /* pre_paint_info */ nullptr, *fragment_data, + context); } - UpdateVisualRect(object, *fragment_data, context); + object.InvalidatePaint(context); } - - object.InvalidatePaint(context); } auto reason = static_cast<const DisplayItemClient&>(object) diff --git a/chromium/third_party/blink/renderer/core/paint/paint_invalidator.h b/chromium/third_party/blink/renderer/core/paint/paint_invalidator.h index a8aed714d64..85e7d672bef 100644 --- a/chromium/third_party/blink/renderer/core/paint/paint_invalidator.h +++ b/chromium/third_party/blink/renderer/core/paint/paint_invalidator.h @@ -152,6 +152,7 @@ class PaintInvalidator { public: // Returns true if the object is invalidated. bool InvalidatePaint(const LayoutObject&, + const NGPrePaintInfo*, const PaintPropertyTreeBuilderContext*, PaintInvalidatorContext&); @@ -164,14 +165,18 @@ class PaintInvalidator { friend class PrePaintTreeWalk; ALWAYS_INLINE IntRect ComputeVisualRect(const LayoutObject&, + const NGPrePaintInfo*, const PaintInvalidatorContext&); ALWAYS_INLINE void UpdatePaintingLayer(const LayoutObject&, - PaintInvalidatorContext&); + PaintInvalidatorContext&, + bool is_ng_painting); ALWAYS_INLINE void UpdatePaintInvalidationContainer(const LayoutObject&, - PaintInvalidatorContext&); + PaintInvalidatorContext&, + bool is_ng_painting); ALWAYS_INLINE void UpdateEmptyVisualRectFlag(const LayoutObject&, PaintInvalidatorContext&); ALWAYS_INLINE void UpdateVisualRect(const LayoutObject&, + const NGPrePaintInfo*, FragmentData&, PaintInvalidatorContext&); 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 b480f2e1c35..0d52eb19e02 100644 --- a/chromium/third_party/blink/renderer/core/paint/paint_layer.cc +++ b/chromium/third_party/blink/renderer/core/paint/paint_layer.cc @@ -77,6 +77,7 @@ #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/filter_effect_builder.h" +#include "third_party/blink/renderer/core/paint/ng/ng_box_fragment_painter.h" #include "third_party/blink/renderer/core/paint/object_paint_invalidator.h" #include "third_party/blink/renderer/core/paint/paint_info.h" #include "third_party/blink/renderer/core/paint/paint_layer_paint_order_iterator.h" @@ -92,6 +93,7 @@ #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/heap/heap.h" #include "third_party/blink/renderer/platform/instrumentation/tracing/trace_event.h" #include "third_party/blink/renderer/platform/runtime_enabled_features.h" #include "third_party/blink/renderer/platform/transforms/transformation_matrix.h" @@ -138,7 +140,7 @@ PaintLayerRareData::PaintLayerRareData() PaintLayerRareData::~PaintLayerRareData() = default; PaintLayer::PaintLayer(LayoutBoxModelObject& layout_object) - : is_root_layer_(layout_object.IsLayoutView()), + : is_root_layer_(IsA<LayoutView>(layout_object)), has_visible_content_(false), needs_descendant_dependent_flags_update_(true), needs_visual_overflow_recalc_(true), @@ -161,7 +163,6 @@ PaintLayer::PaintLayer(LayoutBoxModelObject& layout_object) previous_paint_result_(kFullyPainted), needs_paint_phase_descendant_outlines_(false), needs_paint_phase_float_(false), - needs_paint_phase_descendant_block_backgrounds_(false), has_descendant_with_clip_path_(false), has_non_isolated_descendant_with_blend_mode_(false), has_fixed_position_descendant_(false), @@ -307,12 +308,9 @@ PhysicalOffset PaintLayer::SubpixelAccumulation() const { } void PaintLayer::SetSubpixelAccumulation(const PhysicalOffset& accumulation) { - if (rare_data_ || !accumulation.IsZero()) { + DCHECK(!RuntimeEnabledFeatures::CompositeAfterPaintEnabled()); + if (rare_data_ || !accumulation.IsZero()) EnsureRareData().subpixel_accumulation = accumulation; - if (PaintLayerScrollableArea* scrollable_area = GetScrollableArea()) { - scrollable_area->PositionOverflowControls(); - } - } } void PaintLayer::UpdateLayerPositionsAfterLayout() { @@ -411,7 +409,7 @@ void PaintLayer::UpdateTransformationMatrix() { ComputedStyle::kIncludeIndependentTransformProperties); MakeMatrixRenderable( *transform, - Compositor() ? Compositor()->HasAcceleratedCompositing() : false); + box->GetDocument().GetSettings()->GetAcceleratedCompositingEnabled()); } } @@ -690,8 +688,16 @@ void PaintLayer::UpdateDescendantDependentFlags() { UpdateStackingNode(); if (old_has_non_isolated_descendant_with_blend_mode != - static_cast<bool>(has_non_isolated_descendant_with_blend_mode_)) + static_cast<bool>(has_non_isolated_descendant_with_blend_mode_)) { + // The LayoutView DisplayItemClient owns painting of the background + // of the HTML element. When blending isolation of the HTML element's + // descendants change, there will be an addition or removal of an + // isolation effect node for the HTML element to add (or remove) + // isolated blending, and that case we need to re-paint the LayoutView. + if (Parent() && Parent()->IsRootLayer()) + GetLayoutObject().View()->SetBackgroundNeedsFullPaintInvalidation(); GetLayoutObject().SetNeedsPaintPropertyUpdate(); + } needs_descendant_dependent_flags_update_ = false; if (IsSelfPaintingLayer() && needs_visual_overflow_recalc_) { @@ -859,19 +865,6 @@ void PaintLayer::UpdateSizeAndScrollingAfterLayout() { } } -TransformationMatrix PaintLayer::PerspectiveTransform() const { - if (!GetLayoutObject().HasTransformRelatedProperty()) - return TransformationMatrix(); - - const ComputedStyle& style = GetLayoutObject().StyleRef(); - if (!style.HasPerspective()) - return TransformationMatrix(); - - TransformationMatrix t; - t.ApplyPerspective(style.Perspective()); - return t; -} - FloatPoint PaintLayer::PerspectiveOrigin() const { if (!GetLayoutObject().HasTransformRelatedProperty()) return FloatPoint(); @@ -1027,14 +1020,15 @@ PaintLayer* PaintLayer::EnclosingLayerForPaintInvalidation() const { return nullptr; } -void PaintLayer::SetNeedsCompositingInputsUpdate() { +void PaintLayer::SetNeedsCompositingInputsUpdate(bool mark_ancestor_flags) { SetNeedsCompositingInputsUpdateInternal(); // TODO(chrishtr): These are a bit of a heavy hammer, because not all // things which require compositing inputs update require a descendant- // dependent flags update. Reduce call sites after CAP launch allows /// removal of CompositingInputsUpdater. - MarkAncestorChainForFlagsUpdate(NeedsDescendantDependentUpdate); + if (mark_ancestor_flags) + MarkAncestorChainForFlagsUpdate(NeedsDescendantDependentUpdate); } void PaintLayer::SetNeedsVisualOverflowRecalc() { @@ -1083,6 +1077,8 @@ void PaintLayer::SetNeedsCompositingInputsUpdateInternal() { last_ancestor = current; current->child_needs_compositing_inputs_update_ = true; if (Compositor() && + (current != initial_layer || + !current->GetLayoutObject().IsStickyPositioned()) && current->GetLayoutObject().ShouldApplyStrictContainment()) break; } @@ -1175,106 +1171,6 @@ bool PaintLayer::HasAncestorWithFilterThatMovesPixels() const { return false; } -static void ExpandClipRectForDescendants( - PhysicalRect& clip_rect, - const PaintLayer* layer, - const PaintLayer* root_layer, - PaintLayer::TransparencyClipBoxBehavior transparency_behavior, - const PhysicalOffset& sub_pixel_accumulation, - GlobalPaintFlags global_paint_flags) { - // If we have a mask, then the clip is limited to the border box area (and - // there is no need to examine child layers). - if (!layer->GetLayoutObject().HasMask()) { - // Note: we don't have to walk z-order lists since transparent elements - // always establish a stacking container. This means we can just walk the - // layer tree directly. - for (PaintLayer* curr = layer->FirstChild(); curr; - curr = curr->NextSibling()) - clip_rect.Unite(PaintLayer::TransparencyClipBox( - curr, root_layer, transparency_behavior, - PaintLayer::kDescendantsOfTransparencyClipBox, sub_pixel_accumulation, - global_paint_flags)); - } -} - -PhysicalRect PaintLayer::TransparencyClipBox( - const PaintLayer* layer, - const PaintLayer* root_layer, - TransparencyClipBoxBehavior transparency_behavior, - TransparencyClipBoxMode transparency_mode, - const PhysicalOffset& sub_pixel_accumulation, - GlobalPaintFlags global_paint_flags) { - // FIXME: Although this function completely ignores CSS-imposed clipping, we - // did already intersect with the paintDirtyRect, and that should cut down on - // the amount we have to paint. Still it would be better to respect clips. - - if (root_layer != layer && - ((transparency_behavior == kPaintingTransparencyClipBox && - layer->PaintsWithTransform(global_paint_flags)) || - (transparency_behavior == kHitTestingTransparencyClipBox && - layer->HasTransformRelatedProperty()))) { - // The best we can do here is to use enclosed bounding boxes to establish a - // "fuzzy" enough clip to encompass the transformed layer and all of its - // children. - const PaintLayer* pagination_layer = - transparency_mode == kDescendantsOfTransparencyClipBox - ? layer->EnclosingPaginationLayer() - : nullptr; - const PaintLayer* root_layer_for_transform = - pagination_layer ? pagination_layer : root_layer; - PhysicalOffset delta; - layer->ConvertToLayerCoords(root_layer_for_transform, delta); - - delta += sub_pixel_accumulation; - IntPoint pixel_snapped_delta = RoundedIntPoint(delta); - TransformationMatrix transform; - transform.Translate(pixel_snapped_delta.X(), pixel_snapped_delta.Y()); - if (layer->Transform()) - transform = transform * *layer->Transform(); - - // We don't use fragment boxes when collecting a transformed layer's - // bounding box, since it always paints unfragmented. - PhysicalRect clip_rect = layer->LocalBoundingBox(); - ExpandClipRectForDescendants(clip_rect, layer, layer, transparency_behavior, - sub_pixel_accumulation, global_paint_flags); - PhysicalRect result = PhysicalRect::EnclosingRect( - transform.MapRect(layer->MapRectForFilter(FloatRect(clip_rect)))); - if (!pagination_layer) - return result; - - // We have to break up the transformed extent across our columns. - // Split our box up into the actual fragment boxes that layout in the - // columns/pages and unite those together to get our true bounding box. - LayoutFlowThread& enclosing_flow_thread = - ToLayoutFlowThread(pagination_layer->GetLayoutObject()); - result = PhysicalRectToBeNoop( - enclosing_flow_thread.FragmentsBoundingBox(result.ToLayoutRect())); - - PhysicalOffset root_layer_delta; - pagination_layer->ConvertToLayerCoords(root_layer, root_layer_delta); - result.Move(root_layer_delta); - return result; - } - - PhysicalRect clip_rect = layer->ShouldFragmentCompositedBounds(root_layer) - ? layer->FragmentsBoundingBox(root_layer) - : layer->PhysicalBoundingBox(root_layer); - ExpandClipRectForDescendants(clip_rect, layer, root_layer, - transparency_behavior, sub_pixel_accumulation, - global_paint_flags); - - // Convert clipRect into local coordinates for mapLayerRectForFilter(), and - // convert back after. - PhysicalOffset delta; - layer->ConvertToLayerCoords(root_layer, delta); - clip_rect.Move(-delta); - clip_rect = layer->MapRectForFilter(clip_rect); - clip_rect.Move(delta); - - clip_rect.Move(sub_pixel_accumulation); - return clip_rect; -} - void* PaintLayer::operator new(size_t sz) { return WTF::Partitions::LayoutPartition()->Alloc( sz, WTF_HEAP_PROFILER_TYPE_NAME(PaintLayer)); @@ -1367,6 +1263,9 @@ void PaintLayer::RemoveChild(PaintLayer* old_child) { if (Compositor()) { if (!old_child_style.IsStacked()) Compositor()->SetNeedsCompositingUpdate(kCompositingUpdateRebuildTree); + + if (Compositor()->GetCompositingInputsRoot() == old_child) + Compositor()->ClearCompositingInputsRoot(); } // Dirty the z-order list in which we are contained. old_child->DirtyStackingContextZOrderLists(); @@ -1475,7 +1374,7 @@ void PaintLayer::InsertOnlyThisLayerAfterStyleChange() { // and its descendants to change paint invalidation container. bool did_set_paint_invalidation = false; if (!RuntimeEnabledFeatures::CompositeAfterPaintEnabled() && - !GetLayoutObject().IsLayoutView() && GetLayoutObject().IsRooted() && + !IsA<LayoutView>(GetLayoutObject()) && GetLayoutObject().IsRooted() && GetLayoutObject().StyleRef().IsStacked()) { const LayoutBoxModelObject& previous_paint_invalidation_container = GetLayoutObject().Parent()->ContainerForPaintInvalidation(); @@ -1639,16 +1538,20 @@ bool PaintLayer::RequiresScrollableArea() const { void PaintLayer::UpdateScrollableArea() { if (RequiresScrollableArea() && !scrollable_area_) { - scrollable_area_ = PaintLayerScrollableArea::Create(*this); + scrollable_area_ = MakeGarbageCollected<PaintLayerScrollableArea>(*this); if (Compositor()) { Compositor()->SetNeedsCompositingUpdate(kCompositingUpdateRebuildTree); } + GetLayoutObject().SetNeedsPaintPropertyUpdate(); } else if (!RequiresScrollableArea() && scrollable_area_) { scrollable_area_->Dispose(); scrollable_area_.Clear(); if (Compositor()) { Compositor()->SetNeedsCompositingUpdate(kCompositingUpdateRebuildTree); } + GetLayoutObject().SetBackgroundPaintLocation( + kBackgroundPaintInGraphicsLayer); + GetLayoutObject().SetNeedsPaintPropertyUpdate(); } } @@ -1787,6 +1690,16 @@ void PaintLayer::CollectFragments( fragment.root_fragment_data = root_fragment_data; fragment.fragment_data = fragment_data; + if (GetLayoutObject().CanTraversePhysicalFragments()) { + if (const auto* block = DynamicTo<LayoutBlock>(&GetLayoutObject())) { + fragment.physical_fragment = block->CurrentFragment(); + DCHECK(fragment.physical_fragment); + + // TODO(mstensho): Implement support for multiple fragments per node. + DCHECK(!fragment_data->NextFragment()); + } + } + fragments.push_back(fragment); } } @@ -2052,7 +1965,7 @@ PaintLayer* PaintLayer::HitTestLayer(PaintLayer* root_layer, // The natural thing would be to keep HitTestingTransformState on the stack, // but it's big, so we heap-allocate. HitTestingTransformState* local_transform_state = nullptr; - base::Optional<HitTestingTransformState> storage; + STACK_UNINITIALIZED base::Optional<HitTestingTransformState> storage; if (applied_transform) { // We computed the correct state in the caller (above code), so just @@ -2071,7 +1984,7 @@ PaintLayer* PaintLayer::HitTestLayer(PaintLayer* root_layer, // Check for hit test on backface if backface-visibility is 'hidden' if (local_transform_state && layout_object.StyleRef().BackfaceVisibility() == EBackfaceVisibility::kHidden) { - TransformationMatrix inverted_matrix = + STACK_UNINITIALIZED TransformationMatrix inverted_matrix = local_transform_state->accumulated_transform_.Inverse(); // If the z-vector of the matrix is negative, the back is facing towards the // viewer. @@ -2080,7 +1993,8 @@ PaintLayer* PaintLayer::HitTestLayer(PaintLayer* root_layer, } HitTestingTransformState* unflattened_transform_state = local_transform_state; - base::Optional<HitTestingTransformState> unflattened_storage; + STACK_UNINITIALIZED base::Optional<HitTestingTransformState> + unflattened_storage; if (local_transform_state && !Preserves3D()) { // Keep a copy of the pre-flattening state, for computing z-offsets for the // container @@ -2111,7 +2025,7 @@ PaintLayer* PaintLayer::HitTestLayer(PaintLayer* root_layer, // Collect the fragments. This will compute the clip rectangles for each // layer fragment. - base::Optional<PaintLayerFragments> layer_fragments; + STACK_UNINITIALIZED base::Optional<PaintLayerFragments> layer_fragments; if (recursion_data.intersects_location) { layer_fragments.emplace(); if (applied_transform) { @@ -2189,8 +2103,8 @@ PaintLayer* PaintLayer::HitTestLayer(PaintLayer* root_layer, DisplayLockLifecycleTarget::kChildren)) { // Hit test with a temporary HitTestResult, because we only want to commit // to 'result' if we know we're frontmost. - HitTestResult temp_result(result.GetHitTestRequest(), - recursion_data.original_location); + STACK_UNINITIALIZED HitTestResult temp_result( + result.GetHitTestRequest(), recursion_data.original_location); temp_result.SetInertNode(result.InertNode()); bool inside_fragment_foreground_rect = false; @@ -2237,8 +2151,8 @@ PaintLayer* PaintLayer::HitTestLayer(PaintLayer* root_layer, return candidate_layer; if (recursion_data.intersects_location && IsSelfPaintingLayer()) { - HitTestResult temp_result(result.GetHitTestRequest(), - recursion_data.original_location); + STACK_UNINITIALIZED HitTestResult temp_result( + result.GetHitTestRequest(), recursion_data.original_location); temp_result.SetInertNode(result.InertNode()); bool inside_fragment_background_rect = false; if (HitTestContentsForFragments(*layer_fragments, offset, temp_result, @@ -2286,8 +2200,8 @@ bool PaintLayer::HitTestContentsForFragments( inside_clip_rect = true; PhysicalOffset fragment_offset = offset; fragment_offset += fragment.layer_bounds.offset; - if (HitTestContents(result, fragment_offset, hit_test_location, - hit_test_filter)) + if (HitTestContents(result, fragment.physical_fragment, fragment_offset, + hit_test_location, hit_test_filter)) return true; } @@ -2321,7 +2235,7 @@ PaintLayer* PaintLayer::HitTestTransformedLayerInFragments( PaintLayer* hit_layer = HitTestLayerByApplyingTransform( root_layer, container_layer, result, recursion_data, transform_state, z_offset, check_resizer_only, - fragment.fragment_data->PaginationOffset()); + fragment.fragment_data->LegacyPaginationOffset()); if (hit_layer) return hit_layer; } @@ -2370,12 +2284,23 @@ PaintLayer* PaintLayer::HitTestLayerByApplyingTransform( } bool PaintLayer::HitTestContents(HitTestResult& result, + const NGPhysicalBoxFragment* physical_fragment, const PhysicalOffset& fragment_offset, const HitTestLocation& hit_test_location, HitTestFilter hit_test_filter) const { DCHECK(IsSelfPaintingLayer() || HasSelfPaintingLayerDescendant()); - if (!GetLayoutObject().HitTestAllPhases(result, hit_test_location, - fragment_offset, hit_test_filter)) { + + bool did_hit; + if (physical_fragment) { + did_hit = NGBoxFragmentPainter(*physical_fragment) + .HitTestAllPhases(result, hit_test_location, fragment_offset, + hit_test_filter); + } else { + did_hit = GetLayoutObject().HitTestAllPhases( + result, hit_test_location, fragment_offset, hit_test_filter); + } + + if (!did_hit) { // It's wrong to set innerNode, but then claim that you didn't hit anything, // unless it is a list-based test. DCHECK(!result.InnerNode() || (result.GetHitTestRequest().ListBased() && @@ -2469,8 +2394,8 @@ PaintLayer* PaintLayer::HitTestChildren( } PaintLayer* hit_layer = nullptr; - HitTestResult temp_result(result.GetHitTestRequest(), - recursion_data.original_location); + STACK_UNINITIALIZED HitTestResult temp_result( + result.GetHitTestRequest(), recursion_data.original_location); temp_result.SetInertNode(result.InertNode()); hit_layer = child_layer->HitTestLayer( root_layer, this, temp_result, recursion_data, false, transform_state, @@ -2497,7 +2422,7 @@ PaintLayer* PaintLayer::HitTestChildren( } void PaintLayer::UpdateFilterReferenceBox() { - if (!NeedsFilterReferenceBox()) + if (!HasFilterThatMovesPixels()) return; FloatRect reference_box = FloatRect(PhysicalBoundingBoxIncludingStackingChildren( @@ -2511,16 +2436,6 @@ void PaintLayer::UpdateFilterReferenceBox() { EnsureResourceInfo().SetFilterReferenceBox(reference_box); } -bool PaintLayer::NeedsFilterReferenceBox() const { - if (GetLayoutObject().HasReflection() && GetLayoutObject().IsBox()) - return true; - FilterOperations operations = GetLayoutObject().StyleRef().Filter(); - if (operations.HasBlurOrReferenceFilter()) - return true; - operations = GetLayoutObject().StyleRef().BackdropFilter(); - return !operations.IsEmpty(); -} - FloatRect PaintLayer::FilterReferenceBox() const { DCHECK(IsAllowedToQueryCompositingState()); if (ResourceInfo()) @@ -2571,13 +2486,9 @@ bool PaintLayer::HitTestClippedOutByClipPath( return !clip_path->GetPath(reference_box).Contains(point); } DCHECK_EQ(clip_path_operation->GetType(), ClipPathOperation::REFERENCE); - SVGResource* resource = - To<ReferenceClipPathOperation>(*clip_path_operation).Resource(); - LayoutSVGResourceContainer* container = - resource ? resource->ResourceContainer() : nullptr; - if (!container || container->ResourceType() != kClipperResourceType) + LayoutSVGResourceClipper* clipper = GetSVGResourceAsType(clip_path_operation); + if (!clipper) return false; - auto* clipper = ToLayoutSVGResourceClipper(container); // If the clipPath is using "userspace on use" units, then the origin of // the coordinate system is the top-left of the reference box, so adjust // the point accordingly. @@ -2678,6 +2589,14 @@ void PaintLayer::ExpandRectForStackingChildren( const PaintLayer& composited_layer, PhysicalRect& result, PaintLayer::CalculateBoundsOptions options) const { + // If we're locked, th en the subtree does not contribute painted output. + // Furthermore, we might not have up-to-date sizing and position information + // in the subtree, so skip recursing into the subtree. + if (GetLayoutObject().PaintBlockedByDisplayLock( + DisplayLockLifecycleTarget::kChildren)) { + return; + } + PaintLayerPaintOrderIterator iterator(*this, kAllChildren); while (PaintLayer* child_layer = iterator.Next()) { // Here we exclude both directly composited layers and squashing layers @@ -2928,6 +2847,12 @@ bool PaintLayer::SupportsSubsequenceCaching() const { if (GetLayoutObject().IsSVGRoot()) return true; + // Don't create subsequence for the document element because the subsequence + // for LayoutView serves the same purpose. This can avoid unnecessary paint + // chunks that would otherwise be forced by the subsequence. + if (GetLayoutObject().IsDocumentElement()) + return false; + // Create subsequence for only stacking contexts whose painting are atomic. return GetLayoutObject().StyleRef().IsStackingContext(); } @@ -3438,7 +3363,7 @@ bool PaintLayer::HasFilterThatMovesPixels() const { const ComputedStyle& style = GetLayoutObject().StyleRef(); if (style.HasFilter() && style.Filter().HasFilterThatMovesPixels()) return true; - if (style.HasBoxReflect()) + if (GetLayoutObject().HasReflection()) return true; return false; } 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 29b9f163f4a..98a46602c6d 100644 --- a/chromium/third_party/blink/renderer/core/paint/paint_layer.h +++ b/chromium/third_party/blink/renderer/core/paint/paint_layer.h @@ -315,6 +315,9 @@ class CORE_EXPORT PaintLayer : public DisplayItemClient { // FIXME: size() should DCHECK(!needs_position_update_) as well, but that // fails in some tests, for example, fast/repaint/clipped-relative.html. const LayoutSize& Size() const { return size_; } + // TODO(crbug.com/962299): This method snaps to pixels incorrectly because + // Location() is not the correct paint offset. It's also incorrect in flipped + // blocks writing mode. IntSize PixelSnappedSize() const { LayoutPoint location = layout_object_.IsBox() ? ToLayoutBox(layout_object_).Location() @@ -506,10 +509,6 @@ class CORE_EXPORT PaintLayer : public DisplayItemClient { TransformationMatrix CurrentTransform() const; TransformationMatrix RenderableTransform(GlobalPaintFlags) const; - // Get the perspective transform, which is applied to transformed sublayers. - // Returns true if the layer has a -webkit-perspective. - // Note that this transform does not have the perspective-origin baked in. - TransformationMatrix PerspectiveTransform() const; FloatPoint PerspectiveOrigin() const; bool Preserves3D() const { return GetLayoutObject().StyleRef().Preserves3D(); @@ -793,8 +792,11 @@ class CORE_EXPORT PaintLayer : public DisplayItemClient { bool is_under_position_sticky = false; }; + bool NeedsVisualOverflowRecalc() const { + return needs_visual_overflow_recalc_; + } void SetNeedsVisualOverflowRecalc(); - void SetNeedsCompositingInputsUpdate(); + void SetNeedsCompositingInputsUpdate(bool mark_ancestor_flags = true); // This methods marks everything from this layer up to the |ancestor| argument // (both included). @@ -975,24 +977,6 @@ class CORE_EXPORT PaintLayer : public DisplayItemClient { const PhysicalOffset* offset_from_root = nullptr, const PhysicalOffset& sub_pixel_accumulation = PhysicalOffset()) const; - enum TransparencyClipBoxBehavior { - kPaintingTransparencyClipBox, - kHitTestingTransparencyClipBox - }; - - enum TransparencyClipBoxMode { - kDescendantsOfTransparencyClipBox, - kRootOfTransparencyClipBox - }; - - static PhysicalRect TransparencyClipBox( - const PaintLayer*, - const PaintLayer* root_layer, - TransparencyClipBoxBehavior transparency_behavior, - TransparencyClipBoxMode transparency_mode, - const PhysicalOffset& sub_pixel_accumulation, - GlobalPaintFlags = kGlobalPaintNormalPhase); - bool SelfNeedsRepaint() const { return self_needs_repaint_; } bool DescendantNeedsRepaint() const { return descendant_needs_repaint_; } bool SelfOrDescendantNeedsRepaint() const { @@ -1037,15 +1021,6 @@ class CORE_EXPORT PaintLayer : public DisplayItemClient { needs_paint_phase_float_ = true; } - // Similar to above, but for PaintPhaseDescendantBlockBackgroundsOnly. - bool NeedsPaintPhaseDescendantBlockBackgrounds() const { - return needs_paint_phase_descendant_block_backgrounds_; - } - void SetNeedsPaintPhaseDescendantBlockBackgrounds() { - DCHECK(IsSelfPaintingLayer()); - needs_paint_phase_descendant_block_backgrounds_ = true; - } - bool DescendantHasDirectOrScrollingCompositingReason() const { return descendant_has_direct_or_scrolling_compositing_reason_; } @@ -1123,10 +1098,6 @@ class CORE_EXPORT PaintLayer : public DisplayItemClient { void DirtyStackingContextZOrderLists(); - bool NeedsVisualOverflowRecalcForTesting() const { - return needs_visual_overflow_recalc_; - } - PhysicalOffset OffsetForInFlowRelPosition() const { return rare_data_ ? rare_data_->offset_for_in_flow_rel_position : PhysicalOffset(); @@ -1198,6 +1169,7 @@ class CORE_EXPORT PaintLayer : public DisplayItemClient { const PhysicalOffset& translation_offset = PhysicalOffset()) const; bool HitTestContents(HitTestResult&, + const NGPhysicalBoxFragment*, const PhysicalOffset& fragment_offset, const HitTestLocation&, HitTestFilter) const; @@ -1264,8 +1236,6 @@ class CORE_EXPORT PaintLayer : public DisplayItemClient { needs_paint_phase_descendant_outlines_ |= layer.needs_paint_phase_descendant_outlines_; needs_paint_phase_float_ |= layer.needs_paint_phase_float_; - needs_paint_phase_descendant_block_backgrounds_ |= - layer.needs_paint_phase_descendant_block_backgrounds_; } void ExpandRectForStackingChildren(const PaintLayer& composited_layer, @@ -1281,8 +1251,6 @@ class CORE_EXPORT PaintLayer : public DisplayItemClient { bool ShouldApplyTransformToBoundingBox(const PaintLayer& composited_layer, CalculateBoundsOptions) const; - bool NeedsFilterReferenceBox() const; - AncestorDependentCompositingInputs& EnsureAncestorDependentCompositingInputs() const { if (!ancestor_dependent_compositing_inputs_) { @@ -1347,7 +1315,6 @@ class CORE_EXPORT PaintLayer : public DisplayItemClient { unsigned needs_paint_phase_descendant_outlines_ : 1; unsigned needs_paint_phase_float_ : 1; - unsigned needs_paint_phase_descendant_block_backgrounds_ : 1; // These bitfields are part of ancestor/descendant dependent compositing // inputs. 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 661c7c33a8b..6aaa19c0b1d 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 @@ -183,21 +183,12 @@ TEST_F(PaintLayerClipperTest, ControlClip) { .CalculateRects(context, &target_paint_layer->GetLayoutObject().FirstFragment(), nullptr, layer_bounds, background_rect, foreground_rect); -#if defined(OS_MACOSX) - // If the PaintLayer clips overflow, the background rect is intersected with - // the PaintLayer bounds... - EXPECT_EQ(PhysicalRect(3, 4, 210, 28), background_rect.Rect()); - // and the foreground rect is intersected with the control clip in this case. - EXPECT_EQ(PhysicalRect(8, 8, 200, 18), foreground_rect.Rect()); - EXPECT_EQ(PhysicalRect(8, 8, 200, 18), layer_bounds); -#else // If the PaintLayer clips overflow, the background rect is intersected with // the PaintLayer bounds... EXPECT_EQ(PhysicalRect(8, 8, 200, 300), background_rect.Rect()); // and the foreground rect is intersected with the control clip in this case. EXPECT_EQ(PhysicalRect(10, 10, 196, 296), foreground_rect.Rect()); EXPECT_EQ(PhysicalRect(8, 8, 200, 300), layer_bounds); -#endif } TEST_F(PaintLayerClipperTest, RoundedClip) { @@ -302,8 +293,8 @@ TEST_F(PaintLayerClipperTest, ControlClipSelect) { PhysicalRect content_box_rect = target->PhysicalContentBoxRect(); EXPECT_GT(foreground_rect.Rect().X(), content_box_rect.X() + target->Location().X()); - EXPECT_LT(foreground_rect.Rect().Width(), content_box_rect.Width()); -} // namespace blink + EXPECT_LE(foreground_rect.Rect().Width(), content_box_rect.Width()); +} TEST_F(PaintLayerClipperTest, LayoutSVGRootChild) { SetBodyInnerHTML(R"HTML( diff --git a/chromium/third_party/blink/renderer/core/paint/paint_layer_fragment.h b/chromium/third_party/blink/renderer/core/paint/paint_layer_fragment.h index 6b0d2e3a7d7..9ffa07ab179 100644 --- a/chromium/third_party/blink/renderer/core/paint/paint_layer_fragment.h +++ b/chromium/third_party/blink/renderer/core/paint/paint_layer_fragment.h @@ -33,6 +33,7 @@ namespace blink { class FragmentData; +class NGPhysicalBoxFragment; // PaintLayerFragment is the representation of a fragment. // https://drafts.csswg.org/css-break/#fragment @@ -84,6 +85,8 @@ struct PaintLayerFragment { // The corresponding FragmentData of this structure. const FragmentData* fragment_data = nullptr; + + const NGPhysicalBoxFragment* physical_fragment = nullptr; }; typedef Vector<PaintLayerFragment, 1> PaintLayerFragments; 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 7e1b0ab4992..9017869339e 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 @@ -7,21 +7,24 @@ #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_video.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" +#include "third_party/blink/renderer/core/paint/ng/ng_box_fragment_painter.h" #include "third_party/blink/renderer/core/paint/object_paint_properties.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_paint_order_iterator.h" #include "third_party/blink/renderer/core/paint/paint_layer_scrollable_area.h" +#include "third_party/blink/renderer/core/paint/paint_timing_detector.h" #include "third_party/blink/renderer/core/paint/scrollable_area_painter.h" #include "third_party/blink/renderer/platform/geometry/float_point_3d.h" #include "third_party/blink/renderer/platform/graphics/graphics_layer.h" #include "third_party/blink/renderer/platform/graphics/paint/drawing_recorder.h" #include "third_party/blink/renderer/platform/graphics/paint/geometry_mapper.h" #include "third_party/blink/renderer/platform/graphics/paint/scoped_display_item_fragment.h" -#include "third_party/blink/renderer/platform/graphics/paint/scoped_paint_chunk_properties.h" +#include "third_party/blink/renderer/platform/graphics/paint/scoped_paint_chunk_hint.h" #include "third_party/blink/renderer/platform/graphics/paint/subsequence_recorder.h" #include "third_party/blink/renderer/platform/runtime_enabled_features.h" @@ -46,8 +49,6 @@ static ShouldRespectOverflowClipType ShouldRespectOverflowClip( } bool PaintLayerPainter::PaintedOutputInvisible(const ComputedStyle& style) { - DCHECK(!RuntimeEnabledFeatures::CompositeAfterPaintEnabled()); - if (style.HasBackdropFilter()) return false; @@ -75,19 +76,16 @@ PaintResult PaintLayerPainter::Paint( if (paint_layer_.GetLayoutObject().GetFrameView()->ShouldThrottleRendering()) return kFullyPainted; - // https://code.google.com/p/chromium/issues/detail?id=343772 - DisableCompositingQueryAsserts disabler; - // Non self-painting layers without self-painting descendants don't need to be // painted as their layoutObject() should properly paint itself. if (!paint_layer_.IsSelfPaintingLayer() && !paint_layer_.HasSelfPaintingLayerDescendant()) return kFullyPainted; - // If this layer is totally invisible then there is nothing to paint. In CAP - // we simplify this optimization by painting even when effectively invisible - // but skipping the painted content during layerization in - // PaintArtifactCompositor. + // If this layer is totally invisible then there is nothing to paint. + // In CompositeAfterPaint we simplify this optimization by painting even when + // effectively invisible but skipping the painted content during layerization + // in PaintArtifactCompositor. if (paint_layer_.PaintsWithTransparency( painting_info.GetGlobalPaintFlags())) { if (!RuntimeEnabledFeatures::CompositeAfterPaintEnabled() && @@ -108,10 +106,10 @@ PaintResult PaintLayerPainter::Paint( return PaintLayerContents(context, painting_info, paint_flags); } -static bool ShouldCreateSubsequence(const PaintLayer& paint_layer, - const GraphicsContext& context, - const PaintLayerPaintingInfo& painting_info, - PaintLayerFlags paint_flags) { +static bool ShouldCreateSubsequence( + const PaintLayer& paint_layer, + const GraphicsContext& context, + const PaintLayerPaintingInfo& painting_info) { // Caching is not needed during printing or painting previews. if (context.Printing() || context.IsPaintingPreview()) return false; @@ -127,7 +125,8 @@ static bool ShouldCreateSubsequence(const PaintLayer& paint_layer, // CachedDisplayItemList. This also avoids conflict of // PaintLayer::previousXXX() when paintLayer is composited scrolling and is // painted twice for GraphicsLayers of container and scrolling contents. - if (paint_layer.GetCompositingState() == kPaintsIntoOwnBacking) + if (!RuntimeEnabledFeatures::CompositeAfterPaintEnabled() && + paint_layer.GetCompositingState() == kPaintsIntoOwnBacking) return false; // Don't create subsequence during special painting to avoid cache conflict @@ -136,9 +135,6 @@ static bool ShouldCreateSubsequence(const PaintLayer& paint_layer, kGlobalPaintFlattenCompositingLayers) return false; - if (paint_flags & kPaintLayerPaintingOverlayOverflowControls) - return false; - return true; } @@ -196,7 +192,7 @@ 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()) { + if (IsA<LayoutView>(layer.GetLayoutObject())) { const auto* frame = layer.GetLayoutObject().GetFrame(); if (frame && frame->IsMainFrame() && !frame->ClipsContent()) return true; @@ -326,6 +322,15 @@ PaintResult PaintLayerPainter::PaintLayerContents( return kMayBeClippedByCullRect; } + bool selection_drag_image_only = painting_info_arg.GetGlobalPaintFlags() & + kGlobalPaintSelectionDragImageOnly; + if (selection_drag_image_only && !paint_layer_.GetLayoutObject().IsSelected()) + return result; + + base::Optional<IgnorePaintTimingScope> ignore_paint_timing; + if (PaintedOutputInvisible(paint_layer_.GetLayoutObject().StyleRef())) + ignore_paint_timing.emplace(); + PaintLayerFlags paint_flags = paint_flags_arg; PaintLayerPaintingInfo painting_info = painting_info_arg; AdjustForPaintProperties(context, painting_info, paint_flags); @@ -355,15 +360,25 @@ PaintResult PaintLayerPainter::PaintLayerContents( paint_layer_.GetLayoutObject().StyleRef().HasOutline(); PhysicalOffset subpixel_accumulation = - paint_layer_.GetCompositingState() == kPaintsIntoOwnBacking + (!RuntimeEnabledFeatures::CompositeAfterPaintEnabled() && + paint_layer_.GetCompositingState() == kPaintsIntoOwnBacking) ? paint_layer_.SubpixelAccumulation() : 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, paint_flags); + bool should_paint_content = + paint_layer_.HasVisibleContent() && + // Content under a LayoutSVGHiddenContainer is auxiliary resources for + // painting. Foreign content should never paint in this situation, as it + // is primary, not auxiliary. + !paint_layer_.IsUnderSVGHiddenContainer() && is_self_painting_layer && + !is_painting_overlay_overflow_controls; + + bool should_create_subsequence = + should_paint_content && + ShouldCreateSubsequence(paint_layer_, context, painting_info); base::Optional<SubsequenceRecorder> subsequence_recorder; if (should_create_subsequence) { @@ -397,20 +412,13 @@ PaintResult PaintLayerPainter::PaintLayerContents( subpixel_accumulation) : offset_from_root; clip_path_clipper.emplace(context, paint_layer_.GetLayoutObject(), + paint_layer_.GetLayoutObject(), visual_offset_from_root); } PaintLayerPaintingInfo local_painting_info(painting_info); local_painting_info.sub_pixel_accumulation = subpixel_accumulation; - bool should_paint_content = - paint_layer_.HasVisibleContent() && - // Content under a LayoutSVGHiddenContainer is auxiliary resources for - // painting. Foreign content should never paint in this situation, as it - // is primary, not auxiliary. - !paint_layer_.IsUnderSVGHiddenContainer() && is_self_painting_layer && - !is_painting_overlay_overflow_controls; - PaintLayerFragments layer_fragments; if (should_paint_content || should_paint_self_outline || @@ -437,16 +445,10 @@ PaintResult PaintLayerPainter::PaintLayerContents( } } - bool selection_only = - local_painting_info.GetGlobalPaintFlags() & kGlobalPaintSelectionOnly; - { // Begin block for the lifetime of any filter. - size_t display_item_list_size_before_painting = - context.GetPaintController().NewDisplayItemList().size(); - bool is_painting_root_layer = (&paint_layer_) == painting_info.root_layer; bool should_paint_background = - should_paint_content && !selection_only && + should_paint_content && !selection_drag_image_only && (is_painting_composited_background || (is_painting_root_layer && !(paint_flags & kPaintLayerPaintingSkipRootBackground))); @@ -459,46 +461,45 @@ PaintResult PaintLayerPainter::PaintLayerContents( bool should_paint_normal_flow_and_pos_z_order_lists = is_painting_composited_foreground && !is_painting_overlay_overflow_controls; - bool is_video = paint_layer_.GetLayoutObject().IsVideo(); - - base::Optional<ScopedPaintChunkProperties> - subsequence_forced_chunk_properties; - if (subsequence_recorder && paint_layer_.HasSelfPaintingLayerDescendant()) { - // Prepare for forced paint chunks to ensure chunk id stability to avoid - // unnecessary full chunk raster invalidations on changed chunk ids. - // TODO(crbug.com/834606): This may be unnecessary after we refactor - // raster invalidation not to depend on chunk ids too much. - subsequence_forced_chunk_properties.emplace( - context.GetPaintController(), - paint_layer_.GetLayoutObject() - .FirstFragment() - .LocalBorderBoxProperties(), - paint_layer_, DisplayItem::kUninitializedType); + bool is_video = IsA<LayoutVideo>(paint_layer_.GetLayoutObject()); + + base::Optional<ScopedPaintChunkHint> paint_chunk_hint; + if (should_paint_content) { + paint_chunk_hint.emplace(context.GetPaintController(), + paint_layer_.GetLayoutObject() + .FirstFragment() + .LocalBorderBoxProperties(), + paint_layer_, DisplayItem::kLayerChunk); } if (should_paint_background) { - if (subsequence_forced_chunk_properties) { - context.GetPaintController().ForceNewChunk( - paint_layer_, DisplayItem::kLayerChunkBackground); - } PaintBackgroundForFragments(layer_fragments, context, local_painting_info, paint_flags); } if (should_paint_neg_z_order_list) { - if (subsequence_forced_chunk_properties) { - context.GetPaintController().ForceNewChunk( - paint_layer_, DisplayItem::kLayerChunkNegativeZOrderChildren); - } if (PaintChildren(kNegativeZOrderChildren, context, painting_info, paint_flags) == kMayBeClippedByCullRect) result = kMayBeClippedByCullRect; } if (should_paint_own_contents) { - PaintForegroundForFragments( - layer_fragments, context, local_painting_info, selection_only, - !!subsequence_forced_chunk_properties, paint_flags); + base::Optional<ScopedPaintChunkHint> paint_chunk_hint_foreground; + if (paint_chunk_hint && paint_chunk_hint->HasCreatedPaintChunk()) { + // Hint a foreground chunk if we have created any chunks, to give the + // paint chunk after the previous forced paint chunks a stable id. + paint_chunk_hint_foreground.emplace(context.GetPaintController(), + paint_layer_, + DisplayItem::kLayerChunkForeground); + } + if (selection_drag_image_only) { + PaintForegroundForFragmentsWithPhase(PaintPhase::kSelectionDragImage, + layer_fragments, context, + local_painting_info, paint_flags); + } else { + PaintForegroundForFragments(layer_fragments, context, + local_painting_info, paint_flags); + } } if (!is_video && should_paint_self_outline) { @@ -507,11 +508,6 @@ PaintResult PaintLayerPainter::PaintLayerContents( } if (should_paint_normal_flow_and_pos_z_order_lists) { - if (subsequence_forced_chunk_properties) { - context.GetPaintController().ForceNewChunk( - paint_layer_, - DisplayItem::kLayerChunkNormalFlowAndPositiveZOrderChildren); - } if (PaintChildren(kNormalFlowAndPositiveZOrderChildren, context, painting_info, paint_flags) == kMayBeClippedByCullRect) result = kMayBeClippedByCullRect; @@ -532,24 +528,11 @@ PaintResult PaintLayerPainter::PaintLayerContents( PaintSelfOutlineForFragments(layer_fragments, context, local_painting_info, paint_flags); } - - if (!is_painting_overlay_overflow_controls) { - // For filters, if the layer painted nothing, we need to issue a no-op - // display item to ensure the filters won't be ignored. For backdrop - // filters, we issue the display item regardless of other paintings to - // ensure correct bounds of the composited layer for the backdrop filter. - if ((paint_layer_.PaintsWithFilters() && - display_item_list_size_before_painting == - context.GetPaintController().NewDisplayItemList().size()) || - paint_layer_.GetLayoutObject().HasBackdropFilter()) { - PaintEmptyContentForFilters(context); - } - } } // FilterPainter block bool should_paint_mask = is_painting_mask && should_paint_content && paint_layer_.GetLayoutObject().HasMask() && - !selection_only; + !selection_drag_image_only; if (should_paint_mask) { PaintMaskForFragments(layer_fragments, context, local_painting_info, paint_flags); @@ -739,7 +722,10 @@ void PaintLayerPainter::PaintFragmentWithPhase( DisplayLockLifecycleTarget::kChildren))) { paint_info.SetDescendantPaintingBlocked(true); } - paint_layer_.GetLayoutObject().Paint(paint_info); + if (fragment.physical_fragment) + NGBoxFragmentPainter(*fragment.physical_fragment).Paint(paint_info); + else + paint_layer_.GetLayoutObject().Paint(paint_info); } void PaintLayerPainter::PaintBackgroundForFragments( @@ -759,57 +745,33 @@ void PaintLayerPainter::PaintForegroundForFragments( const PaintLayerFragments& layer_fragments, GraphicsContext& context, const PaintLayerPaintingInfo& local_painting_info, - bool selection_only, - bool force_paint_chunks, PaintLayerFlags paint_flags) { - if (selection_only) { - PaintForegroundForFragmentsWithPhase(PaintPhase::kSelection, + PaintForegroundForFragmentsWithPhase( + PaintPhase::kDescendantBlockBackgroundsOnly, layer_fragments, context, + local_painting_info, paint_flags); + + if (paint_layer_.GetLayoutObject().GetDocument().InForcedColorsMode()) { + PaintForegroundForFragmentsWithPhase(PaintPhase::kForcedColorsModeBackplate, layer_fragments, context, local_painting_info, paint_flags); - } else { - if (RuntimeEnabledFeatures::PaintUnderInvalidationCheckingEnabled() || - paint_layer_.NeedsPaintPhaseDescendantBlockBackgrounds()) { - if (force_paint_chunks) { - context.GetPaintController().ForceNewChunk( - paint_layer_, DisplayItem::kLayerChunkDescendantBackgrounds); - } - PaintForegroundForFragmentsWithPhase( - PaintPhase::kDescendantBlockBackgroundsOnly, layer_fragments, context, - local_painting_info, paint_flags); - } - - if (paint_layer_.GetLayoutObject().GetDocument().InForcedColorsMode()) { - PaintForegroundForFragmentsWithPhase( - PaintPhase::kForcedColorsModeBackplate, layer_fragments, context, - local_painting_info, paint_flags); - } + } - if (RuntimeEnabledFeatures::PaintUnderInvalidationCheckingEnabled() || - paint_layer_.NeedsPaintPhaseFloat()) { - if (force_paint_chunks) { - context.GetPaintController().ForceNewChunk( - paint_layer_, DisplayItem::kLayerChunkFloat); - } - PaintForegroundForFragmentsWithPhase(PaintPhase::kFloat, layer_fragments, - context, local_painting_info, - paint_flags); - } + if (RuntimeEnabledFeatures::PaintUnderInvalidationCheckingEnabled() || + paint_layer_.NeedsPaintPhaseFloat()) { + PaintForegroundForFragmentsWithPhase(PaintPhase::kFloat, layer_fragments, + context, local_painting_info, + paint_flags); + } - if (force_paint_chunks) { - context.GetPaintController().ForceNewChunk( - paint_layer_, DisplayItem::kLayerChunkForeground); - } + PaintForegroundForFragmentsWithPhase(PaintPhase::kForeground, layer_fragments, + context, local_painting_info, + paint_flags); - PaintForegroundForFragmentsWithPhase(PaintPhase::kForeground, + if (RuntimeEnabledFeatures::PaintUnderInvalidationCheckingEnabled() || + paint_layer_.NeedsPaintPhaseDescendantOutlines()) { + PaintForegroundForFragmentsWithPhase(PaintPhase::kDescendantOutlinesOnly, layer_fragments, context, local_painting_info, paint_flags); - - if (RuntimeEnabledFeatures::PaintUnderInvalidationCheckingEnabled() || - paint_layer_.NeedsPaintPhaseDescendantOutlines()) { - PaintForegroundForFragmentsWithPhase(PaintPhase::kDescendantOutlinesOnly, - layer_fragments, context, - local_painting_info, paint_flags); - } } } @@ -878,21 +840,4 @@ void PaintLayerPainter::FillMaskingFragment(GraphicsContext& context, context.FillRect(snapped_clip_rect, Color::kBlack); } -// Generate a no-op DrawingDisplayItem to ensure a non-empty chunk for the -// filter without content. -void PaintLayerPainter::PaintEmptyContentForFilters(GraphicsContext& context) { - DCHECK(paint_layer_.PaintsWithFilters() || - paint_layer_.GetLayoutObject().HasBackdropFilter()); - - ScopedPaintChunkProperties paint_chunk_properties( - context.GetPaintController(), - paint_layer_.GetLayoutObject().FirstFragment().LocalBorderBoxProperties(), - paint_layer_, DisplayItem::kEmptyContentForFilters); - if (DrawingRecorder::UseCachedDrawingIfPossible( - context, paint_layer_, DisplayItem::kEmptyContentForFilters)) - return; - DrawingRecorder recorder(context, paint_layer_, - DisplayItem::kEmptyContentForFilters); -} - } // namespace blink 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 e61f2ceadc5..29e13df3cb4 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 @@ -82,8 +82,6 @@ class CORE_EXPORT PaintLayerPainter { void PaintForegroundForFragments(const PaintLayerFragments&, GraphicsContext&, const PaintLayerPaintingInfo&, - bool selection_only, - bool force_paint_chunks, PaintLayerFlags); void PaintForegroundForFragmentsWithPhase(PaintPhase, const PaintLayerFragments&, @@ -107,8 +105,6 @@ class CORE_EXPORT PaintLayerPainter { const ClipRect&, const DisplayItemClient&); - void PaintEmptyContentForFilters(GraphicsContext&); - void AdjustForPaintProperties(const GraphicsContext&, PaintLayerPaintingInfo&, PaintLayerFlags&); 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 c2c9d6a4ddc..c191b7a6e9a 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 @@ -48,8 +48,9 @@ class PaintLayerPainterTest : public PaintControllerPaintTest { INSTANTIATE_PAINT_TEST_SUITE_P(PaintLayerPainterTest); -TEST_P(PaintLayerPainterTest, CachedSubsequence) { +TEST_P(PaintLayerPainterTest, CachedSubsequenceAndChunksWithBackgrounds) { SetBodyInnerHTML(R"HTML( + <style>body { margin: 0 }</style> <div id='container1' style='position: relative; z-index: 1; width: 200px; height: 200px; background-color: blue'> <div id='content1' style='position: absolute; width: 100px; @@ -66,101 +67,179 @@ TEST_P(PaintLayerPainterTest, CachedSubsequence) { width: 20px; height: 20px; background-color: gray'></div> )HTML"); - auto& container1 = *GetLayoutObjectByElementId("container1"); - auto& content1 = *GetLayoutObjectByElementId("content1"); - auto& filler1 = *GetLayoutObjectByElementId("filler1"); - auto& container2 = *GetLayoutObjectByElementId("container2"); - auto& content2 = *GetLayoutObjectByElementId("content2"); - auto& filler2 = *GetLayoutObjectByElementId("filler2"); - + auto* container1 = GetLayoutObjectByElementId("container1"); + auto* content1 = GetLayoutObjectByElementId("content1"); + auto* filler1 = GetLayoutObjectByElementId("filler1"); + auto* container2 = GetLayoutObjectByElementId("container2"); + auto* content2 = GetLayoutObjectByElementId("content2"); + auto* filler2 = GetLayoutObjectByElementId("filler2"); 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* container1_layer = ToLayoutBoxModelObject(container1)->Layer(); + auto* filler1_layer = ToLayoutBoxModelObject(filler1)->Layer(); + auto* container2_layer = ToLayoutBoxModelObject(container2)->Layer(); + auto* filler2_layer = ToLayoutBoxModelObject(filler2)->Layer(); auto chunk_state = GetLayoutView().FirstFragment().ContentsProperties(); - auto view_chunk_type = kDocumentBackgroundType; - auto chunk_background_type = DisplayItem::kLayerChunkBackground; - auto chunk_foreground_type = - DisplayItem::kLayerChunkNormalFlowAndPositiveZOrderChildren; - auto filler_chunk_type = DisplayItem::PaintPhaseToDrawingType( - PaintPhase::kSelfBlockBackgroundOnly); - - auto check_chunks = [&]() { - // Check that new paint chunks were forced for |container1| and - // |container2|. + auto check_results = [&]() { + 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))); + + EXPECT_SUBSEQUENCE(*container1_layer, 1, 2); + EXPECT_SUBSEQUENCE(*filler1_layer, 2, 3); + EXPECT_SUBSEQUENCE(*container2_layer, 3, 4); + EXPECT_SUBSEQUENCE(*filler2_layer, 4, 5); + + // Check that new paint chunks were forced for the layers. EXPECT_THAT( RootPaintController().PaintChunks(), ElementsAre( - IsPaintChunk(0, 1, PaintChunk::Id(view_client, view_chunk_type), - chunk_state), + IsPaintChunk(0, 1), IsPaintChunk( - 1, 2, PaintChunk::Id(*container1_layer, chunk_background_type), - chunk_state), + 1, 3, + PaintChunk::Id(*container1_layer, DisplayItem::kLayerChunk), + chunk_state, nullptr, IntRect(0, 0, 200, 200)), IsPaintChunk( - 2, 3, PaintChunk::Id(*container1_layer, chunk_foreground_type), - chunk_state), - IsPaintChunk(3, 4, - PaintChunk::Id(*filler1_layer, filler_chunk_type), - chunk_state), + 3, 4, PaintChunk::Id(*filler1_layer, DisplayItem::kLayerChunk), + chunk_state, nullptr, IntRect(0, 200, 20, 20)), IsPaintChunk( - 4, 5, PaintChunk::Id(*container2_layer, chunk_background_type), - chunk_state), + 4, 6, + PaintChunk::Id(*container2_layer, DisplayItem::kLayerChunk), + chunk_state, nullptr, IntRect(0, 220, 200, 200)), IsPaintChunk( - 5, 6, PaintChunk::Id(*container2_layer, chunk_foreground_type), - chunk_state), - IsPaintChunk(6, 7, - PaintChunk::Id(*filler2_layer, filler_chunk_type), - chunk_state))); + 6, 7, PaintChunk::Id(*filler2_layer, DisplayItem::kLayerChunk), + chunk_state, nullptr, IntRect(0, 420, 20, 20)))); }; - check_chunks(); + check_results(); - To<HTMLElement>(content1.GetNode()) + To<HTMLElement>(content1->GetNode()) ->setAttribute(html_names::kStyleAttr, "position: absolute; width: 100px; height: 100px; " "background-color: green"); - GetDocument().View()->UpdateAllLifecyclePhasesExceptPaint(); + GetDocument().View()->UpdateAllLifecyclePhasesExceptPaint( + DocumentUpdateReason::kTest); EXPECT_TRUE(PaintWithoutCommit()); EXPECT_EQ(6, NumCachedNewItems()); CommitAndFinishCycle(); + // We should still have the paint chunks forced by the cached subsequences. + check_results(); +} + +TEST_P(PaintLayerPainterTest, CachedSubsequenceAndChunksWithoutBackgrounds) { + if (!RuntimeEnabledFeatures::CompositeAfterPaintEnabled()) + return; + + SetBodyInnerHTML(R"HTML( + <style> + body { margin: 0 } + ::-webkit-scrollbar { display: none } + </style> + <div id='container' style='position: relative; z-index: 0; + width: 150px; height: 150px; overflow: scroll'> + <div id='content' style='position: relative; z-index: 1; + width: 200px; height: 100px'> + <div id='inner-content' + style='position: absolute; width: 100px; height: 100px'></div> + </div> + <div id='filler' style='position: relative; z-index: 2; + width: 300px; height: 300px'></div> + </div> + )HTML"); + + auto* container = GetLayoutObjectByElementId("container"); + auto* content = GetLayoutObjectByElementId("content"); + auto* inner_content = GetLayoutObjectByElementId("inner-content"); + auto* filler = GetLayoutObjectByElementId("filler"); + const auto& view_client = ViewScrollingBackgroundClient(); + + EXPECT_THAT(RootPaintController().GetDisplayItemList(), + ElementsAre(IsSameId(&view_client, kDocumentBackgroundType))); + + auto* container_layer = ToLayoutBoxModelObject(container)->Layer(); + auto* content_layer = ToLayoutBoxModelObject(content)->Layer(); + auto* inner_content_layer = ToLayoutBoxModelObject(inner_content)->Layer(); + auto* filler_layer = ToLayoutBoxModelObject(filler)->Layer(); + + EXPECT_SUBSEQUENCE(*container_layer, 1, 5); + EXPECT_SUBSEQUENCE(*content_layer, 3, 4); + EXPECT_SUBSEQUENCE(*filler_layer, 4, 5); + + auto container_properties = + container->FirstFragment().LocalBorderBoxProperties(); + auto content_properties = container->FirstFragment().ContentsProperties(); + HitTestData scroll_hit_test; + scroll_hit_test.scroll_translation = &content_properties.Transform(); + scroll_hit_test.scroll_hit_test_rect = IntRect(0, 0, 150, 150); + + EXPECT_THAT( + RootPaintController().PaintChunks(), + ElementsAre( + IsPaintChunk(0, 1), + IsPaintChunk( + 1, 1, PaintChunk::Id(*container_layer, DisplayItem::kLayerChunk), + container_properties, nullptr, IntRect(0, 0, 150, 150)), + IsPaintChunk( + 1, 1, PaintChunk::Id(*container, DisplayItem::kScrollHitTest), + container_properties, &scroll_hit_test, IntRect(0, 0, 150, 150)), + IsPaintChunk(1, 1, + PaintChunk::Id(*content_layer, DisplayItem::kLayerChunk), + content_properties, nullptr, IntRect(0, 0, 200, 100)), + IsPaintChunk( + 1, 1, PaintChunk::Id(*filler_layer, DisplayItem::kLayerChunk), + content_properties, nullptr, IntRect(0, 100, 300, 300)))); + + To<HTMLElement>(inner_content->GetNode()) + ->setAttribute(html_names::kStyleAttr, + "position: absolute; width: 100px; height: 100px; " + "top: 100px; background-color: green"); + UpdateAllLifecyclePhasesForTest(); + 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), + IsSameId(GetDisplayItemClientFromLayoutObject(inner_content), kBackgroundType))); - // We should still have the paint chunks forced by the cached subsequences. - check_chunks(); + EXPECT_SUBSEQUENCE(*container_layer, 1, 6); + EXPECT_SUBSEQUENCE(*content_layer, 3, 5); + EXPECT_SUBSEQUENCE(*filler_layer, 5, 6); + + EXPECT_THAT( + RootPaintController().PaintChunks(), + ElementsAre( + IsPaintChunk(0, 1), + IsPaintChunk( + 1, 1, PaintChunk::Id(*container_layer, DisplayItem::kLayerChunk), + container_properties, nullptr, IntRect(0, 0, 150, 150)), + IsPaintChunk( + 1, 1, PaintChunk::Id(*container, DisplayItem::kScrollHitTest), + container_properties, &scroll_hit_test, IntRect(0, 0, 150, 150)), + IsPaintChunk(1, 1, + PaintChunk::Id(*content_layer, DisplayItem::kLayerChunk), + content_properties, nullptr, IntRect(0, 0, 200, 100)), + IsPaintChunk( + 1, 2, + PaintChunk::Id(*inner_content_layer, DisplayItem::kLayerChunk), + content_properties, nullptr, IntRect(0, 100, 100, 100)), + IsPaintChunk( + 2, 2, PaintChunk::Id(*filler_layer, DisplayItem::kLayerChunk), + content_properties, nullptr, IntRect(0, 100, 300, 300)))); } TEST_P(PaintLayerPainterTest, CachedSubsequenceOnCullRectChange) { @@ -207,7 +286,8 @@ TEST_P(PaintLayerPainterTest, CachedSubsequenceOnCullRectChange) { const DisplayItemClient& content3 = *GetDisplayItemClientFromElementId("content3"); - GetDocument().View()->UpdateAllLifecyclePhasesExceptPaint(); + GetDocument().View()->UpdateAllLifecyclePhasesExceptPaint( + DocumentUpdateReason::kTest); Paint(IntRect(0, 0, 400, 300)); const auto& background_display_item_client = ViewScrollingBackgroundClient(); @@ -226,7 +306,8 @@ TEST_P(PaintLayerPainterTest, CachedSubsequenceOnCullRectChange) { IsSameId(&container3, kBackgroundType), IsSameId(&content3, kBackgroundType))); - GetDocument().View()->UpdateAllLifecyclePhasesExceptPaint(); + GetDocument().View()->UpdateAllLifecyclePhasesExceptPaint( + DocumentUpdateReason::kTest); EXPECT_TRUE(PaintWithoutCommit(IntRect(0, 100, 300, 1000))); // Container1 becomes partly in the interest rect, but uses cached subsequence @@ -262,12 +343,14 @@ TEST_P(PaintLayerPainterTest, InvalidateAll(RootPaintController()); // |target| will be fully painted. - GetDocument().View()->UpdateAllLifecyclePhasesExceptPaint(); + GetDocument().View()->UpdateAllLifecyclePhasesExceptPaint( + DocumentUpdateReason::kTest); Paint(IntRect(0, 0, 400, 300)); // |target| will be partially painted. Should not trigger under-invalidation // checking DCHECKs. - GetDocument().View()->UpdateAllLifecyclePhasesExceptPaint(); + GetDocument().View()->UpdateAllLifecyclePhasesExceptPaint( + DocumentUpdateReason::kTest); Paint(IntRect(0, 100, 300, 1000)); } @@ -285,7 +368,8 @@ TEST_P(PaintLayerPainterTest, height: 100px; background-color: green'></div> </div> )HTML"); - GetDocument().View()->UpdateAllLifecyclePhasesExceptPaint(); + GetDocument().View()->UpdateAllLifecyclePhasesExceptPaint( + DocumentUpdateReason::kTest); // PaintResult of all subsequences will be MayBeClippedByCullRect. Paint(IntRect(0, 0, 50, 300)); @@ -311,7 +395,8 @@ TEST_P(PaintLayerPainterTest, ->setAttribute(html_names::kStyleAttr, "position: absolute; width: 100px; height: 100px; " "background-color: green"); - GetDocument().View()->UpdateAllLifecyclePhasesExceptPaint(); + GetDocument().View()->UpdateAllLifecyclePhasesExceptPaint( + DocumentUpdateReason::kTest); EXPECT_TRUE(PaintWithoutCommit(IntRect(0, 0, 50, 300))); EXPECT_EQ(4, NumCachedNewItems()); @@ -340,8 +425,8 @@ TEST_P(PaintLayerPainterTest, CachedSubsequenceRetainsPreviousPaintResult) { <div id="change" style="display: none"></div> )HTML"); - const auto* target_layer = - ToLayoutBoxModelObject(GetLayoutObjectByElementId("target"))->Layer(); + const auto* target = ToLayoutBox(GetLayoutObjectByElementId("target")); + const auto* target_layer = target->Layer(); const auto* content1 = GetLayoutObjectByElementId("content1"); const auto* content2 = GetLayoutObjectByElementId("content2"); const auto& view_client = ViewScrollingBackgroundClient(); @@ -353,13 +438,13 @@ TEST_P(PaintLayerPainterTest, CachedSubsequenceRetainsPreviousPaintResult) { 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))); + EXPECT_THAT(RootPaintController().GetDisplayItemList(), + ElementsAre(IsSameId(&view_client, kDocumentBackgroundType), + IsSameId(content1, kBackgroundType))); // |target| created subsequence. - EXPECT_SUBSEQUENCE(*target_layer, 2, 3); + EXPECT_SUBSEQUENCE(*target_layer, 2, 4); + EXPECT_EQ(0u, RootPaintController().PaintChunks()[2].size()); + EXPECT_EQ(1u, RootPaintController().PaintChunks()[3].size()); } else { EXPECT_EQ(CullRect(IntRect(0, 0, 800, 4600)), target_layer->PreviousCullRect()); @@ -369,35 +454,34 @@ TEST_P(PaintLayerPainterTest, CachedSubsequenceRetainsPreviousPaintResult) { IsSameId(content1, kBackgroundType))); // |target| created subsequence. EXPECT_SUBSEQUENCE(*target_layer, 1, 2); + EXPECT_EQ(1u, RootPaintController().PaintChunks()[1].size()); } // Change something that triggers a repaint but |target| should use cached // subsequence. GetDocument().getElementById("change")->setAttribute(html_names::kStyleAttr, "display: block"); - GetDocument().View()->UpdateAllLifecyclePhasesExceptPaint(); + GetDocument().View()->UpdateAllLifecyclePhasesExceptPaint( + DocumentUpdateReason::kTest); EXPECT_FALSE(target_layer->SelfNeedsRepaint()); EXPECT_TRUE(PaintWithoutCommit()); - if (RuntimeEnabledFeatures::CompositeAfterPaintEnabled()) - EXPECT_EQ(3, NumCachedNewItems()); - else - EXPECT_EQ(2, NumCachedNewItems()); + EXPECT_EQ(2, NumCachedNewItems()); CommitAndFinishCycle(); // |target| is still partially painted. EXPECT_EQ(kMayBeClippedByCullRect, target_layer->PreviousPaintResult()); if (RuntimeEnabledFeatures::CompositeAfterPaintEnabled()) { - // CAP doens't clip the cull rect by the scrolling contents rect, which + // CAP 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()); - EXPECT_THAT( - RootPaintController().GetDisplayItemList(), - ElementsAre(IsSameId(&GetLayoutView(), DisplayItem::kScrollHitTest), - IsSameId(&view_client, kDocumentBackgroundType), - IsSameId(content1, kBackgroundType))); + EXPECT_THAT(RootPaintController().GetDisplayItemList(), + ElementsAre(IsSameId(&view_client, kDocumentBackgroundType), + IsSameId(content1, kBackgroundType))); // |target| still created subsequence (cached). - EXPECT_SUBSEQUENCE(*target_layer, 2, 3); + EXPECT_SUBSEQUENCE(*target_layer, 2, 4); + EXPECT_EQ(0u, RootPaintController().PaintChunks()[2].size()); + EXPECT_EQ(1u, RootPaintController().PaintChunks()[3].size()); } else { EXPECT_EQ(CullRect(IntRect(0, 0, 800, 4600)), target_layer->PreviousCullRect()); @@ -406,39 +490,38 @@ TEST_P(PaintLayerPainterTest, CachedSubsequenceRetainsPreviousPaintResult) { IsSameId(content1, kBackgroundType))); // |target| still created subsequence (cached). EXPECT_SUBSEQUENCE(*target_layer, 1, 2); + EXPECT_EQ(1u, RootPaintController().PaintChunks()[1].size()); } // Scroll the view so that both |content1| and |content2| are in the interest // rect. - GetLayoutView().GetScrollableArea()->SetScrollOffset(ScrollOffset(0, 3000), - kProgrammaticScroll); - GetDocument().View()->UpdateAllLifecyclePhasesExceptPaint(); + GetLayoutView().GetScrollableArea()->SetScrollOffset( + ScrollOffset(0, 3000), mojom::blink::ScrollType::kProgrammatic); + GetDocument().View()->UpdateAllLifecyclePhasesExceptPaint( + DocumentUpdateReason::kTest); // Scrolling doesn't set SelfNeedsRepaint flag. Change of paint dirty rect of // a partially painted layer will trigger repaint. EXPECT_FALSE(target_layer->SelfNeedsRepaint()); EXPECT_TRUE(PaintWithoutCommit()); - if (RuntimeEnabledFeatures::CompositeAfterPaintEnabled()) - EXPECT_EQ(3, NumCachedNewItems()); - else - EXPECT_EQ(2, NumCachedNewItems()); + EXPECT_EQ(2, NumCachedNewItems()); CommitAndFinishCycle(); // |target| is still partially painted. EXPECT_EQ(kMayBeClippedByCullRect, target_layer->PreviousPaintResult()); if (RuntimeEnabledFeatures::CompositeAfterPaintEnabled()) { - // CAP doens't clip the cull rect by the scrolling contents rect, which + // CAP doesn'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))); + EXPECT_THAT(RootPaintController().GetDisplayItemList(), + ElementsAre(IsSameId(&view_client, kDocumentBackgroundType), + IsSameId(content1, kBackgroundType), + IsSameId(content2, kBackgroundType))); // |target| still created subsequence (repainted). EXPECT_SUBSEQUENCE(*target_layer, 2, 4); + EXPECT_EQ(0u, RootPaintController().PaintChunks()[2].size()); + EXPECT_EQ(2u, RootPaintController().PaintChunks()[3].size()); } else { EXPECT_EQ(CullRect(IntRect(0, 0, 800, 7600)), target_layer->PreviousCullRect()); @@ -448,10 +531,133 @@ TEST_P(PaintLayerPainterTest, CachedSubsequenceRetainsPreviousPaintResult) { IsSameId(content1, kBackgroundType), IsSameId(content2, kBackgroundType))); // |target| still created subsequence (repainted). - EXPECT_SUBSEQUENCE(*target_layer, 1, 3); + EXPECT_SUBSEQUENCE(*target_layer, 1, 2); + EXPECT_EQ(2u, RootPaintController().PaintChunks()[1].size()); } } +TEST_P(PaintLayerPainterTest, HintedPaintChunksWithBackgrounds) { + SetBodyInnerHTML(R"HTML( + <style> + body { margin: 0 } + div { background: blue } + </style> + <div id='container1' style='position: relative; height: 150px; z-index: 1'> + <div id='content1a' style='position: relative; height: 100px'></div> + <div id='content1b' style='position: relative; height: 100px'></div> + </div> + <div id='container2' style='position: relative; z-index: 1'> + <div id='content2a' style='position: relative; height: 100px'></div> + <div id='content2b' style='position: relative; z-index: -1; height: 100px'></div> + </div> + )HTML"); + + auto* container1 = ToLayoutBox(GetLayoutObjectByElementId("container1")); + auto* content1a = ToLayoutBox(GetLayoutObjectByElementId("content1a")); + auto* content1b = ToLayoutBox(GetLayoutObjectByElementId("content1b")); + auto* container2 = ToLayoutBox(GetLayoutObjectByElementId("container2")); + auto* content2a = ToLayoutBox(GetLayoutObjectByElementId("content2a")); + auto* content2b = ToLayoutBox(GetLayoutObjectByElementId("content2b")); + auto chunk_state = GetLayoutView().FirstFragment().ContentsProperties(); + + EXPECT_THAT(RootPaintController().GetDisplayItemList(), + ElementsAre(IsSameId(&ViewScrollingBackgroundClient(), + kDocumentBackgroundType), + IsSameId(container1, kBackgroundType), + IsSameId(content1a, kBackgroundType), + IsSameId(content1b, kBackgroundType), + IsSameId(container2, kBackgroundType), + IsSameId(content2b, kBackgroundType), + IsSameId(content2a, kBackgroundType))); + + EXPECT_THAT( + RootPaintController().PaintChunks(), + ElementsAre( + IsPaintChunk(0, 1, + PaintChunk::Id(ViewScrollingBackgroundClient(), + kDocumentBackgroundType), + chunk_state), + // Includes |container1| and |content1a|. + IsPaintChunk( + 1, 3, + PaintChunk::Id(*container1->Layer(), DisplayItem::kLayerChunk), + chunk_state, nullptr, IntRect(0, 0, 800, 150)), + // Includes |content1b| which overflows |container1|. + IsPaintChunk( + 3, 4, + PaintChunk::Id(*content1b->Layer(), DisplayItem::kLayerChunk), + chunk_state, nullptr, IntRect(0, 100, 800, 100)), + IsPaintChunk( + 4, 5, + PaintChunk::Id(*container2->Layer(), DisplayItem::kLayerChunk), + chunk_state, nullptr, IntRect(0, 150, 800, 200)), + IsPaintChunk( + 5, 6, + PaintChunk::Id(*content2b->Layer(), DisplayItem::kLayerChunk), + chunk_state, nullptr, IntRect(0, 250, 800, 100)), + IsPaintChunk( + 6, 7, + PaintChunk::Id(*content2a->Layer(), DisplayItem::kLayerChunk), + chunk_state, nullptr, IntRect(0, 150, 800, 100)))); +} + +TEST_P(PaintLayerPainterTest, HintedPaintChunksWithoutBackgrounds) { + if (!RuntimeEnabledFeatures::CompositeAfterPaintEnabled()) + return; + + SetBodyInnerHTML(R"HTML( + <style>body { margin: 0 }</style> + <div id='container1' style='position: relative; height: 150px; z-index: 1'> + <div id='content1a' style='position: relative; height: 100px'></div> + <div id='content1b' style='position: relative; height: 100px'></div> + </div> + <div id='container2' style='position: relative; z-index: 1'> + <div id='content2a' style='position: relative; height: 100px'></div> + <div id='content2b' + style='position: relative; z-index: -1; height: 100px'></div> + </div> + )HTML"); + + auto* container1 = ToLayoutBox(GetLayoutObjectByElementId("container1")); + auto* content1b = ToLayoutBox(GetLayoutObjectByElementId("content1b")); + auto* container2 = ToLayoutBox(GetLayoutObjectByElementId("container2")); + auto* content2a = ToLayoutBox(GetLayoutObjectByElementId("content2a")); + auto* content2b = ToLayoutBox(GetLayoutObjectByElementId("content2b")); + auto chunk_state = GetLayoutView().FirstFragment().ContentsProperties(); + + EXPECT_THAT(RootPaintController().GetDisplayItemList(), + ElementsAre(IsSameId(&ViewScrollingBackgroundClient(), + kDocumentBackgroundType))); + + EXPECT_THAT( + RootPaintController().PaintChunks(), + ElementsAre( + IsPaintChunk(0, 1, + PaintChunk::Id(ViewScrollingBackgroundClient(), + kDocumentBackgroundType), + chunk_state), + IsPaintChunk( + 1, 1, + PaintChunk::Id(*container1->Layer(), DisplayItem::kLayerChunk), + chunk_state, nullptr, IntRect(0, 0, 800, 150)), + IsPaintChunk( + 1, 1, + PaintChunk::Id(*content1b->Layer(), DisplayItem::kLayerChunk), + chunk_state, nullptr, IntRect(0, 100, 800, 100)), + IsPaintChunk( + 1, 1, + PaintChunk::Id(*container2->Layer(), DisplayItem::kLayerChunk), + chunk_state, nullptr, IntRect(0, 150, 800, 200)), + IsPaintChunk( + 1, 1, + PaintChunk::Id(*content2b->Layer(), DisplayItem::kLayerChunk), + chunk_state, nullptr, IntRect(0, 250, 800, 100)), + IsPaintChunk( + 1, 1, + PaintChunk::Id(*content2a->Layer(), DisplayItem::kLayerChunk), + chunk_state, nullptr, IntRect(0, 150, 800, 100)))); +} + TEST_P(PaintLayerPainterTest, PaintPhaseOutline) { AtomicString style_without_outline = "width: 50px; height: 50px; background-color: green"; @@ -503,7 +709,8 @@ TEST_P(PaintLayerPainterTest, PaintPhaseOutline) { // same layer has outline. To<HTMLElement>(outline_div.GetNode()) ->setAttribute(html_names::kStyleAttr, style_with_outline); - GetDocument().View()->UpdateAllLifecyclePhasesExceptPaint(); + GetDocument().View()->UpdateAllLifecyclePhasesExceptPaint( + DocumentUpdateReason::kTest); EXPECT_TRUE(self_painting_layer.NeedsPaintPhaseDescendantOutlines()); EXPECT_FALSE(non_self_painting_layer.NeedsPaintPhaseDescendantOutlines()); Paint(); @@ -558,7 +765,8 @@ TEST_P(PaintLayerPainterTest, PaintPhaseFloat) { // has float. To<HTMLElement>(float_div.GetNode()) ->setAttribute(html_names::kStyleAttr, style_with_float); - GetDocument().View()->UpdateAllLifecyclePhasesExceptPaint(); + GetDocument().View()->UpdateAllLifecyclePhasesExceptPaint( + DocumentUpdateReason::kTest); EXPECT_TRUE(self_painting_layer.NeedsPaintPhaseFloat()); EXPECT_FALSE(non_self_painting_layer.NeedsPaintPhaseFloat()); Paint(); @@ -622,75 +830,6 @@ TEST_P(PaintLayerPainterTest, PaintPhaseFloatUnderInlineLayer) { DisplayItem::kBoxDecorationBackground)); } -TEST_P(PaintLayerPainterTest, PaintPhaseBlockBackground) { - AtomicString style_without_background = "width: 50px; height: 50px"; - AtomicString style_with_background = - "background: blue; " + style_without_background; - SetBodyInnerHTML(R"HTML( - <div id='self-painting-layer' style='position: absolute'> - <div id='non-self-painting-layer' style='overflow: hidden'> - <div> - <div id='background'></div> - </div> - </div> - </div> - )HTML"); - LayoutObject& background_div = - *GetDocument().getElementById("background")->GetLayoutObject(); - To<HTMLElement>(background_div.GetNode()) - ->setAttribute(html_names::kStyleAttr, style_without_background); - UpdateAllLifecyclePhasesForTest(); - - LayoutBoxModelObject& self_painting_layer_object = *ToLayoutBoxModelObject( - GetDocument().getElementById("self-painting-layer")->GetLayoutObject()); - PaintLayer& self_painting_layer = *self_painting_layer_object.Layer(); - ASSERT_TRUE(self_painting_layer.IsSelfPaintingLayer()); - PaintLayer& non_self_painting_layer = - *ToLayoutBoxModelObject(GetDocument() - .getElementById("non-self-painting-layer") - ->GetLayoutObject()) - ->Layer(); - ASSERT_FALSE(non_self_painting_layer.IsSelfPaintingLayer()); - ASSERT_TRUE(&non_self_painting_layer == background_div.EnclosingLayer()); - - EXPECT_FALSE(self_painting_layer.NeedsPaintPhaseDescendantBlockBackgrounds()); - EXPECT_FALSE( - non_self_painting_layer.NeedsPaintPhaseDescendantBlockBackgrounds()); - - // Background on the self-painting-layer node itself doesn't affect - // PaintPhaseDescendantBlockBackgrounds. - To<HTMLElement>(self_painting_layer_object.GetNode()) - ->setAttribute(html_names::kStyleAttr, - "position: absolute; background: green"); - UpdateAllLifecyclePhasesForTest(); - EXPECT_FALSE(self_painting_layer.NeedsPaintPhaseDescendantBlockBackgrounds()); - EXPECT_FALSE( - non_self_painting_layer.NeedsPaintPhaseDescendantBlockBackgrounds()); - EXPECT_TRUE(DisplayItemListContains( - RootPaintController().GetDisplayItemList(), self_painting_layer_object, - DisplayItem::kBoxDecorationBackground)); - - // needsPaintPhaseDescendantBlockBackgrounds should be set when any descendant - // on the same layer has Background. - To<HTMLElement>(background_div.GetNode()) - ->setAttribute(html_names::kStyleAttr, style_with_background); - GetDocument().View()->UpdateAllLifecyclePhasesExceptPaint(); - EXPECT_TRUE(self_painting_layer.NeedsPaintPhaseDescendantBlockBackgrounds()); - EXPECT_FALSE( - non_self_painting_layer.NeedsPaintPhaseDescendantBlockBackgrounds()); - Paint(); - EXPECT_TRUE(DisplayItemListContains( - RootPaintController().GetDisplayItemList(), background_div, - DisplayItem::kBoxDecorationBackground)); - - // needsPaintPhaseDescendantBlockBackgrounds should be reset when no outline - // is actually painted. - To<HTMLElement>(background_div.GetNode()) - ->setAttribute(html_names::kStyleAttr, style_without_background); - UpdateAllLifecyclePhasesForTest(); - EXPECT_TRUE(self_painting_layer.NeedsPaintPhaseDescendantBlockBackgrounds()); -} - TEST_P(PaintLayerPainterTest, PaintPhasesUpdateOnLayerAddition) { SetBodyInnerHTML(R"HTML( <div id='will-be-layer'> @@ -712,7 +851,6 @@ TEST_P(PaintLayerPainterTest, PaintPhasesUpdateOnLayerAddition) { ->Layer(); EXPECT_TRUE(html_layer.NeedsPaintPhaseDescendantOutlines()); EXPECT_TRUE(html_layer.NeedsPaintPhaseFloat()); - EXPECT_TRUE(html_layer.NeedsPaintPhaseDescendantBlockBackgrounds()); To<HTMLElement>(layer_div.GetNode()) ->setAttribute(html_names::kStyleAttr, "position: relative"); @@ -722,7 +860,6 @@ TEST_P(PaintLayerPainterTest, PaintPhasesUpdateOnLayerAddition) { ASSERT_TRUE(layer.IsSelfPaintingLayer()); EXPECT_TRUE(layer.NeedsPaintPhaseDescendantOutlines()); EXPECT_TRUE(layer.NeedsPaintPhaseFloat()); - EXPECT_TRUE(layer.NeedsPaintPhaseDescendantBlockBackgrounds()); } TEST_P(PaintLayerPainterTest, PaintPhasesUpdateOnBecomingSelfPainting) { @@ -747,7 +884,6 @@ TEST_P(PaintLayerPainterTest, PaintPhasesUpdateOnBecomingSelfPainting) { GetDocument().documentElement()->GetLayoutObject()) ->Layer(); EXPECT_TRUE(html_layer.NeedsPaintPhaseDescendantOutlines()); - EXPECT_TRUE(html_layer.NeedsPaintPhaseDescendantBlockBackgrounds()); To<HTMLElement>(layer_div.GetNode()) ->setAttribute( @@ -757,7 +893,6 @@ TEST_P(PaintLayerPainterTest, PaintPhasesUpdateOnBecomingSelfPainting) { PaintLayer& layer = *layer_div.Layer(); ASSERT_TRUE(layer.IsSelfPaintingLayer()); EXPECT_TRUE(layer.NeedsPaintPhaseDescendantOutlines()); - EXPECT_TRUE(layer.NeedsPaintPhaseDescendantBlockBackgrounds()); } TEST_P(PaintLayerPainterTest, PaintPhasesUpdateOnBecomingNonSelfPainting) { @@ -780,14 +915,12 @@ TEST_P(PaintLayerPainterTest, PaintPhasesUpdateOnBecomingNonSelfPainting) { PaintLayer& layer = *layer_div.Layer(); EXPECT_TRUE(layer.IsSelfPaintingLayer()); EXPECT_TRUE(layer.NeedsPaintPhaseDescendantOutlines()); - EXPECT_TRUE(layer.NeedsPaintPhaseDescendantBlockBackgrounds()); PaintLayer& html_layer = *ToLayoutBoxModelObject( GetDocument().documentElement()->GetLayoutObject()) ->Layer(); EXPECT_FALSE(html_layer.NeedsPaintPhaseDescendantOutlines()); - EXPECT_FALSE(html_layer.NeedsPaintPhaseDescendantBlockBackgrounds()); To<HTMLElement>(layer_div.GetNode()) ->setAttribute(html_names::kStyleAttr, @@ -795,52 +928,6 @@ TEST_P(PaintLayerPainterTest, PaintPhasesUpdateOnBecomingNonSelfPainting) { UpdateAllLifecyclePhasesForTest(); EXPECT_FALSE(layer.IsSelfPaintingLayer()); EXPECT_TRUE(html_layer.NeedsPaintPhaseDescendantOutlines()); - EXPECT_TRUE(html_layer.NeedsPaintPhaseDescendantBlockBackgrounds()); -} - -TEST_P(PaintLayerPainterTest, - TableCollapsedBorderNeedsPaintPhaseDescendantBlockBackgrounds) { - // "position: relative" makes the table and td self-painting layers. - // The table's layer should be marked needsPaintPhaseDescendantBlockBackground - // because it will paint collapsed borders in the phase. - SetBodyInnerHTML(R"HTML( - <table id='table' style='position: relative; border-collapse: collapse'> - <tr><td style='position: relative; border: 1px solid green'> - Cell - </td></tr> - </table> - )HTML"); - - LayoutBoxModelObject& table = - *ToLayoutBoxModelObject(GetLayoutObjectByElementId("table")); - ASSERT_TRUE(table.HasLayer()); - PaintLayer& layer = *table.Layer(); - EXPECT_TRUE(layer.IsSelfPaintingLayer()); - EXPECT_TRUE(layer.NeedsPaintPhaseDescendantBlockBackgrounds()); -} - -TEST_P(PaintLayerPainterTest, - TableCollapsedBorderNeedsPaintPhaseDescendantBlockBackgroundsDynamic) { - SetBodyInnerHTML(R"HTML( - <table id='table' style='position: relative'> - <tr><td style='position: relative; border: 1px solid green'> - Cell - </td></tr> - </table> - )HTML"); - - LayoutBoxModelObject& table = - *ToLayoutBoxModelObject(GetLayoutObjectByElementId("table")); - ASSERT_TRUE(table.HasLayer()); - PaintLayer& layer = *table.Layer(); - EXPECT_TRUE(layer.IsSelfPaintingLayer()); - EXPECT_FALSE(layer.NeedsPaintPhaseDescendantBlockBackgrounds()); - - To<HTMLElement>(table.GetNode()) - ->setAttribute(html_names::kStyleAttr, - "position: relative; border-collapse: collapse"); - UpdateAllLifecyclePhasesForTest(); - EXPECT_TRUE(layer.NeedsPaintPhaseDescendantBlockBackgrounds()); } TEST_P(PaintLayerPainterTest, DontPaintWithTinyOpacity) { @@ -1035,21 +1122,21 @@ TEST_P(PaintLayerPainterTestCAP, TallScrolledLayerCullRect) { EXPECT_EQ(IntRect(-4000, -4000, 8800, 8600), GetPaintLayerByElementId("target")->PreviousCullRect().Rect()); - GetDocument().View()->LayoutViewport()->SetScrollOffset(ScrollOffset(0, 6000), - kProgrammaticScroll); + GetDocument().View()->LayoutViewport()->SetScrollOffset( + ScrollOffset(0, 6000), mojom::blink::ScrollType::kProgrammatic); UpdateAllLifecyclePhasesForTest(); EXPECT_EQ(IntRect(-4000, 2000, 8800, 8600), GetPaintLayerByElementId("target")->PreviousCullRect().Rect()); - GetDocument().View()->LayoutViewport()->SetScrollOffset(ScrollOffset(0, 6500), - kProgrammaticScroll); + GetDocument().View()->LayoutViewport()->SetScrollOffset( + ScrollOffset(0, 6500), mojom::blink::ScrollType::kProgrammatic); 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); + GetDocument().View()->LayoutViewport()->SetScrollOffset( + ScrollOffset(0, 6600), mojom::blink::ScrollType::kProgrammatic); UpdateAllLifecyclePhasesForTest(); // Used new cull rect. EXPECT_EQ(IntRect(-4000, 2600, 8800, 8600), @@ -1093,14 +1180,11 @@ TEST_P(PaintLayerPainterTestCAP, WholeDocumentCullRect) { 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")) @@ -1124,7 +1208,7 @@ TEST_P(PaintLayerPainterTestCAP, VerticalRightLeftWritingModeDocument) { )HTML"); GetDocument().View()->LayoutViewport()->SetScrollOffset( - ScrollOffset(-5000, 0), kProgrammaticScroll); + ScrollOffset(-5000, 0), mojom::blink::ScrollType::kProgrammatic); UpdateAllLifecyclePhasesForTest(); // A scroll by -5000px is equivalent to a scroll by (10000 - 5000 - 800)px = 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 c2ff5b6ecf3..01c3537f381 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 @@ -49,9 +49,11 @@ #include "cc/input/main_thread_scrolling_reason.h" #include "cc/input/snap_selection_strategy.h" #include "cc/layers/picture_layer.h" +#include "third_party/blink/public/common/features.h" +#include "third_party/blink/public/mojom/scroll/scroll_into_view_params.mojom-blink.h" +#include "third_party/blink/public/mojom/scroll/scrollbar_mode.mojom-blink.h" #include "third_party/blink/public/platform/platform.h" #include "third_party/blink/public/platform/task_type.h" -#include "third_party/blink/public/platform/web_scroll_into_view_params.h" #include "third_party/blink/renderer/core/accessibility/ax_object_cache.h" #include "third_party/blink/renderer/core/animation/scroll_timeline.h" #include "third_party/blink/renderer/core/content_capture/content_capture_manager.h" @@ -98,6 +100,8 @@ #include "third_party/blink/renderer/core/scroll/smooth_scroll_sequencer.h" #include "third_party/blink/renderer/platform/graphics/graphics_layer.h" #include "third_party/blink/renderer/platform/graphics/paint/drawing_recorder.h" +#include "third_party/blink/renderer/platform/heap/heap.h" +#include "ui/base/ui_base_features.h" namespace blink { @@ -123,6 +127,7 @@ PaintLayerScrollableArea::PaintLayerScrollableArea(PaintLayer& layer) needs_composited_scrolling_(false), rebuild_horizontal_scrollbar_layer_(false), rebuild_vertical_scrollbar_layer_(false), + previous_vertical_scrollbar_on_left_(false), needs_scroll_offset_clamp_(false), needs_relayout_(false), had_horizontal_scrollbar_before_relayout_(false), @@ -224,11 +229,32 @@ void PaintLayerScrollableArea::DisposeImpl() { sequencer->DidDisposeScrollableArea(*this); RunScrollCompleteCallbacks(); + InvalidateScrollTimeline(); layer_ = nullptr; } -void PaintLayerScrollableArea::Trace(blink::Visitor* visitor) { +void PaintLayerScrollableArea::ApplyPendingHistoryRestoreScrollOffset() { + if (!pending_view_state_) + return; + + // TODO(pnoland): attempt to restore the anchor in more places than this. + // Anchor-based restore should allow for earlier restoration. + bool did_restore = RestoreScrollAnchor( + {pending_view_state_->scroll_anchor_data_.selector_, + LayoutPoint(pending_view_state_->scroll_anchor_data_.offset_.x(), + pending_view_state_->scroll_anchor_data_.offset_.y()), + pending_view_state_->scroll_anchor_data_.simhash_}); + if (!did_restore) { + SetScrollOffset(pending_view_state_->scroll_offset_, + mojom::blink::ScrollType::kProgrammatic, + mojom::blink::ScrollBehavior::kAuto); + } + + pending_view_state_.reset(); +} + +void PaintLayerScrollableArea::Trace(Visitor* visitor) { visitor->Trace(scrollbar_manager_); visitor->Trace(scroll_anchor_); visitor->Trace(scrolling_background_display_item_client_); @@ -335,8 +361,7 @@ static int CornerStart(const LayoutBox& box, return max_x - thickness - box.StyleRef().BorderRightWidth(); } -IntRect PaintLayerScrollableArea::PaintLayerScrollableArea::CornerRect( - const IntRect& bounds) const { +IntRect PaintLayerScrollableArea::CornerRect() const { int horizontal_thickness; int vertical_thickness; if (!VerticalScrollbar() && !HorizontalScrollbar()) { @@ -362,9 +387,10 @@ IntRect PaintLayerScrollableArea::PaintLayerScrollableArea::CornerRect( horizontal_thickness = VerticalScrollbar()->ScrollbarThickness(); vertical_thickness = HorizontalScrollbar()->ScrollbarThickness(); } - return IntRect(CornerStart(*GetLayoutBox(), bounds.X(), bounds.MaxX(), + IntSize border_box_size = PixelSnappedBorderBoxSize(); + return IntRect(CornerStart(*GetLayoutBox(), 0, border_box_size.Width(), horizontal_thickness), - bounds.MaxY() - vertical_thickness - + border_box_size.Height() - vertical_thickness - GetLayoutBox()->StyleRef().BorderBottomWidth(), horizontal_thickness, vertical_thickness); } @@ -380,8 +406,7 @@ IntRect PaintLayerScrollableArea::ScrollCornerRect() const { bool has_resizer = GetLayoutBox()->StyleRef().HasResize(); if ((has_horizontal_bar && has_vertical_bar) || (has_resizer && (has_horizontal_bar || has_vertical_bar))) { - return CornerRect(GetLayoutBox()->PixelSnappedBorderBoxRect( - Layer()->SubpixelAccumulation())); + return CornerRect(); } return IntRect(); } @@ -466,7 +491,7 @@ int PaintLayerScrollableArea::ScrollSize( void PaintLayerScrollableArea::UpdateScrollOffset( const ScrollOffset& new_offset, - ScrollType scroll_type) { + mojom::blink::ScrollType scroll_type) { if (HasBeenDisposed() || GetScrollOffset() == new_offset) return; @@ -494,7 +519,7 @@ void PaintLayerScrollableArea::UpdateScrollOffset( // should be impacted by a scroll). if (!frame_view->IsInPerformLayout()) { if (!Layer()->IsRootLayer()) { - Layer()->SetNeedsCompositingInputsUpdate(); + Layer()->SetNeedsCompositingInputsUpdate(false); Layer()->ClearClipRects(); } @@ -509,12 +534,21 @@ void PaintLayerScrollableArea::UpdateScrollOffset( else frame_view->SetNeedsUpdateGeometries(); } - UpdateCompositingLayersAfterScroll(); - GetLayoutBox()->MayUpdateHoverWhenContentUnderMouseChanged( - frame->GetEventHandler()); + if (RuntimeEnabledFeatures::CompositeAfterPaintEnabled()) { + if (auto* scrolling_coordinator = GetScrollingCoordinator()) + scrolling_coordinator->UpdateCompositorScrollOffset(*frame, *this); + } else { + UpdateCompositingLayersAfterScroll(); + } - if (scroll_type == kUserScroll || scroll_type == kCompositorScroll) { + // The ScrollOffsetTranslation paint property depends on the scroll offset. + // (see: PaintPropertyTreeBuilder::UpdateScrollAndScrollTranslation). + GetLayoutBox()->SetNeedsPaintPropertyUpdatePreservingCachedRects(); + InvalidateScrollTimeline(); + + if (scroll_type == mojom::blink::ScrollType::kUser || + scroll_type == mojom::blink::ScrollType::kCompositor) { Page* page = frame->GetPage(); if (page) page->GetChromeClient().ClearToolTip(*frame); @@ -522,10 +556,6 @@ void PaintLayerScrollableArea::UpdateScrollOffset( InvalidatePaintForScrollOffsetChange(); - // The scrollOffsetTranslation paint property depends on the scroll offset. - // (see: PaintPropertyTreeBuilder::UpdateScrollAndScrollTranslation). - GetLayoutBox()->SetNeedsPaintPropertyUpdate(); - // Don't enqueue a scroll event yet for scroll reasons that are not about // explicit changes to scroll. Instead, only do so at the time of the next // lifecycle update, to avoid scroll events that are out of date or don't @@ -535,7 +565,8 @@ void PaintLayerScrollableArea::UpdateScrollOffset( // events is at the next lifecycle update (*). // // (*) https://html.spec.whatwg.org/#update-the-rendering steps - if (scroll_type == kClampingScroll || scroll_type == kAnchoringScroll) { + if (scroll_type == mojom::blink::ScrollType::kClamping || + scroll_type == mojom::blink::ScrollType::kAnchoring) { if (GetLayoutBox()->GetNode()) frame_view->SetNeedsEnqueueScrollEvent(this); } else { @@ -549,7 +580,8 @@ void PaintLayerScrollableArea::UpdateScrollOffset( if (is_root_layer) { frame_view->GetFrame().Loader().SaveScrollState(); frame_view->DidChangeScrollOffset(); - if (scroll_type == kCompositorScroll || scroll_type == kUserScroll) { + if (scroll_type == mojom::blink::ScrollType::kCompositor || + scroll_type == mojom::blink::ScrollType::kUser) { if (DocumentLoader* document_loader = frame->Loader().GetDocumentLoader()) document_loader->GetInitialScrollState().was_scrolled_by_user = true; } @@ -559,8 +591,8 @@ void PaintLayerScrollableArea::UpdateScrollOffset( anchor->DidScroll(scroll_type); if (IsExplicitScrollType(scroll_type)) { - if (scroll_type != kCompositorScroll) - ShowOverlayScrollbars(); + if (scroll_type != mojom::blink::ScrollType::kCompositor) + ShowNonMacOverlayScrollbars(); GetScrollAnchor()->Clear(); } if (ContentCaptureManager* manager = @@ -579,7 +611,7 @@ void PaintLayerScrollableArea::InvalidatePaintForScrollOffsetChange() { auto* frame_view = box->GetFrameView(); frame_view->InvalidateBackgroundAttachmentFixedDescendantsOnScroll(*box); - if (box->IsLayoutView() && frame_view->HasViewportConstrainedObjects() && + if (IsA<LayoutView>(box) && frame_view->HasViewportConstrainedObjects() && !frame_view->InvalidateViewportConstrainedObjects()) { box->SetShouldDoFullPaintInvalidation(); box->SetSubtreeShouldCheckForPaintInvalidation(); @@ -590,28 +622,24 @@ void PaintLayerScrollableArea::InvalidatePaintForScrollOffsetChange() { 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::CompositeAfterPaintEnabled() || - UsesCompositedScrolling()) { + if (!box->BackgroundNeedsFullPaintInvalidation()) { auto background_paint_location = box->GetBackgroundPaintLocation(); - background_paint_in_graphics_layer = + bool background_paint_in_graphics_layer = background_paint_location & kBackgroundPaintInGraphicsLayer; - background_paint_in_scrolling_contents = + bool 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(); + // 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 @@ -684,7 +712,8 @@ IntSize PaintLayerScrollableArea::MaximumScrollOffsetInt() const { } void PaintLayerScrollableArea::VisibleSizeChanged() { - ShowOverlayScrollbars(); + InvalidateScrollTimeline(); + ShowNonMacOverlayScrollbars(); } PhysicalRect PaintLayerScrollableArea::LayoutContentRect( @@ -759,6 +788,7 @@ void PaintLayerScrollableArea::ContentsResized() { // Need to update the bounds of the scroll property. GetLayoutBox()->SetNeedsPaintPropertyUpdate(); Layer()->SetNeedsCompositingInputsUpdate(); + InvalidateScrollTimeline(); } IntPoint PaintLayerScrollableArea::LastKnownMousePosition() const { @@ -849,18 +879,19 @@ bool PaintLayerScrollableArea::UserInputScrollable( if (GetLayoutBox()->IsIntrinsicallyScrollable(orientation)) return true; - if (GetLayoutBox()->IsLayoutView()) { + if (IsA<LayoutView>(GetLayoutBox())) { Document& document = GetLayoutBox()->GetDocument(); Element* fullscreen_element = Fullscreen::FullscreenElementFrom(document); if (fullscreen_element && fullscreen_element != document.documentElement()) return false; - ScrollbarMode h_mode; - ScrollbarMode v_mode; - ToLayoutView(GetLayoutBox())->CalculateScrollbarModes(h_mode, v_mode); - ScrollbarMode mode = + mojom::blink::ScrollbarMode h_mode; + mojom::blink::ScrollbarMode v_mode; + To<LayoutView>(GetLayoutBox())->CalculateScrollbarModes(h_mode, v_mode); + mojom::blink::ScrollbarMode mode = (orientation == kHorizontalScrollbar) ? h_mode : v_mode; - return mode == ScrollbarMode::kAuto || mode == ScrollbarMode::kAlwaysOn; + return mode == mojom::blink::ScrollbarMode::kAuto || + mode == mojom::blink::ScrollbarMode::kAlwaysOn; } EOverflow overflow_style = (orientation == kHorizontalScrollbar) @@ -929,9 +960,10 @@ void PaintLayerScrollableArea::UpdateScrollDimensions() { new_overflow_rect.Unite(PhysicalRect( new_overflow_rect.offset, LayoutContentRect(kExcludeScrollbars).size)); - if (overflow_rect_.size != new_overflow_rect.size) - ContentsResized(); + bool resized = overflow_rect_.size != new_overflow_rect.size; overflow_rect_ = new_overflow_rect; + if (resized) + ContentsResized(); UpdateScrollOrigin(); } @@ -956,7 +988,7 @@ void PaintLayerScrollableArea::UpdateScrollbarProportions() { void PaintLayerScrollableArea::SetScrollOffsetUnconditionally( const ScrollOffset& offset, - ScrollType scroll_type) { + mojom::blink::ScrollType scroll_type) { CancelScrollAnimation(); ScrollOffsetChanged(offset, scroll_type); } @@ -1039,7 +1071,7 @@ void PaintLayerScrollableArea::UpdateAfterLayout() { GetLayoutBox()->IsHorizontalWritingMode()) || (horizontal_scrollbar_should_change && !GetLayoutBox()->IsHorizontalWritingMode())) { - GetLayoutBox()->SetPreferredLogicalWidthsDirty(); + GetLayoutBox()->SetIntrinsicLogicalWidthsDirty(); } if (IsManagedByLayoutNG(*GetLayoutBox())) { // If the box is managed by LayoutNG, don't go here. We don't want to @@ -1080,7 +1112,9 @@ void PaintLayerScrollableArea::UpdateAfterLayout() { } else if (!HasScrollbar() && resizer_will_change) { Layer()->DirtyStackingContextZOrderLists(); } - + // The snap container data will be updated at the end of the layout update. If + // the data changes, then this will try to re-snap. + SetSnapContainerDataNeedsUpdate(true); { // Hits in // compositing/overflow/automatically-opt-into-composited-scrolling-after-style-change.html. @@ -1091,13 +1125,6 @@ void PaintLayerScrollableArea::UpdateAfterLayout() { UpdateScrollbarProportions(); } - if (!scrollbars_are_frozen && HasOverlayScrollbars()) { - if (!ScrollSize(kHorizontalScrollbar)) - SetHasHorizontalScrollbar(false); - if (!ScrollSize(kVerticalScrollbar)) - SetHasVerticalScrollbar(false); - } - ClampScrollOffsetAfterOverflowChange(); if (!scrollbars_are_frozen) { @@ -1123,10 +1150,12 @@ void PaintLayerScrollableArea::ClampScrollOffsetAfterOverflowChange() { } UpdateScrollDimensions(); - if (ScrollOriginChanged()) + if (ScrollOriginChanged()) { SetScrollOffsetUnconditionally(ClampScrollOffset(GetScrollOffset())); - else - ScrollableArea::SetScrollOffset(GetScrollOffset(), kClampingScroll); + } else { + ScrollableArea::SetScrollOffset(GetScrollOffset(), + mojom::blink::ScrollType::kClamping); + } SetNeedsScrollOffsetClamp(false); ResetScrollOriginChanged(); @@ -1158,8 +1187,7 @@ void PaintLayerScrollableArea::DidChangeGlobalRootScroller() { // Recalculate the snap container data since the scrolling behaviour for this // layout box changed (i.e. it either became the layout viewport or it // is no longer the layout viewport). - GetLayoutBox()->GetDocument().GetSnapCoordinator().UpdateSnapContainerData( - *GetLayoutBox()); + SetSnapContainerDataNeedsUpdate(true); } bool PaintLayerScrollableArea::ShouldPerformScrollAnchoring() const { @@ -1191,7 +1219,8 @@ PaintLayerScrollableArea::GetTimerTaskRunner() const { return GetLayoutBox()->GetFrame()->GetTaskRunner(TaskType::kInternalDefault); } -ScrollBehavior PaintLayerScrollableArea::ScrollBehaviorStyle() const { +mojom::blink::ScrollBehavior PaintLayerScrollableArea::ScrollBehaviorStyle() + const { return GetLayoutBox()->StyleRef().GetScrollBehavior(); } @@ -1264,9 +1293,8 @@ void PaintLayerScrollableArea::UpdateAfterStyleChange( bool needs_horizontal_scrollbar; bool needs_vertical_scrollbar; - // We add auto scrollbars only during layout to prevent spurious activations. ComputeScrollbarExistence(needs_horizontal_scrollbar, - needs_vertical_scrollbar, kForbidAddingAutoBars); + needs_vertical_scrollbar, kOverflowIndependent); UpdateResizerStyle(old_style); @@ -1288,20 +1316,6 @@ void PaintLayerScrollableArea::UpdateAfterStyleChange( LayoutBlock::ScrollbarChangeContext::kStyleChange); } - // With overflow: scroll, scrollbars are always visible but may be disabled. - // When switching to another value, we need to re-enable them (see bug 11985). - if (HasHorizontalScrollbar() && old_style && - old_style->OverflowX() == EOverflow::kScroll && - GetLayoutBox()->StyleRef().OverflowX() != EOverflow::kScroll) { - HorizontalScrollbar()->SetEnabled(true); - } - - if (HasVerticalScrollbar() && old_style && - old_style->OverflowY() == EOverflow::kScroll && - GetLayoutBox()->StyleRef().OverflowY() != EOverflow::kScroll) { - VerticalScrollbar()->SetEnabled(true); - } - // FIXME: Need to detect a swap from custom to native scrollbars (and vice // versa). if (HorizontalScrollbar()) @@ -1310,6 +1324,14 @@ void PaintLayerScrollableArea::UpdateAfterStyleChange( VerticalScrollbar()->StyleChanged(); UpdateScrollCornerStyle(); + + if (!RuntimeEnabledFeatures::CompositeAfterPaintEnabled()) { + bool vertical_scrollbar_on_left = ShouldPlaceVerticalScrollbarOnLeft(); + if (vertical_scrollbar_on_left != previous_vertical_scrollbar_on_left_) { + rebuild_vertical_scrollbar_layer_ = true; + previous_vertical_scrollbar_on_left_ = vertical_scrollbar_on_left; + } + } } void PaintLayerScrollableArea::UpdateAfterOverflowRecalc() { @@ -1339,58 +1361,51 @@ void PaintLayerScrollableArea::UpdateAfterOverflowRecalc() { UpdateScrollableAreaSet(); } -IntRect PaintLayerScrollableArea::RectForHorizontalScrollbar( - const IntRect& border_box_rect) const { +IntRect PaintLayerScrollableArea::RectForHorizontalScrollbar() const { if (!HasHorizontalScrollbar()) return IntRect(); const IntRect& scroll_corner = ScrollCornerRect(); - + IntSize border_box_size = PixelSnappedBorderBoxSize(); return IntRect( - HorizontalScrollbarStart(border_box_rect.X()), - border_box_rect.MaxY() - GetLayoutBox()->BorderBottom().ToInt() - + HorizontalScrollbarStart(), + border_box_size.Height() - GetLayoutBox()->BorderBottom().ToInt() - HorizontalScrollbar()->ScrollbarThickness(), - border_box_rect.Width() - + border_box_size.Width() - (GetLayoutBox()->BorderLeft() + GetLayoutBox()->BorderRight()) .ToInt() - scroll_corner.Width(), HorizontalScrollbar()->ScrollbarThickness()); } -IntRect PaintLayerScrollableArea::RectForVerticalScrollbar( - const IntRect& border_box_rect) const { +IntRect PaintLayerScrollableArea::RectForVerticalScrollbar() const { if (!HasVerticalScrollbar()) return IntRect(); const IntRect& scroll_corner = ScrollCornerRect(); - return IntRect( - VerticalScrollbarStart(border_box_rect.X(), border_box_rect.MaxX()), - border_box_rect.Y() + GetLayoutBox()->BorderTop().ToInt(), + VerticalScrollbarStart(), GetLayoutBox()->BorderTop().ToInt(), VerticalScrollbar()->ScrollbarThickness(), - border_box_rect.Height() - + PixelSnappedBorderBoxSize().Height() - (GetLayoutBox()->BorderTop() + GetLayoutBox()->BorderBottom()) .ToInt() - scroll_corner.Height()); } -int PaintLayerScrollableArea::VerticalScrollbarStart(int min_x, - int max_x) const { +int PaintLayerScrollableArea::VerticalScrollbarStart() const { if (GetLayoutBox()->ShouldPlaceBlockDirectionScrollbarOnLogicalLeft()) - return min_x + GetLayoutBox()->BorderLeft().ToInt(); - return max_x - GetLayoutBox()->BorderRight().ToInt() - + return GetLayoutBox()->BorderLeft().ToInt(); + return PixelSnappedBorderBoxSize().Width() - + GetLayoutBox()->BorderRight().ToInt() - VerticalScrollbar()->ScrollbarThickness(); } -int PaintLayerScrollableArea::HorizontalScrollbarStart(int min_x) const { - int x = min_x + GetLayoutBox()->BorderLeft().ToInt(); - if (GetLayoutBox()->ShouldPlaceBlockDirectionScrollbarOnLogicalLeft()) - x += HasVerticalScrollbar() - ? VerticalScrollbar()->ScrollbarThickness() - : ResizerCornerRect(GetLayoutBox()->PixelSnappedBorderBoxRect( - Layer()->SubpixelAccumulation()), - kResizerForPointer) - .Width(); +int PaintLayerScrollableArea::HorizontalScrollbarStart() const { + int x = GetLayoutBox()->BorderLeft().ToInt(); + if (GetLayoutBox()->ShouldPlaceBlockDirectionScrollbarOnLogicalLeft()) { + x += HasVerticalScrollbar() ? VerticalScrollbar()->ScrollbarThickness() + : ResizerCornerRect(kResizerForPointer).Width(); + } return x; } @@ -1399,13 +1414,12 @@ IntSize PaintLayerScrollableArea::ScrollbarOffset( // TODO(szager): Factor out vertical offset calculation into other methods, // for symmetry with *ScrollbarStart methods for horizontal offset. if (&scrollbar == VerticalScrollbar()) { - return IntSize( - VerticalScrollbarStart(0, Layer()->PixelSnappedSize().Width()), - GetLayoutBox()->BorderTop().ToInt()); + return IntSize(VerticalScrollbarStart(), + GetLayoutBox()->BorderTop().ToInt()); } if (&scrollbar == HorizontalScrollbar()) { - return IntSize(HorizontalScrollbarStart(0), + return IntSize(HorizontalScrollbarStart(), GetLayoutBox()->BorderTop().ToInt() + VisibleContentRect(kIncludeScrollbars).Height() - HorizontalScrollbar()->ScrollbarThickness()); @@ -1417,7 +1431,7 @@ IntSize PaintLayerScrollableArea::ScrollbarOffset( static inline const LayoutObject& ScrollbarStyleSource( const LayoutBox& layout_box) { - if (layout_box.IsLayoutView()) { + if (IsA<LayoutView>(layout_box)) { Document& doc = layout_box.GetDocument(); if (Settings* settings = doc.GetSettings()) { if (!settings->GetAllowCustomScrollbarInMainFrame() && @@ -1532,45 +1546,89 @@ void PaintLayerScrollableArea::ComputeScrollbarExistence( return; } - needs_horizontal_scrollbar = GetLayoutBox()->ScrollsOverflowX(); - needs_vertical_scrollbar = GetLayoutBox()->ScrollsOverflowY(); + mojom::blink::ScrollbarMode h_mode = mojom::blink::ScrollbarMode::kAuto; + mojom::blink::ScrollbarMode v_mode = mojom::blink::ScrollbarMode::kAuto; - // Don't add auto scrollbars if the box contents aren't visible. - if (GetLayoutBox()->HasAutoHorizontalScrollbar()) { - if (option == kForbidAddingAutoBars) - needs_horizontal_scrollbar &= HasHorizontalScrollbar(); - needs_horizontal_scrollbar &= - GetLayoutBox()->IsRooted() && HasHorizontalOverflow() && - VisibleContentRect(kIncludeScrollbars).Height(); - } + // First, determine what behavior the scrollbars say they should have. + { + if (auto* layout_view = DynamicTo<LayoutView>(GetLayoutBox())) { + // LayoutView is special as there's various quirks and settings that + // style doesn't account for. + layout_view->CalculateScrollbarModes(h_mode, v_mode); + } else { + auto overflow_x = GetLayoutBox()->StyleRef().OverflowX(); + if (overflow_x == EOverflow::kScroll) { + h_mode = mojom::blink::ScrollbarMode::kAlwaysOn; + } else if (overflow_x == EOverflow::kHidden || + overflow_x == EOverflow::kVisible) { + h_mode = mojom::blink::ScrollbarMode::kAlwaysOff; + } + + auto overflow_y = GetLayoutBox()->StyleRef().OverflowY(); + if (overflow_y == EOverflow::kScroll) { + v_mode = mojom::blink::ScrollbarMode::kAlwaysOn; + } else if (overflow_y == EOverflow::kHidden || + overflow_y == EOverflow::kVisible) { + v_mode = mojom::blink::ScrollbarMode::kAlwaysOff; + } + } - if (GetLayoutBox()->HasAutoVerticalScrollbar()) { - if (option == kForbidAddingAutoBars) - needs_vertical_scrollbar &= HasVerticalScrollbar(); - needs_vertical_scrollbar &= GetLayoutBox()->IsRooted() && - HasVerticalOverflow() && - VisibleContentRect(kIncludeScrollbars).Width(); + // Since overlay scrollbars (the fade-in/out kind, not overflow: overlay) + // only appear when scrolling, we don't create them if there isn't overflow + // to scroll. Thus, overlay scrollbars can't be "always on". i.e. + // |overlay:scroll| behaves like |overlay:auto|. + bool has_custom_scrollbar_style = + ScrollbarStyleSource(*GetLayoutBox()) + .StyleRef() + .HasPseudoElementStyle(kPseudoIdScrollbar); + bool will_be_overlay = GetPageScrollbarTheme().UsesOverlayScrollbars() && + !has_custom_scrollbar_style; + if (will_be_overlay) { + if (h_mode == mojom::blink::ScrollbarMode::kAlwaysOn) + h_mode = mojom::blink::ScrollbarMode::kAuto; + if (v_mode == mojom::blink::ScrollbarMode::kAlwaysOn) + v_mode = mojom::blink::ScrollbarMode::kAuto; + } } - if (GetLayoutBox()->IsLayoutView()) { - ScrollbarMode h_mode; - ScrollbarMode v_mode; - ToLayoutView(GetLayoutBox())->CalculateScrollbarModes(h_mode, v_mode); + // By default, don't make any changes. + needs_horizontal_scrollbar = HasHorizontalScrollbar(); + needs_vertical_scrollbar = HasVerticalScrollbar(); - // Look for the scrollbarModes and reset the needs Horizontal & vertical - // Scrollbar values based on scrollbarModes, as during force style change - // StyleResolver::styleForDocument returns documentStyle with no overflow - // values, due to which we are destroying the scrollbars that were already - // present. - if (h_mode == ScrollbarMode::kAlwaysOn) + // If the behavior doesn't depend on overflow or any other information, we + // can set it now. + { + if (h_mode == mojom::blink::ScrollbarMode::kAlwaysOn) needs_horizontal_scrollbar = true; - else if (h_mode == ScrollbarMode::kAlwaysOff) + else if (h_mode == mojom::blink::ScrollbarMode::kAlwaysOff) needs_horizontal_scrollbar = false; - if (v_mode == ScrollbarMode::kAlwaysOn) + + if (v_mode == mojom::blink::ScrollbarMode::kAlwaysOn) needs_vertical_scrollbar = true; - else if (v_mode == ScrollbarMode::kAlwaysOff) + else if (v_mode == mojom::blink::ScrollbarMode::kAlwaysOff) needs_vertical_scrollbar = false; } + + // If this is being performed before layout, we want to only update scrollbar + // existence if its based on purely style based reasons. + if (option == kOverflowIndependent) + return; + + // If we have clean layout, we can make a decision on any scrollbars that + // depend on overflow. + { + if (h_mode == mojom::blink::ScrollbarMode::kAuto) { + // Don't add auto scrollbars if the box contents aren't visible. + needs_horizontal_scrollbar = + GetLayoutBox()->IsRooted() && HasHorizontalOverflow() && + VisibleContentRect(kIncludeScrollbars).Height(); + } + if (v_mode == mojom::blink::ScrollbarMode::kAuto) { + needs_vertical_scrollbar = GetLayoutBox()->IsRooted() && + HasVerticalOverflow() && + VisibleContentRect(kIncludeScrollbars).Width(); + } + } } bool PaintLayerScrollableArea::TryRemovingAutoScrollbars( @@ -1584,11 +1642,12 @@ bool PaintLayerScrollableArea::TryRemovingAutoScrollbars( if (!needs_horizontal_scrollbar && !needs_vertical_scrollbar) return false; - if (GetLayoutBox()->IsLayoutView()) { - ScrollbarMode h_mode; - ScrollbarMode v_mode; - ToLayoutView(GetLayoutBox())->CalculateScrollbarModes(h_mode, v_mode); - if (h_mode != ScrollbarMode::kAuto || v_mode != ScrollbarMode::kAuto) + if (auto* layout_view = DynamicTo<LayoutView>(GetLayoutBox())) { + mojom::blink::ScrollbarMode h_mode; + mojom::blink::ScrollbarMode v_mode; + layout_view->CalculateScrollbarModes(h_mode, v_mode); + if (h_mode != mojom::blink::ScrollbarMode::kAuto || + v_mode != mojom::blink::ScrollbarMode::kAuto) return false; IntSize visible_size_with_scrollbars = @@ -1738,6 +1797,29 @@ bool PaintLayerScrollableArea::SetTargetSnapAreaElementIds( return false; } +bool PaintLayerScrollableArea::SnapContainerDataNeedsUpdate() const { + return RareData() ? RareData()->snap_container_data_needs_update_ : false; +} + +void PaintLayerScrollableArea::SetSnapContainerDataNeedsUpdate( + bool needs_update) { + EnsureRareData().snap_container_data_needs_update_ = needs_update; + if (!needs_update) + return; + GetLayoutBox() + ->GetDocument() + .GetSnapCoordinator() + .SetAnySnapContainerDataNeedsUpdate(true); +} + +bool PaintLayerScrollableArea::NeedsResnap() const { + return RareData() ? RareData()->needs_resnap_ : false; +} + +void PaintLayerScrollableArea::SetNeedsResnap(bool needs_resnap) { + EnsureRareData().needs_resnap_ = needs_resnap; +} + base::Optional<FloatPoint> PaintLayerScrollableArea::GetSnapPositionAndSetTarget( const cc::SnapSelectionStrategy& strategy) { @@ -1779,21 +1861,17 @@ void PaintLayerScrollableArea::PositionOverflowControls() { if (!HasOverflowControls()) return; - const IntRect border_box = - GetLayoutBox()->PixelSnappedBorderBoxRect(layer_->SubpixelAccumulation()); - if (Scrollbar* vertical_scrollbar = VerticalScrollbar()) - vertical_scrollbar->SetFrameRect(RectForVerticalScrollbar(border_box)); + vertical_scrollbar->SetFrameRect(RectForVerticalScrollbar()); if (Scrollbar* horizontal_scrollbar = HorizontalScrollbar()) - horizontal_scrollbar->SetFrameRect(RectForHorizontalScrollbar(border_box)); + horizontal_scrollbar->SetFrameRect(RectForHorizontalScrollbar()); if (scroll_corner_) scroll_corner_->SetFrameRect(LayoutRect(ScrollCornerRect())); if (resizer_) - resizer_->SetFrameRect( - LayoutRect(ResizerCornerRect(border_box, kResizerForPointer))); + resizer_->SetFrameRect(LayoutRect(ResizerCornerRect(kResizerForPointer))); // FIXME, this should eventually be removed, once we are certain that // composited controls get correctly positioned on a compositor update. For @@ -1837,10 +1915,7 @@ bool PaintLayerScrollableArea::HitTestOverflowControls( IntRect resize_control_rect; if (GetLayoutBox()->StyleRef().HasResize()) { - resize_control_rect = - ResizerCornerRect(GetLayoutBox()->PixelSnappedBorderBoxRect( - Layer()->SubpixelAccumulation()), - kResizerForPointer); + resize_control_rect = ResizerCornerRect(kResizerForPointer); if (resize_control_rect.Contains(local_point)) return true; } @@ -1850,14 +1925,13 @@ bool PaintLayerScrollableArea::HitTestOverflowControls( if (HasVerticalScrollbar() && VerticalScrollbar()->ShouldParticipateInHitTesting()) { - LayoutRect v_bar_rect( - VerticalScrollbarStart(0, Layer()->PixelSnappedSize().Width()), - GetLayoutBox()->BorderTop().ToInt(), - VerticalScrollbar()->ScrollbarThickness(), - visible_rect.Height() - - (HasHorizontalScrollbar() - ? HorizontalScrollbar()->ScrollbarThickness() - : resize_control_size)); + LayoutRect v_bar_rect(VerticalScrollbarStart(), + GetLayoutBox()->BorderTop().ToInt(), + VerticalScrollbar()->ScrollbarThickness(), + visible_rect.Height() - + (HasHorizontalScrollbar() + ? HorizontalScrollbar()->ScrollbarThickness() + : resize_control_size)); if (v_bar_rect.Contains(local_point)) { result.SetScrollbar(VerticalScrollbar()); return true; @@ -1870,7 +1944,7 @@ bool PaintLayerScrollableArea::HitTestOverflowControls( // TODO(crbug.com/638981): Are the conversions to int intentional? int h_scrollbar_thickness = HorizontalScrollbar()->ScrollbarThickness(); LayoutRect h_bar_rect( - HorizontalScrollbarStart(0), + HorizontalScrollbarStart(), GetLayoutBox()->BorderTop().ToInt() + visible_rect.Height() - h_scrollbar_thickness, visible_rect.Width() - (HasVerticalScrollbar() @@ -1890,11 +1964,10 @@ bool PaintLayerScrollableArea::HitTestOverflowControls( } IntRect PaintLayerScrollableArea::ResizerCornerRect( - const IntRect& bounds, ResizerHitTestType resizer_hit_test_type) const { if (!GetLayoutBox()->StyleRef().HasResize()) return IntRect(); - IntRect corner = CornerRect(bounds); + IntRect corner = CornerRect(); if (resizer_hit_test_type == kResizerForTouch) { // We make the resizer virtually larger for touch hit testing. With the @@ -1913,12 +1986,8 @@ IntRect PaintLayerScrollableArea::ResizerCornerRect( IntRect PaintLayerScrollableArea::ScrollCornerAndResizerRect() const { IntRect scroll_corner_and_resizer = ScrollCornerRect(); - if (scroll_corner_and_resizer.IsEmpty()) { - scroll_corner_and_resizer = - ResizerCornerRect(GetLayoutBox()->PixelSnappedBorderBoxRect( - Layer()->SubpixelAccumulation()), - kResizerForPointer); - } + if (scroll_corner_and_resizer.IsEmpty()) + return ResizerCornerRect(kResizerForPointer); return scroll_corner_and_resizer; } @@ -1930,9 +1999,7 @@ bool PaintLayerScrollableArea::IsPointInResizeControl( IntPoint local_point = RoundedIntPoint( GetLayoutBox()->AbsoluteToLocalPoint(PhysicalOffset(absolute_point))); - IntRect local_bounds(IntPoint(), Layer()->PixelSnappedSize()); - return ResizerCornerRect(local_bounds, resizer_hit_test_type) - .Contains(local_point); + return ResizerCornerRect(resizer_hit_test_type).Contains(local_point); } bool PaintLayerScrollableArea::HitTestResizerInFragments( @@ -1946,11 +2013,12 @@ bool PaintLayerScrollableArea::HitTestResizerInFragments( for (int i = layer_fragments.size() - 1; i >= 0; --i) { const PaintLayerFragment& fragment = layer_fragments.at(i); - if (fragment.background_rect.Intersects(hit_test_location) && - ResizerCornerRect(PixelSnappedIntRect(fragment.layer_bounds), - kResizerForPointer) - .Contains(hit_test_location.RoundedPoint())) - return true; + if (fragment.background_rect.Intersects(hit_test_location)) { + IntRect resizer_corner_rect = ResizerCornerRect(kResizerForPointer); + resizer_corner_rect.MoveBy(RoundedIntPoint(fragment.layer_bounds.offset)); + if (resizer_corner_rect.Contains(hit_test_location.RoundedPoint())) + return true; + } } return false; @@ -2002,12 +2070,10 @@ void PaintLayerScrollableArea::InvalidateAllStickyConstraints() { } void PaintLayerScrollableArea::InvalidateStickyConstraintsFor( - PaintLayer* layer, - bool needs_compositing_update) { + PaintLayer* layer) { if (PaintLayerScrollableAreaRareData* d = RareData()) { d->sticky_constraints_map_.erase(layer); - if (needs_compositing_update && - layer->GetLayoutObject().StyleRef().HasStickyConstrainedPosition()) { + if (layer->GetLayoutObject().StyleRef().HasStickyConstrainedPosition()) { layer->SetNeedsCompositingInputsUpdate(); layer->GetLayoutObject().SetNeedsPaintPropertyUpdate(); } @@ -2037,7 +2103,7 @@ IntSize PaintLayerScrollableArea::OffsetFromResizeCorner( // left corner. // FIXME: This assumes the location is 0, 0. Is this guaranteed to always be // the case? - IntSize element_size = Layer()->PixelSnappedSize(); + IntSize element_size = PixelSnappedBorderBoxSize(); if (GetLayoutBox()->ShouldPlaceBlockDirectionScrollbarOnLogicalLeft()) element_size.SetWidth(0); IntPoint resizer_point = IntPoint(element_size); @@ -2143,15 +2209,15 @@ void PaintLayerScrollableArea::Resize(const IntPoint& pos, CSSPrimitiveValue::UnitType::kPixels); } - document.UpdateStyleAndLayout(); + document.UpdateStyleAndLayout(DocumentUpdateReason::kSizeChange); - // FIXME (Radar 4118564): We should also autoscroll the window as necessary to + // FIXME: We should also autoscroll the window as necessary to // keep the point under the cursor in view. } PhysicalRect PaintLayerScrollableArea::ScrollIntoView( const PhysicalRect& absolute_rect, - const WebScrollIntoViewParams& params) { + const mojom::blink::ScrollIntoViewParamsPtr& params) { PhysicalRect local_expose_rect = GetLayoutBox()->AbsoluteToLocalRect(absolute_rect); PhysicalOffset border_origin_to_scroll_origin(-GetLayoutBox()->BorderLeft(), @@ -2168,13 +2234,13 @@ PhysicalRect PaintLayerScrollableArea::ScrollIntoView( PhysicalRect scroll_snapport_rect = VisibleScrollSnapportRect(); ScrollOffset target_offset = ScrollAlignment::GetScrollOffsetToExpose( - scroll_snapport_rect, local_expose_rect, params.GetScrollAlignmentX(), - params.GetScrollAlignmentY(), GetScrollOffset()); + scroll_snapport_rect, local_expose_rect, *params->align_x.get(), + *params->align_y.get(), GetScrollOffset()); ScrollOffset new_scroll_offset( ClampScrollOffset(RoundedIntSize(target_offset))); ScrollOffset old_scroll_offset = GetScrollOffset(); - if (params.GetScrollType() == kUserScroll) { + if (params->type == mojom::blink::ScrollType::kUser) { if (!UserInputScrollable(kHorizontalScrollbar)) new_scroll_offset.SetWidth(old_scroll_offset.Width()); if (!UserInputScrollable(kVerticalScrollbar)) @@ -2188,17 +2254,16 @@ PhysicalRect PaintLayerScrollableArea::ScrollIntoView( end_point = GetSnapPositionAndSetTarget(*strategy).value_or(end_point); new_scroll_offset = ScrollPositionToOffset(end_point); - if (params.is_for_scroll_sequence) { - DCHECK(params.GetScrollType() == kProgrammaticScroll || - params.GetScrollType() == kUserScroll); - ScrollBehavior behavior = - DetermineScrollBehavior(params.GetScrollBehavior(), - GetLayoutBox()->StyleRef().GetScrollBehavior()); + if (params->is_for_scroll_sequence) { + DCHECK(params->type == mojom::blink::ScrollType::kProgrammatic || + params->type == mojom::blink::ScrollType::kUser); + mojom::blink::ScrollBehavior behavior = DetermineScrollBehavior( + params->behavior, GetLayoutBox()->StyleRef().GetScrollBehavior()); GetSmoothScrollSequencer()->QueueAnimation(this, new_scroll_offset, behavior); } else { - SetScrollOffset(new_scroll_offset, params.GetScrollType(), - kScrollBehaviorInstant); + SetScrollOffset(new_scroll_offset, params->type, + mojom::blink::ScrollBehavior::kInstant); } ScrollOffset scroll_offset_difference = new_scroll_offset - old_scroll_offset; @@ -2239,12 +2304,12 @@ void PaintLayerScrollableArea::UpdateScrollableAreaSet() { bool is_visible_to_hit_test = GetLayoutBox()->StyleRef().VisibleToHitTesting(); bool did_scroll_overflow = scrolls_overflow_; - if (GetLayoutBox()->IsLayoutView()) { - ScrollbarMode h_mode; - ScrollbarMode v_mode; - ToLayoutView(GetLayoutBox())->CalculateScrollbarModes(h_mode, v_mode); - if (h_mode == ScrollbarMode::kAlwaysOff && - v_mode == ScrollbarMode::kAlwaysOff) + if (auto* layout_view = DynamicTo<LayoutView>(GetLayoutBox())) { + mojom::blink::ScrollbarMode h_mode; + mojom::blink::ScrollbarMode v_mode; + layout_view->CalculateScrollbarModes(h_mode, v_mode); + if (h_mode == mojom::blink::ScrollbarMode::kAlwaysOff && + v_mode == mojom::blink::ScrollbarMode::kAlwaysOff) has_overflow = false; } @@ -2264,7 +2329,7 @@ void PaintLayerScrollableArea::UpdateScrollableAreaSet() { if (RuntimeEnabledFeatures::ImplicitRootScrollerEnabled() && scrolls_overflow_) { - if (GetLayoutBox()->IsLayoutView()) { + if (IsA<LayoutView>(GetLayoutBox())) { if (Element* owner = GetLayoutBox()->GetDocument().LocalOwner()) { owner->GetDocument().GetRootScrollerController().ConsiderForImplicit( *owner); @@ -2281,8 +2346,8 @@ void PaintLayerScrollableArea::UpdateScrollableAreaSet() { // PaintPropertyTreeBuilder::updateScrollAndScrollTranslation). GetLayoutBox()->SetNeedsPaintPropertyUpdate(); - // Scroll hit test display items depend on whether the box scrolls overflow. - // The scroll hit test display items paint in the background phase + // Scroll hit test data depend on whether the box scrolls overflow. + // They are painted in the background phase // (see: BoxPainter::PaintBoxDecorationBackground). GetLayoutBox()->SetBackgroundNeedsFullPaintInvalidation(); @@ -2290,16 +2355,18 @@ void PaintLayerScrollableArea::UpdateScrollableAreaSet() { } void PaintLayerScrollableArea::UpdateCompositingLayersAfterScroll() { + DCHECK(!RuntimeEnabledFeatures::CompositeAfterPaintEnabled()); + PaintLayerCompositor* compositor = GetLayoutBox()->View()->Compositor(); - if (!compositor->InCompositingMode()) + if (!compositor || !compositor->InCompositingMode()) return; if (UsesCompositedScrolling()) { DCHECK(Layer()->HasCompositedLayerMapping()); ScrollingCoordinator* scrolling_coordinator = GetScrollingCoordinator(); - bool handled_scroll = - scrolling_coordinator && - scrolling_coordinator->UpdateCompositedScrollOffset(this); + bool handled_scroll = scrolling_coordinator && + scrolling_coordinator->UpdateCompositorScrollOffset( + *GetLayoutBox()->GetFrame(), *this); if (!handled_scroll) { compositor->SetNeedsCompositingUpdate( @@ -2309,15 +2376,18 @@ void PaintLayerScrollableArea::UpdateCompositingLayersAfterScroll() { // 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. - if (Layer()->IsRootLayer()) { - LocalFrame* frame = GetLayoutBox()->GetFrame(); - if (frame && frame->View() && - frame->View()->HasViewportConstrainedObjects()) { - Layer()->SetNeedsCompositingInputsUpdate(); + if (!base::FeatureList::IsEnabled( + features::kAssumeOverlapAfterFixedOrStickyPosition)) { + if (Layer()->IsRootLayer()) { + LocalFrame* frame = GetLayoutBox()->GetFrame(); + if (frame && frame->View() && + frame->View()->HasViewportConstrainedObjects()) { + Layer()->SetNeedsCompositingInputsUpdate(); + } } } } else { - Layer()->SetNeedsCompositingInputsUpdate(); + Layer()->SetNeedsCompositingInputsUpdate(false); } } @@ -2385,7 +2455,29 @@ bool PaintLayerScrollableArea::ComputeNeedsCompositedScrolling( : DocumentLifecycle::kInCompositingUpdate, GetDocument()->Lifecycle().GetState()); + const auto* box = GetLayoutBox(); + auto old_background_paint_location = box->GetBackgroundPaintLocation(); non_composited_main_thread_scrolling_reasons_ = 0; + auto new_background_paint_location = + box->ComputeBackgroundPaintLocationIfComposited( + &non_composited_main_thread_scrolling_reasons_); + bool needs_composited_scrolling = ComputeNeedsCompositedScrollingInternal( + new_background_paint_location, force_prefer_compositing_to_lcd_text); + if (!needs_composited_scrolling) + new_background_paint_location = kBackgroundPaintInGraphicsLayer; + if (new_background_paint_location != old_background_paint_location) { + box->GetMutableForPainting().SetBackgroundPaintLocation( + new_background_paint_location); + } + + return needs_composited_scrolling; +} + +bool PaintLayerScrollableArea::ComputeNeedsCompositedScrollingInternal( + BackgroundPaintLocation background_paint_location_if_composited, + bool force_prefer_compositing_to_lcd_text) { + DCHECK_EQ(background_paint_location_if_composited, + GetLayoutBox()->ComputeBackgroundPaintLocationIfComposited()); if (CompositingReasonFinder::RequiresCompositingForRootScroller(*layer_)) return true; @@ -2419,15 +2511,16 @@ bool PaintLayerScrollableArea::ComputeNeedsCompositedScrolling( // transforms are also integer. bool background_supports_lcd_text = box->StyleRef().IsStackingContext() && - (box->GetBackgroundPaintLocation( - &non_composited_main_thread_scrolling_reasons_) & + (background_paint_location_if_composited & kBackgroundPaintInScrollingContents) && layer_->BackgroundIsKnownToBeOpaqueInRect(box->PhysicalPaddingBoxRect(), true) && !layer_->CompositesWithTransform() && !layer_->CompositesWithOpacity(); if (!force_prefer_compositing_to_lcd_text && - !layer_->Compositor()->PreferCompositingToLCDTextEnabled() && + !box->GetDocument() + .GetSettings() + ->GetPreferCompositingToLCDTextEnabled() && !background_supports_lcd_text) { if (layer_->CompositesWithOpacity()) { non_composited_main_thread_scrolling_reasons_ |= @@ -2511,12 +2604,13 @@ PaintLayerScrollableArea::GetCompositorAnimationTimeline() const { } bool PaintLayerScrollableArea::HasTickmarks() const { - return layer_->IsRootLayer() && ToLayoutView(GetLayoutBox())->HasTickmarks(); + return layer_->IsRootLayer() && + To<LayoutView>(GetLayoutBox())->HasTickmarks(); } Vector<IntRect> PaintLayerScrollableArea::GetTickmarks() const { if (layer_->IsRootLayer()) - return ToLayoutView(GetLayoutBox())->GetTickmarks(); + return To<LayoutView>(GetLayoutBox())->GetTickmarks(); return Vector<IntRect>(); } @@ -2571,7 +2665,7 @@ Scrollbar* PaintLayerScrollableArea::ScrollbarManager::CreateScrollbar( style_source.StyleRef().HasPseudoElementStyle(kPseudoIdScrollbar); if (has_custom_scrollbar_style) { DCHECK(style_source.GetNode() && style_source.GetNode()->IsElementNode()); - scrollbar = CustomScrollbar::CreateCustomScrollbar( + scrollbar = MakeGarbageCollected<CustomScrollbar>( ScrollableArea(), orientation, To<Element>(style_source.GetNode())); } else { ScrollbarControlSize scrollbar_size = kRegularScrollbar; @@ -2580,7 +2674,7 @@ Scrollbar* PaintLayerScrollableArea::ScrollbarManager::CreateScrollbar( style_source.StyleRef().EffectiveAppearance()); } Element* style_source_element = nullptr; - if (RuntimeEnabledFeatures::FormControlsRefreshEnabled()) { + if (::features::IsFormControlsRefreshEnabled()) { style_source_element = DynamicTo<Element>(style_source.GetNode()); } scrollbar = MakeGarbageCollected<Scrollbar>( @@ -2832,8 +2926,13 @@ static IntRect InvalidatePaintOfScrollbarIfNeeded( bool is_overlay = scrollbar && scrollbar->IsOverlayScrollbar(); IntRect new_visual_rect; - if (scrollbar) + if (scrollbar) { new_visual_rect = scrollbar->FrameRect(); + // TODO(crbug.com/1020913): We should not round paint_offset but should + // consider subpixel accumulation when painting scrollbars. + new_visual_rect.MoveBy( + RoundedIntPoint(context.fragment_data->PaintOffset())); + } if (needs_paint_invalidation && graphics_layer) { // If the scrollbar needs paint invalidation but didn't change location/size @@ -2906,8 +3005,10 @@ void PaintLayerScrollableArea::InvalidatePaintOfScrollControlsIfNeeded( box_geometry_has_been_invalidated, context)); IntRect scroll_corner_and_resizer_visual_rect = ScrollCornerAndResizerRect(); + // TODO(crbug.com/1020913): We should not round paint_offset but should + // consider subpixel accumulation when painting scrollbars. scroll_corner_and_resizer_visual_rect.MoveBy( - RoundedIntPoint(box.FirstFragment().PaintOffset())); + RoundedIntPoint(context.fragment_data->PaintOffset())); if (ScrollControlNeedsPaintInvalidation( scroll_corner_and_resizer_visual_rect, scroll_corner_and_resizer_visual_rect_, @@ -3020,10 +3121,21 @@ CompositorElementId PaintLayerScrollableArea::GetScrollElementId() const { GetLayoutBox()->UniqueId(), CompositorElementIdNamespace::kScroll); } +IntSize PaintLayerScrollableArea::PixelSnappedBorderBoxSize() const { + // TODO(crbug.com/1020913): We use this method during + // PositionOverflowControls() even before the paint offset is updated. + // This can be fixed only after we support subpixels in overflow control + // geometry. For now we ensure correct pixel snapping of overflow controls by + // calling PositionOverflowControls() again when paint offset is updated. + return GetLayoutBox()->PixelSnappedBorderBoxSize( + GetLayoutBox()->FirstFragment().PaintOffset()); +} + IntRect PaintLayerScrollableArea::ScrollingBackgroundDisplayItemClient::VisualRect() const { const auto* box = scrollable_area_->GetLayoutBox(); + const auto& paint_offset = box->FirstFragment().PaintOffset(); auto overflow_clip_rect = PixelSnappedIntRect(box->OverflowClipRect(paint_offset)); @@ -3037,6 +3149,30 @@ PaintLayerScrollableArea::ScrollingBackgroundDisplayItemClient::VisualRect() scrollable_area_->layer_->GraphicsLayerBacking()->VisualRect()); } #endif + + // The HTML element of a document is special, in that it can have a transform, + // but the bounds of the painted area of the element still extends beyond + // its actual size to encompass the entire viewport canvas. This is + // accomplished in ViewPainter by starting with a rect in viewport canvas + // space that is equal to the size of the viewport canvas, then mapping it + // into the local border box space of the HTML element, and painting a rect + // equal to the bounding box of the result. We need to add in that mapped rect + // in such cases. + const Document& document = box->GetDocument(); + if (IsA<LayoutView>(box) && + (document.IsXMLDocument() || document.IsHTMLDocument())) { + if (const auto* document_element = document.documentElement()) { + if (const auto* document_element_object = + document_element->GetLayoutObject()) { + TransformationMatrix matrix; + document_element_object->GetTransformFromContainer( + box, PhysicalOffset(), matrix); + if (matrix.IsInvertible()) + result.Unite(matrix.Inverse().MapRect(result)); + } + } + } + return result; } @@ -3054,12 +3190,6 @@ PaintLayerScrollableArea::ScrollingBackgroundDisplayItemClient::OwnerNodeId() ->OwnerNodeId(); } -bool PaintLayerScrollableArea::ScrollingBackgroundDisplayItemClient:: - PaintedOutputOfObjectHasNoEffectRegardlessOfSize() const { - return scrollable_area_->GetLayoutBox() - ->PaintedOutputOfObjectHasNoEffectRegardlessOfSize(); -} - IntRect PaintLayerScrollableArea::ScrollCornerDisplayItemClient::VisualRect() const { return scrollable_area_->scroll_corner_and_resizer_visual_rect_; 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 c5a4495f359..afe17a03130 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 @@ -46,6 +46,7 @@ #include <memory> #include "base/macros.h" +#include "third_party/blink/public/mojom/scroll/scroll_into_view_params.mojom-blink-forward.h" #include "third_party/blink/renderer/core/core_export.h" #include "third_party/blink/renderer/core/layout/scroll_anchor.h" #include "third_party/blink/renderer/core/page/scrolling/sticky_position_scrolling_constraints.h" @@ -77,6 +78,8 @@ struct CORE_EXPORT PaintLayerScrollableAreaRareData { StickyConstraintsMap sticky_constraints_map_; base::Optional<cc::SnapContainerData> snap_container_data_; + bool snap_container_data_needs_update_ = true; + bool needs_resnap_ = false; DISALLOW_COPY_AND_ASSIGN(PaintLayerScrollableAreaRareData); }; @@ -162,7 +165,7 @@ class CORE_EXPORT PaintLayerScrollableArea final void DestroyDetachedScrollbars(); void Dispose(); - void Trace(blink::Visitor*); + void Trace(Visitor*); private: Scrollbar* CreateScrollbar(ScrollbarOrientation); @@ -246,10 +249,6 @@ 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 MakeGarbageCollected<PaintLayerScrollableArea>(layer); - } - explicit PaintLayerScrollableArea(PaintLayer&); ~PaintLayerScrollableArea() override; @@ -345,7 +344,7 @@ class CORE_EXPORT PaintLayerScrollableArea final bool UserInputScrollable(ScrollbarOrientation) const override; bool ShouldPlaceVerticalScrollbarOnLeft() const override; int PageStep(ScrollbarOrientation) const override; - ScrollBehavior ScrollBehaviorStyle() const override; + mojom::blink::ScrollBehavior ScrollBehaviorStyle() const override; WebColorScheme UsedColorScheme() const override; cc::AnimationHost* GetCompositorAnimationHost() const override; CompositorAnimationTimeline* GetCompositorAnimationTimeline() const override; @@ -358,22 +357,25 @@ class CORE_EXPORT PaintLayerScrollableArea final IntPoint ScrollOrigin() const { return scroll_origin_; } bool ScrollOriginChanged() const { return scroll_origin_changed_; } - void ScrollToAbsolutePosition( - const FloatPoint& position, - ScrollBehavior scroll_behavior = kScrollBehaviorInstant, - ScrollType scroll_type = kProgrammaticScroll) { + void ScrollToAbsolutePosition(const FloatPoint& position, + mojom::blink::ScrollBehavior scroll_behavior = + mojom::blink::ScrollBehavior::kInstant, + mojom::blink::ScrollType scroll_type = + mojom::blink::ScrollType::kProgrammatic) { SetScrollOffset(position - ScrollOrigin(), scroll_type, scroll_behavior); } // This will set the scroll position without clamping, and it will do all // post-update work even if the scroll position didn't change. - void SetScrollOffsetUnconditionally(const ScrollOffset&, - ScrollType = kProgrammaticScroll); + void SetScrollOffsetUnconditionally( + const ScrollOffset&, + mojom::blink::ScrollType = mojom::blink::ScrollType::kProgrammatic); // This will set the scroll position without clamping, and it will do all // post-update work even if the scroll position didn't change. - void SetScrollPositionUnconditionally(const DoublePoint&, - ScrollType = kProgrammaticScroll); + void SetScrollPositionUnconditionally( + const DoublePoint&, + mojom::blink::ScrollType = mojom::blink::ScrollType::kProgrammatic); // TODO(szager): Actually run these after all of layout is finished. // Currently, they run at the end of box()'es layout (or after all flexbox @@ -439,8 +441,9 @@ class CORE_EXPORT PaintLayerScrollableArea final // Returns the new offset, after scrolling, of the given rect in absolute // coordinates, clipped by the parent's client rect. - PhysicalRect ScrollIntoView(const PhysicalRect&, - const WebScrollIntoViewParams&) override; + PhysicalRect ScrollIntoView( + const PhysicalRect&, + const mojom::blink::ScrollIntoViewParamsPtr&) override; // Returns true if scrollable area is in the FrameView's collection of // scrollable areas. This can only happen if we're scrollable, visible to hit @@ -452,6 +455,8 @@ class CORE_EXPORT PaintLayerScrollableArea final // Rectangle encompassing the scroll corner and resizer rect. IntRect ScrollCornerAndResizerRect() const; + // This also updates main thread scrolling reasons and the LayoutBox's + // background paint location. bool ComputeNeedsCompositedScrolling( bool force_prefer_compositing_to_lcd_text); @@ -463,14 +468,14 @@ class CORE_EXPORT PaintLayerScrollableArea final return needs_composited_scrolling_; } - IntRect ResizerCornerRect(const IntRect&, ResizerHitTestType) const; + IntRect ResizerCornerRect(ResizerHitTestType) const; PaintLayer* Layer() const override; LayoutCustomScrollbarPart* Resizer() const { return resizer_; } - IntRect RectForHorizontalScrollbar(const IntRect& border_box_rect) const; - IntRect RectForVerticalScrollbar(const IntRect& border_box_rect) const; + IntRect RectForHorizontalScrollbar() const; + IntRect RectForVerticalScrollbar() const; bool ScheduleAnimation() override; bool ShouldPerformScrollAnchoring() const override; @@ -527,8 +532,7 @@ class CORE_EXPORT PaintLayerScrollableArea final } void InvalidateAllStickyConstraints(); - void InvalidateStickyConstraintsFor(PaintLayer*, - bool needs_compositing_update = true); + void InvalidateStickyConstraintsFor(PaintLayer*); void InvalidatePaintForStickyDescendants(); uint32_t GetNonCompositedMainThreadScrollingReasons() { return non_composited_main_thread_scrolling_reasons_; @@ -562,7 +566,7 @@ class CORE_EXPORT PaintLayerScrollableArea final bool HasHorizontalOverflow() const; bool HasVerticalOverflow() const; - void Trace(blink::Visitor*) override; + void Trace(Visitor*) override; const DisplayItemClient& GetScrollingBackgroundDisplayItemClient() const { return scrolling_background_display_item_client_; @@ -574,12 +578,30 @@ class CORE_EXPORT PaintLayerScrollableArea final const cc::SnapContainerData* GetSnapContainerData() const override; void SetSnapContainerData(base::Optional<cc::SnapContainerData>) override; bool SetTargetSnapAreaElementIds(cc::TargetSnapAreaElementIds) override; + bool SnapContainerDataNeedsUpdate() const override; + void SetSnapContainerDataNeedsUpdate(bool) override; + bool NeedsResnap() const override; + void SetNeedsResnap(bool) override; base::Optional<FloatPoint> GetSnapPositionAndSetTarget( const cc::SnapSelectionStrategy& strategy) override; void DisposeImpl() override; + void SetPendingHistoryRestoreScrollOffset( + const HistoryItem::ViewState& view_state, + bool should_restore_scroll) override { + if (!should_restore_scroll) + return; + pending_view_state_ = view_state; + } + + void ApplyPendingHistoryRestoreScrollOffset() override; + + bool HasPendingHistoryRestoreScrollOffset() override { + return !!pending_view_state_; + } + private: bool NeedsScrollbarReconstruction() const; @@ -591,18 +613,29 @@ class CORE_EXPORT PaintLayerScrollableArea final // Update the proportions used for thumb rect dimensions. void UpdateScrollbarProportions(); - void UpdateScrollOffset(const ScrollOffset&, ScrollType) override; + void UpdateScrollOffset(const ScrollOffset&, + mojom::blink::ScrollType) override; void InvalidatePaintForScrollOffsetChange(); - int VerticalScrollbarStart(int min_x, int max_x) const; - int HorizontalScrollbarStart(int min_x) const; + int VerticalScrollbarStart() const; + int HorizontalScrollbarStart() const; IntSize ScrollbarOffset(const Scrollbar&) const; - enum ComputeScrollbarExistenceOption { kDefault, kForbidAddingAutoBars }; + // If OverflowIndependent is specified, will only change current scrollbar + // existence if the new style doesn't depend on overflow which requires + // layout to be clean. It'd be nice if we could always determine existence at + // one point, after layout. Unfortunately, it seems that parts of layout are + // dependent on scrollbar existence in cases like |overflow:scroll|, removing + // the post style pass causes breaks in tests e.g. forms web_tests. Thus, we + // must do two scrollbar existence passes. + enum ComputeScrollbarExistenceOption { + kDependsOnOverflow, + kOverflowIndependent + }; void ComputeScrollbarExistence( bool& needs_horizontal_scrollbar, bool& needs_vertical_scrollbar, - ComputeScrollbarExistenceOption = kDefault) const; + ComputeScrollbarExistenceOption = kDependsOnOverflow) const; // If the content fits entirely in the area without auto scrollbars, returns // true to try to remove them. This is a heuristic and can be incorrect if the @@ -637,7 +670,7 @@ class CORE_EXPORT PaintLayerScrollableArea final return *rare_data_.get(); } - IntRect CornerRect(const IntRect& bounds) const; + IntRect CornerRect() const; void ScrollControlWasSetNeedsPaintInvalidation() override; @@ -647,6 +680,13 @@ class CORE_EXPORT PaintLayerScrollableArea final bool HasNonCompositedStickyDescendants() const; + IntSize PixelSnappedBorderBoxSize() const; + + using BackgroundPaintLocation = uint8_t; + bool ComputeNeedsCompositedScrollingInternal( + BackgroundPaintLocation background_paint_location_if_composited, + bool force_prefer_compositing_to_lcd_text); + // PaintLayer is destructed before PaintLayerScrollable area, during this // time before PaintLayerScrollableArea has been collected layer_ will // be set to nullptr by the Dispose method. @@ -672,6 +712,7 @@ class CORE_EXPORT PaintLayerScrollableArea final // instance has been reconstructed. unsigned rebuild_horizontal_scrollbar_layer_ : 1; unsigned rebuild_vertical_scrollbar_layer_ : 1; + unsigned previous_vertical_scrollbar_on_left_ : 1; unsigned needs_scroll_offset_clamp_ : 1; unsigned needs_relayout_ : 1; @@ -742,7 +783,6 @@ class CORE_EXPORT PaintLayerScrollableArea final IntRect VisualRect() const final; String DebugName() const final; DOMNodeId OwnerNodeId() const final; - bool PaintedOutputOfObjectHasNoEffectRegardlessOfSize() const final; Member<const PaintLayerScrollableArea> scrollable_area_; }; @@ -768,14 +808,9 @@ class CORE_EXPORT PaintLayerScrollableArea final ScrollingBackgroundDisplayItemClient scrolling_background_display_item_client_{*this}; ScrollCornerDisplayItemClient scroll_corner_display_item_client_{*this}; + base::Optional<HistoryItem::ViewState> pending_view_state_; }; -DEFINE_TYPE_CASTS(PaintLayerScrollableArea, - ScrollableArea, - scrollableArea, - scrollableArea->IsPaintLayerScrollableArea(), - scrollableArea.IsPaintLayerScrollableArea()); - } // namespace blink #endif // LayerScrollableArea_h 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 70a2bd4c2e0..da749e252a3 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 @@ -8,8 +8,11 @@ #include "build/build_config.h" #include "testing/gmock/include/gmock/gmock.h" #include "third_party/blink/public/common/features.h" +#include "third_party/blink/renderer/core/animation/scroll_timeline.h" +#include "third_party/blink/renderer/core/css/css_numeric_literal_value.h" #include "third_party/blink/renderer/core/frame/local_frame_view.h" #include "third_party/blink/renderer/core/layout/layout_box_model_object.h" +#include "third_party/blink/renderer/core/page/scrolling/snap_coordinator.h" #include "third_party/blink/renderer/core/paint/paint_layer.h" #include "third_party/blink/renderer/core/scroll/scroll_types.h" #include "third_party/blink/renderer/core/scroll/scrollbar_theme.h" @@ -677,14 +680,16 @@ TEST_P(PaintLayerScrollableAreaTest, HideTooltipWhenScrollPositionChanges) { EXPECT_CALL(GetChromeClient(), MockSetToolTip(GetDocument().GetFrame(), String(), _)) .Times(1); - scrollable_area->SetScrollOffset(ScrollOffset(1, 1), kUserScroll); + scrollable_area->SetScrollOffset(ScrollOffset(1, 1), + mojom::blink::ScrollType::kUser); // Programmatic scrolling should not dismiss the tooltip, so setToolTip // should not be called for this invocation. EXPECT_CALL(GetChromeClient(), MockSetToolTip(GetDocument().GetFrame(), String(), _)) .Times(0); - scrollable_area->SetScrollOffset(ScrollOffset(2, 2), kProgrammaticScroll); + scrollable_area->SetScrollOffset(ScrollOffset(2, 2), + mojom::blink::ScrollType::kProgrammatic); } TEST_P(PaintLayerScrollableAreaTest, IncludeOverlayScrollbarsInVisibleWidth) { @@ -703,7 +708,8 @@ TEST_P(PaintLayerScrollableAreaTest, IncludeOverlayScrollbarsInVisibleWidth) { PaintLayerScrollableArea* scrollable_area = ToLayoutBoxModelObject(scroller->GetLayoutObject())->GetScrollableArea(); ASSERT_TRUE(scrollable_area); - scrollable_area->SetScrollOffset(ScrollOffset(100, 0), kClampingScroll); + scrollable_area->SetScrollOffset(ScrollOffset(100, 0), + mojom::blink::ScrollType::kClamping); EXPECT_EQ(scrollable_area->GetScrollOffset().Width(), 0); } @@ -819,7 +825,8 @@ TEST_P(PaintLayerScrollableAreaTest, OverflowHiddenScrollOffsetInvalidation) { // Going from zero scroll offset to non-zero may require a new paint property // and should invalidate paint and paint properties. - scrollable_area->SetScrollOffset(ScrollOffset(0, 1), kProgrammaticScroll); + scrollable_area->SetScrollOffset(ScrollOffset(0, 1), + mojom::blink::ScrollType::kProgrammatic); EXPECT_TRUE(scroller->PaintingLayer()->SelfNeedsRepaint()); EXPECT_TRUE(scroller->NeedsPaintPropertyUpdate()); UpdateAllLifecyclePhasesForTest(); @@ -829,7 +836,8 @@ TEST_P(PaintLayerScrollableAreaTest, OverflowHiddenScrollOffsetInvalidation) { EXPECT_NE(nullptr, properties->ScrollTranslation()); // A property update is needed when scroll offset changes. - scrollable_area->SetScrollOffset(ScrollOffset(0, 2), kProgrammaticScroll); + scrollable_area->SetScrollOffset(ScrollOffset(0, 2), + mojom::blink::ScrollType::kProgrammatic); EXPECT_TRUE(scroller->NeedsPaintPropertyUpdate()); UpdateAllLifecyclePhasesForTest(); @@ -839,7 +847,8 @@ TEST_P(PaintLayerScrollableAreaTest, OverflowHiddenScrollOffsetInvalidation) { // Going from non-zero scroll offset to zero may require destroying a paint // property and should invalidate paint and paint properties. - scrollable_area->SetScrollOffset(ScrollOffset(0, 0), kProgrammaticScroll); + scrollable_area->SetScrollOffset(ScrollOffset(0, 0), + mojom::blink::ScrollType::kProgrammatic); EXPECT_TRUE(scroller->PaintingLayer()->SelfNeedsRepaint()); EXPECT_TRUE(scroller->NeedsPaintPropertyUpdate()); UpdateAllLifecyclePhasesForTest(); @@ -873,7 +882,8 @@ TEST_P(PaintLayerScrollableAreaTest, ScrollDoesNotInvalidate) { EXPECT_EQ(FloatSize(0, 0), scrollable_area->GetScrollOffset()); // Changing the scroll offset should not require paint invalidation. - scrollable_area->SetScrollOffset(ScrollOffset(0, 1), kProgrammaticScroll); + scrollable_area->SetScrollOffset(ScrollOffset(0, 1), + mojom::blink::ScrollType::kProgrammatic); EXPECT_FALSE(scroller->ShouldDoFullPaintInvalidation()); EXPECT_TRUE(scroller->NeedsPaintPropertyUpdate()); UpdateAllLifecyclePhasesForTest(); @@ -901,19 +911,17 @@ TEST_P(PaintLayerScrollableAreaTest, auto* scroller = ToLayoutBox(GetLayoutObjectByElementId("scroller")); auto* scrollable_area = scroller->GetScrollableArea(); EXPECT_EQ(kBackgroundPaintInScrollingContents, + scroller->ComputeBackgroundPaintLocationIfComposited()); + EXPECT_EQ(kBackgroundPaintInGraphicsLayer, scroller->GetBackgroundPaintLocation()); // Programmatically changing the scroll offset. - scrollable_area->SetScrollOffset(ScrollOffset(0, 1), kProgrammaticScroll); - if (RuntimeEnabledFeatures::CompositeAfterPaintEnabled()) { - // 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()); - } + scrollable_area->SetScrollOffset(ScrollOffset(0, 1), + mojom::blink::ScrollType::kProgrammatic); + // 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()); @@ -942,10 +950,13 @@ TEST_P(PaintLayerScrollableAreaTest, auto* scrollable_area = scroller->GetScrollableArea(); EXPECT_EQ( kBackgroundPaintInGraphicsLayer | kBackgroundPaintInScrollingContents, - scroller->GetBackgroundPaintLocation()); + scroller->ComputeBackgroundPaintLocationIfComposited()); + EXPECT_EQ(kBackgroundPaintInGraphicsLayer, + scroller->GetBackgroundPaintLocation()); // Programmatically changing the scroll offset. - scrollable_area->SetScrollOffset(ScrollOffset(0, 1), kProgrammaticScroll); + scrollable_area->SetScrollOffset(ScrollOffset(0, 1), + mojom::blink::ScrollType::kProgrammatic); // No invalidation because the background paints into the main layer. EXPECT_TRUE(scroller->ShouldDoFullPaintInvalidation()); EXPECT_TRUE(scroller->BackgroundNeedsFullPaintInvalidation()); @@ -985,8 +996,8 @@ TEST_P(PaintLayerScrollableAreaTest, ViewScrollWithFixedAttachmentBackground) { // Programmatically changing the view's scroll offset. Should invalidate all // objects with fixed attachment background. - view_scrollable_area->SetScrollOffset(ScrollOffset(0, 1), - kProgrammaticScroll); + view_scrollable_area->SetScrollOffset( + ScrollOffset(0, 1), mojom::blink::ScrollType::kProgrammatic); EXPECT_TRUE(fixed_background_div->ShouldDoFullPaintInvalidation()); EXPECT_TRUE(fixed_background_div->BackgroundNeedsFullPaintInvalidation()); EXPECT_FALSE(fixed_background_div->NeedsPaintPropertyUpdate()); @@ -997,13 +1008,14 @@ TEST_P(PaintLayerScrollableAreaTest, ViewScrollWithFixedAttachmentBackground) { // Programmatically changing the div's scroll offset. Should invalidate the // scrolled div with fixed attachment background. - div_scrollable_area->SetScrollOffset(ScrollOffset(0, 1), kProgrammaticScroll); + div_scrollable_area->SetScrollOffset(ScrollOffset(0, 1), + mojom::blink::ScrollType::kProgrammatic); 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()); + EXPECT_FALSE(GetLayoutView().NeedsPaintPropertyUpdate()); } TEST_P(PaintLayerScrollableAreaTest, @@ -1032,14 +1044,16 @@ TEST_P(PaintLayerScrollableAreaTest, auto* fixed_background_div = ToLayoutBox(GetLayoutObjectByElementId("fixed-background")); EXPECT_EQ(kBackgroundPaintInScrollingContents, + fixed_background_div->ComputeBackgroundPaintLocationIfComposited()); + EXPECT_EQ(kBackgroundPaintInGraphicsLayer, fixed_background_div->GetBackgroundPaintLocation()); 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); + view_scrollable_area->SetScrollOffset( + ScrollOffset(0, 1), mojom::blink::ScrollType::kProgrammatic); EXPECT_FALSE(fixed_background_div->ShouldDoFullPaintInvalidation()); EXPECT_FALSE(fixed_background_div->BackgroundNeedsFullPaintInvalidation()); EXPECT_FALSE(fixed_background_div->NeedsPaintPropertyUpdate()); @@ -1050,13 +1064,14 @@ TEST_P(PaintLayerScrollableAreaTest, // Programmatically changing the div's scroll offset. Should invalidate the // scrolled div with fixed attachment background. - div_scrollable_area->SetScrollOffset(ScrollOffset(0, 1), kProgrammaticScroll); + div_scrollable_area->SetScrollOffset(ScrollOffset(0, 1), + mojom::blink::ScrollType::kProgrammatic); EXPECT_FALSE(fixed_background_div->ShouldDoFullPaintInvalidation()); EXPECT_FALSE(fixed_background_div->BackgroundNeedsFullPaintInvalidation()); EXPECT_TRUE(fixed_background_div->NeedsPaintPropertyUpdate()); EXPECT_FALSE(GetLayoutView().ShouldDoFullPaintInvalidation()); EXPECT_FALSE(GetLayoutView().BackgroundNeedsFullPaintInvalidation()); - EXPECT_TRUE(GetLayoutView().NeedsPaintPropertyUpdate()); + EXPECT_FALSE(GetLayoutView().NeedsPaintPropertyUpdate()); } TEST_P(PaintLayerScrollableAreaTest, @@ -1091,8 +1106,8 @@ TEST_P(PaintLayerScrollableAreaTest, // Programmatically changing the view's scroll offset. Should invalidate all // objects with fixed attachment background except the layout view. - view_scrollable_area->SetScrollOffset(ScrollOffset(0, 1), - kProgrammaticScroll); + view_scrollable_area->SetScrollOffset( + ScrollOffset(0, 1), mojom::blink::ScrollType::kProgrammatic); EXPECT_TRUE(fixed_background_div->ShouldDoFullPaintInvalidation()); EXPECT_TRUE(fixed_background_div->BackgroundNeedsFullPaintInvalidation()); EXPECT_FALSE(fixed_background_div->NeedsPaintPropertyUpdate()); @@ -1103,13 +1118,14 @@ TEST_P(PaintLayerScrollableAreaTest, // Programmatically changing the div's scroll offset. Should invalidate the // scrolled div with fixed attachment background. - div_scrollable_area->SetScrollOffset(ScrollOffset(0, 1), kProgrammaticScroll); + div_scrollable_area->SetScrollOffset(ScrollOffset(0, 1), + mojom::blink::ScrollType::kProgrammatic); 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()); + EXPECT_FALSE(GetLayoutView().NeedsPaintPropertyUpdate()); } TEST_P(PaintLayerScrollableAreaTest, HitTestOverlayScrollbars) { @@ -1179,7 +1195,8 @@ TEST_P(PaintLayerScrollableAreaTest, CompositedStickyDescendant) { .Transform() .IsIdentity()); - scrollable_area->SetScrollOffset(ScrollOffset(0, 50), kUserScroll); + scrollable_area->SetScrollOffset(ScrollOffset(0, 50), + mojom::blink::ScrollType::kUser); UpdateAllLifecyclePhasesForTest(); EXPECT_EQ(FloatSize(0, 50), sticky->FirstFragment() @@ -1233,7 +1250,8 @@ TEST_P(PaintLayerScrollableAreaTest, ScrollbarMaximum) { PaintLayerScrollableArea* scrollable_area = scroller->GetScrollableArea(); Scrollbar* scrollbar = scrollable_area->VerticalScrollbar(); - scrollable_area->ScrollBy(ScrollOffset(0, 1000), kProgrammaticScroll); + scrollable_area->ScrollBy(ScrollOffset(0, 1000), + mojom::blink::ScrollType::kProgrammatic); UpdateAllLifecyclePhasesForTest(); EXPECT_EQ(scrollbar->CurrentPos(), scrollbar->Maximum()); } @@ -1332,28 +1350,39 @@ TEST_P(PaintLayerScrollableAreaTest, ShowCustomResizerInTextarea) { EXPECT_NE(paint_layer->GetScrollableArea()->Resizer(), nullptr); } -class PaintLayerScrollableAreaCompositingTest - : public PaintLayerScrollableAreaTest { - public: - PaintLayerScrollableAreaCompositingTest() { - if (GetParam() & kDoNotCompositeTrivial3D) { - scoped_feature_list_.InitAndEnableFeature( - blink::features::kDoNotCompositeTrivial3D); - } else { - scoped_feature_list_.InitAndDisableFeature( - blink::features::kDoNotCompositeTrivial3D); - } - } +TEST_P(PaintLayerScrollableAreaTest, + ApplyPendingHistoryRestoreScrollOffsetTwice) { + GetPage().GetSettings().SetTextAreasAreResizable(true); + SetBodyInnerHTML(R"HTML( + <!doctype HTML> + <div id="target" style="overflow: scroll; width: 50px; height: 50px"> + <div style="width: 50px; height: 500px"> + </div> + </div> + )HTML"); - private: - base::test::ScopedFeatureList scoped_feature_list_; -}; + const auto* paint_layer = + ToLayoutBoxModelObject(GetLayoutObjectByElementId("target"))->Layer(); + + auto* scrollable_area = paint_layer->GetScrollableArea(); + + HistoryItem::ViewState view_state; + view_state.scroll_offset_ = ScrollOffset(0, 100); + scrollable_area->SetPendingHistoryRestoreScrollOffset(view_state, true); + scrollable_area->ApplyPendingHistoryRestoreScrollOffset(); + EXPECT_EQ(ScrollOffset(0, 100), scrollable_area->GetScrollOffset()); -INSTANTIATE_DO_NOT_COMPOSITE_TRIVIAL_3D_P( - PaintLayerScrollableAreaCompositingTest); + scrollable_area->SetScrollOffset(ScrollOffset(0, 50), + mojom::blink::ScrollType::kUser); + + // The second call to ApplyPendingHistoryRestoreScrollOffset should + // do nothing, since the history was already restored. + scrollable_area->ApplyPendingHistoryRestoreScrollOffset(); + EXPECT_EQ(ScrollOffset(0, 50), scrollable_area->GetScrollOffset()); +} // Test that a trivial 3D transform results in composited scrolling. -TEST_P(PaintLayerScrollableAreaCompositingTest, CompositeWithTrivial3D) { +TEST_P(PaintLayerScrollableAreaTest, CompositeWithTrivial3D) { SetBodyInnerHTML(R"HTML( <style> #scroller { @@ -1375,4 +1404,156 @@ TEST_P(PaintLayerScrollableAreaCompositingTest, CompositeWithTrivial3D) { EXPECT_TRUE(UsesCompositedScrolling(GetLayoutObjectByElementId("scroller"))); } +class PaintLayerScrollableAreaTestLowEndPlatform + : public TestingPlatformSupport { + public: + bool IsLowEndDevice() override { return true; } +}; + +// Test that a trivial 3D transform results in composited scrolling even on +// low-end devices that may not composite trivial 3D transforms. +TEST_P(PaintLayerScrollableAreaTest, LowEndCompositeWithTrivial3D) { + ScopedTestingPlatformSupport<PaintLayerScrollableAreaTestLowEndPlatform> + platform; + SetBodyInnerHTML(R"HTML( + <style> + #scroller { + width: 100px; + height: 100px; + overflow: scroll; + transform: translateZ(0); + } + #scrolled { + width: 200px; + height: 200px; + } + </style> + <div id="scroller"> + <div id="scrolled"></div> + </div> + )HTML"); + + EXPECT_TRUE(UsesCompositedScrolling(GetLayoutObjectByElementId("scroller"))); +} + +TEST_P(PaintLayerScrollableAreaTest, SetSnapContainerDataNeedsUpdate) { + SetBodyInnerHTML(R"HTML( + <style> + .scroller { + overflow: scroll; + height: 200px; + width: 200px; + } + </style> + <div id='first_scroller' class='scroller'> + <div style='height: 2000px;'></div> + </div> + <div id='second_scroller' class='scroller'> + <div style='height: 2000px;'></div> + </div> + )HTML"); + + auto* first_scroller = GetLayoutObjectByElementId("first_scroller"); + auto* first_scrollable_area = + ToLayoutBoxModelObject(first_scroller)->GetScrollableArea(); + + auto* second_scroller = GetLayoutObjectByElementId("second_scroller"); + auto* second_scrollable_area = + ToLayoutBoxModelObject(second_scroller)->GetScrollableArea(); + + EXPECT_EQ(&first_scroller->GetDocument().GetSnapCoordinator(), + &second_scroller->GetDocument().GetSnapCoordinator()); + + auto& snap_coordinator = first_scroller->GetDocument().GetSnapCoordinator(); + EXPECT_FALSE(snap_coordinator.AnySnapContainerDataNeedsUpdate()); + + // SnapCoordinator needs to update all its snap containers if one of them asks + // for an update. + first_scrollable_area->SetSnapContainerDataNeedsUpdate(true); + EXPECT_TRUE(snap_coordinator.AnySnapContainerDataNeedsUpdate()); + + // SnapCoordinator still needs to update all its snap containers even if one + // of them asks not to. + second_scrollable_area->SetSnapContainerDataNeedsUpdate(false); + EXPECT_TRUE(snap_coordinator.AnySnapContainerDataNeedsUpdate()); + + first_scrollable_area->SetSnapContainerDataNeedsUpdate(false); + EXPECT_TRUE(snap_coordinator.AnySnapContainerDataNeedsUpdate()); + + snap_coordinator.UpdateAllSnapContainerDataIfNeeded(); + EXPECT_FALSE(snap_coordinator.AnySnapContainerDataNeedsUpdate()); +} + +class ScrollTimelineForTest : public ScrollTimeline { + public: + ScrollTimelineForTest( + Document* document, + Element* scroll_source, + CSSPrimitiveValue* start_scroll_offset = + CSSNumericLiteralValue::Create(10.0, + CSSPrimitiveValue::UnitType::kPixels), + CSSPrimitiveValue* end_scroll_offset = + CSSNumericLiteralValue::Create(90.0, + CSSPrimitiveValue::UnitType::kPixels)) + : ScrollTimeline(document, + scroll_source, + ScrollTimeline::Vertical, + start_scroll_offset, + end_scroll_offset, + 100.0), + invalidated_(false) {} + void Invalidate() override { + ScrollTimeline::Invalidate(); + invalidated_ = true; + } + bool Invalidated() const { return invalidated_; } + void ResetInvalidated() { invalidated_ = false; } + void Trace(Visitor* visitor) override { ScrollTimeline::Trace(visitor); } + + private: + bool invalidated_; +}; + +// Verify that scrollable area changes invalidate scroll timeline. +TEST_P(PaintLayerScrollableAreaTest, ScrollTimelineInvalidation) { + SetBodyInnerHTML(R"HTML( + <style> + #scroller { overflow: scroll; width: 100px; height: 100px; } + #spacer { height: 1000px; } + </style> + <div id='scroller'> + <div id ='spacer'></div> + </div> + )HTML"); + + LayoutBoxModelObject* scroller = + ToLayoutBoxModelObject(GetLayoutObjectByElementId("scroller")); + PaintLayerScrollableArea* scrollable_area = scroller->GetScrollableArea(); + scrollable_area->SetScrollOffset(ScrollOffset(0, 20), + mojom::blink::ScrollType::kProgrammatic); + Element* scroller_element = GetElementById("scroller"); + ScrollTimelineForTest* scroll_timeline = + MakeGarbageCollected<ScrollTimelineForTest>(&GetDocument(), + scroller_element); + scroll_timeline->ResetInvalidated(); + // Verify that changing scroll offset invalidates scroll timeline. + scrollable_area->SetScrollOffset(ScrollOffset(0, 30), + mojom::blink::ScrollType::kProgrammatic); + EXPECT_TRUE(scroll_timeline->Invalidated()); + scroll_timeline->ResetInvalidated(); + + // Verify that changing scroller size invalidates scroll timeline. + scroller_element->setAttribute(html_names::kStyleAttr, "height:110px;"); + UpdateAllLifecyclePhasesForTest(); + EXPECT_TRUE(scroll_timeline->Invalidated()); + scroll_timeline->ResetInvalidated(); + + // Verify that changing content area size invalidates scroll timeline. + Element* spacer_element = GetElementById("spacer"); + spacer_element->setAttribute(html_names::kStyleAttr, "height:900px;"); + UpdateAllLifecyclePhasesForTest(); + EXPECT_TRUE(scroll_timeline->Invalidated()); + scroll_timeline->ResetInvalidated(); +} + } // namespace blink 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 4e1c981916e..e660390d496 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 @@ -109,21 +109,47 @@ static bool ZIndexLessThan(const PaintLayer* first, const PaintLayer* second) { second->GetLayoutObject().StyleRef().ZIndex(); } -static void SetIfHigher(const PaintLayer*& first, const PaintLayer* second) { +static bool SetIfHigher(const PaintLayer*& first, const PaintLayer* second) { if (!second) - return; + return false; DCHECK_GE(second->GetLayoutObject().StyleRef().ZIndex(), 0); // |second| appears later in the tree, so it's higher than |first| if its // z-index >= |first|'s z-index. - if (!first || !ZIndexLessThan(second, first)) + if (!first || !ZIndexLessThan(second, first)) { first = second; + return true; + } + return false; } -// For finding the proper z-order of reparented overlay scrollbars. +// For finding the proper z-order of reparented overlay overflow controls. struct PaintLayerStackingNode::HighestLayers { - const PaintLayer* highest_absolute_position = nullptr; - const PaintLayer* highest_fixed_position = nullptr; - const PaintLayer* highest_in_flow_stacked = nullptr; + enum LayerType { + kAbsolutePosition, + kFixedPosition, + kInFlowStacked, + kLayerTypeCount + }; + std::array<const PaintLayer*, kLayerTypeCount> highest_layers = { + nullptr, nullptr, nullptr}; + Vector<LayerType, kLayerTypeCount> highest_layers_order; + + void UpdateOrderForSubtreeHighestLayers(LayerType type, + const PaintLayer* layer) { + if (SetIfHigher(highest_layers[type], layer)) { + auto* new_end = std::remove(highest_layers_order.begin(), + highest_layers_order.end(), type); + if (new_end != highest_layers_order.end()) { + // |highest_layers_order| doesn't have duplicate elements, std::remove + // will find at most one element at a time. So we don't shrink it and + // just update the value of the |new_end|. + DCHECK(new_end + 1 == highest_layers_order.end()); + *new_end = type; + } else { + highest_layers_order.push_back(type); + } + } + } void Update(const PaintLayer& layer) { const auto& style = layer.GetLayoutObject().StyleRef(); @@ -136,17 +162,18 @@ struct PaintLayerStackingNode::HighestLayers { return; if (style.GetPosition() == EPosition::kAbsolute) - SetIfHigher(highest_absolute_position, &layer); + UpdateOrderForSubtreeHighestLayers(kAbsolutePosition, &layer); else if (style.GetPosition() == EPosition::kFixed) - SetIfHigher(highest_fixed_position, &layer); + UpdateOrderForSubtreeHighestLayers(kFixedPosition, &layer); else - SetIfHigher(highest_in_flow_stacked, &layer); + UpdateOrderForSubtreeHighestLayers(kInFlowStacked, &layer); } void Merge(HighestLayers& child) { - SetIfHigher(highest_absolute_position, child.highest_absolute_position); - SetIfHigher(highest_fixed_position, child.highest_fixed_position); - SetIfHigher(highest_in_flow_stacked, child.highest_in_flow_stacked); + for (auto layer_type : child.highest_layers_order) { + UpdateOrderForSubtreeHighestLayers(layer_type, + child.highest_layers[layer_type]); + } } }; @@ -231,16 +258,18 @@ void PaintLayerStackingNode::CollectLayers(PaintLayer& paint_layer, } if (has_overlay_overflow_controls) { - const PaintLayer* layer_to_paint_overlay_overflow_controls_after = - subtree_highest_layers->highest_in_flow_stacked; - if (object.CanContainFixedPositionObjects()) { + const PaintLayer* layer_to_paint_overlay_overflow_controls_after = nullptr; + for (auto layer_type : subtree_highest_layers->highest_layers_order) { + if (layer_type == HighestLayers::kFixedPosition && + !object.CanContainFixedPositionObjects()) + continue; + if (layer_type == HighestLayers::kAbsolutePosition && + !object.CanContainAbsolutePositionObjects()) + continue; SetIfHigher(layer_to_paint_overlay_overflow_controls_after, - subtree_highest_layers->highest_fixed_position); - } - if (object.CanContainAbsolutePositionObjects()) { - SetIfHigher(layer_to_paint_overlay_overflow_controls_after, - subtree_highest_layers->highest_absolute_position); + subtree_highest_layers->highest_layers[layer_type]); } + if (layer_to_paint_overlay_overflow_controls_after) { layer_to_overlay_overflow_controls_painting_after_ .insert(layer_to_paint_overlay_overflow_controls_after, PaintLayers()) 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 247a10f525d..f0dc94a3304 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 @@ -206,9 +206,10 @@ TEST_P(PaintLayerTest, CompositedScrollingNoNeedsRepaint) { EXPECT_EQ(kNotComposited, content_layer->GetCompositingState()); EXPECT_EQ(PhysicalOffset(), content_layer->LocationWithoutPositionOffset()); - scroll_layer->GetScrollableArea()->SetScrollOffset(ScrollOffset(1000, 1000), - kProgrammaticScroll); - GetDocument().View()->UpdateAllLifecyclePhasesExceptPaint(); + scroll_layer->GetScrollableArea()->SetScrollOffset( + ScrollOffset(1000, 1000), mojom::blink::ScrollType::kProgrammatic); + GetDocument().View()->UpdateAllLifecyclePhasesExceptPaint( + DocumentUpdateReason::kTest); EXPECT_EQ(PhysicalOffset(0, 0), content_layer->LocationWithoutPositionOffset()); EXPECT_EQ( @@ -240,9 +241,10 @@ TEST_P(PaintLayerTest, NonCompositedScrollingNeedsRepaint) { EXPECT_EQ(kNotComposited, scroll_layer->GetCompositingState()); EXPECT_EQ(PhysicalOffset(), content_layer->LocationWithoutPositionOffset()); - scroll_layer->GetScrollableArea()->SetScrollOffset(ScrollOffset(1000, 1000), - kProgrammaticScroll); - GetDocument().View()->UpdateAllLifecyclePhasesExceptPaint(); + scroll_layer->GetScrollableArea()->SetScrollOffset( + ScrollOffset(1000, 1000), mojom::blink::ScrollType::kProgrammatic); + GetDocument().View()->UpdateAllLifecyclePhasesExceptPaint( + DocumentUpdateReason::kTest); EXPECT_EQ(PhysicalOffset(0, 0), content_layer->LocationWithoutPositionOffset()); EXPECT_EQ( @@ -972,7 +974,41 @@ TEST_P(ReorderOverlayOverflowControlsTest, EXPECT_FALSE(LayersPaintingOverlayOverflowControlsAfter(child)); } -TEST_P(PaintLayerTest, SubsequenceCachingStackingContexts) { +TEST_P(ReorderOverlayOverflowControlsTest, + AdjustAccessingOrderForSubtreeHighestLayers) { + SetBodyInnerHTML(R"HTML( + <style> + div { + width: 200px; + height: 200px; + } + div > div { + height: 300px; + } + #ancestor, #child_2 { + position: relative; + } + #child_1 { + position: absolute; + } + </style> + <div id='ancestor'> + <div id='child_1'></div> + <div id='child_2'> + <div id='descendant'></div> + </div> + </div> + )HTML"); + + InitOverflowStyle("ancestor"); + + auto* ancestor = GetPaintLayerByElementId("ancestor"); + auto* child = GetPaintLayerByElementId("child_2"); + EXPECT_TRUE(ancestor->NeedsReorderOverlayOverflowControls()); + EXPECT_TRUE(LayersPaintingOverlayOverflowControlsAfter(child)); +} + +TEST_P(PaintLayerTest, SubsequenceCachingStackedLayers) { SetBodyInnerHTML(R"HTML( <div id='parent' style='position:relative'> <div id='child1' style='position: relative'> @@ -1242,8 +1278,8 @@ TEST_P(PaintLayerTest, PaintInvalidationOnNonCompositedScroll) { content_layer->FirstFragment().VisualRect()); EXPECT_EQ(IntRect(0, 30, 50, 5), content->FirstFragment().VisualRect()); - scroller->GetScrollableArea()->SetScrollOffset(ScrollOffset(0, 20), - kProgrammaticScroll); + scroller->GetScrollableArea()->SetScrollOffset( + ScrollOffset(0, 20), mojom::blink::ScrollType::kProgrammatic); UpdateAllLifecyclePhasesForTest(); EXPECT_EQ(IntRect(0, 30, 50, 10), content_layer->FirstFragment().VisualRect()); @@ -1271,8 +1307,8 @@ TEST_P(PaintLayerTest, PaintInvalidationOnCompositedScroll) { content_layer->FirstFragment().VisualRect()); EXPECT_EQ(IntRect(0, 30, 50, 5), content->FirstFragment().VisualRect()); - scroller->GetScrollableArea()->SetScrollOffset(ScrollOffset(0, 20), - kProgrammaticScroll); + scroller->GetScrollableArea()->SetScrollOffset( + ScrollOffset(0, 20), mojom::blink::ScrollType::kProgrammatic); UpdateAllLifecyclePhasesForTest(); EXPECT_EQ(IntRect(0, 30, 50, 10), content_layer->FirstFragment().VisualRect()); @@ -1602,8 +1638,8 @@ TEST_P(PaintLayerTest, FloatLayerUnderInlineLayerScrolled) { PaintLayer* floating = GetPaintLayerByElementId("floating"); PaintLayer* span = GetPaintLayerByElementId("span"); PaintLayer* container = GetPaintLayerByElementId("container"); - container->GetScrollableArea()->SetScrollOffset(ScrollOffset(0, 400), - kProgrammaticScroll); + container->GetScrollableArea()->SetScrollOffset( + ScrollOffset(0, 400), mojom::blink::ScrollType::kProgrammatic); EXPECT_EQ(span, floating->Parent()); if (RuntimeEnabledFeatures::LayoutNGEnabled()) { @@ -1883,8 +1919,8 @@ TEST_P(PaintLayerTest, ColumnSpanLayerUnderExtraLayerScrolled) { PaintLayer* spanner = GetPaintLayerByElementId("spanner"); PaintLayer* extra_layer = GetPaintLayerByElementId("extraLayer"); PaintLayer* columns = GetPaintLayerByElementId("columns"); - columns->GetScrollableArea()->SetScrollOffset(ScrollOffset(200, 0), - kProgrammaticScroll); + columns->GetScrollableArea()->SetScrollOffset( + ScrollOffset(200, 0), mojom::blink::ScrollType::kProgrammatic); EXPECT_EQ(extra_layer, spanner->Parent()); EXPECT_EQ(columns, spanner->ContainingLayer()); @@ -1952,7 +1988,8 @@ TEST_P(PaintLayerTest, NeedsRepaintOnSelfPaintingStatusChange) { target_element->setAttribute(html_names::kStyleAttr, "overflow: hidden; float: left"); - GetDocument().View()->UpdateAllLifecyclePhasesExceptPaint(); + GetDocument().View()->UpdateAllLifecyclePhasesExceptPaint( + DocumentUpdateReason::kTest); // TODO(yosin): Once multicol in LayoutNG, we should remove following // assignments. This is because the layout tree maybe reattached. In LayoutNG // phase 1, layout tree is reattached because multicol forces legacy layout. @@ -1989,7 +2026,8 @@ TEST_P(PaintLayerTest, NeedsRepaintOnRemovingStackedLayer) { body->setAttribute(html_names::kStyleAttr, "margin-top: 0"); target_element->setAttribute(html_names::kStyleAttr, "top: 0"); - GetDocument().View()->UpdateAllLifecyclePhasesExceptPaint(); + GetDocument().View()->UpdateAllLifecyclePhasesExceptPaint( + DocumentUpdateReason::kTest); EXPECT_FALSE(target_object->HasLayer()); EXPECT_TRUE(body_layer->SelfNeedsRepaint()); @@ -2087,8 +2125,8 @@ TEST_P(PaintLayerTest, SquashingOffsets) { EXPECT_EQ(PhysicalOffset(), squashed->ComputeOffsetFromAncestor( squashed->TransformAncestorOrRoot())); - GetDocument().View()->LayoutViewport()->ScrollBy(ScrollOffset(0, 25), - kUserScroll); + GetDocument().View()->LayoutViewport()->ScrollBy( + ScrollOffset(0, 25), mojom::blink::ScrollType::kUser); UpdateAllLifecyclePhasesForTest(); PaintLayer::MapPointInPaintInvalidationContainerToBacking( diff --git a/chromium/third_party/blink/renderer/core/paint/paint_phase.h b/chromium/third_party/blink/renderer/core/paint/paint_phase.h index 039da1239f3..579ecf8f9a3 100644 --- a/chromium/third_party/blink/renderer/core/paint/paint_phase.h +++ b/chromium/third_party/blink/renderer/core/paint/paint_phase.h @@ -93,7 +93,7 @@ enum class PaintPhase { // The below are auxiliary phases which are used to paint special effects. kOverlayOverflowControls, - kSelection, + kSelectionDragImage, kTextClip, kMask, @@ -128,7 +128,7 @@ enum GlobalPaintFlag { // Used when painting selection as part of a drag-image. This // flag disables a lot of the painting code and specifically // triggers a PaintPhaseSelection. - kGlobalPaintSelectionOnly = 1 << 0, + kGlobalPaintSelectionDragImageOnly = 1 << 0, // Used when painting a drag-image or printing in order to // ignore the hardware layers and paint the whole tree // into the topmost layer. 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 6e0a4994f2e..6810fb3e4f7 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 @@ -20,7 +20,11 @@ #include "third_party/blink/renderer/core/layout/layout_inline.h" #include "third_party/blink/renderer/core/layout/layout_table_row.h" #include "third_party/blink/renderer/core/layout/layout_table_section.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/ng/inline/ng_fragment_item.h" +#include "third_party/blink/renderer/core/layout/ng/ng_fragment_child_iterator.h" +#include "third_party/blink/renderer/core/layout/ng/ng_physical_box_fragment.h" #include "third_party/blink/renderer/core/layout/svg/layout_svg_resource_masker.h" #include "third_party/blink/renderer/core/layout/svg/layout_svg_root.h" #include "third_party/blink/renderer/core/layout/svg/layout_svg_viewport_container.h" @@ -135,13 +139,17 @@ void PaintPropertyTreeBuilder::SetupContextForFrame( namespace { class FragmentPaintPropertyTreeBuilder { + STACK_ALLOCATED(); + public: FragmentPaintPropertyTreeBuilder( const LayoutObject& object, + NGPrePaintInfo* pre_paint_info, PaintPropertyTreeBuilderContext& full_context, PaintPropertyTreeBuilderFragmentContext& context, FragmentData& fragment_data) : object_(object), + pre_paint_info_(pre_paint_info), full_context_(full_context), context_(context), fragment_data_(fragment_data), @@ -187,7 +195,7 @@ class FragmentPaintPropertyTreeBuilder { ALWAYS_INLINE void UpdateClipPathCache(); ALWAYS_INLINE void UpdateStickyTranslation(); ALWAYS_INLINE void UpdateTransform(); - ALWAYS_INLINE void UpdateTransformForNonRootSVG(); + ALWAYS_INLINE void UpdateTransformForSVGChild(); ALWAYS_INLINE bool EffectCanUseCurrentClipAsOutputClip() const; ALWAYS_INLINE void UpdateEffect(); ALWAYS_INLINE void UpdateFilter(); @@ -213,6 +221,8 @@ class FragmentPaintPropertyTreeBuilder { full_context_.force_subtree_update_reasons; } + bool IsInNGFragmentTraversal() const { return pre_paint_info_; } + void OnUpdate(PaintPropertyChangeType change) { property_changed_ = std::max(property_changed_, change); } @@ -251,6 +261,7 @@ class FragmentPaintPropertyTreeBuilder { } const LayoutObject& object_; + NGPrePaintInfo* pre_paint_info_; // The tree builder context for the whole object. PaintPropertyTreeBuilderContext& full_context_; // The tree builder context for the current fragment, which is one of the @@ -262,17 +273,6 @@ class FragmentPaintPropertyTreeBuilder { PaintPropertyChangeType::kUnchanged; }; -static bool NeedsScrollNode(const LayoutObject& object, - CompositingReasons direct_compositing_reasons) { - if (!object.HasOverflowClip()) - return false; - - if (direct_compositing_reasons & CompositingReason::kRootScroller) - return true; - - return ToLayoutBox(object).GetScrollableArea()->ScrollsOverflow(); -} - // True if a scroll translation is needed for static scroll offset (e.g., // overflow hidden with scroll), or if a scroll node is needed for composited // scrolling. @@ -288,7 +288,7 @@ static bool NeedsScrollOrScrollTranslation( ScrollOffset scroll_offset = box.GetScrollableArea()->GetScrollOffset(); return !scroll_offset.IsZero() || - NeedsScrollNode(object, direct_compositing_reasons); + box.NeedsScrollNode(direct_compositing_reasons); } static bool NeedsReplacedContentTransform(const LayoutObject& object) { @@ -350,7 +350,7 @@ 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 (IsA<LayoutView>(object)) { const auto* parent_frame = object.GetFrame()->Tree().Parent(); return IsA<LocalFrame>(parent_frame); } @@ -382,7 +382,7 @@ static bool NeedsPaintOffsetTranslation( const LayoutBoxModelObject& box_model = ToLayoutBoxModelObject(object); - if (box_model.IsLayoutView()) { + if (IsA<LayoutView>(box_model)) { // A translation node for LayoutView is always created to ensure fixed and // absolute contexts use the correct transform space. return true; @@ -421,6 +421,12 @@ static bool NeedsPaintOffsetTranslation( if (RuntimeEnabledFeatures::CompositeAfterPaintEnabled()) { if (direct_compositing_reasons != CompositingReason::kNone) return true; + // In CompositeAfterPaint though we don't treat hidden backface as + // a direct compositing reason, it's very likely that the object will + // be composited, so a paint offset translation will be beneficial. + if (box_model.StyleRef().BackfaceVisibility() == + EBackfaceVisibility::kHidden) + return true; } else if (box_model.GetCompositingState() == kPaintsIntoOwnBacking) { return true; } @@ -481,7 +487,7 @@ bool FragmentPaintPropertyTreeBuilder::IsAffectedByOuterViewportBoundsDelta() // It's affected by viewport only if the container is the LayoutView. DCHECK_EQ(full_context_.container_for_fixed_position, object_.Container()); - return full_context_.container_for_fixed_position->IsLayoutView(); + return IsA<LayoutView>(full_context_.container_for_fixed_position); } void FragmentPaintPropertyTreeBuilder::UpdatePaintOffsetTranslation( @@ -502,12 +508,12 @@ void FragmentPaintPropertyTreeBuilder::UpdatePaintOffsetTranslation( IsAffectedByOuterViewportBoundsDelta(); state.direct_compositing_reasons = full_context_.direct_compositing_reasons & - CompositingReason::kScrollDependentPosition; + CompositingReason::kDirectReasonsForPaintOffsetTranslationProperty; state.rendering_context_id = context_.current.rendering_context_id; OnUpdate(properties_->UpdatePaintOffsetTranslation( *context_.current.transform, std::move(state))); context_.current.transform = properties_->PaintOffsetTranslation(); - if (object_.IsLayoutView()) { + if (IsA<LayoutView>(object_)) { context_.absolute_position.transform = properties_->PaintOffsetTranslation(); context_.fixed_position.transform = properties_->PaintOffsetTranslation(); @@ -548,8 +554,20 @@ void FragmentPaintPropertyTreeBuilder::UpdateStickyTranslation() { // DCHECK_EQ(scroller_properties->Scroll(), context_.current.scroll); // However there is a bug that AncestorOverflowLayer() may be computed // incorrectly with clip escaping involved. - if (scroller_properties && - scroller_properties->Scroll() == context_.current.scroll) { + bool nearest_scroller_is_clip = + scroller_properties && + scroller_properties->Scroll() == context_.current.scroll; + + // Additionally, we also want to make sure that the nearest scroller + // actually translates this node. If it doesn't (e.g. a position fixed + // node in a scrolling document), there's no point in adding a constraint + // since scrolling won't affect it. Indeed, if we do add it, the + // compositor assumes scrolling does affect it and produces incorrect + // results. + bool translates_with_nearest_scroller = + context_.current.transform->NearestScrollTranslationNode() + .ScrollNode() == context_.current.scroll; + if (nearest_scroller_is_clip && translates_with_nearest_scroller) { const StickyPositionScrollingConstraints& layout_constraint = layer->AncestorOverflowLayer() ->GetScrollableArea() @@ -560,19 +578,17 @@ void FragmentPaintPropertyTreeBuilder::UpdateStickyTranslation() { constraint->is_anchored_right = layout_constraint.is_anchored_right; constraint->is_anchored_top = layout_constraint.is_anchored_top; constraint->is_anchored_bottom = layout_constraint.is_anchored_bottom; + constraint->left_offset = layout_constraint.left_offset.ToFloat(); constraint->right_offset = layout_constraint.right_offset.ToFloat(); constraint->top_offset = layout_constraint.top_offset.ToFloat(); constraint->bottom_offset = layout_constraint.bottom_offset.ToFloat(); constraint->constraint_box_rect = - PixelSnappedIntRect(box_model.ComputeStickyConstrainingRect()); - constraint->scroll_container_relative_sticky_box_rect = - PixelSnappedIntRect( - layout_constraint.scroll_container_relative_sticky_box_rect); - constraint->scroll_container_relative_containing_block_rect = - PixelSnappedIntRect( - layout_constraint - .scroll_container_relative_containing_block_rect); + FloatRect(box_model.ComputeStickyConstrainingRect()); + constraint->scroll_container_relative_sticky_box_rect = FloatRect( + layout_constraint.scroll_container_relative_sticky_box_rect); + constraint->scroll_container_relative_containing_block_rect = FloatRect( + layout_constraint.scroll_container_relative_containing_block_rect); if (PaintLayer* sticky_box_shifting_ancestor = layout_constraint.nearest_sticky_layer_shifting_sticky_box) { constraint->nearest_element_shifting_sticky_box = @@ -603,18 +619,29 @@ void FragmentPaintPropertyTreeBuilder::UpdateStickyTranslation() { context_.current.transform = properties_->StickyTranslation(); } -static bool NeedsTransformForNonRootSVG(const LayoutObject& object) { +static bool NeedsTransformForSVGChild(const LayoutObject& object) { // TODO(pdr): Check for the presence of a transform instead of the value. // Checking for an identity matrix will cause the property tree structure // to change during animations if the animation passes through the // identity matrix. + // TODO(crbug.com/666244): Check CompositingReasonsForAnimation here when we + // support composited transform animations in SVG. return object.IsSVGChild() && !object.IsText() && !object.LocalToSVGParentTransform().IsIdentity(); } +static void SetTransformNodeStateFromAffineTransform( + TransformPaintPropertyNode::State& state, + const AffineTransform& transform) { + if (transform.IsIdentityOrTranslation()) + state.transform_and_origin = {FloatSize(transform.E(), transform.F())}; + else + state.transform_and_origin = {TransformationMatrix(transform)}; +} + // SVG does not use the general transform update of |UpdateTransform|, instead // creating a transform node for SVG-specific transforms without 3D. -void FragmentPaintPropertyTreeBuilder::UpdateTransformForNonRootSVG() { +void FragmentPaintPropertyTreeBuilder::UpdateTransformForSVGChild() { DCHECK(properties_); DCHECK(object_.IsSVGChild()); // SVG does not use paint offset internally, except for SVGForeignObject which @@ -624,9 +651,12 @@ void FragmentPaintPropertyTreeBuilder::UpdateTransformForNonRootSVG() { if (NeedsPaintPropertyUpdate()) { AffineTransform transform = object_.LocalToSVGParentTransform(); - if (NeedsTransformForNonRootSVG(object_)) { + if (NeedsTransformForSVGChild(object_)) { // The origin is included in the local transform, so leave origin empty. - TransformPaintPropertyNode::State state{TransformationMatrix(transform)}; + // TODO(crbug.com/666244): Support composited transform animation for SVG + // using similar code as |UpdateTransform| for animations. + TransformPaintPropertyNode::State state; + SetTransformNodeStateFromAffineTransform(state, transform); OnUpdate(properties_->UpdateTransform(*context_.current.transform, std::move(state))); } else { @@ -666,8 +696,7 @@ static CompositingReasons CompositingReasonsForTransformProperty() { // will-change:opacity to avoid raster invalidation (caused by otherwise a // created/deleted effect node) when we start/stop an opacity animation. // https://crbug.com/942681 - if (!RuntimeEnabledFeatures::CompositeAfterPaintEnabled()) - reasons |= CompositingReason::kWillChangeOpacity; + reasons |= CompositingReason::kWillChangeOpacity; return reasons; } @@ -707,7 +736,7 @@ static bool ActiveTransformAnimationIsAxisAligned( void FragmentPaintPropertyTreeBuilder::UpdateTransform() { if (object_.IsSVGChild()) { - UpdateTransformForNonRootSVG(); + UpdateTransformForSVGChild(); return; } @@ -738,10 +767,12 @@ void FragmentPaintPropertyTreeBuilder::UpdateTransform() { bool disable_2d_translation_optimization = full_context_.direct_compositing_reasons & CompositingReason::kActiveTransformAnimation; - state.transform_and_origin = - TransformPaintPropertyNode::TransformAndOrigin( - matrix, TransformOrigin(box), - disable_2d_translation_optimization); + if (!disable_2d_translation_optimization && + matrix.IsIdentityOr2DTranslation()) { + state.transform_and_origin = {matrix.To2DTranslation()}; + } else { + state.transform_and_origin = {matrix, TransformOrigin(box)}; + } // TODO(trchen): transform-style should only be respected if a // PaintLayer is created. If a node with transform-style: preserve-3d @@ -778,9 +809,7 @@ void FragmentPaintPropertyTreeBuilder::UpdateTransform() { style.IsRunningTransformAnimationOnCompositor(); auto effective_change_type = properties_->UpdateTransform( *context_.current.transform, std::move(state), animation_state); - // TODO(crbug.com/953322): We need to fix this to work with CAP as well. - if (!RuntimeEnabledFeatures::CompositeAfterPaintEnabled() && - effective_change_type == + if (effective_change_type == PaintPropertyChangeType::kChangedOnlySimpleValues && properties_->Transform()->HasDirectCompositingReasons()) { if (auto* paint_artifact_compositor = @@ -826,8 +855,9 @@ static bool NeedsClipPathClip(const LayoutObject& object) { return false; } -// TODO(crbug.com/900241): Remove this function and let the caller use -// CompositingReason::kDirectReasonForEffectProperty directly. +// TODO(crbug.com/900241): When this bug is fixed, we should let NeedsEffect() +// use CompositingReason::kDirectReasonForEffectProperty directly instead of +// calling this function. We should still call this function in UpdateEffect(). static CompositingReasons CompositingReasonsForEffectProperty() { CompositingReasons reasons = CompositingReason::kDirectReasonsForEffectProperty; @@ -839,8 +869,9 @@ static CompositingReasons CompositingReasonsForEffectProperty() { // will-change:transform to avoid raster invalidation (caused by otherwise a // created/deleted effect node) when we start/stop a transform animation. // https://crbug.com/942681 - if (!RuntimeEnabledFeatures::CompositeAfterPaintEnabled()) - reasons |= CompositingReason::kWillChangeTransform; + // In CompositeAfterPaint, this also avoids decomposition of the effect when + // the object is forced compositing with will-change:transform. + reasons |= CompositingReason::kWillChangeTransform; return reasons; } @@ -1005,8 +1036,8 @@ void FragmentPaintPropertyTreeBuilder::UpdateEffect() { OnUpdateClip(properties_->UpdateMaskClip( *context_.current.clip, - ClipPaintPropertyNode::State{context_.current.transform, - FloatRoundedRect(combined_clip)})); + ClipPaintPropertyNode::State(context_.current.transform, + FloatRoundedRect(combined_clip)))); output_clip = properties_->MaskClip(); } else { OnClearClip(properties_->ClearMaskClip()); @@ -1079,9 +1110,7 @@ void FragmentPaintPropertyTreeBuilder::UpdateEffect() { // If we have simple value change, which means opacity, we should try to // directly update it on the PaintArtifactCompositor in order to avoid // doing a full rebuild. - // TODO(crbug.com/953322): We need to fix this to work with CAP as well. - if (!RuntimeEnabledFeatures::CompositeAfterPaintEnabled() && - effective_change_type == + if (effective_change_type == PaintPropertyChangeType::kChangedOnlySimpleValues && properties_->Effect()->HasDirectCompositingReasons()) { if (auto* paint_artifact_compositor = @@ -1150,8 +1179,9 @@ static bool NeedsLinkHighlightEffect(const LayoutObject& object) { return page->GetLinkHighlight().NeedsHighlightEffect(object); } -// TODO(crbug.com/900241): Remove this function and let the caller use -// CompositingReason::kDirectReasonForFilterProperty directly. +// TODO(crbug.com/900241): When this bug is fixed, we should let NeedsFilter() +// use CompositingReason::kDirectReasonForFilterProperty directly instead of +// calling this function. We should still call this function in UpdateFilter(). static CompositingReasons CompositingReasonsForFilterProperty() { CompositingReasons reasons = CompositingReason::kDirectReasonsForFilterProperty; @@ -1163,10 +1193,10 @@ static CompositingReasons CompositingReasonsForFilterProperty() { // created for will-change:transform/opacity to avoid raster invalidation // (caused by otherwise a created/deleted filter node) when we start/stop a // transform/opacity animation. https://crbug.com/942681 - if (!RuntimeEnabledFeatures::CompositeAfterPaintEnabled()) { - reasons |= CompositingReason::kWillChangeTransform | - CompositingReason::kWillChangeOpacity; - } + // In CompositeAfterPaint, this also avoids decomposition of the filter when + // the object is forced compositing with will-change:transform/opacity. + reasons |= CompositingReason::kWillChangeTransform | + CompositingReason::kWillChangeOpacity; return reasons; } @@ -1220,7 +1250,9 @@ void FragmentPaintPropertyTreeBuilder::UpdateFilter() { // On the other hand, "B" should not be clipped because the overflow clip // is not in its containing block chain, but as the filter output will be // clipped, so a blurred "B" may still be invisible. - if (!state.filter.IsEmpty()) + if (!state.filter.IsEmpty() || + (full_context_.direct_compositing_reasons & + CompositingReason::kActiveFilterAnimation)) state.output_clip = context_.current.clip; // TODO(trchen): A filter may contain spatial operations such that an @@ -1271,10 +1303,12 @@ void FragmentPaintPropertyTreeBuilder::UpdateFragmentClip() { if (NeedsPaintPropertyUpdate()) { if (context_.fragment_clip) { - const auto& clip_rect = ToSnappedClipRect(*context_.fragment_clip); + const auto& clip_rect = *context_.fragment_clip; OnUpdateClip(properties_->UpdateFragmentClip( *context_.current.clip, - ClipPaintPropertyNode::State{context_.current.transform, clip_rect})); + ClipPaintPropertyNode::State(context_.current.transform, + FloatRoundedRect(FloatRect(clip_rect)), + ToSnappedClipRect(clip_rect)))); } else { OnClearClip(properties_->ClearFragmentClip()); } @@ -1302,11 +1336,13 @@ void FragmentPaintPropertyTreeBuilder::UpdateCssClip() { // object must be a container for absolute position descendants, and will // copy from in-flow context later at updateOutOfFlowContext() step. DCHECK(object_.CanContainAbsolutePositionObjects()); - const auto& clip_rect = ToSnappedClipRect( - ToLayoutBox(object_).ClipRect(context_.current.paint_offset)); + const auto& clip_rect = + ToLayoutBox(object_).ClipRect(context_.current.paint_offset); OnUpdateClip(properties_->UpdateCssClip( *context_.current.clip, - ClipPaintPropertyNode::State{context_.current.transform, clip_rect})); + ClipPaintPropertyNode::State(context_.current.transform, + FloatRoundedRect(FloatRect(clip_rect)), + ToSnappedClipRect(clip_rect)))); } else { OnClearClip(properties_->ClearCssClip()); } @@ -1331,9 +1367,9 @@ void FragmentPaintPropertyTreeBuilder::UpdateClipPathClip( if (!NeedsClipPathClip(object_)) { OnClearClip(properties_->ClearClipPathClip()); } else { - ClipPaintPropertyNode::State state; - state.local_transform_space = context_.current.transform; - state.clip_rect = FloatRoundedRect(*fragment_data_.ClipPathBoundingBox()); + ClipPaintPropertyNode::State state( + context_.current.transform, + FloatRoundedRect(*fragment_data_.ClipPathBoundingBox())); state.clip_path = fragment_data_.ClipPathPath(); OnUpdateClip(properties_->UpdateClipPathClip(*context_.current.clip, std::move(state))); @@ -1351,7 +1387,7 @@ void FragmentPaintPropertyTreeBuilder::UpdateClipPathClip( // issue artificial page clip for each page, and always print from the origin // of the contents for which no scroll offset should be applied. static bool IsPrintingRootLayoutView(const LayoutObject& object) { - if (!object.IsLayoutView()) + if (!IsA<LayoutView>(object)) return false; const auto& frame = *object.GetFrame(); @@ -1441,9 +1477,8 @@ bool FragmentPaintPropertyTreeBuilder::NeedsOverflowControlsClip() const { scroll_controls_bounds.Unite(scrollbar->FrameRect()); if (const auto* scrollbar = scrollable_area->VerticalScrollbar()) scroll_controls_bounds.Unite(scrollbar->FrameRect()); - auto pixel_snapped_border_box_rect = - box.PixelSnappedBorderBoxRect(context_.current.paint_offset); - pixel_snapped_border_box_rect.SetLocation(IntPoint()); + IntRect pixel_snapped_border_box_rect( + IntPoint(), box.PixelSnappedBorderBoxSize(context_.current.paint_offset)); return !pixel_snapped_border_box_rect.Contains(scroll_controls_bounds); } @@ -1491,11 +1526,13 @@ void FragmentPaintPropertyTreeBuilder::UpdateOverflowControlsClip() { if (NeedsOverflowControlsClip()) { // Clip overflow controls to the border box rect. Not wrapped with // OnUpdateClip() because this clip doesn't affect descendants. - const auto& clip_rect = ToSnappedClipRect(PhysicalRect( - context_.current.paint_offset, ToLayoutBox(object_).Size())); + const auto& clip_rect = PhysicalRect(context_.current.paint_offset, + ToLayoutBox(object_).Size()); properties_->UpdateOverflowControlsClip( *context_.current.clip, - ClipPaintPropertyNode::State{context_.current.transform, clip_rect}); + ClipPaintPropertyNode::State(context_.current.transform, + FloatRoundedRect(FloatRect(clip_rect)), + ToSnappedClipRect(clip_rect))); } else { properties_->ClearOverflowControlsClip(); } @@ -1512,10 +1549,12 @@ void FragmentPaintPropertyTreeBuilder::UpdateInnerBorderRadiusClip() { if (NeedsPaintPropertyUpdate()) { if (NeedsInnerBorderRadiusClip(object_)) { const LayoutBox& box = ToLayoutBox(object_); - ClipPaintPropertyNode::State state; - state.local_transform_space = context_.current.transform; - state.clip_rect = box.StyleRef().GetRoundedInnerBorderFor(LayoutRect( - context_.current.paint_offset.ToLayoutPoint(), box.Size())); + LayoutRect box_rect(context_.current.paint_offset.ToLayoutPoint(), + box.Size()); + ClipPaintPropertyNode::State state( + context_.current.transform, + box.StyleRef().GetInnerBorderFor(box_rect), + box.StyleRef().GetRoundedInnerBorderFor(box_rect)); OnUpdateClip(properties_->UpdateInnerBorderRadiusClip( *context_.current.clip, std::move(state))); } else { @@ -1534,15 +1573,16 @@ static PhysicalRect OverflowClipRect(const LayoutBox& box, // here instead of LayoutBox::OverflowClipRect because the layout size of the // scrolling content is still affected by overlay scrollbar behavior, just not // the clip. - auto behavior = box.IsLayoutView() ? kIgnorePlatformAndCSSOverlayScrollbarSize - : kIgnorePlatformOverlayScrollbarSize; + auto behavior = IsA<LayoutView>(box) + ? kIgnorePlatformAndCSSOverlayScrollbarSize + : kIgnorePlatformOverlayScrollbarSize; return box.OverflowClipRect(offset, behavior); } static bool CanOmitOverflowClip(const LayoutObject& object) { DCHECK(NeedsOverflowClip(object)); - if (object.IsLayoutView() && !object.GetFrame()->ClipsContent()) { + if (IsA<LayoutView>(object) && !object.GetFrame()->ClipsContent()) { return true; } @@ -1589,8 +1629,8 @@ void FragmentPaintPropertyTreeBuilder::UpdateOverflowClip() { if (NeedsPaintPropertyUpdate()) { if (NeedsOverflowClip(object_) && !CanOmitOverflowClip(object_)) { - ClipPaintPropertyNode::State state; - state.local_transform_space = context_.current.transform; + ClipPaintPropertyNode::State state(context_.current.transform, + FloatRoundedRect()); if (object_.IsLayoutReplaced()) { const LayoutReplaced& replaced = ToLayoutReplaced(object_); @@ -1600,39 +1640,46 @@ void FragmentPaintPropertyTreeBuilder::UpdateOverflowClip() { // here, before applying padding and corner rounding. PhysicalRect content_rect(context_.current.paint_offset, replaced.Size()); - if (replaced.IsVideo()) { + if (IsA<LayoutVideo>(replaced)) { content_rect = LayoutReplaced::PreSnappedRectForPersistentSizing(content_rect); } // LayoutReplaced clips the foreground by rounded content box. - state.clip_rect = replaced.StyleRef().GetRoundedInnerBorderFor( + auto clip_rect = replaced.StyleRef().GetRoundedInnerBorderFor( content_rect.ToLayoutRect(), LayoutRectOutsets( -(replaced.PaddingTop() + replaced.BorderTop()), -(replaced.PaddingRight() + replaced.BorderRight()), -(replaced.PaddingBottom() + replaced.BorderBottom()), -(replaced.PaddingLeft() + replaced.BorderLeft()))); + state.SetClipRect(clip_rect, clip_rect); if (replaced.IsLayoutEmbeddedContent()) { // Embedded objects are always sized to fit the content rect, but they // could overflow by 1px due to pre-snapping. Adjust clip rect to // match pre-snapped box as a special case. - FloatRect adjusted_rect = state.clip_rect.Rect(); + FloatRect adjusted_rect = clip_rect.Rect(); adjusted_rect.SetSize(FloatSize(replaced.ReplacedContentRect().size)); - state.clip_rect.SetRect(adjusted_rect); + FloatRoundedRect adjusted_clip_rect(adjusted_rect, + clip_rect.GetRadii()); + state.SetClipRect(adjusted_clip_rect, adjusted_clip_rect); } } else if (object_.IsBox()) { - state.clip_rect = ToSnappedClipRect(OverflowClipRect( - ToLayoutBox(object_), context_.current.paint_offset)); - state.clip_rect_excluding_overlay_scrollbars = FloatClipRect( - FloatRect(PixelSnappedIntRect(ToLayoutBox(object_).OverflowClipRect( + const auto& clip_rect = OverflowClipRect(ToLayoutBox(object_), + context_.current.paint_offset); + state.SetClipRect(FloatRoundedRect(FloatRect(clip_rect)), + ToSnappedClipRect(clip_rect)); + + state.clip_rect_excluding_overlay_scrollbars = + FloatClipRect(FloatRect(ToLayoutBox(object_).OverflowClipRect( context_.current.paint_offset, - kExcludeOverlayScrollbarSizeForHitTesting)))); + kExcludeOverlayScrollbarSizeForHitTesting))); } else { DCHECK(object_.IsSVGViewportContainer()); const auto& viewport_container = ToLayoutSVGViewportContainer(object_); - state.clip_rect = FloatRoundedRect( + const auto clip_rect = FloatRoundedRect( viewport_container.LocalToSVGParentTransform().Inverse().MapRect( viewport_container.Viewport())); + state.SetClipRect(clip_rect, clip_rect); } OnUpdateClip(properties_->UpdateOverflowClip(*context_.current.clip, std::move(state))); @@ -1687,14 +1734,6 @@ void FragmentPaintPropertyTreeBuilder::UpdatePerspective() { } } -static bool ImageWasTransposed(const LayoutImage& layout_image, - const Image& image) { - return LayoutObject::ShouldRespectImageOrientation(&layout_image) == - kRespectImageOrientation && - image.IsBitmapImage() && - ToBitmapImage(image).CurrentFrameOrientation().UsesWidthAsHeight(); -} - static AffineTransform RectToRect(const FloatRect& src_rect, const FloatRect& dst_rect) { float x_scale = dst_rect.Width() / src_rect.Width(); @@ -1723,9 +1762,9 @@ void FragmentPaintPropertyTreeBuilder::UpdateReplacedContentTransform() { scoped_refptr<Image> image = layout_image.ImageResource()->GetImage(replaced_rect.Size()); if (image && !image->IsNull()) { - IntRect src_rect = image->Rect(); - if (ImageWasTransposed(layout_image, *image)) - src_rect = src_rect.TransposedRect(); + IntRect src_rect( + IntPoint(), image->Size(LayoutObject::ShouldRespectImageOrientation( + &layout_image))); content_to_parent_space = RectToRect(FloatRect(src_rect), FloatRect(replaced_rect)); } @@ -1733,8 +1772,8 @@ void FragmentPaintPropertyTreeBuilder::UpdateReplacedContentTransform() { NOTREACHED(); } if (!content_to_parent_space.IsIdentity()) { - TransformPaintPropertyNode::State state{ - TransformationMatrix(content_to_parent_space)}; + TransformPaintPropertyNode::State state; + SetTransformNodeStateFromAffineTransform(state, content_to_parent_space); state.flags.flattens_inherited_transform = context_.current.should_flatten_inherited_transform; OnUpdate(properties_->UpdateReplacedContentTransform( @@ -1769,7 +1808,7 @@ static MainThreadScrollingReasons GetMainThreadScrollingReasons( if (!object.IsBox()) return reasons; - if (object.IsLayoutView()) { + if (IsA<LayoutView>(object)) { if (object.GetFrameView() ->RequiresMainThreadScrollingForBackgroundAttachmentFixed()) { reasons |= @@ -1819,7 +1858,8 @@ void FragmentPaintPropertyTreeBuilder::UpdateScrollAndScrollTranslation() { DCHECK(properties_); if (NeedsPaintPropertyUpdate()) { - if (NeedsScrollNode(object_, full_context_.direct_compositing_reasons)) { + if (object_.IsBox() && ToLayoutBox(object_).NeedsScrollNode( + full_context_.direct_compositing_reasons)) { const LayoutBox& box = ToLayoutBox(object_); PaintLayerScrollableArea* scrollable_area = box.GetScrollableArea(); ScrollPaintPropertyNode::State state; @@ -1837,8 +1877,6 @@ void FragmentPaintPropertyTreeBuilder::UpdateScrollAndScrollTranslation() { state.user_scrollable_vertical = scrollable_area->UserInputScrollable(kVerticalScrollbar); - state.scrolls_outer_viewport = box.IsGlobalRootScroller(); - // TODO(bokan): We probably don't need to pass ancestor reasons down the // scroll tree. On the compositor, in // LayerTreeHostImpl::FindScrollNodeForDeviceViewportPoint, we walk up @@ -1981,7 +2019,7 @@ void FragmentPaintPropertyTreeBuilder::UpdateOutOfFlowContext() { if (object_.CanContainAbsolutePositionObjects()) context_.absolute_position = context_.current; - if (object_.IsLayoutView()) { + if (IsA<LayoutView>(object_)) { const auto* initial_fixed_transform = context_.fixed_position.transform; context_.fixed_position = context_.current; @@ -2020,8 +2058,8 @@ void FragmentPaintPropertyTreeBuilder::UpdateOutOfFlowContext() { if (NeedsPaintPropertyUpdate()) { OnUpdate(properties_->UpdateCssClipFixedPosition( *context_.fixed_position.clip, - ClipPaintPropertyNode::State{&css_clip->LocalTransformSpace(), - css_clip->ClipRect()})); + ClipPaintPropertyNode::State(&css_clip->LocalTransformSpace(), + css_clip->PixelSnappedClipRect()))); } if (properties_->CssClipFixedPosition()) context_.fixed_position.clip = properties_->CssClipFixedPosition(); @@ -2202,48 +2240,104 @@ static PhysicalOffset PaintOffsetInPaginationContainer( } void FragmentPaintPropertyTreeBuilder::UpdatePaintOffset() { - // Paint offsets for fragmented content are computed from scratch. - const auto* enclosing_pagination_layer = - full_context_.painting_layer->EnclosingPaginationLayer(); - if (enclosing_pagination_layer && - // Except if the paint_offset_root is below the pagination container, - // in which case fragmentation offsets are already baked into the paint - // offset transform for paint_offset_root. - !context_.current.paint_offset_root->PaintingLayer() - ->EnclosingPaginationLayer()) { - // Set fragment visual paint offset. - PhysicalOffset paint_offset = - PaintOffsetInPaginationContainer(object_, *enclosing_pagination_layer); - - paint_offset += fragment_data_.PaginationOffset(); - paint_offset += context_.repeating_paint_offset_adjustment; - paint_offset += - VisualOffsetFromPaintOffsetRoot(context_, enclosing_pagination_layer); - - // The paint offset root can have a subpixel paint offset adjustment. - // The paint offset root always has one fragment. - const auto& paint_offset_root_fragment = - context_.current.paint_offset_root->FirstFragment(); - paint_offset += paint_offset_root_fragment.PaintOffset(); - - context_.current.paint_offset = paint_offset; - return; - } + if (!IsInNGFragmentTraversal()) { + // Paint offsets for fragmented content are computed from scratch. + const auto* enclosing_pagination_layer = + full_context_.painting_layer->EnclosingPaginationLayer(); + if (enclosing_pagination_layer && + // Except if the paint_offset_root is below the pagination container, in + // which case fragmentation offsets are already baked into the paint + // offset transform for paint_offset_root. + !context_.current.paint_offset_root->PaintingLayer() + ->EnclosingPaginationLayer()) { + // Set fragment visual paint offset. + PhysicalOffset paint_offset = PaintOffsetInPaginationContainer( + object_, *enclosing_pagination_layer); + + paint_offset += fragment_data_.LegacyPaginationOffset(); + paint_offset += context_.repeating_paint_offset_adjustment; + paint_offset += + VisualOffsetFromPaintOffsetRoot(context_, enclosing_pagination_layer); + + // The paint offset root can have a subpixel paint offset adjustment. + // The paint offset root always has one fragment. + const auto& paint_offset_root_fragment = + context_.current.paint_offset_root->FirstFragment(); + paint_offset += paint_offset_root_fragment.PaintOffset(); + + context_.current.paint_offset = paint_offset; + return; + } - if (object_.IsFloating() && !object_.IsInLayoutNGInlineFormattingContext()) - context_.current.paint_offset = context_.paint_offset_for_float; + if (object_.IsFloating() && !object_.IsInLayoutNGInlineFormattingContext()) + context_.current.paint_offset = context_.paint_offset_for_float; - // Multicolumn spanners are painted starting at the multicolumn container (but - // still inherit properties in layout-tree order) so reset the paint offset. - if (object_.IsColumnSpanAll()) { - context_.current.paint_offset = - object_.Container()->FirstFragment().PaintOffset(); + // Multicolumn spanners are painted starting at the multicolumn container + // (but still inherit properties in layout-tree order) so reset the paint + // offset. + if (object_.IsColumnSpanAll()) { + context_.current.paint_offset = + object_.Container()->FirstFragment().PaintOffset(); + } } if (object_.IsBoxModelObject()) { const LayoutBoxModelObject& box_model_object = ToLayoutBoxModelObject(object_); - switch (box_model_object.StyleRef().GetPosition()) { + EPosition position = box_model_object.StyleRef().GetPosition(); + if (IsInNGFragmentTraversal() && + (position == EPosition::kAbsolute || position == EPosition::kFixed)) { + // The LayoutNG fragment tree structure is very similar to the containing + // block structure, with the exception of out-of-flow positioned boxes + // whose containing block is a non-atomic inline. If this is the case, we + // now need to add the offsets introduced by all inlines in the ancestry + // that affect us. This is a way to work around the discrepancy between + // the NG fragment tree structure and the actual containing block tree + // structure. + // TODO(mstensho): It's not good to walk up the ancestry + // (LayoutObject::Container()) like this, in fact pretty disastrous for + // e.g. fixed positioned objects, whose containing block is typically far + // up in the tree. Should we introduce a bit on LayoutObject that's set + // when this is necessary (or likely to be necessary)? + const LayoutObject* container = object_.Container(); + if (container->IsLayoutInline() || container->IsAnonymousBlock()) { + // Set up context_ and full_context_ to be aware of the inline that is + // the actual containing block. This will be used by the code further + // down in this method. + // TODO(mstensho): This is currently incomplete. We're failing cases + // where the containing block sets up a filter, for instance. + if (position == EPosition::kFixed) + full_context_.container_for_fixed_position = container; + full_context_.container_for_absolute_position = container; + PhysicalOffset relative_offset; + if (const LayoutBox* box = ToLayoutBoxOrNull(container)) { + // If the OOF is contained by an anonymous block (because of inline + // continuations), we need to take that into account. + // + // Example: + // <span style="position:relative;"> + // <div> + // <div id="box_model_object" style="position:absolute;"> + DCHECK(box->IsAnonymousBlock()); + relative_offset = + box->PhysicalLocation() + box->OffsetForInFlowPosition(); + } else { + do { + relative_offset += + ToLayoutInline(container)->OffsetForInFlowPosition(); + container = container->Container(); + } while (container->IsLayoutInline()); + } + if (position == EPosition::kFixed) { + context_.fixed_position = context_.current; + context_.fixed_position.paint_offset += relative_offset; + } + context_.absolute_position = context_.current; + context_.absolute_position.paint_offset += relative_offset; + } + } + + switch (position) { case EPosition::kStatic: break; case EPosition::kRelative: @@ -2294,6 +2388,15 @@ void FragmentPaintPropertyTreeBuilder::UpdatePaintOffset() { } } + if (IsInNGFragmentTraversal()) { + // Text and non-atomic inlines are special, in that they share one + // FragmentData per fragmentainer, so their paint offset is kept at their + // container. For all other objects, include the offset now. + if (object_.IsBox()) + context_.current.paint_offset += pre_paint_info_->iterator->Link().offset; + return; + } + if (object_.IsBox()) { // TODO(pdr): Several calls in this function walk back up the tree to // calculate containers (e.g., physicalLocation, offsetForInFlowPosition*). @@ -2396,6 +2499,14 @@ void FragmentPaintPropertyTreeBuilder::UpdateForObjectLocationAndSize( fragment_data_.SetPaintOffset(context_.current.paint_offset); fragment_data_.InvalidateClipPathCache(); + if (object_.IsBox()) { + // See PaintLayerScrollableArea::PixelSnappedBorderBoxRect() for the + // reason of this. + if (auto* scrollable_area = ToLayoutBox(object_).GetScrollableArea()) + scrollable_area->PositionOverflowControls(); + } + + object_.GetMutableForPainting().InvalidateIntersectionObserverCachedRects(); object_.GetFrameView()->SetIntersectionObservationState( LocalFrameView::kDesired); } @@ -2513,9 +2624,7 @@ void FragmentPaintPropertyTreeBuilder::UpdateForChildren() { void PaintPropertyTreeBuilder::InitFragmentPaintProperties( FragmentData& fragment, - bool needs_paint_properties, - const PhysicalOffset& pagination_offset, - LayoutUnit logical_top_in_flow_thread) { + bool needs_paint_properties) { if (needs_paint_properties) { fragment.EnsurePaintProperties(); } else if (fragment.PaintProperties()) { @@ -2524,16 +2633,35 @@ void PaintPropertyTreeBuilder::InitFragmentPaintProperties( PaintPropertyTreeBuilderContext::kSubtreeUpdateIsolationBlocked; fragment.ClearPaintProperties(); } - fragment.SetPaginationOffset(pagination_offset); +} + +void PaintPropertyTreeBuilder::InitFragmentPaintPropertiesForLegacy( + FragmentData& fragment, + bool needs_paint_properties, + const PhysicalOffset& pagination_offset, + LayoutUnit logical_top_in_flow_thread) { + DCHECK(!IsInNGFragmentTraversal()); + InitFragmentPaintProperties(fragment, needs_paint_properties); + fragment.SetLegacyPaginationOffset(pagination_offset); fragment.SetLogicalTopInFlowThread(logical_top_in_flow_thread); } +void PaintPropertyTreeBuilder::InitFragmentPaintPropertiesForNG( + bool needs_paint_properties) { + InitFragmentPaintProperties(pre_paint_info_->fragment_data, + needs_paint_properties); + if (context_.fragments.IsEmpty()) + context_.fragments.push_back(PaintPropertyTreeBuilderFragmentContext()); + else + context_.fragments.resize(1); +} + void PaintPropertyTreeBuilder::InitSingleFragmentFromParent( bool needs_paint_properties) { FragmentData& first_fragment = object_.GetMutableForPainting().FirstFragment(); first_fragment.ClearNextFragment(); - InitFragmentPaintProperties(first_fragment, needs_paint_properties); + InitFragmentPaintPropertiesForLegacy(first_fragment, needs_paint_properties); if (context_.fragments.IsEmpty()) { context_.fragments.push_back(PaintPropertyTreeBuilderFragmentContext()); } else { @@ -2591,6 +2719,7 @@ void PaintPropertyTreeBuilder::InitSingleFragmentFromParent( } void PaintPropertyTreeBuilder::UpdateCompositedLayerPaginationOffset() { + DCHECK(!IsInNGFragmentTraversal()); if (RuntimeEnabledFeatures::CompositeAfterPaintEnabled()) return; @@ -2620,7 +2749,7 @@ void PaintPropertyTreeBuilder::UpdateCompositedLayerPaginationOffset() { BoundingBoxInPaginationContainer(object_, *enclosing_pagination_layer) .ToLayoutRect()); if (!iterator.AtEnd()) { - first_fragment.SetPaginationOffset( + first_fragment.SetLegacyPaginationOffset( PhysicalOffsetToBeNoop(iterator.PaginationOffset())); first_fragment.SetLogicalTopInFlowThread( iterator.FragmentainerLogicalTopInFlowThread()); @@ -2629,7 +2758,7 @@ void PaintPropertyTreeBuilder::UpdateCompositedLayerPaginationOffset() { // All objects under the composited layer use the same pagination offset. const auto& fragment = parent_composited_layer->GetLayoutObject().FirstFragment(); - first_fragment.SetPaginationOffset(fragment.PaginationOffset()); + first_fragment.SetLegacyPaginationOffset(fragment.LegacyPaginationOffset()); first_fragment.SetLogicalTopInFlowThread(fragment.LogicalTopInFlowThread()); } } @@ -2867,6 +2996,7 @@ PaintPropertyTreeBuilderFragmentContext PaintPropertyTreeBuilder::ContextForFragment( const base::Optional<PhysicalRect>& fragment_clip, LayoutUnit logical_top_in_flow_thread) const { + DCHECK(!IsInNGFragmentTraversal()); const auto& parent_fragments = context_.fragments; if (parent_fragments.IsEmpty()) return PaintPropertyTreeBuilderFragmentContext(); @@ -2977,6 +3107,7 @@ PaintPropertyTreeBuilder::ContextForFragment( void PaintPropertyTreeBuilder::CreateFragmentContextsInFlowThread( bool needs_paint_properties) { + DCHECK(!IsInNGFragmentTraversal()); // We need at least the fragments for all fragmented objects, which store // their local border box properties and paint invalidation data (such // as paint offset and visual rect) on each fragment. @@ -3068,11 +3199,11 @@ void PaintPropertyTreeBuilder::CreateFragmentContextsInFlowThread( new_fragment_contexts.back().fragment_clip; fragments_changed = !!old_fragment_clip != !!new_fragment_clip || (old_fragment_clip && new_fragment_clip && - old_fragment_clip->ClipRect() != + old_fragment_clip->PixelSnappedClipRect() != ToSnappedClipRect(*new_fragment_clip)); } - InitFragmentPaintProperties( + InitFragmentPaintPropertiesForLegacy( *current_fragment_data, needs_paint_properties || new_fragment_contexts.back().fragment_clip, pagination_offset, logical_top_in_flow_thread); @@ -3190,9 +3321,9 @@ void PaintPropertyTreeBuilder::CreateFragmentDataForRepeatingInPagedMedia( fragment_data = fragment_data ? &fragment_data->EnsureNextFragment() : &object_.GetMutableForPainting().FirstFragment(); - InitFragmentPaintProperties(*fragment_data, needs_paint_properties, - PhysicalOffset(), - fragment_context.logical_top_in_flow_thread); + InitFragmentPaintPropertiesForLegacy( + *fragment_data, needs_paint_properties, PhysicalOffset(), + fragment_context.logical_top_in_flow_thread); } DCHECK(fragment_data); fragment_data->ClearNextFragment(); @@ -3215,7 +3346,7 @@ bool PaintPropertyTreeBuilder::UpdateFragments() { // the paint offset and border box has been computed. MayNeedClipPathClip(object_) || NeedsEffect(object_, context_.direct_compositing_reasons) || - NeedsTransformForNonRootSVG(object_) || + NeedsTransformForSVGChild(object_) || NeedsFilter(object_, context_.direct_compositing_reasons) || NeedsCssClip(object_) || NeedsInnerBorderRadiusClip(object_) || NeedsOverflowClip(object_) || NeedsPerspective(object_) || @@ -3229,25 +3360,29 @@ bool PaintPropertyTreeBuilder::UpdateFragments() { // Need of fragmentation clip will be determined in CreateFragmentContexts(). - if (object_.IsFixedPositionObjectInPagedMedia()) { - // This flag applies to the object itself and descendants. - context_.is_repeating_fixed_position = true; - CreateFragmentContextsForRepeatingFixedPosition(); - } else if (ObjectIsRepeatingTableSectionInPagedMedia()) { - context_.repeating_table_section = - &ToInterface<LayoutNGTableSectionInterface>(object_); - CreateFragmentContextsForRepeatingTableSectionInPagedMedia(); - } - - if (IsRepeatingInPagedMedia()) { - CreateFragmentDataForRepeatingInPagedMedia(needs_paint_properties); - } else if (context_.painting_layer->ShouldFragmentCompositedBounds()) { - CreateFragmentContextsInFlowThread(needs_paint_properties); + if (IsInNGFragmentTraversal()) { + InitFragmentPaintPropertiesForNG(needs_paint_properties); } else { - InitSingleFragmentFromParent(needs_paint_properties); - UpdateCompositedLayerPaginationOffset(); - context_.is_repeating_fixed_position = false; - context_.repeating_table_section = nullptr; + if (object_.IsFixedPositionObjectInPagedMedia()) { + // This flag applies to the object itself and descendants. + context_.is_repeating_fixed_position = true; + CreateFragmentContextsForRepeatingFixedPosition(); + } else if (ObjectIsRepeatingTableSectionInPagedMedia()) { + context_.repeating_table_section = + &ToInterface<LayoutNGTableSectionInterface>(object_); + CreateFragmentContextsForRepeatingTableSectionInPagedMedia(); + } + + if (IsRepeatingInPagedMedia()) { + CreateFragmentDataForRepeatingInPagedMedia(needs_paint_properties); + } else if (context_.painting_layer->ShouldFragmentCompositedBounds()) { + CreateFragmentContextsInFlowThread(needs_paint_properties); + } else { + InitSingleFragmentFromParent(needs_paint_properties); + UpdateCompositedLayerPaginationOffset(); + context_.is_repeating_fixed_position = false; + context_.repeating_table_section = nullptr; + } } if (object_.IsSVGHiddenContainer()) { @@ -3272,7 +3407,8 @@ bool PaintPropertyTreeBuilder::UpdateFragments() { context_.has_svg_hidden_container_ancestor); } - UpdateRepeatingTableSectionPaintOffsetAdjustment(); + if (!IsInNGFragmentTraversal()) + UpdateRepeatingTableSectionPaintOffsetAdjustment(); return needs_paint_properties != had_paint_properties; } @@ -3288,17 +3424,15 @@ bool PaintPropertyTreeBuilder::ObjectTypeMightNeedMultipleFragmentData() const { } void PaintPropertyTreeBuilder::UpdatePaintingLayer() { - bool changed_painting_layer = false; if (object_.HasLayer() && ToLayoutBoxModelObject(object_).HasSelfPaintingLayer()) { context_.painting_layer = ToLayoutBoxModelObject(object_).Layer(); - changed_painting_layer = true; - } else if (object_.IsColumnSpanAll() || - object_.IsFloatingWithNonContainingBlockParent()) { + } else if (!IsInNGFragmentTraversal() && + (object_.IsColumnSpanAll() || + object_.IsFloatingWithNonContainingBlockParent())) { // See LayoutObject::paintingLayer() for the special-cases of floating under // inline and multicolumn. context_.painting_layer = object_.PaintingLayer(); - changed_painting_layer = true; } DCHECK(context_.painting_layer == object_.PaintingLayer()); } @@ -3318,22 +3452,48 @@ PaintPropertyChangeType PaintPropertyTreeBuilder::UpdateForSelf() { property_changed = PaintPropertyChangeType::kNodeAddedOrRemoved; } else { DCHECK_EQ(context_.direct_compositing_reasons, CompositingReason::kNone); - object_.GetMutableForPainting().FirstFragment().ClearNextFragment(); + if (!IsInNGFragmentTraversal()) + object_.GetMutableForPainting().FirstFragment().ClearNextFragment(); } - auto* fragment_data = &object_.GetMutableForPainting().FirstFragment(); - for (auto& fragment_context : context_.fragments) { - FragmentPaintPropertyTreeBuilder builder(object_, context_, - fragment_context, *fragment_data); + if (pre_paint_info_) { + DCHECK_EQ(context_.fragments.size(), 1u); + FragmentPaintPropertyTreeBuilder builder(object_, pre_paint_info_, context_, + context_.fragments[0], + pre_paint_info_->fragment_data); builder.UpdateForSelf(); property_changed = std::max(property_changed, builder.PropertyChanged()); - fragment_data = fragment_data->NextFragment(); + } else { + auto* fragment_data = &object_.GetMutableForPainting().FirstFragment(); + for (auto& fragment_context : context_.fragments) { + FragmentPaintPropertyTreeBuilder builder( + object_, /* pre_paint_info */ nullptr, context_, fragment_context, + *fragment_data); + builder.UpdateForSelf(); + property_changed = std::max(property_changed, builder.PropertyChanged()); + fragment_data = fragment_data->NextFragment(); + } + DCHECK(!fragment_data); } - DCHECK(!fragment_data); // We need to update property tree states of paint chunks. if (property_changed >= PaintPropertyChangeType::kNodeAddedOrRemoved) { context_.painting_layer->SetNeedsRepaint(); + if (object_.IsDocumentElement()) { + // View background painting depends on existence of the document element's + // paint properties (see callsite of ViewPainter::PaintRootGroup()). + // Invalidate view background display item clients. + // SetBackgroundNeedsFullPaintInvalidation() won't work here because we + // have already walked the LayoutView in PrePaintTreeWalk. + LayoutView* layout_view = object_.View(); + layout_view->Layer()->SetNeedsRepaint(); + auto reason = PaintInvalidationReason::kBackground; + static_cast<const DisplayItemClient*>(layout_view)->Invalidate(reason); + if (auto* scrollable_area = layout_view->GetScrollableArea()) { + scrollable_area->GetScrollingBackgroundDisplayItemClient().Invalidate( + reason); + } + } } return property_changed; @@ -3345,13 +3505,21 @@ PaintPropertyChangeType PaintPropertyTreeBuilder::UpdateForChildren() { if (!ObjectTypeMightNeedPaintProperties()) return property_changed; - auto* fragment_data = &object_.GetMutableForPainting().FirstFragment(); + FragmentData* fragment_data; + if (pre_paint_info_) { + DCHECK_EQ(context_.fragments.size(), 1u); + fragment_data = &pre_paint_info_->fragment_data; + DCHECK(fragment_data); + } else { + fragment_data = &object_.GetMutableForPainting().FirstFragment(); + } + // For now, only consider single fragment elements as possible isolation // boundaries. // TODO(crbug.com/890932): See if this is needed. bool is_isolated = context_.fragments.size() == 1u; for (auto& fragment_context : context_.fragments) { - FragmentPaintPropertyTreeBuilder builder(object_, context_, + FragmentPaintPropertyTreeBuilder builder(object_, pre_paint_info_, context_, fragment_context, *fragment_data); // The element establishes an isolation boundary if it has isolation nodes // before and after updating the children. In other words, if it didn't have @@ -3364,7 +3532,12 @@ PaintPropertyChangeType PaintPropertyTreeBuilder::UpdateForChildren() { property_changed = std::max(property_changed, builder.PropertyChanged()); fragment_data = fragment_data->NextFragment(); } - DCHECK(!fragment_data); + + // With NG fragment traversal we were supplied with the right FragmentData by + // the caller, and we only ran one lap in the loop above. Whether or not there + // are more FragmentData objects following is irrelevant then. + DCHECK(pre_paint_info_ || !fragment_data); + if (object_.SubtreePaintPropertyUpdateReasons() != static_cast<unsigned>(SubtreePaintPropertyUpdateReason::kNone)) { if (AreSubtreeUpdateReasonsIsolationPiercing( diff --git a/chromium/third_party/blink/renderer/core/paint/paint_property_tree_builder.h b/chromium/third_party/blink/renderer/core/paint/paint_property_tree_builder.h index 812cb587cc8..22e4495fda1 100644 --- a/chromium/third_party/blink/renderer/core/paint/paint_property_tree_builder.h +++ b/chromium/third_party/blink/renderer/core/paint/paint_property_tree_builder.h @@ -20,6 +20,7 @@ class FragmentData; class LayoutObject; class LayoutNGTableSectionInterface; class LocalFrameView; +class NGFragmentChildIterator; class PaintLayer; class VisualViewport; @@ -194,6 +195,18 @@ class VisualViewportPaintPropertyTreeBuilder { PaintPropertyTreeBuilderContext&); }; +struct NGPrePaintInfo { + STACK_ALLOCATED(); + + public: + NGPrePaintInfo(const NGFragmentChildIterator& iterator, + FragmentData& fragment_data) + : iterator(iterator), fragment_data(fragment_data) {} + + const NGFragmentChildIterator& iterator; + FragmentData& fragment_data; +}; + // Creates paint property tree nodes for non-local effects in the layout tree. // Non-local effects include but are not limited to: overflow clip, transform, // fixed-pos, animation, mask, filters, etc. It expects to be invoked for each @@ -206,8 +219,9 @@ class PaintPropertyTreeBuilder { PaintPropertyTreeBuilderContext&); PaintPropertyTreeBuilder(const LayoutObject& object, + NGPrePaintInfo* pre_paint_info, PaintPropertyTreeBuilderContext& context) - : object_(object), context_(context) {} + : object_(object), pre_paint_info_(pre_paint_info), context_(context) {} // Update the paint properties that affect this object (e.g., properties like // paint offset translation) and ensure the context is up to date. Also @@ -221,11 +235,15 @@ class PaintPropertyTreeBuilder { PaintPropertyChangeType UpdateForChildren(); private: - ALWAYS_INLINE void InitFragmentPaintProperties( + ALWAYS_INLINE void InitFragmentPaintProperties(FragmentData&, + bool needs_paint_properties); + ALWAYS_INLINE void InitFragmentPaintPropertiesForLegacy( FragmentData&, bool needs_paint_properties, const PhysicalOffset& pagination_offset = PhysicalOffset(), LayoutUnit logical_top_in_flow_thread = LayoutUnit()); + ALWAYS_INLINE void InitFragmentPaintPropertiesForNG( + bool needs_paint_properties); ALWAYS_INLINE void InitSingleFragmentFromParent(bool needs_paint_properties); ALWAYS_INLINE bool ObjectTypeMightNeedMultipleFragmentData() const; ALWAYS_INLINE bool ObjectTypeMightNeedPaintProperties() const; @@ -249,7 +267,11 @@ class PaintPropertyTreeBuilder { ALWAYS_INLINE void UpdateRepeatingTableHeaderPaintOffsetAdjustment(); ALWAYS_INLINE void UpdateRepeatingTableFooterPaintOffsetAdjustment(); + bool IsInNGFragmentTraversal() const { return pre_paint_info_; } + const LayoutObject& object_; + NGPrePaintInfo* pre_paint_info_; + PaintPropertyTreeBuilderContext& context_; }; 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 030a1f0ed31..1805b3db5f4 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 @@ -135,8 +135,7 @@ TEST_P(PaintPropertyTreeBuilderTest, FixedPosition) { transformed_scroll->setScrollTop(5); LocalFrameView* frame_view = GetDocument().View(); - frame_view->UpdateAllLifecyclePhases( - DocumentLifecycle::LifecycleUpdateReason::kTest); + frame_view->UpdateAllLifecyclePhases(DocumentUpdateReason::kTest); // target1 is a fixed-position element inside an absolute-position scrolling // element. It should be attached under the viewport to skip scrolling and @@ -145,7 +144,7 @@ TEST_P(PaintPropertyTreeBuilderTest, FixedPosition) { const ObjectPaintProperties* target1_properties = target1->GetLayoutObject()->FirstFragment().PaintProperties(); EXPECT_EQ(FloatRoundedRect(200, 150, 100, 100), - target1_properties->OverflowClip()->ClipRect()); + target1_properties->OverflowClip()->UnsnappedClipRect()); // Likewise, it inherits clip from the viewport, skipping overflow clip of the // scroller. EXPECT_EQ(DocContentClip(), target1_properties->OverflowClip()->Parent()); @@ -172,7 +171,7 @@ TEST_P(PaintPropertyTreeBuilderTest, FixedPosition) { const ObjectPaintProperties* scroller_properties = scroller->GetLayoutObject()->FirstFragment().PaintProperties(); EXPECT_EQ(FloatRoundedRect(200, 150, 100, 100), - target2_properties->OverflowClip()->ClipRect()); + target2_properties->OverflowClip()->UnsnappedClipRect()); EXPECT_EQ(scroller_properties->OverflowClip(), target2_properties->OverflowClip()->Parent()); // target2 should not have it's own scroll node and instead should inherit @@ -198,8 +197,7 @@ TEST_P(PaintPropertyTreeBuilderTest, PositionAndScroll) { Element* scroller = GetDocument().getElementById("scroller"); scroller->scrollTo(0, 100); LocalFrameView* frame_view = GetDocument().View(); - frame_view->UpdateAllLifecyclePhases( - DocumentLifecycle::LifecycleUpdateReason::kTest); + frame_view->UpdateAllLifecyclePhases(DocumentUpdateReason::kTest); const ObjectPaintProperties* scroller_properties = scroller->GetLayoutObject()->FirstFragment().PaintProperties(); EXPECT_EQ(FloatSize(0, -100), @@ -219,7 +217,7 @@ TEST_P(PaintPropertyTreeBuilderTest, PositionAndScroll) { EXPECT_EQ(FloatSize(120, 340), scroller_properties->PaintOffsetTranslation()->Translation2D()); EXPECT_EQ(FloatRoundedRect(0, 0, 413, 317), - scroller_properties->OverflowClip()->ClipRect()); + scroller_properties->OverflowClip()->UnsnappedClipRect()); EXPECT_EQ(DocContentClip(), scroller_properties->OverflowClip()->Parent()); CHECK_EXACT_VISUAL_RECT(PhysicalRect(120, 340, 413, 317), scroller->GetLayoutObject(), @@ -237,7 +235,7 @@ TEST_P(PaintPropertyTreeBuilderTest, PositionAndScroll) { EXPECT_EQ(rel_pos_properties->Transform(), &rel_pos_properties->OverflowClip()->LocalTransformSpace()); EXPECT_EQ(FloatRoundedRect(0, 0, 100, 200), - rel_pos_properties->OverflowClip()->ClipRect()); + rel_pos_properties->OverflowClip()->UnsnappedClipRect()); EXPECT_EQ(scroller_properties->OverflowClip(), rel_pos_properties->OverflowClip()->Parent()); CHECK_EXACT_VISUAL_RECT(PhysicalRect(), rel_pos->GetLayoutObject(), @@ -255,7 +253,7 @@ TEST_P(PaintPropertyTreeBuilderTest, PositionAndScroll) { EXPECT_EQ(abs_pos_properties->Transform(), &abs_pos_properties->OverflowClip()->LocalTransformSpace()); EXPECT_EQ(FloatRoundedRect(0, 0, 300, 400), - abs_pos_properties->OverflowClip()->ClipRect()); + abs_pos_properties->OverflowClip()->UnsnappedClipRect()); EXPECT_EQ(DocContentClip(), abs_pos_properties->OverflowClip()->Parent()); CHECK_EXACT_VISUAL_RECT(PhysicalRect(123, 456, 300, 400), abs_pos->GetLayoutObject(), @@ -278,7 +276,8 @@ TEST_P(PaintPropertyTreeBuilderTest, OverflowScrollExcludeScrollbars) { EXPECT_EQ(DocContentClip(), overflow_clip->Parent()); EXPECT_EQ(properties->PaintOffsetTranslation(), &overflow_clip->LocalTransformSpace()); - EXPECT_EQ(FloatRoundedRect(10, 10, 100, 100), overflow_clip->ClipRect()); + EXPECT_EQ(FloatRoundedRect(10, 10, 100, 100), + overflow_clip->UnsnappedClipRect()); PaintLayer* paint_layer = ToLayoutBoxModelObject(GetLayoutObjectByElementId("scroller"))->Layer(); @@ -287,7 +286,7 @@ TEST_P(PaintPropertyTreeBuilderTest, OverflowScrollExcludeScrollbars) { ->IsOverlayScrollbar()); EXPECT_EQ(FloatClipRect(FloatRect(10, 10, 93, 93)), - overflow_clip->ClipRectExcludingOverlayScrollbars()); + overflow_clip->UnsnappedClipRectExcludingOverlayScrollbars()); } TEST_P(PaintPropertyTreeBuilderTest, OverflowScrollExcludeScrollbarsSubpixel) { @@ -307,15 +306,18 @@ TEST_P(PaintPropertyTreeBuilderTest, OverflowScrollExcludeScrollbarsSubpixel) { EXPECT_EQ(DocContentClip(), overflow_clip->Parent()); EXPECT_EQ(properties->PaintOffsetTranslation(), &overflow_clip->LocalTransformSpace()); - EXPECT_EQ(FloatRoundedRect(10, 10, 101, 100), overflow_clip->ClipRect()); + EXPECT_EQ(FloatRoundedRect(10, 10, 100.5, 100), + overflow_clip->UnsnappedClipRect()); + EXPECT_EQ(FloatRoundedRect(10, 10, 101, 100), + overflow_clip->PixelSnappedClipRect()); EXPECT_TRUE(ToLayoutBox(scroller) ->GetScrollableArea() ->VerticalScrollbar() ->IsOverlayScrollbar()); - EXPECT_EQ(FloatClipRect(FloatRect(10, 10, 94, 93)), - overflow_clip->ClipRectExcludingOverlayScrollbars()); + EXPECT_EQ(FloatClipRect(FloatRect(10, 10, 93.5, 93)), + overflow_clip->UnsnappedClipRectExcludingOverlayScrollbars()); } TEST_P(PaintPropertyTreeBuilderTest, OverflowScrollExcludeCssOverlayScrollbar) { @@ -335,7 +337,8 @@ TEST_P(PaintPropertyTreeBuilderTest, OverflowScrollExcludeCssOverlayScrollbar) { )HTML"); // The document content should not be clipped by the overlay scrollbar because // the scrollbar can be transparent and the content needs to paint below. - EXPECT_EQ(DocContentClip()->ClipRect(), FloatRoundedRect(0, 0, 800, 600)); + EXPECT_EQ(DocContentClip()->UnsnappedClipRect(), + FloatRoundedRect(0, 0, 800, 600)); } TEST_P(PaintPropertyTreeBuilderTest, OverflowScrollVerticalRL) { @@ -368,9 +371,11 @@ TEST_P(PaintPropertyTreeBuilderTest, OverflowScrollVerticalRL) { EXPECT_EQ(DocContentClip(), overflow_clip->Parent()); EXPECT_EQ(properties->PaintOffsetTranslation(), &overflow_clip->LocalTransformSpace()); - EXPECT_EQ(FloatRoundedRect(10, 10, 85, 85), overflow_clip->ClipRect()); + EXPECT_EQ(FloatRoundedRect(10, 10, 85, 85), + overflow_clip->UnsnappedClipRect()); - scroller->GetScrollableArea()->ScrollBy(ScrollOffset(-100, 0), kUserScroll); + scroller->GetScrollableArea()->ScrollBy(ScrollOffset(-100, 0), + mojom::blink::ScrollType::kUser); UpdateAllLifecyclePhasesForTest(); // Only scroll_translation is affected by scrolling. @@ -386,7 +391,8 @@ TEST_P(PaintPropertyTreeBuilderTest, OverflowScrollVerticalRL) { EXPECT_EQ(DocContentClip(), overflow_clip->Parent()); EXPECT_EQ(properties->PaintOffsetTranslation(), &overflow_clip->LocalTransformSpace()); - EXPECT_EQ(FloatRoundedRect(10, 10, 85, 85), overflow_clip->ClipRect()); + EXPECT_EQ(FloatRoundedRect(10, 10, 85, 85), + overflow_clip->UnsnappedClipRect()); } TEST_P(PaintPropertyTreeBuilderTest, OverflowScrollRTL) { @@ -420,9 +426,11 @@ TEST_P(PaintPropertyTreeBuilderTest, OverflowScrollRTL) { EXPECT_EQ(DocContentClip(), overflow_clip->Parent()); EXPECT_EQ(properties->PaintOffsetTranslation(), &overflow_clip->LocalTransformSpace()); - EXPECT_EQ(FloatRoundedRect(25, 10, 85, 85), overflow_clip->ClipRect()); + EXPECT_EQ(FloatRoundedRect(25, 10, 85, 85), + overflow_clip->UnsnappedClipRect()); - scroller->GetScrollableArea()->ScrollBy(ScrollOffset(-100, 0), kUserScroll); + scroller->GetScrollableArea()->ScrollBy(ScrollOffset(-100, 0), + mojom::blink::ScrollType::kUser); UpdateAllLifecyclePhasesForTest(); // Only scroll_translation is affected by scrolling. @@ -438,7 +446,8 @@ TEST_P(PaintPropertyTreeBuilderTest, OverflowScrollRTL) { EXPECT_EQ(DocContentClip(), overflow_clip->Parent()); EXPECT_EQ(properties->PaintOffsetTranslation(), &overflow_clip->LocalTransformSpace()); - EXPECT_EQ(FloatRoundedRect(25, 10, 85, 85), overflow_clip->ClipRect()); + EXPECT_EQ(FloatRoundedRect(25, 10, 85, 85), + overflow_clip->UnsnappedClipRect()); } TEST_P(PaintPropertyTreeBuilderTest, OverflowScrollVerticalRLMulticol) { @@ -462,7 +471,7 @@ TEST_P(PaintPropertyTreeBuilderTest, OverflowScrollVerticalRLMulticol) { EXPECT_EQ(410, FragmentAt(flow_thread, 0) .PaintProperties() ->FragmentClip() - ->ClipRect() + ->UnsnappedClipRect() .Rect() .X()); EXPECT_EQ(PhysicalOffset(360, 10), @@ -470,7 +479,7 @@ TEST_P(PaintPropertyTreeBuilderTest, OverflowScrollVerticalRLMulticol) { EXPECT_EQ(460, FragmentAt(flow_thread, 1) .PaintProperties() ->FragmentClip() - ->ClipRect() + ->UnsnappedClipRect() .Rect() .MaxX()); EXPECT_EQ(PhysicalOffset(410, 210), @@ -481,7 +490,7 @@ TEST_P(PaintPropertyTreeBuilderTest, OverflowScrollVerticalRLMulticol) { // Fragment geometries are not affected by parent scrolling. ToLayoutBox(GetLayoutObjectByElementId("scroller")) ->GetScrollableArea() - ->ScrollBy(ScrollOffset(-100, 200), kUserScroll); + ->ScrollBy(ScrollOffset(-100, 200), mojom::blink::ScrollType::kUser); UpdateAllLifecyclePhasesForTest(); check_fragments(); } @@ -492,8 +501,7 @@ TEST_P(PaintPropertyTreeBuilderTest, DocScrollingTraditional) { GetDocument().domWindow()->scrollTo(0, 100); LocalFrameView* frame_view = GetDocument().View(); - frame_view->UpdateAllLifecyclePhases( - DocumentLifecycle::LifecycleUpdateReason::kTest); + frame_view->UpdateAllLifecyclePhases(DocumentUpdateReason::kTest); EXPECT_TRUE(DocPreTranslation()->IsIdentity()); EXPECT_EQ( GetDocument().GetPage()->GetVisualViewport().GetScrollTranslationNode(), @@ -501,7 +509,8 @@ TEST_P(PaintPropertyTreeBuilderTest, DocScrollingTraditional) { EXPECT_EQ(FloatSize(0, -100), DocScrollTranslation()->Translation2D()); EXPECT_EQ(DocPreTranslation(), DocScrollTranslation()->Parent()); EXPECT_EQ(DocPreTranslation(), &DocContentClip()->LocalTransformSpace()); - EXPECT_EQ(FloatRoundedRect(0, 0, 800, 600), DocContentClip()->ClipRect()); + EXPECT_EQ(FloatRoundedRect(0, 0, 800, 600), + DocContentClip()->UnsnappedClipRect()); EXPECT_TRUE(DocContentClip()->Parent()->IsRoot()); CHECK_EXACT_VISUAL_RECT(PhysicalRect(8, 8, 784, 10000), @@ -1444,7 +1453,7 @@ TEST_P(PaintPropertyTreeBuilderTest, SVGViewportContainer) { ASSERT_NE(nullptr, clip); EXPECT_EQ(nullptr, transform); EXPECT_EQ(parent_clip, clip->Parent()); - EXPECT_EQ(FloatRect(0, 0, 30, 30), clip->ClipRect().Rect()); + EXPECT_EQ(FloatRect(0, 0, 30, 30), clip->UnsnappedClipRect().Rect()); EXPECT_EQ(parent_transform, &clip->LocalTransformSpace()); // overflow: hidden and non-zero offset and viewport scale: @@ -1456,7 +1465,7 @@ TEST_P(PaintPropertyTreeBuilderTest, SVGViewportContainer) { ASSERT_NE(nullptr, clip); ASSERT_NE(nullptr, transform); EXPECT_EQ(parent_clip, clip->Parent()); - EXPECT_EQ(FloatRect(0, 0, 60, 60), clip->ClipRect().Rect()); + EXPECT_EQ(FloatRect(0, 0, 60, 60), clip->UnsnappedClipRect().Rect()); EXPECT_EQ(transform, &clip->LocalTransformSpace()); EXPECT_EQ(TransformationMatrix().Translate(40, 50).Scale(0.5), transform->Matrix()); @@ -1499,7 +1508,7 @@ TEST_P(PaintPropertyTreeBuilderTest, SVGForeignObjectOverflowClip) { const auto* clip = properties1->OverflowClip(); ASSERT_NE(nullptr, clip); EXPECT_EQ(parent_clip, clip->Parent()); - EXPECT_EQ(FloatRect(10, 20, 30, 40), clip->ClipRect().Rect()); + EXPECT_EQ(FloatRect(10, 20, 30, 40), clip->UnsnappedClipRect().Rect()); EXPECT_EQ(parent_transform, &clip->LocalTransformSpace()); const auto* properties2 = PaintPropertiesForElement("object2"); @@ -1523,7 +1532,7 @@ TEST_P(PaintPropertyTreeBuilderTest, OverflowClipWithEmptyVisualOverflow) { const auto* clip = PaintPropertiesForElement("container")->OverflowClip(); EXPECT_NE(nullptr, clip); - EXPECT_EQ(FloatRect(0, 0, 90, 90), clip->ClipRect().Rect()); + EXPECT_EQ(FloatRect(0, 0, 90, 90), clip->UnsnappedClipRect().Rect()); } TEST_P(PaintPropertyTreeBuilderTest, @@ -1615,7 +1624,7 @@ TEST_P(PaintPropertyTreeBuilderTest, ControlClip) { &button_properties->OverflowClip()->LocalTransformSpace()); EXPECT_EQ(FloatRoundedRect(5, 5, 335, 113), - button_properties->OverflowClip()->ClipRect()); + button_properties->OverflowClip()->UnsnappedClipRect()); EXPECT_EQ(DocContentClip(), button_properties->OverflowClip()->Parent()); CHECK_EXACT_VISUAL_RECT(PhysicalRect(0, 0, 345, 123), &button, GetDocument().View()->GetLayoutView()); @@ -1643,7 +1652,7 @@ TEST_P(PaintPropertyTreeBuilderTest, ControlClipInsideForeignObject) { // not scroll (not enough content). EXPECT_TRUE(DocScrollTranslation()); EXPECT_EQ(FloatRoundedRect(2, 2, 341, 119), - button_properties->OverflowClip()->ClipRect()); + button_properties->OverflowClip()->UnsnappedClipRect()); CHECK_EXACT_VISUAL_RECT(PhysicalRect(8, 8, 345, 123), &button, GetDocument().View()->GetLayoutView()); } @@ -1682,7 +1691,7 @@ TEST_P(PaintPropertyTreeBuilderTest, BorderRadiusClip) { // padding box = border box(500+60+50, 400+45+55) - border outset(60+50, // 45+55) - scrollbars(15, 15) EXPECT_EQ(FloatRoundedRect(60, 45, 500, 400), - div_properties->OverflowClip()->ClipRect()); + div_properties->OverflowClip()->UnsnappedClipRect()); const ClipPaintPropertyNode* border_radius_clip = div_properties->OverflowClip()->Parent(); EXPECT_EQ(DocScrollTranslation(), &border_radius_clip->LocalTransformSpace()); @@ -1703,12 +1712,44 @@ TEST_P(PaintPropertyTreeBuilderTest, BorderRadiusClip) { FloatSize(), // (top right) = max((34, 34) - (50, 45), (0, 0)) FloatSize(18, 23), // (bot left) = max((78, 78) - (60, 55), (0, 0)) FloatSize(6, 1)), // (bot right) = max((56, 56) - (50, 55), (0, 0)) - border_radius_clip->ClipRect()); + border_radius_clip->UnsnappedClipRect()); EXPECT_EQ(DocContentClip(), border_radius_clip->Parent()); CHECK_EXACT_VISUAL_RECT(PhysicalRect(0, 0, 610, 500), &div, GetDocument().View()->GetLayoutView()); } +TEST_P(PaintPropertyTreeBuilderTest, SubpixelBorderRadiusClip) { + SetBodyInnerHTML(R"HTML( + <style> + body { + margin: 0px; + } + #div { + margin-top: 0.5px; + width: 100px; + height: 100px; + overflow: hidden; + border-radius: 50%; + } + </style> + <div id='div'></div> + )HTML"); + + LayoutObject& div = *GetLayoutObjectByElementId("div"); + const ObjectPaintProperties* div_properties = + div.FirstFragment().PaintProperties(); + + const ClipPaintPropertyNode* border_radius_clip = + div_properties->InnerBorderRadiusClip(); + FloatSize corner(50, 50); + EXPECT_EQ(FloatRoundedRect(FloatRect(0, 0.5, 100, 100), corner, corner, + corner, corner), + border_radius_clip->UnsnappedClipRect()); + EXPECT_EQ(FloatRoundedRect(FloatRect(0, 1, 100, 100), corner, corner, corner, + corner), + border_radius_clip->PixelSnappedClipRect()); +} + TEST_P(PaintPropertyTreeBuilderTest, TransformNodesAcrossSubframes) { SetBodyInnerHTML(R"HTML( <style> @@ -1734,8 +1775,7 @@ TEST_P(PaintPropertyTreeBuilderTest, TransformNodesAcrossSubframes) { )HTML"); LocalFrameView* frame_view = GetDocument().View(); - frame_view->UpdateAllLifecyclePhases( - DocumentLifecycle::LifecycleUpdateReason::kTest); + frame_view->UpdateAllLifecyclePhases(DocumentUpdateReason::kTest); LayoutObject* div_with_transform = GetLayoutObjectByElementId("divWithTransform"); @@ -1825,8 +1865,7 @@ TEST_P(PaintPropertyTreeBuilderTest, FramesEstablishIsolation) { )HTML"); LocalFrameView* frame_view = GetDocument().View(); - frame_view->UpdateAllLifecyclePhases( - DocumentLifecycle::LifecycleUpdateReason::kTest); + frame_view->UpdateAllLifecyclePhases(DocumentUpdateReason::kTest); LayoutObject* frame = ChildFrame().View()->GetLayoutView(); const auto& frame_contents_properties = @@ -1884,8 +1923,7 @@ TEST_P(PaintPropertyTreeBuilderTest, FramesEstablishIsolation) { // However, isolation stops this recursion. GetDocument().getElementById("parent")->setAttribute(html_names::kClassAttr, "transformed"); - frame_view->UpdateAllLifecyclePhases( - DocumentLifecycle::LifecycleUpdateReason::kTest); + frame_view->UpdateAllLifecyclePhases(DocumentUpdateReason::kTest); // Verify that our clobbered state is still clobbered. EXPECT_EQ(FloatSize(123, 321), @@ -1922,8 +1960,7 @@ TEST_P(PaintPropertyTreeBuilderTest, TransformNodesInTransformedSubframes) { <div id='transform'></div> )HTML"); LocalFrameView* frame_view = GetDocument().View(); - frame_view->UpdateAllLifecyclePhases( - DocumentLifecycle::LifecycleUpdateReason::kTest); + frame_view->UpdateAllLifecyclePhases(DocumentUpdateReason::kTest); // Assert that we have the following tree structure: // ... @@ -2126,7 +2163,7 @@ TEST_P(PaintPropertyTreeBuilderTest, CSSClipFixedPositionDescendant) { EXPECT_EQ(DocScrollTranslation(), &clip_properties->CssClip()->LocalTransformSpace()); EXPECT_EQ(FloatRoundedRect(FloatRect(absolute_clip_rect)), - clip_properties->CssClip()->ClipRect()); + clip_properties->CssClip()->UnsnappedClipRect()); CHECK_VISUAL_RECT(absolute_clip_rect, &clip, GetDocument().View()->GetLayoutView(), // TODO(crbug.com/599939): mapToVisualRectInAncestorSpace() @@ -2186,7 +2223,7 @@ TEST_P(PaintPropertyTreeBuilderTest, CSSClipAbsPositionDescendant) { EXPECT_EQ(DocScrollTranslation(), &clip_properties->CssClip()->LocalTransformSpace()); EXPECT_EQ(FloatRoundedRect(FloatRect(absolute_clip_rect)), - clip_properties->CssClip()->ClipRect()); + clip_properties->CssClip()->UnsnappedClipRect()); CHECK_VISUAL_RECT(absolute_clip_rect, clip, GetDocument().View()->GetLayoutView(), // TODO(crbug.com/599939): mapToVisualRectInAncestorSpace() @@ -2228,7 +2265,8 @@ TEST_P(PaintPropertyTreeBuilderTest, CSSClipSubpixel) { PhysicalRect local_clip_rect(40, 10, 40, 60); PhysicalRect absolute_clip_rect = local_clip_rect; // Moved by 124 pixels due to pixel-snapping. - absolute_clip_rect.offset += PhysicalOffset(124, 456); + absolute_clip_rect.offset += + PhysicalOffset(LayoutSize(FloatSize(123.5, 456))); auto* clip = GetLayoutObjectByElementId("clip"); const ObjectPaintProperties* clip_properties = @@ -2240,7 +2278,9 @@ TEST_P(PaintPropertyTreeBuilderTest, CSSClipSubpixel) { EXPECT_EQ(DocScrollTranslation(), &clip_properties->CssClip()->LocalTransformSpace()); EXPECT_EQ(FloatRoundedRect(FloatRect(absolute_clip_rect)), - clip_properties->CssClip()->ClipRect()); + clip_properties->CssClip()->UnsnappedClipRect()); + EXPECT_EQ(FloatRoundedRect(PixelSnappedIntRect((absolute_clip_rect))), + clip_properties->CssClip()->PixelSnappedClipRect()); } TEST_P(PaintPropertyTreeBuilderTest, CSSClipFixedPositionDescendantNonShared) { @@ -2298,13 +2338,13 @@ TEST_P(PaintPropertyTreeBuilderTest, CSSClipFixedPositionDescendantNonShared) { EXPECT_EQ(overflow_properties->ScrollTranslation(), &clip_properties->CssClip()->LocalTransformSpace()); EXPECT_EQ(FloatRoundedRect(FloatRect(absolute_clip_rect)), - clip_properties->CssClip()->ClipRect()); + clip_properties->CssClip()->UnsnappedClipRect()); EXPECT_EQ(DocContentClip(), clip_properties->CssClipFixedPosition()->Parent()); EXPECT_EQ(overflow_properties->ScrollTranslation(), &clip_properties->CssClipFixedPosition()->LocalTransformSpace()); EXPECT_EQ(FloatRoundedRect(FloatRect(absolute_clip_rect)), - clip_properties->CssClipFixedPosition()->ClipRect()); + clip_properties->CssClipFixedPosition()->UnsnappedClipRect()); CHECK_EXACT_VISUAL_RECT(PhysicalRect(), clip, GetDocument().View()->GetLayoutView()); @@ -3411,9 +3451,10 @@ TEST_P(PaintPropertyTreeBuilderTest, ContainPaintOrStyleLayoutTreeState) { &clipper->FirstFragment().LocalBorderBoxProperties().Clip()); // Clip isolation node should be big enough to encompass all other clips, // including DocContentClip. - EXPECT_TRUE( - clip_properties->ClipIsolationNode()->ClipRect().Rect().Contains( - DocContentClip()->ClipRect().Rect())); + EXPECT_TRUE(clip_properties->ClipIsolationNode() + ->UnsnappedClipRect() + .Rect() + .Contains(DocContentClip()->UnsnappedClipRect().Rect())); // Verify contents properties and child properties: @@ -3519,12 +3560,12 @@ TEST_P(PaintPropertyTreeBuilderTest, OverflowScrollWithRoundedRect) { EXPECT_EQ( FloatRoundedRect(FloatRect(50, 50, 200, 200), FloatSize(50, 50), FloatSize(50, 50), FloatSize(50, 50), FloatSize(50, 50)), - rounded_box_properties->InnerBorderRadiusClip()->ClipRect()); + rounded_box_properties->InnerBorderRadiusClip()->UnsnappedClipRect()); // Unlike the inner border radius clip, the overflow clip is inset by the // scrollbars (13px). EXPECT_EQ(FloatRoundedRect(50, 50, 187, 187), - rounded_box_properties->OverflowClip()->ClipRect()); + rounded_box_properties->OverflowClip()->UnsnappedClipRect()); EXPECT_EQ(DocContentClip(), rounded_box_properties->InnerBorderRadiusClip()->Parent()); EXPECT_EQ(rounded_box_properties->InnerBorderRadiusClip(), @@ -3920,7 +3961,7 @@ TEST_P(PaintPropertyTreeBuilderTest, SVGRootClip) { .PaintProperties() ->PaintOffsetTranslation() ->Translation2D()); - EXPECT_EQ(FloatRoundedRect(0, 0, 100, 100), clip->ClipRect()); + EXPECT_EQ(FloatRoundedRect(0, 0, 100, 100), clip->UnsnappedClipRect()); } TEST_P(PaintPropertyTreeBuilderTest, SVGRootNoClip) { @@ -3982,8 +4023,8 @@ TEST_P(PaintPropertyTreeBuilderTest, PaintOffsetsUnderMultiColumnScrolled) { )HTML"); LayoutObject* scroller = GetLayoutObjectByElementId("scroller"); - ToLayoutBox(scroller)->GetScrollableArea()->ScrollBy(ScrollOffset(0, 300), - kUserScroll); + ToLayoutBox(scroller)->GetScrollableArea()->ScrollBy( + ScrollOffset(0, 300), mojom::blink::ScrollType::kUser); UpdateAllLifecyclePhasesForTest(); EXPECT_EQ(FloatSize(8, 8), scroller->FirstFragment() @@ -4072,8 +4113,8 @@ TEST_P(PaintPropertyTreeBuilderTest, EXPECT_EQ(PhysicalOffset(51, -20), multicol_container->FirstFragment().NextFragment()->PaintOffset()); - GetDocument().View()->LayoutViewport()->ScrollBy(ScrollOffset(0, 25), - kUserScroll); + GetDocument().View()->LayoutViewport()->ScrollBy( + ScrollOffset(0, 25), mojom::blink::ScrollType::kUser); UpdateAllLifecyclePhasesForTest(); ASSERT_TRUE(multicol_container->FirstFragment().NextFragment()); @@ -4113,62 +4154,66 @@ TEST_P(PaintPropertyTreeBuilderTest, FragmentsUnderMultiColumn) { EXPECT_EQ(4u, NumFragments(flowthread)); EXPECT_EQ(PhysicalOffset(), FragmentAt(relpos, 0).PaintOffset()); - EXPECT_EQ(PhysicalOffset(), FragmentAt(relpos, 0).PaginationOffset()); + EXPECT_EQ(PhysicalOffset(), FragmentAt(relpos, 0).LegacyPaginationOffset()); EXPECT_EQ(LayoutUnit(), FragmentAt(relpos, 0).LogicalTopInFlowThread()); EXPECT_EQ(nullptr, FragmentAt(relpos, 0).PaintProperties()); EXPECT_EQ(PhysicalOffset(), FragmentAt(flowthread, 0).PaintOffset()); - EXPECT_EQ(PhysicalOffset(), FragmentAt(flowthread, 0).PaginationOffset()); + EXPECT_EQ(PhysicalOffset(), + FragmentAt(flowthread, 0).LegacyPaginationOffset()); EXPECT_EQ(LayoutUnit(), FragmentAt(flowthread, 0).LogicalTopInFlowThread()); const auto* fragment_clip = FragmentAt(flowthread, 0).PaintProperties()->FragmentClip(); ASSERT_NE(nullptr, fragment_clip); EXPECT_EQ(FloatRect(-1000000, -1000000, 2000000, 1000030), - fragment_clip->ClipRect().Rect()); + fragment_clip->UnsnappedClipRect().Rect()); EXPECT_EQ(fragment_clip, &FragmentAt(relpos, 0).LocalBorderBoxProperties().Clip()); EXPECT_EQ(PhysicalOffset(100, -30), FragmentAt(relpos, 1).PaintOffset()); - EXPECT_EQ(PhysicalOffset(100, -30), FragmentAt(relpos, 1).PaginationOffset()); + EXPECT_EQ(PhysicalOffset(100, -30), + FragmentAt(relpos, 1).LegacyPaginationOffset()); EXPECT_EQ(LayoutUnit(30), FragmentAt(relpos, 1).LogicalTopInFlowThread()); EXPECT_EQ(nullptr, FragmentAt(relpos, 1).PaintProperties()); EXPECT_EQ(PhysicalOffset(100, -30), FragmentAt(flowthread, 1).PaintOffset()); EXPECT_EQ(PhysicalOffset(100, -30), - FragmentAt(flowthread, 1).PaginationOffset()); + FragmentAt(flowthread, 1).LegacyPaginationOffset()); EXPECT_EQ(LayoutUnit(30), FragmentAt(flowthread, 1).LogicalTopInFlowThread()); fragment_clip = FragmentAt(flowthread, 1).PaintProperties()->FragmentClip(); ASSERT_NE(nullptr, fragment_clip); EXPECT_EQ(FloatRect(-999900, 0, 2000000, 30), - fragment_clip->ClipRect().Rect()); + fragment_clip->UnsnappedClipRect().Rect()); EXPECT_EQ(fragment_clip, &FragmentAt(relpos, 1).LocalBorderBoxProperties().Clip()); EXPECT_EQ(PhysicalOffset(0, 20), FragmentAt(relpos, 2).PaintOffset()); - EXPECT_EQ(PhysicalOffset(0, 20), FragmentAt(relpos, 2).PaginationOffset()); + EXPECT_EQ(PhysicalOffset(0, 20), + FragmentAt(relpos, 2).LegacyPaginationOffset()); EXPECT_EQ(LayoutUnit(60), FragmentAt(relpos, 2).LogicalTopInFlowThread()); EXPECT_EQ(nullptr, FragmentAt(relpos, 2).PaintProperties()); EXPECT_EQ(PhysicalOffset(0, 20), FragmentAt(flowthread, 2).PaintOffset()); EXPECT_EQ(PhysicalOffset(0, 20), - FragmentAt(flowthread, 2).PaginationOffset()); + FragmentAt(flowthread, 2).LegacyPaginationOffset()); EXPECT_EQ(LayoutUnit(60), FragmentAt(flowthread, 2).LogicalTopInFlowThread()); fragment_clip = FragmentAt(flowthread, 2).PaintProperties()->FragmentClip(); ASSERT_NE(nullptr, fragment_clip); EXPECT_EQ(FloatRect(-1000000, 80, 2000000, 30), - fragment_clip->ClipRect().Rect()); + fragment_clip->UnsnappedClipRect().Rect()); EXPECT_EQ(fragment_clip, &FragmentAt(relpos, 2).LocalBorderBoxProperties().Clip()); EXPECT_EQ(PhysicalOffset(100, -10), FragmentAt(relpos, 3).PaintOffset()); - EXPECT_EQ(PhysicalOffset(100, -10), FragmentAt(relpos, 3).PaginationOffset()); + EXPECT_EQ(PhysicalOffset(100, -10), + FragmentAt(relpos, 3).LegacyPaginationOffset()); EXPECT_EQ(LayoutUnit(90), FragmentAt(relpos, 3).LogicalTopInFlowThread()); EXPECT_EQ(nullptr, FragmentAt(relpos, 3).PaintProperties()); EXPECT_EQ(PhysicalOffset(100, -10), FragmentAt(flowthread, 3).PaintOffset()); EXPECT_EQ(PhysicalOffset(100, -10), - FragmentAt(flowthread, 3).PaginationOffset()); + FragmentAt(flowthread, 3).LegacyPaginationOffset()); EXPECT_EQ(LayoutUnit(90), FragmentAt(flowthread, 3).LogicalTopInFlowThread()); fragment_clip = FragmentAt(flowthread, 3).PaintProperties()->FragmentClip(); ASSERT_NE(nullptr, fragment_clip); EXPECT_EQ(FloatRect(-999900, 80, 2000000, 999910), - fragment_clip->ClipRect().Rect()); + fragment_clip->UnsnappedClipRect().Rect()); EXPECT_EQ(fragment_clip, &FragmentAt(relpos, 3).LocalBorderBoxProperties().Clip()); @@ -4242,20 +4287,21 @@ TEST_P(PaintPropertyTreeBuilderTest, EXPECT_TRUE(thread->IsLayoutFlowThread()); EXPECT_EQ(2u, NumFragments(thread)); EXPECT_EQ(PhysicalOffset(100, 0), FragmentAt(thread, 0).PaintOffset()); - EXPECT_EQ(PhysicalOffset(), FragmentAt(thread, 0).PaginationOffset()); + EXPECT_EQ(PhysicalOffset(), FragmentAt(thread, 0).LegacyPaginationOffset()); EXPECT_EQ(LayoutUnit(), FragmentAt(thread, 0).LogicalTopInFlowThread()); EXPECT_EQ(PhysicalOffset(300, 100), FragmentAt(thread, 1).PaintOffset()); - EXPECT_EQ(PhysicalOffset(200, 100), FragmentAt(thread, 1).PaginationOffset()); + EXPECT_EQ(PhysicalOffset(200, 100), + FragmentAt(thread, 1).LegacyPaginationOffset()); EXPECT_EQ(LayoutUnit(200), FragmentAt(thread, 1).LogicalTopInFlowThread()); LayoutObject* content = GetLayoutObjectByElementId("content"); EXPECT_EQ(2u, NumFragments(content)); EXPECT_EQ(PhysicalOffset(-200, 0), FragmentAt(content, 0).PaintOffset()); - EXPECT_EQ(PhysicalOffset(), FragmentAt(content, 0).PaginationOffset()); + EXPECT_EQ(PhysicalOffset(), FragmentAt(content, 0).LegacyPaginationOffset()); EXPECT_EQ(LayoutUnit(), FragmentAt(content, 0).LogicalTopInFlowThread()); EXPECT_EQ(PhysicalOffset(0, 100), FragmentAt(content, 1).PaintOffset()); EXPECT_EQ(PhysicalOffset(200, 100), - FragmentAt(content, 1).PaginationOffset()); + FragmentAt(content, 1).LegacyPaginationOffset()); EXPECT_EQ(LayoutUnit(200), FragmentAt(content, 1).LogicalTopInFlowThread()); } @@ -4277,6 +4323,10 @@ TEST_P(PaintPropertyTreeBuilderTest, LayerUnderOverflowClipUnderMultiColumn) { } TEST_P(PaintPropertyTreeBuilderTest, CompositedUnderMultiColumn) { + // TODO(crbug.com/1064341): This test crashes in CompositeAfterPaint. Fix it. + if (RuntimeEnabledFeatures::CompositeAfterPaintEnabled()) + return; + SetBodyInnerHTML(R"HTML( <style>body { margin: 0; }</style> <div id='multicol' style='columns:3; column-fill:auto; column-gap: 0; @@ -4295,15 +4345,15 @@ TEST_P(PaintPropertyTreeBuilderTest, CompositedUnderMultiColumn) { EXPECT_TRUE(thread->IsLayoutFlowThread()); EXPECT_EQ(3u, NumFragments(thread)); EXPECT_EQ(PhysicalOffset(), FragmentAt(thread, 0).PaintOffset()); - EXPECT_EQ(PhysicalOffset(), FragmentAt(thread, 0).PaginationOffset()); + EXPECT_EQ(PhysicalOffset(), FragmentAt(thread, 0).LegacyPaginationOffset()); EXPECT_EQ(LayoutUnit(), FragmentAt(thread, 0).LogicalTopInFlowThread()); EXPECT_EQ(PhysicalOffset(100, -200), FragmentAt(thread, 1).PaintOffset()); EXPECT_EQ(PhysicalOffset(100, -200), - FragmentAt(thread, 1).PaginationOffset()); + FragmentAt(thread, 1).LegacyPaginationOffset()); EXPECT_EQ(LayoutUnit(200), FragmentAt(thread, 1).LogicalTopInFlowThread()); EXPECT_EQ(PhysicalOffset(200, -400), FragmentAt(thread, 2).PaintOffset()); EXPECT_EQ(PhysicalOffset(200, -400), - FragmentAt(thread, 2).PaginationOffset()); + FragmentAt(thread, 2).LegacyPaginationOffset()); EXPECT_EQ(LayoutUnit(400), FragmentAt(thread, 2).LogicalTopInFlowThread()); LayoutObject* composited = GetLayoutObjectByElementId("composited"); @@ -4317,33 +4367,33 @@ TEST_P(PaintPropertyTreeBuilderTest, CompositedUnderMultiColumn) { EXPECT_EQ(PhysicalOffset(100, 100), FragmentAt(composited, 0).PaintOffset()); EXPECT_EQ(PhysicalOffset(100, -200), - FragmentAt(composited, 0).PaginationOffset()); + FragmentAt(composited, 0).LegacyPaginationOffset()); EXPECT_EQ(LayoutUnit(200), FragmentAt(composited, 0).LogicalTopInFlowThread()); EXPECT_EQ(PhysicalOffset(200, -100), FragmentAt(composited, 1).PaintOffset()); EXPECT_EQ(PhysicalOffset(200, -400), - FragmentAt(composited, 1).PaginationOffset()); + FragmentAt(composited, 1).LegacyPaginationOffset()); EXPECT_EQ(LayoutUnit(400), FragmentAt(composited, 1).LogicalTopInFlowThread()); EXPECT_EQ(2u, NumFragments(non_composited_child)); EXPECT_EQ(PhysicalOffset(100, 100), FragmentAt(non_composited_child, 0).PaintOffset()); EXPECT_EQ(PhysicalOffset(100, -200), - FragmentAt(non_composited_child, 0).PaginationOffset()); + FragmentAt(non_composited_child, 0).LegacyPaginationOffset()); EXPECT_EQ(LayoutUnit(200), FragmentAt(non_composited_child, 0).LogicalTopInFlowThread()); EXPECT_EQ(PhysicalOffset(200, -100), FragmentAt(non_composited_child, 1).PaintOffset()); EXPECT_EQ(PhysicalOffset(200, -400), - FragmentAt(non_composited_child, 1).PaginationOffset()); + FragmentAt(non_composited_child, 1).LegacyPaginationOffset()); EXPECT_EQ(LayoutUnit(400), FragmentAt(non_composited_child, 1).LogicalTopInFlowThread()); EXPECT_EQ(1u, NumFragments(composited_child)); EXPECT_EQ(PhysicalOffset(200, 50), FragmentAt(composited_child, 0).PaintOffset()); EXPECT_EQ(PhysicalOffset(200, -400), - FragmentAt(composited_child, 0).PaginationOffset()); + FragmentAt(composited_child, 0).LegacyPaginationOffset()); EXPECT_EQ(LayoutUnit(400), FragmentAt(composited_child, 0).LogicalTopInFlowThread()); } else { @@ -4352,21 +4402,21 @@ TEST_P(PaintPropertyTreeBuilderTest, CompositedUnderMultiColumn) { EXPECT_EQ(PhysicalOffset(100, 100), FragmentAt(composited, 0).PaintOffset()); EXPECT_EQ(PhysicalOffset(100, -200), - FragmentAt(composited, 0).PaginationOffset()); + FragmentAt(composited, 0).LegacyPaginationOffset()); EXPECT_EQ(LayoutUnit(200), FragmentAt(composited, 0).LogicalTopInFlowThread()); EXPECT_EQ(1u, NumFragments(non_composited_child)); EXPECT_EQ(PhysicalOffset(100, 100), FragmentAt(non_composited_child, 0).PaintOffset()); EXPECT_EQ(PhysicalOffset(100, -200), - FragmentAt(non_composited_child, 0).PaginationOffset()); + FragmentAt(non_composited_child, 0).LegacyPaginationOffset()); EXPECT_EQ(LayoutUnit(200), FragmentAt(non_composited_child, 0).LogicalTopInFlowThread()); EXPECT_EQ(1u, NumFragments(composited_child)); EXPECT_EQ(PhysicalOffset(100, 250), FragmentAt(composited_child, 0).PaintOffset()); EXPECT_EQ(PhysicalOffset(100, -200), - FragmentAt(composited_child, 0).PaginationOffset()); + FragmentAt(composited_child, 0).LegacyPaginationOffset()); EXPECT_EQ(LayoutUnit(200), FragmentAt(composited_child, 0).LogicalTopInFlowThread()); } @@ -4425,6 +4475,10 @@ TEST_P(PaintPropertyTreeBuilderTest, FrameUnderMulticol) { } TEST_P(PaintPropertyTreeBuilderTest, CompositedMulticolFrameUnderMulticol) { + // TODO(crbug.com/1064341): This test crashes in CompositeAfterPaint. Fix it. + if (RuntimeEnabledFeatures::CompositeAfterPaintEnabled()) + return; + SetBodyInnerHTML(R"HTML( <style>body { margin: 0 }</style> <div style='columns: 3; column-gap: 0; column-fill: auto; @@ -4448,8 +4502,9 @@ TEST_P(PaintPropertyTreeBuilderTest, CompositedMulticolFrameUnderMulticol) { // TODO(crbug.com/797779): Add code to verify fragments under the iframe. } -TEST_P(PaintPropertyTreeBuilderTest, - BecomingUnfragmentedClearsPaginationOffsetAndLogicalTopInFlowThread) { +TEST_P( + PaintPropertyTreeBuilderTest, + BecomingUnfragmentedClearsLegacyPaginationOffsetAndLogicalTopInFlowThread) { SetBodyInnerHTML(R"HTML( <style> #target { @@ -4465,13 +4520,13 @@ TEST_P(PaintPropertyTreeBuilderTest, LayoutObject* target = GetLayoutObjectByElementId("target"); EXPECT_EQ(PhysicalOffset(LayoutUnit(392.5f), LayoutUnit(-20)), - target->FirstFragment().PaginationOffset()); + target->FirstFragment().LegacyPaginationOffset()); EXPECT_EQ(LayoutUnit(20), target->FirstFragment().LogicalTopInFlowThread()); Element* target_element = GetDocument().getElementById("target"); target_element->setAttribute(html_names::kStyleAttr, "position: absolute"); UpdateAllLifecyclePhasesForTest(); - EXPECT_EQ(PhysicalOffset(), target->FirstFragment().PaginationOffset()); + EXPECT_EQ(PhysicalOffset(), target->FirstFragment().LegacyPaginationOffset()); EXPECT_EQ(LayoutUnit(), target->FirstFragment().LogicalTopInFlowThread()); } @@ -4740,8 +4795,10 @@ TEST_P(PaintPropertyTreeBuilderTest, OverflowClipSubpixelPosition) { EXPECT_EQ(PhysicalOffset(LayoutUnit(31.5), LayoutUnit(20)), clipper->FirstFragment().PaintOffset()); // Result is pixel-snapped. + EXPECT_EQ(FloatRect(31.5, 20, 400, 300), + clip_properties->OverflowClip()->UnsnappedClipRect().Rect()); EXPECT_EQ(FloatRect(32, 20, 400, 300), - clip_properties->OverflowClip()->ClipRect().Rect()); + clip_properties->OverflowClip()->PixelSnappedClipRect().Rect()); } TEST_P(PaintPropertyTreeBuilderTest, MaskSimple) { @@ -4759,7 +4816,7 @@ TEST_P(PaintPropertyTreeBuilderTest, MaskSimple) { EXPECT_EQ(output_clip, &target->FirstFragment().LocalBorderBoxProperties().Clip()); EXPECT_EQ(DocContentClip(), output_clip->Parent()); - EXPECT_EQ(FloatRoundedRect(8, 8, 300, 200), output_clip->ClipRect()); + EXPECT_EQ(FloatRoundedRect(8, 8, 300, 200), output_clip->UnsnappedClipRect()); EXPECT_EQ(properties->Effect(), &target->FirstFragment().LocalBorderBoxProperties().Effect()); @@ -4788,7 +4845,8 @@ TEST_P(PaintPropertyTreeBuilderTest, MaskWithOutset) { EXPECT_EQ(output_clip, &target->FirstFragment().LocalBorderBoxProperties().Clip()); EXPECT_EQ(DocContentClip(), output_clip->Parent()); - EXPECT_EQ(FloatRoundedRect(-12, -2, 340, 220), output_clip->ClipRect()); + EXPECT_EQ(FloatRoundedRect(-12, -2, 340, 220), + output_clip->UnsnappedClipRect()); EXPECT_EQ(properties->Effect(), &target->FirstFragment().LocalBorderBoxProperties().Effect()); @@ -4831,18 +4889,20 @@ TEST_P(PaintPropertyTreeBuilderTest, MaskEscapeClip) { PaintPropertiesForElement("scroll"); EXPECT_EQ(DocContentClip(), overflow_clip1->Parent()); - EXPECT_EQ(FloatRoundedRect(0, 0, 300, 200), overflow_clip1->ClipRect()); + EXPECT_EQ(FloatRoundedRect(0, 0, 300, 200), + overflow_clip1->UnsnappedClipRect()); EXPECT_EQ(scroll_properties->PaintOffsetTranslation(), &overflow_clip1->LocalTransformSpace()); EXPECT_EQ(mask_clip, &target->FirstFragment().LocalBorderBoxProperties().Clip()); EXPECT_EQ(overflow_clip1, mask_clip->Parent()); - EXPECT_EQ(FloatRoundedRect(0, 0, 220, 320), mask_clip->ClipRect()); + EXPECT_EQ(FloatRoundedRect(0, 0, 220, 320), mask_clip->UnsnappedClipRect()); EXPECT_EQ(&scroll_translation, &mask_clip->LocalTransformSpace()); EXPECT_EQ(mask_clip, overflow_clip2->Parent()); - EXPECT_EQ(FloatRoundedRect(10, 10, 200, 300), overflow_clip2->ClipRect()); + EXPECT_EQ(FloatRoundedRect(10, 10, 200, 300), + overflow_clip2->UnsnappedClipRect()); EXPECT_EQ(&scroll_translation, &overflow_clip2->LocalTransformSpace()); EXPECT_EQ(target_properties->Effect(), @@ -4894,7 +4954,8 @@ TEST_P(PaintPropertyTreeBuilderTest, MaskInline) { EXPECT_EQ(output_clip, &target->FirstFragment().LocalBorderBoxProperties().Clip()); EXPECT_EQ(DocContentClip(), output_clip->Parent()); - EXPECT_EQ(FloatRoundedRect(104, 21, 432, 16), output_clip->ClipRect()); + EXPECT_EQ(FloatRoundedRect(104, 21, 432, 16), + output_clip->UnsnappedClipRect()); EXPECT_EQ(properties->Effect(), &target->FirstFragment().LocalBorderBoxProperties().Effect()); @@ -5097,16 +5158,10 @@ TEST_P(PaintPropertyTreeBuilderTest, BackfaceHidden) { ASSERT_NE(nullptr, target_properties); const auto* paint_offset_translation = target_properties->PaintOffsetTranslation(); - if (RuntimeEnabledFeatures::CompositeAfterPaintEnabled()) { - EXPECT_EQ(nullptr, paint_offset_translation); - EXPECT_EQ(PhysicalOffset(60, 50), target->FirstFragment().PaintOffset()); - } else { - // For SPv1, |target| is composited so we created PaintOffsetTranslation. - ASSERT_NE(nullptr, paint_offset_translation); - EXPECT_EQ(FloatSize(60, 50), paint_offset_translation->Translation2D()); - EXPECT_EQ(TransformPaintPropertyNode::BackfaceVisibility::kInherited, - paint_offset_translation->GetBackfaceVisibilityForTesting()); - } + ASSERT_NE(nullptr, paint_offset_translation); + EXPECT_EQ(FloatSize(60, 50), paint_offset_translation->Translation2D()); + EXPECT_EQ(TransformPaintPropertyNode::BackfaceVisibility::kInherited, + paint_offset_translation->GetBackfaceVisibilityForTesting()); const auto* transform = target_properties->Transform(); ASSERT_NE(nullptr, transform); @@ -5140,7 +5195,7 @@ TEST_P(PaintPropertyTreeBuilderTest, FrameBorderRadius) { FloatSize radius(30, 30); EXPECT_EQ(FloatRoundedRect(FloatRect(28, 28, 200, 200), radius, radius, radius, radius), - border_radius_clip->ClipRect()); + border_radius_clip->UnsnappedClipRect()); EXPECT_EQ(DocContentClip(), border_radius_clip->Parent()); EXPECT_EQ(DocScrollTranslation(), &border_radius_clip->LocalTransformSpace()); EXPECT_EQ(nullptr, properties->InnerBorderRadiusClip()); @@ -5167,7 +5222,7 @@ TEST_P(PaintPropertyTreeBuilderTest, ImageBorderRadius) { FloatSize radius(20, 20); EXPECT_EQ(FloatRoundedRect(FloatRect(18, 18, 50, 50), radius, radius, radius, radius), - border_radius_clip->ClipRect()); + border_radius_clip->UnsnappedClipRect()); EXPECT_EQ(DocContentClip(), border_radius_clip->Parent()); EXPECT_EQ(DocScrollTranslation(), &border_radius_clip->LocalTransformSpace()); EXPECT_EQ(nullptr, properties->InnerBorderRadiusClip()); @@ -5183,10 +5238,10 @@ TEST_P(PaintPropertyTreeBuilderTest, FrameClipWhenPrinting) { auto* const child_frame_doc = &ChildDocument(); ASSERT_NE(nullptr, DocContentClip(main_frame_doc)); EXPECT_NE(FloatRect(LayoutRect::InfiniteIntRect()), - DocContentClip(main_frame_doc)->ClipRect().Rect()); + DocContentClip(main_frame_doc)->UnsnappedClipRect().Rect()); ASSERT_NE(nullptr, DocContentClip(child_frame_doc)); EXPECT_NE(FloatRect(LayoutRect::InfiniteIntRect()), - DocContentClip(child_frame_doc)->ClipRect().Rect()); + DocContentClip(child_frame_doc)->UnsnappedClipRect().Rect()); // When the main frame is printing, it should not have content clip. FloatSize page_size(100, 100); @@ -5195,7 +5250,7 @@ TEST_P(PaintPropertyTreeBuilderTest, FrameClipWhenPrinting) { EXPECT_EQ(nullptr, DocContentClip(main_frame_doc)); ASSERT_NE(nullptr, DocContentClip(child_frame_doc)); EXPECT_NE(FloatRect(LayoutRect::InfiniteIntRect()), - DocContentClip(child_frame_doc)->ClipRect().Rect()); + DocContentClip(child_frame_doc)->UnsnappedClipRect().Rect()); GetFrame().EndPrinting(); UpdateAllLifecyclePhasesForTest(); @@ -5206,7 +5261,7 @@ TEST_P(PaintPropertyTreeBuilderTest, FrameClipWhenPrinting) { GetDocument().View()->UpdateLifecyclePhasesForPrinting(); ASSERT_NE(nullptr, DocContentClip(main_frame_doc)); EXPECT_NE(FloatRect(LayoutRect::InfiniteIntRect()), - DocContentClip(main_frame_doc)->ClipRect().Rect()); + DocContentClip(main_frame_doc)->UnsnappedClipRect().Rect()); EXPECT_EQ(nullptr, DocContentClip(child_frame_doc)); } @@ -5220,7 +5275,8 @@ TEST_P(PaintPropertyTreeBuilderTest, OverflowControlsClip) { const auto* properties1 = PaintPropertiesForElement("div1"); ASSERT_NE(nullptr, properties1); const auto* overflow_controls_clip = properties1->OverflowControlsClip(); - EXPECT_EQ(FloatRect(0, 0, 5, 50), overflow_controls_clip->ClipRect().Rect()); + EXPECT_EQ(FloatRect(0, 0, 5, 50), + overflow_controls_clip->UnsnappedClipRect().Rect()); const auto* properties2 = PaintPropertiesForElement("div2"); ASSERT_NE(nullptr, properties2); @@ -5237,7 +5293,10 @@ TEST_P(PaintPropertyTreeBuilderTest, OverflowControlsClipSubpixel) { const auto* properties1 = PaintPropertiesForElement("div1"); ASSERT_NE(nullptr, properties1); const auto* overflow_controls_clip = properties1->OverflowControlsClip(); - EXPECT_EQ(FloatRect(0, 0, 6, 50), overflow_controls_clip->ClipRect().Rect()); + EXPECT_EQ(FloatRect(0, 0, 5.5, 50), + overflow_controls_clip->UnsnappedClipRect().Rect()); + EXPECT_EQ(FloatRect(0, 0, 6, 50), + overflow_controls_clip->PixelSnappedClipRect().Rect()); const auto* properties2 = PaintPropertiesForElement("div2"); ASSERT_NE(nullptr, properties2); @@ -5289,9 +5348,10 @@ TEST_P(PaintPropertyTreeBuilderTest, FragmentClipPixelSnapped) { FragmentAt(flow_thread, 1).PaintProperties()->FragmentClip(); EXPECT_EQ(FloatRect(-999992, -999992, 2000000, 1000050), - first_clip->ClipRect().Rect()); + first_clip->PixelSnappedClipRect().Rect()); + EXPECT_EQ(FloatRect(-999967, 8, 2000000, 999951), - second_clip->ClipRect().Rect()); + second_clip->PixelSnappedClipRect().Rect()); } TEST_P(PaintPropertyTreeBuilderTest, @@ -5310,18 +5370,11 @@ TEST_P(PaintPropertyTreeBuilderTest, EXPECT_FALSE(ToLayoutBoxModelObject(target)->Layer()->SelfNeedsRepaint()); opacity_element->setAttribute(html_names::kStyleAttr, "opacity: 0.5"); - GetDocument().View()->UpdateAllLifecyclePhasesExceptPaint(); + GetDocument().View()->UpdateAllLifecyclePhasesExceptPaint( + DocumentUpdateReason::kTest); - if (!RuntimeEnabledFeatures::CompositeAfterPaintEnabled()) { - // TODO(crbug.com/900241): Without CompositeAfterPaint, we create effect and - // filter nodes when the transform node needs compositing for - // will-change:transform, for crbug.com/942681. - EXPECT_FALSE(ToLayoutBoxModelObject(target)->Layer()->SelfNeedsRepaint()); - } else { - // All paint chunks contained by the new opacity effect node need to be - // re-painted. - EXPECT_TRUE(ToLayoutBoxModelObject(target)->Layer()->SelfNeedsRepaint()); - } + EXPECT_TRUE(opacity_element->GetLayoutBox()->Layer()->SelfNeedsRepaint()); + EXPECT_FALSE(ToLayoutBoxModelObject(target)->Layer()->SelfNeedsRepaint()); } TEST_P(PaintPropertyTreeBuilderTest, SVGRootWithMask) { @@ -5374,7 +5427,8 @@ TEST_P(PaintPropertyTreeBuilderTest, ClearClipPathEffectNode) { Element* clip = GetDocument().getElementById("clip"); ASSERT_TRUE(clip); clip->remove(); - GetDocument().View()->UpdateAllLifecyclePhasesExceptPaint(); + GetDocument().View()->UpdateAllLifecyclePhasesExceptPaint( + DocumentUpdateReason::kTest); { const auto* rect = GetLayoutObjectByElementId("rect"); @@ -5395,7 +5449,8 @@ TEST_P(PaintPropertyTreeBuilderTest, RootHasCompositedScrolling) { // Remove scrolling from the root. Element* force_scroll_element = GetDocument().getElementById("forceScroll"); force_scroll_element->setAttribute(html_names::kStyleAttr, ""); - GetDocument().View()->UpdateAllLifecyclePhasesExceptPaint(); + GetDocument().View()->UpdateAllLifecyclePhasesExceptPaint( + DocumentUpdateReason::kTest); // Always create scroll translation for layout view even the document does // not scroll (not enough content). EXPECT_TRUE(DocScrollTranslation()); @@ -5505,7 +5560,8 @@ TEST_P(PaintPropertyTreeBuilderTest, ClipHitTestChangeDoesNotCauseFullRepaint) { EXPECT_FALSE(child_layer->SelfNeedsRepaint()); GetDocument().body()->setAttribute(html_names::kClassAttr, "noscrollbars"); - GetDocument().View()->UpdateAllLifecyclePhasesExceptPaint(); + GetDocument().View()->UpdateAllLifecyclePhasesExceptPaint( + DocumentUpdateReason::kTest); EXPECT_FALSE(child_layer->SelfNeedsRepaint()); } @@ -5570,7 +5626,11 @@ TEST_P(PaintPropertyTreeBuilderTest, CompositedLayerSkipsFragmentClip) { .Clip()); } -TEST_P(PaintPropertyTreeBuilderTest, CompositedLayerUnderClipUnerMulticol) { +TEST_P(PaintPropertyTreeBuilderTest, CompositedLayerUnderClipUnderMulticol) { + // TODO(crbug.com/1064341): This test crashes in CompositeAfterPaint. Fix it. + if (RuntimeEnabledFeatures::CompositeAfterPaintEnabled()) + return; + SetBodyInnerHTML(R"HTML( <div id="multicol" style="columns: 2"> <div id="clip" style="height: 100px; overflow: hidden"> @@ -6024,26 +6084,6 @@ TEST_P(PaintPropertyTreeBuilderTest, StickyConstraintChain) { ->nearest_element_shifting_containing_block); } -TEST_P(PaintPropertyTreeBuilderTest, RoundedStickyConstraints) { - // This test verifies that sticky constraint rects are rounded to the nearest - // integer. - SetBodyInnerHTML(R"HTML( - <div id="scroller" style="overflow:scroll; width:300px; height:199.5px;"> - <div id="outer" style="position:sticky; top:10px; height:300px"> - </div> - <div style="height:1000px;"></div> - </div> - )HTML"); - GetDocument().getElementById("scroller")->setScrollTop(50); - UpdateAllLifecyclePhasesForTest(); - - const auto* outer_properties = PaintPropertiesForElement("outer"); - ASSERT_TRUE(outer_properties && outer_properties->StickyTranslation()); - EXPECT_EQ(gfx::Rect(0, 0, 300, 200), outer_properties->StickyTranslation() - ->GetStickyConstraint() - ->constraint_box_rect); -} - TEST_P(PaintPropertyTreeBuilderTest, NonScrollableSticky) { // This test verifies the property tree builder applies sticky offset // correctly when the clipping container cannot be scrolled, and @@ -6098,7 +6138,8 @@ TEST_P(PaintPropertyTreeBuilderTest, WillChangeOpacityInducesAnEffectNode) { auto* div = GetDocument().getElementById("div"); div->setAttribute(html_names::kClassAttr, "transluscent"); - GetDocument().View()->UpdateAllLifecyclePhasesExceptPaint(); + GetDocument().View()->UpdateAllLifecyclePhasesExceptPaint( + DocumentUpdateReason::kTest); EXPECT_FALSE( ToLayoutBox(div->GetLayoutObject())->Layer()->SelfNeedsRepaint()); @@ -6208,39 +6249,49 @@ TEST_P(PaintPropertyTreeBuilderTest, SVGRootCompositedClipPath) { const auto* clip_path_clip = properties->ClipPathClip(); ASSERT_NE(nullptr, clip_path_clip); EXPECT_EQ(DocContentClip(), clip_path_clip->Parent()); - EXPECT_EQ(FloatRect(75, 0, 150, 150), clip_path_clip->ClipRect().Rect()); + EXPECT_EQ(FloatRect(75, 0, 150, 150), + clip_path_clip->UnsnappedClipRect().Rect()); EXPECT_EQ(transform, &clip_path_clip->LocalTransformSpace()); EXPECT_NE(nullptr, clip_path_clip->ClipPath()); const auto* overflow_clip = properties->OverflowClip(); ASSERT_NE(nullptr, overflow_clip); EXPECT_EQ(clip_path_clip, overflow_clip->Parent()); - EXPECT_EQ(FloatRect(0, 0, 300, 150), overflow_clip->ClipRect().Rect()); + EXPECT_EQ(FloatRect(0, 0, 300, 150), + overflow_clip->UnsnappedClipRect().Rect()); EXPECT_EQ(transform, &overflow_clip->LocalTransformSpace()); - // TODO(wangxianzhu): Are the following correct? - EXPECT_EQ(nullptr, properties->Effect()); + const auto* effect = properties->Effect(); + ASSERT_NE(nullptr, effect); + EXPECT_EQ(&EffectPaintPropertyNode::Root(), effect->Parent()); + EXPECT_EQ(transform, &effect->LocalTransformSpace()); + EXPECT_EQ(clip_path_clip, effect->OutputClip()); + EXPECT_EQ(SkBlendMode::kSrcOver, effect->BlendMode()); + EXPECT_EQ(nullptr, properties->Mask()); EXPECT_EQ(nullptr, properties->ClipPath()); } else { const auto* mask_clip = properties->MaskClip(); ASSERT_NE(nullptr, mask_clip); EXPECT_EQ(DocContentClip(), mask_clip->Parent()); - EXPECT_EQ(FloatRect(75, 0, 150, 150), mask_clip->ClipRect().Rect()); + EXPECT_EQ(FloatRect(75, 0, 150, 150), + mask_clip->UnsnappedClipRect().Rect()); EXPECT_EQ(nullptr, mask_clip->ClipPath()); EXPECT_EQ(transform, &mask_clip->LocalTransformSpace()); const auto* clip_path_clip = properties->ClipPathClip(); ASSERT_NE(nullptr, clip_path_clip); EXPECT_EQ(mask_clip, clip_path_clip->Parent()); - EXPECT_EQ(FloatRect(75, 0, 150, 150), clip_path_clip->ClipRect().Rect()); + EXPECT_EQ(FloatRect(75, 0, 150, 150), + clip_path_clip->UnsnappedClipRect().Rect()); EXPECT_EQ(transform, &clip_path_clip->LocalTransformSpace()); EXPECT_NE(nullptr, clip_path_clip->ClipPath()); const auto* overflow_clip = properties->OverflowClip(); ASSERT_NE(nullptr, overflow_clip); EXPECT_EQ(mask_clip, overflow_clip->Parent()); - EXPECT_EQ(FloatRect(0, 0, 300, 150), overflow_clip->ClipRect().Rect()); + EXPECT_EQ(FloatRect(0, 0, 300, 150), + overflow_clip->UnsnappedClipRect().Rect()); EXPECT_EQ(transform, &overflow_clip->LocalTransformSpace()); const auto* effect = properties->Effect(); @@ -6305,7 +6356,8 @@ TEST_P(PaintPropertyTreeBuilderTest, SimpleOpacityChangeDoesNotCausePacUpdate) { Element* element = GetDocument().getElementById("element"); element->setAttribute(html_names::kStyleAttr, "opacity: 0.9"); - GetDocument().View()->UpdateAllLifecyclePhasesExceptPaint(); + GetDocument().View()->UpdateAllLifecyclePhasesExceptPaint( + DocumentUpdateReason::kTest); EXPECT_FLOAT_EQ(properties->Effect()->Opacity(), 0.9f); EXPECT_FLOAT_EQ(cc_effect->opacity, 0.9f); EXPECT_TRUE(cc_effect->effect_changed); @@ -6369,7 +6421,8 @@ TEST_P(PaintPropertyTreeBuilderTest, SimpleScrollChangeDoesNotCausePacUpdate) { EXPECT_FLOAT_EQ(current_scroll_offset.y(), 0); GetDocument().getElementById("element")->setScrollTop(10.); - GetDocument().View()->UpdateAllLifecyclePhasesExceptPaint(); + GetDocument().View()->UpdateAllLifecyclePhasesExceptPaint( + DocumentUpdateReason::kTest); EXPECT_FLOAT_SIZE_EQ(FloatSize(0, -10), properties->ScrollTranslation()->Translation2D()); @@ -6381,7 +6434,6 @@ TEST_P(PaintPropertyTreeBuilderTest, SimpleScrollChangeDoesNotCausePacUpdate) { properties->ScrollTranslation()->ScrollNode()->GetCompositorElementId()); EXPECT_FLOAT_EQ(current_scroll_offset.x(), 0); EXPECT_FLOAT_EQ(current_scroll_offset.y(), 10); - EXPECT_TRUE(property_trees->scroll_tree.needs_update()); EXPECT_TRUE(property_trees->transform_tree.needs_update()); EXPECT_TRUE(cc_transform_node->transform_changed); @@ -6413,7 +6465,8 @@ TEST_P(PaintPropertyTreeBuilderTest, Element* outer = GetDocument().getElementById("outer"); outer->setAttribute(html_names::kStyleAttr, "transform: translateY(10px)"); - GetDocument().View()->UpdateAllLifecyclePhasesExceptPaint(); + GetDocument().View()->UpdateAllLifecyclePhasesExceptPaint( + DocumentUpdateReason::kTest); EXPECT_TRUE( GetDocument().View()->GetPaintArtifactCompositor()->NeedsUpdate()); @@ -6464,24 +6517,22 @@ TEST_P(PaintPropertyTreeBuilderTest, VideoClipRect) { video_element->SetInlineStyleProperty(CSSPropertyID::kTop, "0.1px"); video_element->SetInlineStyleProperty(CSSPropertyID::kLeft, "0.1px"); LocalFrameView* frame_view = GetDocument().View(); - frame_view->UpdateAllLifecyclePhases( - DocumentLifecycle::LifecycleUpdateReason::kTest); + frame_view->UpdateAllLifecyclePhases(DocumentUpdateReason::kTest); const ObjectPaintProperties* video_element_properties = video_element->GetLayoutObject()->FirstFragment().PaintProperties(); // |video_element| is now sub-pixel positioned, at 0.1,0.1 320.2x240. With or // without pixel-snapped clipping, this will get clipped at 0,0 320x240. EXPECT_EQ(FloatRoundedRect(0, 0, 320, 240), - video_element_properties->OverflowClip()->ClipRect()); + video_element_properties->OverflowClip()->UnsnappedClipRect()); // Now, move |video_element| to 10.4,10.4. At this point, without pixel // snapping that doesn't depend on paint offset, it will be clipped at 10,10 // 321x240. With proper pixel snapping, the clip will be at 10,10,320,240. video_element->SetInlineStyleProperty(CSSPropertyID::kTop, "10.4px"); video_element->SetInlineStyleProperty(CSSPropertyID::kLeft, "10.4px"); - frame_view->UpdateAllLifecyclePhases( - DocumentLifecycle::LifecycleUpdateReason::kTest); + frame_view->UpdateAllLifecyclePhases(DocumentUpdateReason::kTest); EXPECT_EQ(FloatRoundedRect(10, 10, 320, 240), - video_element_properties->OverflowClip()->ClipRect()); + video_element_properties->OverflowClip()->UnsnappedClipRect()); } // For NoPaintPropertyForXXXText cases. The styles trigger almost all paint @@ -6529,65 +6580,6 @@ TEST_P(PaintPropertyTreeBuilderTest, NoPaintPropertyForSVGText) { EXPECT_FALSE(text->FirstFragment().PaintProperties()); } -TEST_P(PaintPropertyTreeBuilderTest, SetViewportScrollingBits) { - SetBodyInnerHTML(R"HTML( - <style> - body, html { - margin: 0; - width: 100%; - height: 100%; - } - #scroller { - width: 100%; - height: 200%; - overflow: auto; - } - </style> - <div id="scroller"> - <div style="height: 3000px"></div> - </div> - )HTML"); - - const auto* scroller_node = PaintPropertiesForElement("scroller")->Scroll(); - const auto* document_node = DocScroll(); - - // Ensure the LayoutView's ScrollNode is marked as scrolling the "outer" or - // "layout" viewport. - { - EXPECT_FALSE(scroller_node->ScrollsOuterViewport()); - EXPECT_TRUE(document_node->ScrollsOuterViewport()); - } - - // Ensure the visual viewport is the only one that sets the inner scroll bit. - { - EXPECT_TRUE(GetDocument() - .GetPage() - ->GetVisualViewport() - .GetScrollNode() - ->ScrollsInnerViewport()); - EXPECT_FALSE(scroller_node->ScrollsInnerViewport()); - EXPECT_FALSE(document_node->ScrollsInnerViewport()); - } - - // Make the scroller fill the viewport. This will make it eligible for root - // scroller promotion. Ensure the outer viewport scrolling property is - // correctly recomputed, moving it from the LayoutView to the scroller. - { - Element* scroller = GetDocument().getElementById("scroller"); - scroller->setAttribute(html_names::kStyleAttr, "height: 100%"); - LocalFrameView* frame_view = GetDocument().View(); - frame_view->UpdateAllLifecyclePhases( - DocumentLifecycle::LifecycleUpdateReason::kTest); - ASSERT_TRUE(scroller->GetLayoutObject()->IsGlobalRootScroller()); - - EXPECT_TRUE(scroller_node->ScrollsOuterViewport()); - - // Since the document is no longer scrollable and isn't the root scroller - // it shouldn't have a node. - EXPECT_FALSE(DocScroll()); - } -} - TEST_P(PaintPropertyTreeBuilderTest, IsAffectedByOuterViewportBoundsDelta) { SetBodyInnerHTML(R"HTML( <style>div { will-change: transform; position: fixed; }</style> 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 057f90eb7a5..59a6b43f841 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 @@ -54,7 +54,7 @@ class FrameViewPropertyTreePrinter for (const auto* fragment = &object.FirstFragment(); fragment; fragment = fragment->NextFragment()) { if (const auto* properties = fragment->PaintProperties()) - Traits::AddObjectPaintProperties(object, *properties, *this); + Traits::AddObjectPaintProperties(*properties, *this); } for (const auto* child = object.SlowFirstChild(); child; child = child->NextSibling()) { @@ -75,7 +75,6 @@ class PropertyTreePrinterTraits<TransformPaintPropertyNode> { printer.AddNode(visual_viewport.GetScrollTranslationNode()); } static void AddObjectPaintProperties( - const LayoutObject& object, const ObjectPaintProperties& properties, PropertyTreePrinter<TransformPaintPropertyNode>& printer) { printer.AddNode(properties.PaintOffsetTranslation()); @@ -95,7 +94,6 @@ class PropertyTreePrinterTraits<ClipPaintPropertyNode> { const VisualViewport& visual_viewport, PropertyTreePrinter<ClipPaintPropertyNode>& printer) {} static void AddObjectPaintProperties( - const LayoutObject& object, const ObjectPaintProperties& properties, PropertyTreePrinter<ClipPaintPropertyNode>& printer) { printer.AddNode(properties.FragmentClip()); @@ -118,7 +116,6 @@ class PropertyTreePrinterTraits<EffectPaintPropertyNode> { PropertyTreePrinter<EffectPaintPropertyNode>& printer) {} static void AddObjectPaintProperties( - const LayoutObject& object, const ObjectPaintProperties& properties, PropertyTreePrinter<EffectPaintPropertyNode>& printer) { printer.AddNode(properties.Effect()); @@ -141,7 +138,6 @@ class PropertyTreePrinterTraits<ScrollPaintPropertyNode> { } static void AddObjectPaintProperties( - const LayoutObject& object, const ObjectPaintProperties& properties, PropertyTreePrinter<ScrollPaintPropertyNode>& printer) { printer.AddNode(properties.Scroll()); 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 da6c4c0a9bf..8f6de40bed2 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 @@ -315,8 +315,7 @@ TEST_P(PaintPropertyTreeUpdateTest, DescendantNeedsUpdateAcrossFrames) { "translate3d(4px, 5px, 6px); width: 100px; height: 200px'></div>"); LocalFrameView* frame_view = GetDocument().View(); - frame_view->UpdateAllLifecyclePhases( - DocumentLifecycle::LifecycleUpdateReason::kTest); + frame_view->UpdateAllLifecyclePhases(DocumentUpdateReason::kTest); LayoutObject* div_with_transform = GetLayoutObjectByElementId("divWithTransform"); @@ -342,8 +341,7 @@ 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( - DocumentLifecycle::LifecycleUpdateReason::kTest); + frame_view->UpdateAllLifecyclePhases(DocumentUpdateReason::kTest); EXPECT_FALSE( GetDocument().GetLayoutView()->DescendantNeedsPaintPropertyUpdate()); EXPECT_FALSE(div_with_transform->DescendantNeedsPaintPropertyUpdate()); @@ -356,8 +354,7 @@ TEST_P(PaintPropertyTreeUpdateTest, DescendantNeedsUpdateAcrossFrames) { child_frame_view->SetNeedsPaintPropertyUpdate(); EXPECT_TRUE( GetDocument().GetLayoutView()->DescendantNeedsPaintPropertyUpdate()); - frame_view->UpdateAllLifecyclePhases( - DocumentLifecycle::LifecycleUpdateReason::kTest); + frame_view->UpdateAllLifecyclePhases(DocumentUpdateReason::kTest); EXPECT_FALSE( GetDocument().GetLayoutView()->DescendantNeedsPaintPropertyUpdate()); EXPECT_FALSE(GetDocument().GetLayoutView()->NeedsPaintPropertyUpdate()); @@ -368,16 +365,20 @@ TEST_P(PaintPropertyTreeUpdateTest, DescendantNeedsUpdateAcrossFrames) { TEST_P(PaintPropertyTreeUpdateTest, UpdatingFrameViewContentClip) { SetBodyInnerHTML("hello world."); - EXPECT_EQ(FloatRoundedRect(0, 0, 800, 600), DocContentClip()->ClipRect()); + EXPECT_EQ(FloatRoundedRect(0, 0, 800, 600), + DocContentClip()->UnsnappedClipRect()); GetDocument().View()->Resize(800, 599); UpdateAllLifecyclePhasesForTest(); - EXPECT_EQ(FloatRoundedRect(0, 0, 800, 599), DocContentClip()->ClipRect()); + EXPECT_EQ(FloatRoundedRect(0, 0, 800, 599), + DocContentClip()->UnsnappedClipRect()); GetDocument().View()->Resize(800, 600); UpdateAllLifecyclePhasesForTest(); - EXPECT_EQ(FloatRoundedRect(0, 0, 800, 600), DocContentClip()->ClipRect()); + EXPECT_EQ(FloatRoundedRect(0, 0, 800, 600), + DocContentClip()->UnsnappedClipRect()); GetDocument().View()->Resize(5, 5); UpdateAllLifecyclePhasesForTest(); - EXPECT_EQ(FloatRoundedRect(0, 0, 5, 5), DocContentClip()->ClipRect()); + EXPECT_EQ(FloatRoundedRect(0, 0, 5, 5), + DocContentClip()->UnsnappedClipRect()); } // There is also FrameThrottlingTest.UpdatePaintPropertiesOnUnthrottling @@ -477,17 +478,17 @@ TEST_P(PaintPropertyTreeUpdateTest, ClipChangesUpdateOverflowClip) { UpdateAllLifecyclePhasesForTest(); auto* clip_properties = div->GetLayoutObject()->FirstFragment().PaintProperties()->OverflowClip(); - EXPECT_EQ(FloatRect(0, 0, 7, 0), clip_properties->ClipRect().Rect()); + EXPECT_EQ(FloatRect(0, 0, 7, 0), clip_properties->UnsnappedClipRect().Rect()); // Width changes should update the overflow clip. 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()); + EXPECT_EQ(FloatRect(0, 0, 7, 0), clip_properties->UnsnappedClipRect().Rect()); div->setAttribute(html_names::kStyleAttr, "display:inline-block; width:9px;"); UpdateAllLifecyclePhasesForTest(); - EXPECT_EQ(FloatRect(0, 0, 9, 0), clip_properties->ClipRect().Rect()); + EXPECT_EQ(FloatRect(0, 0, 9, 0), clip_properties->UnsnappedClipRect().Rect()); // An inline block's overflow clip should be updated when padding changes, // even if the border box remains unchanged. @@ -496,33 +497,39 @@ TEST_P(PaintPropertyTreeUpdateTest, ClipChangesUpdateOverflowClip) { UpdateAllLifecyclePhasesForTest(); clip_properties = div->GetLayoutObject()->FirstFragment().PaintProperties()->OverflowClip(); - EXPECT_EQ(FloatRect(0, 0, 10, 0), clip_properties->ClipRect().Rect()); + EXPECT_EQ(FloatRect(0, 0, 10, 0), + clip_properties->UnsnappedClipRect().Rect()); div->setAttribute(html_names::kStyleAttr, "display:inline-block; width:8px; padding-right:2px;"); UpdateAllLifecyclePhasesForTest(); - EXPECT_EQ(FloatRect(0, 0, 10, 0), clip_properties->ClipRect().Rect()); + EXPECT_EQ(FloatRect(0, 0, 10, 0), + clip_properties->UnsnappedClipRect().Rect()); div->setAttribute(html_names::kStyleAttr, "display:inline-block; width:8px;" "padding-right:1px; padding-left:1px;"); UpdateAllLifecyclePhasesForTest(); - EXPECT_EQ(FloatRect(0, 0, 10, 0), clip_properties->ClipRect().Rect()); + EXPECT_EQ(FloatRect(0, 0, 10, 0), + clip_properties->UnsnappedClipRect().Rect()); // An block's overflow clip should be updated when borders change. 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()); + EXPECT_EQ(FloatRect(0, 0, 797, 0), + clip_properties->UnsnappedClipRect().Rect()); div->setAttribute(html_names::kStyleAttr, "border-right:5px solid red;"); UpdateAllLifecyclePhasesForTest(); - EXPECT_EQ(FloatRect(0, 0, 795, 0), clip_properties->ClipRect().Rect()); + EXPECT_EQ(FloatRect(0, 0, 795, 0), + clip_properties->UnsnappedClipRect().Rect()); // Removing overflow clip should remove the property. 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()); + EXPECT_EQ(FloatRect(0, 0, 800, 0), + clip_properties->UnsnappedClipRect().Rect()); div->setAttribute(html_names::kStyleAttr, "overflow:visible;"); UpdateAllLifecyclePhasesForTest(); EXPECT_TRUE(!div->GetLayoutObject()->FirstFragment().PaintProperties() || @@ -546,7 +553,7 @@ TEST_P(PaintPropertyTreeUpdateTest, ContainPaintChangesUpdateOverflowClip) { auto* div = GetDocument().getElementById("div"); auto* properties = div->GetLayoutObject()->FirstFragment().PaintProperties()->OverflowClip(); - EXPECT_EQ(FloatRect(0, 0, 7, 6), properties->ClipRect().Rect()); + EXPECT_EQ(FloatRect(0, 0, 7, 6), properties->UnsnappedClipRect().Rect()); div->setAttribute(html_names::kStyleAttr, ""); UpdateAllLifecyclePhasesForTest(); @@ -564,7 +571,8 @@ TEST_P(PaintPropertyTreeUpdateTest, NoPaintPropertyUpdateOnBackgroundChange) { UpdateAllLifecyclePhasesForTest(); div->setAttribute(html_names::kStyleAttr, "background-color: green"); - GetDocument().View()->UpdateLifecycleToLayoutClean(); + GetDocument().View()->UpdateLifecycleToLayoutClean( + DocumentUpdateReason::kTest); EXPECT_FALSE(div->GetLayoutObject()->NeedsPaintPropertyUpdate()); } @@ -582,16 +590,14 @@ TEST_P(PaintPropertyTreeUpdateTest, "<div id='forceScroll' style='height: 3000px;'></div>"); LocalFrameView* frame_view = GetDocument().View(); - frame_view->UpdateAllLifecyclePhases( - DocumentLifecycle::LifecycleUpdateReason::kTest); + frame_view->UpdateAllLifecyclePhases(DocumentUpdateReason::kTest); EXPECT_EQ(nullptr, DocScroll()); Document* child_doc = &ChildDocument(); EXPECT_NE(nullptr, DocScroll(child_doc)); auto* iframe_container = GetDocument().getElementById("iframeContainer"); iframe_container->setAttribute(html_names::kStyleAttr, "visibility: hidden;"); - frame_view->UpdateAllLifecyclePhases( - DocumentLifecycle::LifecycleUpdateReason::kTest); + frame_view->UpdateAllLifecyclePhases(DocumentUpdateReason::kTest); EXPECT_EQ(nullptr, DocScroll()); EXPECT_EQ(nullptr, DocScroll(child_doc)); @@ -760,15 +766,19 @@ TEST_P(PaintPropertyTreeUpdateTest, CSSClipDependingOnSize) { auto* outer = GetDocument().getElementById("outer"); auto* clip = GetLayoutObjectByElementId("clip"); - EXPECT_EQ( - FloatRect(45, 50, 105, 100), - clip->FirstFragment().PaintProperties()->CssClip()->ClipRect().Rect()); + EXPECT_EQ(FloatRect(45, 50, 105, 100), clip->FirstFragment() + .PaintProperties() + ->CssClip() + ->UnsnappedClipRect() + .Rect()); outer->setAttribute(html_names::kStyleAttr, "height: 200px"); UpdateAllLifecyclePhasesForTest(); - EXPECT_EQ( - FloatRect(45, 50, 105, 200), - clip->FirstFragment().PaintProperties()->CssClip()->ClipRect().Rect()); + EXPECT_EQ(FloatRect(45, 50, 105, 200), clip->FirstFragment() + .PaintProperties() + ->CssClip() + ->UnsnappedClipRect() + .Rect()); } TEST_P(PaintPropertyTreeUpdateTest, ScrollBoundsChange) { @@ -888,7 +898,8 @@ TEST_P(PaintPropertyTreeUpdateTest, ScrollbarWidthChange) { auto* container = GetLayoutObjectByElementId("container"); auto* overflow_clip = container->FirstFragment().PaintProperties()->OverflowClip(); - EXPECT_EQ(FloatSize(80, 80), overflow_clip->ClipRect().Rect().Size()); + EXPECT_EQ(FloatSize(80, 80), + overflow_clip->UnsnappedClipRect().Rect().Size()); auto* new_style = GetDocument().CreateRawElement(html_names::kStyleTag); new_style->setTextContent("::-webkit-scrollbar {width: 40px; height: 40px}"); @@ -897,7 +908,8 @@ TEST_P(PaintPropertyTreeUpdateTest, ScrollbarWidthChange) { UpdateAllLifecyclePhasesForTest(); EXPECT_EQ(overflow_clip, container->FirstFragment().PaintProperties()->OverflowClip()); - EXPECT_EQ(FloatSize(60, 60), overflow_clip->ClipRect().Rect().Size()); + EXPECT_EQ(FloatSize(60, 60), + overflow_clip->UnsnappedClipRect().Rect().Size()); } TEST_P(PaintPropertyTreeUpdateTest, Preserve3DChange) { @@ -956,7 +968,7 @@ TEST_P(PaintPropertyTreeUpdateTest, BoxAddRemoveMask) { EXPECT_NE(nullptr, properties->Mask()); const auto* mask_clip = properties->MaskClip(); ASSERT_NE(nullptr, mask_clip); - EXPECT_EQ(FloatRoundedRect(8, 8, 100, 100), mask_clip->ClipRect()); + EXPECT_EQ(FloatRoundedRect(8, 8, 100, 100), mask_clip->UnsnappedClipRect()); target->setAttribute(html_names::kStyleAttr, ""); UpdateAllLifecyclePhasesForTest(); @@ -981,14 +993,14 @@ TEST_P(PaintPropertyTreeUpdateTest, MaskClipNodeBoxSizeChange) { ASSERT_NE(nullptr, properties); const auto* mask_clip = properties->MaskClip(); ASSERT_NE(nullptr, mask_clip); - EXPECT_EQ(FloatRoundedRect(8, 8, 100, 100), mask_clip->ClipRect()); + EXPECT_EQ(FloatRoundedRect(8, 8, 100, 100), mask_clip->UnsnappedClipRect()); GetDocument().getElementById("target")->setAttribute(html_names::kStyleAttr, "height: 200px"); UpdateAllLifecyclePhasesForTest(); ASSERT_EQ(mask_clip, properties->MaskClip()); - EXPECT_EQ(FloatRoundedRect(8, 8, 100, 200), mask_clip->ClipRect()); + EXPECT_EQ(FloatRoundedRect(8, 8, 100, 200), mask_clip->UnsnappedClipRect()); } TEST_P(PaintPropertyTreeUpdateTest, InlineAddRemoveMask) { @@ -1008,7 +1020,7 @@ TEST_P(PaintPropertyTreeUpdateTest, InlineAddRemoveMask) { EXPECT_NE(nullptr, properties->Mask()); const auto* mask_clip = properties->MaskClip(); ASSERT_NE(nullptr, mask_clip); - EXPECT_EQ(50, mask_clip->ClipRect().Rect().Width()); + EXPECT_EQ(50, mask_clip->UnsnappedClipRect().Rect().Width()); target->setAttribute(html_names::kStyleAttr, ""); UpdateAllLifecyclePhasesForTest(); @@ -1026,14 +1038,14 @@ TEST_P(PaintPropertyTreeUpdateTest, MaskClipNodeInlineBoundsChange) { ASSERT_NE(nullptr, properties); const auto* mask_clip = properties->MaskClip(); ASSERT_NE(nullptr, mask_clip); - EXPECT_EQ(50, mask_clip->ClipRect().Rect().Width()); + EXPECT_EQ(50, mask_clip->UnsnappedClipRect().Rect().Width()); GetDocument().getElementById("img")->setAttribute(html_names::kStyleAttr, "width: 100px"); UpdateAllLifecyclePhasesForTest(); ASSERT_EQ(mask_clip, properties->MaskClip()); - EXPECT_EQ(100, mask_clip->ClipRect().Rect().Width()); + EXPECT_EQ(100, mask_clip->UnsnappedClipRect().Rect().Width()); } TEST_P(PaintPropertyTreeUpdateTest, AddRemoveSVGMask) { @@ -1058,7 +1070,7 @@ TEST_P(PaintPropertyTreeUpdateTest, AddRemoveSVGMask) { EXPECT_NE(nullptr, properties->Mask()); const auto* mask_clip = properties->MaskClip(); ASSERT_NE(nullptr, mask_clip); - EXPECT_EQ(FloatRoundedRect(0, 100, 100, 100), mask_clip->ClipRect()); + EXPECT_EQ(FloatRoundedRect(0, 100, 100, 100), mask_clip->UnsnappedClipRect()); GetDocument().getElementById("rect")->removeAttribute("mask"); UpdateAllLifecyclePhasesForTest(); @@ -1085,13 +1097,13 @@ TEST_P(PaintPropertyTreeUpdateTest, SVGMaskTargetBoundsChange) { EXPECT_NE(nullptr, properties->Mask()); const auto* mask_clip = properties->MaskClip(); ASSERT_NE(nullptr, mask_clip); - EXPECT_EQ(FloatRoundedRect(0, 50, 100, 150), mask_clip->ClipRect()); + EXPECT_EQ(FloatRoundedRect(0, 50, 100, 150), mask_clip->UnsnappedClipRect()); GetDocument().getElementById("rect")->setAttribute("width", "200"); UpdateAllLifecyclePhasesForTest(); EXPECT_NE(nullptr, properties->Effect()); EXPECT_NE(nullptr, properties->Mask()); - EXPECT_EQ(FloatRoundedRect(0, 50, 100, 150), mask_clip->ClipRect()); + EXPECT_EQ(FloatRoundedRect(0, 50, 100, 150), mask_clip->UnsnappedClipRect()); } TEST_P(PaintPropertyTreeUpdateTest, WillTransformChangeAboveFixed) { @@ -1184,7 +1196,7 @@ TEST_P(PaintPropertyTreeUpdateTest, SVGViewportContainerOverflowChange) { const auto* properties = PaintPropertiesForElement("target"); ASSERT_NE(nullptr, properties); EXPECT_EQ(FloatRect(0, 0, 30, 40), - properties->OverflowClip()->ClipRect().Rect()); + properties->OverflowClip()->UnsnappedClipRect().Rect()); GetDocument().getElementById("target")->setAttribute("overflow", "visible"); UpdateAllLifecyclePhasesForTest(); @@ -1195,7 +1207,7 @@ TEST_P(PaintPropertyTreeUpdateTest, SVGViewportContainerOverflowChange) { properties = PaintPropertiesForElement("target"); ASSERT_NE(nullptr, properties); EXPECT_EQ(FloatRect(0, 0, 30, 40), - properties->OverflowClip()->ClipRect().Rect()); + properties->OverflowClip()->UnsnappedClipRect().Rect()); } TEST_P(PaintPropertyTreeUpdateTest, SVGForeignObjectOverflowChange) { @@ -1210,7 +1222,7 @@ TEST_P(PaintPropertyTreeUpdateTest, SVGForeignObjectOverflowChange) { const auto* properties = PaintPropertiesForElement("target"); ASSERT_NE(nullptr, properties); EXPECT_EQ(FloatRect(10, 20, 30, 40), - properties->OverflowClip()->ClipRect().Rect()); + properties->OverflowClip()->UnsnappedClipRect().Rect()); GetDocument().getElementById("target")->setAttribute("overflow", "visible"); UpdateAllLifecyclePhasesForTest(); @@ -1221,7 +1233,7 @@ TEST_P(PaintPropertyTreeUpdateTest, SVGForeignObjectOverflowChange) { properties = PaintPropertiesForElement("target"); ASSERT_NE(nullptr, properties); EXPECT_EQ(FloatRect(10, 20, 30, 40), - properties->OverflowClip()->ClipRect().Rect()); + properties->OverflowClip()->UnsnappedClipRect().Rect()); } TEST_P(PaintPropertyTreeBuilderTest, OmitOverflowClipOnSelectionChange) { @@ -1280,13 +1292,13 @@ TEST_P(PaintPropertyTreeUpdateTest, EXPECT_EQ(1000000, FragmentAt(flow_thread, 0) .PaintProperties() ->FragmentClip() - ->ClipRect() + ->UnsnappedClipRect() .Rect() .MaxX()); EXPECT_EQ(-999950, FragmentAt(flow_thread, 1) .PaintProperties() ->FragmentClip() - ->ClipRect() + ->UnsnappedClipRect() .Rect() .X()); @@ -1298,13 +1310,13 @@ TEST_P(PaintPropertyTreeUpdateTest, EXPECT_EQ(1000000, FragmentAt(flow_thread, 0) .PaintProperties() ->FragmentClip() - ->ClipRect() + ->UnsnappedClipRect() .Rect() .MaxX()); EXPECT_EQ(-999750, FragmentAt(flow_thread, 1) .PaintProperties() ->FragmentClip() - ->ClipRect() + ->UnsnappedClipRect() .Rect() .X()); } @@ -1522,7 +1534,7 @@ TEST_P(PaintPropertyTreeUpdateTest, OverflowClipUpdateForImage) { FloatSize corner(2, 2); FloatRoundedRect::Radii radii(corner, corner, corner, corner); EXPECT_EQ(FloatRoundedRect(FloatRect(8, 8, 8, 8), radii), - properties->OverflowClip()->ClipRect()); + properties->OverflowClip()->UnsnappedClipRect()); // We should update clip rect on border radius change. target->setAttribute(html_names::kStyleAttr, @@ -1532,7 +1544,7 @@ TEST_P(PaintPropertyTreeUpdateTest, OverflowClipUpdateForImage) { ASSERT_TRUE(properties->OverflowClip()); radii.Expand(1); EXPECT_EQ(FloatRoundedRect(FloatRect(8, 8, 8, 8), radii), - properties->OverflowClip()->ClipRect()); + properties->OverflowClip()->UnsnappedClipRect()); // We should update clip rect on padding change. target->setAttribute( @@ -1547,7 +1559,7 @@ TEST_P(PaintPropertyTreeUpdateTest, OverflowClipUpdateForImage) { FloatRoundedRect(FloatRect(12, 9, 2, 4), FloatRoundedRect::Radii(FloatSize(0, 2), FloatSize(1, 2), FloatSize(), FloatSize(1, 0))), - properties->OverflowClip()->ClipRect()); + properties->OverflowClip()->UnsnappedClipRect()); } TEST_P(PaintPropertyTreeUpdateTest, OverflowClipUpdateForVideo) { @@ -1570,14 +1582,14 @@ TEST_P(PaintPropertyTreeUpdateTest, OverflowClipUpdateForVideo) { ASSERT_TRUE(properties); ASSERT_TRUE(properties->OverflowClip()); EXPECT_EQ(FloatRoundedRect(8, 8, 8, 8), - properties->OverflowClip()->ClipRect()); + properties->OverflowClip()->UnsnappedClipRect()); target->setAttribute(html_names::kStyleAttr, "object-fit: cover"); UpdateAllLifecyclePhasesForTest(); ASSERT_EQ(properties, PaintPropertiesForElement("target")); ASSERT_TRUE(properties->OverflowClip()); EXPECT_EQ(FloatRoundedRect(8, 8, 8, 8), - properties->OverflowClip()->ClipRect()); + properties->OverflowClip()->UnsnappedClipRect()); // We need OverflowClip for object-fit: cover, too. target->setAttribute(html_names::kStyleAttr, "object-fit: none"); @@ -1585,7 +1597,7 @@ TEST_P(PaintPropertyTreeUpdateTest, OverflowClipUpdateForVideo) { ASSERT_EQ(properties, PaintPropertiesForElement("target")); ASSERT_TRUE(properties->OverflowClip()); EXPECT_EQ(FloatRoundedRect(8, 8, 8, 8), - properties->OverflowClip()->ClipRect()); + properties->OverflowClip()->UnsnappedClipRect()); // We should update clip rect on padding change. target->setAttribute(html_names::kStyleAttr, @@ -1594,7 +1606,7 @@ TEST_P(PaintPropertyTreeUpdateTest, OverflowClipUpdateForVideo) { ASSERT_EQ(properties, PaintPropertiesForElement("target")); ASSERT_TRUE(properties->OverflowClip()); EXPECT_EQ(FloatRoundedRect(12, 9, 2, 4), - properties->OverflowClip()->ClipRect()); + properties->OverflowClip()->UnsnappedClipRect()); } TEST_P(PaintPropertyTreeUpdateTest, ChangingClipPath) { @@ -1690,7 +1702,8 @@ TEST_P(PaintPropertyTreeUpdateTest, ChangeDuringAnimation) { target->SetStyle(std::move(style)); EXPECT_TRUE(target->NeedsPaintPropertyUpdate()); GetDocument().Lifecycle().AdvanceTo(DocumentLifecycle::kStyleClean); - GetDocument().View()->UpdateAllLifecyclePhasesExceptPaint(); + GetDocument().View()->UpdateAllLifecyclePhasesExceptPaint( + DocumentUpdateReason::kTest); const auto* transform_node = target->FirstFragment().PaintProperties()->Transform(); @@ -1718,7 +1731,8 @@ TEST_P(PaintPropertyTreeUpdateTest, ChangeDuringAnimation) { target->SetStyle(std::move(style)); EXPECT_TRUE(target->NeedsPaintPropertyUpdate()); GetDocument().Lifecycle().AdvanceTo(DocumentLifecycle::kStyleClean); - GetDocument().View()->UpdateAllLifecyclePhasesExceptPaint(); + GetDocument().View()->UpdateAllLifecyclePhasesExceptPaint( + DocumentUpdateReason::kTest); ASSERT_EQ(transform_node, target->FirstFragment().PaintProperties()->Transform()); @@ -1737,7 +1751,8 @@ TEST_P(PaintPropertyTreeUpdateTest, ChangeDuringAnimation) { target->SetStyle(std::move(style)); EXPECT_TRUE(target->NeedsPaintPropertyUpdate()); GetDocument().Lifecycle().AdvanceTo(DocumentLifecycle::kStyleClean); - GetDocument().View()->UpdateAllLifecyclePhasesExceptPaint(); + GetDocument().View()->UpdateAllLifecyclePhasesExceptPaint( + DocumentUpdateReason::kTest); ASSERT_EQ(transform_node, target->FirstFragment().PaintProperties()->Transform()); @@ -1756,7 +1771,8 @@ TEST_P(PaintPropertyTreeUpdateTest, BackfaceVisibilityInvalidatesProperties) { auto* span = GetDocument().getElementById("span"); span->setAttribute(html_names::kStyleAttr, "backface-visibility: hidden;"); - GetDocument().View()->UpdateLifecycleToLayoutClean(); + GetDocument().View()->UpdateLifecycleToLayoutClean( + DocumentUpdateReason::kTest); EXPECT_TRUE(span->GetLayoutObject()->NeedsPaintPropertyUpdate()); } 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 d4c99d65d9f..6e7140526b6 100644 --- a/chromium/third_party/blink/renderer/core/paint/paint_timing.cc +++ b/chromium/third_party/blink/renderer/core/paint/paint_timing.cc @@ -99,12 +99,6 @@ void PaintTiming::SetFirstMeaningfulPaint( "loading,rail,devtools.timeline", "firstMeaningfulPaint", swap_stamp, "frame", ToTraceValue(GetFrame()), "afterUserInput", had_input); - InteractiveDetector* interactive_detector( - InteractiveDetector::From(*GetSupplementable())); - if (interactive_detector) { - interactive_detector->OnFirstMeaningfulPaintDetected(swap_stamp, had_input); - } - // Notify FMP for UMA only if there's no user input before FMP, so that layout // changes caused by user interactions wouldn't be considered as FMP. if (had_input == FirstMeaningfulPaintDetector::kNoUserInput) { @@ -129,7 +123,7 @@ void PaintTiming::SetTickClockForTesting(const base::TickClock* clock) { clock_ = clock; } -void PaintTiming::Trace(blink::Visitor* visitor) { +void PaintTiming::Trace(Visitor* visitor) { visitor->Trace(fmp_detector_); Supplement<Document>::Trace(visitor); } @@ -190,11 +184,11 @@ void PaintTiming::RegisterNotifySwapTime(PaintEvent event, } void PaintTiming::ReportSwapTime(PaintEvent event, - WebWidgetClient::SwapResult result, + WebSwapResult result, base::TimeTicks timestamp) { DCHECK(IsMainThread()); // If the swap fails for any reason, we use the timestamp when the SwapPromise - // was broken. |result| == WebWidgetClient::SwapResult::kDidNotSwapSwapFails + // was broken. |result| == WebSwapResult::kDidNotSwapSwapFails // usually means the compositor decided not swap because there was no actual // damage, which can happen when what's being painted isn't visible. In this // case, the timestamp will be consistent with the case where the swap @@ -246,6 +240,11 @@ void PaintTiming::SetFirstContentfulPaintSwap(base::TimeTicks stamp) { GetFrame()->Loader().Progress().DidFirstContentfulPaint(); NotifyPaintTimingChanged(); fmp_detector_->NotifyFirstContentfulPaint(first_contentful_paint_swap_); + InteractiveDetector* interactive_detector = + InteractiveDetector::From(*GetSupplementable()); + if (interactive_detector) { + interactive_detector->OnFirstContentfulPaint(first_contentful_paint_swap_); + } } void PaintTiming::SetFirstImagePaintSwap(base::TimeTicks stamp) { @@ -256,12 +255,12 @@ void PaintTiming::SetFirstImagePaintSwap(base::TimeTicks stamp) { NotifyPaintTimingChanged(); } -void PaintTiming::ReportSwapResultHistogram( - WebWidgetClient::SwapResult result) { - DEFINE_STATIC_LOCAL(EnumerationHistogram, did_swap_histogram, - ("PageLoad.Internal.Renderer.PaintTiming.SwapResult", - WebWidgetClient::SwapResult::kSwapResultMax)); - did_swap_histogram.Count(result); +void PaintTiming::ReportSwapResultHistogram(WebSwapResult result) { + DEFINE_STATIC_LOCAL( + EnumerationHistogram, did_swap_histogram, + ("PageLoad.Internal.Renderer.PaintTiming.SwapResult", + static_cast<uint32_t>(WebSwapResult::kSwapResultLast) + 1)); + did_swap_histogram.Count(static_cast<uint32_t>(result)); } } // namespace blink 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 ffb1ae2133f..61e4358f997 100644 --- a/chromium/third_party/blink/renderer/core/paint/paint_timing.h +++ b/chromium/third_party/blink/renderer/core/paint/paint_timing.h @@ -8,7 +8,7 @@ #include <memory> #include "base/macros.h" -#include "third_party/blink/public/web/web_widget_client.h" +#include "third_party/blink/public/web/web_swap_result.h" #include "third_party/blink/renderer/core/dom/document.h" #include "third_party/blink/renderer/core/paint/first_meaningful_paint_detector.h" #include "third_party/blink/renderer/core/paint/paint_event.h" @@ -31,8 +31,7 @@ class CORE_EXPORT PaintTiming final : public GarbageCollected<PaintTiming>, USING_GARBAGE_COLLECTED_MIXIN(PaintTiming); friend class FirstMeaningfulPaintDetector; using ReportTimeCallback = - WTF::CrossThreadOnceFunction<void(WebWidgetClient::SwapResult, - base::TimeTicks)>; + WTF::CrossThreadOnceFunction<void(WebSwapResult, base::TimeTicks)>; public: static const char kSupplementName[]; @@ -99,16 +98,14 @@ class CORE_EXPORT PaintTiming final : public GarbageCollected<PaintTiming>, } void RegisterNotifySwapTime(PaintEvent, ReportTimeCallback); - void ReportSwapTime(PaintEvent, - WebWidgetClient::SwapResult, - base::TimeTicks timestamp); + void ReportSwapTime(PaintEvent, WebSwapResult, base::TimeTicks timestamp); - void ReportSwapResultHistogram(WebWidgetClient::SwapResult); + void ReportSwapResultHistogram(WebSwapResult); // The caller owns the |clock| which must outlive the PaintTiming. void SetTickClockForTesting(const base::TickClock* clock); - void Trace(blink::Visitor*) override; + void Trace(Visitor*) override; private: LocalFrame* GetFrame() const; 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 index 8c16c5fc6ce..a5940c4d407 100644 --- a/chromium/third_party/blink/renderer/core/paint/paint_timing_detector.cc +++ b/chromium/third_party/blink/renderer/core/paint/paint_timing_detector.cc @@ -3,7 +3,8 @@ // found in the LICENSE file. #include "third_party/blink/renderer/core/paint/paint_timing_detector.h" -#include "third_party/blink/public/platform/web_input_event.h" +#include "third_party/blink/public/common/input/web_input_event.h" +#include "third_party/blink/public/platform/web_float_rect.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" @@ -21,13 +22,16 @@ #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/core/style/style_fetched_image.h" +#include "third_party/blink/renderer/core/svg/graphics/svg_image.h" #include "third_party/blink/renderer/core/timing/dom_window_performance.h" #include "third_party/blink/renderer/platform/geometry/int_rect.h" +#include "third_party/blink/renderer/platform/graphics/bitmap_image.h" #include "third_party/blink/renderer/platform/graphics/image.h" #include "third_party/blink/renderer/platform/graphics/paint/float_clip_rect.h" #include "third_party/blink/renderer/platform/graphics/paint/geometry_mapper.h" #include "third_party/blink/renderer/platform/graphics/paint/property_tree_state.h" #include "third_party/blink/renderer/platform/graphics/paint/scoped_paint_chunk_properties.h" +#include "third_party/blink/renderer/platform/graphics/static_bitmap_image.h" #include "third_party/blink/renderer/platform/wtf/cross_thread_functional.h" #include "third_party/blink/renderer/platform/wtf/functional.h" @@ -45,19 +49,22 @@ bool IsBackgroundImageContentful(const LayoutObject& object, const Image& image) { // Background images attached to <body> or <html> are likely for background // purpose, so we rule them out. - if (object.IsLayoutView() || object.IsBody() || object.IsDocumentElement()) { + if (IsA<LayoutView>(object) || object.IsBody() || + object.IsDocumentElement()) { return false; } // Generated images are excluded here, as they are likely to serve for // background purpose. - if (!image.IsBitmapImage() && !image.IsStaticBitmapImage() && - !image.IsSVGImage() && !image.IsPlaceholderImage()) + if (!IsA<BitmapImage>(image) && !IsA<StaticBitmapImage>(image) && + !IsA<SVGImage>(image) && !image.IsPlaceholderImage()) return false; return true; } } // namespace +bool IgnorePaintTimingScope::should_ignore_ = false; + PaintTimingDetector::PaintTimingDetector(LocalFrameView* frame_view) : frame_view_(frame_view), text_paint_timing_detector_( @@ -99,7 +106,11 @@ void PaintTimingDetector::NotifyBackgroundImagePaint( const Node* node, const Image* image, const StyleFetchedImage* style_image, - const PropertyTreeState& current_paint_chunk_properties) { + const PropertyTreeState& current_paint_chunk_properties, + const IntRect& image_border) { + if (IgnorePaintTimingScope::ShouldIgnore()) + return; + DCHECK(image); DCHECK(style_image->CachedImage()); if (!node) @@ -117,7 +128,7 @@ void PaintTimingDetector::NotifyBackgroundImagePaint( return; detector.GetImagePaintTimingDetector()->RecordImage( *object, image->Size(), *style_image->CachedImage(), - current_paint_chunk_properties, style_image); + current_paint_chunk_properties, style_image, &image_border); } // static @@ -126,6 +137,9 @@ void PaintTimingDetector::NotifyImagePaint( const IntSize& intrinsic_size, const ImageResourceContent* cached_image, const PropertyTreeState& current_paint_chunk_properties) { + if (IgnorePaintTimingScope::ShouldIgnore()) + return; + LocalFrameView* frame_view = object.GetFrameView(); if (!frame_view) return; @@ -136,7 +150,7 @@ void PaintTimingDetector::NotifyImagePaint( return; detector.GetImagePaintTimingDetector()->RecordImage( object, intrinsic_size, *cached_image, current_paint_chunk_properties, - nullptr); + nullptr, nullptr); } void PaintTimingDetector::NotifyImageFinished( @@ -162,8 +176,11 @@ void PaintTimingDetector::NotifyImageRemoved( } } -void PaintTimingDetector::StopRecordingLargestContentfulPaint() { - DCHECK(frame_view_); +void PaintTimingDetector::OnInputOrScroll() { + // If we have already stopped, then abort. + if (!is_recording_largest_contentful_paint_) + return; + // TextPaintTimingDetector is used for both Largest Contentful Paint and for // Element Timing. Therefore, here we only want to stop recording Largest // Contentful Paint. @@ -173,21 +190,29 @@ void PaintTimingDetector::StopRecordingLargestContentfulPaint() { if (image_paint_timing_detector_) image_paint_timing_detector_->StopRecordEntries(); largest_contentful_paint_calculator_ = nullptr; + + DCHECK_EQ(first_input_or_scroll_notified_timestamp_, base::TimeTicks()); + first_input_or_scroll_notified_timestamp_ = base::TimeTicks::Now(); + DidChangePerformanceTiming(); + is_recording_largest_contentful_paint_ = false; } void PaintTimingDetector::NotifyInputEvent(WebInputEvent::Type type) { + // A single keyup event should be ignored. It could be caused by user actions + // such as refreshing via Ctrl+R. if (type == WebInputEvent::kMouseMove || type == WebInputEvent::kMouseEnter || - type == WebInputEvent::kMouseLeave || + type == WebInputEvent::kMouseLeave || type == WebInputEvent::kKeyUp || WebInputEvent::IsPinchGestureEventType(type)) { return; } - StopRecordingLargestContentfulPaint(); + OnInputOrScroll(); } -void PaintTimingDetector::NotifyScroll(ScrollType scroll_type) { - if (scroll_type != kUserScroll && scroll_type != kCompositorScroll) +void PaintTimingDetector::NotifyScroll(mojom::blink::ScrollType scroll_type) { + if (scroll_type != mojom::blink::ScrollType::kUser && + scroll_type != mojom::blink::ScrollType::kCompositor) return; - StopRecordingLargestContentfulPaint(); + OnInputOrScroll(); } bool PaintTimingDetector::NeedToNotifyInputOrScroll() const { @@ -332,6 +357,9 @@ ScopedPaintTimingDetectorBlockPaintHook* void ScopedPaintTimingDetectorBlockPaintHook::EmplaceIfNeeded( const LayoutBoxModelObject& aggregator, const PropertyTreeState& property_tree_state) { + if (IgnorePaintTimingScope::ShouldIgnore()) + return; + // |reset_top_| is unset when |aggregator| is anonymous so that each // aggregation corresponds to an element. See crbug.com/988593. When set, // |top_| becomes |this|, and |top_| is restored to the previous value when @@ -402,7 +430,7 @@ void PaintTimingCallbackManagerImpl:: void PaintTimingCallbackManagerImpl::ReportPaintTime( std::unique_ptr<PaintTimingCallbackManager::CallbackQueue> frame_callbacks, - WebWidgetClient::SwapResult result, + WebSwapResult result, base::TimeTicks paint_time) { while (!frame_callbacks->empty()) { std::move(frame_callbacks->front()).Run(paint_time); diff --git a/chromium/third_party/blink/renderer/core/paint/paint_timing_detector.h b/chromium/third_party/blink/renderer/core/paint/paint_timing_detector.h index f27a0e04ce2..82391ba7022 100644 --- a/chromium/third_party/blink/renderer/core/paint/paint_timing_detector.h +++ b/chromium/third_party/blink/renderer/core/paint/paint_timing_detector.h @@ -5,8 +5,8 @@ #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/public/platform/web_input_event.h" -#include "third_party/blink/public/web/web_widget_client.h" +#include "third_party/blink/public/common/input/web_input_event.h" +#include "third_party/blink/public/web/web_swap_result.h" #include "third_party/blink/renderer/core/core_export.h" #include "third_party/blink/renderer/core/layout/layout_box_model_object.h" #include "third_party/blink/renderer/core/paint/paint_timing_visualizer.h" @@ -86,7 +86,7 @@ class PaintTimingCallbackManagerImpl final void ReportPaintTime( std::unique_ptr<std::queue< PaintTimingCallbackManager::LocalThreadCallback>> frame_callbacks, - WebWidgetClient::SwapResult, + WebSwapResult, base::TimeTicks paint_time); void Trace(Visitor* visitor) override; @@ -124,7 +124,8 @@ class CORE_EXPORT PaintTimingDetector const Node*, const Image*, const StyleFetchedImage*, - const PropertyTreeState& current_paint_chunk_properties); + const PropertyTreeState& current_paint_chunk_properties, + const IntRect& image_border); static void NotifyImagePaint( const LayoutObject&, const IntSize& intrinsic_size, @@ -138,7 +139,7 @@ class CORE_EXPORT PaintTimingDetector void NotifyPaintFinished(); void NotifyInputEvent(WebInputEvent::Type); bool NeedToNotifyInputOrScroll() const; - void NotifyScroll(ScrollType); + void NotifyScroll(mojom::blink::ScrollType); // The returned value indicates whether the candidates have changed. bool NotifyIfChangedLargestImagePaint(base::TimeTicks, uint64_t size); bool NotifyIfChangedLargestTextPaint(base::TimeTicks, uint64_t size); @@ -171,6 +172,9 @@ class CORE_EXPORT PaintTimingDetector uint64_t LargestImagePaintSize() const { return largest_image_paint_size_; } base::TimeTicks LargestTextPaint() const { return largest_text_paint_time_; } uint64_t LargestTextPaintSize() const { return largest_text_paint_size_; } + base::TimeTicks FirstInputOrScrollNotifiedTimestamp() const { + return first_input_or_scroll_notified_timestamp_; + } void UpdateLargestContentfulPaintCandidate(); @@ -178,7 +182,8 @@ class CORE_EXPORT PaintTimingDetector void Trace(Visitor* visitor); private: - void StopRecordingLargestContentfulPaint(); + // Method called to stop recording the Largest Contentful Paint. + void OnInputOrScroll(); bool HasLargestImagePaintChanged(base::TimeTicks, uint64_t size) const; bool HasLargestTextPaintChanged(base::TimeTicks, uint64_t size) const; Member<LocalFrameView> frame_view_; @@ -188,7 +193,14 @@ class CORE_EXPORT PaintTimingDetector // image paint is found. Member<ImagePaintTimingDetector> image_paint_timing_detector_; + // This member lives for as long as the largest contentful paint is being + // computed. However, it is initialized lazily, so it may be nullptr because + // it has not yet been initialized or because we have stopped computing LCP. Member<LargestContentfulPaintCalculator> largest_contentful_paint_calculator_; + // Time at which the first input or scroll is notified to PaintTimingDetector, + // hence causing LCP to stop being recorded. This is the same time at which + // |largest_contentful_paint_calculator_| is set to nullptr. + base::TimeTicks first_input_or_scroll_notified_timestamp_; Member<PaintTimingCallbackManagerImpl> callback_manager_; @@ -200,6 +212,7 @@ class CORE_EXPORT PaintTimingDetector // Largest text information. base::TimeTicks largest_text_paint_time_; uint64_t largest_text_paint_size_ = 0; + bool is_recording_largest_contentful_paint_ = true; }; // Largest Text Paint and Text Element Timing aggregate text nodes by these @@ -251,7 +264,7 @@ class ScopedPaintTimingDetectorBlockPaintHook { const LayoutBoxModelObject& aggregator_; const PropertyTreeState& property_tree_state_; - Member<TextPaintTimingDetector> detector_; + TextPaintTimingDetector* detector_; IntRect aggregated_visual_rect_; }; base::Optional<Data> data_; @@ -260,9 +273,27 @@ class ScopedPaintTimingDetectorBlockPaintHook { DISALLOW_COPY_AND_ASSIGN(ScopedPaintTimingDetectorBlockPaintHook); }; +// Creates a scope to ignore paint timing, e.g. when we are painting contents +// under opacity:0. +class IgnorePaintTimingScope { + STACK_ALLOCATED(); + + public: + IgnorePaintTimingScope() : auto_reset_(&should_ignore_, true) {} + ~IgnorePaintTimingScope() = default; + + static bool ShouldIgnore() { return should_ignore_; } + + private: + base::AutoReset<bool> auto_reset_; + static bool should_ignore_; +}; + // static inline void PaintTimingDetector::NotifyTextPaint( const IntRect& text_visual_rect) { + if (IgnorePaintTimingScope::ShouldIgnore()) + return; ScopedPaintTimingDetectorBlockPaintHook::AggregateTextPaint(text_visual_rect); } 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 91577fd6474..4fffe2ebb46 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 @@ -16,6 +16,9 @@ #include "third_party/blink/renderer/core/layout/layout_multi_column_spanner_placeholder.h" #include "third_party/blink/renderer/core/layout/layout_shift_tracker.h" #include "third_party/blink/renderer/core/layout/layout_view.h" +#include "third_party/blink/renderer/core/layout/ng/inline/ng_fragment_item.h" +#include "third_party/blink/renderer/core/layout/ng/ng_fragment_child_iterator.h" +#include "third_party/blink/renderer/core/layout/ng/ng_physical_box_fragment.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/compositing/composited_layer_mapping.h" @@ -28,6 +31,42 @@ namespace blink { +namespace { + +// Locate or/and set up the current FragmentData object. This may involve +// creating it, or resetting an existing one. If |allow_reset| is set, we're +// allowed to clear old FragmentData objects. +NGPrePaintInfo SetupFragmentData(const NGFragmentChildIterator& iterator, + bool allow_reset) { + DCHECK(iterator->GetLayoutObject()); + const LayoutObject& object = *iterator->GetLayoutObject(); + FragmentData* fragment_data = &object.GetMutableForPainting().FirstFragment(); + // TODO(crbug.com/1043787): Add support for block fragmentation. Furthermore, + // what's here is mostly gross, and we need to come up with something + // better. The way FragmentData works (and is stored) vs. the way + // NGPhysicalFragment works is less than ideal. + fragment_data->ClearNextFragment(); + if (const NGFragmentItem* fragment_item = iterator->FragmentItem()) { + // We're in an inline formatting context. + if (fragment_item->IsFirstForNode()) { + // This is the first fragment generated for the node (i.e. we're on the + // first line and first fragmentainer (column) that this node occurs + // in). Now is our chance to reset everything (the number or size of + // fragments may have changed since last time). All the other fragments + // will be visited in due course. + if (allow_reset && !object.IsBox()) { + // For text and non-atomic inlines we now reset the visual rect. The + // visual rect will be set and expanded, as we visit each individual + // fragment. + fragment_data->SetVisualRect(IntRect()); + } + } + } + return NGPrePaintInfo(iterator, *fragment_data); +} + +} // anonymous namespace + void PrePaintTreeWalk::WalkTree(LocalFrameView& root_frame_view) { if (root_frame_view.ShouldThrottleRendering()) { // Skip the throttled frame. Will update it when it becomes unthrottled. @@ -135,7 +174,7 @@ void PrePaintTreeWalk::Walk(LocalFrameView& frame_view) { } #endif - Walk(*view); + Walk(*view, /* iterator */ nullptr); #if DCHECK_IS_ON() view->AssertSubtreeClearedPaintInvalidationFlags(); #endif @@ -167,7 +206,7 @@ bool HasBlockingTouchEventHandler(const LocalFrame& frame, } bool HasBlockingTouchEventHandler(const LayoutObject& object) { - if (object.IsLayoutView()) { + if (IsA<LayoutView>(object)) { auto* frame = object.GetFrame(); if (HasBlockingTouchEventHandler(*frame, *frame->DomWindow())) return true; @@ -312,10 +351,23 @@ void PrePaintTreeWalk::CheckTreeBuilderContextState( } void PrePaintTreeWalk::WalkInternal(const LayoutObject& object, + const NGFragmentChildIterator* iterator, PrePaintTreeWalkContext& context) { PaintInvalidatorContext& paint_invalidator_context = context.paint_invalidator_context; + base::Optional<NGPrePaintInfo> pre_paint_info_storage; + NGPrePaintInfo* pre_paint_info = nullptr; + if (iterator) { + bool allow_reset = context.tree_builder_context.has_value() +#if DCHECK_IS_ON() + && context.tree_builder_context->is_actually_needed +#endif + ; + pre_paint_info_storage.emplace(SetupFragmentData(*iterator, allow_reset)); + pre_paint_info = &pre_paint_info_storage.value(); + } + // This must happen before updatePropertiesForSelf, because the latter reads // some of the state computed here. UpdateAuxiliaryObjectProperties(object, context); @@ -324,7 +376,9 @@ void PrePaintTreeWalk::WalkInternal(const LayoutObject& object, PaintPropertyChangeType property_changed = PaintPropertyChangeType::kUnchanged; if (context.tree_builder_context) { - property_tree_builder.emplace(object, *context.tree_builder_context); + property_tree_builder.emplace(object, pre_paint_info, + *context.tree_builder_context); + property_changed = std::max(property_changed, property_tree_builder->UpdateForSelf()); @@ -341,7 +395,8 @@ void PrePaintTreeWalk::WalkInternal(const LayoutObject& object, UpdateEffectiveAllowedTouchAction(object, context); if (paint_invalidator_.InvalidatePaint( - object, base::OptionalOrNullptr(context.tree_builder_context), + object, pre_paint_info, + base::OptionalOrNullptr(context.tree_builder_context), paint_invalidator_context)) needs_invalidate_chrome_client_ = true; @@ -389,6 +444,8 @@ void PrePaintTreeWalk::WalkInternal(const LayoutObject& object, if (context.clip_changed && object.HasLayer()) ToLayoutBoxModelObject(object).Layer()->SetNeedsRepaint(); + // TODO(crbug.com/1058792): Allow multiple fragments for composited elements + // (passing |iterator| here is probably part of the solution). CompositingLayerPropertyUpdater::Update(object); } @@ -403,7 +460,140 @@ LocalFrameView* FindWebViewPluginContentFrameView( return nullptr; } -void PrePaintTreeWalk::Walk(const LayoutObject& object) { +void PrePaintTreeWalk::WalkNGChildren(const LayoutObject* parent, + NGFragmentChildIterator* iterator) { + for (; !iterator->IsAtEnd(); iterator->Advance()) { + const LayoutObject* object = (*iterator)->GetLayoutObject(); + if (const auto* fragment_item = (*iterator)->FragmentItem()) { + // Line boxes are not interesting. They have no paint effects. Descend + // directly into children. + bool descend_directly = fragment_item->Type() == NGFragmentItem::kLine; + if (!descend_directly && fragment_item->IsInlineBox() && + !fragment_item->BoxFragment()) { + // Likewise for culled inlines. + descend_directly = true; + object->GetMutableForPainting().ClearPaintFlags(); + } + if (descend_directly) { + WalkChildren(/* parent */ nullptr, iterator); + continue; + } + } + DCHECK(object); + Walk(*object, iterator); + } +} + +void PrePaintTreeWalk::WalkLegacyChildren(const LayoutObject& object) { + if (const LayoutBox* layout_box = ToLayoutBoxOrNull(&object)) { + if (layout_box->CanTraversePhysicalFragments()) { + // Enter NG child fragment traversal. We'll stay in this mode for all + // descendants that support fragment traversal. We'll re-enter + // LayoutObject traversal for descendants that don't support it. This only + // works correctly if we're not block-fragmented, though, so DCHECK for + // that. + DCHECK_EQ(layout_box->PhysicalFragmentCount(), 1u); + const NGPhysicalBoxFragment& fragment = + To<NGPhysicalBoxFragment>(*layout_box->GetPhysicalFragment(0)); + NGFragmentChildIterator child_iterator(fragment); + WalkNGChildren(&object, &child_iterator); + return; + } + } + + for (const LayoutObject* child = object.SlowFirstChild(); child; + child = child->NextSibling()) { + if (child->IsLayoutMultiColumnSpannerPlaceholder()) { + child->GetMutableForPainting().ClearPaintFlags(); + continue; + } + Walk(*child, /* iterator */ nullptr); + } + + if (!RuntimeEnabledFeatures::LayoutNGFragmentTraversalEnabled()) + return; + + const LayoutBlock* block = DynamicTo<LayoutBlock>(&object); + if (!block) + return; + const auto* positioned_objects = block->PositionedObjects(); + if (!positioned_objects) + return; + + // If we have performed NG fragment traversal in any part of the subtree, we + // may have missed certain out-of-flow positioned objects. LayoutNG fragments + // are always children of their containing block, while the structure of the + // LayoutObject tree corresponds more closely to that of the DOM tree. + // + // Example: if we assume that flexbox isn't natively supported in LayoutNG: + // + // <div id="flex" style="display:flex; position:relative;"> + // <div id="flexitem"> + // <div id="abs" style="position:absolute;"></div> + // <div id="regular"></div> + // + // If we let |object| be #flex, it will be handled by legacy LayoutObject + // traversal, while #flexitem, on the other hand, can traverse its NG child + // fragments. However, #regular will be the only child fragment of #flexitem, + // since the containing block for #abs is #flex. So we'd miss it, unless we + // walk it now. + for (const LayoutBox* box : *positioned_objects) { + // It's important that objects that have already been walked be left alone. + // Otherwise, we might calculate the wrong offsets (and overwrite the + // correct ones) in case of out-of-flow positioned objects whose containing + // block is a relatively positioned non-atomic inline (such objects will + // already have been properly walked, since we don't switch engines within + // an inline formatting context). Put differently, the code here will only + // do the right thing if |object| is truly the containing block of the + // positioned objects in its list (which isn't the case if the containing + // block is a non-atomic inline). + if (!ObjectRequiresPrePaint(*box) && + !ObjectRequiresTreeBuilderContext(*box)) + continue; + DCHECK_EQ(box->Container(), &object); + Walk(*box, /* iterator */ nullptr); + } +} + +void PrePaintTreeWalk::WalkChildren(const LayoutObject* object, + const NGFragmentChildIterator* iterator) { + DCHECK(iterator || object); + + if (!iterator) { + // We're not doing LayoutNG fragment traversal of this object. + WalkLegacyChildren(*object); + return; + } + + // If we are performing LayoutNG fragment traversal, but this object doesn't + // support that, we need to switch back to legacy LayoutObject traversal for + // its children. We're then also assuming that we're either not + // block-fragmenting, or that this is monolithic content. We may re-enter + // LayoutNG fragment traversal if we get to a descendant that supports that. + if (object && !object->CanTraversePhysicalFragments()) { + DCHECK( + !object->FlowThreadContainingBlock() || + (object->IsBox() && ToLayoutBox(object)->GetPaginationBreakability() == + LayoutBox::kForbidBreaks)); + WalkLegacyChildren(*object); + return; + } + + // Traverse child NG fragments. + NGFragmentChildIterator child_iterator(iterator->Descend()); + WalkNGChildren(object, &child_iterator); +} + +void PrePaintTreeWalk::Walk(const LayoutObject& object, + const NGFragmentChildIterator* iterator) { + const NGPhysicalBoxFragment* physical_fragment = nullptr; + bool is_last_fragment = true; + if (iterator) { + physical_fragment = (*iterator)->BoxFragment(); + if (const auto* fragment_item = (*iterator)->FragmentItem()) + is_last_fragment = fragment_item->IsLastForNode(); + } + // 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. @@ -447,8 +637,10 @@ void PrePaintTreeWalk::Walk(const LayoutObject& object) { context().tree_builder_context->clip_changed = false; } - WalkInternal(object, context()); - object.NotifyDisplayLockDidPrePaint(DisplayLockLifecycleTarget::kSelf); + WalkInternal(object, iterator, context()); + + if (is_last_fragment) + object.NotifyDisplayLockDidPrePaint(DisplayLockLifecycleTarget::kSelf); bool child_walk_blocked = object.PrePaintBlockedByDisplayLock( DisplayLockLifecycleTarget::kChildren); @@ -469,14 +661,7 @@ void PrePaintTreeWalk::Walk(const LayoutObject& object) { } if (!child_walk_blocked) { - for (const LayoutObject* child = object.SlowFirstChild(); child; - child = child->NextSibling()) { - if (child->IsLayoutMultiColumnSpannerPlaceholder()) { - child->GetMutableForPainting().ClearPaintFlags(); - continue; - } - Walk(*child); - } + WalkChildren(&object, iterator); if (object.IsLayoutEmbeddedContent()) { const LayoutEmbeddedContent& layout_embedded_content = @@ -505,8 +690,8 @@ void PrePaintTreeWalk::Walk(const LayoutObject& object) { object.NotifyDisplayLockDidPrePaint(DisplayLockLifecycleTarget::kChildren); } - - object.GetMutableForPainting().ClearPaintFlags(); + if (is_last_fragment) + object.GetMutableForPainting().ClearPaintFlags(); context_storage_.pop_back(); } diff --git a/chromium/third_party/blink/renderer/core/paint/pre_paint_tree_walk.h b/chromium/third_party/blink/renderer/core/paint/pre_paint_tree_walk.h index fa95f4bd2fe..5ded2b4f51c 100644 --- a/chromium/third_party/blink/renderer/core/paint/pre_paint_tree_walk.h +++ b/chromium/third_party/blink/renderer/core/paint/pre_paint_tree_walk.h @@ -14,6 +14,7 @@ namespace blink { class LayoutObject; class LocalFrameView; +class NGFragmentChildIterator; // This class walks the whole layout tree, beginning from the root // LocalFrameView, across frame boundaries. Helper classes are called for each @@ -101,8 +102,13 @@ class CORE_EXPORT PrePaintTreeWalk { // very big stack frames. Splitting the heavy lifting to a separate function // makes sure the stack frame is freed prior to making a recursive call. // See https://crbug.com/781301 . - NOINLINE void WalkInternal(const LayoutObject&, PrePaintTreeWalkContext&); - void Walk(const LayoutObject&); + NOINLINE void WalkInternal(const LayoutObject&, + const NGFragmentChildIterator*, + PrePaintTreeWalkContext&); + void WalkNGChildren(const LayoutObject* parent, NGFragmentChildIterator*); + void WalkLegacyChildren(const LayoutObject&); + void WalkChildren(const LayoutObject*, const NGFragmentChildIterator*); + void Walk(const LayoutObject&, const NGFragmentChildIterator*); bool NeedsTreeBuilderContextUpdate(const LocalFrameView&, const PrePaintTreeWalkContext&); 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 b49316436fa..3665e2c0f48 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 @@ -164,7 +164,8 @@ TEST_P(PrePaintTreeWalkTest, ClearSubsequenceCachingClipChange) { EXPECT_FALSE(child_paint_layer->NeedsPaintPhaseFloat()); parent->setAttribute(html_names::kClassAttr, "clip"); - GetDocument().View()->UpdateAllLifecyclePhasesExceptPaint(); + GetDocument().View()->UpdateAllLifecyclePhasesExceptPaint( + DocumentUpdateReason::kTest); EXPECT_TRUE(child_paint_layer->SelfNeedsRepaint()); } @@ -190,7 +191,8 @@ TEST_P(PrePaintTreeWalkTest, ClearSubsequenceCachingClipChange2DTransform) { EXPECT_FALSE(child_paint_layer->NeedsPaintPhaseFloat()); parent->setAttribute(html_names::kClassAttr, "clip"); - GetDocument().View()->UpdateAllLifecyclePhasesExceptPaint(); + GetDocument().View()->UpdateAllLifecyclePhasesExceptPaint( + DocumentUpdateReason::kTest); EXPECT_TRUE(child_paint_layer->SelfNeedsRepaint()); } @@ -219,7 +221,8 @@ 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(html_names::kClassAttr, "clip"); - GetDocument().View()->UpdateAllLifecyclePhasesExceptPaint(); + GetDocument().View()->UpdateAllLifecyclePhasesExceptPaint( + DocumentUpdateReason::kTest); EXPECT_TRUE(child_paint_layer->SelfNeedsRepaint()); } @@ -248,7 +251,8 @@ 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(html_names::kClassAttr, "clip"); - GetDocument().View()->UpdateAllLifecyclePhasesExceptPaint(); + GetDocument().View()->UpdateAllLifecyclePhasesExceptPaint( + DocumentUpdateReason::kTest); EXPECT_TRUE(child_paint_layer->SelfNeedsRepaint()); } @@ -272,7 +276,8 @@ TEST_P(PrePaintTreeWalkTest, ClipChangeRepaintsDescendants) { )HTML"); GetDocument().getElementById("parent")->removeAttribute("style"); - GetDocument().View()->UpdateAllLifecyclePhasesExceptPaint(); + GetDocument().View()->UpdateAllLifecyclePhasesExceptPaint( + DocumentUpdateReason::kTest); auto* greatgrandchild = GetLayoutObjectByElementId("greatgrandchild"); auto* paint_layer = ToLayoutBoxModelObject(greatgrandchild)->Layer(); @@ -320,7 +325,8 @@ TEST_P(PrePaintTreeWalkTest, ClipChangeHasRadius) { auto* target = GetDocument().getElementById("target"); auto* target_object = ToLayoutBoxModelObject(target->GetLayoutObject()); target->setAttribute(html_names::kStyleAttr, "border-radius: 5px"); - GetDocument().View()->UpdateAllLifecyclePhasesExceptPaint(); + GetDocument().View()->UpdateAllLifecyclePhasesExceptPaint( + DocumentUpdateReason::kTest); EXPECT_TRUE(target_object->Layer()->SelfNeedsRepaint()); // And should not trigger any assert failure. UpdateAllLifecyclePhasesForTest(); @@ -413,7 +419,8 @@ TEST_P(PrePaintTreeWalkTest, EffectiveTouchActionStyleUpdate) { GetDocument() .getElementById("touchaction") ->setAttribute(html_names::kClassAttr, "touchaction"); - GetDocument().View()->UpdateLifecycleToLayoutClean(); + GetDocument().View()->UpdateLifecycleToLayoutClean( + DocumentUpdateReason::kTest); EXPECT_FALSE(ancestor.EffectiveAllowedTouchActionChanged()); EXPECT_TRUE(touchaction.EffectiveAllowedTouchActionChanged()); EXPECT_FALSE(descendant.EffectiveAllowedTouchActionChanged()); 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 133aa11e6d4..47048c35bf9 100644 --- a/chromium/third_party/blink/renderer/core/paint/replaced_painter.cc +++ b/chromium/third_party/blink/renderer/core/paint/replaced_painter.cc @@ -141,11 +141,11 @@ void ReplacedPainter::Paint(const PaintInfo& paint_info) { } if (local_paint_info.phase != PaintPhase::kForeground && - local_paint_info.phase != PaintPhase::kSelection && + local_paint_info.phase != PaintPhase::kSelectionDragImage && !layout_replaced_.CanHaveChildren()) return; - if (local_paint_info.phase == PaintPhase::kSelection && + if (local_paint_info.phase == PaintPhase::kSelectionDragImage && !layout_replaced_.IsSelected()) return; @@ -200,7 +200,7 @@ bool ReplacedPainter::ShouldPaint(const ScopedPaintState& paint_state) const { if (paint_info.phase != PaintPhase::kForeground && paint_info.phase != PaintPhase::kForcedColorsModeBackplate && !ShouldPaintSelfOutline(paint_info.phase) && - paint_info.phase != PaintPhase::kSelection && + paint_info.phase != PaintPhase::kSelectionDragImage && paint_info.phase != PaintPhase::kMask && !ShouldPaintSelfBlockBackground(paint_info.phase)) return false; 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 759d8bd72f7..c7ef1d33d18 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 @@ -5,6 +5,7 @@ #include "third_party/blink/renderer/core/paint/scoped_paint_state.h" #include "third_party/blink/renderer/core/layout/layout_replaced.h" +#include "third_party/blink/renderer/core/layout/layout_view.h" #include "third_party/blink/renderer/core/paint/box_model_object_painter.h" #include "third_party/blink/renderer/core/paint/paint_layer.h" @@ -74,7 +75,7 @@ void ScopedBoxContentsPaintState::AdjustForBoxContents(const LayoutBox& box) { // 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()) + if (IsA<LayoutView>(box) && input_paint_info_.GetCullRect().IsInfinite()) return; adjusted_paint_info_.emplace(input_paint_info_); 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 158e6727141..97bf34b9272 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 @@ -28,9 +28,10 @@ class ScopedPaintState { STACK_ALLOCATED(); public: - ScopedPaintState(const LayoutObject& object, const PaintInfo& paint_info) - : fragment_to_paint_(paint_info.FragmentToPaint(object)), - input_paint_info_(paint_info) { + ScopedPaintState(const LayoutObject& object, + const PaintInfo& paint_info, + const FragmentData* fragment_data) + : fragment_to_paint_(fragment_data), input_paint_info_(paint_info) { if (!fragment_to_paint_) { // The object has nothing to paint in the current fragment. // TODO(wangxianzhu): Use DCHECK(fragment_to_paint_) in PaintOffset() @@ -55,9 +56,16 @@ class ScopedPaintState { } } + ScopedPaintState(const LayoutObject& object, const PaintInfo& paint_info) + : ScopedPaintState(object, + paint_info, + paint_info.FragmentToPaint(object)) {} + ScopedPaintState(const NGPhysicalFragment& fragment, const PaintInfo& paint_info) - : ScopedPaintState(*fragment.GetLayoutObject(), paint_info) {} + : ScopedPaintState(*fragment.GetLayoutObject(), + paint_info, + paint_info.FragmentToPaint(fragment)) {} ~ScopedPaintState() { if (paint_offset_translation_as_drawing_) 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 e293f0caa1d..cf1ec8de0e0 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 @@ -25,47 +25,59 @@ #include "third_party/blink/renderer/core/paint/scoped_svg_paint_state.h" #include "third_party/blink/renderer/core/layout/svg/layout_svg_resource_filter.h" -#include "third_party/blink/renderer/core/layout/svg/layout_svg_resource_masker.h" -#include "third_party/blink/renderer/core/layout/svg/svg_layout_support.h" #include "third_party/blink/renderer/core/layout/svg/svg_resources.h" #include "third_party/blink/renderer/core/layout/svg/svg_resources_cache.h" -#include "third_party/blink/renderer/core/paint/svg_mask_painter.h" +#include "third_party/blink/renderer/platform/graphics/paint/drawing_recorder.h" namespace blink { -ScopedSVGPaintState::~ScopedSVGPaintState() { - if (filter_) { - DCHECK(SVGResourcesCache::CachedResourcesForLayoutObject(object_)); - DCHECK( - SVGResourcesCache::CachedResourcesForLayoutObject(object_)->Filter() == - filter_); - DCHECK(filter_recording_context_); - SVGFilterPainter(*filter_).FinishEffect(object_, - *filter_recording_context_); - - // Reset the paint info after the filter effect has been completed. - filter_paint_info_ = nullptr; - } +static void PaintFilteredContent(GraphicsContext& context, + const LayoutObject& object, + const DisplayItemClient& display_item_client, + FilterData* filter_data) { + if (DrawingRecorder::UseCachedDrawingIfPossible(context, display_item_client, + DisplayItem::kSVGFilter)) + return; - if (masker_) { + DrawingRecorder recorder(context, display_item_client, + DisplayItem::kSVGFilter); + sk_sp<PaintFilter> image_filter = filter_data->CreateFilter(); + context.Save(); + + // Clip drawing of filtered image to the minimum required paint rect. + const FloatRect object_bounds = object.StrokeBoundingBox(); + const FloatRect paint_rect = filter_data->MapRect(object_bounds); + context.ClipRect(paint_rect); + + // Use the union of the pre-image and the post-image as the layer bounds. + const FloatRect layer_bounds = UnionRect(object_bounds, paint_rect); + context.BeginLayer(1, SkBlendMode::kSrcOver, &layer_bounds, kColorFilterNone, + std::move(image_filter)); + context.EndLayer(); + context.Restore(); +} + +ScopedSVGPaintState::~ScopedSVGPaintState() { + if (filter_data_ && filter_data_->UpdateStateOnFinish()) { DCHECK(SVGResourcesCache::CachedResourcesForLayoutObject(object_)); DCHECK( - SVGResourcesCache::CachedResourcesForLayoutObject(object_)->Masker() == - masker_); - SVGMaskPainter(*masker_).FinishEffect(object_, GetPaintInfo().context); + SVGResourcesCache::CachedResourcesForLayoutObject(object_)->Filter()); + if (filter_recording_context_) { + filter_data_->UpdateContent( + filter_recording_context_->GetPaintRecord(paint_info_)); + filter_recording_context_ = nullptr; + } + PaintFilteredContent(paint_info_.context, object_, display_item_client_, + filter_data_); + filter_data_ = nullptr; } } -bool ScopedSVGPaintState::ApplyClipMaskAndFilterIfNecessary() { +bool ScopedSVGPaintState::ApplyEffects() { #if DCHECK_IS_ON() DCHECK(!apply_clip_mask_and_filter_if_necessary_called_); apply_clip_mask_and_filter_if_necessary_called_ = true; #endif - // In CAP we should early exit once the paint property state has been - // applied, because all meta (non-drawing) display items are ignored in - // CAP. However we can't simply omit them because there are still - // non-composited painting (e.g. SVG filters in particular) that rely on - // these meta display items. ApplyPaintPropertyState(); // When rendering clip paths as masks, only geometric operations should be @@ -93,8 +105,7 @@ bool ScopedSVGPaintState::ApplyClipMaskAndFilterIfNecessary() { SVGResources* resources = SVGResourcesCache::CachedResourcesForLayoutObject(object_); - if (!ApplyMaskIfNecessary(resources)) - return false; + ApplyMaskIfNecessary(resources); if (is_svg_root_or_foreign_object) { // PaintLayerPainter takes care of filter. @@ -102,7 +113,6 @@ bool ScopedSVGPaintState::ApplyClipMaskAndFilterIfNecessary() { } else if (!ApplyFilterIfNecessary(resources)) { return false; } - return true; } @@ -129,25 +139,20 @@ void ScopedSVGPaintState::ApplyPaintPropertyState() { else if (const auto* clip_path_clip = properties->ClipPathClip()) state.SetClip(*clip_path_clip); scoped_paint_chunk_properties_.emplace( - paint_controller, state, object_, + paint_controller, state, display_item_client_, DisplayItem::PaintPhaseToSVGEffectType(GetPaintInfo().phase)); } void ScopedSVGPaintState::ApplyClipIfNecessary() { if (object_.StyleRef().ClipPath()) { clip_path_clipper_.emplace(GetPaintInfo().context, object_, - PhysicalOffset()); + display_item_client_, PhysicalOffset()); } } -bool ScopedSVGPaintState::ApplyMaskIfNecessary(SVGResources* resources) { - if (LayoutSVGResourceMasker* masker = - resources ? resources->Masker() : nullptr) { - if (!SVGMaskPainter(*masker).PrepareEffect(object_, GetPaintInfo().context)) - return false; - masker_ = masker; - } - return true; +void ScopedSVGPaintState::ApplyMaskIfNecessary(SVGResources* resources) { + if (resources && resources->Masker()) + mask_painter_.emplace(paint_info_.context, object_, display_item_client_); } static bool HasReferenceFilterOnly(const ComputedStyle& style) { @@ -162,27 +167,20 @@ static bool HasReferenceFilterOnly(const ComputedStyle& style) { bool ScopedSVGPaintState::ApplyFilterIfNecessary(SVGResources* resources) { if (!resources) return !HasReferenceFilterOnly(object_.StyleRef()); - LayoutSVGResourceFilter* filter = resources->Filter(); if (!filter) return true; - filter_recording_context_ = - std::make_unique<SVGFilterRecordingContext>(GetPaintInfo().context); - filter_ = filter; - GraphicsContext* filter_context = SVGFilterPainter(*filter).PrepareEffect( - object_, *filter_recording_context_); - if (!filter_context) + filter->ClearInvalidationMask(); + filter_data_ = SVGFilterPainter(*filter).PrepareEffect(object_); + // If we have no filter data (== the filter was invalid) or if we + // don't need to update the source graphics, we can short-circuit + // here. + if (!filter_data_ || !filter_data_->ContentNeedsUpdate()) return false; - // Because the filter needs to cache its contents we replace the context // during filtering with the filter's context. - filter_paint_info_ = - std::make_unique<PaintInfo>(*filter_context, paint_info_); - - // 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_->ApplyInfiniteCullRect(); + filter_recording_context_ = + std::make_unique<SVGFilterRecordingContext>(paint_info_); return true; } diff --git a/chromium/third_party/blink/renderer/core/paint/scoped_svg_paint_state.h b/chromium/third_party/blink/renderer/core/paint/scoped_svg_paint_state.h index 479b12c09f7..8d467e7b4a2 100644 --- a/chromium/third_party/blink/renderer/core/paint/scoped_svg_paint_state.h +++ b/chromium/third_party/blink/renderer/core/paint/scoped_svg_paint_state.h @@ -30,14 +30,14 @@ #include "third_party/blink/renderer/core/paint/object_paint_properties.h" #include "third_party/blink/renderer/core/paint/paint_info.h" #include "third_party/blink/renderer/core/paint/svg_filter_painter.h" +#include "third_party/blink/renderer/core/paint/svg_mask_painter.h" #include "third_party/blink/renderer/platform/graphics/paint/scoped_paint_chunk_properties.h" #include "third_party/blink/renderer/platform/transforms/affine_transform.h" namespace blink { +class FilterData; class LayoutObject; -class LayoutSVGResourceFilter; -class LayoutSVGResourceMasker; class SVGResources; // Hooks up the correct paint property transform node. @@ -81,28 +81,31 @@ class ScopedSVGPaintState { public: ScopedSVGPaintState(const LayoutObject& object, const PaintInfo& paint_info) + : ScopedSVGPaintState(object, paint_info, object) {} + + ScopedSVGPaintState(const LayoutObject& object, + const PaintInfo& paint_info, + const DisplayItemClient& display_item_client) : object_(object), paint_info_(paint_info), - filter_(nullptr), - masker_(nullptr) {} + display_item_client_(display_item_client), + filter_data_(nullptr) {} ~ScopedSVGPaintState(); - PaintInfo& GetPaintInfo() { - return filter_paint_info_ ? *filter_paint_info_ : paint_info_; + const PaintInfo& GetPaintInfo() const { + return filter_recording_context_ ? filter_recording_context_->GetPaintInfo() + : paint_info_; } // Return true if these operations aren't necessary or if they are // successfully applied. - bool ApplyClipMaskAndFilterIfNecessary(); + bool ApplyEffects(); private: void ApplyPaintPropertyState(); void ApplyClipIfNecessary(); - - // Return true if no masking is necessary or if the mask is successfully - // applied. - bool ApplyMaskIfNecessary(SVGResources*); + void ApplyMaskIfNecessary(SVGResources*); // Return true if no filtering is necessary or if the filter is successfully // applied. @@ -110,12 +113,12 @@ class ScopedSVGPaintState { const LayoutObject& object_; PaintInfo paint_info_; - std::unique_ptr<PaintInfo> filter_paint_info_; - LayoutSVGResourceFilter* filter_; - LayoutSVGResourceMasker* masker_; + const DisplayItemClient& display_item_client_; + FilterData* filter_data_; base::Optional<ClipPathClipper> clip_path_clipper_; std::unique_ptr<SVGFilterRecordingContext> filter_recording_context_; base::Optional<ScopedPaintChunkProperties> scoped_paint_chunk_properties_; + base::Optional<SVGMaskPainter> mask_painter_; #if DCHECK_IS_ON() bool apply_clip_mask_and_filter_if_necessary_called_ = false; #endif 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 ab01fef7a7c..df26ad02982 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 @@ -19,7 +19,6 @@ #include "third_party/blink/renderer/platform/graphics/graphics_layer.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/graphics/paint/scroll_hit_test_display_item.h" #include "third_party/blink/renderer/platform/graphics/paint/scrollbar_display_item.h" namespace blink { @@ -30,10 +29,7 @@ void ScrollableAreaPainter::PaintResizer(GraphicsContext& context, if (!GetScrollableArea().GetLayoutBox()->StyleRef().HasResize()) return; - IntRect abs_rect = GetScrollableArea().ResizerCornerRect( - GetScrollableArea().GetLayoutBox()->PixelSnappedBorderBoxRect( - GetScrollableArea().Layer()->SubpixelAccumulation()), - kResizerForPointer); + IntRect abs_rect = GetScrollableArea().ResizerCornerRect(kResizerForPointer); if (abs_rect.IsEmpty()) return; abs_rect.MoveBy(paint_offset); @@ -73,18 +69,15 @@ void ScrollableAreaPainter::PaintResizer(GraphicsContext& context, void ScrollableAreaPainter::RecordResizerScrollHitTestData( GraphicsContext& context, - const PhysicalOffset& paint_offset, - const DisplayItemClient& client) { + const PhysicalOffset& paint_offset) { if (!GetScrollableArea().GetLayoutBox()->CanResize()) return; - IntRect touch_rect = scrollable_area_->ResizerCornerRect( - GetScrollableArea().GetLayoutBox()->PixelSnappedBorderBoxRect( - paint_offset), - kResizerForTouch); + IntRect touch_rect = scrollable_area_->ResizerCornerRect(kResizerForTouch); touch_rect.MoveBy(RoundedIntPoint(paint_offset)); - ScrollHitTestDisplayItem::Record( - context, client, DisplayItem::kResizerScrollHitTest, nullptr, touch_rect); + context.GetPaintController().RecordScrollHitTestData( + DisplayItemClientForCorner(), DisplayItem::kResizerScrollHitTest, nullptr, + touch_rect); } void ScrollableAreaPainter::DrawPlatformResizerImage( @@ -201,18 +194,16 @@ void ScrollableAreaPainter::PaintScrollbar(GraphicsContext& context, Scrollbar& scrollbar, const CullRect& cull_rect, const IntPoint& paint_offset) { - // We create PaintOffsetTranslation for scrollable area, so the rounded - // paint offset is always zero. // TODO(crbug.com/1020913): We should not round paint_offset but should // consider subpixel accumulation when painting scrollbars. - DCHECK_EQ(paint_offset, IntPoint()); IntRect rect = scrollbar.FrameRect(); + rect.MoveBy(paint_offset); if (!cull_rect.Intersects(rect)) return; if (!RuntimeEnabledFeatures::CompositeAfterPaintEnabled() || scrollbar.IsCustomScrollbar()) { - scrollbar.Paint(context); + scrollbar.Paint(context, paint_offset); return; } diff --git a/chromium/third_party/blink/renderer/core/paint/scrollable_area_painter.h b/chromium/third_party/blink/renderer/core/paint/scrollable_area_painter.h index 54d27f793e8..9b702249b5a 100644 --- a/chromium/third_party/blink/renderer/core/paint/scrollable_area_painter.h +++ b/chromium/third_party/blink/renderer/core/paint/scrollable_area_painter.h @@ -38,10 +38,8 @@ class ScrollableAreaPainter { // Records a scroll hit test data to force main thread handling of events // in the expanded resizer touch area. - void RecordResizerScrollHitTestData( - GraphicsContext&, - const PhysicalOffset& paint_offset, - const DisplayItemClient& background_client); + void RecordResizerScrollHitTestData(GraphicsContext&, + const PhysicalOffset& paint_offset); private: void DrawPlatformResizerImage(GraphicsContext&, @@ -54,7 +52,7 @@ class ScrollableAreaPainter { PaintLayerScrollableArea& GetScrollableArea() const; const DisplayItemClient& DisplayItemClientForCorner() const; - Member<PaintLayerScrollableArea> scrollable_area_; + PaintLayerScrollableArea* scrollable_area_; DISALLOW_COPY_AND_ASSIGN(ScrollableAreaPainter); }; diff --git a/chromium/third_party/blink/renderer/core/paint/selection_painting_utils.cc b/chromium/third_party/blink/renderer/core/paint/selection_painting_utils.cc index 98cd2b17a71..d4ed87b374c 100644 --- a/chromium/third_party/blink/renderer/core/paint/selection_painting_utils.cc +++ b/chromium/third_party/blink/renderer/core/paint/selection_painting_utils.cc @@ -73,7 +73,7 @@ Color SelectionColor(const Document& document, // If the element is unselectable, or we are only painting the selection, // don't override the foreground color with the selection foreground color. if ((node && !NodeIsSelectable(style, node)) || - (global_paint_flags & kGlobalPaintSelectionOnly)) + (global_paint_flags & kGlobalPaintSelectionDragImageOnly)) return style.VisitedDependentColor(color_property); if (scoped_refptr<ComputedStyle> pseudo_style = 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 77847cb1415..0da8574d1e4 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 @@ -32,10 +32,8 @@ void SVGContainerPainter::Paint(const PaintInfo& paint_info) { if (svg_svg_element && svg_svg_element->HasEmptyViewBox()) return; - PaintInfo paint_info_before_filtering(paint_info); - if (SVGModelObjectPainter(layout_svg_container_) - .CullRectSkipsPainting(paint_info_before_filtering)) { + .CullRectSkipsPainting(paint_info)) { return; } @@ -44,6 +42,7 @@ void SVGContainerPainter::Paint(const PaintInfo& paint_info) { // 2) Complexity: Difficulty updating clips when ancestor transforms change. // This is why we use an infinite cull rect if there is a transform. Non-svg // content, does this in PaintLayerPainter::PaintSingleFragment. + PaintInfo paint_info_before_filtering(paint_info); if (layout_svg_container_.StyleRef().HasTransform()) { paint_info_before_filtering.ApplyInfiniteCullRect(); } else if (const auto* properties = @@ -59,7 +58,8 @@ void SVGContainerPainter::Paint(const PaintInfo& paint_info) { base::Optional<ScopedPaintChunkProperties> scoped_paint_chunk_properties; if (layout_svg_container_.IsSVGViewportContainer() && SVGLayoutSupport::IsOverflowHidden(layout_svg_container_)) { - const auto* fragment = paint_info.FragmentToPaint(layout_svg_container_); + const auto* fragment = + paint_info_before_filtering.FragmentToPaint(layout_svg_container_); if (!fragment) return; const auto* properties = fragment->PaintProperties(); @@ -68,9 +68,9 @@ void SVGContainerPainter::Paint(const PaintInfo& paint_info) { // properties are ready. if (properties && properties->OverflowClip()) { scoped_paint_chunk_properties.emplace( - paint_info.context.GetPaintController(), + paint_info_before_filtering.context.GetPaintController(), *properties->OverflowClip(), layout_svg_container_, - paint_info.DisplayItemTypeForClipping()); + paint_info_before_filtering.DisplayItemTypeForClipping()); } } @@ -78,13 +78,13 @@ void SVGContainerPainter::Paint(const PaintInfo& paint_info) { paint_info_before_filtering); bool continue_rendering = true; if (paint_state.GetPaintInfo().phase == PaintPhase::kForeground) - continue_rendering = paint_state.ApplyClipMaskAndFilterIfNecessary(); + continue_rendering = paint_state.ApplyEffects(); if (continue_rendering) { for (LayoutObject* child = layout_svg_container_.FirstChild(); child; child = child->NextSibling()) { - if (child->IsSVGForeignObject()) { - SVGForeignObjectPainter(ToLayoutSVGForeignObject(*child)) + if (auto* foreign_object = DynamicTo<LayoutSVGForeignObject>(*child)) { + SVGForeignObjectPainter(*foreign_object) .PaintLayer(paint_state.GetPaintInfo()); } else { child->Paint(paint_state.GetPaintInfo()); 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 66b1ae84e13..47552d0011e 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 @@ -12,157 +12,65 @@ #include "third_party/blink/renderer/core/svg/graphics/filters/svg_filter_builder.h" #include "third_party/blink/renderer/core/svg/svg_filter_element.h" #include "third_party/blink/renderer/platform/graphics/filters/filter.h" -#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/paint/drawing_recorder.h" +#include "third_party/blink/renderer/platform/graphics/graphics_context.h" +#include "third_party/blink/renderer/platform/graphics/paint/paint_controller.h" namespace blink { -GraphicsContext* SVGFilterRecordingContext::BeginContent() { - // Create a new context so the contents of the filter can be drawn and cached. - paint_controller_ = std::make_unique<PaintController>(); - context_ = std::make_unique<GraphicsContext>(*paint_controller_); - - // Use initial_context_'s current paint chunk properties so that any new +SVGFilterRecordingContext::SVGFilterRecordingContext( + const PaintInfo& initial_paint_info) + // Create a new context so the contents of the filter can be drawn and + // cached. + : paint_controller_(std::make_unique<PaintController>()), + context_(std::make_unique<GraphicsContext>(*paint_controller_)), + paint_info_(*context_, initial_paint_info) { + // Use initial_paint_info's current paint chunk properties so that any new // chunk created during painting the content will be in the correct state. paint_controller_->UpdateCurrentPaintChunkProperties( - base::nullopt, - initial_context_.GetPaintController().CurrentPaintChunkProperties()); - - return context_.get(); + nullptr, initial_paint_info.context.GetPaintController() + .CurrentPaintChunkProperties()); + // 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. + paint_info_.ApplyInfiniteCullRect(); } -sk_sp<PaintRecord> SVGFilterRecordingContext::EndContent( - const FloatRect& bounds) { - // Use the context that contains the filtered content. - DCHECK(paint_controller_); - DCHECK(context_); - context_->BeginRecording(bounds); - paint_controller_->CommitNewDisplayItems(); - - paint_controller_->GetPaintArtifact().Replay( - *context_, - initial_context_.GetPaintController().CurrentPaintChunkProperties()); +SVGFilterRecordingContext::~SVGFilterRecordingContext() = default; - sk_sp<PaintRecord> content = context_->EndRecording(); - // Content is cached by the source graphic so temporaries can be freed. - paint_controller_ = nullptr; - context_ = nullptr; - return content; -} - -void SVGFilterRecordingContext::Abort() { - if (!paint_controller_) - return; - EndContent(FloatRect()); -} - -static void PaintFilteredContent(GraphicsContext& context, - const LayoutObject& object, - const FloatRect& bounds, - FilterEffect* effect) { - if (DrawingRecorder::UseCachedDrawingIfPossible(context, object, - DisplayItem::kSVGFilter)) - return; - - DrawingRecorder recorder(context, object, DisplayItem::kSVGFilter); - sk_sp<PaintFilter> image_filter = - paint_filter_builder::Build(effect, kInterpolationSpaceSRGB); - context.Save(); - - // Clip drawing of filtered image to the minimum required paint rect. - context.ClipRect(effect->MapRect(object.StrokeBoundingBox())); - - context.BeginLayer(1, SkBlendMode::kSrcOver, &bounds, kColorFilterNone, - std::move(image_filter)); - context.EndLayer(); - context.Restore(); +sk_sp<PaintRecord> SVGFilterRecordingContext::GetPaintRecord( + const PaintInfo& initial_paint_info) { + paint_controller_->CommitNewDisplayItems(); + return paint_controller_->GetPaintArtifact().GetPaintRecord( + initial_paint_info.context.GetPaintController() + .CurrentPaintChunkProperties()); } -GraphicsContext* SVGFilterPainter::PrepareEffect( - const LayoutObject& object, - SVGFilterRecordingContext& recording_context) { - filter_.ClearInvalidationMask(); - - SVGResourceClient* client = SVGResources::GetClient(object); - if (FilterData* filter_data = filter_.GetFilterDataForClient(client)) { +FilterData* SVGFilterPainter::PrepareEffect(const LayoutObject& object) { + SVGElementResourceClient* client = SVGResources::GetClient(object); + if (FilterData* filter_data = client->GetFilterData()) { // If the filterData already exists we do not need to record the content // to be filtered. This can occur if the content was previously recorded // or we are in a cycle. - if (filter_data->state_ == FilterData::kPaintingFilter) - filter_data->state_ = FilterData::kPaintingFilterCycleDetected; - - if (filter_data->state_ == FilterData::kRecordingContent) - filter_data->state_ = FilterData::kRecordingContentCycleDetected; - - return nullptr; + filter_data->UpdateStateOnPrepare(); + return filter_data; } auto* node_map = MakeGarbageCollected<SVGFilterGraphNodeMap>(); - FilterEffectBuilder builder(object.ObjectBoundingBox(), 1); + FilterEffectBuilder builder(SVGResources::ReferenceBoxForEffects(object), 1); Filter* filter = builder.BuildReferenceFilter( To<SVGFilterElement>(*filter_.GetElement()), nullptr, node_map); if (!filter || !filter->LastEffect()) return nullptr; - IntRect source_region = EnclosingIntRect( - Intersection(filter->FilterRegion(), object.StrokeBoundingBox())); + IntRect source_region = EnclosingIntRect(object.StrokeBoundingBox()); filter->GetSourceGraphic()->SetSourceRect(source_region); - auto* filter_data = MakeGarbageCollected<FilterData>(); - filter_data->last_effect = filter->LastEffect(); - filter_data->node_map = node_map; - DCHECK_EQ(filter_data->state_, FilterData::kInitial); - + auto* filter_data = + MakeGarbageCollected<FilterData>(filter->LastEffect(), node_map); // TODO(pdr): Can this be moved out of painter? - filter_.SetFilterDataForClient(client, filter_data); - filter_data->state_ = FilterData::kRecordingContent; - return recording_context.BeginContent(); -} - -void SVGFilterPainter::FinishEffect( - const LayoutObject& object, - SVGFilterRecordingContext& recording_context) { - SVGResourceClient* client = SVGResources::GetClient(object); - FilterData* filter_data = filter_.GetFilterDataForClient(client); - if (!filter_data) { - // Our state was torn down while we were being painted (selection style for - // <text> can have this effect), or it was never created (invalid filter.) - // In the former case we may have been in the process of recording content, - // so make sure we put recording state into a consistent state. - recording_context.Abort(); - return; - } - - // A painting cycle can occur when an FeImage references a source that - // makes use of the FeImage itself. This is the first place we would hit - // the cycle so we reset the state and continue. - if (filter_data->state_ == FilterData::kPaintingFilterCycleDetected) { - filter_data->state_ = FilterData::kPaintingFilter; - return; - } - if (filter_data->state_ == FilterData::kRecordingContentCycleDetected) { - filter_data->state_ = FilterData::kRecordingContent; - return; - } - - // Check for RecordingContent here because we may can be re-painting - // without re-recording the contents to be filtered. - Filter* filter = filter_data->last_effect->GetFilter(); - FloatRect bounds = filter->FilterRegion(); - if (filter_data->state_ == FilterData::kRecordingContent) { - DCHECK(filter->GetSourceGraphic()); - sk_sp<PaintRecord> content = recording_context.EndContent(bounds); - paint_filter_builder::BuildSourceGraphic(filter->GetSourceGraphic(), - std::move(content), bounds); - filter_data->state_ = FilterData::kReadyToPaint; - } - - DCHECK_EQ(filter_data->state_, FilterData::kReadyToPaint); - filter_data->state_ = FilterData::kPaintingFilter; - PaintFilteredContent(recording_context.PaintingContext(), object, bounds, - filter_data->last_effect); - filter_data->state_ = FilterData::kReadyToPaint; + client->SetFilterData(filter_data); + return filter_data; } } // namespace blink diff --git a/chromium/third_party/blink/renderer/core/paint/svg_filter_painter.h b/chromium/third_party/blink/renderer/core/paint/svg_filter_painter.h index 7dd90ff7c54..f38d6937347 100644 --- a/chromium/third_party/blink/renderer/core/paint/svg_filter_painter.h +++ b/chromium/third_party/blink/renderer/core/paint/svg_filter_painter.h @@ -7,32 +7,31 @@ #include <memory> #include "base/macros.h" -#include "third_party/blink/renderer/platform/graphics/graphics_context.h" -#include "third_party/blink/renderer/platform/graphics/paint/paint_controller.h" +#include "third_party/blink/renderer/core/paint/paint_info.h" #include "third_party/blink/renderer/platform/wtf/allocator/allocator.h" namespace blink { +class FilterData; +class GraphicsContext; class LayoutObject; class LayoutSVGResourceFilter; +class PaintController; class SVGFilterRecordingContext { USING_FAST_MALLOC(SVGFilterRecordingContext); public: - explicit SVGFilterRecordingContext(GraphicsContext& initial_context) - : initial_context_(initial_context) {} + explicit SVGFilterRecordingContext(const PaintInfo&); + ~SVGFilterRecordingContext(); - GraphicsContext* BeginContent(); - sk_sp<PaintRecord> EndContent(const FloatRect&); - void Abort(); - - GraphicsContext& PaintingContext() const { return initial_context_; } + const PaintInfo& GetPaintInfo() const { return paint_info_; } + sk_sp<PaintRecord> GetPaintRecord(const PaintInfo&); private: std::unique_ptr<PaintController> paint_controller_; std::unique_ptr<GraphicsContext> context_; - GraphicsContext& initial_context_; + PaintInfo paint_info_; DISALLOW_COPY_AND_ASSIGN(SVGFilterRecordingContext); }; @@ -42,11 +41,9 @@ class SVGFilterPainter { public: SVGFilterPainter(LayoutSVGResourceFilter& filter) : filter_(filter) {} - // Returns the context that should be used to paint the filter contents, or - // null if the content should not be recorded. - GraphicsContext* PrepareEffect(const LayoutObject&, - SVGFilterRecordingContext&); - void FinishEffect(const LayoutObject&, SVGFilterRecordingContext&); + // Returns the FilterData for the filter effect, or null if the + // filter is invalid. + FilterData* PrepareEffect(const LayoutObject&); private: LayoutSVGResourceFilter& filter_; 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 4d7405fcc7c..fa811d7863e 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 @@ -6,9 +6,7 @@ #include "base/optional.h" #include "third_party/blink/renderer/core/layout/svg/layout_svg_foreign_object.h" -#include "third_party/blink/renderer/core/layout/svg/svg_layout_support.h" #include "third_party/blink/renderer/core/paint/block_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.h" #include "third_party/blink/renderer/core/paint/paint_layer_painter.h" @@ -19,7 +17,7 @@ namespace blink { void SVGForeignObjectPainter::PaintLayer(const PaintInfo& paint_info) { if (paint_info.phase != PaintPhase::kForeground && - paint_info.phase != PaintPhase::kSelection) + paint_info.phase != PaintPhase::kSelectionDragImage) return; // Early out in the case of trying to paint an image filter before @@ -51,14 +49,13 @@ void SVGForeignObjectPainter::PaintLayer(const PaintInfo& paint_info) { } void SVGForeignObjectPainter::Paint(const PaintInfo& paint_info) { - PaintInfo paint_info_before_filtering(paint_info); - ScopedSVGPaintState paint_state(layout_svg_foreign_object_, - paint_info_before_filtering); - - if (paint_state.GetPaintInfo().phase == PaintPhase::kForeground && - !paint_state.ApplyClipMaskAndFilterIfNecessary()) + ScopedSVGPaintState paint_state(layout_svg_foreign_object_, paint_info); + // ScopedSVGPaintState only applies masks (and clips-within-clips) + // here and thus does not mutate PaintInfo, so we can use the passed + // in PaintInfo below. + if (paint_info.phase == PaintPhase::kForeground && + !paint_state.ApplyEffects()) return; - BlockPainter(layout_svg_foreign_object_).Paint(paint_info); } 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 de9deb79d00..e3b5622f87d 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 @@ -27,25 +27,23 @@ void SVGImagePainter::Paint(const PaintInfo& paint_info) { !layout_svg_image_.ImageResource()->HasImage()) return; - PaintInfo paint_info_before_filtering(paint_info); - if (SVGModelObjectPainter(layout_svg_image_) - .CullRectSkipsPainting(paint_info_before_filtering)) { + .CullRectSkipsPainting(paint_info)) { return; } // Images cannot have children so do not call TransformCullRect. ScopedSVGTransformState transform_state( - paint_info_before_filtering, layout_svg_image_, + paint_info, layout_svg_image_, layout_svg_image_.LocalToSVGParentTransform()); { - ScopedSVGPaintState paint_state(layout_svg_image_, - paint_info_before_filtering); - if (paint_state.ApplyClipMaskAndFilterIfNecessary() && + ScopedSVGPaintState paint_state(layout_svg_image_, paint_info); + if (paint_state.ApplyEffects() && !DrawingRecorder::UseCachedDrawingIfPossible( paint_state.GetPaintInfo().context, layout_svg_image_, paint_state.GetPaintInfo().phase)) { - SVGModelObjectPainter::RecordHitTestData(layout_svg_image_, paint_info); + SVGModelObjectPainter::RecordHitTestData(layout_svg_image_, + paint_state.GetPaintInfo()); DrawingRecorder recorder(paint_state.GetPaintInfo().context, layout_svg_image_, paint_state.GetPaintInfo().phase); @@ -53,8 +51,7 @@ void SVGImagePainter::Paint(const PaintInfo& paint_info) { } } - SVGModelObjectPainter(layout_svg_image_) - .PaintOutline(paint_info_before_filtering); + SVGModelObjectPainter(layout_svg_image_).PaintOutline(paint_info); } void SVGImagePainter::PaintForeground(const PaintInfo& paint_info) { @@ -64,14 +61,27 @@ void SVGImagePainter::PaintForeground(const PaintInfo& paint_info) { if (image_viewport_size.IsEmpty()) return; - scoped_refptr<Image> image = - image_resource->GetImage(ExpandedIntSize(image_viewport_size)); + scoped_refptr<Image> image = image_resource->GetImage(image_viewport_size); FloatRect dest_rect = layout_svg_image_.ObjectBoundingBox(); - FloatRect src_rect(0, 0, image->width(), image->height()); auto* image_element = To<SVGImageElement>(layout_svg_image_.GetElement()); - image_element->preserveAspectRatio()->CurrentValue()->TransformRect(dest_rect, - src_rect); + RespectImageOrientationEnum respect_orientation = + LayoutObject::ShouldRespectImageOrientation(&layout_svg_image_); + FloatRect src_rect(FloatPoint(), image->SizeAsFloat(respect_orientation)); + if (respect_orientation && !image->HasDefaultOrientation()) { + // We need the oriented source rect for adjusting the aspect ratio + FloatSize unadjusted_size(src_rect.Size()); + image_element->preserveAspectRatio()->CurrentValue()->TransformRect( + dest_rect, src_rect); + + // Map the oriented_src_rect back into the src_rect space + src_rect = + image->CorrectSrcRectForImageOrientation(unadjusted_size, src_rect); + } else { + image_element->preserveAspectRatio()->CurrentValue()->TransformRect( + dest_rect, src_rect); + } + ScopedInterpolationQuality interpolation_quality_scope( paint_info.context, layout_svg_image_.StyleRef().GetInterpolationQuality()); @@ -79,8 +89,9 @@ void SVGImagePainter::PaintForeground(const PaintInfo& paint_info) { image_element->GetDecodingModeForPainting(image->paint_image_id()); paint_info.context.DrawImage( image.get(), decode_mode, dest_rect, &src_rect, - layout_svg_image_.StyleRef().HasFilterInducingProperty()); - if (!paint_info.context.ContextDisabled() && image_resource->CachedImage() && + layout_svg_image_.StyleRef().HasFilterInducingProperty(), + SkBlendMode::kSrcOver, respect_orientation); + if (image_resource->CachedImage() && image_resource->CachedImage()->IsLoaded()) { LocalDOMWindow* window = layout_svg_image_.GetDocument().domWindow(); DCHECK(window); @@ -117,11 +128,12 @@ FloatSize SVGImagePainter::ComputeImageViewportSize() const { if (cached_image->ErrorOccurred()) return FloatSize(); Image* image = cached_image->GetImage(); - if (image->IsSVGImage()) { - return ToSVGImage(image)->ConcreteObjectSize( + if (auto* svg_image = DynamicTo<SVGImage>(image)) { + return svg_image->ConcreteObjectSize( layout_svg_image_.ObjectBoundingBox().Size()); } - return FloatSize(image->Size()); + // The orientation here does not matter. Just use kRespectImageOrientation. + return image->SizeAsFloat(kRespectImageOrientation); } } // namespace blink diff --git a/chromium/third_party/blink/renderer/core/paint/svg_inline_flow_box_painter.cc b/chromium/third_party/blink/renderer/core/paint/svg_inline_flow_box_painter.cc index a47b291f354..864602286f4 100644 --- a/chromium/third_party/blink/renderer/core/paint/svg_inline_flow_box_painter.cc +++ b/chromium/third_party/blink/renderer/core/paint/svg_inline_flow_box_painter.cc @@ -16,33 +16,34 @@ namespace blink { void SVGInlineFlowBoxPainter::PaintSelectionBackground( const PaintInfo& paint_info) { DCHECK(paint_info.phase == PaintPhase::kForeground || - paint_info.phase == PaintPhase::kSelection); + paint_info.phase == PaintPhase::kSelectionDragImage); - PaintInfo child_paint_info(paint_info); for (InlineBox* child = svg_inline_flow_box_.FirstChild(); child; child = child->NextOnLine()) { - if (child->IsSVGInlineTextBox()) - SVGInlineTextBoxPainter(*ToSVGInlineTextBox(child)) - .PaintSelectionBackground(child_paint_info); - else if (child->IsSVGInlineFlowBox()) - SVGInlineFlowBoxPainter(*ToSVGInlineFlowBox(child)) - .PaintSelectionBackground(child_paint_info); + if (auto* svg_inline_text_box = DynamicTo<SVGInlineTextBox>(child)) { + SVGInlineTextBoxPainter(*svg_inline_text_box) + .PaintSelectionBackground(paint_info); + } else if (auto* svg_inline_flow_box = DynamicTo<SVGInlineFlowBox>(child)) { + SVGInlineFlowBoxPainter(*svg_inline_flow_box) + .PaintSelectionBackground(paint_info); + } } } void SVGInlineFlowBoxPainter::Paint(const PaintInfo& paint_info, const LayoutPoint& paint_offset) { DCHECK(paint_info.phase == PaintPhase::kForeground || - paint_info.phase == PaintPhase::kSelection); + paint_info.phase == PaintPhase::kSelectionDragImage); ScopedSVGPaintState paint_state(*LineLayoutAPIShim::ConstLayoutObjectFrom( svg_inline_flow_box_.GetLineLayoutItem()), - paint_info); - if (paint_state.ApplyClipMaskAndFilterIfNecessary()) { - for (InlineBox* child = svg_inline_flow_box_.FirstChild(); child; - child = child->NextOnLine()) - child->Paint(paint_state.GetPaintInfo(), paint_offset, LayoutUnit(), - LayoutUnit()); + paint_info, svg_inline_flow_box_); + if (!paint_state.ApplyEffects()) + return; + for (InlineBox* child = svg_inline_flow_box_.FirstChild(); child; + child = child->NextOnLine()) { + child->Paint(paint_state.GetPaintInfo(), paint_offset, LayoutUnit(), + LayoutUnit()); } } 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 ea50d27ec60..8dd8f84c994 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 @@ -74,7 +74,7 @@ LayoutSVGInlineText& SVGInlineTextBoxPainter::InlineText() const { void SVGInlineTextBoxPainter::Paint(const PaintInfo& paint_info, const LayoutPoint& paint_offset) { DCHECK(paint_info.phase == PaintPhase::kForeground || - paint_info.phase == PaintPhase::kSelection); + paint_info.phase == PaintPhase::kSelectionDragImage); DCHECK(svg_inline_text_box_.Truncation() == kCNoTruncation); if (svg_inline_text_box_.GetLineLayoutItem().StyleRef().Visibility() != @@ -87,7 +87,7 @@ void SVGInlineTextBoxPainter::Paint(const PaintInfo& paint_info, // very easy to refactor and reuse the code. bool have_selection = ShouldPaintSelection(paint_info); - if (!have_selection && paint_info.phase == PaintPhase::kSelection) + if (!have_selection && paint_info.phase == PaintPhase::kSelectionDragImage) return; LayoutSVGInlineText& text_layout_object = InlineText(); @@ -213,7 +213,7 @@ void SVGInlineTextBoxPainter::PaintSelectionBackground( DCHECK(!paint_info.IsPrinting()); - if (paint_info.phase == PaintPhase::kSelection || + if (paint_info.phase == PaintPhase::kSelectionDragImage || !ShouldPaintSelection(paint_info)) return; @@ -507,7 +507,8 @@ void SVGInlineTextBoxPainter::PaintText( // Eventually draw text using regular style until the start position of the // selection. - bool paint_selected_text_only = paint_info.phase == PaintPhase::kSelection; + bool paint_selected_text_only = + paint_info.phase == PaintPhase::kSelectionDragImage; if (start_position > 0 && !paint_selected_text_only) { PaintFlags flags; if (SetupTextPaint(paint_info, style, resource_mode, flags, diff --git a/chromium/third_party/blink/renderer/core/paint/svg_mask_painter.cc b/chromium/third_party/blink/renderer/core/paint/svg_mask_painter.cc index fbac7c37aa4..b579318ec3b 100644 --- a/chromium/third_party/blink/renderer/core/paint/svg_mask_painter.cc +++ b/chromium/third_party/blink/renderer/core/paint/svg_mask_painter.cc @@ -5,8 +5,8 @@ #include "third_party/blink/renderer/core/paint/svg_mask_painter.h" #include "third_party/blink/renderer/core/layout/svg/layout_svg_resource_masker.h" +#include "third_party/blink/renderer/core/layout/svg/svg_resources.h" #include "third_party/blink/renderer/core/paint/object_paint_properties.h" -#include "third_party/blink/renderer/core/paint/paint_info.h" #include "third_party/blink/renderer/platform/graphics/paint/drawing_display_item.h" #include "third_party/blink/renderer/platform/graphics/paint/drawing_recorder.h" #include "third_party/blink/renderer/platform/graphics/paint/paint_controller.h" @@ -14,44 +14,48 @@ namespace blink { -bool SVGMaskPainter::PrepareEffect(const LayoutObject& object, - GraphicsContext& context) { - DCHECK(mask_.Style()); - SECURITY_DCHECK(!mask_.NeedsLayout()); - - mask_.ClearInvalidationMask(); - return mask_.GetElement()->HasChildren(); +SVGMaskPainter::SVGMaskPainter(GraphicsContext& context, + const LayoutObject& layout_object, + const DisplayItemClient& display_item_client) + : context_(context), + layout_object_(layout_object), + display_item_client_(display_item_client) { + DCHECK(layout_object_.StyleRef().SvgStyle().MaskerResource()); } -void SVGMaskPainter::FinishEffect(const LayoutObject& object, - GraphicsContext& context) { - DCHECK(mask_.Style()); - SECURITY_DCHECK(!mask_.NeedsLayout()); - - base::Optional<ScopedPaintChunkProperties> scoped_paint_chunk_properties; - const auto* properties = object.FirstFragment().PaintProperties(); +SVGMaskPainter::~SVGMaskPainter() { + const auto* properties = layout_object_.FirstFragment().PaintProperties(); // TODO(crbug.com/814815): This condition should be a DCHECK, but for now // we may paint the object for filters during PrePaint before the // properties are ready. - if (properties && properties->Mask()) { - scoped_paint_chunk_properties.emplace(context.GetPaintController(), - *properties->Mask(), object, - DisplayItem::kSVGMask); - } - - AffineTransform content_transformation; - sk_sp<const PaintRecord> record = mask_.CreatePaintRecord( - content_transformation, object.ObjectBoundingBox(), context); + if (!properties || !properties->Mask()) + return; + ScopedPaintChunkProperties scoped_paint_chunk_properties( + context_.GetPaintController(), *properties->Mask(), display_item_client_, + DisplayItem::kSVGMask); - if (DrawingRecorder::UseCachedDrawingIfPossible(context, object, - DisplayItem::kSVGMask)) + if (DrawingRecorder::UseCachedDrawingIfPossible( + context_, display_item_client_, DisplayItem::kSVGMask)) return; + DrawingRecorder recorder(context_, display_item_client_, + DisplayItem::kSVGMask); + + const SVGComputedStyle& svg_style = layout_object_.StyleRef().SvgStyle(); + auto* masker = + GetSVGResourceAsType<LayoutSVGResourceMasker>(svg_style.MaskerResource()); + DCHECK(masker); + SECURITY_DCHECK(!masker->NeedsLayout()); + masker->ClearInvalidationMask(); - DrawingRecorder recorder(context, object, DisplayItem::kSVGMask); - context.Save(); - context.ConcatCTM(content_transformation); - context.DrawRecord(std::move(record)); - context.Restore(); + AffineTransform content_transformation; + sk_sp<const PaintRecord> record = masker->CreatePaintRecord( + content_transformation, + SVGResources::ReferenceBoxForEffects(layout_object_), context_); + + context_.Save(); + context_.ConcatCTM(content_transformation); + context_.DrawRecord(std::move(record)); + context_.Restore(); } } // namespace blink diff --git a/chromium/third_party/blink/renderer/core/paint/svg_mask_painter.h b/chromium/third_party/blink/renderer/core/paint/svg_mask_painter.h index af65990bf2e..d7f37cdb526 100644 --- a/chromium/third_party/blink/renderer/core/paint/svg_mask_painter.h +++ b/chromium/third_party/blink/renderer/core/paint/svg_mask_painter.h @@ -5,26 +5,27 @@ #ifndef THIRD_PARTY_BLINK_RENDERER_CORE_PAINT_SVG_MASK_PAINTER_H_ #define THIRD_PARTY_BLINK_RENDERER_CORE_PAINT_SVG_MASK_PAINTER_H_ -#include "third_party/blink/renderer/platform/geometry/float_rect.h" #include "third_party/blink/renderer/platform/wtf/allocator/allocator.h" namespace blink { +class DisplayItemClient; class GraphicsContext; class LayoutObject; -class LayoutSVGResourceMasker; class SVGMaskPainter { STACK_ALLOCATED(); public: - SVGMaskPainter(LayoutSVGResourceMasker& mask) : mask_(mask) {} - - bool PrepareEffect(const LayoutObject&, GraphicsContext&); - void FinishEffect(const LayoutObject&, GraphicsContext&); + SVGMaskPainter(GraphicsContext&, + const LayoutObject&, + const DisplayItemClient&); + ~SVGMaskPainter(); private: - LayoutSVGResourceMasker& mask_; + GraphicsContext& context_; + const LayoutObject& layout_object_; + const DisplayItemClient& display_item_client_; }; } // namespace blink 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 dc21e160d02..81e3d4b6b97 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_display_item.h" +#include "third_party/blink/renderer/platform/graphics/paint/paint_controller.h" namespace blink { @@ -29,23 +29,19 @@ bool SVGModelObjectPainter::CullRectSkipsPainting(const PaintInfo& paint_info) { layout_svg_model_object_.VisualRectInLocalSVGCoordinates()); } -void SVGModelObjectPainter::RecordHitTestData( - const LayoutSVGModelObject& layout_svg_model_object, - const PaintInfo& paint_info) { +void SVGModelObjectPainter::RecordHitTestData(const LayoutObject& svg_object, + const PaintInfo& paint_info) { + DCHECK(svg_object.IsSVGChild()); DCHECK(paint_info.phase == PaintPhase::kForeground); // 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_svg_model_object.EffectiveAllowedTouchAction(); - if (touch_action == TouchAction::kTouchActionAuto) - return; - - auto rect = - LayoutRect(layout_svg_model_object.VisualRectInLocalSVGCoordinates()); - HitTestDisplayItem::Record(paint_info.context, layout_svg_model_object, - HitTestRect(rect, touch_action)); + paint_info.context.GetPaintController().RecordHitTestData( + svg_object, + EnclosingIntRect(svg_object.VisualRectInLocalSVGCoordinates()), + svg_object.EffectiveAllowedTouchAction()); } void SVGModelObjectPainter::PaintOutline(const PaintInfo& paint_info) { diff --git a/chromium/third_party/blink/renderer/core/paint/svg_model_object_painter.h b/chromium/third_party/blink/renderer/core/paint/svg_model_object_painter.h index 7498f380893..21578ed5f37 100644 --- a/chromium/third_party/blink/renderer/core/paint/svg_model_object_painter.h +++ b/chromium/third_party/blink/renderer/core/paint/svg_model_object_painter.h @@ -10,20 +10,22 @@ namespace blink { struct PaintInfo; +class LayoutObject; class LayoutSVGModelObject; class SVGModelObjectPainter { STACK_ALLOCATED(); public: - // Paint a hit test display item and record hit test data. This should be - // called when painting the background even if there is no other painted - // content. SVG backgrounds are painted in the kForeground paint phase. - static void RecordHitTestData( - const LayoutSVGModelObject& layout_svg_model_object, - const PaintInfo&); - - SVGModelObjectPainter(const LayoutSVGModelObject& layout_svg_model_object) + // Expands the bounds of the current paint chunk for hit test, and records + // special touch action if any. This should be called when painting the + // background even if there is no other painted content. SVG backgrounds are + // painted in the kForeground paint phase. + static void RecordHitTestData(const LayoutObject& svg_object, + const PaintInfo&); + + explicit SVGModelObjectPainter( + const LayoutSVGModelObject& layout_svg_model_object) : layout_svg_model_object_(layout_svg_model_object) {} // If the object is outside the cull rect, painting can be skipped in most diff --git a/chromium/third_party/blink/renderer/core/paint/svg_root_inline_box_painter.cc b/chromium/third_party/blink/renderer/core/paint/svg_root_inline_box_painter.cc index fd9eba5ae8b..2b3079f273f 100644 --- a/chromium/third_party/blink/renderer/core/paint/svg_root_inline_box_painter.cc +++ b/chromium/third_party/blink/renderer/core/paint/svg_root_inline_box_painter.cc @@ -19,40 +19,42 @@ namespace blink { void SVGRootInlineBoxPainter::Paint(const PaintInfo& paint_info, const LayoutPoint& paint_offset) { DCHECK(paint_info.phase == PaintPhase::kForeground || - paint_info.phase == PaintPhase::kSelection); + paint_info.phase == PaintPhase::kSelectionDragImage); bool has_selection = !paint_info.IsPrinting() && svg_root_inline_box_.IsSelected(); - PaintInfo paint_info_before_filtering(paint_info); if (has_selection && !DrawingRecorder::UseCachedDrawingIfPossible( - paint_info_before_filtering.context, + paint_info.context, *LineLayoutAPIShim::ConstLayoutObjectFrom( svg_root_inline_box_.GetLineLayoutItem()), - paint_info_before_filtering.phase)) { - DrawingRecorder recorder(paint_info_before_filtering.context, + paint_info.phase)) { + DrawingRecorder recorder(paint_info.context, *LineLayoutAPIShim::ConstLayoutObjectFrom( svg_root_inline_box_.GetLineLayoutItem()), - paint_info_before_filtering.phase); + paint_info.phase); for (InlineBox* child = svg_root_inline_box_.FirstChild(); child; child = child->NextOnLine()) { - if (child->IsSVGInlineTextBox()) - SVGInlineTextBoxPainter(*ToSVGInlineTextBox(child)) - .PaintSelectionBackground(paint_info_before_filtering); - else if (child->IsSVGInlineFlowBox()) - SVGInlineFlowBoxPainter(*ToSVGInlineFlowBox(child)) - .PaintSelectionBackground(paint_info_before_filtering); + if (auto* svg_inline_text_box = DynamicTo<SVGInlineTextBox>(child)) { + SVGInlineTextBoxPainter(*svg_inline_text_box) + .PaintSelectionBackground(paint_info); + } else if (auto* svg_inline_flow_box = + DynamicTo<SVGInlineFlowBox>(child)) { + SVGInlineFlowBoxPainter(*svg_inline_flow_box) + .PaintSelectionBackground(paint_info); + } } } ScopedSVGPaintState paint_state(*LineLayoutAPIShim::ConstLayoutObjectFrom( svg_root_inline_box_.GetLineLayoutItem()), - paint_info_before_filtering); - if (paint_state.ApplyClipMaskAndFilterIfNecessary()) { - for (InlineBox* child = svg_root_inline_box_.FirstChild(); child; - child = child->NextOnLine()) - child->Paint(paint_state.GetPaintInfo(), paint_offset, LayoutUnit(), - LayoutUnit()); + paint_info); + if (!paint_state.ApplyEffects()) + return; + for (InlineBox* child = svg_root_inline_box_.FirstChild(); child; + child = child->NextOnLine()) { + child->Paint(paint_state.GetPaintInfo(), paint_offset, LayoutUnit(), + LayoutUnit()); } } diff --git a/chromium/third_party/blink/renderer/core/paint/svg_root_painter.cc b/chromium/third_party/blink/renderer/core/paint/svg_root_painter.cc index 50f5aab44c8..ccccf6411ca 100644 --- a/chromium/third_party/blink/renderer/core/paint/svg_root_painter.cc +++ b/chromium/third_party/blink/renderer/core/paint/svg_root_painter.cc @@ -53,7 +53,7 @@ void SVGRootPainter::PaintReplaced(const PaintInfo& paint_info, ScopedSVGPaintState paint_state(layout_svg_root_, paint_info); if (paint_state.GetPaintInfo().phase == PaintPhase::kForeground && - !paint_state.ApplyClipMaskAndFilterIfNecessary()) + !paint_state.ApplyEffects()) return; BoxPainter(layout_svg_root_).PaintChildren(paint_state.GetPaintInfo()); 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 0aea12af64b..1d744ec0f76 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 @@ -4,7 +4,6 @@ #include "third_party/blink/renderer/core/paint/svg_shape_painter.h" -#include "base/optional.h" #include "third_party/blink/renderer/core/layout/svg/layout_svg_resource_marker.h" #include "third_party/blink/renderer/core/layout/svg/layout_svg_shape.h" #include "third_party/blink/renderer/core/layout/svg/svg_layout_support.h" @@ -47,25 +46,22 @@ void SVGShapePainter::Paint(const PaintInfo& paint_info) { layout_svg_shape_.IsShapeEmpty()) return; - PaintInfo paint_info_before_filtering(paint_info); - if (SVGModelObjectPainter(layout_svg_shape_) - .CullRectSkipsPainting(paint_info_before_filtering)) { + .CullRectSkipsPainting(paint_info)) { return; } // Shapes cannot have children so do not call TransformCullRect. ScopedSVGTransformState transform_state( - paint_info_before_filtering, layout_svg_shape_, - layout_svg_shape_.LocalSVGTransform()); + paint_info, layout_svg_shape_, layout_svg_shape_.LocalSVGTransform()); { - ScopedSVGPaintState paint_state(layout_svg_shape_, - paint_info_before_filtering); - if (paint_state.ApplyClipMaskAndFilterIfNecessary() && + ScopedSVGPaintState paint_state(layout_svg_shape_, paint_info); + if (paint_state.ApplyEffects() && !DrawingRecorder::UseCachedDrawingIfPossible( paint_state.GetPaintInfo().context, layout_svg_shape_, paint_state.GetPaintInfo().phase)) { - SVGModelObjectPainter::RecordHitTestData(layout_svg_shape_, paint_info); + SVGModelObjectPainter::RecordHitTestData(layout_svg_shape_, + paint_state.GetPaintInfo()); DrawingRecorder recorder(paint_state.GetPaintInfo().context, layout_svg_shape_, paint_state.GetPaintInfo().phase); @@ -128,8 +124,7 @@ void SVGShapePainter::Paint(const PaintInfo& paint_info) { } break; case PT_MARKERS: - PaintMarkers(paint_state.GetPaintInfo(), - layout_svg_shape_.VisualRectInLocalSVGCoordinates()); + PaintMarkers(paint_state.GetPaintInfo()); break; default: NOTREACHED(); @@ -139,8 +134,7 @@ void SVGShapePainter::Paint(const PaintInfo& paint_info) { } } - SVGModelObjectPainter(layout_svg_shape_) - .PaintOutline(paint_info_before_filtering); + SVGModelObjectPainter(layout_svg_shape_).PaintOutline(paint_info); } class PathWithTemporaryWindingRule { @@ -182,8 +176,7 @@ void SVGShapePainter::FillShape(GraphicsContext& context, void SVGShapePainter::StrokeShape(GraphicsContext& context, const PaintFlags& flags) { - if (!layout_svg_shape_.StyleRef().SvgStyle().HasVisibleStroke()) - return; + DCHECK(layout_svg_shape_.StyleRef().SvgStyle().HasVisibleStroke()); switch (layout_svg_shape_.GeometryCodePath()) { case kRectGeometryFastPath: @@ -204,8 +197,7 @@ void SVGShapePainter::StrokeShape(GraphicsContext& context, } } -void SVGShapePainter::PaintMarkers(const PaintInfo& paint_info, - const FloatRect& bounding_box) { +void SVGShapePainter::PaintMarkers(const PaintInfo& paint_info) { const Vector<MarkerPosition>* marker_positions = layout_svg_shape_.MarkerPositions(); if (!marker_positions || marker_positions->IsEmpty()) @@ -222,7 +214,7 @@ void SVGShapePainter::PaintMarkers(const PaintInfo& paint_info, if (!marker_start && !marker_mid && !marker_end) return; - float stroke_width = layout_svg_shape_.StrokeWidth(); + const float stroke_width = layout_svg_shape_.StrokeWidthForMarkerUnits(); for (const MarkerPosition& marker_position : *marker_positions) { if (LayoutSVGResourceMarker* marker = marker_position.SelectMarker( diff --git a/chromium/third_party/blink/renderer/core/paint/svg_shape_painter.h b/chromium/third_party/blink/renderer/core/paint/svg_shape_painter.h index 7be80f48835..8c991f0df31 100644 --- a/chromium/third_party/blink/renderer/core/paint/svg_shape_painter.h +++ b/chromium/third_party/blink/renderer/core/paint/svg_shape_painter.h @@ -11,7 +11,6 @@ namespace blink { -class FloatRect; class GraphicsContext; class LayoutSVGResourceMarker; class LayoutSVGShape; @@ -31,7 +30,7 @@ class SVGShapePainter { void FillShape(GraphicsContext&, const PaintFlags&, SkPathFillType); void StrokeShape(GraphicsContext&, const PaintFlags&); - void PaintMarkers(const PaintInfo&, const FloatRect& bounding_box); + void PaintMarkers(const PaintInfo&); void PaintMarker(const PaintInfo&, LayoutSVGResourceMarker&, const MarkerPosition&, 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 8398f6a8f3c..d6a3ec5d4ae 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,14 +8,14 @@ #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" +#include "third_party/blink/renderer/core/paint/svg_model_object_painter.h" namespace blink { void SVGTextPainter::Paint(const PaintInfo& paint_info) { if (paint_info.phase != PaintPhase::kForeground && paint_info.phase != PaintPhase::kForcedColorsModeBackplate && - paint_info.phase != PaintPhase::kSelection) + paint_info.phase != PaintPhase::kSelectionDragImage) return; PaintInfo block_info(paint_info); @@ -28,32 +28,16 @@ void SVGTextPainter::Paint(const PaintInfo& paint_info) { block_info, layout_svg_text_, layout_svg_text_.LocalToSVGParentTransform()); - RecordHitTestData(paint_info); + if (block_info.phase == PaintPhase::kForeground) + SVGModelObjectPainter::RecordHitTestData(layout_svg_text_, block_info); + BlockPainter(layout_svg_text_).Paint(block_info); // Paint the outlines, if any - if (paint_info.phase == PaintPhase::kForeground) { + if (block_info.phase == PaintPhase::kForeground) { block_info.phase = PaintPhase::kOutline; BlockPainter(layout_svg_text_).Paint(block_info); } } -void SVGTextPainter::RecordHitTestData(const PaintInfo& paint_info) { - // 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_svg_text_.EffectiveAllowedTouchAction(); - if (touch_action == TouchAction::kTouchActionAuto) - return; - - auto rect = LayoutRect(layout_svg_text_.VisualRectInLocalSVGCoordinates()); - HitTestDisplayItem::Record(paint_info.context, layout_svg_text_, - HitTestRect(rect, touch_action)); -} - } // namespace blink diff --git a/chromium/third_party/blink/renderer/core/paint/svg_text_painter.h b/chromium/third_party/blink/renderer/core/paint/svg_text_painter.h index cb4e44505c4..b05182c36c8 100644 --- a/chromium/third_party/blink/renderer/core/paint/svg_text_painter.h +++ b/chromium/third_party/blink/renderer/core/paint/svg_text_painter.h @@ -21,11 +21,6 @@ class SVGTextPainter { void Paint(const PaintInfo&); private: - // Paint a hit test display item and record hit test data. This should be - // called when painting the background even if there is no other painted - // content. - void RecordHitTestData(const PaintInfo&); - const LayoutSVGText& layout_svg_text_; }; 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 91436bbf1db..f63f5e08aec 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 @@ -35,7 +35,8 @@ TEST_P(TablePainterTest, Background) { LayoutObject& row2 = *GetLayoutObjectByElementId("row2"); InvalidateAll(RootPaintController()); - GetDocument().View()->UpdateAllLifecyclePhasesExceptPaint(); + GetDocument().View()->UpdateAllLifecyclePhasesExceptPaint( + DocumentUpdateReason::kTest); Paint(IntRect(0, 0, 200, 200)); EXPECT_THAT( @@ -44,7 +45,8 @@ TEST_P(TablePainterTest, Background) { DisplayItem::kDocumentBackground), IsSameId(&row1, DisplayItem::kBoxDecorationBackground))); - GetDocument().View()->UpdateAllLifecyclePhasesExceptPaint(); + GetDocument().View()->UpdateAllLifecyclePhasesExceptPaint( + DocumentUpdateReason::kTest); Paint(IntRect(0, 300, 200, 1000)); EXPECT_THAT( @@ -77,7 +79,8 @@ TEST_P(TablePainterTest, BackgroundWithCellSpacing) { LayoutObject& cell2 = *GetLayoutObjectByElementId("cell2"); InvalidateAll(RootPaintController()); - GetDocument().View()->UpdateAllLifecyclePhasesExceptPaint(); + GetDocument().View()->UpdateAllLifecyclePhasesExceptPaint( + DocumentUpdateReason::kTest); // Intersects cell1 and the spacing between cell1 and cell2. Paint(IntRect(0, 200, 200, 150)); @@ -88,7 +91,8 @@ TEST_P(TablePainterTest, BackgroundWithCellSpacing) { IsSameId(&row1, DisplayItem::kBoxDecorationBackground), IsSameId(&cell1, DisplayItem::kBoxDecorationBackground))); - GetDocument().View()->UpdateAllLifecyclePhasesExceptPaint(); + GetDocument().View()->UpdateAllLifecyclePhasesExceptPaint( + DocumentUpdateReason::kTest); // Intersects the spacing only. Paint(IntRect(0, 250, 100, 100)); @@ -98,7 +102,8 @@ TEST_P(TablePainterTest, BackgroundWithCellSpacing) { DisplayItem::kDocumentBackground), IsSameId(&row1, DisplayItem::kBoxDecorationBackground))); - GetDocument().View()->UpdateAllLifecyclePhasesExceptPaint(); + GetDocument().View()->UpdateAllLifecyclePhasesExceptPaint( + DocumentUpdateReason::kTest); // Intersects cell2 only. Paint(IntRect(0, 350, 200, 150)); @@ -130,7 +135,8 @@ TEST_P(TablePainterTest, BackgroundInSelfPaintingRow) { LayoutObject& row = *GetLayoutObjectByElementId("row"); InvalidateAll(RootPaintController()); - GetDocument().View()->UpdateAllLifecyclePhasesExceptPaint(); + GetDocument().View()->UpdateAllLifecyclePhasesExceptPaint( + DocumentUpdateReason::kTest); // Intersects cell1 and the spacing between cell1 and cell2. Paint(IntRect(200, 0, 200, 200)); @@ -141,7 +147,8 @@ TEST_P(TablePainterTest, BackgroundInSelfPaintingRow) { IsSameId(&row, DisplayItem::kBoxDecorationBackground), IsSameId(&cell1, DisplayItem::kBoxDecorationBackground))); - GetDocument().View()->UpdateAllLifecyclePhasesExceptPaint(); + GetDocument().View()->UpdateAllLifecyclePhasesExceptPaint( + DocumentUpdateReason::kTest); // Intersects the spacing only. Paint(IntRect(300, 0, 100, 100)); @@ -149,7 +156,8 @@ TEST_P(TablePainterTest, BackgroundInSelfPaintingRow) { ElementsAre(IsSameId(&ViewScrollingBackgroundClient(), DisplayItem::kDocumentBackground))); - GetDocument().View()->UpdateAllLifecyclePhasesExceptPaint(); + GetDocument().View()->UpdateAllLifecyclePhasesExceptPaint( + DocumentUpdateReason::kTest); // Intersects cell2 only. Paint(IntRect(450, 0, 200, 200)); @@ -178,7 +186,8 @@ TEST_P(TablePainterTest, CollapsedBorderAndOverflow) { const LayoutNGTableCellInterface* cell = ToInterface<LayoutNGTableCellInterface>(cell_layout_object); InvalidateAll(RootPaintController()); - GetDocument().View()->UpdateAllLifecyclePhasesExceptPaint(); + GetDocument().View()->UpdateAllLifecyclePhasesExceptPaint( + DocumentUpdateReason::kTest); // Intersects the overflowing part of cell but not border box. Paint(IntRect(0, 0, 100, 100)); 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 384e9a722fc..d777b775dc3 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,7 +14,6 @@ #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 { @@ -68,34 +67,16 @@ void TableRowPainter::HandleChangedPartialPaint( paint_result, paint_info.GetCullRect()); } -void TableRowPainter::RecordHitTestData(const PaintInfo& paint_info, - const PhysicalOffset& 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 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_.EffectiveAllowedTouchAction(); - if (touch_action == TouchAction::kTouchActionAuto) - return; - - auto rect = layout_table_row_.PhysicalBorderBoxRect(); - rect.offset += paint_offset; - HitTestDisplayItem::Record(paint_info.context, layout_table_row_, - HitTestRect(rect.ToLayoutRect(), touch_action)); -} - void TableRowPainter::PaintBoxDecorationBackground( const PaintInfo& paint_info, const CellSpan& dirtied_columns) { ScopedPaintState paint_state(layout_table_row_, paint_info); const auto& local_paint_info = paint_state.GetPaintInfo(); auto paint_offset = paint_state.PaintOffset(); - RecordHitTestData(local_paint_info, paint_offset); + PhysicalRect paint_rect(paint_offset, layout_table_row_.Size()); + + BoxPainter(layout_table_row_) + .RecordHitTestData(local_paint_info, paint_rect, layout_table_row_); bool has_background = layout_table_row_.StyleRef().HasBackground(); bool has_box_shadow = layout_table_row_.StyleRef().BoxShadow(); @@ -111,7 +92,6 @@ void TableRowPainter::PaintBoxDecorationBackground( DrawingRecorder recorder(local_paint_info.context, layout_table_row_, DisplayItem::kBoxDecorationBackground); - PhysicalRect paint_rect(paint_offset, layout_table_row_.Size()); if (has_box_shadow) { BoxPainterBase::PaintNormalBoxShadow(local_paint_info, paint_rect, diff --git a/chromium/third_party/blink/renderer/core/paint/table_row_painter.h b/chromium/third_party/blink/renderer/core/paint/table_row_painter.h index 09716046eb4..95dd74fa978 100644 --- a/chromium/third_party/blink/renderer/core/paint/table_row_painter.h +++ b/chromium/third_party/blink/renderer/core/paint/table_row_painter.h @@ -13,7 +13,6 @@ namespace blink { class CellSpan; class LayoutTableRow; struct PaintInfo; -struct PhysicalOffset; class TableRowPainter { STACK_ALLOCATED(); @@ -31,10 +30,6 @@ class TableRowPainter { private: void HandleChangedPartialPaint(const PaintInfo&, const CellSpan& dirtied_columns); - // 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 PhysicalOffset& paint_offset); const LayoutTableRow& layout_table_row_; }; 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 6641f3e2275..374063ce4c3 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 @@ -19,6 +19,7 @@ #include "third_party/blink/renderer/core/paint/table_row_painter.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/scoped_display_item_fragment.h" namespace blink { @@ -43,11 +44,14 @@ void TableSectionPainter::Paint(const PaintInfo& paint_info) { return; } + unsigned fragment_index = 0; for (const auto* fragment = &layout_table_section_.FirstFragment(); fragment; fragment = fragment->NextFragment()) { PaintInfo fragment_paint_info = paint_info; fragment_paint_info.SetFragmentLogicalTopInFlowThread( fragment->LogicalTopInFlowThread()); + ScopedDisplayItemFragment scoped_display_item_fragment( + fragment_paint_info.context, fragment_index++); PaintSection(fragment_paint_info); } } diff --git a/chromium/third_party/blink/renderer/core/paint/text_element_timing.cc b/chromium/third_party/blink/renderer/core/paint/text_element_timing.cc index e6e149f5129..676b3e86c54 100644 --- a/chromium/third_party/blink/renderer/core/paint/text_element_timing.cc +++ b/chromium/third_party/blink/renderer/core/paint/text_element_timing.cc @@ -77,7 +77,7 @@ void TextElementTiming::OnTextObjectPainted(const TextRecord& record) { element); } -void TextElementTiming::Trace(blink::Visitor* visitor) { +void TextElementTiming::Trace(Visitor* visitor) { Supplement<LocalDOMWindow>::Trace(visitor); visitor->Trace(performance_); } diff --git a/chromium/third_party/blink/renderer/core/paint/text_element_timing.h b/chromium/third_party/blink/renderer/core/paint/text_element_timing.h index 664a4aed6cc..10f91076462 100644 --- a/chromium/third_party/blink/renderer/core/paint/text_element_timing.h +++ b/chromium/third_party/blink/renderer/core/paint/text_element_timing.h @@ -52,7 +52,7 @@ class CORE_EXPORT TextElementTiming final // resolved. Dispatches PerformanceElementTiming entries to WindowPerformance. void OnTextObjectPainted(const TextRecord&); - void Trace(blink::Visitor* visitor) override; + void Trace(Visitor* visitor) override; Member<WindowPerformance> performance_; diff --git a/chromium/third_party/blink/renderer/core/paint/text_paint_style.h b/chromium/third_party/blink/renderer/core/paint/text_paint_style.h index e5411fdebd5..e184e7338ce 100644 --- a/chromium/third_party/blink/renderer/core/paint/text_paint_style.h +++ b/chromium/third_party/blink/renderer/core/paint/text_paint_style.h @@ -24,14 +24,16 @@ struct CORE_EXPORT TextPaintStyle { float stroke_width; const ShadowList* shadow; - bool operator==(const TextPaintStyle& other) { + bool operator==(const TextPaintStyle& other) const { return current_color == other.current_color && fill_color == other.fill_color && stroke_color == other.stroke_color && emphasis_mark_color == other.emphasis_mark_color && stroke_width == other.stroke_width && shadow == other.shadow; } - bool operator!=(const TextPaintStyle& other) { return !(*this == other); } + bool operator!=(const TextPaintStyle& other) const { + return !(*this == other); + } }; } // namespace blink 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 93c7e63670a..196fc8e59cd 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 @@ -197,7 +197,7 @@ void TextPaintTimingDetector::StopRecordingLargestTextPaint() { records_manager_.CleanUpLargestTextPaint(); } -void TextPaintTimingDetector::Trace(blink::Visitor* visitor) { +void TextPaintTimingDetector::Trace(Visitor* visitor) { visitor->Trace(records_manager_); visitor->Trace(frame_view_); visitor->Trace(callback_manager_); @@ -210,7 +210,7 @@ LargestTextPaintManager::LargestTextPaintManager( frame_view_(frame_view), paint_timing_detector_(paint_timing_detector) {} -void LargestTextPaintManager::Trace(blink::Visitor* visitor) { +void LargestTextPaintManager::Trace(Visitor* visitor) { visitor->Trace(frame_view_); visitor->Trace(paint_timing_detector_); } @@ -327,7 +327,7 @@ TextRecordsManager::TextRecordsManager( ltp_manager_.emplace(frame_view, paint_timing_detector); } -void TextRecordsManager::Trace(blink::Visitor* visitor) { +void TextRecordsManager::Trace(Visitor* visitor) { visitor->Trace(text_element_timing_); visitor->Trace(ltp_manager_); } 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 500a3003a8c..6f48bf95211 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 @@ -80,7 +80,7 @@ class CORE_EXPORT LargestTextPaintManager { SetCachedResultInvalidated(true); } - void Trace(blink::Visitor*); + void Trace(Visitor*); private: friend class LargestContentfulPaintCalculatorTest; @@ -148,7 +148,7 @@ class CORE_EXPORT TextRecordsManager { return ltp_manager_.has_value(); } - void Trace(blink::Visitor*); + void Trace(Visitor*); private: friend class LargestContentfulPaintCalculatorTest; @@ -216,7 +216,7 @@ class CORE_EXPORT TextPaintTimingDetector final return records_manager_.UpdateCandidate(); } void ReportSwapTime(base::TimeTicks timestamp); - void Trace(blink::Visitor*); + void Trace(Visitor*); private: friend class LargestContentfulPaintCalculatorTest; 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 4cdf7c03b0f..9cec3867170 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 @@ -108,7 +108,11 @@ class TextPaintTimingDetectorTest : public testing::Test { } void SimulateScroll() { - GetPaintTimingDetector().NotifyScroll(ScrollType::kUserScroll); + GetPaintTimingDetector().NotifyScroll(mojom::blink::ScrollType::kUser); + } + + void SimulateKeyUp() { + GetPaintTimingDetector().NotifyInputEvent(WebInputEvent::kKeyUp); } void InvokeCallback() { @@ -145,8 +149,7 @@ class TextPaintTimingDetectorTest : public testing::Test { void SetChildBodyInnerHTML(const String& content) { GetChildDocument()->SetBaseURLOverride(KURL("http://test.com")); - GetChildDocument()->body()->SetInnerHTMLFromString(content, - ASSERT_NO_EXCEPTION); + GetChildDocument()->body()->setInnerHTML(content, ASSERT_NO_EXCEPTION); child_frame_mock_callback_manager_ = MakeGarbageCollected<MockPaintTimingCallbackManager>(); GetChildFrameTextPaintTimingDetector()->ResetCallbackManager( @@ -155,8 +158,7 @@ class TextPaintTimingDetectorTest : public testing::Test { } void UpdateAllLifecyclePhases() { - GetDocument().View()->UpdateAllLifecyclePhases( - DocumentLifecycle::LifecycleUpdateReason::kTest); + GetDocument().View()->UpdateAllLifecyclePhases(DocumentUpdateReason::kTest); } // This only triggers ReportSwapTime in main frame. @@ -236,6 +238,25 @@ class TextPaintTimingDetectorTest : public testing::Test { Persistent<MockPaintTimingCallbackManager> child_frame_mock_callback_manager_; }; +// Helper class to run the same test code with and without LayoutNG +class ParameterizedTextPaintTimingDetectorTest + : public ::testing::WithParamInterface<bool>, + private ScopedLayoutNGForTest, + public TextPaintTimingDetectorTest { + public: + ParameterizedTextPaintTimingDetectorTest() + : ScopedLayoutNGForTest(GetParam()) {} + + protected: + bool LayoutNGEnabled() const { + return RuntimeEnabledFeatures::LayoutNGEnabled(); + } +}; + +INSTANTIATE_TEST_SUITE_P(All, + ParameterizedTextPaintTimingDetectorTest, + testing::Bool()); + TEST_F(TextPaintTimingDetectorTest, LargestTextPaint_NoText) { SetBodyInnerHTML(R"HTML( )HTML"); @@ -422,8 +443,7 @@ TEST_F(TextPaintTimingDetectorTest, PendingTextIsLargest) { SetBodyInnerHTML(R"HTML( )HTML"); AppendDivElementToBody("text"); - GetFrameView().UpdateAllLifecyclePhases( - DocumentLifecycle::LifecycleUpdateReason::kTest); + GetFrameView().UpdateAllLifecyclePhases(DocumentUpdateReason::kTest); // We do not call swap-time callback here in order to not set the paint time. EXPECT_FALSE(TextRecordOfLargestTextPaint()); } @@ -434,12 +454,10 @@ TEST_F(TextPaintTimingDetectorTest, VisitSameNodeTwiceBeforePaintTimeIsSet) { SetBodyInnerHTML(R"HTML( )HTML"); Element* text = AppendDivElementToBody("text"); - GetFrameView().UpdateAllLifecyclePhases( - DocumentLifecycle::LifecycleUpdateReason::kTest); + GetFrameView().UpdateAllLifecyclePhases(DocumentUpdateReason::kTest); // Change a property of the text to trigger repaint. text->setAttribute(html_names::kStyleAttr, AtomicString("color:red;")); - GetFrameView().UpdateAllLifecyclePhases( - DocumentLifecycle::LifecycleUpdateReason::kTest); + GetFrameView().UpdateAllLifecyclePhases(DocumentUpdateReason::kTest); InvokeCallback(); EXPECT_EQ(TextRecordOfLargestTextPaint()->node_id, DOMNodeIds::ExistingIdForNode(text)); @@ -544,6 +562,17 @@ TEST_F(TextPaintTimingDetectorTest, EXPECT_FALSE(GetLargestTextPaintManager()); } +TEST_F(TextPaintTimingDetectorTest, KeepLargestTextPaintMangerAfterUserInput) { + SetBodyInnerHTML(R"HTML( + )HTML"); + AppendDivElementToBody("text"); + UpdateAllLifecyclePhasesAndSimulateSwapTime(); + EXPECT_TRUE(GetLargestTextPaintManager()); + + SimulateKeyUp(); + EXPECT_TRUE(GetLargestTextPaintManager()); +} + TEST_F(TextPaintTimingDetectorTest, LargestTextPaint_ReportLastNullCandidate) { SetBodyInnerHTML(R"HTML( )HTML"); @@ -607,7 +636,7 @@ TEST_F(TextPaintTimingDetectorTest, CaptureFileUploadController) { DOMNodeIds::ExistingIdForNode(element)); } -TEST_F(TextPaintTimingDetectorTest, CapturingListMarkers) { +TEST_P(ParameterizedTextPaintTimingDetectorTest, CapturingListMarkers) { SetBodyInnerHTML(R"HTML( <ul> <li>List item</li> @@ -618,7 +647,7 @@ TEST_F(TextPaintTimingDetectorTest, CapturingListMarkers) { )HTML"); UpdateAllLifecyclePhasesAndSimulateSwapTime(); - EXPECT_EQ(CountVisibleTexts(), 2u); + EXPECT_EQ(CountVisibleTexts(), LayoutNGEnabled() ? 3u : 2u); } TEST_F(TextPaintTimingDetectorTest, CaptureSVGText) { diff --git a/chromium/third_party/blink/renderer/core/paint/text_painter_base.cc b/chromium/third_party/blink/renderer/core/paint/text_painter_base.cc index 419b89adbb9..e6b2b918f1f 100644 --- a/chromium/third_party/blink/renderer/core/paint/text_painter_base.cc +++ b/chromium/third_party/blink/renderer/core/paint/text_painter_base.cc @@ -131,8 +131,7 @@ TextPaintStyle TextPainterBase::TextPaintingStyle(const Document& document, text_style.shadow = style.TextShadow(); // Adjust text color when printing with a white background. - DCHECK(document.Printing() == is_printing || - RuntimeEnabledFeatures::PrintBrowserEnabled()); + DCHECK_EQ(document.Printing(), is_printing); bool force_background_to_white = BoxPainterBase::ShouldForceWhiteBackgroundForPrintEconomy(document, style); @@ -179,6 +178,9 @@ void TextPainterBase::DecorationsStripeIntercepts( // pixel makes sure we're always covering. This should only be done on the // clipping rectangle, not when computing the glyph intersects. clip_rect.InflateY(1.0); + + if (!clip_rect.IsFinite()) + continue; graphics_context_.ClipOut(clip_rect); } } 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 8535fc1e91c..343d3275d4a 100644 --- a/chromium/third_party/blink/renderer/core/paint/theme_painter.cc +++ b/chromium/third_party/blink/renderer/core/paint/theme_painter.cc @@ -24,15 +24,12 @@ #include "build/build_config.h" #include "third_party/blink/public/platform/platform.h" #include "third_party/blink/public/platform/web_rect.h" -#include "third_party/blink/renderer/core/frame/deprecation.h" #include "third_party/blink/renderer/core/frame/local_frame_view.h" #include "third_party/blink/renderer/core/html/forms/html_data_list_element.h" #include "third_party/blink/renderer/core/html/forms/html_data_list_options_collection.h" #include "third_party/blink/renderer/core/html/forms/html_input_element.h" #include "third_party/blink/renderer/core/html/forms/html_option_element.h" #include "third_party/blink/renderer/core/html/forms/html_select_element.h" -#include "third_party/blink/renderer/core/html/forms/html_text_area_element.h" -#include "third_party/blink/renderer/core/html/forms/spin_button_element.h" #include "third_party/blink/renderer/core/html/parser/html_parser_idioms.h" #include "third_party/blink/renderer/core/html/shadow/shadow_element_names.h" #include "third_party/blink/renderer/core/input_type_names.h" @@ -44,6 +41,7 @@ #include "third_party/blink/renderer/platform/graphics/graphics_context_state_saver.h" #include "third_party/blink/renderer/platform/graphics/paint/paint_canvas.h" #include "third_party/blink/renderer/platform/instrumentation/use_counter.h" +#include "ui/base/ui_base_features.h" #include "ui/native_theme/native_theme.h" // The methods in this file are shared by all themes on every platform. @@ -73,28 +71,12 @@ static ui::NativeTheme::ColorScheme ToNativeColorScheme( } } -bool IsTemporalInput(const AtomicString& type) { +bool IsMultipleFieldsTemporalInput(const AtomicString& type) { +#if !defined(OS_ANDROID) return type == input_type_names::kDate || type == input_type_names::kDatetimeLocal || type == input_type_names::kMonth || type == input_type_names::kTime || type == input_type_names::kWeek; -} - -bool IsMenulistInput(const Node* node) { - if (auto* input = DynamicTo<HTMLInputElement>(node)) { -#if defined(OS_ANDROID) - if (IsTemporalInput(input->type())) - return true; -#endif - return input->type() == input_type_names::kColor && - input->FastHasAttribute(html_names::kListAttr); - } - return false; -} - -bool IsMultipleFieldsTemporalInput(const AtomicString& type) { -#if !defined(OS_ANDROID) - return IsTemporalInput(type); #else return false; #endif @@ -106,33 +88,23 @@ ThemePainter::ThemePainter() = default; #define COUNT_APPEARANCE(doc, feature) \ doc.CountUse(WebFeature::kCSSValueAppearance##feature##Rendered) -#define DEPRECATE_APPEARANCE(doc, feature) \ - Deprecation::CountDeprecation( \ - doc, WebFeature::kCSSValueAppearance##feature##Rendered) void CountAppearanceTextFieldPart(const Node* node) { - if (!node) { - return; - } - UseCounter::Count(node->GetDocument(), - WebFeature::kCSSValueAppearanceTextFieldRendered); - WebFeature feature = - WebFeature::kCSSValueAppearanceTextFieldForOthersRendered; + DCHECK(node); if (auto* input = DynamicTo<HTMLInputElement>(node)) { const AtomicString& type = input->type(); if (type == input_type_names::kSearch) { - feature = WebFeature::kCSSValueAppearanceTextFieldForSearch; + UseCounter::Count(node->GetDocument(), + WebFeature::kCSSValueAppearanceTextFieldForSearch); } else if (input->IsTextField()) { - feature = WebFeature::kCSSValueAppearanceTextFieldForTextField; + UseCounter::Count(node->GetDocument(), + WebFeature::kCSSValueAppearanceTextFieldForTextField); } else if (IsMultipleFieldsTemporalInput(type)) { - feature = WebFeature::kCSSValueAppearanceTextFieldForTemporalRendered; + UseCounter::Count( + node->GetDocument(), + WebFeature::kCSSValueAppearanceTextFieldForTemporalRendered); } } - if (feature == WebFeature::kCSSValueAppearanceTextFieldForOthersRendered) { - Deprecation::CountDeprecation(node->GetDocument(), feature); - } else { - UseCounter::Count(node->GetDocument(), feature); - } } // Returns true; Needs CSS painting and/or PaintBorderOnly(). @@ -143,18 +115,16 @@ bool ThemePainter::Paint(const LayoutObject& o, Document& doc = o.GetDocument(); const ComputedStyle& style = o.StyleRef(); ControlPart part = o.StyleRef().EffectiveAppearance(); + // LayoutTheme::AdjustAppearanceWithElementType() ensures |node| is a + // non-null Element. + DCHECK(node); + DCHECK_NE(part, kNoControlPart); if (LayoutTheme::GetTheme().ShouldUseFallbackTheme(style)) return PaintUsingFallbackTheme(node, style, paint_info, r); - // TODO(tkent): Clean the counting code when M80 is promoted to the stable - // channel. - if (part == kButtonPart && node) { - if (IsA<HTMLAnchorElement>(node)) { - Deprecation::CountDeprecation( - doc, WebFeature::kCSSValueAppearanceButtonForAnchor); - COUNT_APPEARANCE(doc, ButtonForNonButton); - } else if (IsA<HTMLButtonElement>(node)) { + if (part == kButtonPart) { + if (IsA<HTMLButtonElement>(node)) { UseCounter::Count(doc, WebFeature::kCSSValueAppearanceButtonForButton); } else if (IsA<HTMLInputElement>(node) && To<HTMLInputElement>(node)->IsTextButton()) { @@ -166,22 +136,6 @@ bool ThemePainter::Paint(const LayoutObject& o, To<HTMLInputElement>(node)->type() == input_type_names::kColor) { // 'button' for input[type=color], of which default appearance is // 'square-button', is not deprecated. - } else { - COUNT_APPEARANCE(doc, ButtonForNonButton); - COUNT_APPEARANCE(doc, ButtonForOthers); - if (IsA<HTMLSelectElement>(node) && - To<HTMLSelectElement>(node)->UsesMenuList()) { - DEPRECATE_APPEARANCE(doc, ButtonForSelect); - } else { - const AtomicString& type = - To<Element>(node)->getAttribute(html_names::kTypeAttr); - // https://github.com/twbs/bootstrap/pull/29053 - if (type == "button" || type == "reset" || type == "submit") { - DEPRECATE_APPEARANCE(doc, ButtonForBootstrapLooseSelector); - } else { - DEPRECATE_APPEARANCE(doc, ButtonForOthers2); - } - } } } @@ -189,30 +143,18 @@ bool ThemePainter::Paint(const LayoutObject& o, switch (part) { case kCheckboxPart: { COUNT_APPEARANCE(doc, Checkbox); - auto* input = DynamicTo<HTMLInputElement>(node); - if (!input || input->type() != input_type_names::kCheckbox) - DEPRECATE_APPEARANCE(doc, CheckboxForOthers); return PaintCheckbox(node, o.GetDocument(), style, paint_info, r); } case kRadioPart: { COUNT_APPEARANCE(doc, Radio); - auto* input = DynamicTo<HTMLInputElement>(node); - if (!input || input->type() != input_type_names::kRadio) - DEPRECATE_APPEARANCE(doc, RadioForOthers); return PaintRadio(node, o.GetDocument(), style, paint_info, r); } case kPushButtonPart: { COUNT_APPEARANCE(doc, PushButton); - auto* input = DynamicTo<HTMLInputElement>(node); - if (!input || !input->IsTextButton()) - DEPRECATE_APPEARANCE(doc, PushButtonForOthers); return PaintButton(node, o.GetDocument(), style, paint_info, r); } case kSquareButtonPart: { COUNT_APPEARANCE(doc, SquareButton); - auto* input = DynamicTo<HTMLInputElement>(node); - if (!input || input->type() != input_type_names::kColor) - DEPRECATE_APPEARANCE(doc, SquareButtonForOthers); return PaintButton(node, o.GetDocument(), style, paint_info, r); } case kButtonPart: @@ -220,54 +162,31 @@ bool ThemePainter::Paint(const LayoutObject& o, return PaintButton(node, o.GetDocument(), style, paint_info, r); case kInnerSpinButtonPart: { COUNT_APPEARANCE(doc, InnerSpinButton); - if (!DynamicTo<SpinButtonElement>(node)) - DEPRECATE_APPEARANCE(doc, InnerSpinButtonForOthers); return PaintInnerSpinButton(node, style, paint_info, r); } case kMenulistPart: COUNT_APPEARANCE(doc, MenuList); - if (!IsA<HTMLSelectElement>(node) && !IsMenulistInput(node)) - DEPRECATE_APPEARANCE(doc, MenuListForOthers); return PaintMenuList(node, o.GetDocument(), style, paint_info, r); case kMeterPart: - if (node && !IsA<HTMLMeterElement>(node) && - !IsA<HTMLMeterElement>(node->OwnerShadowHost())) - DEPRECATE_APPEARANCE(doc, MeterForOthers); return true; case kProgressBarPart: COUNT_APPEARANCE(doc, ProgressBar); - if (!o.IsProgress()) - DEPRECATE_APPEARANCE(doc, ProgressBarForOthers); // Note that |-webkit-appearance: progress-bar| works only for <progress>. return PaintProgressBar(o, paint_info, r); case kSliderHorizontalPart: { COUNT_APPEARANCE(doc, SliderHorizontal); - auto* input = DynamicTo<HTMLInputElement>(node); - if (!input || input->type() != input_type_names::kRange) - DEPRECATE_APPEARANCE(doc, SliderHorizontalForOthers); return PaintSliderTrack(o, paint_info, r); } case kSliderVerticalPart: { COUNT_APPEARANCE(doc, SliderVertical); - auto* input = DynamicTo<HTMLInputElement>(node); - if (!input || input->type() != input_type_names::kRange) - DEPRECATE_APPEARANCE(doc, SliderVerticalForOthers); return PaintSliderTrack(o, paint_info, r); } case kSliderThumbHorizontalPart: { COUNT_APPEARANCE(doc, SliderThumbHorizontal); - auto* input = - DynamicTo<HTMLInputElement>(node ? node->OwnerShadowHost() : nullptr); - if (!input || input->type() != input_type_names::kRange) - DEPRECATE_APPEARANCE(doc, SliderThumbHorizontalForOthers); return PaintSliderThumb(node, style, paint_info, r); } case kSliderThumbVerticalPart: { COUNT_APPEARANCE(doc, SliderThumbVertical); - auto* input = - DynamicTo<HTMLInputElement>(node ? node->OwnerShadowHost() : nullptr); - if (!input || input->type() != input_type_names::kRange) - DEPRECATE_APPEARANCE(doc, SliderThumbVerticalForOthers); return PaintSliderThumb(node, style, paint_info, r); } case kMediaSliderPart: @@ -278,53 +197,26 @@ bool ThemePainter::Paint(const LayoutObject& o, case kMenulistButtonPart: return true; case kTextFieldPart: - if (!RuntimeEnabledFeatures::FormControlsRefreshEnabled()) { + if (!features::IsFormControlsRefreshEnabled()) { return true; } CountAppearanceTextFieldPart(node); return PaintTextField(node, style, paint_info, r); case kTextAreaPart: - if (!RuntimeEnabledFeatures::FormControlsRefreshEnabled()) { + if (!features::IsFormControlsRefreshEnabled()) { return true; } - if (node) { - const auto& doc = node->GetDocument(); - COUNT_APPEARANCE(doc, TextArea); - if (!IsA<HTMLTextAreaElement>(node)) - DEPRECATE_APPEARANCE(doc, TextAreaForOthers); - } + COUNT_APPEARANCE(doc, TextArea); return PaintTextArea(node, style, paint_info, r); case kSearchFieldPart: { COUNT_APPEARANCE(doc, SearchField); - auto* input = DynamicTo<HTMLInputElement>(node); - if (!input || input->type() != input_type_names::kSearch) - DEPRECATE_APPEARANCE(doc, SearchFieldForOthers); return PaintSearchField(node, style, paint_info, r); } case kSearchFieldCancelButtonPart: { COUNT_APPEARANCE(doc, SearchCancel); - auto* element = DynamicTo<Element>(node); - if (!element || !element->OwnerShadowHost()) { - COUNT_APPEARANCE(doc, SearchCancelForOthers); - DEPRECATE_APPEARANCE(doc, SearchCancelForOthers2); - } else { - const AtomicString& shadow_id = - element->FastGetAttribute(html_names::kIdAttr); - if (shadow_id == shadow_element_names::SearchClearButton()) { - // Count nothing. - } else if (shadow_id == shadow_element_names::ClearButton()) { - COUNT_APPEARANCE(doc, SearchCancelForOthers); - } else { - COUNT_APPEARANCE(doc, SearchCancelForOthers); - DEPRECATE_APPEARANCE(doc, SearchCancelForOthers2); - } - } return PaintSearchFieldCancelButton(o, paint_info, r); } case kListboxPart: - if (!IsA<HTMLSelectElement>(node) || - To<HTMLSelectElement>(node)->UsesMenuList()) - DEPRECATE_APPEARANCE(doc, ListboxForOthers); return true; default: break; @@ -342,21 +234,16 @@ bool ThemePainter::PaintBorderOnly(const Node* node, // Call the appropriate paint method based off the appearance value. switch (style.EffectiveAppearance()) { case kTextFieldPart: - if (RuntimeEnabledFeatures::FormControlsRefreshEnabled()) { + if (features::IsFormControlsRefreshEnabled()) { return false; } CountAppearanceTextFieldPart(node); return PaintTextField(node, style, paint_info, r); case kTextAreaPart: - if (RuntimeEnabledFeatures::FormControlsRefreshEnabled()) { + if (features::IsFormControlsRefreshEnabled()) { return false; } - if (node) { - const auto& doc = node->GetDocument(); - COUNT_APPEARANCE(doc, TextArea); - if (!IsA<HTMLTextAreaElement>(node)) - DEPRECATE_APPEARANCE(doc, TextAreaForOthers); - } + COUNT_APPEARANCE(node->GetDocument(), TextArea); return PaintTextArea(node, style, paint_info, r); case kMenulistButtonPart: case kSearchFieldPart: @@ -378,11 +265,9 @@ bool ThemePainter::PaintBorderOnly(const Node* node, // Supported appearance values don't need CSS border painting. return false; default: - if (node) { - UseCounter::Count( - node->GetDocument(), - WebFeature::kCSSValueAppearanceNoImplementationSkipBorder); - } + UseCounter::Count( + node->GetDocument(), + WebFeature::kCSSValueAppearanceNoImplementationSkipBorder); // TODO(tkent): Should do CSS border painting for non-supported // appearance values. return false; @@ -400,8 +285,6 @@ bool ThemePainter::PaintDecorations(const Node* node, switch (style.EffectiveAppearance()) { case kMenulistButtonPart: COUNT_APPEARANCE(document, MenuListButton); - if (!IsA<HTMLSelectElement>(node) && !IsMenulistInput(node)) - DEPRECATE_APPEARANCE(document, MenuListButtonForOthers); return PaintMenuListButton(node, document, style, paint_info, r); case kTextFieldPart: case kTextAreaPart: 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 cc25ef54157..9da24a1c670 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 @@ -38,6 +38,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 "ui/base/ui_base_features.h" namespace blink { @@ -151,8 +152,7 @@ bool ThemePainterDefault::PaintCheckbox(const Node* node, extra_params.button.zoom = zoom_level; GraphicsContextStateSaver state_saver(paint_info.context, false); IntRect unzoomed_rect = rect; - if (zoom_level != 1 && - !RuntimeEnabledFeatures::FormControlsRefreshEnabled()) { + if (zoom_level != 1 && !features::IsFormControlsRefreshEnabled()) { state_saver.Save(); unzoomed_rect.SetWidth(unzoomed_rect.Width() / zoom_level); unzoomed_rect.SetHeight(unzoomed_rect.Height() / zoom_level); @@ -227,12 +227,15 @@ bool ThemePainterDefault::PaintTextField(const Node* node, WebThemeEngine::ExtraParams extra_params; extra_params.text_field.is_text_area = part == kTextAreaPart; extra_params.text_field.is_listbox = part == kListboxPart; + extra_params.text_field.has_border = true; cc::PaintCanvas* canvas = paint_info.context.Canvas(); Color background_color = style.VisitedDependentColor(GetCSSPropertyBackgroundColor()); extra_params.text_field.background_color = background_color.Rgb(); + extra_params.text_field.auto_complete_active = + DynamicTo<HTMLFormControlElement>(node)->IsAutofilled(); Platform::Current()->ThemeEngine()->Paint( canvas, WebThemeEngine::kPartTextField, GetWebThemeState(node), @@ -307,9 +310,8 @@ void ThemePainterDefault::SetupMenuListArrow( theme_.ClampedMenuListArrowPaddingSize(document.GetFrame(), style); float arrow_scale_factor = arrow_box_width / theme_.MenuListArrowWidthInDIP(); // TODO(tkent): This should be 7.0 to match scroll bar buttons. - float arrow_size = - (RuntimeEnabledFeatures::FormControlsRefreshEnabled() ? 8.0 : 6.0) * - arrow_scale_factor; + float arrow_size = (features::IsFormControlsRefreshEnabled() ? 8.0 : 6.0) * + arrow_scale_factor; // Put the arrow at the center of paddingForArrow area. // |arrowX| is the left position for Aura theme engine. extra_params.menu_list.arrow_x = @@ -336,8 +338,7 @@ bool ThemePainterDefault::PaintSliderTrack(const LayoutObject& o, extra_params.slider.zoom = zoom_level; GraphicsContextStateSaver state_saver(i.context, false); IntRect unzoomed_rect = rect; - if (zoom_level != 1 && - !RuntimeEnabledFeatures::FormControlsRefreshEnabled()) { + if (zoom_level != 1 && !features::IsFormControlsRefreshEnabled()) { state_saver.Save(); unzoomed_rect.SetWidth(unzoomed_rect.Width() / zoom_level); unzoomed_rect.SetHeight(unzoomed_rect.Height() / zoom_level); @@ -357,7 +358,7 @@ bool ThemePainterDefault::PaintSliderTrack(const LayoutObject& o, LayoutBox* thumb = thumb_element ? thumb_element->GetLayoutBox() : nullptr; if (thumb) { IntRect thumb_rect = PixelSnappedIntRect(thumb->FrameRect()); - if (RuntimeEnabledFeatures::FormControlsRefreshEnabled()) { + if (features::IsFormControlsRefreshEnabled()) { extra_params.slider.thumb_x = thumb_rect.X(); extra_params.slider.thumb_y = thumb_rect.Y(); } else { @@ -387,8 +388,7 @@ bool ThemePainterDefault::PaintSliderThumb(const Node* node, extra_params.slider.zoom = zoom_level; GraphicsContextStateSaver state_saver(paint_info.context, false); IntRect unzoomed_rect = rect; - if (zoom_level != 1 && - !RuntimeEnabledFeatures::FormControlsRefreshEnabled()) { + if (zoom_level != 1 && !features::IsFormControlsRefreshEnabled()) { state_saver.Save(); unzoomed_rect.SetWidth(unzoomed_rect.Width() / zoom_level); unzoomed_rect.SetHeight(unzoomed_rect.Height() / zoom_level); 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 27c37014ec4..b15d3ec197a 100644 --- a/chromium/third_party/blink/renderer/core/paint/video_painter.cc +++ b/chromium/third_party/blink/renderer/core/paint/video_painter.cc @@ -41,6 +41,11 @@ void VideoPainter::PaintReplaced(const PaintInfo& paint_info, PhysicalRect content_box_rect = layout_video_.PhysicalContentBoxRect(); content_box_rect.Move(paint_offset); + if (context.IsPaintingPreview()) { + context.SetURLForRect(layout_video_.GetDocument().Url(), + snapped_replaced_rect); + } + // Since we may have changed the location of the replaced content, we need to // notify PaintArtifactCompositor. if (layout_video_.GetFrameView()) 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 3e03db3ede0..954e9b4fb13 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 @@ -4,9 +4,13 @@ #include "third_party/blink/renderer/core/paint/video_painter.h" +#include "base/unguessable_token.h" #include "cc/layers/layer.h" +#include "components/paint_preview/common/paint_preview_tracker.h" #include "third_party/blink/public/platform/platform.h" #include "third_party/blink/public/platform/web_size.h" +#include "third_party/blink/renderer/core/frame/frame_test_helpers.h" +#include "third_party/blink/renderer/core/frame/web_local_frame_impl.h" #include "third_party/blink/renderer/core/html/media/html_media_element.h" #include "third_party/blink/renderer/core/paint/paint_controller_paint_test.h" #include "third_party/blink/renderer/platform/testing/empty_web_media_player.h" @@ -86,17 +90,87 @@ TEST_F(VideoPainterTestForCAP, VideoLayerAppearsInLayerTree) { // Fetch the layer associated with the <video>, and check that it was // correctly configured in the layer tree. - HTMLMediaElement* element = - ToHTMLMediaElement(GetDocument().body()->firstChild()); + auto* element = To<HTMLMediaElement>(GetDocument().body()->firstChild()); StubWebMediaPlayer* player = static_cast<StubWebMediaPlayer*>(element->GetWebMediaPlayer()); const cc::Layer* layer = player->GetCcLayer(); ASSERT_TRUE(layer); EXPECT_TRUE(HasLayerAttached(*layer)); - // The layer bounds reflects the aspectn ratio and object-fit of the video. - EXPECT_EQ(gfx::Vector2dF(8, 83), layer->offset_to_transform_parent()); + // The layer bounds reflects the aspect ratio and object-fit of the video. + EXPECT_EQ(gfx::Vector2dF(0, 75), layer->offset_to_transform_parent()); EXPECT_EQ(gfx::Size(300, 150), layer->bounds()); } +class VideoPaintPreviewTest : public testing::Test, + public PaintTestConfigurations { + public: + void SetUp() override { + web_view_helper_.Initialize(); + + WebLocalFrameImpl& frame_impl = GetLocalMainFrame(); + frame_impl.ViewImpl()->MainFrameWidget()->Resize(WebSize(bounds().size())); + + frame_test_helpers::LoadFrame(&GetLocalMainFrame(), "about:blank"); + GetDocument().View()->SetParentVisible(true); + GetDocument().View()->SetSelfVisible(true); + } + + void SetBodyInnerHTML(const std::string& content) { + frame_test_helpers::LoadHTMLString(&GetLocalMainFrame(), content, + KURL("http://test.com")); + } + + Document& GetDocument() { return *GetFrame()->GetDocument(); } + + WebLocalFrameImpl& GetLocalMainFrame() { + return *web_view_helper_.LocalMainFrame(); + } + + const gfx::Rect& bounds() { return bounds_; } + + private: + LocalFrame* GetFrame() { return GetLocalMainFrame().GetFrame(); } + + frame_test_helpers::WebViewHelper web_view_helper_; + gfx::Rect bounds_ = {0, 0, 640, 480}; +}; + +INSTANTIATE_PAINT_TEST_SUITE_P(VideoPaintPreviewTest); + +TEST_P(VideoPaintPreviewTest, URLIsRecordedWhenPaintingPreview) { + // Insert a <video> and allow it to begin loading. The image was taken from + // the RFC for the data URI scheme https://tools.ietf.org/html/rfc2397. + SetBodyInnerHTML(R"HTML( + <style>body{margin:0}</style> + <video width=300 height=300 src="test.ogv" poster="data:image/gif;base64,R0 + lGODdhMAAwAPAAAAAAAP///ywAAAAAMAAwAAAC8IyPqcvt3wCcDkiLc7C0qwyGHhSWpjQu5yq + mCYsapyuvUUlvONmOZtfzgFzByTB10QgxOR0TqBQejhRNzOfkVJ+5YiUqrXF5Y5lKh/DeuNcP + 5yLWGsEbtLiOSpa/TPg7JpJHxyendzWTBfX0cxOnKPjgBzi4diinWGdkF8kjdfnycQZXZeYGe + jmJlZeGl9i2icVqaNVailT6F5iJ90m6mvuTS4OK05M0vDk0Q4XUtwvKOzrcd3iq9uisF81M1O + IcR7lEewwcLp7tuNNkM3uNna3F2JQFo97Vriy/Xl4/f1cf5VWzXyym7PHhhx4dbgYKAAA7" + controls> + )HTML"); + test::RunPendingTasks(); + + auto token = base::UnguessableToken::Create(); + const base::UnguessableToken embedding_token = + base::UnguessableToken::Create(); + const bool is_main_frame = true; + + cc::PaintRecorder recorder; + paint_preview::PaintPreviewTracker tracker(token, embedding_token, + is_main_frame); + cc::PaintCanvas* canvas = + recorder.beginRecording(bounds().width(), bounds().height()); + canvas->SetPaintPreviewTracker(&tracker); + + EXPECT_EQ(0lu, tracker.GetLinks().size()); + GetLocalMainFrame().CapturePaintPreview(WebRect(bounds()), canvas); + + ASSERT_EQ(1lu, tracker.GetLinks().size()); + EXPECT_EQ("http://test.com/", tracker.GetLinks()[0].url); + EXPECT_EQ(gfx::Rect(300, 300), tracker.GetLinks()[0].rect); +} + } // namespace } // namespace blink 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 2b8c3cc46eb..1ce2481e0a0 100644 --- a/chromium/third_party/blink/renderer/core/paint/view_painter.cc +++ b/chromium/third_party/blink/renderer/core/paint/view_painter.cc @@ -33,6 +33,38 @@ void ViewPainter::Paint(const PaintInfo& paint_info) { BlockPainter(layout_view_).Paint(paint_info); } +// Behind the root element of the main frame of the page, there is an infinite +// canvas. This is by default white, but it can be overridden by +// BaseBackgroundColor on the LocalFrameView. +// https://drafts.fxtf.org/compositing/#rootgroup +void ViewPainter::PaintRootGroup(const PaintInfo& paint_info, + const IntRect& pixel_snapped_background_rect, + const Document& document, + const DisplayItemClient& client, + const PropertyTreeState& state) { + if (!document.IsInMainFrame()) + return; + bool should_clear_canvas = + document.GetSettings() && + document.GetSettings()->GetShouldClearDocumentBackground(); + + Color base_background_color = + layout_view_.GetFrameView()->BaseBackgroundColor(); + + ScopedPaintChunkProperties frame_view_background_state( + paint_info.context.GetPaintController(), state, client, + DisplayItem::kDocumentRootBackdrop); + GraphicsContext& context = paint_info.context; + if (!DrawingRecorder::UseCachedDrawingIfPossible( + context, client, DisplayItem::kDocumentRootBackdrop)) { + DrawingRecorder recorder(context, client, + DisplayItem::kDocumentRootBackdrop); + context.FillRect( + pixel_snapped_background_rect, base_background_color, + should_clear_canvas ? SkBlendMode::kSrc : SkBlendMode::kSrcOver); + } +} + void ViewPainter::PaintBoxDecorationBackground(const PaintInfo& paint_info) { if (layout_view_.StyleRef().Visibility() != EVisibility::kVisible) return; @@ -57,7 +89,6 @@ void ViewPainter::PaintBoxDecorationBackground(const PaintInfo& paint_info) { const DisplayItemClient* background_client = &layout_view_; - base::Optional<ScopedPaintChunkProperties> scoped_scroll_property; bool painting_scrolling_background = BoxDecorationData::IsPaintingScrollingBackground(paint_info, layout_view_); @@ -71,20 +102,84 @@ void ViewPainter::PaintBoxDecorationBackground(const PaintInfo& paint_info) { background_rect.Unite(document_rect); background_client = &layout_view_.GetScrollableArea() ->GetScrollingBackgroundDisplayItemClient(); - scoped_scroll_property.emplace( - paint_info.context.GetPaintController(), - layout_view_.FirstFragment().ContentsProperties(), *background_client, - DisplayItem::kDocumentBackground); } - if (layout_view_.HasBoxDecorationBackground()) { - PaintBoxDecorationBackgroundInternal( - paint_info, PixelSnappedIntRect(background_rect), *background_client); + IntRect pixel_snapped_background_rect(PixelSnappedIntRect(background_rect)); + + const Document& document = layout_view_.GetDocument(); + + PropertyTreeState root_element_background_painting_state = + layout_view_.FirstFragment().ContentsProperties(); + + base::Optional<ScopedPaintChunkProperties> scoped_properties; + + bool painted_separate_backdrop = false; + bool painted_separate_effect = false; + + bool should_apply_root_background_behavior = + layout_view_.GetDocument().IsHTMLDocument() || + layout_view_.GetDocument().IsXHTMLDocument(); + + bool should_paint_background = !paint_info.SkipRootBackground() && + layout_view_.HasBoxDecorationBackground(); + + LayoutObject* root_object = nullptr; + if (layout_view_.GetDocument().documentElement()) { + root_object = + layout_view_.GetDocument().documentElement()->GetLayoutObject(); + } + + // For HTML and XHTML documents, the root element may paint in a different + // clip, effect or transform state than the LayoutView. For + // example, the HTML element may have a clip-path, filter, blend-mode, + // opacity or transform. + // + // In these cases, we should paint the background of the root element in + // its LocalBorderBoxProperties() state, as part of the Root Element Group + // [1]. In addition, for the main frame of the page, we also need to paint the + // default backdrop color in the Root Group [2]. The Root Group paints in + // the scrolling space of the LayoutView (i.e. its ContentsProperties()). + // + // [1] https://drafts.fxtf.org/compositing/#pagebackdrop + // [2] https://drafts.fxtf.org/compositing/#rootgroup + if (should_paint_background && painting_scrolling_background && + should_apply_root_background_behavior && root_object) { + const PropertyTreeState& document_element_state = + root_object->FirstFragment().LocalBorderBoxProperties(); + + // As an optimization, only paint a separate PaintChunk for the + // root group if its property tree state differs from root element + // group's. Otherwise we can usually avoid both a separate + // PaintChunk and a BeginLayer/EndLayer. + if (document_element_state != root_element_background_painting_state) { + if (&document_element_state.Effect() != + &root_element_background_painting_state.Effect()) + painted_separate_effect = true; + + root_element_background_painting_state = document_element_state; + PaintRootGroup(paint_info, pixel_snapped_background_rect, document, + *background_client, + layout_view_.FirstFragment().ContentsProperties()); + painted_separate_backdrop = true; + } + } + + if (painting_scrolling_background) { + scoped_properties.emplace(paint_info.context.GetPaintController(), + root_element_background_painting_state, + *background_client, + DisplayItem::kDocumentBackground); + } + + if (should_paint_background) { + PaintRootElementGroup(paint_info, pixel_snapped_background_rect, + *background_client, painted_separate_backdrop, + painted_separate_effect); } if (has_touch_action_rect) { BoxPainter(layout_view_) .RecordHitTestData(paint_info, - PhysicalRect(PixelSnappedIntRect(background_rect)), + PhysicalRect(pixel_snapped_background_rect), *background_client); } @@ -119,22 +214,23 @@ void ViewPainter::PaintBoxDecorationBackground(const PaintInfo& paint_info) { // 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. +// element. However, this method assumes that there is already an +// PaintChunk being recorded with the LocalBorderBoxProperties of the +// root element. Therefore the transform of the root element +// are applied via PaintChunksToCcLayer, and not via the display list of the +// PaintChunk itself. // 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. +// cover the whole canvas. // 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( +void ViewPainter::PaintRootElementGroup( 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; - + const IntRect& pixel_snapped_background_rect, + const DisplayItemClient& background_client, + bool painted_separate_backdrop, + bool painted_separate_effect) { GraphicsContext& context = paint_info.context; if (DrawingRecorder::UseCachedDrawingIfPossible( context, background_client, DisplayItem::kDocumentBackground)) { @@ -149,8 +245,9 @@ void ViewPainter::PaintBoxDecorationBackgroundInternal( (frame_view.BaseBackgroundColor().Alpha() > 0); Color base_background_color = paints_base_background ? frame_view.BaseBackgroundColor() : Color(); - Color root_background_color = layout_view_.StyleRef().VisitedDependentColor( - GetCSSPropertyBackgroundColor()); + Color root_element_background_color = + layout_view_.StyleRef().VisitedDependentColor( + GetCSSPropertyBackgroundColor()); const LayoutObject* root_object = document.documentElement() ? document.documentElement()->GetLayoutObject() : nullptr; @@ -162,9 +259,11 @@ void ViewPainter::PaintBoxDecorationBackgroundInternal( if (force_background_to_white) { // 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().AnyLayerHasImage()) - context.FillRect(background_rect, Color::kWhite, SkBlendMode::kSrc); + if (paints_base_background || root_element_background_color.Alpha() || + layout_view_.StyleRef().BackgroundLayers().AnyLayerHasImage()) { + context.FillRect(pixel_snapped_background_rect, Color::kWhite, + SkBlendMode::kSrc); + } return; } @@ -177,49 +276,39 @@ void ViewPainter::PaintBoxDecorationBackgroundInternal( // transform on the document rect to get to the root element space. // Local / scroll positioned background images will be painted into scrolling // contents layer with root layer scrolling. Therefore we need to switch both - // the background_rect and context to documentElement visual space. + // the pixel_snapped_background_rect and context to documentElement visual + // space. bool background_renderable = true; - TransformationMatrix transform; - IntRect paint_rect = background_rect; + bool root_element_has_transform = false; + IntRect paint_rect = pixel_snapped_background_rect; if (!root_object || !root_object->IsBox()) { background_renderable = false; - } else if (root_object->HasLayer()) { - if (BoxDecorationData::IsPaintingScrollingBackground(paint_info, - layout_view_)) { - transform.Translate( - layout_view_.PixelSnappedScrolledContentOffset().Width(), - layout_view_.PixelSnappedScrolledContentOffset().Height()); - } - const PaintLayer& root_layer = - *ToLayoutBoxModelObject(root_object)->Layer(); - PhysicalOffset offset; - root_layer.ConvertToLayerCoords(nullptr, offset); - transform.Translate(offset.left, offset.top); - transform.Multiply( - root_layer.RenderableTransform(paint_info.GetGlobalPaintFlags())); - - if (!transform.IsInvertible()) { + } else { + root_element_has_transform = root_object->StyleRef().HasTransform(); + TransformationMatrix transform; + root_object->GetTransformFromContainer(root_object->View(), + PhysicalOffset(), transform); + if (!transform.IsInvertible()) background_renderable = false; - } else { - bool is_clamped; - paint_rect = transform.Inverse() - .ProjectQuad(FloatQuad(background_rect), &is_clamped) - .EnclosingBoundingBox(); - background_renderable = !is_clamped; - } + else + paint_rect = transform.Inverse().MapRect(pixel_snapped_background_rect); } bool should_clear_canvas = paints_base_background && (document.GetSettings() && document.GetSettings()->GetShouldClearDocumentBackground()); + if (!background_renderable) { - if (base_background_color.Alpha()) { - context.FillRect( - background_rect, base_background_color, - should_clear_canvas ? SkBlendMode::kSrc : SkBlendMode::kSrcOver); - } else if (should_clear_canvas) { - context.FillRect(background_rect, Color(), SkBlendMode::kClear); + if (!painted_separate_backdrop) { + if (base_background_color.Alpha()) { + context.FillRect( + pixel_snapped_background_rect, base_background_color, + should_clear_canvas ? SkBlendMode::kSrc : SkBlendMode::kSrcOver); + } else if (should_clear_canvas) { + context.FillRect(pixel_snapped_background_rect, Color(), + SkBlendMode::kClear); + } } return; } @@ -231,21 +320,30 @@ void ViewPainter::PaintBoxDecorationBackgroundInternal( reversed_paint_list, layout_view_.StyleRef().BackgroundLayers()); DCHECK(reversed_paint_list.size()); - // If the root background color is opaque, isolation group can be skipped - // because the canvas - // will be cleared by root background color. - if (!root_background_color.HasAlpha()) - should_draw_background_in_separate_buffer = false; - - // We are going to clear the canvas with transparent pixels, isolation group - // can be skipped. - if (!base_background_color.Alpha() && should_clear_canvas) - should_draw_background_in_separate_buffer = false; + if (painted_separate_effect) { + should_draw_background_in_separate_buffer = true; + } else { + // If the root background color is opaque, isolation group can be skipped + // because the canvas + // will be cleared by root background color. + if (!root_element_background_color.HasAlpha()) + should_draw_background_in_separate_buffer = false; + + // We are going to clear the canvas with transparent pixels, isolation group + // can be skipped. + if (!base_background_color.Alpha() && should_clear_canvas) + should_draw_background_in_separate_buffer = false; + } - if (should_draw_background_in_separate_buffer) { + // Only use BeginLayer if not only we should draw in a separate buffer, but + // we also didn't paint a separate backdrop. Separate backdrops are always + // painted when there is any effect on the root element, such as a blend + // mode. An extra BeginLayer will result in incorrect blend isolation if + // it is added on top of any effect on the root element. + if (should_draw_background_in_separate_buffer && !painted_separate_effect) { if (base_background_color.Alpha()) { context.FillRect( - background_rect, base_background_color, + paint_rect, base_background_color, should_clear_canvas ? SkBlendMode::kSrc : SkBlendMode::kSrcOver); } context.BeginLayer(); @@ -253,48 +351,38 @@ void ViewPainter::PaintBoxDecorationBackgroundInternal( Color combined_background_color = should_draw_background_in_separate_buffer - ? root_background_color - : base_background_color.Blend(root_background_color); + ? root_element_background_color + : base_background_color.Blend(root_element_background_color); if (combined_background_color != frame_view.BaseBackgroundColor()) context.GetPaintController().SetFirstPainted(); if (combined_background_color.Alpha()) { - if (!combined_background_color.HasAlpha() && - RuntimeEnabledFeatures::CompositeAfterPaintEnabled()) - recorder.SetKnownToBeOpaque(); context.FillRect( - background_rect, combined_background_color, + paint_rect, combined_background_color, (should_draw_background_in_separate_buffer || should_clear_canvas) ? SkBlendMode::kSrc : SkBlendMode::kSrcOver); } else if (should_clear_canvas && !should_draw_background_in_separate_buffer) { - context.FillRect(background_rect, Color(), SkBlendMode::kClear); + context.FillRect(paint_rect, Color(), SkBlendMode::kClear); } - BackgroundImageGeometry geometry(layout_view_); + BackgroundImageGeometry geometry(layout_view_, root_element_has_transform); BoxModelObjectPainter box_model_painter(layout_view_); for (const auto* fill_layer : base::Reversed(reversed_paint_list)) { DCHECK(fill_layer->Clip() == EFillBox::kBorder); - if (BackgroundImageGeometry::ShouldUseFixedAttachment(*fill_layer)) { - box_model_painter.PaintFillLayer(paint_info, Color(), *fill_layer, - PhysicalRect(background_rect), - kBackgroundBleedNone, geometry); - } else { - context.Save(); - // TODO(trchen): We should be able to handle 3D-transformed root - // background with slimming paint by using transform display items. - context.ConcatCTM(transform.ToAffineTransform()); - box_model_painter.PaintFillLayer(paint_info, Color(), *fill_layer, - PhysicalRect(paint_rect), - kBackgroundBleedNone, geometry); - context.Restore(); - } + PhysicalRect painting_rect(paint_rect); + if (!BackgroundImageGeometry::ShouldUseFixedAttachment(*fill_layer)) + painting_rect.Move(root_object->FirstFragment().PaintOffset()); + + box_model_painter.PaintFillLayer(paint_info, Color(), *fill_layer, + painting_rect, kBackgroundBleedNone, + geometry); } - if (should_draw_background_in_separate_buffer) + if (should_draw_background_in_separate_buffer && !painted_separate_effect) context.EndLayer(); } 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 652ffd6cb9e..921839b76f8 100644 --- a/chromium/third_party/blink/renderer/core/paint/view_painter.h +++ b/chromium/third_party/blink/renderer/core/paint/view_painter.h @@ -11,8 +11,10 @@ namespace blink { struct PaintInfo; class DisplayItemClient; +class Document; class IntRect; class LayoutView; +class PropertyTreeState; class ViewPainter { STACK_ALLOCATED(); @@ -26,10 +28,17 @@ class ViewPainter { private: const LayoutView& layout_view_; - void PaintBoxDecorationBackgroundInternal( - const PaintInfo&, - const IntRect& background_rect, - const DisplayItemClient& background_client); + void PaintRootElementGroup(const PaintInfo&, + const IntRect& pixel_snapped_background_rect, + const DisplayItemClient& background_client, + bool painted_separate_backdrop, + bool painted_separate_effect); + + void PaintRootGroup(const PaintInfo& paint_info, + const IntRect& pixel_snapped_background_rect, + const Document&, + const DisplayItemClient& background_client, + const PropertyTreeState& state); }; } // 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 60d2ceb43db..e26b77a20d5 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 @@ -9,20 +9,19 @@ #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/scroll_hit_test_display_item.h" using testing::ElementsAre; namespace blink { -class ViewPainterTest : public PaintControllerPaintTest { +class ViewPainterFixedBackgroundTest : public PaintControllerPaintTest { protected: void RunFixedBackgroundTest(bool prefer_compositing_to_lcd_text); }; -INSTANTIATE_PAINT_TEST_SUITE_P(ViewPainterTest); +INSTANTIATE_PAINT_TEST_SUITE_P(ViewPainterFixedBackgroundTest); -void ViewPainterTest::RunFixedBackgroundTest( +void ViewPainterFixedBackgroundTest::RunFixedBackgroundTest( bool prefer_compositing_to_lcd_text) { if (prefer_compositing_to_lcd_text) { Settings* settings = GetDocument().GetFrame()->GetSettings(); @@ -45,27 +44,19 @@ void ViewPainterTest::RunFixedBackgroundTest( ScrollableArea* layout_viewport = frame_view->LayoutViewport(); ScrollOffset scroll_offset(200, 150); - layout_viewport->SetScrollOffset(scroll_offset, kUserScroll); - frame_view->UpdateAllLifecyclePhases( - DocumentLifecycle::LifecycleUpdateReason::kTest); + layout_viewport->SetScrollOffset(scroll_offset, + mojom::blink::ScrollType::kUser); + frame_view->UpdateAllLifecyclePhases(DocumentUpdateReason::kTest); const DisplayItem* background_display_item = nullptr; if (RuntimeEnabledFeatures::CompositeAfterPaintEnabled()) { const auto& display_items = RootPaintController().GetDisplayItemList(); - if (prefer_compositing_to_lcd_text) { - EXPECT_THAT( - display_items, - ElementsAre(IsSameId(&GetLayoutView(), kDocumentBackgroundType), - IsSameId(&GetLayoutView(), DisplayItem::kScrollHitTest))); - background_display_item = &display_items[0]; - } else { - EXPECT_THAT( - display_items, - ElementsAre(IsSameId(&GetLayoutView(), DisplayItem::kScrollHitTest), - IsSameId(&ViewScrollingBackgroundClient(), - kDocumentBackgroundType))); - background_display_item = &display_items[1]; - } + const auto& background_client = prefer_compositing_to_lcd_text + ? GetLayoutView() + : ViewScrollingBackgroundClient(); + EXPECT_THAT(display_items, ElementsAre(IsSameId(&background_client, + kDocumentBackgroundType))); + background_display_item = &display_items[0]; } else { // 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 @@ -108,66 +99,65 @@ void ViewPainterTest::RunFixedBackgroundTest( } } -TEST_P(ViewPainterTest, DocumentFixedBackgroundLowDPI) { +TEST_P(ViewPainterFixedBackgroundTest, DocumentFixedBackgroundLowDPI) { RunFixedBackgroundTest(false); } -TEST_P(ViewPainterTest, DocumentFixedBackgroundHighDPI) { +TEST_P(ViewPainterFixedBackgroundTest, DocumentFixedBackgroundHighDPI) { RunFixedBackgroundTest(true); } -using ViewPainterScrollHitTestTest = PaintControllerPaintTest; +using ViewPainterTest = PaintControllerPaintTest; -INSTANTIATE_SCROLL_HIT_TEST_SUITE_P(ViewPainterScrollHitTestTest); +INSTANTIATE_PAINT_TEST_SUITE_P(ViewPainterTest); -TEST_P(ViewPainterScrollHitTestTest, DocumentBackgroundWithScroll) { +TEST_P(ViewPainterTest, DocumentBackgroundWithScroll) { SetBodyInnerHTML(R"HTML( <style>::-webkit-scrollbar { display: none }</style> <div style='height: 5000px'></div> )HTML"); + const auto& scrolling_contents_properties = + GetLayoutView().FirstFragment().ContentsProperties(); + if (RuntimeEnabledFeatures::CompositeAfterPaintEnabled()) { + EXPECT_THAT(RootPaintController().GetDisplayItemList(), + ElementsAre(IsSameId(&ViewScrollingBackgroundClient(), + kDocumentBackgroundType))); + HitTestData scroll_hit_test_data; + scroll_hit_test_data.scroll_translation = + &scrolling_contents_properties.Transform(); + scroll_hit_test_data.scroll_hit_test_rect = IntRect(0, 0, 800, 600); // The scroll hit test should be before the scrolled contents to ensure the // hit test does not prevent the background squashing with the scrolling // contents. EXPECT_THAT( - RootPaintController().GetDisplayItemList(), - ElementsAre(IsSameId(&GetLayoutView(), DisplayItem::kScrollHitTest), - IsSameId(&ViewScrollingBackgroundClient(), - kDocumentBackgroundType))); - HitTestData scroll_hit_test_data; - const auto& scrolling_contents_properties = - GetLayoutView().FirstFragment().ContentsProperties(); - scroll_hit_test_data.SetScrollHitTest( - &scrolling_contents_properties.Transform(), IntRect(0, 0, 800, 600)); - EXPECT_THAT( RootPaintController().PaintChunks(), ElementsAre( IsPaintChunk( - 0, 1, + 0, 0, PaintChunk::Id(GetLayoutView(), DisplayItem::kScrollHitTest), GetLayoutView().FirstFragment().LocalBorderBoxProperties(), - scroll_hit_test_data), - IsPaintChunk(1, 2, + &scroll_hit_test_data, IntRect(0, 0, 800, 600)), + IsPaintChunk(0, 1, PaintChunk::Id(ViewScrollingBackgroundClient(), kDocumentBackgroundType), scrolling_contents_properties))); } else { - // Because the frame composited scrolls, no scroll hit test display item is - // needed. + // Because the frame composited scrolls, no scroll hit test data is needed. EXPECT_THAT(RootPaintController().GetDisplayItemList(), ElementsAre(IsSameId(&ViewScrollingBackgroundClient(), kDocumentBackgroundType))); - EXPECT_THAT(RootPaintController().PaintChunks(), - ElementsAre(IsPaintChunk( - 0, 1, - PaintChunk::Id(ViewScrollingBackgroundClient(), - kDocumentBackgroundType), - GetLayoutView().FirstFragment().ContentsProperties()))); + EXPECT_THAT( + RootPaintController().PaintChunks(), + ElementsAre(IsPaintChunk(0, 1, + PaintChunk::Id(ViewScrollingBackgroundClient(), + kDocumentBackgroundType), + scrolling_contents_properties))); } } -TEST_P(ViewPainterScrollHitTestTest, FrameScrollHitTestProperties) { +TEST_P(ViewPainterTest, FrameScrollHitTestProperties) { // This test depends on the CompositeAfterPaint behavior of painting solid // color backgrounds into both the non-scrolled and scrolled spaces. if (!RuntimeEnabledFeatures::CompositeAfterPaintEnabled()) @@ -181,16 +171,10 @@ TEST_P(ViewPainterScrollHitTestTest, FrameScrollHitTestProperties) { <div id='child'></div> )HTML"); - auto& html = - To<LayoutBlock>(*GetDocument().documentElement()->GetLayoutObject()); auto& child = *GetLayoutObjectByElementId("child"); - // The scroll hit test should be before the scrolled contents to ensure the - // hit test does not prevent the background squashing with the scrolling - // contents. EXPECT_THAT(RootPaintController().GetDisplayItemList(), - ElementsAre(IsSameId(&GetLayoutView(), kScrollHitTestType), - IsSameId(&ViewScrollingBackgroundClient(), + ElementsAre(IsSameId(&ViewScrollingBackgroundClient(), kDocumentBackgroundType), IsSameId(&child, kBackgroundType))); @@ -198,24 +182,24 @@ TEST_P(ViewPainterScrollHitTestTest, FrameScrollHitTestProperties) { const auto& view_contents_properties = GetLayoutView().FirstFragment().ContentsProperties(); HitTestData scroll_hit_test_data; - scroll_hit_test_data.SetScrollHitTest(&view_contents_properties.Transform(), - IntRect(0, 0, 800, 600)); + scroll_hit_test_data.scroll_translation = + &view_contents_properties.Transform(); + scroll_hit_test_data.scroll_hit_test_rect = IntRect(0, 0, 800, 600); + // The scroll hit test should be before the scrolled contents to ensure the + // hit test does not prevent the background squashing with the scrolling + // contents. EXPECT_THAT( paint_chunks, ElementsAre( IsPaintChunk( - 0, 1, + 0, 0, PaintChunk::Id(GetLayoutView(), DisplayItem::kScrollHitTest), GetLayoutView().FirstFragment().LocalBorderBoxProperties(), - scroll_hit_test_data), - IsPaintChunk(1, 2, + &scroll_hit_test_data), + IsPaintChunk(0, 2, PaintChunk::Id(ViewScrollingBackgroundClient(), kDocumentBackgroundType), - view_contents_properties), - IsPaintChunk(2, 3, - PaintChunk::Id(*html.Layer(), - kNonScrollingContentsBackgroundChunkType), - html.FirstFragment().ContentsProperties()))); + view_contents_properties))); // The scroll hit test should not be scrolled and should not be clipped. const auto& scroll_hit_test_chunk = RootPaintController().PaintChunks()[0]; @@ -224,25 +208,22 @@ TEST_P(ViewPainterScrollHitTestTest, FrameScrollHitTestProperties) { EXPECT_EQ(nullptr, scroll_hit_test_transform.ScrollNode()); const auto& scroll_hit_test_clip = scroll_hit_test_chunk.properties.Clip(); EXPECT_EQ(FloatRect(LayoutRect::InfiniteIntRect()), - scroll_hit_test_clip.ClipRect().Rect()); + scroll_hit_test_clip.UnsnappedClipRect().Rect()); // The scrolled contents should be scrolled and clipped. - const auto& contents_chunk = RootPaintController().PaintChunks()[2]; + const auto& contents_chunk = RootPaintController().PaintChunks()[1]; 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()); const auto& contents_clip = contents_chunk.properties.Clip(); - EXPECT_EQ(FloatRect(0, 0, 800, 600), contents_clip.ClipRect().Rect()); + EXPECT_EQ(FloatRect(0, 0, 800, 600), + contents_clip.UnsnappedClipRect().Rect()); - // The scroll hit test display item maintains a reference to a scroll offset + // The scroll hit test paint chunk maintains a reference to a scroll offset // translation node and the contents should be scrolled by this node. - const auto& scroll_hit_test_display_item = - static_cast<const ScrollHitTestDisplayItem&>( - RootPaintController() - .GetDisplayItemList()[scroll_hit_test_chunk.begin_index]); EXPECT_EQ(&contents_transform, - scroll_hit_test_display_item.scroll_offset_node()); + scroll_hit_test_chunk.hit_test_data->scroll_translation); } class ViewPainterTouchActionRectTest : public ViewPainterTest { @@ -278,56 +259,42 @@ TEST_P(ViewPainterTouchActionRectTest, TouchActionRectScrollingContents) { 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 = - To<LayoutBlock>(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)); + view_hit_test_data.touch_action_rects = {{IntRect(0, 0, 800, 3000)}, + {IntRect(0, 0, 800, 3000)}, + {IntRect(0, 0, 800, 3000)}}; if (RuntimeEnabledFeatures::CompositeAfterPaintEnabled()) { HitTestData non_scrolling_hit_test_data; - non_scrolling_hit_test_data.touch_action_rects.emplace_back( - LayoutRect(0, 0, 800, 600)); + non_scrolling_hit_test_data.touch_action_rects = { + {IntRect(0, 0, 800, 600)}}; HitTestData scroll_hit_test_data; - scroll_hit_test_data.SetScrollHitTest(&scrolling_properties.Transform(), - IntRect(0, 0, 800, 600)); + scroll_hit_test_data.scroll_translation = &scrolling_properties.Transform(); + scroll_hit_test_data.scroll_hit_test_rect = IntRect(0, 0, 800, 600); EXPECT_THAT( RootPaintController().PaintChunks(), ElementsAre( IsPaintChunk( - 0, 1, + 0, 0, PaintChunk::Id(*GetLayoutView().Layer(), - DisplayItem::kLayerChunkBackground), + DisplayItem::kLayerChunk), GetLayoutView().FirstFragment().LocalBorderBoxProperties(), - non_scrolling_hit_test_data), + &non_scrolling_hit_test_data, IntRect(0, 0, 800, 600)), IsPaintChunk( - 1, 2, + 0, 0, PaintChunk::Id(GetLayoutView(), DisplayItem::kScrollHitTest), GetLayoutView().FirstFragment().LocalBorderBoxProperties(), - scroll_hit_test_data), + &scroll_hit_test_data, IntRect(0, 0, 800, 600)), IsPaintChunk( - 2, 4, PaintChunk::Id(scrolling_client, kDocumentBackgroundType), - scrolling_properties, view_hit_test_data), - IsPaintChunk(4, 6, - PaintChunk::Id(*html->Layer(), - kNonScrollingBackgroundChunkType), - scrolling_properties, html_hit_test_data))); + 0, 1, PaintChunk::Id(scrolling_client, kDocumentBackgroundType), + scrolling_properties, &view_hit_test_data, + IntRect(0, 0, 800, 3000)))); } else { EXPECT_THAT( RootPaintController().PaintChunks(), - ElementsAre( - IsPaintChunk( - 0, 2, PaintChunk::Id(scrolling_client, kDocumentBackgroundType), - scrolling_properties, view_hit_test_data), - IsPaintChunk(2, 4, - PaintChunk::Id(*html->Layer(), - kNonScrollingBackgroundChunkType), - scrolling_properties, html_hit_test_data))); + ElementsAre(IsPaintChunk( + 0, 1, PaintChunk::Id(scrolling_client, kDocumentBackgroundType), + scrolling_properties, &view_hit_test_data, + IntRect(0, 0, 800, 3000)))); } } @@ -354,49 +321,46 @@ TEST_P(ViewPainterTouchActionRectTest, TouchActionRectNonScrollingContents) { 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 = - To<LayoutBlock>(GetDocument().documentElement()->GetLayoutObject()); + view_hit_test_data.touch_action_rects = {{IntRect(0, 0, 800, 600)}}; + auto* html = GetDocument().documentElement()->GetLayoutBox(); 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)); + scrolling_hit_test_data.touch_action_rects = {{IntRect(0, 0, 800, 3000)}, + {IntRect(0, 0, 800, 3000)}}; if (RuntimeEnabledFeatures::CompositeAfterPaintEnabled()) { HitTestData scroll_hit_test_data; - scroll_hit_test_data.SetScrollHitTest(&scrolling_properties.Transform(), - IntRect(0, 0, 800, 600)); + scroll_hit_test_data.scroll_translation = &scrolling_properties.Transform(); + scroll_hit_test_data.scroll_hit_test_rect = IntRect(0, 0, 800, 600); EXPECT_THAT( RootPaintController().PaintChunks(), ElementsAre( - IsPaintChunk(0, 2, - PaintChunk::Id(*view->Layer(), - DisplayItem::kLayerChunkBackground), - non_scrolling_properties, view_hit_test_data), - IsPaintChunk(2, 3, + IsPaintChunk( + 0, 1, PaintChunk::Id(*view->Layer(), DisplayItem::kLayerChunk), + non_scrolling_properties, &view_hit_test_data, + IntRect(0, 0, 800, 600)), + IsPaintChunk(1, 1, PaintChunk::Id(*view, DisplayItem::kScrollHitTest), - non_scrolling_properties, scroll_hit_test_data), - IsPaintChunk(3, 5, - PaintChunk::Id(*html->Layer(), - kNonScrollingBackgroundChunkType), - scrolling_properties, scrolling_hit_test_data))); + non_scrolling_properties, &scroll_hit_test_data, + IntRect(0, 0, 800, 600)), + IsPaintChunk( + 1, 1, PaintChunk::Id(*html->Layer(), DisplayItem::kLayerChunk), + scrolling_properties, &scrolling_hit_test_data, + IntRect(0, 0, 800, 3000)))); } 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))); + 0, 1, PaintChunk::Id(*view->Layer(), DisplayItem::kLayerChunk), + non_scrolling_properties, &view_hit_test_data, + IntRect(0, 0, 800, 600)))); EXPECT_THAT( RootPaintController().PaintChunks(), ElementsAre(IsPaintChunk( - 0, 2, - PaintChunk::Id(*html->Layer(), kNonScrollingBackgroundChunkType), - scrolling_properties, scrolling_hit_test_data))); + 0, 0, PaintChunk::Id(*html->Layer(), DisplayItem::kLayerChunk), + scrolling_properties, &scrolling_hit_test_data, + IntRect(0, 0, 800, 3000)))); } } |