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