Line data Source code
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 : /* representation of a value for a SMIL-animated CSS property */
8 :
9 : #include "nsSMILCSSValueType.h"
10 :
11 : #include "nsComputedDOMStyle.h"
12 : #include "nsString.h"
13 : #include "nsSMILParserUtils.h"
14 : #include "nsSMILValue.h"
15 : #include "nsCSSProps.h"
16 : #include "nsCSSValue.h"
17 : #include "nsColor.h"
18 : #include "nsPresContext.h"
19 : #include "mozilla/ServoBindings.h"
20 : #include "mozilla/StyleAnimationValue.h" // For AnimationValue
21 : #include "mozilla/StyleSetHandleInlines.h"
22 : #include "mozilla/dom/BaseKeyframeTypesBinding.h" // For CompositeOperation
23 : #include "mozilla/dom/Element.h"
24 : #include "nsDebug.h"
25 : #include "nsStyleUtil.h"
26 : #include "nsIDocument.h"
27 :
28 : using namespace mozilla::dom;
29 : using mozilla::StyleAnimationValue;
30 :
31 : typedef AutoTArray<RefPtr<RawServoAnimationValue>, 1> ServoAnimationValues;
32 :
33 : /*static*/ nsSMILCSSValueType nsSMILCSSValueType::sSingleton;
34 :
35 0 : struct ValueWrapper {
36 0 : ValueWrapper(nsCSSPropertyID aPropID, const AnimationValue& aValue)
37 0 : : mPropID(aPropID)
38 : {
39 0 : if (aValue.mServo) {
40 0 : mServoValues.AppendElement(aValue.mServo);
41 0 : return;
42 : }
43 0 : mGeckoValue = aValue.mGecko;
44 : }
45 0 : ValueWrapper(nsCSSPropertyID aPropID, const StyleAnimationValue& aValue)
46 0 : : mPropID(aPropID), mGeckoValue(aValue) {}
47 0 : ValueWrapper(nsCSSPropertyID aPropID,
48 : const RefPtr<RawServoAnimationValue>& aValue)
49 0 : : mPropID(aPropID), mServoValues{(aValue)} {}
50 0 : ValueWrapper(nsCSSPropertyID aPropID, ServoAnimationValues&& aValues)
51 0 : : mPropID(aPropID), mServoValues{aValues} {}
52 :
53 0 : bool operator==(const ValueWrapper& aOther) const
54 : {
55 0 : if (mPropID != aOther.mPropID) {
56 0 : return false;
57 : }
58 :
59 0 : if (!mServoValues.IsEmpty()) {
60 0 : size_t len = mServoValues.Length();
61 0 : if (len != aOther.mServoValues.Length()) {
62 0 : return false;
63 : }
64 0 : for (size_t i = 0; i < len; i++) {
65 0 : if (!Servo_AnimationValue_DeepEqual(mServoValues[i],
66 0 : aOther.mServoValues[i])) {
67 0 : return false;
68 : }
69 : }
70 0 : return true;
71 : }
72 :
73 0 : return mGeckoValue == aOther.mGeckoValue;
74 : }
75 :
76 : bool operator!=(const ValueWrapper& aOther) const
77 : {
78 : return !(*this == aOther);
79 : }
80 :
81 : nsCSSPropertyID mPropID;
82 : ServoAnimationValues mServoValues;
83 : StyleAnimationValue mGeckoValue;
84 :
85 : };
86 :
87 : // Helper Methods
88 : // --------------
89 : static const StyleAnimationValue*
90 0 : GetZeroValueForUnit(StyleAnimationValue::Unit aUnit)
91 : {
92 : static const StyleAnimationValue
93 0 : sZeroCoord(0, StyleAnimationValue::CoordConstructor);
94 : static const StyleAnimationValue
95 0 : sZeroPercent(0.0f, StyleAnimationValue::PercentConstructor);
96 : static const StyleAnimationValue
97 0 : sZeroFloat(0.0f, StyleAnimationValue::FloatConstructor);
98 : static const StyleAnimationValue
99 0 : sZeroColor(NS_RGB(0,0,0), StyleAnimationValue::ColorConstructor);
100 :
101 0 : MOZ_ASSERT(aUnit != StyleAnimationValue::eUnit_Null,
102 : "Need non-null unit for a zero value");
103 0 : switch (aUnit) {
104 : case StyleAnimationValue::eUnit_Coord:
105 0 : return &sZeroCoord;
106 : case StyleAnimationValue::eUnit_Percent:
107 0 : return &sZeroPercent;
108 : case StyleAnimationValue::eUnit_Float:
109 0 : return &sZeroFloat;
110 : case StyleAnimationValue::eUnit_Color:
111 0 : return &sZeroColor;
112 : default:
113 0 : return nullptr;
114 : }
115 : }
116 :
117 : // This method requires at least one of its arguments to be non-null.
118 : //
119 : // If one argument is null, this method updates it to point to "zero"
120 : // for the other argument's Unit (if applicable; otherwise, we return false).
121 : //
122 : // If neither argument is null, this method does nothing.
123 : //
124 : // |aZeroValueStorage| should be a reference to a RefPtr<RawServoAnimationValue>.
125 : // This is used where we may need to allocate a new ServoAnimationValue to
126 : // represent the appropriate zero value.
127 : //
128 : // Returns true on success, or otherwise.
129 : static bool
130 0 : FinalizeServoAnimationValues(const RefPtr<RawServoAnimationValue>*& aValue1,
131 : const RefPtr<RawServoAnimationValue>*& aValue2,
132 : RefPtr<RawServoAnimationValue>& aZeroValueStorage)
133 : {
134 0 : MOZ_ASSERT(aValue1 || aValue2, "expecting at least one non-null value");
135 :
136 : // Are we missing either val? (If so, it's an implied 0 in other val's units)
137 :
138 0 : if (!aValue1) {
139 0 : aZeroValueStorage = Servo_AnimationValues_GetZeroValue(*aValue2).Consume();
140 0 : aValue1 = &aZeroValueStorage;
141 0 : } else if (!aValue2) {
142 0 : aZeroValueStorage = Servo_AnimationValues_GetZeroValue(*aValue1).Consume();
143 0 : aValue2 = &aZeroValueStorage;
144 : }
145 0 : return *aValue1 && *aValue2;
146 : }
147 :
148 : static bool
149 0 : FinalizeStyleAnimationValues(const StyleAnimationValue*& aValue1,
150 : const StyleAnimationValue*& aValue2)
151 : {
152 0 : MOZ_ASSERT(aValue1 || aValue2,
153 : "expecting at least one non-null value");
154 :
155 0 : if (!aValue1) {
156 0 : aValue1 = GetZeroValueForUnit(aValue2->GetUnit());
157 0 : return !!aValue1; // Fail if we have no zero value for this unit.
158 : }
159 0 : if (!aValue2) {
160 0 : aValue2 = GetZeroValueForUnit(aValue1->GetUnit());
161 0 : return !!aValue2; // Fail if we have no zero value for this unit.
162 : }
163 :
164 : // Ok, both values were specified.
165 : // Need to handle a special-case, though: unitless nonzero length (parsed as
166 : // eUnit_Float) mixed with unitless 0 length (parsed as eUnit_Coord). These
167 : // won't interoperate in StyleAnimationValue, since their Units don't match.
168 : // In this case, we replace the eUnit_Coord 0 value with eUnit_Float 0 value.
169 : const StyleAnimationValue& zeroCoord =
170 0 : *GetZeroValueForUnit(StyleAnimationValue::eUnit_Coord);
171 0 : if (*aValue1 == zeroCoord &&
172 0 : aValue2->GetUnit() == StyleAnimationValue::eUnit_Float) {
173 0 : aValue1 = GetZeroValueForUnit(StyleAnimationValue::eUnit_Float);
174 0 : } else if (*aValue2 == zeroCoord &&
175 0 : aValue1->GetUnit() == StyleAnimationValue::eUnit_Float) {
176 0 : aValue2 = GetZeroValueForUnit(StyleAnimationValue::eUnit_Float);
177 : }
178 :
179 0 : return true;
180 : }
181 :
182 : static void
183 0 : InvertSign(StyleAnimationValue& aValue)
184 : {
185 0 : switch (aValue.GetUnit()) {
186 : case StyleAnimationValue::eUnit_Coord:
187 0 : aValue.SetCoordValue(-aValue.GetCoordValue());
188 0 : break;
189 : case StyleAnimationValue::eUnit_Percent:
190 0 : aValue.SetPercentValue(-aValue.GetPercentValue());
191 0 : break;
192 : case StyleAnimationValue::eUnit_Float:
193 0 : aValue.SetFloatValue(-aValue.GetFloatValue());
194 0 : break;
195 : default:
196 0 : NS_NOTREACHED("Calling InvertSign with an unsupported unit");
197 0 : break;
198 : }
199 0 : }
200 :
201 : static ValueWrapper*
202 0 : ExtractValueWrapper(nsSMILValue& aValue)
203 : {
204 0 : return static_cast<ValueWrapper*>(aValue.mU.mPtr);
205 : }
206 :
207 : static const ValueWrapper*
208 0 : ExtractValueWrapper(const nsSMILValue& aValue)
209 : {
210 0 : return static_cast<const ValueWrapper*>(aValue.mU.mPtr);
211 : }
212 :
213 : // Class methods
214 : // -------------
215 : void
216 0 : nsSMILCSSValueType::Init(nsSMILValue& aValue) const
217 : {
218 0 : MOZ_ASSERT(aValue.IsNull(), "Unexpected SMIL value type");
219 :
220 0 : aValue.mU.mPtr = nullptr;
221 0 : aValue.mType = this;
222 0 : }
223 :
224 : void
225 0 : nsSMILCSSValueType::Destroy(nsSMILValue& aValue) const
226 : {
227 0 : MOZ_ASSERT(aValue.mType == this, "Unexpected SMIL value type");
228 0 : delete static_cast<ValueWrapper*>(aValue.mU.mPtr);
229 0 : aValue.mType = nsSMILNullType::Singleton();
230 0 : }
231 :
232 : nsresult
233 0 : nsSMILCSSValueType::Assign(nsSMILValue& aDest, const nsSMILValue& aSrc) const
234 : {
235 0 : MOZ_ASSERT(aDest.mType == aSrc.mType, "Incompatible SMIL types");
236 0 : MOZ_ASSERT(aDest.mType == this, "Unexpected SMIL value type");
237 0 : const ValueWrapper* srcWrapper = ExtractValueWrapper(aSrc);
238 0 : ValueWrapper* destWrapper = ExtractValueWrapper(aDest);
239 :
240 0 : if (srcWrapper) {
241 0 : if (!destWrapper) {
242 : // barely-initialized dest -- need to alloc & copy
243 0 : aDest.mU.mPtr = new ValueWrapper(*srcWrapper);
244 : } else {
245 : // both already fully-initialized -- just copy straight across
246 0 : *destWrapper = *srcWrapper;
247 : }
248 0 : } else if (destWrapper) {
249 : // fully-initialized dest, barely-initialized src -- clear dest
250 0 : delete destWrapper;
251 0 : aDest.mU.mPtr = destWrapper = nullptr;
252 : } // else, both are barely-initialized -- nothing to do.
253 :
254 0 : return NS_OK;
255 : }
256 :
257 : bool
258 0 : nsSMILCSSValueType::IsEqual(const nsSMILValue& aLeft,
259 : const nsSMILValue& aRight) const
260 : {
261 0 : MOZ_ASSERT(aLeft.mType == aRight.mType, "Incompatible SMIL types");
262 0 : MOZ_ASSERT(aLeft.mType == this, "Unexpected SMIL value");
263 0 : const ValueWrapper* leftWrapper = ExtractValueWrapper(aLeft);
264 0 : const ValueWrapper* rightWrapper = ExtractValueWrapper(aRight);
265 :
266 0 : if (leftWrapper) {
267 0 : if (rightWrapper) {
268 : // Both non-null
269 0 : NS_WARNING_ASSERTION(leftWrapper != rightWrapper,
270 : "Two nsSMILValues with matching ValueWrapper ptr");
271 0 : return *leftWrapper == *rightWrapper;
272 : }
273 : // Left non-null, right null
274 0 : return false;
275 : }
276 0 : if (rightWrapper) {
277 : // Left null, right non-null
278 0 : return false;
279 : }
280 : // Both null
281 0 : return true;
282 : }
283 :
284 : static bool
285 0 : AddOrAccumulateForServo(nsSMILValue& aDest,
286 : const ValueWrapper* aValueToAddWrapper,
287 : ValueWrapper* aDestWrapper,
288 : CompositeOperation aCompositeOp,
289 : uint64_t aCount)
290 : {
291 : nsCSSPropertyID property = aValueToAddWrapper
292 0 : ? aValueToAddWrapper->mPropID
293 0 : : aDestWrapper->mPropID;
294 : size_t len = aValueToAddWrapper
295 0 : ? aValueToAddWrapper->mServoValues.Length()
296 0 : : aDestWrapper->mServoValues.Length();
297 :
298 0 : MOZ_ASSERT(!aValueToAddWrapper || !aDestWrapper ||
299 : aValueToAddWrapper->mServoValues.Length() ==
300 : aDestWrapper->mServoValues.Length(),
301 : "Both of values'length in the wrappers should be the same if "
302 : "both of them exist");
303 :
304 0 : for (size_t i = 0; i < len; i++) {
305 : const RefPtr<RawServoAnimationValue>* valueToAdd =
306 : aValueToAddWrapper
307 0 : ? &aValueToAddWrapper->mServoValues[i]
308 0 : : nullptr;
309 : const RefPtr<RawServoAnimationValue>* destValue =
310 : aDestWrapper
311 0 : ? &aDestWrapper->mServoValues[i]
312 0 : : nullptr;
313 0 : RefPtr<RawServoAnimationValue> zeroValueStorage;
314 0 : if (!FinalizeServoAnimationValues(valueToAdd, destValue, zeroValueStorage)) {
315 0 : return false;
316 : }
317 :
318 : // FinalizeServoAnimationValues may have updated destValue so we should make
319 : // sure the aDest and aDestWrapper outparams are up-to-date.
320 0 : if (aDestWrapper) {
321 0 : aDestWrapper->mServoValues[i] = *destValue;
322 : } else {
323 : // aDest may be a barely-initialized "zero" destination.
324 0 : aDest.mU.mPtr = aDestWrapper = new ValueWrapper(property, *destValue);
325 0 : aDestWrapper->mServoValues.SetLength(len);
326 : }
327 :
328 0 : RefPtr<RawServoAnimationValue> result;
329 0 : if (aCompositeOp == CompositeOperation::Add) {
330 0 : result = Servo_AnimationValues_Add(*destValue, *valueToAdd).Consume();
331 : } else {
332 0 : result = Servo_AnimationValues_Accumulate(*destValue,
333 : *valueToAdd,
334 0 : aCount).Consume();
335 : }
336 :
337 0 : if (!result) {
338 0 : return false;
339 : }
340 0 : aDestWrapper->mServoValues[i] = result;
341 : }
342 :
343 0 : return true;
344 : }
345 :
346 : static bool
347 0 : AddOrAccumulate(nsSMILValue& aDest, const nsSMILValue& aValueToAdd,
348 : CompositeOperation aCompositeOp, uint64_t aCount)
349 : {
350 0 : MOZ_ASSERT(aValueToAdd.mType == aDest.mType,
351 : "Trying to add mismatching types");
352 0 : MOZ_ASSERT(aValueToAdd.mType == &nsSMILCSSValueType::sSingleton,
353 : "Unexpected SMIL value type");
354 0 : MOZ_ASSERT(aCompositeOp == CompositeOperation::Add ||
355 : aCompositeOp == CompositeOperation::Accumulate,
356 : "Composite operation should be add or accumulate");
357 0 : MOZ_ASSERT(aCompositeOp != CompositeOperation::Add || aCount == 1,
358 : "Count should be 1 if composite operation is add");
359 :
360 0 : ValueWrapper* destWrapper = ExtractValueWrapper(aDest);
361 0 : const ValueWrapper* valueToAddWrapper = ExtractValueWrapper(aValueToAdd);
362 0 : MOZ_ASSERT(destWrapper || valueToAddWrapper,
363 : "need at least one fully-initialized value");
364 :
365 : nsCSSPropertyID property = valueToAddWrapper
366 0 : ? valueToAddWrapper->mPropID
367 0 : : destWrapper->mPropID;
368 : // Special case: font-size-adjust and stroke-dasharray are explicitly
369 : // non-additive (even though StyleAnimationValue *could* support adding them)
370 0 : if (property == eCSSProperty_font_size_adjust ||
371 : property == eCSSProperty_stroke_dasharray) {
372 0 : return false;
373 : }
374 : // Skip font shorthand since it includes font-size-adjust.
375 0 : if (property == eCSSProperty_font) {
376 0 : return false;
377 : }
378 :
379 : bool isServo = valueToAddWrapper
380 0 : ? !valueToAddWrapper->mServoValues.IsEmpty()
381 0 : : !destWrapper->mServoValues.IsEmpty();
382 0 : if (isServo) {
383 : return AddOrAccumulateForServo(aDest,
384 : valueToAddWrapper,
385 : destWrapper,
386 : aCompositeOp,
387 0 : aCount);
388 : }
389 :
390 0 : const StyleAnimationValue* valueToAdd = valueToAddWrapper ?
391 0 : &valueToAddWrapper->mGeckoValue : nullptr;
392 0 : const StyleAnimationValue* destValue = destWrapper ?
393 0 : &destWrapper->mGeckoValue : nullptr;
394 0 : if (!FinalizeStyleAnimationValues(valueToAdd, destValue)) {
395 0 : return false;
396 : }
397 : // Did FinalizeStyleAnimationValues change destValue?
398 : // If so, update outparam to use the new value.
399 0 : if (destWrapper && &destWrapper->mGeckoValue != destValue) {
400 0 : destWrapper->mGeckoValue = *destValue;
401 : }
402 :
403 : // Handle barely-initialized "zero" destination.
404 0 : if (!destWrapper) {
405 0 : aDest.mU.mPtr = destWrapper = new ValueWrapper(property, *destValue);
406 : }
407 :
408 : // For Gecko, we currently call Add for either composite mode.
409 : //
410 : // This is not ideal, but it doesn't make any difference for the set of
411 : // properties we currently allow adding in SMIL and this code path will
412 : // hopefully become obsolete before we expand that set.
413 0 : return StyleAnimationValue::Add(property,
414 : destWrapper->mGeckoValue,
415 0 : valueToAddWrapper->mGeckoValue, aCount);
416 : }
417 :
418 : nsresult
419 0 : nsSMILCSSValueType::SandwichAdd(nsSMILValue& aDest,
420 : const nsSMILValue& aValueToAdd) const
421 : {
422 0 : return AddOrAccumulate(aDest, aValueToAdd, CompositeOperation::Add, 1)
423 0 : ? NS_OK
424 0 : : NS_ERROR_FAILURE;
425 : }
426 :
427 : nsresult
428 0 : nsSMILCSSValueType::Add(nsSMILValue& aDest, const nsSMILValue& aValueToAdd,
429 : uint32_t aCount) const
430 : {
431 0 : return AddOrAccumulate(aDest, aValueToAdd, CompositeOperation::Accumulate,
432 : aCount)
433 0 : ? NS_OK
434 0 : : NS_ERROR_FAILURE;
435 : }
436 :
437 : static nsresult
438 0 : ComputeDistanceForServo(const ValueWrapper* aFromWrapper,
439 : const ValueWrapper& aToWrapper,
440 : double& aDistance)
441 : {
442 0 : size_t len = aToWrapper.mServoValues.Length();
443 0 : MOZ_ASSERT(!aFromWrapper || aFromWrapper->mServoValues.Length() == len,
444 : "From and to values length should be the same if "
445 : "The start value exists");
446 :
447 0 : double squareDistance = 0;
448 :
449 0 : for (size_t i = 0; i < len; i++) {
450 : const RefPtr<RawServoAnimationValue>* fromValue =
451 0 : aFromWrapper ? &aFromWrapper->mServoValues[0] : nullptr;
452 0 : const RefPtr<RawServoAnimationValue>* toValue = &aToWrapper.mServoValues[0];
453 0 : RefPtr<RawServoAnimationValue> zeroValueStorage;
454 0 : if (!FinalizeServoAnimationValues(fromValue, toValue, zeroValueStorage)) {
455 0 : return NS_ERROR_FAILURE;
456 : }
457 :
458 0 : double distance = Servo_AnimationValues_ComputeDistance(*fromValue, *toValue);
459 0 : if (len == 1) {
460 0 : aDistance = distance;
461 0 : return NS_OK;
462 : }
463 0 : squareDistance += distance * distance;
464 : }
465 :
466 0 : aDistance = sqrt(squareDistance);
467 :
468 0 : return NS_OK;
469 : }
470 :
471 : nsresult
472 0 : nsSMILCSSValueType::ComputeDistance(const nsSMILValue& aFrom,
473 : const nsSMILValue& aTo,
474 : double& aDistance) const
475 : {
476 0 : MOZ_ASSERT(aFrom.mType == aTo.mType,
477 : "Trying to compare different types");
478 0 : MOZ_ASSERT(aFrom.mType == this, "Unexpected source type");
479 :
480 0 : const ValueWrapper* fromWrapper = ExtractValueWrapper(aFrom);
481 0 : const ValueWrapper* toWrapper = ExtractValueWrapper(aTo);
482 0 : MOZ_ASSERT(toWrapper, "expecting non-null endpoint");
483 :
484 0 : if (!toWrapper->mServoValues.IsEmpty()) {
485 0 : return ComputeDistanceForServo(fromWrapper, *toWrapper, aDistance);
486 : }
487 :
488 0 : const StyleAnimationValue* fromCSSValue = fromWrapper ?
489 0 : &fromWrapper->mGeckoValue : nullptr;
490 0 : const StyleAnimationValue* toCSSValue = &toWrapper->mGeckoValue;
491 0 : if (!FinalizeStyleAnimationValues(fromCSSValue, toCSSValue)) {
492 0 : return NS_ERROR_FAILURE;
493 : }
494 :
495 0 : return StyleAnimationValue::ComputeDistance(toWrapper->mPropID,
496 : fromWrapper->mGeckoValue,
497 : toWrapper->mGeckoValue,
498 : nullptr,
499 : aDistance)
500 0 : ? NS_OK
501 0 : : NS_ERROR_FAILURE;
502 : }
503 :
504 : static nsresult
505 0 : InterpolateForGecko(const ValueWrapper* aStartWrapper,
506 : const ValueWrapper& aEndWrapper,
507 : double aUnitDistance,
508 : nsSMILValue& aResult)
509 : {
510 : const StyleAnimationValue* startCSSValue = aStartWrapper
511 0 : ? &aStartWrapper->mGeckoValue
512 0 : : nullptr;
513 0 : const StyleAnimationValue* endCSSValue = &aEndWrapper.mGeckoValue;
514 0 : if (!FinalizeStyleAnimationValues(startCSSValue, endCSSValue)) {
515 0 : return NS_ERROR_FAILURE;
516 : }
517 :
518 0 : StyleAnimationValue resultValue;
519 0 : if (StyleAnimationValue::Interpolate(aEndWrapper.mPropID,
520 : *startCSSValue,
521 : *endCSSValue,
522 : aUnitDistance, resultValue)) {
523 0 : aResult.mU.mPtr = new ValueWrapper(aEndWrapper.mPropID, resultValue);
524 0 : return NS_OK;
525 : }
526 0 : return NS_ERROR_FAILURE;
527 : }
528 :
529 : static nsresult
530 0 : InterpolateForServo(const ValueWrapper* aStartWrapper,
531 : const ValueWrapper& aEndWrapper,
532 : double aUnitDistance,
533 : nsSMILValue& aResult)
534 : {
535 0 : ServoAnimationValues results;
536 0 : size_t len = aEndWrapper.mServoValues.Length();
537 0 : results.SetCapacity(len);
538 0 : MOZ_ASSERT(!aStartWrapper || aStartWrapper->mServoValues.Length() == len,
539 : "Start and end values length should be the same if "
540 : "The start value exists");
541 0 : for (size_t i = 0; i < len; i++) {
542 : const RefPtr<RawServoAnimationValue>*
543 : startValue = aStartWrapper
544 0 : ? &aStartWrapper->mServoValues[i]
545 0 : : nullptr;
546 0 : const RefPtr<RawServoAnimationValue>* endValue = &aEndWrapper.mServoValues[i];
547 0 : RefPtr<RawServoAnimationValue> zeroValueStorage;
548 0 : if (!FinalizeServoAnimationValues(startValue, endValue, zeroValueStorage)) {
549 0 : return NS_ERROR_FAILURE;
550 : }
551 :
552 : RefPtr<RawServoAnimationValue> result =
553 0 : Servo_AnimationValues_Interpolate(*startValue,
554 : *endValue,
555 0 : aUnitDistance).Consume();
556 0 : if (!result) {
557 0 : return NS_ERROR_FAILURE;
558 : }
559 0 : results.AppendElement(result);
560 : }
561 0 : aResult.mU.mPtr = new ValueWrapper(aEndWrapper.mPropID, Move(results));
562 :
563 0 : return NS_OK;
564 : }
565 :
566 : nsresult
567 0 : nsSMILCSSValueType::Interpolate(const nsSMILValue& aStartVal,
568 : const nsSMILValue& aEndVal,
569 : double aUnitDistance,
570 : nsSMILValue& aResult) const
571 : {
572 0 : MOZ_ASSERT(aStartVal.mType == aEndVal.mType,
573 : "Trying to interpolate different types");
574 0 : MOZ_ASSERT(aStartVal.mType == this,
575 : "Unexpected types for interpolation");
576 0 : MOZ_ASSERT(aResult.mType == this, "Unexpected result type");
577 0 : MOZ_ASSERT(aUnitDistance >= 0.0 && aUnitDistance <= 1.0,
578 : "unit distance value out of bounds");
579 0 : MOZ_ASSERT(!aResult.mU.mPtr, "expecting barely-initialized outparam");
580 :
581 0 : const ValueWrapper* startWrapper = ExtractValueWrapper(aStartVal);
582 0 : const ValueWrapper* endWrapper = ExtractValueWrapper(aEndVal);
583 0 : MOZ_ASSERT(endWrapper, "expecting non-null endpoint");
584 :
585 0 : if (!endWrapper->mServoValues.IsEmpty()) {
586 : return InterpolateForServo(startWrapper,
587 : *endWrapper,
588 : aUnitDistance,
589 0 : aResult);
590 : }
591 :
592 : return InterpolateForGecko(startWrapper,
593 : *endWrapper,
594 : aUnitDistance,
595 0 : aResult);
596 : }
597 :
598 : // Helper function to extract presContext
599 : static nsPresContext*
600 0 : GetPresContextForElement(Element* aElem)
601 : {
602 0 : nsIDocument* doc = aElem->GetUncomposedDoc();
603 0 : if (!doc) {
604 : // This can happen if we process certain types of restyles mid-sample
605 : // and remove anonymous animated content from the document as a result.
606 : // See bug 534975.
607 0 : return nullptr;
608 : }
609 0 : nsIPresShell* shell = doc->GetShell();
610 0 : return shell ? shell->GetPresContext() : nullptr;
611 : }
612 :
613 : static const nsDependentSubstring
614 0 : GetNonNegativePropValue(const nsAString& aString, nsCSSPropertyID aPropID,
615 : bool& aIsNegative)
616 : {
617 : // If value is negative, we'll strip off the "-" so the CSS parser won't
618 : // barf, and then manually make the parsed value negative.
619 : // (This is a partial solution to let us accept some otherwise out-of-bounds
620 : // CSS values. Bug 501188 will provide a more complete fix.)
621 0 : aIsNegative = false;
622 0 : uint32_t subStringBegin = 0;
623 :
624 : // NOTE: We need to opt-out 'stroke-dasharray' from the negative-number
625 : // check. Its values might look negative (e.g. by starting with "-1"), but
626 : // they're more complicated than our simple negation logic here can handle.
627 0 : if (aPropID != eCSSProperty_stroke_dasharray) {
628 0 : int32_t absValuePos = nsSMILParserUtils::CheckForNegativeNumber(aString);
629 0 : if (absValuePos > 0) {
630 0 : aIsNegative = true;
631 0 : subStringBegin = (uint32_t)absValuePos; // Start parsing after '-' sign
632 : }
633 : }
634 :
635 0 : return Substring(aString, subStringBegin);
636 : }
637 :
638 : // Helper function to parse a string into a StyleAnimationValue
639 : static bool
640 0 : ValueFromStringHelper(nsCSSPropertyID aPropID,
641 : Element* aTargetElement,
642 : nsPresContext* aPresContext,
643 : nsStyleContext* aStyleContext,
644 : const nsAString& aString,
645 : StyleAnimationValue& aStyleAnimValue,
646 : bool* aIsContextSensitive)
647 : {
648 0 : bool isNegative = false;
649 : const nsDependentSubstring subString =
650 0 : GetNonNegativePropValue(aString, aPropID, isNegative);
651 :
652 0 : if (!StyleAnimationValue::ComputeValue(aPropID, aTargetElement, aStyleContext,
653 : subString, true, aStyleAnimValue,
654 : aIsContextSensitive)) {
655 0 : return false;
656 : }
657 0 : if (isNegative) {
658 0 : InvertSign(aStyleAnimValue);
659 : }
660 :
661 0 : if (aPropID == eCSSProperty_font_size) {
662 : // Divide out text-zoom, since SVG is supposed to ignore it
663 0 : MOZ_ASSERT(aStyleAnimValue.GetUnit() == StyleAnimationValue::eUnit_Coord,
664 : "'font-size' value with unexpected style unit");
665 0 : aStyleAnimValue.SetCoordValue(aStyleAnimValue.GetCoordValue() /
666 0 : aPresContext->EffectiveTextZoom());
667 : }
668 0 : return true;
669 : }
670 :
671 : static ServoAnimationValues
672 0 : ValueFromStringHelper(nsCSSPropertyID aPropID,
673 : Element* aTargetElement,
674 : nsPresContext* aPresContext,
675 : nsStyleContext* aStyleContext,
676 : const nsAString& aString)
677 : {
678 0 : ServoAnimationValues result;
679 :
680 0 : nsIDocument* doc = aTargetElement->GetUncomposedDoc();
681 0 : if (!doc) {
682 0 : return result;
683 : }
684 :
685 : // Parse property
686 : // FIXME this is using the wrong base uri (bug 1343919)
687 0 : RefPtr<URLExtraData> data = new URLExtraData(doc->GetDocumentURI(),
688 0 : doc->GetDocumentURI(),
689 0 : doc->NodePrincipal());
690 0 : NS_ConvertUTF16toUTF8 value(aString);
691 : RefPtr<RawServoDeclarationBlock> servoDeclarationBlock =
692 0 : Servo_ParseProperty(aPropID,
693 : &value,
694 : data,
695 0 : ParsingMode::AllowUnitlessLength |
696 : ParsingMode::AllowAllNumericValues,
697 : doc->GetCompatibilityMode(),
698 0 : doc->CSSLoader()).Consume();
699 0 : if (!servoDeclarationBlock) {
700 0 : return result;
701 : }
702 :
703 : // Get a suitable style context for Servo
704 : const ServoComputedValues* currentStyle =
705 0 : aStyleContext->ComputedValues();
706 :
707 : // Compute value
708 0 : aPresContext->StyleSet()->AsServo()->GetAnimationValues(servoDeclarationBlock,
709 : aTargetElement,
710 : currentStyle,
711 0 : result);
712 0 : if (result.IsEmpty()) {
713 0 : return result;
714 : }
715 :
716 0 : if (aPropID == eCSSProperty_font_size) {
717 : // FIXME (bug 1357296): Divide out text-zoom, since SVG is supposed to
718 : // ignore it.
719 0 : if (aPresContext->EffectiveTextZoom() != 1.0) {
720 : NS_WARNING("stylo: Dividing out text-zoom not yet supported"
721 0 : " (bug 1357296)");
722 : }
723 : }
724 :
725 0 : return result;
726 : }
727 :
728 : // static
729 : void
730 0 : nsSMILCSSValueType::ValueFromString(nsCSSPropertyID aPropID,
731 : Element* aTargetElement,
732 : const nsAString& aString,
733 : nsSMILValue& aValue,
734 : bool* aIsContextSensitive)
735 : {
736 0 : MOZ_ASSERT(aValue.IsNull(), "Outparam should be null-typed");
737 0 : nsPresContext* presContext = GetPresContextForElement(aTargetElement);
738 0 : if (!presContext) {
739 0 : NS_WARNING("Not parsing animation value; unable to get PresContext");
740 0 : return;
741 : }
742 :
743 0 : nsIDocument* doc = aTargetElement->GetUncomposedDoc();
744 0 : if (doc && !nsStyleUtil::CSPAllowsInlineStyle(nullptr,
745 : doc->NodePrincipal(),
746 : doc->GetDocumentURI(),
747 : 0, aString, nullptr)) {
748 0 : return;
749 : }
750 :
751 : RefPtr<nsStyleContext> styleContext =
752 0 : nsComputedDOMStyle::GetStyleContext(aTargetElement, nullptr,
753 0 : presContext->PresShell());
754 0 : if (!styleContext) {
755 0 : return;
756 : }
757 :
758 0 : if (aTargetElement->IsStyledByServo()) {
759 : ServoAnimationValues parsedValues =
760 : ValueFromStringHelper(aPropID, aTargetElement, presContext,
761 0 : styleContext, aString);
762 0 : if (aIsContextSensitive) {
763 : // FIXME: Bug 1358955 - detect context-sensitive values and set this value
764 : // appropriately.
765 0 : *aIsContextSensitive = false;
766 : }
767 :
768 0 : if (!parsedValues.IsEmpty()) {
769 0 : sSingleton.Init(aValue);
770 0 : aValue.mU.mPtr = new ValueWrapper(aPropID, Move(parsedValues));
771 : }
772 0 : return;
773 : }
774 :
775 0 : StyleAnimationValue parsedValue;
776 0 : if (ValueFromStringHelper(aPropID, aTargetElement, presContext, styleContext,
777 : aString, parsedValue, aIsContextSensitive)) {
778 0 : sSingleton.Init(aValue);
779 0 : aValue.mU.mPtr = new ValueWrapper(aPropID, parsedValue);
780 : }
781 : }
782 :
783 : // static
784 : nsSMILValue
785 0 : nsSMILCSSValueType::ValueFromAnimationValue(nsCSSPropertyID aPropID,
786 : Element* aTargetElement,
787 : const AnimationValue& aValue)
788 : {
789 0 : nsSMILValue result;
790 :
791 0 : nsIDocument* doc = aTargetElement->GetUncomposedDoc();
792 : // We'd like to avoid serializing |aValue| if possible, and since the
793 : // string passed to CSPAllowsInlineStyle is only used for reporting violations
794 : // and an intermediate CSS value is not likely to be particularly useful
795 : // in that case, we just use a generic placeholder string instead.
796 : static const nsLiteralString kPlaceholderText =
797 0 : NS_LITERAL_STRING("[SVG animation of CSS]");
798 0 : if (doc && !nsStyleUtil::CSPAllowsInlineStyle(nullptr,
799 : doc->NodePrincipal(),
800 : doc->GetDocumentURI(),
801 0 : 0, kPlaceholderText, nullptr)) {
802 0 : return result;
803 : }
804 :
805 0 : sSingleton.Init(result);
806 0 : result.mU.mPtr = new ValueWrapper(aPropID, aValue);
807 :
808 0 : return result;
809 : }
810 :
811 : // static
812 : void
813 0 : nsSMILCSSValueType::ValueToString(const nsSMILValue& aValue,
814 : nsAString& aString)
815 : {
816 0 : MOZ_ASSERT(aValue.mType == &nsSMILCSSValueType::sSingleton,
817 : "Unexpected SMIL value type");
818 0 : const ValueWrapper* wrapper = ExtractValueWrapper(aValue);
819 0 : if (!wrapper) {
820 0 : return;
821 : }
822 :
823 0 : if (wrapper->mServoValues.IsEmpty()) {
824 : DebugOnly<bool> uncomputeResult =
825 0 : StyleAnimationValue::UncomputeValue(wrapper->mPropID,
826 : wrapper->mGeckoValue,
827 0 : aString);
828 0 : return;
829 : }
830 :
831 0 : if (nsCSSProps::IsShorthand(wrapper->mPropID)) {
832 : // In case of shorthand on servo, we iterate over all mServoValues array
833 : // since we have multiple AnimationValues in the array for each longhand
834 : // component.
835 0 : Servo_Shorthand_AnimationValues_Serialize(wrapper->mPropID,
836 : &wrapper->mServoValues,
837 0 : &aString);
838 0 : return;
839 : }
840 :
841 0 : Servo_AnimationValue_Serialize(wrapper->mServoValues[0],
842 0 : wrapper->mPropID,
843 0 : &aString);
844 : }
845 :
846 : // static
847 : nsCSSPropertyID
848 0 : nsSMILCSSValueType::PropertyFromValue(const nsSMILValue& aValue)
849 : {
850 0 : if (aValue.mType != &nsSMILCSSValueType::sSingleton) {
851 0 : return eCSSProperty_UNKNOWN;
852 : }
853 :
854 0 : const ValueWrapper* wrapper = ExtractValueWrapper(aValue);
855 0 : if (!wrapper) {
856 0 : return eCSSProperty_UNKNOWN;
857 : }
858 :
859 0 : return wrapper->mPropID;
860 : }
|