Coverage Report

Created: 2018-09-25 14:53

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