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