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/replaced_painter.h"
6
7 #include "base/optional.h"
8 #include "third_party/blink/renderer/core/layout/layout_replaced.h"
9 #include "third_party/blink/renderer/core/layout/svg/layout_svg_root.h"
10 #include "third_party/blink/renderer/core/paint/box_painter.h"
11 #include "third_party/blink/renderer/core/paint/compositing/composited_layer_mapping.h"
12 #include "third_party/blink/renderer/core/paint/highlight_painting_utils.h"
13 #include "third_party/blink/renderer/core/paint/object_painter.h"
14 #include "third_party/blink/renderer/core/paint/paint_info.h"
15 #include "third_party/blink/renderer/core/paint/paint_layer.h"
16 #include "third_party/blink/renderer/core/paint/paint_layer_scrollable_area.h"
17 #include "third_party/blink/renderer/core/paint/scoped_paint_state.h"
18 #include "third_party/blink/renderer/core/paint/scrollable_area_painter.h"
19 #include "third_party/blink/renderer/platform/graphics/paint/display_item_cache_skipper.h"
20 #include "third_party/blink/renderer/platform/graphics/paint/drawing_recorder.h"
21 #include "third_party/blink/renderer/platform/graphics/paint/scoped_paint_chunk_properties.h"
22
23 namespace blink {
24
25 namespace {
26
27 // Adjusts cull rect and paint chunk properties of the input ScopedPaintState
28 // for ReplacedContentTransform if needed.
29 class ScopedReplacedContentPaintState : public ScopedPaintState {
30 public:
31 ScopedReplacedContentPaintState(const ScopedPaintState& input,
32 const LayoutReplaced& replaced);
33 };
34
ScopedReplacedContentPaintState(const ScopedPaintState & input,const LayoutReplaced & replaced)35 ScopedReplacedContentPaintState::ScopedReplacedContentPaintState(
36 const ScopedPaintState& input,
37 const LayoutReplaced& replaced)
38 : ScopedPaintState(input) {
39 if (!fragment_to_paint_)
40 return;
41
42 const auto* paint_properties = fragment_to_paint_->PaintProperties();
43 if (!paint_properties)
44 return;
45
46 auto new_properties = input_paint_info_.context.GetPaintController()
47 .CurrentPaintChunkProperties();
48 bool property_changed = false;
49
50 const auto* content_transform = paint_properties->ReplacedContentTransform();
51 if (content_transform && replaced.IsSVGRoot()) {
52 new_properties.SetTransform(*content_transform);
53 adjusted_paint_info_.emplace(input_paint_info_);
54 adjusted_paint_info_->TransformCullRect(*content_transform);
55 property_changed = true;
56 }
57
58 if (const auto* clip = paint_properties->OverflowClip()) {
59 new_properties.SetClip(*clip);
60 property_changed = true;
61 }
62
63 if (property_changed) {
64 chunk_properties_.emplace(input_paint_info_.context.GetPaintController(),
65 new_properties, replaced,
66 input_paint_info_.DisplayItemTypeForClipping());
67 }
68 }
69
70 } // anonymous namespace
71
ShouldPaintBoxDecorationBackground(const PaintInfo & paint_info)72 bool ReplacedPainter::ShouldPaintBoxDecorationBackground(
73 const PaintInfo& paint_info) {
74 // LayoutFrameSet paints everything in the foreground phase.
75 if (layout_replaced_.IsLayoutEmbeddedContent() &&
76 layout_replaced_.Parent()->IsFrameSet())
77 return paint_info.phase == PaintPhase::kForeground;
78 return ShouldPaintSelfBlockBackground(paint_info.phase);
79 }
80
Paint(const PaintInfo & paint_info)81 void ReplacedPainter::Paint(const PaintInfo& paint_info) {
82 // TODO(crbug.com/797779): For now embedded contents don't know whether
83 // they are painted in a fragmented context and may do something bad in a
84 // fragmented context, e.g. creating subsequences. Skip cache to avoid that.
85 // This will be unnecessary when the contents are fragment aware.
86 base::Optional<DisplayItemCacheSkipper> cache_skipper;
87 if (layout_replaced_.IsLayoutEmbeddedContent()) {
88 DCHECK(layout_replaced_.HasLayer());
89 if (layout_replaced_.Layer()->EnclosingPaginationLayer())
90 cache_skipper.emplace(paint_info.context);
91 }
92
93 ScopedPaintState paint_state(layout_replaced_, paint_info);
94 if (!ShouldPaint(paint_state))
95 return;
96
97 const auto& local_paint_info = paint_state.GetPaintInfo();
98 auto paint_offset = paint_state.PaintOffset();
99 PhysicalRect border_rect(paint_offset, layout_replaced_.Size());
100
101 if (ShouldPaintBoxDecorationBackground(local_paint_info)) {
102 bool should_paint_background = false;
103 if (layout_replaced_.StyleRef().Visibility() == EVisibility::kVisible) {
104 if (layout_replaced_.HasBoxDecorationBackground())
105 should_paint_background = true;
106 if (layout_replaced_.HasEffectiveAllowedTouchAction() ||
107 layout_replaced_.InsideBlockingWheelEventHandler()) {
108 should_paint_background = true;
109 }
110 }
111 if (should_paint_background) {
112 if (layout_replaced_.HasLayer() &&
113 layout_replaced_.Layer()->GetCompositingState() ==
114 kPaintsIntoOwnBacking &&
115 layout_replaced_.Layer()
116 ->GetCompositedLayerMapping()
117 ->DrawsBackgroundOntoContentLayer()) {
118 // If the background paints into the content layer, we can skip painting
119 // the background but still need to paint the hit test rects.
120 BoxPainter(layout_replaced_)
121 .RecordHitTestData(local_paint_info, border_rect, layout_replaced_);
122 return;
123 }
124
125 BoxPainter(layout_replaced_)
126 .PaintBoxDecorationBackground(local_paint_info, paint_offset);
127 }
128 // We're done. We don't bother painting any children.
129 if (local_paint_info.phase == PaintPhase::kSelfBlockBackgroundOnly)
130 return;
131 }
132
133 if (local_paint_info.phase == PaintPhase::kMask) {
134 BoxPainter(layout_replaced_).PaintMask(local_paint_info, paint_offset);
135 return;
136 }
137
138 if (ShouldPaintSelfOutline(local_paint_info.phase)) {
139 ObjectPainter(layout_replaced_)
140 .PaintOutline(local_paint_info, paint_offset);
141 return;
142 }
143
144 if (local_paint_info.phase != PaintPhase::kForeground &&
145 local_paint_info.phase != PaintPhase::kSelectionDragImage &&
146 !layout_replaced_.CanHaveChildren())
147 return;
148
149 if (local_paint_info.phase == PaintPhase::kSelectionDragImage &&
150 !layout_replaced_.IsSelected())
151 return;
152
153 bool has_clip =
154 layout_replaced_.FirstFragment().PaintProperties() &&
155 layout_replaced_.FirstFragment().PaintProperties()->OverflowClip();
156 if (!has_clip || !layout_replaced_.PhysicalContentBoxRect().IsEmpty()) {
157 ScopedReplacedContentPaintState content_paint_state(paint_state,
158 layout_replaced_);
159 layout_replaced_.PaintReplaced(content_paint_state.GetPaintInfo(),
160 content_paint_state.PaintOffset());
161 }
162
163 if (layout_replaced_.CanResize()) {
164 auto* scrollable_area = layout_replaced_.GetScrollableArea();
165 DCHECK(scrollable_area);
166 if (!scrollable_area->HasLayerForScrollCorner()) {
167 ScrollableAreaPainter(*scrollable_area)
168 .PaintResizer(local_paint_info.context, RoundedIntPoint(paint_offset),
169 local_paint_info.GetCullRect());
170 }
171 // Otherwise the resizer will be painted by the scroll corner layer.
172 }
173
174 // The selection tint never gets clipped by border-radius rounding, since we
175 // want it to run right up to the edges of surrounding content.
176 bool draw_selection_tint =
177 local_paint_info.phase == PaintPhase::kForeground &&
178 layout_replaced_.IsSelected() && layout_replaced_.CanBeSelectionLeaf() &&
179 !local_paint_info.IsPrinting();
180 if (draw_selection_tint && !DrawingRecorder::UseCachedDrawingIfPossible(
181 local_paint_info.context, layout_replaced_,
182 DisplayItem::kSelectionTint)) {
183 PhysicalRect selection_painting_rect =
184 layout_replaced_.LocalSelectionVisualRect();
185 selection_painting_rect.Move(paint_offset);
186 IntRect selection_painting_int_rect =
187 PixelSnappedIntRect(selection_painting_rect);
188
189 DrawingRecorder recorder(local_paint_info.context, layout_replaced_,
190 DisplayItem::kSelectionTint,
191 selection_painting_int_rect);
192 Color selection_bg = HighlightPaintingUtils::HighlightBackgroundColor(
193 layout_replaced_.GetDocument(), layout_replaced_.StyleRef(),
194 layout_replaced_.GetNode(), kPseudoIdSelection);
195 local_paint_info.context.FillRect(selection_painting_int_rect,
196 selection_bg);
197 }
198 }
199
ShouldPaint(const ScopedPaintState & paint_state) const200 bool ReplacedPainter::ShouldPaint(const ScopedPaintState& paint_state) const {
201 const auto& paint_info = paint_state.GetPaintInfo();
202 if (paint_info.phase != PaintPhase::kForeground &&
203 paint_info.phase != PaintPhase::kForcedColorsModeBackplate &&
204 !ShouldPaintSelfOutline(paint_info.phase) &&
205 paint_info.phase != PaintPhase::kSelectionDragImage &&
206 paint_info.phase != PaintPhase::kMask &&
207 !ShouldPaintSelfBlockBackground(paint_info.phase))
208 return false;
209
210 if (layout_replaced_.IsTruncated())
211 return false;
212
213 // If we're invisible or haven't received a layout yet, just bail.
214 // But if it's an SVG root, there can be children, so we'll check visibility
215 // later.
216 if (!layout_replaced_.IsSVGRoot() &&
217 layout_replaced_.StyleRef().Visibility() != EVisibility::kVisible)
218 return false;
219
220 PhysicalRect local_rect = layout_replaced_.PhysicalVisualOverflowRect();
221 local_rect.Unite(layout_replaced_.LocalSelectionVisualRect());
222 if (!paint_state.LocalRectIntersectsCullRect(local_rect))
223 return false;
224
225 return true;
226 }
227
228 } // namespace blink
229