1 : /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 : /* ***** BEGIN LICENSE BLOCK *****
3 : * Version: MPL 1.1/GPL 2.0/LGPL 2.1
4 : *
5 : * The contents of this file are subject to the Mozilla Public License Version
6 : * 1.1 (the "License"); you may not use this file except in compliance with
7 : * the License. You may obtain a copy of the License at
8 : * http://www.mozilla.org/MPL/
9 : *
10 : * Software distributed under the License is distributed on an "AS IS" basis,
11 : * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
12 : * for the specific language governing rights and limitations under the
13 : * License.
14 : *
15 : * The Original Code is the Mozilla SVG project.
16 : *
17 : * The Initial Developer of the Original Code is Robert Longson.
18 : * Portions created by the Initial Developer are Copyright (C) 2011
19 : * the Initial Developer. All Rights Reserved.
20 : *
21 : * Contributor(s):
22 : *
23 : * Alternatively, the contents of this file may be used under the terms of
24 : * either the GNU General Public License Version 2 or later (the "GPL"), or
25 : * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
26 : * in which case the provisions of the GPL or the LGPL are applicable instead
27 : * of those above. If you wish to allow use of your version of this file only
28 : * under the terms of either the GPL or the LGPL, and not to allow others to
29 : * use your version of this file under the terms of the MPL, indicate your
30 : * decision by deleting the provisions above and replace them with the notice
31 : * and other provisions required by the GPL or the LGPL. If you do not delete
32 : * the provisions above, a recipient may use your version of this file under
33 : * the terms of any one of the MPL, the GPL or the LGPL.
34 : *
35 : * ***** END LICENSE BLOCK ***** */
36 :
37 : #include "nsSVGNumberPair.h"
38 : #include "nsSVGUtils.h"
39 : #include "nsCharSeparatedTokenizer.h"
40 : #include "prdtoa.h"
41 : #include "nsDOMError.h"
42 : #include "nsMathUtils.h"
43 : #include "nsSMILValue.h"
44 : #include "SVGNumberPairSMILType.h"
45 :
46 : using namespace mozilla;
47 :
48 1464 : NS_SVG_VAL_IMPL_CYCLE_COLLECTION(nsSVGNumberPair::DOMAnimatedNumber, mSVGElement)
49 :
50 0 : NS_IMPL_CYCLE_COLLECTING_ADDREF(nsSVGNumberPair::DOMAnimatedNumber)
51 0 : NS_IMPL_CYCLE_COLLECTING_RELEASE(nsSVGNumberPair::DOMAnimatedNumber)
52 :
53 : DOMCI_DATA(SVGAnimatedNumberPair, nsSVGNumberPair::DOMAnimatedNumber)
54 :
55 0 : NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsSVGNumberPair::DOMAnimatedNumber)
56 0 : NS_INTERFACE_MAP_ENTRY(nsIDOMSVGAnimatedNumber)
57 0 : NS_INTERFACE_MAP_ENTRY(nsISupports)
58 0 : NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(SVGAnimatedNumber)
59 0 : NS_INTERFACE_MAP_END
60 :
61 : /* Implementation */
62 :
63 : static nsresult
64 0 : ParseNumberOptionalNumber(const nsAString& aValue,
65 : float aValues[2])
66 : {
67 : nsCharSeparatedTokenizerTemplate<IsSVGWhitespace>
68 : tokenizer(aValue, ',',
69 0 : nsCharSeparatedTokenizer::SEPARATOR_OPTIONAL);
70 0 : if (tokenizer.firstTokenBeganWithWhitespace()) {
71 0 : return NS_ERROR_DOM_SYNTAX_ERR;
72 : }
73 :
74 : PRUint32 i;
75 0 : for (i = 0; i < 2 && tokenizer.hasMoreTokens(); ++i) {
76 0 : NS_ConvertUTF16toUTF8 utf8Token(tokenizer.nextToken());
77 0 : const char *token = utf8Token.get();
78 0 : if (*token == '\0') {
79 0 : return NS_ERROR_DOM_SYNTAX_ERR; // empty string (e.g. two commas in a row)
80 : }
81 :
82 : char *end;
83 0 : aValues[i] = float(PR_strtod(token, &end));
84 0 : if (*end != '\0' || !NS_finite(aValues[i])) {
85 0 : return NS_ERROR_DOM_SYNTAX_ERR; // parse error
86 : }
87 : }
88 0 : if (i == 1) {
89 0 : aValues[1] = aValues[0];
90 : }
91 :
92 0 : if (i == 0 || // Too few values.
93 0 : tokenizer.hasMoreTokens() || // Too many values.
94 0 : tokenizer.lastTokenEndedWithWhitespace() || // Trailing whitespace.
95 0 : tokenizer.lastTokenEndedWithSeparator()) { // Trailing comma.
96 0 : return NS_ERROR_DOM_SYNTAX_ERR;
97 : }
98 :
99 0 : return NS_OK;
100 : }
101 :
102 : nsresult
103 0 : nsSVGNumberPair::SetBaseValueString(const nsAString &aValueAsString,
104 : nsSVGElement *aSVGElement)
105 : {
106 : float val[2];
107 :
108 0 : nsresult rv = ParseNumberOptionalNumber(aValueAsString, val);
109 0 : if (NS_FAILED(rv)) {
110 0 : return rv;
111 : }
112 :
113 0 : mBaseVal[0] = val[0];
114 0 : mBaseVal[1] = val[1];
115 0 : mIsBaseSet = true;
116 0 : if (!mIsAnimated) {
117 0 : mAnimVal[0] = mBaseVal[0];
118 0 : mAnimVal[1] = mBaseVal[1];
119 : }
120 : else {
121 0 : aSVGElement->AnimationNeedsResample();
122 : }
123 :
124 : // We don't need to call Will/DidChange* here - we're only called by
125 : // nsSVGElement::ParseAttribute under nsGenericElement::SetAttr,
126 : // which takes care of notifying.
127 0 : return NS_OK;
128 : }
129 :
130 : void
131 0 : nsSVGNumberPair::GetBaseValueString(nsAString &aValueAsString) const
132 : {
133 0 : aValueAsString.Truncate();
134 0 : aValueAsString.AppendFloat(mBaseVal[0]);
135 0 : if (mBaseVal[0] != mBaseVal[1]) {
136 0 : aValueAsString.AppendLiteral(", ");
137 0 : aValueAsString.AppendFloat(mBaseVal[1]);
138 : }
139 0 : }
140 :
141 : void
142 0 : nsSVGNumberPair::SetBaseValue(float aValue, PairIndex aPairIndex,
143 : nsSVGElement *aSVGElement)
144 : {
145 0 : PRUint32 index = (aPairIndex == eFirst ? 0 : 1);
146 0 : if (mIsBaseSet && mBaseVal[index] == aValue) {
147 0 : return;
148 : }
149 0 : nsAttrValue emptyOrOldValue = aSVGElement->WillChangeNumberPair(mAttrEnum);
150 0 : mBaseVal[index] = aValue;
151 0 : mIsBaseSet = true;
152 0 : if (!mIsAnimated) {
153 0 : mAnimVal[index] = aValue;
154 : }
155 : else {
156 0 : aSVGElement->AnimationNeedsResample();
157 : }
158 0 : aSVGElement->DidChangeNumberPair(mAttrEnum, emptyOrOldValue);
159 : }
160 :
161 : void
162 0 : nsSVGNumberPair::SetBaseValues(float aValue1, float aValue2,
163 : nsSVGElement *aSVGElement)
164 : {
165 0 : if (mIsBaseSet && mBaseVal[0] == aValue1 && mBaseVal[1] == aValue2) {
166 0 : return;
167 : }
168 0 : nsAttrValue emptyOrOldValue = aSVGElement->WillChangeNumberPair(mAttrEnum);
169 0 : mBaseVal[0] = aValue1;
170 0 : mBaseVal[1] = aValue2;
171 0 : mIsBaseSet = true;
172 0 : if (!mIsAnimated) {
173 0 : mAnimVal[0] = aValue1;
174 0 : mAnimVal[1] = aValue2;
175 : }
176 : else {
177 0 : aSVGElement->AnimationNeedsResample();
178 : }
179 0 : aSVGElement->DidChangeNumberPair(mAttrEnum, emptyOrOldValue);
180 : }
181 :
182 : void
183 0 : nsSVGNumberPair::SetAnimValue(const float aValue[2], nsSVGElement *aSVGElement)
184 : {
185 0 : mAnimVal[0] = aValue[0];
186 0 : mAnimVal[1] = aValue[1];
187 0 : mIsAnimated = true;
188 0 : aSVGElement->DidAnimateNumberPair(mAttrEnum);
189 0 : }
190 :
191 : nsresult
192 0 : nsSVGNumberPair::ToDOMAnimatedNumber(nsIDOMSVGAnimatedNumber **aResult,
193 : PairIndex aIndex,
194 : nsSVGElement *aSVGElement)
195 : {
196 0 : *aResult = new DOMAnimatedNumber(this, aIndex, aSVGElement);
197 0 : NS_ADDREF(*aResult);
198 0 : return NS_OK;
199 : }
200 :
201 : nsISMILAttr*
202 0 : nsSVGNumberPair::ToSMILAttr(nsSVGElement *aSVGElement)
203 : {
204 0 : return new SMILNumberPair(this, aSVGElement);
205 : }
206 :
207 : nsresult
208 0 : nsSVGNumberPair::SMILNumberPair::ValueFromString(const nsAString& aStr,
209 : const nsISMILAnimationElement* /*aSrcElement*/,
210 : nsSMILValue& aValue,
211 : bool& aPreventCachingOfSandwich) const
212 : {
213 : float values[2];
214 :
215 0 : nsresult rv = ParseNumberOptionalNumber(aStr, values);
216 0 : if (NS_FAILED(rv)) {
217 0 : return rv;
218 : }
219 :
220 0 : nsSMILValue val(&SVGNumberPairSMILType::sSingleton);
221 0 : val.mU.mNumberPair[0] = values[0];
222 0 : val.mU.mNumberPair[1] = values[1];
223 0 : aValue = val;
224 0 : aPreventCachingOfSandwich = false;
225 :
226 0 : return NS_OK;
227 : }
228 :
229 : nsSMILValue
230 0 : nsSVGNumberPair::SMILNumberPair::GetBaseValue() const
231 : {
232 0 : nsSMILValue val(&SVGNumberPairSMILType::sSingleton);
233 0 : val.mU.mNumberPair[0] = mVal->mBaseVal[0];
234 0 : val.mU.mNumberPair[1] = mVal->mBaseVal[1];
235 : return val;
236 : }
237 :
238 : void
239 0 : nsSVGNumberPair::SMILNumberPair::ClearAnimValue()
240 : {
241 0 : if (mVal->mIsAnimated) {
242 0 : mVal->mIsAnimated = false;
243 0 : mVal->mAnimVal[0] = mVal->mBaseVal[0];
244 0 : mVal->mAnimVal[1] = mVal->mBaseVal[1];
245 0 : mSVGElement->DidAnimateNumberPair(mVal->mAttrEnum);
246 : }
247 0 : }
248 :
249 : nsresult
250 0 : nsSVGNumberPair::SMILNumberPair::SetAnimValue(const nsSMILValue& aValue)
251 : {
252 0 : NS_ASSERTION(aValue.mType == &SVGNumberPairSMILType::sSingleton,
253 : "Unexpected type to assign animated value");
254 0 : if (aValue.mType == &SVGNumberPairSMILType::sSingleton) {
255 0 : mVal->SetAnimValue(aValue.mU.mNumberPair, mSVGElement);
256 : }
257 0 : return NS_OK;
258 4392 : }
|