dom/smil/nsSMILCSSValueType.cpp

Tue, 06 Jan 2015 21:39:09 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Tue, 06 Jan 2015 21:39:09 +0100
branch
TOR_BUG_9701
changeset 8
97036ab72558
permissions
-rw-r--r--

Conditionally force memory storage according to privacy.thirdparty.isolate;
This solves Tor bug #9701, complying with disk avoidance documented in
https://www.torproject.org/projects/torbrowser/design/#disk-avoidance.

michael@0 1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
michael@0 2 /* This Source Code Form is subject to the terms of the Mozilla Public
michael@0 3 * License, v. 2.0. If a copy of the MPL was not distributed with this
michael@0 4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
michael@0 5
michael@0 6 /* representation of a value for a SMIL-animated CSS property */
michael@0 7
michael@0 8 #include "nsSMILCSSValueType.h"
michael@0 9 #include "nsString.h"
michael@0 10 #include "nsStyleAnimation.h"
michael@0 11 #include "nsSMILParserUtils.h"
michael@0 12 #include "nsSMILValue.h"
michael@0 13 #include "nsCSSValue.h"
michael@0 14 #include "nsColor.h"
michael@0 15 #include "nsPresContext.h"
michael@0 16 #include "mozilla/dom/Element.h"
michael@0 17 #include "nsDebug.h"
michael@0 18 #include "nsStyleUtil.h"
michael@0 19 #include "nsIDocument.h"
michael@0 20
michael@0 21 using namespace mozilla::dom;
michael@0 22
michael@0 23 /*static*/ nsSMILCSSValueType nsSMILCSSValueType::sSingleton;
michael@0 24
michael@0 25 struct ValueWrapper {
michael@0 26 ValueWrapper(nsCSSProperty aPropID, const nsStyleAnimation::Value& aValue) :
michael@0 27 mPropID(aPropID), mCSSValue(aValue) {}
michael@0 28
michael@0 29 nsCSSProperty mPropID;
michael@0 30 nsStyleAnimation::Value mCSSValue;
michael@0 31 };
michael@0 32
michael@0 33 // Helper Methods
michael@0 34 // --------------
michael@0 35 static const nsStyleAnimation::Value*
michael@0 36 GetZeroValueForUnit(nsStyleAnimation::Unit aUnit)
michael@0 37 {
michael@0 38 static const nsStyleAnimation::Value
michael@0 39 sZeroCoord(0, nsStyleAnimation::Value::CoordConstructor);
michael@0 40 static const nsStyleAnimation::Value
michael@0 41 sZeroPercent(0.0f, nsStyleAnimation::Value::PercentConstructor);
michael@0 42 static const nsStyleAnimation::Value
michael@0 43 sZeroFloat(0.0f, nsStyleAnimation::Value::FloatConstructor);
michael@0 44 static const nsStyleAnimation::Value
michael@0 45 sZeroColor(NS_RGB(0,0,0), nsStyleAnimation::Value::ColorConstructor);
michael@0 46
michael@0 47 NS_ABORT_IF_FALSE(aUnit != nsStyleAnimation::eUnit_Null,
michael@0 48 "Need non-null unit for a zero value");
michael@0 49 switch (aUnit) {
michael@0 50 case nsStyleAnimation::eUnit_Coord:
michael@0 51 return &sZeroCoord;
michael@0 52 case nsStyleAnimation::eUnit_Percent:
michael@0 53 return &sZeroPercent;
michael@0 54 case nsStyleAnimation::eUnit_Float:
michael@0 55 return &sZeroFloat;
michael@0 56 case nsStyleAnimation::eUnit_Color:
michael@0 57 return &sZeroColor;
michael@0 58 default:
michael@0 59 return nullptr;
michael@0 60 }
michael@0 61 }
michael@0 62
michael@0 63 // This method requires at least one of its arguments to be non-null.
michael@0 64 //
michael@0 65 // If one argument is null, this method updates it to point to "zero"
michael@0 66 // for the other argument's Unit (if applicable; otherwise, we return false).
michael@0 67 //
michael@0 68 // If neither argument is null, this method generally does nothing, though it
michael@0 69 // may apply a workaround for the special case where a 0 length-value is mixed
michael@0 70 // with a eUnit_Float value. (See comment below.)
michael@0 71 //
michael@0 72 // Returns true on success, or false.
michael@0 73 static const bool
michael@0 74 FinalizeStyleAnimationValues(const nsStyleAnimation::Value*& aValue1,
michael@0 75 const nsStyleAnimation::Value*& aValue2)
michael@0 76 {
michael@0 77 NS_ABORT_IF_FALSE(aValue1 || aValue2,
michael@0 78 "expecting at least one non-null value");
michael@0 79
michael@0 80 // Are we missing either val? (If so, it's an implied 0 in other val's units)
michael@0 81 if (!aValue1) {
michael@0 82 aValue1 = GetZeroValueForUnit(aValue2->GetUnit());
michael@0 83 return !!aValue1; // Fail if we have no zero value for this unit.
michael@0 84 }
michael@0 85 if (!aValue2) {
michael@0 86 aValue2 = GetZeroValueForUnit(aValue1->GetUnit());
michael@0 87 return !!aValue2; // Fail if we have no zero value for this unit.
michael@0 88 }
michael@0 89
michael@0 90 // Ok, both values were specified.
michael@0 91 // Need to handle a special-case, though: unitless nonzero length (parsed as
michael@0 92 // eUnit_Float) mixed with unitless 0 length (parsed as eUnit_Coord). These
michael@0 93 // won't interoperate in nsStyleAnimation, since their Units don't match.
michael@0 94 // In this case, we replace the eUnit_Coord 0 value with eUnit_Float 0 value.
michael@0 95 const nsStyleAnimation::Value& zeroCoord =
michael@0 96 *GetZeroValueForUnit(nsStyleAnimation::eUnit_Coord);
michael@0 97 if (*aValue1 == zeroCoord &&
michael@0 98 aValue2->GetUnit() == nsStyleAnimation::eUnit_Float) {
michael@0 99 aValue1 = GetZeroValueForUnit(nsStyleAnimation::eUnit_Float);
michael@0 100 } else if (*aValue2 == zeroCoord &&
michael@0 101 aValue1->GetUnit() == nsStyleAnimation::eUnit_Float) {
michael@0 102 aValue2 = GetZeroValueForUnit(nsStyleAnimation::eUnit_Float);
michael@0 103 }
michael@0 104
michael@0 105 return true;
michael@0 106 }
michael@0 107
michael@0 108 static void
michael@0 109 InvertSign(nsStyleAnimation::Value& aValue)
michael@0 110 {
michael@0 111 switch (aValue.GetUnit()) {
michael@0 112 case nsStyleAnimation::eUnit_Coord:
michael@0 113 aValue.SetCoordValue(-aValue.GetCoordValue());
michael@0 114 break;
michael@0 115 case nsStyleAnimation::eUnit_Percent:
michael@0 116 aValue.SetPercentValue(-aValue.GetPercentValue());
michael@0 117 break;
michael@0 118 case nsStyleAnimation::eUnit_Float:
michael@0 119 aValue.SetFloatValue(-aValue.GetFloatValue());
michael@0 120 break;
michael@0 121 default:
michael@0 122 NS_NOTREACHED("Calling InvertSign with an unsupported unit");
michael@0 123 break;
michael@0 124 }
michael@0 125 }
michael@0 126
michael@0 127 static ValueWrapper*
michael@0 128 ExtractValueWrapper(nsSMILValue& aValue)
michael@0 129 {
michael@0 130 return static_cast<ValueWrapper*>(aValue.mU.mPtr);
michael@0 131 }
michael@0 132
michael@0 133 static const ValueWrapper*
michael@0 134 ExtractValueWrapper(const nsSMILValue& aValue)
michael@0 135 {
michael@0 136 return static_cast<const ValueWrapper*>(aValue.mU.mPtr);
michael@0 137 }
michael@0 138
michael@0 139 // Class methods
michael@0 140 // -------------
michael@0 141 void
michael@0 142 nsSMILCSSValueType::Init(nsSMILValue& aValue) const
michael@0 143 {
michael@0 144 NS_ABORT_IF_FALSE(aValue.IsNull(), "Unexpected SMIL value type");
michael@0 145
michael@0 146 aValue.mU.mPtr = nullptr;
michael@0 147 aValue.mType = this;
michael@0 148 }
michael@0 149
michael@0 150 void
michael@0 151 nsSMILCSSValueType::Destroy(nsSMILValue& aValue) const
michael@0 152 {
michael@0 153 NS_ABORT_IF_FALSE(aValue.mType == this, "Unexpected SMIL value type");
michael@0 154 delete static_cast<ValueWrapper*>(aValue.mU.mPtr);
michael@0 155 aValue.mType = nsSMILNullType::Singleton();
michael@0 156 }
michael@0 157
michael@0 158 nsresult
michael@0 159 nsSMILCSSValueType::Assign(nsSMILValue& aDest, const nsSMILValue& aSrc) const
michael@0 160 {
michael@0 161 NS_ABORT_IF_FALSE(aDest.mType == aSrc.mType, "Incompatible SMIL types");
michael@0 162 NS_ABORT_IF_FALSE(aDest.mType == this, "Unexpected SMIL value type");
michael@0 163 const ValueWrapper* srcWrapper = ExtractValueWrapper(aSrc);
michael@0 164 ValueWrapper* destWrapper = ExtractValueWrapper(aDest);
michael@0 165
michael@0 166 if (srcWrapper) {
michael@0 167 if (!destWrapper) {
michael@0 168 // barely-initialized dest -- need to alloc & copy
michael@0 169 aDest.mU.mPtr = new ValueWrapper(*srcWrapper);
michael@0 170 } else {
michael@0 171 // both already fully-initialized -- just copy straight across
michael@0 172 *destWrapper = *srcWrapper;
michael@0 173 }
michael@0 174 } else if (destWrapper) {
michael@0 175 // fully-initialized dest, barely-initialized src -- clear dest
michael@0 176 delete destWrapper;
michael@0 177 aDest.mU.mPtr = destWrapper = nullptr;
michael@0 178 } // else, both are barely-initialized -- nothing to do.
michael@0 179
michael@0 180 return NS_OK;
michael@0 181 }
michael@0 182
michael@0 183 bool
michael@0 184 nsSMILCSSValueType::IsEqual(const nsSMILValue& aLeft,
michael@0 185 const nsSMILValue& aRight) const
michael@0 186 {
michael@0 187 NS_ABORT_IF_FALSE(aLeft.mType == aRight.mType, "Incompatible SMIL types");
michael@0 188 NS_ABORT_IF_FALSE(aLeft.mType == this, "Unexpected SMIL value");
michael@0 189 const ValueWrapper* leftWrapper = ExtractValueWrapper(aLeft);
michael@0 190 const ValueWrapper* rightWrapper = ExtractValueWrapper(aRight);
michael@0 191
michael@0 192 if (leftWrapper) {
michael@0 193 if (rightWrapper) {
michael@0 194 // Both non-null
michael@0 195 NS_WARN_IF_FALSE(leftWrapper != rightWrapper,
michael@0 196 "Two nsSMILValues with matching ValueWrapper ptr");
michael@0 197 return (leftWrapper->mPropID == rightWrapper->mPropID &&
michael@0 198 leftWrapper->mCSSValue == rightWrapper->mCSSValue);
michael@0 199 }
michael@0 200 // Left non-null, right null
michael@0 201 return false;
michael@0 202 }
michael@0 203 if (rightWrapper) {
michael@0 204 // Left null, right non-null
michael@0 205 return false;
michael@0 206 }
michael@0 207 // Both null
michael@0 208 return true;
michael@0 209 }
michael@0 210
michael@0 211 nsresult
michael@0 212 nsSMILCSSValueType::Add(nsSMILValue& aDest, const nsSMILValue& aValueToAdd,
michael@0 213 uint32_t aCount) const
michael@0 214 {
michael@0 215 NS_ABORT_IF_FALSE(aValueToAdd.mType == aDest.mType,
michael@0 216 "Trying to add invalid types");
michael@0 217 NS_ABORT_IF_FALSE(aValueToAdd.mType == this, "Unexpected source type");
michael@0 218
michael@0 219 ValueWrapper* destWrapper = ExtractValueWrapper(aDest);
michael@0 220 const ValueWrapper* valueToAddWrapper = ExtractValueWrapper(aValueToAdd);
michael@0 221 NS_ABORT_IF_FALSE(destWrapper || valueToAddWrapper,
michael@0 222 "need at least one fully-initialized value");
michael@0 223
michael@0 224 nsCSSProperty property = (valueToAddWrapper ? valueToAddWrapper->mPropID :
michael@0 225 destWrapper->mPropID);
michael@0 226 // Special case: font-size-adjust and stroke-dasharray are explicitly
michael@0 227 // non-additive (even though nsStyleAnimation *could* support adding them)
michael@0 228 if (property == eCSSProperty_font_size_adjust ||
michael@0 229 property == eCSSProperty_stroke_dasharray) {
michael@0 230 return NS_ERROR_FAILURE;
michael@0 231 }
michael@0 232
michael@0 233 const nsStyleAnimation::Value* valueToAdd = valueToAddWrapper ?
michael@0 234 &valueToAddWrapper->mCSSValue : nullptr;
michael@0 235 const nsStyleAnimation::Value* destValue = destWrapper ?
michael@0 236 &destWrapper->mCSSValue : nullptr;
michael@0 237 if (!FinalizeStyleAnimationValues(valueToAdd, destValue)) {
michael@0 238 return NS_ERROR_FAILURE;
michael@0 239 }
michael@0 240 // Did FinalizeStyleAnimationValues change destValue?
michael@0 241 // If so, update outparam to use the new value.
michael@0 242 if (destWrapper && &destWrapper->mCSSValue != destValue) {
michael@0 243 destWrapper->mCSSValue = *destValue;
michael@0 244 }
michael@0 245
michael@0 246 // Handle barely-initialized "zero" destination.
michael@0 247 if (!destWrapper) {
michael@0 248 aDest.mU.mPtr = destWrapper =
michael@0 249 new ValueWrapper(property, *destValue);
michael@0 250 }
michael@0 251
michael@0 252 return nsStyleAnimation::Add(property,
michael@0 253 destWrapper->mCSSValue, *valueToAdd, aCount) ?
michael@0 254 NS_OK : NS_ERROR_FAILURE;
michael@0 255 }
michael@0 256
michael@0 257 nsresult
michael@0 258 nsSMILCSSValueType::ComputeDistance(const nsSMILValue& aFrom,
michael@0 259 const nsSMILValue& aTo,
michael@0 260 double& aDistance) const
michael@0 261 {
michael@0 262 NS_ABORT_IF_FALSE(aFrom.mType == aTo.mType,
michael@0 263 "Trying to compare different types");
michael@0 264 NS_ABORT_IF_FALSE(aFrom.mType == this, "Unexpected source type");
michael@0 265
michael@0 266 const ValueWrapper* fromWrapper = ExtractValueWrapper(aFrom);
michael@0 267 const ValueWrapper* toWrapper = ExtractValueWrapper(aTo);
michael@0 268 NS_ABORT_IF_FALSE(toWrapper, "expecting non-null endpoint");
michael@0 269
michael@0 270 const nsStyleAnimation::Value* fromCSSValue = fromWrapper ?
michael@0 271 &fromWrapper->mCSSValue : nullptr;
michael@0 272 const nsStyleAnimation::Value* toCSSValue = &toWrapper->mCSSValue;
michael@0 273 if (!FinalizeStyleAnimationValues(fromCSSValue, toCSSValue)) {
michael@0 274 return NS_ERROR_FAILURE;
michael@0 275 }
michael@0 276
michael@0 277 return nsStyleAnimation::ComputeDistance(toWrapper->mPropID,
michael@0 278 *fromCSSValue, *toCSSValue,
michael@0 279 aDistance) ?
michael@0 280 NS_OK : NS_ERROR_FAILURE;
michael@0 281 }
michael@0 282
michael@0 283 nsresult
michael@0 284 nsSMILCSSValueType::Interpolate(const nsSMILValue& aStartVal,
michael@0 285 const nsSMILValue& aEndVal,
michael@0 286 double aUnitDistance,
michael@0 287 nsSMILValue& aResult) const
michael@0 288 {
michael@0 289 NS_ABORT_IF_FALSE(aStartVal.mType == aEndVal.mType,
michael@0 290 "Trying to interpolate different types");
michael@0 291 NS_ABORT_IF_FALSE(aStartVal.mType == this,
michael@0 292 "Unexpected types for interpolation");
michael@0 293 NS_ABORT_IF_FALSE(aResult.mType == this, "Unexpected result type");
michael@0 294 NS_ABORT_IF_FALSE(aUnitDistance >= 0.0 && aUnitDistance <= 1.0,
michael@0 295 "unit distance value out of bounds");
michael@0 296 NS_ABORT_IF_FALSE(!aResult.mU.mPtr, "expecting barely-initialized outparam");
michael@0 297
michael@0 298 const ValueWrapper* startWrapper = ExtractValueWrapper(aStartVal);
michael@0 299 const ValueWrapper* endWrapper = ExtractValueWrapper(aEndVal);
michael@0 300 NS_ABORT_IF_FALSE(endWrapper, "expecting non-null endpoint");
michael@0 301
michael@0 302 const nsStyleAnimation::Value* startCSSValue = startWrapper ?
michael@0 303 &startWrapper->mCSSValue : nullptr;
michael@0 304 const nsStyleAnimation::Value* endCSSValue = &endWrapper->mCSSValue;
michael@0 305 if (!FinalizeStyleAnimationValues(startCSSValue, endCSSValue)) {
michael@0 306 return NS_ERROR_FAILURE;
michael@0 307 }
michael@0 308
michael@0 309 nsStyleAnimation::Value resultValue;
michael@0 310 if (nsStyleAnimation::Interpolate(endWrapper->mPropID,
michael@0 311 *startCSSValue, *endCSSValue,
michael@0 312 aUnitDistance, resultValue)) {
michael@0 313 aResult.mU.mPtr = new ValueWrapper(endWrapper->mPropID, resultValue);
michael@0 314 return NS_OK;
michael@0 315 }
michael@0 316 return NS_ERROR_FAILURE;
michael@0 317 }
michael@0 318
michael@0 319 // Helper function to extract presContext
michael@0 320 static nsPresContext*
michael@0 321 GetPresContextForElement(Element* aElem)
michael@0 322 {
michael@0 323 nsIDocument* doc = aElem->GetCurrentDoc();
michael@0 324 if (!doc) {
michael@0 325 // This can happen if we process certain types of restyles mid-sample
michael@0 326 // and remove anonymous animated content from the document as a result.
michael@0 327 // See bug 534975.
michael@0 328 return nullptr;
michael@0 329 }
michael@0 330 nsIPresShell* shell = doc->GetShell();
michael@0 331 return shell ? shell->GetPresContext() : nullptr;
michael@0 332 }
michael@0 333
michael@0 334 // Helper function to parse a string into a nsStyleAnimation::Value
michael@0 335 static bool
michael@0 336 ValueFromStringHelper(nsCSSProperty aPropID,
michael@0 337 Element* aTargetElement,
michael@0 338 nsPresContext* aPresContext,
michael@0 339 const nsAString& aString,
michael@0 340 nsStyleAnimation::Value& aStyleAnimValue,
michael@0 341 bool* aIsContextSensitive)
michael@0 342 {
michael@0 343 // If value is negative, we'll strip off the "-" so the CSS parser won't
michael@0 344 // barf, and then manually make the parsed value negative.
michael@0 345 // (This is a partial solution to let us accept some otherwise out-of-bounds
michael@0 346 // CSS values. Bug 501188 will provide a more complete fix.)
michael@0 347 bool isNegative = false;
michael@0 348 uint32_t subStringBegin = 0;
michael@0 349
michael@0 350 // NOTE: We need to opt-out 'stroke-dasharray' from the negative-number
michael@0 351 // check. Its values might look negative (e.g. by starting with "-1"), but
michael@0 352 // they're more complicated than our simple negation logic here can handle.
michael@0 353 if (aPropID != eCSSProperty_stroke_dasharray) {
michael@0 354 int32_t absValuePos = nsSMILParserUtils::CheckForNegativeNumber(aString);
michael@0 355 if (absValuePos > 0) {
michael@0 356 isNegative = true;
michael@0 357 subStringBegin = (uint32_t)absValuePos; // Start parsing after '-' sign
michael@0 358 }
michael@0 359 }
michael@0 360 nsDependentSubstring subString(aString, subStringBegin);
michael@0 361 if (!nsStyleAnimation::ComputeValue(aPropID, aTargetElement, subString,
michael@0 362 true, aStyleAnimValue,
michael@0 363 aIsContextSensitive)) {
michael@0 364 return false;
michael@0 365 }
michael@0 366 if (isNegative) {
michael@0 367 InvertSign(aStyleAnimValue);
michael@0 368 }
michael@0 369
michael@0 370 if (aPropID == eCSSProperty_font_size) {
michael@0 371 // Divide out text-zoom, since SVG is supposed to ignore it
michael@0 372 NS_ABORT_IF_FALSE(aStyleAnimValue.GetUnit() ==
michael@0 373 nsStyleAnimation::eUnit_Coord,
michael@0 374 "'font-size' value with unexpected style unit");
michael@0 375 aStyleAnimValue.SetCoordValue(aStyleAnimValue.GetCoordValue() /
michael@0 376 aPresContext->TextZoom());
michael@0 377 }
michael@0 378 return true;
michael@0 379 }
michael@0 380
michael@0 381 // static
michael@0 382 void
michael@0 383 nsSMILCSSValueType::ValueFromString(nsCSSProperty aPropID,
michael@0 384 Element* aTargetElement,
michael@0 385 const nsAString& aString,
michael@0 386 nsSMILValue& aValue,
michael@0 387 bool* aIsContextSensitive)
michael@0 388 {
michael@0 389 NS_ABORT_IF_FALSE(aValue.IsNull(), "Outparam should be null-typed");
michael@0 390 nsPresContext* presContext = GetPresContextForElement(aTargetElement);
michael@0 391 if (!presContext) {
michael@0 392 NS_WARNING("Not parsing animation value; unable to get PresContext");
michael@0 393 return;
michael@0 394 }
michael@0 395
michael@0 396 nsIDocument* doc = aTargetElement->GetCurrentDoc();
michael@0 397 if (doc && !nsStyleUtil::CSPAllowsInlineStyle(nullptr,
michael@0 398 doc->NodePrincipal(),
michael@0 399 doc->GetDocumentURI(),
michael@0 400 0, aString, nullptr)) {
michael@0 401 return;
michael@0 402 }
michael@0 403
michael@0 404 nsStyleAnimation::Value parsedValue;
michael@0 405 if (ValueFromStringHelper(aPropID, aTargetElement, presContext,
michael@0 406 aString, parsedValue, aIsContextSensitive)) {
michael@0 407 sSingleton.Init(aValue);
michael@0 408 aValue.mU.mPtr = new ValueWrapper(aPropID, parsedValue);
michael@0 409 }
michael@0 410 }
michael@0 411
michael@0 412 // static
michael@0 413 bool
michael@0 414 nsSMILCSSValueType::ValueToString(const nsSMILValue& aValue,
michael@0 415 nsAString& aString)
michael@0 416 {
michael@0 417 NS_ABORT_IF_FALSE(aValue.mType == &nsSMILCSSValueType::sSingleton,
michael@0 418 "Unexpected SMIL value type");
michael@0 419 const ValueWrapper* wrapper = ExtractValueWrapper(aValue);
michael@0 420 return !wrapper ||
michael@0 421 nsStyleAnimation::UncomputeValue(wrapper->mPropID,
michael@0 422 wrapper->mCSSValue, aString);
michael@0 423 }

mercurial