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