Coverage Report

Created: 2018-09-25 14:53

/src/mozilla-central/gfx/layers/AnimationHelper.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 "AnimationHelper.h"
8
#include "mozilla/ComputedTimingFunction.h" // for ComputedTimingFunction
9
#include "mozilla/dom/AnimationEffectBinding.h" // for dom::FillMode
10
#include "mozilla/dom/KeyframeEffectBinding.h" // for dom::IterationComposite
11
#include "mozilla/dom/KeyframeEffect.h" // for dom::KeyFrameEffectReadOnly
12
#include "mozilla/dom/Nullable.h" // for dom::Nullable
13
#include "mozilla/layers/CompositorThread.h" // for CompositorThreadHolder
14
#include "mozilla/layers/LayerAnimationUtils.h" // for TimingFunctionToComputedTimingFunction
15
#include "mozilla/ServoBindings.h" // for Servo_ComposeAnimationSegment, etc
16
#include "mozilla/StyleAnimationValue.h" // for StyleAnimationValue, etc
17
#include "nsDeviceContext.h"            // for AppUnitsPerCSSPixel
18
#include "nsDisplayList.h"              // for nsDisplayTransform, etc
19
20
namespace mozilla {
21
namespace layers {
22
23
void
24
CompositorAnimationStorage::Clear()
25
0
{
26
0
  MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
27
0
28
0
  mAnimatedValues.Clear();
29
0
  mAnimations.Clear();
30
0
}
31
32
void
33
CompositorAnimationStorage::ClearById(const uint64_t& aId)
34
0
{
35
0
  MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
36
0
37
0
  mAnimatedValues.Remove(aId);
38
0
  mAnimations.Remove(aId);
39
0
}
40
41
AnimatedValue*
42
CompositorAnimationStorage::GetAnimatedValue(const uint64_t& aId) const
43
0
{
44
0
  MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
45
0
  return mAnimatedValues.Get(aId);
46
0
}
47
48
OMTAValue
49
CompositorAnimationStorage::GetOMTAValue(const uint64_t& aId) const
50
0
{
51
0
  OMTAValue omtaValue = mozilla::null_t();
52
0
  auto animatedValue = GetAnimatedValue(aId);
53
0
  if (!animatedValue) {
54
0
    return omtaValue;
55
0
  }
56
0
57
0
  switch (animatedValue->mType) {
58
0
    case AnimatedValue::OPACITY:
59
0
      omtaValue = animatedValue->mOpacity;
60
0
      break;
61
0
    case AnimatedValue::TRANSFORM: {
62
0
      gfx::Matrix4x4 transform = animatedValue->mTransform.mFrameTransform;
63
0
      const TransformData& data = animatedValue->mTransform.mData;
64
0
      float scale = data.appUnitsPerDevPixel();
65
0
      gfx::Point3D transformOrigin = data.transformOrigin();
66
0
67
0
      // Undo the rebasing applied by
68
0
      // nsDisplayTransform::GetResultingTransformMatrixInternal
69
0
      transform.ChangeBasis(-transformOrigin);
70
0
71
0
      // Convert to CSS pixels (this undoes the operations performed by
72
0
      // nsStyleTransformMatrix::ProcessTranslatePart which is called from
73
0
      // nsDisplayTransform::GetResultingTransformMatrix)
74
0
      double devPerCss =
75
0
        double(scale) / double(AppUnitsPerCSSPixel());
76
0
      transform._41 *= devPerCss;
77
0
      transform._42 *= devPerCss;
78
0
      transform._43 *= devPerCss;
79
0
      omtaValue = transform;
80
0
      break;
81
0
    }
82
0
    case AnimatedValue::NONE:
83
0
      break;
84
0
  }
85
0
86
0
  return omtaValue;
87
0
}
88
89
void
90
CompositorAnimationStorage::SetAnimatedValue(uint64_t aId,
91
                                             gfx::Matrix4x4&& aTransformInDevSpace,
92
                                             gfx::Matrix4x4&& aFrameTransform,
93
                                             const TransformData& aData)
94
0
{
95
0
  MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
96
0
  auto count = mAnimatedValues.Count();
97
0
  AnimatedValue* value = mAnimatedValues.LookupOrAdd(aId,
98
0
                                                     std::move(aTransformInDevSpace),
99
0
                                                     std::move(aFrameTransform),
100
0
                                                     aData);
101
0
  if (count == mAnimatedValues.Count()) {
102
0
    MOZ_ASSERT(value->mType == AnimatedValue::TRANSFORM);
103
0
    value->mTransform.mTransformInDevSpace = std::move(aTransformInDevSpace);
104
0
    value->mTransform.mFrameTransform = std::move(aFrameTransform);
105
0
    value->mTransform.mData = aData;
106
0
  }
107
0
}
108
109
void
110
CompositorAnimationStorage::SetAnimatedValue(uint64_t aId,
111
                                             gfx::Matrix4x4&& aTransformInDevSpace)
112
0
{
113
0
  MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
114
0
  const TransformData dontCare = {};
115
0
  SetAnimatedValue(aId,
116
0
                   std::move(aTransformInDevSpace),
117
0
                   gfx::Matrix4x4(),
118
0
                   dontCare);
119
0
}
120
121
void
122
CompositorAnimationStorage::SetAnimatedValue(uint64_t aId,
123
                                             const float& aOpacity)
124
0
{
125
0
  MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
126
0
  auto count = mAnimatedValues.Count();
127
0
  AnimatedValue* value = mAnimatedValues.LookupOrAdd(aId, aOpacity);
128
0
  if (count == mAnimatedValues.Count()) {
129
0
    MOZ_ASSERT(value->mType == AnimatedValue::OPACITY);
130
0
    value->mOpacity = aOpacity;
131
0
  }
132
0
}
133
134
AnimationArray*
135
CompositorAnimationStorage::GetAnimations(const uint64_t& aId) const
136
0
{
137
0
  MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
138
0
  return mAnimations.Get(aId);
139
0
}
140
141
void
142
CompositorAnimationStorage::SetAnimations(uint64_t aId, const AnimationArray& aValue)
143
0
{
144
0
  MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
145
0
  AnimationArray* value = new AnimationArray(aValue);
146
0
  mAnimations.Put(aId, value);
147
0
}
148
149
150
AnimationHelper::SampleResult
151
AnimationHelper::SampleAnimationForEachNode(
152
  TimeStamp aPreviousFrameTime,
153
  TimeStamp aCurrentFrameTime,
154
  AnimationArray& aAnimations,
155
  InfallibleTArray<AnimData>& aAnimationData,
156
  RefPtr<RawServoAnimationValue>& aAnimationValue,
157
  const AnimatedValue* aPreviousValue)
158
0
{
159
0
  MOZ_ASSERT(!aAnimations.IsEmpty(), "Should be called with animations");
160
0
161
0
  bool hasInEffectAnimations = false;
162
#ifdef DEBUG
163
  // In cases where this function returns a SampleResult::Skipped, we actually
164
  // do populate aAnimationValue in debug mode, so that we can MOZ_ASSERT at the
165
  // call site that the value that would have been computed matches the stored
166
  // value that we end up using. This flag is used to ensure we populate
167
  // aAnimationValue in this scenario.
168
  bool shouldBeSkipped = false;
169
#endif
170
  // Process in order, since later aAnimations override earlier ones.
171
0
  for (size_t i = 0, iEnd = aAnimations.Length(); i < iEnd; ++i) {
172
0
    Animation& animation = aAnimations[i];
173
0
    AnimData& animData = aAnimationData[i];
174
0
175
0
    MOZ_ASSERT((!animation.originTime().IsNull() &&
176
0
                animation.startTime().type() ==
177
0
                  MaybeTimeDuration::TTimeDuration) ||
178
0
               animation.isNotPlaying(),
179
0
               "If we are playing, we should have an origin time and a start"
180
0
               " time");
181
0
182
0
    // Determine if the animation was play-pending and used a ready time later
183
0
    // than the previous frame time.
184
0
    //
185
0
    // To determine this, _all_ of the following consitions need to hold:
186
0
    //
187
0
    // * There was no previous animation value (i.e. this is the first frame for
188
0
    //   the animation since it was sent to the compositor), and
189
0
    // * The animation is playing, and
190
0
    // * There is a previous frame time, and
191
0
    // * The ready time of the animation is ahead of the previous frame time.
192
0
    //
193
0
    bool hasFutureReadyTime = false;
194
0
    if (!aPreviousValue &&
195
0
        !animation.isNotPlaying() &&
196
0
        !aPreviousFrameTime.IsNull()) {
197
0
      // This is the inverse of the calculation performed in
198
0
      // AnimationInfo::StartPendingAnimations to calculate the start time of
199
0
      // play-pending animations.
200
0
      // Note that we have to calculate (TimeStamp + TimeDuration) last to avoid
201
0
      // underflow in the middle of the calulation.
202
0
      const TimeStamp readyTime =
203
0
        animation.originTime() +
204
0
        (animation.startTime().get_TimeDuration() +
205
0
         animation.holdTime().MultDouble(1.0 / animation.playbackRate()));
206
0
      hasFutureReadyTime =
207
0
        !readyTime.IsNull() && readyTime > aPreviousFrameTime;
208
0
    }
209
0
    // Use the previous vsync time to make main thread animations and compositor
210
0
    // more closely aligned.
211
0
    //
212
0
    // On the first frame where we have animations the previous timestamp will
213
0
    // not be set so we simply use the current timestamp.  As a result we will
214
0
    // end up painting the first frame twice.  That doesn't appear to be
215
0
    // noticeable, however.
216
0
    //
217
0
    // Likewise, if the animation is play-pending, it may have a ready time that
218
0
    // is *after* |aPreviousFrameTime| (but *before* |aCurrentFrameTime|).
219
0
    // To avoid flicker we need to use |aCurrentFrameTime| to avoid temporarily
220
0
    // jumping backwards into the range prior to when the animation starts.
221
0
    const TimeStamp& timeStamp =
222
0
      aPreviousFrameTime.IsNull() || hasFutureReadyTime
223
0
      ? aCurrentFrameTime
224
0
      : aPreviousFrameTime;
225
0
226
0
    // If the animation is not currently playing, e.g. paused or
227
0
    // finished, then use the hold time to stay at the same position.
228
0
    TimeDuration elapsedDuration =
229
0
      animation.isNotPlaying() ||
230
0
      animation.startTime().type() != MaybeTimeDuration::TTimeDuration
231
0
      ? animation.holdTime()
232
0
      : (timeStamp - animation.originTime() -
233
0
         animation.startTime().get_TimeDuration())
234
0
        .MultDouble(animation.playbackRate());
235
0
236
0
    ComputedTiming computedTiming =
237
0
      dom::AnimationEffect::GetComputedTimingAt(
238
0
        dom::Nullable<TimeDuration>(elapsedDuration), animData.mTiming,
239
0
        animation.playbackRate());
240
0
241
0
    if (computedTiming.mProgress.IsNull()) {
242
0
      continue;
243
0
    }
244
0
245
0
    dom::IterationCompositeOperation iterCompositeOperation =
246
0
        static_cast<dom::IterationCompositeOperation>(
247
0
          animation.iterationComposite());
248
0
249
0
    // Skip caluculation if the progress hasn't changed since the last
250
0
    // calculation.
251
0
    // Note that we don't skip calculate this animation if there is another
252
0
    // animation since the other animation might be 'accumulate' or 'add', or
253
0
    // might have a missing keyframe (i.e. this animation value will be used in
254
0
    // the missing keyframe).
255
0
    // FIXME Bug 1455476: We should do this optimizations for the case where
256
0
    // the layer has multiple animations.
257
0
    if (iEnd == 1 &&
258
0
        !dom::KeyframeEffect::HasComputedTimingChanged(
259
0
          computedTiming,
260
0
          iterCompositeOperation,
261
0
          animData.mProgressOnLastCompose,
262
0
          animData.mCurrentIterationOnLastCompose)) {
263
#ifdef DEBUG
264
      shouldBeSkipped = true;
265
#else
266
      return SampleResult::Skipped;
267
0
#endif
268
0
    }
269
0
270
0
    uint32_t segmentIndex = 0;
271
0
    size_t segmentSize = animation.segments().Length();
272
0
    AnimationSegment* segment = animation.segments().Elements();
273
0
    while (segment->endPortion() < computedTiming.mProgress.Value() &&
274
0
           segmentIndex < segmentSize - 1) {
275
0
      ++segment;
276
0
      ++segmentIndex;
277
0
    }
278
0
279
0
    double positionInSegment =
280
0
      (computedTiming.mProgress.Value() - segment->startPortion()) /
281
0
      (segment->endPortion() - segment->startPortion());
282
0
283
0
    double portion =
284
0
      ComputedTimingFunction::GetPortion(animData.mFunctions[segmentIndex],
285
0
                                         positionInSegment,
286
0
                                     computedTiming.mBeforeFlag);
287
0
288
0
    // Like above optimization, skip caluculation if the target segment isn't
289
0
    // changed and if the portion in the segment isn't changed.
290
0
    // This optimization is needed for CSS animations/transitions with step
291
0
    // timing functions (e.g. the throbber animation on tab or frame based
292
0
    // animations).
293
0
    // FIXME Bug 1455476: Like the above optimization, we should apply this
294
0
    // optimizations for multiple animation cases as well.
295
0
    if (iEnd == 1 &&
296
0
        animData.mSegmentIndexOnLastCompose == segmentIndex &&
297
0
        !animData.mPortionInSegmentOnLastCompose.IsNull() &&
298
0
        animData.mPortionInSegmentOnLastCompose.Value() == portion) {
299
#ifdef DEBUG
300
      shouldBeSkipped = true;
301
#else
302
      return SampleResult::Skipped;
303
0
#endif
304
0
    }
305
0
306
0
    AnimationPropertySegment animSegment;
307
0
    animSegment.mFromKey = 0.0;
308
0
    animSegment.mToKey = 1.0;
309
0
    animSegment.mFromValue =
310
0
      AnimationValue(animData.mStartValues[segmentIndex]);
311
0
    animSegment.mToValue =
312
0
      AnimationValue(animData.mEndValues[segmentIndex]);
313
0
    animSegment.mFromComposite =
314
0
      static_cast<dom::CompositeOperation>(segment->startComposite());
315
0
    animSegment.mToComposite =
316
0
      static_cast<dom::CompositeOperation>(segment->endComposite());
317
0
318
0
    // interpolate the property
319
0
    aAnimationValue =
320
0
      Servo_ComposeAnimationSegment(
321
0
        &animSegment,
322
0
        aAnimationValue,
323
0
        animData.mEndValues.LastElement(),
324
0
        iterCompositeOperation,
325
0
        portion,
326
0
        computedTiming.mCurrentIteration).Consume();
327
0
328
#ifdef DEBUG
329
    if (shouldBeSkipped) {
330
      return SampleResult::Skipped;
331
    }
332
#endif
333
334
0
    hasInEffectAnimations = true;
335
0
    animData.mProgressOnLastCompose = computedTiming.mProgress;
336
0
    animData.mCurrentIterationOnLastCompose = computedTiming.mCurrentIteration;
337
0
    animData.mSegmentIndexOnLastCompose = segmentIndex;
338
0
    animData.mPortionInSegmentOnLastCompose.SetValue(portion);
339
0
  }
340
0
341
#ifdef DEBUG
342
  // Sanity check that all of animation data are the same.
343
  const AnimationData& lastData = aAnimations.LastElement().data();
344
  for (const Animation& animation : aAnimations) {
345
    const AnimationData& data = animation.data();
346
    MOZ_ASSERT(data.type() == lastData.type(),
347
               "The type of AnimationData should be the same");
348
    if (data.type() == AnimationData::Tnull_t) {
349
      continue;
350
    }
351
352
    MOZ_ASSERT(data.type() == AnimationData::TTransformData);
353
    const TransformData& transformData = data.get_TransformData();
354
    const TransformData& lastTransformData = lastData.get_TransformData();
355
    MOZ_ASSERT(transformData.origin() == lastTransformData.origin() &&
356
               transformData.transformOrigin() ==
357
                 lastTransformData.transformOrigin() &&
358
               transformData.bounds() == lastTransformData.bounds() &&
359
               transformData.appUnitsPerDevPixel() ==
360
                 lastTransformData.appUnitsPerDevPixel(),
361
               "All of members of TransformData should be the same");
362
  }
363
#endif
364
365
0
  return hasInEffectAnimations ? SampleResult::Sampled : SampleResult::None;
366
0
}
367
368
struct BogusAnimation {};
369
370
static inline Result<Ok, BogusAnimation>
371
SetCSSAngle(const CSSAngle& aAngle, nsCSSValue& aValue)
372
0
{
373
0
  aValue.SetFloatValue(aAngle.value(), nsCSSUnit(aAngle.unit()));
374
0
  if (!aValue.IsAngularUnit()) {
375
0
    NS_ERROR("Bogus animation from IPC");
376
0
    return Err(BogusAnimation { });
377
0
  }
378
0
  return Ok();
379
0
}
380
381
static Result<nsCSSValueSharedList*, BogusAnimation>
382
CreateCSSValueList(const InfallibleTArray<TransformFunction>& aFunctions)
383
0
{
384
0
  nsAutoPtr<nsCSSValueList> result;
385
0
  nsCSSValueList** resultTail = getter_Transfers(result);
386
0
  for (uint32_t i = 0; i < aFunctions.Length(); i++) {
387
0
    RefPtr<nsCSSValue::Array> arr;
388
0
    switch (aFunctions[i].type()) {
389
0
      case TransformFunction::TRotationX:
390
0
      {
391
0
        const CSSAngle& angle = aFunctions[i].get_RotationX().angle();
392
0
        arr = AnimationValue::AppendTransformFunction(eCSSKeyword_rotatex,
393
0
                                                      resultTail);
394
0
        MOZ_TRY(SetCSSAngle(angle, arr->Item(1)));
395
0
        break;
396
0
      }
397
0
      case TransformFunction::TRotationY:
398
0
      {
399
0
        const CSSAngle& angle = aFunctions[i].get_RotationY().angle();
400
0
        arr = AnimationValue::AppendTransformFunction(eCSSKeyword_rotatey,
401
0
                                                      resultTail);
402
0
        MOZ_TRY(SetCSSAngle(angle, arr->Item(1)));
403
0
        break;
404
0
      }
405
0
      case TransformFunction::TRotationZ:
406
0
      {
407
0
        const CSSAngle& angle = aFunctions[i].get_RotationZ().angle();
408
0
        arr = AnimationValue::AppendTransformFunction(eCSSKeyword_rotatez,
409
0
                                                      resultTail);
410
0
        MOZ_TRY(SetCSSAngle(angle, arr->Item(1)));
411
0
        break;
412
0
      }
413
0
      case TransformFunction::TRotation:
414
0
      {
415
0
        const CSSAngle& angle = aFunctions[i].get_Rotation().angle();
416
0
        arr = AnimationValue::AppendTransformFunction(eCSSKeyword_rotate,
417
0
                                                      resultTail);
418
0
        MOZ_TRY(SetCSSAngle(angle, arr->Item(1)));
419
0
        break;
420
0
      }
421
0
      case TransformFunction::TRotation3D:
422
0
      {
423
0
        float x = aFunctions[i].get_Rotation3D().x();
424
0
        float y = aFunctions[i].get_Rotation3D().y();
425
0
        float z = aFunctions[i].get_Rotation3D().z();
426
0
        const CSSAngle& angle = aFunctions[i].get_Rotation3D().angle();
427
0
        arr = AnimationValue::AppendTransformFunction(eCSSKeyword_rotate3d,
428
0
                                                      resultTail);
429
0
        arr->Item(1).SetFloatValue(x, eCSSUnit_Number);
430
0
        arr->Item(2).SetFloatValue(y, eCSSUnit_Number);
431
0
        arr->Item(3).SetFloatValue(z, eCSSUnit_Number);
432
0
        MOZ_TRY(SetCSSAngle(angle, arr->Item(4)));
433
0
        break;
434
0
      }
435
0
      case TransformFunction::TScale:
436
0
      {
437
0
        arr = AnimationValue::AppendTransformFunction(eCSSKeyword_scale3d,
438
0
                                                      resultTail);
439
0
        arr->Item(1).SetFloatValue(aFunctions[i].get_Scale().x(), eCSSUnit_Number);
440
0
        arr->Item(2).SetFloatValue(aFunctions[i].get_Scale().y(), eCSSUnit_Number);
441
0
        arr->Item(3).SetFloatValue(aFunctions[i].get_Scale().z(), eCSSUnit_Number);
442
0
        break;
443
0
      }
444
0
      case TransformFunction::TTranslation:
445
0
      {
446
0
        arr = AnimationValue::AppendTransformFunction(eCSSKeyword_translate3d,
447
0
                                                      resultTail);
448
0
        arr->Item(1).SetFloatValue(aFunctions[i].get_Translation().x(), eCSSUnit_Pixel);
449
0
        arr->Item(2).SetFloatValue(aFunctions[i].get_Translation().y(), eCSSUnit_Pixel);
450
0
        arr->Item(3).SetFloatValue(aFunctions[i].get_Translation().z(), eCSSUnit_Pixel);
451
0
        break;
452
0
      }
453
0
      case TransformFunction::TSkewX:
454
0
      {
455
0
        const CSSAngle& x = aFunctions[i].get_SkewX().x();
456
0
        arr = AnimationValue::AppendTransformFunction(eCSSKeyword_skewx,
457
0
                                                      resultTail);
458
0
        MOZ_TRY(SetCSSAngle(x, arr->Item(1)));
459
0
        break;
460
0
      }
461
0
      case TransformFunction::TSkewY:
462
0
      {
463
0
        const CSSAngle& y = aFunctions[i].get_SkewY().y();
464
0
        arr = AnimationValue::AppendTransformFunction(eCSSKeyword_skewy,
465
0
                                                      resultTail);
466
0
        MOZ_TRY(SetCSSAngle(y, arr->Item(1)));
467
0
        break;
468
0
      }
469
0
      case TransformFunction::TSkew:
470
0
      {
471
0
        const CSSAngle& x = aFunctions[i].get_Skew().x();
472
0
        const CSSAngle& y = aFunctions[i].get_Skew().y();
473
0
        arr = AnimationValue::AppendTransformFunction(eCSSKeyword_skew,
474
0
                                                      resultTail);
475
0
        MOZ_TRY(SetCSSAngle(x, arr->Item(1)));
476
0
        MOZ_TRY(SetCSSAngle(y, arr->Item(2)));
477
0
        break;
478
0
      }
479
0
      case TransformFunction::TTransformMatrix:
480
0
      {
481
0
        arr = AnimationValue::AppendTransformFunction(eCSSKeyword_matrix3d,
482
0
                                                      resultTail);
483
0
        const gfx::Matrix4x4& matrix = aFunctions[i].get_TransformMatrix().value();
484
0
        arr->Item(1).SetFloatValue(matrix._11, eCSSUnit_Number);
485
0
        arr->Item(2).SetFloatValue(matrix._12, eCSSUnit_Number);
486
0
        arr->Item(3).SetFloatValue(matrix._13, eCSSUnit_Number);
487
0
        arr->Item(4).SetFloatValue(matrix._14, eCSSUnit_Number);
488
0
        arr->Item(5).SetFloatValue(matrix._21, eCSSUnit_Number);
489
0
        arr->Item(6).SetFloatValue(matrix._22, eCSSUnit_Number);
490
0
        arr->Item(7).SetFloatValue(matrix._23, eCSSUnit_Number);
491
0
        arr->Item(8).SetFloatValue(matrix._24, eCSSUnit_Number);
492
0
        arr->Item(9).SetFloatValue(matrix._31, eCSSUnit_Number);
493
0
        arr->Item(10).SetFloatValue(matrix._32, eCSSUnit_Number);
494
0
        arr->Item(11).SetFloatValue(matrix._33, eCSSUnit_Number);
495
0
        arr->Item(12).SetFloatValue(matrix._34, eCSSUnit_Number);
496
0
        arr->Item(13).SetFloatValue(matrix._41, eCSSUnit_Number);
497
0
        arr->Item(14).SetFloatValue(matrix._42, eCSSUnit_Number);
498
0
        arr->Item(15).SetFloatValue(matrix._43, eCSSUnit_Number);
499
0
        arr->Item(16).SetFloatValue(matrix._44, eCSSUnit_Number);
500
0
        break;
501
0
      }
502
0
      case TransformFunction::TPerspective:
503
0
      {
504
0
        float perspective = aFunctions[i].get_Perspective().value();
505
0
        arr = AnimationValue::AppendTransformFunction(eCSSKeyword_perspective,
506
0
                                                      resultTail);
507
0
        arr->Item(1).SetFloatValue(perspective, eCSSUnit_Pixel);
508
0
        break;
509
0
      }
510
0
      default:
511
0
        NS_ASSERTION(false, "All functions should be implemented?");
512
0
    }
513
0
  }
514
0
  if (aFunctions.Length() == 0) {
515
0
    result = new nsCSSValueList();
516
0
    result->mValue.SetNoneValue();
517
0
  }
518
0
  return new nsCSSValueSharedList(result.forget());
519
0
}
520
521
static already_AddRefed<RawServoAnimationValue>
522
ToAnimationValue(const Animatable& aAnimatable)
523
0
{
524
0
  RefPtr<RawServoAnimationValue> result;
525
0
526
0
  switch (aAnimatable.type()) {
527
0
    case Animatable::Tnull_t:
528
0
      break;
529
0
    case Animatable::TArrayOfTransformFunction: {
530
0
      const InfallibleTArray<TransformFunction>& transforms =
531
0
        aAnimatable.get_ArrayOfTransformFunction();
532
0
      auto listOrError = CreateCSSValueList(transforms);
533
0
      if (listOrError.isOk()) {
534
0
        RefPtr<nsCSSValueSharedList> list = listOrError.unwrap();
535
0
        MOZ_ASSERT(list, "Transform list should be non null");
536
0
        result = Servo_AnimationValue_Transform(*list).Consume();
537
0
      }
538
0
      break;
539
0
    }
540
0
    case Animatable::Tfloat:
541
0
      result = Servo_AnimationValue_Opacity(aAnimatable.get_float()).Consume();
542
0
      break;
543
0
    default:
544
0
      MOZ_ASSERT_UNREACHABLE("Unsupported type");
545
0
  }
546
0
  return result.forget();
547
0
}
548
549
void
550
AnimationHelper::SetAnimations(
551
  AnimationArray& aAnimations,
552
  InfallibleTArray<AnimData>& aAnimData,
553
  RefPtr<RawServoAnimationValue>& aBaseAnimationStyle)
554
0
{
555
0
  for (uint32_t i = 0; i < aAnimations.Length(); i++) {
556
0
    Animation& animation = aAnimations[i];
557
0
    // Adjust fill mode to fill forwards so that if the main thread is delayed
558
0
    // in clearing this animation we don't introduce flicker by jumping back to
559
0
    // the old underlying value
560
0
    switch (static_cast<dom::FillMode>(animation.fillMode())) {
561
0
      case dom::FillMode::None:
562
0
        animation.fillMode() = static_cast<uint8_t>(dom::FillMode::Forwards);
563
0
        break;
564
0
      case dom::FillMode::Backwards:
565
0
        animation.fillMode() = static_cast<uint8_t>(dom::FillMode::Both);
566
0
        break;
567
0
      default:
568
0
        break;
569
0
    }
570
0
571
0
    if (animation.baseStyle().type() != Animatable::Tnull_t) {
572
0
      aBaseAnimationStyle = ToAnimationValue(animation.baseStyle());
573
0
    }
574
0
575
0
    AnimData* data = aAnimData.AppendElement();
576
0
577
0
    data->mTiming = TimingParams {
578
0
      animation.duration(),
579
0
      animation.delay(),
580
0
      animation.endDelay(),
581
0
      animation.iterations(),
582
0
      animation.iterationStart(),
583
0
      static_cast<dom::PlaybackDirection>(animation.direction()),
584
0
      static_cast<dom::FillMode>(animation.fillMode()),
585
0
      AnimationUtils::TimingFunctionToComputedTimingFunction(
586
0
           animation.easingFunction())
587
0
    };
588
0
    InfallibleTArray<Maybe<ComputedTimingFunction>>& functions =
589
0
      data->mFunctions;
590
0
    InfallibleTArray<RefPtr<RawServoAnimationValue>>& startValues =
591
0
      data->mStartValues;
592
0
    InfallibleTArray<RefPtr<RawServoAnimationValue>>& endValues =
593
0
      data->mEndValues;
594
0
595
0
    const InfallibleTArray<AnimationSegment>& segments = animation.segments();
596
0
    for (const AnimationSegment& segment : segments) {
597
0
      startValues.AppendElement(ToAnimationValue(segment.startState()));
598
0
      endValues.AppendElement(ToAnimationValue(segment.endState()));
599
0
600
0
      TimingFunction tf = segment.sampleFn();
601
0
      Maybe<ComputedTimingFunction> ctf =
602
0
        AnimationUtils::TimingFunctionToComputedTimingFunction(tf);
603
0
      functions.AppendElement(ctf);
604
0
    }
605
0
  }
606
0
}
607
608
uint64_t
609
AnimationHelper::GetNextCompositorAnimationsId()
610
0
{
611
0
  static uint32_t sNextId = 0;
612
0
  ++sNextId;
613
0
614
0
  uint32_t procId = static_cast<uint32_t>(base::GetCurrentProcId());
615
0
  uint64_t nextId = procId;
616
0
  nextId = nextId << 32 | sNextId;
617
0
  return nextId;
618
0
}
619
620
bool
621
AnimationHelper::SampleAnimations(CompositorAnimationStorage* aStorage,
622
                                  TimeStamp aPreviousFrameTime,
623
                                  TimeStamp aCurrentFrameTime)
624
0
{
625
0
  MOZ_ASSERT(aStorage);
626
0
  bool isAnimating = false;
627
0
628
0
  // Do nothing if there are no compositor animations
629
0
  if (!aStorage->AnimationsCount()) {
630
0
    return isAnimating;
631
0
  }
632
0
633
0
  //Sample the animations in CompositorAnimationStorage
634
0
  for (auto iter = aStorage->ConstAnimationsTableIter();
635
0
       !iter.Done(); iter.Next()) {
636
0
    AnimationArray* animations = iter.UserData();
637
0
    if (animations->IsEmpty()) {
638
0
      continue;
639
0
    }
640
0
641
0
    isAnimating = true;
642
0
    RefPtr<RawServoAnimationValue> animationValue;
643
0
    InfallibleTArray<AnimData> animationData;
644
0
    AnimationHelper::SetAnimations(*animations,
645
0
                                   animationData,
646
0
                                   animationValue);
647
0
    AnimatedValue* previousValue = aStorage->GetAnimatedValue(iter.Key());
648
0
    AnimationHelper::SampleResult sampleResult =
649
0
      AnimationHelper::SampleAnimationForEachNode(aPreviousFrameTime,
650
0
                                                  aCurrentFrameTime,
651
0
                                                  *animations,
652
0
                                                  animationData,
653
0
                                                  animationValue,
654
0
                                                  previousValue);
655
0
656
0
    if (sampleResult != AnimationHelper::SampleResult::Sampled) {
657
0
      continue;
658
0
    }
659
0
660
0
    // Store the AnimatedValue
661
0
    Animation& animation = animations->LastElement();
662
0
    switch (animation.property()) {
663
0
      case eCSSProperty_opacity: {
664
0
        aStorage->SetAnimatedValue(
665
0
          iter.Key(),
666
0
          Servo_AnimationValue_GetOpacity(animationValue));
667
0
        break;
668
0
      }
669
0
      case eCSSProperty_transform: {
670
0
        RefPtr<nsCSSValueSharedList> list;
671
0
        Servo_AnimationValue_GetTransform(animationValue, &list);
672
0
        const TransformData& transformData = animation.data().get_TransformData();
673
0
        nsPoint origin = transformData.origin();
674
0
        // we expect all our transform data to arrive in device pixels
675
0
        gfx::Point3D transformOrigin = transformData.transformOrigin();
676
0
        nsDisplayTransform::FrameTransformProperties props(std::move(list),
677
0
                                                           transformOrigin);
678
0
679
0
        gfx::Matrix4x4 transform =
680
0
          nsDisplayTransform::GetResultingTransformMatrix(props, origin,
681
0
                                                          transformData.appUnitsPerDevPixel(),
682
0
                                                          0, &transformData.bounds());
683
0
        gfx::Matrix4x4 frameTransform = transform;
684
0
        // If the parent has perspective transform, then the offset into reference
685
0
        // frame coordinates is already on this transform. If not, then we need to ask
686
0
        // for it to be added here.
687
0
        if (!transformData.hasPerspectiveParent()) {
688
0
           nsLayoutUtils::PostTranslate(transform, origin,
689
0
                                        transformData.appUnitsPerDevPixel(),
690
0
                                        true);
691
0
        }
692
0
693
0
        transform.PostScale(transformData.inheritedXScale(),
694
0
                            transformData.inheritedYScale(),
695
0
                            1);
696
0
697
0
        aStorage->SetAnimatedValue(iter.Key(),
698
0
                                   std::move(transform), std::move(frameTransform),
699
0
                                   transformData);
700
0
        break;
701
0
      }
702
0
      default:
703
0
        MOZ_ASSERT_UNREACHABLE("Unhandled animated property");
704
0
    }
705
0
  }
706
0
707
0
  return isAnimating;
708
0
}
709
710
} // namespace layers
711
} // namespace mozilla