Coverage Report

Created: 2018-09-25 14:53

/src/mozilla-central/dom/svg/SVGAnimatedLengthList.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 "SVGAnimatedLengthList.h"
8
9
#include "DOMSVGAnimatedLengthList.h"
10
#include "mozilla/Move.h"
11
#include "nsSVGElement.h"
12
#include "nsSVGAttrTearoffTable.h"
13
#include "nsSMILValue.h"
14
#include "SVGLengthListSMILType.h"
15
#include "mozilla/dom/SVGLengthBinding.h"
16
17
namespace mozilla {
18
19
using namespace dom;
20
21
nsresult
22
SVGAnimatedLengthList::SetBaseValueString(const nsAString& aValue)
23
0
{
24
0
  SVGLengthList newBaseValue;
25
0
  nsresult rv = newBaseValue.SetValueFromString(aValue);
26
0
  if (NS_FAILED(rv)) {
27
0
    return rv;
28
0
  }
29
0
30
0
  DOMSVGAnimatedLengthList *domWrapper =
31
0
    DOMSVGAnimatedLengthList::GetDOMWrapperIfExists(this);
32
0
  if (domWrapper) {
33
0
    // We must send this notification *before* changing mBaseVal! If the length
34
0
    // of our baseVal is being reduced, our baseVal's DOM wrapper list may have
35
0
    // to remove DOM items from itself, and any removed DOM items need to copy
36
0
    // their internal counterpart values *before* we change them.
37
0
    //
38
0
    domWrapper->InternalBaseValListWillChangeTo(newBaseValue);
39
0
  }
40
0
41
0
  // We don't need to call DidChange* here - we're only called by
42
0
  // nsSVGElement::ParseAttribute under Element::SetAttr,
43
0
  // which takes care of notifying.
44
0
45
0
  rv = mBaseVal.CopyFrom(newBaseValue);
46
0
  if (NS_FAILED(rv) && domWrapper) {
47
0
    // Attempting to increase mBaseVal's length failed - reduce domWrapper
48
0
    // back to the same length:
49
0
    domWrapper->InternalBaseValListWillChangeTo(mBaseVal);
50
0
  }
51
0
  return rv;
52
0
}
53
54
void
55
SVGAnimatedLengthList::ClearBaseValue(uint32_t aAttrEnum)
56
0
{
57
0
  DOMSVGAnimatedLengthList *domWrapper =
58
0
    DOMSVGAnimatedLengthList::GetDOMWrapperIfExists(this);
59
0
  if (domWrapper) {
60
0
    // We must send this notification *before* changing mBaseVal! (See above.)
61
0
    domWrapper->InternalBaseValListWillChangeTo(SVGLengthList());
62
0
  }
63
0
  mBaseVal.Clear();
64
0
  // Caller notifies
65
0
}
66
67
nsresult
68
SVGAnimatedLengthList::SetAnimValue(const SVGLengthList& aNewAnimValue,
69
                                    nsSVGElement *aElement,
70
                                    uint32_t aAttrEnum)
71
0
{
72
0
  DOMSVGAnimatedLengthList *domWrapper =
73
0
    DOMSVGAnimatedLengthList::GetDOMWrapperIfExists(this);
74
0
  if (domWrapper) {
75
0
    // A new animation may totally change the number of items in the animVal
76
0
    // list, replacing what was essentially a mirror of the baseVal list, or
77
0
    // else replacing and overriding an existing animation. When this happens
78
0
    // we must try and keep our animVal's DOM wrapper in sync (see the comment
79
0
    // in DOMSVGAnimatedLengthList::InternalBaseValListWillChangeTo).
80
0
    //
81
0
    // It's not possible for us to reliably distinguish between calls to this
82
0
    // method that are setting a new sample for an existing animation, and
83
0
    // calls that are setting the first sample of an animation that will
84
0
    // override an existing animation. Happily it's cheap to just blindly
85
0
    // notify our animVal's DOM wrapper of its internal counterpart's new value
86
0
    // each time this method is called, so that's what we do.
87
0
    //
88
0
    // Note that we must send this notification *before* setting or changing
89
0
    // mAnimVal! (See the comment in SetBaseValueString above.)
90
0
    //
91
0
    domWrapper->InternalAnimValListWillChangeTo(aNewAnimValue);
92
0
  }
93
0
  if (!mAnimVal) {
94
0
    mAnimVal = new SVGLengthList();
95
0
  }
96
0
  nsresult rv = mAnimVal->CopyFrom(aNewAnimValue);
97
0
  if (NS_FAILED(rv)) {
98
0
    // OOM. We clear the animation, and, importantly, ClearAnimValue() ensures
99
0
    // that mAnimVal and its DOM wrapper (if any) will have the same length!
100
0
    ClearAnimValue(aElement, aAttrEnum);
101
0
    return rv;
102
0
  }
103
0
  aElement->DidAnimateLengthList(aAttrEnum);
104
0
  return NS_OK;
105
0
}
106
107
void
108
SVGAnimatedLengthList::ClearAnimValue(nsSVGElement *aElement,
109
                                      uint32_t aAttrEnum)
110
0
{
111
0
  DOMSVGAnimatedLengthList *domWrapper =
112
0
    DOMSVGAnimatedLengthList::GetDOMWrapperIfExists(this);
113
0
  if (domWrapper) {
114
0
    // When all animation ends, animVal simply mirrors baseVal, which may have
115
0
    // a different number of items to the last active animated value. We must
116
0
    // keep the length of our animVal's DOM wrapper list in sync, and again we
117
0
    // must do that before touching mAnimVal. See comments above.
118
0
    //
119
0
    domWrapper->InternalAnimValListWillChangeTo(mBaseVal);
120
0
  }
121
0
  mAnimVal = nullptr;
122
0
  aElement->DidAnimateLengthList(aAttrEnum);
123
0
}
124
125
UniquePtr<nsISMILAttr>
126
SVGAnimatedLengthList::ToSMILAttr(nsSVGElement *aSVGElement,
127
                                  uint8_t aAttrEnum,
128
                                  uint8_t aAxis,
129
                                  bool aCanZeroPadList)
130
0
{
131
0
  return MakeUnique<SMILAnimatedLengthList>(this, aSVGElement, aAttrEnum,
132
0
                                            aAxis, aCanZeroPadList);
133
0
}
134
135
nsresult
136
SVGAnimatedLengthList::
137
  SMILAnimatedLengthList::ValueFromString(const nsAString& aStr,
138
                               const dom::SVGAnimationElement* /*aSrcElement*/,
139
                               nsSMILValue& aValue,
140
                               bool& aPreventCachingOfSandwich) const
141
0
{
142
0
  nsSMILValue val(&SVGLengthListSMILType::sSingleton);
143
0
  SVGLengthListAndInfo *llai = static_cast<SVGLengthListAndInfo*>(val.mU.mPtr);
144
0
  nsresult rv = llai->SetValueFromString(aStr);
145
0
  if (NS_SUCCEEDED(rv)) {
146
0
    llai->SetInfo(mElement, mAxis, mCanZeroPadList);
147
0
    aValue = std::move(val);
148
0
149
0
    // If any of the lengths in the list depend on their context, then we must
150
0
    // prevent caching of the entire animation sandwich. This is because the
151
0
    // units of a length at a given index can change from sandwich layer to
152
0
    // layer, and indeed even be different within a single sandwich layer. If
153
0
    // any length in the result of an animation sandwich is the result of the
154
0
    // addition of lengths where one or more of those lengths is context
155
0
    // dependent, then naturally the resultant length is also context
156
0
    // dependent, regardless of whether its actual unit is context dependent or
157
0
    // not. Unfortunately normal invalidation mechanisms won't cause us to
158
0
    // recalculate the result of the sandwich if the context changes, so we
159
0
    // take the (substantial) performance hit of preventing caching of the
160
0
    // sandwich layer, causing the animation sandwich to be recalculated every
161
0
    // single sample.
162
0
163
0
    aPreventCachingOfSandwich = false;
164
0
    for (uint32_t i = 0; i < llai->Length(); ++i) {
165
0
      uint8_t unit = (*llai)[i].GetUnit();
166
0
      if (unit == SVGLength_Binding::SVG_LENGTHTYPE_PERCENTAGE ||
167
0
          unit == SVGLength_Binding::SVG_LENGTHTYPE_EMS ||
168
0
          unit == SVGLength_Binding::SVG_LENGTHTYPE_EXS) {
169
0
        aPreventCachingOfSandwich = true;
170
0
        break;
171
0
      }
172
0
    }
173
0
  }
174
0
  return rv;
175
0
}
176
177
nsSMILValue
178
SVGAnimatedLengthList::SMILAnimatedLengthList::GetBaseValue() const
179
0
{
180
0
  // To benefit from Return Value Optimization and avoid copy constructor calls
181
0
  // due to our use of return-by-value, we must return the exact same object
182
0
  // from ALL return points. This function must only return THIS variable:
183
0
  nsSMILValue val;
184
0
185
0
  nsSMILValue tmp(&SVGLengthListSMILType::sSingleton);
186
0
  SVGLengthListAndInfo *llai = static_cast<SVGLengthListAndInfo*>(tmp.mU.mPtr);
187
0
  nsresult rv = llai->CopyFrom(mVal->mBaseVal);
188
0
  if (NS_SUCCEEDED(rv)) {
189
0
    llai->SetInfo(mElement, mAxis, mCanZeroPadList);
190
0
    val = std::move(tmp);
191
0
  }
192
0
  return val;
193
0
}
194
195
nsresult
196
SVGAnimatedLengthList::SMILAnimatedLengthList::SetAnimValue(const nsSMILValue& aValue)
197
0
{
198
0
  NS_ASSERTION(aValue.mType == &SVGLengthListSMILType::sSingleton,
199
0
               "Unexpected type to assign animated value");
200
0
  if (aValue.mType == &SVGLengthListSMILType::sSingleton) {
201
0
    mVal->SetAnimValue(*static_cast<SVGLengthListAndInfo*>(aValue.mU.mPtr),
202
0
                       mElement,
203
0
                       mAttrEnum);
204
0
  }
205
0
  return NS_OK;
206
0
}
207
208
void
209
SVGAnimatedLengthList::SMILAnimatedLengthList::ClearAnimValue()
210
0
{
211
0
  if (mVal->mAnimVal) {
212
0
    mVal->ClearAnimValue(mElement, mAttrEnum);
213
0
  }
214
0
}
215
216
} // namespace mozilla