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/compositing/compositing_inputs_updater.h"
6
7 #include "third_party/blink/renderer/core/display_lock/display_lock_utilities.h"
8 #include "third_party/blink/renderer/core/dom/document.h"
9 #include "third_party/blink/renderer/core/frame/local_frame_view.h"
10 #include "third_party/blink/renderer/core/layout/layout_block.h"
11 #include "third_party/blink/renderer/core/layout/layout_view.h"
12 #include "third_party/blink/renderer/core/paint/compositing/composited_layer_mapping.h"
13 #include "third_party/blink/renderer/core/paint/compositing/paint_layer_compositor.h"
14 #include "third_party/blink/renderer/core/paint/paint_layer.h"
15 #include "third_party/blink/renderer/core/paint/paint_layer_scrollable_area.h"
16 #include "third_party/blink/renderer/platform/instrumentation/tracing/trace_event.h"
17 #include "third_party/blink/renderer/platform/instrumentation/use_counter.h"
18
19 namespace blink {
ClippingContainerFromClipChainParent(const PaintLayer * clip_chain_parent)20 static const LayoutBoxModelObject* ClippingContainerFromClipChainParent(
21 const PaintLayer* clip_chain_parent) {
22 return clip_chain_parent->GetLayoutObject().HasClipRelatedProperty()
23 ? &clip_chain_parent->GetLayoutObject()
24 : clip_chain_parent->ClippingContainer();
25 }
26
CompositingInputsUpdater(PaintLayer * root_layer,PaintLayer * compositing_inputs_root)27 CompositingInputsUpdater::CompositingInputsUpdater(
28 PaintLayer* root_layer,
29 PaintLayer* compositing_inputs_root)
30 : root_layer_(root_layer),
31 compositing_inputs_root_(compositing_inputs_root) {}
32
33 CompositingInputsUpdater::~CompositingInputsUpdater() = default;
34
LayerOrDescendantShouldBeComposited(PaintLayer * layer)35 bool CompositingInputsUpdater::LayerOrDescendantShouldBeComposited(
36 PaintLayer* layer) {
37 PaintLayerCompositor* compositor =
38 layer->GetLayoutObject().View()->Compositor();
39 return layer->DescendantHasDirectOrScrollingCompositingReason() ||
40 layer->NeedsCompositedScrolling() ||
41 (compositor->CanBeComposited(layer) &&
42 layer->DirectCompositingReasons());
43 }
44
Update()45 void CompositingInputsUpdater::Update() {
46 TRACE_EVENT0("blink", "CompositingInputsUpdater::update");
47
48 AncestorInfo info;
49 UpdateType update_type = kDoNotForceUpdate;
50 PaintLayer* layer =
51 compositing_inputs_root_ ? compositing_inputs_root_ : root_layer_;
52
53 if (DisplayLockUtilities::NearestLockedExclusiveAncestor(
54 layer->GetLayoutObject())) {
55 compositing_inputs_root_ = nullptr;
56 return;
57 }
58
59 CompositingReasons initial_compositing_reasons =
60 layer->DirectCompositingReasons();
61 ApplyAncestorInfoToSelfAndAncestorsRecursively(layer, update_type, info);
62 UpdateSelfAndDescendantsRecursively(layer, update_type, info);
63
64 // The layer has changed from non-compositing to compositing
65 if (initial_compositing_reasons == CompositingReason::kNone &&
66 LayerOrDescendantShouldBeComposited(layer)) {
67 // Update all parent layers
68 PaintLayer* parent_layer = layer->Parent();
69 while (parent_layer) {
70 parent_layer->SetDescendantHasDirectOrScrollingCompositingReason(true);
71 parent_layer = parent_layer->Parent();
72 }
73 }
74 }
75
ApplyAncestorInfoToSelfAndAncestorsRecursively(PaintLayer * layer,UpdateType & update_type,AncestorInfo & info)76 void CompositingInputsUpdater::ApplyAncestorInfoToSelfAndAncestorsRecursively(
77 PaintLayer* layer,
78 UpdateType& update_type,
79 AncestorInfo& info) {
80 if (!layer)
81 return;
82
83 // We first recursively call ApplyAncestorInfoToSelfAndAncestorsRecursively()
84 // to ensure that we start to compute the geometry_map_ and AncestorInfo from
85 // the root layer (as we need to do a top-down tree walk to incrementally
86 // update this information).
87 ApplyAncestorInfoToSelfAndAncestorsRecursively(layer->Parent(), update_type,
88 info);
89 geometry_map_.PushMappingsToAncestor(layer, layer->Parent());
90 UpdateAncestorInfo(layer, update_type, info);
91 if (layer != compositing_inputs_root_ &&
92 (layer->IsRootLayer() || layer->GetLayoutObject().HasOverflowClip()))
93 info.last_overflow_clip_layer = layer;
94 }
95
UpdateSelfAndDescendantsRecursively(PaintLayer * layer,UpdateType update_type,AncestorInfo info)96 void CompositingInputsUpdater::UpdateSelfAndDescendantsRecursively(
97 PaintLayer* layer,
98 UpdateType update_type,
99 AncestorInfo info) {
100 LayoutBoxModelObject& layout_object = layer->GetLayoutObject();
101 const ComputedStyle& style = layout_object.StyleRef();
102
103 const PaintLayer* previous_overflow_layer = layer->AncestorOverflowLayer();
104 layer->UpdateAncestorOverflowLayer(info.last_overflow_clip_layer);
105 if (info.last_overflow_clip_layer && layer->NeedsCompositingInputsUpdate() &&
106 style.HasStickyConstrainedPosition()) {
107 if (info.last_overflow_clip_layer != previous_overflow_layer) {
108 // Old ancestor scroller should no longer have these constraints.
109 DCHECK(!previous_overflow_layer ||
110 !previous_overflow_layer->GetScrollableArea() ||
111 !previous_overflow_layer->GetScrollableArea()
112 ->GetStickyConstraintsMap()
113 .Contains(layer));
114
115 // If our ancestor scroller has changed and the previous one was the
116 // root layer, we are no longer viewport constrained.
117 if (previous_overflow_layer && previous_overflow_layer->IsRootLayer()) {
118 layout_object.View()->GetFrameView()->RemoveViewportConstrainedObject(
119 layout_object);
120 }
121 }
122
123 if (info.last_overflow_clip_layer->IsRootLayer()) {
124 layout_object.View()->GetFrameView()->AddViewportConstrainedObject(
125 layout_object);
126 }
127 layout_object.UpdateStickyPositionConstraints();
128
129 // Sticky position constraints and ancestor overflow scroller affect
130 // the sticky layer position, so we need to update it again here.
131 // TODO(flackr): This should be refactored in the future to be clearer
132 // (i.e. update layer position and ancestor inputs updates in the
133 // same walk)
134 layer->UpdateLayerPosition();
135 }
136
137 // geometry_map_ has been already updated in ApplyAncestorInfo() and
138 // UpdateAncestorInfo has been already computed in ApplyAncestorInfo() for
139 // layers from root_layer_ down to compositing_inputs_root_ both included.
140 if (layer != root_layer_ && layer != compositing_inputs_root_) {
141 geometry_map_.PushMappingsToAncestor(layer, layer->Parent());
142 UpdateAncestorInfo(layer, update_type, info);
143 }
144 if (layer->IsRootLayer() || layout_object.HasOverflowClip())
145 info.last_overflow_clip_layer = layer;
146
147 PaintLayerCompositor* compositor =
148 layer->GetLayoutObject().View()->Compositor();
149
150 // The sequence of updates to compositing triggers goes like this:
151 // 1. Apply all triggers from kComboAllDirectNonStyleDeterminedReasons for
152 // |layer|. This may depend on ancestor composited scrolling (i.e. step
153 // 2 for an ancestor PaintLayer).
154 // 2. Put |layer| in composited scrolling mode if needed.
155 // 3. Reset DescendantHasDirectCompositingReason to false for |layer|.
156 // 4. Recurse into child PaintLayers.
157 // 5. Set DescendantHasDirectCompositingReason to true if it was for any
158 // child.
159 // 6. If |layer| is the root, composite if
160 // DescendantHasDirectCompositingReason is true for |layer|.
161
162 layer->SetPotentialCompositingReasonsFromNonStyle(
163 CompositingReasonFinder::NonStyleDeterminedDirectReasons(*layer));
164
165 if (layer->GetScrollableArea()) {
166 layer->GetScrollableArea()->UpdateNeedsCompositedScrolling(
167 compositor->CanBeComposited(layer) &&
168 layer->DirectCompositingReasons());
169 }
170
171 // Note that prepaint may use the compositing information, so only skip
172 // recursing it if we're skipping prepaint.
173 bool recursion_blocked_by_display_lock =
174 layer->GetLayoutObject().PrePaintBlockedByDisplayLock(
175 DisplayLockLifecycleTarget::kChildren);
176
177 bool should_recurse = (layer->ChildNeedsCompositingInputsUpdate() ||
178 update_type == kForceUpdate) &&
179 !recursion_blocked_by_display_lock;
180
181 layer->SetDescendantHasDirectOrScrollingCompositingReason(false);
182 bool descendant_has_direct_compositing_reason = false;
183 for (PaintLayer* child = layer->FirstChild(); child;
184 child = child->NextSibling()) {
185 if (should_recurse)
186 UpdateSelfAndDescendantsRecursively(child, update_type, info);
187 descendant_has_direct_compositing_reason |=
188 LayerOrDescendantShouldBeComposited(child);
189 }
190 layer->SetDescendantHasDirectOrScrollingCompositingReason(
191 descendant_has_direct_compositing_reason);
192
193 if (layer->IsRootLayer() && layer->ScrollsOverflow() &&
194 layer->DescendantHasDirectOrScrollingCompositingReason() &&
195 !layer->NeedsCompositedScrolling())
196 layer->GetScrollableArea()->UpdateNeedsCompositedScrolling(true);
197
198 // If display lock blocked this recursion, then keep the dirty bit around
199 // since it is a breadcrumb that will allow us to recurse later when we unlock
200 // the element.
201 if (!recursion_blocked_by_display_lock)
202 layer->ClearChildNeedsCompositingInputsUpdate();
203
204 geometry_map_.PopMappingsToAncestor(layer->Parent());
205
206 if (layer->SelfPaintingStatusChanged()) {
207 layer->ClearSelfPaintingStatusChanged();
208 // If the floating object becomes non-self-painting, so some ancestor should
209 // paint it; if it becomes self-painting, it should paint itself and no
210 // ancestor should paint it.
211 if (layout_object.IsFloating()) {
212 LayoutBlockFlow::UpdateAncestorShouldPaintFloatingObject(
213 *layer->GetLayoutBox());
214 }
215 }
216
217 compositor->ClearCompositingInputsRoot();
218 }
219
UpdateAncestorInfo(PaintLayer * const layer,UpdateType & update_type,AncestorInfo & info)220 void CompositingInputsUpdater::UpdateAncestorInfo(PaintLayer* const layer,
221 UpdateType& update_type,
222 AncestorInfo& info) {
223 LayoutBoxModelObject& layout_object = layer->GetLayoutObject();
224 const ComputedStyle& style = layout_object.StyleRef();
225
226 PaintLayer* enclosing_stacking_composited_layer =
227 info.enclosing_stacking_composited_layer;
228 PaintLayer* enclosing_squashing_composited_layer =
229 info.enclosing_squashing_composited_layer;
230
231 if (layer->NeedsCompositingInputsUpdate()) {
232 if (enclosing_stacking_composited_layer) {
233 enclosing_stacking_composited_layer->GetCompositedLayerMapping()
234 ->SetNeedsGraphicsLayerUpdate(kGraphicsLayerUpdateSubtree);
235 }
236
237 if (enclosing_squashing_composited_layer) {
238 enclosing_squashing_composited_layer->GetCompositedLayerMapping()
239 ->SetNeedsGraphicsLayerUpdate(kGraphicsLayerUpdateSubtree);
240 }
241
242 update_type = kForceUpdate;
243 }
244
245
246 switch (layer->GetCompositingState()) {
247 case kNotComposited:
248 break;
249 case kPaintsIntoOwnBacking:
250 if (style.IsStackingContext())
251 enclosing_stacking_composited_layer = layer;
252 break;
253 case kPaintsIntoGroupedBacking:
254 enclosing_squashing_composited_layer =
255 &layer->GroupedMapping()->OwningLayer();
256 break;
257 }
258
259 // invalidate again after the switch, in case
260 // enclosing_stacking_composited_layer or
261 // enclosing_squashing_composited_layer was previously null.
262 if (layer->NeedsCompositingInputsUpdate()) {
263 if (enclosing_stacking_composited_layer) {
264 enclosing_stacking_composited_layer->GetCompositedLayerMapping()
265 ->SetNeedsGraphicsLayerUpdate(kGraphicsLayerUpdateSubtree);
266 }
267
268 if (enclosing_squashing_composited_layer) {
269 enclosing_squashing_composited_layer->GetCompositedLayerMapping()
270 ->SetNeedsGraphicsLayerUpdate(kGraphicsLayerUpdateSubtree);
271 }
272 }
273
274 if (style.GetPosition() == EPosition::kAbsolute) {
275 info.escape_clip_to = info.escape_clip_to_for_absolute;
276 info.scrolling_ancestor = info.scrolling_ancestor_for_absolute;
277 info.needs_reparent_scroll = info.needs_reparent_scroll_for_absolute;
278 } else if (style.GetPosition() == EPosition::kFixed) {
279 info.escape_clip_to = info.escape_clip_to_for_fixed;
280 info.scrolling_ancestor = info.scrolling_ancestor_for_fixed;
281 info.needs_reparent_scroll = info.needs_reparent_scroll_for_fixed;
282 }
283
284 if (layout_object.ShouldApplyLayoutContainment())
285 info.nearest_contained_layout_layer = layer;
286
287 if (update_type == kForceUpdate)
288 UpdateAncestorDependentCompositingInputs(layer, info);
289
290 info.enclosing_stacking_composited_layer =
291 enclosing_stacking_composited_layer;
292 info.enclosing_squashing_composited_layer =
293 enclosing_squashing_composited_layer;
294
295 // Handles sibling scroll problem, i.e. a non-stacking context scroller
296 // needs to propagate scroll to its descendants that are siblings in
297 // paint order. For example:
298 // <div style="overflow:scroll;">
299 // <div style="position:relative;">Paint sibling.</div>
300 // </div>
301 if (layer->ScrollsOverflow()) {
302 info.scrolling_ancestor = layer;
303 info.needs_reparent_scroll = true;
304 }
305 if (layout_object.CanContainAbsolutePositionObjects()) {
306 info.clip_chain_parent_for_absolute = layer;
307 info.escape_clip_to_for_absolute = info.escape_clip_to;
308 info.scrolling_ancestor_for_absolute = info.scrolling_ancestor;
309 info.needs_reparent_scroll_for_absolute = info.needs_reparent_scroll;
310 }
311
312 // LayoutView isn't really the containing block for fixed-pos descendants
313 // in the sense that they don't scroll along with its in-flow contents.
314 // However LayoutView does clip them.
315 if (layout_object.CanContainFixedPositionObjects() &&
316 !IsA<LayoutView>(layout_object)) {
317 info.clip_chain_parent_for_fixed = layer;
318 info.escape_clip_to_for_fixed = info.escape_clip_to;
319 info.scrolling_ancestor_for_fixed = info.scrolling_ancestor;
320 info.needs_reparent_scroll_for_fixed = info.needs_reparent_scroll;
321 }
322 if (IsA<LayoutView>(layout_object))
323 info.clip_chain_parent_for_fixed = layer;
324
325 // CSS clip affects all descendants, not just containing-block descendants.
326 // We don't have to set clip_chain_parent_for_absolute here because CSS clip
327 // requires position:absolute, so the element must contain absolute-positioned
328 // descendants.
329 // However it is incorrect to let fixed-positioned descendants to inherit the
330 // clip state from this element either, because the overflow clip and the
331 // inherited clip of the current element shouldn't apply to them if the
332 // current element is not a fixed-pos container. This is a known bug but too
333 // difficult to fix in SPv1 compositing.
334 if (layout_object.HasClip())
335 info.clip_chain_parent_for_fixed = layer;
336
337 if (style.IsStackingContext()) {
338 info.escape_clip_to = nullptr;
339 const LayoutBoxModelObject* clipping_container =
340 ClippingContainerFromClipChainParent(layer);
341 info.escape_clip_to_for_absolute =
342 ClippingContainerFromClipChainParent(
343 info.clip_chain_parent_for_absolute) != clipping_container
344 ? info.clip_chain_parent_for_absolute
345 : nullptr;
346 info.escape_clip_to_for_fixed =
347 ClippingContainerFromClipChainParent(
348 info.clip_chain_parent_for_fixed) != clipping_container
349 ? info.clip_chain_parent_for_fixed
350 : nullptr;
351 // Workaround crbug.com/817175
352 // We can't escape clip to a layer that paints after us, because in SPv1
353 // cc needs to reverse engineer clip tree from the layer tree, and we
354 // can't refer to a clip node that hasn't been built yet.
355 // This will result in wrong clip in some rare cases, for example:
356 // <div style="display:grid;">
357 // <div style="z-index:-1; overflow:hidden;">
358 // <div style="position:absolute;"></div>
359 // </div>
360 // </div>
361 if (info.escape_clip_to_for_absolute && style.ZIndex() < 0 &&
362 !info.escape_clip_to_for_absolute->GetLayoutObject()
363 .StyleRef()
364 .IsStackingContext())
365 info.escape_clip_to_for_absolute = nullptr;
366 if (info.escape_clip_to_for_fixed && style.ZIndex() < 0 &&
367 !info.escape_clip_to_for_fixed->GetLayoutObject()
368 .StyleRef()
369 .IsStackingContext())
370 info.escape_clip_to_for_fixed = nullptr;
371
372 info.needs_reparent_scroll = info.needs_reparent_scroll_for_absolute =
373 info.needs_reparent_scroll_for_fixed = false;
374 }
375
376 if (layout_object.IsStickyPositioned())
377 info.is_under_position_sticky = true;
378 }
379
UpdateAncestorDependentCompositingInputs(PaintLayer * layer,const AncestorInfo & info)380 void CompositingInputsUpdater::UpdateAncestorDependentCompositingInputs(
381 PaintLayer* layer,
382 const AncestorInfo& info) {
383 if (layer->IsRootLayer()) {
384 layer->UpdateAncestorDependentCompositingInputs(
385 PaintLayer::AncestorDependentCompositingInputs());
386 return;
387 }
388
389 PaintLayer::AncestorDependentCompositingInputs properties;
390 LayoutBoxModelObject& layout_object = layer->GetLayoutObject();
391
392 // The final value for |unclipped_absolute_bounding_box| needs to be
393 // in absolute, unscrolled space, without any scroll applied.
394 properties.unclipped_absolute_bounding_box =
395 EnclosingIntRect(geometry_map_.AbsoluteRect(
396 layer->BoundingBoxForCompositingOverlapTest()));
397
398 bool affected_by_scroll = root_layer_->GetScrollableArea() &&
399 layer->IsAffectedByScrollOf(root_layer_);
400
401 // At ths point, |unclipped_absolute_bounding_box| is in viewport space.
402 // To convert to absolute space, add scroll offset for non-fixed layers.
403 if (affected_by_scroll) {
404 properties.unclipped_absolute_bounding_box.Move(
405 RoundedIntSize(root_layer_->GetScrollableArea()->GetScrollOffset()));
406 }
407
408 // For sticky-positioned elements, the scroll offset is sometimes included and
409 // sometimes not, depending on whether the sticky element is affixed or still
410 // scrolling. This makes caching difficult, as compared to Fixed position
411 // elements which have consistent behavior. So we disable caching for
412 // sticky-positioned subtrees.
413 ClipRectsCacheSlot cache_slot = info.is_under_position_sticky
414 ? kUncachedClipRects
415 : kAbsoluteClipRectsIgnoringViewportClip;
416
417 ClipRect clip_rect;
418 layer->Clipper(PaintLayer::GeometryMapperOption::kDoNotUseGeometryMapper)
419 .CalculateBackgroundClipRect(
420 ClipRectsContext(root_layer_,
421 &root_layer_->GetLayoutObject().FirstFragment(),
422 cache_slot, kIgnorePlatformOverlayScrollbarSize,
423 kIgnoreOverflowClipAndScroll),
424 clip_rect);
425 IntRect snapped_clip_rect = PixelSnappedIntRect(clip_rect.Rect());
426 // |snapped_clip_rect| is in absolute space space, but with scroll applied.
427 // To convert to absolute, unscrolled space, subtract scroll offsets for
428 // fixed layers.
429 if (root_layer_->GetScrollableArea() && !affected_by_scroll) {
430 snapped_clip_rect.Move(
431 RoundedIntSize(-root_layer_->GetScrollableArea()->GetScrollOffset()));
432 }
433
434 properties.clipped_absolute_bounding_box =
435 properties.unclipped_absolute_bounding_box;
436 properties.clipped_absolute_bounding_box.Intersect(snapped_clip_rect);
437
438 const PaintLayer* parent = layer->Parent();
439 properties.opacity_ancestor =
440 parent->IsTransparent() ? parent : parent->OpacityAncestor();
441 properties.transform_ancestor =
442 parent->Transform() ? parent : parent->TransformAncestor();
443 properties.filter_ancestor =
444 parent->HasFilterInducingProperty() ? parent : parent->FilterAncestor();
445 properties.clip_path_ancestor = parent->GetLayoutObject().HasClipPath()
446 ? parent
447 : parent->ClipPathAncestor();
448 properties.mask_ancestor =
449 parent->GetLayoutObject().HasMask() ? parent : parent->MaskAncestor();
450
451 EPosition position = layout_object.StyleRef().GetPosition();
452 properties.nearest_fixed_position_layer =
453 position == EPosition::kFixed ? layer
454 : parent->NearestFixedPositionLayer();
455
456 PaintLayer* clip_chain_parent = layer->Parent();
457 if (position == EPosition::kAbsolute)
458 clip_chain_parent = info.clip_chain_parent_for_absolute;
459 else if (position == EPosition::kFixed)
460 clip_chain_parent = info.clip_chain_parent_for_fixed;
461 properties.clipping_container =
462 ClippingContainerFromClipChainParent(clip_chain_parent);
463 properties.clip_parent = info.escape_clip_to;
464
465 properties.ancestor_scrolling_layer = info.scrolling_ancestor;
466 if (info.needs_reparent_scroll && layout_object.StyleRef().IsStacked())
467 properties.scroll_parent = info.scrolling_ancestor;
468
469 properties.is_under_position_sticky = info.is_under_position_sticky;
470 properties.nearest_contained_layout_layer =
471 info.nearest_contained_layout_layer;
472
473 layer->UpdateAncestorDependentCompositingInputs(properties);
474 }
475
476 #if DCHECK_IS_ON()
477
AssertNeedsCompositingInputsUpdateBitsCleared(PaintLayer * layer)478 void CompositingInputsUpdater::AssertNeedsCompositingInputsUpdateBitsCleared(
479 PaintLayer* layer) {
480 bool recursion_blocked_by_display_lock =
481 layer->GetLayoutObject().PrePaintBlockedByDisplayLock(
482 DisplayLockLifecycleTarget::kChildren);
483
484 DCHECK(recursion_blocked_by_display_lock ||
485 !layer->ChildNeedsCompositingInputsUpdate());
486 DCHECK(!layer->NeedsCompositingInputsUpdate());
487
488 if (recursion_blocked_by_display_lock)
489 return;
490
491 for (PaintLayer* child = layer->FirstChild(); child;
492 child = child->NextSibling())
493 AssertNeedsCompositingInputsUpdateBitsCleared(child);
494 }
495
496 #endif
497
498 } // namespace blink
499