1 // Copyright 2014 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "third_party/blink/renderer/core/paint/block_painter.h"
6
7 #include "base/optional.h"
8 #include "third_party/blink/renderer/core/display_lock/display_lock_context.h"
9 #include "third_party/blink/renderer/core/editing/drag_caret.h"
10 #include "third_party/blink/renderer/core/editing/frame_selection.h"
11 #include "third_party/blink/renderer/core/layout/api/line_layout_api_shim.h"
12 #include "third_party/blink/renderer/core/layout/api/line_layout_box.h"
13 #include "third_party/blink/renderer/core/layout/layout_inline.h"
14 #include "third_party/blink/renderer/core/layout/ng/layout_ng_block_flow.h"
15 #include "third_party/blink/renderer/core/page/page.h"
16 #include "third_party/blink/renderer/core/paint/line_box_list_painter.h"
17 #include "third_party/blink/renderer/core/paint/object_painter.h"
18 #include "third_party/blink/renderer/core/paint/paint_info.h"
19 #include "third_party/blink/renderer/core/paint/paint_timing_detector.h"
20 #include "third_party/blink/renderer/core/paint/scoped_paint_state.h"
21 #include "third_party/blink/renderer/core/paint/scrollable_area_painter.h"
22
23 namespace blink {
24
25 DISABLE_CFI_PERF
Paint(const PaintInfo & paint_info)26 void BlockPainter::Paint(const PaintInfo& paint_info) {
27 ScopedPaintState paint_state(layout_block_, paint_info);
28 if (!ShouldPaint(paint_state))
29 return;
30
31 DCHECK(!layout_block_.ChildPaintBlockedByDisplayLock() ||
32 paint_info.DescendantPaintingBlocked());
33
34 auto paint_offset = paint_state.PaintOffset();
35 auto& local_paint_info = paint_state.MutablePaintInfo();
36 PaintPhase original_phase = local_paint_info.phase;
37
38 if (original_phase == PaintPhase::kOutline) {
39 local_paint_info.phase = PaintPhase::kDescendantOutlinesOnly;
40 } else if (ShouldPaintSelfBlockBackground(original_phase)) {
41 local_paint_info.phase = PaintPhase::kSelfBlockBackgroundOnly;
42 // With CompositeAfterPaint we need to call PaintObject twice: once for the
43 // background painting that does not scroll, and a second time for the
44 // background painting that scrolls.
45 // Without CompositeAfterPaint, this happens as the main graphics layer
46 // paints the background, and then the scrolling contents graphics layer
47 // paints the background.
48 if (RuntimeEnabledFeatures::CompositeAfterPaintEnabled()) {
49 auto paint_location = layout_block_.GetBackgroundPaintLocation();
50 if (!(paint_location & kBackgroundPaintInGraphicsLayer))
51 local_paint_info.SetSkipsBackground(true);
52 layout_block_.PaintObject(local_paint_info, paint_offset);
53 local_paint_info.SetSkipsBackground(false);
54
55 if (paint_location & kBackgroundPaintInScrollingContents) {
56 local_paint_info.SetIsPaintingScrollingBackground(true);
57 layout_block_.PaintObject(local_paint_info, paint_offset);
58 local_paint_info.SetIsPaintingScrollingBackground(false);
59 }
60 } else {
61 layout_block_.PaintObject(local_paint_info, paint_offset);
62 }
63 if (ShouldPaintDescendantBlockBackgrounds(original_phase))
64 local_paint_info.phase = PaintPhase::kDescendantBlockBackgroundsOnly;
65 }
66
67 if (original_phase == PaintPhase::kMask) {
68 layout_block_.PaintObject(local_paint_info, paint_offset);
69 } else if (original_phase != PaintPhase::kSelfBlockBackgroundOnly &&
70 original_phase != PaintPhase::kSelfOutlineOnly &&
71 // For now all scrollers with overlay overflow controls are
72 // self-painting layers, so we don't need to traverse descendants
73 // here.
74 original_phase != PaintPhase::kOverlayOverflowControls) {
75 ScopedBoxContentsPaintState contents_paint_state(paint_state,
76 layout_block_);
77 layout_block_.PaintObject(contents_paint_state.GetPaintInfo(),
78 contents_paint_state.PaintOffset());
79 }
80
81 // Carets are painted in the foreground phase, outside of the contents
82 // properties block. Note that caret painting does not seem to correspond to
83 // any painting order steps within the CSS spec.
84 if (original_phase == PaintPhase::kForeground &&
85 layout_block_.ShouldPaintCarets()) {
86 // Apply overflow clip if needed. TODO(wangxianzhu): Move PaintCarets()
87 // under |contents_paint_state| in the above block and let the caret
88 // painters paint in the space of scrolling contents.
89 base::Optional<ScopedPaintChunkProperties> paint_chunk_properties;
90 if (const auto* fragment = paint_state.FragmentToPaint()) {
91 if (const auto* properties = fragment->PaintProperties()) {
92 if (const auto* overflow_clip = properties->OverflowClip()) {
93 paint_chunk_properties.emplace(
94 paint_info.context.GetPaintController(), *overflow_clip,
95 layout_block_, DisplayItem::kCaret);
96 }
97 }
98 }
99
100 PaintCarets(paint_info, paint_offset);
101 }
102
103 if (ShouldPaintSelfOutline(original_phase)) {
104 local_paint_info.phase = PaintPhase::kSelfOutlineOnly;
105 layout_block_.PaintObject(local_paint_info, paint_offset);
106 }
107
108 // We paint scrollbars after we painted the other things, so that the
109 // scrollbars will sit above them.
110 local_paint_info.phase = original_phase;
111 if (auto* scrollable_area = layout_block_.GetScrollableArea()) {
112 ScrollableAreaPainter(*scrollable_area)
113 .PaintOverflowControls(local_paint_info, RoundedIntPoint(paint_offset));
114 }
115 }
116
PaintChildren(const PaintInfo & paint_info)117 void BlockPainter::PaintChildren(const PaintInfo& paint_info) {
118 if (paint_info.DescendantPaintingBlocked())
119 return;
120
121 // We may use legacy paint to paint the anonymous fieldset child. The layout
122 // object for the rendered legend will be a child of that one, and has to be
123 // skipped here, since it's handled by a special NG fieldset painter.
124 bool may_contain_rendered_legend =
125 layout_block_.IsAnonymousNGFieldsetContentWrapper();
126 for (LayoutBox* child = layout_block_.FirstChildBox(); child;
127 child = child->NextSiblingBox()) {
128 if (may_contain_rendered_legend && child->IsRenderedLegend()) {
129 may_contain_rendered_legend = false;
130 continue;
131 }
132 PaintChild(*child, paint_info);
133 }
134 }
135
PaintChild(const LayoutBox & child,const PaintInfo & paint_info)136 void BlockPainter::PaintChild(const LayoutBox& child,
137 const PaintInfo& paint_info) {
138 if (child.HasSelfPaintingLayer() || child.IsColumnSpanAll())
139 return;
140 if (!child.IsFloating()) {
141 child.Paint(paint_info);
142 return;
143 }
144 // Paint the float now if we're in the right phase and if this is NG. NG
145 // paints floats in regular tree order (the FloatingObjects list is only used
146 // by legacy layout).
147 if (paint_info.phase != PaintPhase::kFloat &&
148 paint_info.phase != PaintPhase::kSelectionDragImage &&
149 paint_info.phase != PaintPhase::kTextClip)
150 return;
151
152 if (!layout_block_.IsLayoutNGObject())
153 return;
154
155 PaintInfo float_paint_info(paint_info);
156 if (paint_info.phase == PaintPhase::kFloat)
157 float_paint_info.phase = PaintPhase::kForeground;
158
159 ObjectPainter(child).PaintAllPhasesAtomically(float_paint_info);
160 }
161
PaintChildrenAtomically(const OrderIterator & order_iterator,const PaintInfo & paint_info)162 void BlockPainter::PaintChildrenAtomically(const OrderIterator& order_iterator,
163 const PaintInfo& paint_info) {
164 if (paint_info.DescendantPaintingBlocked())
165 return;
166 for (const LayoutBox* child = order_iterator.First(); child;
167 child = order_iterator.Next()) {
168 PaintAllChildPhasesAtomically(*child, paint_info);
169 }
170 }
171
PaintAllChildPhasesAtomically(const LayoutBox & child,const PaintInfo & paint_info)172 void BlockPainter::PaintAllChildPhasesAtomically(const LayoutBox& child,
173 const PaintInfo& paint_info) {
174 if (paint_info.DescendantPaintingBlocked())
175 return;
176 if (!child.HasSelfPaintingLayer() && !child.IsFloating())
177 ObjectPainter(child).PaintAllPhasesAtomically(paint_info);
178 }
179
PaintInlineBox(const InlineBox & inline_box,const PaintInfo & paint_info)180 void BlockPainter::PaintInlineBox(const InlineBox& inline_box,
181 const PaintInfo& paint_info) {
182 if (paint_info.phase != PaintPhase::kForeground &&
183 paint_info.phase != PaintPhase::kForcedColorsModeBackplate &&
184 paint_info.phase != PaintPhase::kSelectionDragImage)
185 return;
186
187 // Text clips are painted only for the direct inline children of the object
188 // that has a text clip style on it, not block children.
189 DCHECK(paint_info.phase != PaintPhase::kTextClip);
190
191 ObjectPainter(
192 *LineLayoutAPIShim::ConstLayoutObjectFrom(inline_box.GetLineLayoutItem()))
193 .PaintAllPhasesAtomically(paint_info);
194 }
195
196 DISABLE_CFI_PERF
PaintObject(const PaintInfo & paint_info,const PhysicalOffset & paint_offset)197 void BlockPainter::PaintObject(const PaintInfo& paint_info,
198 const PhysicalOffset& paint_offset) {
199 const PaintPhase paint_phase = paint_info.phase;
200 // This function implements some of the painting order algorithm (described
201 // within the description of stacking context, here
202 // https://www.w3.org/TR/css-position-3/#det-stacking-context). References are
203 // made below to the step numbers described in that document.
204
205 // If this block has been truncated, early-out here, because it will not be
206 // displayed. A truncated block occurs when text-overflow: ellipsis is set on
207 // a block, and there is not enough room to display all elements. The elements
208 // that don't get shown are "Truncated".
209 if (layout_block_.IsTruncated())
210 return;
211
212 ScopedPaintTimingDetectorBlockPaintHook
213 scoped_paint_timing_detector_block_paint_hook;
214 if (paint_info.phase == PaintPhase::kForeground) {
215 scoped_paint_timing_detector_block_paint_hook.EmplaceIfNeeded(
216 layout_block_,
217 paint_info.context.GetPaintController().CurrentPaintChunkProperties());
218 }
219 // If we're *printing or creating a paint preview of* the foreground, paint
220 // the URL.
221 if (paint_phase == PaintPhase::kForeground &&
222 paint_info.ShouldAddUrlMetadata()) {
223 ObjectPainter(layout_block_).AddURLRectIfNeeded(paint_info, paint_offset);
224 }
225
226 // If we're painting our background (either 1. kBlockBackground - background
227 // of the current object and non-self-painting descendants, or 2.
228 // kSelfBlockBackgroundOnly - Paint background of the current object only),
229 // paint those now. This is steps #1, 2, and 4 of the CSS spec (see above).
230 if (ShouldPaintSelfBlockBackground(paint_phase))
231 layout_block_.PaintBoxDecorationBackground(paint_info, paint_offset);
232
233 // Draw a backplate behind all text if in forced colors mode.
234 if (paint_phase == PaintPhase::kForcedColorsModeBackplate &&
235 layout_block_.GetFrame()->GetDocument()->InForcedColorsMode() &&
236 layout_block_.ChildrenInline()) {
237 LineBoxListPainter(To<LayoutBlockFlow>(layout_block_).LineBoxes())
238 .PaintBackplate(layout_block_, paint_info, paint_offset);
239 }
240
241 // If we're in any phase except *just* the self (outline or background) or a
242 // mask, paint children now. This is step #5, 7, 8, and 9 of the CSS spec (see
243 // above).
244 if (paint_phase != PaintPhase::kSelfOutlineOnly &&
245 paint_phase != PaintPhase::kSelfBlockBackgroundOnly &&
246 paint_phase != PaintPhase::kMask &&
247 !paint_info.DescendantPaintingBlocked()) {
248 // Actually paint the contents.
249 if (layout_block_.IsLayoutBlockFlow()) {
250 // All floating descendants will be LayoutBlockFlow objects, and will get
251 // painted here. That is step #5 of the CSS spec (see above).
252 PaintBlockFlowContents(paint_info, paint_offset);
253 } else {
254 PaintContents(paint_info, paint_offset);
255 }
256 }
257
258 // If we're painting the outline, paint it now. This is step #10 of the CSS
259 // spec (see above).
260 if (ShouldPaintSelfOutline(paint_phase))
261 ObjectPainter(layout_block_).PaintOutline(paint_info, paint_offset);
262
263 // If we're painting a visible mask, paint it now. (This does not correspond
264 // to any painting order steps within the CSS spec.)
265 if (paint_phase == PaintPhase::kMask &&
266 layout_block_.StyleRef().Visibility() == EVisibility::kVisible) {
267 layout_block_.PaintMask(paint_info, paint_offset);
268 }
269 }
270
PaintBlockFlowContents(const PaintInfo & paint_info,const PhysicalOffset & paint_offset)271 void BlockPainter::PaintBlockFlowContents(const PaintInfo& paint_info,
272 const PhysicalOffset& paint_offset) {
273 DCHECK(layout_block_.IsLayoutBlockFlow());
274 if (!layout_block_.ChildrenInline()) {
275 PaintContents(paint_info, paint_offset);
276 } else if (ShouldPaintDescendantOutlines(paint_info.phase)) {
277 ObjectPainter(layout_block_).PaintInlineChildrenOutlines(paint_info);
278 } else {
279 LineBoxListPainter(To<LayoutBlockFlow>(layout_block_).LineBoxes())
280 .Paint(layout_block_, paint_info, paint_offset);
281 }
282
283 // If we don't have any floats to paint, or we're in the wrong paint phase,
284 // then we're done for now.
285 auto* floating_objects =
286 To<LayoutBlockFlow>(layout_block_).GetFloatingObjects();
287 const PaintPhase paint_phase = paint_info.phase;
288 if (!floating_objects || !(paint_phase == PaintPhase::kFloat ||
289 paint_phase == PaintPhase::kSelectionDragImage ||
290 paint_phase == PaintPhase::kTextClip)) {
291 return;
292 }
293
294 // LayoutNG paints floats in regular tree order, and doesn't use the
295 // FloatingObjects list.
296 if (layout_block_.IsLayoutNGObject())
297 return;
298
299 // If we're painting floats (not selections or textclips), change
300 // the paint phase to foreground.
301 PaintInfo float_paint_info(paint_info);
302 if (paint_info.phase == PaintPhase::kFloat)
303 float_paint_info.phase = PaintPhase::kForeground;
304
305 // Paint all floats.
306 for (const auto& floating_object : floating_objects->Set()) {
307 if (!floating_object->ShouldPaint())
308 continue;
309 const LayoutBox* floating_layout_object =
310 floating_object->GetLayoutObject();
311 // TODO(wangxianzhu): Should this be a DCHECK?
312 if (floating_layout_object->HasSelfPaintingLayer())
313 continue;
314 ObjectPainter(*floating_layout_object)
315 .PaintAllPhasesAtomically(float_paint_info);
316 }
317 }
318
PaintCarets(const PaintInfo & paint_info,const PhysicalOffset & paint_offset)319 void BlockPainter::PaintCarets(const PaintInfo& paint_info,
320 const PhysicalOffset& paint_offset) {
321 LocalFrame* frame = layout_block_.GetFrame();
322
323 if (layout_block_.ShouldPaintCursorCaret())
324 frame->Selection().PaintCaret(paint_info.context, paint_offset);
325
326 if (layout_block_.ShouldPaintDragCaret()) {
327 frame->GetPage()->GetDragCaret().PaintDragCaret(frame, paint_info.context,
328 paint_offset);
329 }
330 }
331
OverflowRectForCullRectTesting(bool is_printing) const332 PhysicalRect BlockPainter::OverflowRectForCullRectTesting(
333 bool is_printing) const {
334 PhysicalRect overflow_rect;
335 if (is_printing && layout_block_.IsAnonymousBlock() &&
336 layout_block_.ChildrenInline()) {
337 // For case <a href="..."><div>...</div></a>, when layout_block_ is the
338 // anonymous container of <a>, the anonymous container's visual overflow is
339 // empty, but we need to continue painting to output <a>'s PDF URL rect
340 // which covers the continuations, as if we included <a>'s PDF URL rect into
341 // layout_block_'s visual overflow.
342 auto rects = layout_block_.OutlineRects(
343 PhysicalOffset(), NGOutlineType::kIncludeBlockVisualOverflow);
344 overflow_rect = UnionRect(rects);
345 }
346 overflow_rect.Unite(layout_block_.PhysicalVisualOverflowRect());
347
348 bool include_layout_overflow =
349 layout_block_.ScrollsOverflow() &&
350 (layout_block_.UsesCompositedScrolling() ||
351 RuntimeEnabledFeatures::CompositeAfterPaintEnabled());
352
353 if (include_layout_overflow) {
354 overflow_rect.Unite(layout_block_.PhysicalLayoutOverflowRect());
355 overflow_rect.Move(
356 -PhysicalOffset(layout_block_.PixelSnappedScrolledContentOffset()));
357 }
358 return overflow_rect;
359 }
360
361 DISABLE_CFI_PERF
ShouldPaint(const ScopedPaintState & paint_state) const362 bool BlockPainter::ShouldPaint(const ScopedPaintState& paint_state) const {
363 // If there is no fragment to paint for this block, we still need to continue
364 // the paint tree walk in case there are overflowing children that exist in
365 // the current painting fragment of the painting layer. In the case we can't
366 // check the overflow rect against the cull rect in the case because we don't
367 // know the paint offset.
368 if (!paint_state.FragmentToPaint())
369 return true;
370
371 return paint_state.LocalRectIntersectsCullRect(
372 OverflowRectForCullRectTesting(paint_state.GetPaintInfo().IsPrinting()));
373 }
374
PaintContents(const PaintInfo & paint_info,const PhysicalOffset & paint_offset)375 void BlockPainter::PaintContents(const PaintInfo& paint_info,
376 const PhysicalOffset& paint_offset) {
377 DCHECK(!layout_block_.ChildrenInline());
378 PaintInfo paint_info_for_descendants = paint_info.ForDescendants();
379 layout_block_.PaintChildren(paint_info_for_descendants, paint_offset);
380 }
381
382 } // namespace blink
383