1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/gfx/layers/composite/AsyncCompositionManager.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,948 @@ 1.4 +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 1.5 +/* vim: set sw=2 ts=2 et tw=80 : */ 1.6 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.7 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.8 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.9 + 1.10 +#include "mozilla/layers/AsyncCompositionManager.h" 1.11 +#include <stdint.h> // for uint32_t 1.12 +#include "AnimationCommon.h" // for ComputedTimingFunction 1.13 +#include "CompositorParent.h" // for CompositorParent, etc 1.14 +#include "FrameMetrics.h" // for FrameMetrics 1.15 +#include "LayerManagerComposite.h" // for LayerManagerComposite, etc 1.16 +#include "Layers.h" // for Layer, ContainerLayer, etc 1.17 +#include "gfxPoint.h" // for gfxPoint, gfxSize 1.18 +#include "gfxPoint3D.h" // for gfxPoint3D 1.19 +#include "mozilla/WidgetUtils.h" // for ComputeTransformForRotation 1.20 +#include "mozilla/gfx/BaseRect.h" // for BaseRect 1.21 +#include "mozilla/gfx/Point.h" // for RoundedToInt, PointTyped 1.22 +#include "mozilla/gfx/Rect.h" // for RoundedToInt, RectTyped 1.23 +#include "mozilla/gfx/ScaleFactor.h" // for ScaleFactor 1.24 +#include "mozilla/layers/AsyncPanZoomController.h" 1.25 +#include "mozilla/layers/Compositor.h" // for Compositor 1.26 +#include "nsAnimationManager.h" // for ElementAnimations 1.27 +#include "nsCSSPropList.h" 1.28 +#include "nsCoord.h" // for NSAppUnitsToFloatPixels, etc 1.29 +#include "nsDebug.h" // for NS_ASSERTION, etc 1.30 +#include "nsDeviceContext.h" // for nsDeviceContext 1.31 +#include "nsDisplayList.h" // for nsDisplayTransform, etc 1.32 +#include "nsMathUtils.h" // for NS_round 1.33 +#include "nsPoint.h" // for nsPoint 1.34 +#include "nsRect.h" // for nsIntRect 1.35 +#include "nsRegion.h" // for nsIntRegion 1.36 +#include "nsStyleAnimation.h" // for nsStyleAnimation::Value, etc 1.37 +#include "nsTArray.h" // for nsTArray, nsTArray_Impl, etc 1.38 +#include "nsTArrayForwardDeclare.h" // for InfallibleTArray 1.39 +#if defined(MOZ_WIDGET_ANDROID) 1.40 +# include <android/log.h> 1.41 +# include "AndroidBridge.h" 1.42 +#endif 1.43 +#include "GeckoProfiler.h" 1.44 + 1.45 +struct nsCSSValueSharedList; 1.46 + 1.47 +using namespace mozilla::dom; 1.48 +using namespace mozilla::gfx; 1.49 + 1.50 +namespace mozilla { 1.51 +namespace layers { 1.52 + 1.53 +enum Op { Resolve, Detach }; 1.54 + 1.55 +static bool 1.56 +IsSameDimension(ScreenOrientation o1, ScreenOrientation o2) 1.57 +{ 1.58 + bool isO1portrait = (o1 == eScreenOrientation_PortraitPrimary || o1 == eScreenOrientation_PortraitSecondary); 1.59 + bool isO2portrait = (o2 == eScreenOrientation_PortraitPrimary || o2 == eScreenOrientation_PortraitSecondary); 1.60 + return !(isO1portrait ^ isO2portrait); 1.61 +} 1.62 + 1.63 +static bool 1.64 +ContentMightReflowOnOrientationChange(const nsIntRect& rect) 1.65 +{ 1.66 + return rect.width != rect.height; 1.67 +} 1.68 + 1.69 +template<Op OP> 1.70 +static void 1.71 +WalkTheTree(Layer* aLayer, 1.72 + bool& aReady, 1.73 + const TargetConfig& aTargetConfig) 1.74 +{ 1.75 + if (RefLayer* ref = aLayer->AsRefLayer()) { 1.76 + if (const CompositorParent::LayerTreeState* state = CompositorParent::GetIndirectShadowTree(ref->GetReferentId())) { 1.77 + if (Layer* referent = state->mRoot) { 1.78 + if (!ref->GetVisibleRegion().IsEmpty()) { 1.79 + ScreenOrientation chromeOrientation = aTargetConfig.orientation(); 1.80 + ScreenOrientation contentOrientation = state->mTargetConfig.orientation(); 1.81 + if (!IsSameDimension(chromeOrientation, contentOrientation) && 1.82 + ContentMightReflowOnOrientationChange(aTargetConfig.clientBounds())) { 1.83 + aReady = false; 1.84 + } 1.85 + } 1.86 + 1.87 + if (OP == Resolve) { 1.88 + ref->ConnectReferentLayer(referent); 1.89 + } else { 1.90 + ref->DetachReferentLayer(referent); 1.91 + } 1.92 + } 1.93 + } 1.94 + } 1.95 + for (Layer* child = aLayer->GetFirstChild(); 1.96 + child; child = child->GetNextSibling()) { 1.97 + WalkTheTree<OP>(child, aReady, aTargetConfig); 1.98 + } 1.99 +} 1.100 + 1.101 +void 1.102 +AsyncCompositionManager::ResolveRefLayers() 1.103 +{ 1.104 + if (!mLayerManager->GetRoot()) { 1.105 + return; 1.106 + } 1.107 + 1.108 + mReadyForCompose = true; 1.109 + WalkTheTree<Resolve>(mLayerManager->GetRoot(), 1.110 + mReadyForCompose, 1.111 + mTargetConfig); 1.112 +} 1.113 + 1.114 +void 1.115 +AsyncCompositionManager::DetachRefLayers() 1.116 +{ 1.117 + if (!mLayerManager->GetRoot()) { 1.118 + return; 1.119 + } 1.120 + WalkTheTree<Detach>(mLayerManager->GetRoot(), 1.121 + mReadyForCompose, 1.122 + mTargetConfig); 1.123 +} 1.124 + 1.125 +void 1.126 +AsyncCompositionManager::ComputeRotation() 1.127 +{ 1.128 + if (!mTargetConfig.naturalBounds().IsEmpty()) { 1.129 + mLayerManager->SetWorldTransform( 1.130 + ComputeTransformForRotation(mTargetConfig.naturalBounds(), 1.131 + mTargetConfig.rotation())); 1.132 + } 1.133 +} 1.134 + 1.135 +static bool 1.136 +GetBaseTransform2D(Layer* aLayer, Matrix* aTransform) 1.137 +{ 1.138 + // Start with the animated transform if there is one 1.139 + return (aLayer->AsLayerComposite()->GetShadowTransformSetByAnimation() ? 1.140 + aLayer->GetLocalTransform() : aLayer->GetTransform()).Is2D(aTransform); 1.141 +} 1.142 + 1.143 +static void 1.144 +TranslateShadowLayer2D(Layer* aLayer, 1.145 + const gfxPoint& aTranslation) 1.146 +{ 1.147 + Matrix layerTransform; 1.148 + if (!GetBaseTransform2D(aLayer, &layerTransform)) { 1.149 + return; 1.150 + } 1.151 + 1.152 + // Apply the 2D translation to the layer transform. 1.153 + layerTransform._31 += aTranslation.x; 1.154 + layerTransform._32 += aTranslation.y; 1.155 + 1.156 + // The transform already takes the resolution scale into account. Since we 1.157 + // will apply the resolution scale again when computing the effective 1.158 + // transform, we must apply the inverse resolution scale here. 1.159 + Matrix4x4 layerTransform3D = Matrix4x4::From2D(layerTransform); 1.160 + if (ContainerLayer* c = aLayer->AsContainerLayer()) { 1.161 + layerTransform3D.Scale(1.0f/c->GetPreXScale(), 1.162 + 1.0f/c->GetPreYScale(), 1.163 + 1); 1.164 + } 1.165 + layerTransform3D = layerTransform3D * 1.166 + Matrix4x4().Scale(1.0f/aLayer->GetPostXScale(), 1.167 + 1.0f/aLayer->GetPostYScale(), 1.168 + 1); 1.169 + 1.170 + LayerComposite* layerComposite = aLayer->AsLayerComposite(); 1.171 + layerComposite->SetShadowTransform(layerTransform3D); 1.172 + layerComposite->SetShadowTransformSetByAnimation(false); 1.173 + 1.174 + const nsIntRect* clipRect = aLayer->GetClipRect(); 1.175 + if (clipRect) { 1.176 + nsIntRect transformedClipRect(*clipRect); 1.177 + transformedClipRect.MoveBy(aTranslation.x, aTranslation.y); 1.178 + layerComposite->SetShadowClipRect(&transformedClipRect); 1.179 + } 1.180 +} 1.181 + 1.182 +static bool 1.183 +AccumulateLayerTransforms2D(Layer* aLayer, 1.184 + Layer* aAncestor, 1.185 + Matrix& aMatrix) 1.186 +{ 1.187 + // Accumulate the transforms between this layer and the subtree root layer. 1.188 + for (Layer* l = aLayer; l && l != aAncestor; l = l->GetParent()) { 1.189 + Matrix l2D; 1.190 + if (!GetBaseTransform2D(l, &l2D)) { 1.191 + return false; 1.192 + } 1.193 + aMatrix *= l2D; 1.194 + } 1.195 + 1.196 + return true; 1.197 +} 1.198 + 1.199 +static LayerPoint 1.200 +GetLayerFixedMarginsOffset(Layer* aLayer, 1.201 + const LayerMargin& aFixedLayerMargins) 1.202 +{ 1.203 + // Work out the necessary translation, in root scrollable layer space. 1.204 + // Because fixed layer margins are stored relative to the root scrollable 1.205 + // layer, we can just take the difference between these values. 1.206 + LayerPoint translation; 1.207 + const LayerPoint& anchor = aLayer->GetFixedPositionAnchor(); 1.208 + const LayerMargin& fixedMargins = aLayer->GetFixedPositionMargins(); 1.209 + 1.210 + if (fixedMargins.left >= 0) { 1.211 + if (anchor.x > 0) { 1.212 + translation.x -= aFixedLayerMargins.right - fixedMargins.right; 1.213 + } else { 1.214 + translation.x += aFixedLayerMargins.left - fixedMargins.left; 1.215 + } 1.216 + } 1.217 + 1.218 + if (fixedMargins.top >= 0) { 1.219 + if (anchor.y > 0) { 1.220 + translation.y -= aFixedLayerMargins.bottom - fixedMargins.bottom; 1.221 + } else { 1.222 + translation.y += aFixedLayerMargins.top - fixedMargins.top; 1.223 + } 1.224 + } 1.225 + 1.226 + return translation; 1.227 +} 1.228 + 1.229 +static gfxFloat 1.230 +IntervalOverlap(gfxFloat aTranslation, gfxFloat aMin, gfxFloat aMax) 1.231 +{ 1.232 + // Determine the amount of overlap between the 1D vector |aTranslation| 1.233 + // and the interval [aMin, aMax]. 1.234 + if (aTranslation > 0) { 1.235 + return std::max(0.0, std::min(aMax, aTranslation) - std::max(aMin, 0.0)); 1.236 + } else { 1.237 + return std::min(0.0, std::max(aMin, aTranslation) - std::min(aMax, 0.0)); 1.238 + } 1.239 +} 1.240 + 1.241 +void 1.242 +AsyncCompositionManager::AlignFixedAndStickyLayers(Layer* aLayer, 1.243 + Layer* aTransformedSubtreeRoot, 1.244 + const Matrix4x4& aPreviousTransformForRoot, 1.245 + const LayerMargin& aFixedLayerMargins) 1.246 +{ 1.247 + bool isRootFixed = aLayer->GetIsFixedPosition() && 1.248 + !aLayer->GetParent()->GetIsFixedPosition(); 1.249 + bool isStickyForSubtree = aLayer->GetIsStickyPosition() && 1.250 + aTransformedSubtreeRoot->AsContainerLayer() && 1.251 + aLayer->GetStickyScrollContainerId() == 1.252 + aTransformedSubtreeRoot->AsContainerLayer()->GetFrameMetrics().GetScrollId(); 1.253 + if (aLayer != aTransformedSubtreeRoot && (isRootFixed || isStickyForSubtree)) { 1.254 + // Insert a translation so that the position of the anchor point is the same 1.255 + // before and after the change to the transform of aTransformedSubtreeRoot. 1.256 + // This currently only works for fixed layers with 2D transforms. 1.257 + 1.258 + // Accumulate the transforms between this layer and the subtree root layer. 1.259 + Matrix ancestorTransform; 1.260 + if (!AccumulateLayerTransforms2D(aLayer->GetParent(), aTransformedSubtreeRoot, 1.261 + ancestorTransform)) { 1.262 + return; 1.263 + } 1.264 + 1.265 + Matrix oldRootTransform; 1.266 + Matrix newRootTransform; 1.267 + if (!aPreviousTransformForRoot.Is2D(&oldRootTransform) || 1.268 + !aTransformedSubtreeRoot->GetLocalTransform().Is2D(&newRootTransform)) { 1.269 + return; 1.270 + } 1.271 + 1.272 + // Calculate the cumulative transforms between the subtree root with the 1.273 + // old transform and the current transform. 1.274 + Matrix oldCumulativeTransform = ancestorTransform * oldRootTransform; 1.275 + Matrix newCumulativeTransform = ancestorTransform * newRootTransform; 1.276 + if (newCumulativeTransform.IsSingular()) { 1.277 + return; 1.278 + } 1.279 + Matrix newCumulativeTransformInverse = newCumulativeTransform; 1.280 + newCumulativeTransformInverse.Invert(); 1.281 + 1.282 + // Now work out the translation necessary to make sure the layer doesn't 1.283 + // move given the new sub-tree root transform. 1.284 + Matrix layerTransform; 1.285 + if (!GetBaseTransform2D(aLayer, &layerTransform)) { 1.286 + return; 1.287 + } 1.288 + 1.289 + // Calculate any offset necessary, in previous transform sub-tree root 1.290 + // space. This is used to make sure fixed position content respects 1.291 + // content document fixed position margins. 1.292 + LayerPoint offsetInOldSubtreeLayerSpace = GetLayerFixedMarginsOffset(aLayer, aFixedLayerMargins); 1.293 + 1.294 + // Add the above offset to the anchor point so we can offset the layer by 1.295 + // and amount that's specified in old subtree layer space. 1.296 + const LayerPoint& anchorInOldSubtreeLayerSpace = aLayer->GetFixedPositionAnchor(); 1.297 + LayerPoint offsetAnchorInOldSubtreeLayerSpace = anchorInOldSubtreeLayerSpace + offsetInOldSubtreeLayerSpace; 1.298 + 1.299 + // Add the local layer transform to the two points to make the equation 1.300 + // below this section more convenient. 1.301 + Point anchor(anchorInOldSubtreeLayerSpace.x, anchorInOldSubtreeLayerSpace.y); 1.302 + Point offsetAnchor(offsetAnchorInOldSubtreeLayerSpace.x, offsetAnchorInOldSubtreeLayerSpace.y); 1.303 + Point locallyTransformedAnchor = layerTransform * anchor; 1.304 + Point locallyTransformedOffsetAnchor = layerTransform * offsetAnchor; 1.305 + 1.306 + // Transforming the locallyTransformedAnchor by oldCumulativeTransform 1.307 + // returns the layer's anchor point relative to the parent of 1.308 + // aTransformedSubtreeRoot, before the new transform was applied. 1.309 + // Then, applying newCumulativeTransformInverse maps that point relative 1.310 + // to the layer's parent, which is the same coordinate space as 1.311 + // locallyTransformedAnchor again, allowing us to subtract them and find 1.312 + // out the offset necessary to make sure the layer stays stationary. 1.313 + Point oldAnchorPositionInNewSpace = 1.314 + newCumulativeTransformInverse * (oldCumulativeTransform * locallyTransformedOffsetAnchor); 1.315 + Point translation = oldAnchorPositionInNewSpace - locallyTransformedAnchor; 1.316 + 1.317 + if (aLayer->GetIsStickyPosition()) { 1.318 + // For sticky positioned layers, the difference between the two rectangles 1.319 + // defines a pair of translation intervals in each dimension through which 1.320 + // the layer should not move relative to the scroll container. To 1.321 + // accomplish this, we limit each dimension of the |translation| to that 1.322 + // part of it which overlaps those intervals. 1.323 + const LayerRect& stickyOuter = aLayer->GetStickyScrollRangeOuter(); 1.324 + const LayerRect& stickyInner = aLayer->GetStickyScrollRangeInner(); 1.325 + 1.326 + translation.y = IntervalOverlap(translation.y, stickyOuter.y, stickyOuter.YMost()) - 1.327 + IntervalOverlap(translation.y, stickyInner.y, stickyInner.YMost()); 1.328 + translation.x = IntervalOverlap(translation.x, stickyOuter.x, stickyOuter.XMost()) - 1.329 + IntervalOverlap(translation.x, stickyInner.x, stickyInner.XMost()); 1.330 + } 1.331 + 1.332 + // Finally, apply the 2D translation to the layer transform. 1.333 + TranslateShadowLayer2D(aLayer, ThebesPoint(translation)); 1.334 + 1.335 + // The transform has now been applied, so there's no need to iterate over 1.336 + // child layers. 1.337 + return; 1.338 + } 1.339 + 1.340 + // Fixed layers are relative to their nearest scrollable layer, so when we 1.341 + // encounter a scrollable layer, reset the transform to that layer and remove 1.342 + // the fixed margins. 1.343 + if (aLayer->AsContainerLayer() && 1.344 + aLayer->AsContainerLayer()->GetFrameMetrics().IsScrollable() && 1.345 + aLayer != aTransformedSubtreeRoot) { 1.346 + AlignFixedAndStickyLayers(aLayer, aLayer, aLayer->GetTransform(), LayerMargin(0, 0, 0, 0)); 1.347 + return; 1.348 + } 1.349 + 1.350 + for (Layer* child = aLayer->GetFirstChild(); 1.351 + child; child = child->GetNextSibling()) { 1.352 + AlignFixedAndStickyLayers(child, aTransformedSubtreeRoot, 1.353 + aPreviousTransformForRoot, aFixedLayerMargins); 1.354 + } 1.355 +} 1.356 + 1.357 +static void 1.358 +SampleValue(float aPortion, Animation& aAnimation, nsStyleAnimation::Value& aStart, 1.359 + nsStyleAnimation::Value& aEnd, Animatable* aValue) 1.360 +{ 1.361 + nsStyleAnimation::Value interpolatedValue; 1.362 + NS_ASSERTION(aStart.GetUnit() == aEnd.GetUnit() || 1.363 + aStart.GetUnit() == nsStyleAnimation::eUnit_None || 1.364 + aEnd.GetUnit() == nsStyleAnimation::eUnit_None, "Must have same unit"); 1.365 + nsStyleAnimation::Interpolate(aAnimation.property(), aStart, aEnd, 1.366 + aPortion, interpolatedValue); 1.367 + if (aAnimation.property() == eCSSProperty_opacity) { 1.368 + *aValue = interpolatedValue.GetFloatValue(); 1.369 + return; 1.370 + } 1.371 + 1.372 + nsCSSValueSharedList* interpolatedList = 1.373 + interpolatedValue.GetCSSValueSharedListValue(); 1.374 + 1.375 + TransformData& data = aAnimation.data().get_TransformData(); 1.376 + nsPoint origin = data.origin(); 1.377 + // we expect all our transform data to arrive in css pixels, so here we must 1.378 + // adjust to dev pixels. 1.379 + double cssPerDev = double(nsDeviceContext::AppUnitsPerCSSPixel()) 1.380 + / double(data.appUnitsPerDevPixel()); 1.381 + gfxPoint3D transformOrigin = data.transformOrigin(); 1.382 + transformOrigin.x = transformOrigin.x * cssPerDev; 1.383 + transformOrigin.y = transformOrigin.y * cssPerDev; 1.384 + gfxPoint3D perspectiveOrigin = data.perspectiveOrigin(); 1.385 + perspectiveOrigin.x = perspectiveOrigin.x * cssPerDev; 1.386 + perspectiveOrigin.y = perspectiveOrigin.y * cssPerDev; 1.387 + nsDisplayTransform::FrameTransformProperties props(interpolatedList, 1.388 + transformOrigin, 1.389 + perspectiveOrigin, 1.390 + data.perspective()); 1.391 + gfx3DMatrix transform = 1.392 + nsDisplayTransform::GetResultingTransformMatrix(props, origin, 1.393 + data.appUnitsPerDevPixel(), 1.394 + &data.bounds()); 1.395 + gfxPoint3D scaledOrigin = 1.396 + gfxPoint3D(NS_round(NSAppUnitsToFloatPixels(origin.x, data.appUnitsPerDevPixel())), 1.397 + NS_round(NSAppUnitsToFloatPixels(origin.y, data.appUnitsPerDevPixel())), 1.398 + 0.0f); 1.399 + 1.400 + transform.Translate(scaledOrigin); 1.401 + 1.402 + InfallibleTArray<TransformFunction> functions; 1.403 + Matrix4x4 realTransform; 1.404 + ToMatrix4x4(transform, realTransform); 1.405 + functions.AppendElement(TransformMatrix(realTransform)); 1.406 + *aValue = functions; 1.407 +} 1.408 + 1.409 +static bool 1.410 +SampleAnimations(Layer* aLayer, TimeStamp aPoint) 1.411 +{ 1.412 + AnimationArray& animations = aLayer->GetAnimations(); 1.413 + InfallibleTArray<AnimData>& animationData = aLayer->GetAnimationData(); 1.414 + 1.415 + bool activeAnimations = false; 1.416 + 1.417 + for (uint32_t i = animations.Length(); i-- !=0; ) { 1.418 + Animation& animation = animations[i]; 1.419 + AnimData& animData = animationData[i]; 1.420 + 1.421 + activeAnimations = true; 1.422 + 1.423 + TimeDuration elapsedDuration = aPoint - animation.startTime(); 1.424 + // Skip animations that are yet to start. 1.425 + // 1.426 + // Currently, this should only happen when the refresh driver is under test 1.427 + // control and is made to produce a time in the past or is restored from 1.428 + // test control causing it to jump backwards in time. 1.429 + // 1.430 + // Since activeAnimations is true, this could mean we keep compositing 1.431 + // unnecessarily during the delay, but so long as this only happens while 1.432 + // the refresh driver is under test control that should be ok. 1.433 + if (elapsedDuration.ToSeconds() < 0) { 1.434 + continue; 1.435 + } 1.436 + 1.437 + double numIterations = animation.numIterations() != -1 ? 1.438 + animation.numIterations() : NS_IEEEPositiveInfinity(); 1.439 + double positionInIteration = 1.440 + ElementAnimations::GetPositionInIteration(elapsedDuration, 1.441 + animation.duration(), 1.442 + numIterations, 1.443 + animation.direction()); 1.444 + 1.445 + NS_ABORT_IF_FALSE(0.0 <= positionInIteration && 1.446 + positionInIteration <= 1.0, 1.447 + "position should be in [0-1]"); 1.448 + 1.449 + int segmentIndex = 0; 1.450 + AnimationSegment* segment = animation.segments().Elements(); 1.451 + while (segment->endPortion() < positionInIteration) { 1.452 + ++segment; 1.453 + ++segmentIndex; 1.454 + } 1.455 + 1.456 + double positionInSegment = (positionInIteration - segment->startPortion()) / 1.457 + (segment->endPortion() - segment->startPortion()); 1.458 + 1.459 + double portion = animData.mFunctions[segmentIndex]->GetValue(positionInSegment); 1.460 + 1.461 + // interpolate the property 1.462 + Animatable interpolatedValue; 1.463 + SampleValue(portion, animation, animData.mStartValues[segmentIndex], 1.464 + animData.mEndValues[segmentIndex], &interpolatedValue); 1.465 + LayerComposite* layerComposite = aLayer->AsLayerComposite(); 1.466 + switch (animation.property()) { 1.467 + case eCSSProperty_opacity: 1.468 + { 1.469 + layerComposite->SetShadowOpacity(interpolatedValue.get_float()); 1.470 + break; 1.471 + } 1.472 + case eCSSProperty_transform: 1.473 + { 1.474 + Matrix4x4 matrix = interpolatedValue.get_ArrayOfTransformFunction()[0].get_TransformMatrix().value(); 1.475 + if (ContainerLayer* c = aLayer->AsContainerLayer()) { 1.476 + matrix = matrix * Matrix4x4().Scale(c->GetInheritedXScale(), 1.477 + c->GetInheritedYScale(), 1.478 + 1); 1.479 + } 1.480 + layerComposite->SetShadowTransform(matrix); 1.481 + layerComposite->SetShadowTransformSetByAnimation(true); 1.482 + break; 1.483 + } 1.484 + default: 1.485 + NS_WARNING("Unhandled animated property"); 1.486 + } 1.487 + } 1.488 + 1.489 + for (Layer* child = aLayer->GetFirstChild(); child; 1.490 + child = child->GetNextSibling()) { 1.491 + activeAnimations |= SampleAnimations(child, aPoint); 1.492 + } 1.493 + 1.494 + return activeAnimations; 1.495 +} 1.496 + 1.497 +bool 1.498 +AsyncCompositionManager::ApplyAsyncContentTransformToTree(TimeStamp aCurrentFrame, 1.499 + Layer *aLayer, 1.500 + bool* aWantNextFrame) 1.501 +{ 1.502 + bool appliedTransform = false; 1.503 + for (Layer* child = aLayer->GetFirstChild(); 1.504 + child; child = child->GetNextSibling()) { 1.505 + appliedTransform |= 1.506 + ApplyAsyncContentTransformToTree(aCurrentFrame, child, aWantNextFrame); 1.507 + } 1.508 + 1.509 + ContainerLayer* container = aLayer->AsContainerLayer(); 1.510 + if (!container) { 1.511 + return appliedTransform; 1.512 + } 1.513 + 1.514 + if (AsyncPanZoomController* controller = container->GetAsyncPanZoomController()) { 1.515 + LayerComposite* layerComposite = aLayer->AsLayerComposite(); 1.516 + Matrix4x4 oldTransform = aLayer->GetTransform(); 1.517 + 1.518 + ViewTransform treeTransform; 1.519 + ScreenPoint scrollOffset; 1.520 + *aWantNextFrame |= 1.521 + controller->SampleContentTransformForFrame(aCurrentFrame, 1.522 + &treeTransform, 1.523 + scrollOffset); 1.524 + 1.525 + const FrameMetrics& metrics = container->GetFrameMetrics(); 1.526 + CSSToLayerScale paintScale = metrics.LayersPixelsPerCSSPixel(); 1.527 + CSSRect displayPort(metrics.mCriticalDisplayPort.IsEmpty() ? 1.528 + metrics.mDisplayPort : metrics.mCriticalDisplayPort); 1.529 + LayerMargin fixedLayerMargins(0, 0, 0, 0); 1.530 + ScreenPoint offset(0, 0); 1.531 + SyncFrameMetrics(scrollOffset, treeTransform.mScale.scale, metrics.mScrollableRect, 1.532 + mLayersUpdated, displayPort, paintScale, 1.533 + mIsFirstPaint, fixedLayerMargins, offset); 1.534 + 1.535 + mIsFirstPaint = false; 1.536 + mLayersUpdated = false; 1.537 + 1.538 + // Apply the render offset 1.539 + mLayerManager->GetCompositor()->SetScreenRenderOffset(offset); 1.540 + 1.541 + Matrix4x4 transform; 1.542 + ToMatrix4x4(gfx3DMatrix(treeTransform), transform); 1.543 + transform = transform * aLayer->GetTransform(); 1.544 + 1.545 + // GetTransform already takes the pre- and post-scale into account. Since we 1.546 + // will apply the pre- and post-scale again when computing the effective 1.547 + // transform, we must apply the inverses here. 1.548 + transform.Scale(1.0f/container->GetPreXScale(), 1.549 + 1.0f/container->GetPreYScale(), 1.550 + 1); 1.551 + transform = transform * Matrix4x4().Scale(1.0f/aLayer->GetPostXScale(), 1.552 + 1.0f/aLayer->GetPostYScale(), 1.553 + 1); 1.554 + layerComposite->SetShadowTransform(transform); 1.555 + NS_ASSERTION(!layerComposite->GetShadowTransformSetByAnimation(), 1.556 + "overwriting animated transform!"); 1.557 + 1.558 + // Apply resolution scaling to the old transform - the layer tree as it is 1.559 + // doesn't have the necessary transform to display correctly. 1.560 + LayoutDeviceToLayerScale resolution = metrics.mCumulativeResolution; 1.561 + oldTransform.Scale(resolution.scale, resolution.scale, 1); 1.562 + 1.563 + AlignFixedAndStickyLayers(aLayer, aLayer, oldTransform, fixedLayerMargins); 1.564 + 1.565 + appliedTransform = true; 1.566 + } 1.567 + 1.568 + if (container->GetScrollbarDirection() != Layer::NONE) { 1.569 + ApplyAsyncTransformToScrollbar(aCurrentFrame, container); 1.570 + } 1.571 + return appliedTransform; 1.572 +} 1.573 + 1.574 +static bool 1.575 +LayerHasNonContainerDescendants(ContainerLayer* aContainer) 1.576 +{ 1.577 + for (Layer* child = aContainer->GetFirstChild(); 1.578 + child; child = child->GetNextSibling()) { 1.579 + ContainerLayer* container = child->AsContainerLayer(); 1.580 + if (!container || LayerHasNonContainerDescendants(container)) { 1.581 + return true; 1.582 + } 1.583 + } 1.584 + 1.585 + return false; 1.586 +} 1.587 + 1.588 +static bool 1.589 +LayerIsContainerForScrollbarTarget(Layer* aTarget, ContainerLayer* aScrollbar) 1.590 +{ 1.591 + if (!aTarget->AsContainerLayer()) { 1.592 + return false; 1.593 + } 1.594 + AsyncPanZoomController* apzc = aTarget->AsContainerLayer()->GetAsyncPanZoomController(); 1.595 + if (!apzc) { 1.596 + return false; 1.597 + } 1.598 + const FrameMetrics& metrics = aTarget->AsContainerLayer()->GetFrameMetrics(); 1.599 + if (metrics.GetScrollId() != aScrollbar->GetScrollbarTargetContainerId()) { 1.600 + return false; 1.601 + } 1.602 + return true; 1.603 +} 1.604 + 1.605 +static void 1.606 +ApplyAsyncTransformToScrollbarForContent(TimeStamp aCurrentFrame, ContainerLayer* aScrollbar, 1.607 + Layer* aContent, bool aScrollbarIsChild) 1.608 +{ 1.609 + ContainerLayer* content = aContent->AsContainerLayer(); 1.610 + if (!LayerHasNonContainerDescendants(content)) { 1.611 + return; 1.612 + } 1.613 + 1.614 + const FrameMetrics& metrics = content->GetFrameMetrics(); 1.615 + AsyncPanZoomController* apzc = content->GetAsyncPanZoomController(); 1.616 + 1.617 + if (aScrollbarIsChild) { 1.618 + // Because we try to apply the scrollbar transform before we apply the async transform on 1.619 + // the actual content, we need to ensure that the APZC has updated any pending animations 1.620 + // to the current frame timestamp before we extract the transforms from it. The code in this 1.621 + // block accomplishes that and throws away the temp variables. 1.622 + // TODO: it might be cleaner to do a pass through the layer tree to advance all the APZC 1.623 + // transforms before updating the layer shadow transforms. That will allow removal of this code. 1.624 + ViewTransform treeTransform; 1.625 + ScreenPoint scrollOffset; 1.626 + apzc->SampleContentTransformForFrame(aCurrentFrame, &treeTransform, scrollOffset); 1.627 + } 1.628 + 1.629 + gfx3DMatrix asyncTransform = gfx3DMatrix(apzc->GetCurrentAsyncTransform()); 1.630 + gfx3DMatrix nontransientTransform = apzc->GetNontransientAsyncTransform(); 1.631 + gfx3DMatrix transientTransform = asyncTransform * nontransientTransform.Inverse(); 1.632 + 1.633 + // |transientTransform| represents the amount by which we have scrolled and 1.634 + // zoomed since the last paint. Because the scrollbar was sized and positioned based 1.635 + // on the painted content, we need to adjust it based on transientTransform so that 1.636 + // it reflects what the user is actually seeing now. 1.637 + // - The scroll thumb needs to be scaled in the direction of scrolling by the inverse 1.638 + // of the transientTransform scale (representing the zoom). This is because zooming 1.639 + // in decreases the fraction of the whole scrollable rect that is in view. 1.640 + // - It needs to be translated in opposite direction of the transientTransform 1.641 + // translation (representing the scroll). This is because scrolling down, which 1.642 + // translates the layer content up, should result in moving the scroll thumb down. 1.643 + // The amount of the translation to the scroll thumb should be such that the ratio 1.644 + // of the translation to the size of the scroll port is the same as the ratio 1.645 + // of the scroll amount to the size of the scrollable rect. 1.646 + Matrix4x4 scrollbarTransform; 1.647 + if (aScrollbar->GetScrollbarDirection() == Layer::VERTICAL) { 1.648 + float scale = metrics.CalculateCompositedSizeInCssPixels().height / metrics.mScrollableRect.height; 1.649 + scrollbarTransform = scrollbarTransform * Matrix4x4().Scale(1.f, 1.f / transientTransform.GetYScale(), 1.f); 1.650 + scrollbarTransform = scrollbarTransform * Matrix4x4().Translate(0, -transientTransform._42 * scale, 0); 1.651 + } 1.652 + if (aScrollbar->GetScrollbarDirection() == Layer::HORIZONTAL) { 1.653 + float scale = metrics.CalculateCompositedSizeInCssPixels().width / metrics.mScrollableRect.width; 1.654 + scrollbarTransform = scrollbarTransform * Matrix4x4().Scale(1.f / transientTransform.GetXScale(), 1.f, 1.f); 1.655 + scrollbarTransform = scrollbarTransform * Matrix4x4().Translate(-transientTransform._41 * scale, 0, 0); 1.656 + } 1.657 + 1.658 + Matrix4x4 transform = scrollbarTransform * aScrollbar->GetTransform(); 1.659 + 1.660 + if (aScrollbarIsChild) { 1.661 + // If the scrollbar layer is a child of the content it is a scrollbar for, then we 1.662 + // need to do an extra untransform to cancel out the transient async transform on 1.663 + // the content. This is needed because otherwise that transient async transform is 1.664 + // part of the effective transform of this scrollbar, and the scrollbar will jitter 1.665 + // as the content scrolls. 1.666 + Matrix4x4 targetUntransform; 1.667 + ToMatrix4x4(transientTransform.Inverse(), targetUntransform); 1.668 + transform = transform * targetUntransform; 1.669 + } 1.670 + 1.671 + // GetTransform already takes the pre- and post-scale into account. Since we 1.672 + // will apply the pre- and post-scale again when computing the effective 1.673 + // transform, we must apply the inverses here. 1.674 + transform.Scale(1.0f/aScrollbar->GetPreXScale(), 1.675 + 1.0f/aScrollbar->GetPreYScale(), 1.676 + 1); 1.677 + transform = transform * Matrix4x4().Scale(1.0f/aScrollbar->GetPostXScale(), 1.678 + 1.0f/aScrollbar->GetPostYScale(), 1.679 + 1); 1.680 + aScrollbar->AsLayerComposite()->SetShadowTransform(transform); 1.681 +} 1.682 + 1.683 +void 1.684 +AsyncCompositionManager::ApplyAsyncTransformToScrollbar(TimeStamp aCurrentFrame, ContainerLayer* aLayer) 1.685 +{ 1.686 + // If this layer corresponds to a scrollbar, then there should be a layer that 1.687 + // is a previous sibling or a parent that has a matching ViewID on its FrameMetrics. 1.688 + // That is the content that this scrollbar is for. We pick up the transient 1.689 + // async transform from that layer and use it to update the scrollbar position. 1.690 + // Note that it is possible that the content layer is no longer there; in 1.691 + // this case we don't need to do anything because there can't be an async 1.692 + // transform on the content. 1.693 + // We only apply the transform if the scroll-target layer has non-container 1.694 + // children (i.e. when it has some possibly-visible content). This is to 1.695 + // avoid moving scroll-bars in the situation that only a scroll information 1.696 + // layer has been built for a scroll frame, as this would result in a 1.697 + // disparity between scrollbars and visible content. 1.698 + for (Layer* scrollTarget = aLayer->GetPrevSibling(); 1.699 + scrollTarget; 1.700 + scrollTarget = scrollTarget->GetPrevSibling()) { 1.701 + if (LayerIsContainerForScrollbarTarget(scrollTarget, aLayer)) { 1.702 + // Found a sibling that matches our criteria 1.703 + ApplyAsyncTransformToScrollbarForContent(aCurrentFrame, aLayer, scrollTarget, false); 1.704 + return; 1.705 + } 1.706 + } 1.707 + 1.708 + // If we didn't find a sibling, look for a parent 1.709 + Layer* scrollTarget = aLayer->GetParent(); 1.710 + if (scrollTarget && LayerIsContainerForScrollbarTarget(scrollTarget, aLayer)) { 1.711 + ApplyAsyncTransformToScrollbarForContent(aCurrentFrame, aLayer, scrollTarget, true); 1.712 + } 1.713 +} 1.714 + 1.715 +void 1.716 +AsyncCompositionManager::TransformScrollableLayer(Layer* aLayer) 1.717 +{ 1.718 + LayerComposite* layerComposite = aLayer->AsLayerComposite(); 1.719 + ContainerLayer* container = aLayer->AsContainerLayer(); 1.720 + 1.721 + const FrameMetrics& metrics = container->GetFrameMetrics(); 1.722 + // We must apply the resolution scale before a pan/zoom transform, so we call 1.723 + // GetTransform here. 1.724 + gfx3DMatrix currentTransform; 1.725 + To3DMatrix(aLayer->GetTransform(), currentTransform); 1.726 + Matrix4x4 oldTransform = aLayer->GetTransform(); 1.727 + 1.728 + gfx3DMatrix treeTransform; 1.729 + 1.730 + CSSToLayerScale geckoZoom = metrics.LayersPixelsPerCSSPixel(); 1.731 + 1.732 + LayerIntPoint scrollOffsetLayerPixels = RoundedToInt(metrics.GetScrollOffset() * geckoZoom); 1.733 + 1.734 + if (mIsFirstPaint) { 1.735 + mContentRect = metrics.mScrollableRect; 1.736 + SetFirstPaintViewport(scrollOffsetLayerPixels, 1.737 + geckoZoom, 1.738 + mContentRect); 1.739 + mIsFirstPaint = false; 1.740 + } else if (!metrics.mScrollableRect.IsEqualEdges(mContentRect)) { 1.741 + mContentRect = metrics.mScrollableRect; 1.742 + SetPageRect(mContentRect); 1.743 + } 1.744 + 1.745 + // We synchronise the viewport information with Java after sending the above 1.746 + // notifications, so that Java can take these into account in its response. 1.747 + // Calculate the absolute display port to send to Java 1.748 + LayerIntRect displayPort = RoundedToInt( 1.749 + (metrics.mCriticalDisplayPort.IsEmpty() 1.750 + ? metrics.mDisplayPort 1.751 + : metrics.mCriticalDisplayPort 1.752 + ) * geckoZoom); 1.753 + displayPort += scrollOffsetLayerPixels; 1.754 + 1.755 + LayerMargin fixedLayerMargins(0, 0, 0, 0); 1.756 + ScreenPoint offset(0, 0); 1.757 + 1.758 + // Ideally we would initialize userZoom to AsyncPanZoomController::CalculateResolution(metrics) 1.759 + // but this causes a reftest-ipc test to fail (see bug 883646 comment 27). The reason for this 1.760 + // appears to be that metrics.mZoom is poorly initialized in some scenarios. In these scenarios, 1.761 + // however, we can assume there is no async zooming in progress and so the following statement 1.762 + // works fine. 1.763 + CSSToScreenScale userZoom(metrics.mDevPixelsPerCSSPixel * metrics.mCumulativeResolution * LayerToScreenScale(1)); 1.764 + ScreenPoint userScroll = metrics.GetScrollOffset() * userZoom; 1.765 + SyncViewportInfo(displayPort, geckoZoom, mLayersUpdated, 1.766 + userScroll, userZoom, fixedLayerMargins, 1.767 + offset); 1.768 + mLayersUpdated = false; 1.769 + 1.770 + // Apply the render offset 1.771 + mLayerManager->GetCompositor()->SetScreenRenderOffset(offset); 1.772 + 1.773 + // Handle transformations for asynchronous panning and zooming. We determine the 1.774 + // zoom used by Gecko from the transformation set on the root layer, and we 1.775 + // determine the scroll offset used by Gecko from the frame metrics of the 1.776 + // primary scrollable layer. We compare this to the user zoom and scroll 1.777 + // offset in the view transform we obtained from Java in order to compute the 1.778 + // transformation we need to apply. 1.779 + LayerToScreenScale zoomAdjust = userZoom / geckoZoom; 1.780 + 1.781 + LayerPoint geckoScroll(0, 0); 1.782 + if (metrics.IsScrollable()) { 1.783 + geckoScroll = metrics.GetScrollOffset() * geckoZoom; 1.784 + } 1.785 + 1.786 + LayerPoint translation = (userScroll / zoomAdjust) - geckoScroll; 1.787 + treeTransform = gfx3DMatrix(ViewTransform(-translation, 1.788 + userZoom 1.789 + / metrics.mDevPixelsPerCSSPixel 1.790 + / metrics.GetParentResolution())); 1.791 + 1.792 + // The transform already takes the resolution scale into account. Since we 1.793 + // will apply the resolution scale again when computing the effective 1.794 + // transform, we must apply the inverse resolution scale here. 1.795 + gfx3DMatrix computedTransform = treeTransform * currentTransform; 1.796 + computedTransform.Scale(1.0f/container->GetPreXScale(), 1.797 + 1.0f/container->GetPreYScale(), 1.798 + 1); 1.799 + computedTransform.ScalePost(1.0f/container->GetPostXScale(), 1.800 + 1.0f/container->GetPostYScale(), 1.801 + 1); 1.802 + Matrix4x4 matrix; 1.803 + ToMatrix4x4(computedTransform, matrix); 1.804 + layerComposite->SetShadowTransform(matrix); 1.805 + NS_ASSERTION(!layerComposite->GetShadowTransformSetByAnimation(), 1.806 + "overwriting animated transform!"); 1.807 + 1.808 + // Apply resolution scaling to the old transform - the layer tree as it is 1.809 + // doesn't have the necessary transform to display correctly. 1.810 + oldTransform.Scale(metrics.mResolution.scale, metrics.mResolution.scale, 1); 1.811 + 1.812 + // Make sure that overscroll and under-zoom are represented in the old 1.813 + // transform so that fixed position content moves and scales accordingly. 1.814 + // These calculations will effectively scale and offset fixed position layers 1.815 + // in screen space when the compensatory transform is performed in 1.816 + // AlignFixedAndStickyLayers. 1.817 + ScreenRect contentScreenRect = mContentRect * userZoom; 1.818 + gfxPoint3D overscrollTranslation; 1.819 + if (userScroll.x < contentScreenRect.x) { 1.820 + overscrollTranslation.x = contentScreenRect.x - userScroll.x; 1.821 + } else if (userScroll.x + metrics.mCompositionBounds.width > contentScreenRect.XMost()) { 1.822 + overscrollTranslation.x = contentScreenRect.XMost() - 1.823 + (userScroll.x + metrics.mCompositionBounds.width); 1.824 + } 1.825 + if (userScroll.y < contentScreenRect.y) { 1.826 + overscrollTranslation.y = contentScreenRect.y - userScroll.y; 1.827 + } else if (userScroll.y + metrics.mCompositionBounds.height > contentScreenRect.YMost()) { 1.828 + overscrollTranslation.y = contentScreenRect.YMost() - 1.829 + (userScroll.y + metrics.mCompositionBounds.height); 1.830 + } 1.831 + oldTransform.Translate(overscrollTranslation.x, 1.832 + overscrollTranslation.y, 1.833 + overscrollTranslation.z); 1.834 + 1.835 + gfx::Size underZoomScale(1.0f, 1.0f); 1.836 + if (mContentRect.width * userZoom.scale < metrics.mCompositionBounds.width) { 1.837 + underZoomScale.width = (mContentRect.width * userZoom.scale) / 1.838 + metrics.mCompositionBounds.width; 1.839 + } 1.840 + if (mContentRect.height * userZoom.scale < metrics.mCompositionBounds.height) { 1.841 + underZoomScale.height = (mContentRect.height * userZoom.scale) / 1.842 + metrics.mCompositionBounds.height; 1.843 + } 1.844 + oldTransform.Scale(underZoomScale.width, underZoomScale.height, 1); 1.845 + 1.846 + // Make sure fixed position layers don't move away from their anchor points 1.847 + // when we're asynchronously panning or zooming 1.848 + AlignFixedAndStickyLayers(aLayer, aLayer, oldTransform, fixedLayerMargins); 1.849 +} 1.850 + 1.851 +bool 1.852 +AsyncCompositionManager::TransformShadowTree(TimeStamp aCurrentFrame) 1.853 +{ 1.854 + PROFILER_LABEL("AsyncCompositionManager", "TransformShadowTree"); 1.855 + Layer* root = mLayerManager->GetRoot(); 1.856 + if (!root) { 1.857 + return false; 1.858 + } 1.859 + 1.860 + // NB: we must sample animations *before* sampling pan/zoom 1.861 + // transforms. 1.862 + bool wantNextFrame = SampleAnimations(root, aCurrentFrame); 1.863 + 1.864 + // FIXME/bug 775437: unify this interface with the ~native-fennec 1.865 + // derived code 1.866 + // 1.867 + // Attempt to apply an async content transform to any layer that has 1.868 + // an async pan zoom controller (which means that it is rendered 1.869 + // async using Gecko). If this fails, fall back to transforming the 1.870 + // primary scrollable layer. "Failing" here means that we don't 1.871 + // find a frame that is async scrollable. Note that the fallback 1.872 + // code also includes Fennec which is rendered async. Fennec uses 1.873 + // its own platform-specific async rendering that is done partially 1.874 + // in Gecko and partially in Java. 1.875 + if (!ApplyAsyncContentTransformToTree(aCurrentFrame, root, &wantNextFrame)) { 1.876 + nsAutoTArray<Layer*,1> scrollableLayers; 1.877 +#ifdef MOZ_WIDGET_ANDROID 1.878 + scrollableLayers.AppendElement(mLayerManager->GetPrimaryScrollableLayer()); 1.879 +#else 1.880 + mLayerManager->GetScrollableLayers(scrollableLayers); 1.881 +#endif 1.882 + 1.883 + for (uint32_t i = 0; i < scrollableLayers.Length(); i++) { 1.884 + if (scrollableLayers[i]) { 1.885 + TransformScrollableLayer(scrollableLayers[i]); 1.886 + } 1.887 + } 1.888 + } 1.889 + 1.890 + return wantNextFrame; 1.891 +} 1.892 + 1.893 +void 1.894 +AsyncCompositionManager::SetFirstPaintViewport(const LayerIntPoint& aOffset, 1.895 + const CSSToLayerScale& aZoom, 1.896 + const CSSRect& aCssPageRect) 1.897 +{ 1.898 +#ifdef MOZ_WIDGET_ANDROID 1.899 + AndroidBridge::Bridge()->SetFirstPaintViewport(aOffset, aZoom, aCssPageRect); 1.900 +#endif 1.901 +} 1.902 + 1.903 +void 1.904 +AsyncCompositionManager::SetPageRect(const CSSRect& aCssPageRect) 1.905 +{ 1.906 +#ifdef MOZ_WIDGET_ANDROID 1.907 + AndroidBridge::Bridge()->SetPageRect(aCssPageRect); 1.908 +#endif 1.909 +} 1.910 + 1.911 +void 1.912 +AsyncCompositionManager::SyncViewportInfo(const LayerIntRect& aDisplayPort, 1.913 + const CSSToLayerScale& aDisplayResolution, 1.914 + bool aLayersUpdated, 1.915 + ScreenPoint& aScrollOffset, 1.916 + CSSToScreenScale& aScale, 1.917 + LayerMargin& aFixedLayerMargins, 1.918 + ScreenPoint& aOffset) 1.919 +{ 1.920 +#ifdef MOZ_WIDGET_ANDROID 1.921 + AndroidBridge::Bridge()->SyncViewportInfo(aDisplayPort, 1.922 + aDisplayResolution, 1.923 + aLayersUpdated, 1.924 + aScrollOffset, 1.925 + aScale, 1.926 + aFixedLayerMargins, 1.927 + aOffset); 1.928 +#endif 1.929 +} 1.930 + 1.931 +void 1.932 +AsyncCompositionManager::SyncFrameMetrics(const ScreenPoint& aScrollOffset, 1.933 + float aZoom, 1.934 + const CSSRect& aCssPageRect, 1.935 + bool aLayersUpdated, 1.936 + const CSSRect& aDisplayPort, 1.937 + const CSSToLayerScale& aDisplayResolution, 1.938 + bool aIsFirstPaint, 1.939 + LayerMargin& aFixedLayerMargins, 1.940 + ScreenPoint& aOffset) 1.941 +{ 1.942 +#ifdef MOZ_WIDGET_ANDROID 1.943 + AndroidBridge::Bridge()->SyncFrameMetrics(aScrollOffset, aZoom, aCssPageRect, 1.944 + aLayersUpdated, aDisplayPort, 1.945 + aDisplayResolution, aIsFirstPaint, 1.946 + aFixedLayerMargins, aOffset); 1.947 +#endif 1.948 +} 1.949 + 1.950 +} // namespace layers 1.951 +} // namespace mozilla