LCOV - code coverage report
Current view: top level - gfx/layers/composite - AsyncCompositionManager.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 73 461 15.8 %
Date: 2018-08-07 16:42:27 Functions: 0 0 -
Legend: Lines: hit not hit

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

Generated by: LCOV version 1.13-14-ga5dd952