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