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