Coverage Report

Created: 2018-09-25 14:53

/src/mozilla-central/dom/smil/nsSMILCompositor.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 "nsSMILCompositor.h"
8
9
#include "nsComputedDOMStyle.h"
10
#include "nsCSSProps.h"
11
#include "nsHashKeys.h"
12
#include "nsSMILCSSProperty.h"
13
14
// PLDHashEntryHdr methods
15
bool
16
nsSMILCompositor::KeyEquals(KeyTypePointer aKey) const
17
0
{
18
0
  return aKey && aKey->Equals(mKey);
19
0
}
20
21
/*static*/ PLDHashNumber
22
nsSMILCompositor::HashKey(KeyTypePointer aKey)
23
0
{
24
0
  // Combine the 3 values into one numeric value, which will be hashed.
25
0
  // NOTE: We right-shift one of the pointers by 2 to get some randomness in
26
0
  // its 2 lowest-order bits. (Those shifted-off bits will always be 0 since
27
0
  // our pointers will be word-aligned.)
28
0
  return (NS_PTR_TO_UINT32(aKey->mElement.get()) >> 2) +
29
0
    NS_PTR_TO_UINT32(aKey->mAttributeName.get());
30
0
}
31
32
// Cycle-collection support
33
void
34
nsSMILCompositor::Traverse(nsCycleCollectionTraversalCallback* aCallback)
35
0
{
36
0
  if (!mKey.mElement)
37
0
    return;
38
0
39
0
  NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(*aCallback, "Compositor mKey.mElement");
40
0
  aCallback->NoteXPCOMChild(mKey.mElement);
41
0
}
42
43
// Other methods
44
void
45
nsSMILCompositor::AddAnimationFunction(nsSMILAnimationFunction* aFunc)
46
0
{
47
0
  if (aFunc) {
48
0
    mAnimationFunctions.AppendElement(aFunc);
49
0
  }
50
0
}
51
52
void
53
nsSMILCompositor::ComposeAttribute(bool& aMightHavePendingStyleUpdates)
54
0
{
55
0
  if (!mKey.mElement)
56
0
    return;
57
0
58
0
  // If we might need to resolve base styles, grab a suitable ComputedStyle
59
0
  // for initializing our nsISMILAttr with.
60
0
  RefPtr<ComputedStyle> baseComputedStyle;
61
0
  if (MightNeedBaseStyle()) {
62
0
    baseComputedStyle =
63
0
      nsComputedDOMStyle::GetUnanimatedComputedStyleNoFlush(mKey.mElement,
64
0
                                                            nullptr);
65
0
  }
66
0
67
0
  // FIRST: Get the nsISMILAttr (to grab base value from, and to eventually
68
0
  // give animated value to)
69
0
  UniquePtr<nsISMILAttr> smilAttr = CreateSMILAttr(baseComputedStyle);
70
0
  if (!smilAttr) {
71
0
    // Target attribute not found (or, out of memory)
72
0
    return;
73
0
  }
74
0
  if (mAnimationFunctions.IsEmpty()) {
75
0
    // No active animation functions. (We can still have a nsSMILCompositor in
76
0
    // that case if an animation function has *just* become inactive)
77
0
    smilAttr->ClearAnimValue();
78
0
    // Removing the animation effect may require a style update.
79
0
    aMightHavePendingStyleUpdates = true;
80
0
    return;
81
0
  }
82
0
83
0
  // SECOND: Sort the animationFunctions, to prepare for compositing.
84
0
  nsSMILAnimationFunction::Comparator comparator;
85
0
  mAnimationFunctions.Sort(comparator);
86
0
87
0
  // THIRD: Step backwards through animation functions to find out
88
0
  // which ones we actually care about.
89
0
  uint32_t firstFuncToCompose = GetFirstFuncToAffectSandwich();
90
0
91
0
  // FOURTH: Get & cache base value
92
0
  nsSMILValue sandwichResultValue;
93
0
  if (!mAnimationFunctions[firstFuncToCompose]->WillReplace()) {
94
0
    sandwichResultValue = smilAttr->GetBaseValue();
95
0
  }
96
0
  UpdateCachedBaseValue(sandwichResultValue);
97
0
98
0
  if (!mForceCompositing) {
99
0
    return;
100
0
  }
101
0
102
0
  // FIFTH: Compose animation functions
103
0
  aMightHavePendingStyleUpdates = true;
104
0
  uint32_t length = mAnimationFunctions.Length();
105
0
  for (uint32_t i = firstFuncToCompose; i < length; ++i) {
106
0
    mAnimationFunctions[i]->ComposeResult(*smilAttr, sandwichResultValue);
107
0
  }
108
0
  if (sandwichResultValue.IsNull()) {
109
0
    smilAttr->ClearAnimValue();
110
0
    return;
111
0
  }
112
0
113
0
  // SIXTH: Set the animated value to the final composited result.
114
0
  nsresult rv = smilAttr->SetAnimValue(sandwichResultValue);
115
0
  if (NS_FAILED(rv)) {
116
0
    NS_WARNING("nsISMILAttr::SetAnimValue failed");
117
0
  }
118
0
}
119
120
void
121
nsSMILCompositor::ClearAnimationEffects()
122
0
{
123
0
  if (!mKey.mElement || !mKey.mAttributeName)
124
0
    return;
125
0
126
0
  UniquePtr<nsISMILAttr> smilAttr = CreateSMILAttr(nullptr);
127
0
  if (!smilAttr) {
128
0
    // Target attribute not found (or, out of memory)
129
0
    return;
130
0
  }
131
0
  smilAttr->ClearAnimValue();
132
0
}
133
134
// Protected Helper Functions
135
// --------------------------
136
UniquePtr<nsISMILAttr>
137
nsSMILCompositor::CreateSMILAttr(ComputedStyle* aBaseComputedStyle)
138
0
{
139
0
  nsCSSPropertyID propID = GetCSSPropertyToAnimate();
140
0
141
0
  if (propID != eCSSProperty_UNKNOWN) {
142
0
    return MakeUnique<nsSMILCSSProperty>(propID, mKey.mElement.get(),
143
0
                                         aBaseComputedStyle);
144
0
  }
145
0
146
0
  return mKey.mElement->GetAnimatedAttr(mKey.mAttributeNamespaceID,
147
0
                                        mKey.mAttributeName);
148
0
}
149
150
nsCSSPropertyID
151
nsSMILCompositor::GetCSSPropertyToAnimate() const
152
0
{
153
0
  if (mKey.mAttributeNamespaceID != kNameSpaceID_None) {
154
0
    return eCSSProperty_UNKNOWN;
155
0
  }
156
0
157
0
  nsCSSPropertyID propID =
158
0
    nsCSSProps::LookupProperty(nsDependentAtomString(mKey.mAttributeName));
159
0
160
0
  if (!nsSMILCSSProperty::IsPropertyAnimatable(propID)) {
161
0
    return eCSSProperty_UNKNOWN;
162
0
  }
163
0
164
0
  // If we are animating the 'width' or 'height' of an outer SVG
165
0
  // element we should animate it as a CSS property, but for other elements
166
0
  // (e.g. <rect>) we should animate it as a length attribute.
167
0
  // The easiest way to test for an outer SVG element, is to see if it is an
168
0
  // SVG-namespace element mapping its width/height attribute to style.
169
0
  //
170
0
  // If we have animation of 'width' or 'height' on an SVG element that is
171
0
  // NOT mapping that attributes to style then it must not be an outermost SVG
172
0
  // element so we should return eCSSProperty_UNKNOWN to indicate that we
173
0
  // should animate as an attribute instead.
174
0
  if ((mKey.mAttributeName == nsGkAtoms::width ||
175
0
       mKey.mAttributeName == nsGkAtoms::height) &&
176
0
      mKey.mElement->GetNameSpaceID() == kNameSpaceID_SVG &&
177
0
      !mKey.mElement->IsAttributeMapped(mKey.mAttributeName)) {
178
0
    return eCSSProperty_UNKNOWN;
179
0
  }
180
0
181
0
  return propID;
182
0
}
183
184
bool
185
nsSMILCompositor::MightNeedBaseStyle() const
186
0
{
187
0
  if (GetCSSPropertyToAnimate() == eCSSProperty_UNKNOWN) {
188
0
    return false;
189
0
  }
190
0
191
0
  // We should return true if at least one animation function might build on
192
0
  // the base value.
193
0
  for (const nsSMILAnimationFunction* func : mAnimationFunctions) {
194
0
    if (!func->WillReplace()) {
195
0
      return true;
196
0
    }
197
0
  }
198
0
199
0
  return false;
200
0
}
201
202
uint32_t
203
nsSMILCompositor::GetFirstFuncToAffectSandwich()
204
0
{
205
0
  // For performance reasons, we throttle most animations on elements in
206
0
  // display:none subtrees. (We can't throttle animations that target the
207
0
  // "display" property itself, though -- if we did, display:none elements
208
0
  // could never be dynamically displayed via animations.)
209
0
  // To determine whether we're in a display:none subtree, we will check the
210
0
  // element's primary frame since element in display:none subtree doesn't have
211
0
  // a primary frame. Before this process, we will construct frame when we
212
0
  // append an element to subtree. So we will not need to worry about pending
213
0
  // frame construction in this step.
214
0
  bool canThrottle = mKey.mAttributeName != nsGkAtoms::display &&
215
0
                     !mKey.mElement->GetPrimaryFrame();
216
0
217
0
  uint32_t i;
218
0
  for (i = mAnimationFunctions.Length(); i > 0; --i) {
219
0
    nsSMILAnimationFunction* curAnimFunc = mAnimationFunctions[i-1];
220
0
    // In the following, the lack of short-circuit behavior of |= means that we
221
0
    // will ALWAYS run UpdateCachedTarget (even if mForceCompositing is true)
222
0
    // but only call HasChanged and WasSkippedInPrevSample if necessary.  This
223
0
    // is important since we need UpdateCachedTarget to run in order to detect
224
0
    // changes to the target in subsequent samples.
225
0
    mForceCompositing |=
226
0
      curAnimFunc->UpdateCachedTarget(mKey) ||
227
0
      (curAnimFunc->HasChanged() && !canThrottle) ||
228
0
      curAnimFunc->WasSkippedInPrevSample();
229
0
230
0
    if (curAnimFunc->WillReplace()) {
231
0
      --i;
232
0
      break;
233
0
    }
234
0
  }
235
0
236
0
  // Mark remaining animation functions as having been skipped so if we later
237
0
  // use them we'll know to force compositing.
238
0
  // Note that we only really need to do this if something has changed
239
0
  // (otherwise we would have set the flag on a previous sample) and if
240
0
  // something has changed mForceCompositing will be true.
241
0
  if (mForceCompositing) {
242
0
    for (uint32_t j = i; j > 0; --j) {
243
0
      mAnimationFunctions[j-1]->SetWasSkipped();
244
0
    }
245
0
  }
246
0
  return i;
247
0
}
248
249
void
250
nsSMILCompositor::UpdateCachedBaseValue(const nsSMILValue& aBaseValue)
251
0
{
252
0
  if (mCachedBaseValue != aBaseValue) {
253
0
    // Base value has changed since last sample.
254
0
    mCachedBaseValue = aBaseValue;
255
0
    mForceCompositing = true;
256
0
  }
257
0
}