gfx/layers/composite/AsyncCompositionManager.cpp

Sat, 03 Jan 2015 20:18:00 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Sat, 03 Jan 2015 20:18:00 +0100
branch
TOR_BUG_3246
changeset 7
129ffea94266
permissions
-rw-r--r--

Conditionally enable double key logic according to:
private browsing mode or privacy.thirdparty.isolate preference and
implement in GetCookieStringCommon and FindCookie where it counts...
With some reservations of how to convince FindCookie users to test
condition and pass a nullptr when disabling double key logic.

     1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
     2 /* vim: set sw=2 ts=2 et 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/. */
     7 #include "mozilla/layers/AsyncCompositionManager.h"
     8 #include <stdint.h>                     // for uint32_t
     9 #include "AnimationCommon.h"            // for ComputedTimingFunction
    10 #include "CompositorParent.h"           // for CompositorParent, etc
    11 #include "FrameMetrics.h"               // for FrameMetrics
    12 #include "LayerManagerComposite.h"      // for LayerManagerComposite, etc
    13 #include "Layers.h"                     // for Layer, ContainerLayer, etc
    14 #include "gfxPoint.h"                   // for gfxPoint, gfxSize
    15 #include "gfxPoint3D.h"                 // for gfxPoint3D
    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/AsyncPanZoomController.h"
    22 #include "mozilla/layers/Compositor.h"  // for Compositor
    23 #include "nsAnimationManager.h"         // for ElementAnimations
    24 #include "nsCSSPropList.h"
    25 #include "nsCoord.h"                    // for NSAppUnitsToFloatPixels, etc
    26 #include "nsDebug.h"                    // for NS_ASSERTION, etc
    27 #include "nsDeviceContext.h"            // for nsDeviceContext
    28 #include "nsDisplayList.h"              // for nsDisplayTransform, etc
    29 #include "nsMathUtils.h"                // for NS_round
    30 #include "nsPoint.h"                    // for nsPoint
    31 #include "nsRect.h"                     // for nsIntRect
    32 #include "nsRegion.h"                   // for nsIntRegion
    33 #include "nsStyleAnimation.h"           // for nsStyleAnimation::Value, etc
    34 #include "nsTArray.h"                   // for nsTArray, nsTArray_Impl, etc
    35 #include "nsTArrayForwardDeclare.h"     // for InfallibleTArray
    36 #if defined(MOZ_WIDGET_ANDROID)
    37 # include <android/log.h>
    38 # include "AndroidBridge.h"
    39 #endif
    40 #include "GeckoProfiler.h"
    42 struct nsCSSValueSharedList;
    44 using namespace mozilla::dom;
    45 using namespace mozilla::gfx;
    47 namespace mozilla {
    48 namespace layers {
    50 enum Op { Resolve, Detach };
    52 static bool
    53 IsSameDimension(ScreenOrientation o1, ScreenOrientation o2)
    54 {
    55   bool isO1portrait = (o1 == eScreenOrientation_PortraitPrimary || o1 == eScreenOrientation_PortraitSecondary);
    56   bool isO2portrait = (o2 == eScreenOrientation_PortraitPrimary || o2 == eScreenOrientation_PortraitSecondary);
    57   return !(isO1portrait ^ isO2portrait);
    58 }
    60 static bool
    61 ContentMightReflowOnOrientationChange(const nsIntRect& rect)
    62 {
    63   return rect.width != rect.height;
    64 }
    66 template<Op OP>
    67 static void
    68 WalkTheTree(Layer* aLayer,
    69             bool& aReady,
    70             const TargetConfig& aTargetConfig)
    71 {
    72   if (RefLayer* ref = aLayer->AsRefLayer()) {
    73     if (const CompositorParent::LayerTreeState* state = CompositorParent::GetIndirectShadowTree(ref->GetReferentId())) {
    74       if (Layer* referent = state->mRoot) {
    75         if (!ref->GetVisibleRegion().IsEmpty()) {
    76           ScreenOrientation chromeOrientation = aTargetConfig.orientation();
    77           ScreenOrientation contentOrientation = state->mTargetConfig.orientation();
    78           if (!IsSameDimension(chromeOrientation, contentOrientation) &&
    79               ContentMightReflowOnOrientationChange(aTargetConfig.clientBounds())) {
    80             aReady = false;
    81           }
    82         }
    84         if (OP == Resolve) {
    85           ref->ConnectReferentLayer(referent);
    86         } else {
    87           ref->DetachReferentLayer(referent);
    88         }
    89       }
    90     }
    91   }
    92   for (Layer* child = aLayer->GetFirstChild();
    93        child; child = child->GetNextSibling()) {
    94     WalkTheTree<OP>(child, aReady, aTargetConfig);
    95   }
    96 }
    98 void
    99 AsyncCompositionManager::ResolveRefLayers()
   100 {
   101   if (!mLayerManager->GetRoot()) {
   102     return;
   103   }
   105   mReadyForCompose = true;
   106   WalkTheTree<Resolve>(mLayerManager->GetRoot(),
   107                        mReadyForCompose,
   108                        mTargetConfig);
   109 }
   111 void
   112 AsyncCompositionManager::DetachRefLayers()
   113 {
   114   if (!mLayerManager->GetRoot()) {
   115     return;
   116   }
   117   WalkTheTree<Detach>(mLayerManager->GetRoot(),
   118                       mReadyForCompose,
   119                       mTargetConfig);
   120 }
   122 void
   123 AsyncCompositionManager::ComputeRotation()
   124 {
   125   if (!mTargetConfig.naturalBounds().IsEmpty()) {
   126     mLayerManager->SetWorldTransform(
   127       ComputeTransformForRotation(mTargetConfig.naturalBounds(),
   128                                   mTargetConfig.rotation()));
   129   }
   130 }
   132 static bool
   133 GetBaseTransform2D(Layer* aLayer, Matrix* aTransform)
   134 {
   135   // Start with the animated transform if there is one
   136   return (aLayer->AsLayerComposite()->GetShadowTransformSetByAnimation() ?
   137           aLayer->GetLocalTransform() : aLayer->GetTransform()).Is2D(aTransform);
   138 }
   140 static void
   141 TranslateShadowLayer2D(Layer* aLayer,
   142                        const gfxPoint& aTranslation)
   143 {
   144   Matrix layerTransform;
   145   if (!GetBaseTransform2D(aLayer, &layerTransform)) {
   146     return;
   147   }
   149   // Apply the 2D translation to the layer transform.
   150   layerTransform._31 += aTranslation.x;
   151   layerTransform._32 += aTranslation.y;
   153   // The transform already takes the resolution scale into account.  Since we
   154   // will apply the resolution scale again when computing the effective
   155   // transform, we must apply the inverse resolution scale here.
   156   Matrix4x4 layerTransform3D = Matrix4x4::From2D(layerTransform);
   157   if (ContainerLayer* c = aLayer->AsContainerLayer()) {
   158     layerTransform3D.Scale(1.0f/c->GetPreXScale(),
   159                            1.0f/c->GetPreYScale(),
   160                            1);
   161   }
   162   layerTransform3D = layerTransform3D *
   163     Matrix4x4().Scale(1.0f/aLayer->GetPostXScale(),
   164                       1.0f/aLayer->GetPostYScale(),
   165                       1);
   167   LayerComposite* layerComposite = aLayer->AsLayerComposite();
   168   layerComposite->SetShadowTransform(layerTransform3D);
   169   layerComposite->SetShadowTransformSetByAnimation(false);
   171   const nsIntRect* clipRect = aLayer->GetClipRect();
   172   if (clipRect) {
   173     nsIntRect transformedClipRect(*clipRect);
   174     transformedClipRect.MoveBy(aTranslation.x, aTranslation.y);
   175     layerComposite->SetShadowClipRect(&transformedClipRect);
   176   }
   177 }
   179 static bool
   180 AccumulateLayerTransforms2D(Layer* aLayer,
   181                             Layer* aAncestor,
   182                             Matrix& aMatrix)
   183 {
   184   // Accumulate the transforms between this layer and the subtree root layer.
   185   for (Layer* l = aLayer; l && l != aAncestor; l = l->GetParent()) {
   186     Matrix l2D;
   187     if (!GetBaseTransform2D(l, &l2D)) {
   188       return false;
   189     }
   190     aMatrix *= l2D;
   191   }
   193   return true;
   194 }
   196 static LayerPoint
   197 GetLayerFixedMarginsOffset(Layer* aLayer,
   198                            const LayerMargin& aFixedLayerMargins)
   199 {
   200   // Work out the necessary translation, in root scrollable layer space.
   201   // Because fixed layer margins are stored relative to the root scrollable
   202   // layer, we can just take the difference between these values.
   203   LayerPoint translation;
   204   const LayerPoint& anchor = aLayer->GetFixedPositionAnchor();
   205   const LayerMargin& fixedMargins = aLayer->GetFixedPositionMargins();
   207   if (fixedMargins.left >= 0) {
   208     if (anchor.x > 0) {
   209       translation.x -= aFixedLayerMargins.right - fixedMargins.right;
   210     } else {
   211       translation.x += aFixedLayerMargins.left - fixedMargins.left;
   212     }
   213   }
   215   if (fixedMargins.top >= 0) {
   216     if (anchor.y > 0) {
   217       translation.y -= aFixedLayerMargins.bottom - fixedMargins.bottom;
   218     } else {
   219       translation.y += aFixedLayerMargins.top - fixedMargins.top;
   220     }
   221   }
   223   return translation;
   224 }
   226 static gfxFloat
   227 IntervalOverlap(gfxFloat aTranslation, gfxFloat aMin, gfxFloat aMax)
   228 {
   229   // Determine the amount of overlap between the 1D vector |aTranslation|
   230   // and the interval [aMin, aMax].
   231   if (aTranslation > 0) {
   232     return std::max(0.0, std::min(aMax, aTranslation) - std::max(aMin, 0.0));
   233   } else {
   234     return std::min(0.0, std::max(aMin, aTranslation) - std::min(aMax, 0.0));
   235   }
   236 }
   238 void
   239 AsyncCompositionManager::AlignFixedAndStickyLayers(Layer* aLayer,
   240                                                    Layer* aTransformedSubtreeRoot,
   241                                                    const Matrix4x4& aPreviousTransformForRoot,
   242                                                    const LayerMargin& aFixedLayerMargins)
   243 {
   244   bool isRootFixed = aLayer->GetIsFixedPosition() &&
   245     !aLayer->GetParent()->GetIsFixedPosition();
   246   bool isStickyForSubtree = aLayer->GetIsStickyPosition() &&
   247     aTransformedSubtreeRoot->AsContainerLayer() &&
   248     aLayer->GetStickyScrollContainerId() ==
   249       aTransformedSubtreeRoot->AsContainerLayer()->GetFrameMetrics().GetScrollId();
   250   if (aLayer != aTransformedSubtreeRoot && (isRootFixed || isStickyForSubtree)) {
   251     // Insert a translation so that the position of the anchor point is the same
   252     // before and after the change to the transform of aTransformedSubtreeRoot.
   253     // This currently only works for fixed layers with 2D transforms.
   255     // Accumulate the transforms between this layer and the subtree root layer.
   256     Matrix ancestorTransform;
   257     if (!AccumulateLayerTransforms2D(aLayer->GetParent(), aTransformedSubtreeRoot,
   258                                      ancestorTransform)) {
   259       return;
   260     }
   262     Matrix oldRootTransform;
   263     Matrix newRootTransform;
   264     if (!aPreviousTransformForRoot.Is2D(&oldRootTransform) ||
   265         !aTransformedSubtreeRoot->GetLocalTransform().Is2D(&newRootTransform)) {
   266       return;
   267     }
   269     // Calculate the cumulative transforms between the subtree root with the
   270     // old transform and the current transform.
   271     Matrix oldCumulativeTransform = ancestorTransform * oldRootTransform;
   272     Matrix newCumulativeTransform = ancestorTransform * newRootTransform;
   273     if (newCumulativeTransform.IsSingular()) {
   274       return;
   275     }
   276     Matrix newCumulativeTransformInverse = newCumulativeTransform;
   277     newCumulativeTransformInverse.Invert();
   279     // Now work out the translation necessary to make sure the layer doesn't
   280     // move given the new sub-tree root transform.
   281     Matrix layerTransform;
   282     if (!GetBaseTransform2D(aLayer, &layerTransform)) {
   283       return;
   284     }
   286     // Calculate any offset necessary, in previous transform sub-tree root
   287     // space. This is used to make sure fixed position content respects
   288     // content document fixed position margins.
   289     LayerPoint offsetInOldSubtreeLayerSpace = GetLayerFixedMarginsOffset(aLayer, aFixedLayerMargins);
   291     // Add the above offset to the anchor point so we can offset the layer by
   292     // and amount that's specified in old subtree layer space.
   293     const LayerPoint& anchorInOldSubtreeLayerSpace = aLayer->GetFixedPositionAnchor();
   294     LayerPoint offsetAnchorInOldSubtreeLayerSpace = anchorInOldSubtreeLayerSpace + offsetInOldSubtreeLayerSpace;
   296     // Add the local layer transform to the two points to make the equation
   297     // below this section more convenient.
   298     Point anchor(anchorInOldSubtreeLayerSpace.x, anchorInOldSubtreeLayerSpace.y);
   299     Point offsetAnchor(offsetAnchorInOldSubtreeLayerSpace.x, offsetAnchorInOldSubtreeLayerSpace.y);
   300     Point locallyTransformedAnchor = layerTransform * anchor;
   301     Point locallyTransformedOffsetAnchor = layerTransform * offsetAnchor;
   303     // Transforming the locallyTransformedAnchor by oldCumulativeTransform
   304     // returns the layer's anchor point relative to the parent of
   305     // aTransformedSubtreeRoot, before the new transform was applied.
   306     // Then, applying newCumulativeTransformInverse maps that point relative
   307     // to the layer's parent, which is the same coordinate space as
   308     // locallyTransformedAnchor again, allowing us to subtract them and find
   309     // out the offset necessary to make sure the layer stays stationary.
   310     Point oldAnchorPositionInNewSpace =
   311       newCumulativeTransformInverse * (oldCumulativeTransform * locallyTransformedOffsetAnchor);
   312     Point translation = oldAnchorPositionInNewSpace - locallyTransformedAnchor;
   314     if (aLayer->GetIsStickyPosition()) {
   315       // For sticky positioned layers, the difference between the two rectangles
   316       // defines a pair of translation intervals in each dimension through which
   317       // the layer should not move relative to the scroll container. To
   318       // accomplish this, we limit each dimension of the |translation| to that
   319       // part of it which overlaps those intervals.
   320       const LayerRect& stickyOuter = aLayer->GetStickyScrollRangeOuter();
   321       const LayerRect& stickyInner = aLayer->GetStickyScrollRangeInner();
   323       translation.y = IntervalOverlap(translation.y, stickyOuter.y, stickyOuter.YMost()) -
   324                       IntervalOverlap(translation.y, stickyInner.y, stickyInner.YMost());
   325       translation.x = IntervalOverlap(translation.x, stickyOuter.x, stickyOuter.XMost()) -
   326                       IntervalOverlap(translation.x, stickyInner.x, stickyInner.XMost());
   327     }
   329     // Finally, apply the 2D translation to the layer transform.
   330     TranslateShadowLayer2D(aLayer, ThebesPoint(translation));
   332     // The transform has now been applied, so there's no need to iterate over
   333     // child layers.
   334     return;
   335   }
   337   // Fixed layers are relative to their nearest scrollable layer, so when we
   338   // encounter a scrollable layer, reset the transform to that layer and remove
   339   // the fixed margins.
   340   if (aLayer->AsContainerLayer() &&
   341       aLayer->AsContainerLayer()->GetFrameMetrics().IsScrollable() &&
   342       aLayer != aTransformedSubtreeRoot) {
   343     AlignFixedAndStickyLayers(aLayer, aLayer, aLayer->GetTransform(), LayerMargin(0, 0, 0, 0));
   344     return;
   345   }
   347   for (Layer* child = aLayer->GetFirstChild();
   348        child; child = child->GetNextSibling()) {
   349     AlignFixedAndStickyLayers(child, aTransformedSubtreeRoot,
   350                               aPreviousTransformForRoot, aFixedLayerMargins);
   351   }
   352 }
   354 static void
   355 SampleValue(float aPortion, Animation& aAnimation, nsStyleAnimation::Value& aStart,
   356             nsStyleAnimation::Value& aEnd, Animatable* aValue)
   357 {
   358   nsStyleAnimation::Value interpolatedValue;
   359   NS_ASSERTION(aStart.GetUnit() == aEnd.GetUnit() ||
   360                aStart.GetUnit() == nsStyleAnimation::eUnit_None ||
   361                aEnd.GetUnit() == nsStyleAnimation::eUnit_None, "Must have same unit");
   362   nsStyleAnimation::Interpolate(aAnimation.property(), aStart, aEnd,
   363                                 aPortion, interpolatedValue);
   364   if (aAnimation.property() == eCSSProperty_opacity) {
   365     *aValue = interpolatedValue.GetFloatValue();
   366     return;
   367   }
   369   nsCSSValueSharedList* interpolatedList =
   370     interpolatedValue.GetCSSValueSharedListValue();
   372   TransformData& data = aAnimation.data().get_TransformData();
   373   nsPoint origin = data.origin();
   374   // we expect all our transform data to arrive in css pixels, so here we must
   375   // adjust to dev pixels.
   376   double cssPerDev = double(nsDeviceContext::AppUnitsPerCSSPixel())
   377                      / double(data.appUnitsPerDevPixel());
   378   gfxPoint3D transformOrigin = data.transformOrigin();
   379   transformOrigin.x = transformOrigin.x * cssPerDev;
   380   transformOrigin.y = transformOrigin.y * cssPerDev;
   381   gfxPoint3D perspectiveOrigin = data.perspectiveOrigin();
   382   perspectiveOrigin.x = perspectiveOrigin.x * cssPerDev;
   383   perspectiveOrigin.y = perspectiveOrigin.y * cssPerDev;
   384   nsDisplayTransform::FrameTransformProperties props(interpolatedList,
   385                                                      transformOrigin,
   386                                                      perspectiveOrigin,
   387                                                      data.perspective());
   388   gfx3DMatrix transform =
   389     nsDisplayTransform::GetResultingTransformMatrix(props, origin,
   390                                                     data.appUnitsPerDevPixel(),
   391                                                     &data.bounds());
   392   gfxPoint3D scaledOrigin =
   393     gfxPoint3D(NS_round(NSAppUnitsToFloatPixels(origin.x, data.appUnitsPerDevPixel())),
   394                NS_round(NSAppUnitsToFloatPixels(origin.y, data.appUnitsPerDevPixel())),
   395                0.0f);
   397   transform.Translate(scaledOrigin);
   399   InfallibleTArray<TransformFunction> functions;
   400   Matrix4x4 realTransform;
   401   ToMatrix4x4(transform, realTransform);
   402   functions.AppendElement(TransformMatrix(realTransform));
   403   *aValue = functions;
   404 }
   406 static bool
   407 SampleAnimations(Layer* aLayer, TimeStamp aPoint)
   408 {
   409   AnimationArray& animations = aLayer->GetAnimations();
   410   InfallibleTArray<AnimData>& animationData = aLayer->GetAnimationData();
   412   bool activeAnimations = false;
   414   for (uint32_t i = animations.Length(); i-- !=0; ) {
   415     Animation& animation = animations[i];
   416     AnimData& animData = animationData[i];
   418     activeAnimations = true;
   420     TimeDuration elapsedDuration = aPoint - animation.startTime();
   421     // Skip animations that are yet to start.
   422     //
   423     // Currently, this should only happen when the refresh driver is under test
   424     // control and is made to produce a time in the past or is restored from
   425     // test control causing it to jump backwards in time.
   426     //
   427     // Since activeAnimations is true, this could mean we keep compositing
   428     // unnecessarily during the delay, but so long as this only happens while
   429     // the refresh driver is under test control that should be ok.
   430     if (elapsedDuration.ToSeconds() < 0) {
   431       continue;
   432     }
   434     double numIterations = animation.numIterations() != -1 ?
   435       animation.numIterations() : NS_IEEEPositiveInfinity();
   436     double positionInIteration =
   437       ElementAnimations::GetPositionInIteration(elapsedDuration,
   438                                                 animation.duration(),
   439                                                 numIterations,
   440                                                 animation.direction());
   442     NS_ABORT_IF_FALSE(0.0 <= positionInIteration &&
   443                       positionInIteration <= 1.0,
   444                       "position should be in [0-1]");
   446     int segmentIndex = 0;
   447     AnimationSegment* segment = animation.segments().Elements();
   448     while (segment->endPortion() < positionInIteration) {
   449       ++segment;
   450       ++segmentIndex;
   451     }
   453     double positionInSegment = (positionInIteration - segment->startPortion()) /
   454                                  (segment->endPortion() - segment->startPortion());
   456     double portion = animData.mFunctions[segmentIndex]->GetValue(positionInSegment);
   458     // interpolate the property
   459     Animatable interpolatedValue;
   460     SampleValue(portion, animation, animData.mStartValues[segmentIndex],
   461                 animData.mEndValues[segmentIndex], &interpolatedValue);
   462     LayerComposite* layerComposite = aLayer->AsLayerComposite();
   463     switch (animation.property()) {
   464     case eCSSProperty_opacity:
   465     {
   466       layerComposite->SetShadowOpacity(interpolatedValue.get_float());
   467       break;
   468     }
   469     case eCSSProperty_transform:
   470     {
   471       Matrix4x4 matrix = interpolatedValue.get_ArrayOfTransformFunction()[0].get_TransformMatrix().value();
   472       if (ContainerLayer* c = aLayer->AsContainerLayer()) {
   473         matrix = matrix * Matrix4x4().Scale(c->GetInheritedXScale(),
   474                                             c->GetInheritedYScale(),
   475                                             1);
   476       }
   477       layerComposite->SetShadowTransform(matrix);
   478       layerComposite->SetShadowTransformSetByAnimation(true);
   479       break;
   480     }
   481     default:
   482       NS_WARNING("Unhandled animated property");
   483     }
   484   }
   486   for (Layer* child = aLayer->GetFirstChild(); child;
   487        child = child->GetNextSibling()) {
   488     activeAnimations |= SampleAnimations(child, aPoint);
   489   }
   491   return activeAnimations;
   492 }
   494 bool
   495 AsyncCompositionManager::ApplyAsyncContentTransformToTree(TimeStamp aCurrentFrame,
   496                                                           Layer *aLayer,
   497                                                           bool* aWantNextFrame)
   498 {
   499   bool appliedTransform = false;
   500   for (Layer* child = aLayer->GetFirstChild();
   501       child; child = child->GetNextSibling()) {
   502     appliedTransform |=
   503       ApplyAsyncContentTransformToTree(aCurrentFrame, child, aWantNextFrame);
   504   }
   506   ContainerLayer* container = aLayer->AsContainerLayer();
   507   if (!container) {
   508     return appliedTransform;
   509   }
   511   if (AsyncPanZoomController* controller = container->GetAsyncPanZoomController()) {
   512     LayerComposite* layerComposite = aLayer->AsLayerComposite();
   513     Matrix4x4 oldTransform = aLayer->GetTransform();
   515     ViewTransform treeTransform;
   516     ScreenPoint scrollOffset;
   517     *aWantNextFrame |=
   518       controller->SampleContentTransformForFrame(aCurrentFrame,
   519                                                  &treeTransform,
   520                                                  scrollOffset);
   522     const FrameMetrics& metrics = container->GetFrameMetrics();
   523     CSSToLayerScale paintScale = metrics.LayersPixelsPerCSSPixel();
   524     CSSRect displayPort(metrics.mCriticalDisplayPort.IsEmpty() ?
   525                         metrics.mDisplayPort : metrics.mCriticalDisplayPort);
   526     LayerMargin fixedLayerMargins(0, 0, 0, 0);
   527     ScreenPoint offset(0, 0);
   528     SyncFrameMetrics(scrollOffset, treeTransform.mScale.scale, metrics.mScrollableRect,
   529                      mLayersUpdated, displayPort, paintScale,
   530                      mIsFirstPaint, fixedLayerMargins, offset);
   532     mIsFirstPaint = false;
   533     mLayersUpdated = false;
   535     // Apply the render offset
   536     mLayerManager->GetCompositor()->SetScreenRenderOffset(offset);
   538     Matrix4x4 transform;
   539     ToMatrix4x4(gfx3DMatrix(treeTransform), transform);
   540     transform = transform * aLayer->GetTransform();
   542     // GetTransform already takes the pre- and post-scale into account.  Since we
   543     // will apply the pre- and post-scale again when computing the effective
   544     // transform, we must apply the inverses here.
   545     transform.Scale(1.0f/container->GetPreXScale(),
   546                     1.0f/container->GetPreYScale(),
   547                     1);
   548     transform = transform * Matrix4x4().Scale(1.0f/aLayer->GetPostXScale(),
   549                                               1.0f/aLayer->GetPostYScale(),
   550                                               1);
   551     layerComposite->SetShadowTransform(transform);
   552     NS_ASSERTION(!layerComposite->GetShadowTransformSetByAnimation(),
   553                  "overwriting animated transform!");
   555     // Apply resolution scaling to the old transform - the layer tree as it is
   556     // doesn't have the necessary transform to display correctly.
   557     LayoutDeviceToLayerScale resolution = metrics.mCumulativeResolution;
   558     oldTransform.Scale(resolution.scale, resolution.scale, 1);
   560     AlignFixedAndStickyLayers(aLayer, aLayer, oldTransform, fixedLayerMargins);
   562     appliedTransform = true;
   563   }
   565   if (container->GetScrollbarDirection() != Layer::NONE) {
   566     ApplyAsyncTransformToScrollbar(aCurrentFrame, container);
   567   }
   568   return appliedTransform;
   569 }
   571 static bool
   572 LayerHasNonContainerDescendants(ContainerLayer* aContainer)
   573 {
   574   for (Layer* child = aContainer->GetFirstChild();
   575        child; child = child->GetNextSibling()) {
   576     ContainerLayer* container = child->AsContainerLayer();
   577     if (!container || LayerHasNonContainerDescendants(container)) {
   578       return true;
   579     }
   580   }
   582   return false;
   583 }
   585 static bool
   586 LayerIsContainerForScrollbarTarget(Layer* aTarget, ContainerLayer* aScrollbar)
   587 {
   588   if (!aTarget->AsContainerLayer()) {
   589     return false;
   590   }
   591   AsyncPanZoomController* apzc = aTarget->AsContainerLayer()->GetAsyncPanZoomController();
   592   if (!apzc) {
   593     return false;
   594   }
   595   const FrameMetrics& metrics = aTarget->AsContainerLayer()->GetFrameMetrics();
   596   if (metrics.GetScrollId() != aScrollbar->GetScrollbarTargetContainerId()) {
   597     return false;
   598   }
   599   return true;
   600 }
   602 static void
   603 ApplyAsyncTransformToScrollbarForContent(TimeStamp aCurrentFrame, ContainerLayer* aScrollbar,
   604                                          Layer* aContent, bool aScrollbarIsChild)
   605 {
   606   ContainerLayer* content = aContent->AsContainerLayer();
   607   if (!LayerHasNonContainerDescendants(content)) {
   608     return;
   609   }
   611   const FrameMetrics& metrics = content->GetFrameMetrics();
   612   AsyncPanZoomController* apzc = content->GetAsyncPanZoomController();
   614   if (aScrollbarIsChild) {
   615     // Because we try to apply the scrollbar transform before we apply the async transform on
   616     // the actual content, we need to ensure that the APZC has updated any pending animations
   617     // to the current frame timestamp before we extract the transforms from it. The code in this
   618     // block accomplishes that and throws away the temp variables.
   619     // TODO: it might be cleaner to do a pass through the layer tree to advance all the APZC
   620     // transforms before updating the layer shadow transforms. That will allow removal of this code.
   621     ViewTransform treeTransform;
   622     ScreenPoint scrollOffset;
   623     apzc->SampleContentTransformForFrame(aCurrentFrame, &treeTransform, scrollOffset);
   624   }
   626   gfx3DMatrix asyncTransform = gfx3DMatrix(apzc->GetCurrentAsyncTransform());
   627   gfx3DMatrix nontransientTransform = apzc->GetNontransientAsyncTransform();
   628   gfx3DMatrix transientTransform = asyncTransform * nontransientTransform.Inverse();
   630   // |transientTransform| represents the amount by which we have scrolled and
   631   // zoomed since the last paint. Because the scrollbar was sized and positioned based
   632   // on the painted content, we need to adjust it based on transientTransform so that
   633   // it reflects what the user is actually seeing now.
   634   // - The scroll thumb needs to be scaled in the direction of scrolling by the inverse
   635   //   of the transientTransform scale (representing the zoom). This is because zooming
   636   //   in decreases the fraction of the whole scrollable rect that is in view.
   637   // - It needs to be translated in opposite direction of the transientTransform
   638   //   translation (representing the scroll). This is because scrolling down, which
   639   //   translates the layer content up, should result in moving the scroll thumb down.
   640   //   The amount of the translation to the scroll thumb should be such that the ratio
   641   //   of the translation to the size of the scroll port is the same as the ratio
   642   //   of the scroll amount to the size of the scrollable rect.
   643   Matrix4x4 scrollbarTransform;
   644   if (aScrollbar->GetScrollbarDirection() == Layer::VERTICAL) {
   645     float scale = metrics.CalculateCompositedSizeInCssPixels().height / metrics.mScrollableRect.height;
   646     scrollbarTransform = scrollbarTransform * Matrix4x4().Scale(1.f, 1.f / transientTransform.GetYScale(), 1.f);
   647     scrollbarTransform = scrollbarTransform * Matrix4x4().Translate(0, -transientTransform._42 * scale, 0);
   648   }
   649   if (aScrollbar->GetScrollbarDirection() == Layer::HORIZONTAL) {
   650     float scale = metrics.CalculateCompositedSizeInCssPixels().width / metrics.mScrollableRect.width;
   651     scrollbarTransform = scrollbarTransform * Matrix4x4().Scale(1.f / transientTransform.GetXScale(), 1.f, 1.f);
   652     scrollbarTransform = scrollbarTransform * Matrix4x4().Translate(-transientTransform._41 * scale, 0, 0);
   653   }
   655   Matrix4x4 transform = scrollbarTransform * aScrollbar->GetTransform();
   657   if (aScrollbarIsChild) {
   658     // If the scrollbar layer is a child of the content it is a scrollbar for, then we
   659     // need to do an extra untransform to cancel out the transient async transform on
   660     // the content. This is needed because otherwise that transient async transform is
   661     // part of the effective transform of this scrollbar, and the scrollbar will jitter
   662     // as the content scrolls.
   663     Matrix4x4 targetUntransform;
   664     ToMatrix4x4(transientTransform.Inverse(), targetUntransform);
   665     transform = transform * targetUntransform;
   666   }
   668   // GetTransform already takes the pre- and post-scale into account.  Since we
   669   // will apply the pre- and post-scale again when computing the effective
   670   // transform, we must apply the inverses here.
   671   transform.Scale(1.0f/aScrollbar->GetPreXScale(),
   672                   1.0f/aScrollbar->GetPreYScale(),
   673                   1);
   674   transform = transform * Matrix4x4().Scale(1.0f/aScrollbar->GetPostXScale(),
   675                                             1.0f/aScrollbar->GetPostYScale(),
   676                                             1);
   677   aScrollbar->AsLayerComposite()->SetShadowTransform(transform);
   678 }
   680 void
   681 AsyncCompositionManager::ApplyAsyncTransformToScrollbar(TimeStamp aCurrentFrame, ContainerLayer* aLayer)
   682 {
   683   // If this layer corresponds to a scrollbar, then there should be a layer that
   684   // is a previous sibling or a parent that has a matching ViewID on its FrameMetrics.
   685   // That is the content that this scrollbar is for. We pick up the transient
   686   // async transform from that layer and use it to update the scrollbar position.
   687   // Note that it is possible that the content layer is no longer there; in
   688   // this case we don't need to do anything because there can't be an async
   689   // transform on the content.
   690   // We only apply the transform if the scroll-target layer has non-container
   691   // children (i.e. when it has some possibly-visible content). This is to
   692   // avoid moving scroll-bars in the situation that only a scroll information
   693   // layer has been built for a scroll frame, as this would result in a
   694   // disparity between scrollbars and visible content.
   695   for (Layer* scrollTarget = aLayer->GetPrevSibling();
   696        scrollTarget;
   697        scrollTarget = scrollTarget->GetPrevSibling()) {
   698     if (LayerIsContainerForScrollbarTarget(scrollTarget, aLayer)) {
   699       // Found a sibling that matches our criteria
   700       ApplyAsyncTransformToScrollbarForContent(aCurrentFrame, aLayer, scrollTarget, false);
   701       return;
   702     }
   703   }
   705   // If we didn't find a sibling, look for a parent
   706   Layer* scrollTarget = aLayer->GetParent();
   707   if (scrollTarget && LayerIsContainerForScrollbarTarget(scrollTarget, aLayer)) {
   708     ApplyAsyncTransformToScrollbarForContent(aCurrentFrame, aLayer, scrollTarget, true);
   709   }
   710 }
   712 void
   713 AsyncCompositionManager::TransformScrollableLayer(Layer* aLayer)
   714 {
   715   LayerComposite* layerComposite = aLayer->AsLayerComposite();
   716   ContainerLayer* container = aLayer->AsContainerLayer();
   718   const FrameMetrics& metrics = container->GetFrameMetrics();
   719   // We must apply the resolution scale before a pan/zoom transform, so we call
   720   // GetTransform here.
   721   gfx3DMatrix currentTransform;
   722   To3DMatrix(aLayer->GetTransform(), currentTransform);
   723   Matrix4x4 oldTransform = aLayer->GetTransform();
   725   gfx3DMatrix treeTransform;
   727   CSSToLayerScale geckoZoom = metrics.LayersPixelsPerCSSPixel();
   729   LayerIntPoint scrollOffsetLayerPixels = RoundedToInt(metrics.GetScrollOffset() * geckoZoom);
   731   if (mIsFirstPaint) {
   732     mContentRect = metrics.mScrollableRect;
   733     SetFirstPaintViewport(scrollOffsetLayerPixels,
   734                           geckoZoom,
   735                           mContentRect);
   736     mIsFirstPaint = false;
   737   } else if (!metrics.mScrollableRect.IsEqualEdges(mContentRect)) {
   738     mContentRect = metrics.mScrollableRect;
   739     SetPageRect(mContentRect);
   740   }
   742   // We synchronise the viewport information with Java after sending the above
   743   // notifications, so that Java can take these into account in its response.
   744   // Calculate the absolute display port to send to Java
   745   LayerIntRect displayPort = RoundedToInt(
   746     (metrics.mCriticalDisplayPort.IsEmpty()
   747       ? metrics.mDisplayPort
   748       : metrics.mCriticalDisplayPort
   749     ) * geckoZoom);
   750   displayPort += scrollOffsetLayerPixels;
   752   LayerMargin fixedLayerMargins(0, 0, 0, 0);
   753   ScreenPoint offset(0, 0);
   755   // Ideally we would initialize userZoom to AsyncPanZoomController::CalculateResolution(metrics)
   756   // but this causes a reftest-ipc test to fail (see bug 883646 comment 27). The reason for this
   757   // appears to be that metrics.mZoom is poorly initialized in some scenarios. In these scenarios,
   758   // however, we can assume there is no async zooming in progress and so the following statement
   759   // works fine.
   760   CSSToScreenScale userZoom(metrics.mDevPixelsPerCSSPixel * metrics.mCumulativeResolution * LayerToScreenScale(1));
   761   ScreenPoint userScroll = metrics.GetScrollOffset() * userZoom;
   762   SyncViewportInfo(displayPort, geckoZoom, mLayersUpdated,
   763                    userScroll, userZoom, fixedLayerMargins,
   764                    offset);
   765   mLayersUpdated = false;
   767   // Apply the render offset
   768   mLayerManager->GetCompositor()->SetScreenRenderOffset(offset);
   770   // Handle transformations for asynchronous panning and zooming. We determine the
   771   // zoom used by Gecko from the transformation set on the root layer, and we
   772   // determine the scroll offset used by Gecko from the frame metrics of the
   773   // primary scrollable layer. We compare this to the user zoom and scroll
   774   // offset in the view transform we obtained from Java in order to compute the
   775   // transformation we need to apply.
   776   LayerToScreenScale zoomAdjust = userZoom / geckoZoom;
   778   LayerPoint geckoScroll(0, 0);
   779   if (metrics.IsScrollable()) {
   780     geckoScroll = metrics.GetScrollOffset() * geckoZoom;
   781   }
   783   LayerPoint translation = (userScroll / zoomAdjust) - geckoScroll;
   784   treeTransform = gfx3DMatrix(ViewTransform(-translation,
   785                                             userZoom
   786                                           / metrics.mDevPixelsPerCSSPixel
   787                                           / metrics.GetParentResolution()));
   789   // The transform already takes the resolution scale into account.  Since we
   790   // will apply the resolution scale again when computing the effective
   791   // transform, we must apply the inverse resolution scale here.
   792   gfx3DMatrix computedTransform = treeTransform * currentTransform;
   793   computedTransform.Scale(1.0f/container->GetPreXScale(),
   794                           1.0f/container->GetPreYScale(),
   795                           1);
   796   computedTransform.ScalePost(1.0f/container->GetPostXScale(),
   797                               1.0f/container->GetPostYScale(),
   798                               1);
   799   Matrix4x4 matrix;
   800   ToMatrix4x4(computedTransform, matrix);
   801   layerComposite->SetShadowTransform(matrix);
   802   NS_ASSERTION(!layerComposite->GetShadowTransformSetByAnimation(),
   803                "overwriting animated transform!");
   805   // Apply resolution scaling to the old transform - the layer tree as it is
   806   // doesn't have the necessary transform to display correctly.
   807   oldTransform.Scale(metrics.mResolution.scale, metrics.mResolution.scale, 1);
   809   // Make sure that overscroll and under-zoom are represented in the old
   810   // transform so that fixed position content moves and scales accordingly.
   811   // These calculations will effectively scale and offset fixed position layers
   812   // in screen space when the compensatory transform is performed in
   813   // AlignFixedAndStickyLayers.
   814   ScreenRect contentScreenRect = mContentRect * userZoom;
   815   gfxPoint3D overscrollTranslation;
   816   if (userScroll.x < contentScreenRect.x) {
   817     overscrollTranslation.x = contentScreenRect.x - userScroll.x;
   818   } else if (userScroll.x + metrics.mCompositionBounds.width > contentScreenRect.XMost()) {
   819     overscrollTranslation.x = contentScreenRect.XMost() -
   820       (userScroll.x + metrics.mCompositionBounds.width);
   821   }
   822   if (userScroll.y < contentScreenRect.y) {
   823     overscrollTranslation.y = contentScreenRect.y - userScroll.y;
   824   } else if (userScroll.y + metrics.mCompositionBounds.height > contentScreenRect.YMost()) {
   825     overscrollTranslation.y = contentScreenRect.YMost() -
   826       (userScroll.y + metrics.mCompositionBounds.height);
   827   }
   828   oldTransform.Translate(overscrollTranslation.x,
   829                          overscrollTranslation.y,
   830                          overscrollTranslation.z);
   832   gfx::Size underZoomScale(1.0f, 1.0f);
   833   if (mContentRect.width * userZoom.scale < metrics.mCompositionBounds.width) {
   834     underZoomScale.width = (mContentRect.width * userZoom.scale) /
   835       metrics.mCompositionBounds.width;
   836   }
   837   if (mContentRect.height * userZoom.scale < metrics.mCompositionBounds.height) {
   838     underZoomScale.height = (mContentRect.height * userZoom.scale) /
   839       metrics.mCompositionBounds.height;
   840   }
   841   oldTransform.Scale(underZoomScale.width, underZoomScale.height, 1);
   843   // Make sure fixed position layers don't move away from their anchor points
   844   // when we're asynchronously panning or zooming
   845   AlignFixedAndStickyLayers(aLayer, aLayer, oldTransform, fixedLayerMargins);
   846 }
   848 bool
   849 AsyncCompositionManager::TransformShadowTree(TimeStamp aCurrentFrame)
   850 {
   851   PROFILER_LABEL("AsyncCompositionManager", "TransformShadowTree");
   852   Layer* root = mLayerManager->GetRoot();
   853   if (!root) {
   854     return false;
   855   }
   857   // NB: we must sample animations *before* sampling pan/zoom
   858   // transforms.
   859   bool wantNextFrame = SampleAnimations(root, aCurrentFrame);
   861   // FIXME/bug 775437: unify this interface with the ~native-fennec
   862   // derived code
   863   //
   864   // Attempt to apply an async content transform to any layer that has
   865   // an async pan zoom controller (which means that it is rendered
   866   // async using Gecko). If this fails, fall back to transforming the
   867   // primary scrollable layer.  "Failing" here means that we don't
   868   // find a frame that is async scrollable.  Note that the fallback
   869   // code also includes Fennec which is rendered async.  Fennec uses
   870   // its own platform-specific async rendering that is done partially
   871   // in Gecko and partially in Java.
   872   if (!ApplyAsyncContentTransformToTree(aCurrentFrame, root, &wantNextFrame)) {
   873     nsAutoTArray<Layer*,1> scrollableLayers;
   874 #ifdef MOZ_WIDGET_ANDROID
   875     scrollableLayers.AppendElement(mLayerManager->GetPrimaryScrollableLayer());
   876 #else
   877     mLayerManager->GetScrollableLayers(scrollableLayers);
   878 #endif
   880     for (uint32_t i = 0; i < scrollableLayers.Length(); i++) {
   881       if (scrollableLayers[i]) {
   882         TransformScrollableLayer(scrollableLayers[i]);
   883       }
   884     }
   885   }
   887   return wantNextFrame;
   888 }
   890 void
   891 AsyncCompositionManager::SetFirstPaintViewport(const LayerIntPoint& aOffset,
   892                                                const CSSToLayerScale& aZoom,
   893                                                const CSSRect& aCssPageRect)
   894 {
   895 #ifdef MOZ_WIDGET_ANDROID
   896   AndroidBridge::Bridge()->SetFirstPaintViewport(aOffset, aZoom, aCssPageRect);
   897 #endif
   898 }
   900 void
   901 AsyncCompositionManager::SetPageRect(const CSSRect& aCssPageRect)
   902 {
   903 #ifdef MOZ_WIDGET_ANDROID
   904   AndroidBridge::Bridge()->SetPageRect(aCssPageRect);
   905 #endif
   906 }
   908 void
   909 AsyncCompositionManager::SyncViewportInfo(const LayerIntRect& aDisplayPort,
   910                                           const CSSToLayerScale& aDisplayResolution,
   911                                           bool aLayersUpdated,
   912                                           ScreenPoint& aScrollOffset,
   913                                           CSSToScreenScale& aScale,
   914                                           LayerMargin& aFixedLayerMargins,
   915                                           ScreenPoint& aOffset)
   916 {
   917 #ifdef MOZ_WIDGET_ANDROID
   918   AndroidBridge::Bridge()->SyncViewportInfo(aDisplayPort,
   919                                             aDisplayResolution,
   920                                             aLayersUpdated,
   921                                             aScrollOffset,
   922                                             aScale,
   923                                             aFixedLayerMargins,
   924                                             aOffset);
   925 #endif
   926 }
   928 void
   929 AsyncCompositionManager::SyncFrameMetrics(const ScreenPoint& aScrollOffset,
   930                                           float aZoom,
   931                                           const CSSRect& aCssPageRect,
   932                                           bool aLayersUpdated,
   933                                           const CSSRect& aDisplayPort,
   934                                           const CSSToLayerScale& aDisplayResolution,
   935                                           bool aIsFirstPaint,
   936                                           LayerMargin& aFixedLayerMargins,
   937                                           ScreenPoint& aOffset)
   938 {
   939 #ifdef MOZ_WIDGET_ANDROID
   940   AndroidBridge::Bridge()->SyncFrameMetrics(aScrollOffset, aZoom, aCssPageRect,
   941                                             aLayersUpdated, aDisplayPort,
   942                                             aDisplayResolution, aIsFirstPaint,
   943                                             aFixedLayerMargins, aOffset);
   944 #endif
   945 }
   947 } // namespace layers
   948 } // namespace mozilla

mercurial