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