summaryrefslogtreecommitdiff
path: root/chromium/third_party/blink/renderer/core/paint
diff options
context:
space:
mode:
Diffstat (limited to 'chromium/third_party/blink/renderer/core/paint')
-rw-r--r--chromium/third_party/blink/renderer/core/paint/DEPS2
-rw-r--r--chromium/third_party/blink/renderer/core/paint/OWNERS4
-rw-r--r--chromium/third_party/blink/renderer/core/paint/background_image_geometry.cc17
-rw-r--r--chromium/third_party/blink/renderer/core/paint/background_image_geometry.h3
-rw-r--r--chromium/third_party/blink/renderer/core/paint/block_flow_paint_invalidator.cc1
-rw-r--r--chromium/third_party/blink/renderer/core/paint/block_painter.cc9
-rw-r--r--chromium/third_party/blink/renderer/core/paint/block_painter_test.cc20
-rw-r--r--chromium/third_party/blink/renderer/core/paint/box_border_painter.cc904
-rw-r--r--chromium/third_party/blink/renderer/core/paint/box_border_painter.h139
-rw-r--r--chromium/third_party/blink/renderer/core/paint/box_decoration_data.h1
-rw-r--r--chromium/third_party/blink/renderer/core/paint/box_model_object_painter.cc17
-rw-r--r--chromium/third_party/blink/renderer/core/paint/box_model_object_painter.h2
-rw-r--r--chromium/third_party/blink/renderer/core/paint/box_painter_base.cc471
-rw-r--r--chromium/third_party/blink/renderer/core/paint/box_painter_base.h5
-rw-r--r--chromium/third_party/blink/renderer/core/paint/build.gni6
-rw-r--r--chromium/third_party/blink/renderer/core/paint/clip_path_clipper.cc143
-rw-r--r--chromium/third_party/blink/renderer/core/paint/collapsed_border_painter.cc26
-rw-r--r--chromium/third_party/blink/renderer/core/paint/compositing/composited_layer_mapping.cc6
-rw-r--r--chromium/third_party/blink/renderer/core/paint/compositing/composited_layer_mapping_test.cc2
-rw-r--r--chromium/third_party/blink/renderer/core/paint/compositing/compositing_layer_property_updater.cc10
-rw-r--r--chromium/third_party/blink/renderer/core/paint/compositing/compositing_reason_finder.cc55
-rw-r--r--chromium/third_party/blink/renderer/core/paint/compositing/compositing_reason_finder.h3
-rw-r--r--chromium/third_party/blink/renderer/core/paint/compositing/compositing_reason_finder_test.cc71
-rw-r--r--chromium/third_party/blink/renderer/core/paint/compositing/compositing_test.cc320
-rw-r--r--chromium/third_party/blink/renderer/core/paint/compositing/paint_layer_compositor.cc2
-rw-r--r--chromium/third_party/blink/renderer/core/paint/cull_rect_updater.cc147
-rw-r--r--chromium/third_party/blink/renderer/core/paint/cull_rect_updater.h23
-rw-r--r--chromium/third_party/blink/renderer/core/paint/document_marker_painter.cc8
-rw-r--r--chromium/third_party/blink/renderer/core/paint/embedded_object_painter.cc6
-rw-r--r--chromium/third_party/blink/renderer/core/paint/filter_effect_builder.cc11
-rw-r--r--chromium/third_party/blink/renderer/core/paint/find_paint_offset_needing_update.h1
-rw-r--r--chromium/third_party/blink/renderer/core/paint/find_properties_needing_update.h1
-rw-r--r--chromium/third_party/blink/renderer/core/paint/first_meaningful_paint_detector.cc1
-rw-r--r--chromium/third_party/blink/renderer/core/paint/fragment_data.h37
-rw-r--r--chromium/third_party/blink/renderer/core/paint/highlight_painting_utils.cc56
-rw-r--r--chromium/third_party/blink/renderer/core/paint/highlight_painting_utils.h37
-rw-r--r--chromium/third_party/blink/renderer/core/paint/image_paint_timing_detector.cc17
-rw-r--r--chromium/third_party/blink/renderer/core/paint/image_paint_timing_detector.h2
-rw-r--r--chromium/third_party/blink/renderer/core/paint/image_paint_timing_detector_test.cc16
-rw-r--r--chromium/third_party/blink/renderer/core/paint/image_painter.cc13
-rw-r--r--chromium/third_party/blink/renderer/core/paint/inline_text_box_painter.cc38
-rw-r--r--chromium/third_party/blink/renderer/core/paint/inline_text_box_painter.h3
-rw-r--r--chromium/third_party/blink/renderer/core/paint/inline_text_box_painter_test.cc61
-rw-r--r--chromium/third_party/blink/renderer/core/paint/line_box_list_painter.cc1
-rw-r--r--chromium/third_party/blink/renderer/core/paint/link_highlight_impl.cc8
-rw-r--r--chromium/third_party/blink/renderer/core/paint/link_highlight_impl.h2
-rw-r--r--chromium/third_party/blink/renderer/core/paint/list_marker_painter.cc21
-rw-r--r--chromium/third_party/blink/renderer/core/paint/multi_column_set_painter.cc6
-rw-r--r--chromium/third_party/blink/renderer/core/paint/ng/ng_box_fragment_painter.cc370
-rw-r--r--chromium/third_party/blink/renderer/core/paint/ng/ng_box_fragment_painter.h28
-rw-r--r--chromium/third_party/blink/renderer/core/paint/ng/ng_box_fragment_painter_test.cc22
-rw-r--r--chromium/third_party/blink/renderer/core/paint/ng/ng_fragment_painter.cc4
-rw-r--r--chromium/third_party/blink/renderer/core/paint/ng/ng_fragment_painter.h5
-rw-r--r--chromium/third_party/blink/renderer/core/paint/ng/ng_highlight_painter.cc71
-rw-r--r--chromium/third_party/blink/renderer/core/paint/ng/ng_highlight_painter.h7
-rw-r--r--chromium/third_party/blink/renderer/core/paint/ng/ng_inline_box_fragment_painter.cc11
-rw-r--r--chromium/third_party/blink/renderer/core/paint/ng/ng_inline_box_fragment_painter.h1
-rw-r--r--chromium/third_party/blink/renderer/core/paint/ng/ng_mathml_painter.cc11
-rw-r--r--chromium/third_party/blink/renderer/core/paint/ng/ng_table_painters.cc22
-rw-r--r--chromium/third_party/blink/renderer/core/paint/ng/ng_text_combine_painter.cc127
-rw-r--r--chromium/third_party/blink/renderer/core/paint/ng/ng_text_combine_painter.h46
-rw-r--r--chromium/third_party/blink/renderer/core/paint/ng/ng_text_fragment_painter.cc175
-rw-r--r--chromium/third_party/blink/renderer/core/paint/ng/ng_text_fragment_painter.h1
-rw-r--r--chromium/third_party/blink/renderer/core/paint/ng/ng_text_painter.cc353
-rw-r--r--chromium/third_party/blink/renderer/core/paint/ng/ng_text_painter.h34
-rw-r--r--chromium/third_party/blink/renderer/core/paint/nine_piece_image_painter.cc48
-rw-r--r--chromium/third_party/blink/renderer/core/paint/object_paint_invalidator.h1
-rw-r--r--chromium/third_party/blink/renderer/core/paint/object_paint_properties.h3
-rw-r--r--chromium/third_party/blink/renderer/core/paint/object_painter.cc4
-rw-r--r--chromium/third_party/blink/renderer/core/paint/object_painter.h3
-rw-r--r--chromium/third_party/blink/renderer/core/paint/object_painter_base.cc666
-rw-r--r--chromium/third_party/blink/renderer/core/paint/object_painter_base.h63
-rw-r--r--chromium/third_party/blink/renderer/core/paint/outline_painter.cc724
-rw-r--r--chromium/third_party/blink/renderer/core/paint/outline_painter.h33
-rw-r--r--chromium/third_party/blink/renderer/core/paint/paint_and_raster_invalidation_test.cc56
-rw-r--r--chromium/third_party/blink/renderer/core/paint/paint_and_raster_invalidation_test.h4
-rw-r--r--chromium/third_party/blink/renderer/core/paint/paint_controller_paint_test.cc38
-rw-r--r--chromium/third_party/blink/renderer/core/paint/paint_controller_paint_test.h30
-rw-r--r--chromium/third_party/blink/renderer/core/paint/paint_info.h73
-rw-r--r--chromium/third_party/blink/renderer/core/paint/paint_invalidator.cc65
-rw-r--r--chromium/third_party/blink/renderer/core/paint/paint_invalidator.h10
-rw-r--r--chromium/third_party/blink/renderer/core/paint/paint_layer.cc304
-rw-r--r--chromium/third_party/blink/renderer/core/paint/paint_layer.h35
-rw-r--r--chromium/third_party/blink/renderer/core/paint/paint_layer_clipper.cc2
-rw-r--r--chromium/third_party/blink/renderer/core/paint/paint_layer_clipper.h2
-rw-r--r--chromium/third_party/blink/renderer/core/paint/paint_layer_fragment.h1
-rw-r--r--chromium/third_party/blink/renderer/core/paint/paint_layer_painter.cc207
-rw-r--r--chromium/third_party/blink/renderer/core/paint/paint_layer_painter.h2
-rw-r--r--chromium/third_party/blink/renderer/core/paint/paint_layer_painter_test.cc446
-rw-r--r--chromium/third_party/blink/renderer/core/paint/paint_layer_painting_info.h5
-rw-r--r--chromium/third_party/blink/renderer/core/paint/paint_layer_scrollable_area.cc48
-rw-r--r--chromium/third_party/blink/renderer/core/paint/paint_layer_scrollable_area.h9
-rw-r--r--chromium/third_party/blink/renderer/core/paint/paint_layer_scrollable_area_test.cc7
-rw-r--r--chromium/third_party/blink/renderer/core/paint/paint_layer_stacking_node.cc2
-rw-r--r--chromium/third_party/blink/renderer/core/paint/paint_layer_stacking_node.h2
-rw-r--r--chromium/third_party/blink/renderer/core/paint/paint_layer_test.cc51
-rw-r--r--chromium/third_party/blink/renderer/core/paint/paint_property_tree_builder.cc378
-rw-r--r--chromium/third_party/blink/renderer/core/paint/paint_property_tree_builder.h63
-rw-r--r--chromium/third_party/blink/renderer/core/paint/paint_property_tree_builder_test.cc19
-rw-r--r--chromium/third_party/blink/renderer/core/paint/paint_property_tree_printer.cc8
-rw-r--r--chromium/third_party/blink/renderer/core/paint/paint_property_tree_update_tests.cc30
-rw-r--r--chromium/third_party/blink/renderer/core/paint/paint_timing.cc40
-rw-r--r--chromium/third_party/blink/renderer/core/paint/paint_timing.h10
-rw-r--r--chromium/third_party/blink/renderer/core/paint/paint_timing_detector.cc1
-rw-r--r--chromium/third_party/blink/renderer/core/paint/paint_timing_detector.h1
-rw-r--r--chromium/third_party/blink/renderer/core/paint/pre_paint_tree_walk.cc900
-rw-r--r--chromium/third_party/blink/renderer/core/paint/pre_paint_tree_walk.h183
-rw-r--r--chromium/third_party/blink/renderer/core/paint/rounded_border_geometry.cc35
-rw-r--r--chromium/third_party/blink/renderer/core/paint/rounded_border_geometry.h6
-rw-r--r--chromium/third_party/blink/renderer/core/paint/scoped_paint_state.cc22
-rw-r--r--chromium/third_party/blink/renderer/core/paint/scoped_paint_state.h5
-rw-r--r--chromium/third_party/blink/renderer/core/paint/scoped_svg_paint_state.h1
-rw-r--r--chromium/third_party/blink/renderer/core/paint/scrollable_area_painter.cc24
-rw-r--r--chromium/third_party/blink/renderer/core/paint/scrollable_area_painter_test.cc54
-rw-r--r--chromium/third_party/blink/renderer/core/paint/selection_bounds_recorder.cc17
-rw-r--r--chromium/third_party/blink/renderer/core/paint/selection_bounds_recorder_test.cc206
-rw-r--r--chromium/third_party/blink/renderer/core/paint/svg_container_painter_test.cc1
-rw-r--r--chromium/third_party/blink/renderer/core/paint/svg_image_painter.cc7
-rw-r--r--chromium/third_party/blink/renderer/core/paint/svg_inline_text_box_painter.cc36
-rw-r--r--chromium/third_party/blink/renderer/core/paint/svg_inline_text_box_painter.h1
-rw-r--r--chromium/third_party/blink/renderer/core/paint/svg_mask_painter.cc5
-rw-r--r--chromium/third_party/blink/renderer/core/paint/svg_object_painter.cc14
-rw-r--r--chromium/third_party/blink/renderer/core/paint/svg_object_painter.h4
-rw-r--r--chromium/third_party/blink/renderer/core/paint/svg_shape_painter.cc14
-rw-r--r--chromium/third_party/blink/renderer/core/paint/table_paint_invalidator.cc1
-rw-r--r--chromium/third_party/blink/renderer/core/paint/table_section_painter.cc9
-rw-r--r--chromium/third_party/blink/renderer/core/paint/text_decoration_info.cc7
-rw-r--r--chromium/third_party/blink/renderer/core/paint/text_decoration_info.h2
-rw-r--r--chromium/third_party/blink/renderer/core/paint/text_paint_style.h1
-rw-r--r--chromium/third_party/blink/renderer/core/paint/text_paint_timing_detector_test.cc15
-rw-r--r--chromium/third_party/blink/renderer/core/paint/text_painter.cc43
-rw-r--r--chromium/third_party/blink/renderer/core/paint/text_painter.h8
-rw-r--r--chromium/third_party/blink/renderer/core/paint/text_painter_base.cc251
-rw-r--r--chromium/third_party/blink/renderer/core/paint/text_painter_base.h55
-rw-r--r--chromium/third_party/blink/renderer/core/paint/theme_painter.cc20
-rw-r--r--chromium/third_party/blink/renderer/core/paint/theme_painter_default.cc137
-rw-r--r--chromium/third_party/blink/renderer/core/paint/theme_painter_default.h2
137 files changed, 6023 insertions, 3600 deletions
diff --git a/chromium/third_party/blink/renderer/core/paint/DEPS b/chromium/third_party/blink/renderer/core/paint/DEPS
index 00b071d198a..6fb36eba5f9 100644
--- a/chromium/third_party/blink/renderer/core/paint/DEPS
+++ b/chromium/third_party/blink/renderer/core/paint/DEPS
@@ -8,7 +8,7 @@ include_rules = [
]
specific_include_rules = {
- "(theme_painter|theme_painter_default|object_painter_base)\.cc": [
+ "(theme_painter|theme_painter_default|outline_painter)\.cc": [
"+ui/native_theme/native_theme.h",
"+ui/native_theme/native_theme_base.h",
"+ui/gfx/color_utils.h"
diff --git a/chromium/third_party/blink/renderer/core/paint/OWNERS b/chromium/third_party/blink/renderer/core/paint/OWNERS
index 51aafa98fea..3c3161d0130 100644
--- a/chromium/third_party/blink/renderer/core/paint/OWNERS
+++ b/chromium/third_party/blink/renderer/core/paint/OWNERS
@@ -1,11 +1,7 @@
# OWNERS specializing in painting code.
-
chrishtr@chromium.org
-enne@chromium.org
fmalita@chromium.org
fs@opera.com
pdr@chromium.org
schenney@chromium.org
-senorblanco@chromium.org
-trchen@chromium.org
wangxianzhu@chromium.org
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 193ab56845d..67e8f8c2d90 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
@@ -62,7 +62,7 @@ PhysicalOffset AccumulatedScrollOffsetForFixedBackground(
block && !skip_info.AncestorSkipped();
block = block->ContainingBlock(&skip_info)) {
if (block->IsScrollContainer())
- result += PhysicalOffsetToBeNoop(block->ScrolledContentOffset());
+ result += block->ScrolledContentOffset();
if (block == container)
break;
}
@@ -355,7 +355,7 @@ PhysicalRect FixedAttachmentPositioningArea(
return rect;
// The LayoutView is the only object that can paint a fixed background into
// its scrolling contents layer, so it gets a special adjustment here.
- rect.offset = PhysicalOffsetToBeNoop(layout_view->ScrolledContentOffset());
+ rect.offset = layout_view->ScrolledContentOffset();
}
rect.Move(AccumulatedScrollOffsetForFixedBackground(obj, container));
@@ -692,8 +692,7 @@ void BackgroundImageGeometry::CalculateFillTileSize(
? snapped_positioning_area_size
: unsnapped_positioning_area_size;
PhysicalSize image_intrinsic_size = PhysicalSize::FromFloatSizeFloor(
- image->ImageSize(positioning_box_->GetDocument(),
- positioning_box_->StyleRef().EffectiveZoom(),
+ image->ImageSize(positioning_box_->StyleRef().EffectiveZoom(),
FloatSize(positioning_area_size),
LayoutObject::ShouldRespectImageOrientation(box_)));
switch (type) {
@@ -979,4 +978,14 @@ PhysicalOffset BackgroundImageGeometry::OffsetInBackground(
return element_positioning_area_offset_;
}
+PhysicalOffset BackgroundImageGeometry::ComputeDestPhase() const {
+ // Given the size that the whole image should draw at, and the input phase
+ // requested by the content, and the space between repeated tiles, compute a
+ // phase that is no more than one size + space in magnitude.
+ const PhysicalSize step_per_tile = tile_size_ + repeat_spacing_;
+ const PhysicalOffset phase = {IntMod(-phase_.left, step_per_tile.width),
+ IntMod(-phase_.top, step_per_tile.height)};
+ return snapped_dest_rect_.offset + phase;
+}
+
} // namespace blink
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 9f294ef3b73..52c2f4593bc 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
@@ -64,6 +64,9 @@ class BackgroundImageGeometry {
const PhysicalRect& UnsnappedDestRect() const { return unsnapped_dest_rect_; }
const PhysicalRect& SnappedDestRect() const { return snapped_dest_rect_; }
+ // Compute the phase relative to the (snapped) destination offset.
+ PhysicalOffset ComputeDestPhase() const;
+
// Tile size is the area into which to draw one copy of the image. It
// need not be the same as the intrinsic size of the image; if not,
// the image will be resized (via an image filter) when painted into
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 d060df86a42..0b95523e435 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
@@ -9,6 +9,7 @@
#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/object_paint_invalidator.h"
#include "third_party/blink/renderer/core/paint/paint_invalidator.h"
namespace blink {
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 1fc8030a694..93455b24c9a 100644
--- a/chromium/third_party/blink/renderer/core/paint/block_painter.cc
+++ b/chromium/third_party/blink/renderer/core/paint/block_painter.cc
@@ -135,17 +135,8 @@ void BlockPainter::PaintChildren(const PaintInfo& paint_info) {
if (paint_info.DescendantPaintingBlocked())
return;
- // We may use legacy paint to paint the anonymous fieldset child. The layout
- // object for the rendered legend will be a child of that one, and has to be
- // skipped here, since it's handled by a special NG fieldset painter.
- bool may_contain_rendered_legend =
- layout_block_.IsAnonymousNGFieldsetContentWrapper();
for (LayoutBox* child = layout_block_.FirstChildBox(); child;
child = child->NextSiblingBox()) {
- if (may_contain_rendered_legend && child->IsRenderedLegend()) {
- may_contain_rendered_legend = false;
- continue;
- }
PaintChild(*child, paint_info);
}
}
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 c9906300cb6..e75ba37e2be 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
@@ -166,10 +166,10 @@ TEST_P(BlockPainterTest, BlockingWheelEventRectSubsequenceCaching) {
// Trigger a repaint with the whole stacking-context subsequence cached.
GetLayoutView().Layer()->SetNeedsRepaint();
- CachedItemAndSubsequenceCounter counter;
+ PaintController::CounterForTesting counter;
UpdateAllLifecyclePhasesForTest();
- EXPECT_EQ(1u, counter.NumNewCachedItems());
- EXPECT_EQ(1u, counter.NumNewCachedSubsequences());
+ EXPECT_EQ(1u, counter.num_cached_items);
+ EXPECT_EQ(1u, counter.num_cached_subsequences);
EXPECT_SUBSEQUENCE_FROM_CHUNK(hit_test_client,
ContentPaintChunks().begin() + 1, 1);
@@ -217,10 +217,10 @@ TEST_P(BlockPainterTest, WheelEventRectPaintCaching) {
ElementsAre(VIEW_SCROLLING_BACKGROUND_CHUNK(2, &hit_test_data)));
sibling_element->setAttribute(html_names::kStyleAttr, "background: green;");
- CachedItemAndSubsequenceCounter counter;
+ PaintController::CounterForTesting counter;
UpdateAllLifecyclePhasesForTest();
// Only the background display item of the sibling should be invalidated.
- EXPECT_EQ(1u, counter.NumNewCachedItems());
+ EXPECT_EQ(1u, counter.num_cached_items);
EXPECT_THAT(ContentPaintChunks(),
ElementsAre(VIEW_SCROLLING_BACKGROUND_CHUNK(2, &hit_test_data)));
@@ -432,10 +432,10 @@ TEST_P(BlockPainterTest, TouchActionRectSubsequenceCaching) {
// Trigger a repaint with the whole stacking-context subsequence cached.
GetLayoutView().Layer()->SetNeedsRepaint();
- CachedItemAndSubsequenceCounter counter;
+ PaintController::CounterForTesting counter;
UpdateAllLifecyclePhasesForTest();
- EXPECT_EQ(1u, counter.NumNewCachedItems());
- EXPECT_EQ(1u, counter.NumNewCachedSubsequences());
+ EXPECT_EQ(1u, counter.num_cached_items);
+ EXPECT_EQ(1u, counter.num_cached_subsequences);
EXPECT_SUBSEQUENCE_FROM_CHUNK(hit_test_client,
ContentPaintChunks().begin() + 1, 1);
@@ -480,10 +480,10 @@ TEST_P(BlockPainterTest, TouchActionRectPaintCaching) {
ElementsAre(VIEW_SCROLLING_BACKGROUND_CHUNK(2, &hit_test_data)));
sibling_element->setAttribute(html_names::kStyleAttr, "background: green;");
- CachedItemAndSubsequenceCounter counter;
+ PaintController::CounterForTesting counter;
UpdateAllLifecyclePhasesForTest();
// Only the background display item of the sibling should be invalidated.
- EXPECT_EQ(1u, counter.NumNewCachedItems());
+ EXPECT_EQ(1u, counter.num_cached_items);
EXPECT_THAT(ContentPaintChunks(),
ElementsAre(VIEW_SCROLLING_BACKGROUND_CHUNK(2, &hit_test_data)));
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 d6ca5091100..2340e9f10f4 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
@@ -6,10 +6,9 @@
#include <algorithm>
-#include "base/stl_util.h"
+#include "base/cxx17_backports.h"
#include "third_party/blink/renderer/core/paint/box_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/rounded_border_geometry.h"
#include "third_party/blink/renderer/core/style/border_edge.h"
#include "third_party/blink/renderer/core/style/computed_style.h"
@@ -86,20 +85,6 @@ inline bool BorderStyleHasUnmatchedColorsAtCorner(EBorderStyle style,
return false;
}
-inline bool ColorsMatchAtCorner(BoxSide side,
- BoxSide adjacent_side,
- const BorderEdge edges[]) {
- if (!edges[static_cast<unsigned>(adjacent_side)].ShouldRender())
- return false;
-
- if (!edges[static_cast<unsigned>(side)].SharesColorWith(
- edges[static_cast<unsigned>(adjacent_side)]))
- return false;
-
- return !BorderStyleHasUnmatchedColorsAtCorner(
- edges[static_cast<unsigned>(side)].BorderStyle(), side, adjacent_side);
-}
-
inline bool BorderWillArcInnerEdge(const FloatSize& first_radius,
const FloatSize& second_radius) {
return !first_radius.IsZero() || !second_radius.IsZero();
@@ -140,15 +125,15 @@ inline bool BorderStylesRequireMiter(BoxSide side,
FloatRect CalculateSideRect(const FloatRoundedRect& outer_border,
const BorderEdge& edge,
- int side) {
+ BoxSide side) {
FloatRect side_rect = outer_border.Rect();
float width = edge.Width();
- if (side == static_cast<unsigned>(BoxSide::kTop))
+ if (side == BoxSide::kTop)
side_rect.SetHeight(width);
- else if (side == static_cast<unsigned>(BoxSide::kBottom))
+ else if (side == BoxSide::kBottom)
side_rect.ShiftYEdgeTo(side_rect.MaxY() - width);
- else if (side == static_cast<unsigned>(BoxSide::kLeft))
+ else if (side == BoxSide::kLeft)
side_rect.SetWidth(width);
else
side_rect.ShiftXEdgeTo(side_rect.MaxX() - width);
@@ -156,38 +141,6 @@ FloatRect CalculateSideRect(const FloatRoundedRect& outer_border,
return side_rect;
}
-FloatRect CalculateSideRectIncludingInner(const FloatRoundedRect& outer_border,
- const BorderEdge edges[],
- BoxSide side) {
- FloatRect side_rect = outer_border.Rect();
- float width;
-
- switch (side) {
- case BoxSide::kTop:
- width = side_rect.Height() -
- edges[static_cast<unsigned>(BoxSide::kBottom)].Width();
- side_rect.SetHeight(width);
- break;
- case BoxSide::kBottom:
- width = side_rect.Height() -
- edges[static_cast<unsigned>(BoxSide::kTop)].Width();
- side_rect.ShiftYEdgeTo(side_rect.MaxY() - width);
- break;
- case BoxSide::kLeft:
- width = side_rect.Width() -
- edges[static_cast<unsigned>(BoxSide::kRight)].Width();
- side_rect.SetWidth(width);
- break;
- case BoxSide::kRight:
- width = side_rect.Width() -
- edges[static_cast<unsigned>(BoxSide::kLeft)].Width();
- side_rect.ShiftXEdgeTo(side_rect.MaxX() - width);
- break;
- }
-
- return side_rect;
-}
-
FloatRoundedRect CalculateAdjustedInnerBorder(
const FloatRoundedRect& inner_border,
BoxSide side) {
@@ -276,20 +229,6 @@ FloatRoundedRect CalculateAdjustedInnerBorder(
return FloatRoundedRect(new_rect, new_radii);
}
-LayoutRectOutsets DoubleStripeInsets(const BorderEdge edges[],
- BorderEdge::DoubleBorderStripe stripe) {
- // Insets are representes as negative outsets.
- return LayoutRectOutsets(
- -edges[static_cast<unsigned>(BoxSide::kTop)].GetDoubleBorderStripeWidth(
- stripe),
- -edges[static_cast<unsigned>(BoxSide::kRight)].GetDoubleBorderStripeWidth(
- stripe),
- -edges[static_cast<unsigned>(BoxSide::kBottom)]
- .GetDoubleBorderStripeWidth(stripe),
- -edges[static_cast<unsigned>(BoxSide::kLeft)].GetDoubleBorderStripeWidth(
- stripe));
-}
-
void DrawSolidBorderRect(GraphicsContext& context,
const FloatRect& border_rect,
float border_width,
@@ -383,20 +322,25 @@ static_assert(static_cast<unsigned>(BoxSide::kLeft) == 3,
// before solid edges (inset/outset/groove/ridge/solid) to maximize overdraw
// opportunities.
const unsigned kStylePriority[] = {
- 0 /* BorderStyleNone */, 0 /* BorderStyleHidden */,
- 2 /* BorderStyleInset */, 2 /* BorderStyleGroove */,
- 2 /* BorderStyleOutset */, 2 /* BorderStyleRidge */,
- 1 /* BorderStyleDotted */, 1 /* BorderStyleDashed */,
- 3 /* BorderStyleSolid */, 1 /* BorderStyleDouble */
+ 0, // EBorderStyle::kNone
+ 0, // EBorderStyle::kHidden
+ 2, // EBorderStyle::kInset
+ 2, // EBorderStyle::kGroove
+ 2, // EBorderStyle::kOutset
+ 2, // EBorderStyle::kRidge,
+ 1, // EBorderStyle::kDotted
+ 1, // EBorderStyle::kDashed
+ 3, // EBorderStyle::kSolid
+ 1, // EBorderStyle::kDouble
};
// Given the same style, prefer drawing in non-adjacent order to minimize the
// number of sides which require miters.
const unsigned kSidePriority[] = {
- 0, /* BSTop */
- 2, /* BSRight */
- 1, /* BSBottom */
- 3, /* BSLeft */
+ 0, // BoxSide::kTop
+ 2, // BoxSide::kRight
+ 1, // BoxSide::kBottom
+ 3, // BoxSide::kLeft
};
// Edges sharing the same opacity. Stores both a side list and an edge bitfield
@@ -421,12 +365,297 @@ void ClipQuad(GraphicsContext& context,
context.ClipPath(path.detach(), antialiased ? kAntiAliased : kNotAntiAliased);
}
+void DrawDashedOrDottedBoxSide(GraphicsContext& context,
+ int x1,
+ int y1,
+ int x2,
+ int y2,
+ BoxSide side,
+ Color color,
+ int thickness,
+ EBorderStyle style,
+ bool antialias) {
+ DCHECK_GT(thickness, 0);
+
+ GraphicsContextStateSaver state_saver(context);
+ context.SetShouldAntialias(antialias);
+ context.SetStrokeColor(color);
+ context.SetStrokeThickness(thickness);
+ context.SetStrokeStyle(style == EBorderStyle::kDashed ? kDashedStroke
+ : kDottedStroke);
+
+ switch (side) {
+ case BoxSide::kBottom:
+ case BoxSide::kTop: {
+ int mid_y = y1 + thickness / 2;
+ context.DrawLine(IntPoint(x1, mid_y), IntPoint(x2, mid_y));
+ break;
+ }
+ case BoxSide::kRight:
+ case BoxSide::kLeft: {
+ int mid_x = x1 + thickness / 2;
+ context.DrawLine(IntPoint(mid_x, y1), IntPoint(mid_x, y2));
+ break;
+ }
+ }
+}
+
+void DrawDoubleBoxSide(GraphicsContext& context,
+ int x1,
+ int y1,
+ int x2,
+ int y2,
+ int length,
+ BoxSide side,
+ Color color,
+ float thickness,
+ int adjacent_width1,
+ int adjacent_width2,
+ bool antialias) {
+ int third_of_thickness = (thickness + 1) / 3;
+ DCHECK_GT(third_of_thickness, 0);
+
+ if (!adjacent_width1 && !adjacent_width2) {
+ StrokeStyle old_stroke_style = context.GetStrokeStyle();
+ context.SetStrokeStyle(kNoStroke);
+ context.SetFillColor(color);
+
+ bool was_antialiased = context.ShouldAntialias();
+ context.SetShouldAntialias(antialias);
+
+ switch (side) {
+ case BoxSide::kTop:
+ case BoxSide::kBottom:
+ context.DrawRect(IntRect(x1, y1, length, third_of_thickness));
+ context.DrawRect(
+ IntRect(x1, y2 - third_of_thickness, length, third_of_thickness));
+ break;
+ case BoxSide::kLeft:
+ case BoxSide::kRight:
+ context.DrawRect(IntRect(x1, y1, third_of_thickness, length));
+ context.DrawRect(
+ IntRect(x2 - third_of_thickness, y1, third_of_thickness, length));
+ break;
+ }
+
+ context.SetShouldAntialias(was_antialiased);
+ context.SetStrokeStyle(old_stroke_style);
+ return;
+ }
+
+ int adjacent1_big_third =
+ ((adjacent_width1 > 0) ? adjacent_width1 + 1 : adjacent_width1 - 1) / 3;
+ int adjacent2_big_third =
+ ((adjacent_width2 > 0) ? adjacent_width2 + 1 : adjacent_width2 - 1) / 3;
+
+ switch (side) {
+ case BoxSide::kTop:
+ BoxBorderPainter::DrawLineForBoxSide(
+ context, x1 + std::max((-adjacent_width1 * 2 + 1) / 3, 0), y1,
+ x2 - std::max((-adjacent_width2 * 2 + 1) / 3, 0),
+ y1 + third_of_thickness, side, color, EBorderStyle::kSolid,
+ adjacent1_big_third, adjacent2_big_third, antialias);
+ BoxBorderPainter::DrawLineForBoxSide(
+ context, x1 + std::max((adjacent_width1 * 2 + 1) / 3, 0),
+ y2 - third_of_thickness,
+ x2 - std::max((adjacent_width2 * 2 + 1) / 3, 0), y2, side, color,
+ EBorderStyle::kSolid, adjacent1_big_third, adjacent2_big_third,
+ antialias);
+ break;
+ case BoxSide::kLeft:
+ BoxBorderPainter::DrawLineForBoxSide(
+ context, x1, y1 + std::max((-adjacent_width1 * 2 + 1) / 3, 0),
+ x1 + third_of_thickness,
+ y2 - std::max((-adjacent_width2 * 2 + 1) / 3, 0), side, color,
+ EBorderStyle::kSolid, adjacent1_big_third, adjacent2_big_third,
+ antialias);
+ BoxBorderPainter::DrawLineForBoxSide(
+ context, x2 - third_of_thickness,
+ y1 + std::max((adjacent_width1 * 2 + 1) / 3, 0), x2,
+ y2 - std::max((adjacent_width2 * 2 + 1) / 3, 0), side, color,
+ EBorderStyle::kSolid, adjacent1_big_third, adjacent2_big_third,
+ antialias);
+ break;
+ case BoxSide::kBottom:
+ BoxBorderPainter::DrawLineForBoxSide(
+ context, x1 + std::max((adjacent_width1 * 2 + 1) / 3, 0), y1,
+ x2 - std::max((adjacent_width2 * 2 + 1) / 3, 0),
+ y1 + third_of_thickness, side, color, EBorderStyle::kSolid,
+ adjacent1_big_third, adjacent2_big_third, antialias);
+ BoxBorderPainter::DrawLineForBoxSide(
+ context, x1 + std::max((-adjacent_width1 * 2 + 1) / 3, 0),
+ y2 - third_of_thickness,
+ x2 - std::max((-adjacent_width2 * 2 + 1) / 3, 0), y2, side, color,
+ EBorderStyle::kSolid, adjacent1_big_third, adjacent2_big_third,
+ antialias);
+ break;
+ case BoxSide::kRight:
+ BoxBorderPainter::DrawLineForBoxSide(
+ context, x1, y1 + std::max((adjacent_width1 * 2 + 1) / 3, 0),
+ x1 + third_of_thickness,
+ y2 - std::max((adjacent_width2 * 2 + 1) / 3, 0), side, color,
+ EBorderStyle::kSolid, adjacent1_big_third, adjacent2_big_third,
+ antialias);
+ BoxBorderPainter::DrawLineForBoxSide(
+ context, x2 - third_of_thickness,
+ y1 + std::max((-adjacent_width1 * 2 + 1) / 3, 0), x2,
+ y2 - std::max((-adjacent_width2 * 2 + 1) / 3, 0), side, color,
+ EBorderStyle::kSolid, adjacent1_big_third, adjacent2_big_third,
+ antialias);
+ break;
+ default:
+ break;
+ }
+}
+
+void DrawRidgeOrGrooveBoxSide(GraphicsContext& context,
+ int x1,
+ int y1,
+ int x2,
+ int y2,
+ BoxSide side,
+ Color color,
+ EBorderStyle style,
+ int adjacent_width1,
+ int adjacent_width2,
+ bool antialias) {
+ EBorderStyle s1;
+ EBorderStyle s2;
+ if (style == EBorderStyle::kGroove) {
+ s1 = EBorderStyle::kInset;
+ s2 = EBorderStyle::kOutset;
+ } else {
+ s1 = EBorderStyle::kOutset;
+ s2 = EBorderStyle::kInset;
+ }
+
+ int adjacent1_big_half =
+ ((adjacent_width1 > 0) ? adjacent_width1 + 1 : adjacent_width1 - 1) / 2;
+ int adjacent2_big_half =
+ ((adjacent_width2 > 0) ? adjacent_width2 + 1 : adjacent_width2 - 1) / 2;
+
+ switch (side) {
+ case BoxSide::kTop:
+ BoxBorderPainter::DrawLineForBoxSide(
+ context, x1 + std::max(-adjacent_width1, 0) / 2, y1,
+ x2 - std::max(-adjacent_width2, 0) / 2, (y1 + y2 + 1) / 2, side,
+ color, s1, adjacent1_big_half, adjacent2_big_half, antialias);
+ BoxBorderPainter::DrawLineForBoxSide(
+ context, x1 + std::max(adjacent_width1 + 1, 0) / 2, (y1 + y2 + 1) / 2,
+ x2 - std::max(adjacent_width2 + 1, 0) / 2, y2, side, color, s2,
+ adjacent_width1 / 2, adjacent_width2 / 2, antialias);
+ break;
+ case BoxSide::kLeft:
+ BoxBorderPainter::DrawLineForBoxSide(
+ context, x1, y1 + std::max(-adjacent_width1, 0) / 2,
+ (x1 + x2 + 1) / 2, y2 - std::max(-adjacent_width2, 0) / 2, side,
+ color, s1, adjacent1_big_half, adjacent2_big_half, antialias);
+ BoxBorderPainter::DrawLineForBoxSide(
+ context, (x1 + x2 + 1) / 2, y1 + std::max(adjacent_width1 + 1, 0) / 2,
+ x2, y2 - std::max(adjacent_width2 + 1, 0) / 2, side, color, s2,
+ adjacent_width1 / 2, adjacent_width2 / 2, antialias);
+ break;
+ case BoxSide::kBottom:
+ BoxBorderPainter::DrawLineForBoxSide(
+ context, x1 + std::max(adjacent_width1, 0) / 2, y1,
+ x2 - std::max(adjacent_width2, 0) / 2, (y1 + y2 + 1) / 2, side, color,
+ s2, adjacent1_big_half, adjacent2_big_half, antialias);
+ BoxBorderPainter::DrawLineForBoxSide(
+ context, x1 + std::max(-adjacent_width1 + 1, 0) / 2,
+ (y1 + y2 + 1) / 2, x2 - std::max(-adjacent_width2 + 1, 0) / 2, y2,
+ side, color, s1, adjacent_width1 / 2, adjacent_width2 / 2, antialias);
+ break;
+ case BoxSide::kRight:
+ BoxBorderPainter::DrawLineForBoxSide(
+ context, x1, y1 + std::max(adjacent_width1, 0) / 2, (x1 + x2 + 1) / 2,
+ y2 - std::max(adjacent_width2, 0) / 2, side, color, s2,
+ adjacent1_big_half, adjacent2_big_half, antialias);
+ BoxBorderPainter::DrawLineForBoxSide(
+ context, (x1 + x2 + 1) / 2,
+ y1 + std::max(-adjacent_width1 + 1, 0) / 2, x2,
+ y2 - std::max(-adjacent_width2 + 1, 0) / 2, side, color, s1,
+ adjacent_width1 / 2, adjacent_width2 / 2, antialias);
+ break;
+ }
+}
+
+void FillQuad(GraphicsContext& context,
+ const FloatPoint quad[],
+ const Color& color,
+ bool antialias) {
+ SkPathBuilder path;
+ path.moveTo(FloatPointToSkPoint(quad[0]));
+ path.lineTo(FloatPointToSkPoint(quad[1]));
+ path.lineTo(FloatPointToSkPoint(quad[2]));
+ path.lineTo(FloatPointToSkPoint(quad[3]));
+ PaintFlags flags(context.FillFlags());
+ flags.setAntiAlias(antialias);
+ flags.setColor(color.Rgb());
+
+ context.DrawPath(path.detach(), flags);
+}
+
+void DrawSolidBoxSide(GraphicsContext& context,
+ int x1,
+ int y1,
+ int x2,
+ int y2,
+ BoxSide side,
+ Color color,
+ int adjacent_width1,
+ int adjacent_width2,
+ bool antialias) {
+ DCHECK_GE(x2, x1);
+ DCHECK_GE(y2, y1);
+
+ if (!adjacent_width1 && !adjacent_width2) {
+ // Tweak antialiasing to match the behavior of fillQuad();
+ // this matters for rects in transformed contexts.
+ bool was_antialiased = context.ShouldAntialias();
+ if (antialias != was_antialiased)
+ context.SetShouldAntialias(antialias);
+ context.FillRect(IntRect(x1, y1, x2 - x1, y2 - y1), color);
+ if (antialias != was_antialiased)
+ context.SetShouldAntialias(was_antialiased);
+ return;
+ }
+
+ FloatPoint quad[4];
+ switch (side) {
+ case BoxSide::kTop:
+ quad[0] = FloatPoint(x1 + std::max(-adjacent_width1, 0), y1);
+ quad[1] = FloatPoint(x1 + std::max(adjacent_width1, 0), y2);
+ quad[2] = FloatPoint(x2 - std::max(adjacent_width2, 0), y2);
+ quad[3] = FloatPoint(x2 - std::max(-adjacent_width2, 0), y1);
+ break;
+ case BoxSide::kBottom:
+ quad[0] = FloatPoint(x1 + std::max(adjacent_width1, 0), y1);
+ quad[1] = FloatPoint(x1 + std::max(-adjacent_width1, 0), y2);
+ quad[2] = FloatPoint(x2 - std::max(-adjacent_width2, 0), y2);
+ quad[3] = FloatPoint(x2 - std::max(adjacent_width2, 0), y1);
+ break;
+ case BoxSide::kLeft:
+ quad[0] = FloatPoint(x1, y1 + std::max(-adjacent_width1, 0));
+ quad[1] = FloatPoint(x1, y2 - std::max(-adjacent_width2, 0));
+ quad[2] = FloatPoint(x2, y2 - std::max(adjacent_width2, 0));
+ quad[3] = FloatPoint(x2, y1 + std::max(adjacent_width1, 0));
+ break;
+ case BoxSide::kRight:
+ quad[0] = FloatPoint(x1, y1 + std::max(adjacent_width1, 0));
+ quad[1] = FloatPoint(x1, y2 - std::max(adjacent_width2, 0));
+ quad[2] = FloatPoint(x2, y2 - std::max(-adjacent_width2, 0));
+ quad[3] = FloatPoint(x2, y1 + std::max(-adjacent_width1, 0));
+ break;
+ }
+
+ FillQuad(context, quad, color, antialias);
+}
+
} // anonymous namespace
// Holds edges grouped by opacity and sorted in paint order.
struct BoxBorderPainter::ComplexBorderInfo {
- ComplexBorderInfo(const BoxBorderPainter& border_painter, bool anti_alias)
- : anti_alias(anti_alias) {
+ explicit ComplexBorderInfo(const BoxBorderPainter& border_painter) {
Vector<BoxSide, 4> sorted_sides;
// First, collect all visible sides.
@@ -442,10 +671,8 @@ struct BoxBorderPainter::ComplexBorderInfo {
// alpha, style, side.
std::sort(sorted_sides.begin(), sorted_sides.end(),
[&border_painter](BoxSide a, BoxSide b) -> bool {
- const BorderEdge& edge_a =
- border_painter.edges_[static_cast<unsigned>(a)];
- const BorderEdge& edge_b =
- border_painter.edges_[static_cast<unsigned>(b)];
+ const BorderEdge& edge_a = border_painter.Edge(a);
+ const BorderEdge& edge_b = border_painter.Edge(b);
const unsigned alpha_a = edge_a.color.Alpha();
const unsigned alpha_b = edge_b.color.Alpha();
@@ -475,15 +702,12 @@ struct BoxBorderPainter::ComplexBorderInfo {
// Potentially used when drawing rounded borders.
Path rounded_border_path;
- bool anti_alias;
-
private:
void BuildOpacityGroups(const BoxBorderPainter& border_painter,
const Vector<BoxSide, 4>& sorted_sides) {
unsigned current_alpha = 0;
for (BoxSide side : sorted_sides) {
- const BorderEdge& edge =
- border_painter.edges_[static_cast<unsigned>(side)];
+ const BorderEdge& edge = border_painter.Edge(side);
const unsigned edge_alpha = edge.color.Alpha();
DCHECK_GT(edge_alpha, 0u);
@@ -503,8 +727,7 @@ struct BoxBorderPainter::ComplexBorderInfo {
}
};
-void BoxBorderPainter::DrawDoubleBorder(GraphicsContext& context,
- const PhysicalRect& border_rect) const {
+void BoxBorderPainter::DrawDoubleBorder() const {
DCHECK(is_uniform_color_);
DCHECK(is_uniform_style_);
DCHECK(FirstEdge().BorderStyle() == EBorderStyle::kDouble);
@@ -516,30 +739,28 @@ void BoxBorderPainter::DrawDoubleBorder(GraphicsContext& context,
const auto force_rectangular = !outer_.IsRounded() && !inner_.IsRounded();
// outer stripe
- const LayoutRectOutsets outer_third_insets =
- DoubleStripeInsets(edges_, BorderEdge::kDoubleBorderStripeOuter);
+ const LayoutRectOutsets outer_third_outsets =
+ DoubleStripeOutsets(BorderEdge::kDoubleBorderStripeOuter);
FloatRoundedRect outer_third_rect =
- RoundedBorderGeometry::PixelSnappedRoundedInnerBorder(
- style_, border_rect, outer_third_insets, sides_to_include_);
+ RoundedBorderGeometry::PixelSnappedRoundedBorderWithOutsets(
+ style_, border_rect_, outer_third_outsets, sides_to_include_);
if (force_rectangular)
outer_third_rect.SetRadii(FloatRoundedRect::Radii());
- DrawBleedAdjustedDRRect(context, bleed_avoidance_, outer_, outer_third_rect,
+ DrawBleedAdjustedDRRect(context_, bleed_avoidance_, outer_, outer_third_rect,
color);
// inner stripe
- const LayoutRectOutsets inner_third_insets =
- DoubleStripeInsets(edges_, BorderEdge::kDoubleBorderStripeInner);
+ const LayoutRectOutsets inner_third_outsets =
+ DoubleStripeOutsets(BorderEdge::kDoubleBorderStripeInner);
FloatRoundedRect inner_third_rect =
- RoundedBorderGeometry::PixelSnappedRoundedInnerBorder(
- style_, border_rect, inner_third_insets, sides_to_include_);
+ RoundedBorderGeometry::PixelSnappedRoundedBorderWithOutsets(
+ style_, border_rect_, inner_third_outsets, sides_to_include_);
if (force_rectangular)
inner_third_rect.SetRadii(FloatRoundedRect::Radii());
- context.FillDRRect(inner_third_rect, inner_, color);
+ context_.FillDRRect(inner_third_rect, inner_, color);
}
-bool BoxBorderPainter::PaintBorderFastPath(
- GraphicsContext& context,
- const PhysicalRect& border_rect) const {
+bool BoxBorderPainter::PaintBorderFastPath() const {
if (!is_uniform_color_ || !is_uniform_style_ || !inner_.IsRenderable())
return false;
@@ -551,17 +772,17 @@ bool BoxBorderPainter::PaintBorderFastPath(
if (FirstEdge().BorderStyle() == EBorderStyle::kSolid) {
if (is_uniform_width_ && !outer_.IsRounded()) {
// 4-side, solid, uniform-width, rectangular border => one drawRect()
- DrawSolidBorderRect(context, outer_.Rect(), FirstEdge().Width(),
+ DrawSolidBorderRect(context_, outer_.Rect(), FirstEdge().Width(),
FirstEdge().color);
} else {
// 4-side, solid border => one drawDRRect()
- DrawBleedAdjustedDRRect(context, bleed_avoidance_, outer_, inner_,
+ DrawBleedAdjustedDRRect(context_, bleed_avoidance_, outer_, inner_,
FirstEdge().color);
}
} else {
// 4-side, double border => 2x drawDRRect()
DCHECK(FirstEdge().BorderStyle() == EBorderStyle::kDouble);
- DrawDoubleBorder(context, border_rect);
+ DrawDoubleBorder();
}
return true;
@@ -576,26 +797,29 @@ bool BoxBorderPainter::PaintBorderFastPath(
Path path;
path.SetWindRule(RULE_NONZERO);
- for (unsigned int i = static_cast<unsigned>(BoxSide::kTop);
- i <= static_cast<unsigned>(BoxSide::kLeft); ++i) {
- const BorderEdge& curr_edge = edges_[i];
+ for (auto side :
+ {BoxSide::kTop, BoxSide::kRight, BoxSide::kBottom, BoxSide::kLeft}) {
+ const BorderEdge& curr_edge = Edge(side);
if (curr_edge.ShouldRender())
- path.AddRect(CalculateSideRect(outer_, curr_edge, i));
+ path.AddRect(CalculateSideRect(outer_, curr_edge, side));
}
- context.SetFillColor(FirstEdge().color);
- context.FillPath(path);
+ context_.SetFillColor(FirstEdge().color);
+ context_.FillPath(path);
return true;
}
return false;
}
-BoxBorderPainter::BoxBorderPainter(const PhysicalRect& border_rect,
+BoxBorderPainter::BoxBorderPainter(GraphicsContext& context,
+ const PhysicalRect& border_rect,
const ComputedStyle& style,
BackgroundBleedAvoidance bleed_avoidance,
PhysicalBoxSides sides_to_include)
- : style_(style),
+ : context_(context),
+ border_rect_(border_rect),
+ style_(style),
bleed_avoidance_(bleed_avoidance),
sides_to_include_(sides_to_include),
visible_edge_count_(0),
@@ -622,23 +846,26 @@ BoxBorderPainter::BoxBorderPainter(const PhysicalRect& border_rect,
// can pixel snap smaller.
float max_width = outer_.Rect().Width();
float max_height = outer_.Rect().Height();
- edges_[static_cast<unsigned>(BoxSide::kTop)].ClampWidth(max_height);
- edges_[static_cast<unsigned>(BoxSide::kRight)].ClampWidth(max_width);
- edges_[static_cast<unsigned>(BoxSide::kBottom)].ClampWidth(max_height);
- edges_[static_cast<unsigned>(BoxSide::kLeft)].ClampWidth(max_width);
+ Edge(BoxSide::kTop).ClampWidth(max_height);
+ Edge(BoxSide::kRight).ClampWidth(max_width);
+ Edge(BoxSide::kBottom).ClampWidth(max_height);
+ Edge(BoxSide::kLeft).ClampWidth(max_width);
is_rounded_ = outer_.IsRounded();
}
-BoxBorderPainter::BoxBorderPainter(const ComputedStyle& style,
- const PhysicalRect& outer,
- const PhysicalRect& inner,
- const BorderEdge& uniform_edge_info)
- : style_(style),
+BoxBorderPainter::BoxBorderPainter(GraphicsContext& context,
+ const ComputedStyle& style,
+ const PhysicalRect& border_rect,
+ int inner_outset_x,
+ int inner_outset_y)
+ : context_(context),
+ border_rect_(border_rect),
+ outer_outset_x_(inner_outset_x + style.OutlineWidthInt()),
+ outer_outset_y_(inner_outset_y + style.OutlineWidthInt()),
+ style_(style),
bleed_avoidance_(kBackgroundBleedNone),
sides_to_include_(PhysicalBoxSides()),
- outer_(FloatRect(outer)),
- inner_(FloatRect(inner)),
visible_edge_count_(0),
first_visible_edge_(0),
visible_edge_set_(0),
@@ -647,10 +874,25 @@ BoxBorderPainter::BoxBorderPainter(const ComputedStyle& style,
is_uniform_color_(true),
is_rounded_(false),
has_alpha_(false) {
- for (auto& edge : edges_)
- edge = uniform_edge_info;
+ DCHECK(style.HasOutline());
+ BorderEdge edge(style.OutlineWidthInt(),
+ style.VisitedDependentColor(GetCSSPropertyOutlineColor()),
+ style.OutlineStyle());
+ for (auto& e : edges_)
+ e = edge;
ComputeBorderProperties();
+
+ outer_ = RoundedBorderGeometry::PixelSnappedRoundedBorderWithOutsets(
+ style, border_rect,
+ LayoutRectOutsets(outer_outset_y_, outer_outset_x_, outer_outset_y_,
+ outer_outset_x_));
+ is_rounded_ = outer_.IsRounded();
+
+ inner_ = RoundedBorderGeometry::PixelSnappedRoundedBorderWithOutsets(
+ style, border_rect,
+ LayoutRectOutsets(inner_outset_y, inner_outset_x, inner_outset_y,
+ inner_outset_x));
}
void BoxBorderPainter::ComputeBorderProperties() {
@@ -685,30 +927,27 @@ void BoxBorderPainter::ComputeBorderProperties() {
}
}
-void BoxBorderPainter::PaintBorder(const PaintInfo& info,
- const PhysicalRect& rect) const {
+void BoxBorderPainter::Paint() const {
if (!visible_edge_count_ || outer_.Rect().IsEmpty())
return;
- GraphicsContext& graphics_context = info.context;
-
- if (PaintBorderFastPath(graphics_context, rect))
+ if (PaintBorderFastPath())
return;
bool clip_to_outer_border = outer_.IsRounded();
- GraphicsContextStateSaver state_saver(graphics_context, clip_to_outer_border);
+ GraphicsContextStateSaver state_saver(context_, clip_to_outer_border);
if (clip_to_outer_border) {
// For BackgroundBleedClip{Only,Layer}, the outer rrect clip is already
// applied.
if (!BleedAvoidanceIsClipping(bleed_avoidance_))
- graphics_context.ClipRoundedRect(outer_);
+ context_.ClipRoundedRect(outer_);
if (inner_.IsRenderable() && !inner_.IsEmpty())
- graphics_context.ClipOutRoundedRect(inner_);
+ context_.ClipOutRoundedRect(inner_);
}
- const ComplexBorderInfo border_info(*this, true);
- PaintOpacityGroup(graphics_context, border_info, 0, 1);
+ const ComplexBorderInfo border_info(*this);
+ PaintOpacityGroup(border_info, 0, 1);
}
// In order to maximize the use of overdraw as a corner seam avoidance
@@ -721,11 +960,11 @@ void BoxBorderPainter::PaintBorder(const PaintInfo& info,
// transparency layers with adjusted/relative opacity [paintOpacityGroup]
// 4) iterate over groups (increasing opacity order), painting actual group
// contents and then ending their corresponding transparency layer
-// [paintOpacityGroup]
+// [PaintOpacityGroup]
//
// Layers are created in decreasing opacity order (top -> bottom), while actual
// border sides are drawn in increasing opacity order (bottom -> top). At each
-// level, opacity is adjusted to acount for accumulated/ancestor layer alpha.
+// level, opacity is adjusted to account for accumulated/ancestor layer alpha.
// Because opacity is applied via layers, the actual draw paint is opaque.
//
// As an example, let's consider a border with the following sides/opacities:
@@ -756,7 +995,6 @@ void BoxBorderPainter::PaintBorder(const PaintInfo& info,
// content - hence we can use overdraw to mask portions of the previous sides.
//
BorderEdgeFlags BoxBorderPainter::PaintOpacityGroup(
- GraphicsContext& context,
const ComplexBorderInfo& border_info,
unsigned index,
float effective_opacity) const {
@@ -789,7 +1027,7 @@ BorderEdgeFlags BoxBorderPainter::PaintOpacityGroup(
const float group_opacity = static_cast<float>(group.alpha) / 255;
DCHECK_LT(group_opacity, effective_opacity);
- context.BeginLayer(group_opacity / effective_opacity);
+ context_.BeginLayer(group_opacity / effective_opacity);
effective_opacity = group_opacity;
// Group opacity is applied via a layer => we draw the members using opaque
@@ -802,27 +1040,26 @@ BorderEdgeFlags BoxBorderPainter::PaintOpacityGroup(
// b) only triggers at all when mixing border sides with different opacities
// c) it allows us to express the layer nesting algorithm more naturally
BorderEdgeFlags completed_edges =
- PaintOpacityGroup(context, border_info, index + 1, effective_opacity);
+ PaintOpacityGroup(border_info, index + 1, effective_opacity);
// Paint the actual group edges with an alpha adjusted to account for
// ancenstor layers opacity.
for (BoxSide side : group.sides) {
- PaintSide(context, border_info, side, paint_alpha, completed_edges);
+ PaintSide(border_info, side, paint_alpha, completed_edges);
completed_edges |= EdgeFlagForSide(side);
}
if (needs_layer)
- context.EndLayer();
+ context_.EndLayer();
return completed_edges;
}
-void BoxBorderPainter::PaintSide(GraphicsContext& context,
- const ComplexBorderInfo& border_info,
+void BoxBorderPainter::PaintSide(const ComplexBorderInfo& border_info,
BoxSide side,
unsigned alpha,
BorderEdgeFlags completed_edges) const {
- const BorderEdge& edge = edges_[static_cast<unsigned>(side)];
+ const BorderEdge& edge = Edge(side);
DCHECK(edge.ShouldRender());
const Color color(edge.color.Red(), edge.color.Green(), edge.color.Blue(),
alpha);
@@ -843,9 +1080,8 @@ void BoxBorderPainter::PaintSide(GraphicsContext& context,
else
side_rect.SetHeight(floorf(edge.Width()));
- PaintOneBorderSide(context, side_rect, BoxSide::kTop, BoxSide::kLeft,
- BoxSide::kRight, path, border_info.anti_alias, color,
- completed_edges);
+ PaintOneBorderSide(side_rect, BoxSide::kTop, BoxSide::kLeft,
+ BoxSide::kRight, path, color, completed_edges);
break;
}
case BoxSide::kBottom: {
@@ -858,9 +1094,8 @@ void BoxBorderPainter::PaintSide(GraphicsContext& context,
else
side_rect.ShiftYEdgeTo(side_rect.MaxY() - floorf(edge.Width()));
- PaintOneBorderSide(context, side_rect, BoxSide::kBottom, BoxSide::kLeft,
- BoxSide::kRight, path, border_info.anti_alias, color,
- completed_edges);
+ PaintOneBorderSide(side_rect, BoxSide::kBottom, BoxSide::kLeft,
+ BoxSide::kRight, path, color, completed_edges);
break;
}
case BoxSide::kLeft: {
@@ -873,9 +1108,8 @@ void BoxBorderPainter::PaintSide(GraphicsContext& context,
else
side_rect.SetWidth(floorf(edge.Width()));
- PaintOneBorderSide(context, side_rect, BoxSide::kLeft, BoxSide::kTop,
- BoxSide::kBottom, path, border_info.anti_alias, color,
- completed_edges);
+ PaintOneBorderSide(side_rect, BoxSide::kLeft, BoxSide::kTop,
+ BoxSide::kBottom, path, color, completed_edges);
break;
}
case BoxSide::kRight: {
@@ -888,9 +1122,8 @@ void BoxBorderPainter::PaintSide(GraphicsContext& context,
else
side_rect.ShiftXEdgeTo(side_rect.MaxX() - floorf(edge.Width()));
- PaintOneBorderSide(context, side_rect, BoxSide::kRight, BoxSide::kTop,
- BoxSide::kBottom, path, border_info.anti_alias, color,
- completed_edges);
+ PaintOneBorderSide(side_rect, BoxSide::kRight, BoxSide::kTop,
+ BoxSide::kBottom, path, color, completed_edges);
break;
}
default:
@@ -901,10 +1134,8 @@ void BoxBorderPainter::PaintSide(GraphicsContext& context,
BoxBorderPainter::MiterType BoxBorderPainter::ComputeMiter(
BoxSide side,
BoxSide adjacent_side,
- BorderEdgeFlags completed_edges,
- bool antialias) const {
- const BorderEdge& adjacent_edge =
- edges_[static_cast<unsigned>(adjacent_side)];
+ BorderEdgeFlags completed_edges) const {
+ const BorderEdge& adjacent_edge = Edge(adjacent_side);
// No miters for missing edges.
if (!adjacent_edge.is_present)
@@ -916,15 +1147,13 @@ BoxBorderPainter::MiterType BoxBorderPainter::ComputeMiter(
// Color transitions require miters. Use miters compatible with the AA drawing
// mode to avoid introducing extra clips.
- if (!ColorsMatchAtCorner(side, adjacent_side, edges_))
- return antialias ? kSoftMiter : kHardMiter;
+ if (!ColorsMatchAtCorner(side, adjacent_side))
+ return kSoftMiter;
// Non-anti-aliased miters ensure correct same-color seaming when required by
// style.
- if (BorderStylesRequireMiter(
- side, adjacent_side,
- edges_[static_cast<unsigned>(side)].BorderStyle(),
- adjacent_edge.BorderStyle()))
+ if (BorderStylesRequireMiter(side, adjacent_side, Edge(side).BorderStyle(),
+ adjacent_edge.BorderStyle()))
return kHardMiter;
// Overdraw the adjacent edge when the colors match and we have no style
@@ -934,12 +1163,10 @@ BoxBorderPainter::MiterType BoxBorderPainter::ComputeMiter(
bool BoxBorderPainter::MitersRequireClipping(MiterType miter1,
MiterType miter2,
- EBorderStyle style,
- bool antialias) {
+ EBorderStyle style) {
// Clipping is required if any of the present miters doesn't match the current
// AA mode.
- bool should_clip = antialias ? miter1 == kHardMiter || miter2 == kHardMiter
- : miter1 == kSoftMiter || miter2 == kSoftMiter;
+ bool should_clip = miter1 == kHardMiter || miter2 == kHardMiter;
// Some styles require clipping for any type of miter.
should_clip = should_clip || ((miter1 != kNoMiter || miter2 != kNoMiter) &&
@@ -949,69 +1176,57 @@ bool BoxBorderPainter::MitersRequireClipping(MiterType miter1,
}
void BoxBorderPainter::PaintOneBorderSide(
- GraphicsContext& graphics_context,
const FloatRect& side_rect,
BoxSide side,
BoxSide adjacent_side1,
BoxSide adjacent_side2,
const Path* path,
- bool antialias,
Color color,
BorderEdgeFlags completed_edges) const {
- const BorderEdge& edge_to_render = edges_[static_cast<unsigned>(side)];
+ const BorderEdge& edge_to_render = Edge(side);
DCHECK(edge_to_render.Width());
- const BorderEdge& adjacent_edge1 =
- edges_[static_cast<unsigned>(adjacent_side1)];
- const BorderEdge& adjacent_edge2 =
- edges_[static_cast<unsigned>(adjacent_side2)];
+ const BorderEdge& adjacent_edge1 = Edge(adjacent_side1);
+ const BorderEdge& adjacent_edge2 = Edge(adjacent_side2);
if (path) {
- MiterType miter1 = ColorsMatchAtCorner(side, adjacent_side1, edges_)
- ? kHardMiter
- : kSoftMiter;
- MiterType miter2 = ColorsMatchAtCorner(side, adjacent_side2, edges_)
- ? kHardMiter
- : kSoftMiter;
-
- GraphicsContextStateSaver state_saver(graphics_context);
+ MiterType miter1 =
+ ColorsMatchAtCorner(side, adjacent_side1) ? kHardMiter : kSoftMiter;
+ MiterType miter2 =
+ ColorsMatchAtCorner(side, adjacent_side2) ? kHardMiter : kSoftMiter;
+
+ GraphicsContextStateSaver state_saver(context_);
if (inner_.IsRenderable())
- ClipBorderSidePolygon(graphics_context, side, miter1, miter2);
+ ClipBorderSidePolygon(side, miter1, miter2);
else
- ClipBorderSideForComplexInnerPath(graphics_context, side);
+ ClipBorderSideForComplexInnerPath(side);
float stroke_thickness =
std::max(std::max(edge_to_render.Width(), adjacent_edge1.Width()),
adjacent_edge2.Width());
- DrawBoxSideFromPath(graphics_context,
- PhysicalRect::EnclosingRect(outer_.Rect()), *path,
- edge_to_render.Width(), stroke_thickness, side, color,
- edge_to_render.BorderStyle());
+ DrawBoxSideFromPath(*path, edge_to_render.Width(), stroke_thickness, side,
+ color, edge_to_render.BorderStyle());
} else {
- MiterType miter1 =
- ComputeMiter(side, adjacent_side1, completed_edges, antialias);
- MiterType miter2 =
- ComputeMiter(side, adjacent_side2, completed_edges, antialias);
- bool should_clip = MitersRequireClipping(
- miter1, miter2, edge_to_render.BorderStyle(), antialias);
+ MiterType miter1 = ComputeMiter(side, adjacent_side1, completed_edges);
+ MiterType miter2 = ComputeMiter(side, adjacent_side2, completed_edges);
+ bool should_clip =
+ MitersRequireClipping(miter1, miter2, edge_to_render.BorderStyle());
- GraphicsContextStateSaver clip_state_saver(graphics_context, should_clip);
+ GraphicsContextStateSaver clip_state_saver(context_, should_clip);
if (should_clip) {
- ClipBorderSidePolygon(graphics_context, side, miter1, miter2);
-
+ ClipBorderSidePolygon(side, miter1, miter2);
// Miters are applied via clipping, no need to draw them.
miter1 = miter2 = kNoMiter;
}
- ObjectPainter::DrawLineForBoxSide(
- graphics_context, side_rect.X(), side_rect.Y(), side_rect.MaxX(),
- side_rect.MaxY(), side, color, edge_to_render.BorderStyle(),
- miter1 != kNoMiter ? floorf(adjacent_edge1.Width()) : 0,
- miter2 != kNoMiter ? floorf(adjacent_edge2.Width()) : 0, antialias);
+ DrawLineForBoxSide(context_, side_rect.X(), side_rect.Y(), side_rect.MaxX(),
+ side_rect.MaxY(), side, color,
+ edge_to_render.BorderStyle(),
+ miter1 != kNoMiter ? floorf(adjacent_edge1.Width()) : 0,
+ miter2 != kNoMiter ? floorf(adjacent_edge2.Width()) : 0,
+ /*antialias*/ true);
}
}
-void BoxBorderPainter::DrawBoxSideFromPath(GraphicsContext& graphics_context,
- const PhysicalRect& border_rect,
- const Path& border_path,
+void BoxBorderPainter::DrawBoxSideFromPath(const Path& border_path,
float border_thickness,
float stroke_thickness,
BoxSide side,
@@ -1029,22 +1244,20 @@ void BoxBorderPainter::DrawBoxSideFromPath(GraphicsContext& graphics_context,
return;
case EBorderStyle::kDotted:
case EBorderStyle::kDashed: {
- DrawDashedDottedBoxSideFromPath(graphics_context, border_rect,
- border_thickness, stroke_thickness, color,
+ DrawDashedDottedBoxSideFromPath(border_thickness, stroke_thickness, color,
border_style);
return;
}
case EBorderStyle::kDouble: {
- DrawDoubleBoxSideFromPath(graphics_context, border_rect, border_path,
- border_thickness, stroke_thickness, side,
- color);
+ DrawDoubleBoxSideFromPath(border_path, border_thickness, stroke_thickness,
+ side, color);
return;
}
case EBorderStyle::kRidge:
case EBorderStyle::kGroove: {
- DrawRidgeGrooveBoxSideFromPath(graphics_context, border_rect, border_path,
- border_thickness, stroke_thickness, side,
- color, border_style);
+ DrawRidgeGrooveBoxSideFromPath(border_path, border_thickness,
+ stroke_thickness, side, color,
+ border_style);
return;
}
case EBorderStyle::kInset:
@@ -1059,37 +1272,29 @@ void BoxBorderPainter::DrawBoxSideFromPath(GraphicsContext& graphics_context,
break;
}
- graphics_context.SetStrokeStyle(kNoStroke);
- graphics_context.SetFillColor(color);
- graphics_context.DrawRect(PixelSnappedIntRect(border_rect));
+ context_.SetStrokeStyle(kNoStroke);
+ context_.SetFillColor(color);
+ context_.DrawRect(RoundedIntRect(outer_.Rect()));
}
void BoxBorderPainter::DrawDashedDottedBoxSideFromPath(
- GraphicsContext& graphics_context,
- const PhysicalRect& border_rect,
float border_thickness,
float stroke_thickness,
Color color,
EBorderStyle border_style) const {
// Convert the path to be down the middle of the dots or dashes.
- const LayoutRectOutsets center_offsets(
- -edges_[static_cast<unsigned>(BoxSide::kTop)].UsedWidth() * 0.5,
- -edges_[static_cast<unsigned>(BoxSide::kRight)].UsedWidth() * 0.5,
- -edges_[static_cast<unsigned>(BoxSide::kBottom)].UsedWidth() * 0.5,
- -edges_[static_cast<unsigned>(BoxSide::kLeft)].UsedWidth() * 0.5);
Path centerline_path;
centerline_path.AddRoundedRect(
- RoundedBorderGeometry::PixelSnappedRoundedInnerBorder(
- style_, border_rect, center_offsets, sides_to_include_));
+ RoundedBorderGeometry::PixelSnappedRoundedBorderWithOutsets(
+ style_, border_rect_, CenterOutsets(), sides_to_include_));
- graphics_context.SetStrokeColor(color);
+ context_.SetStrokeColor(color);
if (!StrokeData::StrokeIsDashed(border_thickness,
border_style == EBorderStyle::kDashed
? kDashedStroke
: kDottedStroke)) {
- DrawWideDottedBoxSideFromPath(graphics_context, centerline_path,
- border_thickness);
+ DrawWideDottedBoxSideFromPath(centerline_path, border_thickness);
return;
}
@@ -1098,33 +1303,29 @@ void BoxBorderPainter::DrawDashedDottedBoxSideFromPath(
// the extra multiplier so that the clipping mask can antialias
// the edges to prevent jaggies.
const float thickness_multiplier = 2 * 1.1f;
- graphics_context.SetStrokeThickness(stroke_thickness * thickness_multiplier);
- graphics_context.SetStrokeStyle(
+ context_.SetStrokeThickness(stroke_thickness * thickness_multiplier);
+ context_.SetStrokeStyle(
border_style == EBorderStyle::kDashed ? kDashedStroke : kDottedStroke);
// TODO(schenney): stroking the border path causes issues with tight corners:
// https://bugs.chromium.org/p/chromium/issues/detail?id=344234
- graphics_context.StrokePath(centerline_path, centerline_path.length(),
- border_thickness);
+ context_.StrokePath(centerline_path, centerline_path.length(),
+ border_thickness);
}
void BoxBorderPainter::DrawWideDottedBoxSideFromPath(
- GraphicsContext& graphics_context,
const Path& border_path,
float border_thickness) const {
- graphics_context.SetStrokeThickness(border_thickness);
- graphics_context.SetStrokeStyle(kDottedStroke);
- graphics_context.SetLineCap(kRoundCap);
+ context_.SetStrokeThickness(border_thickness);
+ context_.SetStrokeStyle(kDottedStroke);
+ context_.SetLineCap(kRoundCap);
// TODO(schenney): stroking the border path causes issues with tight corners:
// https://bugs.webkit.org/show_bug.cgi?id=58711
- graphics_context.StrokePath(border_path, border_path.length(),
- border_thickness);
+ context_.StrokePath(border_path, border_path.length(), border_thickness);
}
void BoxBorderPainter::DrawDoubleBoxSideFromPath(
- GraphicsContext& graphics_context,
- const PhysicalRect& border_rect,
const Path& border_path,
float border_thickness,
float stroke_thickness,
@@ -1132,47 +1333,43 @@ void BoxBorderPainter::DrawDoubleBoxSideFromPath(
Color color) const {
// Draw inner border line
{
- GraphicsContextStateSaver state_saver(graphics_context);
- const LayoutRectOutsets inner_insets =
- DoubleStripeInsets(edges_, BorderEdge::kDoubleBorderStripeInner);
+ GraphicsContextStateSaver state_saver(context_);
+ const LayoutRectOutsets inner_outsets =
+ DoubleStripeOutsets(BorderEdge::kDoubleBorderStripeInner);
FloatRoundedRect inner_clip =
- RoundedBorderGeometry::PixelSnappedRoundedInnerBorder(
- style_, border_rect, inner_insets, sides_to_include_);
+ RoundedBorderGeometry::PixelSnappedRoundedBorderWithOutsets(
+ style_, border_rect_, inner_outsets, sides_to_include_);
- graphics_context.ClipRoundedRect(inner_clip);
- DrawBoxSideFromPath(graphics_context, border_rect, border_path,
- border_thickness, stroke_thickness, side, color,
- EBorderStyle::kSolid);
+ context_.ClipRoundedRect(inner_clip);
+ DrawBoxSideFromPath(border_path, border_thickness, stroke_thickness, side,
+ color, EBorderStyle::kSolid);
}
// Draw outer border line
{
- GraphicsContextStateSaver state_saver(graphics_context);
- PhysicalRect outer_rect = border_rect;
- LayoutRectOutsets outer_insets =
- DoubleStripeInsets(edges_, BorderEdge::kDoubleBorderStripeOuter);
+ GraphicsContextStateSaver state_saver(context_);
+ PhysicalRect used_border_rect = border_rect_;
+ LayoutRectOutsets outer_outsets =
+ DoubleStripeOutsets(BorderEdge::kDoubleBorderStripeOuter);
if (BleedAvoidanceIsClipping(bleed_avoidance_)) {
- outer_rect.Inflate(LayoutUnit(1));
- outer_insets.SetTop(outer_insets.Top() - 1);
- outer_insets.SetRight(outer_insets.Right() - 1);
- outer_insets.SetBottom(outer_insets.Bottom() - 1);
- outer_insets.SetLeft(outer_insets.Left() - 1);
+ used_border_rect.Inflate(LayoutUnit(1));
+ outer_outsets.SetTop(outer_outsets.Top() - 1);
+ outer_outsets.SetRight(outer_outsets.Right() - 1);
+ outer_outsets.SetBottom(outer_outsets.Bottom() - 1);
+ outer_outsets.SetLeft(outer_outsets.Left() - 1);
}
FloatRoundedRect outer_clip =
- RoundedBorderGeometry::PixelSnappedRoundedInnerBorder(
- style_, outer_rect, outer_insets, sides_to_include_);
- graphics_context.ClipOutRoundedRect(outer_clip);
- DrawBoxSideFromPath(graphics_context, border_rect, border_path,
- border_thickness, stroke_thickness, side, color,
- EBorderStyle::kSolid);
+ RoundedBorderGeometry::PixelSnappedRoundedBorderWithOutsets(
+ style_, used_border_rect, outer_outsets, sides_to_include_);
+ context_.ClipOutRoundedRect(outer_clip);
+ DrawBoxSideFromPath(border_path, border_thickness, stroke_thickness, side,
+ color, EBorderStyle::kSolid);
}
}
void BoxBorderPainter::DrawRidgeGrooveBoxSideFromPath(
- GraphicsContext& graphics_context,
- const PhysicalRect& border_rect,
const Path& border_path,
float border_thickness,
float stroke_thickness,
@@ -1190,43 +1387,56 @@ void BoxBorderPainter::DrawRidgeGrooveBoxSideFromPath(
}
// Paint full border
- DrawBoxSideFromPath(graphics_context, border_rect, border_path,
- border_thickness, stroke_thickness, side, color, s1);
+ DrawBoxSideFromPath(border_path, border_thickness, stroke_thickness, side,
+ color, s1);
// Paint inner only
- GraphicsContextStateSaver state_saver(graphics_context);
- int top_width = edges_[static_cast<unsigned>(BoxSide::kTop)].UsedWidth() / 2;
- int bottom_width =
- edges_[static_cast<unsigned>(BoxSide::kBottom)].UsedWidth() / 2;
- int left_width =
- edges_[static_cast<unsigned>(BoxSide::kLeft)].UsedWidth() / 2;
- int right_width =
- edges_[static_cast<unsigned>(BoxSide::kRight)].UsedWidth() / 2;
-
+ GraphicsContextStateSaver state_saver(context_);
FloatRoundedRect clip_rect =
- RoundedBorderGeometry::PixelSnappedRoundedInnerBorder(
- style_, border_rect,
- LayoutRectOutsets(-top_width, -right_width, -bottom_width,
- -left_width),
- sides_to_include_);
-
- graphics_context.ClipRoundedRect(clip_rect);
- DrawBoxSideFromPath(graphics_context, border_rect, border_path,
- border_thickness, stroke_thickness, side, color, s2);
+ RoundedBorderGeometry::PixelSnappedRoundedBorderWithOutsets(
+ style_, border_rect_, CenterOutsets(), sides_to_include_);
+
+ context_.ClipRoundedRect(clip_rect);
+ DrawBoxSideFromPath(border_path, border_thickness, stroke_thickness, side,
+ color, s2);
}
-void BoxBorderPainter::ClipBorderSideForComplexInnerPath(
- GraphicsContext& graphics_context,
+FloatRect BoxBorderPainter::CalculateSideRectIncludingInner(
BoxSide side) const {
- graphics_context.Clip(CalculateSideRectIncludingInner(outer_, edges_, side));
+ FloatRect side_rect = outer_.Rect();
+ float width;
+
+ switch (side) {
+ case BoxSide::kTop:
+ width = side_rect.Height() - Edge(BoxSide::kBottom).Width();
+ side_rect.SetHeight(width);
+ break;
+ case BoxSide::kBottom:
+ width = side_rect.Height() - Edge(BoxSide::kTop).Width();
+ side_rect.ShiftYEdgeTo(side_rect.MaxY() - width);
+ break;
+ case BoxSide::kLeft:
+ width = side_rect.Width() - Edge(BoxSide::kRight).Width();
+ side_rect.SetWidth(width);
+ break;
+ case BoxSide::kRight:
+ width = side_rect.Width() - Edge(BoxSide::kLeft).Width();
+ side_rect.ShiftXEdgeTo(side_rect.MaxX() - width);
+ break;
+ }
+
+ return side_rect;
+}
+
+void BoxBorderPainter::ClipBorderSideForComplexInnerPath(BoxSide side) const {
+ context_.Clip(CalculateSideRectIncludingInner(side));
FloatRoundedRect adjusted_inner_rect =
CalculateAdjustedInnerBorder(inner_, side);
if (!adjusted_inner_rect.IsEmpty())
- graphics_context.ClipOutRoundedRect(adjusted_inner_rect);
+ context_.ClipOutRoundedRect(adjusted_inner_rect);
}
-void BoxBorderPainter::ClipBorderSidePolygon(GraphicsContext& graphics_context,
- BoxSide side,
+void BoxBorderPainter::ClipBorderSidePolygon(BoxSide side,
MiterType first_miter,
MiterType second_miter) const {
DCHECK(first_miter != kNoMiter || second_miter != kNoMiter);
@@ -1446,7 +1656,7 @@ void BoxBorderPainter::ClipBorderSidePolygon(GraphicsContext& graphics_context,
}
if (first_miter == second_miter) {
- ClipQuad(graphics_context, edge_quad, first_miter == kSoftMiter);
+ ClipQuad(context_, edge_quad, first_miter == kSoftMiter);
return;
}
@@ -1466,7 +1676,7 @@ void BoxBorderPainter::ClipBorderSidePolygon(GraphicsContext& graphics_context,
clipping_quad[2] = bound_quad2;
clipping_quad[3] = edge_quad[3];
- ClipQuad(graphics_context, clipping_quad, first_miter == kSoftMiter);
+ ClipQuad(context_, clipping_quad, first_miter == kSoftMiter);
}
if (second_miter != kNoMiter) {
@@ -1479,7 +1689,105 @@ void BoxBorderPainter::ClipBorderSidePolygon(GraphicsContext& graphics_context,
clipping_quad[2] -= extension_offset;
clipping_quad[3] = edge_quad[3] - extension_offset;
- ClipQuad(graphics_context, clipping_quad, second_miter == kSoftMiter);
+ ClipQuad(context_, clipping_quad, second_miter == kSoftMiter);
+ }
+}
+
+LayoutRectOutsets BoxBorderPainter::DoubleStripeOutsets(
+ BorderEdge::DoubleBorderStripe stripe) const {
+ return LayoutRectOutsets(
+ outer_outset_y_ - Edge(BoxSide::kTop).GetDoubleBorderStripeWidth(stripe),
+ outer_outset_x_ -
+ Edge(BoxSide::kRight).GetDoubleBorderStripeWidth(stripe),
+ outer_outset_y_ -
+ Edge(BoxSide::kBottom).GetDoubleBorderStripeWidth(stripe),
+ outer_outset_x_ -
+ Edge(BoxSide::kLeft).GetDoubleBorderStripeWidth(stripe));
+}
+
+LayoutRectOutsets BoxBorderPainter::CenterOutsets() const {
+ return LayoutRectOutsets(
+ outer_outset_y_ - Edge(BoxSide::kTop).UsedWidth() * 0.5,
+ outer_outset_x_ - Edge(BoxSide::kRight).UsedWidth() * 0.5,
+ outer_outset_y_ - Edge(BoxSide::kBottom).UsedWidth() * 0.5,
+ outer_outset_x_ - Edge(BoxSide::kLeft).UsedWidth() * 0.5);
+}
+
+bool BoxBorderPainter::ColorsMatchAtCorner(BoxSide side,
+ BoxSide adjacent_side) const {
+ if (!Edge(adjacent_side).ShouldRender())
+ return false;
+
+ if (!Edge(side).SharesColorWith(Edge(adjacent_side)))
+ return false;
+
+ return !BorderStyleHasUnmatchedColorsAtCorner(Edge(side).BorderStyle(), side,
+ adjacent_side);
+}
+
+void BoxBorderPainter::DrawLineForBoxSide(GraphicsContext& context,
+ float x1,
+ float y1,
+ float x2,
+ float y2,
+ BoxSide side,
+ Color color,
+ EBorderStyle style,
+ int adjacent_width1,
+ int adjacent_width2,
+ bool antialias) {
+ float thickness;
+ float length;
+ if (side == BoxSide::kTop || side == BoxSide::kBottom) {
+ thickness = y2 - y1;
+ length = x2 - x1;
+ } else {
+ thickness = x2 - x1;
+ length = y2 - y1;
+ }
+
+ // We would like this check to be an ASSERT as we don't want to draw empty
+ // borders. However nothing guarantees that the following recursive calls to
+ // DrawLineForBoxSide() will have positive thickness and length.
+ if (length <= 0 || thickness <= 0)
+ return;
+
+ if (style == EBorderStyle::kDouble && thickness < 3)
+ style = EBorderStyle::kSolid;
+
+ switch (style) {
+ case EBorderStyle::kNone:
+ case EBorderStyle::kHidden:
+ return;
+ case EBorderStyle::kDotted:
+ case EBorderStyle::kDashed:
+ DrawDashedOrDottedBoxSide(context, x1, y1, x2, y2, side, color, thickness,
+ style, antialias);
+ break;
+ case EBorderStyle::kDouble:
+ DrawDoubleBoxSide(context, x1, y1, x2, y2, length, side, color, thickness,
+ adjacent_width1, adjacent_width2, antialias);
+ break;
+ case EBorderStyle::kRidge:
+ case EBorderStyle::kGroove:
+ DrawRidgeOrGrooveBoxSide(context, x1, y1, x2, y2, side, color, style,
+ adjacent_width1, adjacent_width2, antialias);
+ break;
+ case EBorderStyle::kInset:
+ // FIXME: Maybe we should lighten the colors on one side like Firefox.
+ // https://bugs.webkit.org/show_bug.cgi?id=58608
+ if (side == BoxSide::kTop || side == BoxSide::kLeft)
+ color = color.Dark();
+ FALLTHROUGH;
+ case EBorderStyle::kOutset:
+ if (style == EBorderStyle::kOutset &&
+ (side == BoxSide::kBottom || side == BoxSide::kRight))
+ color = color.Dark();
+ FALLTHROUGH;
+ case EBorderStyle::kSolid:
+ DrawSolidBoxSide(context, x1, y1, x2, y2, side, color, adjacent_width1,
+ adjacent_width2, antialias);
+ break;
}
}
diff --git a/chromium/third_party/blink/renderer/core/paint/box_border_painter.h b/chromium/third_party/blink/renderer/core/paint/box_border_painter.h
index 42c60592d46..27fa15d11ef 100644
--- a/chromium/third_party/blink/renderer/core/paint/box_border_painter.h
+++ b/chromium/third_party/blink/renderer/core/paint/box_border_painter.h
@@ -7,6 +7,7 @@
#include "third_party/blink/renderer/core/layout/background_bleed_avoidance.h"
#include "third_party/blink/renderer/core/layout/geometry/box_sides.h"
+#include "third_party/blink/renderer/core/layout/geometry/physical_rect.h"
#include "third_party/blink/renderer/core/style/border_edge.h"
#include "third_party/blink/renderer/platform/geometry/float_rounded_rect.h"
@@ -15,7 +16,6 @@ namespace blink {
class ComputedStyle;
class GraphicsContext;
class Path;
-struct PaintInfo;
struct PhysicalRect;
typedef unsigned BorderEdgeFlags;
@@ -24,19 +24,68 @@ class BoxBorderPainter {
STACK_ALLOCATED();
public:
- BoxBorderPainter(const PhysicalRect& border_rect,
+ static void PaintBorder(GraphicsContext& context,
+ const PhysicalRect& border_rect,
+ const ComputedStyle& style,
+ BackgroundBleedAvoidance bleed_avoidance,
+ PhysicalBoxSides sides_to_include) {
+ BoxBorderPainter(context, border_rect, style, bleed_avoidance,
+ sides_to_include)
+ .Paint();
+ }
+
+ static void PaintSingleRectOutline(GraphicsContext& context,
+ const ComputedStyle& style,
+ const PhysicalRect& border_rect,
+ int inner_outset_x,
+ int inner_outset_y) {
+ BoxBorderPainter(context, style, border_rect, inner_outset_x,
+ inner_outset_y)
+ .Paint();
+ }
+
+ static void DrawBoxSide(GraphicsContext& context,
+ const IntRect& snapped_edge_rect,
+ BoxSide side,
+ Color color,
+ EBorderStyle style) {
+ DrawLineForBoxSide(context, snapped_edge_rect.X(), snapped_edge_rect.Y(),
+ snapped_edge_rect.MaxX(), snapped_edge_rect.MaxY(), side,
+ color, style, 0, 0, true);
+ }
+
+ // TODO(crbug.com/1201762): The float parameters are truncated to int in the
+ // function, which implicitly snaps to whole pixels perhaps unexpectedly. To
+ // avoid the problem, we should use the above function which requires the
+ // caller to snap to whole pixels explicitly.
+ static void DrawLineForBoxSide(GraphicsContext&,
+ float x1,
+ float y1,
+ float x2,
+ float y2,
+ BoxSide,
+ Color,
+ EBorderStyle,
+ int adjacent_edge_width1,
+ int adjacent_edge_width2,
+ bool antialias);
+
+ private:
+ // For PaintBorder().
+ BoxBorderPainter(GraphicsContext&,
+ const PhysicalRect& border_rect,
const ComputedStyle&,
BackgroundBleedAvoidance,
PhysicalBoxSides sides_to_include);
+ // For PaintSingleRectOutline().
+ BoxBorderPainter(GraphicsContext&,
+ const ComputedStyle&,
+ const PhysicalRect& border_rect,
+ int inner_outset_x,
+ int inner_outset_y);
- BoxBorderPainter(const ComputedStyle&,
- const PhysicalRect& outer,
- const PhysicalRect& inner,
- const BorderEdge& uniform_edge_info);
-
- void PaintBorder(const PaintInfo&, const PhysicalRect& border_rect) const;
+ void Paint() const;
- private:
struct ComplexBorderInfo;
enum MiterType {
kNoMiter,
@@ -46,82 +95,76 @@ class BoxBorderPainter {
void ComputeBorderProperties();
- BorderEdgeFlags PaintOpacityGroup(GraphicsContext&,
- const ComplexBorderInfo&,
+ BorderEdgeFlags PaintOpacityGroup(const ComplexBorderInfo&,
unsigned index,
float accumulated_opacity) const;
- void PaintSide(GraphicsContext&,
- const ComplexBorderInfo&,
+ void PaintSide(const ComplexBorderInfo&,
BoxSide,
unsigned alpha,
BorderEdgeFlags) const;
- void PaintOneBorderSide(GraphicsContext&,
- const FloatRect& side_rect,
+ void PaintOneBorderSide(const FloatRect& side_rect,
BoxSide,
BoxSide adjacent_side1,
BoxSide adjacent_side2,
const Path*,
- bool antialias,
Color,
BorderEdgeFlags) const;
- bool PaintBorderFastPath(GraphicsContext&,
- const PhysicalRect& border_rect) const;
- void DrawDoubleBorder(GraphicsContext&,
- const PhysicalRect& border_rect) const;
-
- void DrawBoxSideFromPath(GraphicsContext&,
- const PhysicalRect&,
- const Path&,
+ bool PaintBorderFastPath() const;
+ void DrawDoubleBorder() const;
+
+ void DrawBoxSideFromPath(const Path&,
float thickness,
float draw_thickness,
BoxSide,
Color,
EBorderStyle) const;
- void DrawDashedDottedBoxSideFromPath(GraphicsContext&,
- const PhysicalRect&,
- float thickness,
+ void DrawDashedDottedBoxSideFromPath(float thickness,
float draw_thickness,
Color,
EBorderStyle) const;
- void DrawWideDottedBoxSideFromPath(GraphicsContext&,
- const Path&,
- float thickness) const;
- void DrawDoubleBoxSideFromPath(GraphicsContext&,
- const PhysicalRect&,
- const Path&,
+ void DrawWideDottedBoxSideFromPath(const Path&, float thickness) const;
+ void DrawDoubleBoxSideFromPath(const Path&,
float thickness,
float draw_thickness,
BoxSide,
Color) const;
- void DrawRidgeGrooveBoxSideFromPath(GraphicsContext&,
- const PhysicalRect&,
- const Path&,
+ void DrawRidgeGrooveBoxSideFromPath(const Path&,
float thickness,
float draw_thickness,
BoxSide,
Color,
EBorderStyle) const;
- void ClipBorderSidePolygon(GraphicsContext&,
- BoxSide,
- MiterType miter1,
- MiterType miter2) const;
- void ClipBorderSideForComplexInnerPath(GraphicsContext&, BoxSide) const;
-
- MiterType ComputeMiter(BoxSide,
- BoxSide adjacent_side,
- BorderEdgeFlags,
- bool antialias) const;
+ void ClipBorderSidePolygon(BoxSide, MiterType miter1, MiterType miter2) const;
+ FloatRect CalculateSideRectIncludingInner(BoxSide) const;
+ void ClipBorderSideForComplexInnerPath(BoxSide) const;
+
+ MiterType ComputeMiter(BoxSide, BoxSide adjacent_side, BorderEdgeFlags) const;
static bool MitersRequireClipping(MiterType miter1,
MiterType miter2,
- EBorderStyle,
- bool antialias);
+ EBorderStyle);
+
+ LayoutRectOutsets DoubleStripeOutsets(
+ BorderEdge::DoubleBorderStripe stripe) const;
+ LayoutRectOutsets CenterOutsets() const;
+
+ bool ColorsMatchAtCorner(BoxSide side, BoxSide adjacent_side) const;
const BorderEdge& FirstEdge() const {
DCHECK(visible_edge_set_);
return edges_[first_visible_edge_];
}
+ BorderEdge& Edge(BoxSide side) { return edges_[static_cast<unsigned>(side)]; }
+ const BorderEdge& Edge(BoxSide side) const {
+ return edges_[static_cast<unsigned>(side)];
+ }
+
+ GraphicsContext& context_;
+
// const inputs
+ const PhysicalRect border_rect_;
+ const LayoutUnit outer_outset_x_;
+ const LayoutUnit outer_outset_y_;
const ComputedStyle& style_;
const BackgroundBleedAvoidance bleed_avoidance_;
const PhysicalBoxSides sides_to_include_;
diff --git a/chromium/third_party/blink/renderer/core/paint/box_decoration_data.h b/chromium/third_party/blink/renderer/core/paint/box_decoration_data.h
index 958b9665987..18c9465a405 100644
--- a/chromium/third_party/blink/renderer/core/paint/box_decoration_data.h
+++ b/chromium/third_party/blink/renderer/core/paint/box_decoration_data.h
@@ -5,6 +5,7 @@
#ifndef THIRD_PARTY_BLINK_RENDERER_CORE_PAINT_BOX_DECORATION_DATA_H_
#define THIRD_PARTY_BLINK_RENDERER_CORE_PAINT_BOX_DECORATION_DATA_H_
+#include "third_party/abseil-cpp/absl/types/optional.h"
#include "third_party/blink/renderer/core/layout/background_bleed_avoidance.h"
#include "third_party/blink/renderer/core/layout/layout_box.h"
#include "third_party/blink/renderer/core/layout/ng/ng_physical_box_fragment.h"
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 de7526d1945..12b4e7b7ded 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
@@ -58,12 +58,13 @@ BoxModelObjectPainter::BoxModelObjectPainter(const LayoutBoxModelObject& box,
flow_box_(flow_box) {}
void BoxModelObjectPainter::PaintTextClipMask(
- GraphicsContext& context,
+ const PaintInfo& paint_info,
const IntRect& mask_rect,
const PhysicalOffset& paint_offset,
bool object_has_multiple_boxes) {
- PaintInfo paint_info(context, CullRect(mask_rect), PaintPhase::kTextClip,
- kGlobalPaintNormalPhase, 0);
+ PaintInfo mask_paint_info(paint_info.context, CullRect(mask_rect),
+ PaintPhase::kTextClip, kGlobalPaintNormalPhase, 0);
+ mask_paint_info.SetFragmentID(paint_info.FragmentID());
if (flow_box_) {
LayoutSize local_offset = ToLayoutSize(flow_box_->Location());
if (object_has_multiple_boxes &&
@@ -75,10 +76,10 @@ void BoxModelObjectPainter::PaintTextClipMask(
PhysicalOffset physical_local_offset(local_offset.Width(),
local_offset.Height());
const RootInlineBox& root = flow_box_->Root();
- flow_box_->Paint(paint_info, paint_offset - physical_local_offset,
+ flow_box_->Paint(mask_paint_info, paint_offset - physical_local_offset,
root.LineTop(), root.LineBottom());
} else if (auto* layout_block = DynamicTo<LayoutBlock>(box_model_)) {
- layout_block->PaintObject(paint_info, paint_offset);
+ layout_block->PaintObject(mask_paint_info, paint_offset);
} else {
// We should go through the above path for LayoutInlines.
DCHECK(!box_model_.IsLayoutInline());
@@ -98,7 +99,6 @@ PhysicalRect BoxModelObjectPainter::AdjustRectForScrolledContent(
if (BoxDecorationData::IsPaintingScrollingBackground(paint_info, this_box))
return rect;
- PhysicalRect scrolled_paint_rect = rect;
GraphicsContext& context = paint_info.context;
// Clip to the overflow area.
// TODO(chrishtr): this should be pixel-snapped.
@@ -106,8 +106,9 @@ PhysicalRect BoxModelObjectPainter::AdjustRectForScrolledContent(
// Adjust the paint rect to reflect a scrolled content box with borders at
// the ends.
- PhysicalOffset offset(this_box.PixelSnappedScrolledContentOffset());
- scrolled_paint_rect.Move(-offset);
+ PhysicalRect scrolled_paint_rect = rect;
+ scrolled_paint_rect.offset -=
+ PhysicalOffset(this_box.PixelSnappedScrolledContentOffset());
LayoutRectOutsets border = AdjustedBorderOutsets(info);
scrolled_paint_rect.SetWidth(border.Left() + this_box.ScrollWidth() +
border.Right());
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 03f15100911..5134ba6ecad 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
@@ -37,7 +37,7 @@ class BoxModelObjectPainter : public BoxPainterBase {
bool is_painting_scrolling_background) const override;
bool IsPaintingScrollingBackground(const PaintInfo&) const override;
- void PaintTextClipMask(GraphicsContext&,
+ void PaintTextClipMask(const PaintInfo&,
const IntRect& mask_rect,
const PhysicalOffset& paint_offset,
bool object_has_multiple_boxes) override;
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 a701c72ddc0..2f5093bf4a9 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
@@ -5,7 +5,6 @@
#include "third_party/blink/renderer/core/paint/box_painter_base.h"
#include "third_party/abseil-cpp/absl/types/optional.h"
-#include "third_party/blink/renderer/core/animation/element_animations.h"
#include "third_party/blink/renderer/core/css/background_color_paint_image_generator.h"
#include "third_party/blink/renderer/core/dom/document.h"
#include "third_party/blink/renderer/core/dom/node_computed_style.h"
@@ -192,7 +191,7 @@ void BoxPainterBase::PaintInsetBoxShadowWithInnerRect(
const ComputedStyle& style) {
if (!style.BoxShadow())
return;
- auto bounds = RoundedBorderGeometry::PixelSnappedRoundedInnerBorder(
+ auto bounds = RoundedBorderGeometry::PixelSnappedRoundedBorderWithOutsets(
style, inner_rect, LayoutRectOutsets());
PaintInsetBoxShadow(info, bounds, style);
}
@@ -397,39 +396,6 @@ BoxPainterBase::FillLayerInfo::FillLayerInfo(
namespace {
-// Given the size that the whole image should draw at, and the input phase
-// requested by the content, and the space between repeated tiles, return a
-// phase that is no more than one size + space in magnitude.
-PhysicalOffset ComputePhaseForBackground(
- const BackgroundImageGeometry& geometry) {
- const PhysicalSize step_per_tile = geometry.TileSize() + geometry.SpaceSize();
- return {IntMod(-geometry.Phase().left, step_per_tile.width),
- IntMod(-geometry.Phase().top, step_per_tile.height)};
-}
-
-// Compute the image subset, in intrinsic image coordinates, that gets mapped
-// onto the |subset|, when the whole image would be drawn with phase and size
-// given by |phase_and_size|. Assumes |phase_and_size| contains |subset|. The
-// location of the requested subset should be the painting snapped location, or
-// whatever was used as a destination_offset in ComputePhaseForBackground.
-//
-// It is used to undo the offset added in ComputePhaseForBackground. The size
-// of requested subset should be the unsnapped size so that the computed
-// scale and location in the source image can be correctly determined.
-FloatRect ComputeSubsetForBackground(const PhysicalRect& phase_and_size,
- const PhysicalRect& subset,
- const FloatSize& intrinsic_size) {
- // TODO(schenney): Re-enable this after determining why it fails for
- // CAP, and maybe other cases.
- // DCHECK(phase_and_size.Contains(subset));
- const PhysicalOffset offset_in_tile = subset.offset - phase_and_size.offset;
- const FloatSize scale(phase_and_size.Width() / intrinsic_size.Width(),
- phase_and_size.Height() / intrinsic_size.Height());
- return FloatRect(
- offset_in_tile.left / scale.Width(), offset_in_tile.top / scale.Height(),
- subset.Width() / scale.Width(), 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.
@@ -444,11 +410,85 @@ FloatRect SnapSourceRectIfNearIntegral(const FloatRect src_rect) {
LayoutUnit::Epsilon() &&
std::abs(std::round(src_rect.MaxY()) - src_rect.MaxY()) <=
LayoutUnit::Epsilon()) {
- return FloatRect(RoundedIntRect(src_rect));
+ IntRect rounded_src_rect = RoundedIntRect(src_rect);
+ // If we have snapped the image size to 0, revert the rounding.
+ if (rounded_src_rect.IsEmpty())
+ return src_rect;
+ return FloatRect(rounded_src_rect);
}
return src_rect;
}
+absl::optional<FloatRect> OptimizeToSingleTileDraw(
+ const BackgroundImageGeometry& geometry,
+ const PhysicalRect& dest_rect,
+ Image* image,
+ RespectImageOrientationEnum respect_orientation) {
+ const PhysicalOffset dest_phase = geometry.ComputeDestPhase();
+
+ // Phase calculation uses the actual painted location, given by the
+ // border-snapped destination rect.
+ const PhysicalRect one_tile_rect(dest_phase, geometry.TileSize());
+
+ // We cannot optimize if the tile is misaligned.
+ if (!one_tile_rect.Contains(dest_rect))
+ return absl::nullopt;
+
+ const PhysicalOffset offset_in_tile =
+ geometry.SnappedDestRect().offset - dest_phase;
+ if (!image->HasIntrinsicSize()) {
+ // This is a generated image sized according to the tile size so we can use
+ // the snapped dest rect directly.
+ const PhysicalRect offset_tile(offset_in_tile,
+ geometry.SnappedDestRect().size);
+ return FloatRect(offset_tile);
+ }
+
+ // Compute the image subset, in intrinsic image coordinates, that gets mapped
+ // onto the |dest_rect|, when the whole image would be drawn with phase and
+ // size given by |one_tile_rect|. Assumes |one_tile_rect| contains
+ // |dest_rect|. The location of the requested subset should be the painting
+ // snapped location.
+ //
+ // The size of requested subset should be the unsnapped size so that the
+ // computed scale and location in the source image can be correctly
+ // determined.
+ //
+ // image-resolution information is baked into the given parameters, but we
+ // need oriented size.
+ const FloatSize intrinsic_tile_size = image->SizeAsFloat(respect_orientation);
+
+ // Subset computation needs the same location as was used above, but needs the
+ // unsnapped destination size to correctly calculate sprite subsets in the
+ // presence of zoom.
+ // TODO(schenney): Re-enable this after determining why it fails for
+ // CAP, and maybe other cases.
+ // DCHECK(one_tile_rect.Contains(dest_rect_for_subset));
+ const FloatSize scale(
+ geometry.TileSize().width / intrinsic_tile_size.Width(),
+ geometry.TileSize().height / intrinsic_tile_size.Height());
+ FloatRect visible_src_rect(
+ offset_in_tile.left / scale.Width(), offset_in_tile.top / scale.Height(),
+ geometry.UnsnappedDestRect().Width() / scale.Width(),
+ geometry.UnsnappedDestRect().Height() / scale.Height());
+
+ // Content providers almost always choose source pixels at integer locations,
+ // so snap to integers. This is particularly important for sprite maps.
+ // Calculation up to this point, in LayoutUnits, can lead to small variations
+ // from integer size, so it is safe to round without introducing major issues.
+ 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(
+ intrinsic_tile_size, visible_src_rect);
+ }
+ return visible_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.
@@ -465,34 +505,6 @@ void DrawTiledBackground(GraphicsContext& context,
RespectImageOrientationEnum respect_orientation) {
DCHECK(!geometry.TileSize().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());
- // image-resolution information is baked into the given parameters, but we
- // need oriented size. That requires explicitly applying orientation here.
- if (respect_orientation &&
- image->CurrentFrameOrientation().UsesWidthAsHeight()) {
- intrinsic_tile_size = intrinsic_tile_size.TransposedSize();
- }
-
- FloatSize scale(1, 1);
- 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 = FloatSize(geometry.TileSize());
- } else {
- scale =
- FloatSize(geometry.TileSize().width / intrinsic_tile_size.Width(),
- geometry.TileSize().height / intrinsic_tile_size.Height());
- }
-
- const PhysicalOffset dest_phase =
- geometry.SnappedDestRect().offset + ComputePhaseForBackground(geometry);
-
// Check and see if a single draw of the image can cover the entire area we
// are supposed to tile. The dest_rect_for_subset must use the same
// location that was used in ComputePhaseForBackground and the unsnapped
@@ -500,30 +512,30 @@ void DrawTiledBackground(GraphicsContext& context,
// location in the presence of border snapping and zoom.
const PhysicalRect dest_rect_for_subset(geometry.SnappedDestRect().offset,
geometry.UnsnappedDestRect().size);
- const PhysicalRect one_tile_rect(dest_phase, geometry.TileSize());
- if (one_tile_rect.Contains(dest_rect_for_subset)) {
- FloatRect visible_src_rect = ComputeSubsetForBackground(
- one_tile_rect, dest_rect_for_subset, intrinsic_tile_size);
- 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(
- intrinsic_tile_size, visible_src_rect);
- }
-
+ if (absl::optional<FloatRect> single_tile_src = OptimizeToSingleTileDraw(
+ geometry, dest_rect_for_subset, image, respect_orientation)) {
context.DrawImage(image, Image::kSyncDecode,
- FloatRect(geometry.SnappedDestRect()), &visible_src_rect,
+ FloatRect(geometry.SnappedDestRect()), &*single_tile_src,
has_filter_property, op, respect_orientation);
return;
}
// At this point we have decided to tile the image to fill the dest rect.
+
+ // Use the intrinsic size of the image if it has one, otherwise force the
+ // generated image to be the tile size.
+ // image-resolution information is baked into the given parameters, but we
+ // need oriented size. That requires explicitly applying orientation here.
+ Image::SizeConfig size_config;
+ size_config.apply_orientation = respect_orientation;
+ const FloatSize intrinsic_tile_size =
+ image->SizeWithConfigAsFloat(size_config);
+
// Note that this tile rect uses the image's pre-scaled size.
- FloatRect tile_rect(FloatPoint(), intrinsic_tile_size);
+ ImageTilingInfo tiling_info;
+ tiling_info.image_rect.SetSize(intrinsic_tile_size);
+ tiling_info.phase = FloatPoint(geometry.ComputeDestPhase());
+ tiling_info.spacing = FloatSize(geometry.SpaceSize());
// 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
@@ -535,82 +547,86 @@ void DrawTiledBackground(GraphicsContext& context,
// values in that dimension.
const PhysicalSize tile_dest_diff =
geometry.TileSize() - geometry.SnappedDestRect().size;
- if (tile_dest_diff.width.Abs() <= 0.5f) {
- scale.SetWidth(geometry.SnappedDestRect().Width() /
- intrinsic_tile_size.Width());
- }
- if (tile_dest_diff.height.Abs() <= 0.5f) {
- scale.SetHeight(geometry.SnappedDestRect().Height() /
- intrinsic_tile_size.Height());
- }
+ const LayoutUnit ref_tile_width = tile_dest_diff.width.Abs() <= 0.5f
+ ? geometry.SnappedDestRect().Width()
+ : geometry.TileSize().width;
+ const LayoutUnit ref_tile_height = tile_dest_diff.height.Abs() <= 0.5f
+ ? geometry.SnappedDestRect().Height()
+ : geometry.TileSize().height;
+ tiling_info.scale = {ref_tile_width / tiling_info.image_rect.Width(),
+ ref_tile_height / tiling_info.image_rect.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, FloatRect(geometry.SnappedDestRect()),
- tile_rect, scale, FloatPoint(dest_phase),
- FloatSize(geometry.SpaceSize()), op,
+ tiling_info, has_filter_property, op,
respect_orientation);
}
-// Returning false meaning that we cannot paint background color with
-// BackgroundColorPaintWorklet.
-bool GetBGColorPaintWorkletParams(const BoxPainterBase::FillLayerInfo& info,
- const Document* document,
- Node* node,
- Vector<Color>* animated_colors,
- Vector<double>* offsets,
- absl::optional<double>* progress) {
- if (!info.should_paint_color_with_paint_worklet_image)
- return false;
- BackgroundColorPaintImageGenerator* generator =
- document->GetFrame()->GetBackgroundColorPaintImageGenerator();
- return generator->GetBGColorPaintWorkletParams(node, animated_colors, offsets,
- progress);
-}
-
-void FillRectWithPaintWorklet(const Document* document,
- const BoxPainterBase::FillLayerInfo& info,
- Node* node,
- const FloatRoundedRect& dest_rect,
- GraphicsContext& context,
- const Vector<Color>& animated_colors,
- const Vector<double>& offsets,
- const absl::optional<double>& progress) {
- FloatRect src_rect(FloatPoint(), dest_rect.Rect().Size());
+scoped_refptr<Image> GetBGColorPaintWorkletImage(const Document* document,
+ Node* node,
+ const FloatSize& image_size) {
+ LocalFrame* frame = document->GetFrame();
+ if (!frame)
+ return nullptr;
BackgroundColorPaintImageGenerator* generator =
- document->GetFrame()->GetBackgroundColorPaintImageGenerator();
- scoped_refptr<Image> paint_worklet_image = generator->Paint(
- src_rect.Size(), node, animated_colors, offsets, progress);
- context.DrawImageRRect(
- paint_worklet_image.get(), Image::kSyncDecode, dest_rect, src_rect,
- node && node->ComputedStyleRef().HasFilterInducingProperty(),
- SkBlendMode::kSrcOver, info.respect_image_orientation);
+ frame->GetBackgroundColorPaintImageGenerator();
+ // The generator can be null in testing environment.
+ if (!generator)
+ return nullptr;
+ Vector<Color> animated_colors;
+ Vector<double> offsets;
+ absl::optional<double> progress;
+ if (!generator->GetBGColorPaintWorkletParams(node, &animated_colors, &offsets,
+ &progress)) {
+ return nullptr;
+ }
+ return generator->Paint(image_size, node, animated_colors, offsets, progress);
}
-// Returns true if we can paint the background color with paint worklet.
+// Returns true if the background color was painted by the paint worklet.
bool PaintBGColorWithPaintWorklet(const Document* document,
const BoxPainterBase::FillLayerInfo& info,
Node* node,
const FloatRoundedRect& dest_rect,
GraphicsContext& context) {
- // TODO(xidachen): Consider merge this into GetBGColorPaintWorkletParams, so
- // that function doesn't need to return these parameters.
- Vector<Color> animated_colors;
- Vector<double> offsets;
- absl::optional<double> progress;
- if (GetBGColorPaintWorkletParams(info, document, node, &animated_colors,
- &offsets, &progress)) {
- FillRectWithPaintWorklet(document, info, node, dest_rect, context,
- animated_colors, offsets, progress);
- return true;
- }
- return false;
+ if (!info.should_paint_color_with_paint_worklet_image)
+ return false;
+ scoped_refptr<Image> paint_worklet_image =
+ GetBGColorPaintWorkletImage(document, node, dest_rect.Rect().Size());
+ if (!paint_worklet_image)
+ return false;
+ FloatRect src_rect(FloatPoint(), dest_rect.Rect().Size());
+ context.DrawImageRRect(paint_worklet_image.get(), Image::kSyncDecode,
+ dest_rect, src_rect,
+ node && node->ComputedStyleRef().DisableForceDark());
+ return true;
+}
+
+void DidDrawImage(
+ Node* node,
+ const Image& image,
+ const StyleImage& style_image,
+ const PropertyTreeStateOrAlias& current_paint_chunk_properties,
+ const FloatRect& image_rect) {
+ if (!node || !style_image.IsImageResource())
+ return;
+ const IntRect enclosing_rect = EnclosingIntRect(image_rect);
+ PaintTimingDetector::NotifyBackgroundImagePaint(
+ *node, image, To<StyleFetchedImage>(style_image),
+ current_paint_chunk_properties, enclosing_rect);
+
+ LocalDOMWindow* window = node->GetDocument().domWindow();
+ DCHECK(window);
+ ImageElementTiming::From(*window).NotifyBackgroundImagePainted(
+ *node, To<StyleFetchedImage>(style_image), current_paint_chunk_properties,
+ enclosing_rect);
}
inline bool PaintFastBottomLayer(const Document* document,
Node* node,
- const PaintInfo& paint_info,
+ GraphicsContext& context,
const BoxPainterBase::FillLayerInfo& info,
const PhysicalRect& rect,
const FloatRoundedRect& border_rect,
@@ -630,7 +646,6 @@ inline bool PaintFastBottomLayer(const Document* document,
// Compute the destination rect for painting the color here because we may
// need it for computing the image painting rect for optimization.
- GraphicsContext& context = paint_info.context;
FloatRoundedRect color_border =
info.is_rounded_fill ? border_rect
: FloatRoundedRect(PixelSnappedIntRect(rect));
@@ -638,9 +653,9 @@ inline bool PaintFastBottomLayer(const Document* document,
// tile. The border for painting images may not be the same as the color due
// to optimizations for the image painting destination that avoid painting
// under the border.
- PhysicalRect image_tile;
+ FloatRect src_rect;
FloatRoundedRect image_border;
- if (info.should_paint_image) {
+ if (info.should_paint_image && image) {
// Avoid image shaders when printing (poorly supported in PDF).
if (info.is_rounded_fill && info.is_printing)
return false;
@@ -651,32 +666,32 @@ inline bool PaintFastBottomLayer(const Document* document,
? color_border
: FloatRoundedRect(FloatRect(geometry.SnappedDestRect()));
- if (!image_border.Rect().IsEmpty()) {
+ const FloatRect& image_rect = image_border.Rect();
+ if (!image_rect.IsEmpty()) {
// We cannot optimize if the tile is too small.
- if (geometry.TileSize().width < image_border.Rect().Width() ||
- geometry.TileSize().height < image_border.Rect().Height())
+ if (geometry.TileSize().width < image_rect.Width() ||
+ geometry.TileSize().height < image_rect.Height())
return false;
- // Phase calculation uses the actual painted location, given by the
- // border-snapped destination rect.
- image_tile = PhysicalRect(geometry.SnappedDestRect().offset +
- ComputePhaseForBackground(geometry),
- geometry.TileSize());
-
// Use FastAndLossyFromFloatRect when converting the image border rect.
// At this point it should have been derived from a snapped rectangle, so
// the conversion from float should be as precise as it can be.
+ const PhysicalRect dest_rect =
+ PhysicalRect::FastAndLossyFromFloatRect(image_rect);
- // We cannot optimize if the tile is misaligned.
- if (!image_tile.Contains(
- PhysicalRect::FastAndLossyFromFloatRect(image_border.Rect())))
+ absl::optional<FloatRect> single_tile_src = OptimizeToSingleTileDraw(
+ geometry, dest_rect, image, info.respect_image_orientation);
+ if (!single_tile_src)
return false;
+ src_rect = *single_tile_src;
}
}
// At this point we're committed to the fast path: the destination (r)rect
// fits within a single tile, and we can paint it using direct draw(R)Rect()
- // calls.
+ // calls. Furthermore, if an image should be painted, |src_rect| has been
+ // updated to account for positioning and size parameters by
+ // OptimizeToSingleTileDraw() in the above code block.
absl::optional<RoundedInnerRectClipper> clipper;
if (info.is_rounded_fill && !color_border.IsRenderable()) {
// When the rrect is not renderable, we resort to clipping.
@@ -698,47 +713,9 @@ inline bool PaintFastBottomLayer(const Document* document,
}
// Paint the image if needed.
- if (!info.should_paint_image || !image || image_tile.IsEmpty())
+ if (!info.should_paint_image || src_rect.IsEmpty())
return true;
- // Generated images will be created at the desired tile size, so assume their
- // intrinsic size is the requested tile size.
- bool has_intrinsic_size = image->HasIntrinsicSize();
- const FloatSize intrinsic_tile_size =
- !has_intrinsic_size
- ? FloatSize(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
- // this is a generated image sized according to the tile size (which is a
- // snapped value), use the snapped dest rect instead.
- const PhysicalRect dest_rect_for_subset(
- geometry.SnappedDestRect().offset,
- !has_intrinsic_size ? geometry.SnappedDestRect().size
- : geometry.UnsnappedDestRect().size);
- // Content providers almost always choose source pixels at integer locations,
- // so snap to integers. This is particuarly important for sprite maps.
- // Calculation up to this point, in LayoutUnits, can lead to small variations
- // from integer size, so it is safe to round without introducing major issues.
- const FloatRect unrounded_subset = ComputeSubsetForBackground(
- image_tile, dest_rect_for_subset, intrinsic_tile_size);
- FloatRect src_rect = SnapSourceRectIfNearIntegral(unrounded_subset);
-
- // 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(intrinsic_tile_size, src_rect);
- }
-
DEVTOOLS_TIMELINE_TRACE_EVENT_WITH_CATEGORIES(
TRACE_DISABLED_BY_DEFAULT("devtools.timeline"), "PaintImage",
inspector_paint_image_event::Data, node, *info.image,
@@ -746,24 +723,13 @@ inline bool PaintFastBottomLayer(const Document* document,
// Since there is no way for the developer to specify decode behavior, use
// kSync by default.
- context.DrawImageRRect(
- image, Image::kSyncDecode, image_border, src_rect,
- node && node->ComputedStyleRef().HasFilterInducingProperty(),
- composite_op, info.respect_image_orientation);
-
- if (node && info.image && info.image->IsImageResource()) {
- PaintTimingDetector::NotifyBackgroundImagePaint(
- *node, *image, To<StyleFetchedImage>(*info.image),
- paint_info.context.GetPaintController().CurrentPaintChunkProperties(),
- RoundedIntRect(image_border.Rect()));
-
- LocalDOMWindow* window = node->GetDocument().domWindow();
- DCHECK(window);
- ImageElementTiming::From(*window).NotifyBackgroundImagePainted(
- *node, To<StyleFetchedImage>(*info.image),
- context.GetPaintController().CurrentPaintChunkProperties(),
- RoundedIntRect(image_border.Rect()));
- }
+ context.DrawImageRRect(image, Image::kSyncDecode, image_border, src_rect,
+ node && node->ComputedStyleRef().DisableForceDark(),
+ composite_op, info.respect_image_orientation);
+
+ DidDrawImage(node, *image, *info.image,
+ context.GetPaintController().CurrentPaintChunkProperties(),
+ image_border.Rect());
return true;
}
@@ -843,7 +809,7 @@ FloatRoundedRect RoundedBorderRectForClip(
PhysicalRect border_rect =
PhysicalRect::FastAndLossyFromFloatRect(border.Rect());
if (bg_layer.Clip() == EFillBox::kContent) {
- border = RoundedBorderGeometry::PixelSnappedRoundedInnerBorder(
+ border = RoundedBorderGeometry::PixelSnappedRoundedBorderWithOutsets(
style, border_rect, border_padding_insets, info.sides_to_include);
} else if (bg_layer.Clip() == EFillBox::kPadding) {
border = RoundedBorderGeometry::PixelSnappedRoundedInnerBorder(
@@ -865,7 +831,7 @@ void PaintFillLayerBackground(const Document* document,
// TODO(trchen): In the !bgLayer.hasRepeatXY() case, we could improve the
// culling test by verifying whether the background image covers the entire
// painting area.
- if (info.is_bottom_layer && info.color.Alpha() && info.should_paint_color) {
+ if (info.should_paint_color) {
IntRect background_rect(PixelSnappedIntRect(scrolled_paint_rect));
// Try to paint the background with a paint worklet first in case it will be
// animated. Otherwise, paint it directly into the context.
@@ -884,23 +850,12 @@ void PaintFillLayerBackground(const Document* document,
TRACE_DISABLED_BY_DEFAULT("devtools.timeline"), "PaintImage",
inspector_paint_image_event::Data, node, *info.image,
FloatRect(image->Rect()), FloatRect(scrolled_paint_rect));
- DrawTiledBackground(
- context, image, geometry, composite_op,
- node && node->ComputedStyleRef().HasFilterInducingProperty(),
- info.respect_image_orientation);
- if (node && info.image && info.image->IsImageResource()) {
- PaintTimingDetector::NotifyBackgroundImagePaint(
- *node, *image, To<StyleFetchedImage>(*info.image),
- context.GetPaintController().CurrentPaintChunkProperties(),
- EnclosingIntRect(geometry.SnappedDestRect()));
-
- LocalDOMWindow* window = node->GetDocument().domWindow();
- DCHECK(window);
- ImageElementTiming::From(*window).NotifyBackgroundImagePainted(
- *node, To<StyleFetchedImage>(*info.image),
- context.GetPaintController().CurrentPaintChunkProperties(),
- EnclosingIntRect(geometry.SnappedDestRect()));
- }
+ DrawTiledBackground(context, image, geometry, composite_op,
+ node && node->ComputedStyleRef().DisableForceDark(),
+ info.respect_image_orientation);
+ DidDrawImage(node, *image, *info.image,
+ context.GetPaintController().CurrentPaintChunkProperties(),
+ FloatRect(geometry.SnappedDestRect()));
}
}
@@ -940,36 +895,37 @@ void BoxPainterBase::PaintFillLayer(const PaintInfo& paint_info,
BackgroundImageGeometry& geometry,
bool object_has_multiple_boxes,
const PhysicalSize& flow_box_size) {
- GraphicsContext& context = paint_info.context;
if (rect.IsEmpty())
return;
- const FillLayerInfo info =
+ const FillLayerInfo fill_layer_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)
+ if (!fill_layer_info.should_paint_image &&
+ !fill_layer_info.should_paint_color)
return;
+ GraphicsContext& context = paint_info.context;
GraphicsContextStateSaver clip_with_scrolling_state_saver(
- context, info.is_clipped_with_local_scrolling);
+ context, fill_layer_info.is_clipped_with_local_scrolling);
auto scrolled_paint_rect =
- AdjustRectForScrolledContent(paint_info, info, rect);
+ AdjustRectForScrolledContent(paint_info, fill_layer_info, rect);
const auto did_adjust_paint_rect = scrolled_paint_rect != rect;
scoped_refptr<Image> image;
SkBlendMode composite_op = SkBlendMode::kSrcOver;
absl::optional<ScopedInterpolationQuality> interpolation_quality_context;
- if (info.should_paint_image) {
+ if (fill_layer_info.should_paint_image) {
geometry.Calculate(paint_info.PaintContainer(), paint_info.phase, bg_layer,
scrolled_paint_rect);
- image = info.image->GetImage(
+ image = fill_layer_info.image->GetImage(
geometry.ImageClient(), geometry.ImageDocument(),
geometry.ImageStyle(style_), FloatSize(geometry.TileSize()));
interpolation_quality_context.emplace(context,
geometry.ImageInterpolationQuality());
- if (ShouldApplyBlendOperation(info, bg_layer)) {
+ if (ShouldApplyBlendOperation(fill_layer_info, bg_layer)) {
composite_op = WebCoreCompositeToSkiaComposite(bg_layer.Composite(),
bg_layer.GetBlendMode());
}
@@ -979,8 +935,8 @@ void BoxPainterBase::PaintFillLayer(const PaintInfo& paint_info,
LayoutRectOutsets padding = ComputePadding();
LayoutRectOutsets border_padding_insets = -(border + padding);
FloatRoundedRect border_rect = RoundedBorderRectForClip(
- style_, info, bg_layer, rect, object_has_multiple_boxes, flow_box_size,
- bleed_avoidance, border_padding_insets);
+ style_, fill_layer_info, bg_layer, rect, object_has_multiple_boxes,
+ flow_box_size, bleed_avoidance, border_padding_insets);
// Fast path for drawing simple color backgrounds. Do not use the fast
// path with images if the dest rect has been adjusted for scrolling
@@ -989,22 +945,22 @@ void BoxPainterBase::PaintFillLayer(const PaintInfo& paint_info,
// if we are shrinking the background for bleed avoidance, because this
// adjusts the border rects in a way that breaks the optimization.
bool disable_fast_path =
- info.should_paint_image &&
+ fill_layer_info.should_paint_image &&
(bleed_avoidance == kBackgroundBleedShrinkBackground ||
did_adjust_paint_rect);
if (!disable_fast_path &&
- PaintFastBottomLayer(document_, node_, paint_info, info, rect,
+ PaintFastBottomLayer(document_, node_, context, fill_layer_info, rect,
border_rect, geometry, image.get(), composite_op)) {
return;
}
absl::optional<RoundedInnerRectClipper> clip_to_border;
- if (info.is_rounded_fill)
+ if (fill_layer_info.is_rounded_fill)
clip_to_border.emplace(context, rect, border_rect);
if (bg_layer.Clip() == EFillBox::kText) {
- PaintFillLayerTextFillBox(context, info, image.get(), composite_op,
- geometry, rect, scrolled_paint_rect,
+ PaintFillLayerTextFillBox(paint_info, fill_layer_info, image.get(),
+ composite_op, geometry, rect, scrolled_paint_rect,
object_has_multiple_boxes);
return;
}
@@ -1013,14 +969,17 @@ void BoxPainterBase::PaintFillLayer(const PaintInfo& paint_info,
switch (bg_layer.Clip()) {
case EFillBox::kPadding:
case EFillBox::kContent: {
- if (info.is_rounded_fill)
+ if (fill_layer_info.is_rounded_fill)
break;
// Clip to the padding or content boxes as necessary.
PhysicalRect clip_rect = scrolled_paint_rect;
- clip_rect.Contract(AdjustOutsetsForEdgeInclusion(border, info));
- if (bg_layer.Clip() == EFillBox::kContent)
- clip_rect.Contract(AdjustOutsetsForEdgeInclusion(padding, info));
+ clip_rect.Contract(
+ AdjustOutsetsForEdgeInclusion(border, fill_layer_info));
+ if (bg_layer.Clip() == EFillBox::kContent) {
+ clip_rect.Contract(
+ AdjustOutsetsForEdgeInclusion(padding, fill_layer_info));
+ }
background_clip_state_saver.Save();
context.Clip(PixelSnappedIntRect(clip_rect));
break;
@@ -1033,12 +992,13 @@ void BoxPainterBase::PaintFillLayer(const PaintInfo& paint_info,
break;
}
- PaintFillLayerBackground(document_, context, info, node_, image.get(),
- composite_op, geometry, scrolled_paint_rect);
+ PaintFillLayerBackground(document_, context, fill_layer_info, node_,
+ image.get(), composite_op, geometry,
+ scrolled_paint_rect);
}
void BoxPainterBase::PaintFillLayerTextFillBox(
- GraphicsContext& context,
+ const PaintInfo& paint_info,
const BoxPainterBase::FillLayerInfo& info,
Image* image,
SkBlendMode composite_op,
@@ -1051,6 +1011,8 @@ void BoxPainterBase::PaintFillLayerTextFillBox(
// rect with the border box of the background.
IntRect mask_rect = PixelSnappedIntRect(rect);
+ GraphicsContext& context = paint_info.context;
+
// We draw the background into a separate layer, to be later masked with
// yet another layer holding the text content.
GraphicsContextStateSaver background_clip_state_saver(context, false);
@@ -1067,7 +1029,7 @@ void BoxPainterBase::PaintFillLayerTextFillBox(
// they should just add their contents to the clip.
context.BeginLayer(1, SkBlendMode::kDstIn);
- PaintTextClipMask(context, mask_rect, scrolled_paint_rect.offset,
+ PaintTextClipMask(paint_info, mask_rect, scrolled_paint_rect.offset,
object_has_multiple_boxes);
context.EndLayer(); // Text mask layer.
@@ -1088,9 +1050,8 @@ void BoxPainterBase::PaintBorder(const ImageResourceObserver& obj,
return;
}
- const BoxBorderPainter border_painter(rect, style, bleed_avoidance,
- sides_to_include);
- border_painter.PaintBorder(info, rect);
+ BoxBorderPainter::PaintBorder(info.context, rect, style, bleed_avoidance,
+ sides_to_include);
}
void BoxPainterBase::PaintMaskImages(const PaintInfo& paint_info,
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 868f23d6e7a..f0cf368ecb7 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
@@ -22,7 +22,6 @@ class ComputedStyle;
class Document;
class FillLayer;
class FloatRoundedRect;
-class GraphicsContext;
class ImageResourceObserver;
class IntRect;
class LayoutBox;
@@ -145,7 +144,7 @@ class BoxPainterBase {
virtual LayoutRectOutsets ComputeBorders() const = 0;
virtual LayoutRectOutsets ComputePadding() const = 0;
LayoutRectOutsets AdjustedBorderOutsets(const FillLayerInfo&) const;
- void PaintFillLayerTextFillBox(GraphicsContext&,
+ void PaintFillLayerTextFillBox(const PaintInfo&,
const FillLayerInfo&,
Image*,
SkBlendMode composite_op,
@@ -153,7 +152,7 @@ class BoxPainterBase {
const PhysicalRect&,
const PhysicalRect& scrolled_paint_rect,
bool object_has_multiple_boxes);
- virtual void PaintTextClipMask(GraphicsContext&,
+ virtual void PaintTextClipMask(const PaintInfo&,
const IntRect& mask_rect,
const PhysicalOffset& paint_offset,
bool object_has_multiple_boxes) = 0;
diff --git a/chromium/third_party/blink/renderer/core/paint/build.gni b/chromium/third_party/blink/renderer/core/paint/build.gni
index bc9e44b569e..7812d2a77be 100644
--- a/chromium/third_party/blink/renderer/core/paint/build.gni
+++ b/chromium/third_party/blink/renderer/core/paint/build.gni
@@ -137,6 +137,8 @@ blink_core_sources_paint = [
"ng/ng_table_cell_paint_invalidator.h",
"ng/ng_table_painters.cc",
"ng/ng_table_painters.h",
+ "ng/ng_text_combine_painter.cc",
+ "ng/ng_text_combine_painter.h",
"ng/ng_text_fragment_painter.cc",
"ng/ng_text_fragment_painter.h",
"ng/ng_text_painter.cc",
@@ -150,8 +152,8 @@ blink_core_sources_paint = [
"object_paint_properties.h",
"object_painter.cc",
"object_painter.h",
- "object_painter_base.cc",
- "object_painter_base.h",
+ "outline_painter.cc",
+ "outline_painter.h",
"paint_event.h",
"paint_info.h",
"paint_invalidator.cc",
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 066bff54d9c..99d2790ba2a 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
@@ -4,6 +4,9 @@
#include "third_party/blink/renderer/core/paint/clip_path_clipper.h"
+#include "third_party/blink/renderer/core/css/clip_path_paint_image_generator.h"
+#include "third_party/blink/renderer/core/display_lock/display_lock_utilities.h"
+#include "third_party/blink/renderer/core/frame/local_frame.h"
#include "third_party/blink/renderer/core/layout/layout_box.h"
#include "third_party/blink/renderer/core/layout/layout_inline.h"
#include "third_party/blink/renderer/core/layout/svg/layout_svg_resource_clipper.h"
@@ -13,6 +16,7 @@
#include "third_party/blink/renderer/core/style/clip_path_operation.h"
#include "third_party/blink/renderer/core/style/reference_clip_path_operation.h"
#include "third_party/blink/renderer/core/style/shape_clip_path_operation.h"
+#include "third_party/blink/renderer/platform/graphics/image.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"
@@ -40,10 +44,14 @@ LayoutSVGResourceClipper* ResolveElementReference(
return nullptr;
LayoutSVGResourceClipper* resource_clipper =
GetSVGResourceAsType(*client, reference_clip_path_operation);
- if (resource_clipper) {
- SECURITY_DCHECK(!resource_clipper->NeedsLayout());
- resource_clipper->ClearInvalidationMask();
- }
+ if (!resource_clipper)
+ return nullptr;
+
+ resource_clipper->ClearInvalidationMask();
+ if (DisplayLockUtilities::LockedAncestorPreventingLayout(*resource_clipper))
+ return nullptr;
+
+ SECURITY_DCHECK(!resource_clipper->SelfNeedsLayout());
return resource_clipper;
}
@@ -55,6 +63,34 @@ static bool UsesZoomedReferenceBox(const LayoutObject& clip_path_owner) {
return !clip_path_owner.IsSVGChild() || clip_path_owner.IsSVGForeignObject();
}
+static void PaintWorkletBasedClip(GraphicsContext& context,
+ const LayoutObject& clip_path_owner,
+ const FloatRect& reference_box,
+ bool uses_zoomed_reference_box) {
+ DCHECK(RuntimeEnabledFeatures::CompositeClipPathAnimationEnabled());
+ DCHECK_EQ(clip_path_owner.StyleRef().ClipPath()->GetType(),
+ ClipPathOperation::SHAPE);
+
+ float zoom = uses_zoomed_reference_box
+ ? clip_path_owner.StyleRef().EffectiveZoom()
+ : 1;
+ ClipPathPaintImageGenerator* generator =
+ clip_path_owner.GetFrame()->GetClipPathPaintImageGenerator();
+
+ scoped_refptr<Image> paint_worklet_image =
+ generator->Paint(zoom, reference_box, *clip_path_owner.GetNode());
+
+ // TODO(crbug.com/1223975): Fix bounding box. It should enclose affected area
+ // of the animation.
+ absl::optional<FloatRect> bounding_box =
+ ClipPathClipper::LocalClipPathBoundingBox(clip_path_owner);
+ DCHECK(bounding_box);
+ FloatRect src_rect(bounding_box.value());
+ context.DrawImage(paint_worklet_image.get(), Image::kSyncDecode, src_rect,
+ &src_rect, clip_path_owner.StyleRef().DisableForceDark(),
+ SkBlendMode::kSrcOver, kRespectImageOrientation);
+}
+
FloatRect ClipPathClipper::LocalReferenceBox(const LayoutObject& object) {
if (object.IsSVGChild())
return SVGResources::ReferenceBoxForEffects(object);
@@ -166,6 +202,8 @@ void ClipPathClipper::PaintClipPathAsMaskImage(
DisplayItem::kSVGClip))
return;
+ // TODO(crbug.com/1223975): Fix paint rectangle for
+ // CompositeClipPathAnimation.
DrawingRecorder recorder(
context, display_item_client, DisplayItem::kSVGClip,
EnclosingIntRect(properties->MaskClip()->UnsnappedClipRect().Rect()));
@@ -174,44 +212,57 @@ void ClipPathClipper::PaintClipPathAsMaskImage(
bool uses_zoomed_reference_box = UsesZoomedReferenceBox(layout_object);
FloatRect reference_box = LocalReferenceBox(layout_object);
- bool is_first = true;
- bool rest_of_the_chain_already_appled = false;
- const LayoutObject* current_object = &layout_object;
- while (!rest_of_the_chain_already_appled && current_object) {
- const ClipPathOperation* clip_path = current_object->StyleRef().ClipPath();
- if (!clip_path)
- break;
- // We wouldn't have reached here if the current clip-path is a shape,
- // because it would have been applied as a path-based clip already.
- LayoutSVGResourceClipper* resource_clipper = ResolveElementReference(
- *current_object, To<ReferenceClipPathOperation>(*clip_path));
- if (!resource_clipper)
- break;
-
- if (is_first)
- context.Save();
- else
- context.BeginLayer(1.f, SkBlendMode::kDstIn);
-
- if (resource_clipper->StyleRef().HasClipPath()) {
- // Try to apply nested clip-path as path-based clip.
- if (const absl::optional<Path>& path = PathBasedClipInternal(
- *resource_clipper, uses_zoomed_reference_box, reference_box)) {
- context.ClipPath(path->GetSkPath(), kAntiAliased);
- rest_of_the_chain_already_appled = true;
+ // TODO(crbug.com/1223975): Currently for CompositeClipPathAnimation feature
+ // to be activated a node must have clip-path attribute.
+ if (RuntimeEnabledFeatures::CompositeClipPathAnimationEnabled() &&
+ layout_object.StyleRef().HasCurrentClipPathAnimation() &&
+ layout_object.StyleRef().ClipPath()->GetType() ==
+ ClipPathOperation::SHAPE) {
+ if (!layout_object.GetFrame())
+ return;
+ PaintWorkletBasedClip(context, layout_object, reference_box,
+ uses_zoomed_reference_box);
+ } else {
+ bool is_first = true;
+ bool rest_of_the_chain_already_appled = false;
+ const LayoutObject* current_object = &layout_object;
+ while (!rest_of_the_chain_already_appled && current_object) {
+ const ClipPathOperation* clip_path =
+ current_object->StyleRef().ClipPath();
+ if (!clip_path)
+ break;
+ // We wouldn't have reached here if the current clip-path is a shape,
+ // because it would have been applied as a path-based clip already.
+ LayoutSVGResourceClipper* resource_clipper = ResolveElementReference(
+ *current_object, To<ReferenceClipPathOperation>(*clip_path));
+ if (!resource_clipper)
+ break;
+
+ if (is_first)
+ context.Save();
+ else
+ context.BeginLayer(1.f, SkBlendMode::kDstIn);
+
+ if (resource_clipper->StyleRef().HasClipPath()) {
+ // Try to apply nested clip-path as path-based clip.
+ if (const absl::optional<Path>& path = PathBasedClipInternal(
+ *resource_clipper, uses_zoomed_reference_box, reference_box)) {
+ context.ClipPath(path->GetSkPath(), kAntiAliased);
+ rest_of_the_chain_already_appled = true;
+ }
}
- }
- context.ConcatCTM(MaskToContentTransform(
- *resource_clipper, uses_zoomed_reference_box, reference_box));
- context.DrawRecord(resource_clipper->CreatePaintRecord());
+ context.ConcatCTM(MaskToContentTransform(
+ *resource_clipper, uses_zoomed_reference_box, reference_box));
+ context.DrawRecord(resource_clipper->CreatePaintRecord());
- if (is_first)
- context.Restore();
- else
- context.EndLayer();
+ if (is_first)
+ context.Restore();
+ else
+ context.EndLayer();
- is_first = false;
- current_object = resource_clipper;
+ is_first = false;
+ current_object = resource_clipper;
+ }
}
context.Restore();
}
@@ -219,6 +270,12 @@ void ClipPathClipper::PaintClipPathAsMaskImage(
bool ClipPathClipper::ShouldUseMaskBasedClip(const LayoutObject& object) {
if (object.IsText() || !object.StyleRef().HasClipPath())
return false;
+ // TODO(crbug.com/1223975): Currently for CompositeClipPathAnimation feature
+ // to be activated a node must have clip-path attribute.
+ if (RuntimeEnabledFeatures::CompositeClipPathAnimationEnabled() &&
+ object.StyleRef().ClipPath()->GetType() == ClipPathOperation::SHAPE &&
+ object.StyleRef().HasCurrentClipPathAnimation())
+ return true;
const auto* reference_clip =
DynamicTo<ReferenceClipPathOperation>(object.StyleRef().ClipPath());
if (!reference_clip)
@@ -232,6 +289,14 @@ bool ClipPathClipper::ShouldUseMaskBasedClip(const LayoutObject& object) {
absl::optional<Path> ClipPathClipper::PathBasedClip(
const LayoutObject& clip_path_owner) {
+ // TODO(crbug.com/1223975): Currently for CompositeClipPathAnimation feature
+ // to be activated a node must have clip-path attribute.
+ if (RuntimeEnabledFeatures::CompositeClipPathAnimationEnabled() &&
+ clip_path_owner.StyleRef().HasCurrentClipPathAnimation()) {
+ const ClipPathOperation& clip_path = *clip_path_owner.StyleRef().ClipPath();
+ if (clip_path.GetType() == ClipPathOperation::SHAPE)
+ return absl::nullopt;
+ }
return PathBasedClipInternal(clip_path_owner,
UsesZoomedReferenceBox(clip_path_owner),
LocalReferenceBox(clip_path_owner));
diff --git a/chromium/third_party/blink/renderer/core/paint/collapsed_border_painter.cc b/chromium/third_party/blink/renderer/core/paint/collapsed_border_painter.cc
index b7147fd7f3f..5d84c77416d 100644
--- a/chromium/third_party/blink/renderer/core/paint/collapsed_border_painter.cc
+++ b/chromium/third_party/blink/renderer/core/paint/collapsed_border_painter.cc
@@ -5,7 +5,7 @@
#include "third_party/blink/renderer/core/paint/collapsed_border_painter.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/box_border_painter.h"
#include "third_party/blink/renderer/core/paint/paint_info.h"
#include "third_party/blink/renderer/core/paint/scoped_paint_state.h"
#include "third_party/blink/renderer/core/paint/table_cell_painter.h"
@@ -362,36 +362,36 @@ void CollapsedBorderPainter::PaintCollapsedBorders(
rect.Y() - before_.outer_width,
rect.Width() + before_.begin_outset + before_.end_outset,
before_.outer_width + before_.inner_width);
- ObjectPainter::DrawBoxSide(context, edge_rect, BoxSide::kTop,
- before_.value->GetColor(),
- CollapsedBorderStyle(before_.value->Style()));
+ BoxBorderPainter::DrawBoxSide(context, edge_rect, BoxSide::kTop,
+ before_.value->GetColor(),
+ CollapsedBorderStyle(before_.value->Style()));
}
if (after_.value) {
IntRect edge_rect(rect.X() - after_.begin_outset,
rect.MaxY() - after_.inner_width,
rect.Width() + after_.begin_outset + after_.end_outset,
after_.inner_width + after_.outer_width);
- ObjectPainter::DrawBoxSide(context, edge_rect, BoxSide::kBottom,
- after_.value->GetColor(),
- CollapsedBorderStyle(after_.value->Style()));
+ BoxBorderPainter::DrawBoxSide(context, edge_rect, BoxSide::kBottom,
+ after_.value->GetColor(),
+ CollapsedBorderStyle(after_.value->Style()));
}
if (start_.value) {
IntRect edge_rect(rect.X() - start_.outer_width,
rect.Y() - start_.begin_outset,
start_.outer_width + start_.inner_width,
rect.Height() + start_.begin_outset + start_.end_outset);
- ObjectPainter::DrawBoxSide(context, edge_rect, BoxSide::kLeft,
- start_.value->GetColor(),
- CollapsedBorderStyle(start_.value->Style()));
+ BoxBorderPainter::DrawBoxSide(context, edge_rect, BoxSide::kLeft,
+ start_.value->GetColor(),
+ CollapsedBorderStyle(start_.value->Style()));
}
if (end_.value) {
IntRect edge_rect(rect.MaxX() - end_.inner_width,
rect.Y() - end_.begin_outset,
end_.inner_width + end_.outer_width,
rect.Height() + end_.begin_outset + end_.end_outset);
- ObjectPainter::DrawBoxSide(context, edge_rect, BoxSide::kRight,
- end_.value->GetColor(),
- CollapsedBorderStyle(end_.value->Style()));
+ BoxBorderPainter::DrawBoxSide(context, edge_rect, BoxSide::kRight,
+ end_.value->GetColor(),
+ CollapsedBorderStyle(end_.value->Style()));
}
}
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 68204175c25..ef97248755a 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
@@ -43,6 +43,7 @@
#include "third_party/blink/renderer/core/html/media/html_media_element.h"
#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/inspector/inspector_trace_events.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"
@@ -143,7 +144,7 @@ static bool NeedsDecorationOutlineLayer(const PaintLayer& paint_layer,
// only 2/3 of the width is outside of the offset.
const int outline_drawn_inside =
style.OutlineStyleIsAuto()
- ? std::ceil(style.GetOutlineStrokeWidthForFocusRing() / 3.f) + 1
+ ? std::ceil(style.FocusRingInnerStrokeWidth()) + 1
: 0;
return could_obscure_decorations && style.HasOutline() &&
@@ -690,8 +691,7 @@ void CompositedLayerMapping::ComputeGraphicsLayerParentLocation(
if (compositing_container &&
compositing_container->NeedsCompositedScrolling()) {
auto& layout_box = To<LayoutBox>(compositing_container->GetLayoutObject());
- IntSize scroll_offset =
- FlooredIntSize(layout_box.PixelSnappedScrolledContentOffset());
+ IntPoint scroll_offset = layout_box.PixelSnappedScrolledContentOffset();
IntPoint scroll_origin =
compositing_container->GetScrollableArea()->ScrollOrigin();
scroll_origin.Move(-layout_box.OriginAdjustmentForScrollbars());
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 f0c7eaceb34..352db784fcb 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
@@ -1899,7 +1899,6 @@ TEST_P(CompositedLayerMappingTest,
// Unlike CompositingTest.WillChangeTransformHintInSVG, will-change hints on the
// SVG element itself should not opt into creating layers after paint.
TEST_P(CompositedLayerMappingTest, WillChangeTransformHintOnSVG) {
- ScopedCompositeSVGForTest enable_feature(true);
SetBodyInnerHTML(R"HTML(
<svg width="99" height="99" id="willChange" style="will-change: transform;">
<rect width="100%" height="100%" fill="blue"></rect>
@@ -1914,7 +1913,6 @@ TEST_P(CompositedLayerMappingTest, WillChangeTransformHintOnSVG) {
// Test that will-change changes inside SVG correctly update whether the
// graphics layer should create layers after paint.
TEST_P(CompositedLayerMappingTest, WillChangeTransformHintInSVGChanged) {
- ScopedCompositeSVGForTest enable_feature(true);
SetBodyInnerHTML(R"HTML(
<svg width="99" height="99" id="svg" style="will-change: transform;">
<rect id="rect" width="100%" height="100%" fill="blue"></rect>
diff --git a/chromium/third_party/blink/renderer/core/paint/compositing/compositing_layer_property_updater.cc b/chromium/third_party/blink/renderer/core/paint/compositing/compositing_layer_property_updater.cc
index 10e6f5be696..bd35955cb37 100644
--- a/chromium/third_party/blink/renderer/core/paint/compositing/compositing_layer_property_updater.cc
+++ b/chromium/third_party/blink/renderer/core/paint/compositing/compositing_layer_property_updater.cc
@@ -198,12 +198,10 @@ void CompositingLayerPropertyUpdater::Update(const LayoutObject& object) {
state, snapped_paint_offset + mask_layer->OffsetFromLayoutObject());
}
- if (RuntimeEnabledFeatures::CompositeSVGEnabled()) {
- if (object.IsSVGRoot()) {
- main_graphics_layer->SetShouldCreateLayersAfterPaint(
- To<LayoutSVGRoot>(object).HasDescendantCompositingReasons() &&
- main_graphics_layer->PaintsContentOrHitTest());
- }
+ if (object.IsSVGRoot()) {
+ main_graphics_layer->SetShouldCreateLayersAfterPaint(
+ To<LayoutSVGRoot>(object).HasDescendantCompositingReasons() &&
+ main_graphics_layer->PaintsContentOrHitTest());
}
}
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 054da3469e4..c033093e054 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
@@ -111,7 +111,7 @@ static bool ShouldPreferCompositingForLayoutView(
static CompositingReasons BackfaceInvisibility3DAncestorReason(
const PaintLayer& layer) {
- if (RuntimeEnabledFeatures::TransformInteropEnabled()) {
+ if (RuntimeEnabledFeatures::BackfaceVisibilityInteropEnabled()) {
if (auto* compositing_container = layer.CompositingContainer()) {
if (compositing_container->GetLayoutObject()
.StyleRef()
@@ -153,8 +153,7 @@ CompositingReasons CompositingReasonFinder::DirectReasonsForPaintProperties(
if (RequiresCompositingForRootScroller(*layer))
reasons |= CompositingReason::kRootScroller;
- if (RequiresCompositingForScrollDependentPosition(*layer))
- reasons |= CompositingReason::kScrollDependentPosition;
+ reasons |= CompositingReasonsForScrollDependentPosition(*layer);
if (RequiresCompositingForAffectedByOuterViewportBoundsDelta(object))
reasons |= CompositingReason::kAffectedByOuterViewportBoundsDelta;
@@ -162,6 +161,16 @@ CompositingReasons CompositingReasonFinder::DirectReasonsForPaintProperties(
if (style.HasBackdropFilter())
reasons |= CompositingReason::kBackdropFilter;
+ reasons |= BackfaceInvisibility3DAncestorReason(*layer);
+
+ if (auto* element = DynamicTo<Element>(object.GetNode())) {
+ if (element->ShouldCompositeForDocumentTransition())
+ reasons |= CompositingReason::kDocumentTransitionSharedElement;
+ }
+
+ if (object.CanHaveAdditionalCompositingReasons())
+ reasons |= object.AdditionalCompositingReasons();
+
if (auto* scrollable_area = layer->GetScrollableArea()) {
if (RuntimeEnabledFeatures::CompositeAfterPaintEnabled()) {
bool force_prefer_compositing_to_lcd_text =
@@ -181,16 +190,6 @@ CompositingReasons CompositingReasonFinder::DirectReasonsForPaintProperties(
reasons |= CompositingReason::kOverflowScrolling;
}
- reasons |= BackfaceInvisibility3DAncestorReason(*layer);
-
- if (auto* element = DynamicTo<Element>(object.GetNode())) {
- if (element->ShouldCompositeForDocumentTransition())
- reasons |= CompositingReason::kDocumentTransitionSharedElement;
- }
-
- if (object.CanHaveAdditionalCompositingReasons())
- reasons |= object.AdditionalCompositingReasons();
-
return reasons;
}
@@ -198,8 +197,6 @@ CompositingReasons
CompositingReasonFinder::DirectReasonsForSVGChildPaintProperties(
const LayoutObject& object) {
DCHECK(object.IsSVGChild());
- if (!RuntimeEnabledFeatures::CompositeSVGEnabled())
- return CompositingReason::kNone;
if (object.IsText())
return CompositingReason::kNone;
@@ -280,8 +277,7 @@ CompositingReasons CompositingReasonFinder::NonStyleDeterminedDirectReasons(
}
}
- if (RequiresCompositingForScrollDependentPosition(layer))
- direct_reasons |= CompositingReason::kScrollDependentPosition;
+ direct_reasons |= CompositingReasonsForScrollDependentPosition(layer);
if (RequiresCompositingForAffectedByOuterViewportBoundsDelta(layout_object))
direct_reasons |= CompositingReason::kAffectedByOuterViewportBoundsDelta;
@@ -324,8 +320,6 @@ CompositingReasons CompositingReasonFinder::NonStyleDeterminedDirectReasons(
static bool ObjectTypeSupportsCompositedTransformAnimation(
const LayoutObject& object) {
if (object.IsSVGChild()) {
- if (!RuntimeEnabledFeatures::CompositeSVGEnabled())
- return false;
// Transforms are not supported on hidden containers, inlines, or text.
return !object.IsSVGHiddenContainer() && !object.IsLayoutInline() &&
!object.IsText();
@@ -390,8 +384,10 @@ bool CompositingReasonFinder::RequiresCompositingForRootScroller(
return layer.GetLayoutObject().IsGlobalRootScroller();
}
-bool CompositingReasonFinder::RequiresCompositingForScrollDependentPosition(
+CompositingReasons
+CompositingReasonFinder::CompositingReasonsForScrollDependentPosition(
const PaintLayer& layer) {
+ CompositingReasons reasons = CompositingReason::kNone;
// Don't promote fixed position elements that are descendants of a non-view
// container, e.g. transformed elements. They will stay fixed wrt the
// container rather than the enclosing frame.
@@ -400,20 +396,19 @@ bool CompositingReasonFinder::RequiresCompositingForScrollDependentPosition(
// position elements are composited under overflow: hidden, which can still
// have smooth scroll animations.
LocalFrameView* frame_view = layer.GetLayoutObject().GetFrameView();
- return frame_view->LayoutViewport()->HasOverflow();
+ if (frame_view->LayoutViewport()->HasOverflow())
+ reasons |= CompositingReason::kFixedPosition;
}
// Don't promote sticky position elements that cannot move with scrolls.
- if (layer.SticksToScroller()) {
- // We check for |HasOverflow| instead of |ScrollsOverflow| to ensure sticky
- // position elements are composited under overflow: hidden, which can still
- // have smooth scroll animations.
- return layer.AncestorScrollContainerLayer()
- ->GetScrollableArea()
- ->HasOverflow();
- }
+ // We check for |HasOverflow| instead of |ScrollsOverflow| to ensure sticky
+ // position elements are composited under overflow: hidden, which can still
+ // have smooth scroll animations.
+ if (layer.SticksToScroller() &&
+ layer.AncestorScrollContainerLayer()->GetScrollableArea()->HasOverflow())
+ reasons |= CompositingReason::kStickyPosition;
- return false;
+ return reasons;
}
bool CompositingReasonFinder::
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 7640882046b..e865fa835aa 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
@@ -48,7 +48,8 @@ class CORE_EXPORT CompositingReasonFinder {
const LayoutObject&);
static bool RequiresCompositingForRootScroller(const PaintLayer&);
- static bool RequiresCompositingForScrollDependentPosition(const PaintLayer&);
+ static CompositingReasons CompositingReasonsForScrollDependentPosition(
+ const PaintLayer&);
static bool RequiresCompositingForAffectedByOuterViewportBoundsDelta(
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 67da1f6ba88..0379506f503 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
@@ -6,6 +6,7 @@
#include "base/test/scoped_feature_list.h"
#include "third_party/blink/public/common/features.h"
+#include "third_party/blink/renderer/core/css/resolver/style_resolver.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_block.h"
@@ -140,27 +141,31 @@ TEST_F(CompositingReasonFinderTest, OnlyScrollingStickyPositionPromoted) {
auto& sticky_scrolling =
*To<LayoutBoxModelObject>(GetLayoutObjectByElementId("sticky-scrolling"));
- EXPECT_TRUE(
- CompositingReasonFinder::RequiresCompositingForScrollDependentPosition(
- *sticky_scrolling.Layer()));
+ EXPECT_EQ(
+ CompositingReasonFinder::CompositingReasonsForScrollDependentPosition(
+ *sticky_scrolling.Layer()),
+ CompositingReason::kStickyPosition);
auto& sticky_no_scrolling = *To<LayoutBoxModelObject>(
GetLayoutObjectByElementId("sticky-no-scrolling"));
- EXPECT_FALSE(
- CompositingReasonFinder::RequiresCompositingForScrollDependentPosition(
- *sticky_no_scrolling.Layer()));
+ EXPECT_EQ(
+ CompositingReasonFinder::CompositingReasonsForScrollDependentPosition(
+ *sticky_no_scrolling.Layer()),
+ CompositingReason::kNone);
auto& overflow_hidden_scrolling = *To<LayoutBoxModelObject>(
GetLayoutObjectByElementId("overflow-hidden-scrolling"));
- EXPECT_TRUE(
- CompositingReasonFinder::RequiresCompositingForScrollDependentPosition(
- *overflow_hidden_scrolling.Layer()));
+ EXPECT_EQ(
+ CompositingReasonFinder::CompositingReasonsForScrollDependentPosition(
+ *overflow_hidden_scrolling.Layer()),
+ CompositingReason::kStickyPosition);
auto& overflow_hidden_no_scrolling = *To<LayoutBoxModelObject>(
GetLayoutObjectByElementId("overflow-hidden-no-scrolling"));
- EXPECT_FALSE(
- CompositingReasonFinder::RequiresCompositingForScrollDependentPosition(
- *overflow_hidden_no_scrolling.Layer()));
+ EXPECT_EQ(
+ CompositingReasonFinder::CompositingReasonsForScrollDependentPosition(
+ *overflow_hidden_no_scrolling.Layer()),
+ CompositingReason::kNone);
if (RuntimeEnabledFeatures::CompositeAfterPaintEnabled()) {
EXPECT_EQ(kPaintsIntoOwnBacking,
@@ -252,27 +257,31 @@ TEST_F(CompositingReasonFinderTest, PromoteCrossOriginIframe) {
<!DOCTYPE html>
<iframe id=iframe></iframe>
)HTML");
- UpdateAllLifecyclePhasesForTest();
HTMLFrameOwnerElement* iframe =
To<HTMLFrameOwnerElement>(GetDocument().getElementById("iframe"));
ASSERT_TRUE(iframe);
+ iframe->contentDocument()->OverrideIsInitialEmptyDocument();
+ To<LocalFrame>(iframe->ContentFrame())->View()->BeginLifecycleUpdates();
ASSERT_FALSE(iframe->ContentFrame()->IsCrossOriginToMainFrame());
+ UpdateAllLifecyclePhasesForTest();
LayoutView* iframe_layout_view =
To<LocalFrame>(iframe->ContentFrame())->ContentLayoutObject();
ASSERT_TRUE(iframe_layout_view);
PaintLayer* iframe_layer = iframe_layout_view->Layer();
ASSERT_TRUE(iframe_layer);
- EXPECT_EQ(kNotComposited, iframe_layer->DirectCompositingReasons());
+ EXPECT_EQ(CompositingReason::kNone, iframe_layer->DirectCompositingReasons());
+ EXPECT_FALSE(iframe_layer->GetScrollableArea()->NeedsCompositedScrolling());
+ EXPECT_EQ(CompositingReason::kNone,
+ CompositingReasonFinder::DirectReasonsForPaintProperties(
+ *iframe_layout_view));
SetBodyInnerHTML(R"HTML(
<!DOCTYPE html>
<iframe id=iframe sandbox></iframe>
)HTML");
iframe = To<HTMLFrameOwnerElement>(GetDocument().getElementById("iframe"));
- To<LocalFrame>(iframe->ContentFrame())
- ->GetDocument()
- ->OverrideIsInitialEmptyDocument();
+ iframe->contentDocument()->OverrideIsInitialEmptyDocument();
To<LocalFrame>(iframe->ContentFrame())->View()->BeginLifecycleUpdates();
UpdateAllLifecyclePhasesForTest();
iframe_layout_view =
@@ -282,11 +291,25 @@ TEST_F(CompositingReasonFinderTest, PromoteCrossOriginIframe) {
ASSERT_TRUE(iframe->ContentFrame()->IsCrossOriginToMainFrame());
EXPECT_EQ(CompositingReason::kIFrame,
iframe_layer->DirectCompositingReasons());
+ EXPECT_FALSE(iframe_layer->GetScrollableArea()->NeedsCompositedScrolling());
+ EXPECT_EQ(CompositingReason::kIFrame,
+ CompositingReasonFinder::DirectReasonsForPaintProperties(
+ *iframe_layout_view));
+
+ // Make the iframe contents scrollable.
+ iframe->contentDocument()->body()->setAttribute(html_names::kStyleAttr,
+ "height: 2000px");
+ UpdateAllLifecyclePhasesForTest();
+ EXPECT_TRUE(iframe_layer->GetScrollableArea()->NeedsCompositedScrolling());
+ EXPECT_EQ(CompositingReason::kIFrame | CompositingReason::kOverflowScrolling,
+ CompositingReasonFinder::DirectReasonsForPaintProperties(
+ *iframe_layout_view));
}
TEST_F(CompositingReasonFinderTest,
CompositeWithBackfaceVisibilityAncestorAndPreserve3D) {
- ScopedTransformInteropForTest enabled(true);
+ ScopedTransformInteropForTest ti_enabled(true);
+ ScopedBackfaceVisibilityInteropForTest bfi_enabled(true);
SetBodyInnerHTML(R"HTML(
<!DOCTYPE html>
@@ -307,7 +330,8 @@ TEST_F(CompositingReasonFinderTest,
TEST_F(CompositingReasonFinderTest,
CompositeWithBackfaceVisibilityAncestorAndPreserve3DWithInterveningDiv) {
- ScopedTransformInteropForTest enabled(true);
+ ScopedTransformInteropForTest ti_enabled(true);
+ ScopedBackfaceVisibilityInteropForTest bfi_enabled(true);
SetBodyInnerHTML(R"HTML(
<!DOCTYPE html>
@@ -330,7 +354,8 @@ TEST_F(CompositingReasonFinderTest,
TEST_F(CompositingReasonFinderTest,
CompositeWithBackfaceVisibilityAncestorWithInterveningStackingDiv) {
- ScopedTransformInteropForTest enabled(true);
+ ScopedTransformInteropForTest ti_enabled(true);
+ ScopedBackfaceVisibilityInteropForTest bfi_enabled(true);
SetBodyInnerHTML(R"HTML(
<!DOCTYPE html>
@@ -358,7 +383,8 @@ TEST_F(CompositingReasonFinderTest,
TEST_F(CompositingReasonFinderTest,
CompositeWithBackfaceVisibilityAncestorAndFlattening) {
- ScopedTransformInteropForTest enabled(true);
+ ScopedTransformInteropForTest ti_enabled(true);
+ ScopedBackfaceVisibilityInteropForTest bfi_enabled(true);
SetBodyInnerHTML(R"HTML(
<!DOCTYPE html>
@@ -378,7 +404,8 @@ TEST_F(CompositingReasonFinderTest,
}
TEST_F(CompositingReasonFinderTest, CompositeWithBackfaceVisibility) {
- ScopedTransformInteropForTest enabled(true);
+ ScopedTransformInteropForTest ti_enabled(true);
+ ScopedBackfaceVisibilityInteropForTest bfi_enabled(true);
SetBodyInnerHTML(R"HTML(
<!DOCTYPE html>
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 2606987c68e..c0078d40a28 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
@@ -5,6 +5,8 @@
#include "base/test/scoped_feature_list.h"
#include "build/build_config.h"
#include "cc/layers/picture_layer.h"
+#include "cc/layers/recording_source.h"
+#include "cc/layers/surface_layer.h"
#include "cc/trees/compositor_commit_data.h"
#include "cc/trees/effect_node.h"
#include "cc/trees/layer_tree_host.h"
@@ -25,6 +27,7 @@
#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/svg_names.h"
+#include "third_party/blink/renderer/core/testing/fake_remote_frame_host.h"
#include "third_party/blink/renderer/core/testing/sim/sim_request.h"
#include "third_party/blink/renderer/core/testing/sim/sim_test.h"
#include "third_party/blink/renderer/platform/graphics/compositing/paint_artifact_compositor.h"
@@ -245,7 +248,6 @@ TEST_P(CompositingTest, WillChangeTransformHint) {
}
TEST_P(CompositingTest, WillChangeTransformHintInSVG) {
- ScopedCompositeSVGForTest enable_feature(true);
InitializeWithHTML(*WebView()->MainFrameImpl()->GetFrame(), R"HTML(
<!doctype html>
<style>
@@ -268,7 +270,6 @@ TEST_P(CompositingTest, WillChangeTransformHintInSVG) {
}
TEST_P(CompositingTest, Compositing3DTransformOnSVGModelObject) {
- ScopedCompositeSVGForTest enable_feature(true);
InitializeWithHTML(*WebView()->MainFrameImpl()->GetFrame(), R"HTML(
<!doctype html>
<svg width="200" height="200">
@@ -310,7 +311,6 @@ TEST_P(CompositingTest, Compositing3DTransformOnSVGModelObject) {
}
TEST_P(CompositingTest, Compositing3DTransformOnSVGBlock) {
- ScopedCompositeSVGForTest enable_feature(true);
InitializeWithHTML(*WebView()->MainFrameImpl()->GetFrame(), R"HTML(
<!doctype html>
<svg width="200" height="200">
@@ -354,7 +354,6 @@ TEST_P(CompositingTest, Compositing3DTransformOnSVGBlock) {
// Inlines do not support the transform property and should not be composited
// due to 3D transforms.
TEST_P(CompositingTest, NotCompositing3DTransformOnSVGInline) {
- ScopedCompositeSVGForTest enable_feature(true);
InitializeWithHTML(*WebView()->MainFrameImpl()->GetFrame(), R"HTML(
<!doctype html>
<svg width="200" height="200">
@@ -379,7 +378,6 @@ TEST_P(CompositingTest, NotCompositing3DTransformOnSVGInline) {
}
TEST_P(CompositingTest, PaintPropertiesWhenCompositingSVG) {
- ScopedCompositeSVGForTest enable_feature(true);
InitializeWithHTML(*WebView()->MainFrameImpl()->GetFrame(), R"HTML(
<!doctype html>
<style>
@@ -886,7 +884,6 @@ TEST_P(CompositingSimTest, DirectTransformPropertyUpdate) {
}
TEST_P(CompositingSimTest, DirectSVGTransformPropertyUpdate) {
- ScopedCompositeSVGForTest enable_feature(true);
InitializeWithHTML(R"HTML(
<!doctype html>
<style>
@@ -1002,11 +999,6 @@ TEST_P(CompositingSimTest, DirectTransformPropertyUpdateCausesChange) {
// so that the browser controls movement adjustments needed by bottom-fixed
// elements will work.
TEST_P(CompositingSimTest, AffectedByOuterViewportBoundsDelta) {
- // TODO(bokan): This test will have to be reevaluated for CAP. It looks like
- // the fixed layer isn't composited in CAP.
- if (RuntimeEnabledFeatures::CompositeAfterPaintEnabled())
- return;
-
InitializeWithHTML(R"HTML(
<!DOCTYPE html>
<style>
@@ -1024,10 +1016,6 @@ TEST_P(CompositingSimTest, AffectedByOuterViewportBoundsDelta) {
auto* fixed_element = GetElementById("fixed");
auto* fixed_element_layer = CcLayerByDOMElementId("fixed");
- DCHECK_EQ(fixed_element_layer->element_id(),
- CompositorElementIdFromUniqueObjectId(
- fixed_element->GetLayoutObject()->UniqueId(),
- CompositorElementIdNamespace::kPrimary));
// Fix the DIV to the bottom of the viewport. Since the viewport height will
// expand/contract, the fixed element will need to be moved as the bounds
@@ -1108,12 +1096,6 @@ TEST_P(CompositingSimTest, DirectTransformOriginPropertyUpdate) {
// This test is similar to |LayerSubtreeTransformPropertyChanged| but for
// effect property node changes.
TEST_P(CompositingSimTest, LayerSubtreeEffectPropertyChanged) {
- // 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>
@@ -1140,16 +1122,7 @@ TEST_P(CompositingSimTest, LayerSubtreeEffectPropertyChanged) {
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());
@@ -1161,11 +1134,9 @@ TEST_P(CompositingSimTest, LayerSubtreeEffectPropertyChanged) {
// both layers.
outer_element->setAttribute(html_names::kStyleAttr, "filter: blur(20px)");
UpdateAllLifecyclePhases();
- // TODO(wangxianzhu): Probably avoid setting this flag on transform change.
EXPECT_TRUE(outer_element_layer->subtree_property_changed());
// Set by blink::PropertyTreeManager.
EXPECT_TRUE(GetEffectNode(outer_element_layer)->effect_changed);
- // TODO(wangxianzhu): Probably avoid setting this flag on transform change.
EXPECT_TRUE(inner_element_layer->subtree_property_changed());
EXPECT_FALSE(GetEffectNode(inner_element_layer)->effect_changed);
@@ -1229,12 +1200,6 @@ TEST_P(CompositingSimTest, LayerSubtreeClipPropertyChanged) {
}
TEST_P(CompositingSimTest, LayerSubtreeOverflowClipPropertyChanged) {
- // 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>
@@ -1262,12 +1227,7 @@ TEST_P(CompositingSimTest, LayerSubtreeOverflowClipPropertyChanged) {
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());
@@ -1482,11 +1442,6 @@ TEST_P(CompositingSimTest, RootScrollingContentsSafeOpaqueBackgroundColor) {
}
TEST_P(CompositingSimTest, NonDrawableLayersIgnoredForRenderSurfaces) {
- // TODO(crbug.com/765003): CAP may make different layerization decisions. When
- // CAP gets closer to launch, this test should be updated to pass.
- if (RuntimeEnabledFeatures::CompositeAfterPaintEnabled())
- return;
-
InitializeWithHTML(R"HTML(
<!DOCTYPE html>
<style>
@@ -1746,13 +1701,6 @@ TEST_P(CompositingTest, EffectNodesShouldHaveStableIds) {
}
TEST_P(CompositingSimTest, ImplSideScrollSkipsCommit) {
- // TODO(crbug.com/1046544): This test fails with CompositeAfterPaint because
- // PaintArtifactCompositor::Update is run for scroll offset changes. When we
- // have an early-out to avoid SetNeedsCommit for non-changing interest-rects,
- // this test will pass.
- if (RuntimeEnabledFeatures::CompositeAfterPaintEnabled())
- return;
-
InitializeWithHTML(R"HTML(
<div id='scroller' style='will-change: transform; overflow: scroll;
width: 100px; height: 100px'>
@@ -2006,11 +1954,10 @@ TEST_P(CompositingSimTest, MultipleChunkBackgroundColorChangeRepaintUpdate) {
EXPECT_EQ(scrolling_contents->background_color(), SK_ColorWHITE);
}
-// Similar to |BackgroundColorChangeUsesRepaintUpdate| but with CompositeSVG.
-// This test changes paint for a composited SVG element, as well as a regular
-// HTML element in the presence of composited SVG.
+// Similar to |BackgroundColorChangeUsesRepaintUpdate| but with post-paint
+// composited SVG. This test changes paint for a composited SVG element, as well
+// as a regular HTML element in the presence of composited SVG.
TEST_P(CompositingSimTest, SVGColorChangeUsesRepaintUpdate) {
- ScopedCompositeSVGForTest enable_feature(true);
InitializeWithHTML(R"HTML(
<!DOCTYPE html>
<style>
@@ -2150,6 +2097,63 @@ TEST_P(CompositingSimTest, ChangingContentsOpaqueForTextRequiresFullUpdate) {
EXPECT_FALSE(CcLayerByDOMElementId("target")->contents_opaque_for_text());
}
+TEST_P(CompositingSimTest, ContentsOpaqueForTextWithSubpixelSizeSimpleBg) {
+ InitializeWithHTML(R"HTML(
+ <!DOCTYPE html>
+ <div id="target" style="will-change: transform; background: white;
+ width: 100.6px; height: 10.3px">
+ TEXT
+ </div>
+ )HTML");
+ Compositor().BeginFrame();
+ auto* cc_layer = CcLayerByDOMElementId("target");
+ // In CompositeAfterPaint, we adjust visual rect of the DrawingDisplayItem
+ // with simple painting to the bounds of the painting.
+ EXPECT_EQ(gfx::Size(101, 10), cc_layer->bounds());
+ EXPECT_TRUE(cc_layer->contents_opaque());
+ EXPECT_TRUE(cc_layer->contents_opaque_for_text());
+}
+
+TEST_P(CompositingSimTest, ContentsOpaqueForTextWithSubpixelSizeComplexBg) {
+ InitializeWithHTML(R"HTML(
+ <!DOCTYPE html>
+ <div id="target" style="will-change: transform; background: white;
+ border: 2px inset blue;
+ width: 100.6px; height: 10.3px">
+ TEXT
+ </div>
+ )HTML");
+ Compositor().BeginFrame();
+ auto* cc_layer = CcLayerByDOMElementId("target");
+ if (RuntimeEnabledFeatures::CompositeAfterPaintEnabled()) {
+ EXPECT_EQ(gfx::Size(105, 15), cc_layer->bounds());
+ EXPECT_FALSE(cc_layer->contents_opaque());
+ } else {
+ // Pre-CAP always pixel-snaps composited layer bounds, which might be
+ // incorrect in some corner cases where we don't pixel-snap painting.
+ EXPECT_EQ(gfx::Size(105, 14), cc_layer->bounds());
+ EXPECT_TRUE(cc_layer->contents_opaque());
+ }
+ EXPECT_TRUE(cc_layer->contents_opaque_for_text());
+}
+
+TEST_P(CompositingSimTest, ContentsOpaqueForTextWithPartialBackground) {
+ // This test works only with the new text opaque algorithm.
+ if (!RuntimeEnabledFeatures::CompositeAfterPaintEnabled())
+ return;
+
+ InitializeWithHTML(R"HTML(
+ <!DOCTYPE html>
+ <div id="target" style="will-change: transform; padding: 10px">
+ <div style="background: white">TEXT</div>
+ </div>
+ )HTML");
+ Compositor().BeginFrame();
+ auto* cc_layer = CcLayerByDOMElementId("target");
+ EXPECT_FALSE(cc_layer->contents_opaque());
+ EXPECT_TRUE(cc_layer->contents_opaque_for_text());
+}
+
TEST_P(CompositingSimTest, FullCompositingUpdateReasons) {
InitializeWithHTML(R"HTML(
<!DOCTYPE html>
@@ -2218,9 +2222,9 @@ TEST_P(CompositingSimTest, FullCompositingUpdateReasons) {
PaintArtifactCompositor::PreviousUpdateType::kFull);
}
-// Similar to |FullCompositingUpdateReasons| but for changes in CompositeSVG.
-TEST_P(CompositingSimTest, FullCompositingUpdateReasonInCompositeSVG) {
- ScopedCompositeSVGForTest enable_feature(true);
+// Similar to |FullCompositingUpdateReasons| but for changes in post-paint
+// composited SVG.
+TEST_P(CompositingSimTest, FullCompositingUpdateReasonWithCompositedSVG) {
InitializeWithHTML(R"HTML(
<!DOCTYPE html>
<style>
@@ -2252,7 +2256,6 @@ TEST_P(CompositingSimTest, FullCompositingUpdateReasonInCompositeSVG) {
}
TEST_P(CompositingSimTest, FullCompositingUpdateForJustCreatedChunks) {
- ScopedCompositeSVGForTest enable_feature(true);
InitializeWithHTML(R"HTML(
<!DOCTYPE html>
<style>
@@ -2290,7 +2293,6 @@ TEST_P(CompositingSimTest, FullCompositingUpdateForJustCreatedChunks) {
}
TEST_P(CompositingSimTest, FullCompositingUpdateForUncachableChunks) {
- ScopedCompositeSVGForTest enable_feature(true);
InitializeWithHTML(R"HTML(
<!DOCTYPE html>
<style>
@@ -2376,4 +2378,194 @@ TEST_P(CompositingSimTest, DecompositeScrollerInHiddenIframe) {
EXPECT_FALSE(scroller->GetScrollableArea()->NeedsCompositedScrolling());
}
+TEST_P(CompositingSimTest, ForeignLayersInMovedSubsequence) {
+ SimRequest main_resource("https://origin-a.com/a.html", "text/html");
+ LoadURL("https://origin-a.com/a.html");
+ main_resource.Complete(R"HTML(
+ <!DOCTYPE html>
+ <style> iframe { isolation: isolate; } </style>
+ <iframe sandbox src="https://origin-b.com/b.html"></iframe>
+ <div id="target" style="background: blue;">a</div>
+ )HTML");
+
+ frame_test_helpers::TestWebRemoteFrameClient remote_frame_client;
+ FakeRemoteFrameHost remote_frame_host;
+ remote_frame_host.Init(remote_frame_client.GetRemoteAssociatedInterfaces());
+ WebRemoteFrameImpl* remote_frame =
+ frame_test_helpers::CreateRemote(&remote_frame_client);
+ MainFrame().FirstChild()->Swap(remote_frame);
+
+ Compositor().BeginFrame();
+
+ auto remote_surface_layer = cc::SurfaceLayer::Create();
+ remote_frame->GetFrame()->SetCcLayerForTesting(remote_surface_layer, true);
+ Compositor().BeginFrame();
+
+ // Initially, no update is needed.
+ EXPECT_FALSE(paint_artifact_compositor()->NeedsUpdate());
+
+ // Clear the previous update to ensure we record a new one in the next update.
+ paint_artifact_compositor()->ClearPreviousUpdateForTesting();
+
+ // Modifying paint in a simple way only requires a repaint update.
+ auto* target_element = GetElementById("target");
+ target_element->setAttribute(html_names::kStyleAttr, "background: green;");
+ Compositor().BeginFrame();
+ EXPECT_EQ(paint_artifact_compositor()->PreviousUpdateForTesting(),
+ PaintArtifactCompositor::PreviousUpdateType::kRepaint);
+
+ remote_frame->Detach();
+}
+
+// While not required for correctness, it is important for performance that
+// snapped backgrounds use solid color layers which avoid tiling.
+TEST_P(CompositingSimTest, SolidColorLayersWithSnapping) {
+ InitializeWithHTML(R"HTML(
+ <!DOCTYPE html>
+ <style>
+ #snapDown {
+ width: 60.1px;
+ height: 100px;
+ will-change: opacity;
+ background: blue;
+ }
+ #snapUp {
+ width: 60.9px;
+ height: 100px;
+ will-change: opacity;
+ background: blue;
+ }
+ </style>
+ <div id="snapDown"></div>
+ <div id="snapUp"></div>
+ )HTML");
+
+ Compositor().BeginFrame();
+
+ auto* snap_down =
+ static_cast<const cc::PictureLayer*>(CcLayerByDOMElementId("snapDown"));
+ EXPECT_TRUE(snap_down->GetRecordingSourceForTesting()->is_solid_color());
+ auto* snap_up =
+ static_cast<const cc::PictureLayer*>(CcLayerByDOMElementId("snapUp"));
+ EXPECT_TRUE(snap_up->GetRecordingSourceForTesting()->is_solid_color());
+}
+
+TEST_P(CompositingSimTest, SolidColorLayerWithSubpixelTransform) {
+ if (!RuntimeEnabledFeatures::CompositeAfterPaintEnabled())
+ return;
+
+ InitializeWithHTML(R"HTML(
+ <!DOCTYPE html>
+ <style>
+ #forceCompositing {
+ position: absolute;
+ width: 100px;
+ height: 100px;
+ will-change: transform;
+ }
+ #target {
+ position: absolute;
+ top: 0;
+ left: 0;
+ width: 60.9px;
+ height: 60.1px;
+ transform: translate(0.4px, 0.6px);
+ background: blue;
+ }
+ </style>
+ <div id="forceCompositing"></div>
+ <div id="target"></div>
+ )HTML");
+
+ Compositor().BeginFrame();
+
+ auto* target =
+ static_cast<const cc::PictureLayer*>(CcLayerByDOMElementId("target"));
+ EXPECT_TRUE(target->GetRecordingSourceForTesting()->is_solid_color());
+ EXPECT_NEAR(0.4, target->offset_to_transform_parent().x(), 0.001);
+ EXPECT_NEAR(0.6, target->offset_to_transform_parent().y(), 0.001);
+}
+
+// While not required for correctness, it is important for performance (e.g.,
+// the MotionMark Focus benchmark) that we do not decomposite effect nodes (see:
+// |PaintArtifactCompositor::DecompositeEffect|) when the author has specified
+// 3D transforms which are frequently used as a generic compositing trigger.
+TEST_P(CompositingSimTest, EffectCompositedWith3DTransform) {
+ InitializeWithHTML(R"HTML(
+ <!DOCTYPE html>
+ <style>
+ div {
+ width: 100px;
+ height: 100px;
+ background: rebeccapurple;
+ transform: translate3d(1px, 1px, 0);
+ }
+ </style>
+ <div id="opacity" style="opacity: 0.5;"></div>
+ <div id="filter" style="filter: blur(1px);"></div>
+ )HTML");
+ Compositor().BeginFrame();
+
+ auto* opacity_effect = GetEffectNode(CcLayerByDOMElementId("opacity"));
+ EXPECT_TRUE(opacity_effect);
+ EXPECT_EQ(opacity_effect->opacity, 0.5f);
+ EXPECT_TRUE(opacity_effect->filters.IsEmpty());
+
+ auto* filter_effect = GetEffectNode(CcLayerByDOMElementId("filter"));
+ EXPECT_TRUE(filter_effect);
+ EXPECT_EQ(filter_effect->opacity, 1.f);
+ EXPECT_FALSE(filter_effect->filters.IsEmpty());
+}
+
+// The main thread will not have a chance to update the painted content of an
+// animation running on the compositor, so ensure the cc::Layer with animating
+// opacity has content when starting the animation, even if the opacity is
+// initially 0.
+TEST_P(CompositingSimTest, CompositorAnimationOfOpacityHasPaintedContent) {
+ InitializeWithHTML(R"HTML(
+ <!DOCTYPE html>
+ <style>
+ @keyframes opacity {
+ 0% { opacity: 0; }
+ 99% { opacity: 0; }
+ 100% { opacity: 0.5; }
+ }
+ #animation {
+ animation-name: opacity;
+ animation-duration: 999s;
+ width: 100px;
+ height: 100px;
+ background: lightblue;
+ }
+ </style>
+ <div id="animation"></div>
+ )HTML");
+ Compositor().BeginFrame();
+ EXPECT_TRUE(CcLayerByDOMElementId("animation")->DrawsContent());
+}
+
+TEST_P(CompositingSimTest, CompositorAnimationOfNonInvertibleTransform) {
+ InitializeWithHTML(R"HTML(
+ <!DOCTYPE html>
+ <style>
+ @keyframes anim {
+ 0% { transform: scale(0); }
+ 99% { transform: scale(0); }
+ 100% { transform: scale(1); }
+ }
+ #animation {
+ animation-name: anim;
+ animation-duration: 999s;
+ width: 100px;
+ height: 100px;
+ background: lightblue;
+ }
+ </style>
+ <div id="animation"></div>
+ )HTML");
+ Compositor().BeginFrame();
+ EXPECT_TRUE(CcLayerByDOMElementId("animation"));
+ EXPECT_TRUE(CcLayerByDOMElementId("animation")->DrawsContent());
+}
+
} // namespace blink
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 72c31017cc4..96f0d086e68 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
@@ -174,7 +174,7 @@ void PaintLayerCompositor::UpdateAssignmentsIfNeededRecursive(
UpdateAssignmentsIfNeededRecursiveInternal(target_state,
compositing_reasons_stats);
UMA_HISTOGRAM_CUSTOM_COUNTS("Blink.Compositing.LayerPromotionCount.Overlap",
- compositing_reasons_stats.overlap_layers, 1, 100,
+ compositing_reasons_stats.overlap_layers, 1, 1000,
5);
UMA_HISTOGRAM_CUSTOM_COUNTS(
"Blink.Compositing.LayerPromotionCount.ActiveAnimation",
diff --git a/chromium/third_party/blink/renderer/core/paint/cull_rect_updater.cc b/chromium/third_party/blink/renderer/core/paint/cull_rect_updater.cc
index bc3a8a1da42..e024453dfc3 100644
--- a/chromium/third_party/blink/renderer/core/paint/cull_rect_updater.cc
+++ b/chromium/third_party/blink/renderer/core/paint/cull_rect_updater.cc
@@ -49,34 +49,37 @@ bool SetFragmentContentsCullRect(PaintLayer& layer,
} // anonymous namespace
-CullRectUpdater::CullRectUpdater(PaintLayer& root_layer)
- : root_layer_(root_layer),
- root_state_(root_layer.GetLayoutObject()
- .FirstFragment()
- .LocalBorderBoxProperties()
- .Unalias()) {
- DCHECK(root_layer.IsRootLayer());
+void CullRectUpdater::Update() {
+ DCHECK(starting_layer_.IsRootLayer());
+ UpdateInternal(CullRect::Infinite());
+#if DCHECK_IS_ON()
+ if (VLOG_IS_ON(2)) {
+ VLOG(2) << "PaintLayer tree after cull rect update:";
+ showLayerTree(&starting_layer_);
+ }
+#endif
}
void CullRectUpdater::UpdateInternal(const CullRect& input_cull_rect) {
DCHECK(RuntimeEnabledFeatures::CullRectUpdateEnabled());
- DCHECK(root_layer_.IsRootLayer());
- if (root_layer_.GetLayoutObject().GetFrameView()->ShouldThrottleRendering())
+ const auto& object = starting_layer_.GetLayoutObject();
+ if (object.GetFrameView()->ShouldThrottleRendering())
return;
+ root_state_ =
+ object.View()->FirstFragment().LocalBorderBoxProperties().Unalias();
bool should_use_infinite =
- PaintLayerPainter(root_layer_).ShouldUseInfiniteCullRect();
- auto& fragment =
- root_layer_.GetLayoutObject().GetMutableForPainting().FirstFragment();
+ PaintLayerPainter(starting_layer_).ShouldUseInfiniteCullRect();
+ auto& fragment = object.GetMutableForPainting().FirstFragment();
SetFragmentCullRect(
- root_layer_, fragment,
+ starting_layer_, fragment,
should_use_infinite ? CullRect::Infinite() : input_cull_rect);
bool force_update_children = SetFragmentContentsCullRect(
- root_layer_, fragment,
+ starting_layer_, fragment,
should_use_infinite ? CullRect::Infinite()
: ComputeFragmentContentsCullRect(
- root_layer_, fragment, input_cull_rect));
- UpdateForDescendants(root_layer_, force_update_children);
+ starting_layer_, fragment, input_cull_rect));
+ UpdateForDescendants(starting_layer_, force_update_children);
}
void CullRectUpdater::UpdateRecursively(PaintLayer& layer,
@@ -89,13 +92,27 @@ void CullRectUpdater::UpdateRecursively(PaintLayer& layer,
// This defines the scope of force_proactive_update_ (which may be set by
// ComputeFragmentCullRect() and ComputeFragmentContentsCullRect()) to the
// subtree.
- base::AutoReset<bool> reset(&force_proactive_update_,
- force_proactive_update_);
+ base::AutoReset<bool> reset_force_update(&force_proactive_update_,
+ force_proactive_update_);
if (force_update_self || should_proactively_update ||
layer.NeedsCullRectUpdate())
force_update_children |= UpdateForSelf(layer, parent_painting_layer);
+ absl::optional<base::AutoReset<bool>> reset_subtree_is_out_of_cull_rect;
+ if (!subtree_is_out_of_cull_rect_ && layer.KnownToClipSubtree() &&
+ !layer.GetLayoutObject().FirstFragment().NextFragment()) {
+ const auto* box = layer.GetLayoutBox();
+ DCHECK(box);
+ PhysicalRect overflow_rect = box->PhysicalSelfVisualOverflowRect();
+ overflow_rect.Move(box->FirstFragment().PaintOffset());
+ if (!box->FirstFragment().GetCullRect().Intersects(
+ EnclosingIntRect(overflow_rect))) {
+ reset_subtree_is_out_of_cull_rect.emplace(&subtree_is_out_of_cull_rect_,
+ true);
+ }
+ }
+
if (force_update_children || layer.DescendantNeedsCullRectUpdate())
UpdateForDescendants(layer, force_update_children);
@@ -134,9 +151,9 @@ void CullRectUpdater::UpdateForDescendants(PaintLayer& layer,
// <div id="stacked-child" style="position: relative"></div>
// </div>
// </div>
- // If |child|'s contents cull rect changes, we need to update |stack-child|'s
- // cull rect because it's clipped by |child|. The is done in the following
- // order:
+ // If |child|'s contents cull rect changes, we need to update
+ // |stacked-child|'s cull rect because it's clipped by |child|. The is done in
+ // the following order:
// UpdateForDescendants(|layer|)
// UpdateRecursively(|child|) (in the following loop)
// |stacked-child|->SetNeedsCullRectUpdate()
@@ -171,46 +188,55 @@ bool CullRectUpdater::UpdateForSelf(PaintLayer& layer,
parent_painting_layer.GetLayoutObject().FirstFragment();
auto& first_fragment =
layer.GetLayoutObject().GetMutableForPainting().FirstFragment();
- // If both |this| and |root_layer| are fragmented and are inside the same
- // pagination container, then try to match fragments from |root_layer| to
- // |this|, so that any fragment clip for |root_layer|'s fragment matches
- // |this|'s. Note we check both ShouldFragmentCompositedBounds() and next
- // fragment here because the former may return false even if |this| is
- // fragmented, e.g. for fixed-position objects in paged media, and the next
- // fragment can be null even if the first fragment is actually in a fragmented
- // context when the current layer appears in only one of the multiple
- // fragments of the pagination container.
+ // If both |layer| and |parent_painting_layer| are fragmented and are inside
+ // the same pagination container, then try to match fragments from
+ // |parent_painting_layer| to |layer|, so that any fragment clip for
+ // |parent_painting_layer|'s fragment matches |layer|'s. Note we check both
+ // ShouldFragmentCompositedBounds() and next fragment here because the former
+ // may return false even if |layer| is fragmented, e.g. for fixed-position
+ // objects in paged media, and the next fragment can be null even if the first
+ // fragment is actually in a fragmented context when the current layer appears
+ // in only one of the multiple fragments of the pagination container.
bool is_fragmented =
layer.ShouldFragmentCompositedBounds() || first_fragment.NextFragment();
bool should_match_fragments =
is_fragmented && parent_painting_layer.EnclosingPaginationLayer() ==
layer.EnclosingPaginationLayer();
bool force_update_children = false;
+ bool should_use_infinite_cull_rect =
+ !subtree_is_out_of_cull_rect_ &&
+ PaintLayerPainter(layer).ShouldUseInfiniteCullRect();
for (auto* fragment = &first_fragment; fragment;
fragment = fragment->NextFragment()) {
- const FragmentData* parent_fragment = nullptr;
- if (should_match_fragments) {
- for (parent_fragment = &first_parent_fragment; parent_fragment;
- parent_fragment = parent_fragment->NextFragment()) {
- if (parent_fragment->LogicalTopInFlowThread() ==
- fragment->LogicalTopInFlowThread())
- break;
- }
- } else {
- parent_fragment = &first_parent_fragment;
- }
-
CullRect cull_rect;
CullRect contents_cull_rect;
- if (!parent_fragment ||
- PaintLayerPainter(layer).ShouldUseInfiniteCullRect()) {
- cull_rect = CullRect::Infinite();
- contents_cull_rect = CullRect::Infinite();
+ if (subtree_is_out_of_cull_rect_) {
+ // PaintLayerPainter may skip the subtree including this layer, so we
+ // need to SetPreviousPaintResult() here.
+ layer.SetPreviousPaintResult(kMayBeClippedByCullRect);
} else {
- cull_rect = ComputeFragmentCullRect(layer, *fragment, *parent_fragment);
- contents_cull_rect =
- ComputeFragmentContentsCullRect(layer, *fragment, cull_rect);
+ const FragmentData* parent_fragment = nullptr;
+ if (!should_use_infinite_cull_rect) {
+ if (should_match_fragments) {
+ for (parent_fragment = &first_parent_fragment; parent_fragment;
+ parent_fragment = parent_fragment->NextFragment()) {
+ if (parent_fragment->FragmentID() == fragment->FragmentID())
+ break;
+ }
+ } else {
+ parent_fragment = &first_parent_fragment;
+ }
+ }
+
+ if (should_use_infinite_cull_rect || !parent_fragment) {
+ cull_rect = CullRect::Infinite();
+ contents_cull_rect = CullRect::Infinite();
+ } else {
+ cull_rect = ComputeFragmentCullRect(layer, *fragment, *parent_fragment);
+ contents_cull_rect =
+ ComputeFragmentContentsCullRect(layer, *fragment, cull_rect);
+ }
}
SetFragmentCullRect(layer, *fragment, cull_rect);
@@ -276,32 +302,29 @@ bool CullRectUpdater::ShouldProactivelyUpdate(const PaintLayer& layer) const {
return layer.SelfOrDescendantNeedsRepaint();
}
-OverriddenCullRectScope::OverriddenCullRectScope(LocalFrameView& frame_view,
+OverriddenCullRectScope::OverriddenCullRectScope(PaintLayer& starting_layer,
const CullRect& cull_rect)
- : frame_view_(frame_view) {
+ : starting_layer_(starting_layer) {
if (!RuntimeEnabledFeatures::CullRectUpdateEnabled())
return;
- PaintLayer* root_layer = frame_view_.GetLayoutView()->Layer();
- DCHECK(root_layer);
-
- if (frame_view.GetFrame().IsLocalRoot() &&
- !root_layer->NeedsCullRectUpdate() &&
- !root_layer->DescendantNeedsCullRectUpdate() &&
+ if (starting_layer.GetLayoutObject().GetFrame()->IsLocalRoot() &&
+ !starting_layer.NeedsCullRectUpdate() &&
+ !starting_layer.DescendantNeedsCullRectUpdate() &&
cull_rect ==
- root_layer->GetLayoutObject().FirstFragment().GetCullRect()) {
- // The cull rects calculated during PrePaint are good.
+ starting_layer.GetLayoutObject().FirstFragment().GetCullRect()) {
+ // The current cull rects are good.
return;
}
updated_ = true;
- root_layer->SetNeedsCullRectUpdate();
- CullRectUpdater(*root_layer).UpdateInternal(cull_rect);
+ starting_layer.SetNeedsCullRectUpdate();
+ CullRectUpdater(starting_layer).UpdateInternal(cull_rect);
}
OverriddenCullRectScope::~OverriddenCullRectScope() {
if (RuntimeEnabledFeatures::CullRectUpdateEnabled() && updated_)
- frame_view_.GetLayoutView()->Layer()->SetNeedsCullRectUpdate();
+ starting_layer_.SetNeedsCullRectUpdate();
}
} // namespace blink
diff --git a/chromium/third_party/blink/renderer/core/paint/cull_rect_updater.h b/chromium/third_party/blink/renderer/core/paint/cull_rect_updater.h
index 0e5d933f72a..87899064187 100644
--- a/chromium/third_party/blink/renderer/core/paint/cull_rect_updater.h
+++ b/chromium/third_party/blink/renderer/core/paint/cull_rect_updater.h
@@ -14,7 +14,6 @@ namespace blink {
class FragmentData;
class PaintLayer;
-class LocalFrameView;
// This class is used for updating the cull rects of PaintLayer fragments (see:
// |FragmentData::cull_rect_| and |FragmentData::contents_cull_rect_|.
@@ -28,9 +27,10 @@ class CORE_EXPORT CullRectUpdater {
STACK_ALLOCATED();
public:
- explicit CullRectUpdater(PaintLayer& root_layer);
+ explicit CullRectUpdater(PaintLayer& starting_layer)
+ : starting_layer_(starting_layer) {}
- void Update() { UpdateInternal(CullRect::Infinite()); }
+ void Update();
private:
friend class OverriddenCullRectScope;
@@ -51,24 +51,27 @@ class CORE_EXPORT CullRectUpdater {
const CullRect& cull_rect);
bool ShouldProactivelyUpdate(const PaintLayer&) const;
- PaintLayer& root_layer_;
- PropertyTreeState root_state_;
+ PaintLayer& starting_layer_;
+ PropertyTreeState root_state_ = PropertyTreeState::Uninitialized();
bool force_proactive_update_ = false;
+ bool subtree_is_out_of_cull_rect_ = false;
};
// Used when painting with a custom top-level cull rect, e.g. when printing a
-// page. It temporarily overrides the cull rect on the PaintLayer of the
-// LocalFrameView and marks the PaintLayer as needing to recalculate the cull
-// rect when leaving this scope.
+// page. It temporarily overrides the cull rect on the PaintLayer (which must be
+// a stacking context) and marks the PaintLayer as needing to recalculate the
+// cull rect when leaving this scope.
+// TODO(crbug.com/1215251): Avoid repaint after the scope if the scope is used
+// to paint into a separate PaintController.
class OverriddenCullRectScope {
STACK_ALLOCATED();
public:
- OverriddenCullRectScope(LocalFrameView&, const CullRect&);
+ OverriddenCullRectScope(PaintLayer&, const CullRect&);
~OverriddenCullRectScope();
private:
- LocalFrameView& frame_view_;
+ PaintLayer& starting_layer_;
bool updated_ = false;
};
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 7c2ed4a4276..f7ff03b59c0 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
@@ -4,7 +4,7 @@
#include "third_party/blink/renderer/core/paint/document_marker_painter.h"
-#include "base/stl_util.h"
+#include "base/cxx17_backports.h"
#include "build/build_config.h"
#include "third_party/blink/renderer/core/editing/markers/document_marker_controller.h"
#include "third_party/blink/renderer/core/layout/geometry/physical_rect.h"
@@ -56,7 +56,8 @@ sk_sp<PaintRecord> RecordMarker(Color blink_color) {
PaintRecorder recorder;
recorder.beginRecording(kMarkerWidth, kMarkerHeight);
- recorder.getRecordingCanvas()->drawPath(path.detach(), flags);
+ recorder.getRecordingCanvas()->cc::PaintCanvas::drawPath(path.detach(),
+ flags);
return recorder.finishRecordingAsPicture();
}
@@ -285,8 +286,7 @@ TextPaintStyle DocumentMarkerPainter::ComputeTextPaintStyleFrom(
if (marker.GetType() != DocumentMarker::kTextFragment) {
const Color platform_text_color =
LayoutTheme::GetTheme().PlatformTextSearchColor(
- marker.IsActiveMatch(), document.InForcedColorsMode(),
- style.UsedColorScheme());
+ marker.IsActiveMatch(), style.UsedColorScheme());
if (platform_text_color == text_color)
return {};
text_color = platform_text_color;
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 bebbbd985ff..c88a78dd3f3 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
@@ -25,10 +25,10 @@ static const float kReplacementTextRoundedRectOpacity = 0.20f;
static const float kReplacementTextRoundedRectRadius = 5;
static const float kReplacementTextTextOpacity = 0.55f;
-static Font ReplacementTextFont() {
+static Font ReplacementTextFont(const Document* document) {
FontDescription font_description;
LayoutTheme::GetTheme().SystemFont(CSSValueID::kWebkitSmallControl,
- font_description);
+ font_description, document);
font_description.SetWeight(BoldWeightValue());
font_description.SetComputedSize(font_description.SpecifiedSize());
Font font(font_description);
@@ -56,7 +56,7 @@ void EmbeddedObjectPainter::PaintReplaced(const PaintInfo& paint_info,
BoxDrawingRecorder recorder(context, layout_embedded_object_,
paint_info.phase, paint_offset);
- Font font = ReplacementTextFont();
+ Font font = ReplacementTextFont(&layout_embedded_object_.GetDocument());
const SimpleFontData* font_data = font.PrimaryFont();
DCHECK(font_data);
if (!font_data)
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 858cddfe305..0298359969a 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
@@ -312,6 +312,16 @@ FilterEffect* FilterEffectBuilder::BuildFilterEffect(
convolve_matrix_operation->KernelMatrix());
break;
}
+ case FilterOperation::COMPONENT_TRANSFER: {
+ ComponentTransferFilterOperation* component_transfer_operation =
+ To<ComponentTransferFilterOperation>(filter_operation);
+ effect = MakeGarbageCollected<FEComponentTransfer>(
+ parent_filter, component_transfer_operation->RedFunc(),
+ component_transfer_operation->GreenFunc(),
+ component_transfer_operation->BlueFunc(),
+ component_transfer_operation->AlphaFunc());
+ break;
+ }
default:
break;
}
@@ -385,6 +395,7 @@ CompositorFilterOperations FilterEffectBuilder::BuildFilterOperations(
}
case FilterOperation::LUMINANCE_TO_ALPHA:
case FilterOperation::CONVOLVE_MATRIX:
+ case FilterOperation::COMPONENT_TRANSFER:
// These filter types only exist for Canvas filters.
NOTREACHED();
break;
diff --git a/chromium/third_party/blink/renderer/core/paint/find_paint_offset_needing_update.h b/chromium/third_party/blink/renderer/core/paint/find_paint_offset_needing_update.h
index 2b21d3fb3a2..e902fca391b 100644
--- a/chromium/third_party/blink/renderer/core/paint/find_paint_offset_needing_update.h
+++ b/chromium/third_party/blink/renderer/core/paint/find_paint_offset_needing_update.h
@@ -9,6 +9,7 @@
#if DCHECK_IS_ON()
+#include "third_party/abseil-cpp/absl/types/optional.h"
#include "third_party/blink/renderer/core/layout/layout_object.h"
#include "third_party/blink/renderer/core/paint/fragment_data.h"
#include "third_party/blink/renderer/platform/wtf/allocator/allocator.h"
diff --git a/chromium/third_party/blink/renderer/core/paint/find_properties_needing_update.h b/chromium/third_party/blink/renderer/core/paint/find_properties_needing_update.h
index 1737801c869..1f765b7abec 100644
--- a/chromium/third_party/blink/renderer/core/paint/find_properties_needing_update.h
+++ b/chromium/third_party/blink/renderer/core/paint/find_properties_needing_update.h
@@ -9,7 +9,6 @@
#if DCHECK_IS_ON()
-#include "third_party/abseil-cpp/absl/types/optional.h"
#include "third_party/blink/renderer/core/layout/layout_object.h"
#include "third_party/blink/renderer/core/paint/object_paint_properties.h"
#include "third_party/blink/renderer/core/paint/paint_property_tree_builder.h"
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 30e45501136..27c75f8989b 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
@@ -178,7 +178,6 @@ void FirstMeaningfulPaintDetector::ReportPresentationTime(
//
// TODO(crbug.com/738235): Consider not reporting any timestamp when failing
// for reasons other than kDidNotSwapSwapFails.
- paint_timing_->ReportSwapResultHistogram(result);
provisional_first_meaningful_paint_presentation_ = timestamp;
probe::PaintTiming(GetDocument(), "firstMeaningfulPaintCandidate",
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 e54f4b38b7b..641bc33c1f7 100644
--- a/chromium/third_party/blink/renderer/core/paint/fragment_data.h
+++ b/chromium/third_party/blink/renderer/core/paint/fragment_data.h
@@ -52,12 +52,32 @@ class CORE_EXPORT FragmentData {
}
void SetLayer(std::unique_ptr<PaintLayer>);
+ // A fragment ID unique within the LayoutObject. In NG block fragmentation,
+ // this is the fragmentainer index. In legacy block fragmentation, it's the
+ // flow thread block-offset.
+ wtf_size_t FragmentID() const {
+ return rare_data_ ? rare_data_->fragment_id : 0;
+ }
+ void SetFragmentID(wtf_size_t id) {
+ if (!rare_data_ && id == 0)
+ return;
+ EnsureRareData().fragment_id = id;
+ }
+
LayoutUnit LogicalTopInFlowThread() const {
- return rare_data_ ? rare_data_->logical_top_in_flow_thread : LayoutUnit();
+#if DCHECK_IS_ON()
+ DCHECK(!rare_data_ || rare_data_->has_set_flow_thread_offset_ ||
+ !rare_data_->fragment_id);
+#endif
+ return LayoutUnit::FromRawValue(static_cast<int>(FragmentID()));
}
+
void SetLogicalTopInFlowThread(LayoutUnit top) {
- if (rare_data_ || top)
- EnsureRareData().logical_top_in_flow_thread = top;
+ SetFragmentID(top.RawValue());
+#if DCHECK_IS_ON()
+ if (rare_data_)
+ rare_data_->has_set_flow_thread_offset_ = true;
+#endif
}
// The pagination offset is the additional factor to add in to map from flow
@@ -225,7 +245,7 @@ class CORE_EXPORT FragmentData {
// Fragment specific data.
PhysicalOffset legacy_pagination_offset;
- LayoutUnit logical_top_in_flow_thread;
+ wtf_size_t fragment_id = 0;
std::unique_ptr<ObjectPaintProperties> paint_properties;
std::unique_ptr<RefCountedPropertyTreeState> local_border_box_properties;
bool is_clip_path_cache_valid = false;
@@ -234,6 +254,15 @@ class CORE_EXPORT FragmentData {
CullRect cull_rect_;
CullRect contents_cull_rect_;
std::unique_ptr<FragmentData> next_fragment_;
+
+#if DCHECK_IS_ON()
+ // Legacy block fragmentation sets the flow thread offset for each
+ // FragmentData object, and this is used as its fragment_id, whereas NG
+ // block fragmentation uses the fragmentainer index instead. Here's a flag
+ // which can be used to assert that legacy code which expects flow thread
+ // offsets actually gets that.
+ bool has_set_flow_thread_offset_ = false;
+#endif
};
RareData& EnsureRareData();
diff --git a/chromium/third_party/blink/renderer/core/paint/highlight_painting_utils.cc b/chromium/third_party/blink/renderer/core/paint/highlight_painting_utils.cc
index 6c6dbb81d23..f2c1b45dc7b 100644
--- a/chromium/third_party/blink/renderer/core/paint/highlight_painting_utils.cc
+++ b/chromium/third_party/blink/renderer/core/paint/highlight_painting_utils.cc
@@ -48,6 +48,9 @@ Color ForcedSystemForegroundColor(PseudoId pseudo_id,
case kPseudoIdSelection:
keyword = CSSValueID::kHighlighttext;
break;
+ case kPseudoIdHighlight:
+ keyword = CSSValueID::kHighlighttext;
+ break;
default:
NOTREACHED();
break;
@@ -66,6 +69,9 @@ Color ForcedSystemBackgroundColor(PseudoId pseudo_id,
case kPseudoIdSelection:
keyword = CSSValueID::kHighlight;
break;
+ case kPseudoIdHighlight:
+ keyword = CSSValueID::kHighlight;
+ break;
default:
NOTREACHED();
break;
@@ -89,8 +95,12 @@ Color HighlightThemeForegroundColor(const Document& document,
style.UsedColorScheme());
case kPseudoIdTargetText:
return LayoutTheme::GetTheme().PlatformTextSearchColor(
- false /* active match */, document.InForcedColorsMode(),
- style.UsedColorScheme());
+ false /* active match */, style.UsedColorScheme());
+ case kPseudoIdHighlight:
+ // TODO(ffiori): not assigning any visual effects to custom highlights by
+ // default as the spec doesn't define it. See
+ // https://github.com/w3c/csswg-drafts/issues/6375.
+ return style.VisitedDependentColor(color_property);
default:
NOTREACHED();
return Color();
@@ -112,16 +122,22 @@ Color HighlightThemeBackgroundColor(const Document& document,
return Color(shared_highlighting::kFragmentTextBackgroundColorARGB);
return LayoutTheme::GetTheme().PlatformTextSearchHighlightColor(
- false /* active match */, document.InForcedColorsMode(),
- style.UsedColorScheme());
+ false /* active match */, style.UsedColorScheme());
+ case kPseudoIdHighlight:
+ // TODO(ffiori): not assigning any visual effects to custom highlights by
+ // default as the spec doesn't define it. See
+ // https://github.com/w3c/csswg-drafts/issues/6375.
+ return style.VisitedDependentColor(GetCSSPropertyBackgroundColor());
default:
NOTREACHED();
return Color();
}
}
-scoped_refptr<const ComputedStyle> HighlightPseudoStyle(Node* node,
- PseudoId pseudo) {
+scoped_refptr<const ComputedStyle> HighlightPseudoStyle(
+ Node* node,
+ PseudoId pseudo,
+ const AtomicString& pseudo_argument) {
if (!node)
return nullptr;
@@ -155,10 +171,10 @@ scoped_refptr<const ComputedStyle> HighlightPseudoStyle(Node* node,
// cache the styles for ::selection if there are no :window-inactive
// selector, or if the page is active.
return element->UncachedStyleForPseudoElement(
- StyleRequest(pseudo, element->GetComputedStyle()));
+ StyleRequest(pseudo, element->GetComputedStyle(), pseudo_argument));
}
- return element->CachedStyleForPseudoElement(pseudo);
+ return element->CachedStyleForPseudoElement(pseudo, pseudo_argument);
}
Color HighlightColor(const Document& document,
@@ -166,7 +182,8 @@ Color HighlightColor(const Document& document,
Node* node,
PseudoId pseudo,
const CSSProperty& color_property,
- const GlobalPaintFlags global_paint_flags) {
+ const GlobalPaintFlags global_paint_flags,
+ const AtomicString& pseudo_argument = g_null_atom) {
if (pseudo == kPseudoIdSelection) {
// If the element is unselectable, or we are only painting the selection,
// don't override the foreground color with the selection foreground color.
@@ -177,7 +194,7 @@ Color HighlightColor(const Document& document,
}
scoped_refptr<const ComputedStyle> pseudo_style =
- HighlightPseudoStyle(node, pseudo);
+ HighlightPseudoStyle(node, pseudo, pseudo_argument);
mojom::blink::ColorScheme color_scheme = style.UsedColorScheme();
if (pseudo_style) {
@@ -199,7 +216,8 @@ Color HighlightPaintingUtils::HighlightBackgroundColor(
const Document& document,
const ComputedStyle& style,
Node* node,
- PseudoId pseudo) {
+ PseudoId pseudo,
+ const AtomicString& pseudo_argument) {
if (pseudo == kPseudoIdSelection) {
if (node && !NodeIsSelectable(style, node))
return Color::kTransparent;
@@ -207,7 +225,7 @@ Color HighlightPaintingUtils::HighlightBackgroundColor(
mojom::blink::ColorScheme color_scheme = style.UsedColorScheme();
if (scoped_refptr<const ComputedStyle> pseudo_style =
- HighlightPseudoStyle(node, pseudo)) {
+ HighlightPseudoStyle(node, pseudo, pseudo_argument)) {
if (!document.InForcedColorsMode() ||
pseudo_style->ForcedColorAdjust() == EForcedColorAdjust::kNone) {
Color highlight_color =
@@ -259,10 +277,11 @@ Color HighlightPaintingUtils::HighlightForegroundColor(
const ComputedStyle& style,
Node* node,
PseudoId pseudo,
- const GlobalPaintFlags global_paint_flags) {
+ const GlobalPaintFlags global_paint_flags,
+ const AtomicString& pseudo_argument) {
return HighlightColor(document, style, node, pseudo,
- GetCSSPropertyWebkitTextFillColor(),
- global_paint_flags);
+ GetCSSPropertyWebkitTextFillColor(), global_paint_flags,
+ pseudo_argument);
}
Color HighlightPaintingUtils::HighlightEmphasisMarkColor(
@@ -282,7 +301,8 @@ TextPaintStyle HighlightPaintingUtils::HighlightPaintingStyle(
Node* node,
PseudoId pseudo,
const TextPaintStyle& text_style,
- const PaintInfo& paint_info) {
+ const PaintInfo& paint_info,
+ const AtomicString& pseudo_argument) {
TextPaintStyle highlight_style = text_style;
bool uses_text_as_clip = paint_info.phase == PaintPhase::kTextClip;
const GlobalPaintFlags global_paint_flags = paint_info.GetGlobalPaintFlags();
@@ -293,13 +313,13 @@ TextPaintStyle HighlightPaintingUtils::HighlightPaintingStyle(
if (!uses_text_as_clip) {
highlight_style.fill_color = HighlightForegroundColor(
- document, style, node, pseudo, global_paint_flags);
+ document, style, node, pseudo, global_paint_flags, pseudo_argument);
highlight_style.emphasis_mark_color = HighlightEmphasisMarkColor(
document, style, node, pseudo, global_paint_flags);
}
if (scoped_refptr<const ComputedStyle> pseudo_style =
- HighlightPseudoStyle(node, pseudo)) {
+ HighlightPseudoStyle(node, pseudo, pseudo_argument)) {
highlight_style.stroke_color =
uses_text_as_clip ? Color::kBlack
: pseudo_style->VisitedDependentColor(
diff --git a/chromium/third_party/blink/renderer/core/paint/highlight_painting_utils.h b/chromium/third_party/blink/renderer/core/paint/highlight_painting_utils.h
index c29608ddc58..f1dbe602376 100644
--- a/chromium/third_party/blink/renderer/core/paint/highlight_painting_utils.h
+++ b/chromium/third_party/blink/renderer/core/paint/highlight_painting_utils.h
@@ -11,6 +11,7 @@
#include "third_party/blink/renderer/core/paint/paint_phase.h"
#include "third_party/blink/renderer/core/style/applied_text_decoration.h"
#include "third_party/blink/renderer/platform/wtf/allocator/allocator.h"
+#include "third_party/blink/renderer/platform/wtf/text/atomic_string.h"
namespace blink {
@@ -28,26 +29,32 @@ class CORE_EXPORT HighlightPaintingUtils {
static absl::optional<AppliedTextDecoration> HighlightTextDecoration(
const ComputedStyle& style,
const ComputedStyle& pseudo_style);
- static Color HighlightBackgroundColor(const Document&,
- const ComputedStyle&,
- Node*,
- PseudoId);
- static Color HighlightForegroundColor(const Document&,
- const ComputedStyle&,
- Node*,
- PseudoId,
- const GlobalPaintFlags);
+ static Color HighlightBackgroundColor(
+ const Document&,
+ const ComputedStyle&,
+ Node*,
+ PseudoId,
+ const AtomicString& pseudo_argument = g_null_atom);
+ static Color HighlightForegroundColor(
+ const Document&,
+ const ComputedStyle&,
+ Node*,
+ PseudoId,
+ const GlobalPaintFlags,
+ const AtomicString& pseudo_argument = g_null_atom);
static Color HighlightEmphasisMarkColor(const Document&,
const ComputedStyle&,
Node*,
PseudoId,
const GlobalPaintFlags);
- static TextPaintStyle HighlightPaintingStyle(const Document&,
- const ComputedStyle&,
- Node*,
- PseudoId,
- const TextPaintStyle& text_style,
- const PaintInfo&);
+ static TextPaintStyle HighlightPaintingStyle(
+ const Document&,
+ const ComputedStyle&,
+ Node*,
+ PseudoId,
+ const TextPaintStyle& text_style,
+ const PaintInfo&,
+ const AtomicString& pseudo_argument = g_null_atom);
};
} // namespace blink
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 ba995d59323..9b3ff951616 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,10 +3,13 @@
// found in the LICENSE file.
#include "third_party/blink/renderer/core/paint/image_paint_timing_detector.h"
+#include "base/feature_list.h"
#include "services/metrics/public/cpp/ukm_builders.h"
#include "services/metrics/public/cpp/ukm_recorder.h"
+#include "third_party/blink/public/common/features.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/visual_viewport.h"
#include "third_party/blink/renderer/core/layout/layout_image_resource.h"
#include "third_party/blink/renderer/core/layout/svg/layout_svg_image.h"
#include "third_party/blink/renderer/core/page/chrome_client.h"
@@ -66,7 +69,9 @@ static bool LargeImageFirst(const base::WeakPtr<ImageRecord>& a,
ImagePaintTimingDetector::ImagePaintTimingDetector(
LocalFrameView* frame_view,
PaintTimingCallbackManager* callback_manager)
- : records_manager_(frame_view),
+ : uses_page_viewport_(
+ base::FeatureList::IsEnabled(features::kUsePageViewportInLCP)),
+ records_manager_(frame_view),
frame_view_(frame_view),
callback_manager_(callback_manager) {}
@@ -314,8 +319,16 @@ uint64_t ImagePaintTimingDetector::ComputeImageRectSize(
frame_view_->GetPaintTimingDetector().BlinkSpaceToDIPs(
FloatRect(image_border));
if (!viewport_size_.has_value()) {
+ // If the flag to use page viewport is enabled, we use the page viewport
+ // (aka the main frame viewport) for all frames, including iframes. This
+ // prevents us from discarding images with size equal to the size of its
+ // embedding iframe.
+ IntRect viewport_int_rect =
+ uses_page_viewport_
+ ? frame_view_->GetPage()->GetVisualViewport().VisibleContentRect()
+ : frame_view_->GetScrollableArea()->VisibleContentRect();
FloatRect viewport = frame_view_->GetPaintTimingDetector().BlinkSpaceToDIPs(
- FloatRect(frame_view_->GetScrollableArea()->VisibleContentRect()));
+ FloatRect(viewport_int_rect));
viewport_size_ = viewport.Size().Area();
}
// An SVG image size is computed with respect to the virtual viewport of the
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 802c124f9c2..dd7fa784da3 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
@@ -344,6 +344,8 @@ class CORE_EXPORT ImagePaintTimingDetector final
// image. This value is reset when paint is finished and is computed if unset
// when needed. 0 means that the size has not been computed.
absl::optional<uint64_t> viewport_size_;
+ // Whether the viewport size used is the page viewport.
+ bool uses_page_viewport_;
ImageRecordsManager records_manager_;
Member<LocalFrameView> frame_view_;
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 d7f5982f4e3..b5429832e59 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
@@ -385,10 +385,10 @@ TEST_P(ImagePaintTimingDetectorTest, LargestImagePaint_TraceEvent_Candidate) {
EXPECT_TRUE(events[0]->HasArg("frame"));
EXPECT_TRUE(events[0]->HasArg("data"));
- std::unique_ptr<base::Value> arg;
+ base::Value arg;
EXPECT_TRUE(events[0]->GetArgAsValue("data", &arg));
base::DictionaryValue* arg_dict;
- EXPECT_TRUE(arg->GetAsDictionary(&arg_dict));
+ EXPECT_TRUE(arg.GetAsDictionary(&arg_dict));
DOMNodeId node_id;
EXPECT_TRUE(arg_dict->GetInteger("DOMNodeId", &node_id));
EXPECT_GT(node_id, 0);
@@ -455,10 +455,10 @@ TEST_P(ImagePaintTimingDetectorTest,
EXPECT_TRUE(events[0]->HasArg("frame"));
EXPECT_TRUE(events[0]->HasArg("data"));
- std::unique_ptr<base::Value> arg;
+ base::Value arg;
EXPECT_TRUE(events[0]->GetArgAsValue("data", &arg));
base::DictionaryValue* arg_dict;
- EXPECT_TRUE(arg->GetAsDictionary(&arg_dict));
+ EXPECT_TRUE(arg.GetAsDictionary(&arg_dict));
DOMNodeId node_id;
EXPECT_TRUE(arg_dict->GetInteger("DOMNodeId", &node_id));
EXPECT_GT(node_id, 0);
@@ -521,10 +521,10 @@ TEST_P(ImagePaintTimingDetectorTest, LargestImagePaint_TraceEvent_NoCandidate) {
EXPECT_EQ("loading", events[0]->category);
EXPECT_TRUE(events[0]->HasArg("frame"));
EXPECT_TRUE(events[0]->HasArg("data"));
- std::unique_ptr<base::Value> arg;
+ base::Value arg;
EXPECT_TRUE(events[0]->GetArgAsValue("data", &arg));
base::DictionaryValue* arg_dict;
- EXPECT_TRUE(arg->GetAsDictionary(&arg_dict));
+ EXPECT_TRUE(arg.GetAsDictionary(&arg_dict));
DOMNodeId candidate_index;
EXPECT_TRUE(arg_dict->GetInteger("candidateIndex", &candidate_index));
EXPECT_EQ(candidate_index, 1);
@@ -539,10 +539,10 @@ TEST_P(ImagePaintTimingDetectorTest, LargestImagePaint_TraceEvent_NoCandidate) {
// Use block to reuse the temp variable names.
{
EXPECT_TRUE(events[1]->HasArg("data"));
- std::unique_ptr<base::Value> arg;
+ base::Value arg;
EXPECT_TRUE(events[1]->GetArgAsValue("data", &arg));
base::DictionaryValue* arg_dict;
- EXPECT_TRUE(arg->GetAsDictionary(&arg_dict));
+ EXPECT_TRUE(arg.GetAsDictionary(&arg_dict));
DOMNodeId candidate_index;
EXPECT_TRUE(arg_dict->GetInteger("candidateIndex", &candidate_index));
EXPECT_EQ(candidate_index, 3);
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 367f07a6919..674a6801271 100644
--- a/chromium/third_party/blink/renderer/core/paint/image_painter.cc
+++ b/chromium/third_party/blink/renderer/core/paint/image_painter.cc
@@ -13,6 +13,7 @@
#include "third_party/blink/renderer/core/frame/local_frame.h"
#include "third_party/blink/renderer/core/html/html_area_element.h"
#include "third_party/blink/renderer/core/html/html_image_element.h"
+#include "third_party/blink/renderer/core/inspector/inspector_trace_events.h"
#include "third_party/blink/renderer/core/layout/adjust_for_absolute_zoom.h"
#include "third_party/blink/renderer/core/layout/layout_image.h"
#include "third_party/blink/renderer/core/layout/layout_replaced.h"
@@ -21,6 +22,7 @@
#include "third_party/blink/renderer/core/page/page.h"
#include "third_party/blink/renderer/core/paint/box_painter.h"
#include "third_party/blink/renderer/core/paint/image_element_timing.h"
+#include "third_party/blink/renderer/core/paint/outline_painter.h"
#include "third_party/blink/renderer/core/paint/paint_info.h"
#include "third_party/blink/renderer/core/paint/paint_timing_detector.h"
#include "third_party/blink/renderer/core/paint/scoped_paint_state.h"
@@ -127,11 +129,8 @@ void ImagePainter::PaintAreaElementFocusRing(const PaintInfo& paint_info) {
PhysicalRect focus_rect = layout_image_.PhysicalContentBoxRect();
focus_rect.Move(paint_offset);
paint_info.context.Clip(PixelSnappedIntRect(focus_rect));
- paint_info.context.DrawFocusRing(
- path, area_element_style->GetOutlineStrokeWidthForFocusRing(),
- area_element_style->OutlineOffsetInt(),
- layout_image_.ResolveColor(*area_element_style,
- GetCSSPropertyOutlineColor()));
+ OutlinePainter::PaintFocusRingPath(paint_info.context, path,
+ *area_element_style);
paint_info.context.Restore();
}
@@ -254,7 +253,7 @@ void ImagePainter::PaintIntoRect(GraphicsContext& context,
// Does not set an observer for the placeholder image, setting it to null.
scoped_refptr<PlaceholderImage> placeholder_image =
PlaceholderImage::Create(nullptr, image->Size(),
- image->Data() ? image->Data()->size() : 0);
+ image->HasData() ? image->DataSize() : 0);
placeholder_image->SetIconAndTextScaleFactor(
layout_image_.GetFrame()->PageZoomFactor());
image = std::move(placeholder_image);
@@ -263,7 +262,7 @@ void ImagePainter::PaintIntoRect(GraphicsContext& context,
context.DrawImage(image.get(), decode_mode,
FloatRect(pixel_snapped_dest_rect), &src_rect,
- layout_image_.StyleRef().HasFilterInducingProperty(),
+ layout_image_.StyleRef().DisableForceDark(),
SkBlendMode::kSrcOver, respect_orientation);
if (ImageResourceContent* image_content = image_resource.CachedImage()) {
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 4eac70704b3..c86a356c39c 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
@@ -31,6 +31,7 @@
#include "third_party/blink/renderer/platform/graphics/paint/paint_record.h"
#include "third_party/blink/renderer/platform/graphics/paint/paint_recorder.h"
#include "third_party/blink/renderer/platform/graphics/paint/paint_shader.h"
+#include "third_party/blink/renderer/platform/instrumentation/use_counter.h"
#include "third_party/skia/include/effects/SkGradientShader.h"
namespace blink {
@@ -172,7 +173,12 @@ void InlineTextBoxPainter::Paint(const PaintInfo& paint_info,
inline_text_box_.LogicalHeight()));
absl::optional<SelectionBoundsRecorder> selection_recorder;
- if (have_selection && paint_info.phase == PaintPhase::kForeground &&
+ // Empty selections might be the boundary of the document selection, and thus
+ // need to get recorded.
+ const bool should_record_selection =
+ have_selection ||
+ inline_text_box_.GetLineLayoutItem().GetLayoutObject()->IsSelected();
+ if (should_record_selection && paint_info.phase == PaintPhase::kForeground &&
!is_printing) {
const FrameSelection& frame_selection =
InlineLayoutObject().GetFrame()->Selection();
@@ -183,7 +189,8 @@ void InlineTextBoxPainter::Paint(const PaintInfo& paint_info,
selection_state)) {
PhysicalRect selection_rect =
GetSelectionRect<InlineTextBoxPainter::PaintOptions::kNormal>(
- context, box_rect, style_to_use, style_to_use.GetFont());
+ context, box_rect, style_to_use, style_to_use.GetFont(), nullptr,
+ /* allow_empty_selection*/ true);
TextDirection direction = inline_text_box_.IsLeftToRightDirection()
? TextDirection::kLtr
@@ -413,10 +420,9 @@ void InlineTextBoxPainter::Paint(const PaintInfo& paint_info,
? absl::optional<AppliedTextDecoration>(
selection_style.selection_text_decoration)
: absl::nullopt;
- decoration_info.emplace(box_origin, local_origin, width,
- inline_text_box_.Root().BaselineType(),
- style_to_use, selection_text_decoration,
- decorating_box_style);
+ decoration_info.emplace(
+ local_origin, width, inline_text_box_.Root().BaselineType(),
+ style_to_use, selection_text_decoration, decorating_box_style);
TextDecorationOffset decoration_offset(decoration_info->Style(),
&inline_text_box_, decorating_box);
text_painter.PaintDecorationsExceptLineThrough(
@@ -661,6 +667,10 @@ void InlineTextBoxPainter::PaintDocumentMarkers(
styleable_marker, style, font);
}
} break;
+ case DocumentMarker::kHighlight:
+ inline_text_box_.PaintDocumentMarker(paint_info, box_origin, marker,
+ style, font, false);
+ break;
default:
// Marker is not painted, or painting code has not been added yet
break;
@@ -728,11 +738,14 @@ PhysicalRect InlineTextBoxPainter::GetSelectionRect(
const PhysicalRect& box_rect,
const ComputedStyle& style,
const Font& font,
- LayoutTextCombine* combined_text) {
+ LayoutTextCombine* combined_text,
+ bool allow_empty_selection) {
// See if we have a selection to paint at all.
int start_pos, end_pos;
inline_text_box_.SelectionStartEnd(start_pos, end_pos);
- if (start_pos >= end_pos)
+ if (start_pos > end_pos)
+ return PhysicalRect();
+ if (!allow_empty_selection && start_pos == end_pos)
return PhysicalRect();
// If the text is truncated, let the thing being painted in the truncation
@@ -833,8 +846,11 @@ PhysicalRect InlineTextBoxPainter::PaintSelection(
// If the text color ends up being the same as the selection background,
// invert the selection background.
- if (text_color == c)
+ if (text_color == c) {
+ UseCounter::Count(layout_item.GetDocument(),
+ WebFeature::kSelectionBackgroundColorInversion);
c = Color(0xff - c.Red(), 0xff - c.Green(), 0xff - c.Blue());
+ }
GraphicsContextStateSaver state_saver(context);
@@ -935,9 +951,7 @@ void InlineTextBoxPainter::PaintTextMarkerBackground(
TextRun run = inline_text_box_.ConstructTextRun(style);
Color color = LayoutTheme::GetTheme().PlatformTextSearchHighlightColor(
- marker.IsActiveMatch(),
- inline_text_box_.GetLineLayoutItem().GetDocument().InForcedColorsMode(),
- style.UsedColorScheme());
+ marker.IsActiveMatch(), style.UsedColorScheme());
GraphicsContext& context = paint_info.context;
GraphicsContextStateSaver state_saver(context);
diff --git a/chromium/third_party/blink/renderer/core/paint/inline_text_box_painter.h b/chromium/third_party/blink/renderer/core/paint/inline_text_box_painter.h
index 42c30224e34..a9bb0b494bb 100644
--- a/chromium/third_party/blink/renderer/core/paint/inline_text_box_painter.h
+++ b/chromium/third_party/blink/renderer/core/paint/inline_text_box_painter.h
@@ -91,7 +91,8 @@ class InlineTextBoxPainter {
const PhysicalRect& box_rect,
const ComputedStyle&,
const Font&,
- LayoutTextCombine* = nullptr);
+ LayoutTextCombine* = nullptr,
+ bool allow_empty_selection = false);
void PaintStyleableMarkerUnderline(GraphicsContext&,
const PhysicalOffset& box_origin,
diff --git a/chromium/third_party/blink/renderer/core/paint/inline_text_box_painter_test.cc b/chromium/third_party/blink/renderer/core/paint/inline_text_box_painter_test.cc
index 1b505e57d1e..52c697610dd 100644
--- a/chromium/third_party/blink/renderer/core/paint/inline_text_box_painter_test.cc
+++ b/chromium/third_party/blink/renderer/core/paint/inline_text_box_painter_test.cc
@@ -7,6 +7,7 @@
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/blink/renderer/core/editing/testing/selection_sample.h"
+#include "third_party/blink/renderer/core/page/focus_controller.h"
#include "third_party/blink/renderer/core/paint/paint_controller_paint_test.h"
using testing::ElementsAre;
@@ -28,64 +29,4 @@ TEST_P(InlineTextBoxPainterTest, LineBreak) {
EXPECT_EQ(6u, ContentDisplayItems().size());
}
-class InlineTextBoxPainterNonNGTest : public PaintControllerPaintTest,
- public ScopedLayoutNGForTest {
- public:
- InlineTextBoxPainterNonNGTest() : ScopedLayoutNGForTest(false) {}
-};
-
-INSTANTIATE_PAINT_TEST_SUITE_P(InlineTextBoxPainterNonNGTest);
-
-TEST_P(InlineTextBoxPainterNonNGTest, RecordedSelectionAll) {
- if (!RuntimeEnabledFeatures::CompositeAfterPaintEnabled())
- return;
- SetBodyInnerHTML("<span>A<br>B<br>C</span>");
-
- GetDocument().GetFrame()->Selection().SetHandleVisibleForTesting();
- GetDocument().GetFrame()->Selection().SelectAll();
- UpdateAllLifecyclePhasesForTest();
-
- auto chunks = ContentPaintChunks();
- EXPECT_EQ(chunks.size(), 1u);
- EXPECT_TRUE(chunks.begin()->layer_selection_data->start.has_value());
- EXPECT_TRUE(chunks.begin()->layer_selection_data->end.has_value());
- PaintedSelectionBound start =
- chunks.begin()->layer_selection_data->start.value();
- EXPECT_EQ(start.type, gfx::SelectionBound::LEFT);
- EXPECT_EQ(start.edge_start, IntPoint(8, 8));
- EXPECT_EQ(start.edge_end, IntPoint(8, 9));
-
- PaintedSelectionBound end = chunks.begin()->layer_selection_data->end.value();
- EXPECT_EQ(end.type, gfx::SelectionBound::RIGHT);
- EXPECT_EQ(end.edge_start, IntPoint(9, 10));
- EXPECT_EQ(end.edge_end, IntPoint(9, 11));
-}
-
-TEST_P(InlineTextBoxPainterNonNGTest, RecordedSelectionMultiline) {
- if (!RuntimeEnabledFeatures::CompositeAfterPaintEnabled())
- return;
-
- GetDocument().GetFrame()->Selection().SetSelectionAndEndTyping(
- SelectionSample::SetSelectionText(
- GetDocument().body(),
- "<div style='white-space:pre'>f^oo\nbar\nb|az</div>"));
- GetDocument().GetFrame()->Selection().SetHandleVisibleForTesting();
- UpdateAllLifecyclePhasesForTest();
-
- auto chunks = ContentPaintChunks();
- EXPECT_EQ(chunks.size(), 1u);
- EXPECT_TRUE(chunks.begin()->layer_selection_data->start.has_value());
- EXPECT_TRUE(chunks.begin()->layer_selection_data->end.has_value());
- PaintedSelectionBound start =
- chunks.begin()->layer_selection_data->start.value();
- EXPECT_EQ(start.type, gfx::SelectionBound::LEFT);
- EXPECT_EQ(start.edge_start, IntPoint(8, 8));
- EXPECT_EQ(start.edge_end, IntPoint(8, 9));
-
- PaintedSelectionBound end = chunks.begin()->layer_selection_data->end.value();
- EXPECT_EQ(end.type, gfx::SelectionBound::RIGHT);
- EXPECT_EQ(end.edge_start, IntPoint(9, 10));
- EXPECT_EQ(end.edge_end, IntPoint(9, 11));
-}
-
} // namespace blink
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 6841df62c20..355247b1ebe 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
@@ -4,6 +4,7 @@
#include "third_party/blink/renderer/core/paint/line_box_list_painter.h"
+#include "third_party/blink/renderer/core/css/style_engine.h"
#include "third_party/blink/renderer/core/layout/api/line_layout_box_model.h"
#include "third_party/blink/renderer/core/layout/geometry/physical_rect.h"
#include "third_party/blink/renderer/core/layout/layout_box_model_object.h"
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 7a381c48e5e..9b50fd32cfe 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
@@ -32,6 +32,7 @@
#include "base/memory/ptr_util.h"
#include "cc/layers/picture_layer.h"
#include "cc/paint/display_item_list.h"
+#include "skia/ext/skia_matrix_44.h"
#include "third_party/blink/public/platform/platform.h"
#include "third_party/blink/public/web/blink.h"
#include "third_party/blink/renderer/core/dom/layout_tree_builder_traversal.h"
@@ -62,7 +63,6 @@
#include "third_party/blink/renderer/platform/graphics/paint/transform_paint_property_node.h"
#include "third_party/blink/renderer/platform/web_test_support.h"
#include "third_party/blink/renderer/platform/wtf/vector.h"
-#include "third_party/skia/include/core/SkMatrix44.h"
#include "ui/gfx/geometry/rect.h"
#include "ui/gfx/geometry/rect_conversions.h"
#include "ui/gfx/geometry/size_f.h"
@@ -217,7 +217,9 @@ void LinkHighlightImpl::StartHighlightAnimationIfNeeded() {
timing_function));
auto keyframe_model = std::make_unique<CompositorKeyframeModel>(
- *curve, compositor_target_property::OPACITY, 0, 0);
+ *curve, 0, 0,
+ CompositorKeyframeModel::TargetPropertyId(
+ compositor_target_property::OPACITY));
compositor_animation_->AddKeyframeModel(std::move(keyframe_model));
}
@@ -244,7 +246,7 @@ void LinkHighlightImpl::UpdateAfterPrePaint() {
return;
DCHECK(!object->GetFrameView()->ShouldThrottleRendering());
- size_t fragment_count = 0;
+ wtf_size_t fragment_count = 0;
for (const auto* fragment = &object->FirstFragment(); fragment;
fragment = fragment->NextFragment())
++fragment_count;
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 3f9b2dae2a1..34cd91faed8 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
@@ -79,7 +79,7 @@ class CORE_EXPORT LinkHighlightImpl final : public CompositorAnimationDelegate,
void Paint(GraphicsContext&);
wtf_size_t FragmentCountForTesting() const { return fragments_.size(); }
- cc::PictureLayer* LayerForTesting(size_t index) const {
+ cc::PictureLayer* LayerForTesting(wtf_size_t index) const {
return fragments_[index].Layer();
}
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 0ead31d5967..f8695af86b6 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
@@ -8,7 +8,6 @@
#include "third_party/blink/renderer/core/layout/layout_list_item.h"
#include "third_party/blink/renderer/core/layout/layout_list_marker.h"
#include "third_party/blink/renderer/core/layout/list_marker.h"
-#include "third_party/blink/renderer/core/layout/list_marker_text.h"
#include "third_party/blink/renderer/core/paint/box_model_object_painter.h"
#include "third_party/blink/renderer/core/paint/box_painter.h"
#include "third_party/blink/renderer/core/paint/highlight_painting_utils.h"
@@ -86,8 +85,8 @@ void ListMarkerPainter::PaintSymbol(const PaintInfo& paint_info,
const ComputedStyle& style,
const LayoutRect& marker) {
DCHECK(object);
- DCHECK(style.GetListStyleType());
- DCHECK(style.GetListStyleType()->IsCounterStyle());
+ DCHECK(style.ListStyleType());
+ DCHECK(style.ListStyleType()->IsCounterStyle());
GraphicsContext& context = paint_info.context;
ScopedDarkModeElementRoleOverride list_symbol(
&context, DarkModeFilter::ElementRole::kListSymbol);
@@ -101,7 +100,7 @@ void ListMarkerPainter::PaintSymbol(const PaintInfo& paint_info,
context.SetStrokeStyle(kSolidStroke);
context.SetStrokeThickness(1.0f);
IntRect snapped_rect = PixelSnappedIntRect(marker);
- const AtomicString& type = style.GetListStyleType()->GetCounterStyleName();
+ const AtomicString& type = style.ListStyleType()->GetCounterStyleName();
if (type == "disc") {
context.FillEllipse(FloatRect(snapped_rect));
} else if (type == "circle") {
@@ -232,17 +231,9 @@ void ListMarkerPainter::Paint(const PaintInfo& paint_info) {
String prefix_str;
String suffix_str;
- if (RuntimeEnabledFeatures::CSSAtRuleCounterStyleEnabled()) {
- const CounterStyle& counter_style = layout_list_marker_.GetCounterStyle();
- prefix_str = counter_style.GetPrefix();
- suffix_str = counter_style.GetSuffix();
- } else {
- UChar chars[] = {
- list_marker_text::Suffix(layout_list_marker_.StyleRef().ListStyleType(),
- layout_list_marker_.ListItem()->Value()),
- ' '};
- suffix_str = String(chars, 2);
- }
+ const CounterStyle& counter_style = layout_list_marker_.GetCounterStyle();
+ prefix_str = counter_style.GetPrefix();
+ suffix_str = counter_style.GetSuffix();
TextRun prefix_run =
ConstructTextRun(font, prefix_str, layout_list_marker_.StyleRef(),
layout_list_marker_.StyleRef().Direction());
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 cfc9b9d21c7..043e55ae4c2 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
@@ -6,7 +6,7 @@
#include "third_party/blink/renderer/core/layout/layout_multi_column_set.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/box_border_painter.h"
#include "third_party/blink/renderer/core/paint/paint_info.h"
#include "third_party/blink/renderer/platform/geometry/layout_point.h"
#include "third_party/blink/renderer/platform/graphics/paint/drawing_recorder.h"
@@ -64,8 +64,8 @@ void MultiColumnSetPainter::PaintColumnRules(
for (auto& bound : column_rule_bounds) {
IntRect pixel_snapped_rule_rect = PixelSnappedIntRect(bound);
- ObjectPainter::DrawBoxSide(paint_info.context, pixel_snapped_rule_rect,
- box_side, rule_color, rule_style);
+ BoxBorderPainter::DrawBoxSide(paint_info.context, pixel_snapped_rule_rect,
+ box_side, rule_color, rule_style);
}
}
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 360f0a19e45..1642dea2ade 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
@@ -5,6 +5,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/css/style_engine.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"
@@ -14,6 +15,7 @@
#include "third_party/blink/renderer/core/layout/layout_inline.h"
#include "third_party/blink/renderer/core/layout/layout_table_cell.h"
#include "third_party/blink/renderer/core/layout/ng/geometry/ng_box_strut.h"
+#include "third_party/blink/renderer/core/layout/ng/inline/layout_ng_text_combine.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/layout/ng/inline/ng_physical_line_box_fragment.h"
@@ -21,8 +23,10 @@
#include "third_party/blink/renderer/core/layout/ng/ng_block_break_token.h"
#include "third_party/blink/renderer/core/layout/ng/ng_outline_utils.h"
#include "third_party/blink/renderer/core/layout/ng/ng_physical_box_fragment.h"
+#include "third_party/blink/renderer/core/layout/pointer_events_hit_rules.h"
#include "third_party/blink/renderer/core/page/page.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/box_decoration_data.h"
#include "third_party/blink/renderer/core/paint/box_painter.h"
#include "third_party/blink/renderer/core/paint/compositing/composited_layer_mapping.h"
@@ -31,6 +35,7 @@
#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_table_painters.h"
+#include "third_party/blink/renderer/core/paint/ng/ng_text_combine_painter.h"
#include "third_party/blink/renderer/core/paint/ng/ng_text_fragment_painter.h"
#include "third_party/blink/renderer/core/paint/object_painter.h"
#include "third_party/blink/renderer/core/paint/paint_info.h"
@@ -40,6 +45,7 @@
#include "third_party/blink/renderer/core/paint/paint_timing_detector.h"
#include "third_party/blink/renderer/core/paint/rounded_border_geometry.h"
#include "third_party/blink/renderer/core/paint/scoped_paint_state.h"
+#include "third_party/blink/renderer/core/paint/scoped_svg_paint_state.h"
#include "third_party/blink/renderer/core/paint/scrollable_area_painter.h"
#include "third_party/blink/renderer/core/paint/theme_painter.h"
#include "third_party/blink/renderer/core/paint/url_metadata_utils.h"
@@ -111,7 +117,21 @@ inline bool IsVisibleToHitTest(const ComputedStyle& style,
inline bool IsVisibleToHitTest(const NGFragmentItem& item,
const HitTestRequest& request) {
const ComputedStyle& style = item.Style();
- return IsVisibleToPaint(item, style) && IsVisibleToHitTest(style, request);
+ if (item.Type() != NGFragmentItem::kSvgText)
+ return IsVisibleToPaint(item, style) && IsVisibleToHitTest(style, request);
+
+ if (item.IsHiddenForPaint())
+ return false;
+ PointerEventsHitRules hit_rules(PointerEventsHitRules::SVG_TEXT_HITTESTING,
+ request, style.PointerEvents());
+ if (hit_rules.require_visible && style.Visibility() != EVisibility::kVisible)
+ return false;
+ if (hit_rules.can_hit_bounding_box ||
+ (hit_rules.can_hit_stroke &&
+ (style.HasStroke() || !hit_rules.require_stroke)) ||
+ (hit_rules.can_hit_fill && (style.HasFill() || !hit_rules.require_fill)))
+ return IsVisibleToHitTest(style, request);
+ return false;
}
inline bool IsVisibleToHitTest(const NGPhysicalFragment& fragment,
@@ -348,13 +368,13 @@ bool ShouldPaintCarets(const NGPhysicalBoxFragment& fragment) {
} // anonymous namespace
-PhysicalRect NGBoxFragmentPainter::SelfInkOverflow() const {
+PhysicalRect NGBoxFragmentPainter::InkOverflowIncludingFilters() const {
if (box_item_)
return box_item_->SelfInkOverflow();
const NGPhysicalFragment& fragment = PhysicalFragment();
DCHECK(!fragment.IsInlineBox());
return To<LayoutBox>(fragment.GetLayoutObject())
- ->PhysicalSelfVisualOverflowRect();
+ ->PhysicalVisualOverflowRectIncludingFilters();
}
void NGBoxFragmentPainter::Paint(const PaintInfo& paint_info) {
@@ -375,8 +395,32 @@ void NGBoxFragmentPainter::PaintInternal(const PaintInfo& paint_info) {
return;
PaintInfo& info = paint_state.MutablePaintInfo();
- PhysicalOffset paint_offset = paint_state.PaintOffset();
- PaintPhase original_phase = info.phase;
+ const PhysicalOffset paint_offset = paint_state.PaintOffset();
+ const PaintPhase original_phase = info.phase;
+
+ // For text-combine-upright:all, we need to realize canvas here for scaling
+ // to fit text content in 1em and shear for "font-style: oblique -15deg".
+ absl::optional<DrawingRecorder> recorder;
+ absl::optional<GraphicsContextStateSaver> graphics_context_state_saver;
+ const auto* const text_combine =
+ DynamicTo<LayoutNGTextCombine>(box_fragment_.GetLayoutObject());
+ if (UNLIKELY(text_combine)) {
+ if (text_combine->NeedsAffineTransformInPaint()) {
+ if (original_phase == PaintPhase::kForeground)
+ PaintCaretsIfNeeded(paint_state, paint_info, paint_offset);
+ if (!paint_info.context.InDrawingRecorder()) {
+ if (DrawingRecorder::UseCachedDrawingIfPossible(
+ paint_info.context, GetDisplayItemClient(), paint_info.phase))
+ return;
+ recorder.emplace(paint_info.context, GetDisplayItemClient(),
+ paint_info.phase,
+ text_combine->VisualRectForPaint(paint_offset));
+ }
+ graphics_context_state_saver.emplace(paint_info.context);
+ paint_info.context.ConcatCTM(
+ text_combine->ComputeAffineTransformForPaint(paint_offset));
+ }
+ }
ScopedPaintTimingDetectorBlockPaintHook
scoped_paint_timing_detector_block_paint_hook;
@@ -436,22 +480,9 @@ void NGBoxFragmentPainter::PaintInternal(const PaintInfo& paint_info) {
// If the caret's node's fragment's containing block is this block, and
// the paint action is PaintPhaseForeground, then paint the caret.
- if (original_phase == PaintPhase::kForeground &&
- ShouldPaintCarets(box_fragment_)) {
- // Apply overflow clip if needed.
- // reveal-caret-of-multiline-contenteditable.html needs this.
- // TDOO(yoisn): We should share this code with |BlockPainter::Paint()|
- absl::optional<ScopedPaintChunkProperties> paint_chunk_properties;
- if (const auto* fragment = paint_state.FragmentToPaint()) {
- if (const auto* properties = fragment->PaintProperties()) {
- if (const auto* overflow_clip = properties->OverflowClip()) {
- paint_chunk_properties.emplace(
- paint_info.context.GetPaintController(), *overflow_clip,
- *box_fragment_.GetLayoutObject(), DisplayItem::kCaret);
- }
- }
- }
- PaintCarets(paint_info, paint_offset);
+ if (original_phase == PaintPhase::kForeground && LIKELY(!recorder)) {
+ DCHECK(!text_combine || !text_combine->NeedsAffineTransformInPaint());
+ PaintCaretsIfNeeded(paint_state, paint_info, paint_offset);
}
if (ShouldPaintSelfOutline(original_phase)) {
@@ -459,10 +490,28 @@ void NGBoxFragmentPainter::PaintInternal(const PaintInfo& paint_info) {
PaintObject(info, paint_offset);
}
+ if (UNLIKELY(text_combine) &&
+ NGTextCombinePainter::ShouldPaint(*text_combine)) {
+ if (recorder) {
+ // Paint text decorations and emphasis marks without scaling and share.
+ DCHECK(text_combine->NeedsAffineTransformInPaint());
+ graphics_context_state_saver->Restore();
+ } else if (!paint_info.context.InDrawingRecorder()) {
+ if (DrawingRecorder::UseCachedDrawingIfPossible(
+ paint_info.context, GetDisplayItemClient(), paint_info.phase))
+ return;
+ recorder.emplace(paint_info.context, GetDisplayItemClient(),
+ paint_info.phase,
+ text_combine->VisualRectForPaint(paint_offset));
+ }
+ NGTextCombinePainter::Paint(info, paint_offset, *text_combine);
+ }
+
// We paint scrollbars after we painted other things, so that the scrollbars
// will sit above them.
info.phase = original_phase;
if (box_fragment_.IsScrollContainer()) {
+ DCHECK(!text_combine);
ScrollableAreaPainter(*PhysicalFragment().Layer()->GetScrollableArea())
.PaintOverflowControls(info, RoundedIntPoint(paint_offset));
}
@@ -503,7 +552,7 @@ void NGBoxFragmentPainter::PaintObject(
const PaintPhase paint_phase = paint_info.phase;
const NGPhysicalBoxFragment& fragment = PhysicalFragment();
const ComputedStyle& style = fragment.Style();
- bool is_visible = IsVisibleToPaint(fragment, style);
+ const bool is_visible = IsVisibleToPaint(fragment, style);
if (ShouldPaintSelfBlockBackground(paint_phase)) {
if (is_visible) {
PaintBoxDecorationBackground(paint_info, paint_offset,
@@ -561,13 +610,6 @@ void NGBoxFragmentPainter::PaintObject(
}
} else if (!fragment.IsInlineFormattingContext()) {
PaintBlockChildren(paint_info, paint_offset);
-
- if (is_visible && paint_info.phase == PaintPhase::kForeground &&
- box_fragment_.IsTableNG()) {
- NGTablePainter(box_fragment_)
- .PaintCollapsedBorders(paint_info, paint_offset,
- VisualRect(paint_offset));
- }
}
}
@@ -581,6 +623,15 @@ void NGBoxFragmentPainter::PaintObject(
if (!is_visible)
return;
+
+ // Collapsed borders paint *after* children have painted their backgrounds.
+ if (box_fragment_.IsTableNG() &&
+ paint_phase == PaintPhase::kDescendantBlockBackgroundsOnly) {
+ NGTablePainter(box_fragment_)
+ .PaintCollapsedBorders(paint_info, paint_offset,
+ VisualRect(paint_offset));
+ }
+
if (ShouldPaintSelfOutline(paint_phase)) {
if (NGOutlineUtils::HasPaintedOutline(style, fragment.GetNode())) {
NGFragmentPainter(fragment, GetDisplayItemClient())
@@ -595,14 +646,32 @@ void NGBoxFragmentPainter::PaintObject(
}
}
-void NGBoxFragmentPainter::PaintCarets(const PaintInfo& paint_info,
- const PhysicalOffset& paint_offset) {
- const NGPhysicalBoxFragment& fragment = PhysicalFragment();
- LocalFrame* frame = fragment.GetLayoutObject()->GetFrame();
- if (ShouldPaintCursorCaret(fragment))
+void NGBoxFragmentPainter::PaintCaretsIfNeeded(
+ const ScopedPaintState& paint_state,
+ const PaintInfo& paint_info,
+ const PhysicalOffset& paint_offset) {
+ if (!ShouldPaintCarets(box_fragment_))
+ return;
+
+ // Apply overflow clip if needed.
+ // reveal-caret-of-multiline-contenteditable.html needs this.
+ // TDOO(yoisn): We should share this code with |BlockPainter::Paint()|
+ absl::optional<ScopedPaintChunkProperties> paint_chunk_properties;
+ if (const auto* fragment = paint_state.FragmentToPaint()) {
+ if (const auto* properties = fragment->PaintProperties()) {
+ if (const auto* overflow_clip = properties->OverflowClip()) {
+ paint_chunk_properties.emplace(
+ paint_info.context.GetPaintController(), *overflow_clip,
+ *box_fragment_.GetLayoutObject(), DisplayItem::kCaret);
+ }
+ }
+ }
+
+ LocalFrame* frame = box_fragment_.GetLayoutObject()->GetFrame();
+ if (ShouldPaintCursorCaret(box_fragment_))
frame->Selection().PaintCaret(paint_info.context, paint_offset);
- if (ShouldPaintDragCaret(fragment)) {
+ if (ShouldPaintDragCaret(box_fragment_)) {
frame->GetPage()->GetDragCaret().PaintDragCaret(frame, paint_info.context,
paint_offset);
}
@@ -652,6 +721,11 @@ void NGBoxFragmentPainter::PaintBlockFlowContents(
DCHECK(items_);
NGInlineCursor children(fragment, *items_);
+ if (fragment.IsSvgText()) {
+ ScopedSVGPaintState paint_state(*fragment.GetLayoutObject(), paint_info);
+ PaintLineBoxChildren(&children, paint_info.ForDescendants(), paint_offset);
+ return;
+ }
PaintLineBoxChildren(&children, paint_info.ForDescendants(), paint_offset);
}
@@ -659,63 +733,74 @@ void NGBoxFragmentPainter::PaintBlockChildren(const PaintInfo& paint_info,
PhysicalOffset paint_offset) {
DCHECK(!box_fragment_.IsInlineFormattingContext());
PaintInfo paint_info_for_descendants = paint_info.ForDescendants();
+ paint_info_for_descendants.SetIsInFragmentTraversal();
for (const NGLink& child : box_fragment_.Children()) {
const NGPhysicalFragment& child_fragment = *child;
DCHECK(child_fragment.IsBox());
if (child_fragment.HasSelfPaintingLayer() || child_fragment.IsFloating())
continue;
+ PaintBlockChild(child, paint_info, paint_info_for_descendants,
+ paint_offset);
+ }
+}
- 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;
-
- if (box_child_fragment.IsFragmentainerBox()) {
- // This is a fragmentainer, and when node inside a fragmentation
- // context paints multiple block fragments, we need to distinguish
- // between them somehow, for paint caching to work. Therefore,
- // establish a display item scope here.
- unsigned identifier =
- FragmentainerUniqueIdentifier(box_child_fragment);
- ScopedDisplayItemFragment scope(paint_info.context, identifier);
- NGBoxFragmentPainter(box_child_fragment)
- .PaintObject(paint_info, child_offset);
- continue;
- }
-
+void NGBoxFragmentPainter::PaintBlockChild(
+ const NGLink& child,
+ const PaintInfo& paint_info,
+ const PaintInfo& paint_info_for_descendants,
+ PhysicalOffset paint_offset) {
+ const NGPhysicalFragment& child_fragment = *child;
+ DCHECK(child_fragment.IsBox());
+ DCHECK(!child_fragment.HasSelfPaintingLayer());
+ DCHECK(!child_fragment.IsFloating());
+ 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;
+
+ if (box_child_fragment.IsFragmentainerBox()) {
+ // This is a fragmentainer, and when node inside a fragmentation
+ // context paints multiple block fragments, we need to distinguish
+ // between them somehow, for paint caching to work. Therefore,
+ // establish a display item scope here.
+ unsigned identifier = FragmentainerUniqueIdentifier(box_child_fragment);
+ ScopedDisplayItemFragment scope(paint_info.context, identifier);
NGBoxFragmentPainter(box_child_fragment)
.PaintObject(paint_info, child_offset);
- continue;
+ return;
}
NGBoxFragmentPainter(box_child_fragment)
- .Paint(paint_info_for_descendants);
- continue;
+ .PaintObject(paint_info, child_offset);
+ return;
}
- // Fall back to flow-thread painting when reaching a column (the flow thread
- // is treated as a self-painting PaintLayer when fragment traversal is
- // disabled, so nothing to do here).
- if (box_child_fragment.IsColumnBox())
- continue;
+ NGBoxFragmentPainter(box_child_fragment).Paint(paint_info_for_descendants);
+ return;
+ }
- auto* layout_object = child_fragment.GetLayoutObject();
- DCHECK(layout_object);
- if (child_fragment.IsPaintedAtomically() &&
- child_fragment.IsLegacyLayoutRoot()) {
- ObjectPainter(*layout_object)
- .PaintAllPhasesAtomically(paint_info_for_descendants);
- } else {
- // TODO(ikilpatrick): Once FragmentItem ships we should call the
- // NGBoxFragmentPainter directly for NG objects.
- layout_object->Paint(paint_info_for_descendants);
- }
+ // Fall back to flow-thread painting when reaching a column (the flow thread
+ // is treated as a self-painting PaintLayer when fragment traversal is
+ // disabled, so nothing to do here).
+ if (box_child_fragment.IsColumnBox())
+ return;
+
+ auto* layout_object = child_fragment.GetLayoutObject();
+ DCHECK(layout_object);
+ if (child_fragment.IsPaintedAtomically() &&
+ child_fragment.IsLegacyLayoutRoot()) {
+ ObjectPainter(*layout_object)
+ .PaintAllPhasesAtomically(paint_info_for_descendants);
+ } else {
+ // TODO(ikilpatrick): Once FragmentItem ships we should call the
+ // NGBoxFragmentPainter directly for NG objects.
+ layout_object->Paint(paint_info_for_descendants);
}
}
@@ -1128,6 +1213,20 @@ void NGBoxFragmentPainter::PaintBoxDecorationBackgroundWithRectImpl(
paint_info.context.EndLayer();
}
+void NGBoxFragmentPainter::PaintBoxDecorationBackgroundForBlockInInline(
+ NGInlineCursor* children,
+ const PaintInfo& paint_info,
+ const PhysicalOffset& paint_offset) {
+ for (; *children; children->MoveToNext()) {
+ const NGFragmentItem* item = children->Current().Item();
+ if (item->Type() != NGFragmentItem::kBox)
+ continue;
+ const NGPhysicalBoxFragment* fragment = item->BoxFragment();
+ if (fragment && fragment->IsBlockInInline())
+ PaintBoxItem(*item, *fragment, *children, paint_info, paint_offset);
+ }
+}
+
void NGBoxFragmentPainter::PaintColumnRules(
const PaintInfo& paint_info,
const PhysicalOffset& paint_offset) {
@@ -1218,8 +1317,8 @@ void NGBoxFragmentPainter::PaintColumnRules(
rule.Move(paint_offset);
IntRect snapped_rule = PixelSnappedIntRect(rule);
- ObjectPainter::DrawBoxSide(paint_info.context, snapped_rule, box_side,
- rule_color, rule_style);
+ BoxBorderPainter::DrawBoxSide(paint_info.context, snapped_rule, box_side,
+ rule_color, rule_style);
recorder.UniteVisualRect(snapped_rule);
previous_column = current_column;
@@ -1312,7 +1411,7 @@ void NGBoxFragmentPainter::PaintInlineItems(const PaintInfo& paint_info,
}
switch (item->Type()) {
case NGFragmentItem::kText:
- case NGFragmentItem::kSVGText:
+ case NGFragmentItem::kSvgText:
case NGFragmentItem::kGeneratedText:
if (!item->IsHiddenForPaint())
PaintTextItem(*cursor, paint_info, paint_offset, parent_offset);
@@ -1375,8 +1474,14 @@ void NGBoxFragmentPainter::PaintLineBoxChildren(
paint_info.phase != PaintPhase::kTextClip &&
paint_info.phase != PaintPhase::kMask &&
paint_info.phase != PaintPhase::kDescendantOutlinesOnly &&
- paint_info.phase != PaintPhase::kOutline)
+ paint_info.phase != PaintPhase::kOutline) {
+ if (UNLIKELY(ShouldPaintDescendantBlockBackgrounds(paint_info.phase))) {
+ // When block-in-inline, block backgrounds need to be painted.
+ PaintBoxDecorationBackgroundForBlockInInline(children, paint_info,
+ paint_offset);
+ }
return;
+ }
// The only way an inline could paint like this is if it has a layer.
const auto* layout_object = box_fragment_.GetLayoutObject();
@@ -1556,9 +1661,19 @@ void NGBoxFragmentPainter::PaintBoxItem(
return;
}
- DCHECK(child_fragment.IsInlineBox());
- NGInlineBoxFragmentPainter(cursor, item, child_fragment)
- .Paint(paint_info, paint_offset);
+ if (child_fragment.IsInlineBox()) {
+ NGInlineBoxFragmentPainter(cursor, item, child_fragment)
+ .Paint(paint_info, paint_offset);
+ return;
+ }
+
+ // Block-in-inline
+ DCHECK(RuntimeEnabledFeatures::LayoutNGBlockInInlineEnabled());
+ DCHECK(!child_fragment.GetLayoutObject()->IsInline());
+ PaintInfo paint_info_for_descendants = paint_info.ForDescendants();
+ paint_info_for_descendants.SetIsInFragmentTraversal();
+ PaintBlockChild({&child_fragment, item.OffsetInContainerFragment()},
+ paint_info, paint_info_for_descendants, paint_offset);
}
void NGBoxFragmentPainter::PaintBoxItem(const NGFragmentItem& item,
@@ -1616,14 +1731,15 @@ bool NGBoxFragmentPainter::ShouldPaint(
return false;
}
-void NGBoxFragmentPainter::PaintTextClipMask(GraphicsContext& context,
+void NGBoxFragmentPainter::PaintTextClipMask(const PaintInfo& paint_info,
const IntRect& mask_rect,
const PhysicalOffset& paint_offset,
bool object_has_multiple_boxes) {
- PaintInfo paint_info(context, CullRect(mask_rect), PaintPhase::kTextClip,
- kGlobalPaintNormalPhase, 0);
+ PaintInfo mask_paint_info(paint_info.context, CullRect(mask_rect),
+ PaintPhase::kTextClip, kGlobalPaintNormalPhase, 0);
+ mask_paint_info.SetFragmentID(paint_info.FragmentID());
if (!object_has_multiple_boxes) {
- PaintObject(paint_info, paint_offset);
+ PaintObject(mask_paint_info, paint_offset);
return;
}
@@ -1631,7 +1747,7 @@ void NGBoxFragmentPainter::PaintTextClipMask(GraphicsContext& context,
DCHECK(box_item_);
NGInlineBoxFragmentPainter inline_box_painter(*inline_box_cursor_,
*box_item_);
- PaintTextClipMask(paint_info,
+ PaintTextClipMask(mask_paint_info,
paint_offset - box_item_->OffsetInContainerFragment(),
&inline_box_painter);
}
@@ -1669,8 +1785,8 @@ PhysicalRect NGBoxFragmentPainter::AdjustRectForScrolledContent(
// Adjust the paint rect to reflect a scrolled content box with borders at
// the ends.
- PhysicalOffset offset(physical.PixelSnappedScrolledContentOffset());
- scrolled_paint_rect.Move(-offset);
+ scrolled_paint_rect.offset -=
+ PhysicalOffset(physical.PixelSnappedScrolledContentOffset());
LayoutRectOutsets borders = AdjustedBorderOutsets(info);
scrolled_paint_rect.size =
physical.ScrollSize() + PhysicalSize(borders.Size());
@@ -1708,10 +1824,11 @@ BoxPainterBase::FillLayerInfo NGBoxFragmentPainter::GetFillLayerInfo(
is_painting_scrolling_background);
}
+template <typename T>
bool NGBoxFragmentPainter::HitTestContext::AddNodeToResult(
Node* node,
const NGPhysicalBoxFragment* box_fragment,
- const PhysicalRect& bounds_rect,
+ const T& bounds_rect,
const PhysicalOffset& offset) const {
if (node && !result->InnerNode())
result->SetNodeAndPosition(node, box_fragment, location.Point() - offset);
@@ -1719,10 +1836,11 @@ bool NGBoxFragmentPainter::HitTestContext::AddNodeToResult(
kStopHitTesting;
}
+template <typename T>
bool NGBoxFragmentPainter::HitTestContext::AddNodeToResultWithContentOffset(
Node* node,
const NGPhysicalBoxFragment& container,
- const PhysicalRect& bounds_rect,
+ const T& bounds_rect,
PhysicalOffset offset) const {
if (container.IsScrollContainer())
offset += PhysicalOffset(container.PixelSnappedScrolledContentOffset());
@@ -1754,12 +1872,19 @@ bool NGBoxFragmentPainter::NodeAtPoint(const HitTestContext& hit_test,
physical_offset))
return false;
+ bool pointer_events_bounding_box = false;
bool hit_test_self = fragment.IsInSelfHitTestingPhase(hit_test.action);
if (hit_test_self) {
// Table row and table section are never a hit target.
+ // SVG <text> is not a hit target except if 'pointer-events: bounding-box'.
if (PhysicalFragment().IsTableNGRow() ||
- PhysicalFragment().IsTableNGSection())
+ PhysicalFragment().IsTableNGSection()) {
hit_test_self = false;
+ } else if (fragment.IsSvgText()) {
+ pointer_events_bounding_box =
+ fragment.Style().PointerEvents() == EPointerEvents::kBoundingBox;
+ hit_test_self = pointer_events_bounding_box;
+ }
}
if (hit_test_self && box_fragment_.IsScrollContainer() &&
@@ -1813,11 +1938,20 @@ bool NGBoxFragmentPainter::NodeAtPoint(const HitTestContext& hit_test,
if (hit_test_self &&
IsVisibleToHitTest(box_fragment_, hit_test.result->GetHitTestRequest())) {
PhysicalRect bounds_rect(physical_offset, size);
- if (UNLIKELY(hit_test.result->GetHitTestRequest().GetType() &
- HitTestRequest::kHitTestVisualOverflow)) {
- bounds_rect = SelfInkOverflow();
+ if (UNLIKELY(
+ hit_test.result->GetHitTestRequest().IsHitTestVisualOverflow())) {
+ // We'll include overflow from children here (in addition to self-overflow
+ // caused by filters), because we want to record a match if we hit the
+ // overflow of a child below the stop node. This matches legacy behavior
+ // in LayoutBox::NodeAtPoint(); see call to
+ // PhysicalVisualOverflowRectIncludingFilters().
+ bounds_rect = InkOverflowIncludingFilters();
bounds_rect.Move(physical_offset);
}
+ if (UNLIKELY(pointer_events_bounding_box)) {
+ bounds_rect = PhysicalRect::EnclosingRect(
+ PhysicalFragment().GetLayoutObject()->ObjectBoundingBox());
+ }
// 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())
@@ -1891,16 +2025,27 @@ bool NGBoxFragmentPainter::HitTestTextItem(
if (!IsVisibleToHitTest(text_item, hit_test.result->GetHitTestRequest()))
return false;
- // TODO(layout-dev): Clip to line-top/bottom.
- const PhysicalOffset offset =
- hit_test.inline_root_offset + text_item.OffsetInContainerFragment();
- PhysicalRect border_rect(offset, text_item.Size());
- PhysicalRect rect(PixelSnappedIntRect(border_rect));
- if (UNLIKELY(hit_test.result->GetHitTestRequest().GetType() &
- HitTestRequest::kHitTestVisualOverflow)) {
- rect = text_item.SelfInkOverflow();
- rect.Move(border_rect.offset);
+ if (text_item.Type() == NGFragmentItem::kSvgText &&
+ text_item.HasSvgTransformForBoundingBox()) {
+ const FloatQuad quad = text_item.SvgUnscaledQuad();
+ if (!hit_test.location.Intersects(quad))
+ return false;
+ return hit_test.AddNodeToResultWithContentOffset(
+ text_item.NodeForHitTest(), cursor.ContainerFragment(), quad,
+ hit_test.inline_root_offset);
}
+
+ const auto* const text_combine =
+ DynamicTo<LayoutNGTextCombine>(box_fragment_.GetLayoutObject());
+
+ // TODO(layout-dev): Clip to line-top/bottom.
+ const PhysicalRect rect =
+ UNLIKELY(text_combine)
+ ? text_combine->ComputeTextBoundsRectForHitTest(
+ text_item, hit_test.inline_root_offset)
+ : text_item.ComputeTextBoundsRectForHitTest(
+ hit_test.inline_root_offset,
+ hit_test.result->GetHitTestRequest().IsHitTestVisualOverflow());
if (!hit_test.location.Intersects(rect))
return false;
@@ -1943,6 +2088,9 @@ bool NGBoxFragmentPainter::HitTestLineBoxFragment(
bounds_rect)))
return false;
+ if (cursor.ContainerFragment().IsSvgText())
+ return false;
+
// Now hit test ourselves.
if (!hit_test.location.Intersects(bounds_rect))
return false;
@@ -2033,14 +2181,18 @@ bool NGBoxFragmentPainter::HitTestChildBoxItem(
return true;
}
+ if (cursor.ContainerFragment().IsSvgText() &&
+ item.Style().PointerEvents() != EPointerEvents::kBoundingBox)
+ return false;
+
// 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.OffsetInContainerFragment();
PhysicalRect bounds_rect(child_offset, item.Size());
- if (UNLIKELY(hit_test.result->GetHitTestRequest().GetType() &
- HitTestRequest::kHitTestVisualOverflow)) {
+ if (UNLIKELY(
+ hit_test.result->GetHitTestRequest().IsHitTestVisualOverflow())) {
bounds_rect = item.SelfInkOverflow();
bounds_rect.Move(child_offset);
}
diff --git a/chromium/third_party/blink/renderer/core/paint/ng/ng_box_fragment_painter.h b/chromium/third_party/blink/renderer/core/paint/ng/ng_box_fragment_painter.h
index 1d7e9b305de..0338e7ada07 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
@@ -82,7 +82,7 @@ class CORE_EXPORT NGBoxFragmentPainter : public BoxPainterBase {
bool is_painting_scrolling_background) const override;
bool IsPaintingScrollingBackground(const PaintInfo&) const override;
- void PaintTextClipMask(GraphicsContext&,
+ void PaintTextClipMask(const PaintInfo&,
const IntRect& mask_rect,
const PhysicalOffset& paint_offset,
bool object_has_multiple_boxes) override;
@@ -115,11 +115,20 @@ class CORE_EXPORT NGBoxFragmentPainter : public BoxPainterBase {
const PhysicalRect& paint_rect,
const DisplayItemClient&);
+ void PaintBoxDecorationBackgroundForBlockInInline(
+ NGInlineCursor* children,
+ const PaintInfo&,
+ const PhysicalOffset& paint_offset);
+
void PaintColumnRules(const PaintInfo&, const PhysicalOffset& paint_offset);
void PaintInternal(const PaintInfo&);
void PaintAllPhasesAtomically(const PaintInfo&);
void PaintBlockChildren(const PaintInfo&, PhysicalOffset);
+ void PaintBlockChild(const NGLink& child,
+ const PaintInfo& paint_info,
+ const PaintInfo& paint_info_for_descendants,
+ PhysicalOffset paint_offset);
void PaintInlineItems(const PaintInfo&,
const PhysicalOffset& paint_offset,
const PhysicalOffset& parent_offset,
@@ -167,7 +176,9 @@ class CORE_EXPORT NGBoxFragmentPainter : public BoxPainterBase {
const PhysicalRect&,
const Color& background_color,
BackgroundBleedAvoidance = kBackgroundBleedNone);
- void PaintCarets(const PaintInfo&, const PhysicalOffset& paint_offset);
+ void PaintCaretsIfNeeded(const ScopedPaintState&,
+ const PaintInfo&,
+ const PhysicalOffset& paint_offset);
// This should be called in the background paint phase even if there is no
// other painted content.
@@ -193,17 +204,21 @@ class CORE_EXPORT NGBoxFragmentPainter : public BoxPainterBase {
// Add |node| to |HitTestResult|. Returns true if the hit-testing should
// stop.
+ // T is PhysicalRect or FloatQuad.
+ template <typename T>
bool AddNodeToResult(Node* node,
const NGPhysicalBoxFragment* box_fragment,
- const PhysicalRect& bounds_rect,
+ const T& bounds_rect,
const PhysicalOffset& offset) const;
// Same as |AddNodeToResult|, except that |offset| is in the content
// coordinate system rather than the container coordinate system. They
// differ when |container| is a scroll container.
+ // T is PhysicalRect or FloatQuad.
+ template <typename T>
bool AddNodeToResultWithContentOffset(
Node* node,
const NGPhysicalBoxFragment& container,
- const PhysicalRect& bounds_rect,
+ const T& bounds_rect,
PhysicalOffset offset) const;
HitTestAction action;
@@ -288,7 +303,7 @@ class CORE_EXPORT NGBoxFragmentPainter : public BoxPainterBase {
const DisplayItemClient& GetDisplayItemClient() const {
return display_item_client_;
}
- PhysicalRect SelfInkOverflow() const;
+ PhysicalRect InkOverflowIncludingFilters() const;
const NGPhysicalBoxFragment& box_fragment_;
const DisplayItemClient& display_item_client_;
@@ -315,8 +330,6 @@ inline NGBoxFragmentPainter::NGBoxFragmentPainter(
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_);
#endif
}
@@ -336,7 +349,6 @@ inline NGBoxFragmentPainter::NGBoxFragmentPainter(
&inline_box_cursor,
&item) {
DCHECK_EQ(item.BoxFragment(), &fragment);
- DCHECK(fragment.IsInlineBox());
}
} // 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 4ad80e25d81..9d9bfd812c1 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
@@ -200,4 +200,26 @@ TEST_P(NGBoxFragmentPainterTest, SelectionTablePainting) {
auto record = builder.EndRecording();
}
+TEST_P(NGBoxFragmentPainterTest, ClippedText) {
+ SetBodyInnerHTML(R"HTML(
+ <div id="target" style="overflow: hidden; position: relative;
+ width: 100px; height: 100px">
+ A<br>B<br>C<br>D
+ </div>
+ )HTML");
+ // Initially all the texts are painted.
+ auto num_all_display_items = ContentDisplayItems().size();
+ auto* target = GetDocument().getElementById("target");
+
+ target->SetInlineStyleProperty(CSSPropertyID::kHeight, "0px");
+ UpdateAllLifecyclePhasesForTest();
+ // None of the texts should be painted.
+ EXPECT_EQ(num_all_display_items - 4, ContentDisplayItems().size());
+
+ target->SetInlineStyleProperty(CSSPropertyID::kHeight, "1px");
+ UpdateAllLifecyclePhasesForTest();
+ // Only "A" should be painted.
+ EXPECT_EQ(num_all_display_items - 3, ContentDisplayItems().size());
+}
+
} // namespace blink
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 dd8d402df61..d8e252149e5 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
@@ -5,6 +5,7 @@
#include "third_party/blink/renderer/core/paint/ng/ng_fragment_painter.h"
#include "third_party/blink/renderer/core/layout/ng/ng_outline_utils.h"
+#include "third_party/blink/renderer/core/paint/outline_painter.h"
#include "third_party/blink/renderer/core/paint/paint_info.h"
#include "third_party/blink/renderer/core/style/computed_style.h"
#include "third_party/blink/renderer/platform/graphics/graphics_context_state_saver.h"
@@ -35,7 +36,8 @@ void NGFragmentPainter::PaintOutline(const PaintInfo& paint_info,
visual_rect.Inflate(style_to_use.OutlineOutsetExtent());
DrawingRecorder recorder(paint_info.context, display_item_client,
paint_info.phase, visual_rect);
- PaintOutlineRects(paint_info, outline_rects, style_to_use);
+ OutlinePainter::PaintOutlineRects(paint_info.context, outline_rects,
+ style_to_use);
}
void NGFragmentPainter::AddURLRectIfNeeded(const PaintInfo& paint_info,
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 679d9aad40e..b62e6145a01 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
@@ -6,7 +6,6 @@
#define THIRD_PARTY_BLINK_RENDERER_CORE_PAINT_NG_NG_FRAGMENT_PAINTER_H_
#include "third_party/blink/renderer/core/layout/ng/ng_physical_box_fragment.h"
-#include "third_party/blink/renderer/core/paint/object_painter_base.h"
#include "third_party/blink/renderer/platform/wtf/hash_map.h"
namespace blink {
@@ -15,8 +14,8 @@ struct PaintInfo;
struct PhysicalOffset;
// Generic fragment painter for paint logic shared between all types of
-// fragments. LayoutNG version of ObjectPainter, based on ObjectPainterBase.
-class NGFragmentPainter : public ObjectPainterBase {
+// fragments. LayoutNG version of ObjectPainter.
+class NGFragmentPainter {
STACK_ALLOCATED();
public:
diff --git a/chromium/third_party/blink/renderer/core/paint/ng/ng_highlight_painter.cc b/chromium/third_party/blink/renderer/core/paint/ng/ng_highlight_painter.cc
index ac1af42b6b1..6ef75fc91e1 100644
--- a/chromium/third_party/blink/renderer/core/paint/ng/ng_highlight_painter.cc
+++ b/chromium/third_party/blink/renderer/core/paint/ng/ng_highlight_painter.cc
@@ -8,9 +8,11 @@
#include "third_party/blink/renderer/core/editing/editor.h"
#include "third_party/blink/renderer/core/editing/frame_selection.h"
#include "third_party/blink/renderer/core/editing/markers/document_marker_controller.h"
+#include "third_party/blink/renderer/core/editing/markers/highlight_marker.h"
#include "third_party/blink/renderer/core/editing/markers/styleable_marker.h"
#include "third_party/blink/renderer/core/editing/markers/text_marker_base.h"
#include "third_party/blink/renderer/core/frame/local_frame.h"
+#include "third_party/blink/renderer/core/highlight/highlight.h"
#include "third_party/blink/renderer/core/layout/layout_object.h"
#include "third_party/blink/renderer/core/layout/ng/inline/ng_inline_cursor.h"
#include "third_party/blink/renderer/core/paint/document_marker_painter.h"
@@ -18,6 +20,7 @@
#include "third_party/blink/renderer/core/paint/inline_text_box_painter.h"
#include "third_party/blink/renderer/core/paint/ng/ng_text_painter.h"
#include "third_party/blink/renderer/core/paint/paint_info.h"
+#include "third_party/blink/renderer/platform/instrumentation/use_counter.h"
namespace blink {
@@ -110,8 +113,11 @@ Color SelectionBackgroundColor(const Document& document,
// If the text color ends up being the same as the selection background,
// invert the selection background.
- if (text_color == color)
+ if (text_color == color) {
+ UseCounter::Count(node->GetDocument(),
+ WebFeature::kSelectionBackgroundColorInversion);
return Color(0xff - color.Red(), 0xff - color.Green(), 0xff - color.Blue());
+ }
return color;
}
@@ -231,15 +237,14 @@ void NGHighlightPainter::SelectionPaintState::
}
}
-NGHighlightPainter::NGHighlightPainter(
- NGTextPainter& text_painter,
- const PaintInfo& paint_info,
- const NGInlineCursor& cursor,
- const NGFragmentItem& fragment_item,
- const PhysicalOffset& box_origin,
- const ComputedStyle& style,
- absl::optional<SelectionPaintState> selection,
- bool is_printing)
+NGHighlightPainter::NGHighlightPainter(NGTextPainter& text_painter,
+ const PaintInfo& paint_info,
+ const NGInlineCursor& cursor,
+ const NGFragmentItem& fragment_item,
+ const PhysicalOffset& box_origin,
+ const ComputedStyle& style,
+ SelectionPaintState* selection,
+ bool is_printing)
: text_painter_(text_painter),
paint_info_(paint_info),
cursor_(cursor),
@@ -301,8 +306,7 @@ void NGHighlightPainter::Paint(Phase phase) {
Color color;
if (marker->GetType() == DocumentMarker::kTextMatch) {
color = LayoutTheme::GetTheme().PlatformTextSearchHighlightColor(
- text_marker.IsActiveMatch(), document.InForcedColorsMode(),
- style_.UsedColorScheme());
+ text_marker.IsActiveMatch(), style_.UsedColorScheme());
} else {
color = HighlightPaintingUtils::HighlightBackgroundColor(
document, style_, node_, kPseudoIdTargetText);
@@ -347,6 +351,49 @@ void NGHighlightPainter::Paint(Phase phase) {
}
} break;
+ case DocumentMarker::kHighlight: {
+ const auto& highlight_marker = To<HighlightMarker>(*marker);
+ const Document& document = node_->GetDocument();
+
+ // Paint background
+ if (phase == kBackground) {
+ Color background_color =
+ HighlightPaintingUtils::HighlightBackgroundColor(
+ document, style_, node_, kPseudoIdHighlight,
+ highlight_marker.GetHighlightName());
+
+ PaintRect(paint_info_.context, PhysicalOffset(box_origin_),
+ fragment_item_.LocalRect(text, paint_start_offset,
+ paint_end_offset),
+ background_color);
+ break;
+ }
+
+ DCHECK_EQ(phase, kForeground);
+ Color text_color = style_.VisitedDependentColor(GetCSSPropertyColor());
+
+ TextPaintStyle text_style;
+ text_style.current_color = text_style.fill_color =
+ text_style.stroke_color = text_style.emphasis_mark_color =
+ text_color;
+ text_style.stroke_width = style_.TextStrokeWidth();
+ text_style.color_scheme = style_.UsedColorScheme();
+ text_style.shadow = nullptr;
+
+ const TextPaintStyle final_text_style =
+ HighlightPaintingUtils::HighlightPaintingStyle(
+ document, style_, node_, kPseudoIdHighlight, text_style,
+ paint_info_, highlight_marker.GetHighlightName());
+
+ if (final_text_style.current_color == Color::kTransparent)
+ break;
+
+ text_painter_.Paint(paint_start_offset, paint_end_offset,
+ paint_end_offset - paint_start_offset,
+ final_text_style, kInvalidDOMNodeId);
+
+ } break;
+
default:
NOTREACHED();
break;
diff --git a/chromium/third_party/blink/renderer/core/paint/ng/ng_highlight_painter.h b/chromium/third_party/blink/renderer/core/paint/ng/ng_highlight_painter.h
index d01ec5ff291..69185992a54 100644
--- a/chromium/third_party/blink/renderer/core/paint/ng/ng_highlight_painter.h
+++ b/chromium/third_party/blink/renderer/core/paint/ng/ng_highlight_painter.h
@@ -5,6 +5,7 @@
#ifndef THIRD_PARTY_BLINK_RENDERER_CORE_PAINT_NG_NG_HIGHLIGHT_PAINTER_H_
#define THIRD_PARTY_BLINK_RENDERER_CORE_PAINT_NG_NG_HIGHLIGHT_PAINTER_H_
+#include "third_party/abseil-cpp/absl/types/optional.h"
#include "third_party/blink/renderer/core/core_export.h"
#include "third_party/blink/renderer/core/editing/frame_selection.h"
#include "third_party/blink/renderer/core/editing/markers/document_marker.h"
@@ -98,13 +99,13 @@ class CORE_EXPORT NGHighlightPainter {
const NGFragmentItem& fragment_item,
const PhysicalOffset& box_origin,
const ComputedStyle& style,
- absl::optional<SelectionPaintState>,
+ SelectionPaintState*,
bool is_printing);
enum Phase { kBackground, kForeground };
void Paint(Phase phase);
- absl::optional<SelectionPaintState>& Selection() { return selection_; }
+ SelectionPaintState* Selection() { return selection_; }
absl::optional<AppliedTextDecoration> SelectionDecoration() {
return selection_
? selection_->GetSelectionStyle().selection_text_decoration
@@ -118,7 +119,7 @@ class CORE_EXPORT NGHighlightPainter {
const NGFragmentItem& fragment_item_;
const PhysicalOffset& box_origin_;
const ComputedStyle& style_;
- absl::optional<SelectionPaintState> selection_;
+ SelectionPaintState* selection_;
const LayoutObject* layout_object_;
Node* node_;
const DocumentMarkerVector markers_;
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 acddffe60bd..e08bf386fb9 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
@@ -12,6 +12,7 @@
#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_phase.h"
+#include "third_party/blink/renderer/core/paint/scoped_svg_paint_state.h"
#include "third_party/blink/renderer/core/style/nine_piece_image.h"
#include "third_party/blink/renderer/platform/graphics/graphics_context_state_saver.h"
#include "third_party/blink/renderer/platform/graphics/paint/drawing_recorder.h"
@@ -50,10 +51,15 @@ void NGInlineBoxFragmentPainter::Paint(const PaintInfo& paint_info,
const PhysicalOffset& paint_offset) {
ScopedDisplayItemFragment display_item_fragment(
paint_info.context, inline_box_item_.FragmentId());
+ const LayoutObject& layout_object = *inline_box_fragment_.GetLayoutObject();
+ absl::optional<ScopedSVGPaintState> svg_paint_state;
+ if (layout_object.IsSVGInline())
+ svg_paint_state.emplace(layout_object, paint_info);
const PhysicalOffset adjusted_paint_offset =
paint_offset + inline_box_item_.OffsetInContainerFragment();
- if (paint_info.phase == PaintPhase::kForeground)
+ if (paint_info.phase == PaintPhase::kForeground &&
+ !layout_object.IsSVGInline())
PaintBackgroundBorderShadow(paint_info, adjusted_paint_offset);
const bool suppress_box_decoration_background = true;
@@ -68,7 +74,8 @@ void NGInlineBoxFragmentPainterBase::PaintBackgroundBorderShadow(
const PaintInfo& paint_info,
const PhysicalOffset& paint_offset) {
DCHECK(paint_info.phase == PaintPhase::kForeground);
- if (inline_box_fragment_.Style().Visibility() != EVisibility::kVisible)
+ if (inline_box_fragment_.Style().Visibility() != EVisibility::kVisible ||
+ inline_box_fragment_.IsOpaque())
return;
// You can use p::first-line to specify a background. If so, the direct child
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 1a0fefb2448..a3a79d8f6a7 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 "base/dcheck_is_on.h"
+#include "third_party/blink/renderer/core/layout/layout_object_inlines.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"
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
index d764b2f2567..fbffad0c60b 100644
--- 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
@@ -79,6 +79,17 @@ void NGMathMLPainter::PaintOperator(const PaintInfo& info,
auto padding = box_fragment_.Padding();
physical_offset.left += borders.left + padding.left;
physical_offset.top += borders.top + padding.top;
+
+ // TODO(http://crbug.com/1124301): NGMathOperatorLayoutAlgorithm::Layout
+ // passes the operator's inline size but this does not match the width of the
+ // box fragment, which relies on the min-max sizes instead. Shift the paint
+ // offset to work around that issue, splitting the size error symmetrically.
+ DCHECK(box_fragment_.Style().IsHorizontalWritingMode());
+ physical_offset.left +=
+ (box_fragment_.Size().width - borders.HorizontalSum() -
+ padding.HorizontalSum() - parameters.operator_inline_size) /
+ 2;
+
PaintStretchyOrLargeOperator(info, paint_offset + physical_offset);
}
diff --git a/chromium/third_party/blink/renderer/core/paint/ng/ng_table_painters.cc b/chromium/third_party/blink/renderer/core/paint/ng/ng_table_painters.cc
index e44355ff1e8..53bca621ece 100644
--- a/chromium/third_party/blink/renderer/core/paint/ng/ng_table_painters.cc
+++ b/chromium/third_party/blink/renderer/core/paint/ng/ng_table_painters.cc
@@ -13,11 +13,11 @@
#include "third_party/blink/renderer/core/layout/ng/table/layout_ng_table_column.h"
#include "third_party/blink/renderer/core/layout/ng/table/ng_table_borders.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/box_decoration_data.h"
#include "third_party/blink/renderer/core/paint/box_model_object_painter.h"
#include "third_party/blink/renderer/core/paint/box_painter.h"
#include "third_party/blink/renderer/core/paint/ng/ng_box_fragment_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/scoped_paint_state.h"
@@ -53,6 +53,16 @@ class NGTableCollapsedEdge {
InitCachedProps();
}
+ NGTableCollapsedEdge(const NGTableCollapsedEdge& edge)
+ : NGTableCollapsedEdge(edge, 0) {}
+
+ NGTableCollapsedEdge& operator=(const NGTableCollapsedEdge& edge) {
+ edge_index_ = edge.edge_index_;
+ border_width_ = edge.border_width_;
+ border_style_ = edge.border_style_;
+ return *this;
+ }
+
bool Exists() const { return edge_index_ != UINT_MAX; }
bool CanPaint() const {
@@ -206,13 +216,6 @@ class NGTableCollapsedEdge {
return !(*this == rhs);
}
- NGTableCollapsedEdge& operator=(const NGTableCollapsedEdge& edge) {
- edge_index_ = edge.edge_index_;
- border_width_ = edge.border_width_;
- border_style_ = edge.border_style_;
- return *this;
- }
-
private:
void InitCachedProps() {
if (edge_index_ == UINT_MAX) {
@@ -420,7 +423,6 @@ void NGTablePainter::PaintBoxDecorationBackground(
void NGTablePainter::PaintCollapsedBorders(const PaintInfo& paint_info,
const PhysicalOffset& paint_offset,
const IntRect& visual_rect) {
- DCHECK_EQ(paint_info.phase, PaintPhase::kForeground);
const NGTableBorders* collapsed_borders = fragment_.TableCollapsedBorders();
if (!collapsed_borders)
return;
@@ -525,7 +527,7 @@ void NGTablePainter::PaintCollapsedBorders(const PaintInfo& paint_info,
} else {
box_side = edge.IsInlineAxis() ? BoxSide::kLeft : BoxSide::kTop;
}
- ObjectPainter::DrawBoxSide(
+ BoxBorderPainter::DrawBoxSide(
paint_info.context, PixelSnappedIntRect(physical_border_rect), box_side,
edge.BorderColor(), edge.BorderStyle());
}
diff --git a/chromium/third_party/blink/renderer/core/paint/ng/ng_text_combine_painter.cc b/chromium/third_party/blink/renderer/core/paint/ng/ng_text_combine_painter.cc
new file mode 100644
index 00000000000..82a618e8a1e
--- /dev/null
+++ b/chromium/third_party/blink/renderer/core/paint/ng/ng_text_combine_painter.cc
@@ -0,0 +1,127 @@
+// Copyright 2021 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_text_combine_painter.h"
+
+#include "third_party/blink/renderer/core/layout/ng/inline/layout_ng_text_combine.h"
+#include "third_party/blink/renderer/core/layout/ng/ng_text_decoration_offset.h"
+#include "third_party/blink/renderer/core/paint/paint_info.h"
+#include "third_party/blink/renderer/core/style/computed_style.h"
+#include "third_party/blink/renderer/platform/graphics/graphics_context.h"
+#include "third_party/blink/renderer/platform/graphics/graphics_context_state_saver.h"
+
+namespace blink {
+
+NGTextCombinePainter::NGTextCombinePainter(GraphicsContext& context,
+ const ComputedStyle& style,
+ const PhysicalRect& text_frame_rect)
+ : TextPainterBase(context,
+ style.GetFont(),
+ text_frame_rect.offset,
+ text_frame_rect,
+ /* horizontal */ false),
+ style_(style) {}
+
+NGTextCombinePainter::~NGTextCombinePainter() = default;
+
+void NGTextCombinePainter::Paint(const PaintInfo& paint_info,
+ const PhysicalOffset& paint_offset,
+ const LayoutNGTextCombine& text_combine) {
+ if (paint_info.phase == PaintPhase::kBlockBackground ||
+ paint_info.phase == PaintPhase::kForcedColorsModeBackplate ||
+ paint_info.phase == PaintPhase::kFloat ||
+ paint_info.phase == PaintPhase::kSelfBlockBackgroundOnly ||
+ paint_info.phase == PaintPhase::kDescendantBlockBackgroundsOnly ||
+ paint_info.phase == PaintPhase::kSelfOutlineOnly) {
+ // Note: We should not paint text decoration and emphasis markr in above
+ // paint phases. Otherwise, text decoration and emphasis mark are painted
+ // multiple time and anti-aliasing is broken.
+ // See virtual/text-antialias/emphasis-combined-text.html
+ return;
+ }
+
+ // Here |paint_info.phases| is one of following:
+ // PaintPhase::kSelectionDragImage
+ // PaintPhase::kTextClip
+ // PaintPhase::kForeground
+ // PaintPhase::kOutline
+ // These values come from |NGBoxFragmentPainter::PaintAllPhasesAtomically()|.
+
+ const ComputedStyle& style = text_combine.Parent()->StyleRef();
+ const bool has_text_decoration =
+ style.TextDecorationsInEffect() != TextDecoration::kNone;
+ const bool has_emphasis_mark =
+ style.GetTextEmphasisMark() != TextEmphasisMark::kNone;
+ DCHECK(has_text_decoration | has_emphasis_mark);
+
+ const PhysicalRect& text_frame_rect =
+ text_combine.ComputeTextFrameRect(paint_offset);
+
+ // To match the logical direction
+ GraphicsContextStateSaver state_saver(paint_info.context);
+ paint_info.context.ConcatCTM(
+ TextPainterBase::Rotation(text_frame_rect, style.GetWritingMode()));
+
+ NGTextCombinePainter text_painter(paint_info.context, style, text_frame_rect);
+ const TextPaintStyle text_style = TextPainterBase::TextPaintingStyle(
+ text_combine.GetDocument(), style, paint_info);
+
+ if (has_emphasis_mark) {
+ text_painter.PaintEmphasisMark(text_style,
+ text_combine.Parent()->StyleRef().GetFont());
+ }
+
+ if (has_text_decoration)
+ text_painter.PaintDecorations(paint_info, text_style);
+}
+
+// static
+bool NGTextCombinePainter::ShouldPaint(
+ const LayoutNGTextCombine& text_combine) {
+ const auto& style = text_combine.Parent()->StyleRef();
+ return style.TextDecorationsInEffect() != TextDecoration::kNone ||
+ style.GetTextEmphasisMark() != TextEmphasisMark::kNone;
+}
+
+void NGTextCombinePainter::ClipDecorationsStripe(float upper,
+ float stripe_width,
+ float dilation) {
+ // Nothing to do.
+}
+
+void NGTextCombinePainter::PaintDecorations(const PaintInfo& paint_info,
+ const TextPaintStyle& text_style) {
+ // Setup arguments for painting text decorations
+ const absl::optional<AppliedTextDecoration> selection_text_decoration;
+ const ComputedStyle* const decorating_box_style = nullptr;
+ TextDecorationInfo decoration_info(
+ text_frame_rect_.offset, text_frame_rect_.size.width,
+ style_.GetFontBaseline(), style_, selection_text_decoration,
+ decorating_box_style);
+
+ const NGTextDecorationOffset decoration_offset(style_, style_, nullptr);
+ const auto& applied_text_decorations = style_.AppliedTextDecorations();
+
+ // Paint text decorations except line through
+ bool has_line_through_decoration = false;
+ PaintDecorationsExceptLineThrough(decoration_offset, decoration_info,
+ paint_info, applied_text_decorations,
+ text_style, &has_line_through_decoration);
+ if (!has_line_through_decoration)
+ return;
+
+ // Paint line through
+ PaintDecorationsOnlyLineThrough(decoration_info, paint_info,
+ applied_text_decorations, text_style);
+}
+
+void NGTextCombinePainter::PaintEmphasisMark(const TextPaintStyle& text_style,
+ const Font& emphasis_mark_font) {
+ DCHECK_NE(style_.GetTextEmphasisMark(), TextEmphasisMark::kNone);
+ SetEmphasisMark(style_.TextEmphasisMarkString(),
+ style_.GetTextEmphasisPosition());
+ PaintEmphasisMarkForCombinedText(text_style, emphasis_mark_font);
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/core/paint/ng/ng_text_combine_painter.h b/chromium/third_party/blink/renderer/core/paint/ng/ng_text_combine_painter.h
new file mode 100644
index 00000000000..479fae900eb
--- /dev/null
+++ b/chromium/third_party/blink/renderer/core/paint/ng/ng_text_combine_painter.h
@@ -0,0 +1,46 @@
+// Copyright 2021 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_TEXT_COMBINE_PAINTER_H_
+#define THIRD_PARTY_BLINK_RENDERER_CORE_PAINT_NG_NG_TEXT_COMBINE_PAINTER_H_
+
+#include "third_party/blink/renderer/core/paint/text_painter_base.h"
+#include "third_party/blink/renderer/platform/graphics/dom_node_id.h"
+
+namespace blink {
+
+class ComputedStyle;
+class LayoutNGTextCombine;
+
+// The painter for painting text decorations and emphasis marks for
+// LayoutNGTextCombine.
+class NGTextCombinePainter final : public TextPainterBase {
+ public:
+ NGTextCombinePainter(GraphicsContext& context,
+ const ComputedStyle& style,
+ const PhysicalRect& text_frame_rect);
+ ~NGTextCombinePainter();
+
+ static void Paint(const PaintInfo& paint_info,
+ const PhysicalOffset& paint_offset,
+ const LayoutNGTextCombine& text_combine);
+
+ static bool ShouldPaint(const LayoutNGTextCombine& text_combine);
+
+ private:
+ void ClipDecorationsStripe(float upper,
+ float stripe_width,
+ float dilation) override;
+
+ void PaintDecorations(const PaintInfo& paint_info,
+ const TextPaintStyle& text_style);
+ void PaintEmphasisMark(const TextPaintStyle& text_style,
+ const Font& emphasis_mark_font);
+
+ const ComputedStyle& style_;
+};
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_CORE_PAINT_NG_NG_TEXT_COMBINE_PAINTER_H_
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 00f418a60e3..ed7e78071b1 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
@@ -13,7 +13,9 @@
#include "third_party/blink/renderer/core/frame/local_frame.h"
#include "third_party/blink/renderer/core/layout/geometry/logical_rect.h"
#include "third_party/blink/renderer/core/layout/layout_ruby_run.h"
+#include "third_party/blink/renderer/core/layout/layout_ruby_text.h"
#include "third_party/blink/renderer/core/layout/list_marker.h"
+#include "third_party/blink/renderer/core/layout/ng/inline/layout_ng_text_combine.h"
#include "third_party/blink/renderer/core/layout/ng/inline/ng_inline_cursor.h"
#include "third_party/blink/renderer/core/layout/ng/inline/ng_offset_mapping.h"
#include "third_party/blink/renderer/core/layout/ng/ng_physical_box_fragment.h"
@@ -56,7 +58,7 @@ inline PhysicalRect ComputeBoxRect(const NGInlineCursor& cursor,
const PhysicalOffset& paint_offset,
const PhysicalOffset& parent_offset) {
PhysicalRect box_rect;
- if (const auto* svg_data = cursor.CurrentItem()->SVGFragmentData())
+ if (const auto* svg_data = cursor.CurrentItem()->SvgFragmentData())
box_rect = PhysicalRect::FastAndLossyFromFloatRect(svg_data->rect);
else
box_rect = cursor.CurrentItem()->RectInContainerFragment();
@@ -89,6 +91,9 @@ bool ShouldPaintEmphasisMark(const ComputedStyle& style,
const LayoutObject& layout_object) {
if (style.GetTextEmphasisMark() == TextEmphasisMark::kNone)
return false;
+ // Note: We set text-emphasis-style:none for combined text and we paint
+ // emphasis mark at left/right side of |LayoutNGTextCombine|.
+ DCHECK(!IsA<LayoutNGTextCombine>(layout_object.Parent()));
const LayoutObject* containing_block = layout_object.ContainingBlock();
if (!containing_block || !containing_block->IsRubyBase())
return true;
@@ -144,16 +149,32 @@ void NGTextFragmentPainter::Paint(const PaintInfo& paint_info,
const LayoutObject* layout_object = text_item.GetLayoutObject();
const Document& document = layout_object->GetDocument();
const bool is_printing = document.Printing();
+ // Don't paint selections when rendering a mask, clip-path (as a mask),
+ // pattern or feImage (element reference.)
+ const bool is_rendering_resource = paint_info.IsRenderingResourceSubtree();
+ const auto* const text_combine =
+ DynamicTo<LayoutNGTextCombine>(layout_object->Parent());
+#if DCHECK_IS_ON()
+ if (UNLIKELY(text_combine))
+ LayoutNGTextCombine::AssertStyleIsValid(style);
+#endif
// Determine whether or not we're selected.
- absl::optional<NGHighlightPainter::SelectionPaintState> selection;
- if (UNLIKELY(!is_printing && paint_info.phase != PaintPhase::kTextClip &&
+ NGHighlightPainter::SelectionPaintState* selection = nullptr;
+ absl::optional<NGHighlightPainter::SelectionPaintState>
+ selection_for_bounds_recording;
+ if (UNLIKELY(!is_printing && !is_rendering_resource &&
+ 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();
+
+ // Empty selections might be the boundary of the document selection, and
+ // thus need to get recorded. We only need to paint the selection if it
+ // has a valid range.
+ selection_for_bounds_recording.emplace(root_inline_cursor);
+ if (selection_for_bounds_recording->Status().HasValidRange())
+ selection = &selection_for_bounds_recording.value();
}
if (!selection) {
// When only painting the selection drag image, don't bother to paint if
@@ -167,54 +188,64 @@ void NGTextFragmentPainter::Paint(const PaintInfo& paint_info,
}
PhysicalRect box_rect = ComputeBoxRect(cursor_, paint_offset, parent_offset_);
+ if (UNLIKELY(text_combine)) {
+ box_rect.offset.left =
+ text_combine->AdjustTextLeftForPaint(box_rect.offset.left);
+ }
+
IntRect visual_rect;
- const LayoutSVGInlineText* svg_inline_text = nullptr;
+ const auto* const svg_inline_text =
+ DynamicTo<LayoutSVGInlineText>(layout_object);
float scaling_factor = 1.0f;
- if (text_item.Type() == NGFragmentItem::kSVGText) {
- svg_inline_text = To<LayoutSVGInlineText>(layout_object);
+ if (UNLIKELY(svg_inline_text)) {
+ DCHECK_EQ(text_item.Type(), NGFragmentItem::kSvgText);
scaling_factor = svg_inline_text->ScalingFactor();
DCHECK_NE(scaling_factor, 0.0f);
visual_rect = EnclosingIntRect(
svg_inline_text->Parent()->VisualRectInLocalSVGCoordinates());
} else {
+ DCHECK_NE(text_item.Type(), NGFragmentItem::kSvgText);
PhysicalRect ink_overflow = text_item.SelfInkOverflow();
ink_overflow.Move(box_rect.offset);
visual_rect = EnclosingIntRect(ink_overflow);
}
- // The text clip phase already has a DrawingRecorder. Text clips are initiated
- // only in BoxPainterBase::PaintFillLayer, which is already within a
- // DrawingRecorder.
- absl::optional<DrawingRecorder> recorder;
- const auto& display_item_client =
- AsDisplayItemClient(cursor_, selection.has_value());
-
// Ensure the selection bounds are recorded on the paint chunk regardless of
- // whether the diplay item that contains the actual selection painting is
+ // whether the display item that contains the actual selection painting is
// reused.
absl::optional<SelectionBoundsRecorder> selection_recorder;
- if (UNLIKELY(selection && paint_info.phase == PaintPhase::kForeground &&
- !is_printing)) {
+ if (UNLIKELY(selection_for_bounds_recording &&
+ paint_info.phase == PaintPhase::kForeground && !is_printing)) {
if (SelectionBoundsRecorder::ShouldRecordSelection(
cursor_.Current().GetLayoutObject()->GetFrame()->Selection(),
- selection->State())) {
+ selection_for_bounds_recording->State())) {
PhysicalRect selection_rect =
- selection->ComputeSelectionRect(box_rect.offset);
- selection_recorder.emplace(selection->State(), selection_rect,
- paint_info.context.GetPaintController(),
- cursor_.Current().ResolvedDirection(),
- style.GetWritingMode(),
- *cursor_.Current().GetLayoutObject());
+ selection_for_bounds_recording->ComputeSelectionRect(box_rect.offset);
+ selection_recorder.emplace(
+ selection_for_bounds_recording->State(), selection_rect,
+ paint_info.context.GetPaintController(),
+ cursor_.Current().ResolvedDirection(), style.GetWritingMode(),
+ *cursor_.Current().GetLayoutObject());
}
}
+ // This is declared after selection_recorder so that this will be destructed
+ // before selection_recorder to ensure the selection is painted before
+ // selection_recorder records the selection bounds.
+ absl::optional<DrawingRecorder> recorder;
+ const auto& display_item_client =
+ AsDisplayItemClient(cursor_, selection != nullptr);
+ // Text clips are initiated only in BoxPainterBase::PaintFillLayer, which is
+ // already within a DrawingRecorder.
if (paint_info.phase != PaintPhase::kTextClip) {
- if (DrawingRecorder::UseCachedDrawingIfPossible(
- paint_info.context, display_item_client, paint_info.phase)) {
- return;
+ if (LIKELY(!paint_info.context.InDrawingRecorder())) {
+ if (DrawingRecorder::UseCachedDrawingIfPossible(
+ paint_info.context, display_item_client, paint_info.phase)) {
+ return;
+ }
+ recorder.emplace(paint_info.context, display_item_client,
+ paint_info.phase, visual_rect);
}
- recorder.emplace(paint_info.context, display_item_client, paint_info.phase,
- visual_rect);
}
if (UNLIKELY(text_item.IsSymbolMarker())) {
@@ -238,50 +269,62 @@ void NGTextFragmentPainter::Paint(const PaintInfo& paint_info,
// Determine text colors.
Node* node = layout_object->GetNode();
- DCHECK(!svg_inline_text ||
- (!IsA<SVGElement>(node) && IsA<SVGElement>(node->parentNode())));
TextPaintStyle text_style =
- svg_inline_text
- ? TextPainterBase::SvgTextPaintingStyle(
- document, SVGLengthContext(To<SVGElement>(node->parentNode())),
- style, paint_info)
- : TextPainterBase::TextPaintingStyle(document, style, paint_info);
- // TODO(crbug.com/1179585): Support SVG Paint Servers (e.g. Gradient, Pattern)
+ TextPainterBase::TextPaintingStyle(document, style, paint_info);
if (UNLIKELY(selection)) {
selection->ComputeSelectionStyle(document, style, node, paint_info,
text_style);
}
// Set our font.
- const Font& font =
- svg_inline_text ? svg_inline_text->ScaledFont() : style.GetFont();
+ const Font& font = UNLIKELY(svg_inline_text)
+ ? svg_inline_text->ScaledFont()
+ : UNLIKELY(text_combine)
+ ? text_combine->UsesCompressedFont()
+ ? text_combine->CompressedFont()
+ : style.GetFont()
+ : style.GetFont();
const SimpleFontData* font_data = font.PrimaryFont();
DCHECK(font_data);
const bool paint_marker_backgrounds =
paint_info.phase != PaintPhase::kSelectionDragImage &&
paint_info.phase != PaintPhase::kTextClip && !is_printing;
- absl::optional<GraphicsContextStateSaver> state_saver;
+ GraphicsContextStateSaver state_saver(context, /*save_and_restore=*/false);
absl::optional<AffineTransform> rotation;
const WritingMode writing_mode = style.GetWritingMode();
const bool is_horizontal = IsHorizontalWritingMode(writing_mode);
- int ascent = font_data ? font_data->GetFontMetrics().Ascent() : 0;
- PhysicalOffset text_origin(box_rect.offset.left,
- box_rect.offset.top + ascent);
- if (svg_inline_text && scaling_factor != 1.0f) {
- state_saver.emplace(context);
- context.Scale(1 / scaling_factor, 1 / scaling_factor);
- }
- if (text_item.HasSVGTransformForPaint()) {
- if (!state_saver)
- state_saver.emplace(context);
- context.ConcatCTM(text_item.BuildSVGTransformForPaint());
- }
+ const int ascent = font_data ? font_data->GetFontMetrics().Ascent() : 0;
+ PhysicalOffset text_origin(
+ box_rect.offset.left,
+ UNLIKELY(text_combine)
+ ? text_combine->AdjustTextTopForPaint(box_rect.offset.top)
+ : box_rect.offset.top + ascent);
+
NGTextPainter text_painter(context, font, fragment_paint_info, visual_rect,
text_origin, box_rect, is_horizontal);
- NGHighlightPainter highlight_painter(
- text_painter, paint_info, cursor_, *cursor_.CurrentItem(),
- box_rect.offset, style, std::move(selection), is_printing);
+ NGHighlightPainter highlight_painter(text_painter, paint_info, cursor_,
+ *cursor_.CurrentItem(), box_rect.offset,
+ style, selection, is_printing);
+
+ if (svg_inline_text) {
+ NGTextPainter::SvgTextPaintState& svg_state = text_painter.SetSvgState(
+ *svg_inline_text, style, paint_info.IsRenderingClipPathAsMaskImage());
+
+ if (scaling_factor != 1.0f) {
+ state_saver.SaveIfNeeded();
+ context.Scale(1 / scaling_factor, 1 / scaling_factor);
+ svg_state.EnsureShaderTransform().Scale(scaling_factor);
+ }
+ if (text_item.HasSvgTransformForPaint()) {
+ state_saver.SaveIfNeeded();
+ const auto fragment_transform = text_item.BuildSvgTransformForPaint();
+ context.ConcatCTM(fragment_transform);
+ DCHECK(fragment_transform.IsInvertible());
+ svg_state.EnsureShaderTransform().PreMultiply(
+ fragment_transform.Inverse());
+ }
+ }
// 1. Paint backgrounds for document markers that don’t participate in the CSS
// highlight overlay system, such as composition highlights. They use physical
@@ -289,21 +332,25 @@ void NGTextFragmentPainter::Paint(const PaintInfo& paint_info,
highlight_painter.Paint(NGHighlightPainter::kBackground);
if (!is_horizontal) {
- if (!state_saver)
- state_saver.emplace(context);
+ state_saver.SaveIfNeeded();
// Because we rotate the GraphicsContext to match the logical direction,
// transpose the |box_rect| to match to it.
box_rect.size = PhysicalSize(box_rect.Height(), box_rect.Width());
- rotation.emplace(TextPainterBase::Rotation(
- box_rect, writing_mode != WritingMode::kSidewaysLr
- ? TextPainterBase::kClockwise
- : TextPainterBase::kCounterclockwise));
+ rotation.emplace(TextPainterBase::Rotation(box_rect, writing_mode));
context.ConcatCTM(*rotation);
+ if (NGTextPainter::SvgTextPaintState* state = text_painter.GetSvgState()) {
+ DCHECK(rotation->IsInvertible());
+ state->EnsureShaderTransform().PreMultiply(rotation->Inverse());
+ }
}
if (UNLIKELY(highlight_painter.Selection())) {
PhysicalRect before_rotation =
highlight_painter.Selection()->ComputeSelectionRect(box_rect.offset);
+ if (scaling_factor != 1.0f) {
+ before_rotation.offset.Scale(1 / scaling_factor);
+ before_rotation.size.Scale(1 / scaling_factor);
+ }
// The selection rect is given in physical coordinates, so we need to map
// them into our now-possibly-rotated space before calling any methods
@@ -344,6 +391,7 @@ void NGTextFragmentPainter::Paint(const PaintInfo& paint_info,
&has_line_through_decoration);
text_painter.Paint(start_offset, end_offset, length, text_style, node_id);
if (has_line_through_decoration) {
+ DCHECK(!text_combine);
text_painter.PaintDecorationsOnlyLineThrough(
text_item, paint_info, style, text_style, box_rect, absl::nullopt);
}
@@ -375,6 +423,7 @@ void NGTextFragmentPainter::Paint(const PaintInfo& paint_info,
text_style, node_id);
if (has_line_through_decoration) {
+ DCHECK(!text_combine);
text_painter.PaintDecorationsOnlyLineThrough(
text_item, paint_info, style, text_style, box_rect,
highlight_painter.SelectionDecoration());
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 24047eed809..87af0ca8d3e 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/abseil-cpp/absl/types/optional.h"
#include "third_party/blink/renderer/core/layout/ng/inline/ng_inline_cursor.h"
#include "third_party/blink/renderer/core/style/computed_style_constants.h"
#include "third_party/blink/renderer/platform/geometry/layout_rect.h"
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 89650c6b601..69a63b78781 100644
--- a/chromium/third_party/blink/renderer/core/paint/ng/ng_text_painter.cc
+++ b/chromium/third_party/blink/renderer/core/paint/ng/ng_text_painter.cc
@@ -4,28 +4,114 @@
#include "third_party/blink/renderer/core/paint/ng/ng_text_painter.h"
+#include "base/stl_util.h"
#include "third_party/blink/renderer/core/css/css_property_names.h"
#include "third_party/blink/renderer/core/frame/settings.h"
#include "third_party/blink/renderer/core/layout/ng/inline/ng_fragment_item.h"
#include "third_party/blink/renderer/core/layout/ng/ng_text_decoration_offset.h"
#include "third_party/blink/renderer/core/layout/ng/ng_unpositioned_float.h"
+#include "third_party/blink/renderer/core/layout/svg/layout_svg_inline_text.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/paint/applied_decoration_painter.h"
#include "third_party/blink/renderer/core/paint/box_painter.h"
#include "third_party/blink/renderer/core/paint/paint_info.h"
#include "third_party/blink/renderer/core/paint/paint_timing_detector.h"
+#include "third_party/blink/renderer/core/paint/svg_object_painter.h"
#include "third_party/blink/renderer/core/style/computed_style.h"
#include "third_party/blink/renderer/core/style/shadow_list.h"
+#include "third_party/blink/renderer/core/svg/svg_element.h"
#include "third_party/blink/renderer/platform/fonts/font.h"
#include "third_party/blink/renderer/platform/fonts/shaping/shape_result_view.h"
#include "third_party/blink/renderer/platform/graphics/graphics_context.h"
#include "third_party/blink/renderer/platform/graphics/graphics_context_state_saver.h"
#include "third_party/blink/renderer/platform/graphics/paint/paint_controller.h"
+#include "third_party/blink/renderer/platform/graphics/paint/paint_flags.h"
#include "third_party/blink/renderer/platform/wtf/text/character_names.h"
namespace blink {
namespace {
+class SelectionStyleScope {
+ STACK_ALLOCATED();
+
+ public:
+ SelectionStyleScope(LayoutObject&,
+ const ComputedStyle& style,
+ const ComputedStyle& selection_style);
+ SelectionStyleScope(const SelectionStyleScope&) = delete;
+ SelectionStyleScope& operator=(const SelectionStyleScope) = delete;
+ ~SelectionStyleScope();
+
+ private:
+ LayoutObject& layout_object_;
+ const ComputedStyle& selection_style_;
+ const bool styles_are_equal_;
+};
+
+SelectionStyleScope::SelectionStyleScope(LayoutObject& layout_object,
+ const ComputedStyle& style,
+ const ComputedStyle& selection_style)
+ : layout_object_(layout_object),
+ selection_style_(selection_style),
+ styles_are_equal_(style == selection_style) {
+ if (styles_are_equal_)
+ return;
+ DCHECK(!layout_object.IsSVGInlineText());
+ auto& element = To<SVGElement>(*layout_object_.GetNode());
+ SVGResources::UpdatePaints(element, nullptr, selection_style_);
+}
+
+SelectionStyleScope::~SelectionStyleScope() {
+ if (styles_are_equal_)
+ return;
+ auto& element = To<SVGElement>(*layout_object_.GetNode());
+ SVGResources::ClearPaints(element, &selection_style_);
+}
+
+bool SetupPaintForSvgText(const LayoutSVGInlineText& svg_inline_text,
+ const GraphicsContext& context,
+ bool is_rendering_clip_path_as_mask_image,
+ const ComputedStyle& style,
+ const AffineTransform* shader_transform,
+ LayoutSVGResourceMode resource_mode,
+ PaintFlags& flags) {
+ const LayoutObject* layout_parent = svg_inline_text.Parent();
+ if (!SVGObjectPainter(*layout_parent)
+ .PreparePaint(context, is_rendering_clip_path_as_mask_image, style,
+ resource_mode, flags, shader_transform)) {
+ return false;
+ }
+
+ flags.setAntiAlias(true);
+
+ if (style.TextShadow() &&
+ // Text shadows are disabled when printing. http://crbug.com/258321
+ !svg_inline_text.GetDocument().Printing()) {
+ flags.setLooper(TextPainterBase::CreateDrawLooper(
+ style.TextShadow(), DrawLooperBuilder::kShadowRespectsAlpha,
+ style.VisitedDependentColor(GetCSSPropertyColor()),
+ style.UsedColorScheme()));
+ }
+
+ if (resource_mode == kApplyToStrokeMode) {
+ // The stroke geometry needs be generated based on the scaled font.
+ float stroke_scale_factor =
+ style.VectorEffect() != EVectorEffect::kNonScalingStroke
+ ? svg_inline_text.ScalingFactor()
+ : 1;
+ StrokeData stroke_data;
+ SVGLayoutSupport::ApplyStrokeStyleToStrokeData(
+ stroke_data, style, *layout_parent, stroke_scale_factor);
+ if (stroke_scale_factor != 1)
+ stroke_data.SetThickness(stroke_data.Thickness() * stroke_scale_factor);
+ stroke_data.SetupPaint(&flags);
+ }
+
+ return true;
+}
+
absl::optional<TextDecorationInfo> DecorationsForLayer(
const NGFragmentItem& text_item,
const PhysicalRect& decoration_rect,
@@ -37,9 +123,9 @@ absl::optional<TextDecorationInfo> DecorationsForLayer(
text_item.IsEllipsis()) {
return absl::nullopt;
}
- return TextDecorationInfo(decoration_rect.offset, decoration_rect.offset,
- decoration_rect.Width(), style.GetFontBaseline(),
- style, selection_text_decoration, nullptr);
+ return TextDecorationInfo(decoration_rect.offset, decoration_rect.Width(),
+ style.GetFontBaseline(), style,
+ selection_text_decoration, nullptr);
}
} // namespace
@@ -87,6 +173,11 @@ void NGTextPainter::PaintSelectedText(unsigned start_offset,
// painting in most small text.
snapped_selection_rect.Inflate(1);
if (snapped_selection_rect.Contains(visual_rect_)) {
+ absl::optional<base::AutoReset<bool>> is_painting_selection_reset;
+ if (svg_text_paint_state_.has_value()) {
+ is_painting_selection_reset.emplace(
+ &svg_text_paint_state_->is_painting_selection_, true);
+ }
Paint(start_offset, end_offset, length, selection_style, node_id);
return;
}
@@ -109,6 +200,11 @@ void NGTextPainter::PaintSelectedText(unsigned start_offset,
}
// Then draw the glyphs inside the selection area, with the selection style.
{
+ absl::optional<base::AutoReset<bool>> is_painting_selection_reset;
+ if (svg_text_paint_state_.has_value()) {
+ is_painting_selection_reset.emplace(
+ &svg_text_paint_state_->is_painting_selection_, true);
+ }
GraphicsContextStateSaver state_saver(graphics_context_);
graphics_context_.Clip(float_selection_rect);
Paint(start_offset, end_offset, length, selection_style, node_id);
@@ -136,86 +232,9 @@ void NGTextPainter::PaintDecorationsExceptLineThrough(
const NGTextDecorationOffset decoration_offset(decoration_info->Style(),
text_item.Style(), nullptr);
- GraphicsContext& context = paint_info.context;
- GraphicsContextStateSaver state_saver(context);
- UpdateGraphicsContext(context, text_style, horizontal_, state_saver);
-
- if (has_combined_text_)
- context.ConcatCTM(Rotation(text_frame_rect_, kClockwise));
-
- // text-underline-position may flip underline and overline.
- ResolvedUnderlinePosition underline_position =
- decoration_info->UnderlinePosition();
- bool flip_underline_and_overline = false;
- if (underline_position == ResolvedUnderlinePosition::kOver) {
- flip_underline_and_overline = true;
- underline_position = ResolvedUnderlinePosition::kUnder;
- }
-
- const Vector<AppliedTextDecoration>& decorations =
- style.AppliedTextDecorations();
- for (size_t applied_decoration_index = 0;
- applied_decoration_index < decorations.size();
- ++applied_decoration_index) {
- const AppliedTextDecoration& decoration =
- decorations[applied_decoration_index];
- TextDecoration lines = decoration.Lines();
- bool has_underline = EnumHasFlags(lines, TextDecoration::kUnderline);
- bool has_overline = EnumHasFlags(lines, TextDecoration::kOverline);
- if (flip_underline_and_overline)
- std::swap(has_underline, has_overline);
-
- decoration_info->SetDecorationIndex(applied_decoration_index);
-
- float resolved_thickness = decoration_info->ResolvedThickness();
- context.SetStrokeThickness(resolved_thickness);
-
- if (has_underline && decoration_info->FontData()) {
- // Don't apply text-underline-offset to overline.
- Length line_offset =
- flip_underline_and_overline ? Length() : decoration.UnderlineOffset();
-
- const int paint_underline_offset =
- decoration_offset.ComputeUnderlineOffset(
- underline_position, decoration_info->Style().ComputedFontSize(),
- decoration_info->FontData()->GetFontMetrics(), line_offset,
- resolved_thickness);
- decoration_info->SetPerLineData(
- TextDecoration::kUnderline, paint_underline_offset,
- TextDecorationInfo::DoubleOffsetFromThickness(resolved_thickness), 1);
- PaintDecorationUnderOrOverLine(context, *decoration_info,
- TextDecoration::kUnderline);
- }
-
- if (has_overline && decoration_info->FontData()) {
- // Don't apply text-underline-offset to overline.
- Length line_offset =
- flip_underline_and_overline ? decoration.UnderlineOffset() : Length();
-
- FontVerticalPositionType position =
- flip_underline_and_overline ? FontVerticalPositionType::TopOfEmHeight
- : FontVerticalPositionType::TextTop;
- const int paint_overline_offset =
- decoration_offset.ComputeUnderlineOffsetForUnder(
- line_offset, decoration_info->Style().ComputedFontSize(),
- resolved_thickness, position);
- decoration_info->SetPerLineData(
- TextDecoration::kOverline, paint_overline_offset,
- -TextDecorationInfo::DoubleOffsetFromThickness(resolved_thickness),
- 1);
- PaintDecorationUnderOrOverLine(context, *decoration_info,
- TextDecoration::kOverline);
- }
-
- // We could instead build a vector of the TextDecoration instances needing
- // line-through but this is a rare case so better to avoid vector overhead.
- *has_line_through_decoration |=
- EnumHasFlags(lines, TextDecoration::kLineThrough);
- }
-
- // Restore rotation as needed.
- if (has_combined_text_)
- context.ConcatCTM(Rotation(text_frame_rect_, kCounterclockwise));
+ TextPainterBase::PaintDecorationsExceptLineThrough(
+ decoration_offset, *decoration_info, paint_info,
+ style.AppliedTextDecorations(), text_style, has_line_through_decoration);
}
// Based on legacy TextPainter.
@@ -231,55 +250,8 @@ void NGTextPainter::PaintDecorationsOnlyLineThrough(
DCHECK(decoration_info);
- const NGTextDecorationOffset decoration_offset(decoration_info->Style(),
- text_item.Style(), nullptr);
-
- GraphicsContext& context = paint_info.context;
- GraphicsContextStateSaver state_saver(context);
- UpdateGraphicsContext(context, text_style, horizontal_, state_saver);
-
- if (has_combined_text_)
- context.ConcatCTM(Rotation(text_frame_rect_, kClockwise));
-
- const Vector<AppliedTextDecoration>& decorations =
- style.AppliedTextDecorations();
- for (size_t applied_decoration_index = 0;
- applied_decoration_index < decorations.size();
- ++applied_decoration_index) {
- const AppliedTextDecoration& decoration =
- decorations[applied_decoration_index];
- TextDecoration lines = decoration.Lines();
- if (EnumHasFlags(lines, TextDecoration::kLineThrough)) {
- decoration_info->SetDecorationIndex(applied_decoration_index);
-
- float resolved_thickness = decoration_info->ResolvedThickness();
- context.SetStrokeThickness(resolved_thickness);
-
- // For increased line thickness, the line-through decoration needs to grow
- // in both directions from its origin, subtract half the thickness to keep
- // it centered at the same origin.
- const float line_through_offset =
- 2 * decoration_info->Baseline() / 3 - resolved_thickness / 2;
- // Floor double_offset in order to avoid double-line gap to appear
- // of different size depending on position where the double line
- // is drawn because of rounding downstream in
- // GraphicsContext::DrawLineForText.
- decoration_info->SetPerLineData(
- TextDecoration::kLineThrough, line_through_offset,
- floorf(TextDecorationInfo::DoubleOffsetFromThickness(
- resolved_thickness)),
- 0);
- AppliedDecorationPainter decoration_painter(context, *decoration_info,
- TextDecoration::kLineThrough);
- // No skip: ink for line-through,
- // compare https://github.com/w3c/csswg-drafts/issues/711
- decoration_painter.Paint();
- }
- }
-
- // Restore rotation as needed.
- if (has_combined_text_)
- context.ConcatCTM(Rotation(text_frame_rect_, kCounterclockwise));
+ TextPainterBase::PaintDecorationsOnlyLineThrough(
+ *decoration_info, paint_info, style.AppliedTextDecorations(), text_style);
}
template <NGTextPainter::PaintInternalStep step>
@@ -299,8 +271,12 @@ void NGTextPainter::PaintInternalFragment(
FloatPoint(text_origin_) + IntSize(0, emphasis_mark_offset_));
} else {
DCHECK(step == kPaintText);
- graphics_context_.DrawText(font_, fragment_paint_info_,
- FloatPoint(text_origin_), node_id);
+ if (svg_text_paint_state_.has_value()) {
+ PaintSvgTextFragment(node_id);
+ } else {
+ graphics_context_.DrawText(font_, fragment_paint_info_,
+ FloatPoint(text_origin_), node_id);
+ }
// TODO(npm): Check that there are non-whitespace characters. See
// crbug.com/788444.
graphics_context_.GetPaintController().SetTextPainted();
@@ -347,6 +323,113 @@ void NGTextPainter::ClipDecorationsStripe(float upper,
DecorationsStripeIntercepts(upper, stripe_width, dilation, text_intercepts);
}
-void NGTextPainter::PaintEmphasisMarkForCombinedText() {}
+void NGTextPainter::PaintSvgTextFragment(DOMNodeId node_id) {
+ const NGTextPainter::SvgTextPaintState& state = *svg_text_paint_state_;
+ absl::optional<SelectionStyleScope> selection_style_scope;
+ bool has_fill = state.Style().HasFill();
+ bool has_visible_stroke = state.Style().HasVisibleStroke();
+ const ComputedStyle* style_to_paint = &state.Style();
+ if (state.IsPaintingSelection()) {
+ LayoutObject* layout_parent = state.InlineText().Parent();
+ style_to_paint =
+ layout_parent->GetCachedPseudoElementStyle(kPseudoIdSelection);
+ if (style_to_paint) {
+ if (!has_fill)
+ has_fill = style_to_paint->HasFill();
+ if (!has_visible_stroke)
+ has_visible_stroke = style_to_paint->HasVisibleStroke();
+ } else {
+ style_to_paint = &state.Style();
+ }
+
+ selection_style_scope.emplace(*layout_parent, state.Style(),
+ *style_to_paint);
+ }
+
+ if (state.IsRenderingClipPathAsMaskImage()) {
+ has_fill = true;
+ has_visible_stroke = false;
+ }
+
+ for (int i = 0; i < 3; i++) {
+ absl::optional<LayoutSVGResourceMode> resource_mode;
+
+ switch (state.Style().PaintOrderType(i)) {
+ case PT_FILL:
+ if (has_fill)
+ resource_mode = kApplyToFillMode;
+ break;
+ case PT_STROKE:
+ if (has_visible_stroke)
+ resource_mode = kApplyToStrokeMode;
+ break;
+ case PT_MARKERS:
+ // Markers don't apply to text
+ break;
+ default:
+ NOTREACHED();
+ break;
+ }
+
+ if (resource_mode) {
+ PaintFlags flags;
+ if (SetupPaintForSvgText(state.InlineText(), graphics_context_,
+ state.IsRenderingClipPathAsMaskImage(),
+ *style_to_paint, state.GetShaderTransform(),
+ *resource_mode, flags)) {
+ graphics_context_.DrawText(font_, fragment_paint_info_,
+ FloatPoint(text_origin_), flags, node_id);
+ }
+ }
+ }
+}
+
+NGTextPainter::SvgTextPaintState& NGTextPainter::SetSvgState(
+ const LayoutSVGInlineText& svg_inline_text,
+ const ComputedStyle& style,
+ bool is_rendering_clip_path_as_mask_image) {
+ return svg_text_paint_state_.emplace(svg_inline_text, style,
+ is_rendering_clip_path_as_mask_image);
+}
+
+NGTextPainter::SvgTextPaintState* NGTextPainter::GetSvgState() {
+ return base::OptionalOrNullptr(svg_text_paint_state_);
+}
+
+NGTextPainter::SvgTextPaintState::SvgTextPaintState(
+ const LayoutSVGInlineText& layout_svg_inline_text,
+ const ComputedStyle& style,
+ bool is_rendering_clip_path_as_mask_image)
+ : layout_svg_inline_text_(layout_svg_inline_text),
+ style_(style),
+ is_rendering_clip_path_as_mask_image_(
+ is_rendering_clip_path_as_mask_image) {}
+
+const LayoutSVGInlineText& NGTextPainter::SvgTextPaintState::InlineText()
+ const {
+ return layout_svg_inline_text_;
+}
+
+const ComputedStyle& NGTextPainter::SvgTextPaintState::Style() const {
+ return style_;
+}
+
+bool NGTextPainter::SvgTextPaintState::IsPaintingSelection() const {
+ return is_painting_selection_;
+}
+
+bool NGTextPainter::SvgTextPaintState::IsRenderingClipPathAsMaskImage() const {
+ return is_rendering_clip_path_as_mask_image_;
+}
+
+AffineTransform& NGTextPainter::SvgTextPaintState::EnsureShaderTransform() {
+ return shader_transform_ ? shader_transform_.value()
+ : shader_transform_.emplace();
+}
+
+const AffineTransform* NGTextPainter::SvgTextPaintState::GetShaderTransform()
+ const {
+ return base::OptionalOrNullptr(shader_transform_);
+}
} // namespace blink
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 42e8bc6ad2a..26b4bb67899 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
@@ -12,6 +12,7 @@
namespace blink {
+class LayoutSVGInlineText;
class NGFragmentItem;
struct NGTextFragmentPaintInfo;
@@ -24,6 +25,29 @@ class CORE_EXPORT NGTextPainter : public TextPainterBase {
STACK_ALLOCATED();
public:
+ class SvgTextPaintState final {
+ public:
+ SvgTextPaintState(const LayoutSVGInlineText&,
+ const ComputedStyle&,
+ bool is_rendering_clip_path_as_mask_image);
+
+ const LayoutSVGInlineText& InlineText() const;
+ const ComputedStyle& Style() const;
+ bool IsPaintingSelection() const;
+ bool IsRenderingClipPathAsMaskImage() const;
+
+ AffineTransform& EnsureShaderTransform();
+ const AffineTransform* GetShaderTransform() const;
+
+ private:
+ const LayoutSVGInlineText& layout_svg_inline_text_;
+ const ComputedStyle& style_;
+ absl::optional<AffineTransform> shader_transform_;
+ bool is_painting_selection_ = false;
+ bool is_rendering_clip_path_as_mask_image_ = false;
+ friend class NGTextPainter;
+ };
+
NGTextPainter(GraphicsContext& context,
const Font& font,
const NGTextFragmentPaintInfo& fragment_paint_info,
@@ -58,7 +82,6 @@ class CORE_EXPORT NGTextPainter : public TextPainterBase {
const PhysicalRect& selection_rect,
DOMNodeId node_id);
- // Based on legacy TextPainter.
void PaintDecorationsExceptLineThrough(
const NGFragmentItem& text_item,
const PaintInfo& paint_info,
@@ -68,7 +91,6 @@ class CORE_EXPORT NGTextPainter : public TextPainterBase {
const absl::optional<AppliedTextDecoration>& selection_decoration,
bool* has_line_through_decoration);
- // Based on legacy TextPainter.
void PaintDecorationsOnlyLineThrough(
const NGFragmentItem& text_item,
const PaintInfo& paint_info,
@@ -77,6 +99,11 @@ class CORE_EXPORT NGTextPainter : public TextPainterBase {
const PhysicalRect& decoration_rect,
const absl::optional<AppliedTextDecoration>& selection_decoration);
+ SvgTextPaintState& SetSvgState(const LayoutSVGInlineText&,
+ const ComputedStyle&,
+ bool is_rendering_clip_path_as_mask_image);
+ SvgTextPaintState* GetSvgState();
+
private:
template <PaintInternalStep step>
void PaintInternalFragment(unsigned from, unsigned to, DOMNodeId node_id);
@@ -87,10 +114,11 @@ class CORE_EXPORT NGTextPainter : public TextPainterBase {
unsigned truncation_point,
DOMNodeId node_id);
- void PaintEmphasisMarkForCombinedText();
+ void PaintSvgTextFragment(DOMNodeId node_id);
NGTextFragmentPaintInfo fragment_paint_info_;
const IntRect& visual_rect_;
+ absl::optional<SvgTextPaintState> svg_text_paint_state_;
};
} // namespace blink
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 292c087dcb2..d6637d60483 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
@@ -40,9 +40,7 @@ struct TileParameters {
absl::optional<TileParameters> ComputeTileParameters(
ENinePieceImageRule tile_rule,
- float dst_pos,
float dst_extent,
- float src_pos,
float src_extent,
float in_scale_factor) {
switch (tile_rule) {
@@ -50,14 +48,13 @@ absl::optional<TileParameters> ComputeTileParameters(
float repetitions =
std::max(1.0f, roundf(dst_extent / (src_extent * in_scale_factor)));
float scale_factor = dst_extent / (src_extent * repetitions);
- return TileParameters{scale_factor, src_pos * scale_factor, 0};
+ return TileParameters{scale_factor, 0, 0};
}
case kRepeatImageRule: {
float scaled_tile_extent = src_extent * in_scale_factor;
// We want to construct the phase such that the pattern is centered (when
// stretch is not set for a particular rule).
- float phase = src_pos * in_scale_factor;
- phase -= (dst_extent - scaled_tile_extent) / 2;
+ float phase = (dst_extent - scaled_tile_extent) / 2;
return TileParameters{in_scale_factor, phase, 0};
}
case kSpaceImageRule: {
@@ -65,10 +62,10 @@ absl::optional<TileParameters> ComputeTileParameters(
CalculateSpaceNeeded(dst_extent, src_extent);
if (!spacing)
return absl::nullopt;
- return TileParameters{1, src_pos - *spacing, *spacing};
+ return TileParameters{1, *spacing, *spacing};
}
case kStretchImageRule:
- return TileParameters{in_scale_factor, src_pos * in_scale_factor, 0};
+ return TileParameters{in_scale_factor, 0, 0};
default:
NOTREACHED();
}
@@ -124,35 +121,41 @@ void PaintPieces(GraphicsContext& context,
// Since there is no way for the developer to specify decode behavior,
// use kSync by default.
context.DrawImage(image, Image::kSyncDecode, draw_info.destination,
- &draw_info.source, style.HasFilterInducingProperty());
+ &draw_info.source, style.DisableForceDark());
continue;
}
// TODO(cavalcantii): see crbug.com/662513.
absl::optional<TileParameters> h_tile = ComputeTileParameters(
- draw_info.tile_rule.horizontal, draw_info.destination.X(),
- draw_info.destination.Width(), draw_info.source.X(),
+ draw_info.tile_rule.horizontal, draw_info.destination.Width(),
draw_info.source.Width(), draw_info.tile_scale.Width());
absl::optional<TileParameters> v_tile = ComputeTileParameters(
- draw_info.tile_rule.vertical, draw_info.destination.Y(),
- draw_info.destination.Height(), draw_info.source.Y(),
+ draw_info.tile_rule.vertical, draw_info.destination.Height(),
draw_info.source.Height(), draw_info.tile_scale.Height());
if (!h_tile || !v_tile)
continue;
- FloatSize tile_scale_factor(h_tile->scale_factor, v_tile->scale_factor);
- FloatPoint tile_phase(draw_info.destination.X() - h_tile->phase,
- draw_info.destination.Y() - v_tile->phase);
- FloatSize tile_spacing(h_tile->spacing, v_tile->spacing);
-
// TODO(cavalcantii): see crbug.com/662507.
absl::optional<ScopedInterpolationQuality> interpolation_quality_override;
if (draw_info.tile_rule.horizontal == kRoundImageRule ||
draw_info.tile_rule.vertical == kRoundImageRule)
interpolation_quality_override.emplace(context, kInterpolationMedium);
- context.DrawImageTiled(image, draw_info.destination, draw_info.source,
- tile_scale_factor, tile_phase, tile_spacing);
+ ImageTilingInfo tiling_info;
+ tiling_info.image_rect = draw_info.source;
+ tiling_info.scale = FloatSize(h_tile->scale_factor, v_tile->scale_factor);
+ // The phase defines the origin of the whole image - not the image
+ // rect (see ImageTilingInfo) - so we need to adjust it to account
+ // for that.
+ FloatPoint tile_origin_in_dest_space = draw_info.source.Location();
+ tile_origin_in_dest_space.Scale(tiling_info.scale.Width(),
+ tiling_info.scale.Height());
+ tiling_info.phase =
+ draw_info.destination.Location() +
+ (FloatPoint(h_tile->phase, v_tile->phase) - tile_origin_in_dest_space);
+ tiling_info.spacing = FloatSize(h_tile->spacing, v_tile->spacing);
+
+ context.DrawImageTiled(image, draw_info.destination, tiling_info);
}
}
@@ -189,9 +192,8 @@ bool NinePieceImagePainter::Paint(GraphicsContext& graphics_context,
// image with either "native" size (raster images) or size scaled by effective
// zoom.
const FloatSize default_object_size(border_image_rect.size);
- FloatSize image_size =
- style_image->ImageSize(document, style.EffectiveZoom(),
- default_object_size, kRespectImageOrientation);
+ FloatSize image_size = style_image->ImageSize(
+ style.EffectiveZoom(), default_object_size, kRespectImageOrientation);
scoped_refptr<Image> image =
style_image->GetImage(observer, document, style, image_size);
if (!image)
@@ -201,7 +203,7 @@ bool NinePieceImagePainter::Paint(GraphicsContext& graphics_context,
// yield the size in CSS pixels. This is the unit/scale we expect the
// 'border-image-slice' values to be in.
FloatSize unzoomed_image_size = style_image->ImageSize(
- document, 1, default_object_size.ScaledBy(1 / style.EffectiveZoom()),
+ 1, default_object_size.ScaledBy(1 / style.EffectiveZoom()),
kRespectImageOrientation);
DEVTOOLS_TIMELINE_TRACE_EVENT_WITH_CATEGORIES(
diff --git a/chromium/third_party/blink/renderer/core/paint/object_paint_invalidator.h b/chromium/third_party/blink/renderer/core/paint/object_paint_invalidator.h
index c4afc37d1ab..bc53051bc14 100644
--- a/chromium/third_party/blink/renderer/core/paint/object_paint_invalidator.h
+++ b/chromium/third_party/blink/renderer/core/paint/object_paint_invalidator.h
@@ -7,7 +7,6 @@
#include "base/auto_reset.h"
#include "base/dcheck_is_on.h"
-#include "base/macros.h"
#include "third_party/blink/renderer/core/core_export.h"
#include "third_party/blink/renderer/platform/graphics/paint/display_item_client.h"
#include "third_party/blink/renderer/platform/graphics/paint_invalidation_reason.h"
diff --git a/chromium/third_party/blink/renderer/core/paint/object_paint_properties.h b/chromium/third_party/blink/renderer/core/paint/object_paint_properties.h
index 7e0d9f6d0b1..a41e8898545 100644
--- a/chromium/third_party/blink/renderer/core/paint/object_paint_properties.h
+++ b/chromium/third_party/blink/renderer/core/paint/object_paint_properties.h
@@ -225,7 +225,8 @@ class CORE_EXPORT ObjectPaintProperties {
public:
#if DCHECK_IS_ON()
- // Used by FindPropertiesNeedingUpdate.h for verifying state doesn't change.
+ // Used by find_properties_needing_update.h for verifying state doesn't
+ // change.
void SetImmutable() const { is_immutable_ = true; }
bool IsImmutable() const { return is_immutable_; }
void SetMutable() const { is_immutable_ = false; }
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 a59fa4d19f7..51893f575e7 100644
--- a/chromium/third_party/blink/renderer/core/paint/object_painter.cc
+++ b/chromium/third_party/blink/renderer/core/paint/object_painter.cc
@@ -8,6 +8,7 @@
#include "third_party/blink/renderer/core/layout/layout_inline.h"
#include "third_party/blink/renderer/core/layout/layout_object.h"
#include "third_party/blink/renderer/core/layout/layout_theme.h"
+#include "third_party/blink/renderer/core/paint/outline_painter.h"
#include "third_party/blink/renderer/core/paint/paint_info.h"
#include "third_party/blink/renderer/core/style/border_edge.h"
#include "third_party/blink/renderer/core/style/computed_style.h"
@@ -50,7 +51,8 @@ void ObjectPainter::PaintOutline(const PaintInfo& paint_info,
visual_rect.Inflate(style_to_use.OutlineOutsetExtent());
DrawingRecorder recorder(paint_info.context, layout_object_, paint_info.phase,
visual_rect);
- PaintOutlineRects(paint_info, outline_rects, style_to_use);
+ OutlinePainter::PaintOutlineRects(paint_info.context, outline_rects,
+ style_to_use);
}
void ObjectPainter::PaintInlineChildrenOutlines(const PaintInfo& paint_info) {
diff --git a/chromium/third_party/blink/renderer/core/paint/object_painter.h b/chromium/third_party/blink/renderer/core/paint/object_painter.h
index 25a0c8b3ccb..5d001b57a13 100644
--- a/chromium/third_party/blink/renderer/core/paint/object_painter.h
+++ b/chromium/third_party/blink/renderer/core/paint/object_painter.h
@@ -5,7 +5,6 @@
#ifndef THIRD_PARTY_BLINK_RENDERER_CORE_PAINT_OBJECT_PAINTER_H_
#define THIRD_PARTY_BLINK_RENDERER_CORE_PAINT_OBJECT_PAINTER_H_
-#include "third_party/blink/renderer/core/paint/object_painter_base.h"
#include "third_party/blink/renderer/core/style/computed_style_constants.h"
#include "third_party/blink/renderer/platform/wtf/allocator/allocator.h"
@@ -15,7 +14,7 @@ class LayoutObject;
struct PaintInfo;
struct PhysicalOffset;
-class ObjectPainter : public ObjectPainterBase {
+class ObjectPainter {
STACK_ALLOCATED();
public:
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
deleted file mode 100644
index 1029ef5e606..00000000000
--- a/chromium/third_party/blink/renderer/core/paint/object_painter_base.cc
+++ /dev/null
@@ -1,666 +0,0 @@
-// Copyright 2014 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/object_painter_base.h"
-
-#include "third_party/abseil-cpp/absl/types/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"
-#include "third_party/blink/renderer/core/style/computed_style.h"
-#include "third_party/blink/renderer/platform/graphics/color.h"
-#include "third_party/blink/renderer/platform/graphics/graphics_context.h"
-#include "third_party/blink/renderer/platform/graphics/graphics_context_state_saver.h"
-#include "third_party/blink/renderer/platform/graphics/skia/skia_utils.h"
-#include "ui/base/ui_base_features.h"
-#include "ui/native_theme/native_theme.h"
-
-namespace blink {
-
-namespace {
-
-struct OutlineEdgeInfo {
- int x1;
- int y1;
- int x2;
- int y2;
- BoxSide side;
-};
-
-// Adjust length of edges if needed. Returns the width of the joint.
-int AdjustJoint(int outline_width,
- OutlineEdgeInfo& edge1,
- OutlineEdgeInfo& edge2) {
- // A clockwise joint:
- // - needs no adjustment of edge length because our edges are along the
- // clockwise outer edge of the outline;
- // - needs a positive adjacent joint width (required by
- // ObjectPainterBase::DrawLineForBoxSide). A counterclockwise joint: - needs
- // to increase the edge length to include the joint; - needs a negative
- // adjacent joint width (required by ObjectPainterBase::DrawLineForBoxSide).
- switch (edge1.side) {
- case BoxSide::kTop:
- switch (edge2.side) {
- case BoxSide::kRight: // Clockwise
- return outline_width;
- case BoxSide::kLeft: // Counterclockwise
- edge1.x2 += outline_width;
- edge2.y2 += outline_width;
- return -outline_width;
- default: // Same side or no joint.
- return 0;
- }
- case BoxSide::kRight:
- switch (edge2.side) {
- case BoxSide::kBottom: // Clockwise
- return outline_width;
- case BoxSide::kTop: // Counterclockwise
- edge1.y2 += outline_width;
- edge2.x1 -= outline_width;
- return -outline_width;
- default: // Same side or no joint.
- return 0;
- }
- case BoxSide::kBottom:
- switch (edge2.side) {
- case BoxSide::kLeft: // Clockwise
- return outline_width;
- case BoxSide::kRight: // Counterclockwise
- edge1.x1 -= outline_width;
- edge2.y1 -= outline_width;
- return -outline_width;
- default: // Same side or no joint.
- return 0;
- }
- case BoxSide::kLeft:
- switch (edge2.side) {
- case BoxSide::kTop: // Clockwise
- return outline_width;
- case BoxSide::kBottom: // Counterclockwise
- edge1.y1 -= outline_width;
- edge2.x2 += outline_width;
- return -outline_width;
- default: // Same side or no joint.
- return 0;
- }
- default:
- NOTREACHED();
- return 0;
- }
-}
-
-void ApplyOutlineOffset(IntRect& rect, int offset) {
- // A negative outline-offset should not cause the rendered outline shape to
- // become smaller than twice the computed value of the outline-width, in each
- // direction separately. See: https://drafts.csswg.org/css-ui/#outline-offset
- rect.InflateX(std::max(offset, -rect.Width() / 2));
- rect.InflateY(std::max(offset, -rect.Height() / 2));
-}
-
-void PaintComplexOutline(GraphicsContext& graphics_context,
- const Vector<IntRect> rects,
- const ComputedStyle& style,
- const Color& color) {
- DCHECK(!style.OutlineStyleIsAuto());
-
- // Construct a clockwise path along the outer edge of the outline.
- SkRegion region;
- uint16_t width = style.OutlineWidthInt();
- int offset = style.OutlineOffsetInt();
- for (auto& r : rects) {
- IntRect rect = r;
- ApplyOutlineOffset(rect, offset);
- rect.Inflate(width);
- region.op(rect, SkRegion::kUnion_Op);
- }
- SkPath path;
- if (!region.getBoundaryPath(&path))
- return;
-
- Vector<OutlineEdgeInfo, 4> edges;
-
- SkPath::RawIter iter(path);
- SkPoint points[4], first_point, last_point;
- wtf_size_t count = 0;
- for (SkPath::Verb verb = iter.next(points); verb != SkPath::kDone_Verb;
- verb = iter.next(points)) {
- // Keep track of the first and last point of each contour (started with
- // kMove_Verb) so we can add the closing-line on kClose_Verb.
- if (verb == SkPath::kMove_Verb) {
- first_point = points[0];
- last_point = first_point; // this gets reset after each line, but we
- // initialize it here
- } else if (verb == SkPath::kClose_Verb) {
- // create an artificial line to close the contour
- verb = SkPath::kLine_Verb;
- points[0] = last_point;
- points[1] = first_point;
- }
- if (verb != SkPath::kLine_Verb)
- continue;
- last_point = points[1];
-
- edges.Grow(++count);
- OutlineEdgeInfo& edge = edges.back();
- edge.x1 = SkScalarTruncToInt(points[0].x());
- edge.y1 = SkScalarTruncToInt(points[0].y());
- edge.x2 = SkScalarTruncToInt(points[1].x());
- edge.y2 = SkScalarTruncToInt(points[1].y());
- if (edge.x1 == edge.x2) {
- if (edge.y1 < edge.y2) {
- edge.x1 -= width;
- edge.side = BoxSide::kRight;
- } else {
- std::swap(edge.y1, edge.y2);
- edge.x2 += width;
- edge.side = BoxSide::kLeft;
- }
- } else {
- DCHECK(edge.y1 == edge.y2);
- if (edge.x1 < edge.x2) {
- edge.y2 += width;
- edge.side = BoxSide::kTop;
- } else {
- std::swap(edge.x1, edge.x2);
- edge.y1 -= width;
- edge.side = BoxSide::kBottom;
- }
- }
- }
-
- if (!count)
- return;
-
- Color outline_color = color;
- bool use_transparency_layer = color.HasAlpha();
- if (use_transparency_layer) {
- graphics_context.BeginLayer(static_cast<float>(color.Alpha()) / 255);
- outline_color =
- Color(outline_color.Red(), outline_color.Green(), outline_color.Blue());
- }
-
- DCHECK(count >= 4 && edges.size() == count);
- int first_adjacent_width = AdjustJoint(width, edges.back(), edges.front());
-
- // The width of the angled part of starting and ending joint of the current
- // edge.
- int adjacent_width_start = first_adjacent_width;
- int adjacent_width_end;
- for (wtf_size_t i = 0; i < count; ++i) {
- OutlineEdgeInfo& edge = edges[i];
- adjacent_width_end = i == count - 1
- ? first_adjacent_width
- : AdjustJoint(width, edge, edges[i + 1]);
- int adjacent_width1 = adjacent_width_start;
- int adjacent_width2 = adjacent_width_end;
- if (edge.side == BoxSide::kLeft || edge.side == BoxSide::kBottom)
- std::swap(adjacent_width1, adjacent_width2);
- ObjectPainterBase::DrawLineForBoxSide(
- graphics_context, edge.x1, edge.y1, edge.x2, edge.y2, edge.side,
- outline_color, style.OutlineStyle(), adjacent_width1, adjacent_width2,
- false);
- adjacent_width_start = adjacent_width_end;
- }
-
- if (use_transparency_layer)
- graphics_context.EndLayer();
-}
-
-void PaintSingleRectangleOutline(const PaintInfo& paint_info,
- const IntRect& rect,
- const ComputedStyle& style,
- const Color& color) {
- DCHECK(!style.OutlineStyleIsAuto());
-
- IntRect offset_rect = rect;
- ApplyOutlineOffset(offset_rect, style.OutlineOffsetInt());
-
- PhysicalRect inner(offset_rect);
- PhysicalRect outer(inner);
- outer.Inflate(LayoutUnit(style.OutlineWidthInt()));
- const BorderEdge common_edge_info(style.OutlineWidthInt(), color,
- style.OutlineStyle());
- BoxBorderPainter(style, outer, inner, common_edge_info)
- .PaintBorder(paint_info, outer);
-}
-
-void FillQuad(GraphicsContext& context,
- const FloatPoint quad[],
- const Color& color,
- bool antialias) {
- SkPathBuilder path;
- path.moveTo(FloatPointToSkPoint(quad[0]));
- path.lineTo(FloatPointToSkPoint(quad[1]));
- path.lineTo(FloatPointToSkPoint(quad[2]));
- path.lineTo(FloatPointToSkPoint(quad[3]));
- PaintFlags flags(context.FillFlags());
- flags.setAntiAlias(antialias);
- flags.setColor(color.Rgb());
-
- context.DrawPath(path.detach(), flags);
-}
-
-void DrawDashedOrDottedBoxSide(GraphicsContext& graphics_context,
- int x1,
- int y1,
- int x2,
- int y2,
- BoxSide side,
- Color color,
- int thickness,
- EBorderStyle style,
- bool antialias) {
- DCHECK_GT(thickness, 0);
-
- GraphicsContextStateSaver state_saver(graphics_context);
- graphics_context.SetShouldAntialias(antialias);
- graphics_context.SetStrokeColor(color);
- graphics_context.SetStrokeThickness(thickness);
- graphics_context.SetStrokeStyle(
- style == EBorderStyle::kDashed ? kDashedStroke : kDottedStroke);
-
- switch (side) {
- case BoxSide::kBottom:
- case BoxSide::kTop: {
- int mid_y = y1 + thickness / 2;
- graphics_context.DrawLine(IntPoint(x1, mid_y), IntPoint(x2, mid_y));
- break;
- }
- case BoxSide::kRight:
- case BoxSide::kLeft: {
- int mid_x = x1 + thickness / 2;
- graphics_context.DrawLine(IntPoint(mid_x, y1), IntPoint(mid_x, y2));
- break;
- }
- }
-}
-
-void DrawDoubleBoxSide(GraphicsContext& graphics_context,
- int x1,
- int y1,
- int x2,
- int y2,
- int length,
- BoxSide side,
- Color color,
- float thickness,
- int adjacent_width1,
- int adjacent_width2,
- bool antialias) {
- int third_of_thickness = (thickness + 1) / 3;
- DCHECK_GT(third_of_thickness, 0);
-
- if (!adjacent_width1 && !adjacent_width2) {
- StrokeStyle old_stroke_style = graphics_context.GetStrokeStyle();
- graphics_context.SetStrokeStyle(kNoStroke);
- graphics_context.SetFillColor(color);
-
- bool was_antialiased = graphics_context.ShouldAntialias();
- graphics_context.SetShouldAntialias(antialias);
-
- switch (side) {
- case BoxSide::kTop:
- case BoxSide::kBottom:
- graphics_context.DrawRect(IntRect(x1, y1, length, third_of_thickness));
- graphics_context.DrawRect(
- IntRect(x1, y2 - third_of_thickness, length, third_of_thickness));
- break;
- case BoxSide::kLeft:
- case BoxSide::kRight:
- graphics_context.DrawRect(IntRect(x1, y1, third_of_thickness, length));
- graphics_context.DrawRect(
- IntRect(x2 - third_of_thickness, y1, third_of_thickness, length));
- break;
- }
-
- graphics_context.SetShouldAntialias(was_antialiased);
- graphics_context.SetStrokeStyle(old_stroke_style);
- return;
- }
-
- int adjacent1_big_third =
- ((adjacent_width1 > 0) ? adjacent_width1 + 1 : adjacent_width1 - 1) / 3;
- int adjacent2_big_third =
- ((adjacent_width2 > 0) ? adjacent_width2 + 1 : adjacent_width2 - 1) / 3;
-
- switch (side) {
- case BoxSide::kTop:
- ObjectPainterBase::DrawLineForBoxSide(
- graphics_context, x1 + std::max((-adjacent_width1 * 2 + 1) / 3, 0),
- y1, x2 - std::max((-adjacent_width2 * 2 + 1) / 3, 0),
- y1 + third_of_thickness, side, color, EBorderStyle::kSolid,
- adjacent1_big_third, adjacent2_big_third, antialias);
- ObjectPainterBase::DrawLineForBoxSide(
- graphics_context, x1 + std::max((adjacent_width1 * 2 + 1) / 3, 0),
- y2 - third_of_thickness,
- x2 - std::max((adjacent_width2 * 2 + 1) / 3, 0), y2, side, color,
- EBorderStyle::kSolid, adjacent1_big_third, adjacent2_big_third,
- antialias);
- break;
- case BoxSide::kLeft:
- ObjectPainterBase::DrawLineForBoxSide(
- graphics_context, x1,
- y1 + std::max((-adjacent_width1 * 2 + 1) / 3, 0),
- x1 + third_of_thickness,
- y2 - std::max((-adjacent_width2 * 2 + 1) / 3, 0), side, color,
- EBorderStyle::kSolid, adjacent1_big_third, adjacent2_big_third,
- antialias);
- ObjectPainterBase::DrawLineForBoxSide(
- graphics_context, x2 - third_of_thickness,
- y1 + std::max((adjacent_width1 * 2 + 1) / 3, 0), x2,
- y2 - std::max((adjacent_width2 * 2 + 1) / 3, 0), side, color,
- EBorderStyle::kSolid, adjacent1_big_third, adjacent2_big_third,
- antialias);
- break;
- case BoxSide::kBottom:
- ObjectPainterBase::DrawLineForBoxSide(
- graphics_context, x1 + std::max((adjacent_width1 * 2 + 1) / 3, 0), y1,
- x2 - std::max((adjacent_width2 * 2 + 1) / 3, 0),
- y1 + third_of_thickness, side, color, EBorderStyle::kSolid,
- adjacent1_big_third, adjacent2_big_third, antialias);
- ObjectPainterBase::DrawLineForBoxSide(
- graphics_context, x1 + std::max((-adjacent_width1 * 2 + 1) / 3, 0),
- y2 - third_of_thickness,
- x2 - std::max((-adjacent_width2 * 2 + 1) / 3, 0), y2, side, color,
- EBorderStyle::kSolid, adjacent1_big_third, adjacent2_big_third,
- antialias);
- break;
- case BoxSide::kRight:
- ObjectPainterBase::DrawLineForBoxSide(
- graphics_context, x1, y1 + std::max((adjacent_width1 * 2 + 1) / 3, 0),
- x1 + third_of_thickness,
- y2 - std::max((adjacent_width2 * 2 + 1) / 3, 0), side, color,
- EBorderStyle::kSolid, adjacent1_big_third, adjacent2_big_third,
- antialias);
- ObjectPainterBase::DrawLineForBoxSide(
- graphics_context, x2 - third_of_thickness,
- y1 + std::max((-adjacent_width1 * 2 + 1) / 3, 0), x2,
- y2 - std::max((-adjacent_width2 * 2 + 1) / 3, 0), side, color,
- EBorderStyle::kSolid, adjacent1_big_third, adjacent2_big_third,
- antialias);
- break;
- default:
- break;
- }
-}
-
-void DrawRidgeOrGrooveBoxSide(GraphicsContext& graphics_context,
- int x1,
- int y1,
- int x2,
- int y2,
- BoxSide side,
- Color color,
- EBorderStyle style,
- int adjacent_width1,
- int adjacent_width2,
- bool antialias) {
- EBorderStyle s1;
- EBorderStyle s2;
- if (style == EBorderStyle::kGroove) {
- s1 = EBorderStyle::kInset;
- s2 = EBorderStyle::kOutset;
- } else {
- s1 = EBorderStyle::kOutset;
- s2 = EBorderStyle::kInset;
- }
-
- int adjacent1_big_half =
- ((adjacent_width1 > 0) ? adjacent_width1 + 1 : adjacent_width1 - 1) / 2;
- int adjacent2_big_half =
- ((adjacent_width2 > 0) ? adjacent_width2 + 1 : adjacent_width2 - 1) / 2;
-
- switch (side) {
- case BoxSide::kTop:
- ObjectPainterBase::DrawLineForBoxSide(
- graphics_context, x1 + std::max(-adjacent_width1, 0) / 2, y1,
- x2 - std::max(-adjacent_width2, 0) / 2, (y1 + y2 + 1) / 2, side,
- color, s1, adjacent1_big_half, adjacent2_big_half, antialias);
- ObjectPainterBase::DrawLineForBoxSide(
- graphics_context, x1 + std::max(adjacent_width1 + 1, 0) / 2,
- (y1 + y2 + 1) / 2, x2 - std::max(adjacent_width2 + 1, 0) / 2, y2,
- side, color, s2, adjacent_width1 / 2, adjacent_width2 / 2, antialias);
- break;
- case BoxSide::kLeft:
- ObjectPainterBase::DrawLineForBoxSide(
- graphics_context, x1, y1 + std::max(-adjacent_width1, 0) / 2,
- (x1 + x2 + 1) / 2, y2 - std::max(-adjacent_width2, 0) / 2, side,
- color, s1, adjacent1_big_half, adjacent2_big_half, antialias);
- ObjectPainterBase::DrawLineForBoxSide(
- graphics_context, (x1 + x2 + 1) / 2,
- y1 + std::max(adjacent_width1 + 1, 0) / 2, x2,
- y2 - std::max(adjacent_width2 + 1, 0) / 2, side, color, s2,
- adjacent_width1 / 2, adjacent_width2 / 2, antialias);
- break;
- case BoxSide::kBottom:
- ObjectPainterBase::DrawLineForBoxSide(
- graphics_context, x1 + std::max(adjacent_width1, 0) / 2, y1,
- x2 - std::max(adjacent_width2, 0) / 2, (y1 + y2 + 1) / 2, side, color,
- s2, adjacent1_big_half, adjacent2_big_half, antialias);
- ObjectPainterBase::DrawLineForBoxSide(
- graphics_context, x1 + std::max(-adjacent_width1 + 1, 0) / 2,
- (y1 + y2 + 1) / 2, x2 - std::max(-adjacent_width2 + 1, 0) / 2, y2,
- side, color, s1, adjacent_width1 / 2, adjacent_width2 / 2, antialias);
- break;
- case BoxSide::kRight:
- ObjectPainterBase::DrawLineForBoxSide(
- graphics_context, x1, y1 + std::max(adjacent_width1, 0) / 2,
- (x1 + x2 + 1) / 2, y2 - std::max(adjacent_width2, 0) / 2, side, color,
- s2, adjacent1_big_half, adjacent2_big_half, antialias);
- ObjectPainterBase::DrawLineForBoxSide(
- graphics_context, (x1 + x2 + 1) / 2,
- y1 + std::max(-adjacent_width1 + 1, 0) / 2, x2,
- y2 - std::max(-adjacent_width2 + 1, 0) / 2, side, color, s1,
- adjacent_width1 / 2, adjacent_width2 / 2, antialias);
- break;
- }
-}
-
-void DrawSolidBoxSide(GraphicsContext& graphics_context,
- int x1,
- int y1,
- int x2,
- int y2,
- BoxSide side,
- Color color,
- int adjacent_width1,
- int adjacent_width2,
- bool antialias) {
- DCHECK_GE(x2, x1);
- DCHECK_GE(y2, y1);
-
- if (!adjacent_width1 && !adjacent_width2) {
- // Tweak antialiasing to match the behavior of fillQuad();
- // this matters for rects in transformed contexts.
- bool was_antialiased = graphics_context.ShouldAntialias();
- if (antialias != was_antialiased)
- graphics_context.SetShouldAntialias(antialias);
- graphics_context.FillRect(IntRect(x1, y1, x2 - x1, y2 - y1), color);
- if (antialias != was_antialiased)
- graphics_context.SetShouldAntialias(was_antialiased);
- return;
- }
-
- FloatPoint quad[4];
- switch (side) {
- case BoxSide::kTop:
- quad[0] = FloatPoint(x1 + std::max(-adjacent_width1, 0), y1);
- quad[1] = FloatPoint(x1 + std::max(adjacent_width1, 0), y2);
- quad[2] = FloatPoint(x2 - std::max(adjacent_width2, 0), y2);
- quad[3] = FloatPoint(x2 - std::max(-adjacent_width2, 0), y1);
- break;
- case BoxSide::kBottom:
- quad[0] = FloatPoint(x1 + std::max(adjacent_width1, 0), y1);
- quad[1] = FloatPoint(x1 + std::max(-adjacent_width1, 0), y2);
- quad[2] = FloatPoint(x2 - std::max(-adjacent_width2, 0), y2);
- quad[3] = FloatPoint(x2 - std::max(adjacent_width2, 0), y1);
- break;
- case BoxSide::kLeft:
- quad[0] = FloatPoint(x1, y1 + std::max(-adjacent_width1, 0));
- quad[1] = FloatPoint(x1, y2 - std::max(-adjacent_width2, 0));
- quad[2] = FloatPoint(x2, y2 - std::max(adjacent_width2, 0));
- quad[3] = FloatPoint(x2, y1 + std::max(adjacent_width1, 0));
- break;
- case BoxSide::kRight:
- quad[0] = FloatPoint(x1, y1 + std::max(adjacent_width1, 0));
- quad[1] = FloatPoint(x1, y2 - std::max(adjacent_width2, 0));
- quad[2] = FloatPoint(x2, y2 - std::max(-adjacent_width2, 0));
- quad[3] = FloatPoint(x2, y1 + std::max(-adjacent_width1, 0));
- break;
- }
-
- 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.
- absl::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) {
- border_radius =
- ui::NativeTheme::GetInstanceForWeb()->GetBorderRadiusForPart(
- part.value(), style.Width().GetFloatValue(),
- style.Height().GetFloatValue());
-
- // Form controls send to NativeTheme have zoom applied. But the focus ring
- // outline does not. Apply zoom to checkbox focus ring.
- return (style.EffectiveAppearance() == kCheckboxPart)
- ? border_radius * style.EffectiveZoom()
- : border_radius;
- }
- }
-
- return border_radius;
-}
-
-} // anonymous namespace
-
-void ObjectPainterBase::PaintOutlineRects(
- const PaintInfo& paint_info,
- const Vector<PhysicalRect>& outline_rects,
- const ComputedStyle& style) {
- Vector<IntRect> pixel_snapped_outline_rects;
- for (auto& r : outline_rects)
- pixel_snapped_outline_rects.push_back(PixelSnappedIntRect(r));
-
- 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.OutlineOffsetInt(), border_radius, min_border_width, color,
- style.UsedColorScheme());
- return;
- }
-
- IntRect united_outline_rect = UnionRect(pixel_snapped_outline_rects);
- if (united_outline_rect == pixel_snapped_outline_rects[0]) {
- PaintSingleRectangleOutline(paint_info, united_outline_rect, style, color);
- return;
- }
- PaintComplexOutline(paint_info.context, pixel_snapped_outline_rects, style,
- color);
-}
-
-void ObjectPainterBase::DrawLineForBoxSide(GraphicsContext& graphics_context,
- float x1,
- float y1,
- float x2,
- float y2,
- BoxSide side,
- Color color,
- EBorderStyle style,
- int adjacent_width1,
- int adjacent_width2,
- bool antialias) {
- float thickness;
- float length;
- if (side == BoxSide::kTop || side == BoxSide::kBottom) {
- thickness = y2 - y1;
- length = x2 - x1;
- } else {
- thickness = x2 - x1;
- length = y2 - y1;
- }
-
- // We would like this check to be an ASSERT as we don't want to draw empty
- // borders. However nothing guarantees that the following recursive calls to
- // ObjectPainterBase::DrawLineForBoxSide will have positive thickness and
- // length.
- if (length <= 0 || thickness <= 0)
- return;
-
- if (style == EBorderStyle::kDouble && thickness < 3)
- style = EBorderStyle::kSolid;
-
- switch (style) {
- case EBorderStyle::kNone:
- case EBorderStyle::kHidden:
- return;
- case EBorderStyle::kDotted:
- case EBorderStyle::kDashed:
- DrawDashedOrDottedBoxSide(graphics_context, x1, y1, x2, y2, side, color,
- thickness, style, antialias);
- break;
- case EBorderStyle::kDouble:
- DrawDoubleBoxSide(graphics_context, x1, y1, x2, y2, length, side, color,
- thickness, adjacent_width1, adjacent_width2, antialias);
- break;
- case EBorderStyle::kRidge:
- case EBorderStyle::kGroove:
- DrawRidgeOrGrooveBoxSide(graphics_context, x1, y1, x2, y2, side, color,
- style, adjacent_width1, adjacent_width2,
- antialias);
- break;
- case EBorderStyle::kInset:
- // FIXME: Maybe we should lighten the colors on one side like Firefox.
- // https://bugs.webkit.org/show_bug.cgi?id=58608
- if (side == BoxSide::kTop || side == BoxSide::kLeft)
- color = color.Dark();
- FALLTHROUGH;
- case EBorderStyle::kOutset:
- if (style == EBorderStyle::kOutset &&
- (side == BoxSide::kBottom || side == BoxSide::kRight))
- color = color.Dark();
- FALLTHROUGH;
- case EBorderStyle::kSolid:
- DrawSolidBoxSide(graphics_context, x1, y1, x2, y2, side, color,
- adjacent_width1, adjacent_width2, antialias);
- break;
- }
-}
-
-} // namespace blink
diff --git a/chromium/third_party/blink/renderer/core/paint/object_painter_base.h b/chromium/third_party/blink/renderer/core/paint/object_painter_base.h
deleted file mode 100644
index 4f83f196202..00000000000
--- a/chromium/third_party/blink/renderer/core/paint/object_painter_base.h
+++ /dev/null
@@ -1,63 +0,0 @@
-// Copyright 2014 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_OBJECT_PAINTER_BASE_H_
-#define THIRD_PARTY_BLINK_RENDERER_CORE_PAINT_OBJECT_PAINTER_BASE_H_
-
-#include "third_party/blink/renderer/core/style/computed_style_constants.h"
-#include "third_party/blink/renderer/platform/geometry/int_rect.h"
-#include "third_party/blink/renderer/platform/graphics/color.h"
-#include "third_party/blink/renderer/platform/wtf/allocator/allocator.h"
-#include "third_party/blink/renderer/platform/wtf/vector.h"
-
-namespace blink {
-
-class ComputedStyle;
-class GraphicsContext;
-struct PaintInfo;
-struct PhysicalRect;
-
-// Base class for object painting. Has no dependencies on the layout tree and
-// thus provides functionality and definitions that can be shared between both
-// legacy layout and LayoutNG.
-class ObjectPainterBase {
- STACK_ALLOCATED();
-
- public:
- static void DrawBoxSide(GraphicsContext& context,
- const IntRect& snapped_edge_rect,
- BoxSide side,
- Color color,
- EBorderStyle style) {
- DrawLineForBoxSide(context, snapped_edge_rect.X(), snapped_edge_rect.Y(),
- snapped_edge_rect.MaxX(), snapped_edge_rect.MaxY(), side,
- color, style, 0, 0, true);
- }
-
- // TODO(wangxianzhu): The float parameters are truncated to int in the
- // function, which implicitly snaps to whole pixels incorrectly. We should
- // always use the above function. For now the only outside caller is
- // BoxBorderPainter::PaintOneBorderSide().
- static void DrawLineForBoxSide(GraphicsContext&,
- float x1,
- float y1,
- float x2,
- float y2,
- BoxSide,
- Color,
- EBorderStyle,
- int adjacent_edge_width1,
- int adjacent_edge_width2,
- bool antialias = false);
-
- protected:
- ObjectPainterBase() = default;
- void PaintOutlineRects(const PaintInfo&,
- const Vector<PhysicalRect>&,
- const ComputedStyle&);
-};
-
-} // namespace blink
-
-#endif // THIRD_PARTY_BLINK_RENDERER_CORE_PAINT_OBJECT_PAINTER_BASE_H_
diff --git a/chromium/third_party/blink/renderer/core/paint/outline_painter.cc b/chromium/third_party/blink/renderer/core/paint/outline_painter.cc
new file mode 100644
index 00000000000..bb1a6d18914
--- /dev/null
+++ b/chromium/third_party/blink/renderer/core/paint/outline_painter.cc
@@ -0,0 +1,724 @@
+// Copyright 2014 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/outline_painter.h"
+
+#include "build/build_config.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
+#include "third_party/blink/renderer/core/layout/geometry/physical_rect.h"
+#include "third_party/blink/renderer/core/paint/box_border_painter.h"
+#include "third_party/blink/renderer/core/paint/rounded_border_geometry.h"
+#include "third_party/blink/renderer/core/style/border_edge.h"
+#include "third_party/blink/renderer/core/style/computed_style.h"
+#include "third_party/blink/renderer/platform/geometry/int_rect.h"
+#include "third_party/blink/renderer/platform/graphics/color.h"
+#include "third_party/blink/renderer/platform/graphics/graphics_context.h"
+#include "third_party/blink/renderer/platform/graphics/graphics_context_state_saver.h"
+#include "third_party/blink/renderer/platform/graphics/path.h"
+#include "third_party/blink/renderer/platform/graphics/skia/skia_utils.h"
+#include "ui/native_theme/native_theme.h"
+
+namespace blink {
+
+namespace {
+
+struct OutlineEdgeInfo {
+ int x1;
+ int y1;
+ int x2;
+ int y2;
+ BoxSide side;
+};
+
+// Adjust length of edges if needed. Returns the width of the joint.
+int AdjustJoint(int outline_width,
+ OutlineEdgeInfo& edge1,
+ OutlineEdgeInfo& edge2) {
+ // A clockwise joint:
+ // - needs no adjustment of edge length because our edges are along the
+ // clockwise outer edge of the outline;
+ // - needs a positive adjacent joint width (required by
+ // BoxBorderPainter::DrawLineForBoxSide).
+ // A counterclockwise joint:
+ // - needs to increase the edge length to include the joint;
+ // - needs a negative adjacent joint width (required by
+ // BoxBorderPainter::DrawLineForBoxSide).
+ switch (edge1.side) {
+ case BoxSide::kTop:
+ switch (edge2.side) {
+ case BoxSide::kRight: // Clockwise
+ return outline_width;
+ case BoxSide::kLeft: // Counterclockwise
+ edge1.x2 += outline_width;
+ edge2.y2 += outline_width;
+ return -outline_width;
+ default: // Same side or no joint.
+ return 0;
+ }
+ case BoxSide::kRight:
+ switch (edge2.side) {
+ case BoxSide::kBottom: // Clockwise
+ return outline_width;
+ case BoxSide::kTop: // Counterclockwise
+ edge1.y2 += outline_width;
+ edge2.x1 -= outline_width;
+ return -outline_width;
+ default: // Same side or no joint.
+ return 0;
+ }
+ case BoxSide::kBottom:
+ switch (edge2.side) {
+ case BoxSide::kLeft: // Clockwise
+ return outline_width;
+ case BoxSide::kRight: // Counterclockwise
+ edge1.x1 -= outline_width;
+ edge2.y1 -= outline_width;
+ return -outline_width;
+ default: // Same side or no joint.
+ return 0;
+ }
+ case BoxSide::kLeft:
+ switch (edge2.side) {
+ case BoxSide::kTop: // Clockwise
+ return outline_width;
+ case BoxSide::kBottom: // Counterclockwise
+ edge1.y1 -= outline_width;
+ edge2.x2 += outline_width;
+ return -outline_width;
+ default: // Same side or no joint.
+ return 0;
+ }
+ default:
+ NOTREACHED();
+ return 0;
+ }
+}
+
+// A negative outline-offset should not cause the rendered outline shape to
+// become smaller than twice the computed value of the outline-width, in each
+// direction separately. See: https://drafts.csswg.org/css-ui/#outline-offset
+int AdjustedOutlineOffsetX(const IntRect& rect, int offset) {
+ return std::max(offset, -rect.Width() / 2);
+}
+int AdjustedOutlineOffsetY(const IntRect& rect, int offset) {
+ return std::max(offset, -rect.Height() / 2);
+}
+
+// Construct a clockwise path along the outer edge of the region covered by
+// |rects| expanded by |outline_offset| (which can be negative and clamped by
+// the rect size) and |additional_outset| (which should be non-negative).
+bool ComputeRightAnglePath(SkPath& path,
+ const Vector<IntRect>& rects,
+ int outline_offset,
+ int additional_outset) {
+ DCHECK_GE(additional_outset, 0);
+ SkRegion region;
+ for (auto& r : rects) {
+ IntRect rect = r;
+ rect.InflateX(AdjustedOutlineOffsetX(rect, outline_offset));
+ rect.InflateY(AdjustedOutlineOffsetY(rect, outline_offset));
+ rect.Inflate(additional_outset);
+ region.op(rect, SkRegion::kUnion_Op);
+ }
+ return region.getBoundaryPath(&path);
+}
+
+struct Line {
+ SkPoint start;
+ SkPoint end;
+};
+
+// Merge line2 into line1 if they are in the same straight line.
+bool MergeLineIfPossible(Line& line1, const Line& line2) {
+ DCHECK(line1.end == line2.start);
+ if ((line1.start.x() == line1.end.x() && line1.start.x() == line2.end.x()) ||
+ (line1.start.y() == line1.end.y() && line1.start.y() == line2.end.y())) {
+ line1.end = line2.end;
+ return true;
+ }
+ return false;
+}
+
+// Iterate a right angle |path| by running |contour_action| on each contour.
+// The path contains one or more contours each of which is like (kMove_Verb,
+// kLine_Verb, ..., kClose_Verb). Each line must be either horizontal or
+// vertical. Each pair of adjacent lines (including the last and the first)
+// should either create a right angle or be in the same straight line.
+template <typename Action>
+void IterateRightAnglePath(const SkPath& path, const Action& contour_action) {
+ SkPath::Iter iter(path, /*forceClose*/ true);
+ SkPoint points[4];
+ Vector<Line> lines;
+ for (SkPath::Verb verb = iter.next(points); verb != SkPath::kDone_Verb;
+ verb = iter.next(points)) {
+ switch (verb) {
+ case SkPath::kMove_Verb:
+ DCHECK(lines.IsEmpty());
+ break;
+ case SkPath::kLine_Verb: {
+ Line new_line{points[0], points[1]};
+ if (lines.IsEmpty() || !MergeLineIfPossible(lines.back(), new_line)) {
+ lines.push_back(new_line);
+ DCHECK(lines.size() == 1 ||
+ lines.back().start == lines[lines.size() - 2].end);
+ }
+ break;
+ }
+ case SkPath::kClose_Verb: {
+ if (lines.size() >= 4u) {
+ if (MergeLineIfPossible(lines.back(), lines.front())) {
+ lines.front() = lines.back();
+ lines.pop_back();
+ }
+ DCHECK(lines.front().start == lines.back().end);
+ DCHECK_GE(lines.size(), 4u);
+ contour_action(lines);
+ }
+ lines.clear();
+ break;
+ }
+ default:
+ NOTREACHED();
+ }
+ }
+}
+
+void PaintComplexRightAngleOutlineContour(GraphicsContext& context,
+ const Vector<Line>& lines,
+ const ComputedStyle& style,
+ Color color) {
+ int width = style.OutlineWidthInt();
+ Vector<OutlineEdgeInfo> edges;
+ edges.ReserveInitialCapacity(lines.size());
+ for (auto& line : lines) {
+ auto& edge = edges.emplace_back();
+ edge.x1 = SkScalarTruncToInt(line.start.x());
+ edge.y1 = SkScalarTruncToInt(line.start.y());
+ edge.x2 = SkScalarTruncToInt(line.end.x());
+ edge.y2 = SkScalarTruncToInt(line.end.y());
+ if (edge.x1 == edge.x2) {
+ if (edge.y1 < edge.y2) {
+ edge.x1 -= width;
+ edge.side = BoxSide::kRight;
+ } else {
+ std::swap(edge.y1, edge.y2);
+ edge.x2 += width;
+ edge.side = BoxSide::kLeft;
+ }
+ } else {
+ DCHECK(edge.y1 == edge.y2);
+ if (edge.x1 < edge.x2) {
+ edge.y2 += width;
+ edge.side = BoxSide::kTop;
+ } else {
+ std::swap(edge.x1, edge.x2);
+ edge.y1 -= width;
+ edge.side = BoxSide::kBottom;
+ }
+ }
+ }
+
+ int first_adjacent_width = AdjustJoint(width, edges.back(), edges.front());
+ // The width of the angled part of starting and ending joint of the current
+ // edge.
+ int adjacent_width_start = first_adjacent_width;
+ int adjacent_width_end;
+ for (wtf_size_t i = 0; i < edges.size(); ++i) {
+ OutlineEdgeInfo& edge = edges[i];
+ adjacent_width_end = i == edges.size() - 1
+ ? first_adjacent_width
+ : AdjustJoint(width, edge, edges[i + 1]);
+ int adjacent_width1 = adjacent_width_start;
+ int adjacent_width2 = adjacent_width_end;
+ if (edge.side == BoxSide::kLeft || edge.side == BoxSide::kBottom)
+ std::swap(adjacent_width1, adjacent_width2);
+ BoxBorderPainter::DrawLineForBoxSide(
+ context, edge.x1, edge.y1, edge.x2, edge.y2, edge.side, color,
+ style.OutlineStyle(), adjacent_width1, adjacent_width2,
+ /*antialias*/ false);
+ adjacent_width_start = adjacent_width_end;
+ }
+}
+
+void PaintComplexRightAngleOutline(GraphicsContext& context,
+ const Vector<IntRect>& rects,
+ const ComputedStyle& style) {
+ DCHECK(!style.OutlineStyleIsAuto());
+
+ SkPath path;
+ if (!ComputeRightAnglePath(path, rects, style.OutlineOffsetInt(),
+ style.OutlineWidthInt())) {
+ return;
+ }
+
+ Color color = style.VisitedDependentColor(GetCSSPropertyOutlineColor());
+ bool use_transparency_layer = color.HasAlpha();
+ if (use_transparency_layer) {
+ context.BeginLayer(static_cast<float>(color.Alpha()) / 255);
+ color.SetRGB(color.Red(), color.Green(), color.Blue());
+ }
+
+ IterateRightAnglePath(path, [&](const Vector<Line>& lines) {
+ PaintComplexRightAngleOutlineContour(context, lines, style, color);
+ });
+
+ if (use_transparency_layer)
+ context.EndLayer();
+}
+
+// Given 3 points defining a right angle corner, returns |p2| shifted to make
+// the containing path shrink by |inset|.
+SkPoint ShrinkCorner(const SkPoint& p1,
+ const SkPoint& p2,
+ const SkPoint& p3,
+ int inset) {
+ if (p1.x() == p2.x()) {
+ if (p1.y() < p2.y()) {
+ return p2.x() < p3.x() ? p2 + SkVector::Make(-inset, inset)
+ : p2 + SkVector::Make(-inset, -inset);
+ }
+ return p2.x() < p3.x() ? p2 + SkVector::Make(inset, inset)
+ : p2 + SkVector::Make(inset, -inset);
+ }
+ if (p1.x() < p2.x()) {
+ return p2.y() < p3.y() ? p2 + SkVector::Make(-inset, inset)
+ : p2 + SkVector::Make(inset, inset);
+ }
+ return p2.y() < p3.y() ? p2 + SkVector::Make(-inset, -inset)
+ : p2 + SkVector::Make(inset, -inset);
+}
+
+void ShrinkRightAnglePath(SkPath& path, int inset) {
+ SkPath input;
+ std::swap(input, path);
+ IterateRightAnglePath(input, [&path, inset](const Vector<Line>& lines) {
+ for (wtf_size_t i = 0; i < lines.size(); i++) {
+ const SkPoint& prev_point =
+ lines[i == 0 ? lines.size() - 1 : i - 1].start;
+ SkPoint new_point =
+ ShrinkCorner(prev_point, lines[i].start, lines[i].end, inset);
+ if (i == 0) {
+ path.moveTo(new_point);
+ } else {
+ path.lineTo(new_point);
+ }
+ }
+ path.close();
+ });
+}
+
+FloatRoundedRect::Radii ComputeCornerRadii(
+ const ComputedStyle& style,
+ const PhysicalRect& reference_border_rect,
+ float offset) {
+ return RoundedBorderGeometry::PixelSnappedRoundedBorderWithOutsets(
+ style, reference_border_rect,
+ LayoutRectOutsets(offset, offset, offset, offset))
+ .GetRadii();
+}
+
+// Given 3 points defining a right angle corner, returns the corresponding
+// corner in |convex_radii| or |concave_radii|.
+FloatSize GetRadiiCorner(const FloatRoundedRect::Radii& convex_radii,
+ const FloatRoundedRect::Radii& concave_radii,
+ const SkPoint& p1,
+ const SkPoint& p2,
+ const SkPoint& p3) {
+ if (p1.x() == p2.x()) {
+ if (p1.y() == p2.y() || p2.x() == p3.x())
+ return FloatSize();
+ DCHECK_EQ(p2.y(), p3.y());
+ if (p1.y() < p2.y()) {
+ return p2.x() < p3.x() ? concave_radii.BottomLeft()
+ : convex_radii.BottomRight();
+ }
+ return p2.x() < p3.x() ? convex_radii.TopLeft() : concave_radii.TopRight();
+ }
+ DCHECK_EQ(p1.y(), p2.y());
+ if (p2.x() != p3.x() || p2.y() == p3.y())
+ return FloatSize();
+ if (p1.x() < p2.x()) {
+ return p2.y() < p3.y() ? convex_radii.TopRight()
+ : concave_radii.BottomRight();
+ }
+ return p2.y() < p3.y() ? concave_radii.TopLeft() : convex_radii.BottomLeft();
+}
+
+// Shorten |line| between rounded corners.
+void AdjustLineBetweenCorners(Line& line,
+ const FloatRoundedRect::Radii& convex_radii,
+ const FloatRoundedRect::Radii& concave_radii,
+ const SkPoint& prev_point,
+ const SkPoint& next_point) {
+ FloatSize corner1 = GetRadiiCorner(convex_radii, concave_radii, prev_point,
+ line.start, line.end);
+ FloatSize corner2 = GetRadiiCorner(convex_radii, concave_radii, line.start,
+ line.end, next_point);
+ if (line.start.x() == line.end.x()) {
+ // |line| is vertical, and adjacent lines are horizontal.
+ float height = std::abs(line.end.y() - line.start.y());
+ float corner1_height = corner1.Height();
+ float corner2_height = corner2.Height();
+ if (corner1_height + corner2_height > height) {
+ // Scale down the corner heights to make the corners fit in |height|.
+ float scale = height / (corner1_height + corner2_height);
+ corner1_height = floorf(corner1_height * scale);
+ corner2_height = floorf(corner2_height * scale);
+ }
+ if (line.start.y() < line.end.y()) {
+ line.start.offset(0, corner1_height);
+ line.end.offset(0, -corner2_height);
+ } else {
+ line.start.offset(0, -corner1_height);
+ line.end.offset(0, corner2_height);
+ }
+ } else {
+ // |line| is horizontal, and adjacent lines are vertical.
+ float width = std::abs(line.end.x() - line.start.x());
+ float corner1_width = corner1.Width();
+ float corner2_width = corner2.Width();
+ if (corner1_width + corner2_width > width) {
+ // Scale down the corner widths to make the corners fit in |width|.
+ float scale = width / (corner1_width + corner2_width);
+ corner1_width = floorf(corner1_width * scale);
+ corner2_width = floorf(corner2_width * scale);
+ }
+ if (line.start.x() < line.end.x()) {
+ line.start.offset(corner1_width, 0);
+ line.end.offset(-corner2_width, 0);
+ } else {
+ line.start.offset(-corner1_width, 0);
+ line.end.offset(corner2_width, 0);
+ }
+ }
+}
+
+// Create a rounded path from a right angle |path| by
+// - inserting arc segments for corners;
+// - adjusting length of the lines.
+void AddCornerRadiiToPath(SkPath& path,
+ const FloatRoundedRect::Radii& convex_radii,
+ const FloatRoundedRect::Radii& concave_radii) {
+ SkPath input;
+ input.swap(path);
+ IterateRightAnglePath(input, [&](const Vector<Line>& lines) {
+ auto new_lines = lines;
+ for (wtf_size_t i = 0; i < lines.size(); i++) {
+ const SkPoint& prev_point =
+ lines[i == 0 ? lines.size() - 1 : i - 1].start;
+ const SkPoint& next_point = lines[i == lines.size() - 1 ? 0 : i + 1].end;
+ AdjustLineBetweenCorners(new_lines[i], convex_radii, concave_radii,
+ prev_point, next_point);
+ }
+ // Generate the new contour into |path|.
+ DCHECK_EQ(lines.size(), new_lines.size());
+ path.moveTo(new_lines[0].start);
+ for (wtf_size_t i = 0; i < new_lines.size(); i++) {
+ const Line& line = new_lines[i];
+ if (line.end != line.start)
+ path.lineTo(line.end);
+ const Line& next_line = new_lines[i == lines.size() - 1 ? 0 : i + 1];
+ if (line.end != next_line.start) {
+ constexpr float kCornerConicWeight = 0.707106781187; // 1/sqrt(2)
+ // This produces a 90 degree arc from line.end towards lines[i].end
+ // to next_line.start.
+ path.conicTo(lines[i].end, next_line.start, kCornerConicWeight);
+ }
+ }
+ path.close();
+ });
+}
+
+class ComplexRoundedOutlinePainter {
+ public:
+ ComplexRoundedOutlinePainter(GraphicsContext& context,
+ const Vector<IntRect>& rects,
+ const PhysicalRect& reference_border_rect,
+ const ComputedStyle& style)
+ : context_(context),
+ rects_(rects),
+ reference_border_rect_(reference_border_rect),
+ style_(style),
+ outline_style_(style.OutlineStyle()),
+ offset_(style.OutlineOffsetInt()),
+ width_(style.OutlineWidthInt()),
+ color_(style.VisitedDependentColor(GetCSSPropertyOutlineColor())) {
+ DCHECK(!style.OutlineStyleIsAuto());
+ if (width_ <= 2 && outline_style_ == EBorderStyle::kDouble) {
+ outline_style_ = EBorderStyle::kSolid;
+ } else if (width_ == 1 && (outline_style_ == EBorderStyle::kRidge ||
+ outline_style_ == EBorderStyle::kGroove)) {
+ outline_style_ = EBorderStyle::kSolid;
+ Color dark = color_.Dark();
+ color_ = Color((color_.Red() + dark.Red()) / 2,
+ (color_.Green() + dark.Green()) / 2,
+ (color_.Blue() + dark.Blue()) / 2, color_.Alpha());
+ }
+ }
+
+ bool Paint() {
+ if (width_ == 0)
+ return true;
+
+ if (!ComputeRightAnglePath(right_angle_outer_path_, rects_, offset_,
+ width_)) {
+ return true;
+ }
+
+ SkPath outer_path = right_angle_outer_path_;
+ SkPath inner_path = right_angle_outer_path_;
+ ShrinkRightAnglePath(inner_path, width_);
+ auto inner_radii = ComputeRadii(0);
+ auto outer_radii = ComputeRadii(width_);
+ AddCornerRadiiToPath(outer_path, outer_radii, inner_radii);
+ AddCornerRadiiToPath(inner_path, inner_radii, outer_radii);
+
+ GraphicsContextStateSaver saver(context_);
+ context_.ClipPath(outer_path, kAntiAliased);
+ context_.ClipOut(inner_path);
+ context_.SetFillColor(color_);
+
+ switch (outline_style_) {
+ case EBorderStyle::kSolid:
+ context_.FillRect(outer_path.getBounds());
+ break;
+ case EBorderStyle::kDouble:
+ PaintDoubleOutline();
+ break;
+ case EBorderStyle::kDotted:
+ case EBorderStyle::kDashed:
+ PaintDottedOrDashedOutline();
+ break;
+ default:
+ // TODO(wangxianzhu): Draw kRidge, kGroove, kInset, kOutset by calling
+ // BoxBorderPainter::DrawBoxSideFromPath() for each segment of the path.
+ return false;
+ }
+ return true;
+ }
+
+ private:
+ void PaintDoubleOutline() {
+ SkPath inner_third_path = right_angle_outer_path_;
+ SkPath outer_third_path = right_angle_outer_path_;
+ int stroke_width = std::round(width_ / 3.0);
+ ShrinkRightAnglePath(inner_third_path, width_ - stroke_width);
+ ShrinkRightAnglePath(outer_third_path, stroke_width);
+ auto inner_third_radii = ComputeRadii(stroke_width);
+ auto outer_third_radii = ComputeRadii(width_ - stroke_width);
+ AddCornerRadiiToPath(inner_third_path, inner_third_radii,
+ outer_third_radii);
+ AddCornerRadiiToPath(outer_third_path, outer_third_radii,
+ inner_third_radii);
+ {
+ GraphicsContextStateSaver saver(context_);
+ context_.ClipOut(outer_third_path);
+ context_.FillRect(right_angle_outer_path_.getBounds());
+ }
+ context_.FillPath(inner_third_path);
+ }
+
+ void PaintDottedOrDashedOutline() {
+ SkPath center_path = right_angle_outer_path_;
+ int center_outset = width_ / 2;
+ ShrinkRightAnglePath(center_path, width_ - center_outset);
+ auto center_radii = ComputeRadii(center_outset);
+ AddCornerRadiiToPath(center_path, center_radii, center_radii);
+ context_.SetStrokeColor(color_);
+ auto stroke_style =
+ outline_style_ == EBorderStyle::kDashed ? kDashedStroke : kDottedStroke;
+ context_.SetStrokeStyle(stroke_style);
+ if (StrokeData::StrokeIsDashed(width_, stroke_style)) {
+ // Draw wider to fill the clip area between inner_path_ and outer_path_,
+ // to get smoother edges, and even stroke thickness when the outline is
+ // thin.
+ context_.SetStrokeThickness(width_ + 2);
+ } else {
+ context_.SetStrokeThickness(width_);
+ context_.SetLineCap(kRoundCap);
+ }
+ context_.StrokePath(center_path, Path(center_path).length(), width_);
+ }
+
+ FloatRoundedRect::Radii ComputeRadii(int outset) const {
+ return ComputeCornerRadii(style_, reference_border_rect_, offset_ + outset);
+ }
+
+ GraphicsContext& context_;
+ const Vector<IntRect>& rects_;
+ const PhysicalRect& reference_border_rect_;
+ const ComputedStyle& style_;
+ EBorderStyle outline_style_;
+ int offset_;
+ int width_;
+ Color color_;
+ SkPath right_angle_outer_path_;
+};
+
+float DefaultFocusRingCornerRadius(const ComputedStyle& style) {
+ // Default style is corner radius equal to outline width.
+ return style.FocusRingStrokeWidth();
+}
+
+FloatRoundedRect::Radii GetFocusRingCornerRadii(
+ const ComputedStyle& style,
+ const PhysicalRect& reference_border_rect) {
+ if (style.HasBorderRadius() &&
+ (!style.HasEffectiveAppearance() || style.HasAuthorBorderRadius())) {
+ auto radii = ComputeCornerRadii(style, reference_border_rect,
+ style.OutlineOffsetInt());
+ radii.SetMinimumRadius(DefaultFocusRingCornerRadius(style));
+ return radii;
+ }
+
+ if (!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.
+ absl::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) {
+ float corner_radius =
+ ui::NativeTheme::GetInstanceForWeb()->GetBorderRadiusForPart(
+ part.value(), style.Width().GetFloatValue(),
+ style.Height().GetFloatValue());
+ corner_radius =
+ ui::NativeTheme::GetInstanceForWeb()->AdjustBorderRadiusByZoom(
+ part.value(), corner_radius, style.EffectiveZoom());
+ return FloatRoundedRect::Radii(corner_radius);
+ }
+ }
+
+ return FloatRoundedRect::Radii(DefaultFocusRingCornerRadius(style));
+}
+
+void PaintSingleFocusRing(GraphicsContext& context,
+ const Vector<IntRect>& rects,
+ float width,
+ int offset,
+ const FloatRoundedRect::Radii& corner_radii,
+ const Color& color) {
+ DCHECK(!rects.IsEmpty());
+ SkPath path;
+ if (!ComputeRightAnglePath(path, rects, offset, 0))
+ return;
+
+ SkRect rect;
+ if (path.isRect(&rect)) {
+ context.DrawFocusRingRect(FloatRoundedRect(rect, corner_radii), color,
+ width);
+ return;
+ }
+
+ absl::optional<float> corner_radius = corner_radii.UniformRadius();
+ if (corner_radius.has_value()) {
+ context.DrawFocusRingPath(path, color, width, *corner_radius);
+ return;
+ }
+
+ // Bake non-uniform radii into the path, and draw the path with 0 corner
+ // radius as the path already has rounded corners.
+ AddCornerRadiiToPath(path, corner_radii, corner_radii);
+ context.DrawFocusRingPath(path, color, width, 0);
+}
+
+void PaintFocusRing(GraphicsContext& context,
+ const Vector<IntRect>& rects,
+ const ComputedStyle& style,
+ const FloatRoundedRect::Radii& corner_radii) {
+ Color inner_color = style.VisitedDependentColor(GetCSSPropertyOutlineColor());
+#if !defined(OS_MAC)
+ if (style.DarkColorScheme())
+ inner_color = Color::kWhite;
+#endif
+
+ const float outer_ring_width = style.FocusRingOuterStrokeWidth();
+ const float inner_ring_width = style.FocusRingInnerStrokeWidth();
+ const int offset = style.FocusRingOffset();
+ Color outer_color =
+ style.DarkColorScheme() ? Color(0x10, 0x10, 0x10) : Color::kWhite;
+ PaintSingleFocusRing(context, rects, outer_ring_width,
+ offset + std::ceil(inner_ring_width), corner_radii,
+ outer_color);
+ // Draw the inner ring using |outer_ring_width| (which should be wider than
+ // the additional offset of the outer ring) over the outer ring to ensure no
+ // gaps or AA artifacts.
+ DCHECK_GE(outer_ring_width, std::ceil(inner_ring_width));
+ PaintSingleFocusRing(context, rects, outer_ring_width, offset, corner_radii,
+ inner_color);
+}
+
+} // anonymous namespace
+
+void OutlinePainter::PaintOutlineRects(
+ GraphicsContext& context,
+ const Vector<PhysicalRect>& outline_rects,
+ const ComputedStyle& style) {
+ Vector<IntRect> pixel_snapped_outline_rects;
+ for (auto& r : outline_rects) {
+ IntRect pixel_snapped_rect = PixelSnappedIntRect(r);
+ // Keep empty rect for normal outline, but not for focus rings.
+ if (!pixel_snapped_rect.IsEmpty() || !style.OutlineStyleIsAuto())
+ pixel_snapped_outline_rects.push_back(pixel_snapped_rect);
+ }
+ if (pixel_snapped_outline_rects.IsEmpty())
+ return;
+
+ if (style.OutlineStyleIsAuto()) {
+ auto corner_radii = GetFocusRingCornerRadii(style, outline_rects[0]);
+ PaintFocusRing(context, pixel_snapped_outline_rects, style, corner_radii);
+ return;
+ }
+
+ IntRect united_outline_rect = UnionRect(pixel_snapped_outline_rects);
+ if (united_outline_rect == pixel_snapped_outline_rects[0]) {
+ BoxBorderPainter::PaintSingleRectOutline(
+ context, style, outline_rects[0],
+ AdjustedOutlineOffsetX(united_outline_rect, style.OutlineOffsetInt()),
+ AdjustedOutlineOffsetY(united_outline_rect, style.OutlineOffsetInt()));
+ return;
+ }
+
+ if (style.HasBorderRadius() &&
+ ComplexRoundedOutlinePainter(context, pixel_snapped_outline_rects,
+ outline_rects[0], style)
+ .Paint()) {
+ return;
+ }
+
+ PaintComplexRightAngleOutline(context, pixel_snapped_outline_rects, style);
+}
+
+void OutlinePainter::PaintFocusRingPath(GraphicsContext& context,
+ const Path& focus_ring_path,
+ const ComputedStyle& style) {
+ // TODO(crbug/251206): Implement outline-offset and double focus rings like
+ // right angle focus rings, which requires SkPathOps to support expanding and
+ // shrinking generic paths.
+ context.DrawFocusRingPath(
+ focus_ring_path.GetSkPath(),
+ style.VisitedDependentColor(GetCSSPropertyOutlineColor()),
+ style.FocusRingStrokeWidth(), DefaultFocusRingCornerRadius(style));
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/core/paint/outline_painter.h b/chromium/third_party/blink/renderer/core/paint/outline_painter.h
new file mode 100644
index 00000000000..68e76bb9ac5
--- /dev/null
+++ b/chromium/third_party/blink/renderer/core/paint/outline_painter.h
@@ -0,0 +1,33 @@
+// Copyright 2014 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_OUTLINE_PAINTER_H_
+#define THIRD_PARTY_BLINK_RENDERER_CORE_PAINT_OUTLINE_PAINTER_H_
+
+#include "third_party/blink/renderer/platform/wtf/allocator/allocator.h"
+#include "third_party/blink/renderer/platform/wtf/vector.h"
+
+namespace blink {
+
+class ComputedStyle;
+class GraphicsContext;
+class Path;
+struct PhysicalRect;
+
+class OutlinePainter {
+ STATIC_ONLY(OutlinePainter);
+
+ public:
+ static void PaintOutlineRects(GraphicsContext&,
+ const Vector<PhysicalRect>&,
+ const ComputedStyle&);
+
+ static void PaintFocusRingPath(GraphicsContext&,
+ const Path&,
+ const ComputedStyle&);
+};
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_CORE_PAINT_OUTLINE_PAINTER_H_
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 d21438ff2c5..694579235e8 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
@@ -195,7 +195,7 @@ TEST_P(PaintAndRasterInvalidationTest, SubpixelChange) {
IntRect(0, 0, 50, 100),
PaintInvalidationReason::kGeometry},
RasterInvalidationInfo{object, object->DebugName(),
- IntRect(0, 0, 101, 71),
+ IntRect(0, 0, 101, 70),
PaintInvalidationReason::kGeometry}));
GetDocument().View()->SetTracksRasterInvalidations(false);
@@ -208,7 +208,7 @@ TEST_P(PaintAndRasterInvalidationTest, SubpixelChange) {
IntRect(0, 0, 50, 100),
PaintInvalidationReason::kGeometry},
RasterInvalidationInfo{object, object->DebugName(),
- IntRect(0, 0, 101, 71),
+ IntRect(0, 0, 101, 70),
PaintInvalidationReason::kGeometry}));
GetDocument().View()->SetTracksRasterInvalidations(false);
}
@@ -230,7 +230,7 @@ TEST_P(PaintAndRasterInvalidationTest, SubpixelVisualRectChangeWithTransform) {
IntRect(0, 0, 100, 200),
PaintInvalidationReason::kGeometry},
RasterInvalidationInfo{object, object->DebugName(),
- IntRect(0, 0, 202, 142),
+ IntRect(0, 0, 202, 140),
PaintInvalidationReason::kGeometry}));
GetDocument().View()->SetTracksRasterInvalidations(false);
@@ -243,7 +243,7 @@ TEST_P(PaintAndRasterInvalidationTest, SubpixelVisualRectChangeWithTransform) {
IntRect(0, 0, 100, 200),
PaintInvalidationReason::kGeometry},
RasterInvalidationInfo{object, object->DebugName(),
- IntRect(0, 0, 202, 142),
+ IntRect(0, 0, 202, 140),
PaintInvalidationReason::kGeometry}));
GetDocument().View()->SetTracksRasterInvalidations(false);
}
@@ -269,7 +269,7 @@ TEST_P(PaintAndRasterInvalidationTest, SubpixelWithinPixelsChange) {
UpdateAllLifecyclePhasesForTest();
EXPECT_THAT(GetRasterInvalidationTracking()->Invalidations(),
UnorderedElementsAre(RasterInvalidationInfo{
- object, object->DebugName(), IntRect(0, 0, 50, 100),
+ object, object->DebugName(), IntRect(0, 1, 50, 99),
PaintInvalidationReason::kGeometry}));
GetDocument().View()->SetTracksRasterInvalidations(false);
}
@@ -951,6 +951,52 @@ TEST_P(PaintAndRasterInvalidationTest, ScrollingInvalidatesStickyOffset) {
EXPECT_EQ(PhysicalOffset(), inner->FirstFragment().PaintOffset());
}
+TEST_P(PaintAndRasterInvalidationTest, NoDamageDueToFloatingPointError) {
+ SetBodyInnerHTML(R"HTML(
+ <style>
+ #canvas {
+ position: absolute;
+ top: 0;
+ left: 0;
+ width: 0;
+ height: 0;
+ will-change: transform;
+ transform-origin: top left;
+ }
+ .initial { transform: translateX(0px) scale(1.8); }
+ .updated { transform: translateX(47.22222222222222px) scale(1.8); }
+ #tile {
+ position: absolute;
+ will-change: transform;
+ transform-origin: top left;
+ transform: scale(0.55555555555556);
+ }
+ #tileInner {
+ transform-origin: top left;
+ transform: scale(1.8);
+ width: 200px;
+ height: 200px;
+ background: lightblue;
+ }
+ </style>
+ <div id="canvas" class="initial">
+ <div id="tile">
+ <div id="tileInner"></div>
+ </div>
+ </div>
+ )HTML");
+
+ GetDocument().View()->SetTracksRasterInvalidations(true);
+
+ auto* canvas = GetDocument().getElementById("canvas");
+ canvas->setAttribute(html_names::kClassAttr, "updated");
+ GetDocument().View()->SetPaintArtifactCompositorNeedsUpdate();
+
+ UpdateAllLifecyclePhasesForTest();
+ EXPECT_FALSE(GetRasterInvalidationTracking(1)->HasInvalidations());
+ GetDocument().View()->SetTracksRasterInvalidations(false);
+}
+
TEST_P(PaintAndRasterInvalidationTest, ResizeElementWhichHasNonCustomResizer) {
SetBodyInnerHTML(R"HTML(
<!DOCTYPE html>
diff --git a/chromium/third_party/blink/renderer/core/paint/paint_and_raster_invalidation_test.h b/chromium/third_party/blink/renderer/core/paint/paint_and_raster_invalidation_test.h
index d1da3abf39a..a6d5c358146 100644
--- a/chromium/third_party/blink/renderer/core/paint/paint_and_raster_invalidation_test.h
+++ b/chromium/third_party/blink/renderer/core/paint/paint_and_raster_invalidation_test.h
@@ -19,7 +19,7 @@ class PaintAndRasterInvalidationTest : public PaintControllerPaintTest {
MakeGarbageCollected<SingleChildLocalFrameClient>()) {}
protected:
- ContentLayerClientImpl* GetContentLayerClient(size_t index = 0) const {
+ ContentLayerClientImpl* GetContentLayerClient(wtf_size_t index = 0) const {
DCHECK(RuntimeEnabledFeatures::CompositeAfterPaintEnabled());
const auto& clients = GetDocument()
.View()
@@ -29,7 +29,7 @@ class PaintAndRasterInvalidationTest : public PaintControllerPaintTest {
}
const RasterInvalidationTracking* GetRasterInvalidationTracking(
- size_t index = 0) const {
+ wtf_size_t index = 0) const {
if (RuntimeEnabledFeatures::CompositeAfterPaintEnabled()) {
if (auto* client = GetContentLayerClient(index))
return client->GetRasterInvalidator().GetTracking();
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 e176942d118..0c543f7cc54 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
@@ -128,10 +128,10 @@ TEST_P(PaintControllerPaintTestForCAP, FrameScrollingContents) {
<div id='div4' style='top: 9000px; left: 9000px'></div>
)HTML");
- const auto& div1 = *GetLayoutObjectByElementId("div1");
- const auto& div2 = *GetLayoutObjectByElementId("div2");
- const auto& div3 = *GetLayoutObjectByElementId("div3");
- const auto& div4 = *GetLayoutObjectByElementId("div4");
+ const auto& div1 = To<LayoutBox>(*GetLayoutObjectByElementId("div1"));
+ const auto& div2 = To<LayoutBox>(*GetLayoutObjectByElementId("div2"));
+ const auto& div3 = To<LayoutBox>(*GetLayoutObjectByElementId("div3"));
+ const auto& div4 = To<LayoutBox>(*GetLayoutObjectByElementId("div4"));
EXPECT_THAT(ContentDisplayItems(),
ElementsAre(VIEW_SCROLLING_BACKGROUND_DISPLAY_ITEM,
@@ -147,8 +147,18 @@ TEST_P(PaintControllerPaintTestForCAP, FrameScrollingContents) {
PaintChunk::Id(GetLayoutView(), DisplayItem::kScrollHitTest),
GetLayoutView().FirstFragment().LocalBorderBoxProperties(),
&view_scroll_hit_test, IntRect(0, 0, 800, 600)));
- EXPECT_THAT(ContentPaintChunks(),
- ElementsAre(VIEW_SCROLLING_BACKGROUND_CHUNK(3, nullptr)));
+ auto contents_properties =
+ GetLayoutView().FirstFragment().ContentsProperties();
+ EXPECT_THAT(
+ ContentPaintChunks(),
+ ElementsAre(
+ VIEW_SCROLLING_BACKGROUND_CHUNK_COMMON,
+ IsPaintChunk(1, 2,
+ PaintChunk::Id(*div1.Layer(), DisplayItem::kLayerChunk),
+ contents_properties),
+ IsPaintChunk(2, 3,
+ PaintChunk::Id(*div2.Layer(), DisplayItem::kLayerChunk),
+ contents_properties)));
GetDocument().View()->LayoutViewport()->SetScrollOffset(
ScrollOffset(5000, 5000), mojom::blink::ScrollType::kProgrammatic);
@@ -165,8 +175,20 @@ TEST_P(PaintControllerPaintTestForCAP, FrameScrollingContents) {
PaintChunk::Id(GetLayoutView(), DisplayItem::kScrollHitTest),
GetLayoutView().FirstFragment().LocalBorderBoxProperties(),
&view_scroll_hit_test, IntRect(0, 0, 800, 600)));
- EXPECT_THAT(ContentPaintChunks(),
- ElementsAre(VIEW_SCROLLING_BACKGROUND_CHUNK(4, nullptr)));
+ EXPECT_THAT(
+ ContentPaintChunks(),
+ ElementsAre(
+ VIEW_SCROLLING_BACKGROUND_CHUNK_COMMON,
+ // html and div1 are out of the cull rect.
+ IsPaintChunk(1, 2,
+ PaintChunk::Id(*div2.Layer(), DisplayItem::kLayerChunk),
+ contents_properties),
+ IsPaintChunk(2, 3,
+ PaintChunk::Id(*div3.Layer(), DisplayItem::kLayerChunk),
+ contents_properties),
+ IsPaintChunk(3, 4,
+ PaintChunk::Id(*div4.Layer(), DisplayItem::kLayerChunk),
+ contents_properties)));
}
TEST_P(PaintControllerPaintTestForCAP, BlockScrollingNonLayeredContents) {
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 d5ede23a64a..3471dfd69f8 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
@@ -8,6 +8,7 @@
#include "third_party/blink/renderer/core/editing/frame_selection.h"
#include "third_party/blink/renderer/core/frame/local_frame_view.h"
#include "third_party/blink/renderer/core/layout/layout_view.h"
+#include "third_party/blink/renderer/core/paint/cull_rect_updater.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"
@@ -49,6 +50,10 @@ class PaintControllerPaintTestBase : public RenderingTest {
void UpdateAllLifecyclePhasesExceptPaint() {
GetDocument().View()->UpdateAllLifecyclePhasesExceptPaint(
DocumentUpdateReason::kTest);
+ // Run CullRectUpdater to ease testing of cull rects and repaint flags of
+ // PaintLayers on cull rect change.
+ if (RuntimeEnabledFeatures::CullRectUpdateEnabled())
+ CullRectUpdater(*GetLayoutView().Layer()).Update();
}
void PaintContents(const IntRect& interest_rect) {
@@ -111,31 +116,6 @@ class PaintControllerPaintTestBase : public RenderingTest {
return PaintChunkSubset(RootPaintController().GetPaintArtifactShared(),
begin_index, end_index);
}
-
- class CachedItemAndSubsequenceCounter {
- public:
- CachedItemAndSubsequenceCounter()
- : reset_uma_reporting_(&PaintController::disable_uma_reporting_, true) {
- Reset();
- }
- void Reset() {
- old_num_cached_items_ = PaintController::sum_num_cached_items_;
- old_num_cached_subsequences_ =
- PaintController::sum_num_cached_subsequences_;
- }
- size_t NumNewCachedItems() const {
- return PaintController::sum_num_cached_items_ - old_num_cached_items_;
- }
- size_t NumNewCachedSubsequences() const {
- return PaintController::sum_num_cached_subsequences_ -
- old_num_cached_subsequences_;
- }
-
- private:
- base::AutoReset<bool> reset_uma_reporting_;
- size_t old_num_cached_items_;
- size_t old_num_cached_subsequences_;
- };
};
class PaintControllerPaintTest : public PaintTestConfigurations,
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 37f149818af..971354f09b8 100644
--- a/chromium/third_party/blink/renderer/core/paint/paint_info.h
+++ b/chromium/third_party/blink/renderer/core/paint/paint_info.h
@@ -28,8 +28,8 @@
#define THIRD_PARTY_BLINK_RENDERER_CORE_PAINT_PAINT_INFO_H_
#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"
+#include "third_party/blink/renderer/core/layout/layout_box.h"
+#include "third_party/blink/renderer/core/layout/ng/ng_physical_box_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
@@ -59,14 +59,11 @@ struct CORE_EXPORT PaintInfo {
PaintPhase phase,
GlobalPaintFlags global_paint_flags,
PaintLayerFlags paint_flags,
- const LayoutBoxModelObject* paint_container = nullptr,
- LayoutUnit fragment_logical_top_in_flow_thread = LayoutUnit())
+ const LayoutBoxModelObject* paint_container = nullptr)
: context(context),
phase(phase),
cull_rect_(cull_rect),
paint_container_(paint_container),
- fragment_logical_top_in_flow_thread_(
- fragment_logical_top_in_flow_thread),
paint_flags_(paint_flags),
global_paint_flags_(global_paint_flags) {}
@@ -76,8 +73,7 @@ struct CORE_EXPORT PaintInfo {
phase(copy_other_fields_from.phase),
cull_rect_(copy_other_fields_from.cull_rect_),
paint_container_(copy_other_fields_from.paint_container_),
- fragment_logical_top_in_flow_thread_(
- copy_other_fields_from.fragment_logical_top_in_flow_thread_),
+ fragment_id_(copy_other_fields_from.fragment_id_),
paint_flags_(copy_other_fields_from.paint_flags_),
global_paint_flags_(copy_other_fields_from.global_paint_flags_) {
// We should never pass is_painting_scrolling_background_ other PaintInfo.
@@ -152,11 +148,16 @@ struct CORE_EXPORT PaintInfo {
// Returns the fragment of the current painting object matching the current
// layer fragment.
- const FragmentData* FragmentToPaint(const LayoutObject& object) const {
+ const FragmentData* LegacyFragmentToPaint(const LayoutObject& object) const {
+ if (fragment_id_ == WTF::kNotFound) {
+ // We haven't been set up for legacy block fragmentation, so the object
+ // better not be fragmented, then.
+ DCHECK(!object.FirstFragment().NextFragment());
+ return &object.FirstFragment();
+ }
for (const auto* fragment = &object.FirstFragment(); fragment;
fragment = fragment->NextFragment()) {
- if (fragment->LogicalTopInFlowThread() ==
- fragment_logical_top_in_flow_thread_)
+ if (fragment->FragmentID() == fragment_id_)
return fragment;
}
// No fragment of the current painting object matches the layer fragment,
@@ -164,20 +165,44 @@ 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
+ const FragmentData* FragmentToPaint(const LayoutObject& object) const {
+ if (const auto* box = DynamicTo<LayoutBox>(&object)) {
+ // We're are looking up FragmentData via LayoutObject, even though the
+ // object has NG fragments. This happens with objects that don't support
+ // fragment traversal, such as replaced content. We cannot use legacy-
+ // based lookup in such cases, as we might not have set a fragment ID to
+ // match against. Since we got here, though, it has to mean that we should
+ // paint the one and only fragment.
+ if (box->PhysicalFragmentCount()) {
+ // TODO(mstensho): We should DCHECK that box->PhysicalFragmentCount() is
+ // exactly 1 here (i.e. that the object is monolithic), but we are not
+ // ready for that yet, as there's code that enters legacy paint
+ // functions when we're traversing the fragment tree. See
+ // e.g. NGBoxFragmentPainter::RecordScrollHitTestData(), and how it does
+ // the job by invoking BoxPainter, which has no concept of
+ // fragments. One of the tests that would fail with such a DCHECK here
+ // is:
+ // virtual/layout_ng_block_frag/fast/multicol/overflow-across-columns.html
+ return &box->FirstFragment();
+ }
+ }
+ return LegacyFragmentToPaint(object);
+ }
+
+ // Returns the FragmentData of the specified physical fragment. If we're
+ // performing fragment traversal, 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())
+ if (fragment_id_ == WTF::kNotFound)
return fragment.GetFragmentData();
- return FragmentToPaint(*fragment.GetLayoutObject());
+ return LegacyFragmentToPaint(*fragment.GetLayoutObject());
}
- void SetFragmentLogicalTopInFlowThread(LayoutUnit fragment_logical_top) {
- fragment_logical_top_in_flow_thread_ = fragment_logical_top;
- }
+ wtf_size_t FragmentID() const { return fragment_id_; }
+ void SetFragmentID(wtf_size_t id) { fragment_id_ = id; }
+ void SetIsInFragmentTraversal() { fragment_id_ = WTF::kNotFound; }
bool IsPaintingScrollingBackground() const {
DCHECK(RuntimeEnabledFeatures::CompositeAfterPaintEnabled());
@@ -206,9 +231,13 @@ struct CORE_EXPORT PaintInfo {
// The box model object that originates the current painting.
const LayoutBoxModelObject* paint_container_;
- // The logical top of the current fragment of the self-painting PaintLayer
- // which initiated the current painting, in the containing flow thread.
- LayoutUnit fragment_logical_top_in_flow_thread_;
+ // The ID of the fragment that we're currently painting.
+ //
+ // This is always used in legacy block fragmentation. In NG block
+ // fragmentation, it's only used when painting self-painting non-atomic
+ // inlines (because we currently have no way of mapping from
+ // NGPhysicalFragment to FragmentData in such cases).
+ wtf_size_t fragment_id_ = WTF::kNotFound;
PaintLayerFlags paint_flags_;
const GlobalPaintFlags global_paint_flags_;
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 ea066d96c00..648f2c0bb20 100644
--- a/chromium/third_party/blink/renderer/core/paint/paint_invalidator.cc
+++ b/chromium/third_party/blink/renderer/core/paint/paint_invalidator.cc
@@ -4,6 +4,7 @@
#include "third_party/blink/renderer/core/paint/paint_invalidator.h"
+#include "base/trace_event/trace_event.h"
#include "third_party/abseil-cpp/absl/types/optional.h"
#include "third_party/blink/renderer/core/accessibility/ax_object_cache.h"
#include "third_party/blink/renderer/core/editing/frame_selection.h"
@@ -17,7 +18,6 @@
#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/mobile_metrics/mobile_friendliness_checker.h"
#include "third_party/blink/renderer/core/page/link_highlight.h"
@@ -32,14 +32,12 @@
namespace blink {
void PaintInvalidator::UpdatePaintingLayer(const LayoutObject& object,
- PaintInvalidatorContext& context,
- bool is_ng_painting) {
+ PaintInvalidatorContext& context) {
if (object.HasLayer() &&
To<LayoutBoxModelObject>(object).HasSelfPaintingLayer()) {
context.painting_layer = To<LayoutBoxModelObject>(object).Layer();
- } else if (!is_ng_painting &&
- (object.IsColumnSpanAll() ||
- object.IsFloatingWithNonContainingBlockParent())) {
+ } else if (object.IsColumnSpanAll() ||
+ object.IsFloatingWithNonContainingBlockParent()) {
// See |LayoutObject::PaintingLayer| for the special-cases of floating under
// inline and multicolumn.
// Post LayoutNG the |LayoutObject::IsFloatingWithNonContainingBlockParent|
@@ -68,8 +66,7 @@ void PaintInvalidator::UpdatePaintingLayer(const LayoutObject& object,
void PaintInvalidator::UpdateDirectlyCompositedContainer(
const LayoutObject& object,
- PaintInvalidatorContext& context,
- bool is_ng_painting) {
+ PaintInvalidatorContext& context) {
if (RuntimeEnabledFeatures::CompositeAfterPaintEnabled())
return;
@@ -88,9 +85,8 @@ void PaintInvalidator::UpdateDirectlyCompositedContainer(
context.directly_composited_container_for_stacked_contents =
context.directly_composited_container =
&object.DirectlyCompositableContainer();
- } else if (!is_ng_painting &&
- (object.IsColumnSpanAll() ||
- object.IsFloatingWithNonContainingBlockParent())) {
+ } else if (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|
@@ -141,11 +137,6 @@ void PaintInvalidator::UpdateDirectlyCompositedContainer(
context.subtree_flags = 0;
}
}
-
- DCHECK_EQ(context.directly_composited_container,
- object.DirectlyCompositableContainer())
- << object;
- DCHECK_EQ(context.painting_layer, object.PaintingLayer()) << object;
}
void PaintInvalidator::UpdateFromTreeBuilderContext(
@@ -227,6 +218,7 @@ void PaintInvalidator::UpdateLayoutShiftTracking(
adjusted_old_paint_offset,
tree_builder_context.translation_2d_to_layout_shift_root_delta,
tree_builder_context.current.scroll_offset_to_layout_shift_root_delta,
+ tree_builder_context.current.pending_scroll_anchor_adjustment,
new_paint_offset, logical_height);
return;
}
@@ -288,6 +280,7 @@ void PaintInvalidator::UpdateLayoutShiftTracking(
box, property_tree_state, old_rect, new_rect, adjusted_old_paint_offset,
tree_builder_context.translation_2d_to_layout_shift_root_delta,
tree_builder_context.current.scroll_offset_to_layout_shift_root_delta,
+ tree_builder_context.current.pending_scroll_anchor_adjustment,
new_paint_offset);
}
}
@@ -309,9 +302,29 @@ bool PaintInvalidator::InvalidatePaint(
object.GetMutableForPainting().EnsureIsReadyForPaintInvalidation();
- UpdatePaintingLayer(object, context, /* is_ng_painting */ !!pre_paint_info);
- UpdateDirectlyCompositedContainer(object, context,
- /* is_ng_painting */ !!pre_paint_info);
+ UpdatePaintingLayer(object, context);
+ UpdateDirectlyCompositedContainer(object, context);
+
+#if DCHECK_IS_ON()
+ // Assert that the container state in the invalidation context is consistent
+ // with what the LayoutObject tree says. We cannot do this if we're fragment-
+ // traversing an "orphaned" object (an object that has a fragment inside a
+ // fragmentainer, even though not all its ancestor objects have it; this may
+ // happen to OOFs, and also to floats, if they are inside a non-atomic
+ // inline). In such cases we'll just have to live with the inconsitency, which
+ // means that we'll lose any paint effects from such "missing" ancestors.
+ if (!pre_paint_info || !pre_paint_info->is_inside_orphaned_object) {
+ if (!RuntimeEnabledFeatures::CompositeAfterPaintEnabled()) {
+ DCHECK_EQ(context.directly_composited_container,
+ object.DirectlyCompositableContainer())
+ << object;
+ }
+ DCHECK_EQ(context.painting_layer, object.PaintingLayer()) << object;
+ }
+#endif // DCHECK_IS_ON()
+
+ if (AXObjectCache* cache = object.GetDocument().ExistingAXObjectCache())
+ cache->InvalidateBoundingBox(&object);
if (!object.ShouldCheckForPaintInvalidation() && !context.NeedsSubtreeWalk())
return false;
@@ -337,7 +350,7 @@ bool PaintInvalidator::InvalidatePaint(
}
if (pre_paint_info) {
- FragmentData& fragment_data = pre_paint_info->fragment_data;
+ FragmentData& fragment_data = *pre_paint_info->fragment_data;
context.fragment_data = &fragment_data;
if (tree_builder_context) {
@@ -368,10 +381,6 @@ bool PaintInvalidator::InvalidatePaint(
UpdateFromTreeBuilderContext(fragment_tree_builder_context, context);
UpdateLayoutShiftTracking(object, fragment_tree_builder_context,
context);
-
- if (auto* mf_checker =
- object.GetFrameView()->GetMobileFriendlinessChecker())
- mf_checker->NotifyInvalidatePaint(object);
} else {
context.old_paint_offset = fragment_data->PaintOffset();
}
@@ -388,8 +397,12 @@ bool PaintInvalidator::InvalidatePaint(
reason == PaintInvalidationReason::kJustCreated))
pending_delayed_paint_invalidations_.push_back(&object);
- if (AXObjectCache* cache = object.GetDocument().ExistingAXObjectCache())
- cache->InvalidateBoundingBox(&object);
+ if (auto* mf_checker =
+ object.GetFrameView()->GetMobileFriendlinessChecker()) {
+ if (tree_builder_context &&
+ (!pre_paint_info || pre_paint_info->is_last_for_node))
+ mf_checker->NotifyInvalidatePaint(object);
+ }
return reason != PaintInvalidationReason::kNone;
}
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 245e629d4ec..7c6d9747747 100644
--- a/chromium/third_party/blink/renderer/core/paint/paint_invalidator.h
+++ b/chromium/third_party/blink/renderer/core/paint/paint_invalidator.h
@@ -5,6 +5,7 @@
#ifndef THIRD_PARTY_BLINK_RENDERER_CORE_PAINT_PAINT_INVALIDATOR_H_
#define THIRD_PARTY_BLINK_RENDERER_CORE_PAINT_PAINT_INVALIDATOR_H_
+#include "third_party/abseil-cpp/absl/types/optional.h"
#include "third_party/blink/renderer/core/layout/layout_object.h"
#include "third_party/blink/renderer/core/layout/layout_shift_tracker.h"
#include "third_party/blink/renderer/core/paint/paint_property_tree_builder.h"
@@ -105,11 +106,10 @@ class PaintInvalidator final {
friend struct PaintInvalidatorContext;
ALWAYS_INLINE void UpdatePaintingLayer(const LayoutObject&,
- PaintInvalidatorContext&,
- bool is_ng_painting);
- ALWAYS_INLINE void UpdateDirectlyCompositedContainer(const LayoutObject&,
- PaintInvalidatorContext&,
- bool is_ng_painting);
+ PaintInvalidatorContext&);
+ ALWAYS_INLINE void UpdateDirectlyCompositedContainer(
+ const LayoutObject&,
+ PaintInvalidatorContext&);
ALWAYS_INLINE void UpdateFromTreeBuilderContext(
const PaintPropertyTreeBuilderFragmentContext&,
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 4eec5f301b4..f7a02c22431 100644
--- a/chromium/third_party/blink/renderer/core/paint/paint_layer.cc
+++ b/chromium/third_party/blink/renderer/core/paint/paint_layer.cc
@@ -6,7 +6,7 @@
*
* Other contributors:
* Robert O'Callahan <roc+@cs.cmu.edu>
- * David Baron <dbaron@fas.harvard.edu>
+ * David Baron <dbaron@dbaron.org>
* Christian Biesinger <cbiesinger@web.de>
* Randall Jesup <rjesup@wgate.com>
* Roland Mainz <roland.mainz@informatik.med.uni-giessen.de>
@@ -48,6 +48,7 @@
#include "base/allocator/partition_allocator/partition_alloc.h"
#include "base/containers/adapters.h"
+#include "build/build_config.h"
#include "third_party/blink/public/platform/task_type.h"
#include "third_party/blink/renderer/core/animation/scroll_timeline.h"
#include "third_party/blink/renderer/core/css/css_property_names.h"
@@ -85,6 +86,7 @@
#include "third_party/blink/renderer/core/paint/paint_layer_painter.h"
#include "third_party/blink/renderer/core/paint/paint_layer_scrollable_area.h"
#include "third_party/blink/renderer/core/paint/rounded_border_geometry.h"
+#include "third_party/blink/renderer/core/style/computed_style.h"
#include "third_party/blink/renderer/core/style/reference_clip_path_operation.h"
#include "third_party/blink/renderer/core/style/shape_clip_path_operation.h"
#include "third_party/blink/renderer/platform/bindings/runtime_call_stats.h"
@@ -195,7 +197,7 @@ PaintLayer::PaintLayer(LayoutBoxModelObject& layout_object)
needs_cull_rect_update_(false),
forces_children_cull_rect_update_(false),
descendant_needs_cull_rect_update_(false),
- previous_paint_result_(kFullyPainted),
+ previous_paint_result_(kMayBeClippedByCullRect),
needs_paint_phase_descendant_outlines_(false),
needs_paint_phase_float_(false),
has_non_isolated_descendant_with_blend_mode_(false),
@@ -206,6 +208,7 @@ PaintLayer::PaintLayer(LayoutBoxModelObject& layout_object)
self_painting_status_changed_(false),
filter_on_effect_node_dirty_(false),
backdrop_filter_on_effect_node_dirty_(false),
+ has_filter_that_moves_pixels_(false),
is_under_svg_hidden_container_(false),
descendant_has_direct_or_scrolling_compositing_reason_(false),
needs_compositing_reasons_update_(
@@ -969,10 +972,8 @@ PaintLayer* PaintLayer::ContainingLayer(const PaintLayer* ancestor,
// inline parent to find the actual containing layer through the containing
// block chain.
// Column span need to find the containing layer through its containing block.
- // A rendered legend needs to find the containing layer through its containing
- // block to skip anonymous fieldset content box.
if ((!Parent() || Parent()->GetLayoutObject().IsLayoutBlock()) &&
- !layout_object.IsColumnSpanAll() && !layout_object.IsRenderedLegend())
+ !layout_object.IsColumnSpanAll())
return Parent();
return SlowContainingLayer(ancestor, skipped_ancestor, &layout_object);
@@ -1135,6 +1136,21 @@ PaintLayer* PaintLayer::EnclosingDirectlyCompositableLayer(
return nullptr;
}
+const PaintLayer* PaintLayer::EnclosingCompositedScrollingLayerUnderPagination(
+ IncludeSelfOrNot include_self_or_not) const {
+ DCHECK(RuntimeEnabledFeatures::CompositeAfterPaintEnabled());
+ const auto* start_layer =
+ include_self_or_not == kIncludeSelf ? this : CompositingContainer();
+ for (const auto* curr = start_layer; curr && curr->EnclosingPaginationLayer();
+ curr = curr->CompositingContainer()) {
+ if (const auto* scrollable_area = curr->GetScrollableArea()) {
+ if (scrollable_area->NeedsCompositedScrolling())
+ return curr;
+ }
+ }
+ return nullptr;
+}
+
void PaintLayer::SetNeedsCompositingInputsUpdate(bool mark_ancestor_flags) {
SetNeedsCompositingInputsUpdateInternal();
@@ -1469,8 +1485,17 @@ void PaintLayer::RemoveOnlyThisLayerAfterStyleChange(
if (!parent_)
return;
- if (old_style && GetLayoutObject().IsStacked(*old_style))
- DirtyStackingContextZOrderLists();
+ if (old_style) {
+ if (GetLayoutObject().IsStacked(*old_style))
+ DirtyStackingContextZOrderLists();
+
+ if (RuntimeEnabledFeatures::CompositeAfterPaintEnabled() &&
+ PaintLayerPainter::PaintedOutputInvisible(*old_style)) {
+ // This layer is removed because opacity becomes 1. Do the same as
+ // StyleDidChange() on change of PaintedOutputInvisible().
+ GetLayoutObject().SetSubtreeShouldDoFullPaintInvalidation();
+ }
+ }
bool did_set_paint_invalidation = false;
if (!RuntimeEnabledFeatures::CompositeAfterPaintEnabled()) {
@@ -1675,10 +1700,12 @@ bool PaintLayer::RequiresScrollableArea() const {
// PaintLayerScrollableArea.
if (GetLayoutBox()->CanResize())
return true;
- // With custom scrollbars, we need a PaintLayerScrollableArea
- // so we can calculate the size of scrollbar gutters.
- if (GetLayoutObject().StyleRef().IsScrollbarGutterForce() &&
- GetLayoutObject().StyleRef().HasPseudoElementStyle(kPseudoIdScrollbar)) {
+ // With custom scrollbars unfortunately we may need a PaintLayerScrollableArea
+ // to be able to calculate the size of scrollbar gutters.
+ const ComputedStyle& style = GetLayoutObject().StyleRef();
+ if (style.IsScrollbarGutterStable() &&
+ style.OverflowBlockDirection() == EOverflow::kHidden &&
+ style.HasPseudoElementStyle(kPseudoIdScrollbar)) {
return true;
}
return false;
@@ -1711,26 +1738,18 @@ bool PaintLayer::HasOverflowControls() const {
GetLayoutObject().StyleRef().HasResize());
}
-void PaintLayer::AppendSingleFragmentIgnoringPagination(
+void PaintLayer::AppendSingleFragmentIgnoringPaginationForHitTesting(
PaintLayerFragments& fragments,
- const PaintLayer* root_layer,
- const CullRect* cull_rect,
- OverlayScrollbarClipBehavior overlay_scrollbar_clip_behavior,
- ShouldRespectOverflowClipType respect_overflow_clip,
- const PhysicalOffset* offset_from_root,
- const PhysicalOffset& sub_pixel_accumulation) const {
+ ShouldRespectOverflowClipType respect_overflow_clip) const {
PaintLayerFragment fragment;
- ClipRectsContext clip_rects_context(
- root_layer, &root_layer->GetLayoutObject().FirstFragment(),
- overlay_scrollbar_clip_behavior, respect_overflow_clip,
- sub_pixel_accumulation);
- Clipper(GeometryMapperOption::kUseGeometryMapper)
- .CalculateRects(clip_rects_context, &GetLayoutObject().FirstFragment(),
- cull_rect, fragment.layer_bounds,
- fragment.background_rect, fragment.foreground_rect,
- offset_from_root);
- fragment.root_fragment_data = &root_layer->GetLayoutObject().FirstFragment();
fragment.fragment_data = &GetLayoutObject().FirstFragment();
+ ClipRectsContext clip_rects_context(this, fragment.fragment_data,
+ kExcludeOverlayScrollbarSizeForHitTesting,
+ respect_overflow_clip);
+ Clipper(GeometryMapperOption::kUseGeometryMapper)
+ .CalculateRects(clip_rects_context, fragment.fragment_data, nullptr,
+ fragment.layer_bounds, fragment.background_rect,
+ fragment.foreground_rect);
if (GetLayoutObject().CanTraversePhysicalFragments()) {
// Make sure that we actually traverse the fragment tree, by providing a
// physical fragment. Otherwise we'd fall back to LayoutObject traversal.
@@ -1745,8 +1764,12 @@ bool PaintLayer::ShouldFragmentCompositedBounds(
const PaintLayer* compositing_layer) const {
if (!EnclosingPaginationLayer())
return false;
- if (RuntimeEnabledFeatures::CompositeAfterPaintEnabled())
- return true;
+ if (RuntimeEnabledFeatures::CompositeAfterPaintEnabled()) {
+ // We should not fragment composited scrolling layers and descendants, which
+ // is not only to render the scroller correctly, but also to prevent
+ // multiple cc::Layers with the same scrolling element id.
+ return !EnclosingCompositedScrollingLayerUnderPagination(kIncludeSelf);
+ }
if (Transform() &&
!PaintsWithDirectReasonIntoOwnBacking(kGlobalPaintNormalPhase))
return true;
@@ -1763,7 +1786,7 @@ bool PaintLayer::ShouldFragmentCompositedBounds(
void PaintLayer::CollectFragments(
PaintLayerFragments& fragments,
const PaintLayer* root_layer,
- const CullRect* cull_rect,
+ const CullRect* painting_cull_rect,
OverlayScrollbarClipBehavior overlay_scrollbar_clip_behavior,
ShouldRespectOverflowClipType respect_overflow_clip,
const PhysicalOffset* offset_from_root,
@@ -1794,58 +1817,63 @@ void PaintLayer::CollectFragments(
wtf_size_t physical_fragment_idx = 0u;
for (auto* fragment_data = &first_fragment_data; fragment_data;
fragment_data = fragment_data->NextFragment(), physical_fragment_idx++) {
- const FragmentData* root_fragment_data;
- if (root_layer == this) {
- root_fragment_data = fragment_data;
- } else if (should_match_fragments) {
- for (root_fragment_data = &first_root_fragment_data; root_fragment_data;
- root_fragment_data = root_fragment_data->NextFragment()) {
- if (root_fragment_data->LogicalTopInFlowThread() ==
- fragment_data->LogicalTopInFlowThread())
- break;
+ // If CullRectUpdateEnabled, skip fragment geometry logic if we are
+ // collecting fragments for painting.
+ if (!RuntimeEnabledFeatures::CullRectUpdateEnabled() ||
+ !painting_cull_rect) {
+ const FragmentData* root_fragment_data;
+ if (root_layer == this) {
+ root_fragment_data = fragment_data;
+ } else if (should_match_fragments) {
+ for (root_fragment_data = &first_root_fragment_data; root_fragment_data;
+ root_fragment_data = root_fragment_data->NextFragment()) {
+ if (root_fragment_data->FragmentID() == fragment_data->FragmentID())
+ break;
+ }
+ } else {
+ root_fragment_data = &first_root_fragment_data;
}
- } else {
- root_fragment_data = &first_root_fragment_data;
- }
- bool cant_find_fragment = !root_fragment_data;
- if (cant_find_fragment) {
- DCHECK(should_match_fragments);
- // Fall back to the first fragment, in order to have
- // PaintLayerClipper at least compute |fragment.layer_bounds|.
- root_fragment_data = &first_root_fragment_data;
- }
+ bool cant_find_fragment = !root_fragment_data;
+ if (cant_find_fragment) {
+ DCHECK(should_match_fragments);
+ // Fall back to the first fragment, in order to have
+ // PaintLayerClipper at least compute |fragment.layer_bounds|.
+ root_fragment_data = &first_root_fragment_data;
+ }
- ClipRectsContext clip_rects_context(
- root_layer, root_fragment_data, overlay_scrollbar_clip_behavior,
- respect_overflow_clip, sub_pixel_accumulation);
-
- absl::optional<CullRect> fragment_cull_rect;
- if (cull_rect) {
- // |cull_rect| is in the coordinate space of |root_layer| (i.e. the
- // space of |root_layer|'s first fragment). Map the rect to the space of
- // the current root fragment.
- auto rect = cull_rect->Rect();
- first_root_fragment_data.MapRectToFragment(*root_fragment_data, rect);
- fragment_cull_rect.emplace(rect);
- }
+ ClipRectsContext clip_rects_context(
+ root_layer, root_fragment_data, overlay_scrollbar_clip_behavior,
+ respect_overflow_clip, sub_pixel_accumulation);
+
+ absl::optional<CullRect> fragment_cull_rect;
+ if (painting_cull_rect) {
+ // |cull_rect| is in the coordinate space of |root_layer| (i.e. the
+ // space of |root_layer|'s first fragment). Map the rect to the space of
+ // the current root fragment.
+ auto rect = painting_cull_rect->Rect();
+ first_root_fragment_data.MapRectToFragment(*root_fragment_data, rect);
+ fragment_cull_rect.emplace(rect);
+ }
- Clipper(GeometryMapperOption::kUseGeometryMapper)
- .CalculateRects(
- clip_rects_context, fragment_data,
- fragment_cull_rect ? &*fragment_cull_rect : nullptr,
- fragment.layer_bounds, fragment.background_rect,
- fragment.foreground_rect,
- offset_from_root_can_be_used ? offset_from_root : nullptr);
-
- if (cant_find_fragment) {
- // If we couldn't find a matching fragment when |should_match_fragments|
- // was true, then fall back to no clip.
- fragment.background_rect.Reset();
- fragment.foreground_rect.Reset();
+ Clipper(GeometryMapperOption::kUseGeometryMapper)
+ .CalculateRects(
+ clip_rects_context, fragment_data,
+ fragment_cull_rect ? &*fragment_cull_rect : nullptr,
+ fragment.layer_bounds, fragment.background_rect,
+ fragment.foreground_rect,
+ offset_from_root_can_be_used ? offset_from_root : nullptr);
+
+ if (cant_find_fragment) {
+ // If we couldn't find a matching fragment when |should_match_fragments|
+ // was true, then fall back to no clip.
+ fragment.background_rect.Reset();
+ fragment.foreground_rect.Reset();
+ }
+
+ fragment.root_fragment_data = root_fragment_data;
}
- fragment.root_fragment_data = root_fragment_data;
fragment.fragment_data = fragment_data;
if (GetLayoutObject().CanTraversePhysicalFragments()) {
@@ -1974,6 +2002,16 @@ HitTestingTransformState PaintLayer::CreateLocalTransformState(
recursion_data.location.TransformedRect(),
FloatQuad(FloatRect(recursion_data.rect)));
+ if (container_transform_state &&
+ RuntimeEnabledFeatures::TransformInteropEnabled() &&
+ (!container_layer || &container_layer->GetLayoutObject() !=
+ GetLayoutObject().NearestAncestorForElement())) {
+ // Our parent *layer* is preserve-3d, but that preserve-3d doesn't
+ // apply to this layer because our element is not a child of our
+ // parent layer's element.
+ transform_state.Flatten();
+ }
+
PhysicalOffset offset;
if (container_transform_state)
ConvertToLayerCoords(container_layer, offset);
@@ -2203,11 +2241,9 @@ PaintLayer* PaintLayer::HitTestLayer(PaintLayer* root_layer,
if (recursion_data.intersects_location) {
layer_fragments.emplace();
if (applied_transform) {
- DCHECK(root_layer == this);
- PhysicalOffset ignored;
- AppendSingleFragmentIgnoringPagination(
- *layer_fragments, root_layer, nullptr,
- kExcludeOverlayScrollbarSizeForHitTesting, clip_behavior, &ignored);
+ DCHECK_EQ(root_layer, this);
+ AppendSingleFragmentIgnoringPaginationForHitTesting(*layer_fragments,
+ clip_behavior);
} else {
CollectFragments(*layer_fragments, root_layer, nullptr,
kExcludeOverlayScrollbarSizeForHitTesting,
@@ -2872,19 +2908,11 @@ void PaintLayer::ExpandRectForSelfPaintingDescendants(
if (!HasSelfPaintingLayerDescendant())
return;
- if (const auto* box = GetLayoutBox()) {
- // If the layer clips overflow and all descendants are contained, then no
- // need to expand for children. Not checking kIncludeAncestorClips because
- // the clip of the current layer is always applied. The doesn't check
- // whether the non-contained descendants are actual descendants of this
- // layer in paint order because it's not easy.
- if (box->ShouldClipOverflowAlongBothAxis() &&
- !HasNonContainedAbsolutePositionDescendant() &&
- !(HasFixedPositionDescendant() &&
- !box->CanContainFixedPositionObjects())) {
- return;
- }
- }
+ // If the layer is known to clip the whole subtree, then we don't need to
+ // expand for children. Not checking kIncludeAncestorClips because the clip of
+ // the current layer is always applied.
+ if (KnownToClipSubtree())
+ return;
PaintLayerPaintOrderIterator iterator(*this, kAllChildren);
while (PaintLayer* child_layer = iterator.Next()) {
@@ -2901,6 +2929,22 @@ void PaintLayer::ExpandRectForSelfPaintingDescendants(
}
}
+bool PaintLayer::KnownToClipSubtree() const {
+ if (const auto* box = GetLayoutBox()) {
+ if (!box->ShouldClipOverflowAlongBothAxis())
+ return false;
+ if (HasNonContainedAbsolutePositionDescendant())
+ return false;
+ if (HasFixedPositionDescendant() && !box->CanContainFixedPositionObjects())
+ return false;
+ // The root frame's clip is special at least in Android WebView.
+ if (is_root_layer_ && box->GetFrame()->IsLocalRoot())
+ return false;
+ return true;
+ }
+ return false;
+}
+
PhysicalRect PaintLayer::BoundingBoxForCompositing() const {
return BoundingBoxForCompositingInternal(
*this, nullptr,
@@ -3131,8 +3175,8 @@ bool PaintLayer::SupportsSubsequenceCaching() const {
if (EnclosingPaginationLayer())
return false;
- // SVG paints atomically.
- if (GetLayoutObject().IsSVGRoot())
+ // SVG root and SVG foreign object paint atomically.
+ if (GetLayoutObject().IsSVGRoot() || GetLayoutObject().IsSVGForeignObject())
return true;
// Don't create subsequence for the document element because the subsequence
@@ -3141,8 +3185,8 @@ bool PaintLayer::SupportsSubsequenceCaching() const {
if (GetLayoutObject().IsDocumentElement())
return false;
- // Create subsequence for only stacking contexts whose painting are atomic.
- return GetLayoutObject().IsStackingContext();
+ // Create subsequence for only stacked objects whose paintings are atomic.
+ return GetLayoutObject().IsStacked();
}
ScrollingCoordinator* PaintLayer::GetScrollingCoordinator() {
@@ -3157,6 +3201,7 @@ bool PaintLayer::CompositesWithTransform() const {
bool PaintLayer::BackgroundIsKnownToBeOpaqueInRect(
const PhysicalRect& local_rect,
bool should_check_children) const {
+ DCHECK(!RuntimeEnabledFeatures::CompositeAfterPaintEnabled());
// We can't use hasVisibleContent(), because that will be true if our
// layoutObject is hidden, but some child is visible and that child doesn't
// cover the entire rect.
@@ -3201,6 +3246,7 @@ bool PaintLayer::BackgroundIsKnownToBeOpaqueInRect(
bool PaintLayer::ChildBackgroundIsKnownToBeOpaqueInRect(
const PhysicalRect& local_rect) const {
+ DCHECK(!RuntimeEnabledFeatures::CompositeAfterPaintEnabled());
PaintLayerPaintOrderReverseIterator reverse_iterator(*this, kAllChildren);
while (PaintLayer* child_layer = reverse_iterator.Next()) {
// Stop at composited paint boundaries and non-self-painting layers.
@@ -3228,8 +3274,7 @@ bool PaintLayer::ShouldBeSelfPaintingLayer() const {
return GetLayoutObject().LayerTypeRequired() == kNormalPaintLayer ||
(scrollable_area_ && scrollable_area_->HasOverlayOverflowControls()) ||
ScrollsOverflow() ||
- (RuntimeEnabledFeatures::CompositeSVGEnabled() &&
- GetLayoutObject().IsSVGRoot() &&
+ (GetLayoutObject().IsSVGRoot() &&
To<LayoutSVGRoot>(GetLayoutObject())
.HasDescendantCompositingReasons());
}
@@ -3420,6 +3465,8 @@ void PaintLayer::StyleDidChange(StyleDifference diff,
const ComputedStyle* old_style) {
UpdateScrollableArea();
+ has_filter_that_moves_pixels_ = ComputeHasFilterThatMovesPixels();
+
if (AttemptDirectCompositingUpdate(diff, old_style)) {
if (diff.HasDifference())
GetLayoutObject().SetNeedsPaintPropertyUpdate();
@@ -3483,40 +3530,39 @@ void PaintLayer::StyleDidChange(StyleDifference diff,
UpdateBackdropFilters(old_style, new_style);
UpdateClipPath(old_style, new_style);
- if (!SelfNeedsRepaint()) {
- if (diff.ZIndexChanged()) {
- // We don't need to invalidate paint of objects when paint order
- // changes. However, we do need to repaint the containing stacking
- // context, in order to generate new paint chunks in the correct order.
- // Raster invalidation will be issued if needed during paint.
- SetNeedsRepaint();
- } else if (old_style) {
- bool new_painted_output_invisible =
- PaintLayerPainter::PaintedOutputInvisible(new_style);
- if (PaintLayerPainter::PaintedOutputInvisible(*old_style) !=
- new_painted_output_invisible) {
- if (RuntimeEnabledFeatures::CompositeAfterPaintEnabled()) {
- // Though CompositeAfterPaint ignores PaintedOutputInvisible during
- // paint, we still force repaint to ensure FCP/LCP will be reported.
- // See crbug.com/1184903. Only SetNeedsRepaint() won't work because
- // we won't repaint the display items which are already in the old
- // painted result. TODO(crbug.com/1104218): Optimize this.
- if (!new_painted_output_invisible)
- GetLayoutObject().SetSubtreeShouldDoFullPaintInvalidation();
- } else {
- // Change of PaintedOutputInvisible() will affect existence of paint
- // chunks, so needs repaint.
- SetNeedsRepaint();
- }
+ if (diff.ZIndexChanged()) {
+ // We don't need to invalidate paint of objects when paint order
+ // changes. However, we do need to repaint the containing stacking
+ // context, in order to generate new paint chunks in the correct order.
+ // Raster invalidation will be issued if needed during paint.
+ if (auto* stacking_context = AncestorStackingContext())
+ stacking_context->SetNeedsRepaint();
+ }
+
+ if (old_style) {
+ bool new_painted_output_invisible =
+ PaintLayerPainter::PaintedOutputInvisible(new_style);
+ if (PaintLayerPainter::PaintedOutputInvisible(*old_style) !=
+ new_painted_output_invisible) {
+ if (RuntimeEnabledFeatures::CompositeAfterPaintEnabled()) {
+ // Force repaint of the subtree for two purposes:
+ // 1. To ensure FCP/LCP will be reported. See crbug.com/1184903.
+ // 2. To update effectively_invisible flags of PaintChunks.
+ // TODO(crbug.com/1104218): Optimize this.
+ GetLayoutObject().SetSubtreeShouldDoFullPaintInvalidation();
+ } else {
+ // Change of PaintedOutputInvisible() will affect existence of paint
+ // chunks, so needs repaint.
+ SetNeedsRepaint();
}
}
}
}
-LayoutSize PaintLayer::PixelSnappedScrolledContentOffset() const {
+IntPoint PaintLayer::PixelSnappedScrolledContentOffset() const {
if (GetLayoutObject().IsScrollContainer())
return GetLayoutBox()->PixelSnappedScrolledContentOffset();
- return LayoutSize();
+ return IntPoint();
}
PaintLayerClipper PaintLayer::Clipper(
@@ -3666,7 +3712,7 @@ PhysicalRect PaintLayer::MapRectForFilter(const PhysicalRect& rect) const {
return PhysicalRect::EnclosingRect(MapRectForFilter(FloatRect(rect)));
}
-bool PaintLayer::HasFilterThatMovesPixels() const {
+bool PaintLayer::ComputeHasFilterThatMovesPixels() const {
if (!HasFilterInducingProperty())
return false;
const ComputedStyle& style = GetLayoutObject().StyleRef();
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 9ec703a5b60..92145820440 100644
--- a/chromium/third_party/blink/renderer/core/paint/paint_layer.h
+++ b/chromium/third_party/blink/renderer/core/paint/paint_layer.h
@@ -6,7 +6,7 @@
*
* Other contributors:
* Robert O'Callahan <roc+@cs.cmu.edu>
- * David Baron <dbaron@fas.harvard.edu>
+ * David Baron <dbaron@dbaron.org>
* Christian Biesinger <cbiesinger@web.de>
* Randall Jesup <rjesup@wgate.com>
* Roland Mainz <roland.mainz@informatik.med.uni-giessen.de>
@@ -313,7 +313,7 @@ class CORE_EXPORT PaintLayer : public DisplayItemClient {
// testing, intersection observer). Most other cases should use the unsnapped
// offset from LayoutBox (for layout) or the source offset from the
// ScrollableArea.
- LayoutSize PixelSnappedScrolledContentOffset() const;
+ IntPoint PixelSnappedScrolledContentOffset() const;
// FIXME: size() should DCHECK(!needs_position_update_) as well, but that
// fails in some tests, for example, fast/repaint/clipped-relative.html.
@@ -408,6 +408,10 @@ class CORE_EXPORT PaintLayer : public DisplayItemClient {
PaintLayer* EnclosingDirectlyCompositableLayer(IncludeSelfOrNot) const;
+ // For CompositeAfterPaint, but not for LayoutNGBlockFragmentation.
+ const PaintLayer* EnclosingCompositedScrollingLayerUnderPagination(
+ IncludeSelfOrNot) const;
+
// https://crbug.com/751768, this function can return nullptr sometimes.
// Always check the result before using it, don't just DCHECK.
PaintLayer* EnclosingLayerForPaintInvalidationCrossingFrameBoundaries() const;
@@ -619,6 +623,7 @@ class CORE_EXPORT PaintLayer : public DisplayItemClient {
PhysicalOffset&);
bool PaintsWithTransparency(GlobalPaintFlags global_paint_flags) const {
+ DCHECK(!RuntimeEnabledFeatures::CompositeAfterPaintEnabled());
return IsTransparent() && !PaintsIntoOwnBacking(global_paint_flags);
}
@@ -681,7 +686,9 @@ class CORE_EXPORT PaintLayer : public DisplayItemClient {
// Calls the above, rounding outwards.
PhysicalRect MapRectForFilter(const PhysicalRect&) const;
- bool HasFilterThatMovesPixels() const;
+ bool HasFilterThatMovesPixels() const {
+ return has_filter_that_moves_pixels_;
+ }
PaintLayerResourceInfo* ResourceInfo() const {
return rare_data_ ? rare_data_->resource_info.Get() : nullptr;
@@ -990,19 +997,10 @@ class CORE_EXPORT PaintLayer : public DisplayItemClient {
void DidUpdateScrollsOverflow();
- void AppendSingleFragmentIgnoringPagination(
- PaintLayerFragments&,
- const PaintLayer* root_layer,
- const CullRect* cull_rect,
- OverlayScrollbarClipBehavior = kIgnoreOverlayScrollbarSize,
- ShouldRespectOverflowClipType = kRespectOverflowClip,
- const PhysicalOffset* offset_from_root = nullptr,
- const PhysicalOffset& sub_pixel_accumulation = PhysicalOffset()) const;
-
void CollectFragments(
PaintLayerFragments&,
const PaintLayer* root_layer,
- const CullRect* cull_rect,
+ const CullRect* painting_cull_rect,
OverlayScrollbarClipBehavior = kIgnoreOverlayScrollbarSize,
ShouldRespectOverflowClipType = kRespectOverflowClip,
const PhysicalOffset* offset_from_root = nullptr,
@@ -1162,6 +1160,8 @@ class CORE_EXPORT PaintLayer : public DisplayItemClient {
needs_paint_offset_translation_for_compositing_ = b;
}
+ bool KnownToClipSubtree() const;
+
private:
PhysicalRect LocalBoundingBoxForCompositingOverlapTest() const;
bool PaintsWithDirectReasonIntoOwnBacking(GlobalPaintFlags) const;
@@ -1185,6 +1185,10 @@ class CORE_EXPORT PaintLayer : public DisplayItemClient {
void UpdateHasSelfPaintingLayerDescendant() const;
+ void AppendSingleFragmentIgnoringPaginationForHitTesting(
+ PaintLayerFragments&,
+ ShouldRespectOverflowClipType) const;
+
struct HitTestRecursionData {
const PhysicalRect& rect;
// Whether location.Intersects(rect) returns true.
@@ -1333,6 +1337,8 @@ class CORE_EXPORT PaintLayer : public DisplayItemClient {
needs_reorder_overlay_overflow_controls_ = b;
}
+ bool ComputeHasFilterThatMovesPixels() const;
+
// Self-painting layer is an optimization where we avoid the heavy Layer
// painting machinery for a Layer allocated only to handle the overflow clip
// case.
@@ -1404,6 +1410,9 @@ class CORE_EXPORT PaintLayer : public DisplayItemClient {
unsigned filter_on_effect_node_dirty_ : 1;
unsigned backdrop_filter_on_effect_node_dirty_ : 1;
+ // Caches |ComputeHasFilterThatMovesPixels()|, updated on style changes.
+ unsigned has_filter_that_moves_pixels_ : 1;
+
// True if the current subtree is underneath a LayoutSVGHiddenContainer
// ancestor.
unsigned is_under_svg_hidden_container_ : 1;
diff --git a/chromium/third_party/blink/renderer/core/paint/paint_layer_clipper.cc b/chromium/third_party/blink/renderer/core/paint/paint_layer_clipper.cc
index 85d08c83b45..92e3f133bfe 100644
--- a/chromium/third_party/blink/renderer/core/paint/paint_layer_clipper.cc
+++ b/chromium/third_party/blink/renderer/core/paint/paint_layer_clipper.cc
@@ -6,7 +6,7 @@
*
* Other contributors:
* Robert O'Callahan <roc+@cs.cmu.edu>
- * David Baron <dbaron@fas.harvard.edu>
+ * David Baron <dbaron@dbaron.org>
* Christian Biesinger <cbiesinger@web.de>
* Randall Jesup <rjesup@wgate.com>
* Roland Mainz <roland.mainz@informatik.med.uni-giessen.de>
diff --git a/chromium/third_party/blink/renderer/core/paint/paint_layer_clipper.h b/chromium/third_party/blink/renderer/core/paint/paint_layer_clipper.h
index 680c0b5400b..707bc99c901 100644
--- a/chromium/third_party/blink/renderer/core/paint/paint_layer_clipper.h
+++ b/chromium/third_party/blink/renderer/core/paint/paint_layer_clipper.h
@@ -6,7 +6,7 @@
*
* Other contributors:
* Robert O'Callahan <roc+@cs.cmu.edu>
- * David Baron <dbaron@fas.harvard.edu>
+ * David Baron <dbaron@dbaron.org>
* Christian Biesinger <cbiesinger@web.de>
* Randall Jesup <rjesup@wgate.com>
* Roland Mainz <roland.mainz@informatik.med.uni-giessen.de>
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 9ffa07ab179..ab911889fa2 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
@@ -81,6 +81,7 @@ struct PaintLayerFragment {
// Defines the coordinate space of the above rects:
// root_fragment_data->LocalBorderBoxProperties().Transform() +
// root_fragment_data.PaintOffset().
+ // It's for legacy cull rect calculation (pre-CompositeAfterPaint) only.
const FragmentData* root_fragment_data = nullptr;
// The corresponding FragmentData of this structure.
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 34ef71b4929..b263886d8a2 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
@@ -25,6 +25,7 @@
#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_effectively_invisible.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"
@@ -59,6 +60,9 @@ bool PaintLayerPainter::PaintedOutputInvisible(const ComputedStyle& style) {
if (style.HasWillChangeOpacityHint())
return false;
+ if (style.HasCurrentOpacityAnimation())
+ return false;
+
// 0.0004f < 1/2048. With 10-bit color channels (only available on the
// newest Macs; otherwise it's 8-bit), we see that an alpha of 1/2048 or
// less leads to a color output of less than 0.5 in all channels, hence
@@ -87,17 +91,27 @@ PaintResult PaintLayerPainter::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() &&
- PaintedOutputInvisible(paint_layer_.GetLayoutObject().StyleRef()))
- return kFullyPainted;
-
- paint_flags |= kPaintLayerHaveTransparency;
+ if (!RuntimeEnabledFeatures::CompositeAfterPaintEnabled() &&
+ paint_layer_.PaintsWithTransparency(
+ painting_info.GetGlobalPaintFlags()) &&
+ PaintedOutputInvisible(paint_layer_.GetLayoutObject().StyleRef())) {
+ return kFullyPainted;
}
- // If the transform can't be inverted, then don't paint anything.
- if (paint_layer_.PaintsWithTransform(painting_info.GetGlobalPaintFlags()) &&
+ // If the transform can't be inverted, don't paint anything. We still need
+ // to paint with CompositeAfterPaint if there are animations to ensure the
+ // animation can be setup to run on the compositor.
+ bool paint_non_invertible_transforms = false;
+ if (RuntimeEnabledFeatures::CompositeAfterPaintEnabled()) {
+ const auto* properties =
+ paint_layer_.GetLayoutObject().FirstFragment().PaintProperties();
+ if (properties && properties->Transform() &&
+ properties->Transform()->HasActiveTransformAnimation()) {
+ paint_non_invertible_transforms = true;
+ }
+ }
+ if (!paint_non_invertible_transforms &&
+ paint_layer_.PaintsWithTransform(painting_info.GetGlobalPaintFlags()) &&
!paint_layer_.RenderableTransform(painting_info.GetGlobalPaintFlags())
.IsInvertible()) {
return kFullyPainted;
@@ -211,15 +225,37 @@ bool PaintLayerPainter::ShouldUseInfiniteCullRectInternal(
if (const auto* properties =
paint_layer_.GetLayoutObject().FirstFragment().PaintProperties()) {
+ // Cull rect mapping doesn't work under perspective in some cases.
+ // See http://crbug.com/887558 for details.
if (properties->Perspective())
return true;
if (for_cull_rect_update) {
- // A CSS transform can also have perspective like
- // "transform: perspective(100px) rotateY(45deg)".
if (const auto* transform = properties->Transform()) {
+ // A CSS transform can also have perspective like
+ // "transform: perspective(100px) rotateY(45deg)". In these cases, we
+ // also want to skip cull rect mapping. See http://crbug.com/887558 for
+ // details.
if (!transform->IsIdentityOr2DTranslation() &&
- transform->Matrix().HasPerspective())
+ transform->Matrix().HasPerspective()) {
+ return true;
+ }
+
+ // Ensure content under animating transforms is not culled out, even if
+ // the initial matrix is non-invertible.
+ if (transform->HasActiveTransformAnimation() &&
+ !transform->IsIdentityOr2DTranslation() &&
+ !transform->Matrix().IsInvertible()) {
+ return true;
+ }
+
+ // As an optimization, skip cull rect updating for non-composited
+ // transforms which have already been painted. This is because the cull
+ // rect update, which needs to do complex mapping of the cull rect, can
+ // be more expensive than over-painting.
+ if (!transform->HasDirectCompositingReasons() &&
+ paint_layer_.PreviousPaintResult() == kFullyPainted) {
return true;
+ }
}
}
}
@@ -277,35 +313,7 @@ void PaintLayerPainter::AdjustForPaintProperties(
return;
if (RuntimeEnabledFeatures::CompositeAfterPaintEnabled()) {
- auto& cull_rect = painting_info.cull_rect;
- // CullRect::ApplyTransforms() requires the cull rect in the source
- // transform space. Convert cull_rect from the root layer's local space.
- //
- // TODO(paint-dev): Become block fragmentation aware. Just using the first
- // fragment seems wrong.
- cull_rect.MoveBy(RoundedIntPoint(first_root_fragment.PaintOffset()));
- absl::optional<CullRect> old_cull_rect;
- if (!paint_layer_.SelfOrDescendantNeedsRepaint() &&
- !RuntimeEnabledFeatures::CullRectUpdateEnabled()) {
- old_cull_rect = paint_layer_.PreviousCullRect();
- // Convert old_cull_rect into the layer's transform space.
- old_cull_rect->MoveBy(RoundedIntPoint(first_fragment.PaintOffset()));
- }
- if (paint_flags & kPaintLayerPaintingOverflowContents) {
- // Use PostScrollTranslation as the source transform to avoid clipping
- // of the scrolling contents in CullRect::ApplyTransforms().
- source_transform = &first_root_fragment.PostScrollTranslation();
- // Map cull_rect into scrolling contents space (i.e. source_transform).
- if (const auto* properties = first_root_fragment.PaintProperties()) {
- if (const auto* scroll_translation = properties->ScrollTranslation())
- cull_rect.Move(-scroll_translation->Translation2D());
- }
- }
- cull_rect.ApplyTransforms(source_transform->Unalias(),
- destination_transform.Unalias(), old_cull_rect);
- // Convert cull_rect from the layer's transform space to the layer's local
- // space.
- cull_rect.MoveBy(-RoundedIntPoint(first_fragment.PaintOffset()));
+ DCHECK(RuntimeEnabledFeatures::CullRectUpdateEnabled());
} else if (!painting_info.cull_rect.IsInfinite()) {
auto rect = painting_info.cull_rect.Rect();
const FragmentData& primary_stitching_fragment =
@@ -432,21 +440,6 @@ PaintResult PaintLayerPainter::PaintLayerContents(
!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);
-
- absl::optional<SubsequenceRecorder> subsequence_recorder;
- if (should_create_subsequence) {
- if (!ShouldRepaintSubsequence(paint_layer_, painting_info) &&
- SubsequenceRecorder::UseCachedSubsequenceIfPossible(context,
- paint_layer_)) {
- return paint_layer_.PreviousPaintResult();
- }
- DCHECK(paint_layer_.SupportsSubsequenceCaching());
- subsequence_recorder.emplace(context, paint_layer_);
- }
-
// TODO(paint-dev): Become block fragmentation aware. Unconditionally using
// the first fragment doesn't seem right.
PhysicalOffset offset_from_root = object.FirstFragment().PaintOffset();
@@ -458,17 +451,36 @@ PaintResult PaintLayerPainter::PaintLayerContents(
if (RuntimeEnabledFeatures::CullRectUpdateEnabled()) {
if (object.FirstFragment().NextFragment()) {
result = kMayBeClippedByCullRect;
- } else if (!object.FirstFragment().GetCullRect().Rect().Contains(
- visual_rect)) {
- result = kMayBeClippedByCullRect;
- } else if (const auto* box = DynamicTo<LayoutBox>(object)) {
- PhysicalRect contents_visual_rect =
- box->PhysicalContentsVisualOverflowRect();
- contents_visual_rect.Move(object.FirstFragment().PaintOffset());
- if (!PhysicalRect(object.FirstFragment().GetContentsCullRect().Rect())
- .Contains(contents_visual_rect)) {
+ } else {
+ IntRect cull_rect = object.FirstFragment().GetCullRect().Rect();
+ bool cull_rect_intersects_self = cull_rect.Intersects(visual_rect);
+ if (!cull_rect.Contains(visual_rect))
result = kMayBeClippedByCullRect;
+
+ bool cull_rect_intersects_contents = true;
+ if (const auto* box = DynamicTo<LayoutBox>(object)) {
+ PhysicalRect contents_visual_rect =
+ box->PhysicalContentsVisualOverflowRect();
+ contents_visual_rect.Move(object.FirstFragment().PaintOffset());
+ PhysicalRect contents_cull_rect(
+ object.FirstFragment().GetContentsCullRect().Rect());
+ cull_rect_intersects_contents =
+ contents_cull_rect.Intersects(contents_visual_rect);
+ if (!contents_cull_rect.Contains(contents_visual_rect))
+ result = kMayBeClippedByCullRect;
+ } else {
+ cull_rect_intersects_contents = cull_rect_intersects_self;
}
+
+ if (!cull_rect_intersects_self && !cull_rect_intersects_contents) {
+ if (!is_painting_overflow_contents &&
+ paint_layer_.KnownToClipSubtree()) {
+ paint_layer_.SetPreviousPaintResult(kMayBeClippedByCullRect);
+ return kMayBeClippedByCullRect;
+ }
+ should_paint_content = false;
+ }
+
// The above doesn't consider clips on non-self-painting contents.
// Will update in ScopedBoxContentsPaintState.
}
@@ -485,26 +497,45 @@ PaintResult PaintLayerPainter::PaintLayerContents(
if (should_paint_content || should_paint_self_outline ||
is_painting_overlay_overflow_controls) {
- // Collect the fragments. This will compute the clip rectangles and paint
- // offsets for each layer fragment.
+ // Collect the fragments. If CullRectUpdate is enabled, this will just
+ // create a light-weight adapter from FragmentData to PaintLayerFragment
+ // and we'll remove the adapter in the future. Otherwise this will compute
+ // the clip rectangles and paint offsets for each layer fragment.
paint_layer_.CollectFragments(
layer_fragments, local_painting_info.root_layer,
&local_painting_info.cull_rect, kIgnoreOverlayScrollbarSize,
respect_overflow_clip, &offset_from_root,
local_painting_info.sub_pixel_accumulation);
- // PaintLayer::CollectFragments depends on the paint dirty rect in
- // complicated ways. For now, always assume a partially painted output
- // for fragmented content.
- if (layer_fragments.size() > 1)
- result = kMayBeClippedByCullRect;
-
- if (should_paint_content) {
- should_paint_content = AtLeastOneFragmentIntersectsDamageRect(
- layer_fragments, local_painting_info, paint_flags, offset_from_root);
- if (!should_paint_content)
+ if (!RuntimeEnabledFeatures::CullRectUpdateEnabled()) {
+ // PaintLayer::CollectFragments depends on the paint dirty rect in
+ // complicated ways. For now, always assume a partially painted output
+ // for fragmented content.
+ if (layer_fragments.size() > 1)
result = kMayBeClippedByCullRect;
+
+ if (should_paint_content) {
+ should_paint_content = AtLeastOneFragmentIntersectsDamageRect(
+ layer_fragments, local_painting_info, paint_flags,
+ offset_from_root);
+ if (!should_paint_content)
+ result = kMayBeClippedByCullRect;
+ }
+ }
+ }
+
+ bool should_create_subsequence =
+ should_paint_content &&
+ ShouldCreateSubsequence(paint_layer_, context, painting_info);
+ absl::optional<SubsequenceRecorder> subsequence_recorder;
+ if (should_create_subsequence) {
+ if (!ShouldRepaintSubsequence(paint_layer_, painting_info) &&
+ SubsequenceRecorder::UseCachedSubsequenceIfPossible(context,
+ paint_layer_)) {
+ return paint_layer_.PreviousPaintResult();
}
+ DCHECK(paint_layer_.SupportsSubsequenceCaching());
+ subsequence_recorder.emplace(context, paint_layer_);
}
bool is_painting_root_layer = (&paint_layer_) == painting_info.root_layer;
@@ -524,6 +555,11 @@ PaintResult PaintLayerPainter::PaintLayerContents(
!is_painting_overlay_overflow_controls;
bool is_video = IsA<LayoutVideo>(object);
+ absl::optional<ScopedEffectivelyInvisible> effectively_invisible;
+ if (RuntimeEnabledFeatures::CompositeAfterPaintEnabled() &&
+ PaintedOutputInvisible(object.StyleRef()))
+ effectively_invisible.emplace(context.GetPaintController());
+
absl::optional<ScopedPaintChunkHint> paint_chunk_hint;
if (should_paint_content) {
paint_chunk_hint.emplace(context.GetPaintController(),
@@ -623,6 +659,8 @@ bool PaintLayerPainter::AtLeastOneFragmentIntersectsDamageRect(
const PaintLayerPaintingInfo& local_painting_info,
PaintLayerFlags local_paint_flags,
const PhysicalOffset& offset_from_root) {
+ DCHECK(!RuntimeEnabledFeatures::CullRectUpdateEnabled());
+
if (&paint_layer_ == local_painting_info.root_layer &&
(local_paint_flags & kPaintLayerPaintingOverflowContents))
return true;
@@ -736,18 +774,19 @@ void PaintLayerPainter::PaintFragmentWithPhase(
context.GetPaintController(), chunk_properties, paint_layer_,
DisplayItem::PaintPhaseToDrawingType(phase));
- PaintInfo paint_info(
- context, cull_rect, phase, painting_info.GetGlobalPaintFlags(),
- paint_flags, &painting_info.root_layer->GetLayoutObject(),
- fragment.fragment_data ? fragment.fragment_data->LogicalTopInFlowThread()
- : LayoutUnit());
+ PaintInfo paint_info(context, cull_rect, phase,
+ painting_info.GetGlobalPaintFlags(), paint_flags,
+ &painting_info.root_layer->GetLayoutObject());
if (paint_layer_.GetLayoutObject().ChildPaintBlockedByDisplayLock())
paint_info.SetDescendantPaintingBlocked(true);
- if (fragment.physical_fragment)
+ if (fragment.physical_fragment) {
NGBoxFragmentPainter(*fragment.physical_fragment).Paint(paint_info);
- else
+ } else {
+ if (fragment.fragment_data)
+ paint_info.SetFragmentID(fragment.fragment_data->FragmentID());
paint_layer_.GetLayoutObject().Paint(paint_info);
+ }
}
static CullRect LegacyCullRect(const PaintLayerFragment& fragment,
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 27ad0e60a6e..c8e3343a6b5 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
@@ -35,7 +35,7 @@ class CORE_EXPORT PaintLayerPainter {
void Paint(GraphicsContext&,
const CullRect&,
const GlobalPaintFlags = kGlobalPaintNormalPhase,
- PaintLayerFlags = 0);
+ PaintLayerFlags = kPaintLayerNoFlag);
// Paint() assumes that the caller will clip to the bounds of the painting
// dirty if necessary.
PaintResult Paint(GraphicsContext&,
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 4fadc94b899..9728634781a 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
@@ -9,6 +9,7 @@
#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/graphics_context.h"
+#include "third_party/blink/renderer/platform/testing/find_cc_layer.h"
#include "third_party/blink/renderer/platform/testing/paint_property_test_helpers.h"
#include "third_party/blink/renderer/platform/testing/runtime_enabled_features_test_helpers.h"
@@ -22,23 +23,6 @@ class PaintLayerPainterTest : public PaintControllerPaintTest {
USING_FAST_MALLOC(PaintLayerPainterTest);
public:
- void ExpectPaintedOutputInvisibleAndPaintsWithTransparency(
- const char* element_name,
- bool expected_invisible,
- bool expected_paints_with_transparency) {
- // The optimization to skip painting for effectively-invisible content is
- // limited to pre-CAP.
- if (RuntimeEnabledFeatures::CompositeAfterPaintEnabled())
- return;
-
- PaintLayer* target_layer = GetPaintLayerByElementId(element_name);
- bool invisible = PaintLayerPainter::PaintedOutputInvisible(
- target_layer->GetLayoutObject().StyleRef());
- EXPECT_EQ(expected_invisible, invisible);
- EXPECT_EQ(expected_paints_with_transparency,
- target_layer->PaintsWithTransparency(kGlobalPaintNormalPhase));
- }
-
PaintController& MainGraphicsLayerPaintController() {
return GetLayoutView()
.Layer()
@@ -82,8 +66,10 @@ TEST_P(PaintLayerPainterTest, CachedSubsequenceAndChunksWithBackgrounds) {
auto* filler2 = GetLayoutObjectByElementId("filler2");
auto* container1_layer = To<LayoutBoxModelObject>(container1)->Layer();
+ auto* content1_layer = To<LayoutBoxModelObject>(content1)->Layer();
auto* filler1_layer = To<LayoutBoxModelObject>(filler1)->Layer();
auto* container2_layer = To<LayoutBoxModelObject>(container2)->Layer();
+ auto* content2_layer = To<LayoutBoxModelObject>(content2)->Layer();
auto* filler2_layer = To<LayoutBoxModelObject>(filler2)->Layer();
auto chunk_state = GetLayoutView().FirstFragment().ContentsProperties();
@@ -106,27 +92,36 @@ TEST_P(PaintLayerPainterTest, CachedSubsequenceAndChunksWithBackgrounds) {
// Check that new paint chunks were forced for the layers.
auto chunks = ContentPaintChunks();
- EXPECT_SUBSEQUENCE_FROM_CHUNK(*container1_layer, chunks.begin() + 1, 1);
- EXPECT_SUBSEQUENCE_FROM_CHUNK(*filler1_layer, chunks.begin() + 2, 1);
- EXPECT_SUBSEQUENCE_FROM_CHUNK(*container2_layer, chunks.begin() + 3, 1);
- EXPECT_SUBSEQUENCE_FROM_CHUNK(*filler2_layer, chunks.begin() + 4, 1);
+ auto chunk_it = chunks.begin();
+ EXPECT_SUBSEQUENCE_FROM_CHUNK(*container1_layer, chunk_it + 1, 2);
+ EXPECT_SUBSEQUENCE_FROM_CHUNK(*content1_layer, chunk_it + 2, 1);
+ EXPECT_SUBSEQUENCE_FROM_CHUNK(*filler1_layer, chunk_it + 3, 1);
+ EXPECT_SUBSEQUENCE_FROM_CHUNK(*container2_layer, chunk_it + 4, 2);
+ EXPECT_SUBSEQUENCE_FROM_CHUNK(*content2_layer, chunk_it + 5, 1);
+ EXPECT_SUBSEQUENCE_FROM_CHUNK(*filler2_layer, chunk_it + 6, 1);
EXPECT_THAT(
chunks,
ElementsAre(
VIEW_SCROLLING_BACKGROUND_CHUNK_COMMON,
IsPaintChunk(
- 1, 3,
+ 1, 2,
PaintChunk::Id(*container1_layer, DisplayItem::kLayerChunk),
chunk_state, nullptr, IntRect(0, 0, 200, 200)),
IsPaintChunk(
+ 2, 3, PaintChunk::Id(*content1_layer, DisplayItem::kLayerChunk),
+ chunk_state, nullptr, IntRect(0, 0, 100, 100)),
+ IsPaintChunk(
3, 4, PaintChunk::Id(*filler1_layer, DisplayItem::kLayerChunk),
chunk_state, nullptr, IntRect(0, 200, 20, 20)),
IsPaintChunk(
- 4, 6,
+ 4, 5,
PaintChunk::Id(*container2_layer, DisplayItem::kLayerChunk),
chunk_state, nullptr, IntRect(0, 220, 200, 200)),
IsPaintChunk(
+ 5, 6, PaintChunk::Id(*content2_layer, DisplayItem::kLayerChunk),
+ chunk_state, nullptr, IntRect(0, 220, 100, 100)),
+ IsPaintChunk(
6, 7, PaintChunk::Id(*filler2_layer, DisplayItem::kLayerChunk),
chunk_state, nullptr, IntRect(0, 420, 20, 20))));
};
@@ -137,10 +132,10 @@ TEST_P(PaintLayerPainterTest, CachedSubsequenceAndChunksWithBackgrounds) {
->setAttribute(html_names::kStyleAttr,
"position: absolute; width: 100px; height: 100px; "
"background-color: green");
- CachedItemAndSubsequenceCounter counter;
+ PaintController::CounterForTesting counter;
UpdateAllLifecyclePhasesForTest();
- EXPECT_EQ(6u, counter.NumNewCachedItems());
- EXPECT_EQ(3u, counter.NumNewCachedSubsequences());
+ EXPECT_EQ(6u, counter.num_cached_items);
+ EXPECT_EQ(4u, counter.num_cached_subsequences);
// We should still have the paint chunks forced by the cached subsequences.
check_results();
@@ -181,9 +176,10 @@ TEST_P(PaintLayerPainterTest, CachedSubsequenceAndChunksWithoutBackgrounds) {
auto* filler_layer = To<LayoutBoxModelObject>(filler)->Layer();
auto chunks = ContentPaintChunks();
- EXPECT_SUBSEQUENCE_FROM_CHUNK(*container_layer, chunks.begin() + 1, 4);
- EXPECT_SUBSEQUENCE_FROM_CHUNK(*content_layer, chunks.begin() + 3, 1);
- EXPECT_SUBSEQUENCE_FROM_CHUNK(*filler_layer, chunks.begin() + 4, 1);
+ EXPECT_SUBSEQUENCE_FROM_CHUNK(*container_layer, chunks.begin() + 1, 5);
+ EXPECT_SUBSEQUENCE_FROM_CHUNK(*content_layer, chunks.begin() + 3, 2);
+ EXPECT_SUBSEQUENCE_FROM_CHUNK(*inner_content_layer, chunks.begin() + 4, 1);
+ EXPECT_SUBSEQUENCE_FROM_CHUNK(*filler_layer, chunks.begin() + 5, 1);
auto container_properties =
container->FirstFragment().LocalBorderBoxProperties();
@@ -207,6 +203,10 @@ TEST_P(PaintLayerPainterTest, CachedSubsequenceAndChunksWithoutBackgrounds) {
PaintChunk::Id(*content_layer, DisplayItem::kLayerChunk),
content_properties, nullptr, IntRect(0, 0, 200, 100)),
IsPaintChunk(
+ 1, 1,
+ PaintChunk::Id(*inner_content_layer, DisplayItem::kLayerChunk),
+ content_properties, nullptr, IntRect(0, 0, 100, 100)),
+ IsPaintChunk(
1, 1, PaintChunk::Id(*filler_layer, DisplayItem::kLayerChunk),
content_properties, nullptr, IntRect(0, 100, 300, 300))));
@@ -214,8 +214,12 @@ TEST_P(PaintLayerPainterTest, CachedSubsequenceAndChunksWithoutBackgrounds) {
->setAttribute(html_names::kStyleAttr,
"position: absolute; width: 100px; height: 100px; "
"top: 100px; background-color: green");
+ PaintController::CounterForTesting counter;
UpdateAllLifecyclePhasesForTest();
+ EXPECT_EQ(1u, counter.num_cached_items); // view background.
+ EXPECT_EQ(1u, counter.num_cached_subsequences); // filler layer.
+
EXPECT_THAT(
ContentDisplayItems(),
ElementsAre(VIEW_SCROLLING_BACKGROUND_DISPLAY_ITEM,
@@ -225,6 +229,7 @@ TEST_P(PaintLayerPainterTest, CachedSubsequenceAndChunksWithoutBackgrounds) {
chunks = ContentPaintChunks();
EXPECT_SUBSEQUENCE_FROM_CHUNK(*container_layer, chunks.begin() + 1, 5);
EXPECT_SUBSEQUENCE_FROM_CHUNK(*content_layer, chunks.begin() + 3, 2);
+ EXPECT_SUBSEQUENCE_FROM_CHUNK(*inner_content_layer, chunks.begin() + 4, 1);
EXPECT_SUBSEQUENCE_FROM_CHUNK(*filler_layer, chunks.begin() + 5, 1);
EXPECT_THAT(
@@ -250,12 +255,6 @@ TEST_P(PaintLayerPainterTest, CachedSubsequenceAndChunksWithoutBackgrounds) {
}
TEST_P(PaintLayerPainterTest, CachedSubsequenceOnCullRectChange) {
- // This test doesn't work in CompositeAfterPaint mode because we always paint
- // from the local root frame view, and we always expand cull rect for
- // scrolling.
- if (RuntimeEnabledFeatures::CompositeAfterPaintEnabled())
- return;
-
SetBodyInnerHTML(R"HTML(
<div id='container1' style='position: relative; z-index: 1;
width: 200px; height: 200px; background-color: blue'>
@@ -310,15 +309,15 @@ TEST_P(PaintLayerPainterTest, CachedSubsequenceOnCullRectChange) {
IsSameId(&content3, kBackgroundType)));
UpdateAllLifecyclePhasesExceptPaint();
- CachedItemAndSubsequenceCounter counter;
+ PaintController::CounterForTesting counter;
PaintContents(IntRect(0, 100, 300, 1000));
// Container1 becomes partly in the interest rect, but uses cached subsequence
// because it was fully painted before;
// Container2's intersection with the interest rect changes;
// Content2b is out of the interest rect and outputs nothing;
// Container3 becomes out of the interest rect and outputs nothing.
- EXPECT_EQ(5u, counter.NumNewCachedItems());
- EXPECT_EQ(1u, counter.NumNewCachedSubsequences());
+ EXPECT_EQ(5u, counter.num_cached_items);
+ EXPECT_EQ(2u, counter.num_cached_subsequences);
EXPECT_THAT(ContentDisplayItems(),
ElementsAre(VIEW_SCROLLING_BACKGROUND_DISPLAY_ITEM,
@@ -356,12 +355,12 @@ TEST_P(PaintLayerPainterTest,
SetBodyInnerHTML(R"HTML(
<div id='container1' style='position: relative; z-index: 1;
width: 200px; height: 200px; background-color: blue'>
- <div id='content1' style='position: absolute; width: 100px;
+ <div id='content1' style='overflow: hidden; width: 100px;
height: 100px; background-color: red'></div>
</div>
<div id='container2' style='position: relative; z-index: 1;
width: 200px; height: 200px; background-color: blue'>
- <div id='content2' style='position: absolute; width: 100px;
+ <div id='content2' style='overflow: hidden; width: 100px;
height: 100px; background-color: green'></div>
</div>
)HTML");
@@ -390,10 +389,9 @@ TEST_P(PaintLayerPainterTest,
"position: absolute; width: 100px; height: 100px; "
"background-color: green");
UpdateAllLifecyclePhasesExceptPaint();
- CachedItemAndSubsequenceCounter counter;
+ PaintController::CounterForTesting counter;
PaintContents(IntRect(0, 0, 50, 300));
- EXPECT_EQ(4u, counter.NumNewCachedItems());
- EXPECT_EQ(1u, counter.NumNewCachedSubsequences());
+ EXPECT_EQ(4u, counter.num_cached_items);
EXPECT_THAT(ContentDisplayItems(),
ElementsAre(VIEW_SCROLLING_BACKGROUND_DISPLAY_ITEM,
@@ -450,10 +448,10 @@ TEST_P(PaintLayerPainterTest, CachedSubsequenceRetainsPreviousPaintResult) {
"display: block");
UpdateAllLifecyclePhasesExceptPaint();
EXPECT_FALSE(target_layer->SelfNeedsRepaint());
- CachedItemAndSubsequenceCounter counter;
+ PaintController::CounterForTesting counter;
UpdateAllLifecyclePhasesForTest();
- EXPECT_EQ(2u, counter.NumNewCachedItems());
- EXPECT_EQ(1u, counter.NumNewCachedSubsequences());
+ EXPECT_EQ(2u, counter.num_cached_items);
+ EXPECT_EQ(1u, counter.num_cached_subsequences);
// |target| is still partially painted.
EXPECT_EQ(kMayBeClippedByCullRect, target_layer->PreviousPaintResult());
@@ -494,8 +492,8 @@ TEST_P(PaintLayerPainterTest, CachedSubsequenceRetainsPreviousPaintResult) {
counter.Reset();
UpdateAllLifecyclePhasesForTest();
- EXPECT_EQ(2u, counter.NumNewCachedItems());
- EXPECT_EQ(0u, counter.NumNewCachedSubsequences());
+ EXPECT_EQ(2u, counter.num_cached_items);
+ EXPECT_EQ(0u, counter.num_cached_subsequences);
// |target| is still partially painted.
EXPECT_EQ(kMayBeClippedByCullRect, target_layer->PreviousPaintResult());
@@ -527,12 +525,13 @@ TEST_P(PaintLayerPainterTest, HintedPaintChunksWithBackgrounds) {
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 id='content1a' style='overflow: hidden; height: 100px'></div>
+ <div id='content1b' style='overflow: hidden; 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 id='content2a' style='overflow: hidden; height: 100px'></div>
+ <div id='content2b'
+ style='position: relative; z-index: -1; height: 100px'></div>
</div>
)HTML");
@@ -559,14 +558,9 @@ TEST_P(PaintLayerPainterTest, HintedPaintChunksWithBackgrounds) {
VIEW_SCROLLING_BACKGROUND_CHUNK_COMMON,
// Includes |container1| and |content1a|.
IsPaintChunk(
- 1, 3,
+ 1, 4,
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)),
+ chunk_state, nullptr, IntRect(0, 0, 800, 200)),
IsPaintChunk(
4, 5,
PaintChunk::Id(*container2->Layer(), DisplayItem::kLayerChunk),
@@ -575,10 +569,10 @@ TEST_P(PaintLayerPainterTest, HintedPaintChunksWithBackgrounds) {
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))));
+ IsPaintChunk(6, 7,
+ PaintChunk::Id(*container2->Layer(),
+ DisplayItem::kLayerChunkForeground),
+ chunk_state, nullptr, IntRect(0, 150, 800, 100))));
}
TEST_P(PaintLayerPainterTest, HintedPaintChunksWithoutBackgrounds) {
@@ -588,20 +582,18 @@ TEST_P(PaintLayerPainterTest, HintedPaintChunksWithoutBackgrounds) {
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 id='content1a' style='overflow: hidden; height: 100px'></div>
+ <div id='content1b' style='overflow: hidden; height: 100px'></div>
</div>
<div id='container2' style='position: relative; z-index: 1'>
- <div id='content2a' style='position: relative; height: 100px'></div>
+ <div id='content2a' style='overflow: hidden; height: 100px'></div>
<div id='content2b'
style='position: relative; z-index: -1; height: 100px'></div>
</div>
)HTML");
auto* container1 = GetLayoutBoxByElementId("container1");
- auto* content1b = GetLayoutBoxByElementId("content1b");
auto* container2 = GetLayoutBoxByElementId("container2");
- auto* content2a = GetLayoutBoxByElementId("content2a");
auto* content2b = GetLayoutBoxByElementId("content2b");
auto chunk_state = GetLayoutView().FirstFragment().ContentsProperties();
@@ -615,11 +607,7 @@ TEST_P(PaintLayerPainterTest, HintedPaintChunksWithoutBackgrounds) {
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)),
+ chunk_state, nullptr, IntRect(0, 0, 800, 200)),
IsPaintChunk(
1, 1,
PaintChunk::Id(*container2->Layer(), DisplayItem::kLayerChunk),
@@ -628,10 +616,10 @@ TEST_P(PaintLayerPainterTest, HintedPaintChunksWithoutBackgrounds) {
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))));
+ IsPaintChunk(1, 1,
+ PaintChunk::Id(*container2->Layer(),
+ DisplayItem::kLayerChunkForeground),
+ chunk_state, nullptr, IntRect(0, 150, 800, 100))));
}
TEST_P(PaintLayerPainterTest, PaintPhaseOutline) {
@@ -891,149 +879,6 @@ TEST_P(PaintLayerPainterTest, PaintPhasesUpdateOnBecomingNonSelfPainting) {
EXPECT_TRUE(html_layer.NeedsPaintPhaseDescendantOutlines());
}
-TEST_P(PaintLayerPainterTest, DontPaintWithTinyOpacity) {
- SetBodyInnerHTML(
- "<div id='target' style='background: blue; opacity: 0.0001'></div>");
- ExpectPaintedOutputInvisibleAndPaintsWithTransparency("target", true, true);
-}
-
-TEST_P(PaintLayerPainterTest, DoPaintWithTinyOpacityAndWillChangeOpacity) {
- SetBodyInnerHTML(
- "<div id='target' style='background: blue; opacity: 0.0001; "
- " will-change: opacity'></div>");
- ExpectPaintedOutputInvisibleAndPaintsWithTransparency("target", false, false);
-}
-
-TEST_P(PaintLayerPainterTest, DoPaintWithTinyOpacityAndBackdropFilter) {
- SetBodyInnerHTML(
- "<div id='target' style='background: blue; opacity: 0.0001;"
- " backdrop-filter: blur(2px);'></div>");
- ExpectPaintedOutputInvisibleAndPaintsWithTransparency("target", false, false);
-}
-
-TEST_P(PaintLayerPainterTest,
- DoPaintWithTinyOpacityAndBackdropFilterAndWillChangeOpacity) {
- SetBodyInnerHTML(
- "<div id='target' style='background: blue; opacity: 0.0001;"
- " backdrop-filter: blur(2px); will-change: opacity'></div>");
- ExpectPaintedOutputInvisibleAndPaintsWithTransparency("target", false, false);
-}
-
-TEST_P(PaintLayerPainterTest, DoPaintWithCompositedTinyOpacity) {
- SetBodyInnerHTML(
- "<div id='target' style='background: blue; opacity: 0.0001;"
- " will-change: transform'></div>");
- ExpectPaintedOutputInvisibleAndPaintsWithTransparency("target", true, false);
-}
-
-TEST_P(PaintLayerPainterTest, DoPaintWithNonTinyOpacity) {
- SetBodyInnerHTML(
- "<div id='target' style='background: blue; opacity: 0.1'></div>");
- ExpectPaintedOutputInvisibleAndPaintsWithTransparency("target", false, true);
-}
-
-TEST_P(PaintLayerPainterTest, DoPaintWithEffectAnimationZeroOpacity) {
- SetBodyInnerHTML(R"HTML(
- <style>
- div {
- width: 100px;
- height: 100px;
- animation-name: example;
- animation-duration: 4s;
- }
- @keyframes example {
- from { opacity: 0.0;}
- to { opacity: 1.0;}
- }
- </style>
- <div id='target'></div>
- )HTML");
- ExpectPaintedOutputInvisibleAndPaintsWithTransparency("target", true, false);
-}
-
-TEST_P(PaintLayerPainterTest, DoPaintWithTransformAnimationZeroOpacity) {
- SetBodyInnerHTML(R"HTML(
- <style>
- div#target {
- animation-name: example;
- animation-duration: 4s;
- opacity: 0.0;
- }
- @keyframes example {
- from { transform: translate(0px, 0px); }
- to { transform: translate(3em, 0px); }
- }
- </style>
- <div id='target'>x</div></div>
- )HTML");
- ExpectPaintedOutputInvisibleAndPaintsWithTransparency("target", true, false);
-}
-
-TEST_P(PaintLayerPainterTest,
- DoPaintWithTransformAnimationZeroOpacityWillChangeOpacity) {
- SetBodyInnerHTML(R"HTML(
- <style>
- div#target {
- animation-name: example;
- animation-duration: 4s;
- opacity: 0.0;
- will-change: opacity;
- }
- @keyframes example {
- from { transform: translate(0px, 0px); }
- to { transform: translate(3em, 0px); }
- }
- </style>
- <div id='target'>x</div></div>
- )HTML");
- ExpectPaintedOutputInvisibleAndPaintsWithTransparency("target", false, false);
-}
-
-TEST_P(PaintLayerPainterTest, DoPaintWithWillChangeOpacity) {
- SetBodyInnerHTML(R"HTML(
- <style>
- div {
- width: 100px;
- height: 100px;
- will-change: opacity;
- }
- </style>
- <div id='target'></div>
- )HTML");
- ExpectPaintedOutputInvisibleAndPaintsWithTransparency("target", false, false);
-}
-
-TEST_P(PaintLayerPainterTest, DoPaintWithZeroOpacityAndWillChangeOpacity) {
- SetBodyInnerHTML(R"HTML(
- <style>
- div {
- width: 100px;
- height: 100px;
- opacity: 0;
- will-change: opacity;
- }
- </style>
- <div id='target'></div>
- )HTML");
- ExpectPaintedOutputInvisibleAndPaintsWithTransparency("target", false, false);
-}
-
-TEST_P(PaintLayerPainterTest,
- DoPaintWithNoContentAndZeroOpacityAndWillChangeOpacity) {
- SetBodyInnerHTML(R"HTML(
- <style>
- div {
- width: 100px;
- height: 100px;
- opacity: 0;
- will-change: opacity;
- }
- </style>
- <div id='target'></div>
- )HTML");
- ExpectPaintedOutputInvisibleAndPaintsWithTransparency("target", false, false);
-}
-
using PaintLayerPainterTestCAP = PaintLayerPainterTest;
INSTANTIATE_CAP_TEST_SUITE_P(PaintLayerPainterTestCAP);
@@ -1214,6 +1059,33 @@ TEST_P(PaintLayerPainterTestCAP, ScaledAndRotatedCullRect) {
GetCullRect(*GetPaintLayerByElementId("target")).Rect());
}
+// This is a testcase for https://crbug.com/1227907 where repeated cull rect
+// updates are expensive on the motionmark microbenchmark.
+TEST_P(PaintLayerPainterTestCAP, OptimizeNonCompositedTransformUpdate) {
+ SetBodyInnerHTML(R"HTML(
+ <style>
+ #target {
+ width: 50px;
+ height: 50px;
+ background: green;
+ transform: translate(-8px, -8px);
+ }
+ </style>
+ <div id='target'></div>
+ )HTML");
+
+ // The cull rect should be correctly calculated on first paint.
+ EXPECT_EQ(IntRect(0, 0, 800, 600),
+ GetCullRect(*GetPaintLayerByElementId("target")).Rect());
+
+ // On subsequent paints, fall back to an infinite cull rect.
+ GetDocument().getElementById("target")->setAttribute(
+ html_names::kStyleAttr, "transform: rotate(10deg);");
+ UpdateAllLifecyclePhasesForTest();
+ EXPECT_EQ(CullRect::Infinite().Rect(),
+ GetCullRect(*GetPaintLayerByElementId("target")).Rect());
+}
+
TEST_P(PaintLayerPainterTestCAP, 3DRotated90DegreesCullRect) {
GetDocument().GetSettings()->SetPreferCompositingToLCDTextEnabled(true);
SetBodyInnerHTML(R"HTML(
@@ -1381,4 +1253,138 @@ TEST_P(PaintLayerPainterTestCAP, ClippedBigLayer) {
GetCullRect(*GetPaintLayerByElementId("target")).Rect());
}
+class PaintLayerPainterPaintedOutputInvisibleTest
+ : public PaintLayerPainterTest {
+ protected:
+ void RunTest() {
+ SetBodyInnerHTML(R"HTML(
+ <div id="parent">
+ <div id="target">
+ <div id="child"></div>
+ </div>
+ </div>
+ <style>
+ #parent {
+ width: 10px;
+ height: 10px;
+ will-change: transform;
+ }
+ #target {
+ width: 100px;
+ height: 100px;
+ opacity: 0.0001;
+ }
+ #child {
+ width: 200px;
+ height: 50px;
+ opacity: 0.9;
+ }
+ )HTML" + additional_style_ +
+ "</style>");
+
+ auto* parent = GetLayoutObjectByElementId("parent");
+ auto* parent_layer = To<LayoutBox>(parent)->Layer();
+ auto* target = GetLayoutObjectByElementId("target");
+ auto* target_layer = To<LayoutBox>(target)->Layer();
+ auto* child = GetLayoutObjectByElementId("child");
+ auto* child_layer = To<LayoutBox>(child)->Layer();
+
+ EXPECT_EQ(expected_invisible_,
+ PaintLayerPainter::PaintedOutputInvisible(
+ target_layer->GetLayoutObject().StyleRef()));
+
+ auto* cc_layer =
+ CcLayersByDOMElementId(GetDocument().View()->RootCcLayer(),
+ expected_composited_ ? "target" : "parent")[0];
+ ASSERT_TRUE(cc_layer);
+ EXPECT_EQ(gfx::Size(200, 100), cc_layer->bounds());
+
+ auto chunks = ContentPaintChunks();
+ if (RuntimeEnabledFeatures::CompositeAfterPaintEnabled()) {
+ EXPECT_THAT(
+ chunks,
+ ElementsAre(
+ VIEW_SCROLLING_BACKGROUND_CHUNK_COMMON,
+ IsPaintChunk(
+ 1, 1, PaintChunk::Id(*parent_layer, DisplayItem::kLayerChunk),
+ parent->FirstFragment().LocalBorderBoxProperties(), nullptr,
+ IntRect(0, 0, 10, 10)),
+ IsPaintChunk(
+ 1, 1, PaintChunk::Id(*target_layer, DisplayItem::kLayerChunk),
+ target->FirstFragment().LocalBorderBoxProperties(), nullptr,
+ IntRect(0, 0, 100, 100)),
+ IsPaintChunk(
+ 1, 1, PaintChunk::Id(*child_layer, DisplayItem::kLayerChunk),
+ child->FirstFragment().LocalBorderBoxProperties(), nullptr,
+ IntRect(0, 0, 200, 50))));
+ EXPECT_FALSE((chunks.begin() + 1)->effectively_invisible);
+ EXPECT_EQ(expected_invisible_,
+ (chunks.begin() + 2)->effectively_invisible);
+ EXPECT_EQ(expected_invisible_,
+ (chunks.begin() + 3)->effectively_invisible);
+ } else {
+ EXPECT_EQ(expected_paints_with_transparency_,
+ target_layer->PaintsWithTransparency(kGlobalPaintNormalPhase));
+ }
+ }
+
+ String additional_style_;
+ bool expected_composited_ = false;
+ bool expected_invisible_ = true;
+ bool expected_paints_with_transparency_ = true;
+};
+
+INSTANTIATE_PAINT_TEST_SUITE_P(PaintLayerPainterPaintedOutputInvisibleTest);
+
+TEST_P(PaintLayerPainterPaintedOutputInvisibleTest, TinyOpacity) {
+ expected_composited_ = false;
+ expected_invisible_ = true;
+ expected_paints_with_transparency_ = true;
+ RunTest();
+}
+
+TEST_P(PaintLayerPainterPaintedOutputInvisibleTest,
+ TinyOpacityAndWillChangeOpacity) {
+ additional_style_ = "#target { will-change: opacity; }";
+ expected_composited_ = true;
+ expected_invisible_ = false;
+ expected_paints_with_transparency_ = false;
+ RunTest();
+}
+
+TEST_P(PaintLayerPainterPaintedOutputInvisibleTest,
+ TinyOpacityAndBackdropFilter) {
+ additional_style_ = "#target { backdrop-filter: blur(2px); }";
+ expected_composited_ = true;
+ expected_invisible_ = false;
+ expected_paints_with_transparency_ = false;
+ RunTest();
+}
+
+TEST_P(PaintLayerPainterPaintedOutputInvisibleTest,
+ TinyOpacityAndWillChangeTransform) {
+ additional_style_ = "#target { will-change: transform; }";
+ expected_composited_ = true;
+ expected_invisible_ = true;
+ expected_paints_with_transparency_ = false;
+ RunTest();
+}
+
+TEST_P(PaintLayerPainterPaintedOutputInvisibleTest, NonTinyOpacity) {
+ additional_style_ = "#target { opacity: 0.5; }";
+ expected_composited_ = false;
+ expected_invisible_ = false;
+ expected_paints_with_transparency_ = true;
+ RunTest();
+}
+
+TEST_P(PaintLayerPainterPaintedOutputInvisibleTest,
+ NonTinyOpacityAndWillChangeOpacity) {
+ additional_style_ = "#target { opacity: 1; will-change: opacity; }";
+ expected_composited_ = true;
+ expected_invisible_ = false;
+ expected_paints_with_transparency_ = false;
+ RunTest();
+}
+
} // namespace blink
diff --git a/chromium/third_party/blink/renderer/core/paint/paint_layer_painting_info.h b/chromium/third_party/blink/renderer/core/paint/paint_layer_painting_info.h
index d5d86d38692..b0bb26c4a57 100644
--- a/chromium/third_party/blink/renderer/core/paint/paint_layer_painting_info.h
+++ b/chromium/third_party/blink/renderer/core/paint/paint_layer_painting_info.h
@@ -6,7 +6,7 @@
*
* Other contributors:
* Robert O'Callahan <roc+@cs.cmu.edu>
- * David Baron <dbaron@fas.harvard.edu>
+ * David Baron <dbaron@dbaron.org>
* Christian Biesinger <cbiesinger@web.de>
* Randall Jesup <rjesup@wgate.com>
* Roland Mainz <roland.mainz@informatik.med.uni-giessen.de>
@@ -63,7 +63,6 @@ class PaintLayer;
enum PaintLayerFlag {
kPaintLayerNoFlag = 0,
- kPaintLayerHaveTransparency = 1,
kPaintLayerPaintingOverlayOverflowControls = 1 << 3,
kPaintLayerPaintingCompositingBackgroundPhase = 1 << 4,
kPaintLayerPaintingCompositingForegroundPhase = 1 << 5,
@@ -135,8 +134,6 @@ inline String PaintLayerFlagsToDebugString(PaintLayerFlags flags) {
append("kPaintLayerPaintingCompositingDecorationPhase");
}
- if (flags & kPaintLayerHaveTransparency)
- append("kPaintLayerHaveTransparency");
if (flags & kPaintLayerPaintingOverlayOverflowControls)
append("kPaintLayerPaintingOverlayOverflowControls");
if (flags & kPaintLayerPaintingCompositingScrollingPhase)
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 c838509b144..072c1af3382 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
@@ -6,7 +6,7 @@
*
* Other contributors:
* Robert O'Callahan <roc+@cs.cmu.edu>
- * David Baron <dbaron@fas.harvard.edu>
+ * David Baron <dbaron@dbaron.org>
* Christian Biesinger <cbiesinger@gmail.com>
* Randall Jesup <rjesup@wgate.com>
* Roland Mainz <roland.mainz@informatik.med.uni-giessen.de>
@@ -76,6 +76,7 @@
#include "third_party/blink/renderer/core/html/forms/text_control_element.h"
#include "third_party/blink/renderer/core/html/html_frame_owner_element.h"
#include "third_party/blink/renderer/core/input/event_handler.h"
+#include "third_party/blink/renderer/core/inspector/inspector_trace_events.h"
#include "third_party/blink/renderer/core/layout/custom_scrollbar.h"
#include "third_party/blink/renderer/core/layout/layout_custom_scrollbar_part.h"
#include "third_party/blink/renderer/core/layout/layout_embedded_content.h"
@@ -94,6 +95,7 @@
#include "third_party/blink/renderer/core/page/scrolling/top_document_root_scroller_controller.h"
#include "third_party/blink/renderer/core/paint/compositing/composited_layer_mapping.h"
#include "third_party/blink/renderer/core/paint/compositing/paint_layer_compositor.h"
+#include "third_party/blink/renderer/core/paint/object_paint_invalidator.h"
#include "third_party/blink/renderer/core/paint/paint_invalidator.h"
#include "third_party/blink/renderer/core/paint/paint_layer_fragment.h"
#include "third_party/blink/renderer/core/scroll/scroll_alignment.h"
@@ -179,8 +181,8 @@ PaintLayerScrollableArea* PaintLayerScrollableArea::FromNode(const Node& node) {
return box ? box->GetScrollableArea() : nullptr;
}
-void PaintLayerScrollableArea::DidScroll(const FloatPoint& position) {
- ScrollableArea::DidScroll(position);
+void PaintLayerScrollableArea::DidCompositorScroll(const FloatPoint& position) {
+ ScrollableArea::DidCompositorScroll(position);
// This should be alive if it receives composited scroll callbacks.
CHECK(!HasBeenDisposed());
}
@@ -646,16 +648,30 @@ void PaintLayerScrollableArea::InvalidatePaintForScrollOffsetChange() {
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
+ // Invalidate background on scroll if needed.
+ // 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))
+ const auto& background_layers = box->StyleRef().BackgroundLayers();
+ if (background_layers.AnyLayerHasLocalAttachmentImage() &&
+ background_paint_in_graphics_layer) {
+ // Local-attachment background image scrolls, so needs invalidation if it
+ // paints in non-scrolling space.
box->SetBackgroundNeedsFullPaintInvalidation();
+ } else if (background_layers.AnyLayerHasDefaultAttachmentImage() &&
+ background_paint_in_scrolling_contents) {
+ // Normal attachment background image doesn't scroll, so needs
+ // invalidation if it paints in scrolling contents.
+ box->SetBackgroundNeedsFullPaintInvalidation();
+ } else if (background_layers.AnyLayerHasLocalAttachment() &&
+ background_layers.AnyLayerUsesContentBox() &&
+ background_paint_in_graphics_layer &&
+ (box->PaddingLeft() || box->PaddingTop() ||
+ box->PaddingRight() || box->PaddingBottom())) {
+ // Local attachment content box background needs invalidation if there is
+ // padding because the content area can change on scroll (e.g. the top
+ // padding can disappear when the box scrolls to the bottom).
+ box->SetBackgroundNeedsFullPaintInvalidation();
+ }
}
// If any scrolling content might have been clipped by a cull rect, then
@@ -2669,6 +2685,12 @@ bool PaintLayerScrollableArea::ComputeNeedsCompositedScrollingInternal(
return needs_composited_scrolling;
}
+bool PaintLayerScrollableArea::UsesCompositedScrolling() const {
+ if (RuntimeEnabledFeatures::CompositeAfterPaintEnabled())
+ return GetLayoutBox()->UsesCompositedScrolling();
+ return ScrollableArea::UsesCompositedScrolling();
+}
+
void PaintLayerScrollableArea::UpdateNeedsCompositedScrolling(
bool force_prefer_compositing_to_lcd_text) {
bool new_needs_composited_scrolling =
@@ -2785,9 +2807,7 @@ Scrollbar* PaintLayerScrollableArea::ScrollbarManager::CreateScrollbar(
ScrollableArea(), orientation, To<Element>(style_source.GetNode()));
} else {
Element* style_source_element = nullptr;
- if (::features::IsFormControlsRefreshEnabled()) {
- style_source_element = DynamicTo<Element>(style_source.GetNode());
- }
+ style_source_element = DynamicTo<Element>(style_source.GetNode());
scrollbar = MakeGarbageCollected<Scrollbar>(ScrollableArea(), orientation,
style_source_element);
}
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 81e80996e04..d794536ad08 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
@@ -5,7 +5,7 @@
*
* Other contributors:
* Robert O'Callahan <roc+@cs.cmu.edu>
- * David Baron <dbaron@fas.harvard.edu>
+ * David Baron <dbaron@dbaron.org>
* Christian Biesinger <cbiesinger@web.de>
* Randall Jesup <rjesup@wgate.com>
* Roland Mainz <roland.mainz@informatik.med.uni-giessen.de>
@@ -278,7 +278,7 @@ class CORE_EXPORT PaintLayerScrollableArea final
// only a helper.
cc::Layer* LayerForScrolling() const override;
- void DidScroll(const FloatPoint&) override;
+ void DidCompositorScroll(const FloatPoint&) override;
// GraphicsLayers for the scrolling components.
// Any function can return nullptr if they are not accelerated.
@@ -459,6 +459,11 @@ class CORE_EXPORT PaintLayerScrollableArea final
// Rectangle encompassing the scroll corner and resizer rect.
IntRect ScrollCornerAndResizerRect() const;
+ // The difference between this function and NeedsCompositedScrolling() is
+ // that this function returns the composited scrolling status based on paint
+ // properties which are updated based on the latter.
+ bool UsesCompositedScrolling() const override;
+
void UpdateNeedsCompositedScrolling(
bool force_prefer_compositing_to_lcd_text);
bool NeedsCompositedScrolling() const { return needs_composited_scrolling_; }
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 78187982a9d..bac1b5d58ad 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
@@ -5,6 +5,7 @@
#include "third_party/blink/renderer/core/paint/paint_layer_scrollable_area.h"
#include "build/build_config.h"
+#include "cc/layers/picture_layer.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"
@@ -17,6 +18,7 @@
#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"
+#include "third_party/blink/renderer/platform/graphics/compositing/paint_artifact_compositor.h"
#include "third_party/blink/renderer/platform/graphics/graphics_layer.h"
using testing::_;
@@ -921,7 +923,7 @@ TEST_P(PaintLayerScrollableAreaTest,
}
TEST_P(PaintLayerScrollableAreaTest,
- ScrollWithFixedDoesNotNeedCompositingInputsUpdate) {
+ ScrollWithFixedDoesNotNeedCompositingUpdate) {
SetBodyInnerHTML(R"HTML(
<style>
* {
@@ -951,6 +953,9 @@ TEST_P(PaintLayerScrollableAreaTest,
scrollable_area->SetScrollOffset(ScrollOffset(0, 1),
mojom::blink::ScrollType::kProgrammatic);
EXPECT_FALSE(scrollable_area->Layer()->NeedsCompositingInputsUpdate());
+ UpdateAllLifecyclePhasesExceptPaint();
+ EXPECT_FALSE(
+ GetDocument().View()->GetPaintArtifactCompositor()->NeedsUpdate());
UpdateAllLifecyclePhasesForTest();
EXPECT_EQ(FloatSize(0, 1), scrollable_area->GetScrollOffset());
}
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 30ab95fbbb3..5ae07789067 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
@@ -6,7 +6,7 @@
*
* Other contributors:
* Robert O'Callahan <roc+@cs.cmu.edu>
- * David Baron <dbaron@fas.harvard.edu>
+ * David Baron <dbaron@dbaron.org>
* Christian Biesinger <cbiesinger@web.de>
* Randall Jesup <rjesup@wgate.com>
* Roland Mainz <roland.mainz@informatik.med.uni-giessen.de>
diff --git a/chromium/third_party/blink/renderer/core/paint/paint_layer_stacking_node.h b/chromium/third_party/blink/renderer/core/paint/paint_layer_stacking_node.h
index ff04f7c7ae9..9544b3f6374 100644
--- a/chromium/third_party/blink/renderer/core/paint/paint_layer_stacking_node.h
+++ b/chromium/third_party/blink/renderer/core/paint/paint_layer_stacking_node.h
@@ -6,7 +6,7 @@
*
* Other contributors:
* Robert O'Callahan <roc+@cs.cmu.edu>
- * David Baron <dbaron@fas.harvard.edu>
+ * David Baron <dbaron@dbaron.org>
* Christian Biesinger <cbiesinger@web.de>
* Randall Jesup <rjesup@wgate.com>
* Roland Mainz <roland.mainz@informatik.med.uni-giessen.de>
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 5bf75df25e1..398368e4ff3 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
@@ -212,7 +212,7 @@ TEST_P(PaintLayerTest, CompositedScrollingNoNeedsRepaint) {
EXPECT_EQ(PhysicalOffset(0, 0),
content_layer->LocationWithoutPositionOffset());
EXPECT_EQ(
- LayoutSize(1000, 1000),
+ IntPoint(1000, 1000),
content_layer->ContainingLayer()->PixelSnappedScrolledContentOffset());
EXPECT_FALSE(content_layer->SelfNeedsRepaint());
EXPECT_FALSE(scroll_layer->SelfNeedsRepaint());
@@ -249,7 +249,7 @@ TEST_P(PaintLayerTest, NonCompositedScrollingNeedsRepaint) {
EXPECT_EQ(PhysicalOffset(0, 0),
content_layer->LocationWithoutPositionOffset());
EXPECT_EQ(
- LayoutSize(1000, 1000),
+ IntPoint(1000, 1000),
content_layer->ContainingLayer()->PixelSnappedScrolledContentOffset());
if (RuntimeEnabledFeatures::CullRectUpdateEnabled()) {
@@ -1074,44 +1074,35 @@ TEST_P(PaintLayerTest, SubsequenceCachingStackedLayers) {
PaintLayer* grandchild1 = GetPaintLayerByElementId("grandchild1");
PaintLayer* grandchild2 = GetPaintLayerByElementId("grandchild2");
- EXPECT_FALSE(parent->SupportsSubsequenceCaching());
- EXPECT_FALSE(child1->SupportsSubsequenceCaching());
- EXPECT_TRUE(child2->SupportsSubsequenceCaching());
- EXPECT_FALSE(grandchild1->SupportsSubsequenceCaching());
- EXPECT_FALSE(grandchild2->SupportsSubsequenceCaching());
-
- GetDocument()
- .getElementById("grandchild1")
- ->setAttribute(html_names::kStyleAttr, "isolation: isolate");
- UpdateAllLifecyclePhasesForTest();
-
- EXPECT_FALSE(parent->SupportsSubsequenceCaching());
- EXPECT_FALSE(child1->SupportsSubsequenceCaching());
+ EXPECT_TRUE(parent->SupportsSubsequenceCaching());
+ EXPECT_TRUE(child1->SupportsSubsequenceCaching());
EXPECT_TRUE(child2->SupportsSubsequenceCaching());
EXPECT_TRUE(grandchild1->SupportsSubsequenceCaching());
- EXPECT_FALSE(grandchild2->SupportsSubsequenceCaching());
+ EXPECT_TRUE(grandchild2->SupportsSubsequenceCaching());
}
-TEST_P(PaintLayerTest, SubsequenceCachingSVGRoot) {
+TEST_P(PaintLayerTest, SubsequenceCachingSVG) {
SetBodyInnerHTML(R"HTML(
- <div id='parent' style='position: relative'>
- <svg id='svgroot' style='position: relative'></svg>
- </div>
+ <svg id='svgroot'>
+ <foreignObject id='foreignObject'/>
+ </svg>
)HTML");
PaintLayer* svgroot = GetPaintLayerByElementId("svgroot");
+ PaintLayer* foreign_object = GetPaintLayerByElementId("foreignObject");
EXPECT_TRUE(svgroot->SupportsSubsequenceCaching());
+ EXPECT_TRUE(foreign_object->SupportsSubsequenceCaching());
}
TEST_P(PaintLayerTest, SubsequenceCachingMuticol) {
SetBodyInnerHTML(R"HTML(
<div style='columns: 2'>
- <svg id='svgroot' style='position: relative'></svg>
+ <div id='target' style='position: relative'></div>
</div>
)HTML");
- PaintLayer* svgroot = GetPaintLayerByElementId("svgroot");
- EXPECT_FALSE(svgroot->SupportsSubsequenceCaching());
+ PaintLayer* target = GetPaintLayerByElementId("target");
+ EXPECT_FALSE(target->SupportsSubsequenceCaching());
}
TEST_P(PaintLayerTest, NegativeZIndexChangeToPositive) {
@@ -1635,7 +1626,7 @@ TEST_P(PaintLayerTest, FloatLayerUnderInlineLayerScrolled) {
EXPECT_EQ(PhysicalOffset(0, 0), span->LocationWithoutPositionOffset());
EXPECT_EQ(PhysicalOffset(0, 0),
span->GetLayoutObject().OffsetForInFlowPosition());
- EXPECT_EQ(LayoutSize(0, 400),
+ EXPECT_EQ(IntPoint(0, 400),
span->ContainingLayer()->PixelSnappedScrolledContentOffset());
EXPECT_EQ(PhysicalOffset(150, 150),
floating->LocationWithoutPositionOffset());
@@ -1649,12 +1640,12 @@ TEST_P(PaintLayerTest, FloatLayerUnderInlineLayerScrolled) {
EXPECT_EQ(PhysicalOffset(0, 0), span->LocationWithoutPositionOffset());
EXPECT_EQ(PhysicalOffset(100, 100),
span->GetLayoutObject().OffsetForInFlowPosition());
- EXPECT_EQ(LayoutSize(0, 400),
+ EXPECT_EQ(IntPoint(0, 400),
span->ContainingLayer()->PixelSnappedScrolledContentOffset());
EXPECT_EQ(PhysicalOffset(0, 0), floating->LocationWithoutPositionOffset());
EXPECT_EQ(PhysicalOffset(50, 50),
floating->GetLayoutObject().OffsetForInFlowPosition());
- EXPECT_EQ(LayoutSize(0, 400),
+ EXPECT_EQ(IntPoint(0, 400),
floating->ContainingLayer()->PixelSnappedScrolledContentOffset());
EXPECT_EQ(PhysicalOffset(-50, -50),
floating->VisualOffsetFromAncestor(span));
@@ -1963,7 +1954,7 @@ TEST_P(PaintLayerTest, ColumnSpanLayerUnderExtraLayerScrolled) {
EXPECT_EQ(PhysicalOffset(50, 50),
spanner->GetLayoutObject().OffsetForInFlowPosition());
- EXPECT_EQ(LayoutSize(200, 0),
+ EXPECT_EQ(IntPoint(200, 0),
spanner->ContainingLayer()->PixelSnappedScrolledContentOffset());
EXPECT_EQ(PhysicalOffset(0, 0), extra_layer->LocationWithoutPositionOffset());
EXPECT_EQ(PhysicalOffset(100, 100),
@@ -2518,6 +2509,10 @@ TEST_P(PaintLayerTest, HitTestOverlayResizer) {
}
TEST_P(PaintLayerTest, BackgroundIsKnownToBeOpaqueInRectChildren) {
+ // This test doesn't apply in CompositeAfterPaint.
+ if (RuntimeEnabledFeatures::CompositeAfterPaintEnabled())
+ return;
+
SetBodyInnerHTML(R"HTML(
<style>
div {
@@ -2772,8 +2767,6 @@ TEST_P(PaintLayerTest, HasNonEmptyChildLayoutObjectsZeroSizeOverflowVisible) {
EXPECT_TRUE(layer->HasNonEmptyChildLayoutObjects());
}
-enum { kCompositingOptimizations = 1 << 0 };
-
class PaintLayerOverlapTest : public RenderingTest {
public:
PaintLayerOverlapTest()
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 0d45e6fcda3..fb7fcfa2e15 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
@@ -28,7 +28,7 @@
#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_block_break_token.h"
-#include "third_party/blink/renderer/core/layout/ng/ng_fragment_child_iterator.h"
+#include "third_party/blink/renderer/core/layout/ng/ng_fragmentation_utils.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"
@@ -111,6 +111,7 @@ PaintPropertyTreeBuilderContext::PaintPropertyTreeBuilderContext()
was_main_thread_scrolling(false) {}
PaintPropertyChangeType VisualViewportPaintPropertyTreeBuilder::Update(
+ LocalFrameView& main_frame_view,
VisualViewport& visual_viewport,
PaintPropertyTreeBuilderContext& full_context) {
if (full_context.fragments.IsEmpty())
@@ -121,6 +122,11 @@ PaintPropertyChangeType VisualViewportPaintPropertyTreeBuilder::Update(
auto property_changed =
visual_viewport.UpdatePaintPropertyNodesIfNeeded(context);
+ if (const EffectPaintPropertyNode* overscroll_elasticity_effect_node =
+ visual_viewport.GetOverscrollElasticityEffectNode()) {
+ context.current_effect = overscroll_elasticity_effect_node;
+ }
+
context.current.transform = visual_viewport.GetScrollTranslationNode();
context.absolute_position.transform =
visual_viewport.GetScrollTranslationNode();
@@ -135,6 +141,10 @@ PaintPropertyChangeType VisualViewportPaintPropertyTreeBuilder::Update(
// removed). Not a big deal for performance because this is rare.
full_context.force_subtree_update_reasons |=
PaintPropertyTreeBuilderContext::kSubtreeUpdateIsolationPiercing;
+ // The main frame's paint chunks (e.g. scrollbars) may reference paint
+ // properties of the visual viewport.
+ if (auto* layout_view = main_frame_view.GetLayoutView())
+ layout_view->Layer()->SetNeedsRepaint();
}
#if DCHECK_IS_ON()
@@ -250,6 +260,38 @@ class FragmentPaintPropertyTreeBuilder {
bool IsInNGFragmentTraversal() const { return pre_paint_info_; }
+ void SwitchToOOFContext(
+ PaintPropertyTreeBuilderFragmentContext::ContainingBlockContext&
+ oof_context) const {
+ context_.current = oof_context;
+
+ // If we're not block-fragmented, or if we're traversing the fragment tree
+ // to an orphaned object, simply setting a new context is all we have to do.
+ if (!oof_context.is_in_block_fragmentation ||
+ (pre_paint_info_ && pre_paint_info_->is_inside_orphaned_object))
+ return;
+
+ // Inside NG block fragmentation we have to perform an offset adjustment.
+ // An OOF fragment that is contained by something inside a fragmentainer
+ // will be a direct child of the fragmentainer, rather than a child of its
+ // actual containing block. We therefore need to adjust the offset to make
+ // us relative to the fragmentainer before applying the offset of the OOF.
+ PhysicalOffset delta =
+ oof_context.paint_offset - context_.fragmentainer_paint_offset;
+ // So, we did store |fragmentainer_paint_offset| when entering the
+ // fragmentainer, but the offset may have been reset by
+ // UpdateForPaintOffsetTranslation() since we entered it, which we'll need
+ // to compensate for now.
+ delta += context_.adjustment_for_oof_in_fragmentainer;
+ context_.current.paint_offset -= delta;
+ }
+
+ void ResetPaintOffset(PhysicalOffset new_offset = PhysicalOffset()) {
+ context_.adjustment_for_oof_in_fragmentainer +=
+ context_.current.paint_offset - new_offset;
+ context_.current.paint_offset = new_offset;
+ }
+
void OnUpdate(PaintPropertyChangeType change) {
property_changed_ = std::max(property_changed_, change);
}
@@ -499,7 +541,7 @@ void FragmentPaintPropertyTreeBuilder::UpdateForPaintOffsetTranslation(
// compositing code.
if (RuntimeEnabledFeatures::CompositeAfterPaintEnabled() &&
NeedsIsolationNodes(object_)) {
- context_.current.paint_offset = PhysicalOffset();
+ ResetPaintOffset();
context_.current.directly_composited_container_paint_offset_subpixel_delta =
PhysicalOffset();
return;
@@ -514,9 +556,7 @@ void FragmentPaintPropertyTreeBuilder::UpdateForPaintOffsetTranslation(
PhysicalOffset subpixel_accumulation;
if (RequiresFragmentStitching()) {
const LayoutBox& box = To<LayoutBox>(object_);
- const NGFragmentChildIterator& iterator = pre_paint_info_->iterator;
- const NGPhysicalBoxFragment& fragment = *iterator->BoxFragment();
- const NGBlockBreakToken* incoming_break_token = iterator->BlockBreakToken();
+ const NGPhysicalBoxFragment& fragment = pre_paint_info_->box_fragment;
// Calculate this fragment's rectangle relatively to the enclosing stitched
// layout object, with help from the break token.
@@ -528,7 +568,8 @@ void FragmentPaintPropertyTreeBuilder::UpdateForPaintOffsetTranslation(
WritingModeConverter converter(box.StyleRef().GetWritingDirection(),
PhysicalSize(box.Size()));
LogicalOffset logical_offset;
- if (incoming_break_token)
+ if (const NGBlockBreakToken* incoming_break_token =
+ FindPreviousBreakToken(fragment))
logical_offset.block_offset = incoming_break_token->ConsumedBlockSize();
LogicalRect logical_rect(logical_offset,
converter.ToLogical(fragment.Size()));
@@ -540,7 +581,7 @@ void FragmentPaintPropertyTreeBuilder::UpdateForPaintOffsetTranslation(
// offset in order to get to the right offset for each fragment.
new_paint_offset = converter.ToPhysical(logical_rect).offset;
- if (&pre_paint_info_->fragment_data == &object_.FirstFragment()) {
+ if (pre_paint_info_->fragment_data == &object_.FirstFragment()) {
// Make the translation relatively to the top/left corner of the box. In
// vertical-rl writing mode, the first fragment is not the leftmost one.
PhysicalOffset topleft = context_.current.paint_offset - new_paint_offset;
@@ -571,7 +612,7 @@ void FragmentPaintPropertyTreeBuilder::UpdateForPaintOffsetTranslation(
// If the object has a non-translation transform, discard the fractional
// paint offset which can't be transformed by the transform.
if (!CanPropagateSubpixelAccumulation()) {
- context_.current.paint_offset = new_paint_offset;
+ ResetPaintOffset(new_paint_offset);
context_.current
.directly_composited_container_paint_offset_subpixel_delta =
PhysicalOffset();
@@ -579,7 +620,7 @@ void FragmentPaintPropertyTreeBuilder::UpdateForPaintOffsetTranslation(
}
}
- context_.current.paint_offset = new_paint_offset + subpixel_accumulation;
+ ResetPaintOffset(new_paint_offset + subpixel_accumulation);
bool can_be_directly_composited =
RuntimeEnabledFeatures::CompositeAfterPaintEnabled()
@@ -616,6 +657,14 @@ void FragmentPaintPropertyTreeBuilder::UpdatePaintOffsetTranslation(
state.direct_compositing_reasons =
full_context_.direct_compositing_reasons &
CompositingReason::kDirectReasonsForPaintOffsetTranslationProperty;
+ if (state.direct_compositing_reasons & CompositingReason::kFixedPosition &&
+ object_.View()->FirstFragment().PaintProperties()->Scroll()) {
+ state.scroll_translation_for_fixed = object_.View()
+ ->FirstFragment()
+ .PaintProperties()
+ ->ScrollTranslation();
+ }
+
if (IsA<LayoutView>(object_)) {
DCHECK(object_.GetFrame());
state.flags.is_frame_paint_offset_translation = true;
@@ -761,7 +810,7 @@ static CompositingReasons CompositingReasonsForTransformProperty() {
reasons |= CompositingReason::kWillChangeFilter;
reasons |= CompositingReason::kWillChangeBackdropFilter;
- if (RuntimeEnabledFeatures::TransformInteropEnabled())
+ if (RuntimeEnabledFeatures::BackfaceVisibilityInteropEnabled())
reasons |= CompositingReason::kBackfaceInvisibility3DAncestor;
return reasons;
@@ -842,19 +891,16 @@ void FragmentPaintPropertyTreeBuilder::UpdateTransformForSVGChild(
// TODO(pdr): There is additional logic in
// FragmentPaintPropertyTreeBuilder::UpdateTransform that likely needs to
- // be included here, such as setting animation_is_axis_aligned, which
- // may be the only important difference remaining.
- if (RuntimeEnabledFeatures::CompositeSVGEnabled()) {
- state.direct_compositing_reasons =
- direct_compositing_reasons &
- CompositingReasonsForTransformProperty();
- state.flags.flattens_inherited_transform =
- context_.current.should_flatten_inherited_transform;
- state.rendering_context_id = context_.current.rendering_context_id;
- state.flags.is_for_svg_child = true;
- state.compositor_element_id = GetCompositorElementId(
- CompositorElementIdNamespace::kPrimaryTransform);
- }
+ // be included here, such as setting animation_is_axis_aligned, which may
+ // be the only important difference remaining.
+ state.direct_compositing_reasons =
+ direct_compositing_reasons & CompositingReasonsForTransformProperty();
+ state.flags.flattens_inherited_transform =
+ context_.current.should_flatten_inherited_transform;
+ state.rendering_context_id = context_.current.rendering_context_id;
+ state.flags.is_for_svg_child = true;
+ state.compositor_element_id = GetCompositorElementId(
+ CompositorElementIdNamespace::kPrimaryTransform);
TransformPaintPropertyNode::AnimationState animation_state;
animation_state.is_running_animation_on_compositor =
@@ -900,8 +946,11 @@ static FloatPoint3D TransformOrigin(const ComputedStyle& style,
style.TransformOriginZ());
}
-static bool NeedsTransform(const LayoutObject& object,
- CompositingReasons direct_compositing_reasons) {
+} // namespace
+
+bool PaintPropertyTreeBuilder::NeedsTransform(
+ const LayoutObject& object,
+ CompositingReasons direct_compositing_reasons) {
if (object.IsText())
return false;
@@ -920,6 +969,8 @@ static bool NeedsTransform(const LayoutObject& object,
return false;
}
+namespace {
+
static bool UpdateBoxSizeAndCheckActiveAnimationAxisAlignment(
const LayoutBox& object,
CompositingReasons compositing_reasons) {
@@ -949,7 +1000,8 @@ void FragmentPaintPropertyTreeBuilder::UpdateTransform() {
// direct compositing reason. The latter is required because this is the
// only way to represent compositing both an element and its stacking
// descendants.
- if (NeedsTransform(object_, full_context_.direct_compositing_reasons)) {
+ if (PaintPropertyTreeBuilder::NeedsTransform(
+ object_, full_context_.direct_compositing_reasons)) {
TransformPaintPropertyNode::State state;
if (object_.IsBox()) {
@@ -957,10 +1009,11 @@ void FragmentPaintPropertyTreeBuilder::UpdateTransform() {
// Each individual fragment should have its own transform origin, based
// on the fragment size. We'll do that, unless the fragments aren't to
// be stitched together.
- PhysicalSize size(
- !pre_paint_info_ || RequiresFragmentStitching()
- ? PhysicalSize(box.Size())
- : pre_paint_info_->iterator->BoxFragment()->Size());
+ PhysicalSize size;
+ if (!pre_paint_info_ || RequiresFragmentStitching())
+ size = PhysicalSize(box.Size());
+ else
+ size = pre_paint_info_->box_fragment.Size();
TransformationMatrix matrix;
style.ApplyTransform(
matrix, size.ToLayoutSize(), ComputedStyle::kExcludeTransformOrigin,
@@ -1068,7 +1121,7 @@ void FragmentPaintPropertyTreeBuilder::UpdateTransform() {
transform->Translation2D();
}
} else if (RuntimeEnabledFeatures::TransformInteropEnabled() &&
- !object_.IsAnonymous()) {
+ object_.IsForElement()) {
// With kTransformInterop enabled, 3D rendering contexts follow the
// DOM ancestor chain, so flattening should apply regardless of
// presence of transform.
@@ -1084,9 +1137,10 @@ static bool MayNeedClipPathClip(const LayoutObject& object) {
(object.HasLayer() || object.IsSVGChild());
}
-static bool NeedsClipPathClip(const LayoutObject& object) {
+static bool NeedsClipPathClip(const LayoutObject& object,
+ const FragmentData& fragment_data) {
// We should have already updated the clip path cache when this is called.
- if (object.FirstFragment().ClipPathPath()) {
+ if (fragment_data.ClipPathPath()) {
DCHECK(MayNeedClipPathClip(object));
return true;
}
@@ -1285,6 +1339,17 @@ void FragmentPaintPropertyTreeBuilder::UpdateEffect() {
full_context_.direct_compositing_reasons &
CompositingReasonsForEffectProperty();
+ if (RuntimeEnabledFeatures::CompositeAfterPaintEnabled()) {
+ // If an effect node exists, add an additional direct compositing reason
+ // for 3d transforms to ensure it is composited.
+ CompositingReasons additional_transform_compositing_trigger =
+ CompositingReason::k3DTransform |
+ CompositingReason::kTrivial3DTransform;
+ state.direct_compositing_reasons |=
+ (full_context_.direct_compositing_reasons &
+ additional_transform_compositing_trigger);
+ }
+
if (!RuntimeEnabledFeatures::CompositeAfterPaintEnabled()) {
state.direct_compositing_reasons |=
full_context_.direct_compositing_reasons &
@@ -1534,6 +1599,18 @@ void FragmentPaintPropertyTreeBuilder::UpdateFilter() {
state.direct_compositing_reasons =
full_context_.direct_compositing_reasons &
CompositingReasonsForFilterProperty();
+
+ if (RuntimeEnabledFeatures::CompositeAfterPaintEnabled()) {
+ // If an effect node exists, add an additional direct compositing reason
+ // for 3d transforms to ensure it is composited.
+ CompositingReasons additional_transform_compositing_trigger =
+ CompositingReason::k3DTransform |
+ CompositingReason::kTrivial3DTransform;
+ state.direct_compositing_reasons |=
+ (full_context_.direct_compositing_reasons &
+ additional_transform_compositing_trigger);
+ }
+
state.compositor_element_id =
GetCompositorElementId(CompositorElementIdNamespace::kEffectFilter);
@@ -1623,7 +1700,7 @@ void FragmentPaintPropertyTreeBuilder::UpdateCssClip() {
void FragmentPaintPropertyTreeBuilder::UpdateClipPathClip() {
if (NeedsPaintPropertyUpdate()) {
- if (!NeedsClipPathClip(object_)) {
+ if (!NeedsClipPathClip(object_, fragment_data_)) {
OnClearClip(properties_->ClearClipPathClip());
} else {
ClipPaintPropertyNode::State state(
@@ -1831,13 +1908,14 @@ void FragmentPaintPropertyTreeBuilder::UpdateOverflowClip() {
LayoutReplaced::PreSnappedRectForPersistentSizing(content_rect);
}
// LayoutReplaced clips the foreground by rounded content box.
- auto clip_rect = RoundedBorderGeometry::PixelSnappedRoundedInnerBorder(
- replaced.StyleRef(), content_rect,
- LayoutRectOutsets(
- -(replaced.PaddingTop() + replaced.BorderTop()),
- -(replaced.PaddingRight() + replaced.BorderRight()),
- -(replaced.PaddingBottom() + replaced.BorderBottom()),
- -(replaced.PaddingLeft() + replaced.BorderLeft())));
+ auto clip_rect =
+ RoundedBorderGeometry::PixelSnappedRoundedBorderWithOutsets(
+ replaced.StyleRef(), content_rect,
+ 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
@@ -1852,9 +1930,9 @@ void FragmentPaintPropertyTreeBuilder::UpdateOverflowClip() {
} else if (object_.IsBox()) {
PhysicalRect clip_rect;
if (pre_paint_info_) {
- const NGFragmentChildIterator& iterator = pre_paint_info_->iterator;
- clip_rect = iterator->BoxFragment()->OverflowClipRect(
- context_.current.paint_offset, iterator->BlockBreakToken());
+ clip_rect = pre_paint_info_->box_fragment.OverflowClipRect(
+ context_.current.paint_offset,
+ FindPreviousBreakToken(pre_paint_info_->box_fragment));
} else {
clip_rect = To<LayoutBox>(object_).OverflowClipRect(
context_.current.paint_offset);
@@ -1909,7 +1987,7 @@ void FragmentPaintPropertyTreeBuilder::UpdatePerspective() {
// most transform nodes do.
TransformPaintPropertyNode::State state{
TransformPaintPropertyNode::TransformAndOrigin(
- TransformationMatrix().ApplyPerspective(style.Perspective()),
+ TransformationMatrix().ApplyPerspective(style.UsedPerspective()),
PerspectiveOrigin(To<LayoutBox>(object_)) +
FloatSize(context_.current.paint_offset))};
state.flags.flattens_inherited_transform =
@@ -2117,6 +2195,11 @@ void FragmentPaintPropertyTreeBuilder::UpdateScrollAndScrollTranslation() {
FloatPoint scroll_position = FloatPoint(box.ScrollOrigin()) +
box.GetScrollableArea()->GetScrollOffset();
TransformPaintPropertyNode::State state{-ToFloatSize(scroll_position)};
+ if (!box.GetScrollableArea()->PendingScrollAnchorAdjustment().IsZero()) {
+ context_.current.pending_scroll_anchor_adjustment +=
+ box.GetScrollableArea()->PendingScrollAnchorAdjustment();
+ box.GetScrollableArea()->ClearPendingScrollAnchorAdjustment();
+ }
state.flags.flattens_inherited_transform =
context_.current.should_flatten_inherited_transform;
state.rendering_context_id = context_.current.rendering_context_id;
@@ -2166,11 +2249,20 @@ void FragmentPaintPropertyTreeBuilder::UpdateScrollAndScrollTranslation() {
if (RuntimeEnabledFeatures::CompositeAfterPaintEnabled() &&
scroll_translation->Translation2D() != old_scroll_offset) {
- // Scrolling can change overlap relationship.
+ // Scrolling can change overlap relationship for sticky positioned or
+ // elements fixed to an overflow: hidden view that programmatically
+ // scrolls via script. In this case the fixed transform doesn't have
+ // enough information to perform the expansion - there is no scroll node
+ // to describe the bounds of the scrollable content.
auto* frame_view = object_.GetFrameView();
- if (frame_view->HasViewportConstrainedObjects()) {
- // TODO(crbug.com/1099379): Implement better fixed/sticky overlap
- // testing.
+ if (frame_view->HasStickyViewportConstrainedObject()) {
+ // TODO(crbug.com/1117658): Implement better sticky overlap testing.
+ frame_view->SetPaintArtifactCompositorNeedsUpdate();
+ } else if (frame_view->HasViewportConstrainedObjects() &&
+ !frame_view->GetLayoutView()
+ ->FirstFragment()
+ .PaintProperties()
+ ->Scroll()) {
frame_view->SetPaintArtifactCompositorNeedsUpdate();
} else if (!object_.IsStackingContext() &&
// TODO(wangxianzhu): for accuracy, this should be something
@@ -2422,15 +2514,6 @@ static PhysicalOffset PaintOffsetInPaginationContainer(
}
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;
- }
-
// Paint offsets for fragmented content are computed from scratch.
const auto* enclosing_pagination_layer =
full_context_.painting_layer->EnclosingPaginationLayer();
@@ -2485,14 +2568,17 @@ void FragmentPaintPropertyTreeBuilder::UpdatePaintOffset() {
return;
}
- if (object_.IsFloating() && !object_.IsInLayoutNGInlineFormattingContext())
- context_.current.paint_offset = context_.paint_offset_for_float;
+ if (!pre_paint_info_) {
+ 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()) {
@@ -2505,8 +2591,12 @@ void FragmentPaintPropertyTreeBuilder::UpdatePaintOffset() {
box_model_object.OffsetForInFlowPosition();
break;
case EPosition::kAbsolute: {
- DCHECK_EQ(full_context_.container_for_absolute_position,
- box_model_object.Container());
+#if DCHECK_IS_ON()
+ if (!pre_paint_info_ || !pre_paint_info_->is_inside_orphaned_object) {
+ DCHECK_EQ(full_context_.container_for_absolute_position,
+ box_model_object.Container());
+ }
+#endif
if (RuntimeEnabledFeatures::TransformInteropEnabled()) {
// FIXME(dbaron): When the TransformInteropEnabled flag is removed
// because it's always enabled, we should move these variables from
@@ -2517,7 +2607,7 @@ void FragmentPaintPropertyTreeBuilder::UpdatePaintOffset() {
context_.absolute_position.rendering_context_id =
context_.current.rendering_context_id;
}
- context_.current = context_.absolute_position;
+ SwitchToOOFContext(context_.absolute_position);
// Absolutely positioned content in an inline should be positioned
// relative to the inline.
@@ -2534,8 +2624,12 @@ void FragmentPaintPropertyTreeBuilder::UpdatePaintOffset() {
case EPosition::kSticky:
break;
case EPosition::kFixed: {
- DCHECK_EQ(full_context_.container_for_fixed_position,
- box_model_object.Container());
+#if DCHECK_IS_ON()
+ if (!pre_paint_info_ || !pre_paint_info_->is_inside_orphaned_object) {
+ DCHECK_EQ(full_context_.container_for_fixed_position,
+ box_model_object.Container());
+ }
+#endif
if (RuntimeEnabledFeatures::TransformInteropEnabled()) {
// FIXME(dbaron): When the TransformInteropEnabled flag is removed
// because it's always enabled, we should move these variables from
@@ -2546,7 +2640,7 @@ void FragmentPaintPropertyTreeBuilder::UpdatePaintOffset() {
context_.fixed_position.rendering_context_id =
context_.current.rendering_context_id;
}
- context_.current = context_.fixed_position;
+ SwitchToOOFContext(context_.fixed_position);
// Fixed-position elements that are fixed to the viewport have a
// transform above the scroll of the LayoutView. Child content is
// relative to that transform, and hence the fixed-position element.
@@ -2568,21 +2662,32 @@ void FragmentPaintPropertyTreeBuilder::UpdatePaintOffset() {
}
}
- if (object_.IsBox()) {
- // TODO(pdr): Several calls in this function walk back up the tree to
- // calculate containers (e.g., physicalLocation, offsetForInFlowPosition*).
- // The containing block and other containers can be stored on
- // PaintPropertyTreeBuilderFragmentContext instead of recomputing them.
- context_.current.paint_offset += To<LayoutBox>(object_).PhysicalLocation();
+ if (const auto* box = DynamicTo<LayoutBox>(&object_)) {
+ if (pre_paint_info_) {
+ context_.current.paint_offset += pre_paint_info_->paint_offset;
- // This is a weird quirk that table cells paint as children of table rows,
- // but their location have the row's location baked-in.
- // Similar adjustment is done in LayoutTableCell::offsetFromContainer().
- if (object_.IsTableCellLegacy()) {
- LayoutObject* parent_row = object_.Parent();
- DCHECK(parent_row && parent_row->IsTableRow());
- context_.current.paint_offset -=
- To<LayoutBox>(parent_row)->PhysicalLocation();
+ // Determine whether we're inside block fragmentation or not. OOF
+ // descendants need special treatment inside block fragmentation.
+ context_.current.is_in_block_fragmentation =
+ pre_paint_info_->fragmentainer_idx != WTF::kNotFound &&
+ box->GetNGPaginationBreakability() != LayoutBox::kForbidBreaks;
+ } else {
+ // TODO(pdr): Several calls in this function walk back up the tree to
+ // calculate containers (e.g., physicalLocation,
+ // offsetForInFlowPosition*). The containing block and other containers
+ // can be stored on PaintPropertyTreeBuilderFragmentContext instead of
+ // recomputing them.
+ context_.current.paint_offset += box->PhysicalLocation();
+
+ // This is a weird quirk that table cells paint as children of table rows,
+ // but their location have the row's location baked-in.
+ // Similar adjustment is done in LayoutTableCell::offsetFromContainer().
+ if (object_.IsTableCellLegacy()) {
+ LayoutObject* parent_row = object_.Parent();
+ DCHECK(parent_row && parent_row->IsTableRow());
+ context_.current.paint_offset -=
+ To<LayoutBox>(parent_row)->PhysicalLocation();
+ }
}
}
@@ -2742,8 +2847,11 @@ static bool IsLayoutShiftRoot(const LayoutObject& object,
void FragmentPaintPropertyTreeBuilder::UpdateForSelf() {
#if DCHECK_IS_ON()
- FindPaintOffsetNeedingUpdateScope check_paint_offset(
- object_, fragment_data_, full_context_.is_actually_needed);
+ absl::optional<FindPaintOffsetNeedingUpdateScope> check_paint_offset;
+ if (RuntimeEnabledFeatures::PaintUnderInvalidationCheckingEnabled()) {
+ check_paint_offset.emplace(object_, fragment_data_,
+ full_context_.is_actually_needed);
+ }
#endif
// This is not in FindObjectPropertiesNeedingUpdateScope because paint offset
@@ -2757,8 +2865,12 @@ void FragmentPaintPropertyTreeBuilder::UpdateForSelf() {
if (properties_) {
{
#if DCHECK_IS_ON()
- FindPropertiesNeedingUpdateScope check_fragment_clip(
- object_, fragment_data_, full_context_.force_subtree_update_reasons);
+ absl::optional<FindPropertiesNeedingUpdateScope> check_fragment_clip;
+ if (RuntimeEnabledFeatures::PaintUnderInvalidationCheckingEnabled()) {
+ bool force_subtree_update = full_context_.force_subtree_update_reasons;
+ check_fragment_clip.emplace(object_, fragment_data_,
+ force_subtree_update);
+ }
#endif
UpdateFragmentClip();
}
@@ -2768,8 +2880,12 @@ void FragmentPaintPropertyTreeBuilder::UpdateForSelf() {
}
#if DCHECK_IS_ON()
- FindPropertiesNeedingUpdateScope check_paint_properties(
- object_, fragment_data_, full_context_.force_subtree_update_reasons);
+ absl::optional<FindPropertiesNeedingUpdateScope> check_paint_properties;
+ if (RuntimeEnabledFeatures::PaintUnderInvalidationCheckingEnabled()) {
+ bool force_subtree_update = full_context_.force_subtree_update_reasons;
+ check_paint_properties.emplace(object_, fragment_data_,
+ force_subtree_update);
+ }
#endif
if (properties_) {
@@ -2781,7 +2897,7 @@ void FragmentPaintPropertyTreeBuilder::UpdateForSelf() {
UpdateFilter();
UpdateOverflowControlsClip();
} else if (RuntimeEnabledFeatures::TransformInteropEnabled() &&
- !object_.IsAnonymous()) {
+ object_.IsForElement()) {
// With kTransformInterop enabled, 3D rendering contexts follow the
// DOM ancestor chain, so flattening should apply regardless of
// presence of transform.
@@ -2806,13 +2922,19 @@ void FragmentPaintPropertyTreeBuilder::UpdateForSelf() {
void FragmentPaintPropertyTreeBuilder::UpdateForChildren() {
#if DCHECK_IS_ON()
- // Paint offset should not change during this function.
+ // Will be used though a reference by check_paint_offset, so it's declared
+ // here to out-live check_paint_offset. It's false because paint offset
+ // should not change during this function.
const bool needs_paint_offset_update = false;
- FindPaintOffsetNeedingUpdateScope check_paint_offset(
- object_, fragment_data_, needs_paint_offset_update);
-
- FindPropertiesNeedingUpdateScope check_paint_properties(
- object_, fragment_data_, full_context_.force_subtree_update_reasons);
+ absl::optional<FindPaintOffsetNeedingUpdateScope> check_paint_offset;
+ absl::optional<FindPropertiesNeedingUpdateScope> check_paint_properties;
+ if (RuntimeEnabledFeatures::PaintUnderInvalidationCheckingEnabled()) {
+ check_paint_offset.emplace(object_, fragment_data_,
+ needs_paint_offset_update);
+ bool force_subtree_update = full_context_.force_subtree_update_reasons;
+ check_paint_properties.emplace(object_, fragment_data_,
+ force_subtree_update);
+ }
#endif
if (properties_) {
@@ -2838,8 +2960,10 @@ void FragmentPaintPropertyTreeBuilder::UpdateForChildren() {
context_.translation_2d_to_layout_shift_root_delta = FloatSize();
// Don't reset scroll_offset_to_layout_shift_root_delta if this object has
// scroll translation because we need to propagate the delta to descendants.
- if (!properties_ || !properties_->ScrollTranslation())
+ if (!properties_ || !properties_->ScrollTranslation()) {
context_.current.scroll_offset_to_layout_shift_root_delta = FloatSize();
+ context_.current.pending_scroll_anchor_adjustment = FloatSize();
+ }
}
#if DCHECK_IS_ON()
@@ -2910,7 +3034,7 @@ void PaintPropertyTreeBuilder::InitFragmentPaintPropertiesForNG(
context_.fragments.push_back(PaintPropertyTreeBuilderFragmentContext());
else
context_.fragments.resize(1);
- InitFragmentPaintProperties(pre_paint_info_->fragment_data,
+ InitFragmentPaintProperties(*pre_paint_info_->fragment_data,
needs_paint_properties, context_.fragments[0]);
}
@@ -2931,12 +3055,15 @@ void PaintPropertyTreeBuilder::InitSingleFragmentFromParent(
// Column-span:all skips pagination container in the tree hierarchy, so it
// should also skip any fragment clip created by the skipped pagination
- // container. We also need to skip fragment clip if the object is a paint
- // invalidation container which doesn't allow fragmentation.
- bool skip_fragment_clip_for_composited_layer =
- !RuntimeEnabledFeatures::CompositeAfterPaintEnabled() &&
- object_.CanBeCompositedForDirectReasons() &&
- To<LayoutBoxModelObject>(object_).Layer()->EnclosingPaginationLayer();
+ // container. We also need to skip fragment clip if the layer doesn't allow
+ // fragmentation.
+ bool skip_fragment_clip_for_composited_layer = false;
+ if (object_.HasLayer()) {
+ const auto* layer = To<LayoutBoxModelObject>(object_).Layer();
+ skip_fragment_clip_for_composited_layer =
+ layer->EnclosingPaginationLayer() &&
+ !layer->ShouldFragmentCompositedBounds();
+ }
if (!skip_fragment_clip_for_composited_layer && !object_.IsColumnSpanAll())
return;
@@ -2979,8 +3106,6 @@ void PaintPropertyTreeBuilder::InitSingleFragmentFromParent(
void PaintPropertyTreeBuilder::UpdateCompositedLayerPaginationOffset() {
DCHECK(!IsInNGFragmentTraversal());
- if (RuntimeEnabledFeatures::CompositeAfterPaintEnabled())
- return;
const auto* enclosing_pagination_layer =
context_.painting_layer->EnclosingPaginationLayer();
@@ -2994,13 +3119,28 @@ void PaintPropertyTreeBuilder::UpdateCompositedLayerPaginationOffset() {
DCHECK(!context_.painting_layer->ShouldFragmentCompositedBounds());
FragmentData& first_fragment =
object_.GetMutableForPainting().FirstFragment();
- bool can_be_directly_composited = object_.CanBeCompositedForDirectReasons();
- const auto* parent_directly_composited_layer =
- context_.painting_layer->EnclosingDirectlyCompositableLayer(
- can_be_directly_composited ? kExcludeSelf : kIncludeSelf);
- if (can_be_directly_composited &&
- (!parent_directly_composited_layer ||
- !parent_directly_composited_layer->EnclosingPaginationLayer())) {
+ bool may_use_self_pagination_offset = false;
+ const PaintLayer* parent_pagination_offset_layer = nullptr;
+ if (RuntimeEnabledFeatures::CompositeAfterPaintEnabled()) {
+ parent_pagination_offset_layer =
+ context_.painting_layer
+ ->EnclosingCompositedScrollingLayerUnderPagination(kIncludeSelf);
+ if (parent_pagination_offset_layer->GetLayoutObject() == object_) {
+ may_use_self_pagination_offset = true;
+ parent_pagination_offset_layer =
+ parent_pagination_offset_layer
+ ->EnclosingCompositedScrollingLayerUnderPagination(kExcludeSelf);
+ }
+ } else {
+ may_use_self_pagination_offset = object_.CanBeCompositedForDirectReasons();
+ parent_pagination_offset_layer =
+ context_.painting_layer->EnclosingDirectlyCompositableLayer(
+ may_use_self_pagination_offset ? kExcludeSelf : kIncludeSelf);
+ }
+
+ if (may_use_self_pagination_offset &&
+ (!parent_pagination_offset_layer ||
+ !parent_pagination_offset_layer->EnclosingPaginationLayer())) {
// |object_| establishes the top level composited layer under the
// pagination layer.
FragmentainerIterator iterator(
@@ -3013,10 +3153,10 @@ void PaintPropertyTreeBuilder::UpdateCompositedLayerPaginationOffset() {
first_fragment.SetLogicalTopInFlowThread(
iterator.FragmentainerLogicalTopInFlowThread());
}
- } else if (parent_directly_composited_layer) {
+ } else if (parent_pagination_offset_layer) {
// All objects under the composited layer use the same pagination offset.
const auto& fragment =
- parent_directly_composited_layer->GetLayoutObject().FirstFragment();
+ parent_pagination_offset_layer->GetLayoutObject().FirstFragment();
first_fragment.SetLegacyPaginationOffset(fragment.LegacyPaginationOffset());
first_fragment.SetLogicalTopInFlowThread(fragment.LogicalTopInFlowThread());
}
@@ -3762,7 +3902,7 @@ PaintPropertyChangeType PaintPropertyTreeBuilder::UpdateForSelf() {
DCHECK_EQ(context_.fragments.size(), 1u);
FragmentPaintPropertyTreeBuilder builder(object_, pre_paint_info_, context_,
context_.fragments[0],
- pre_paint_info_->fragment_data);
+ *pre_paint_info_->fragment_data);
builder.UpdateForSelf();
property_changed = std::max(property_changed, builder.PropertyChanged());
} else {
@@ -3813,7 +3953,7 @@ PaintPropertyChangeType PaintPropertyTreeBuilder::UpdateForChildren() {
FragmentData* fragment_data;
if (pre_paint_info_) {
DCHECK_EQ(context_.fragments.size(), 1u);
- fragment_data = &pre_paint_info_->fragment_data;
+ fragment_data = pre_paint_info_->fragment_data;
DCHECK(fragment_data);
} else {
fragment_data = &object_.GetMutableForPainting().FirstFragment();
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 a8d5224beb2..9ef282663dd 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
@@ -21,7 +21,7 @@ class FragmentData;
class LayoutObject;
class LayoutNGTableSectionInterface;
class LocalFrameView;
-class NGFragmentChildIterator;
+class NGPhysicalBoxFragment;
class PaintLayer;
class VisualViewport;
@@ -96,6 +96,8 @@ struct PaintPropertyTreeBuilderFragmentContext {
// object has changed.
bool layout_shift_root_changed = false;
+ bool is_in_block_fragmentation = false;
+
// Rendering context for 3D sorting. See
// TransformPaintPropertyNode::renderingContextId.
unsigned rendering_context_id = 0;
@@ -110,6 +112,8 @@ struct PaintPropertyTreeBuilderFragmentContext {
// reference a scroll offset transform, scroll nodes should be updated if
// the transform tree changes.
const ScrollPaintPropertyNode* scroll = nullptr;
+
+ FloatSize pending_scroll_anchor_adjustment;
};
ContainingBlockContext current;
@@ -121,8 +125,6 @@ struct PaintPropertyTreeBuilderFragmentContext {
// containing block of corresponding positioned descendants. Overflow clips
// are also inherited by containing block tree instead of DOM tree, thus they
// are included in the additional context too.
- //
- // Note that these contexts are not used in LayoutNGFragmentTraversal.
ContainingBlockContext absolute_position;
ContainingBlockContext fixed_position;
@@ -154,6 +156,13 @@ struct PaintPropertyTreeBuilderFragmentContext {
PhysicalOffset old_paint_offset;
+ // Paint offset at the current innermost fragmentainer.
+ PhysicalOffset fragmentainer_paint_offset;
+
+ // Amount of adjustment done by UpdateForPaintOffsetTranslation() since we
+ // entered the innermost fragmentainer.
+ PhysicalOffset adjustment_for_oof_in_fragmentainer;
+
// An additional offset that applies to the current fragment, but is detected
// *before* the ContainingBlockContext is updated for it. Once the
// ContainingBlockContext is set, this value should be added to
@@ -175,7 +184,6 @@ struct PaintPropertyTreeBuilderContext final {
Vector<PaintPropertyTreeBuilderFragmentContext, 1> fragments;
- // TODO(mstensho): Stop using these in LayoutNGFragmentTraversal.
const LayoutObject* container_for_absolute_position = nullptr;
const LayoutObject* container_for_fixed_position = nullptr;
@@ -252,7 +260,8 @@ class VisualViewportPaintPropertyTreeBuilder {
// Update the paint properties for the visual viewport and ensure the context
// is up to date. Returns the maximum paint property change type for any of
// the viewport nodes.
- static PaintPropertyChangeType Update(VisualViewport&,
+ static PaintPropertyChangeType Update(LocalFrameView& main_frame_view,
+ VisualViewport&,
PaintPropertyTreeBuilderContext&);
};
@@ -260,12 +269,41 @@ struct NGPrePaintInfo {
STACK_ALLOCATED();
public:
- NGPrePaintInfo(const NGFragmentChildIterator& iterator,
- FragmentData& fragment_data)
- : iterator(iterator), fragment_data(fragment_data) {}
-
- const NGFragmentChildIterator& iterator;
- FragmentData& fragment_data;
+ NGPrePaintInfo(const NGPhysicalBoxFragment& box_fragment,
+ PhysicalOffset paint_offset,
+ wtf_size_t fragmentainer_idx,
+ bool is_first_for_node,
+ bool is_last_for_node,
+ bool is_inside_orphaned_object,
+ bool is_inside_fragment_child)
+ : box_fragment(box_fragment),
+ paint_offset(paint_offset),
+ fragmentainer_idx(fragmentainer_idx),
+ is_first_for_node(is_first_for_node),
+ is_last_for_node(is_last_for_node),
+ is_inside_orphaned_object(is_inside_orphaned_object),
+ is_inside_fragment_child(is_inside_fragment_child) {}
+
+ // The fragment for the LayoutObject currently being processed, or, in the
+ // case of text and non-atomic inlines: the fragment of the containing block.
+ const NGPhysicalBoxFragment& box_fragment;
+
+ FragmentData* fragment_data = nullptr;
+ PhysicalOffset paint_offset;
+ wtf_size_t fragmentainer_idx;
+ bool is_first_for_node;
+ bool is_last_for_node;
+
+ // True if we're fragment-traversing an object (OOF or float) directly,
+ // instead of walking the layout object tree. In this case, the property /
+ // invalidation context chains will be missing ancestors between the
+ // fragmentainer and the OOF / float.
+ bool is_inside_orphaned_object;
+
+ // True if |box_fragment| is the containing block of the LayoutObject
+ // currently being processed. Otherwise, |box_fragment| is a fragment for the
+ // LayoutObject itself.
+ bool is_inside_fragment_child;
};
// Creates paint property tree nodes for non-local effects in the layout tree.
@@ -295,6 +333,9 @@ class PaintPropertyTreeBuilder {
// Returns whether any paint property of the object has changed.
PaintPropertyChangeType UpdateForChildren();
+ static bool NeedsTransform(const LayoutObject& object,
+ CompositingReasons direct_compositing_reasons);
+
private:
ALWAYS_INLINE void InitFragmentPaintProperties(
FragmentData&,
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 f8028de1716..0c159000b5b 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
@@ -125,7 +125,13 @@ void PaintPropertyTreeBuilderTest::SetUp() {
#define CHECK_EXACT_VISUAL_RECT(expected, source_object, ancestor) \
CHECK_VISUAL_RECT(expected, source_object, ancestor, 0)
-INSTANTIATE_PAINT_TEST_SUITE_P(PaintPropertyTreeBuilderTest);
+INSTANTIATE_TEST_SUITE_P(All,
+ PaintPropertyTreeBuilderTest,
+ ::testing::Values(0,
+ kCompositeAfterPaint,
+ kUnderInvalidationChecking,
+ kCompositeAfterPaint |
+ kUnderInvalidationChecking));
TEST_P(PaintPropertyTreeBuilderTest, FixedPosition) {
LoadTestData("fixed-position.html");
@@ -833,8 +839,9 @@ TEST_P(PaintPropertyTreeBuilderTest, WillChangeContents) {
TEST_P(PaintPropertyTreeBuilderTest,
BackfaceVisibilityWithPseudoStacking3DChildren) {
- ScopedTransformInteropForTest enabled(true);
- // TODO(chrishtr): implement for CAP. This entails computing
+ ScopedTransformInteropForTest ti_enabled(true);
+ ScopedBackfaceVisibilityInteropForTest bfi_enabled(true);
+ // TODO(chrishtr, dbaron): implement for CAP. This entails computing
// has_backface_invisible_ancestor_in_same_3d_context in the pre-paint tree
// walk.
if (RuntimeEnabledFeatures::CompositeAfterPaintEnabled())
@@ -4318,6 +4325,12 @@ TEST_P(PaintPropertyTreeBuilderTest, SpanFragmentsLimitedToSize) {
TEST_P(PaintPropertyTreeBuilderTest,
PaintOffsetUnderMulticolumnScrollFixedPos) {
+ // Raster under-invalidation will fail to allocate bitmap when checking a huge
+ // layer created without LayoutNGBlockFragmentation.
+ if (RuntimeEnabledFeatures::PaintUnderInvalidationCheckingEnabled() &&
+ !RuntimeEnabledFeatures::LayoutNGBlockFragmentationEnabled())
+ return;
+
SetBodyInnerHTML(R"HTML(
<div id=fixed style='position: fixed; columns: 2'>
<div style='width: 50px; height: 20px; background: lightblue'></div>
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 f3b66da3b0a..0570c2d62e1 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
@@ -113,7 +113,9 @@ class PropertyTreePrinterTraits<EffectPaintPropertyNodeOrAlias> {
public:
static void AddVisualViewportProperties(
const VisualViewport& visual_viewport,
- PropertyTreePrinter<EffectPaintPropertyNodeOrAlias>& printer) {}
+ PropertyTreePrinter<EffectPaintPropertyNodeOrAlias>& printer) {
+ printer.AddNode(visual_viewport.GetOverscrollElasticityEffectNode());
+ }
static void AddObjectPaintProperties(
const ObjectPaintProperties& properties,
@@ -165,6 +167,10 @@ namespace paint_property_tree_printer {
void UpdateDebugNames(const VisualViewport& viewport) {
if (auto* device_emulation_node = viewport.GetDeviceEmulationTransformNode())
device_emulation_node->SetDebugName("Device Emulation Node");
+ if (auto* overscroll_effect_node =
+ viewport.GetOverscrollElasticityEffectNode()) {
+ overscroll_effect_node->SetDebugName("Overscroll Elasticity Effect Node");
+ }
if (auto* overscroll_node = viewport.GetOverscrollElasticityTransformNode())
overscroll_node->SetDebugName("Overscroll Elasticity Node");
viewport.GetPageScaleNode()->SetDebugName("VisualViewport Scale Node");
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 279953b0614..b989f2f8287 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
@@ -18,7 +18,13 @@ namespace blink {
// Tests covering incremental updates of paint property trees.
class PaintPropertyTreeUpdateTest : public PaintPropertyTreeBuilderTest {};
-INSTANTIATE_PAINT_TEST_SUITE_P(PaintPropertyTreeUpdateTest);
+INSTANTIATE_TEST_SUITE_P(All,
+ PaintPropertyTreeUpdateTest,
+ ::testing::Values(0,
+ kCompositeAfterPaint,
+ kUnderInvalidationChecking,
+ kCompositeAfterPaint |
+ kUnderInvalidationChecking));
TEST_P(PaintPropertyTreeUpdateTest,
ThreadedScrollingDisabledMainThreadScrollReason) {
@@ -826,7 +832,7 @@ TEST_P(PaintPropertyTreeUpdateTest,
SetBodyInnerHTML(R"HTML(
<style>
::-webkit-scrollbar {width: 20px; height: 20px}
- body {height: 10000px; width: 10000px; margin: 0;}
+ body {height: 2000px; width: 2000px; margin: 0;}
</style>
)HTML");
@@ -840,7 +846,7 @@ TEST_P(PaintPropertyTreeUpdateTest,
TEST_P(PaintPropertyTreeUpdateTest, ViewportAddRemoveDeviceEmulationNode) {
SetBodyInnerHTML(
- "<style>body {height: 10000px; width: 10000px; margin: 0;}</style>");
+ "<style>body {height: 2000px; width: 2000px; margin: 0;}</style>");
auto& visual_viewport = GetDocument().GetPage()->GetVisualViewport();
EXPECT_FALSE(visual_viewport.GetDeviceEmulationTransformNode());
@@ -858,8 +864,10 @@ TEST_P(PaintPropertyTreeUpdateTest, ViewportAddRemoveDeviceEmulationNode) {
EXPECT_EQ(&TransformPaintPropertyNode::Root(),
&scrollbar_layer->GetPropertyTreeState().Transform());
} else {
- // TODO(wangxianzhu): Test for CompositeAfterPaint.
- EXPECT_FALSE(scrollbar_layer);
+ auto& chunk = *(ContentPaintChunks().begin() + 1);
+ EXPECT_EQ(DisplayItem::kScrollbarHorizontal, chunk.id.type);
+ EXPECT_EQ(&TransformPaintPropertyNode::Root(),
+ &chunk.properties.Transform());
}
// These emulate WebViewImpl::SetDeviceEmulationTransform().
@@ -874,8 +882,10 @@ TEST_P(PaintPropertyTreeUpdateTest, ViewportAddRemoveDeviceEmulationNode) {
EXPECT_EQ(visual_viewport.GetDeviceEmulationTransformNode(),
&scrollbar_layer->GetPropertyTreeState().Transform());
} else {
- // TODO(wangxianzhu): Test for CompositeAfterPaint.
- EXPECT_FALSE(scrollbar_layer);
+ auto& chunk = *(ContentPaintChunks().begin() + 1);
+ EXPECT_EQ(DisplayItem::kScrollbarHorizontal, chunk.id.type);
+ EXPECT_EQ(visual_viewport.GetDeviceEmulationTransformNode(),
+ &chunk.properties.Transform());
}
// These emulate WebViewImpl::SetDeviceEmulationTransform().
@@ -889,8 +899,10 @@ TEST_P(PaintPropertyTreeUpdateTest, ViewportAddRemoveDeviceEmulationNode) {
EXPECT_EQ(&TransformPaintPropertyNode::Root(),
&scrollbar_layer->GetPropertyTreeState().Transform());
} else {
- // TODO(wangxianzhu): Test for CompositeAfterPaint.
- EXPECT_FALSE(scrollbar_layer);
+ auto& chunk = *(ContentPaintChunks().begin() + 1);
+ EXPECT_EQ(DisplayItem::kScrollbarHorizontal, chunk.id.type);
+ EXPECT_EQ(&TransformPaintPropertyNode::Root(),
+ &chunk.properties.Transform());
}
}
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 5b92ab258b1..44da167084b 100644
--- a/chromium/third_party/blink/renderer/core/paint/paint_timing.cc
+++ b/chromium/third_party/blink/renderer/core/paint/paint_timing.cc
@@ -23,6 +23,7 @@
#include "third_party/blink/renderer/core/probe/core_probes.h"
#include "third_party/blink/renderer/core/timing/dom_window_performance.h"
#include "third_party/blink/renderer/core/timing/window_performance.h"
+#include "third_party/blink/renderer/platform/graphics/paint/ignore_paint_timing_scope.h"
#include "third_party/blink/renderer/platform/instrumentation/resource_coordinator/document_resource_coordinator.h"
#include "third_party/blink/renderer/platform/instrumentation/tracing/trace_event.h"
#include "third_party/blink/renderer/platform/loader/fetch/resource_fetcher.h"
@@ -49,7 +50,7 @@ class RecodingTimeAfterBackForwardCacheRestoreFrameCallback
public:
RecodingTimeAfterBackForwardCacheRestoreFrameCallback(
PaintTiming* paint_timing,
- size_t record_index)
+ wtf_size_t record_index)
: paint_timing_(paint_timing), record_index_(record_index) {}
~RecodingTimeAfterBackForwardCacheRestoreFrameCallback() override = default;
@@ -81,7 +82,7 @@ class RecodingTimeAfterBackForwardCacheRestoreFrameCallback
private:
Member<PaintTiming> paint_timing_;
- const size_t record_index_;
+ const wtf_size_t record_index_;
size_t count_ = 0;
};
@@ -104,6 +105,7 @@ void PaintTiming::MarkFirstPaint() {
// markFirstPaint().
if (!first_paint_.is_null())
return;
+ DCHECK_EQ(IgnorePaintTimingScope::IgnoreDepth(), 0);
SetFirstPaint(clock_->NowTicks());
}
@@ -114,12 +116,15 @@ void PaintTiming::MarkFirstContentfulPaint() {
// markFirstContentfulPaint().
if (!first_contentful_paint_.is_null())
return;
+ if (IgnorePaintTimingScope::IgnoreDepth() > 0)
+ return;
SetFirstContentfulPaint(clock_->NowTicks());
}
void PaintTiming::MarkFirstImagePaint() {
if (!first_image_paint_.is_null())
return;
+ DCHECK_EQ(IgnorePaintTimingScope::IgnoreDepth(), 0);
first_image_paint_ = clock_->NowTicks();
SetFirstContentfulPaint(first_image_paint_);
RegisterNotifyPresentationTime(PaintEvent::kFirstImagePaint);
@@ -176,6 +181,8 @@ void PaintTiming::SetFirstMeaningfulPaint(
void PaintTiming::NotifyPaint(bool is_first_paint,
bool text_painted,
bool image_painted) {
+ if (IgnorePaintTimingScope::IgnoreDepth() > 0)
+ return;
if (is_first_paint)
MarkFirstPaint();
if (text_painted)
@@ -223,13 +230,7 @@ void PaintTiming::SetFirstPaint(base::TimeTicks stamp) {
if (!first_paint_.is_null())
return;
- LocalFrame* frame = GetFrame();
- if (frame && frame->GetDocument()) {
- Document* document = frame->GetDocument();
- document->MarkFirstPaint();
- if (frame->IsMainFrame())
- document->Fetcher()->MarkFirstPaint();
- }
+ DCHECK_EQ(IgnorePaintTimingScope::IgnoreDepth(), 0);
first_paint_ = stamp;
RegisterNotifyPresentationTime(PaintEvent::kFirstPaint);
@@ -238,6 +239,7 @@ void PaintTiming::SetFirstPaint(base::TimeTicks stamp) {
void PaintTiming::SetFirstContentfulPaint(base::TimeTicks stamp) {
if (!first_contentful_paint_.is_null())
return;
+ DCHECK_EQ(IgnorePaintTimingScope::IgnoreDepth(), 0);
SetFirstPaint(stamp);
first_contentful_paint_ = stamp;
RegisterNotifyPresentationTime(PaintEvent::kFirstContentfulPaint);
@@ -248,9 +250,6 @@ void PaintTiming::SetFirstContentfulPaint(base::TimeTicks stamp) {
return;
frame->View()->OnFirstContentfulPaint();
- if (frame->GetDocument() && frame->GetDocument()->Fetcher())
- frame->GetDocument()->Fetcher()->MarkFirstContentfulPaint();
-
if (frame->GetFrameScheduler())
frame->GetFrameScheduler()->OnFirstContentfulPaintInMainFrame();
@@ -266,7 +265,7 @@ void PaintTiming::RegisterNotifyPresentationTime(PaintEvent event) {
void PaintTiming::
RegisterNotifyFirstPaintAfterBackForwardCacheRestorePresentationTime(
- size_t index) {
+ wtf_size_t index) {
RegisterNotifyPresentationTime(CrossThreadBindOnce(
&PaintTiming::
ReportFirstPaintAfterBackForwardCacheRestorePresentationTime,
@@ -299,7 +298,6 @@ void PaintTiming::ReportPresentationTime(PaintEvent event,
//
// TODO(crbug.com/738235): Consider not reporting any timestamp when failing
// for reasons other than kDidNotSwapSwapFails.
- ReportSwapResultHistogram(result);
switch (event) {
case PaintEvent::kFirstPaint:
SetFirstPaintPresentation(timestamp);
@@ -319,11 +317,10 @@ void PaintTiming::ReportPresentationTime(PaintEvent event,
}
void PaintTiming::ReportFirstPaintAfterBackForwardCacheRestorePresentationTime(
- size_t index,
+ wtf_size_t index,
WebSwapResult result,
base::TimeTicks timestamp) {
DCHECK(IsMainThread());
- ReportSwapResultHistogram(result);
SetFirstPaintAfterBackForwardCacheRestorePresentation(timestamp, index);
}
@@ -382,7 +379,7 @@ void PaintTiming::SetFirstImagePaintPresentation(base::TimeTicks stamp) {
void PaintTiming::SetFirstPaintAfterBackForwardCacheRestorePresentation(
base::TimeTicks stamp,
- size_t index) {
+ wtf_size_t index) {
// The elements are allocated when the page is restored from the cache.
DCHECK_GE(first_paints_after_back_forward_cache_restore_presentation_.size(),
index);
@@ -393,7 +390,7 @@ void PaintTiming::SetFirstPaintAfterBackForwardCacheRestorePresentation(
}
void PaintTiming::SetRequestAnimationFrameAfterBackForwardCacheRestore(
- size_t index,
+ wtf_size_t index,
size_t count) {
auto now = clock_->NowTicks();
@@ -407,15 +404,10 @@ void PaintTiming::SetRequestAnimationFrameAfterBackForwardCacheRestore(
current_rafs[count] = now;
}
-void PaintTiming::ReportSwapResultHistogram(WebSwapResult result) {
- UMA_HISTOGRAM_ENUMERATION("PageLoad.Internal.Renderer.PaintTiming.SwapResult",
- result);
-}
-
void PaintTiming::OnRestoredFromBackForwardCache() {
// Allocate the last element with 0, which indicates that the first paint
// after this navigation doesn't happen yet.
- size_t index =
+ wtf_size_t index =
first_paints_after_back_forward_cache_restore_presentation_.size();
DCHECK_EQ(index,
request_animation_frames_after_back_forward_cache_restore_.size());
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 d0dbdf06ace..7a44f1d77ca 100644
--- a/chromium/third_party/blink/renderer/core/paint/paint_timing.h
+++ b/chromium/third_party/blink/renderer/core/paint/paint_timing.h
@@ -146,12 +146,10 @@ class CORE_EXPORT PaintTiming final : public GarbageCollected<PaintTiming>,
WebSwapResult,
base::TimeTicks timestamp);
void ReportFirstPaintAfterBackForwardCacheRestorePresentationTime(
- size_t index,
+ wtf_size_t index,
WebSwapResult,
base::TimeTicks timestamp);
- void ReportSwapResultHistogram(WebSwapResult);
-
// The caller owns the |clock| which must outlive the PaintTiming.
void SetTickClockForTesting(const base::TickClock* clock);
@@ -191,13 +189,13 @@ class CORE_EXPORT PaintTiming final : public GarbageCollected<PaintTiming>,
// index to avoid confusing the data from different navigations.
void SetFirstPaintAfterBackForwardCacheRestorePresentation(
base::TimeTicks stamp,
- size_t index);
- void SetRequestAnimationFrameAfterBackForwardCacheRestore(size_t index,
+ wtf_size_t index);
+ void SetRequestAnimationFrameAfterBackForwardCacheRestore(wtf_size_t index,
size_t count);
void RegisterNotifyPresentationTime(PaintEvent);
void RegisterNotifyFirstPaintAfterBackForwardCacheRestorePresentationTime(
- size_t index);
+ wtf_size_t index);
base::TimeTicks FirstPaintRendered() const { return first_paint_; }
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 85e53c825b4..9a795855075 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
@@ -9,6 +9,7 @@
#include "third_party/blink/renderer/core/frame/local_frame_view.h"
#include "third_party/blink/renderer/core/frame/web_frame_widget_impl.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/layout/layout_box_model_object.h"
#include "third_party/blink/renderer/core/layout/layout_object.h"
#include "third_party/blink/renderer/core/layout/layout_view.h"
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 0c8698caf2d..9b8bd4c7c66 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
@@ -13,6 +13,7 @@
#include "third_party/blink/renderer/core/scroll/scroll_types.h"
#include "third_party/blink/renderer/platform/graphics/paint/ignore_paint_timing_scope.h"
#include "third_party/blink/renderer/platform/heap/member.h"
+#include "third_party/blink/renderer/platform/instrumentation/tracing/trace_event.h"
#include "third_party/blink/renderer/platform/wtf/cross_thread_functional.h"
namespace blink {
diff --git a/chromium/third_party/blink/renderer/core/paint/pre_paint_tree_walk.cc b/chromium/third_party/blink/renderer/core/paint/pre_paint_tree_walk.cc
index 08626f312fb..9806adced82 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
@@ -5,6 +5,7 @@
#include "third_party/blink/renderer/core/paint/pre_paint_tree_walk.h"
#include "base/auto_reset.h"
+#include "base/stl_util.h"
#include "cc/base/features.h"
#include "third_party/blink/renderer/core/dom/document_lifecycle.h"
#include "third_party/blink/renderer/core/frame/event_handler_registry.h"
@@ -15,20 +16,19 @@
#include "third_party/blink/renderer/core/frame/visual_viewport.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_fieldset.h"
-#include "third_party/blink/renderer/core/layout/layout_multi_column_spanner_placeholder.h"
+#include "third_party/blink/renderer/core/layout/layout_multi_column_flow_thread.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/inline/ng_inline_cursor.h"
#include "third_party/blink/renderer/core/layout/ng/ng_block_break_token.h"
-#include "third_party/blink/renderer/core/layout/ng/ng_fragment_child_iterator.h"
#include "third_party/blink/renderer/core/layout/ng/ng_fragmentation_utils.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"
#include "third_party/blink/renderer/core/paint/compositing/paint_layer_compositor.h"
-#include "third_party/blink/renderer/core/paint/cull_rect_updater.h"
+#include "third_party/blink/renderer/core/paint/object_paint_invalidator.h"
#include "third_party/blink/renderer/core/paint/paint_layer.h"
#include "third_party/blink/renderer/core/paint/paint_property_tree_printer.h"
#include "third_party/blink/renderer/platform/graphics/paint/geometry_mapper.h"
@@ -36,114 +36,6 @@
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) {
- // TODO(crbug.com/1043787): 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.
- //
- // There's essentially a 1:1 correspondence between a block-level
- // NGPhysicalBoxFragment and FragmentData, but there's no direct link between
- // them, so we have to do some work. In the future we might want to make
- // FragmentData part of NGPhysicalBoxFragment objects, to simplify this, and
- // to get rid of O(n^2) performance complexity (where n is the number of
- // fragments generated by a node). Note that this performance complexity also
- // exists in the legacy engine.
- //
- // For inline-level nodes, it gets a bit more complicated. There's one
- // FragmentData object per fragmentainer said node occurs in. The offset and
- // invalidation rectangle in each FragmentData will be a union of all the
- // fragments generated by the node (one per line box, typically) in that
- // fragmentainer. This also matches how we do it in legacy layout. It's
- // considered too expensive to have one FragmentData object per line for each
- // text node or non-atomic inline.
- DCHECK(iterator->GetLayoutObject());
- const LayoutObject& object = *iterator->GetLayoutObject();
- FragmentData* fragment_data = &object.GetMutableForPainting().FirstFragment();
- const auto* incoming_break_token = iterator->BlockBreakToken();
- const NGPhysicalBoxFragment* box_fragment = iterator->BoxFragment();
-
- // The need for paint properties is the same across all fragments, so if the
- // first FragmentData needs it, so do all the others.
- bool needs_paint_properties = fragment_data->PaintProperties();
-
- if (const NGFragmentItem* fragment_item = iterator->FragmentItem()) {
- // We're in an inline formatting context. The consumed block-size stored in
- // the incoming break token will be stored in FragmentData objects to
- // identify each portion for a given fragmentainer.
- LayoutUnit consumed_block_size;
- if (incoming_break_token)
- consumed_block_size = incoming_break_token->ConsumedBlockSize();
- 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 remove additional
- // FragmentData objects, and reset the visual rect. The visual rect will
- // be set and expanded, as we visit each individual fragment.
- fragment_data->ClearNextFragment();
- }
- fragment_data->SetLogicalTopInFlowThread(consumed_block_size);
- } else {
- // This is not the first fragment. Now see if we can find a FragmentData
- // with the right consumed block-size (or flow thread logical top). If
- // not, we'll have to create one now.
- while (consumed_block_size > fragment_data->LogicalTopInFlowThread()) {
- FragmentData* next_fragment_data = fragment_data->NextFragment();
- if (!next_fragment_data) {
- fragment_data = &fragment_data->EnsureNextFragment();
- fragment_data->SetLogicalTopInFlowThread(consumed_block_size);
- break;
- }
- fragment_data = next_fragment_data;
- }
- DCHECK_EQ(fragment_data->LogicalTopInFlowThread(), consumed_block_size);
- }
- } else {
- // The fragment is block-level.
- if (IsResumingLayout(incoming_break_token)) {
- // This isn't the first fragment for the node. We now need to walk past
- // all preceding fragments to figure out which FragmentData to return (or
- // create, if it doesn't already exist).
- const auto& layout_box = To<LayoutBox>(object);
- for (wtf_size_t idx = 0;; idx++) {
- DCHECK_LT(idx, layout_box.PhysicalFragmentCount());
- if (layout_box.GetPhysicalFragment(idx) == box_fragment)
- break;
- FragmentData* next = fragment_data->NextFragment();
- if (!next) {
- DCHECK_EQ(layout_box.GetPhysicalFragment(idx + 1), box_fragment);
- fragment_data = &fragment_data->EnsureNextFragment();
- break;
- }
- fragment_data = next;
- }
- fragment_data->SetLogicalTopInFlowThread(
- incoming_break_token->ConsumedBlockSize());
- }
- if (!box_fragment->BreakToken()) {
- // We have reached the end. There may be more data entries that were
- // needed in the previous layout, but not any more. Clear them.
- fragment_data->ClearNextFragment();
- }
- }
-
- if (needs_paint_properties)
- fragment_data->EnsurePaintProperties();
-
- return NGPrePaintInfo(iterator, *fragment_data);
-}
-
-} // anonymous namespace
-
static void SetNeedsCompositingLayerPropertyUpdate(const LayoutObject& object) {
if (RuntimeEnabledFeatures::CompositeAfterPaintEnabled())
return;
@@ -192,7 +84,7 @@ void PrePaintTreeWalk::WalkTree(LocalFrameView& root_frame_view) {
if (root_frame_view.GetFrame().IsMainFrame()) {
auto property_changed = VisualViewportPaintPropertyTreeBuilder::Update(
- root_frame_view.GetPage()->GetVisualViewport(),
+ root_frame_view, root_frame_view.GetPage()->GetVisualViewport(),
*context.tree_builder_context);
if (property_changed >
@@ -206,14 +98,10 @@ void PrePaintTreeWalk::WalkTree(LocalFrameView& root_frame_view) {
Walk(root_frame_view, context);
paint_invalidator_.ProcessPendingDelayedPaintInvalidations();
- if (RuntimeEnabledFeatures::CullRectUpdateEnabled()) {
- if (auto* layout_view = root_frame_view.GetLayoutView())
- CullRectUpdater(*layout_view->Layer()).Update();
- }
-
#if DCHECK_IS_ON()
if (needs_tree_builder_context_update) {
- if (VLOG_IS_ON(2) && root_frame_view.GetLayoutView()) {
+ if (!RuntimeEnabledFeatures::CullRectUpdateEnabled() && VLOG_IS_ON(2) &&
+ root_frame_view.GetLayoutView()) {
VLOG(2) << "PrePaintTreeWalk::Walk(root_frame_view=" << &root_frame_view
<< ")\nPaintLayer tree:";
showLayerTree(root_frame_view.GetLayoutView()->Layer());
@@ -257,6 +145,12 @@ void PrePaintTreeWalk::Walk(LocalFrameView& frame_view,
PrePaintTreeWalkContext context(parent_context,
needs_tree_builder_context_update);
+ // Block fragmentation doesn't cross frame boundaries.
+ context.current_fragmentainer = {};
+ context.absolute_positioned_container = {};
+ context.fixed_positioned_container = {};
+ context.oof_container_candidate_fragment = nullptr;
+
// ancestor_scroll_container_paint_layer does not cross frame boundaries.
context.ancestor_scroll_container_paint_layer = nullptr;
if (context.tree_builder_context) {
@@ -273,14 +167,14 @@ void PrePaintTreeWalk::Walk(LocalFrameView& frame_view,
<< ")\nLayout tree:";
showLayoutTree(view);
VLOG(3) << "Fragment tree:";
- NGPhysicalFragment::ShowFragmentTree(*view);
+ ShowFragmentTree(*view);
}
#endif
is_wheel_event_regions_enabled_ =
base::FeatureList::IsEnabled(::features::kWheelEventRegions);
- Walk(*view, context, /* iterator */ nullptr);
+ Walk(*view, context, /* pre_paint_info */ nullptr);
#if DCHECK_IS_ON()
view->AssertSubtreeClearedPaintInvalidationFlags();
#endif
@@ -428,8 +322,14 @@ bool PrePaintTreeWalk::NeedsTreeBuilderContextUpdate(
}
return frame_view.GetLayoutView() &&
- (ObjectRequiresTreeBuilderContext(*frame_view.GetLayoutView()) ||
- ContextRequiresChildTreeBuilderContext(context));
+ NeedsTreeBuilderContextUpdate(*frame_view.GetLayoutView(), context);
+}
+
+bool PrePaintTreeWalk::NeedsTreeBuilderContextUpdate(
+ const LayoutObject& object,
+ const PrePaintTreeWalkContext& parent_context) {
+ return ContextRequiresChildTreeBuilderContext(parent_context) ||
+ ObjectRequiresTreeBuilderContext(object);
}
bool PrePaintTreeWalk::ObjectRequiresPrePaint(const LayoutObject& object) {
@@ -548,18 +448,108 @@ void PrePaintTreeWalk::UpdatePaintInvalidationContainer(
}
}
+NGPrePaintInfo PrePaintTreeWalk::CreatePrePaintInfo(
+ const NGLink& child,
+ const PrePaintTreeWalkContext& context) {
+ const auto& fragment = *To<NGPhysicalBoxFragment>(child.fragment);
+ return NGPrePaintInfo(fragment, child.offset,
+ context.current_fragmentainer.fragmentainer_idx,
+ fragment.IsFirstForNode(), !fragment.BreakToken(),
+ context.is_inside_orphaned_object,
+ /* is_inside_fragment_child */ false);
+}
+
+FragmentData* PrePaintTreeWalk::GetOrCreateFragmentData(
+ const LayoutObject& object,
+ const PrePaintTreeWalkContext& context,
+ const NGPrePaintInfo& pre_paint_info) {
+ // If |allow_update| is set, we're allowed to add, remove and modify
+ // FragmentData objects. Otherwise they will be left alone.
+ bool allow_update = context.NeedsTreeBuilderContext();
+
+ FragmentData* fragment_data = &object.GetMutableForPainting().FirstFragment();
+
+ // The need for paint properties is the same across all fragments, so if the
+ // first FragmentData needs it, so do all the others.
+ bool needs_paint_properties = fragment_data->PaintProperties();
+
+ wtf_size_t fragment_id = pre_paint_info.fragmentainer_idx;
+ // TODO(mstensho): For now we need to treat unfragmented as ID 0. It doesn't
+ // really matter for LayoutNG, but legacy
+ // PaintPropertyTreeBuilder::ContextForFragment() may take a walk up the tree
+ // and end up querying this (LayoutNG) object, and
+ // FragmentData::LogicalTopInFlowThread() will DCHECK that the value is 0
+ // unless it has been explicitly set by legacy code (which won't happen, since
+ // it's an NG object).
+ if (fragment_id == WTF::kNotFound)
+ fragment_id = 0;
+
+ if (pre_paint_info.is_first_for_node) {
+ if (allow_update)
+ fragment_data->ClearNextFragment();
+ else
+ DCHECK_EQ(fragment_data->FragmentID(), fragment_id);
+ } else {
+ FragmentData* last_fragment = nullptr;
+ do {
+ if (fragment_data->FragmentID() >= fragment_id)
+ break;
+ last_fragment = fragment_data;
+ fragment_data = fragment_data->NextFragment();
+ } while (fragment_data);
+ if (fragment_data) {
+ if (pre_paint_info.is_last_for_node) {
+ // We have reached the end. There may be more data entries that were
+ // needed in the previous layout, but not any more. Clear them.
+ if (allow_update)
+ fragment_data->ClearNextFragment();
+ else
+ DCHECK(!fragment_data->NextFragment());
+ } else if (fragment_data->FragmentID() != fragment_id) {
+ // There are entries for fragmentainers after this one, but none for
+ // this one. Remove the fragment tail.
+ DCHECK(allow_update);
+ DCHECK_GT(fragment_data->FragmentID(), fragment_id);
+ fragment_data->ClearNextFragment();
+ }
+ } else {
+ DCHECK(allow_update);
+ fragment_data = &last_fragment->EnsureNextFragment();
+ }
+ }
+
+ if (allow_update) {
+ fragment_data->SetFragmentID(fragment_id);
+
+ if (needs_paint_properties)
+ fragment_data->EnsurePaintProperties();
+ } else {
+ DCHECK_EQ(fragment_data->FragmentID(), fragment_id);
+ DCHECK(!needs_paint_properties || fragment_data->PaintProperties());
+ }
+
+ return fragment_data;
+}
+
void PrePaintTreeWalk::WalkInternal(const LayoutObject& object,
PrePaintTreeWalkContext& context,
- const NGFragmentChildIterator* iterator) {
+ NGPrePaintInfo* pre_paint_info) {
PaintInvalidatorContext& paint_invalidator_context =
context.paint_invalidator_context;
- absl::optional<NGPrePaintInfo> pre_paint_info_storage;
- NGPrePaintInfo* pre_paint_info = nullptr;
- if (iterator) {
- bool allow_reset = context.NeedsTreeBuilderContext();
- pre_paint_info_storage.emplace(SetupFragmentData(*iterator, allow_reset));
- pre_paint_info = &pre_paint_info_storage.value();
+ if (pre_paint_info) {
+ DCHECK(!pre_paint_info->fragment_data);
+ // Find, update or create a FragmentData object to match the current block
+ // fragment.
+ //
+ // TODO(mstensho): If this is collapsed text or a culled inline, we might
+ // not have any work to do (we could just return early here), as there'll be
+ // no need for paint property updates or invalidation. However, this is a
+ // bit tricky to determine, because of things like LinkHighlight, which
+ // might set paint properties on a culled inline.
+ pre_paint_info->fragment_data =
+ GetOrCreateFragmentData(object, context, *pre_paint_info);
+ DCHECK(pre_paint_info->fragment_data);
}
// This must happen before updatePropertiesForSelf, because the latter reads
@@ -677,6 +667,70 @@ void PrePaintTreeWalk::WalkInternal(const LayoutObject& object,
}
}
+bool PrePaintTreeWalk::CollectMissableChildren(
+ PrePaintTreeWalkContext& context,
+ const NGPhysicalBoxFragment& parent) {
+ bool has_missable_children = false;
+ for (const NGLink& child : parent.Children()) {
+ if ((child->IsOutOfFlowPositioned() &&
+ (context.current_fragmentainer.fragment ||
+ child->IsFixedPositioned())) ||
+ (child->IsFloating() && parent.IsInlineFormattingContext() &&
+ context.current_fragmentainer.fragment)) {
+ // We'll add resumed floats (or floats that couldn't fit a fragment in the
+ // fragmentainer where it was discovered) that have escaped their inline
+ // formatting context.
+ //
+ // We'll also add all out-of-flow positioned fragments inside a
+ // fragmentation context. If a fragment is fixed-positioned, we even need
+ // to add those that aren't inside a fragmentation context, because they
+ // may have an ancestor LayoutObject inside one, and one of those
+ // ancestors may be out-of-flow positioned, which may be missed, in which
+ // case we'll miss this fixed-positioned one as well (since we don't enter
+ // descendant OOFs when walking missed children) (example: fixedpos inside
+ // missed abspos in relpos in multicol).
+ pending_missables_.insert(child.fragment);
+ has_missable_children = true;
+ }
+ }
+ return has_missable_children;
+}
+
+void PrePaintTreeWalk::WalkMissedChildren(const NGPhysicalBoxFragment& fragment,
+ PrePaintTreeWalkContext& context) {
+ if (pending_missables_.IsEmpty())
+ return;
+
+ for (const NGLink& child : fragment.Children()) {
+ if (!child->IsOutOfFlowPositioned() && !child->IsFloating())
+ continue;
+ if (!pending_missables_.Contains(child.fragment))
+ continue;
+ const LayoutObject& descendant_object = *child->GetLayoutObject();
+ PrePaintTreeWalkContext descendant_context(
+ context, NeedsTreeBuilderContextUpdate(descendant_object, context));
+ if (child->IsOutOfFlowPositioned() &&
+ descendant_context.tree_builder_context) {
+ PaintPropertyTreeBuilderFragmentContext& fragment_context =
+ descendant_context.tree_builder_context->fragments[0];
+ // Reset the relevant OOF context to this fragmentainer, since this is its
+ // containing block, as far as the NG fragment structure is concerned.
+ // Note that when walking a missed child OOF fragment, we'll also
+ // forcefully miss any OOF descendant nodes, which is why we only set the
+ // context for the OOF type we're dealing with here.
+ if (child->IsFixedPositioned())
+ fragment_context.fixed_position = fragment_context.current;
+ else
+ fragment_context.absolute_position = fragment_context.current;
+ }
+ descendant_context.is_inside_orphaned_object = true;
+
+ NGPrePaintInfo pre_paint_info =
+ CreatePrePaintInfo(child, descendant_context);
+ Walk(descendant_object, descendant_context, &pre_paint_info);
+ }
+}
+
LocalFrameView* FindWebViewPluginContentFrameView(
const LayoutEmbeddedContent& embedded_content) {
for (Frame* frame = embedded_content.GetFrame()->Tree().FirstChild(); frame;
@@ -688,248 +742,454 @@ LocalFrameView* FindWebViewPluginContentFrameView(
return nullptr;
}
-void PrePaintTreeWalk::WalkNGChildren(const LayoutObject* parent,
- PrePaintTreeWalkContext& parent_context,
- NGFragmentChildIterator* iterator) {
+void PrePaintTreeWalk::WalkFragmentationContextRootChildren(
+ const LayoutObject& object,
+ const NGPhysicalBoxFragment& fragment,
+ PrePaintTreeWalkContext& context) {
+ // The actual children are inside the flow thread child of |object|.
+ const auto* flow_thread =
+ To<LayoutBlockFlow>(&object)->MultiColumnFlowThread();
+ const LayoutObject& actual_parent = flow_thread ? *flow_thread : object;
+
FragmentData* fragmentainer_fragment_data = nullptr;
#if DCHECK_IS_ON()
const LayoutObject* fragmentainer_owner_box = nullptr;
#endif
- 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.
- if (fragment_item->Type() == NGFragmentItem::kLine) {
- WalkChildren(/* parent */ nullptr, parent_context, iterator);
- continue;
- }
- } else if (!object) {
- const NGPhysicalBoxFragment* box_fragment = (*iterator)->BoxFragment();
- if (UNLIKELY(box_fragment->IsLayoutObjectDestroyedOrMoved()))
- continue;
- // Check |box_fragment| and the |LayoutBox| that produced it are in sync.
- // |OwnerLayoutBox()| has a few DCHECKs for this purpose.
- DCHECK(box_fragment->OwnerLayoutBox());
+ DCHECK(fragment.IsFragmentationContextRoot());
- // A fragmentainer doesn't paint anything itself. Just include its offset
- // and descend into children.
- DCHECK((*iterator)->BoxFragment()->IsFragmentainerBox());
- if (UNLIKELY(!parent_context.tree_builder_context)) {
- WalkChildren(/* parent */ nullptr, parent_context, iterator);
+ const auto outer_fragmentainer = context.current_fragmentainer;
+ absl::optional<wtf_size_t> inner_fragmentainer_idx;
+
+ for (NGLink child : fragment.Children()) {
+ const auto* box_fragment = To<NGPhysicalBoxFragment>(child.fragment);
+ if (UNLIKELY(box_fragment->IsLayoutObjectDestroyedOrMoved()))
+ continue;
+
+ if (box_fragment->GetLayoutObject()) {
+ // OOFs contained by a multicol container will be visited during object
+ // tree traversal.
+ if (box_fragment->IsOutOfFlowPositioned())
continue;
- }
- PaintPropertyTreeBuilderContext& tree_builder_context =
- *parent_context.tree_builder_context;
- PaintPropertyTreeBuilderFragmentContext& context =
- tree_builder_context.fragments[0];
- PaintPropertyTreeBuilderFragmentContext::ContainingBlockContext*
- containing_block_context = &context.current;
- const PhysicalOffset offset = (*iterator)->Link().offset;
- containing_block_context->paint_offset += offset;
- const PhysicalOffset paint_offset =
- containing_block_context->paint_offset;
-
- // Create corresponding |FragmentData|. Hit-testing needs
- // |FragmentData.PaintOffset|.
- if (fragmentainer_fragment_data) {
- DCHECK(!box_fragment->IsFirstForNode());
+ // We'll walk all other non-fragmentainer children directly now, entering
+ // them from the fragment tree, rather than from the LayoutObject tree.
+ // One consequence of this is that paint effects on any ancestors between
+ // a column spanner and its multicol container will not be applied on the
+ // spanner. This is fixable, but it would require non-trivial amounts of
+ // special-code for such a special case. If anyone complains, we can
+ // revisit this decision.
+ if (box_fragment->IsColumnSpanAll())
+ context.current_fragmentainer = outer_fragmentainer;
+
+ NGPrePaintInfo pre_paint_info = CreatePrePaintInfo(child, context);
+ Walk(*box_fragment->GetLayoutObject(), context, &pre_paint_info);
+ continue;
+ }
+
+ // Check |box_fragment| and the |LayoutBox| that produced it are in sync.
+ // |OwnerLayoutBox()| has a few DCHECKs for this purpose.
+ DCHECK(box_fragment->OwnerLayoutBox());
+
+ // A fragmentainer doesn't paint anything itself. Just include its offset
+ // and descend into children.
+ DCHECK(box_fragment->IsFragmentainerBox());
+
+ // Always keep track of the current innermost fragmentainer we're handling,
+ // as they may serve as containing blocks for OOF descendants.
+ context.current_fragmentainer.fragment = box_fragment;
+
+ // Set up |inner_fragmentainer_idx| lazily, as it's O(n) (n == number of
+ // multicol container fragments).
+ if (!inner_fragmentainer_idx)
+ inner_fragmentainer_idx = PreviousInnerFragmentainerIndex(fragment);
+ context.current_fragmentainer.fragmentainer_idx = *inner_fragmentainer_idx;
+
+ if (UNLIKELY(!context.tree_builder_context)) {
+ WalkChildren(actual_parent, box_fragment, context);
+ continue;
+ }
+
+ PaintPropertyTreeBuilderContext& tree_builder_context =
+ *context.tree_builder_context;
+ PaintPropertyTreeBuilderFragmentContext& fragment_context =
+ tree_builder_context.fragments[0];
+ PaintPropertyTreeBuilderFragmentContext::ContainingBlockContext*
+ containing_block_context = &fragment_context.current;
+ containing_block_context->paint_offset += child.offset;
+
+ const PhysicalOffset paint_offset = containing_block_context->paint_offset;
+ // Keep track of the paint offset at the fragmentainer, and also reset the
+ // offset adjustment tracker. This is needed when entering OOF
+ // descendants. OOFs have the nearest fragmentainer as their containing
+ // block, so when entering them during LayoutObject tree traversal, we have
+ // to compensate for this.
+ fragment_context.fragmentainer_paint_offset = paint_offset;
+ fragment_context.adjustment_for_oof_in_fragmentainer = PhysicalOffset();
+
+ // Create corresponding |FragmentData|. Hit-testing needs
+ // |FragmentData.PaintOffset|.
+ if (fragmentainer_fragment_data) {
+ DCHECK(!box_fragment->IsFirstForNode());
#if DCHECK_IS_ON()
- DCHECK_EQ(fragmentainer_owner_box, box_fragment->OwnerLayoutBox());
+ DCHECK_EQ(fragmentainer_owner_box, box_fragment->OwnerLayoutBox());
#endif
- fragmentainer_fragment_data =
- &fragmentainer_fragment_data->EnsureNextFragment();
- } else {
- const LayoutBox* owner_box = box_fragment->OwnerLayoutBox();
+ fragmentainer_fragment_data =
+ &fragmentainer_fragment_data->EnsureNextFragment();
+ } else {
+ const LayoutBox* owner_box = box_fragment->OwnerLayoutBox();
#if DCHECK_IS_ON()
- DCHECK(!fragmentainer_owner_box);
- fragmentainer_owner_box = owner_box;
+ DCHECK(!fragmentainer_owner_box);
+ fragmentainer_owner_box = owner_box;
#endif
+ fragmentainer_fragment_data =
+ &owner_box->GetMutableForPainting().FirstFragment();
+ if (box_fragment->IsFirstForNode()) {
+ fragmentainer_fragment_data->ClearNextFragment();
+ } else {
+ // |box_fragment| is nested in another fragmentainer, and that it is
+ // the first one in this loop, but not the first one for the
+ // |LayoutObject|. Append a new |FragmentData| to the last one.
fragmentainer_fragment_data =
- &owner_box->GetMutableForPainting().FirstFragment();
- if (box_fragment->IsFirstForNode()) {
- fragmentainer_fragment_data->ClearNextFragment();
- } else {
- // |box_fragment| is nested in another fragmentainer, and that it is
- // the first one in this loop, but not the first one for the
- // |LayoutObject|. Append a new |FragmentData| to the last one.
- fragmentainer_fragment_data =
- &fragmentainer_fragment_data->LastFragment().EnsureNextFragment();
- }
+ &fragmentainer_fragment_data->LastFragment().EnsureNextFragment();
}
- fragmentainer_fragment_data->SetPaintOffset(paint_offset);
-
- WalkChildren(/* parent */ nullptr, parent_context, iterator);
- containing_block_context->paint_offset -= offset;
- continue;
}
- Walk(*object, parent_context, iterator);
+ fragmentainer_fragment_data->SetPaintOffset(paint_offset);
+
+ WalkChildren(actual_parent, box_fragment, context);
+
+ containing_block_context->paint_offset -= child.offset;
+ (*inner_fragmentainer_idx)++;
}
- const LayoutBlockFlow* parent_block = DynamicTo<LayoutBlockFlow>(parent);
- if (!parent_block || !parent_block->MultiColumnFlowThread())
+ if (!flow_thread)
return;
// Multicol containers only contain special legacy children invisible to
// LayoutNG, so we need to clean them manually.
- for (const LayoutObject* child = parent->SlowFirstChild(); child;
+ if (fragment.BreakToken())
+ return; // Wait until we've reached the end.
+ for (const LayoutObject* child = object.SlowFirstChild(); child;
child = child->NextSibling()) {
DCHECK(child->IsLayoutFlowThread() || child->IsLayoutMultiColumnSet() ||
child->IsLayoutMultiColumnSpannerPlaceholder());
child->GetMutableForPainting().ClearPaintFlags();
}
-}
-void PrePaintTreeWalk::WalkLegacyChildren(const LayoutObject& object,
- PrePaintTreeWalkContext& context) {
- if (const auto* layout_box = DynamicTo<LayoutBox>(&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));
- DCHECK(!fragment.BreakToken());
- NGFragmentChildIterator child_iterator(fragment);
- WalkNGChildren(&object, context, &child_iterator);
- return;
+ // If we missed any nested fixpos elements during fragment traversal, that
+ // means that their containing block lives outside the fragmentation context
+ // root. Walk these missed fixepos elements now.
+ if (!pending_fixedpos_missables_.IsEmpty()) {
+ for (const auto* fixedpos : pending_fixedpos_missables_) {
+ DCHECK(!walked_fixedpos_.Contains(fixedpos));
+ Walk(*fixedpos, context, /* pre_paint_info */ nullptr);
}
}
+}
- if (UNLIKELY(object.IsFieldsetIncludingNG())) {
- // Handle the rendered legend of the fieldset right away. It may not be a
- // direct child in the layout object tree (there may be an anonymous
- // fieldset content wrapper in-between, and even a flow thread), but it is
- // to be treated as such (similarly to out-of-flow positioned elements in a
- // way).
- if (const LayoutBox* legend =
- LayoutFieldset::FindInFlowLegend(To<LayoutBlock>(object)))
- Walk(*legend, context, /* iterator */ nullptr);
- }
+void PrePaintTreeWalk::WalkLayoutObjectChildren(
+ const LayoutObject& parent_object,
+ const NGPhysicalBoxFragment* parent_fragment,
+ PrePaintTreeWalkContext& context) {
+ absl::optional<NGInlineCursor> inline_cursor;
+ for (const LayoutObject* child = parent_object.SlowFirstChild(); child;
+ // Stay on the |child| while iterating fragments of |child|.
+ child = inline_cursor ? child : child->NextSibling()) {
+ if (!parent_fragment) {
+ // If we haven't found a fragment tree to accompany us in our walk,
+ // perform a pure LayoutObject tree walk. This is needed for legacy block
+ // fragmentation, and it also works fine if there's no block fragmentation
+ // involved at all (in such cases we can either to do this, or perform the
+ // NGPhysicalBoxFragment-accompanied walk that we do further down).
+
+ if (child->IsLayoutMultiColumnSpannerPlaceholder()) {
+ child->GetMutableForPainting().ClearPaintFlags();
+ continue;
+ }
- for (const LayoutObject* child = object.SlowFirstChild(); child;
- child = child->NextSibling()) {
- if (child->IsLayoutMultiColumnSpannerPlaceholder()) {
- child->GetMutableForPainting().ClearPaintFlags();
+ Walk(*child, context, /* pre_paint_info */ nullptr);
continue;
}
- // Skip out-of-flow positioned children lest they be walked twice. If |this|
- // is an NG object, but it still walks its children the legacy way (this may
- // happen to table-cells; see LayoutObject::CanTraversePhysicalFragments()),
- // we're either going to handle it in the code below after the loop - if
- // |this| is actually the containing block. Otherwise it will be handled by
- // some ancestor - either in the same code below (if it's a legacy-walking
- // object) or via regular child fragment traversal. If we walk it here as
- // well, we'd end up walking it twice. This is both bad for performance, and
- // also correctness, as fragment items are sensitive to how they're walked
- // (see SetupFragmentData()).
- if (UNLIKELY(RuntimeEnabledFeatures::LayoutNGFragmentTraversalEnabled() &&
- child->IsOutOfFlowPositioned() && object.IsLayoutNGObject()))
- continue;
- // The rendered legend was handled above, before processing the children of
- // the fieldset. So skip it when found during normal child traversal.
- if (UNLIKELY(child->IsRenderedLegend()))
+ // If we're in the middle of walking a missed OOF, don't enter nested OOFs
+ // (but miss those as well, and handle them via fragment traversal).
+ if (context.is_inside_orphaned_object && child->IsOutOfFlowPositioned()) {
+ if (child->IsFixedPositioned() && !walked_fixedpos_.Contains(child))
+ pending_fixedpos_missables_.insert(child);
continue;
+ }
- Walk(*child, context, /* iterator */ nullptr);
- }
+ // Perform an NGPhysicalBoxFragment-accompanied walk of the child
+ // LayoutObject tree.
+ //
+ // We'll map each child LayoutObject to a corresponding
+ // NGPhysicalBoxFragment. For text and non-atomic inlines this will be the
+ // fragment of their containing block, while for all other objects, it will
+ // be a fragment generated by the object itself. Even when we have LayoutNG
+ // fragments, we'll try to do the pre-paint walk it in LayoutObject tree
+ // order. This will ensure that paint properties are applied correctly (the
+ // LayoutNG fragment tree follows the containing block structure closely,
+ // but for paint effects, it's actually the LayoutObject / DOM tree
+ // structure that matters, e.g. when there's a relpos with a child with
+ // opacity, which has an absolutely positioned child, the absolutely
+ // positioned child should be affected by opacity, even if the object that
+ // establishes the opacity layer isn't in the containing block
+ // chain). Furthermore, culled inlines have no fragments, but they still
+ // need to be visited, since the invalidation code marks them for pre-paint.
+ const NGPhysicalBoxFragment* box_fragment = nullptr;
+ wtf_size_t fragmentainer_idx =
+ context.current_fragmentainer.fragmentainer_idx;
+ PhysicalOffset paint_offset;
+ const auto* child_box = DynamicTo<LayoutBox>(child);
+ bool is_first_for_node = true;
+ bool is_last_for_node = true;
+ bool is_inside_fragment_child = false;
+
+ if (!inline_cursor && parent_fragment->HasItems() &&
+ child->HasInlineFragments()) {
+ // Limit the search to descendants of |parent_fragment|.
+ inline_cursor.emplace(*parent_fragment);
+ inline_cursor->MoveTo(*child);
+ // Searching fragments of |child| may not find any because they may be in
+ // other fragmentainers than |parent_fragment|.
+ }
+ if (inline_cursor) {
+ for (; inline_cursor->Current();
+ inline_cursor->MoveToNextForSameLayoutObject()) {
+ // Check if the search is limited to descendants of |parent_fragment|.
+ DCHECK_EQ(&inline_cursor->ContainerFragment(), parent_fragment);
+ const NGFragmentItem& item = *inline_cursor->Current().Item();
+ DCHECK_EQ(item.GetLayoutObject(), child);
+
+ is_last_for_node = item.IsLastForNode();
+ if (box_fragment) {
+ if (is_last_for_node)
+ break;
+ continue;
+ }
- if (!RuntimeEnabledFeatures::LayoutNGFragmentTraversalEnabled())
- return;
+ paint_offset = item.OffsetInContainerFragment();
+ is_first_for_node = item.IsFirstForNode();
- const LayoutBlock* block = DynamicTo<LayoutBlock>(&object);
- if (!block)
- return;
- const auto* positioned_objects = block->PositionedObjects();
- if (!positioned_objects)
- return;
+ if (item.BoxFragment() && !item.BoxFragment()->IsInlineBox()) {
+ box_fragment = item.BoxFragment();
+ is_last_for_node = !box_fragment->BreakToken();
+ break;
+ } else {
+ // Inlines will pass their containing block fragment (and its incoming
+ // break token).
+ box_fragment = parent_fragment;
+ is_inside_fragment_child = true;
+ }
- // 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, context, /* iterator */ nullptr);
+ if (is_last_for_node)
+ break;
+
+ // Keep looking for the end. We need to know whether this is the last
+ // time we're going to visit this object.
+ }
+ if (is_last_for_node || !inline_cursor->Current()) {
+ // If all fragments are done, move to the next sibling of |child|.
+ inline_cursor.reset();
+ } else {
+ inline_cursor->MoveToNextForSameLayoutObject();
+ }
+ if (!box_fragment)
+ continue;
+ } else if (child->IsInline() && !child_box) {
+ // Deal with inline-level objects not searched for above.
+ //
+ // Needed for fragment-less objects that have children. This is the case
+ // for culled inlines. We're going to have to enter them for every
+ // fragment in the parent.
+ //
+ // The child is inline-level even if the parent fragment doesn't establish
+ // an inline formatting context. This may happen if there's only collapsed
+ // text, or if we had to insert a break in a block before we got to any
+ // inline content. Make sure that we visit such objects once.
+
+ // Inlines will pass their containing block fragment (and its incoming
+ // break token).
+ box_fragment = parent_fragment;
+ is_inside_fragment_child = true;
+
+ is_first_for_node = parent_fragment->IsFirstForNode();
+ is_last_for_node = !parent_fragment->BreakToken();
+ } else if (child_box && child_box->PhysicalFragmentCount()) {
+ // Figure out which fragment the child may be found inside. The fragment
+ // tree follows the structure of containing blocks closely, while here
+ // we're walking the LayoutObject tree (which follows the structure of the
+ // flat DOM tree, more or less). This means that for out-of-flow
+ // positioned objects, the fragment of the parent LayoutObject might not
+ // be the right place to search.
+ const NGPhysicalBoxFragment* search_fragment = parent_fragment;
+ if (child_box->IsOutOfFlowPositioned()) {
+ if (child_box->IsFixedPositioned()) {
+ search_fragment = context.fixed_positioned_container.fragment;
+ fragmentainer_idx =
+ context.fixed_positioned_container.fragmentainer_idx;
+ } else {
+ search_fragment = context.absolute_positioned_container.fragment;
+ fragmentainer_idx =
+ context.absolute_positioned_container.fragmentainer_idx;
+ }
+ if (!search_fragment) {
+ // Only walk unfragmented legacy-contained OOFs once.
+ if (!parent_fragment->IsFirstForNode())
+ continue;
+ }
+ }
+
+ if (search_fragment) {
+ // See if we can find a fragment for the child.
+ for (NGLink link : search_fragment->Children()) {
+ if (link->GetLayoutObject() != child)
+ continue;
+ // We found it!
+ box_fragment = To<NGPhysicalBoxFragment>(link.get());
+ paint_offset = link.offset;
+ is_first_for_node = box_fragment->IsFirstForNode();
+ is_last_for_node = !box_fragment->BreakToken();
+ break;
+ }
+ // If we didn't find a fragment for the child, it means that the child
+ // doesn't occur inside the fragmentainer that we're currently handling.
+ if (!box_fragment)
+ continue;
+ }
+ }
+
+ if (box_fragment) {
+ NGPrePaintInfo pre_paint_info(
+ *box_fragment, paint_offset, fragmentainer_idx, is_first_for_node,
+ is_last_for_node, context.is_inside_orphaned_object,
+ is_inside_fragment_child);
+ Walk(*child, context, &pre_paint_info);
+ } else {
+ Walk(*child, context, /* pre_paint_info */ nullptr);
+ }
}
}
-void PrePaintTreeWalk::WalkChildren(const LayoutObject* object,
+void PrePaintTreeWalk::WalkChildren(const LayoutObject& object,
+ const NGPhysicalBoxFragment* fragment,
PrePaintTreeWalkContext& context,
- const NGFragmentChildIterator* iterator) {
- DCHECK(iterator || object);
+ bool is_inside_fragment_child) {
+ const LayoutBox* box = DynamicTo<LayoutBox>(&object);
+ if (box) {
+ if (fragment) {
+ if (!box->IsLayoutFlowThread() && (!box->CanTraversePhysicalFragments() ||
+ !box->PhysicalFragmentCount())) {
+ // Leave LayoutNGBoxFragment-accompanied child LayoutObject traversal,
+ // since this object doesn't support that (or has no fragments (happens
+ // for table columns)). 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 LayoutNGBoxFragment-accompanied traversal if we get to a
+ // descendant that supports that.
+ DCHECK(
+ !box->FlowThreadContainingBlock() ||
+ (box->GetNGPaginationBreakability() == LayoutBox::kForbidBreaks));
+
+ fragment = nullptr;
+ context.oof_container_candidate_fragment = nullptr;
+ }
+ } else if (box->PhysicalFragmentCount()) {
+ // Enter LayoutNGBoxFragment-accompanied child LayoutObject traversal if
+ // we're at an NG fragmentation context root. While we in theory *could*
+ // enter this mode for any object that has a traversable fragment, without
+ // affecting correctness, we're better off with plain LayoutObject
+ // traversal when possible, as fragment-accompanied traversal has O(n^2)
+ // performance complexity (where n is the number of siblings).
+ //
+ // We'll stay in this mode for all descendants that support fragment
+ // traversal. We'll re-enter legacy traversal for descendants that don't
+ // support it. This only works correctly as long as there's no block
+ // fragmentation in the ancestry, though, so DCHECK for that.
+ DCHECK_EQ(box->PhysicalFragmentCount(), 1u);
+ const auto* first_fragment =
+ To<NGPhysicalBoxFragment>(box->GetPhysicalFragment(0));
+ DCHECK(!first_fragment->BreakToken());
+ if (first_fragment->IsFragmentationContextRoot() &&
+ box->CanTraversePhysicalFragments())
+ fragment = first_fragment;
+ }
- if (!iterator) {
- // We're not doing LayoutNG fragment traversal of this object.
- WalkLegacyChildren(*object, context);
- return;
+ // Inline-contained OOFs are placed in the containing block of the
+ // containing inline in NG, not an anonymous block that's part of a
+ // continuation, if any. We need to know where these might be stored, so
+ // that we eventually search the right ancestor fragment for them.
+ if (fragment && !box->IsAnonymousBlock())
+ context.oof_container_candidate_fragment = fragment;
}
- // 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() &&
- To<LayoutBox>(object)->GetNGPaginationBreakability() ==
- LayoutBox::kForbidBreaks));
- WalkLegacyChildren(*object, context);
- return;
+ // Keep track of fragments that act as containers for OOFs, so that we can
+ // search their children when looking for an OOF further down in the tree.
+ if (object.CanContainAbsolutePositionObjects()) {
+ if (context.current_fragmentainer.fragment && box &&
+ box->GetNGPaginationBreakability() == LayoutBox::kForbidBreaks) {
+ // If we're in a fragmentation context, the parent fragment of OOFs is the
+ // fragmentainer, unless the object is monolithic, in which case nothing
+ // inside the object participates in the current block fragmentation
+ // context. This means that this object (and not the nearest
+ // fragmentainer) acts as a containing block for OOF descendants,
+ context.current_fragmentainer = {};
+ }
+ // The OOF containing block structure is special under block fragmentation:
+ // A fragmentable OOF is always a direct child of a fragmentainer.
+ const auto* container_fragment = context.current_fragmentainer.fragment;
+ if (!container_fragment)
+ container_fragment = context.oof_container_candidate_fragment;
+ context.absolute_positioned_container = {
+ container_fragment, context.current_fragmentainer.fragmentainer_idx};
+ if (object.CanContainFixedPositionObjects()) {
+ context.fixed_positioned_container = {
+ container_fragment, context.current_fragmentainer.fragmentainer_idx};
+ }
}
- // Traverse child NG fragments.
- NGFragmentChildIterator child_iterator(iterator->Descend());
- WalkNGChildren(object, context, &child_iterator);
+ if (fragment) {
+ bool has_missable_children = false;
+ // If we are at a block fragment, collect any missable children.
+ DCHECK(!is_inside_fragment_child || !object.IsBox());
+ if (!is_inside_fragment_child)
+ has_missable_children = CollectMissableChildren(context, *fragment);
+
+ // We'll always walk the LayoutObject tree when possible, but if this is a
+ // fragmentation context root (such as a multicol container), we need to
+ // enter each fragmentainer child and then walk all the LayoutObject
+ // children.
+ if (fragment->IsFragmentationContextRoot())
+ WalkFragmentationContextRootChildren(object, *fragment, context);
+ else
+ WalkLayoutObjectChildren(object, fragment, context);
+
+ if (has_missable_children)
+ WalkMissedChildren(*fragment, context);
+ } else {
+ WalkLayoutObjectChildren(object, fragment, context);
+ }
}
void PrePaintTreeWalk::Walk(const LayoutObject& object,
const PrePaintTreeWalkContext& parent_context,
- const NGFragmentChildIterator* iterator) {
+ NGPrePaintInfo* pre_paint_info) {
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();
- else if (physical_fragment)
- is_last_fragment = !physical_fragment->BreakToken();
+ bool is_inside_fragment_child = false;
+ if (pre_paint_info) {
+ physical_fragment = &pre_paint_info->box_fragment;
+ if (physical_fragment && (physical_fragment->IsOutOfFlowPositioned() ||
+ physical_fragment->IsFloating())) {
+ pending_missables_.erase(physical_fragment);
+ if (object.IsFixedPositioned()) {
+ pending_fixedpos_missables_.erase(&object);
+ walked_fixedpos_.insert(&object);
+ }
+ }
+ is_inside_fragment_child = pre_paint_info->is_inside_fragment_child;
}
bool needs_tree_builder_context_update =
- ContextRequiresChildTreeBuilderContext(parent_context) ||
- ObjectRequiresTreeBuilderContext(object);
+ NeedsTreeBuilderContextUpdate(object, parent_context);
#if DCHECK_IS_ON()
CheckTreeBuilderContextState(object, parent_context);
@@ -951,7 +1211,7 @@ void PrePaintTreeWalk::Walk(const LayoutObject& object,
context.tree_builder_context->clip_changed = false;
}
- WalkInternal(object, context, iterator);
+ WalkInternal(object, context, pre_paint_info);
bool child_walk_blocked = object.ChildPrePaintBlockedByDisplayLock();
// If we need a subtree walk due to context flags, we need to store that
@@ -971,7 +1231,7 @@ void PrePaintTreeWalk::Walk(const LayoutObject& object,
}
if (!child_walk_blocked) {
- WalkChildren(&object, context, iterator);
+ WalkChildren(object, physical_fragment, context, is_inside_fragment_child);
if (const auto* layout_embedded_content =
DynamicTo<LayoutEmbeddedContent>(object)) {
@@ -1000,7 +1260,7 @@ void PrePaintTreeWalk::Walk(const LayoutObject& object,
}
}
}
- if (is_last_fragment)
+ if (!pre_paint_info || pre_paint_info->is_last_for_node)
object.GetMutableForPainting().ClearPaintFlags();
}
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 42ae9c2d19d..7971ae5340a 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
@@ -6,15 +6,19 @@
#define THIRD_PARTY_BLINK_RENDERER_CORE_PAINT_PRE_PAINT_TREE_WALK_H_
#include "base/dcheck_is_on.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
#include "third_party/blink/renderer/core/paint/paint_invalidator.h"
#include "third_party/blink/renderer/core/paint/paint_property_tree_builder.h"
#include "third_party/blink/renderer/platform/wtf/allocator/allocator.h"
+#include "third_party/blink/renderer/platform/wtf/hash_set.h"
namespace blink {
class LayoutObject;
class LocalFrameView;
-class NGFragmentChildIterator;
+struct NGLink;
+class NGPhysicalBoxFragment;
+class NGPhysicalFragment;
// This class walks the whole layout tree, beginning from the root
// LocalFrameView, across frame boundaries. Helper classes are called for each
@@ -30,56 +34,24 @@ class CORE_EXPORT PrePaintTreeWalk final {
static bool ObjectRequiresPrePaint(const LayoutObject&);
static bool ObjectRequiresTreeBuilderContext(const LayoutObject&);
- struct PrePaintTreeWalkContext {
- STACK_ALLOCATED();
+ struct ContainingFragment {
+ const NGPhysicalBoxFragment* fragment = nullptr;
+ wtf_size_t fragmentainer_idx = WTF::kNotFound;
+ };
- public:
- PrePaintTreeWalkContext() {
- tree_builder_context.emplace();
- }
- PrePaintTreeWalkContext(const PrePaintTreeWalkContext& parent_context,
- bool needs_tree_builder_context)
- : paint_invalidator_context(parent_context.paint_invalidator_context),
- ancestor_scroll_container_paint_layer(
- parent_context.ancestor_scroll_container_paint_layer),
- inside_blocking_touch_event_handler(
- parent_context.inside_blocking_touch_event_handler),
- effective_allowed_touch_action_changed(
- parent_context.effective_allowed_touch_action_changed),
- inside_blocking_wheel_event_handler(
- parent_context.inside_blocking_wheel_event_handler),
- blocking_wheel_event_handler_changed(
- parent_context.blocking_wheel_event_handler_changed),
- clip_changed(parent_context.clip_changed),
- paint_invalidation_container(
- parent_context.paint_invalidation_container),
- paint_invalidation_container_for_stacked_contents(
- parent_context
- .paint_invalidation_container_for_stacked_contents) {
- if (needs_tree_builder_context || DCHECK_IS_ON()) {
- DCHECK(parent_context.tree_builder_context);
- tree_builder_context.emplace(*parent_context.tree_builder_context);
- }
-#if DCHECK_IS_ON()
- if (needs_tree_builder_context)
- DCHECK(parent_context.tree_builder_context->is_actually_needed);
- tree_builder_context->is_actually_needed = needs_tree_builder_context;
-#endif
- }
+ // This provides a default base copy constructor for PrePaintTreeWalkContext.
+ // It contains all fields except for tree_builder_context which needs special
+ // treatment in the copy constructor.
+ struct PrePaintTreeWalkContextBase {
+ STACK_ALLOCATED();
- absl::optional<PaintPropertyTreeBuilderContext> tree_builder_context;
+ protected:
+ PrePaintTreeWalkContextBase() = default;
+ PrePaintTreeWalkContextBase(const PrePaintTreeWalkContextBase&) = default;
+ public:
PaintInvalidatorContext paint_invalidator_context;
- bool NeedsTreeBuilderContext() const {
-#if DCHECK_IS_ON()
- DCHECK(tree_builder_context);
- return tree_builder_context->is_actually_needed;
-#else
- return tree_builder_context.has_value();
-#endif
- }
-
// The ancestor in the PaintLayer tree which is a scroll container. Note
// that it is tree ancestor, not containing block or stacking ancestor.
PaintLayer* ancestor_scroll_container_paint_layer = nullptr;
@@ -104,9 +76,60 @@ class CORE_EXPORT PrePaintTreeWalk final {
// enabled.
bool clip_changed = false;
+ // True if we're fragment-traversing an object whose fragment wasn't found
+ // and walked when walking the layout object tree. This may happen for
+ // out-of-flow positioned and floated fragments inside block fragmentation,
+ // when an ancestor object doesn't have a fragment representation in a
+ // fragmentainer even if the OOF / float is there.
+ bool is_inside_orphaned_object = false;
+
const LayoutBoxModelObject* paint_invalidation_container = nullptr;
const LayoutBoxModelObject*
paint_invalidation_container_for_stacked_contents = nullptr;
+
+ ContainingFragment current_fragmentainer;
+ ContainingFragment absolute_positioned_container;
+ ContainingFragment fixed_positioned_container;
+
+ // When walking down the tree and discovering containers for OOFs, not every
+ // such container has the fragment actually containing OOF descendants; they
+ // may instead be inside a fragment generated by a parent (this happens for
+ // inline continuations, for instance). So keep track of the innermost valid
+ // container fragment for OOFs, and set
+ // absolute_positioned_container_fragment and
+ // fixed_positioned_container_fragment to this one as appropriate.
+ const NGPhysicalBoxFragment* oof_container_candidate_fragment = nullptr;
+ };
+
+ struct PrePaintTreeWalkContext : public PrePaintTreeWalkContextBase {
+ PrePaintTreeWalkContext() { tree_builder_context.emplace(); }
+ PrePaintTreeWalkContext(const PrePaintTreeWalkContext& parent_context,
+ bool needs_tree_builder_context)
+ : PrePaintTreeWalkContextBase(parent_context) {
+ if (needs_tree_builder_context || DCHECK_IS_ON()) {
+ DCHECK(parent_context.tree_builder_context);
+ tree_builder_context.emplace(*parent_context.tree_builder_context);
+ }
+#if DCHECK_IS_ON()
+ if (needs_tree_builder_context)
+ DCHECK(parent_context.tree_builder_context->is_actually_needed);
+ tree_builder_context->is_actually_needed = needs_tree_builder_context;
+#endif
+ }
+
+ PrePaintTreeWalkContext(const PrePaintTreeWalkContext&) = delete;
+ PrePaintTreeWalkContext& operator=(const PrePaintTreeWalkContext&) = delete;
+
+ bool NeedsTreeBuilderContext() const {
+#if DCHECK_IS_ON()
+ DCHECK(tree_builder_context);
+ return tree_builder_context->is_actually_needed;
+#else
+ return tree_builder_context.has_value();
+#endif
+ }
+
+ absl::optional<PaintPropertyTreeBuilderContext> tree_builder_context;
};
static bool ContextRequiresChildPrePaint(const PrePaintTreeWalkContext&);
@@ -118,6 +141,19 @@ class CORE_EXPORT PrePaintTreeWalk final {
const PrePaintTreeWalkContext&);
#endif
+ // Upon entering a child LayoutObject, create an NGPrePaintInfo, and populate
+ // everything except its FragmentData. We need to get a bit further inside the
+ // child (WalkInternal()) before we can set up FragmentData (if we get there
+ // at all).
+ NGPrePaintInfo CreatePrePaintInfo(const NGLink& child,
+ const PrePaintTreeWalkContext& context);
+
+ // Locate and/or set up a FragmentData object for the current object /
+ // physical fragment.
+ FragmentData* GetOrCreateFragmentData(const LayoutObject&,
+ const PrePaintTreeWalkContext&,
+ const NGPrePaintInfo&);
+
void Walk(LocalFrameView&, const PrePaintTreeWalkContext& parent_context);
// This is to minimize stack frame usage during recursion. Modern compilers
@@ -127,20 +163,44 @@ class CORE_EXPORT PrePaintTreeWalk final {
// See https://crbug.com/781301 .
NOINLINE void WalkInternal(const LayoutObject&,
PrePaintTreeWalkContext&,
- const NGFragmentChildIterator*);
- void WalkNGChildren(const LayoutObject* parent,
- PrePaintTreeWalkContext& parent_context,
- NGFragmentChildIterator*);
- void WalkLegacyChildren(const LayoutObject&, PrePaintTreeWalkContext&);
- void WalkChildren(const LayoutObject*,
+ NGPrePaintInfo*);
+
+ // Add any "missable" children to a list. Missable children are children that
+ // we might not find during LayoutObject traversal. This happens when an
+ // ancestor LayoutObject (of the missable child) has no fragment inside a
+ // given fragmentainer, e.g. when there's an OOF fragment, but its containing
+ // block has no fragment inside that fragmentainer. Later, during the child
+ // walk, when a missable child is actually walked, it's removed from the
+ // list.
+ //
+ // Returns true if there are any missable children inside the fragment, false
+ // otherwise.
+ bool CollectMissableChildren(PrePaintTreeWalkContext&,
+ const NGPhysicalBoxFragment&);
+
+ // Walk any missed children (i.e. those collected by CollectMissableChildren()
+ // and not walked by Walk()) after child object traversal.
+ void WalkMissedChildren(const NGPhysicalBoxFragment&,
+ PrePaintTreeWalkContext&);
+
+ void WalkFragmentationContextRootChildren(const LayoutObject&,
+ const NGPhysicalBoxFragment&,
+ PrePaintTreeWalkContext&);
+ void WalkLayoutObjectChildren(const LayoutObject&,
+ const NGPhysicalBoxFragment*,
+ PrePaintTreeWalkContext&);
+ void WalkChildren(const LayoutObject&,
+ const NGPhysicalBoxFragment*,
PrePaintTreeWalkContext&,
- const NGFragmentChildIterator*);
+ bool is_inside_fragment_child = false);
void Walk(const LayoutObject&,
const PrePaintTreeWalkContext& parent_context,
- const NGFragmentChildIterator*);
+ NGPrePaintInfo*);
bool NeedsTreeBuilderContextUpdate(const LocalFrameView&,
const PrePaintTreeWalkContext&);
+ bool NeedsTreeBuilderContextUpdate(const LayoutObject&,
+ const PrePaintTreeWalkContext&);
void UpdateAuxiliaryObjectProperties(const LayoutObject&,
PrePaintTreeWalkContext&);
// Updates |LayoutObject::InsideBlockingTouchEventHandler|. Also ensures
@@ -163,6 +223,21 @@ class CORE_EXPORT PrePaintTreeWalk final {
PaintInvalidator paint_invalidator_;
+ // List of fragments that may be missed during LayoutObject walking. See
+ // CollectMissableChildren() and WalkMissedChildren().
+ HashSet<const NGPhysicalFragment*> pending_missables_;
+
+ // List of fixedpos objects that may be missed during fragment traversal. This
+ // can happen if a fixedpos is nested in another OOF inside a multicol, and
+ // the OOF parent is a pending missable (see |pending_missables_|). If that
+ // fixedpos' containing block is located outside of the multicol, we can would
+ // miss it during normal fragment traversal.
+ HashSet<const LayoutObject*> pending_fixedpos_missables_;
+
+ // List of fixedpos objects that have already been walked. This helps to avoid
+ // re-walking any fixedpos objects handled by |pending_fixedpos_missables_|.
+ HashSet<const LayoutObject*> walked_fixedpos_;
+
// TODO(https://crbug.com/841364): Remove is_wheel_event_regions_enabled
// argument once kWheelEventRegions feature flag is removed.
bool is_wheel_event_regions_enabled_ = false;
diff --git a/chromium/third_party/blink/renderer/core/paint/rounded_border_geometry.cc b/chromium/third_party/blink/renderer/core/paint/rounded_border_geometry.cc
index 32d127242e4..db66edbb0ef 100644
--- a/chromium/third_party/blink/renderer/core/paint/rounded_border_geometry.cc
+++ b/chromium/third_party/blink/renderer/core/paint/rounded_border_geometry.cc
@@ -112,39 +112,46 @@ FloatRoundedRect RoundedBorderGeometry::PixelSnappedRoundedInnerBorder(
int bottom_width =
sides_to_include.bottom ? style.BorderBottomWidth().Floor() : 0;
- return PixelSnappedRoundedInnerBorder(
+ return PixelSnappedRoundedBorderWithOutsets(
style, border_rect,
LayoutRectOutsets(-top_width, -right_width, -bottom_width, -left_width),
sides_to_include);
}
-FloatRoundedRect RoundedBorderGeometry::PixelSnappedRoundedInnerBorder(
+FloatRoundedRect RoundedBorderGeometry::PixelSnappedRoundedBorderWithOutsets(
const ComputedStyle& style,
const PhysicalRect& border_rect,
- const LayoutRectOutsets& insets,
+ const LayoutRectOutsets& outsets,
PhysicalBoxSides sides_to_include) {
- PhysicalRect inner_rect = border_rect;
- inner_rect.Expand(insets);
- inner_rect.size.ClampNegativeToZero();
+ PhysicalRect rect_with_outsets = border_rect;
+ rect_with_outsets.Expand(outsets);
+ rect_with_outsets.size.ClampNegativeToZero();
// The standard LayoutRect::PixelSnappedIntRect() method will not
// let small sizes snap to zero, but that has the side effect here of
// preventing an inner border for a very thin element from snapping to
// zero size as occurs when a unit width border is applied to a sub-pixel
// sized element. So round without forcing non-near-zero sizes to one.
- FloatRoundedRect rounded_rect(IntRect(
- RoundedIntPoint(inner_rect.offset),
- IntSize(
- SnapSizeToPixelAllowingZero(inner_rect.Width(), inner_rect.X()),
- SnapSizeToPixelAllowingZero(inner_rect.Height(), inner_rect.Y()))));
+ FloatRoundedRect rounded_rect(
+ IntRect(RoundedIntPoint(rect_with_outsets.offset),
+ IntSize(SnapSizeToPixelAllowingZero(rect_with_outsets.Width(),
+ rect_with_outsets.X()),
+ SnapSizeToPixelAllowingZero(rect_with_outsets.Height(),
+ rect_with_outsets.Y()))));
if (style.HasBorderRadius()) {
FloatRoundedRect::Radii radii =
PixelSnappedRoundedBorder(style, border_rect, sides_to_include)
.GetRadii();
- // Insets use negative values.
- radii.Shrink(-insets.Top().ToFloat(), -insets.Bottom().ToFloat(),
- -insets.Left().ToFloat(), -insets.Right().ToFloat());
+ if (outsets.Top() <= 0 && outsets.Bottom() <= 0 && outsets.Left() <= 0 &&
+ outsets.Right() <= 0) {
+ radii.Shrink(-outsets.Top().ToFloat(), -outsets.Bottom().ToFloat(),
+ -outsets.Left().ToFloat(), -outsets.Right().ToFloat());
+ } else {
+ // radii.Expand() will DCHECK if all values are >= 0.
+ radii.Expand(outsets.Top().ToFloat(), outsets.Bottom().ToFloat(),
+ outsets.Left().ToFloat(), outsets.Right().ToFloat());
+ }
ExcludeSides(sides_to_include, &radii);
rounded_rect.SetRadii(radii);
}
diff --git a/chromium/third_party/blink/renderer/core/paint/rounded_border_geometry.h b/chromium/third_party/blink/renderer/core/paint/rounded_border_geometry.h
index 4881daeb366..5e9c4dfed5e 100644
--- a/chromium/third_party/blink/renderer/core/paint/rounded_border_geometry.h
+++ b/chromium/third_party/blink/renderer/core/paint/rounded_border_geometry.h
@@ -36,10 +36,12 @@ class CORE_EXPORT RoundedBorderGeometry {
const PhysicalRect& border_rect,
PhysicalBoxSides edges_to_include = PhysicalBoxSides());
- static FloatRoundedRect PixelSnappedRoundedInnerBorder(
+ // Values in |outsets| must be either all >= 0 to expand from |border_rect|,
+ // or all <= 0 to shrink from |border_rect|.
+ static FloatRoundedRect PixelSnappedRoundedBorderWithOutsets(
const ComputedStyle&,
const PhysicalRect& border_rect,
- const LayoutRectOutsets& insets,
+ const LayoutRectOutsets& outsets_from_border,
PhysicalBoxSides edges_to_include = PhysicalBoxSides());
};
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 3e302b138f0..db29fae1c85 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
@@ -55,25 +55,25 @@ void ScopedBoxContentsPaintState::AdjustForBoxContents(const LayoutBox& box) {
fragment_to_paint_->ContentsProperties(), box,
input_paint_info_.DisplayItemTypeForClipping());
- // Then adjust paint offset and cull rect for scroll translation.
const auto* properties = fragment_to_paint_->PaintProperties();
- if (!properties)
- return;
- const auto* scroll_translation = properties->ScrollTranslation();
- if (!scroll_translation)
- return;
+ const auto* scroll_translation =
+ properties ? properties->ScrollTranslation() : nullptr;
// See comments for ScrollTranslation in object_paint_properties.h
// for the reason of adding ScrollOrigin(). The paint offset will
// be used only for the scrolling contents that are not painted through
// descendant objects' Paint() method, e.g. inline boxes.
- paint_offset_ += PhysicalOffset(box.ScrollOrigin());
+ if (scroll_translation)
+ paint_offset_ += PhysicalOffset(box.ScrollOrigin());
if (RuntimeEnabledFeatures::CullRectUpdateEnabled()) {
+ // We calculated cull rects for PaintLayers only.
+ if (!box.HasLayer())
+ return;
adjusted_paint_info_.emplace(input_paint_info_);
adjusted_paint_info_->SetCullRect(
fragment_to_paint_->GetContentsCullRect());
- if (box.HasLayer() && box.Layer()->PreviousPaintResult() == kFullyPainted) {
+ if (box.Layer()->PreviousPaintResult() == kFullyPainted) {
PhysicalRect contents_visual_rect =
box.PhysicalContentsVisualOverflowRect();
contents_visual_rect.Move(fragment_to_paint_->PaintOffset());
@@ -94,8 +94,10 @@ void ScopedBoxContentsPaintState::AdjustForBoxContents(const LayoutBox& box) {
if (IsA<LayoutView>(box) && input_paint_info_.GetCullRect().IsInfinite())
return;
- adjusted_paint_info_.emplace(input_paint_info_);
- adjusted_paint_info_->TransformCullRect(*scroll_translation);
+ if (scroll_translation) {
+ adjusted_paint_info_.emplace(input_paint_info_);
+ adjusted_paint_info_->TransformCullRect(*scroll_translation);
+ }
}
} // namespace blink
diff --git a/chromium/third_party/blink/renderer/core/paint/scoped_paint_state.h b/chromium/third_party/blink/renderer/core/paint/scoped_paint_state.h
index e2a0f807a38..8a1d5f85777 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
@@ -5,6 +5,7 @@
#ifndef THIRD_PARTY_BLINK_RENDERER_CORE_PAINT_SCOPED_PAINT_STATE_H_
#define THIRD_PARTY_BLINK_RENDERER_CORE_PAINT_SCOPED_PAINT_STATE_H_
+#include "third_party/abseil-cpp/absl/types/optional.h"
#include "third_party/blink/renderer/core/layout/layout_box.h"
#include "third_party/blink/renderer/core/layout/ng/ng_physical_fragment.h"
#include "third_party/blink/renderer/core/paint/paint_info.h"
@@ -45,7 +46,9 @@ class ScopedPaintState {
return;
}
const auto* properties = fragment_to_paint_->PaintProperties();
- if (properties && properties->PaintOffsetTranslation()) {
+ if (!properties)
+ return;
+ if (properties->PaintOffsetTranslation()) {
AdjustForPaintOffsetTranslation(object,
*properties->PaintOffsetTranslation());
}
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 750aed68085..6cc9ccc4014 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
@@ -26,6 +26,7 @@
#define THIRD_PARTY_BLINK_RENDERER_CORE_PAINT_SCOPED_SVG_PAINT_STATE_H_
#include "base/dcheck_is_on.h"
+#include "third_party/abseil-cpp/absl/types/optional.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/scoped_paint_chunk_properties.h"
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 e6fc87badf9..089a702ffac 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
@@ -4,6 +4,7 @@
#include "third_party/blink/renderer/core/paint/scrollable_area_painter.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
#include "third_party/blink/renderer/core/frame/visual_viewport.h"
#include "third_party/blink/renderer/core/layout/layout_view.h"
#include "third_party/blink/renderer/core/page/chrome_client.h"
@@ -225,6 +226,20 @@ void ScrollableAreaPainter::PaintScrollbar(GraphicsContext& context,
if (!cull_rect.Intersects(visual_rect))
return;
+ const auto* properties =
+ GetScrollableArea().GetLayoutBox()->FirstFragment().PaintProperties();
+ DCHECK(properties);
+ auto type = scrollbar.Orientation() == kHorizontalScrollbar
+ ? DisplayItem::kScrollbarHorizontal
+ : DisplayItem::kScrollbarVertical;
+ absl::optional<ScopedPaintChunkProperties> chunk_properties;
+ if (const auto* effect = scrollbar.Orientation() == kHorizontalScrollbar
+ ? properties->HorizontalScrollbarEffect()
+ : properties->VerticalScrollbarEffect()) {
+ chunk_properties.emplace(context.GetPaintController(), *effect, scrollbar,
+ type);
+ }
+
if (scrollbar.IsCustomScrollbar()) {
scrollbar.Paint(context, paint_offset);
@@ -240,19 +255,12 @@ void ScrollableAreaPainter::PaintScrollbar(GraphicsContext& context,
return;
}
- auto type = scrollbar.Orientation() == kHorizontalScrollbar
- ? DisplayItem::kScrollbarHorizontal
- : DisplayItem::kScrollbarVertical;
if (context.GetPaintController().UseCachedItemIfPossible(scrollbar, type))
return;
const TransformPaintPropertyNode* scroll_translation = nullptr;
- if (scrollable_area_->ShouldDirectlyCompositeScrollbar(scrollbar)) {
- auto* properties =
- GetScrollableArea().GetLayoutBox()->FirstFragment().PaintProperties();
- DCHECK(properties);
+ if (scrollable_area_->ShouldDirectlyCompositeScrollbar(scrollbar))
scroll_translation = properties->ScrollTranslation();
- }
auto delegate = base::MakeRefCounted<ScrollbarLayerDelegate>(
scrollbar, context.DeviceScaleFactor());
ScrollbarDisplayItem::Record(context, scrollbar, type, delegate, visual_rect,
diff --git a/chromium/third_party/blink/renderer/core/paint/scrollable_area_painter_test.cc b/chromium/third_party/blink/renderer/core/paint/scrollable_area_painter_test.cc
new file mode 100644
index 00000000000..c438826ab37
--- /dev/null
+++ b/chromium/third_party/blink/renderer/core/paint/scrollable_area_painter_test.cc
@@ -0,0 +1,54 @@
+// Copyright 2021 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/scrollable_area_painter.h"
+
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/blink/renderer/core/frame/local_frame_view.h"
+#include "third_party/blink/renderer/core/paint/paint_controller_paint_test.h"
+
+using testing::_;
+using testing::ElementsAre;
+
+namespace blink {
+
+using ScrollableAreaPainterTest = PaintControllerPaintTest;
+
+INSTANTIATE_CAP_TEST_SUITE_P(ScrollableAreaPainterTest);
+
+TEST_P(ScrollableAreaPainterTest, OverlayScrollbars) {
+ SetBodyInnerHTML(R"HTML(
+ <div id="target" style="overflow: scroll; width: 50px; height: 50px">
+ <div style="width: 200px; height: 200px"></div>
+ </div>
+ )HTML");
+
+ ASSERT_TRUE(
+ GetDocument().GetPage()->GetScrollbarTheme().UsesOverlayScrollbars());
+ const auto* target = To<LayoutBox>(GetLayoutObjectByElementId("target"));
+ const auto* properties =
+ GetLayoutObjectByElementId("target")->FirstFragment().PaintProperties();
+ ASSERT_TRUE(properties);
+ ASSERT_TRUE(properties->HorizontalScrollbarEffect());
+ ASSERT_TRUE(properties->VerticalScrollbarEffect());
+
+ PaintChunk::Id horizontal_id(
+ *target->GetScrollableArea()->HorizontalScrollbar(),
+ DisplayItem::kScrollbarHorizontal);
+ auto horizontal_state = target->FirstFragment().LocalBorderBoxProperties();
+ horizontal_state.SetEffect(*properties->HorizontalScrollbarEffect());
+
+ PaintChunk::Id vertical_id(*target->GetScrollableArea()->VerticalScrollbar(),
+ DisplayItem::kScrollbarVertical);
+ auto vertical_state = target->FirstFragment().LocalBorderBoxProperties();
+ vertical_state.SetEffect(*properties->VerticalScrollbarEffect());
+
+ EXPECT_THAT(ContentPaintChunks(),
+ ElementsAre(VIEW_SCROLLING_BACKGROUND_CHUNK_COMMON, _, _, _,
+ IsPaintChunk(1, 2, horizontal_id, horizontal_state),
+ IsPaintChunk(2, 3, vertical_id, vertical_state)));
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/core/paint/selection_bounds_recorder.cc b/chromium/third_party/blink/renderer/core/paint/selection_bounds_recorder.cc
index 58c5409858b..f9266ca880b 100644
--- a/chromium/third_party/blink/renderer/core/paint/selection_bounds_recorder.cc
+++ b/chromium/third_party/blink/renderer/core/paint/selection_bounds_recorder.cc
@@ -3,12 +3,15 @@
// found in the LICENSE file.
#include "third_party/blink/renderer/core/paint/selection_bounds_recorder.h"
+
#include "third_party/blink/renderer/core/editing/frame_selection.h"
+#include "third_party/blink/renderer/core/frame/local_frame.h"
#include "third_party/blink/renderer/core/html/forms/text_control_element.h"
#include "third_party/blink/renderer/core/layout/api/selection_state.h"
#include "third_party/blink/renderer/core/layout/geometry/physical_rect.h"
#include "third_party/blink/renderer/core/layout/layout_box.h"
-#include "third_party/blink/renderer/core/layout/layout_view.h"
+#include "third_party/blink/renderer/core/page/focus_controller.h"
+#include "third_party/blink/renderer/core/page/page.h"
#include "third_party/blink/renderer/platform/graphics/paint/paint_controller.h"
namespace blink {
@@ -164,6 +167,16 @@ bool SelectionBoundsRecorder::ShouldRecordSelection(
if (!frame_selection.IsHandleVisible() || frame_selection.IsHidden())
return false;
+ // If the currently focused frame is not the one in which selection
+ // lives, don't paint the selection bounds. Note this is subtly different
+ // from whether the frame has focus (i.e. `FrameSelection::SelectionHasFocus`)
+ // which is false if the hosting window is not focused.
+ LocalFrame* local_frame = frame_selection.GetFrame();
+ LocalFrame* focused_frame =
+ local_frame->GetPage()->GetFocusController().FocusedFrame();
+ if (local_frame != focused_frame)
+ return false;
+
if (state == SelectionState::kInside || state == SelectionState::kNone)
return false;
@@ -189,7 +202,7 @@ bool SelectionBoundsRecorder::IsVisible(const LayoutObject& rect_layout_object,
return true;
const PhysicalOffset sample_point = GetSamplePointForVisibility(
- edge_start, edge_end, rect_layout_object.View()->ZoomFactor());
+ edge_start, edge_end, rect_layout_object.GetFrame()->PageZoomFactor());
auto* const text_control_object = To<LayoutBox>(layout_object);
const PhysicalOffset position_in_input =
diff --git a/chromium/third_party/blink/renderer/core/paint/selection_bounds_recorder_test.cc b/chromium/third_party/blink/renderer/core/paint/selection_bounds_recorder_test.cc
new file mode 100644
index 00000000000..2f14c05d1d6
--- /dev/null
+++ b/chromium/third_party/blink/renderer/core/paint/selection_bounds_recorder_test.cc
@@ -0,0 +1,206 @@
+// Copyright 2021 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 "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/blink/renderer/core/editing/testing/selection_sample.h"
+#include "third_party/blink/renderer/core/page/focus_controller.h"
+#include "third_party/blink/renderer/core/paint/paint_controller_paint_test.h"
+
+using testing::ElementsAre;
+
+namespace blink {
+
+class SelectionBoundsRecorderTest : public PaintControllerPaintTestBase,
+ public testing::WithParamInterface<bool>,
+ public ScopedLayoutNGForTest,
+ public ScopedCompositeAfterPaintForTest {
+ public:
+ SelectionBoundsRecorderTest()
+ : ScopedLayoutNGForTest(GetParam()),
+ ScopedCompositeAfterPaintForTest(true) {}
+};
+
+struct SelectionBoundsRecorderTestPassToString {
+ std::string operator()(const testing::TestParamInfo<bool> b) const {
+ return b.param ? "LayoutNG" : "LegacyLayout";
+ }
+};
+
+INSTANTIATE_TEST_SUITE_P(All,
+ SelectionBoundsRecorderTest,
+ ::testing::Bool(),
+ SelectionBoundsRecorderTestPassToString());
+
+TEST_P(SelectionBoundsRecorderTest, SelectAll) {
+ SetBodyInnerHTML("<span>A<br>B<br>C</span>");
+
+ LocalFrame* local_frame = GetDocument().GetFrame();
+ local_frame->Selection().SetHandleVisibleForTesting();
+ local_frame->GetPage()->GetFocusController().SetFocusedFrame(local_frame);
+ local_frame->Selection().SelectAll();
+ UpdateAllLifecyclePhasesForTest();
+
+ auto chunks = ContentPaintChunks();
+ EXPECT_EQ(chunks.size(), 1u);
+ EXPECT_TRUE(chunks.begin()->layer_selection_data->start.has_value());
+ EXPECT_TRUE(chunks.begin()->layer_selection_data->end.has_value());
+ PaintedSelectionBound start =
+ chunks.begin()->layer_selection_data->start.value();
+ EXPECT_EQ(start.type, gfx::SelectionBound::LEFT);
+ EXPECT_EQ(start.edge_start, IntPoint(8, 8));
+ EXPECT_EQ(start.edge_end, IntPoint(8, 9));
+
+ PaintedSelectionBound end = chunks.begin()->layer_selection_data->end.value();
+ EXPECT_EQ(end.type, gfx::SelectionBound::RIGHT);
+ EXPECT_EQ(end.edge_start, IntPoint(9, 10));
+ EXPECT_EQ(end.edge_end, IntPoint(9, 11));
+}
+
+TEST_P(SelectionBoundsRecorderTest, SelectMultiline) {
+ LocalFrame* local_frame = GetDocument().GetFrame();
+ local_frame->Selection().SetSelectionAndEndTyping(
+ SelectionSample::SetSelectionText(
+ GetDocument().body(),
+ "<div style='white-space:pre'>f^oo\nbar\nb|az</div>"));
+ local_frame->Selection().SetHandleVisibleForTesting();
+ local_frame->GetPage()->GetFocusController().SetFocusedFrame(local_frame);
+ UpdateAllLifecyclePhasesForTest();
+
+ auto chunks = ContentPaintChunks();
+ EXPECT_EQ(chunks.size(), 1u);
+ EXPECT_TRUE(chunks.begin()->layer_selection_data->start.has_value());
+ EXPECT_TRUE(chunks.begin()->layer_selection_data->end.has_value());
+ PaintedSelectionBound start =
+ chunks.begin()->layer_selection_data->start.value();
+ EXPECT_EQ(start.type, gfx::SelectionBound::LEFT);
+ EXPECT_EQ(start.edge_start, IntPoint(8, 8));
+ EXPECT_EQ(start.edge_end, IntPoint(8, 9));
+
+ PaintedSelectionBound end = chunks.begin()->layer_selection_data->end.value();
+ EXPECT_EQ(end.type, gfx::SelectionBound::RIGHT);
+ EXPECT_EQ(end.edge_start, IntPoint(9, 10));
+ EXPECT_EQ(end.edge_end, IntPoint(9, 11));
+}
+
+TEST_P(SelectionBoundsRecorderTest, SelectMultilineEmptyStartEnd) {
+ LocalFrame* local_frame = GetDocument().GetFrame();
+ LoadAhem(*local_frame);
+ local_frame->Selection().SetSelectionAndEndTyping(
+ SelectionSample::SetSelectionText(GetDocument().body(),
+ R"HTML(
+ <style>
+ body { margin: 0; }
+ * { font: 10px/1 Ahem; }
+ </style>
+ <div>foo^<br>bar<br>|baz</div>
+ )HTML"));
+ local_frame->Selection().SetHandleVisibleForTesting();
+ local_frame->GetPage()->GetFocusController().SetFocusedFrame(local_frame);
+ UpdateAllLifecyclePhasesForTest();
+
+ auto chunks = ContentPaintChunks();
+ EXPECT_EQ(chunks.size(), 1u);
+ EXPECT_TRUE(chunks.begin()->layer_selection_data->start.has_value());
+ EXPECT_TRUE(chunks.begin()->layer_selection_data->end.has_value());
+ PaintedSelectionBound start =
+ chunks.begin()->layer_selection_data->start.value();
+ EXPECT_EQ(start.type, gfx::SelectionBound::LEFT);
+ EXPECT_EQ(start.edge_start, IntPoint(30, 0));
+ EXPECT_EQ(start.edge_end, IntPoint(30, 10));
+
+ PaintedSelectionBound end = chunks.begin()->layer_selection_data->end.value();
+ EXPECT_EQ(end.type, gfx::SelectionBound::RIGHT);
+ EXPECT_EQ(end.edge_start, IntPoint(0, 20));
+ EXPECT_EQ(end.edge_end, IntPoint(0, 30));
+}
+
+TEST_P(SelectionBoundsRecorderTest, InvalidationForEmptyBounds) {
+ LocalFrame* local_frame = GetDocument().GetFrame();
+ LoadAhem(*local_frame);
+
+ // Set a selection that has empty start and end in separate paint chunks.
+ // We'll move these empty endpoints into the middle div and make sure
+ // everything is invalidated/re-painted/recorded correctly.
+ local_frame->Selection().SetSelectionAndEndTyping(
+ SelectionSample::SetSelectionText(GetDocument().body(),
+ R"HTML(
+ <style>
+ body { margin: 0; }
+ div { will-change: transform; }
+ * { font: 10px/1 Ahem; }
+ </style>
+ <div>foo^</div><div id=target>bar</div><div>|baz</div>
+ )HTML"));
+ local_frame->Selection().SetHandleVisibleForTesting();
+ local_frame->GetPage()->GetFocusController().SetFocusedFrame(local_frame);
+ UpdateAllLifecyclePhasesForTest();
+
+ auto chunks = ContentPaintChunks();
+ EXPECT_EQ(chunks.size(), 4u);
+
+ PaintChunkSubset::Iterator chunk_iterator = chunks.begin();
+ // Skip the root chunk to get to the first div.
+ ++chunk_iterator;
+ EXPECT_TRUE(chunk_iterator->layer_selection_data->start.has_value());
+ PaintedSelectionBound start =
+ chunk_iterator->layer_selection_data->start.value();
+ EXPECT_EQ(start.type, gfx::SelectionBound::LEFT);
+ EXPECT_EQ(start.edge_start, IntPoint(30, 0));
+ EXPECT_EQ(start.edge_end, IntPoint(30, 10));
+
+ // Skip the middle div as well to get to the third div where the end of the
+ // selection is.
+ ++chunk_iterator;
+ ++chunk_iterator;
+
+ EXPECT_TRUE(chunk_iterator->layer_selection_data->end.has_value());
+ PaintedSelectionBound end = chunk_iterator->layer_selection_data->end.value();
+ EXPECT_EQ(end.type, gfx::SelectionBound::RIGHT);
+ // Coordinates are chunk-relative, so they should start at 0 y coordinate.
+ EXPECT_EQ(end.edge_start, IntPoint(0, 0));
+ EXPECT_EQ(end.edge_end, IntPoint(0, 10));
+
+ // Move the selection around the start and end of the second div.
+ local_frame->Selection().SetSelectionAndEndTyping(
+ SelectionInDOMTree::Builder()
+ .Collapse(Position(GetElementById("target")->firstChild(), 0))
+ .Extend(Position(GetElementById("target")->firstChild(), 3))
+ .Build());
+
+ // Ensure the handle will be visible for the next paint (previous call to
+ // SetSelectionAndEndTyping will clear the bit).
+ local_frame->Selection().SetHandleVisibleForTesting();
+
+ UpdateAllLifecyclePhasesForTest();
+
+ chunks = ContentPaintChunks();
+ chunk_iterator = chunks.begin();
+ EXPECT_EQ(chunks.size(), 4u);
+
+ // Skip the root chunk to get to the first div, which should no longer have
+ // a recorded value.
+ ++chunk_iterator;
+ EXPECT_FALSE(chunk_iterator->layer_selection_data);
+
+ // Validate start/end in second div.
+ ++chunk_iterator;
+ EXPECT_TRUE(chunk_iterator->layer_selection_data->start.has_value());
+ EXPECT_TRUE(chunk_iterator->layer_selection_data->end.has_value());
+ start = chunk_iterator->layer_selection_data->start.value();
+ EXPECT_EQ(start.type, gfx::SelectionBound::LEFT);
+ EXPECT_EQ(start.edge_start, IntPoint(0, 0));
+ EXPECT_EQ(start.edge_end, IntPoint(0, 10));
+
+ end = chunk_iterator->layer_selection_data->end.value();
+ EXPECT_EQ(end.type, gfx::SelectionBound::RIGHT);
+ EXPECT_EQ(end.edge_start, IntPoint(30, 0));
+ EXPECT_EQ(end.edge_end, IntPoint(30, 10));
+
+ // Third div's chunk should no longer have an end value.
+ ++chunk_iterator;
+ EXPECT_FALSE(chunk_iterator->layer_selection_data);
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/core/paint/svg_container_painter_test.cc b/chromium/third_party/blink/renderer/core/paint/svg_container_painter_test.cc
index 80d94a1b198..fbe8b7ad23e 100644
--- a/chromium/third_party/blink/renderer/core/paint/svg_container_painter_test.cc
+++ b/chromium/third_party/blink/renderer/core/paint/svg_container_painter_test.cc
@@ -23,7 +23,6 @@ using SVGContainerPainterTest = PaintControllerPaintTest;
INSTANTIATE_PAINT_TEST_SUITE_P(SVGContainerPainterTest);
TEST_P(SVGContainerPainterTest, FilterPaintProperties) {
- ScopedCompositeSVGForTest enable_feature(true);
SetBodyInnerHTML(R"HTML(
<style>
#container, #before, #after { will-change: transform; }
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 0f1f5c9970d..944feb5e496 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
@@ -87,10 +87,9 @@ void SVGImagePainter::PaintForeground(const PaintInfo& paint_info) {
layout_svg_image_.StyleRef().GetInterpolationQuality());
Image::ImageDecodingMode decode_mode =
image_element->GetDecodingModeForPainting(image->paint_image_id());
- paint_info.context.DrawImage(
- image.get(), decode_mode, dest_rect, &src_rect,
- layout_svg_image_.StyleRef().HasFilterInducingProperty(),
- SkBlendMode::kSrcOver, respect_orientation);
+ paint_info.context.DrawImage(image.get(), decode_mode, dest_rect, &src_rect,
+ layout_svg_image_.StyleRef().DisableForceDark(),
+ SkBlendMode::kSrcOver, respect_orientation);
ImageResourceContent* image_content = image_resource.CachedImage();
if (image_content->IsLoaded()) {
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 430e7873c4c..c4ec5e29300 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
@@ -5,6 +5,8 @@
#include "third_party/blink/renderer/core/paint/svg_inline_text_box_painter.h"
#include <memory>
+
+#include "base/stl_util.h"
#include "third_party/abseil-cpp/absl/types/optional.h"
#include "third_party/blink/renderer/core/editing/editor.h"
#include "third_party/blink/renderer/core/editing/markers/document_marker_controller.h"
@@ -379,9 +381,12 @@ void SVGInlineTextBoxPainter::PaintDecoration(const PaintInfo& paint_info,
if (decoration_style.HasFill()) {
PaintFlags fill_flags;
if (!SVGObjectPainter(*decoration_layout_object)
- .PreparePaint(paint_info, decoration_style, kApplyToFillMode,
- fill_flags))
+ .PreparePaint(paint_info.context,
+ paint_info.IsRenderingClipPathAsMaskImage(),
+ decoration_style, kApplyToFillMode,
+ fill_flags)) {
break;
+ }
fill_flags.setAntiAlias(true);
paint_info.context.DrawPath(path.GetSkPath(), fill_flags);
}
@@ -390,9 +395,12 @@ void SVGInlineTextBoxPainter::PaintDecoration(const PaintInfo& paint_info,
if (decoration_style.HasVisibleStroke()) {
PaintFlags stroke_flags;
if (!SVGObjectPainter(*decoration_layout_object)
- .PreparePaint(paint_info, decoration_style,
- kApplyToStrokeMode, stroke_flags))
+ .PreparePaint(paint_info.context,
+ paint_info.IsRenderingClipPathAsMaskImage(),
+ decoration_style, kApplyToStrokeMode,
+ stroke_flags)) {
break;
+ }
stroke_flags.setAntiAlias(true);
float stroke_scale_factor = decoration_style.VectorEffect() ==
EVectorEffect::kNonScalingStroke
@@ -441,9 +449,13 @@ bool SVGInlineTextBoxPainter::SetupTextPaint(
}
if (!SVGObjectPainter(ParentInlineLayoutObject())
- .PreparePaint(paint_info, style, resource_mode, flags,
- base::OptionalOrNullptr(paint_server_transform)))
+ .PreparePaint(paint_info.context,
+ paint_info.IsRenderingClipPathAsMaskImage(), style,
+ resource_mode, flags,
+ base::OptionalOrNullptr(paint_server_transform))) {
return false;
+ }
+
flags.setAntiAlias(true);
if (style.TextShadow() &&
@@ -677,11 +689,7 @@ void SVGInlineTextBoxPainter::PaintTextMarkerForeground(
return;
Color text_color = LayoutTheme::GetTheme().PlatformTextSearchColor(
- marker.IsActiveMatch(),
- svg_inline_text_box_.GetLineLayoutItem()
- .GetDocument()
- .InForcedColorsMode(),
- style.UsedColorScheme());
+ marker.IsActiveMatch(), style.UsedColorScheme());
PaintFlags fill_flags;
fill_flags.setColor(text_color.Rgb());
@@ -724,11 +732,7 @@ void SVGInlineTextBoxPainter::PaintTextMarkerBackground(
return;
Color color = LayoutTheme::GetTheme().PlatformTextSearchHighlightColor(
- marker.IsActiveMatch(),
- svg_inline_text_box_.GetLineLayoutItem()
- .GetDocument()
- .InForcedColorsMode(),
- style.UsedColorScheme());
+ marker.IsActiveMatch(), style.UsedColorScheme());
for (const SVGTextFragmentWithRange& text_match_info : text_match_info_list) {
const SVGTextFragment& fragment = text_match_info.fragment;
diff --git a/chromium/third_party/blink/renderer/core/paint/svg_inline_text_box_painter.h b/chromium/third_party/blink/renderer/core/paint/svg_inline_text_box_painter.h
index 6870ae22f33..865ffad3cdc 100644
--- a/chromium/third_party/blink/renderer/core/paint/svg_inline_text_box_painter.h
+++ b/chromium/third_party/blink/renderer/core/paint/svg_inline_text_box_painter.h
@@ -5,6 +5,7 @@
#ifndef THIRD_PARTY_BLINK_RENDERER_CORE_PAINT_SVG_INLINE_TEXT_BOX_PAINTER_H_
#define THIRD_PARTY_BLINK_RENDERER_CORE_PAINT_SVG_INLINE_TEXT_BOX_PAINTER_H_
+#include "third_party/abseil-cpp/absl/types/optional.h"
#include "third_party/blink/renderer/core/paint/svg_object_painter.h"
#include "third_party/blink/renderer/core/style/computed_style_constants.h"
#include "third_party/blink/renderer/platform/wtf/allocator/allocator.h"
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 72f23f43e38..893109d449b 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
@@ -4,6 +4,7 @@
#include "third_party/blink/renderer/core/paint/svg_mask_painter.h"
+#include "third_party/blink/renderer/core/display_lock/display_lock_utilities.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"
@@ -46,7 +47,9 @@ void SVGMaskPainter::Paint(GraphicsContext& context,
auto* masker = GetSVGResourceAsType<LayoutSVGResourceMasker>(
*client, style.MaskerResource());
DCHECK(masker);
- SECURITY_DCHECK(!masker->NeedsLayout());
+ if (DisplayLockUtilities::LockedAncestorPreventingLayout(*masker))
+ return;
+ SECURITY_DCHECK(!masker->SelfNeedsLayout());
masker->ClearInvalidationMask();
FloatRect reference_box = SVGResources::ReferenceBoxForEffects(layout_object);
diff --git a/chromium/third_party/blink/renderer/core/paint/svg_object_painter.cc b/chromium/third_party/blink/renderer/core/paint/svg_object_painter.cc
index acd0c2d1623..f675b25f63d 100644
--- a/chromium/third_party/blink/renderer/core/paint/svg_object_painter.cc
+++ b/chromium/third_party/blink/renderer/core/paint/svg_object_painter.cc
@@ -14,7 +14,8 @@ namespace blink {
namespace {
-void CopyStateFromGraphicsContext(GraphicsContext& context, PaintFlags& flags) {
+void CopyStateFromGraphicsContext(const GraphicsContext& context,
+ PaintFlags& flags) {
// TODO(fs): The color filter can be set when generating a picture for a mask
// due to color-interpolation. We could also just apply the
// color-interpolation property from the the shape itself (which could mean
@@ -31,7 +32,7 @@ void CopyStateFromGraphicsContext(GraphicsContext& context, PaintFlags& flags) {
} // namespace
void SVGObjectPainter::PaintResourceSubtree(GraphicsContext& context) {
- DCHECK(!layout_object_.NeedsLayout());
+ DCHECK(!layout_object_.SelfNeedsLayout());
PaintInfo info(context, CullRect::Infinite(), PaintPhase::kForeground,
kGlobalPaintNormalPhase | kGlobalPaintFlattenCompositingLayers,
@@ -57,12 +58,13 @@ bool SVGObjectPainter::ApplyPaintResource(
}
bool SVGObjectPainter::PreparePaint(
- const PaintInfo& paint_info,
+ const GraphicsContext& context,
+ bool is_rendering_clip_path_as_mask_image,
const ComputedStyle& style,
LayoutSVGResourceMode resource_mode,
PaintFlags& flags,
const AffineTransform* additional_paint_server_transform) {
- if (paint_info.IsRenderingClipPathAsMaskImage()) {
+ if (is_rendering_clip_path_as_mask_image) {
if (resource_mode == kApplyToStrokeMode)
return false;
flags.setColor(SK_ColorBLACK);
@@ -78,7 +80,7 @@ bool SVGObjectPainter::PreparePaint(
if (paint.HasUrl()) {
if (ApplyPaintResource(paint, additional_paint_server_transform, flags)) {
flags.setColor(ScaleAlpha(SK_ColorBLACK, alpha));
- CopyStateFromGraphicsContext(paint_info.context, flags);
+ CopyStateFromGraphicsContext(context, flags);
return true;
}
}
@@ -88,7 +90,7 @@ bool SVGObjectPainter::PreparePaint(
const Color color = style.VisitedDependentColor(property);
flags.setColor(ScaleAlpha(color.Rgb(), alpha));
flags.setShader(nullptr);
- CopyStateFromGraphicsContext(paint_info.context, flags);
+ CopyStateFromGraphicsContext(context, flags);
return true;
}
return false;
diff --git a/chromium/third_party/blink/renderer/core/paint/svg_object_painter.h b/chromium/third_party/blink/renderer/core/paint/svg_object_painter.h
index 2be9fa3f5c2..a45b590e5be 100644
--- a/chromium/third_party/blink/renderer/core/paint/svg_object_painter.h
+++ b/chromium/third_party/blink/renderer/core/paint/svg_object_painter.h
@@ -11,7 +11,6 @@
namespace blink {
-struct PaintInfo;
class AffineTransform;
class ComputedStyle;
class GraphicsContext;
@@ -34,7 +33,8 @@ class SVGObjectPainter {
// object. Returns true if successful, and the caller can continue to paint
// using |paint_flags|.
bool PreparePaint(
- const PaintInfo&,
+ const GraphicsContext& context,
+ bool is_rendering_clip_path_as_mask_image,
const ComputedStyle&,
LayoutSVGResourceMode,
PaintFlags& paint_flags,
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 fc56de03b3d..252cb9ae20d 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,6 +4,7 @@
#include "third_party/blink/renderer/core/paint/svg_shape_painter.h"
+#include "base/stl_util.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"
@@ -74,9 +75,11 @@ void SVGShapePainter::Paint(const PaintInfo& paint_info) {
case PT_FILL: {
PaintFlags fill_flags;
if (!SVGObjectPainter(layout_svg_shape_)
- .PreparePaint(paint_info, style, kApplyToFillMode,
- fill_flags))
+ .PreparePaint(paint_info.context,
+ paint_info.IsRenderingClipPathAsMaskImage(),
+ style, kApplyToFillMode, fill_flags)) {
break;
+ }
fill_flags.setAntiAlias(should_anti_alias);
FillShape(paint_info.context, fill_flags,
FillRuleFromStyle(paint_info, style));
@@ -99,9 +102,12 @@ void SVGShapePainter::Paint(const PaintInfo& paint_info) {
PaintFlags stroke_flags;
if (!SVGObjectPainter(layout_svg_shape_)
.PreparePaint(
- paint_info, style, kApplyToStrokeMode, stroke_flags,
- base::OptionalOrNullptr(non_scaling_transform)))
+ paint_info.context,
+ paint_info.IsRenderingClipPathAsMaskImage(), style,
+ kApplyToStrokeMode, stroke_flags,
+ base::OptionalOrNullptr(non_scaling_transform))) {
break;
+ }
stroke_flags.setAntiAlias(should_anti_alias);
StrokeData stroke_data;
diff --git a/chromium/third_party/blink/renderer/core/paint/table_paint_invalidator.cc b/chromium/third_party/blink/renderer/core/paint/table_paint_invalidator.cc
index fb7125dee16..00889fb2825 100644
--- a/chromium/third_party/blink/renderer/core/paint/table_paint_invalidator.cc
+++ b/chromium/third_party/blink/renderer/core/paint/table_paint_invalidator.cc
@@ -10,6 +10,7 @@
#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/paint/box_paint_invalidator.h"
+#include "third_party/blink/renderer/core/paint/object_paint_invalidator.h"
#include "third_party/blink/renderer/core/paint/paint_invalidator.h"
namespace blink {
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 9c099775574..886e3ccdf21 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
@@ -48,8 +48,7 @@ void TableSectionPainter::Paint(const PaintInfo& paint_info) {
for (const auto* fragment = &layout_table_section_.FirstFragment(); fragment;
fragment = fragment->NextFragment()) {
PaintInfo fragment_paint_info = paint_info;
- fragment_paint_info.SetFragmentLogicalTopInFlowThread(
- fragment->LogicalTopInFlowThread());
+ fragment_paint_info.SetFragmentID(fragment->FragmentID());
ScopedDisplayItemFragment scoped_display_item_fragment(
fragment_paint_info.context, fragment_index++);
PaintSection(fragment_paint_info);
@@ -106,11 +105,13 @@ void TableSectionPainter::PaintCollapsedBorders(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());
+ fragment_paint_info.SetFragmentID(fragment->FragmentID());
+ ScopedDisplayItemFragment scoped_display_item_fragment(
+ fragment_paint_info.context, fragment_index++);
PaintCollapsedSectionBorders(fragment_paint_info);
}
}
diff --git a/chromium/third_party/blink/renderer/core/paint/text_decoration_info.cc b/chromium/third_party/blink/renderer/core/paint/text_decoration_info.cc
index 3409e759e1b..31983132cb7 100644
--- a/chromium/third_party/blink/renderer/core/paint/text_decoration_info.cc
+++ b/chromium/third_party/blink/renderer/core/paint/text_decoration_info.cc
@@ -28,7 +28,7 @@ static ResolvedUnderlinePosition ResolveUnderlinePosition(
if (style.TextUnderlinePosition() & kTextUnderlinePositionFromFont)
return ResolvedUnderlinePosition::kNearAlphabeticBaselineFromFont;
return ResolvedUnderlinePosition::kNearAlphabeticBaselineAuto;
- case kIdeographicBaseline:
+ case kCentralBaseline: {
// Compute language-appropriate default underline position.
// https://drafts.csswg.org/css-text-decor-3/#default-stylesheet
UScriptCode script = style.GetFontDescription().GetScript();
@@ -42,6 +42,10 @@ static ResolvedUnderlinePosition ResolveUnderlinePosition(
return ResolvedUnderlinePosition::kOver;
}
return ResolvedUnderlinePosition::kUnder;
+ }
+ default:
+ NOTREACHED();
+ break;
}
NOTREACHED();
return ResolvedUnderlinePosition::kNearAlphabeticBaselineAuto;
@@ -154,7 +158,6 @@ static int TextDecorationToLineDataIndex(TextDecoration line) {
} // anonymous namespace
TextDecorationInfo::TextDecorationInfo(
- const PhysicalOffset& box_origin,
PhysicalOffset local_origin,
LayoutUnit width,
FontBaseline baseline_type,
diff --git a/chromium/third_party/blink/renderer/core/paint/text_decoration_info.h b/chromium/third_party/blink/renderer/core/paint/text_decoration_info.h
index 1973942f40b..be9d2e63831 100644
--- a/chromium/third_party/blink/renderer/core/paint/text_decoration_info.h
+++ b/chromium/third_party/blink/renderer/core/paint/text_decoration_info.h
@@ -5,6 +5,7 @@
#ifndef THIRD_PARTY_BLINK_RENDERER_CORE_PAINT_TEXT_DECORATION_INFO_H_
#define THIRD_PARTY_BLINK_RENDERER_CORE_PAINT_TEXT_DECORATION_INFO_H_
+#include "third_party/abseil-cpp/absl/types/optional.h"
#include "third_party/blink/renderer/core/core_export.h"
#include "third_party/blink/renderer/core/layout/geometry/physical_offset.h"
#include "third_party/blink/renderer/core/paint/text_paint_style.h"
@@ -37,7 +38,6 @@ class CORE_EXPORT TextDecorationInfo {
public:
TextDecorationInfo(
- const PhysicalOffset& box_origin,
PhysicalOffset local_origin,
LayoutUnit width,
FontBaseline baseline_type,
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 427f81b1f55..33fb74c26af 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
@@ -5,6 +5,7 @@
#ifndef THIRD_PARTY_BLINK_RENDERER_CORE_PAINT_TEXT_PAINT_STYLE_H_
#define THIRD_PARTY_BLINK_RENDERER_CORE_PAINT_TEXT_PAINT_STYLE_H_
+#include "third_party/abseil-cpp/absl/types/optional.h"
#include "third_party/blink/public/mojom/frame/color_scheme.mojom-blink-forward.h"
#include "third_party/blink/renderer/core/core_export.h"
#include "third_party/blink/renderer/core/style/applied_text_decoration.h"
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 63fc7b0ed8b..14a8233ba68 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
@@ -85,7 +85,8 @@ class TextPaintTimingDetectorTest : public testing::Test {
wtf_size_t CountRankingSetSize() {
DCHECK(GetTextPaintTimingDetector());
- return GetLargestTextPaintManager()->size_ordered_set_.size();
+ return static_cast<wtf_size_t>(
+ GetLargestTextPaintManager()->size_ordered_set_.size());
}
wtf_size_t CountInvisibleTexts() {
@@ -326,10 +327,10 @@ TEST_F(TextPaintTimingDetectorTest, LargestTextPaint_TraceEvent_Candidate) {
EXPECT_TRUE(events[0]->HasArg("frame"));
EXPECT_TRUE(events[0]->HasArg("data"));
- std::unique_ptr<base::Value> arg;
+ base::Value arg;
EXPECT_TRUE(events[0]->GetArgAsValue("data", &arg));
base::DictionaryValue* arg_dict;
- EXPECT_TRUE(arg->GetAsDictionary(&arg_dict));
+ EXPECT_TRUE(arg.GetAsDictionary(&arg_dict));
DOMNodeId node_id;
EXPECT_TRUE(arg_dict->GetInteger("DOMNodeId", &node_id));
EXPECT_GT(node_id, 0);
@@ -395,10 +396,10 @@ TEST_F(TextPaintTimingDetectorTest,
EXPECT_TRUE(events[0]->HasArg("frame"));
EXPECT_TRUE(events[0]->HasArg("data"));
- std::unique_ptr<base::Value> arg;
+ base::Value arg;
EXPECT_TRUE(events[0]->GetArgAsValue("data", &arg));
base::DictionaryValue* arg_dict;
- EXPECT_TRUE(arg->GetAsDictionary(&arg_dict));
+ EXPECT_TRUE(arg.GetAsDictionary(&arg_dict));
DOMNodeId node_id;
EXPECT_TRUE(arg_dict->GetInteger("DOMNodeId", &node_id));
EXPECT_GT(node_id, 0);
@@ -463,10 +464,10 @@ TEST_F(TextPaintTimingDetectorTest, LargestTextPaint_TraceEvent_NoCandidate) {
EXPECT_TRUE(events[0]->HasArg("frame"));
EXPECT_TRUE(events[0]->HasArg("data"));
- std::unique_ptr<base::Value> arg;
+ base::Value arg;
EXPECT_TRUE(events[0]->GetArgAsValue("data", &arg));
base::DictionaryValue* arg_dict;
- EXPECT_TRUE(arg->GetAsDictionary(&arg_dict));
+ EXPECT_TRUE(arg.GetAsDictionary(&arg_dict));
DOMNodeId candidate_index;
EXPECT_TRUE(arg_dict->GetInteger("candidateIndex", &candidate_index));
EXPECT_EQ(candidate_index, 2);
diff --git a/chromium/third_party/blink/renderer/core/paint/text_painter.cc b/chromium/third_party/blink/renderer/core/paint/text_painter.cc
index f8379c71dd4..77e220b71b1 100644
--- a/chromium/third_party/blink/renderer/core/paint/text_painter.cc
+++ b/chromium/third_party/blink/renderer/core/paint/text_painter.cc
@@ -44,12 +44,15 @@ void TextPainter::Paint(unsigned start_offset,
}
if (!emphasis_mark_.IsEmpty()) {
- if (text_style.emphasis_mark_color != text_style.fill_color)
- graphics_context_.SetFillColor(text_style.emphasis_mark_color);
-
if (combined_text_) {
- PaintEmphasisMarkForCombinedText();
+ graphics_context_.ConcatCTM(Rotation(text_frame_rect_, kClockwise));
+ PaintEmphasisMarkForCombinedText(text_style,
+ combined_text_->OriginalFont());
+ graphics_context_.ConcatCTM(
+ Rotation(text_frame_rect_, kCounterclockwise));
} else {
+ if (text_style.emphasis_mark_color != text_style.fill_color)
+ graphics_context_.SetFillColor(text_style.emphasis_mark_color);
PaintInternal<kPaintEmphasisMark>(start_offset, end_offset, length,
node_id);
}
@@ -67,7 +70,7 @@ void TextPainter::PaintDecorationsExceptLineThrough(
GraphicsContextStateSaver state_saver(context);
UpdateGraphicsContext(context, text_style, horizontal_, state_saver);
- if (has_combined_text_)
+ if (combined_text_)
context.ConcatCTM(Rotation(text_frame_rect_, kClockwise));
// text-underline-position may flip underline and overline.
@@ -79,7 +82,7 @@ void TextPainter::PaintDecorationsExceptLineThrough(
underline_position = ResolvedUnderlinePosition::kUnder;
}
- for (size_t applied_decoration_index = 0;
+ for (wtf_size_t applied_decoration_index = 0;
applied_decoration_index < decorations.size();
++applied_decoration_index) {
const AppliedTextDecoration& decoration =
@@ -139,7 +142,7 @@ void TextPainter::PaintDecorationsExceptLineThrough(
}
// Restore rotation as needed.
- if (has_combined_text_)
+ if (combined_text_)
context.ConcatCTM(Rotation(text_frame_rect_, kCounterclockwise));
}
@@ -152,10 +155,10 @@ void TextPainter::PaintDecorationsOnlyLineThrough(
GraphicsContextStateSaver state_saver(context);
UpdateGraphicsContext(context, text_style, horizontal_, state_saver);
- if (has_combined_text_)
+ if (combined_text_)
context.ConcatCTM(Rotation(text_frame_rect_, kClockwise));
- for (size_t applied_decoration_index = 0;
+ for (wtf_size_t applied_decoration_index = 0;
applied_decoration_index < decorations.size();
++applied_decoration_index) {
const AppliedTextDecoration& decoration =
@@ -190,7 +193,7 @@ void TextPainter::PaintDecorationsOnlyLineThrough(
}
// Restore rotation as needed.
- if (has_combined_text_)
+ if (combined_text_)
context.ConcatCTM(Rotation(text_frame_rect_, kCounterclockwise));
}
@@ -253,24 +256,4 @@ void TextPainter::ClipDecorationsStripe(float upper,
DecorationsStripeIntercepts(upper, stripe_width, dilation, text_intercepts);
}
-void TextPainter::PaintEmphasisMarkForCombinedText() {
- const SimpleFontData* font_data = font_.PrimaryFont();
- DCHECK(font_data);
- if (!font_data)
- return;
-
- DCHECK(combined_text_);
- TextRun placeholder_text_run(&kIdeographicFullStopCharacter, 1);
- FloatPoint emphasis_mark_text_origin(
- text_frame_rect_.X().ToFloat(), text_frame_rect_.Y().ToFloat() +
- font_data->GetFontMetrics().Ascent() +
- emphasis_mark_offset_);
- TextRunPaintInfo text_run_paint_info(placeholder_text_run);
- graphics_context_.ConcatCTM(Rotation(text_frame_rect_, kClockwise));
- graphics_context_.DrawEmphasisMarks(combined_text_->OriginalFont(),
- text_run_paint_info, emphasis_mark_,
- emphasis_mark_text_origin);
- graphics_context_.ConcatCTM(Rotation(text_frame_rect_, kCounterclockwise));
-}
-
} // namespace blink
diff --git a/chromium/third_party/blink/renderer/core/paint/text_painter.h b/chromium/third_party/blink/renderer/core/paint/text_painter.h
index 423743444e0..232c67d496b 100644
--- a/chromium/third_party/blink/renderer/core/paint/text_painter.h
+++ b/chromium/third_party/blink/renderer/core/paint/text_painter.h
@@ -32,13 +32,11 @@ class CORE_EXPORT TextPainter : public TextPainterBase {
text_origin,
text_frame_rect,
horizontal),
- run_(run),
- combined_text_(nullptr) {}
+ run_(run) {}
~TextPainter() = default;
void SetCombinedText(LayoutTextCombine* combined_text) {
combined_text_ = combined_text;
- has_combined_text_ = combined_text_ ? true : false;
}
void ClipDecorationsStripe(float upper,
@@ -74,10 +72,8 @@ class CORE_EXPORT TextPainter : public TextPainterBase {
unsigned truncation_point,
DOMNodeId node_id);
- void PaintEmphasisMarkForCombinedText();
-
const TextRun& run_;
- LayoutTextCombine* combined_text_;
+ LayoutTextCombine* combined_text_ = nullptr;
};
} // namespace blink
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 74463be4405..18107374fc3 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
@@ -13,8 +13,8 @@
#include "third_party/blink/renderer/core/paint/text_decoration_info.h"
#include "third_party/blink/renderer/core/style/computed_style.h"
#include "third_party/blink/renderer/core/style/shadow_list.h"
-#include "third_party/blink/renderer/core/svg/svg_length_context.h"
#include "third_party/blink/renderer/platform/fonts/font.h"
+#include "third_party/blink/renderer/platform/fonts/text_run_paint_info.h"
#include "third_party/blink/renderer/platform/geometry/length_functions.h"
#include "third_party/blink/renderer/platform/graphics/graphics_context.h"
#include "third_party/blink/renderer/platform/graphics/graphics_context_state_saver.h"
@@ -42,10 +42,7 @@ TextPainterBase::TextPainterBase(GraphicsContext& context,
font_(font),
text_origin_(text_origin),
text_frame_rect_(text_frame_rect),
- horizontal_(horizontal),
- has_combined_text_(false),
- emphasis_mark_offset_(0),
- ellipsis_offset_(0) {}
+ horizontal_(horizontal) {}
TextPainterBase::~TextPainterBase() = default;
@@ -74,34 +71,6 @@ void TextPainterBase::SetEmphasisMark(const AtomicString& emphasis_mark,
}
// static
-void TextPainterBase::AdjustTextStyleForClip(TextPaintStyle& text_style) {
- // When we use the text as a clip, we only care about the alpha, thus we
- // make all the colors black.
- text_style.current_color = Color::kBlack;
- text_style.fill_color = Color::kBlack;
- text_style.stroke_color = Color::kBlack;
- text_style.emphasis_mark_color = Color::kBlack;
- text_style.shadow = nullptr;
-}
-
-// static
-void TextPainterBase::AdjustTextStyleForPrint(const Document& document,
- const ComputedStyle& style,
- TextPaintStyle& text_style) {
- // Adjust text color when printing with a white background.
- bool force_background_to_white =
- BoxPainterBase::ShouldForceWhiteBackgroundForPrintEconomy(document,
- style);
- if (force_background_to_white) {
- text_style.fill_color = TextColorForWhiteBackground(text_style.fill_color);
- text_style.stroke_color =
- TextColorForWhiteBackground(text_style.stroke_color);
- text_style.emphasis_mark_color =
- TextColorForWhiteBackground(text_style.emphasis_mark_color);
- }
-}
-
-// static
void TextPainterBase::UpdateGraphicsContext(
GraphicsContext& context,
const TextPaintStyle& text_style,
@@ -188,7 +157,13 @@ TextPaintStyle TextPainterBase::TextPaintingStyle(const Document& document,
text_style.color_scheme = style.UsedColorScheme();
if (paint_info.phase == PaintPhase::kTextClip) {
- AdjustTextStyleForClip(text_style);
+ // When we use the text as a clip, we only care about the alpha, thus we
+ // make all the colors black.
+ text_style.current_color = Color::kBlack;
+ text_style.fill_color = Color::kBlack;
+ text_style.stroke_color = Color::kBlack;
+ text_style.emphasis_mark_color = Color::kBlack;
+ text_style.shadow = nullptr;
} else {
text_style.current_color =
style.VisitedDependentColor(GetCSSPropertyColor());
@@ -200,54 +175,18 @@ TextPaintStyle TextPainterBase::TextPaintingStyle(const Document& document,
style.VisitedDependentColor(GetCSSPropertyWebkitTextEmphasisColor());
text_style.shadow = style.TextShadow();
- AdjustTextStyleForPrint(document, style, text_style);
- }
-
- return text_style;
-}
-
-// static
-TextPaintStyle TextPainterBase::SvgTextPaintingStyle(
- const Document& document,
- const SVGLengthContext& length_context,
- const ComputedStyle& style,
- const PaintInfo& paint_info) {
- TextPaintStyle text_style;
- text_style.stroke_width =
- style.HasStroke() ? length_context.ValueForLength(style.StrokeWidth())
- : 0;
- text_style.color_scheme = style.UsedColorScheme();
-
- if (paint_info.phase == PaintPhase::kTextClip) {
- AdjustTextStyleForClip(text_style);
- } else {
- text_style.current_color =
- style.VisitedDependentColor(GetCSSPropertyColor());
-
- const SVGPaint fill_paint = style.FillPaint();
- if (fill_paint.IsNone()) {
- text_style.fill_color = Color::kTransparent;
- } else if (fill_paint.HasColor()) {
- const Color color = style.VisitedDependentColor(GetCSSPropertyFill());
- const float alpha = style.FillOpacity();
- text_style.fill_color = ScaleAlpha(color.Rgb(), alpha);
- } else {
- text_style.fill_color = Color::kBlack;
+ // Adjust text color when printing with a white background.
+ bool force_background_to_white =
+ BoxPainterBase::ShouldForceWhiteBackgroundForPrintEconomy(document,
+ style);
+ if (force_background_to_white) {
+ text_style.fill_color =
+ TextColorForWhiteBackground(text_style.fill_color);
+ text_style.stroke_color =
+ TextColorForWhiteBackground(text_style.stroke_color);
+ text_style.emphasis_mark_color =
+ TextColorForWhiteBackground(text_style.emphasis_mark_color);
}
-
- if (style.StrokePaint().HasColor()) {
- const Color color = style.VisitedDependentColor(GetCSSPropertyStroke());
- const float alpha = style.StrokeOpacity();
- text_style.stroke_color = ScaleAlpha(color.Rgb(), alpha);
- } else {
- text_style.stroke_color = Color::kTransparent;
- }
-
- text_style.emphasis_mark_color =
- style.VisitedDependentColor(GetCSSPropertyWebkitTextEmphasisColor());
- text_style.shadow = style.TextShadow();
-
- AdjustTextStyleForPrint(document, style, text_style);
}
return text_style;
@@ -289,6 +228,130 @@ void TextPainterBase::DecorationsStripeIntercepts(
}
}
+void TextPainterBase::PaintDecorationsExceptLineThrough(
+ const TextDecorationOffsetBase& decoration_offset,
+ TextDecorationInfo& decoration_info,
+ const PaintInfo& paint_info,
+ const Vector<AppliedTextDecoration>& decorations,
+ const TextPaintStyle& text_style,
+ bool* has_line_through_decoration) {
+ GraphicsContext& context = paint_info.context;
+ GraphicsContextStateSaver state_saver(context);
+ UpdateGraphicsContext(context, text_style, horizontal_, state_saver);
+
+ // text-underline-position may flip underline and overline.
+ ResolvedUnderlinePosition underline_position =
+ decoration_info.UnderlinePosition();
+ bool flip_underline_and_overline = false;
+ if (underline_position == ResolvedUnderlinePosition::kOver) {
+ flip_underline_and_overline = true;
+ underline_position = ResolvedUnderlinePosition::kUnder;
+ }
+
+ for (wtf_size_t applied_decoration_index = 0;
+ applied_decoration_index < decorations.size();
+ ++applied_decoration_index) {
+ const AppliedTextDecoration& decoration =
+ decorations[applied_decoration_index];
+ TextDecoration lines = decoration.Lines();
+ bool has_underline = EnumHasFlags(lines, TextDecoration::kUnderline);
+ bool has_overline = EnumHasFlags(lines, TextDecoration::kOverline);
+ if (flip_underline_and_overline)
+ std::swap(has_underline, has_overline);
+
+ decoration_info.SetDecorationIndex(applied_decoration_index);
+
+ float resolved_thickness = decoration_info.ResolvedThickness();
+ context.SetStrokeThickness(resolved_thickness);
+
+ if (has_underline && decoration_info.FontData()) {
+ // Don't apply text-underline-offset to overline.
+ Length line_offset =
+ flip_underline_and_overline ? Length() : decoration.UnderlineOffset();
+
+ const int paint_underline_offset =
+ decoration_offset.ComputeUnderlineOffset(
+ underline_position, decoration_info.Style().ComputedFontSize(),
+ decoration_info.FontData()->GetFontMetrics(), line_offset,
+ resolved_thickness);
+ decoration_info.SetPerLineData(
+ TextDecoration::kUnderline, paint_underline_offset,
+ TextDecorationInfo::DoubleOffsetFromThickness(resolved_thickness), 1);
+ PaintDecorationUnderOrOverLine(context, decoration_info,
+ TextDecoration::kUnderline);
+ }
+
+ if (has_overline && decoration_info.FontData()) {
+ // Don't apply text-underline-offset to overline.
+ Length line_offset =
+ flip_underline_and_overline ? decoration.UnderlineOffset() : Length();
+
+ FontVerticalPositionType position =
+ flip_underline_and_overline ? FontVerticalPositionType::TopOfEmHeight
+ : FontVerticalPositionType::TextTop;
+ const int paint_overline_offset =
+ decoration_offset.ComputeUnderlineOffsetForUnder(
+ line_offset, decoration_info.Style().ComputedFontSize(),
+ resolved_thickness, position);
+ decoration_info.SetPerLineData(
+ TextDecoration::kOverline, paint_overline_offset,
+ -TextDecorationInfo::DoubleOffsetFromThickness(resolved_thickness),
+ 1);
+ PaintDecorationUnderOrOverLine(context, decoration_info,
+ TextDecoration::kOverline);
+ }
+
+ // We could instead build a vector of the TextDecoration instances needing
+ // line-through but this is a rare case so better to avoid vector overhead.
+ *has_line_through_decoration |=
+ EnumHasFlags(lines, TextDecoration::kLineThrough);
+ }
+}
+
+void TextPainterBase::PaintDecorationsOnlyLineThrough(
+ TextDecorationInfo& decoration_info,
+ const PaintInfo& paint_info,
+ const Vector<AppliedTextDecoration>& decorations,
+ const TextPaintStyle& text_style) {
+ GraphicsContext& context = paint_info.context;
+ GraphicsContextStateSaver state_saver(context);
+ UpdateGraphicsContext(context, text_style, horizontal_, state_saver);
+
+ for (wtf_size_t applied_decoration_index = 0;
+ applied_decoration_index < decorations.size();
+ ++applied_decoration_index) {
+ const AppliedTextDecoration& decoration =
+ decorations[applied_decoration_index];
+ TextDecoration lines = decoration.Lines();
+ if (EnumHasFlags(lines, TextDecoration::kLineThrough)) {
+ decoration_info.SetDecorationIndex(applied_decoration_index);
+
+ float resolved_thickness = decoration_info.ResolvedThickness();
+ context.SetStrokeThickness(resolved_thickness);
+
+ // For increased line thickness, the line-through decoration needs to grow
+ // in both directions from its origin, subtract half the thickness to keep
+ // it centered at the same origin.
+ const float line_through_offset =
+ 2 * decoration_info.Baseline() / 3 - resolved_thickness / 2;
+ // Floor double_offset in order to avoid double-line gap to appear
+ // of different size depending on position where the double line
+ // is drawn because of rounding downstream in
+ // GraphicsContext::DrawLineForText.
+ decoration_info.SetPerLineData(
+ TextDecoration::kLineThrough, line_through_offset,
+ floorf(TextDecorationInfo::DoubleOffsetFromThickness(
+ resolved_thickness)),
+ 0);
+ AppliedDecorationPainter decoration_painter(context, decoration_info,
+ TextDecoration::kLineThrough);
+ // No skip: ink for line-through,
+ // compare https://github.com/w3c/csswg-drafts/issues/711
+ decoration_painter.Paint();
+ }
+ }
+}
+
void TextPainterBase::PaintDecorationUnderOrOverLine(
GraphicsContext& context,
TextDecorationInfo& decoration_info,
@@ -308,4 +371,30 @@ void TextPainterBase::PaintDecorationUnderOrOverLine(
decoration_painter.Paint();
}
+void TextPainterBase::PaintEmphasisMarkForCombinedText(
+ const TextPaintStyle& text_style,
+ const Font& emphasis_mark_font) {
+ DCHECK(emphasis_mark_font.GetFontDescription().IsVerticalBaseline());
+ DCHECK(emphasis_mark_);
+ const SimpleFontData* const font_data = font_.PrimaryFont();
+ DCHECK(font_data);
+ if (!font_data)
+ return;
+
+ if (text_style.emphasis_mark_color != text_style.fill_color) {
+ // See virtual/text-antialias/emphasis-combined-text.html
+ graphics_context_.SetFillColor(text_style.emphasis_mark_color);
+ }
+
+ const auto font_ascent = font_data->GetFontMetrics().Ascent();
+ const TextRun placeholder_text_run(&kIdeographicFullStopCharacter, 1);
+ const FloatPoint emphasis_mark_text_origin(
+ text_frame_rect_.X().ToFloat(),
+ text_frame_rect_.Y().ToFloat() + font_ascent + emphasis_mark_offset_);
+ const TextRunPaintInfo text_run_paint_info(placeholder_text_run);
+ graphics_context_.DrawEmphasisMarks(emphasis_mark_font, text_run_paint_info,
+ emphasis_mark_,
+ emphasis_mark_text_origin);
+}
+
} // namespace blink
diff --git a/chromium/third_party/blink/renderer/core/paint/text_painter_base.h b/chromium/third_party/blink/renderer/core/paint/text_painter_base.h
index 35271337b3f..bba7e4bb175 100644
--- a/chromium/third_party/blink/renderer/core/paint/text_painter_base.h
+++ b/chromium/third_party/blink/renderer/core/paint/text_painter_base.h
@@ -25,7 +25,7 @@ class Document;
class GraphicsContext;
class GraphicsContextStateSaver;
class Node;
-class SVGLengthContext;
+class TextDecorationOffsetBase;
struct PaintInfo;
// Base class for text painting. Has no dependencies on the layout tree and thus
@@ -71,10 +71,6 @@ class CORE_EXPORT TextPainterBase {
static TextPaintStyle TextPaintingStyle(const Document&,
const ComputedStyle&,
const PaintInfo&);
- static TextPaintStyle SvgTextPaintingStyle(const Document&,
- const SVGLengthContext&,
- const ComputedStyle&,
- const PaintInfo&);
static TextPaintStyle SelectionPaintingStyle(
const Document&,
const ComputedStyle&,
@@ -85,12 +81,9 @@ class CORE_EXPORT TextPainterBase {
enum RotationDirection { kCounterclockwise, kClockwise };
static AffineTransform Rotation(const PhysicalRect& box_rect,
RotationDirection);
+ static AffineTransform Rotation(const PhysicalRect& box_rect, WritingMode);
protected:
- static void AdjustTextStyleForClip(TextPaintStyle&);
- static void AdjustTextStyleForPrint(const Document&,
- const ComputedStyle&,
- TextPaintStyle&);
void UpdateGraphicsContext(const TextPaintStyle& style,
GraphicsContextStateSaver& saver) {
UpdateGraphicsContext(graphics_context_, style, horizontal_, saver);
@@ -101,17 +94,42 @@ class CORE_EXPORT TextPainterBase {
float dilation,
const Vector<Font::TextIntercept>& text_intercepts);
+ // We have two functions to paint text decoations, because we should paint
+ // text and decorations in following order:
+ // 1. Paint text decorations except line through
+ // 2. Paint text
+ // 3. Paint line throguh
+ void PaintDecorationsExceptLineThrough(const TextDecorationOffsetBase&,
+ TextDecorationInfo&,
+ const PaintInfo&,
+ const Vector<AppliedTextDecoration>&,
+ const TextPaintStyle& text_style,
+ bool* has_line_through_decoration);
+ void PaintDecorationsOnlyLineThrough(TextDecorationInfo&,
+ const PaintInfo&,
+ const Vector<AppliedTextDecoration>&,
+ const TextPaintStyle&);
+
+ // Paints emphasis mark as for ideographic full stop character. Callers of
+ // this function should rotate canvas to paint emphasis mark at left/right
+ // side instead of top/bottom side.
+ // |emphasis_mark_font| is used for painting emphasis mark because |font_|
+ // may be compressed font (width variants).
+ // TODO(yosin): Once legacy inline layout gone, we should move this function
+ // to |NGTextCombinePainter|.
+ void PaintEmphasisMarkForCombinedText(const TextPaintStyle& text_style,
+ const Font& emphasis_mark_font);
+
enum PaintInternalStep { kPaintText, kPaintEmphasisMark };
GraphicsContext& graphics_context_;
const Font& font_;
- PhysicalOffset text_origin_;
- PhysicalRect text_frame_rect_;
- bool horizontal_;
- bool has_combined_text_;
+ const PhysicalOffset text_origin_;
+ const PhysicalRect text_frame_rect_;
AtomicString emphasis_mark_;
- int emphasis_mark_offset_;
- int ellipsis_offset_;
+ int emphasis_mark_offset_ = 0;
+ int ellipsis_offset_ = 0;
+ const bool horizontal_;
};
inline AffineTransform TextPainterBase::Rotation(
@@ -139,6 +157,13 @@ inline AffineTransform TextPainterBase::Rotation(
box_rect.X() + box_rect.Bottom());
}
+inline AffineTransform TextPainterBase::Rotation(const PhysicalRect& box_rect,
+ WritingMode writing_mode) {
+ return Rotation(box_rect, writing_mode != WritingMode::kSidewaysLr
+ ? TextPainterBase::kClockwise
+ : TextPainterBase::kCounterclockwise);
+}
+
} // namespace blink
#endif // THIRD_PARTY_BLINK_RENDERER_CORE_PAINT_TEXT_PAINTER_BASE_H_
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 f228a00a533..9ef1c2346e8 100644
--- a/chromium/third_party/blink/renderer/core/paint/theme_painter.cc
+++ b/chromium/third_party/blink/renderer/core/paint/theme_painter.cc
@@ -23,6 +23,7 @@
#include "build/build_config.h"
#include "third_party/blink/public/platform/platform.h"
+#include "third_party/blink/renderer/core/dom/shadow_root.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"
@@ -171,15 +172,9 @@ bool ThemePainter::Paint(const LayoutObject& o,
case kMenulistButtonPart:
return true;
case kTextFieldPart:
- if (!features::IsFormControlsRefreshEnabled()) {
- return true;
- }
CountAppearanceTextFieldPart(element);
return PaintTextField(element, style, paint_info, r);
case kTextAreaPart:
- if (!features::IsFormControlsRefreshEnabled()) {
- return true;
- }
COUNT_APPEARANCE(doc, TextArea);
return PaintTextArea(element, style, paint_info, r);
case kSearchFieldPart: {
@@ -211,17 +206,8 @@ bool ThemePainter::PaintBorderOnly(const Node* node,
// Call the appropriate paint method based off the appearance value.
switch (style.EffectiveAppearance()) {
case kTextFieldPart:
- if (features::IsFormControlsRefreshEnabled()) {
- return false;
- }
- CountAppearanceTextFieldPart(element);
- return PaintTextField(element, style, paint_info, r);
case kTextAreaPart:
- if (features::IsFormControlsRefreshEnabled()) {
- return false;
- }
- COUNT_APPEARANCE(element.GetDocument(), TextArea);
- return PaintTextArea(element, style, paint_info, r);
+ return false;
case kMenulistButtonPart:
case kSearchFieldPart:
case kListboxPart:
@@ -249,8 +235,6 @@ bool ThemePainter::PaintBorderOnly(const Node* node,
// appearance values.
return false;
}
-
- return false;
}
bool ThemePainter::PaintDecorations(const Node* node,
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 8832e863a1a..ed10b07cd5d 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
@@ -27,6 +27,7 @@
#include "third_party/blink/public/platform/platform.h"
#include "third_party/blink/public/platform/web_theme_engine.h"
#include "third_party/blink/public/resources/grit/blink_image_resources.h"
+#include "third_party/blink/renderer/core/dom/shadow_root.h"
#include "third_party/blink/renderer/core/frame/local_frame_view.h"
#include "third_party/blink/renderer/core/html/forms/html_input_element.h"
#include "third_party/blink/renderer/core/html/forms/slider_thumb_element.h"
@@ -187,10 +188,14 @@ bool ThemePainterDefault::PaintCheckbox(const Element& element,
GraphicsContextStateSaver state_saver(paint_info.context, false);
IntRect unzoomed_rect =
ApplyZoomToRect(rect, paint_info, state_saver, zoom_level);
+ bool enable_force_dark =
+ paint_info.context.IsDarkModeEnabled() && !style.DisableForceDark();
+ mojom::blink::ColorScheme color_scheme = style.UsedColorScheme();
+ AdjustColorScheme(color_scheme, enable_force_dark);
Platform::Current()->ThemeEngine()->Paint(
canvas, WebThemeEngine::kPartCheckbox, GetWebThemeState(element),
- gfx::Rect(unzoomed_rect), &extra_params, style.UsedColorScheme(),
+ gfx::Rect(unzoomed_rect), &extra_params, color_scheme,
GetAccentColor(style));
return false;
}
@@ -209,13 +214,15 @@ bool ThemePainterDefault::PaintRadio(const Element& element,
extra_params.button.zoom = zoom_level;
GraphicsContextStateSaver state_saver(paint_info.context, false);
IntRect unzoomed_rect =
- features::IsFormControlsRefreshEnabled()
- ? ApplyZoomToRect(rect, paint_info, state_saver, zoom_level)
- : rect;
+ ApplyZoomToRect(rect, paint_info, state_saver, zoom_level);
+ bool enable_force_dark =
+ paint_info.context.IsDarkModeEnabled() && !style.DisableForceDark();
+ mojom::blink::ColorScheme color_scheme = style.UsedColorScheme();
+ AdjustColorScheme(color_scheme, enable_force_dark);
Platform::Current()->ThemeEngine()->Paint(
canvas, WebThemeEngine::kPartRadio, GetWebThemeState(element),
- gfx::Rect(unzoomed_rect), &extra_params, style.UsedColorScheme(),
+ gfx::Rect(unzoomed_rect), &extra_params, color_scheme,
GetAccentColor(style));
return false;
}
@@ -235,10 +242,14 @@ bool ThemePainterDefault::PaintButton(const Element& element,
extra_params.button.background_color =
style.VisitedDependentColor(GetCSSPropertyBackgroundColor()).Rgb();
}
+ bool enable_force_dark =
+ paint_info.context.IsDarkModeEnabled() && !style.DisableForceDark();
+ mojom::blink::ColorScheme color_scheme = style.UsedColorScheme();
+ AdjustColorScheme(color_scheme, enable_force_dark);
+
Platform::Current()->ThemeEngine()->Paint(
canvas, WebThemeEngine::kPartButton, GetWebThemeState(element),
- gfx::Rect(rect), &extra_params, style.UsedColorScheme(),
- GetAccentColor(style));
+ gfx::Rect(rect), &extra_params, color_scheme, GetAccentColor(style));
return false;
}
@@ -251,15 +262,6 @@ bool ThemePainterDefault::PaintTextField(const Element& element,
if (style.HasBorderRadius() || style.HasBackgroundImage())
return true;
- // Don't use the theme painter if dark mode is enabled. It has a separate
- // graphics pipeline that doesn't go through GraphicsContext and so does not
- // currently know how to handle Dark Mode, causing elements to be rendered
- // incorrectly (e.g. https://crbug.com/937872).
- // TODO(gilmanmh): Implement a more permanent solution that allows use of
- // native dark themes.
- if (paint_info.context.IsDarkModeEnabled())
- return true;
-
ControlPart part = style.EffectiveAppearance();
WebThemeEngine::ExtraParams extra_params;
@@ -276,10 +278,14 @@ bool ThemePainterDefault::PaintTextField(const Element& element,
extra_params.text_field.auto_complete_active =
DynamicTo<HTMLFormControlElement>(element)->IsAutofilled();
+ bool enable_force_dark =
+ paint_info.context.IsDarkModeEnabled() && !style.DisableForceDark();
+ mojom::blink::ColorScheme color_scheme = style.UsedColorScheme();
+ AdjustColorScheme(color_scheme, enable_force_dark);
+
Platform::Current()->ThemeEngine()->Paint(
canvas, WebThemeEngine::kPartTextField, GetWebThemeState(element),
- gfx::Rect(rect), &extra_params, style.UsedColorScheme(),
- GetAccentColor(style));
+ gfx::Rect(rect), &extra_params, color_scheme, GetAccentColor(style));
return false;
}
@@ -312,10 +318,14 @@ bool ThemePainterDefault::PaintMenuList(const Element& element,
SetupMenuListArrow(document, style, rect, extra_params);
cc::PaintCanvas* canvas = i.context.Canvas();
+ bool enable_force_dark =
+ i.context.IsDarkModeEnabled() && !style.DisableForceDark();
+ mojom::blink::ColorScheme color_scheme = style.UsedColorScheme();
+ AdjustColorScheme(color_scheme, enable_force_dark);
+
Platform::Current()->ThemeEngine()->Paint(
canvas, WebThemeEngine::kPartMenuList, GetWebThemeState(element),
- gfx::Rect(rect), &extra_params, style.UsedColorScheme(),
- GetAccentColor(style));
+ gfx::Rect(rect), &extra_params, color_scheme, GetAccentColor(style));
return false;
}
@@ -332,10 +342,14 @@ bool ThemePainterDefault::PaintMenuListButton(const Element& element,
SetupMenuListArrow(document, style, rect, extra_params);
cc::PaintCanvas* canvas = paint_info.context.Canvas();
+ bool enable_force_dark =
+ paint_info.context.IsDarkModeEnabled() && !style.DisableForceDark();
+ mojom::blink::ColorScheme color_scheme = style.UsedColorScheme();
+ AdjustColorScheme(color_scheme, enable_force_dark);
+
Platform::Current()->ThemeEngine()->Paint(
canvas, WebThemeEngine::kPartMenuList, GetWebThemeState(element),
- gfx::Rect(rect), &extra_params, style.UsedColorScheme(),
- GetAccentColor(style));
+ gfx::Rect(rect), &extra_params, color_scheme, GetAccentColor(style));
return false;
}
@@ -353,8 +367,7 @@ 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 = (features::IsFormControlsRefreshEnabled() ? 8.0 : 6.0) *
- arrow_scale_factor;
+ float arrow_size = 8.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 =
@@ -362,6 +375,8 @@ void ThemePainterDefault::SetupMenuListArrow(
? left + (arrow_box_width - arrow_size) / 2
: right - (arrow_box_width + arrow_size) / 2;
extra_params.menu_list.arrow_size = arrow_size;
+ // TODO: (https://crbug.com/1227305)This color still does not support forced
+ // dark mode
extra_params.menu_list.arrow_color =
style.VisitedDependentColor(GetCSSPropertyColor()).Rgb();
}
@@ -383,14 +398,6 @@ bool ThemePainterDefault::PaintSliderTrack(const Element& element,
extra_params.slider.zoom = zoom_level;
GraphicsContextStateSaver state_saver(i.context, false);
IntRect unzoomed_rect = rect;
- 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);
- i.context.Translate(unzoomed_rect.X(), unzoomed_rect.Y());
- i.context.Scale(zoom_level, zoom_level);
- i.context.Translate(-unzoomed_rect.X(), -unzoomed_rect.Y());
- }
auto* input = DynamicTo<HTMLInputElement>(element);
extra_params.slider.thumb_x = 0;
@@ -405,29 +412,22 @@ bool ThemePainterDefault::PaintSliderTrack(const Element& element,
LayoutBox* input_box = input->GetLayoutBox();
if (thumb) {
IntRect thumb_rect = PixelSnappedIntRect(thumb->FrameRect());
- if (features::IsFormControlsRefreshEnabled()) {
- extra_params.slider.thumb_x = thumb_rect.X() +
- input_box->PaddingLeft().ToInt() +
- input_box->BorderLeft().ToInt();
- extra_params.slider.thumb_y = thumb_rect.Y() +
- input_box->PaddingTop().ToInt() +
- input_box->BorderTop().ToInt();
- } else {
- extra_params.slider.thumb_x =
- (thumb_rect.X() + input_box->PaddingLeft().ToInt() +
- input_box->BorderLeft().ToInt()) /
- zoom_level;
- extra_params.slider.thumb_y =
- (thumb_rect.Y() + input_box->PaddingTop().ToInt() +
- input_box->BorderTop().ToInt()) /
- zoom_level;
- }
+ extra_params.slider.thumb_x = thumb_rect.X() +
+ input_box->PaddingLeft().ToInt() +
+ input_box->BorderLeft().ToInt();
+ extra_params.slider.thumb_y = thumb_rect.Y() +
+ input_box->PaddingTop().ToInt() +
+ input_box->BorderTop().ToInt();
}
}
+ bool enable_force_dark =
+ i.context.IsDarkModeEnabled() && !style.DisableForceDark();
+ mojom::blink::ColorScheme color_scheme = o.StyleRef().UsedColorScheme();
+ AdjustColorScheme(color_scheme, enable_force_dark);
Platform::Current()->ThemeEngine()->Paint(
canvas, WebThemeEngine::kPartSliderTrack, GetWebThemeState(element),
- gfx::Rect(unzoomed_rect), &extra_params, o.StyleRef().UsedColorScheme(),
+ gfx::Rect(unzoomed_rect), &extra_params, color_scheme,
GetAccentColor(style));
return false;
}
@@ -446,14 +446,6 @@ bool ThemePainterDefault::PaintSliderThumb(const Element& element,
extra_params.slider.zoom = zoom_level;
GraphicsContextStateSaver state_saver(paint_info.context, false);
IntRect unzoomed_rect = rect;
- 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);
- paint_info.context.Translate(unzoomed_rect.X(), unzoomed_rect.Y());
- paint_info.context.Scale(zoom_level, zoom_level);
- paint_info.context.Translate(-unzoomed_rect.X(), -unzoomed_rect.Y());
- }
// The element passed in is inside the user agent shadowdom of the input
// element, so we have to access the parent input element in order to get the
@@ -464,11 +456,14 @@ bool ThemePainterDefault::PaintSliderThumb(const Element& element,
// SliderThumbElement
absl::optional<SkColor> accent_color =
GetAccentColor(*slider_element->HostInput()->EnsureComputedStyle());
+ bool enable_force_dark =
+ paint_info.context.IsDarkModeEnabled() && !style.DisableForceDark();
+ mojom::blink::ColorScheme color_scheme = style.UsedColorScheme();
+ AdjustColorScheme(color_scheme, enable_force_dark);
Platform::Current()->ThemeEngine()->Paint(
canvas, WebThemeEngine::kPartSliderThumb, GetWebThemeState(element),
- gfx::Rect(unzoomed_rect), &extra_params, style.UsedColorScheme(),
- accent_color);
+ gfx::Rect(unzoomed_rect), &extra_params, color_scheme, accent_color);
return false;
}
@@ -491,11 +486,14 @@ bool ThemePainterDefault::PaintInnerSpinButton(const Element& element,
extra_params.inner_spin.spin_up = spin_up;
extra_params.inner_spin.read_only = read_only;
+ bool enable_force_dark =
+ paint_info.context.IsDarkModeEnabled() && !style.DisableForceDark();
+ mojom::blink::ColorScheme color_scheme = style.UsedColorScheme();
+ AdjustColorScheme(color_scheme, enable_force_dark);
Platform::Current()->ThemeEngine()->Paint(
canvas, WebThemeEngine::kPartInnerSpinButton, GetWebThemeState(element),
- gfx::Rect(rect), &extra_params, style.UsedColorScheme(),
- GetAccentColor(style));
+ gfx::Rect(rect), &extra_params, color_scheme, GetAccentColor(style));
return false;
}
@@ -520,10 +518,14 @@ bool ThemePainterDefault::PaintProgressBar(const Element& element,
DirectionFlippingScope scope(o, i, rect);
cc::PaintCanvas* canvas = i.context.Canvas();
+ bool enable_force_dark =
+ i.context.IsDarkModeEnabled() && !style.DisableForceDark();
+ mojom::blink::ColorScheme color_scheme = o.StyleRef().UsedColorScheme();
+ AdjustColorScheme(color_scheme, enable_force_dark);
+
Platform::Current()->ThemeEngine()->Paint(
canvas, WebThemeEngine::kPartProgressBar, GetWebThemeState(element),
- gfx::Rect(rect), &extra_params, o.StyleRef().UsedColorScheme(),
- GetAccentColor(style));
+ gfx::Rect(rect), &extra_params, color_scheme, GetAccentColor(style));
return false;
}
@@ -641,4 +643,11 @@ IntRect ThemePainterDefault::ApplyZoomToRect(
return unzoomed_rect;
}
+void ThemePainterDefault::AdjustColorScheme(mojom::ColorScheme& color_scheme,
+ bool enable_force_dark) {
+ if (color_scheme == mojom::blink::ColorScheme::kLight && enable_force_dark) {
+ color_scheme = mojom::blink::ColorScheme::kDark;
+ }
+}
+
} // namespace blink
diff --git a/chromium/third_party/blink/renderer/core/paint/theme_painter_default.h b/chromium/third_party/blink/renderer/core/paint/theme_painter_default.h
index 965c7a92b87..377fd89e263 100644
--- a/chromium/third_party/blink/renderer/core/paint/theme_painter_default.h
+++ b/chromium/third_party/blink/renderer/core/paint/theme_painter_default.h
@@ -111,6 +111,8 @@ class ThemePainterDefault final : public ThemePainter {
GraphicsContextStateSaver&,
float zoom_level);
+ void AdjustColorScheme(mojom::ColorScheme&, bool);
+
// ThemePaintDefault is a part object of m_theme.
LayoutThemeDefault& theme_;
};