1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4  * License, v. 2.0. If a copy of the MPL was not distributed with this
5  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 
7 #include "mozilla/layers/AsyncCompositionManager.h"
8 #include <stdint.h>  // for uint32_t
9 #include "apz/src/AsyncPanZoomController.h"
10 #include "FrameMetrics.h"                 // for FrameMetrics
11 #include "LayerManagerComposite.h"        // for LayerManagerComposite, etc
12 #include "Layers.h"                       // for Layer, ContainerLayer, etc
13 #include "gfxPoint.h"                     // for gfxPoint, gfxSize
14 #include "gfxPrefs.h"                     // for gfxPrefs
15 #include "mozilla/StyleAnimationValue.h"  // for StyleAnimationValue, etc
16 #include "mozilla/WidgetUtils.h"          // for ComputeTransformForRotation
17 #include "mozilla/gfx/BaseRect.h"         // for BaseRect
18 #include "mozilla/gfx/Point.h"            // for RoundedToInt, PointTyped
19 #include "mozilla/gfx/Rect.h"             // for RoundedToInt, RectTyped
20 #include "mozilla/gfx/ScaleFactor.h"      // for ScaleFactor
21 #include "mozilla/layers/AnimationHelper.h"
22 #include "mozilla/layers/APZUtils.h"    // for CompleteAsyncTransform
23 #include "mozilla/layers/Compositor.h"  // for Compositor
24 #include "mozilla/layers/CompositorBridgeParent.h"  // for CompositorBridgeParent, etc
25 #include "mozilla/layers/CompositorThread.h"
26 #include "mozilla/layers/LayerAnimationUtils.h"  // for TimingFunctionToComputedTimingFunction
27 #include "mozilla/layers/LayerMetricsWrapper.h"  // for LayerMetricsWrapper
28 #include "nsCoord.h"                 // for NSAppUnitsToFloatPixels, etc
29 #include "nsDebug.h"                 // for NS_ASSERTION, etc
30 #include "nsDeviceContext.h"         // for nsDeviceContext
31 #include "nsDisplayList.h"           // for nsDisplayTransform, etc
32 #include "nsMathUtils.h"             // for NS_round
33 #include "nsPoint.h"                 // for nsPoint
34 #include "nsRect.h"                  // for mozilla::gfx::IntRect
35 #include "nsRegion.h"                // for nsIntRegion
36 #include "nsTArray.h"                // for nsTArray, nsTArray_Impl, etc
37 #include "nsTArrayForwardDeclare.h"  // for InfallibleTArray
38 #include "UnitTransforms.h"          // for TransformTo
39 #include "gfxPrefs.h"
40 #if defined(MOZ_WIDGET_ANDROID)
41 #include <android/log.h>
42 #include "mozilla/layers/UiCompositorControllerParent.h"
43 #include "mozilla/widget/AndroidCompositorWidget.h"
44 #endif
45 #include "GeckoProfiler.h"
46 #include "FrameUniformityData.h"
47 #include "TreeTraversal.h"  // for ForEachNode, BreadthFirstSearch
48 #include "VsyncSource.h"
49 
50 struct nsCSSValueSharedList;
51 
52 namespace mozilla {
53 namespace layers {
54 
55 using namespace mozilla::gfx;
56 
IsSameDimension(dom::ScreenOrientationInternal o1,dom::ScreenOrientationInternal o2)57 static bool IsSameDimension(dom::ScreenOrientationInternal o1,
58                             dom::ScreenOrientationInternal o2) {
59   bool isO1portrait = (o1 == dom::eScreenOrientation_PortraitPrimary ||
60                        o1 == dom::eScreenOrientation_PortraitSecondary);
61   bool isO2portrait = (o2 == dom::eScreenOrientation_PortraitPrimary ||
62                        o2 == dom::eScreenOrientation_PortraitSecondary);
63   return !(isO1portrait ^ isO2portrait);
64 }
65 
ContentMightReflowOnOrientationChange(const IntRect & rect)66 static bool ContentMightReflowOnOrientationChange(const IntRect& rect) {
67   return rect.Width() != rect.Height();
68 }
69 
AsyncCompositionManager(CompositorBridgeParent * aParent,HostLayerManager * aManager)70 AsyncCompositionManager::AsyncCompositionManager(
71     CompositorBridgeParent* aParent, HostLayerManager* aManager)
72     : mLayerManager(aManager),
73       mIsFirstPaint(true),
74       mLayersUpdated(false),
75       mReadyForCompose(true),
76       mCompositorBridge(aParent) {}
77 
~AsyncCompositionManager()78 AsyncCompositionManager::~AsyncCompositionManager() {}
79 
ResolveRefLayers(CompositorBridgeParent * aCompositor,bool * aHasRemoteContent,bool * aResolvePlugins)80 void AsyncCompositionManager::ResolveRefLayers(
81     CompositorBridgeParent* aCompositor, bool* aHasRemoteContent,
82     bool* aResolvePlugins) {
83   if (aHasRemoteContent) {
84     *aHasRemoteContent = false;
85   }
86 
87 #if defined(XP_WIN) || defined(MOZ_WIDGET_GTK)
88   // If valid *aResolvePlugins indicates if we need to update plugin geometry
89   // when we walk the tree.
90   bool resolvePlugins = (aCompositor && aResolvePlugins && *aResolvePlugins);
91 #endif
92 
93   if (!mLayerManager->GetRoot()) {
94     // Updated the return value since this result controls completing
95     // composition.
96     if (aResolvePlugins) {
97       *aResolvePlugins = false;
98     }
99     return;
100   }
101 
102   mReadyForCompose = true;
103   bool hasRemoteContent = false;
104   bool didResolvePlugins = false;
105 
106   ForEachNode<ForwardIterator>(mLayerManager->GetRoot(), [&](Layer* layer) {
107     RefLayer* refLayer = layer->AsRefLayer();
108     if (!refLayer) {
109       return;
110     }
111 
112     hasRemoteContent = true;
113     const CompositorBridgeParent::LayerTreeState* state =
114         CompositorBridgeParent::GetIndirectShadowTree(
115             refLayer->GetReferentId());
116     if (!state) {
117       return;
118     }
119 
120     Layer* referent = state->mRoot;
121     if (!referent) {
122       return;
123     }
124 
125     if (!refLayer->GetLocalVisibleRegion().IsEmpty()) {
126       dom::ScreenOrientationInternal chromeOrientation =
127           mTargetConfig.orientation();
128       dom::ScreenOrientationInternal contentOrientation =
129           state->mTargetConfig.orientation();
130       if (!IsSameDimension(chromeOrientation, contentOrientation) &&
131           ContentMightReflowOnOrientationChange(
132               mTargetConfig.naturalBounds())) {
133         mReadyForCompose = false;
134       }
135     }
136 
137     refLayer->ConnectReferentLayer(referent);
138 
139 #if defined(XP_WIN) || defined(MOZ_WIDGET_GTK)
140     if (resolvePlugins) {
141       didResolvePlugins |=
142           aCompositor->UpdatePluginWindowState(refLayer->GetReferentId());
143     }
144 #endif
145   });
146 
147   if (aHasRemoteContent) {
148     *aHasRemoteContent = hasRemoteContent;
149   }
150   if (aResolvePlugins) {
151     *aResolvePlugins = didResolvePlugins;
152   }
153 }
154 
DetachRefLayers()155 void AsyncCompositionManager::DetachRefLayers() {
156   if (!mLayerManager->GetRoot()) {
157     return;
158   }
159 
160   mReadyForCompose = false;
161 
162   ForEachNodePostOrder<ForwardIterator>(
163       mLayerManager->GetRoot(), [&](Layer* layer) {
164         RefLayer* refLayer = layer->AsRefLayer();
165         if (!refLayer) {
166           return;
167         }
168 
169         const CompositorBridgeParent::LayerTreeState* state =
170             CompositorBridgeParent::GetIndirectShadowTree(
171                 refLayer->GetReferentId());
172         if (!state) {
173           return;
174         }
175 
176         Layer* referent = state->mRoot;
177         if (referent) {
178           refLayer->DetachReferentLayer(referent);
179         }
180       });
181 }
182 
ComputeRotation()183 void AsyncCompositionManager::ComputeRotation() {
184   if (!mTargetConfig.naturalBounds().IsEmpty()) {
185     mWorldTransform = ComputeTransformForRotation(mTargetConfig.naturalBounds(),
186                                                   mTargetConfig.rotation());
187   }
188 }
189 
190 #ifdef DEBUG
GetBaseTransform(Layer * aLayer,Matrix4x4 * aTransform)191 static void GetBaseTransform(Layer* aLayer, Matrix4x4* aTransform) {
192   // Start with the animated transform if there is one
193   *aTransform = (aLayer->AsHostLayer()->GetShadowTransformSetByAnimation()
194                      ? aLayer->GetLocalTransform()
195                      : aLayer->GetTransform());
196 }
197 #endif
198 
TransformClipRect(Layer * aLayer,const ParentLayerToParentLayerMatrix4x4 & aTransform)199 static void TransformClipRect(
200     Layer* aLayer, const ParentLayerToParentLayerMatrix4x4& aTransform) {
201   MOZ_ASSERT(aTransform.Is2D());
202   const Maybe<ParentLayerIntRect>& clipRect =
203       aLayer->AsHostLayer()->GetShadowClipRect();
204   if (clipRect) {
205     ParentLayerIntRect transformed = TransformBy(aTransform, *clipRect);
206     aLayer->AsHostLayer()->SetShadowClipRect(Some(transformed));
207   }
208 }
209 
210 // Similar to TransformFixedClip(), but only transforms the fixed part of the
211 // clip.
TransformFixedClip(Layer * aLayer,const ParentLayerToParentLayerMatrix4x4 & aTransform,AsyncCompositionManager::ClipParts & aClipParts)212 static void TransformFixedClip(
213     Layer* aLayer, const ParentLayerToParentLayerMatrix4x4& aTransform,
214     AsyncCompositionManager::ClipParts& aClipParts) {
215   MOZ_ASSERT(aTransform.Is2D());
216   if (aClipParts.mFixedClip) {
217     *aClipParts.mFixedClip = TransformBy(aTransform, *aClipParts.mFixedClip);
218     aLayer->AsHostLayer()->SetShadowClipRect(aClipParts.Intersect());
219   }
220 }
221 
222 /**
223  * Set the given transform as the shadow transform on the layer, assuming
224  * that the given transform already has the pre- and post-scales applied.
225  * That is, this function cancels out the pre- and post-scales from aTransform
226  * before setting it as the shadow transform on the layer, so that when
227  * the layer's effective transform is computed, the pre- and post-scales will
228  * only be applied once.
229  */
SetShadowTransform(Layer * aLayer,LayerToParentLayerMatrix4x4 aTransform)230 static void SetShadowTransform(Layer* aLayer,
231                                LayerToParentLayerMatrix4x4 aTransform) {
232   if (ContainerLayer* c = aLayer->AsContainerLayer()) {
233     aTransform.PreScale(1.0f / c->GetPreXScale(), 1.0f / c->GetPreYScale(), 1);
234   }
235   aTransform.PostScale(1.0f / aLayer->GetPostXScale(),
236                        1.0f / aLayer->GetPostYScale(), 1);
237   aLayer->AsHostLayer()->SetShadowBaseTransform(aTransform.ToUnknownMatrix());
238 }
239 
TranslateShadowLayer(Layer * aLayer,const ParentLayerPoint & aTranslation,bool aAdjustClipRect,AsyncCompositionManager::ClipPartsCache * aClipPartsCache)240 static void TranslateShadowLayer(
241     Layer* aLayer, const ParentLayerPoint& aTranslation, bool aAdjustClipRect,
242     AsyncCompositionManager::ClipPartsCache* aClipPartsCache) {
243   // This layer might also be a scrollable layer and have an async transform.
244   // To make sure we don't clobber that, we start with the shadow transform.
245   // (i.e. GetLocalTransform() instead of GetTransform()).
246   // Note that the shadow transform is reset on every frame of composition so
247   // we don't have to worry about the adjustments compounding over successive
248   // frames.
249   LayerToParentLayerMatrix4x4 layerTransform = aLayer->GetLocalTransformTyped();
250 
251   // Apply the translation to the layer transform.
252   layerTransform.PostTranslate(aTranslation);
253 
254   SetShadowTransform(aLayer, layerTransform);
255   aLayer->AsHostLayer()->SetShadowTransformSetByAnimation(false);
256 
257   if (aAdjustClipRect) {
258     auto transform =
259         ParentLayerToParentLayerMatrix4x4::Translation(aTranslation);
260     // If we're passed a clip parts cache, only transform the fixed part of
261     // the clip.
262     if (aClipPartsCache) {
263       auto iter = aClipPartsCache->find(aLayer);
264       MOZ_ASSERT(iter != aClipPartsCache->end());
265       TransformFixedClip(aLayer, transform, iter->second);
266     } else {
267       TransformClipRect(aLayer, transform);
268     }
269 
270     // If a fixed- or sticky-position layer has a mask layer, that mask should
271     // move along with the layer, so apply the translation to the mask layer
272     // too.
273     if (Layer* maskLayer = aLayer->GetMaskLayer()) {
274       TranslateShadowLayer(maskLayer, aTranslation, false, aClipPartsCache);
275     }
276   }
277 }
278 
279 #ifdef DEBUG
AccumulateLayerTransforms(Layer * aLayer,Layer * aAncestor,Matrix4x4 & aMatrix)280 static void AccumulateLayerTransforms(Layer* aLayer, Layer* aAncestor,
281                                       Matrix4x4& aMatrix) {
282   // Accumulate the transforms between this layer and the subtree root layer.
283   for (Layer* l = aLayer; l && l != aAncestor; l = l->GetParent()) {
284     Matrix4x4 transform;
285     GetBaseTransform(l, &transform);
286     aMatrix *= transform;
287   }
288 }
289 #endif
290 
GetLayerFixedMarginsOffset(Layer * aLayer,const ScreenMargin & aFixedLayerMargins)291 static LayerPoint GetLayerFixedMarginsOffset(
292     Layer* aLayer, const ScreenMargin& aFixedLayerMargins) {
293   // Work out the necessary translation, in root scrollable layer space.
294   // Because fixed layer margins are stored relative to the root scrollable
295   // layer, we can just take the difference between these values.
296   LayerPoint translation;
297   int32_t sides = aLayer->GetFixedPositionSides();
298 
299   if ((sides & eSideBitsLeftRight) == eSideBitsLeftRight) {
300     translation.x += (aFixedLayerMargins.left - aFixedLayerMargins.right) / 2;
301   } else if (sides & eSideBitsRight) {
302     translation.x -= aFixedLayerMargins.right;
303   } else if (sides & eSideBitsLeft) {
304     translation.x += aFixedLayerMargins.left;
305   }
306 
307   if ((sides & eSideBitsTopBottom) == eSideBitsTopBottom) {
308     translation.y += (aFixedLayerMargins.top - aFixedLayerMargins.bottom) / 2;
309   } else if (sides & eSideBitsBottom) {
310     translation.y -= aFixedLayerMargins.bottom;
311   } else if (sides & eSideBitsTop) {
312     translation.y += aFixedLayerMargins.top;
313   }
314 
315   return translation;
316 }
317 
IntervalOverlap(gfxFloat aTranslation,gfxFloat aMin,gfxFloat aMax)318 static gfxFloat IntervalOverlap(gfxFloat aTranslation, gfxFloat aMin,
319                                 gfxFloat aMax) {
320   // Determine the amount of overlap between the 1D vector |aTranslation|
321   // and the interval [aMin, aMax].
322   if (aTranslation > 0) {
323     return std::max(0.0, std::min(aMax, aTranslation) - std::max(aMin, 0.0));
324   } else {
325     return std::min(0.0, std::max(aMin, aTranslation) - std::min(aMax, 0.0));
326   }
327 }
328 
329 /**
330  * Finds the metrics on |aLayer| with scroll id |aScrollId|, and returns a
331  * LayerMetricsWrapper representing the (layer, metrics) pair, or the null
332  * LayerMetricsWrapper if no matching metrics could be found.
333  */
FindMetricsWithScrollId(Layer * aLayer,FrameMetrics::ViewID aScrollId)334 static LayerMetricsWrapper FindMetricsWithScrollId(
335     Layer* aLayer, FrameMetrics::ViewID aScrollId) {
336   for (uint64_t i = 0; i < aLayer->GetScrollMetadataCount(); ++i) {
337     if (aLayer->GetFrameMetrics(i).GetScrollId() == aScrollId) {
338       return LayerMetricsWrapper(aLayer, i);
339     }
340   }
341   return LayerMetricsWrapper();
342 }
343 
344 /**
345  * Checks whether the (layer, metrics) pair (aTransformedLayer,
346  * aTransformedMetrics) is on the path from |aFixedLayer| to the metrics with
347  * scroll id |aFixedWithRespectTo|, inclusive.
348  */
AsyncTransformShouldBeUnapplied(Layer * aFixedLayer,FrameMetrics::ViewID aFixedWithRespectTo,Layer * aTransformedLayer,FrameMetrics::ViewID aTransformedMetrics)349 static bool AsyncTransformShouldBeUnapplied(
350     Layer* aFixedLayer, FrameMetrics::ViewID aFixedWithRespectTo,
351     Layer* aTransformedLayer, FrameMetrics::ViewID aTransformedMetrics) {
352   LayerMetricsWrapper transformed =
353       FindMetricsWithScrollId(aTransformedLayer, aTransformedMetrics);
354   if (!transformed.IsValid()) {
355     return false;
356   }
357   // It's important to start at the bottom, because the fixed layer itself
358   // could have the transformed metrics, and they can be at the bottom.
359   LayerMetricsWrapper current(aFixedLayer,
360                               LayerMetricsWrapper::StartAt::BOTTOM);
361   bool encounteredTransformedLayer = false;
362   // The transformed layer is on the path from |aFixedLayer| to the fixed-to
363   // layer if as we walk up the (layer, metrics) tree starting from
364   // |aFixedLayer|, we *first* encounter the transformed layer, and *then* (or
365   // at the same time) the fixed-to layer.
366   while (current) {
367     if (!encounteredTransformedLayer && current == transformed) {
368       encounteredTransformedLayer = true;
369     }
370     if (current.Metrics().GetScrollId() == aFixedWithRespectTo) {
371       return encounteredTransformedLayer;
372     }
373     current = current.GetParent();
374     // It's possible that we reach a layers id boundary before we reach an
375     // ancestor with the scroll id |aFixedWithRespectTo| (this could happen
376     // e.g. if the scroll frame with that scroll id uses containerless
377     // scrolling). In such a case, stop the walk, as a new layers id could
378     // have a different layer with scroll id |aFixedWithRespectTo| which we
379     // don't intend to match.
380     if (current && current.AsRefLayer() != nullptr) {
381       break;
382     }
383   }
384   return false;
385 }
386 
387 // If |aLayer| is fixed or sticky, returns the scroll id of the scroll frame
388 // that it's fixed or sticky to. Otherwise, returns Nothing().
IsFixedOrSticky(Layer * aLayer)389 static Maybe<FrameMetrics::ViewID> IsFixedOrSticky(Layer* aLayer) {
390   bool isRootOfFixedSubtree = aLayer->GetIsFixedPosition() &&
391                               !aLayer->GetParent()->GetIsFixedPosition();
392   if (isRootOfFixedSubtree) {
393     return Some(aLayer->GetFixedPositionScrollContainerId());
394   }
395   if (aLayer->GetIsStickyPosition()) {
396     return Some(aLayer->GetStickyScrollContainerId());
397   }
398   return Nothing();
399 }
400 
AlignFixedAndStickyLayers(Layer * aTransformedSubtreeRoot,Layer * aStartTraversalAt,FrameMetrics::ViewID aTransformScrollId,const LayerToParentLayerMatrix4x4 & aPreviousTransformForRoot,const LayerToParentLayerMatrix4x4 & aCurrentTransformForRoot,const ScreenMargin & aFixedLayerMargins,ClipPartsCache * aClipPartsCache)401 void AsyncCompositionManager::AlignFixedAndStickyLayers(
402     Layer* aTransformedSubtreeRoot, Layer* aStartTraversalAt,
403     FrameMetrics::ViewID aTransformScrollId,
404     const LayerToParentLayerMatrix4x4& aPreviousTransformForRoot,
405     const LayerToParentLayerMatrix4x4& aCurrentTransformForRoot,
406     const ScreenMargin& aFixedLayerMargins, ClipPartsCache* aClipPartsCache) {
407   // We're going to be inverting |aCurrentTransformForRoot|.
408   // If it's singular, there's nothing we can do.
409   if (aCurrentTransformForRoot.IsSingular()) {
410     return;
411   }
412 
413   Layer* layer = aStartTraversalAt;
414   bool needsAsyncTransformUnapplied = false;
415   if (Maybe<FrameMetrics::ViewID> fixedTo = IsFixedOrSticky(layer)) {
416     needsAsyncTransformUnapplied = AsyncTransformShouldBeUnapplied(
417         layer, *fixedTo, aTransformedSubtreeRoot, aTransformScrollId);
418   }
419 
420   // We want to process all the fixed and sticky descendants of
421   // aTransformedSubtreeRoot. Once we do encounter such a descendant, we don't
422   // need to recurse any deeper because the adjustment to the fixed or sticky
423   // layer will apply to its subtree.
424   if (!needsAsyncTransformUnapplied) {
425     for (Layer* child = layer->GetFirstChild(); child;
426          child = child->GetNextSibling()) {
427       AlignFixedAndStickyLayers(aTransformedSubtreeRoot, child,
428                                 aTransformScrollId, aPreviousTransformForRoot,
429                                 aCurrentTransformForRoot, aFixedLayerMargins,
430                                 aClipPartsCache);
431     }
432     return;
433   }
434 
435     // Insert a translation so that the position of the anchor point is the same
436     // before and after the change to the transform of aTransformedSubtreeRoot.
437 
438     // A transform creates a containing block for fixed-position descendants,
439     // so there shouldn't be a transform in between the fixed layer and
440     // the subtree root layer.
441 #ifdef DEBUG
442   Matrix4x4 ancestorTransform;
443   if (layer != aTransformedSubtreeRoot) {
444     AccumulateLayerTransforms(layer->GetParent(), aTransformedSubtreeRoot,
445                               ancestorTransform);
446   }
447   ancestorTransform.NudgeToIntegersFixedEpsilon();
448   MOZ_ASSERT(ancestorTransform.IsIdentity());
449 #endif
450 
451   // Since we create container layers for fixed layers, there shouldn't
452   // a local CSS or OMTA transform on the fixed layer, either (any local
453   // transform would go onto a descendant layer inside the container
454   // layer).
455 #ifdef DEBUG
456   Matrix4x4 localTransform;
457   GetBaseTransform(layer, &localTransform);
458   localTransform.NudgeToIntegersFixedEpsilon();
459   MOZ_ASSERT(localTransform.IsIdentity());
460 #endif
461 
462   // Now work out the translation necessary to make sure the layer doesn't
463   // move given the new sub-tree root transform.
464 
465   // Get the layer's fixed anchor point, in the layer's local coordinate space
466   // (before any transform is applied).
467   LayerPoint anchor = layer->GetFixedPositionAnchor();
468 
469   // Offset the layer's anchor point to make sure fixed position content
470   // respects content document fixed position margins.
471   LayerPoint offsetAnchor =
472       anchor + GetLayerFixedMarginsOffset(layer, aFixedLayerMargins);
473 
474   // Additionally transform the anchor to compensate for the change
475   // from the old transform to the new transform. We do
476   // this by using the old transform to take the offset anchor back into
477   // subtree root space, and then the inverse of the new transform
478   // to bring it back to layer space.
479   ParentLayerPoint offsetAnchorInSubtreeRootSpace =
480       aPreviousTransformForRoot.TransformPoint(offsetAnchor);
481   LayerPoint transformedAnchor =
482       aCurrentTransformForRoot.Inverse().TransformPoint(
483           offsetAnchorInSubtreeRootSpace);
484 
485   // We want to translate the layer by the difference between
486   // |transformedAnchor| and |anchor|.
487   LayerPoint translation = transformedAnchor - anchor;
488 
489   // A fixed layer will "consume" (be unadjusted by) the entire translation
490   // calculated above. A sticky layer may consume all, part, or none of it,
491   // depending on where we are relative to its sticky scroll range.
492   // The remainder of the translation (the unconsumed portion) needs to
493   // be propagated to descendant fixed/sticky layers.
494   LayerPoint unconsumedTranslation;
495 
496   if (layer->GetIsStickyPosition()) {
497     // For sticky positioned layers, the difference between the two rectangles
498     // defines a pair of translation intervals in each dimension through which
499     // the layer should not move relative to the scroll container. To
500     // accomplish this, we limit each dimension of the |translation| to that
501     // part of it which overlaps those intervals.
502     const LayerRectAbsolute& stickyOuter = layer->GetStickyScrollRangeOuter();
503     const LayerRectAbsolute& stickyInner = layer->GetStickyScrollRangeInner();
504 
505     LayerPoint originalTranslation = translation;
506     translation.y =
507         IntervalOverlap(translation.y, stickyOuter.Y(), stickyOuter.YMost()) -
508         IntervalOverlap(translation.y, stickyInner.Y(), stickyInner.YMost());
509     translation.x =
510         IntervalOverlap(translation.x, stickyOuter.X(), stickyOuter.XMost()) -
511         IntervalOverlap(translation.x, stickyInner.X(), stickyInner.XMost());
512     unconsumedTranslation = translation - originalTranslation;
513   }
514 
515   // Finally, apply the translation to the layer transform. Note that in cases
516   // where the async transform on |aTransformedSubtreeRoot| affects this layer's
517   // clip rect, we need to apply the same translation to said clip rect, so
518   // that the effective transform on the clip rect takes it back to where it was
519   // originally, had there been no async scroll.
520   TranslateShadowLayer(
521       layer,
522       ViewAs<ParentLayerPixel>(translation,
523                                PixelCastJustification::NoTransformOnLayer),
524       true, aClipPartsCache);
525 
526   // Propragate the unconsumed portion of the translation to descendant
527   // fixed/sticky layers.
528   if (unconsumedTranslation != LayerPoint()) {
529     // Take the computations we performed to derive |translation| from
530     // |aCurrentTransformForRoot|, and perform them in reverse, keeping other
531     // quantities fixed, to come up with a new transform |newTransform| that
532     // would produce |unconsumedTranslation|.
533     LayerPoint newTransformedAnchor = unconsumedTranslation + anchor;
534     ParentLayerPoint newTransformedAnchorInSubtreeRootSpace =
535         aPreviousTransformForRoot.TransformPoint(newTransformedAnchor);
536     LayerToParentLayerMatrix4x4 newTransform = aPreviousTransformForRoot;
537     newTransform.PostTranslate(newTransformedAnchorInSubtreeRootSpace -
538                                offsetAnchorInSubtreeRootSpace);
539 
540     // Propagate this new transform to our descendants as the new value of
541     // |aCurrentTransformForRoot|. This allows them to consume the unconsumed
542     // translation.
543     for (Layer* child = layer->GetFirstChild(); child;
544          child = child->GetNextSibling()) {
545       AlignFixedAndStickyLayers(aTransformedSubtreeRoot, child,
546                                 aTransformScrollId, aPreviousTransformForRoot,
547                                 newTransform, aFixedLayerMargins,
548                                 aClipPartsCache);
549     }
550   }
551 }
552 
ApplyAnimatedValue(Layer * aLayer,CompositorAnimationStorage * aStorage,nsCSSPropertyID aProperty,const AnimationData & aAnimationData,const AnimationValue & aValue)553 static void ApplyAnimatedValue(Layer* aLayer,
554                                CompositorAnimationStorage* aStorage,
555                                nsCSSPropertyID aProperty,
556                                const AnimationData& aAnimationData,
557                                const AnimationValue& aValue) {
558   if (aValue.IsNull()) {
559     // Return gracefully if we have no valid AnimationValue.
560     return;
561   }
562 
563   HostLayer* layerCompositor = aLayer->AsHostLayer();
564   switch (aProperty) {
565     case eCSSProperty_opacity: {
566       layerCompositor->SetShadowOpacity(aValue.GetOpacity());
567       layerCompositor->SetShadowOpacitySetByAnimation(true);
568       aStorage->SetAnimatedValue(aLayer->GetCompositorAnimationsId(),
569                                  aValue.GetOpacity());
570 
571       break;
572     }
573     case eCSSProperty_transform: {
574       RefPtr<const nsCSSValueSharedList> list = aValue.GetTransformList();
575       const TransformData& transformData = aAnimationData.get_TransformData();
576       nsPoint origin = transformData.origin();
577       // we expect all our transform data to arrive in device pixels
578       Point3D transformOrigin = transformData.transformOrigin();
579       nsDisplayTransform::FrameTransformProperties props(Move(list),
580                                                          transformOrigin);
581 
582       Matrix4x4 transform = nsDisplayTransform::GetResultingTransformMatrix(
583           props, origin, transformData.appUnitsPerDevPixel(), 0,
584           &transformData.bounds());
585       Matrix4x4 frameTransform = transform;
586 
587       // If our parent layer is a perspective layer, then the offset into
588       // reference frame coordinates is already on that layer. If not, then we
589       // need to ask for it to be added here.
590       if (!aLayer->GetParent() ||
591           !aLayer->GetParent()->GetTransformIsPerspective()) {
592         nsLayoutUtils::PostTranslate(transform, origin,
593                                      transformData.appUnitsPerDevPixel(), true);
594       }
595 
596       if (ContainerLayer* c = aLayer->AsContainerLayer()) {
597         transform.PostScale(c->GetInheritedXScale(), c->GetInheritedYScale(),
598                             1);
599       }
600 
601       layerCompositor->SetShadowBaseTransform(transform);
602       layerCompositor->SetShadowTransformSetByAnimation(true);
603       aStorage->SetAnimatedValue(aLayer->GetCompositorAnimationsId(),
604                                  Move(transform), Move(frameTransform),
605                                  transformData);
606       break;
607     }
608     default:
609       MOZ_ASSERT_UNREACHABLE("Unhandled animated property");
610   }
611 }
612 
SampleAnimations(Layer * aLayer,CompositorAnimationStorage * aStorage,TimeStamp aTime,uint64_t * aLayerAreaAnimated)613 static AnimationProcessTypes SampleAnimations(
614     Layer* aLayer, CompositorAnimationStorage* aStorage, TimeStamp aTime,
615     uint64_t* aLayerAreaAnimated) {
616   // This tracks the first-encountered RefLayer in the layer tree. Since we are
617   // doing a depth-first traversal, it is set to a non-null value if and only if
618   // the currently-being-traversed node has a RefLayer ancestor. In the case of
619   // nested RefLayers it points to the rootmost RefLayer.
620   RefLayer* ancestorRefLayer = nullptr;
621 
622   // This bitfield-enum tracks which processes have active animations. Anything
623   // "above" the |ancestorRefLayer| in the layer tree is assumed to be the
624   // chrome process, and anything "below" is assumed to be the content process.
625   AnimationProcessTypes animProcess = AnimationProcessTypes::eNone;
626 
627   ForEachNode<ForwardIterator>(
628       aLayer,
629       [&](Layer* layer) {
630         if (!ancestorRefLayer) {
631           ancestorRefLayer = layer->AsRefLayer();
632         }
633 
634         bool hasInEffectAnimations = false;
635         AnimationValue animationValue = layer->GetBaseAnimationStyle();
636         if (AnimationHelper::SampleAnimationForEachNode(
637                 aTime, layer->GetAnimations(), layer->GetAnimationData(),
638                 animationValue, hasInEffectAnimations)) {
639           animProcess |= (ancestorRefLayer ? AnimationProcessTypes::eContent
640                                            : AnimationProcessTypes::eChrome);
641         }
642         if (hasInEffectAnimations) {
643           Animation& animation = layer->GetAnimations().LastElement();
644           ApplyAnimatedValue(layer, aStorage, animation.property(),
645                              animation.data(), animationValue);
646           if (aLayerAreaAnimated) {
647             *aLayerAreaAnimated += (layer->GetVisibleRegion().Area());
648           }
649         }
650       },
651       [&ancestorRefLayer](Layer* aLayer) {
652         // If we're unwinding up past the rootmost RefLayer, clear our pointer
653         if (ancestorRefLayer && aLayer->AsRefLayer() == ancestorRefLayer) {
654           ancestorRefLayer = nullptr;
655         }
656       });
657 
658   return animProcess;
659 }
660 
SampleAPZAnimations(const LayerMetricsWrapper & aLayer,TimeStamp aSampleTime)661 static bool SampleAPZAnimations(const LayerMetricsWrapper& aLayer,
662                                 TimeStamp aSampleTime) {
663   bool activeAnimations = false;
664 
665   ForEachNodePostOrder<ForwardIterator>(
666       aLayer,
667       [&activeAnimations, &aSampleTime](LayerMetricsWrapper aLayerMetrics) {
668         if (AsyncPanZoomController* apzc = aLayerMetrics.GetApzc()) {
669           apzc->ReportCheckerboard(aSampleTime);
670           activeAnimations |= apzc->AdvanceAnimations(aSampleTime);
671         }
672       });
673 
674   return activeAnimations;
675 }
676 
RecordShadowTransforms(Layer * aLayer)677 void AsyncCompositionManager::RecordShadowTransforms(Layer* aLayer) {
678   MOZ_ASSERT(gfxPrefs::CollectScrollTransforms());
679   MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
680 
681   ForEachNodePostOrder<ForwardIterator>(aLayer, [this](Layer* layer) {
682     for (uint32_t i = 0; i < layer->GetScrollMetadataCount(); i++) {
683       AsyncPanZoomController* apzc = layer->GetAsyncPanZoomController(i);
684       if (!apzc) {
685         continue;
686       }
687       gfx::Matrix4x4 shadowTransform =
688           layer->AsHostLayer()->GetShadowBaseTransform();
689       if (!shadowTransform.Is2D()) {
690         continue;
691       }
692 
693       Matrix transform = shadowTransform.As2D();
694       if (transform.IsTranslation() && !shadowTransform.IsIdentity()) {
695         Point translation = transform.GetTranslation();
696         mLayerTransformRecorder.RecordTransform(layer, translation);
697         return;
698       }
699     }
700   });
701 }
702 
AdjustForClip(const AsyncTransformComponentMatrix & asyncTransform,Layer * aLayer)703 static AsyncTransformComponentMatrix AdjustForClip(
704     const AsyncTransformComponentMatrix& asyncTransform, Layer* aLayer) {
705   AsyncTransformComponentMatrix result = asyncTransform;
706 
707   // Container layers start at the origin, but they are clipped to where they
708   // actually have content on the screen. The tree transform is meant to apply
709   // to the clipped area. If the tree transform includes a scale component,
710   // then applying it to container as-is will produce incorrect results. To
711   // avoid this, translate the layer so that the clip rect starts at the origin,
712   // apply the tree transform, and translate back.
713   if (const Maybe<ParentLayerIntRect>& shadowClipRect =
714           aLayer->AsHostLayer()->GetShadowClipRect()) {
715     if (shadowClipRect->TopLeft() !=
716         ParentLayerIntPoint()) {  // avoid a gratuitous change of basis
717       result.ChangeBasis(shadowClipRect->X(), shadowClipRect->Y(), 0);
718     }
719   }
720   return result;
721 }
722 
ExpandRootClipRect(Layer * aLayer,const ScreenMargin & aFixedLayerMargins)723 static void ExpandRootClipRect(Layer* aLayer,
724                                const ScreenMargin& aFixedLayerMargins) {
725   // For Fennec we want to expand the root scrollable layer clip rect based on
726   // the fixed position margins. In particular, we want this while the dynamic
727   // toolbar is in the process of sliding offscreen and the area of the
728   // LayerView visible to the user is larger than the viewport size that Gecko
729   // knows about (and therefore larger than the clip rect). We could also just
730   // clear the clip rect on aLayer entirely but this seems more precise.
731   Maybe<ParentLayerIntRect> rootClipRect =
732       aLayer->AsHostLayer()->GetShadowClipRect();
733   if (rootClipRect && aFixedLayerMargins != ScreenMargin()) {
734 #ifndef MOZ_WIDGET_ANDROID
735     // We should never enter here on anything other than Fennec, since
736     // aFixedLayerMargins should be empty everywhere else.
737     MOZ_ASSERT(false);
738 #endif
739     ParentLayerRect rect(rootClipRect.value());
740     rect.Deflate(ViewAs<ParentLayerPixel>(
741         aFixedLayerMargins,
742         PixelCastJustification::ScreenIsParentLayerForRoot));
743     aLayer->AsHostLayer()->SetShadowClipRect(Some(RoundedOut(rect)));
744   }
745 }
746 
747 #ifdef MOZ_WIDGET_ANDROID
MoveScrollbarForLayerMargin(Layer * aRoot,FrameMetrics::ViewID aRootScrollId,const ScreenMargin & aFixedLayerMargins)748 static void MoveScrollbarForLayerMargin(
749     Layer* aRoot, FrameMetrics::ViewID aRootScrollId,
750     const ScreenMargin& aFixedLayerMargins) {
751   // See bug 1223928 comment 9 - once we can detect the RCD with just the
752   // isRootContent flag on the metrics, we can probably move this code into
753   // ApplyAsyncTransformToScrollbar rather than having it as a separate
754   // adjustment on the layer tree.
755   Layer* scrollbar =
756       BreadthFirstSearch<ReverseIterator>(aRoot, [aRootScrollId](Layer* aNode) {
757         return (aNode->GetScrollThumbData().mDirection.isSome() &&
758                 *aNode->GetScrollThumbData().mDirection ==
759                     ScrollDirection::eHorizontal &&
760                 aNode->GetScrollbarTargetContainerId() == aRootScrollId);
761       });
762   if (scrollbar) {
763     // Shift the horizontal scrollbar down into the new space exposed by the
764     // dynamic toolbar hiding. Technically we should also scale the vertical
765     // scrollbar a bit to expand into the new space but it's not as noticeable
766     // and it would add a lot more complexity, so we're going with the "it's not
767     // worth it" justification.
768     TranslateShadowLayer(scrollbar,
769                          ParentLayerPoint(0, -aFixedLayerMargins.bottom), true,
770                          nullptr);
771     if (scrollbar->GetParent()) {
772       // The layer that has the HORIZONTAL direction sits inside another
773       // ContainerLayer. This ContainerLayer also has a clip rect that causes
774       // the scrollbar to get clipped. We need to expand that clip rect to
775       // prevent that from happening. This is kind of ugly in that we're
776       // assuming a particular layer tree structure but short of adding more
777       // flags to the layer there doesn't appear to be a good way to do this.
778       ExpandRootClipRect(scrollbar->GetParent(), aFixedLayerMargins);
779     }
780   }
781 }
782 #endif
783 
ApplyAsyncContentTransformToTree(Layer * aLayer,bool * aOutFoundRoot)784 bool AsyncCompositionManager::ApplyAsyncContentTransformToTree(
785     Layer* aLayer, bool* aOutFoundRoot) {
786   bool appliedTransform = false;
787   std::stack<Maybe<ParentLayerIntRect>> stackDeferredClips;
788 
789   // Maps layers to their ClipParts. The parts are not stored individually
790   // on the layer, but during AlignFixedAndStickyLayers we need access to
791   // the individual parts for descendant layers.
792   ClipPartsCache clipPartsCache;
793 
794   ForEachNode<ForwardIterator>(
795       aLayer,
796       [&stackDeferredClips](Layer* layer) {
797         stackDeferredClips.push(Maybe<ParentLayerIntRect>());
798       },
799       [this, &aOutFoundRoot, &stackDeferredClips, &appliedTransform,
800        &clipPartsCache](Layer* layer) {
801         Maybe<ParentLayerIntRect> clipDeferredFromChildren =
802             stackDeferredClips.top();
803         stackDeferredClips.pop();
804         LayerToParentLayerMatrix4x4 oldTransform =
805             layer->GetTransformTyped() * AsyncTransformMatrix();
806 
807         AsyncTransformComponentMatrix combinedAsyncTransform;
808         bool hasAsyncTransform = false;
809         // Only set on the root layer for Android.
810         ScreenMargin fixedLayerMargins;
811 
812         // Each layer has multiple clips:
813         //  - Its local clip, which is fixed to the layer contents, i.e. it
814         //    moves with those async transforms which the layer contents move
815         //    with.
816         //  - Its scrolled clip, which moves with all async transforms.
817         //  - For each ScrollMetadata on the layer, a scroll clip. This
818         //    includes the composition bounds and any other clips induced by
819         //    layout. This moves with async transforms from ScrollMetadatas
820         //    above it.
821         // In this function, these clips are combined into two shadow clip
822         // parts:
823         //  - The fixed clip, which consists of the local clip only, initially
824         //    transformed by all async transforms.
825         //  - The scrolled clip, which consists of the other clips, transformed
826         //    by the appropriate transforms.
827         // These two parts are kept separate for now, because for fixed layers,
828         // we need to adjust the fixed clip (to cancel out some async
829         // transforms). The parts are kept in a cache which is cleared at the
830         // beginning of every composite. The final shadow clip for the layer is
831         // the intersection of the (possibly adjusted) fixed clip and the
832         // scrolled clip.
833         ClipParts& clipParts = clipPartsCache[layer];
834         clipParts.mFixedClip = layer->GetClipRect();
835         clipParts.mScrolledClip = layer->GetScrolledClipRect();
836 
837         // If we are a perspective transform ContainerLayer, apply the clip
838         // deferred from our child (if there is any) before we iterate over our
839         // frame metrics, because this clip is subject to all async transforms
840         // of this layer. Since this clip came from the a scroll clip on the
841         // child, it becomes part of our scrolled clip.
842         clipParts.mScrolledClip = IntersectMaybeRects(clipDeferredFromChildren,
843                                                       clipParts.mScrolledClip);
844 
845         // The transform of a mask layer is relative to the masked layer's
846         // parent layer. So whenever we apply an async transform to a layer, we
847         // need to apply that same transform to the layer's own mask layer. A
848         // layer can also have "ancestor" mask layers for any rounded clips from
849         // its ancestor scroll frames. A scroll frame mask layer only needs to
850         // be async transformed for async scrolls of this scroll frame's
851         // ancestor scroll frames, not for async scrolls of this scroll frame
852         // itself. In the loop below, we iterate over scroll frames from inside
853         // to outside. At each iteration, this array contains the layer's
854         // ancestor mask layers of all scroll frames inside the current one.
855         nsTArray<Layer*> ancestorMaskLayers;
856 
857         // The layer's scrolled clip can have an ancestor mask layer as well,
858         // which is moved by all async scrolls on this layer.
859         if (const Maybe<LayerClip>& scrolledClip = layer->GetScrolledClip()) {
860           if (scrolledClip->GetMaskLayerIndex()) {
861             ancestorMaskLayers.AppendElement(layer->GetAncestorMaskLayerAt(
862                 *scrolledClip->GetMaskLayerIndex()));
863           }
864         }
865 
866         for (uint32_t i = 0; i < layer->GetScrollMetadataCount(); i++) {
867           AsyncPanZoomController* controller =
868               layer->GetAsyncPanZoomController(i);
869           if (!controller) {
870             continue;
871           }
872 
873           hasAsyncTransform = true;
874 
875           AsyncTransform asyncTransformWithoutOverscroll =
876               controller->GetCurrentAsyncTransform(
877                   AsyncPanZoomController::eForCompositing);
878           AsyncTransformComponentMatrix overscrollTransform =
879               controller->GetOverscrollTransform(
880                   AsyncPanZoomController::eForCompositing);
881           AsyncTransformComponentMatrix asyncTransform =
882               AsyncTransformComponentMatrix(asyncTransformWithoutOverscroll) *
883               overscrollTransform;
884 
885           if (!layer->IsScrollableWithoutContent()) {
886             controller->MarkAsyncTransformAppliedToContent();
887           }
888 
889           const ScrollMetadata& scrollMetadata = layer->GetScrollMetadata(i);
890           const FrameMetrics& metrics = scrollMetadata.GetMetrics();
891 
892 #if defined(MOZ_WIDGET_ANDROID)
893           // If we find a metrics which is the root content doc, use that. If
894           // not, use the root layer. Since this function recurses on children
895           // first we should only end up using the root layer if the entire tree
896           // was devoid of a root content metrics. This is a temporary solution;
897           // in the long term we should not need the root content metrics at
898           // all. See bug 1201529 comment 6 for details.
899           if (!(*aOutFoundRoot)) {
900             *aOutFoundRoot =
901                 metrics.IsRootContent() ||        /* RCD */
902                 (layer->GetParent() == nullptr && /* rootmost metrics */
903                  i + 1 >= layer->GetScrollMetadataCount());
904             if (*aOutFoundRoot) {
905               mRootScrollableId = metrics.GetScrollId();
906               Compositor* compositor = mLayerManager->GetCompositor();
907               if (CompositorBridgeParent* bridge =
908                       compositor->GetCompositorBridgeParent()) {
909                 AndroidDynamicToolbarAnimator* animator =
910                     bridge->GetAndroidDynamicToolbarAnimator();
911                 MOZ_ASSERT(animator);
912                 if (mIsFirstPaint) {
913                   animator->UpdateRootFrameMetrics(metrics);
914                   animator->FirstPaint();
915                   mIsFirstPaint = false;
916                 }
917                 if (mLayersUpdated) {
918                   animator->NotifyLayersUpdated();
919                   mLayersUpdated = false;
920                 }
921                 // If this is not actually the root content then the animator is
922                 // not getting updated in
923                 // AsyncPanZoomController::NotifyLayersUpdated because the root
924                 // content document is not scrollable. So update it here so it
925                 // knows if the root composition size has changed.
926                 if (!metrics.IsRootContent()) {
927                   animator->MaybeUpdateCompositionSizeAndRootFrameMetrics(
928                       metrics);
929                 }
930               }
931               fixedLayerMargins = mFixedLayerMargins;
932             }
933           }
934 #else
935           *aOutFoundRoot = false;
936           // Non-Android platforms still care about this flag being cleared
937           // after the first call to TransformShadowTree().
938           mIsFirstPaint = false;
939 #endif
940 
941           // Transform the current local clips by this APZC's async transform.
942           // If we're using containerful scrolling, then the clip is not part of
943           // the scrolled frame and should not be transformed.
944           if (!scrollMetadata.UsesContainerScrolling()) {
945             MOZ_ASSERT(asyncTransform.Is2D());
946             if (clipParts.mFixedClip) {
947               *clipParts.mFixedClip =
948                   TransformBy(asyncTransform, *clipParts.mFixedClip);
949             }
950             if (clipParts.mScrolledClip) {
951               *clipParts.mScrolledClip =
952                   TransformBy(asyncTransform, *clipParts.mScrolledClip);
953             }
954           }
955           // Note: we don't set the layer's shadow clip rect property yet;
956           // AlignFixedAndStickyLayers will use the clip parts from the clip
957           // parts cache.
958 
959           combinedAsyncTransform *= asyncTransform;
960 
961           // For the purpose of aligning fixed and sticky layers, we disregard
962           // the overscroll transform as well as any OMTA transform when
963           // computing the 'aCurrentTransformForRoot' parameter. This ensures
964           // that the overscroll and OMTA transforms are not unapplied, and
965           // therefore that the visual effects apply to fixed and sticky layers.
966           // We do this by using GetTransform() as the base transform rather
967           // than GetLocalTransform(), which would include those factors.
968           LayerToParentLayerMatrix4x4 transformWithoutOverscrollOrOmta =
969               layer->GetTransformTyped() *
970               CompleteAsyncTransform(
971                   AdjustForClip(asyncTransformWithoutOverscroll, layer));
972 
973           AlignFixedAndStickyLayers(layer, layer, metrics.GetScrollId(),
974                                     oldTransform,
975                                     transformWithoutOverscrollOrOmta,
976                                     fixedLayerMargins, &clipPartsCache);
977 
978           // Combine the local clip with the ancestor scrollframe clip. This is
979           // not included in the async transform above, since the ancestor clip
980           // should not move with this APZC.
981           if (scrollMetadata.HasScrollClip()) {
982             ParentLayerIntRect clip = scrollMetadata.ScrollClip().GetClipRect();
983             if (layer->GetParent() &&
984                 layer->GetParent()->GetTransformIsPerspective()) {
985               // If our parent layer has a perspective transform, we want to
986               // apply our scroll clip to it instead of to this layer (see bug
987               // 1168263). A layer with a perspective transform shouldn't have
988               // multiple children with FrameMetrics, nor a child with multiple
989               // FrameMetrics. (A child with multiple FrameMetrics would mean
990               // that there's *another* scrollable element between the one with
991               // the CSS perspective and the transformed element. But you'd have
992               // to use preserve-3d on the inner scrollable element in order to
993               // have the perspective apply to the transformed child, and
994               // preserve-3d is not supported on scrollable elements, so this
995               // case can't occur.)
996               MOZ_ASSERT(!stackDeferredClips.top());
997               stackDeferredClips.top().emplace(clip);
998             } else {
999               clipParts.mScrolledClip =
1000                   IntersectMaybeRects(Some(clip), clipParts.mScrolledClip);
1001             }
1002           }
1003 
1004           // Do the same for the ancestor mask layers: ancestorMaskLayers
1005           // contains the ancestor mask layers for scroll frames *inside* the
1006           // current scroll frame, so these are the ones we need to shift by our
1007           // async transform.
1008           for (Layer* ancestorMaskLayer : ancestorMaskLayers) {
1009             SetShadowTransform(
1010                 ancestorMaskLayer,
1011                 ancestorMaskLayer->GetLocalTransformTyped() * asyncTransform);
1012           }
1013 
1014           // Append the ancestor mask layer for this scroll frame to
1015           // ancestorMaskLayers.
1016           if (scrollMetadata.HasScrollClip()) {
1017             const LayerClip& scrollClip = scrollMetadata.ScrollClip();
1018             if (scrollClip.GetMaskLayerIndex()) {
1019               size_t maskLayerIndex = scrollClip.GetMaskLayerIndex().value();
1020               Layer* ancestorMaskLayer =
1021                   layer->GetAncestorMaskLayerAt(maskLayerIndex);
1022               ancestorMaskLayers.AppendElement(ancestorMaskLayer);
1023             }
1024           }
1025         }
1026 
1027         bool clipChanged = (hasAsyncTransform || clipDeferredFromChildren ||
1028                             layer->GetScrolledClipRect());
1029         if (clipChanged) {
1030           // Intersect the two clip parts and apply them to the layer.
1031           // During ApplyAsyncContentTransformTree on an ancestor layer,
1032           // AlignFixedAndStickyLayers may overwrite this with a new clip it
1033           // computes from the clip parts, but if that doesn't happen, this
1034           // is the layer's final clip rect.
1035           layer->AsHostLayer()->SetShadowClipRect(clipParts.Intersect());
1036         }
1037 
1038         if (hasAsyncTransform) {
1039           // Apply the APZ transform on top of GetLocalTransform() here (rather
1040           // than GetTransform()) in case the OMTA code in SampleAnimations
1041           // already set a shadow transform; in that case we want to apply ours
1042           // on top of that one rather than clobber it.
1043           SetShadowTransform(layer,
1044                              layer->GetLocalTransformTyped() *
1045                                  AdjustForClip(combinedAsyncTransform, layer));
1046 
1047           // Do the same for the layer's own mask layer, if it has one.
1048           if (Layer* maskLayer = layer->GetMaskLayer()) {
1049             SetShadowTransform(maskLayer, maskLayer->GetLocalTransformTyped() *
1050                                               combinedAsyncTransform);
1051           }
1052 
1053           appliedTransform = true;
1054         }
1055 
1056         ExpandRootClipRect(layer, fixedLayerMargins);
1057 
1058         if (layer->GetScrollThumbData().mDirection.isSome()) {
1059           ApplyAsyncTransformToScrollbar(layer);
1060         }
1061       });
1062 
1063   return appliedTransform;
1064 }
1065 
LayerIsScrollbarTarget(const LayerMetricsWrapper & aTarget,Layer * aScrollbar)1066 static bool LayerIsScrollbarTarget(const LayerMetricsWrapper& aTarget,
1067                                    Layer* aScrollbar) {
1068   AsyncPanZoomController* apzc = aTarget.GetApzc();
1069   if (!apzc) {
1070     return false;
1071   }
1072   const FrameMetrics& metrics = aTarget.Metrics();
1073   if (metrics.GetScrollId() != aScrollbar->GetScrollbarTargetContainerId()) {
1074     return false;
1075   }
1076   return !metrics.IsScrollInfoLayer();
1077 }
1078 
ApplyAsyncTransformToScrollbarForContent(Layer * aScrollbar,const LayerMetricsWrapper & aContent,bool aScrollbarIsDescendant)1079 static void ApplyAsyncTransformToScrollbarForContent(
1080     Layer* aScrollbar, const LayerMetricsWrapper& aContent,
1081     bool aScrollbarIsDescendant) {
1082   AsyncTransformComponentMatrix clipTransform;
1083 
1084   LayerToParentLayerMatrix4x4 transform =
1085       AsyncCompositionManager::ComputeTransformForScrollThumb(
1086           aScrollbar->GetLocalTransformTyped(), aContent.GetTransform(),
1087           aContent.GetApzc(), aContent.Metrics(),
1088           aScrollbar->GetScrollThumbData(), aScrollbarIsDescendant,
1089           &clipTransform);
1090 
1091   if (aScrollbarIsDescendant) {
1092     // We also need to make a corresponding change on the clip rect of all the
1093     // layers on the ancestor chain from the scrollbar layer up to but not
1094     // including the layer with the async transform. Otherwise the scrollbar
1095     // shifts but gets clipped and so appears to flicker.
1096     for (Layer* ancestor = aScrollbar; ancestor != aContent.GetLayer();
1097          ancestor = ancestor->GetParent()) {
1098       TransformClipRect(ancestor, clipTransform);
1099     }
1100   }
1101 
1102   SetShadowTransform(aScrollbar, transform);
1103 }
1104 
1105 /* static */ LayerToParentLayerMatrix4x4
ComputeTransformForScrollThumb(const LayerToParentLayerMatrix4x4 & aCurrentTransform,const Matrix4x4 & aScrollableContentTransform,AsyncPanZoomController * aApzc,const FrameMetrics & aMetrics,const ScrollThumbData & aThumbData,bool aScrollbarIsDescendant,AsyncTransformComponentMatrix * aOutClipTransform)1106 AsyncCompositionManager::ComputeTransformForScrollThumb(
1107     const LayerToParentLayerMatrix4x4& aCurrentTransform,
1108     const Matrix4x4& aScrollableContentTransform, AsyncPanZoomController* aApzc,
1109     const FrameMetrics& aMetrics, const ScrollThumbData& aThumbData,
1110     bool aScrollbarIsDescendant,
1111     AsyncTransformComponentMatrix* aOutClipTransform) {
1112   // We only apply the transform if the scroll-target layer has non-container
1113   // children (i.e. when it has some possibly-visible content). This is to
1114   // avoid moving scroll-bars in the situation that only a scroll information
1115   // layer has been built for a scroll frame, as this would result in a
1116   // disparity between scrollbars and visible content.
1117   if (aMetrics.IsScrollInfoLayer()) {
1118     return LayerToParentLayerMatrix4x4{};
1119   }
1120 
1121   MOZ_RELEASE_ASSERT(aApzc);
1122 
1123   AsyncTransformComponentMatrix asyncTransform =
1124       aApzc->GetCurrentAsyncTransform(AsyncPanZoomController::eForCompositing);
1125 
1126   // |asyncTransform| represents the amount by which we have scrolled and
1127   // zoomed since the last paint. Because the scrollbar was sized and positioned
1128   // based on the painted content, we need to adjust it based on asyncTransform
1129   // so that it reflects what the user is actually seeing now.
1130   AsyncTransformComponentMatrix scrollbarTransform;
1131   if (*aThumbData.mDirection == ScrollDirection::eVertical) {
1132     const ParentLayerCoord asyncScrollY = asyncTransform._42;
1133     const float asyncZoomY = asyncTransform._22;
1134 
1135     // The scroll thumb needs to be scaled in the direction of scrolling by the
1136     // inverse of the async zoom. This is because zooming in decreases the
1137     // fraction of the whole srollable rect that is in view.
1138     const float yScale = 1.f / asyncZoomY;
1139 
1140     // Note: |metrics.GetZoom()| doesn't yet include the async zoom.
1141     const CSSToParentLayerScale effectiveZoom(aMetrics.GetZoom().yScale *
1142                                               asyncZoomY);
1143 
1144     // Here we convert the scrollbar thumb ratio into a true unitless ratio by
1145     // dividing out the conversion factor from the scrollframe's parent's space
1146     // to the scrollframe's space.
1147     const float ratio = aThumbData.mThumbRatio /
1148                         (aMetrics.GetPresShellResolution() * asyncZoomY);
1149     // The scroll thumb needs to be translated in opposite direction of the
1150     // async scroll. This is because scrolling down, which translates the layer
1151     // content up, should result in moving the scroll thumb down.
1152     ParentLayerCoord yTranslation = -asyncScrollY * ratio;
1153 
1154     // The scroll thumb additionally needs to be translated to compensate for
1155     // the scale applied above. The origin with respect to which the scale is
1156     // applied is the origin of the entire scrollbar, rather than the origin of
1157     // the scroll thumb (meaning, for a vertical scrollbar it's at the top of
1158     // the composition bounds). This means that empty space above the thumb
1159     // is scaled too, effectively translating the thumb. We undo that
1160     // translation here.
1161     // (One can think of the adjustment being done to the translation here as
1162     // a change of basis. We have a method to help with that,
1163     // Matrix4x4::ChangeBasis(), but it wouldn't necessarily make the code
1164     // cleaner in this case).
1165     const CSSCoord thumbOrigin = (aMetrics.GetScrollOffset().y * ratio);
1166     const CSSCoord thumbOriginScaled = thumbOrigin * yScale;
1167     const CSSCoord thumbOriginDelta = thumbOriginScaled - thumbOrigin;
1168     const ParentLayerCoord thumbOriginDeltaPL =
1169         thumbOriginDelta * effectiveZoom;
1170     yTranslation -= thumbOriginDeltaPL;
1171 
1172     if (aMetrics.IsRootContent()) {
1173       // Scrollbar for the root are painted at the same resolution as the
1174       // content. Since the coordinate space we apply this transform in includes
1175       // the resolution, we need to adjust for it as well here. Note that in
1176       // another metrics.IsRootContent() hunk below we apply a
1177       // resolution-cancelling transform which ensures the scroll thumb isn't
1178       // actually rendered at a larger scale.
1179       yTranslation *= aMetrics.GetPresShellResolution();
1180     }
1181 
1182     scrollbarTransform.PostScale(1.f, yScale, 1.f);
1183     scrollbarTransform.PostTranslate(0, yTranslation, 0);
1184   }
1185   if (*aThumbData.mDirection == ScrollDirection::eHorizontal) {
1186     // See detailed comments under the VERTICAL case.
1187 
1188     const ParentLayerCoord asyncScrollX = asyncTransform._41;
1189     const float asyncZoomX = asyncTransform._11;
1190 
1191     const float xScale = 1.f / asyncZoomX;
1192 
1193     const CSSToParentLayerScale effectiveZoom(aMetrics.GetZoom().xScale *
1194                                               asyncZoomX);
1195 
1196     const float ratio = aThumbData.mThumbRatio /
1197                         (aMetrics.GetPresShellResolution() * asyncZoomX);
1198     ParentLayerCoord xTranslation = -asyncScrollX * ratio;
1199 
1200     const CSSCoord thumbOrigin = (aMetrics.GetScrollOffset().x * ratio);
1201     const CSSCoord thumbOriginScaled = thumbOrigin * xScale;
1202     const CSSCoord thumbOriginDelta = thumbOriginScaled - thumbOrigin;
1203     const ParentLayerCoord thumbOriginDeltaPL =
1204         thumbOriginDelta * effectiveZoom;
1205     xTranslation -= thumbOriginDeltaPL;
1206 
1207     if (aMetrics.IsRootContent()) {
1208       xTranslation *= aMetrics.GetPresShellResolution();
1209     }
1210 
1211     scrollbarTransform.PostScale(xScale, 1.f, 1.f);
1212     scrollbarTransform.PostTranslate(xTranslation, 0, 0);
1213   }
1214 
1215   LayerToParentLayerMatrix4x4 transform =
1216       aCurrentTransform * scrollbarTransform;
1217 
1218   AsyncTransformComponentMatrix compensation;
1219   // If the scrollbar layer is for the root then the content's resolution
1220   // applies to the scrollbar as well. Since we don't actually want the scroll
1221   // thumb's size to vary with the zoom (other than its length reflecting the
1222   // fraction of the scrollable length that's in view, which is taken care of
1223   // above), we apply a transform to cancel out this resolution.
1224   if (aMetrics.IsRootContent()) {
1225     compensation = AsyncTransformComponentMatrix::Scaling(
1226                        aMetrics.GetPresShellResolution(),
1227                        aMetrics.GetPresShellResolution(), 1.0f)
1228                        .Inverse();
1229   }
1230   // If the scrollbar layer is a child of the content it is a scrollbar for,
1231   // then we need to adjust for any async transform (including an overscroll
1232   // transform) on the content. This needs to be cancelled out because layout
1233   // positions and sizes the scrollbar on the assumption that there is no async
1234   // transform, and without this adjustment the scrollbar will end up in the
1235   // wrong place.
1236   //
1237   // Note that since the async transform is applied on top of the content's
1238   // regular transform, we need to make sure to unapply the async transform in
1239   // the same coordinate space. This requires applying the content transform
1240   // and then unapplying it after unapplying the async transform.
1241   if (aScrollbarIsDescendant) {
1242     AsyncTransformComponentMatrix overscroll =
1243         aApzc->GetOverscrollTransform(AsyncPanZoomController::eForCompositing);
1244     Matrix4x4 asyncUntransform =
1245         (asyncTransform * overscroll).Inverse().ToUnknownMatrix();
1246     Matrix4x4 contentTransform = aScrollableContentTransform;
1247     Matrix4x4 contentUntransform = contentTransform.Inverse();
1248 
1249     compensation *= ViewAs<AsyncTransformComponentMatrix>(
1250         contentTransform * asyncUntransform * contentUntransform);
1251 
1252     // Pass the total compensation out to the caller so that it can use it
1253     // to transform clip transforms as needed.
1254     if (aOutClipTransform) {
1255       *aOutClipTransform = compensation;
1256     }
1257   }
1258   transform = transform * compensation;
1259 
1260   return transform;
1261 }
1262 
FindScrolledLayerForScrollbar(Layer * aScrollbar,bool * aOutIsAncestor)1263 static LayerMetricsWrapper FindScrolledLayerForScrollbar(Layer* aScrollbar,
1264                                                          bool* aOutIsAncestor) {
1265   // First check if the scrolled layer is an ancestor of the scrollbar layer.
1266   LayerMetricsWrapper root(aScrollbar->Manager()->GetRoot());
1267   LayerMetricsWrapper prevAncestor(aScrollbar);
1268   LayerMetricsWrapper scrolledLayer;
1269 
1270   for (LayerMetricsWrapper ancestor(aScrollbar); ancestor;
1271        ancestor = ancestor.GetParent()) {
1272     // Don't walk into remote layer trees; the scrollbar will always be in
1273     // the same layer space.
1274     if (ancestor.AsRefLayer()) {
1275       root = prevAncestor;
1276       break;
1277     }
1278     prevAncestor = ancestor;
1279 
1280     if (LayerIsScrollbarTarget(ancestor, aScrollbar)) {
1281       *aOutIsAncestor = true;
1282       return ancestor;
1283     }
1284   }
1285 
1286   // Search the entire layer space of the scrollbar.
1287   ForEachNode<ForwardIterator>(root, [&root, &scrolledLayer, &aScrollbar](
1288                                          LayerMetricsWrapper aLayerMetrics) {
1289     // Do not recurse into RefLayers, since our initial aSubtreeRoot is the
1290     // root (or RefLayer root) of a single layer space to search.
1291     if (root != aLayerMetrics && aLayerMetrics.AsRefLayer()) {
1292       return TraversalFlag::Skip;
1293     }
1294     if (LayerIsScrollbarTarget(aLayerMetrics, aScrollbar)) {
1295       scrolledLayer = aLayerMetrics;
1296       return TraversalFlag::Abort;
1297     }
1298     return TraversalFlag::Continue;
1299   });
1300   return scrolledLayer;
1301 }
1302 
ApplyAsyncTransformToScrollbar(Layer * aLayer)1303 void AsyncCompositionManager::ApplyAsyncTransformToScrollbar(Layer* aLayer) {
1304   // If this layer corresponds to a scrollbar, then there should be a layer that
1305   // is a previous sibling or a parent that has a matching ViewID on its
1306   // FrameMetrics. That is the content that this scrollbar is for. We pick up
1307   // the transient async transform from that layer and use it to update the
1308   // scrollbar position. Note that it is possible that the content layer is no
1309   // longer there; in this case we don't need to do anything because there can't
1310   // be an async transform on the content.
1311   bool isAncestor = false;
1312   const LayerMetricsWrapper& scrollTarget =
1313       FindScrolledLayerForScrollbar(aLayer, &isAncestor);
1314   if (scrollTarget) {
1315     ApplyAsyncTransformToScrollbarForContent(aLayer, scrollTarget, isAncestor);
1316   }
1317 }
1318 
GetFrameUniformity(FrameUniformityData * aOutData)1319 void AsyncCompositionManager::GetFrameUniformity(
1320     FrameUniformityData* aOutData) {
1321   MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
1322   mLayerTransformRecorder.EndTest(aOutData);
1323 }
1324 
TransformShadowTree(TimeStamp aCurrentFrame,TimeDuration aVsyncRate,TransformsToSkip aSkip)1325 bool AsyncCompositionManager::TransformShadowTree(TimeStamp aCurrentFrame,
1326                                                   TimeDuration aVsyncRate,
1327                                                   TransformsToSkip aSkip) {
1328   AUTO_PROFILER_LABEL("AsyncCompositionManager::TransformShadowTree", GRAPHICS);
1329 
1330   Layer* root = mLayerManager->GetRoot();
1331   if (!root) {
1332     return false;
1333   }
1334 
1335   CompositorAnimationStorage* storage =
1336       mCompositorBridge->GetAnimationStorage();
1337   // First, compute and set the shadow transforms from OMT animations.
1338   // NB: we must sample animations *before* sampling pan/zoom
1339   // transforms.
1340   // Use a previous vsync time to make main thread animations and compositor
1341   // more in sync with each other.
1342   // On the initial frame we use aVsyncTimestamp here so the timestamp on the
1343   // second frame are the same as the initial frame, but it does not matter.
1344   uint64_t layerAreaAnimated = 0;
1345   AnimationProcessTypes animationProcess = SampleAnimations(
1346       root, storage,
1347       !mPreviousFrameTimeStamp.IsNull() ? mPreviousFrameTimeStamp
1348                                         : aCurrentFrame,
1349       &layerAreaAnimated);
1350   bool wantNextFrame = (animationProcess != AnimationProcessTypes::eNone);
1351 
1352   mAnimationMetricsTracker.UpdateAnimationInProgress(
1353       animationProcess, layerAreaAnimated, aVsyncRate);
1354 
1355   if (!wantNextFrame) {
1356     // Clean up the CompositorAnimationStorage because
1357     // there are no active animations running
1358     storage->Clear();
1359   }
1360 
1361   // Advance animations to the next expected vsync timestamp, if we can
1362   // get it.
1363   TimeStamp nextFrame = aCurrentFrame;
1364 
1365   MOZ_ASSERT(aVsyncRate != TimeDuration::Forever());
1366   if (aVsyncRate != TimeDuration::Forever()) {
1367     nextFrame += aVsyncRate;
1368   }
1369 
1370 #if defined(MOZ_WIDGET_ANDROID)
1371   Compositor* compositor = mLayerManager->GetCompositor();
1372   if (CompositorBridgeParent* bridge =
1373           compositor->GetCompositorBridgeParent()) {
1374     AndroidDynamicToolbarAnimator* animator =
1375         bridge->GetAndroidDynamicToolbarAnimator();
1376     MOZ_ASSERT(animator);
1377     wantNextFrame |= animator->UpdateAnimation(nextFrame);
1378   }
1379 #endif  // defined(MOZ_WIDGET_ANDROID)
1380 
1381   // Reset the previous time stamp if we don't already have any running
1382   // animations to avoid using the time which is far behind for newly
1383   // started animations.
1384   mPreviousFrameTimeStamp = wantNextFrame ? aCurrentFrame : TimeStamp();
1385 
1386   if (!(aSkip & TransformsToSkip::APZ)) {
1387     // FIXME/bug 775437: unify this interface with the ~native-fennec
1388     // derived code
1389     //
1390     // Attempt to apply an async content transform to any layer that has
1391     // an async pan zoom controller (which means that it is rendered
1392     // async using Gecko). If this fails, fall back to transforming the
1393     // primary scrollable layer.  "Failing" here means that we don't
1394     // find a frame that is async scrollable.  Note that the fallback
1395     // code also includes Fennec which is rendered async.  Fennec uses
1396     // its own platform-specific async rendering that is done partially
1397     // in Gecko and partially in Java.
1398     bool foundRoot = false;
1399     if (ApplyAsyncContentTransformToTree(root, &foundRoot)) {
1400 #if defined(MOZ_WIDGET_ANDROID)
1401       MOZ_ASSERT(foundRoot);
1402       if (foundRoot && mFixedLayerMargins != ScreenMargin()) {
1403         MoveScrollbarForLayerMargin(root, mRootScrollableId,
1404                                     mFixedLayerMargins);
1405       }
1406 #endif
1407     }
1408 
1409     bool apzAnimating =
1410         SampleAPZAnimations(LayerMetricsWrapper(root), nextFrame);
1411     mAnimationMetricsTracker.UpdateApzAnimationInProgress(apzAnimating,
1412                                                           aVsyncRate);
1413     wantNextFrame |= apzAnimating;
1414   }
1415 
1416   HostLayer* rootComposite = root->AsHostLayer();
1417 
1418   gfx::Matrix4x4 trans = rootComposite->GetShadowBaseTransform();
1419   trans *= gfx::Matrix4x4::From2D(mWorldTransform);
1420   rootComposite->SetShadowBaseTransform(trans);
1421 
1422   if (gfxPrefs::CollectScrollTransforms()) {
1423     RecordShadowTransforms(root);
1424   }
1425 
1426   return wantNextFrame;
1427 }
1428 
1429 #if defined(MOZ_WIDGET_ANDROID)
SetFixedLayerMargins(ScreenIntCoord aTop,ScreenIntCoord aBottom)1430 void AsyncCompositionManager::SetFixedLayerMargins(ScreenIntCoord aTop,
1431                                                    ScreenIntCoord aBottom) {
1432   mFixedLayerMargins.top = aTop;
1433   mFixedLayerMargins.bottom = aBottom;
1434 }
1435 #endif  // defined(MOZ_WIDGET_ANDROID)
1436 
1437 }  // namespace layers
1438 }  // namespace mozilla
1439