forked from mirrors/gecko-dev
		
	-Wmissing-prototypes is a new optional warning available in clang ToT. It warns about global functions that have no previous function declaration (e.g. from an #included header file). These functions can probably be made static (allowing the compiler to better optimize them) or they may be unused. Confusingly, clang's -Wmissing-prototypes is equivalent to gcc's -Wmissing-declarations, not gcc's -Wmissing-prototypes. A function prototype is a function declaration that specifies the function's argument types. C++ requires that all function declarations specify their argument types, but C does not. As such, gcc's -Wmissing-prototypes is a C-only warning about C functions that have no previous function *prototypes* (with argument types), even if a previous function *declaration* (without argument types) was seen. MozReview-Commit-ID: FGKVLzeQ2oK --HG-- extra : rebase_source : 81e62163bf41a5d5dd87abf5397e6e8c62ed4096 extra : source : 653a9fc279e2f6a6d066474a94a70d65ac703d6b
		
			
				
	
	
		
			5633 lines
		
	
	
	
		
			201 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			5633 lines
		
	
	
	
		
			201 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 | 
						|
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
 | 
						|
/* This Source Code Form is subject to the terms of the Mozilla Public
 | 
						|
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 | 
						|
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 | 
						|
 | 
						|
/* Utilities for animation of computed style values */
 | 
						|
 | 
						|
#include "mozilla/StyleAnimationValue.h"
 | 
						|
 | 
						|
#include "mozilla/ArrayUtils.h"
 | 
						|
#include "mozilla/MathAlgorithms.h"
 | 
						|
#include "mozilla/RuleNodeCacheConditions.h"
 | 
						|
#include "mozilla/ServoBindings.h"
 | 
						|
#include "mozilla/StyleSetHandle.h"
 | 
						|
#include "mozilla/StyleSetHandleInlines.h"
 | 
						|
#include "mozilla/Tuple.h"
 | 
						|
#include "mozilla/UniquePtr.h"
 | 
						|
#include "nsAutoPtr.h"
 | 
						|
#include "nsCOMArray.h"
 | 
						|
#ifdef MOZ_OLD_STYLE
 | 
						|
#include "nsIStyleRule.h"
 | 
						|
#include "mozilla/css/StyleRule.h"
 | 
						|
#endif
 | 
						|
#include "nsString.h"
 | 
						|
#include "nsStyleContext.h"
 | 
						|
#ifdef MOZ_OLD_STYLE
 | 
						|
#include "nsStyleSet.h"
 | 
						|
#endif
 | 
						|
#include "nsComputedDOMStyle.h"
 | 
						|
#include "nsContentUtils.h"
 | 
						|
#include "nsCSSParser.h"
 | 
						|
#include "nsCSSPseudoElements.h"
 | 
						|
#ifdef MOZ_OLD_STYLE
 | 
						|
#include "mozilla/css/Declaration.h"
 | 
						|
#endif
 | 
						|
#include "mozilla/dom/Element.h"
 | 
						|
#include "mozilla/FloatingPoint.h"
 | 
						|
#include "mozilla/Likely.h"
 | 
						|
#include "mozilla/ServoBindings.h" // RawServoDeclarationBlock
 | 
						|
#include "mozilla/ServoCSSParser.h"
 | 
						|
#include "gfxMatrix.h"
 | 
						|
#include "gfxQuaternion.h"
 | 
						|
#include "nsIDocument.h"
 | 
						|
#include "nsIFrame.h"
 | 
						|
#include "gfx2DGlue.h"
 | 
						|
#include "nsStyleContextInlines.h"
 | 
						|
 | 
						|
using namespace mozilla;
 | 
						|
using namespace mozilla::css;
 | 
						|
using namespace mozilla::gfx;
 | 
						|
using nsStyleTransformMatrix::Decompose2DMatrix;
 | 
						|
using nsStyleTransformMatrix::Decompose3DMatrix;
 | 
						|
using nsStyleTransformMatrix::ShearType;
 | 
						|
 | 
						|
#ifdef MOZ_OLD_STYLE
 | 
						|
// HELPER METHODS
 | 
						|
// --------------
 | 
						|
/*
 | 
						|
 * Given two units, this method returns a common unit that they can both be
 | 
						|
 * converted into, if possible.  This is intended to facilitate
 | 
						|
 * interpolation, distance-computation, and addition between "similar" units.
 | 
						|
 *
 | 
						|
 * The ordering of the arguments should not affect the output of this method.
 | 
						|
 *
 | 
						|
 * If there's no sensible common unit, this method returns eUnit_Null.
 | 
						|
 *
 | 
						|
 * @param   aFirstUnit One unit to resolve.
 | 
						|
 * @param   aFirstUnit The other unit to resolve.
 | 
						|
 * @return  A "common" unit that both source units can be converted into, or
 | 
						|
 *          eUnit_Null if that's not possible.
 | 
						|
 */
 | 
						|
static
 | 
						|
StyleAnimationValue::Unit
 | 
						|
GetCommonUnit(nsCSSPropertyID aProperty,
 | 
						|
              StyleAnimationValue::Unit aFirstUnit,
 | 
						|
              StyleAnimationValue::Unit aSecondUnit)
 | 
						|
{
 | 
						|
  if (aFirstUnit != aSecondUnit) {
 | 
						|
    if (nsCSSProps::PropHasFlags(aProperty, CSS_PROPERTY_STORES_CALC) &&
 | 
						|
        (aFirstUnit == StyleAnimationValue::eUnit_Coord ||
 | 
						|
         aFirstUnit == StyleAnimationValue::eUnit_Percent ||
 | 
						|
         aFirstUnit == StyleAnimationValue::eUnit_Calc) &&
 | 
						|
        (aSecondUnit == StyleAnimationValue::eUnit_Coord ||
 | 
						|
         aSecondUnit == StyleAnimationValue::eUnit_Percent ||
 | 
						|
         aSecondUnit == StyleAnimationValue::eUnit_Calc)) {
 | 
						|
      // We can use calc() as the common unit.
 | 
						|
      return StyleAnimationValue::eUnit_Calc;
 | 
						|
    }
 | 
						|
    if ((aFirstUnit == StyleAnimationValue::eUnit_Color ||
 | 
						|
         aFirstUnit == StyleAnimationValue::eUnit_CurrentColor ||
 | 
						|
         aFirstUnit == StyleAnimationValue::eUnit_ComplexColor) &&
 | 
						|
        (aSecondUnit == StyleAnimationValue::eUnit_Color ||
 | 
						|
         aSecondUnit == StyleAnimationValue::eUnit_CurrentColor ||
 | 
						|
         aSecondUnit == StyleAnimationValue::eUnit_ComplexColor)) {
 | 
						|
      // We can use complex color as the common unit.
 | 
						|
      return StyleAnimationValue::eUnit_ComplexColor;
 | 
						|
    }
 | 
						|
    return StyleAnimationValue::eUnit_Null;
 | 
						|
  }
 | 
						|
  return aFirstUnit;
 | 
						|
}
 | 
						|
 | 
						|
static
 | 
						|
nsCSSUnit
 | 
						|
GetCommonUnit(nsCSSPropertyID aProperty,
 | 
						|
              nsCSSUnit aFirstUnit,
 | 
						|
              nsCSSUnit aSecondUnit)
 | 
						|
{
 | 
						|
  if (aFirstUnit != aSecondUnit) {
 | 
						|
    if (nsCSSProps::PropHasFlags(aProperty, CSS_PROPERTY_STORES_CALC) &&
 | 
						|
        (aFirstUnit == eCSSUnit_Pixel ||
 | 
						|
         aFirstUnit == eCSSUnit_Percent ||
 | 
						|
         aFirstUnit == eCSSUnit_Calc) &&
 | 
						|
        (aSecondUnit == eCSSUnit_Pixel ||
 | 
						|
         aSecondUnit == eCSSUnit_Percent ||
 | 
						|
         aSecondUnit == eCSSUnit_Calc)) {
 | 
						|
      // We can use calc() as the common unit.
 | 
						|
      return eCSSUnit_Calc;
 | 
						|
    }
 | 
						|
    return eCSSUnit_Null;
 | 
						|
  }
 | 
						|
  return aFirstUnit;
 | 
						|
}
 | 
						|
 | 
						|
static nsCSSKeyword
 | 
						|
ToPrimitive(nsCSSKeyword aKeyword)
 | 
						|
{
 | 
						|
  switch (aKeyword) {
 | 
						|
    case eCSSKeyword_translatex:
 | 
						|
    case eCSSKeyword_translatey:
 | 
						|
    case eCSSKeyword_translatez:
 | 
						|
    case eCSSKeyword_translate:
 | 
						|
      return eCSSKeyword_translate3d;
 | 
						|
    case eCSSKeyword_scalex:
 | 
						|
    case eCSSKeyword_scaley:
 | 
						|
    case eCSSKeyword_scalez:
 | 
						|
    case eCSSKeyword_scale:
 | 
						|
      return eCSSKeyword_scale3d;
 | 
						|
    default:
 | 
						|
      return aKeyword;
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
static bool
 | 
						|
HasAccumulateMatrix(const nsCSSValueList* aList)
 | 
						|
{
 | 
						|
  const nsCSSValueList *item = aList;
 | 
						|
  do {
 | 
						|
    nsCSSKeyword func =
 | 
						|
      nsStyleTransformMatrix::TransformFunctionOf(item->mValue.GetArrayValue());
 | 
						|
    if (func == eCSSKeyword_accumulatematrix) {
 | 
						|
      return true;
 | 
						|
    }
 | 
						|
    item = item->mNext;
 | 
						|
  } while (item);
 | 
						|
 | 
						|
  return false;
 | 
						|
}
 | 
						|
 | 
						|
static bool
 | 
						|
TransformFunctionsMatch(nsCSSKeyword func1, nsCSSKeyword func2)
 | 
						|
{
 | 
						|
  // Handle eCSSKeyword_accumulatematrix as different function to be calculated
 | 
						|
  // (decomposed and recomposed) them later.
 | 
						|
  if (func1 == eCSSKeyword_accumulatematrix ||
 | 
						|
      func2 == eCSSKeyword_accumulatematrix) {
 | 
						|
    return false;
 | 
						|
  }
 | 
						|
 | 
						|
  return ToPrimitive(func1) == ToPrimitive(func2);
 | 
						|
}
 | 
						|
 | 
						|
static bool
 | 
						|
TransformFunctionListsMatch(const nsCSSValueList *list1,
 | 
						|
                            const nsCSSValueList *list2)
 | 
						|
{
 | 
						|
  const nsCSSValueList *item1 = list1, *item2 = list2;
 | 
						|
  do {
 | 
						|
    nsCSSKeyword func1 = nsStyleTransformMatrix::TransformFunctionOf(
 | 
						|
        item1->mValue.GetArrayValue());
 | 
						|
    nsCSSKeyword func2 = nsStyleTransformMatrix::TransformFunctionOf(
 | 
						|
        item2->mValue.GetArrayValue());
 | 
						|
 | 
						|
    if (!TransformFunctionsMatch(func1, func2)) {
 | 
						|
      return false;
 | 
						|
    }
 | 
						|
 | 
						|
    item1 = item1->mNext;
 | 
						|
    item2 = item2->mNext;
 | 
						|
  } while (item1 && item2);
 | 
						|
 | 
						|
  // Length match?
 | 
						|
  return !item1 && !item2;
 | 
						|
}
 | 
						|
#endif
 | 
						|
 | 
						|
static already_AddRefed<nsCSSValue::Array>
 | 
						|
AppendFunction(nsCSSKeyword aTransformFunction)
 | 
						|
{
 | 
						|
  uint32_t nargs;
 | 
						|
  switch (aTransformFunction) {
 | 
						|
    case eCSSKeyword_matrix3d:
 | 
						|
      nargs = 16;
 | 
						|
      break;
 | 
						|
    case eCSSKeyword_matrix:
 | 
						|
      nargs = 6;
 | 
						|
      break;
 | 
						|
    case eCSSKeyword_rotate3d:
 | 
						|
      nargs = 4;
 | 
						|
      break;
 | 
						|
    case eCSSKeyword_interpolatematrix:
 | 
						|
    case eCSSKeyword_accumulatematrix:
 | 
						|
    case eCSSKeyword_translate3d:
 | 
						|
    case eCSSKeyword_scale3d:
 | 
						|
      nargs = 3;
 | 
						|
      break;
 | 
						|
    case eCSSKeyword_translate:
 | 
						|
    case eCSSKeyword_skew:
 | 
						|
    case eCSSKeyword_scale:
 | 
						|
      nargs = 2;
 | 
						|
      break;
 | 
						|
    default:
 | 
						|
      NS_ERROR("must be a transform function");
 | 
						|
      MOZ_FALLTHROUGH;
 | 
						|
    case eCSSKeyword_translatex:
 | 
						|
    case eCSSKeyword_translatey:
 | 
						|
    case eCSSKeyword_translatez:
 | 
						|
    case eCSSKeyword_scalex:
 | 
						|
    case eCSSKeyword_scaley:
 | 
						|
    case eCSSKeyword_scalez:
 | 
						|
    case eCSSKeyword_skewx:
 | 
						|
    case eCSSKeyword_skewy:
 | 
						|
    case eCSSKeyword_rotate:
 | 
						|
    case eCSSKeyword_rotatex:
 | 
						|
    case eCSSKeyword_rotatey:
 | 
						|
    case eCSSKeyword_rotatez:
 | 
						|
    case eCSSKeyword_perspective:
 | 
						|
      nargs = 1;
 | 
						|
      break;
 | 
						|
  }
 | 
						|
 | 
						|
  RefPtr<nsCSSValue::Array> arr = nsCSSValue::Array::Create(nargs + 1);
 | 
						|
  arr->Item(0).SetIntValue(aTransformFunction, eCSSUnit_Enumerated);
 | 
						|
 | 
						|
  return arr.forget();
 | 
						|
}
 | 
						|
 | 
						|
#ifdef MOZ_OLD_STYLE
 | 
						|
static already_AddRefed<nsCSSValue::Array>
 | 
						|
ToPrimitive(nsCSSValue::Array* aArray)
 | 
						|
{
 | 
						|
  nsCSSKeyword tfunc = nsStyleTransformMatrix::TransformFunctionOf(aArray);
 | 
						|
  nsCSSKeyword primitive = ToPrimitive(tfunc);
 | 
						|
  RefPtr<nsCSSValue::Array> arr = AppendFunction(primitive);
 | 
						|
 | 
						|
  // FIXME: This would produce fewer calc() expressions if the
 | 
						|
  // zero were of compatible type (length vs. percent) when
 | 
						|
  // needed.
 | 
						|
 | 
						|
  nsCSSValue zero(0.0f, eCSSUnit_Pixel);
 | 
						|
  nsCSSValue one(1.0f, eCSSUnit_Number);
 | 
						|
  switch(tfunc) {
 | 
						|
    case eCSSKeyword_translate:
 | 
						|
    {
 | 
						|
      MOZ_ASSERT(aArray->Count() == 2 || aArray->Count() == 3,
 | 
						|
                 "unexpected count");
 | 
						|
      arr->Item(1) = aArray->Item(1);
 | 
						|
      arr->Item(2) = aArray->Count() == 3 ? aArray->Item(2) : zero;
 | 
						|
      arr->Item(3) = zero;
 | 
						|
      break;
 | 
						|
    }
 | 
						|
    case eCSSKeyword_translatex:
 | 
						|
    {
 | 
						|
      MOZ_ASSERT(aArray->Count() == 2, "unexpected count");
 | 
						|
      arr->Item(1) = aArray->Item(1);
 | 
						|
      arr->Item(2) = zero;
 | 
						|
      arr->Item(3) = zero;
 | 
						|
      break;
 | 
						|
    }
 | 
						|
    case eCSSKeyword_translatey:
 | 
						|
    {
 | 
						|
      MOZ_ASSERT(aArray->Count() == 2, "unexpected count");
 | 
						|
      arr->Item(1) = zero;
 | 
						|
      arr->Item(2) = aArray->Item(1);
 | 
						|
      arr->Item(3) = zero;
 | 
						|
      break;
 | 
						|
    }
 | 
						|
    case eCSSKeyword_translatez:
 | 
						|
    {
 | 
						|
      MOZ_ASSERT(aArray->Count() == 2, "unexpected count");
 | 
						|
      arr->Item(1) = zero;
 | 
						|
      arr->Item(2) = zero;
 | 
						|
      arr->Item(3) = aArray->Item(1);
 | 
						|
      break;
 | 
						|
    }
 | 
						|
    case eCSSKeyword_scale:
 | 
						|
    {
 | 
						|
      MOZ_ASSERT(aArray->Count() == 2 || aArray->Count() == 3,
 | 
						|
                 "unexpected count");
 | 
						|
      arr->Item(1) = aArray->Item(1);
 | 
						|
      arr->Item(2) = aArray->Count() == 3 ? aArray->Item(2) : aArray->Item(1);
 | 
						|
      arr->Item(3) = one;
 | 
						|
      break;
 | 
						|
    }
 | 
						|
    case eCSSKeyword_scalex:
 | 
						|
    {
 | 
						|
      MOZ_ASSERT(aArray->Count() == 2, "unexpected count");
 | 
						|
      arr->Item(1) = aArray->Item(1);
 | 
						|
      arr->Item(2) = one;
 | 
						|
      arr->Item(3) = one;
 | 
						|
      break;
 | 
						|
    }
 | 
						|
    case eCSSKeyword_scaley:
 | 
						|
    {
 | 
						|
      MOZ_ASSERT(aArray->Count() == 2, "unexpected count");
 | 
						|
      arr->Item(1) = one;
 | 
						|
      arr->Item(2) = aArray->Item(1);
 | 
						|
      arr->Item(3) = one;
 | 
						|
      break;
 | 
						|
    }
 | 
						|
    case eCSSKeyword_scalez:
 | 
						|
    {
 | 
						|
      MOZ_ASSERT(aArray->Count() == 2, "unexpected count");
 | 
						|
      arr->Item(1) = one;
 | 
						|
      arr->Item(2) = one;
 | 
						|
      arr->Item(3) = aArray->Item(1);
 | 
						|
      break;
 | 
						|
    }
 | 
						|
    default:
 | 
						|
      arr = aArray;
 | 
						|
  }
 | 
						|
  return arr.forget();
 | 
						|
}
 | 
						|
 | 
						|
static void
 | 
						|
AppendCSSShadowValue(const nsCSSShadowItem *aShadow,
 | 
						|
                     nsCSSValueList **&aResultTail,
 | 
						|
                     nsCSSPropertyID aProperty)
 | 
						|
{
 | 
						|
  MOZ_ASSERT(aShadow, "shadow expected");
 | 
						|
 | 
						|
  // X, Y, Radius, Spread, Color, Inset
 | 
						|
  RefPtr<nsCSSValue::Array> arr = nsCSSValue::Array::Create(6);
 | 
						|
  arr->Item(0).SetIntegerCoordValue(aShadow->mXOffset);
 | 
						|
  arr->Item(1).SetIntegerCoordValue(aShadow->mYOffset);
 | 
						|
  arr->Item(2).SetIntegerCoordValue(aShadow->mRadius);
 | 
						|
  if (aProperty == eCSSProperty_box_shadow) {
 | 
						|
    arr->Item(3).SetIntegerCoordValue(aShadow->mSpread);
 | 
						|
  }
 | 
						|
  if (aShadow->mHasColor) {
 | 
						|
    arr->Item(4).SetColorValue(aShadow->mColor);
 | 
						|
  }
 | 
						|
  if (aShadow->mInset) {
 | 
						|
    arr->Item(5).SetEnumValue(StyleBoxShadowType::Inset);
 | 
						|
  }
 | 
						|
 | 
						|
  nsCSSValueList *resultItem = new nsCSSValueList;
 | 
						|
  resultItem->mValue.SetArrayValue(arr, eCSSUnit_Array);
 | 
						|
  *aResultTail = resultItem;
 | 
						|
  aResultTail = &resultItem->mNext;
 | 
						|
}
 | 
						|
 | 
						|
// Like nsStyleCoord::CalcValue, but with length in float pixels instead
 | 
						|
// of nscoord.
 | 
						|
struct PixelCalcValue
 | 
						|
{
 | 
						|
  float mLength, mPercent;
 | 
						|
  bool mHasPercent;
 | 
						|
};
 | 
						|
 | 
						|
// Requires a canonical calc() value that we generated.
 | 
						|
static PixelCalcValue
 | 
						|
ExtractCalcValueInternal(const nsCSSValue& aValue)
 | 
						|
{
 | 
						|
  MOZ_ASSERT(aValue.GetUnit() == eCSSUnit_Calc, "unexpected unit");
 | 
						|
  nsCSSValue::Array *arr = aValue.GetArrayValue();
 | 
						|
  MOZ_ASSERT(arr->Count() == 1, "unexpected length");
 | 
						|
 | 
						|
  const nsCSSValue &topval = arr->Item(0);
 | 
						|
  PixelCalcValue result;
 | 
						|
  if (topval.GetUnit() == eCSSUnit_Pixel) {
 | 
						|
    result.mLength = topval.GetFloatValue();
 | 
						|
    result.mPercent = 0.0f;
 | 
						|
    result.mHasPercent = false;
 | 
						|
  } else {
 | 
						|
    MOZ_ASSERT(topval.GetUnit() == eCSSUnit_Calc_Plus,
 | 
						|
               "unexpected unit");
 | 
						|
    nsCSSValue::Array *arr2 = topval.GetArrayValue();
 | 
						|
    const nsCSSValue &len = arr2->Item(0);
 | 
						|
    const nsCSSValue &pct = arr2->Item(1);
 | 
						|
    MOZ_ASSERT(len.GetUnit() == eCSSUnit_Pixel, "unexpected unit");
 | 
						|
    MOZ_ASSERT(pct.GetUnit() == eCSSUnit_Percent, "unexpected unit");
 | 
						|
    result.mLength = len.GetFloatValue();
 | 
						|
    result.mPercent = pct.GetPercentValue();
 | 
						|
    result.mHasPercent = true;
 | 
						|
  }
 | 
						|
 | 
						|
  return result;
 | 
						|
}
 | 
						|
 | 
						|
// Requires a canonical calc() value that we generated.
 | 
						|
static PixelCalcValue
 | 
						|
ExtractCalcValue(const StyleAnimationValue& aValue)
 | 
						|
{
 | 
						|
  PixelCalcValue result;
 | 
						|
  if (aValue.GetUnit() == StyleAnimationValue::eUnit_Coord) {
 | 
						|
    result.mLength =
 | 
						|
      nsPresContext::AppUnitsToFloatCSSPixels(aValue.GetCoordValue());
 | 
						|
    result.mPercent = 0.0f;
 | 
						|
    result.mHasPercent = false;
 | 
						|
    return result;
 | 
						|
  }
 | 
						|
  if (aValue.GetUnit() == StyleAnimationValue::eUnit_Percent) {
 | 
						|
    result.mLength = 0.0f;
 | 
						|
    result.mPercent = aValue.GetPercentValue();
 | 
						|
    result.mHasPercent = true;
 | 
						|
    return result;
 | 
						|
  }
 | 
						|
  MOZ_ASSERT(aValue.GetUnit() == StyleAnimationValue::eUnit_Calc,
 | 
						|
             "unexpected unit");
 | 
						|
  nsCSSValue *val = aValue.GetCSSValueValue();
 | 
						|
  return ExtractCalcValueInternal(*val);
 | 
						|
}
 | 
						|
 | 
						|
static PixelCalcValue
 | 
						|
ExtractCalcValue(const nsCSSValue& aValue)
 | 
						|
{
 | 
						|
  PixelCalcValue result;
 | 
						|
  if (aValue.GetUnit() == eCSSUnit_Pixel) {
 | 
						|
    result.mLength = aValue.GetFloatValue();
 | 
						|
    result.mPercent = 0.0f;
 | 
						|
    result.mHasPercent = false;
 | 
						|
    return result;
 | 
						|
  }
 | 
						|
  if (aValue.GetUnit() == eCSSUnit_Percent) {
 | 
						|
    result.mLength = 0.0f;
 | 
						|
    result.mPercent = aValue.GetPercentValue();
 | 
						|
    result.mHasPercent = true;
 | 
						|
    return result;
 | 
						|
  }
 | 
						|
  return ExtractCalcValueInternal(aValue);
 | 
						|
}
 | 
						|
 | 
						|
static void
 | 
						|
CalcValueToCSSValue(const nsStyleCoord::CalcValue* aCalc, nsCSSValue& aValue)
 | 
						|
{
 | 
						|
  aValue.SetCalcValue(aCalc);
 | 
						|
}
 | 
						|
 | 
						|
static void
 | 
						|
CalcValueToCSSValue(const PixelCalcValue& aCalc, nsCSSValue& aValue)
 | 
						|
{
 | 
						|
  RefPtr<nsCSSValue::Array> arr = nsCSSValue::Array::Create(1);
 | 
						|
  if (!aCalc.mHasPercent) {
 | 
						|
    arr->Item(0).SetFloatValue(aCalc.mLength, eCSSUnit_Pixel);
 | 
						|
  } else {
 | 
						|
    nsCSSValue::Array *arr2 = nsCSSValue::Array::Create(2);
 | 
						|
    arr->Item(0).SetArrayValue(arr2, eCSSUnit_Calc_Plus);
 | 
						|
    arr2->Item(0).SetFloatValue(aCalc.mLength, eCSSUnit_Pixel);
 | 
						|
    arr2->Item(1).SetPercentValue(aCalc.mPercent);
 | 
						|
  }
 | 
						|
 | 
						|
  aValue.SetArrayValue(arr, eCSSUnit_Calc);
 | 
						|
}
 | 
						|
 | 
						|
static double
 | 
						|
CalcPositionSquareDistance(const nsCSSValue& aPos1,
 | 
						|
                           const nsCSSValue& aPos2)
 | 
						|
{
 | 
						|
  NS_ASSERTION(aPos1.GetUnit() == eCSSUnit_Array &&
 | 
						|
               aPos2.GetUnit() == eCSSUnit_Array,
 | 
						|
               "Expected two arrays");
 | 
						|
 | 
						|
  PixelCalcValue calcVal[4];
 | 
						|
 | 
						|
  nsCSSValue::Array* posArray = aPos1.GetArrayValue();
 | 
						|
  MOZ_ASSERT(posArray->Count() == 4, "Invalid position value");
 | 
						|
  NS_ASSERTION(posArray->Item(0).GetUnit() == eCSSUnit_Null &&
 | 
						|
               posArray->Item(2).GetUnit() == eCSSUnit_Null,
 | 
						|
               "Invalid list used");
 | 
						|
  for (int i = 0; i < 2; ++i) {
 | 
						|
    MOZ_ASSERT(posArray->Item(i*2+1).GetUnit() != eCSSUnit_Null,
 | 
						|
               "Invalid position value");
 | 
						|
    calcVal[i] = ExtractCalcValue(posArray->Item(i*2+1));
 | 
						|
  }
 | 
						|
 | 
						|
  posArray = aPos2.GetArrayValue();
 | 
						|
  MOZ_ASSERT(posArray->Count() == 4, "Invalid position value");
 | 
						|
  NS_ASSERTION(posArray->Item(0).GetUnit() == eCSSUnit_Null &&
 | 
						|
               posArray->Item(2).GetUnit() == eCSSUnit_Null,
 | 
						|
               "Invalid list used");
 | 
						|
  for (int i = 0; i < 2; ++i) {
 | 
						|
    MOZ_ASSERT(posArray->Item(i*2+1).GetUnit() != eCSSUnit_Null,
 | 
						|
               "Invalid position value");
 | 
						|
    calcVal[i+2] = ExtractCalcValue(posArray->Item(i*2+1));
 | 
						|
  }
 | 
						|
 | 
						|
  double squareDistance = 0.0;
 | 
						|
  for (int i = 0; i < 2; ++i) {
 | 
						|
    float difflen = calcVal[i+2].mLength - calcVal[i].mLength;
 | 
						|
    float diffpct = calcVal[i+2].mPercent - calcVal[i].mPercent;
 | 
						|
    squareDistance += difflen * difflen + diffpct * diffpct;
 | 
						|
  }
 | 
						|
 | 
						|
  return squareDistance;
 | 
						|
}
 | 
						|
 | 
						|
static PixelCalcValue
 | 
						|
CalcBackgroundCoord(const nsCSSValue& aCoord)
 | 
						|
{
 | 
						|
  NS_ASSERTION(aCoord.GetUnit() == eCSSUnit_Array,
 | 
						|
               "Expected array");
 | 
						|
 | 
						|
  nsCSSValue::Array* array = aCoord.GetArrayValue();
 | 
						|
  MOZ_ASSERT(array->Count() == 2 &&
 | 
						|
             array->Item(0).GetUnit() == eCSSUnit_Null &&
 | 
						|
             array->Item(1).GetUnit() != eCSSUnit_Null,
 | 
						|
             "Invalid position value");
 | 
						|
  return ExtractCalcValue(array->Item(1));
 | 
						|
}
 | 
						|
 | 
						|
static double
 | 
						|
CalcPositionCoordSquareDistance(const nsCSSValue& aPos1,
 | 
						|
                                const nsCSSValue& aPos2)
 | 
						|
{
 | 
						|
  PixelCalcValue calcVal1 = CalcBackgroundCoord(aPos1);
 | 
						|
  PixelCalcValue calcVal2 = CalcBackgroundCoord(aPos2);
 | 
						|
 | 
						|
  float difflen = calcVal2.mLength - calcVal1.mLength;
 | 
						|
  float diffpct = calcVal2.mPercent - calcVal1.mPercent;
 | 
						|
  return difflen * difflen + diffpct * diffpct;
 | 
						|
}
 | 
						|
 | 
						|
// Ensure that a float/double value isn't NaN by returning zero instead
 | 
						|
// (NaN doesn't have a sign) as a general restriction for floating point
 | 
						|
// values in RestrictValue.
 | 
						|
template<typename T>
 | 
						|
MOZ_ALWAYS_INLINE T
 | 
						|
EnsureNotNan(T aValue)
 | 
						|
{
 | 
						|
  return aValue;
 | 
						|
}
 | 
						|
template<>
 | 
						|
MOZ_ALWAYS_INLINE float
 | 
						|
EnsureNotNan(float aValue)
 | 
						|
{
 | 
						|
  // This would benefit from a MOZ_FLOAT_IS_NaN if we had one.
 | 
						|
  return MOZ_LIKELY(!mozilla::IsNaN(aValue)) ? aValue : 0;
 | 
						|
}
 | 
						|
template<>
 | 
						|
MOZ_ALWAYS_INLINE double
 | 
						|
EnsureNotNan(double aValue)
 | 
						|
{
 | 
						|
  return MOZ_LIKELY(!mozilla::IsNaN(aValue)) ? aValue : 0;
 | 
						|
}
 | 
						|
 | 
						|
template <typename T>
 | 
						|
T
 | 
						|
RestrictValue(uint32_t aRestrictions, T aValue)
 | 
						|
{
 | 
						|
  T result = EnsureNotNan(aValue);
 | 
						|
  switch (aRestrictions) {
 | 
						|
    case 0:
 | 
						|
      break;
 | 
						|
    case CSS_PROPERTY_VALUE_NONNEGATIVE:
 | 
						|
      if (result < 0) {
 | 
						|
        result = 0;
 | 
						|
      }
 | 
						|
      break;
 | 
						|
    case CSS_PROPERTY_VALUE_AT_LEAST_ONE:
 | 
						|
      if (result < 1) {
 | 
						|
        result = 1;
 | 
						|
      }
 | 
						|
      break;
 | 
						|
    default:
 | 
						|
      MOZ_ASSERT(false, "bad value restriction");
 | 
						|
      break;
 | 
						|
  }
 | 
						|
  return result;
 | 
						|
}
 | 
						|
 | 
						|
template <typename T>
 | 
						|
T
 | 
						|
RestrictValue(nsCSSPropertyID aProperty, T aValue)
 | 
						|
{
 | 
						|
  return RestrictValue(nsCSSProps::ValueRestrictions(aProperty), aValue);
 | 
						|
}
 | 
						|
 | 
						|
static void
 | 
						|
AddCSSValueAngle(double aCoeff1, const nsCSSValue &aValue1,
 | 
						|
                 double aCoeff2, const nsCSSValue &aValue2,
 | 
						|
                 nsCSSValue &aResult)
 | 
						|
{
 | 
						|
  if (aValue1.GetUnit() == aValue2.GetUnit()) {
 | 
						|
    // To avoid floating point error, if the units match, maintain the unit.
 | 
						|
    aResult.SetFloatValue(
 | 
						|
      EnsureNotNan(aCoeff1 * aValue1.GetFloatValue() +
 | 
						|
                   aCoeff2 * aValue2.GetFloatValue()),
 | 
						|
      aValue1.GetUnit());
 | 
						|
  } else {
 | 
						|
    aResult.SetFloatValue(
 | 
						|
      EnsureNotNan(aCoeff1 * aValue1.GetAngleValueInRadians() +
 | 
						|
                   aCoeff2 * aValue2.GetAngleValueInRadians()),
 | 
						|
      eCSSUnit_Radian);
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
static inline void
 | 
						|
AddCSSValuePercent(double aCoeff1, const nsCSSValue &aValue1,
 | 
						|
                   double aCoeff2, const nsCSSValue &aValue2,
 | 
						|
                   nsCSSValue &aResult, uint32_t aValueRestrictions = 0)
 | 
						|
{
 | 
						|
  MOZ_ASSERT(aValue1.GetUnit() == eCSSUnit_Percent, "unexpected unit");
 | 
						|
  MOZ_ASSERT(aValue2.GetUnit() == eCSSUnit_Percent, "unexpected unit");
 | 
						|
  aResult.SetPercentValue(RestrictValue(aValueRestrictions,
 | 
						|
                                        aCoeff1 * aValue1.GetPercentValue() +
 | 
						|
                                        aCoeff2 * aValue2.GetPercentValue()));
 | 
						|
}
 | 
						|
 | 
						|
// Add two canonical-form calc values (eUnit_Calc) to make another
 | 
						|
// canonical-form calc value.
 | 
						|
static void
 | 
						|
AddCSSValueCanonicalCalc(double aCoeff1, const nsCSSValue &aValue1,
 | 
						|
                         double aCoeff2, const nsCSSValue &aValue2,
 | 
						|
                         nsCSSValue &aResult)
 | 
						|
{
 | 
						|
  PixelCalcValue v1 = ExtractCalcValue(aValue1);
 | 
						|
  PixelCalcValue v2 = ExtractCalcValue(aValue2);
 | 
						|
  PixelCalcValue result;
 | 
						|
  result.mLength = aCoeff1 * v1.mLength + aCoeff2 * v2.mLength;
 | 
						|
  result.mPercent = aCoeff1 * v1.mPercent + aCoeff2 * v2.mPercent;
 | 
						|
  result.mHasPercent = v1.mHasPercent || v2.mHasPercent;
 | 
						|
  MOZ_ASSERT(result.mHasPercent || result.mPercent == 0.0f,
 | 
						|
             "can't have a nonzero percentage part without having percentages");
 | 
						|
  CalcValueToCSSValue(result, aResult);
 | 
						|
}
 | 
						|
 | 
						|
static inline void
 | 
						|
AddCSSValuePixel(double aCoeff1, const nsCSSValue &aValue1,
 | 
						|
                 double aCoeff2, const nsCSSValue &aValue2,
 | 
						|
                 nsCSSValue &aResult, uint32_t aValueRestrictions = 0)
 | 
						|
{
 | 
						|
  MOZ_ASSERT(aValue1.GetUnit() == eCSSUnit_Pixel, "unexpected unit");
 | 
						|
  MOZ_ASSERT(aValue2.GetUnit() == eCSSUnit_Pixel, "unexpected unit");
 | 
						|
  aResult.SetFloatValue(RestrictValue(aValueRestrictions,
 | 
						|
                                      aCoeff1 * aValue1.GetFloatValue() +
 | 
						|
                                      aCoeff2 * aValue2.GetFloatValue()),
 | 
						|
                        eCSSUnit_Pixel);
 | 
						|
}
 | 
						|
 | 
						|
static bool
 | 
						|
AddCSSValuePixelPercentCalc(const uint32_t aValueRestrictions,
 | 
						|
                            const nsCSSUnit aCommonUnit,
 | 
						|
                            double aCoeff1, const nsCSSValue &aValue1,
 | 
						|
                            double aCoeff2, const nsCSSValue &aValue2,
 | 
						|
                            nsCSSValue &aResult)
 | 
						|
{
 | 
						|
  switch (aCommonUnit) {
 | 
						|
    case eCSSUnit_Pixel:
 | 
						|
      AddCSSValuePixel(aCoeff1, aValue1,
 | 
						|
                       aCoeff2, aValue2,
 | 
						|
                       aResult, aValueRestrictions);
 | 
						|
      break;
 | 
						|
    case eCSSUnit_Percent:
 | 
						|
      AddCSSValuePercent(aCoeff1, aValue1,
 | 
						|
                         aCoeff2, aValue2,
 | 
						|
                         aResult, aValueRestrictions);
 | 
						|
      break;
 | 
						|
    case eCSSUnit_Calc:
 | 
						|
      AddCSSValueCanonicalCalc(aCoeff1, aValue1,
 | 
						|
                               aCoeff2, aValue2,
 | 
						|
                               aResult);
 | 
						|
      break;
 | 
						|
    default:
 | 
						|
      return false;
 | 
						|
  }
 | 
						|
 | 
						|
  return true;
 | 
						|
}
 | 
						|
 | 
						|
static void
 | 
						|
AddTransformTranslate(double aCoeff1, const nsCSSValue &aValue1,
 | 
						|
                      double aCoeff2, const nsCSSValue &aValue2,
 | 
						|
                      nsCSSValue &aResult)
 | 
						|
{
 | 
						|
  // Only three possible units: eCSSUnit_Pixel, eCSSUnit_Percent, or
 | 
						|
  // eCSSUnit_Calc.
 | 
						|
  MOZ_ASSERT(aValue1.GetUnit() == eCSSUnit_Percent ||
 | 
						|
             aValue1.GetUnit() == eCSSUnit_Pixel ||
 | 
						|
             aValue1.IsCalcUnit(),
 | 
						|
             "unexpected unit");
 | 
						|
  MOZ_ASSERT(aValue2.GetUnit() == eCSSUnit_Percent ||
 | 
						|
             aValue2.GetUnit() == eCSSUnit_Pixel ||
 | 
						|
             aValue2.IsCalcUnit(),
 | 
						|
             "unexpected unit");
 | 
						|
  AddCSSValuePixelPercentCalc(0,
 | 
						|
                              (aValue1.GetUnit() != aValue2.GetUnit() ||
 | 
						|
                               aValue1.IsCalcUnit())
 | 
						|
                                ? eCSSUnit_Calc
 | 
						|
                                : aValue1.GetUnit(),
 | 
						|
                              aCoeff1, aValue1,
 | 
						|
                              aCoeff2, aValue2,
 | 
						|
                              aResult);
 | 
						|
}
 | 
						|
 | 
						|
// Unclamped AddWeightedColors.
 | 
						|
static RGBAColorData
 | 
						|
AddWeightedColors(double aCoeff1, const RGBAColorData& aValue1,
 | 
						|
                  double aCoeff2, const RGBAColorData& aValue2)
 | 
						|
{
 | 
						|
  float factor1 = aValue1.mA * aCoeff1;
 | 
						|
  float factor2 = aValue2.mA * aCoeff2;
 | 
						|
  float resultA = factor1 + factor2;
 | 
						|
  if (resultA <= 0.0) {
 | 
						|
    return {0, 0, 0, 0};
 | 
						|
  }
 | 
						|
 | 
						|
  if (resultA > 1.0) {
 | 
						|
    resultA = 1.0;
 | 
						|
  }
 | 
						|
 | 
						|
  float resultFactor = 1.0f / resultA;
 | 
						|
  return RGBAColorData(
 | 
						|
    (aValue1.mR * factor1 + aValue2.mR * factor2) * resultFactor,
 | 
						|
    (aValue1.mG * factor1 + aValue2.mG * factor2) * resultFactor,
 | 
						|
    (aValue1.mB * factor1 + aValue2.mB * factor2) * resultFactor,
 | 
						|
    resultA);
 | 
						|
}
 | 
						|
 | 
						|
// CLASS METHODS
 | 
						|
// -------------
 | 
						|
 | 
						|
static RGBAColorData
 | 
						|
ExtractColor(const nsCSSValue& aValue)
 | 
						|
{
 | 
						|
  MOZ_ASSERT(aValue.IsNumericColorUnit(), "The unit should be color");
 | 
						|
  // PercentageRGBColor and PercentageRGBAColor component value might be
 | 
						|
  // greater than 1.0 in case when the color value is accumulated, so we
 | 
						|
  // can't use nsCSSValue::GetColorValue() here because that function
 | 
						|
  // clamps its values.
 | 
						|
  if (aValue.GetUnit() == eCSSUnit_PercentageRGBColor ||
 | 
						|
      aValue.GetUnit() == eCSSUnit_PercentageRGBAColor) {
 | 
						|
    nsCSSValueFloatColor* floatColor = aValue.GetFloatColorValue();
 | 
						|
    return {
 | 
						|
      floatColor->Comp1(),
 | 
						|
      floatColor->Comp2(),
 | 
						|
      floatColor->Comp3(),
 | 
						|
      floatColor->Alpha()
 | 
						|
    };
 | 
						|
  }
 | 
						|
  return RGBAColorData(aValue.GetColorValue());
 | 
						|
}
 | 
						|
 | 
						|
static RGBAColorData
 | 
						|
ExtractColor(const StyleAnimationValue& aValue)
 | 
						|
{
 | 
						|
  MOZ_ASSERT(aValue.GetUnit() == StyleAnimationValue::eUnit_Color);
 | 
						|
  nsCSSValue* value = aValue.GetCSSValueValue();
 | 
						|
  MOZ_ASSERT(value, "CSS value must be valid");
 | 
						|
  return ExtractColor(*value);
 | 
						|
}
 | 
						|
 | 
						|
static ComplexColorData
 | 
						|
ExtractComplexColor(const StyleAnimationValue& aValue)
 | 
						|
{
 | 
						|
  switch (aValue.GetUnit()) {
 | 
						|
    case StyleAnimationValue::eUnit_Color:
 | 
						|
      return ComplexColorData(ExtractColor(aValue), 0.0f);
 | 
						|
    case StyleAnimationValue::eUnit_CurrentColor:
 | 
						|
      return ComplexColorData({0, 0, 0, 0}, 1.0f);
 | 
						|
    case StyleAnimationValue::eUnit_ComplexColor:
 | 
						|
      return ComplexColorData(aValue.GetComplexColorData());
 | 
						|
    default:
 | 
						|
      MOZ_ASSERT_UNREACHABLE("Unknown unit");
 | 
						|
      return ComplexColorData({0, 0, 0, 0}, 0.0f);
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
StyleAnimationValue
 | 
						|
StyleAnimationValue::Add(nsCSSPropertyID aProperty,
 | 
						|
                         const StyleAnimationValue& aA,
 | 
						|
                         StyleAnimationValue&& aB)
 | 
						|
{
 | 
						|
  StyleAnimationValue result(Move(aB));
 | 
						|
 | 
						|
  Unit commonUnit =
 | 
						|
    GetCommonUnit(aProperty, result.GetUnit(), aA.GetUnit());
 | 
						|
  switch (commonUnit) {
 | 
						|
    case eUnit_Color: {
 | 
						|
      RGBAColorData color1 = ExtractColor(result);
 | 
						|
      RGBAColorData color2 = ExtractColor(aA);
 | 
						|
      result.mValue.mCSSValue->SetRGBAColorValue(
 | 
						|
        AddWeightedColors(1.0, color1, 1, color2));
 | 
						|
      break;
 | 
						|
    }
 | 
						|
    case eUnit_Filter:
 | 
						|
    case eUnit_Shadow: {
 | 
						|
      // If |aA| has no function list, don't concatinate anything, just return
 | 
						|
      // |aB| as the result.
 | 
						|
      if (!aA.GetCSSValueListValue() ||
 | 
						|
          aA.GetCSSValueListValue()->mValue.GetUnit() == eCSSUnit_None) {
 | 
						|
        break;
 | 
						|
      }
 | 
						|
      UniquePtr<nsCSSValueList> resultList(aA.GetCSSValueListValue()->Clone());
 | 
						|
 | 
						|
      // If |aB| has function list, concatinate it to |aA|, then return
 | 
						|
      // the concatinated list.
 | 
						|
      if (result.GetCSSValueListValue() &&
 | 
						|
          result.GetCSSValueListValue()->mValue.GetUnit() != eCSSUnit_None) {
 | 
						|
        nsCSSValueList* listA = resultList.get();
 | 
						|
        while (listA->mNext) {
 | 
						|
          listA = listA->mNext;
 | 
						|
        }
 | 
						|
 | 
						|
        listA->mNext = result.GetCSSValueListValue();
 | 
						|
      }
 | 
						|
      result.mValue.mCSSValueList = resultList.release();
 | 
						|
      break;
 | 
						|
    }
 | 
						|
    case eUnit_Transform: {
 | 
						|
      // If |aA| is 'transform:none', don't concatinate anything, just return
 | 
						|
      // |aB| as the result.
 | 
						|
      if (aA.GetCSSValueSharedListValue()->mHead->mValue.GetUnit() ==
 | 
						|
            eCSSUnit_None) {
 | 
						|
        break;
 | 
						|
      }
 | 
						|
 | 
						|
      UniquePtr<nsCSSValueList>
 | 
						|
        resultList(aA.GetCSSValueSharedListValue()->mHead->Clone());
 | 
						|
 | 
						|
      // If |aB| is not 'transform:none', concatinate it to |aA|, then return
 | 
						|
      // the concatinated list.
 | 
						|
      if (result.GetCSSValueSharedListValue()->mHead->mValue.GetUnit() !=
 | 
						|
            eCSSUnit_None) {
 | 
						|
        nsCSSValueList* listA = resultList.get();
 | 
						|
        while (listA->mNext) {
 | 
						|
          listA = listA->mNext;
 | 
						|
        }
 | 
						|
 | 
						|
        listA->mNext = result.GetCSSValueSharedListValue()->mHead->Clone();
 | 
						|
      }
 | 
						|
 | 
						|
      result.SetTransformValue(new nsCSSValueSharedList(resultList.release()));
 | 
						|
      break;
 | 
						|
    }
 | 
						|
    default:
 | 
						|
      Unused << AddWeighted(aProperty,
 | 
						|
                            1.0, result,
 | 
						|
                            1, aA,
 | 
						|
                            result);
 | 
						|
      break;
 | 
						|
  }
 | 
						|
 | 
						|
  return result;
 | 
						|
}
 | 
						|
 | 
						|
double
 | 
						|
StyleAnimationValue::ComputeColorDistance(const RGBAColorData& aStartColor,
 | 
						|
                                          const RGBAColorData& aEndColor)
 | 
						|
{
 | 
						|
  // http://www.w3.org/TR/smil-animation/#animateColorElement says
 | 
						|
  // that we should use Euclidean RGB cube distance.  However, we
 | 
						|
  // have to extend that to RGBA.  For now, we'll just use the
 | 
						|
  // Euclidean distance in the (part of the) 4-cube of premultiplied
 | 
						|
  // colors.
 | 
						|
  double startA = aStartColor.mA;
 | 
						|
  double startR = aStartColor.mR * startA;
 | 
						|
  double startG = aStartColor.mG * startA;
 | 
						|
  double startB = aStartColor.mB * startA;
 | 
						|
  double endA = aEndColor.mA;
 | 
						|
  double endR = aEndColor.mR * endA;
 | 
						|
  double endG = aEndColor.mG * endA;
 | 
						|
  double endB = aEndColor.mB * endA;
 | 
						|
 | 
						|
  double diffA = startA - endA;
 | 
						|
  double diffR = startR - endR;
 | 
						|
  double diffG = startG - endG;
 | 
						|
  double diffB = startB - endB;
 | 
						|
  return sqrt(diffA * diffA + diffR * diffR + diffG * diffG + diffB * diffB);
 | 
						|
}
 | 
						|
 | 
						|
enum class ColorAdditionType {
 | 
						|
  Clamped, // Clamp each color channel after adding.
 | 
						|
  Unclamped // Do not clamp color channels after adding.
 | 
						|
};
 | 
						|
 | 
						|
static UniquePtr<nsCSSValueList>
 | 
						|
AddWeightedFilterFunction(double aCoeff1, const nsCSSValueList* aList1,
 | 
						|
                          double aCoeff2, const nsCSSValueList* aList2,
 | 
						|
                          ColorAdditionType aColorAdditionType);
 | 
						|
 | 
						|
static inline float
 | 
						|
GetNumberOrPercent(const nsCSSValue &aValue);
 | 
						|
 | 
						|
static bool
 | 
						|
ComputeSingleShadowSquareDistance(const nsCSSValueList* aShadow1,
 | 
						|
                                  const nsCSSValueList* aShadow2,
 | 
						|
                                  double& aSquareDistance,
 | 
						|
                                  nsCSSPropertyID aProperty)
 | 
						|
{
 | 
						|
  MOZ_ASSERT(aShadow1->mValue.GetUnit() == eCSSUnit_Array, "wrong unit");
 | 
						|
  MOZ_ASSERT(aShadow2->mValue.GetUnit() == eCSSUnit_Array, "wrong unit");
 | 
						|
  const nsCSSValue::Array* array1 = aShadow1->mValue.GetArrayValue();
 | 
						|
  const nsCSSValue::Array* array2 = aShadow2->mValue.GetArrayValue();
 | 
						|
 | 
						|
  double squareDistance = 0.0;
 | 
						|
  // X, Y, Radius, Spread
 | 
						|
  for (size_t i = 0; i < 4; ++i) {
 | 
						|
    // Spread value is not necessary on text-shadow,
 | 
						|
    // so we skip the computing distance.
 | 
						|
    if (i == 3 && (aProperty != eCSSProperty_box_shadow)) {
 | 
						|
      continue;
 | 
						|
    }
 | 
						|
    MOZ_ASSERT(array1->Item(i).GetUnit() == eCSSUnit_Pixel,
 | 
						|
               "unexpected unit");
 | 
						|
    MOZ_ASSERT(array2->Item(i).GetUnit() == eCSSUnit_Pixel,
 | 
						|
               "unexpected unit");
 | 
						|
    double diff = array1->Item(i).GetFloatValue() -
 | 
						|
                  array2->Item(i).GetFloatValue();
 | 
						|
    squareDistance += diff * diff;
 | 
						|
  }
 | 
						|
 | 
						|
  // Color, Inset
 | 
						|
  const nsCSSValue& color1 = array1->Item(4);
 | 
						|
  const nsCSSValue& color2 = array2->Item(4);
 | 
						|
  const nsCSSValue& inset1 = array1->Item(5);
 | 
						|
  const nsCSSValue& inset2 = array2->Item(5);
 | 
						|
  if ((color1.GetUnit() != color2.GetUnit() &&
 | 
						|
        (!color1.IsNumericColorUnit() ||
 | 
						|
         !color2.IsNumericColorUnit())) ||
 | 
						|
      inset1 != inset2) {
 | 
						|
    // According to AddWeightedShadowItems, we don't know how to animate
 | 
						|
    // between color and no-color, or between inset and not-inset,
 | 
						|
    // so we cannot compute the distance either.
 | 
						|
    // Note: There are only two possible states of the inset value:
 | 
						|
    //  (1) GetUnit() == eCSSUnit_Null
 | 
						|
    //  (2) GetUnit() == eCSSUnit_Enumerated &&
 | 
						|
    //      GetIntValue() == NS_STYLE_BOX_SHADOW_INSET
 | 
						|
    return false;
 | 
						|
  }
 | 
						|
 | 
						|
  // We compute the distance of colors only if both are numeric color units.
 | 
						|
  if (color1.GetUnit() != eCSSUnit_Null) {
 | 
						|
    double colorDistance =
 | 
						|
      StyleAnimationValue::ComputeColorDistance(ExtractColor(color1),
 | 
						|
                                                ExtractColor(color2));
 | 
						|
    squareDistance += colorDistance * colorDistance;
 | 
						|
  }
 | 
						|
 | 
						|
  aSquareDistance = squareDistance;
 | 
						|
  return true;
 | 
						|
}
 | 
						|
 | 
						|
// Return false if we cannot compute the distance between these filter
 | 
						|
// functions.
 | 
						|
static bool
 | 
						|
ComputeFilterSquareDistance(const nsCSSValueList* aList1,
 | 
						|
                            const nsCSSValueList* aList2,
 | 
						|
                            double& aSquareDistance)
 | 
						|
{
 | 
						|
  MOZ_ASSERT(aList1, "expected filter list");
 | 
						|
  MOZ_ASSERT(aList2, "expected filter list");
 | 
						|
  MOZ_ASSERT(aList1->mValue.GetUnit() == eCSSUnit_Function,
 | 
						|
             "expected function");
 | 
						|
  MOZ_ASSERT(aList2->mValue.GetUnit() == eCSSUnit_Function,
 | 
						|
             "expected function");
 | 
						|
 | 
						|
  RefPtr<nsCSSValue::Array> a1 = aList1->mValue.GetArrayValue();
 | 
						|
  RefPtr<nsCSSValue::Array> a2 = aList2->mValue.GetArrayValue();
 | 
						|
  nsCSSKeyword filterFunction = a1->Item(0).GetKeywordValue();
 | 
						|
  if (filterFunction != a2->Item(0).GetKeywordValue()) {
 | 
						|
    return false;
 | 
						|
  }
 | 
						|
 | 
						|
  const nsCSSValue& func1 = a1->Item(1);
 | 
						|
  const nsCSSValue& func2 = a2->Item(1);
 | 
						|
  switch (filterFunction) {
 | 
						|
    case eCSSKeyword_blur: {
 | 
						|
      nsCSSValue diff;
 | 
						|
      // In AddWeightedFilterFunctionImpl, blur may have different units, so we
 | 
						|
      // use eCSSUnit_Calc for that case.
 | 
						|
      if (!AddCSSValuePixelPercentCalc(0,
 | 
						|
                                       func1.GetUnit() == func2.GetUnit()
 | 
						|
                                         ? func1.GetUnit()
 | 
						|
                                         : eCSSUnit_Calc,
 | 
						|
                                       1.0, func2,
 | 
						|
                                       -1.0, func1,
 | 
						|
                                       diff)) {
 | 
						|
        return false;
 | 
						|
      }
 | 
						|
      // ExtractCalcValue makes sure mHasPercent and mPercent are correct.
 | 
						|
      PixelCalcValue v = ExtractCalcValue(diff);
 | 
						|
      aSquareDistance = v.mLength * v.mLength + v.mPercent * v.mPercent;
 | 
						|
      break;
 | 
						|
    }
 | 
						|
    case eCSSKeyword_grayscale:
 | 
						|
    case eCSSKeyword_invert:
 | 
						|
    case eCSSKeyword_sepia:
 | 
						|
    case eCSSKeyword_brightness:
 | 
						|
    case eCSSKeyword_contrast:
 | 
						|
    case eCSSKeyword_opacity:
 | 
						|
    case eCSSKeyword_saturate: {
 | 
						|
      double diff =
 | 
						|
        EnsureNotNan(GetNumberOrPercent(func2) - GetNumberOrPercent(func1));
 | 
						|
      aSquareDistance = diff * diff;
 | 
						|
      break;
 | 
						|
    }
 | 
						|
    case eCSSKeyword_hue_rotate: {
 | 
						|
      nsCSSValue v;
 | 
						|
      AddCSSValueAngle(1.0, func2, -1.0, func1, v);
 | 
						|
      double diff = v.GetAngleValueInRadians();
 | 
						|
      aSquareDistance = diff * diff;
 | 
						|
      break;
 | 
						|
    }
 | 
						|
    case eCSSKeyword_drop_shadow: {
 | 
						|
      MOZ_ASSERT(!func1.GetListValue()->mNext && !func2.GetListValue()->mNext,
 | 
						|
                 "drop-shadow filter func doesn't support lists");
 | 
						|
      if (!ComputeSingleShadowSquareDistance(func1.GetListValue(),
 | 
						|
                                             func2.GetListValue(),
 | 
						|
                                             aSquareDistance,
 | 
						|
                                             eCSSProperty_filter)) {
 | 
						|
        return false;
 | 
						|
      }
 | 
						|
      break;
 | 
						|
    }
 | 
						|
    default:
 | 
						|
      MOZ_ASSERT_UNREACHABLE("unknown filter function");
 | 
						|
      return false;
 | 
						|
  }
 | 
						|
  return true;
 | 
						|
}
 | 
						|
 | 
						|
static bool
 | 
						|
ComputeFilterListDistance(const nsCSSValueList* aList1,
 | 
						|
                          const nsCSSValueList* aList2,
 | 
						|
                          double& aDistance)
 | 
						|
{
 | 
						|
  double squareDistance = 0.0;
 | 
						|
  while (aList1 || aList2) {
 | 
						|
    // Return false if one of the lists is neither none nor a function.
 | 
						|
    if ((aList1 && aList1->mValue.GetUnit() != eCSSUnit_Function) ||
 | 
						|
        (aList2 && aList2->mValue.GetUnit() != eCSSUnit_Function)) {
 | 
						|
      return false;
 | 
						|
    }
 | 
						|
 | 
						|
    MOZ_ASSERT(aList1 || aList2, "one function list item must not be null");
 | 
						|
 | 
						|
    double currentSquareDistance = 0.0;
 | 
						|
    if (!aList1) {
 | 
						|
      // This is a tricky to get an equivalent none filter function by 0.0
 | 
						|
      // coefficients. Although we don't guarantee this function can get the
 | 
						|
      // correct default values, it can reuse the code from the interpolation.
 | 
						|
      UniquePtr<nsCSSValueList> none =
 | 
						|
        AddWeightedFilterFunction(0, aList2, 0, aList2,
 | 
						|
                                  ColorAdditionType::Clamped);
 | 
						|
      if (!ComputeFilterSquareDistance(none.get(), aList2,
 | 
						|
                                       currentSquareDistance)) {
 | 
						|
        return false;
 | 
						|
      }
 | 
						|
      aList2 = aList2->mNext;
 | 
						|
    } else if (!aList2) {
 | 
						|
      UniquePtr<nsCSSValueList> none =
 | 
						|
        AddWeightedFilterFunction(0, aList1, 0, aList1,
 | 
						|
                                  ColorAdditionType::Clamped);
 | 
						|
      if (!ComputeFilterSquareDistance(aList1, none.get(),
 | 
						|
                                       currentSquareDistance)) {
 | 
						|
        return false;
 | 
						|
      }
 | 
						|
      aList1 = aList1->mNext;
 | 
						|
    } else {
 | 
						|
      if (!ComputeFilterSquareDistance(aList1, aList2,
 | 
						|
                                       currentSquareDistance)) {
 | 
						|
        return false;
 | 
						|
      }
 | 
						|
      aList1 = aList1->mNext;
 | 
						|
      aList2 = aList2->mNext;
 | 
						|
    }
 | 
						|
    squareDistance += currentSquareDistance;
 | 
						|
  }
 | 
						|
  aDistance = sqrt(squareDistance);
 | 
						|
  return true;
 | 
						|
}
 | 
						|
 | 
						|
enum class Restrictions {
 | 
						|
  Enable,
 | 
						|
  Disable
 | 
						|
};
 | 
						|
 | 
						|
static already_AddRefed<nsCSSValue::Array>
 | 
						|
AddShapeFunction(nsCSSPropertyID aProperty,
 | 
						|
                 double aCoeff1, const nsCSSValue::Array* aArray1,
 | 
						|
                 double aCoeff2, const nsCSSValue::Array* aArray2,
 | 
						|
                 Restrictions aRestriction = Restrictions::Enable);
 | 
						|
 | 
						|
static bool
 | 
						|
ComputeShapeDistance(nsCSSPropertyID aProperty,
 | 
						|
                     const nsCSSValue::Array* aArray1,
 | 
						|
                     const nsCSSValue::Array* aArray2,
 | 
						|
                     double& aDistance)
 | 
						|
{
 | 
						|
  // Use AddShapeFunction to get the difference between two shape functions.
 | 
						|
  RefPtr<nsCSSValue::Array> diffShape =
 | 
						|
    AddShapeFunction(aProperty, 1.0, aArray2, -1.0, aArray1,
 | 
						|
                     Restrictions::Disable);
 | 
						|
  if (!diffShape) {
 | 
						|
    return false;
 | 
						|
  }
 | 
						|
 | 
						|
  // A helper function to convert a calc() diff value into a double distance.
 | 
						|
  auto pixelCalcDistance = [](const PixelCalcValue& aValue) {
 | 
						|
    MOZ_ASSERT(aValue.mHasPercent || aValue.mPercent == 0.0f,
 | 
						|
             "can't have a nonzero percentage part without having percentages");
 | 
						|
    return aValue.mLength * aValue.mLength + aValue.mPercent * aValue.mPercent;
 | 
						|
  };
 | 
						|
 | 
						|
  double squareDistance = 0.0;
 | 
						|
  const nsCSSValue::Array* func = diffShape->Item(0).GetArrayValue();
 | 
						|
  nsCSSKeyword shapeFuncName = func->Item(0).GetKeywordValue();
 | 
						|
  switch (shapeFuncName) {
 | 
						|
    case eCSSKeyword_ellipse:
 | 
						|
    case eCSSKeyword_circle: {
 | 
						|
      // Skip the first element which is the function keyword.
 | 
						|
      // Also, skip the last element which is an array for <position>
 | 
						|
      const size_t len = func->Count();
 | 
						|
      for (size_t i = 1; i < len - 1; ++i) {
 | 
						|
        squareDistance += pixelCalcDistance(ExtractCalcValue(func->Item(i)));
 | 
						|
      }
 | 
						|
      // Only iterate over elements 1 and 3. The <position> is 'uncomputed' to
 | 
						|
      // only those elements.  See also the comment in SetPositionValue.
 | 
						|
      for (size_t i = 1; i < 4; i += 2) {
 | 
						|
        const nsCSSValue& value = func->Item(len - 1).GetArrayValue()->Item(i);
 | 
						|
        squareDistance += pixelCalcDistance(ExtractCalcValue(value));
 | 
						|
      }
 | 
						|
      break;
 | 
						|
    }
 | 
						|
    case eCSSKeyword_polygon: {
 | 
						|
      // Don't care about the first element which is the function keyword, and
 | 
						|
      // the second element which is the fill rule.
 | 
						|
      const nsCSSValuePairList* list = func->Item(2).GetPairListValue();
 | 
						|
      do {
 | 
						|
        squareDistance += pixelCalcDistance(ExtractCalcValue(list->mXValue)) +
 | 
						|
                          pixelCalcDistance(ExtractCalcValue(list->mYValue));
 | 
						|
        list = list->mNext;
 | 
						|
      } while (list);
 | 
						|
      break;
 | 
						|
    }
 | 
						|
    case eCSSKeyword_inset: {
 | 
						|
      // Items 1-4 are respectively the top, right, bottom and left offsets
 | 
						|
      // from the reference box.
 | 
						|
      for (size_t i = 1; i <= 4; ++i) {
 | 
						|
        const nsCSSValue& value = func->Item(i);
 | 
						|
        squareDistance += pixelCalcDistance(ExtractCalcValue(value));
 | 
						|
      }
 | 
						|
      // Item 5 contains the radii of the rounded corners for the inset
 | 
						|
      // rectangle.
 | 
						|
      const nsCSSValue::Array* array = func->Item(5).GetArrayValue();
 | 
						|
      const size_t len = array->Count();
 | 
						|
      for (size_t i = 0; i < len; ++i) {
 | 
						|
        const nsCSSValuePair& pair = array->Item(i).GetPairValue();
 | 
						|
        squareDistance += pixelCalcDistance(ExtractCalcValue(pair.mXValue)) +
 | 
						|
                          pixelCalcDistance(ExtractCalcValue(pair.mYValue));
 | 
						|
      }
 | 
						|
      break;
 | 
						|
    }
 | 
						|
    default:
 | 
						|
      MOZ_ASSERT_UNREACHABLE("Unknown shape type");
 | 
						|
  }
 | 
						|
  aDistance = sqrt(squareDistance);
 | 
						|
  return true;
 | 
						|
}
 | 
						|
 | 
						|
static nsCSSValueList*
 | 
						|
AddTransformLists(double aCoeff1, const nsCSSValueList* aList1,
 | 
						|
                  double aCoeff2, const nsCSSValueList* aList2,
 | 
						|
                  nsCSSKeyword aOperatorType = eCSSKeyword_interpolatematrix);
 | 
						|
 | 
						|
static double
 | 
						|
ComputeTransform2DMatrixDistance(const Matrix& aMatrix1,
 | 
						|
                                 const Matrix& aMatrix2)
 | 
						|
{
 | 
						|
  Point3D scale1(1, 1, 1);
 | 
						|
  Point3D translate1;
 | 
						|
  gfxQuaternion rotate1;
 | 
						|
  nsStyleTransformMatrix::ShearArray shear1{0.0f, 0.0f, 0.0f};
 | 
						|
  Decompose2DMatrix(aMatrix1, scale1, shear1, rotate1, translate1);
 | 
						|
 | 
						|
  Point3D scale2(1, 1, 1);
 | 
						|
  Point3D translate2;
 | 
						|
  gfxQuaternion rotate2;
 | 
						|
  nsStyleTransformMatrix::ShearArray shear2{0.0f, 0.0f, 0.0f};
 | 
						|
  Decompose2DMatrix(aMatrix2, scale2, shear2, rotate2, translate2);
 | 
						|
 | 
						|
  // Note:
 | 
						|
  // 1. Shear factor is the tangent value of shear angle, so we need to
 | 
						|
  //    call atan() to get the angle. For 2D transform, we only have XYSHEAR.
 | 
						|
  // 2. The quaternion vector of the decomposed 2d matrix is got by
 | 
						|
  //    "gfxQuaternion(0, 0, sin(rotate/2), cos(rotate/2))"
 | 
						|
  //                         ^^^^^^^^^^^^^  ^^^^^^^^^^^^^
 | 
						|
  //                               z              w
 | 
						|
  //    Therefore, we can get the rotate angle by 2 * atan2f(z, w).
 | 
						|
  //
 | 
						|
  //    However, we can also get the rotate angle by the inner product of
 | 
						|
  //    two quaternion vectors, just as what we do for eCSSKeyword_rotate3d.
 | 
						|
  //    e.g.
 | 
						|
  //      rotate3d(0, 0, 1, 60deg)  =>  rotate3d(0, 0, 1, 120deg);
 | 
						|
  //      quaternion 1: (0, 0, sin(30deg), cos(30deg)) = (0, 0, 1/2, sqrt(3)/2)
 | 
						|
  //      quaternion 2: (0, 0, sin(60deg), cos(60deg)) = (0, 0, sqrt(3)/2, 1/2)
 | 
						|
  //      inner product:  sqrt(3)/4 + sqrt(3)/4 = sqrt(3)/2
 | 
						|
  //      Finally, the rotate angle: 2 * acos(sqrt(3)/2) = 60deg
 | 
						|
  //
 | 
						|
  //    I think doing atan() may be faster than doing inner product together
 | 
						|
  //    with acos(), so let's adopt atan2f().
 | 
						|
  const Point3D diffTranslate = translate2 - translate1;
 | 
						|
  const Point3D diffScale = scale2 - scale1;
 | 
						|
  const double diffShear = atan(shear2[ShearType::XYSHEAR]) -
 | 
						|
                           atan(shear1[ShearType::XYSHEAR]);
 | 
						|
  const double diffRotate = 2.0 * (atan2f(rotate2.z, rotate2.w) -
 | 
						|
                                   atan2f(rotate1.z, rotate1.w));
 | 
						|
  // Returns the sum of squares because we will take a square root in
 | 
						|
  // ComputeTransformListDistance.
 | 
						|
  return diffTranslate.DotProduct(diffTranslate) +
 | 
						|
         diffScale.DotProduct(diffScale) +
 | 
						|
         diffRotate * diffRotate +
 | 
						|
         diffShear * diffShear;
 | 
						|
}
 | 
						|
 | 
						|
static double
 | 
						|
ComputeTransform3DMatrixDistance(const Matrix4x4& aMatrix1,
 | 
						|
                                 const Matrix4x4& aMatrix2)
 | 
						|
{
 | 
						|
  Point3D scale1(1, 1, 1);
 | 
						|
  Point3D translate1;
 | 
						|
  Point4D perspective1(0, 0, 0, 1);
 | 
						|
  gfxQuaternion rotate1;
 | 
						|
  nsStyleTransformMatrix::ShearArray shear1{0.0f, 0.0f, 0.0f};
 | 
						|
  Decompose3DMatrix(aMatrix1, scale1, shear1, rotate1, translate1,
 | 
						|
                    perspective1);
 | 
						|
 | 
						|
  Point3D scale2(1, 1, 1);
 | 
						|
  Point3D translate2;
 | 
						|
  Point4D perspective2(0, 0, 0, 1);
 | 
						|
  gfxQuaternion rotate2;
 | 
						|
  nsStyleTransformMatrix::ShearArray shear2{0.0f, 0.0f, 0.0f};
 | 
						|
  Decompose3DMatrix(aMatrix2, scale2, shear2, rotate2, translate2,
 | 
						|
                    perspective2);
 | 
						|
 | 
						|
  // Note:
 | 
						|
  // 1. Shear factor is the tangent value of shear angle, so we need to
 | 
						|
  //    call atan() to get the angle.
 | 
						|
  // 2. We use the same way to get the rotate angle of two quaternion vectors as
 | 
						|
  //    what we do for rotate3d.
 | 
						|
  const Point3D diffTranslate = translate2 - translate1;
 | 
						|
  const Point3D diffScale = scale2 - scale1;
 | 
						|
  const Point3D diffShear(atan(shear2[ShearType::XYSHEAR]) -
 | 
						|
                            atan(shear1[ShearType::XYSHEAR]),
 | 
						|
                          atan(shear2[ShearType::XZSHEAR]) -
 | 
						|
                            atan(shear1[ShearType::XZSHEAR]),
 | 
						|
                          atan(shear2[ShearType::YZSHEAR]) -
 | 
						|
                            atan(shear1[ShearType::YZSHEAR]));
 | 
						|
  const Point4D diffPerspective = perspective2 - perspective1;
 | 
						|
  const double dot = clamped(rotate1.DotProduct(rotate2), -1.0, 1.0);
 | 
						|
  const double diffRotate = 2.0 * acos(dot);
 | 
						|
  // Returns the sum of squares because we will take a square root in
 | 
						|
  // ComputeTransformListDistance.
 | 
						|
  return diffTranslate.DotProduct(diffTranslate) +
 | 
						|
         diffScale.DotProduct(diffScale) +
 | 
						|
         diffPerspective.DotProduct(diffPerspective) +
 | 
						|
         diffShear.DotProduct(diffShear) +
 | 
						|
         diffRotate * diffRotate;
 | 
						|
}
 | 
						|
 | 
						|
static double
 | 
						|
ComputeTransformDistance(nsCSSValue::Array* aArray1,
 | 
						|
                         nsCSSValue::Array* aArray2)
 | 
						|
{
 | 
						|
  MOZ_ASSERT(aArray1, "aArray1 should be non-null.");
 | 
						|
  MOZ_ASSERT(aArray2, "aArray2 should be non-null.");
 | 
						|
 | 
						|
  // Normalize translate and scale functions to equivalent "translate3d" and
 | 
						|
  // "scale3d" functions.
 | 
						|
  RefPtr<nsCSSValue::Array> a1 = ToPrimitive(aArray1),
 | 
						|
                            a2 = ToPrimitive(aArray2);
 | 
						|
  nsCSSKeyword tfunc = nsStyleTransformMatrix::TransformFunctionOf(a1);
 | 
						|
  MOZ_ASSERT(nsStyleTransformMatrix::TransformFunctionOf(a2) == tfunc);
 | 
						|
 | 
						|
  double distance = 0.0;
 | 
						|
  switch (tfunc) {
 | 
						|
    case eCSSKeyword_translate3d: {
 | 
						|
      MOZ_ASSERT(a1->Count() == 4, "unexpected count");
 | 
						|
      MOZ_ASSERT(a2->Count() == 4, "unexpected count");
 | 
						|
 | 
						|
      nsCSSValue x, y, z;
 | 
						|
      AddTransformTranslate(1.0, a2->Item(1), -1.0, a1->Item(1), x);
 | 
						|
      AddTransformTranslate(1.0, a2->Item(2), -1.0, a1->Item(2), y);
 | 
						|
      AddTransformTranslate(1.0, a2->Item(3), -1.0, a1->Item(3), z);
 | 
						|
      // Drop percent part because we only compute distance by computed values.
 | 
						|
      double c1 = ExtractCalcValue(x).mLength;
 | 
						|
      double c2 = ExtractCalcValue(y).mLength;
 | 
						|
      double c3 = z.GetFloatValue();
 | 
						|
      distance = c1 * c1 + c2 * c2 + c3 * c3;
 | 
						|
      break;
 | 
						|
    }
 | 
						|
    case eCSSKeyword_scale3d: {
 | 
						|
      MOZ_ASSERT(a1->Count() == 4, "unexpected count");
 | 
						|
      MOZ_ASSERT(a2->Count() == 4, "unexpected count");
 | 
						|
 | 
						|
      auto ComputeScaleDiff = [](const nsCSSValue& aValue1,
 | 
						|
                                 const nsCSSValue& aValue2) {
 | 
						|
        float v1 = aValue1.GetFloatValue();
 | 
						|
        float v2 = aValue2.GetFloatValue();
 | 
						|
        return EnsureNotNan(v2 - v1);
 | 
						|
      };
 | 
						|
 | 
						|
      double c1 = ComputeScaleDiff(a1->Item(1), a2->Item(1));
 | 
						|
      double c2 = ComputeScaleDiff(a1->Item(2), a2->Item(2));
 | 
						|
      double c3 = ComputeScaleDiff(a1->Item(3), a2->Item(3));
 | 
						|
      distance = c1 * c1 + c2 * c2 + c3 * c3;
 | 
						|
      break;
 | 
						|
    }
 | 
						|
    case eCSSKeyword_skew: {
 | 
						|
      MOZ_ASSERT(a1->Count() == 2 || a1->Count() == 3, "unexpected count");
 | 
						|
      MOZ_ASSERT(a2->Count() == 2 || a2->Count() == 3, "unexpected count");
 | 
						|
 | 
						|
      const nsCSSValue zero(0.0f, eCSSUnit_Radian);
 | 
						|
      nsCSSValue x, y;
 | 
						|
      AddCSSValueAngle(1.0, a2->Item(1), -1.0, a1->Item(1), x);
 | 
						|
      AddCSSValueAngle(1.0, a2->Count() == 3 ? a2->Item(2) : zero,
 | 
						|
                      -1.0, a1->Count() == 3 ? a1->Item(2) : zero,
 | 
						|
                       y);
 | 
						|
      distance = x.GetAngleValueInRadians() * x.GetAngleValueInRadians() +
 | 
						|
                 y.GetAngleValueInRadians() * y.GetAngleValueInRadians();
 | 
						|
      break;
 | 
						|
    }
 | 
						|
    case eCSSKeyword_skewx:
 | 
						|
    case eCSSKeyword_skewy:
 | 
						|
    case eCSSKeyword_rotate:
 | 
						|
    case eCSSKeyword_rotatex:
 | 
						|
    case eCSSKeyword_rotatey:
 | 
						|
    case eCSSKeyword_rotatez: {
 | 
						|
      MOZ_ASSERT(a1->Count() == 2, "unexpected count");
 | 
						|
      MOZ_ASSERT(a2->Count() == 2, "unexpected count");
 | 
						|
 | 
						|
      nsCSSValue angle;
 | 
						|
      AddCSSValueAngle(1.0, a2->Item(1), -1.0, a1->Item(1), angle);
 | 
						|
      distance = angle.GetAngleValueInRadians() *
 | 
						|
                 angle.GetAngleValueInRadians();
 | 
						|
      break;
 | 
						|
    }
 | 
						|
    case eCSSKeyword_rotate3d: {
 | 
						|
      MOZ_ASSERT(a1->Count() == 5, "unexpected count");
 | 
						|
      MOZ_ASSERT(a2->Count() == 5, "unexpected count");
 | 
						|
 | 
						|
      Point3D vector1(a1->Item(1).GetFloatValue(),
 | 
						|
                      a1->Item(2).GetFloatValue(),
 | 
						|
                      a1->Item(3).GetFloatValue());
 | 
						|
      double angle1 = a1->Item(4).GetAngleValueInRadians();
 | 
						|
 | 
						|
      Point3D vector2(a2->Item(1).GetFloatValue(),
 | 
						|
                      a2->Item(2).GetFloatValue(),
 | 
						|
                      a2->Item(3).GetFloatValue());
 | 
						|
      double angle2 = a2->Item(4).GetAngleValueInRadians();
 | 
						|
 | 
						|
      auto normalizeVector = [](Point3D& vector, double& angle) {
 | 
						|
        if (vector.Length() > 0) {
 | 
						|
          vector.Normalize();
 | 
						|
        } else {
 | 
						|
          vector.x = 0.0;
 | 
						|
          vector.y = 0.0;
 | 
						|
          vector.z = 1.0;
 | 
						|
          angle = 0.0;
 | 
						|
        }
 | 
						|
      };
 | 
						|
      normalizeVector(vector1, angle1);
 | 
						|
      normalizeVector(vector2, angle2);
 | 
						|
 | 
						|
      if (vector1 == vector2) {
 | 
						|
        // Handle rotate3d with matched (normalized) vectors.
 | 
						|
        distance = EnsureNotNan(angle2 - angle1);
 | 
						|
      } else {
 | 
						|
        // Use quaternion vectors to get the angle difference. Both q1 and q2
 | 
						|
        // are unit vectors, so we can get their angle difference by
 | 
						|
        // cos(theta/2) = (q1 dot q2) / (|q1| * |q2|) = q1 dot q2.
 | 
						|
        gfxQuaternion q1(vector1, angle1);
 | 
						|
        gfxQuaternion q2(vector2, angle2);
 | 
						|
        distance =
 | 
						|
          EnsureNotNan(2.0 * acos(clamped(q1.DotProduct(q2), -1.0, 1.0)));
 | 
						|
      }
 | 
						|
      distance = distance * distance;
 | 
						|
      break;
 | 
						|
    }
 | 
						|
    case eCSSKeyword_perspective: {
 | 
						|
      MOZ_ASSERT(a1->Count() == 2, "unexpected count");
 | 
						|
      MOZ_ASSERT(a2->Count() == 2, "unexpected count");
 | 
						|
 | 
						|
      // We convert a perspective function into an equivalent matrix3d, and
 | 
						|
      // then do matrix decomposition to get the distance.
 | 
						|
      // Why don't we just subtract one perspective depth from the other?
 | 
						|
      // I think it's better to follow the logic of our interpolation,
 | 
						|
      // which does linear interpolation between two decomposed perspective
 | 
						|
      // vectors.
 | 
						|
      // e.g.
 | 
						|
      // Do interpolation between perspective(100px) and perspective(1000px).
 | 
						|
      //   1) Convert them into matrix3d, and then do matrix decomposition:
 | 
						|
      //      perspective vector 1: perspective(0, 0, -1/100, 1);
 | 
						|
      //      perspective vector 2: perspective(0, 0, -1/1000, 1);
 | 
						|
      //   2) Do linear interpolation between these two vectors.
 | 
						|
      // Therefore, we use the same rule to get the distance as what we do for
 | 
						|
      // matrix3d.
 | 
						|
 | 
						|
      using nsStyleTransformMatrix::ApplyPerspectiveToMatrix;
 | 
						|
      Matrix4x4 m1;
 | 
						|
      ApplyPerspectiveToMatrix(m1, a1->Item(1).GetFloatValue());
 | 
						|
      Matrix4x4 m2;
 | 
						|
      ApplyPerspectiveToMatrix(m2, a2->Item(1).GetFloatValue());
 | 
						|
 | 
						|
      distance = ComputeTransform3DMatrixDistance(m1, m2);
 | 
						|
      break;
 | 
						|
    }
 | 
						|
    case eCSSKeyword_matrix: {
 | 
						|
      MOZ_ASSERT(a1->Count() == 7, "unexpected count");
 | 
						|
      MOZ_ASSERT(a2->Count() == 7, "unexpected count");
 | 
						|
 | 
						|
      distance = ComputeTransform2DMatrixDistance(
 | 
						|
        nsStyleTransformMatrix::CSSValueArrayTo2DMatrix(a1),
 | 
						|
        nsStyleTransformMatrix::CSSValueArrayTo2DMatrix(a2));
 | 
						|
      break;
 | 
						|
    }
 | 
						|
    case eCSSKeyword_matrix3d: {
 | 
						|
      MOZ_ASSERT(a1->Count() == 17, "unexpected count");
 | 
						|
      MOZ_ASSERT(a2->Count() == 17, "unexpected count");
 | 
						|
 | 
						|
      distance = ComputeTransform3DMatrixDistance(
 | 
						|
        nsStyleTransformMatrix::CSSValueArrayTo3DMatrix(a1),
 | 
						|
        nsStyleTransformMatrix::CSSValueArrayTo3DMatrix(a2));
 | 
						|
      break;
 | 
						|
    }
 | 
						|
    case eCSSKeyword_interpolatematrix:
 | 
						|
    case eCSSKeyword_accumulatematrix:
 | 
						|
    default:
 | 
						|
      MOZ_ASSERT_UNREACHABLE("Unsupported transform function");
 | 
						|
      break;
 | 
						|
  }
 | 
						|
  return distance;
 | 
						|
}
 | 
						|
 | 
						|
static double
 | 
						|
ComputeTransformListDistance(const nsCSSValueList* aList1,
 | 
						|
                             const nsCSSValueList* aList2)
 | 
						|
{
 | 
						|
  MOZ_ASSERT(aList1, "aList1 should be non-null.");
 | 
						|
  MOZ_ASSERT(aList2, "aList2 should be non-null.");
 | 
						|
 | 
						|
  double distance = 0.0;
 | 
						|
  do {
 | 
						|
    distance += ComputeTransformDistance(aList1->mValue.GetArrayValue(),
 | 
						|
                                         aList2->mValue.GetArrayValue());
 | 
						|
    aList1 = aList1->mNext;
 | 
						|
    aList2 = aList2->mNext;
 | 
						|
    MOZ_ASSERT(!aList1 == !aList2,
 | 
						|
               "aList1 and aList2 should have the same length.");
 | 
						|
  } while (aList1);
 | 
						|
  return sqrt(distance);
 | 
						|
}
 | 
						|
 | 
						|
static double
 | 
						|
ComputeMismatchedTransfromListDistance(const nsCSSValueList* aList1,
 | 
						|
                                       const nsCSSValueList* aList2,
 | 
						|
                                       GeckoStyleContext* aStyleContext)
 | 
						|
{
 | 
						|
  // We need nsStyleContext and nsPresContext to compute calc() values while
 | 
						|
  // processing the translate part of transforms.
 | 
						|
  if (!aStyleContext) {
 | 
						|
    return 0.0;
 | 
						|
  }
 | 
						|
 | 
						|
  RuleNodeCacheConditions dontCare;
 | 
						|
  bool dontCareBool;
 | 
						|
  nsStyleTransformMatrix::TransformReferenceBox emptyRefBox;
 | 
						|
 | 
						|
  Matrix4x4 m1 = nsStyleTransformMatrix::ReadTransforms(
 | 
						|
                   aList1,
 | 
						|
                   aStyleContext,
 | 
						|
                   aStyleContext->PresContext(),
 | 
						|
                   dontCare,
 | 
						|
                   emptyRefBox,
 | 
						|
                   nsPresContext::AppUnitsPerCSSPixel(),
 | 
						|
                   &dontCareBool);
 | 
						|
  Matrix4x4 m2 = nsStyleTransformMatrix::ReadTransforms(
 | 
						|
                   aList2,
 | 
						|
                   aStyleContext,
 | 
						|
                   aStyleContext->PresContext(),
 | 
						|
                   dontCare,
 | 
						|
                   emptyRefBox,
 | 
						|
                   nsPresContext::AppUnitsPerCSSPixel(),
 | 
						|
                   &dontCareBool);
 | 
						|
  return sqrt(ComputeTransform3DMatrixDistance(m1, m2));
 | 
						|
}
 | 
						|
 | 
						|
bool
 | 
						|
StyleAnimationValue::ComputeDistance(nsCSSPropertyID aProperty,
 | 
						|
                                     const StyleAnimationValue& aStartValue,
 | 
						|
                                     const StyleAnimationValue& aEndValue,
 | 
						|
                                     GeckoStyleContext* aStyleContext,
 | 
						|
                                     double& aDistance)
 | 
						|
{
 | 
						|
  Unit commonUnit =
 | 
						|
    GetCommonUnit(aProperty, aStartValue.GetUnit(), aEndValue.GetUnit());
 | 
						|
 | 
						|
  switch (commonUnit) {
 | 
						|
    case eUnit_Null:
 | 
						|
    case eUnit_Auto:
 | 
						|
    case eUnit_None:
 | 
						|
    case eUnit_Normal:
 | 
						|
    case eUnit_UnparsedString:
 | 
						|
    case eUnit_URL:
 | 
						|
    case eUnit_DiscreteCSSValue:
 | 
						|
      return false;
 | 
						|
 | 
						|
    case eUnit_Enumerated:
 | 
						|
      switch (aProperty) {
 | 
						|
        case eCSSProperty_font_stretch: {
 | 
						|
          // just like eUnit_Integer.
 | 
						|
          int32_t startInt = aStartValue.GetIntValue();
 | 
						|
          int32_t endInt = aEndValue.GetIntValue();
 | 
						|
          aDistance = Abs(endInt - startInt);
 | 
						|
          return true;
 | 
						|
        }
 | 
						|
        default:
 | 
						|
          return false;
 | 
						|
      }
 | 
						|
   case eUnit_Visibility: {
 | 
						|
      int32_t startEnum = aStartValue.GetIntValue();
 | 
						|
      int32_t endEnum = aEndValue.GetIntValue();
 | 
						|
      if (startEnum == endEnum) {
 | 
						|
        aDistance = 0;
 | 
						|
        return true;
 | 
						|
      }
 | 
						|
      if ((startEnum == NS_STYLE_VISIBILITY_VISIBLE) ==
 | 
						|
          (endEnum == NS_STYLE_VISIBILITY_VISIBLE)) {
 | 
						|
        return false;
 | 
						|
      }
 | 
						|
      aDistance = 1;
 | 
						|
      return true;
 | 
						|
    }
 | 
						|
    case eUnit_Integer: {
 | 
						|
      int32_t startInt = aStartValue.GetIntValue();
 | 
						|
      int32_t endInt = aEndValue.GetIntValue();
 | 
						|
      aDistance = Abs(double(endInt) - double(startInt));
 | 
						|
      return true;
 | 
						|
    }
 | 
						|
    case eUnit_Coord: {
 | 
						|
      nscoord startCoord = aStartValue.GetCoordValue();
 | 
						|
      nscoord endCoord = aEndValue.GetCoordValue();
 | 
						|
      aDistance = Abs(double(endCoord) - double(startCoord));
 | 
						|
      return true;
 | 
						|
    }
 | 
						|
    case eUnit_Percent: {
 | 
						|
      float startPct = aStartValue.GetPercentValue();
 | 
						|
      float endPct = aEndValue.GetPercentValue();
 | 
						|
      aDistance = Abs(double(endPct) - double(startPct));
 | 
						|
      return true;
 | 
						|
    }
 | 
						|
    case eUnit_Float: {
 | 
						|
      float startFloat = aStartValue.GetFloatValue();
 | 
						|
      float endFloat = aEndValue.GetFloatValue();
 | 
						|
      aDistance = Abs(double(endFloat) - double(startFloat));
 | 
						|
      return true;
 | 
						|
    }
 | 
						|
    case eUnit_Color: {
 | 
						|
      aDistance = ComputeColorDistance(ExtractColor(aStartValue),
 | 
						|
                                       ExtractColor(aEndValue));
 | 
						|
      return true;
 | 
						|
    }
 | 
						|
    case eUnit_CurrentColor: {
 | 
						|
      aDistance = 0;
 | 
						|
      return true;
 | 
						|
    }
 | 
						|
    case eUnit_ComplexColor: {
 | 
						|
      ComplexColorData color1 = ExtractComplexColor(aStartValue);
 | 
						|
      ComplexColorData color2 = ExtractComplexColor(aEndValue);
 | 
						|
      // Common case is interpolating between a color and a currentcolor
 | 
						|
      if (color1.IsNumericColor() && color2.IsCurrentColor()) {
 | 
						|
        double dist = ComputeColorDistance(color1.mColor, NS_RGBA(0, 0, 0, 0));
 | 
						|
        aDistance = sqrt(dist * dist + 1);
 | 
						|
        return true;
 | 
						|
      }
 | 
						|
      if (color1.IsCurrentColor() && color2.IsNumericColor()) {
 | 
						|
        double dist = ComputeColorDistance(NS_RGBA(0, 0, 0, 0), color2.mColor);
 | 
						|
        aDistance = sqrt(dist * dist + 1);
 | 
						|
        return true;
 | 
						|
      }
 | 
						|
      // If we ever reach here, we may want to use the code in
 | 
						|
      // bug 1299741 comment 79 to compute it.
 | 
						|
      MOZ_ASSERT_UNREACHABLE("We shouldn't get here as we only call "
 | 
						|
                             "ComputeDistance on pre-interpolation values");
 | 
						|
      aDistance = 0.0;
 | 
						|
      return true;
 | 
						|
    }
 | 
						|
    case eUnit_Calc: {
 | 
						|
      PixelCalcValue v1 = ExtractCalcValue(aStartValue);
 | 
						|
      PixelCalcValue v2 = ExtractCalcValue(aEndValue);
 | 
						|
      float difflen = v2.mLength - v1.mLength;
 | 
						|
      float diffpct = v2.mPercent - v1.mPercent;
 | 
						|
      aDistance = sqrt(difflen * difflen + diffpct * diffpct);
 | 
						|
      return true;
 | 
						|
    }
 | 
						|
    case eUnit_ObjectPosition: {
 | 
						|
      const nsCSSValue* position1 = aStartValue.GetCSSValueValue();
 | 
						|
      const nsCSSValue* position2 = aEndValue.GetCSSValueValue();
 | 
						|
      double squareDistance =
 | 
						|
        CalcPositionSquareDistance(*position1,
 | 
						|
                                   *position2);
 | 
						|
      aDistance = sqrt(squareDistance);
 | 
						|
      return true;
 | 
						|
    }
 | 
						|
    case eUnit_CSSValuePair: {
 | 
						|
      const nsCSSValuePair *pair1 = aStartValue.GetCSSValuePairValue();
 | 
						|
      const nsCSSValuePair *pair2 = aEndValue.GetCSSValuePairValue();
 | 
						|
      nsCSSUnit unit[2];
 | 
						|
      unit[0] = GetCommonUnit(aProperty, pair1->mXValue.GetUnit(),
 | 
						|
                              pair2->mXValue.GetUnit());
 | 
						|
      unit[1] = GetCommonUnit(aProperty, pair1->mYValue.GetUnit(),
 | 
						|
                              pair2->mYValue.GetUnit());
 | 
						|
      if (unit[0] == eCSSUnit_Null || unit[1] == eCSSUnit_Null ||
 | 
						|
          unit[0] == eCSSUnit_URL || unit[0] == eCSSUnit_Enumerated) {
 | 
						|
        return false;
 | 
						|
      }
 | 
						|
 | 
						|
      double squareDistance = 0.0;
 | 
						|
      static nsCSSValue nsCSSValuePair::* const pairValues[2] = {
 | 
						|
        &nsCSSValuePair::mXValue, &nsCSSValuePair::mYValue
 | 
						|
      };
 | 
						|
      for (uint32_t i = 0; i < 2; ++i) {
 | 
						|
        nsCSSValue nsCSSValuePair::*member = pairValues[i];
 | 
						|
        double diffsquared;
 | 
						|
        switch (unit[i]) {
 | 
						|
          case eCSSUnit_Pixel: {
 | 
						|
            float diff = (pair1->*member).GetFloatValue() -
 | 
						|
                         (pair2->*member).GetFloatValue();
 | 
						|
            diffsquared = diff * diff;
 | 
						|
            break;
 | 
						|
          }
 | 
						|
          case eCSSUnit_Percent: {
 | 
						|
            float diff = (pair1->*member).GetPercentValue() -
 | 
						|
                         (pair2->*member).GetPercentValue();
 | 
						|
            diffsquared = diff * diff;
 | 
						|
            break;
 | 
						|
          }
 | 
						|
          case eCSSUnit_Calc: {
 | 
						|
            PixelCalcValue v1 = ExtractCalcValue(pair1->*member);
 | 
						|
            PixelCalcValue v2 = ExtractCalcValue(pair2->*member);
 | 
						|
            float difflen = v2.mLength - v1.mLength;
 | 
						|
            float diffpct = v2.mPercent - v1.mPercent;
 | 
						|
            diffsquared = difflen * difflen + diffpct * diffpct;
 | 
						|
            break;
 | 
						|
          }
 | 
						|
          default:
 | 
						|
            MOZ_ASSERT(false, "unexpected unit");
 | 
						|
            return false;
 | 
						|
        }
 | 
						|
        squareDistance += diffsquared;
 | 
						|
      }
 | 
						|
 | 
						|
      aDistance = sqrt(squareDistance);
 | 
						|
      return true;
 | 
						|
    }
 | 
						|
    case eUnit_CSSValueTriplet: {
 | 
						|
      const nsCSSValueTriplet *triplet1 = aStartValue.GetCSSValueTripletValue();
 | 
						|
      const nsCSSValueTriplet *triplet2 = aEndValue.GetCSSValueTripletValue();
 | 
						|
      nsCSSUnit unit[3];
 | 
						|
      unit[0] = GetCommonUnit(aProperty, triplet1->mXValue.GetUnit(),
 | 
						|
                              triplet2->mXValue.GetUnit());
 | 
						|
      unit[1] = GetCommonUnit(aProperty, triplet1->mYValue.GetUnit(),
 | 
						|
                              triplet2->mYValue.GetUnit());
 | 
						|
      unit[2] = GetCommonUnit(aProperty, triplet1->mZValue.GetUnit(),
 | 
						|
                              triplet2->mZValue.GetUnit());
 | 
						|
      if (unit[0] == eCSSUnit_Null || unit[1] == eCSSUnit_Null ||
 | 
						|
          unit[2] == eCSSUnit_Null) {
 | 
						|
        return false;
 | 
						|
      }
 | 
						|
 | 
						|
      double squareDistance = 0.0;
 | 
						|
      static nsCSSValue nsCSSValueTriplet::* const pairValues[3] = {
 | 
						|
        &nsCSSValueTriplet::mXValue, &nsCSSValueTriplet::mYValue, &nsCSSValueTriplet::mZValue
 | 
						|
      };
 | 
						|
      for (uint32_t i = 0; i < 3; ++i) {
 | 
						|
        nsCSSValue nsCSSValueTriplet::*member = pairValues[i];
 | 
						|
        double diffsquared;
 | 
						|
        switch (unit[i]) {
 | 
						|
          case eCSSUnit_Pixel: {
 | 
						|
            float diff = (triplet1->*member).GetFloatValue() -
 | 
						|
                         (triplet2->*member).GetFloatValue();
 | 
						|
            diffsquared = diff * diff;
 | 
						|
            break;
 | 
						|
          }
 | 
						|
          case eCSSUnit_Percent: {
 | 
						|
            float diff = (triplet1->*member).GetPercentValue() -
 | 
						|
                         (triplet2->*member).GetPercentValue();
 | 
						|
             diffsquared = diff * diff;
 | 
						|
             break;
 | 
						|
          }
 | 
						|
          case eCSSUnit_Calc: {
 | 
						|
            PixelCalcValue v1 = ExtractCalcValue(triplet1->*member);
 | 
						|
            PixelCalcValue v2 = ExtractCalcValue(triplet2->*member);
 | 
						|
            float difflen = v2.mLength - v1.mLength;
 | 
						|
            float diffpct = v2.mPercent - v1.mPercent;
 | 
						|
            diffsquared = difflen * difflen + diffpct * diffpct;
 | 
						|
            break;
 | 
						|
          }
 | 
						|
          case eCSSUnit_Null:
 | 
						|
            diffsquared = 0;
 | 
						|
            break;
 | 
						|
          default:
 | 
						|
            MOZ_ASSERT(false, "unexpected unit");
 | 
						|
            return false;
 | 
						|
        }
 | 
						|
        squareDistance += diffsquared;
 | 
						|
      }
 | 
						|
 | 
						|
      aDistance = sqrt(squareDistance);
 | 
						|
      return true;
 | 
						|
    }
 | 
						|
    case eUnit_CSSRect: {
 | 
						|
      const nsCSSRect *rect1 = aStartValue.GetCSSRectValue();
 | 
						|
      const nsCSSRect *rect2 = aEndValue.GetCSSRectValue();
 | 
						|
      if (rect1->mTop.GetUnit() != rect2->mTop.GetUnit() ||
 | 
						|
          rect1->mRight.GetUnit() != rect2->mRight.GetUnit() ||
 | 
						|
          rect1->mBottom.GetUnit() != rect2->mBottom.GetUnit() ||
 | 
						|
          rect1->mLeft.GetUnit() != rect2->mLeft.GetUnit()) {
 | 
						|
        // At least until we have calc()
 | 
						|
        return false;
 | 
						|
      }
 | 
						|
 | 
						|
      double squareDistance = 0.0;
 | 
						|
      for (uint32_t i = 0; i < ArrayLength(nsCSSRect::sides); ++i) {
 | 
						|
        nsCSSValue nsCSSRect::*member = nsCSSRect::sides[i];
 | 
						|
        MOZ_ASSERT((rect1->*member).GetUnit() == (rect2->*member).GetUnit(),
 | 
						|
                   "should have returned above");
 | 
						|
        double diff;
 | 
						|
        switch ((rect1->*member).GetUnit()) {
 | 
						|
          case eCSSUnit_Pixel:
 | 
						|
            diff = (rect1->*member).GetFloatValue() -
 | 
						|
                   (rect2->*member).GetFloatValue();
 | 
						|
            break;
 | 
						|
          case eCSSUnit_Auto:
 | 
						|
            diff = 0;
 | 
						|
            break;
 | 
						|
          default:
 | 
						|
            MOZ_ASSERT(false, "unexpected unit");
 | 
						|
            return false;
 | 
						|
        }
 | 
						|
        squareDistance += diff * diff;
 | 
						|
      }
 | 
						|
 | 
						|
      aDistance = sqrt(squareDistance);
 | 
						|
      return true;
 | 
						|
    }
 | 
						|
    case eUnit_Dasharray: {
 | 
						|
      // NOTE: This produces results on substantially different scales
 | 
						|
      // for length values and percentage values, which might even be
 | 
						|
      // mixed in the same property value.  This means the result isn't
 | 
						|
      // particularly useful for paced animation.
 | 
						|
 | 
						|
      // Call AddWeighted to make us lists of the same length.
 | 
						|
      StyleAnimationValue normValue1, normValue2;
 | 
						|
      if (!AddWeighted(aProperty, 1.0, aStartValue, 0.0, aEndValue,
 | 
						|
                       normValue1) ||
 | 
						|
          !AddWeighted(aProperty, 0.0, aStartValue, 1.0, aEndValue,
 | 
						|
                       normValue2)) {
 | 
						|
        return false;
 | 
						|
      }
 | 
						|
 | 
						|
      double squareDistance = 0.0;
 | 
						|
      const nsCSSValueList *list1 = normValue1.GetCSSValueListValue();
 | 
						|
      const nsCSSValueList *list2 = normValue2.GetCSSValueListValue();
 | 
						|
 | 
						|
      MOZ_ASSERT(!list1 == !list2, "lists should be same length");
 | 
						|
      while (list1) {
 | 
						|
        const nsCSSValue &val1 = list1->mValue;
 | 
						|
        const nsCSSValue &val2 = list2->mValue;
 | 
						|
 | 
						|
        MOZ_ASSERT(val1.GetUnit() == val2.GetUnit(),
 | 
						|
                   "unit match should be assured by AddWeighted");
 | 
						|
        double diff;
 | 
						|
        switch (val1.GetUnit()) {
 | 
						|
          case eCSSUnit_Percent:
 | 
						|
            diff = val1.GetPercentValue() - val2.GetPercentValue();
 | 
						|
            break;
 | 
						|
          case eCSSUnit_Number:
 | 
						|
            diff = val1.GetFloatValue() - val2.GetFloatValue();
 | 
						|
            break;
 | 
						|
          default:
 | 
						|
            MOZ_ASSERT(false, "unexpected unit");
 | 
						|
            return false;
 | 
						|
        }
 | 
						|
        squareDistance += diff * diff;
 | 
						|
 | 
						|
        list1 = list1->mNext;
 | 
						|
        list2 = list2->mNext;
 | 
						|
        MOZ_ASSERT(!list1 == !list2, "lists should be same length");
 | 
						|
      }
 | 
						|
 | 
						|
      aDistance = sqrt(squareDistance);
 | 
						|
      return true;
 | 
						|
    }
 | 
						|
    case eUnit_Shadow: {
 | 
						|
      // Call AddWeighted to make us lists of the same length.
 | 
						|
      StyleAnimationValue normValue1, normValue2;
 | 
						|
      if (!AddWeighted(aProperty, 1.0, aStartValue, 0.0, aEndValue,
 | 
						|
                       normValue1) ||
 | 
						|
          !AddWeighted(aProperty, 0.0, aStartValue, 1.0, aEndValue,
 | 
						|
                       normValue2)) {
 | 
						|
        return false;
 | 
						|
      }
 | 
						|
 | 
						|
      const nsCSSValueList* shadow1 = normValue1.GetCSSValueListValue();
 | 
						|
      const nsCSSValueList* shadow2 = normValue2.GetCSSValueListValue();
 | 
						|
 | 
						|
      aDistance = 0.0;
 | 
						|
      MOZ_ASSERT(!shadow1 == !shadow2, "lists should be same length");
 | 
						|
      while (shadow1) {
 | 
						|
        double squareDistance = 0.0;
 | 
						|
        if (!ComputeSingleShadowSquareDistance(shadow1, shadow2,
 | 
						|
                                               squareDistance,
 | 
						|
                                               aProperty)) {
 | 
						|
          NS_ERROR("Unexpected ComputeSingleShadowSquareDistance failure; "
 | 
						|
                   "why didn't we fail earlier, in AddWeighted calls above?");
 | 
						|
        }
 | 
						|
        aDistance += squareDistance; // cumulative distance^2; sqrt()'d below.
 | 
						|
 | 
						|
        shadow1 = shadow1->mNext;
 | 
						|
        shadow2 = shadow2->mNext;
 | 
						|
        MOZ_ASSERT(!shadow1 == !shadow2, "lists should be same length");
 | 
						|
      }
 | 
						|
      aDistance = sqrt(aDistance);
 | 
						|
      return true;
 | 
						|
    }
 | 
						|
    case eUnit_Shape:
 | 
						|
      return ComputeShapeDistance(aProperty,
 | 
						|
                                  aStartValue.GetCSSValueArrayValue(),
 | 
						|
                                  aEndValue.GetCSSValueArrayValue(),
 | 
						|
                                  aDistance);
 | 
						|
 | 
						|
    case eUnit_Filter:
 | 
						|
      return ComputeFilterListDistance(aStartValue.GetCSSValueListValue(),
 | 
						|
                                       aEndValue.GetCSSValueListValue(),
 | 
						|
                                       aDistance);
 | 
						|
 | 
						|
    case eUnit_Transform: {
 | 
						|
      // FIXME: We don't have an official spec to define the distance of
 | 
						|
      // two transform lists, but paced spacing (defined in Web Animations API)
 | 
						|
      // needs this, so we implement this according to the concept of the
 | 
						|
      // interpolation of two transform lists.
 | 
						|
      // Issue: https://www.w3.org/TR/web-animations-1/#issue-789f9fd1
 | 
						|
 | 
						|
      const nsCSSValueList* list1 =
 | 
						|
        aStartValue.GetCSSValueSharedListValue()->mHead;
 | 
						|
      const nsCSSValueList* list2 =
 | 
						|
        aEndValue.GetCSSValueSharedListValue()->mHead;
 | 
						|
      MOZ_ASSERT(list1);
 | 
						|
      MOZ_ASSERT(list2);
 | 
						|
 | 
						|
      if (list1->mValue.GetUnit() == eCSSUnit_None &&
 | 
						|
          list2->mValue.GetUnit() == eCSSUnit_None) {
 | 
						|
        // Both none, nothing happens.
 | 
						|
        aDistance = 0.0;
 | 
						|
      } else if (list1->mValue.GetUnit() == eCSSUnit_None) {
 | 
						|
        nsAutoPtr<nsCSSValueList> none(AddTransformLists(0, list2, 0, list2));
 | 
						|
        aDistance = ComputeTransformListDistance(none, list2);
 | 
						|
      } else if (list2->mValue.GetUnit() == eCSSUnit_None) {
 | 
						|
        nsAutoPtr<nsCSSValueList> none(AddTransformLists(0, list1, 0, list1));
 | 
						|
        aDistance = ComputeTransformListDistance(list1, none);
 | 
						|
      } else if (TransformFunctionListsMatch(list1, list2)) {
 | 
						|
        aDistance = ComputeTransformListDistance(list1, list2);
 | 
						|
      } else {
 | 
						|
        aDistance =
 | 
						|
          ComputeMismatchedTransfromListDistance(list1, list2, aStyleContext);
 | 
						|
      }
 | 
						|
      return true;
 | 
						|
    }
 | 
						|
    case eUnit_BackgroundPositionCoord: {
 | 
						|
      const nsCSSValueList *position1 = aStartValue.GetCSSValueListValue();
 | 
						|
      const nsCSSValueList *position2 = aEndValue.GetCSSValueListValue();
 | 
						|
 | 
						|
      double squareDistance = 0.0;
 | 
						|
      MOZ_ASSERT(!position1 == !position2, "lists should be same length");
 | 
						|
 | 
						|
      while (position1 && position2) {
 | 
						|
        squareDistance += CalcPositionCoordSquareDistance(position1->mValue,
 | 
						|
                                                          position2->mValue);
 | 
						|
        position1 = position1->mNext;
 | 
						|
        position2 = position2->mNext;
 | 
						|
      }
 | 
						|
      // fail if lists differ in length.
 | 
						|
      if (position1 || position2) {
 | 
						|
        return false;
 | 
						|
      }
 | 
						|
 | 
						|
      aDistance = sqrt(squareDistance);
 | 
						|
      return true;
 | 
						|
    }
 | 
						|
    case eUnit_CSSValuePairList: {
 | 
						|
      const nsCSSValuePairList *list1 = aStartValue.GetCSSValuePairListValue();
 | 
						|
      const nsCSSValuePairList *list2 = aEndValue.GetCSSValuePairListValue();
 | 
						|
      double squareDistance = 0.0;
 | 
						|
      do {
 | 
						|
        static nsCSSValue nsCSSValuePairList::* const pairListValues[] = {
 | 
						|
          &nsCSSValuePairList::mXValue,
 | 
						|
          &nsCSSValuePairList::mYValue,
 | 
						|
        };
 | 
						|
        for (uint32_t i = 0; i < ArrayLength(pairListValues); ++i) {
 | 
						|
          const nsCSSValue &v1 = list1->*(pairListValues[i]);
 | 
						|
          const nsCSSValue &v2 = list2->*(pairListValues[i]);
 | 
						|
          nsCSSUnit unit =
 | 
						|
            GetCommonUnit(aProperty, v1.GetUnit(), v2.GetUnit());
 | 
						|
          if (unit == eCSSUnit_Null) {
 | 
						|
            return false;
 | 
						|
          }
 | 
						|
          double diffsquared = 0.0;
 | 
						|
          switch (unit) {
 | 
						|
            case eCSSUnit_Number:
 | 
						|
            case eCSSUnit_Pixel: {
 | 
						|
              float diff = v1.GetFloatValue() - v2.GetFloatValue();
 | 
						|
              diffsquared = diff * diff;
 | 
						|
              break;
 | 
						|
            }
 | 
						|
            case eCSSUnit_Percent: {
 | 
						|
              float diff = v1.GetPercentValue() - v2.GetPercentValue();
 | 
						|
              diffsquared = diff * diff;
 | 
						|
              break;
 | 
						|
            }
 | 
						|
            case eCSSUnit_Calc: {
 | 
						|
              PixelCalcValue val1 = ExtractCalcValue(v1);
 | 
						|
              PixelCalcValue val2 = ExtractCalcValue(v2);
 | 
						|
              float difflen = val2.mLength - val1.mLength;
 | 
						|
              float diffpct = val2.mPercent - val1.mPercent;
 | 
						|
              diffsquared = difflen * difflen + diffpct * diffpct;
 | 
						|
              break;
 | 
						|
            }
 | 
						|
            default:
 | 
						|
              if (v1 != v2) {
 | 
						|
                return false;
 | 
						|
              }
 | 
						|
              break;
 | 
						|
          }
 | 
						|
          squareDistance += diffsquared;
 | 
						|
        }
 | 
						|
        list1 = list1->mNext;
 | 
						|
        list2 = list2->mNext;
 | 
						|
      } while (list1 && list2);
 | 
						|
      if (list1 || list2) {
 | 
						|
        // We can't interpolate lists of different lengths.
 | 
						|
        return false;
 | 
						|
      }
 | 
						|
      aDistance = sqrt(squareDistance);
 | 
						|
      return true;
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  MOZ_ASSERT(false, "Can't compute distance using the given common unit");
 | 
						|
  return false;
 | 
						|
}
 | 
						|
 | 
						|
static inline void
 | 
						|
AddCSSValueNumber(double aCoeff1, const nsCSSValue &aValue1,
 | 
						|
                  double aCoeff2, const nsCSSValue &aValue2,
 | 
						|
                  nsCSSValue &aResult, uint32_t aValueRestrictions = 0)
 | 
						|
{
 | 
						|
  MOZ_ASSERT(aValue1.GetUnit() == eCSSUnit_Number, "unexpected unit");
 | 
						|
  MOZ_ASSERT(aValue2.GetUnit() == eCSSUnit_Number, "unexpected unit");
 | 
						|
  aResult.SetFloatValue(RestrictValue(aValueRestrictions,
 | 
						|
                                      aCoeff1 * aValue1.GetFloatValue() +
 | 
						|
                                      aCoeff2 * aValue2.GetFloatValue()),
 | 
						|
                        eCSSUnit_Number);
 | 
						|
}
 | 
						|
 | 
						|
static inline float
 | 
						|
GetNumberOrPercent(const nsCSSValue &aValue)
 | 
						|
{
 | 
						|
  nsCSSUnit unit = aValue.GetUnit();
 | 
						|
  MOZ_ASSERT(unit == eCSSUnit_Number || unit == eCSSUnit_Percent,
 | 
						|
             "unexpected unit");
 | 
						|
  return (unit == eCSSUnit_Number) ?
 | 
						|
    aValue.GetFloatValue() : aValue.GetPercentValue();
 | 
						|
}
 | 
						|
 | 
						|
static inline void
 | 
						|
AddCSSValuePercentNumber(const uint32_t aValueRestrictions,
 | 
						|
                         double aCoeff1, const nsCSSValue &aValue1,
 | 
						|
                         double aCoeff2, const nsCSSValue &aValue2,
 | 
						|
                         nsCSSValue &aResult, float aInitialVal)
 | 
						|
{
 | 
						|
  float n1 = GetNumberOrPercent(aValue1);
 | 
						|
  float n2 = GetNumberOrPercent(aValue2);
 | 
						|
 | 
						|
  // Rather than interpolating aValue1 and aValue2 directly, we
 | 
						|
  // interpolate their *distances from aInitialVal* (the initial value,
 | 
						|
  // which is either 1 or 0 for "filter" functions).  This matters in
 | 
						|
  // cases where aInitialVal is nonzero and the coefficients don't add
 | 
						|
  // up to 1.  For example, if initialVal is 1, aCoeff1 is 0.5, and
 | 
						|
  // aCoeff2 is 0, then we'll return the value halfway between 1 and
 | 
						|
  // aValue1, rather than the value halfway between 0 and aValue1.
 | 
						|
  // Note that we do something similar in AddTransformScale().
 | 
						|
  float result = (n1 - aInitialVal) * aCoeff1 + (n2 - aInitialVal) * aCoeff2;
 | 
						|
  aResult.SetFloatValue(RestrictValue(aValueRestrictions, result + aInitialVal),
 | 
						|
                        eCSSUnit_Number);
 | 
						|
}
 | 
						|
 | 
						|
// Multiplies |aValue| color by |aDilutionRatio|.
 | 
						|
static nscolor
 | 
						|
DiluteColor(const RGBAColorData& aValue, double aDilutionRatio)
 | 
						|
{
 | 
						|
  float resultA = aValue.mA * aDilutionRatio;
 | 
						|
  return resultA <= 0.0 ? NS_RGBA(0, 0, 0, 0)
 | 
						|
                        : aValue.WithAlpha(resultA).ToColor();
 | 
						|
}
 | 
						|
 | 
						|
// Clamped AddWeightedColors.
 | 
						|
static nscolor
 | 
						|
AddWeightedColorsAndClamp(double aCoeff1, const RGBAColorData& aValue1,
 | 
						|
                          double aCoeff2, const RGBAColorData& aValue2)
 | 
						|
{
 | 
						|
  // We are using AddWeighted() with a zero aCoeff2 for colors to
 | 
						|
  // pretend AddWeighted() against transparent color, i.e. rgba(0, 0, 0, 0).
 | 
						|
  // But unpremultiplication in AddWeightedColors() does not work well
 | 
						|
  // for such cases, so we use another function named DiluteColor() which
 | 
						|
  // has a similar logic to AddWeightedColors().
 | 
						|
  return aCoeff2 == 0.0
 | 
						|
    ? DiluteColor(aValue1, aCoeff1)
 | 
						|
    : AddWeightedColors(aCoeff1, aValue1, aCoeff2, aValue2).ToColor();
 | 
						|
}
 | 
						|
 | 
						|
static void
 | 
						|
AppendToCSSValueList(UniquePtr<nsCSSValueList>& aHead,
 | 
						|
                     UniquePtr<nsCSSValueList>&& aValueToAppend,
 | 
						|
                     nsCSSValueList** aTail)
 | 
						|
{
 | 
						|
  MOZ_ASSERT(!aHead == !*aTail,
 | 
						|
             "Can't have head w/o tail, & vice versa");
 | 
						|
 | 
						|
  if (!aHead) {
 | 
						|
    aHead = Move(aValueToAppend);
 | 
						|
    *aTail = aHead.get();
 | 
						|
  } else {
 | 
						|
    (*aTail) = (*aTail)->mNext = aValueToAppend.release();
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
static void
 | 
						|
AppendToCSSValuePairList(UniquePtr<nsCSSValuePairList>& aHead,
 | 
						|
                         UniquePtr<nsCSSValuePairList>&& aValueToAppend,
 | 
						|
                         nsCSSValuePairList** aTail)
 | 
						|
{
 | 
						|
  MOZ_ASSERT(!aHead == !*aTail,
 | 
						|
             "Can't have head w/o tail, & vice versa");
 | 
						|
 | 
						|
  if (!aHead) {
 | 
						|
    aHead = Move(aValueToAppend);
 | 
						|
    *aTail = aHead.get();
 | 
						|
  } else {
 | 
						|
    (*aTail) = (*aTail)->mNext = aValueToAppend.release();
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
static UniquePtr<nsCSSValueList>
 | 
						|
AddWeightedShadowItems(double aCoeff1, const nsCSSValue &aValue1,
 | 
						|
                       double aCoeff2, const nsCSSValue &aValue2,
 | 
						|
                       ColorAdditionType aColorAdditionType,
 | 
						|
                       nsCSSPropertyID aProperty)
 | 
						|
{
 | 
						|
  // X, Y, Radius, Spread, Color, Inset
 | 
						|
  MOZ_ASSERT(aValue1.GetUnit() == eCSSUnit_Array,
 | 
						|
             "wrong unit");
 | 
						|
  MOZ_ASSERT(aValue2.GetUnit() == eCSSUnit_Array,
 | 
						|
             "wrong unit");
 | 
						|
  nsCSSValue::Array *array1 = aValue1.GetArrayValue();
 | 
						|
  nsCSSValue::Array *array2 = aValue2.GetArrayValue();
 | 
						|
  RefPtr<nsCSSValue::Array> resultArray = nsCSSValue::Array::Create(6);
 | 
						|
 | 
						|
  for (size_t i = 0; i < 4; ++i) {
 | 
						|
    // The text-shadow is not need to spread radius,
 | 
						|
    // So we skip this interpolation.
 | 
						|
    if (i == 3 && (aProperty != eCSSProperty_box_shadow)) continue;
 | 
						|
    AddCSSValuePixel(aCoeff1, array1->Item(i), aCoeff2, array2->Item(i),
 | 
						|
                     resultArray->Item(i),
 | 
						|
                     // blur radius must be nonnegative
 | 
						|
                     (i == 2) ? CSS_PROPERTY_VALUE_NONNEGATIVE : 0);
 | 
						|
  }
 | 
						|
 | 
						|
  const nsCSSValue& colorValue1 = array1->Item(4);
 | 
						|
  const nsCSSValue& colorValue2 = array2->Item(4);
 | 
						|
  const nsCSSValue& inset1 = array1->Item(5);
 | 
						|
  const nsCSSValue& inset2 = array2->Item(5);
 | 
						|
  if ((colorValue1.GetUnit() != colorValue2.GetUnit() &&
 | 
						|
       (!colorValue1.IsNumericColorUnit() ||
 | 
						|
        !colorValue2.IsNumericColorUnit())) ||
 | 
						|
      inset1.GetUnit() != inset2.GetUnit()) {
 | 
						|
    // We don't know how to animate between color and no-color, or
 | 
						|
    // between inset and not-inset.
 | 
						|
    // NOTE: In case when both colors' units are eCSSUnit_Null, that means
 | 
						|
    // neither color value was specified, so we can interpolate.
 | 
						|
    return nullptr;
 | 
						|
  }
 | 
						|
 | 
						|
  if (colorValue1.GetUnit() != eCSSUnit_Null) {
 | 
						|
    RGBAColorData color1 = ExtractColor(colorValue1);
 | 
						|
    RGBAColorData color2 = ExtractColor(colorValue2);
 | 
						|
    if (aColorAdditionType == ColorAdditionType::Clamped) {
 | 
						|
      resultArray->Item(4).SetColorValue(
 | 
						|
        AddWeightedColorsAndClamp(aCoeff1, color1, aCoeff2, color2));
 | 
						|
    } else {
 | 
						|
      resultArray->Item(4).SetRGBAColorValue(
 | 
						|
        AddWeightedColors(aCoeff1, color1, aCoeff2, color2));
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  MOZ_ASSERT(inset1 == inset2, "should match");
 | 
						|
  resultArray->Item(5) = inset1;
 | 
						|
 | 
						|
  auto resultItem = MakeUnique<nsCSSValueList>();
 | 
						|
  resultItem->mValue.SetArrayValue(resultArray, eCSSUnit_Array);
 | 
						|
  return resultItem;
 | 
						|
}
 | 
						|
 | 
						|
static void
 | 
						|
AddTransformScale(double aCoeff1, const nsCSSValue &aValue1,
 | 
						|
                  double aCoeff2, const nsCSSValue &aValue2,
 | 
						|
                  nsCSSValue &aResult)
 | 
						|
{
 | 
						|
  // Handle scale, and the two matrix components where identity is 1, by
 | 
						|
  // subtracting 1, multiplying by the coefficients, and then adding 1
 | 
						|
  // back.  This gets the right AddWeighted behavior and gets us the
 | 
						|
  // interpolation-against-identity behavior for free.
 | 
						|
  MOZ_ASSERT(aValue1.GetUnit() == eCSSUnit_Number, "unexpected unit");
 | 
						|
  MOZ_ASSERT(aValue2.GetUnit() == eCSSUnit_Number, "unexpected unit");
 | 
						|
 | 
						|
  float v1 = aValue1.GetFloatValue() - 1.0f,
 | 
						|
        v2 = aValue2.GetFloatValue() - 1.0f;
 | 
						|
  float result = v1 * aCoeff1 + v2 * aCoeff2;
 | 
						|
  aResult.SetFloatValue(EnsureNotNan(result + 1.0f), eCSSUnit_Number);
 | 
						|
}
 | 
						|
 | 
						|
static nsCSSValueList*
 | 
						|
AddDifferentTransformLists(double aCoeff1, const nsCSSValueList* aList1,
 | 
						|
                           double aCoeff2, const nsCSSValueList* aList2,
 | 
						|
                           nsCSSKeyword aOperatorType)
 | 
						|
{
 | 
						|
  nsAutoPtr<nsCSSValueList> result;
 | 
						|
  nsCSSValueList **resultTail = getter_Transfers(result);
 | 
						|
 | 
						|
  RefPtr<nsCSSValue::Array> arr;
 | 
						|
  arr = AnimationValue::AppendTransformFunction(aOperatorType, resultTail);
 | 
						|
 | 
						|
  if (aCoeff1 == 0) {
 | 
						|
    // If the first coeffient is zero, we don't need to care about the first
 | 
						|
    // list at all.
 | 
						|
    arr->Item(1).Reset();
 | 
						|
  } else if (aList1 == aList2) {
 | 
						|
    // If we have the same list, clear the first list, add the first coefficient
 | 
						|
    // into the second one so that we can simply multiply the second list by the
 | 
						|
    // second coefficient value.
 | 
						|
    arr->Item(1).Reset();
 | 
						|
    aCoeff2 += aCoeff1;
 | 
						|
  } else {
 | 
						|
    MOZ_ASSERT((aOperatorType == eCSSKeyword_accumulatematrix &&
 | 
						|
                aCoeff1 == 1.0) ||
 | 
						|
               (aOperatorType == eCSSKeyword_interpolatematrix &&
 | 
						|
                FuzzyEqualsAdditive(aCoeff1 + aCoeff2, 1.0)),
 | 
						|
              "|aCoeff1| should be 1.0 for accumulation, "
 | 
						|
              "|aCoeff1| + |aCoeff2| == 1.0 for interpolation");
 | 
						|
    aList1->CloneInto(arr->Item(1).SetListValue());
 | 
						|
  }
 | 
						|
 | 
						|
  aList2->CloneInto(arr->Item(2).SetListValue());
 | 
						|
  arr->Item(3).SetPercentValue(aCoeff2);
 | 
						|
 | 
						|
  return result.forget();
 | 
						|
}
 | 
						|
 | 
						|
static UniquePtr<nsCSSValueList>
 | 
						|
AddWeightedFilterFunctionImpl(double aCoeff1, const nsCSSValueList* aList1,
 | 
						|
                              double aCoeff2, const nsCSSValueList* aList2,
 | 
						|
                              ColorAdditionType aColorAdditionType)
 | 
						|
{
 | 
						|
  // AddWeightedFilterFunction should be our only caller, and it should ensure
 | 
						|
  // that both args are non-null.
 | 
						|
  MOZ_ASSERT(aList1, "expected filter list");
 | 
						|
  MOZ_ASSERT(aList2, "expected filter list");
 | 
						|
  MOZ_ASSERT(aList1->mValue.GetUnit() == eCSSUnit_Function,
 | 
						|
             "expected function");
 | 
						|
  MOZ_ASSERT(aList2->mValue.GetUnit() == eCSSUnit_Function,
 | 
						|
             "expected function");
 | 
						|
  RefPtr<nsCSSValue::Array> a1 = aList1->mValue.GetArrayValue(),
 | 
						|
                              a2 = aList2->mValue.GetArrayValue();
 | 
						|
  nsCSSKeyword filterFunction = a1->Item(0).GetKeywordValue();
 | 
						|
  if (filterFunction != a2->Item(0).GetKeywordValue()) {
 | 
						|
    return nullptr; // Can't add two filters of different types.
 | 
						|
  }
 | 
						|
 | 
						|
  auto resultList = MakeUnique<nsCSSValueList>();
 | 
						|
  nsCSSValue::Array* result =
 | 
						|
    resultList->mValue.InitFunction(filterFunction, 1);
 | 
						|
 | 
						|
  // "hue-rotate" is the only filter-function that accepts negative values, and
 | 
						|
  // we don't use this "restrictions" variable in its clause below.
 | 
						|
  const uint32_t restrictions = CSS_PROPERTY_VALUE_NONNEGATIVE;
 | 
						|
  const nsCSSValue& funcArg1 = a1->Item(1);
 | 
						|
  const nsCSSValue& funcArg2 = a2->Item(1);
 | 
						|
  nsCSSValue& resultArg = result->Item(1);
 | 
						|
  float initialVal = 1.0f;
 | 
						|
  switch (filterFunction) {
 | 
						|
    case eCSSKeyword_blur: {
 | 
						|
      nsCSSUnit unit;
 | 
						|
      if (funcArg1.GetUnit() == funcArg2.GetUnit()) {
 | 
						|
        unit = funcArg1.GetUnit();
 | 
						|
      } else {
 | 
						|
        // If units differ, we'll just combine them with calc().
 | 
						|
        unit = eCSSUnit_Calc;
 | 
						|
      }
 | 
						|
      if (!AddCSSValuePixelPercentCalc(restrictions,
 | 
						|
                                       unit,
 | 
						|
                                       aCoeff1, funcArg1,
 | 
						|
                                       aCoeff2, funcArg2,
 | 
						|
                                       resultArg)) {
 | 
						|
        return nullptr;
 | 
						|
      }
 | 
						|
      break;
 | 
						|
    }
 | 
						|
    case eCSSKeyword_grayscale:
 | 
						|
    case eCSSKeyword_invert:
 | 
						|
    case eCSSKeyword_sepia:
 | 
						|
      initialVal = 0.0f;
 | 
						|
      MOZ_FALLTHROUGH;
 | 
						|
    case eCSSKeyword_brightness:
 | 
						|
    case eCSSKeyword_contrast:
 | 
						|
    case eCSSKeyword_opacity:
 | 
						|
    case eCSSKeyword_saturate:
 | 
						|
      AddCSSValuePercentNumber(restrictions,
 | 
						|
                               aCoeff1, funcArg1,
 | 
						|
                               aCoeff2, funcArg2,
 | 
						|
                               resultArg,
 | 
						|
                               initialVal);
 | 
						|
      break;
 | 
						|
    case eCSSKeyword_hue_rotate:
 | 
						|
      AddCSSValueAngle(aCoeff1, funcArg1,
 | 
						|
                       aCoeff2, funcArg2,
 | 
						|
                       resultArg);
 | 
						|
      break;
 | 
						|
    case eCSSKeyword_drop_shadow: {
 | 
						|
      MOZ_ASSERT(!funcArg1.GetListValue()->mNext &&
 | 
						|
                 !funcArg2.GetListValue()->mNext,
 | 
						|
                 "drop-shadow filter func doesn't support lists");
 | 
						|
      UniquePtr<nsCSSValueList> shadowValue =
 | 
						|
        AddWeightedShadowItems(aCoeff1,
 | 
						|
                               funcArg1.GetListValue()->mValue,
 | 
						|
                               aCoeff2,
 | 
						|
                               funcArg2.GetListValue()->mValue,
 | 
						|
                               aColorAdditionType,
 | 
						|
                               eCSSProperty_filter);
 | 
						|
      if (!shadowValue) {
 | 
						|
        return nullptr;
 | 
						|
      }
 | 
						|
      resultArg.AdoptListValue(Move(shadowValue));
 | 
						|
      break;
 | 
						|
    }
 | 
						|
    default:
 | 
						|
      MOZ_ASSERT(false, "unknown filter function");
 | 
						|
      return nullptr;
 | 
						|
  }
 | 
						|
 | 
						|
  return resultList;
 | 
						|
}
 | 
						|
 | 
						|
static UniquePtr<nsCSSValueList>
 | 
						|
AddWeightedFilterFunction(double aCoeff1, const nsCSSValueList* aList1,
 | 
						|
                          double aCoeff2, const nsCSSValueList* aList2,
 | 
						|
                          ColorAdditionType aColorAdditionType)
 | 
						|
{
 | 
						|
  MOZ_ASSERT(aList1 || aList2,
 | 
						|
             "one function list item must not be null");
 | 
						|
  // Note that one of our arguments could be null, indicating that
 | 
						|
  // it's the initial value. Rather than adding special null-handling
 | 
						|
  // logic, we just check for null values and replace them with
 | 
						|
  // 0 * the other value. That way, AddWeightedFilterFunctionImpl can assume
 | 
						|
  // its args are non-null.
 | 
						|
  if (!aList1) {
 | 
						|
    return AddWeightedFilterFunctionImpl(aCoeff2, aList2, 0, aList2,
 | 
						|
                                         aColorAdditionType);
 | 
						|
  }
 | 
						|
  if (!aList2) {
 | 
						|
    return AddWeightedFilterFunctionImpl(aCoeff1, aList1, 0, aList1,
 | 
						|
                                         aColorAdditionType);
 | 
						|
  }
 | 
						|
 | 
						|
  return AddWeightedFilterFunctionImpl(aCoeff1, aList1, aCoeff2, aList2,
 | 
						|
                                       aColorAdditionType);
 | 
						|
}
 | 
						|
 | 
						|
static inline uint32_t
 | 
						|
ShapeArgumentCount(nsCSSKeyword aShapeFunction)
 | 
						|
{
 | 
						|
  switch (aShapeFunction) {
 | 
						|
    case eCSSKeyword_circle:
 | 
						|
      return 2; // radius and center point
 | 
						|
    case eCSSKeyword_polygon:
 | 
						|
      return 2; // fill rule and a list of points
 | 
						|
    case eCSSKeyword_ellipse:
 | 
						|
      return 3; // two radii and center point
 | 
						|
    case eCSSKeyword_inset:
 | 
						|
      return 5; // four edge offsets and a list of corner radii
 | 
						|
    default:
 | 
						|
      MOZ_ASSERT_UNREACHABLE("Unknown shape type");
 | 
						|
      return 0;
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
static void
 | 
						|
AddPositions(double aCoeff1, const nsCSSValue& aPos1,
 | 
						|
             double aCoeff2, const nsCSSValue& aPos2,
 | 
						|
             nsCSSValue& aResultPos)
 | 
						|
{
 | 
						|
  MOZ_ASSERT(aPos1.GetUnit() == eCSSUnit_Array &&
 | 
						|
             aPos2.GetUnit() == eCSSUnit_Array,
 | 
						|
             "Args should be CSS <position>s, encoded as arrays");
 | 
						|
 | 
						|
  const nsCSSValue::Array* posArray1 = aPos1.GetArrayValue();
 | 
						|
  const nsCSSValue::Array* posArray2 = aPos2.GetArrayValue();
 | 
						|
  MOZ_ASSERT(posArray1->Count() == 4 && posArray2->Count() == 4,
 | 
						|
             "CSSParserImpl::ParsePositionValue creates an array of length "
 | 
						|
             "4 - how did we get here?");
 | 
						|
 | 
						|
  nsCSSValue::Array* resultPosArray = nsCSSValue::Array::Create(4);
 | 
						|
  aResultPos.SetArrayValue(resultPosArray, eCSSUnit_Array);
 | 
						|
 | 
						|
  // Only iterate over elements 1 and 3.  The <position> is 'uncomputed' to
 | 
						|
  // only those elements.  See also the comment in SetPositionValue.
 | 
						|
  for (size_t i = 1; i < 4; i += 2) {
 | 
						|
    const nsCSSValue& v1 = posArray1->Item(i);
 | 
						|
    const nsCSSValue& v2 = posArray2->Item(i);
 | 
						|
    nsCSSValue& vr = resultPosArray->Item(i);
 | 
						|
    AddCSSValueCanonicalCalc(aCoeff1, v1,
 | 
						|
                             aCoeff2, v2, vr);
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
static Maybe<nsCSSValuePair>
 | 
						|
AddCSSValuePair(nsCSSPropertyID aProperty, uint32_t aRestrictions,
 | 
						|
                double aCoeff1, const nsCSSValuePair* aPair1,
 | 
						|
                double aCoeff2, const nsCSSValuePair* aPair2)
 | 
						|
{
 | 
						|
  MOZ_ASSERT(aPair1, "expected pair");
 | 
						|
  MOZ_ASSERT(aPair2, "expected pair");
 | 
						|
 | 
						|
  Maybe<nsCSSValuePair> result;
 | 
						|
  nsCSSUnit unit[2];
 | 
						|
  unit[0] = GetCommonUnit(aProperty, aPair1->mXValue.GetUnit(),
 | 
						|
                          aPair2->mXValue.GetUnit());
 | 
						|
  unit[1] = GetCommonUnit(aProperty, aPair1->mYValue.GetUnit(),
 | 
						|
                          aPair2->mYValue.GetUnit());
 | 
						|
  if (unit[0] == eCSSUnit_Null || unit[1] == eCSSUnit_Null ||
 | 
						|
      unit[0] == eCSSUnit_URL || unit[0] == eCSSUnit_Enumerated) {
 | 
						|
    return result; // Nothing() (returning |result| for RVO)
 | 
						|
  }
 | 
						|
 | 
						|
  result.emplace();
 | 
						|
 | 
						|
  static nsCSSValue nsCSSValuePair::* const pairValues[2] = {
 | 
						|
    &nsCSSValuePair::mXValue, &nsCSSValuePair::mYValue
 | 
						|
  };
 | 
						|
  for (uint32_t i = 0; i < 2; ++i) {
 | 
						|
    nsCSSValue nsCSSValuePair::*member = pairValues[i];
 | 
						|
    if (!AddCSSValuePixelPercentCalc(aRestrictions, unit[i],
 | 
						|
                                     aCoeff1, aPair1->*member,
 | 
						|
                                     aCoeff2, aPair2->*member,
 | 
						|
                                     result.ref().*member) ) {
 | 
						|
      MOZ_ASSERT(false, "unexpected unit");
 | 
						|
      result.reset();
 | 
						|
      return result; // Nothing() (returning |result| for RVO)
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  return result;
 | 
						|
}
 | 
						|
 | 
						|
static UniquePtr<nsCSSValuePairList>
 | 
						|
AddCSSValuePairList(nsCSSPropertyID aProperty,
 | 
						|
                    double aCoeff1, const nsCSSValuePairList* aList1,
 | 
						|
                    double aCoeff2, const nsCSSValuePairList* aList2)
 | 
						|
{
 | 
						|
  MOZ_ASSERT(aList1, "Can't add a null list");
 | 
						|
  MOZ_ASSERT(aList2, "Can't add a null list");
 | 
						|
 | 
						|
  auto result = MakeUnique<nsCSSValuePairList>();
 | 
						|
  nsCSSValuePairList* resultPtr = result.get();
 | 
						|
 | 
						|
  do {
 | 
						|
    static nsCSSValue nsCSSValuePairList::* const pairListValues[] = {
 | 
						|
      &nsCSSValuePairList::mXValue,
 | 
						|
      &nsCSSValuePairList::mYValue,
 | 
						|
    };
 | 
						|
    uint32_t restrictions = nsCSSProps::ValueRestrictions(aProperty);
 | 
						|
    for (uint32_t i = 0; i < ArrayLength(pairListValues); ++i) {
 | 
						|
      const nsCSSValue& v1 = aList1->*(pairListValues[i]);
 | 
						|
      const nsCSSValue& v2 = aList2->*(pairListValues[i]);
 | 
						|
 | 
						|
      nsCSSValue& vr = resultPtr->*(pairListValues[i]);
 | 
						|
      nsCSSUnit unit =
 | 
						|
        GetCommonUnit(aProperty, v1.GetUnit(), v2.GetUnit());
 | 
						|
      if (unit == eCSSUnit_Null) {
 | 
						|
        return nullptr;
 | 
						|
      }
 | 
						|
      if (unit == eCSSUnit_Number) {
 | 
						|
        AddCSSValueNumber(aCoeff1, v1,
 | 
						|
                          aCoeff2, v2,
 | 
						|
                          vr, restrictions);
 | 
						|
      } else if (!AddCSSValuePixelPercentCalc(restrictions, unit,
 | 
						|
                                              aCoeff1, v1,
 | 
						|
                                              aCoeff2, v2, vr)) {
 | 
						|
        if (v1 != v2) {
 | 
						|
          return nullptr;
 | 
						|
        }
 | 
						|
        vr = v1;
 | 
						|
      }
 | 
						|
    }
 | 
						|
    aList1 = aList1->mNext;
 | 
						|
    aList2 = aList2->mNext;
 | 
						|
    if (!aList1 || !aList2) {
 | 
						|
      break;
 | 
						|
    }
 | 
						|
    resultPtr->mNext = new nsCSSValuePairList;
 | 
						|
    resultPtr = resultPtr->mNext;
 | 
						|
  } while (aList1 && aList2);
 | 
						|
 | 
						|
  if (aList1 || aList2) {
 | 
						|
    return nullptr; // We can't interpolate lists of different lengths
 | 
						|
  }
 | 
						|
 | 
						|
  return result;
 | 
						|
}
 | 
						|
 | 
						|
static already_AddRefed<nsCSSValue::Array>
 | 
						|
AddShapeFunction(nsCSSPropertyID aProperty,
 | 
						|
                 double aCoeff1, const nsCSSValue::Array* aArray1,
 | 
						|
                 double aCoeff2, const nsCSSValue::Array* aArray2,
 | 
						|
                 Restrictions aRestriction)
 | 
						|
{
 | 
						|
  MOZ_ASSERT(aArray1 && aArray1->Count() == 2, "expected shape function");
 | 
						|
  MOZ_ASSERT(aArray2 && aArray2->Count() == 2, "expected shape function");
 | 
						|
  MOZ_ASSERT(aArray1->Item(0).GetUnit() == eCSSUnit_Function,
 | 
						|
             "expected function");
 | 
						|
  MOZ_ASSERT(aArray2->Item(0).GetUnit() == eCSSUnit_Function,
 | 
						|
             "expected function");
 | 
						|
  MOZ_ASSERT(aArray1->Item(1).GetUnit() == eCSSUnit_Enumerated,
 | 
						|
             "expected geometry-box");
 | 
						|
  MOZ_ASSERT(aArray2->Item(1).GetUnit() == eCSSUnit_Enumerated,
 | 
						|
             "expected geometry-box");
 | 
						|
 | 
						|
  if (aArray1->Item(1).GetIntValue() != aArray2->Item(1).GetIntValue()) {
 | 
						|
    return nullptr; // Both shapes must use the same reference box.
 | 
						|
  }
 | 
						|
 | 
						|
  const nsCSSValue::Array* func1 = aArray1->Item(0).GetArrayValue();
 | 
						|
  const nsCSSValue::Array* func2 = aArray2->Item(0).GetArrayValue();
 | 
						|
  nsCSSKeyword shapeFuncName = func1->Item(0).GetKeywordValue();
 | 
						|
  if (shapeFuncName != func2->Item(0).GetKeywordValue()) {
 | 
						|
    return nullptr; // Can't add two shapes of different types.
 | 
						|
  }
 | 
						|
 | 
						|
  RefPtr<nsCSSValue::Array> result = nsCSSValue::Array::Create(2);
 | 
						|
 | 
						|
  nsCSSValue::Array* resultFuncArgs =
 | 
						|
    result->Item(0).InitFunction(shapeFuncName,
 | 
						|
                                 ShapeArgumentCount(shapeFuncName));
 | 
						|
  switch (shapeFuncName) {
 | 
						|
    case eCSSKeyword_ellipse:
 | 
						|
      // Add ellipses' |ry| values (but fail if we encounter an enum):
 | 
						|
      if (!AddCSSValuePixelPercentCalc(aRestriction == Restrictions::Enable
 | 
						|
                                         ? CSS_PROPERTY_VALUE_NONNEGATIVE
 | 
						|
                                         : 0,
 | 
						|
                                       GetCommonUnit(aProperty,
 | 
						|
                                                     func1->Item(2).GetUnit(),
 | 
						|
                                                     func2->Item(2).GetUnit()),
 | 
						|
                                       aCoeff1, func1->Item(2),
 | 
						|
                                       aCoeff2, func2->Item(2),
 | 
						|
                                       resultFuncArgs->Item(2))) {
 | 
						|
        return nullptr;
 | 
						|
      }
 | 
						|
      MOZ_FALLTHROUGH;  // to handle rx and center point
 | 
						|
    case eCSSKeyword_circle: {
 | 
						|
      // Add circles' |r| (or ellipses' |rx|) values:
 | 
						|
      if (!AddCSSValuePixelPercentCalc(aRestriction == Restrictions::Enable
 | 
						|
                                         ? CSS_PROPERTY_VALUE_NONNEGATIVE
 | 
						|
                                         : 0,
 | 
						|
                                       GetCommonUnit(aProperty,
 | 
						|
                                                     func1->Item(1).GetUnit(),
 | 
						|
                                                     func2->Item(1).GetUnit()),
 | 
						|
                                       aCoeff1, func1->Item(1),
 | 
						|
                                       aCoeff2, func2->Item(1),
 | 
						|
                                       resultFuncArgs->Item(1))) {
 | 
						|
        return nullptr;
 | 
						|
      }
 | 
						|
      // Add center points (defined as a <position>).
 | 
						|
      size_t posIndex = shapeFuncName == eCSSKeyword_circle ? 2 : 3;
 | 
						|
      AddPositions(aCoeff1, func1->Item(posIndex),
 | 
						|
                   aCoeff2, func2->Item(posIndex),
 | 
						|
                   resultFuncArgs->Item(posIndex));
 | 
						|
      break;
 | 
						|
    }
 | 
						|
    case eCSSKeyword_polygon: {
 | 
						|
      // Add polygons' corresponding points (if the fill rule matches):
 | 
						|
      int32_t fillRule = func1->Item(1).GetIntValue();
 | 
						|
      if (fillRule != func2->Item(1).GetIntValue()) {
 | 
						|
        return nullptr; // can't interpolate between different fill rules
 | 
						|
      }
 | 
						|
      resultFuncArgs->Item(1).SetIntValue(fillRule, eCSSUnit_Enumerated);
 | 
						|
 | 
						|
      const nsCSSValuePairList* points1 = func1->Item(2).GetPairListValue();
 | 
						|
      const nsCSSValuePairList* points2 = func2->Item(2).GetPairListValue();
 | 
						|
      UniquePtr<nsCSSValuePairList> resultPoints =
 | 
						|
        AddCSSValuePairList(aProperty, aCoeff1, points1, aCoeff2, points2);
 | 
						|
      if (!resultPoints) {
 | 
						|
        return nullptr;
 | 
						|
      }
 | 
						|
      resultFuncArgs->Item(2).AdoptPairListValue(Move(resultPoints));
 | 
						|
      break;
 | 
						|
    }
 | 
						|
    case eCSSKeyword_inset: {
 | 
						|
      MOZ_ASSERT(func1->Count() == 6 && func2->Count() == 6,
 | 
						|
                 "Update for CSSParserImpl::ParseInsetFunction changes");
 | 
						|
      // Items 1-4 are respectively the top, right, bottom and left offsets
 | 
						|
      // from the reference box.
 | 
						|
      for (size_t i = 1; i <= 4; ++i) {
 | 
						|
        if (!AddCSSValuePixelPercentCalc(aRestriction == Restrictions::Enable
 | 
						|
                                           ? CSS_PROPERTY_VALUE_NONNEGATIVE
 | 
						|
                                           : 0,
 | 
						|
                                         GetCommonUnit(aProperty,
 | 
						|
                                                       func1->Item(i).GetUnit(),
 | 
						|
                                                       func2->Item(i).GetUnit()),
 | 
						|
                                         aCoeff1, func1->Item(i),
 | 
						|
                                         aCoeff2, func2->Item(i),
 | 
						|
                                         resultFuncArgs->Item(i))) {
 | 
						|
          return nullptr;
 | 
						|
        }
 | 
						|
      }
 | 
						|
      // Item 5 contains the radii of the rounded corners for the inset
 | 
						|
      // rectangle.
 | 
						|
      MOZ_ASSERT(func1->Item(5).GetUnit() == eCSSUnit_Array &&
 | 
						|
                 func2->Item(5).GetUnit() == eCSSUnit_Array,
 | 
						|
                 "Expected two arrays");
 | 
						|
      const nsCSSValue::Array* radii1 = func1->Item(5).GetArrayValue();
 | 
						|
      const nsCSSValue::Array* radii2 = func2->Item(5).GetArrayValue();
 | 
						|
      MOZ_ASSERT(radii1->Count() == 4 && radii2->Count() == 4);
 | 
						|
      nsCSSValue::Array* resultRadii = nsCSSValue::Array::Create(4);
 | 
						|
      resultFuncArgs->Item(5).SetArrayValue(resultRadii, eCSSUnit_Array);
 | 
						|
      // We use an arbitrary border-radius property here to get the appropriate
 | 
						|
      // restrictions for radii since this is a <border-radius> value.
 | 
						|
      uint32_t restrictions =
 | 
						|
        aRestriction == Restrictions::Enable
 | 
						|
          ? nsCSSProps::ValueRestrictions(eCSSProperty_border_top_left_radius)
 | 
						|
          : 0;
 | 
						|
      for (size_t i = 0; i < 4; ++i) {
 | 
						|
        const nsCSSValuePair& pair1 = radii1->Item(i).GetPairValue();
 | 
						|
        const nsCSSValuePair& pair2 = radii2->Item(i).GetPairValue();
 | 
						|
        const Maybe<nsCSSValuePair> pairResult =
 | 
						|
          AddCSSValuePair(aProperty, restrictions,
 | 
						|
                          aCoeff1, &pair1,
 | 
						|
                          aCoeff2, &pair2);
 | 
						|
        if (!pairResult) {
 | 
						|
          return nullptr;
 | 
						|
        }
 | 
						|
        resultRadii->Item(i).SetPairValue(pairResult.ptr());
 | 
						|
      }
 | 
						|
      break;
 | 
						|
    }
 | 
						|
    default:
 | 
						|
      MOZ_ASSERT_UNREACHABLE("Unknown shape type");
 | 
						|
      return nullptr;
 | 
						|
  }
 | 
						|
 | 
						|
  // set the geometry-box value
 | 
						|
  result->Item(1).SetIntValue(aArray1->Item(1).GetIntValue(),
 | 
						|
                              eCSSUnit_Enumerated);
 | 
						|
 | 
						|
  return result.forget();
 | 
						|
}
 | 
						|
 | 
						|
static nsCSSValueList*
 | 
						|
AddTransformLists(double aCoeff1, const nsCSSValueList* aList1,
 | 
						|
                  double aCoeff2, const nsCSSValueList* aList2,
 | 
						|
                  nsCSSKeyword aOperatorType)
 | 
						|
{
 | 
						|
  nsAutoPtr<nsCSSValueList> result;
 | 
						|
  nsCSSValueList **resultTail = getter_Transfers(result);
 | 
						|
 | 
						|
  do {
 | 
						|
    RefPtr<nsCSSValue::Array> a1 = ToPrimitive(aList1->mValue.GetArrayValue()),
 | 
						|
                                a2 = ToPrimitive(aList2->mValue.GetArrayValue());
 | 
						|
    MOZ_ASSERT(
 | 
						|
      TransformFunctionsMatch(nsStyleTransformMatrix::TransformFunctionOf(a1),
 | 
						|
                              nsStyleTransformMatrix::TransformFunctionOf(a2)),
 | 
						|
      "transform function mismatch");
 | 
						|
    MOZ_ASSERT(!*resultTail,
 | 
						|
               "resultTail isn't pointing to the tail (may leak)");
 | 
						|
 | 
						|
    nsCSSKeyword tfunc = nsStyleTransformMatrix::TransformFunctionOf(a1);
 | 
						|
    RefPtr<nsCSSValue::Array> arr;
 | 
						|
    if (tfunc != eCSSKeyword_matrix &&
 | 
						|
        tfunc != eCSSKeyword_matrix3d &&
 | 
						|
        tfunc != eCSSKeyword_interpolatematrix &&
 | 
						|
        tfunc != eCSSKeyword_rotate3d &&
 | 
						|
        tfunc != eCSSKeyword_perspective) {
 | 
						|
      arr = AnimationValue::AppendTransformFunction(tfunc, resultTail);
 | 
						|
    }
 | 
						|
 | 
						|
    switch (tfunc) {
 | 
						|
      case eCSSKeyword_translate3d: {
 | 
						|
          MOZ_ASSERT(a1->Count() == 4, "unexpected count");
 | 
						|
          MOZ_ASSERT(a2->Count() == 4, "unexpected count");
 | 
						|
          AddTransformTranslate(aCoeff1, a1->Item(1), aCoeff2, a2->Item(1),
 | 
						|
                                arr->Item(1));
 | 
						|
          AddTransformTranslate(aCoeff1, a1->Item(2), aCoeff2, a2->Item(2),
 | 
						|
                                arr->Item(2));
 | 
						|
          AddTransformTranslate(aCoeff1, a1->Item(3), aCoeff2, a2->Item(3),
 | 
						|
                                arr->Item(3));
 | 
						|
          break;
 | 
						|
      }
 | 
						|
      case eCSSKeyword_scale3d: {
 | 
						|
          MOZ_ASSERT(a1->Count() == 4, "unexpected count");
 | 
						|
          MOZ_ASSERT(a2->Count() == 4, "unexpected count");
 | 
						|
 | 
						|
          AddTransformScale(aCoeff1, a1->Item(1), aCoeff2, a2->Item(1),
 | 
						|
                            arr->Item(1));
 | 
						|
          AddTransformScale(aCoeff1, a1->Item(2), aCoeff2, a2->Item(2),
 | 
						|
                            arr->Item(2));
 | 
						|
          AddTransformScale(aCoeff1, a1->Item(3), aCoeff2, a2->Item(3),
 | 
						|
                            arr->Item(3));
 | 
						|
 | 
						|
          break;
 | 
						|
      }
 | 
						|
      // It would probably be nicer to animate skew in tangent space
 | 
						|
      // rather than angle space.  However, it's easy to specify
 | 
						|
      // skews with infinite tangents, and behavior changes pretty
 | 
						|
      // drastically when crossing such skews (since the direction of
 | 
						|
      // animation flips), so interop is probably more important here.
 | 
						|
      case eCSSKeyword_skew: {
 | 
						|
        MOZ_ASSERT(a1->Count() == 2 || a1->Count() == 3,
 | 
						|
                   "unexpected count");
 | 
						|
        MOZ_ASSERT(a2->Count() == 2 || a2->Count() == 3,
 | 
						|
                   "unexpected count");
 | 
						|
 | 
						|
        nsCSSValue zero(0.0f, eCSSUnit_Radian);
 | 
						|
        // Add Y component of skew.
 | 
						|
        AddCSSValueAngle(aCoeff1,
 | 
						|
                         a1->Count() == 3 ? a1->Item(2) : zero,
 | 
						|
                         aCoeff2,
 | 
						|
                         a2->Count() == 3 ? a2->Item(2) : zero,
 | 
						|
                         arr->Item(2));
 | 
						|
 | 
						|
        // Add X component of skew (which can be merged with case below
 | 
						|
        // in non-DEBUG).
 | 
						|
        AddCSSValueAngle(aCoeff1, a1->Item(1), aCoeff2, a2->Item(1),
 | 
						|
                         arr->Item(1));
 | 
						|
 | 
						|
        break;
 | 
						|
      }
 | 
						|
      case eCSSKeyword_skewx:
 | 
						|
      case eCSSKeyword_skewy:
 | 
						|
      case eCSSKeyword_rotate:
 | 
						|
      case eCSSKeyword_rotatex:
 | 
						|
      case eCSSKeyword_rotatey:
 | 
						|
      case eCSSKeyword_rotatez: {
 | 
						|
        MOZ_ASSERT(a1->Count() == 2, "unexpected count");
 | 
						|
        MOZ_ASSERT(a2->Count() == 2, "unexpected count");
 | 
						|
 | 
						|
        AddCSSValueAngle(aCoeff1, a1->Item(1), aCoeff2, a2->Item(1),
 | 
						|
                         arr->Item(1));
 | 
						|
 | 
						|
        break;
 | 
						|
      }
 | 
						|
      case eCSSKeyword_rotate3d: {
 | 
						|
        Point3D vector1(a1->Item(1).GetFloatValue(),
 | 
						|
                        a1->Item(2).GetFloatValue(),
 | 
						|
                        a1->Item(3).GetFloatValue());
 | 
						|
        vector1.Normalize();
 | 
						|
        Point3D vector2(a2->Item(1).GetFloatValue(),
 | 
						|
                        a2->Item(2).GetFloatValue(),
 | 
						|
                        a2->Item(3).GetFloatValue());
 | 
						|
        vector2.Normalize();
 | 
						|
 | 
						|
        // Handle rotate3d with matched (normalized) vectors,
 | 
						|
        // otherwise fallthrough to the next switch statement
 | 
						|
        // and do matrix decomposition.
 | 
						|
        if (vector1 == vector2) {
 | 
						|
          // We skipped appending a transform function above for rotate3d,
 | 
						|
          // so do it now.
 | 
						|
          arr = AnimationValue::AppendTransformFunction(tfunc, resultTail);
 | 
						|
          arr->Item(1).SetFloatValue(vector1.x, eCSSUnit_Number);
 | 
						|
          arr->Item(2).SetFloatValue(vector1.y, eCSSUnit_Number);
 | 
						|
          arr->Item(3).SetFloatValue(vector1.z, eCSSUnit_Number);
 | 
						|
 | 
						|
          AddCSSValueAngle(aCoeff1, a1->Item(4), aCoeff2, a2->Item(4),
 | 
						|
                           arr->Item(4));
 | 
						|
          break;
 | 
						|
        }
 | 
						|
        MOZ_FALLTHROUGH;
 | 
						|
      }
 | 
						|
      case eCSSKeyword_matrix:
 | 
						|
      case eCSSKeyword_matrix3d:
 | 
						|
      case eCSSKeyword_perspective:
 | 
						|
        if (aCoeff1 == 0.0 && aCoeff2 == 0.0) {
 | 
						|
          // Special case. If both coefficients are 0.0, we should apply an
 | 
						|
          // identity transform function.
 | 
						|
          arr = AnimationValue::AppendTransformFunction(tfunc, resultTail);
 | 
						|
 | 
						|
          if (tfunc == eCSSKeyword_rotate3d) {
 | 
						|
            arr->Item(1).SetFloatValue(0.0, eCSSUnit_Number);
 | 
						|
            arr->Item(2).SetFloatValue(0.0, eCSSUnit_Number);
 | 
						|
            arr->Item(3).SetFloatValue(1.0, eCSSUnit_Number);
 | 
						|
            arr->Item(4).SetFloatValue(0.0, eCSSUnit_Radian);
 | 
						|
          } else if (tfunc == eCSSKeyword_perspective) {
 | 
						|
            // The parameter of the identity perspective function is
 | 
						|
            // positive infinite.
 | 
						|
            arr->Item(1).SetFloatValue(std::numeric_limits<float>::infinity(),
 | 
						|
                                       eCSSUnit_Pixel);
 | 
						|
          } else {
 | 
						|
            nsStyleTransformMatrix::SetIdentityMatrix(arr);
 | 
						|
          }
 | 
						|
          break;
 | 
						|
        }
 | 
						|
        MOZ_FALLTHROUGH;
 | 
						|
      case eCSSKeyword_interpolatematrix: {
 | 
						|
        // FIXME: If the matrix contains only numbers then we could decompose
 | 
						|
        // here.
 | 
						|
 | 
						|
        // Construct temporary lists with only this item in them.
 | 
						|
        nsCSSValueList tempList1, tempList2;
 | 
						|
        tempList1.mValue = aList1->mValue;
 | 
						|
        tempList2.mValue = aList2->mValue;
 | 
						|
 | 
						|
        if (aList1 == aList2) {
 | 
						|
          *resultTail =
 | 
						|
            AddDifferentTransformLists(aCoeff1, &tempList1,
 | 
						|
                                       aCoeff2, &tempList1,
 | 
						|
                                       aOperatorType);
 | 
						|
        } else {
 | 
						|
          *resultTail =
 | 
						|
            AddDifferentTransformLists(aCoeff1, &tempList1,
 | 
						|
                                       aCoeff2, &tempList2,
 | 
						|
                                       aOperatorType);
 | 
						|
        }
 | 
						|
 | 
						|
        // Now advance resultTail to point to the new tail slot.
 | 
						|
        while (*resultTail) {
 | 
						|
          resultTail = &(*resultTail)->mNext;
 | 
						|
        }
 | 
						|
 | 
						|
        break;
 | 
						|
      }
 | 
						|
      default:
 | 
						|
        MOZ_ASSERT_UNREACHABLE(
 | 
						|
          "unknown transform function or accumulatematrix");
 | 
						|
    }
 | 
						|
 | 
						|
    aList1 = aList1->mNext;
 | 
						|
    aList2 = aList2->mNext;
 | 
						|
  } while (aList1);
 | 
						|
  MOZ_ASSERT(!aList2, "list length mismatch");
 | 
						|
  MOZ_ASSERT(!*resultTail,
 | 
						|
             "resultTail isn't pointing to the tail");
 | 
						|
 | 
						|
  return result.forget();
 | 
						|
}
 | 
						|
 | 
						|
static void
 | 
						|
AddPositionCoords(double aCoeff1, const nsCSSValue& aPos1,
 | 
						|
                  double aCoeff2, const nsCSSValue& aPos2,
 | 
						|
                  nsCSSValue& aResultPos)
 | 
						|
{
 | 
						|
  const nsCSSValue::Array* posArray1 = aPos1.GetArrayValue();
 | 
						|
  const nsCSSValue::Array* posArray2 = aPos2.GetArrayValue();
 | 
						|
  nsCSSValue::Array* resultPosArray = nsCSSValue::Array::Create(2);
 | 
						|
  aResultPos.SetArrayValue(resultPosArray, eCSSUnit_Array);
 | 
						|
 | 
						|
  /* Only compute element 1. The <position-coord> is
 | 
						|
   * 'uncomputed' to only that element.
 | 
						|
   */
 | 
						|
  const nsCSSValue& v1 = posArray1->Item(1);
 | 
						|
  const nsCSSValue& v2 = posArray2->Item(1);
 | 
						|
  nsCSSValue& vr = resultPosArray->Item(1);
 | 
						|
  AddCSSValueCanonicalCalc(aCoeff1, v1,
 | 
						|
                           aCoeff2, v2, vr);
 | 
						|
}
 | 
						|
 | 
						|
static UniquePtr<nsCSSValueList>
 | 
						|
AddWeightedShadowList(double aCoeff1,
 | 
						|
                      const nsCSSValueList* aShadow1,
 | 
						|
                      double aCoeff2,
 | 
						|
                      const nsCSSValueList* aShadow2,
 | 
						|
                      ColorAdditionType aColorAdditionType,
 | 
						|
                      nsCSSPropertyID aProperty)
 | 
						|
{
 | 
						|
  // This is implemented according to:
 | 
						|
  // http://dev.w3.org/csswg/css3-transitions/#animation-of-property-types-
 | 
						|
  // and the third item in the summary of:
 | 
						|
  // http://lists.w3.org/Archives/Public/www-style/2009Jul/0050.html
 | 
						|
  UniquePtr<nsCSSValueList> result;
 | 
						|
  nsCSSValueList* tail = nullptr;
 | 
						|
  while (aShadow1 && aShadow2) {
 | 
						|
    UniquePtr<nsCSSValueList> shadowValue =
 | 
						|
      AddWeightedShadowItems(aCoeff1, aShadow1->mValue,
 | 
						|
                             aCoeff2, aShadow2->mValue,
 | 
						|
                             aColorAdditionType,
 | 
						|
                             aProperty);
 | 
						|
    if (!shadowValue) {
 | 
						|
      return nullptr;
 | 
						|
    }
 | 
						|
    aShadow1 = aShadow1->mNext;
 | 
						|
    aShadow2 = aShadow2->mNext;
 | 
						|
    AppendToCSSValueList(result, Move(shadowValue), &tail);
 | 
						|
  }
 | 
						|
  if (aShadow1 || aShadow2) {
 | 
						|
    const nsCSSValueList *longShadow;
 | 
						|
    double longCoeff;
 | 
						|
    if (aShadow1) {
 | 
						|
      longShadow = aShadow1;
 | 
						|
      longCoeff = aCoeff1;
 | 
						|
    } else {
 | 
						|
      longShadow = aShadow2;
 | 
						|
      longCoeff = aCoeff2;
 | 
						|
    }
 | 
						|
 | 
						|
    while (longShadow) {
 | 
						|
      // Passing coefficients that add to less than 1 produces the
 | 
						|
      // desired result of interpolating "0 0 0 transparent" with
 | 
						|
      // the current shadow.
 | 
						|
      UniquePtr<nsCSSValueList> shadowValue =
 | 
						|
        AddWeightedShadowItems(longCoeff, longShadow->mValue,
 | 
						|
                               0.0, longShadow->mValue,
 | 
						|
                               aColorAdditionType, aProperty);
 | 
						|
      if (!shadowValue) {
 | 
						|
        return nullptr;
 | 
						|
      }
 | 
						|
 | 
						|
      longShadow = longShadow->mNext;
 | 
						|
      AppendToCSSValueList(result, Move(shadowValue), &tail);
 | 
						|
    }
 | 
						|
  }
 | 
						|
  return result;
 | 
						|
}
 | 
						|
 | 
						|
static UniquePtr<nsCSSValueList>
 | 
						|
AddWeightedFilterList(double aCoeff1, const nsCSSValueList* aList1,
 | 
						|
                      double aCoeff2, const nsCSSValueList* aList2,
 | 
						|
                      ColorAdditionType aColorAdditionType)
 | 
						|
{
 | 
						|
  UniquePtr<nsCSSValueList> result;
 | 
						|
  nsCSSValueList* tail = nullptr;
 | 
						|
  while (aList1 || aList2) {
 | 
						|
    if ((aList1 && aList1->mValue.GetUnit() != eCSSUnit_Function) ||
 | 
						|
        (aList2 && aList2->mValue.GetUnit() != eCSSUnit_Function)) {
 | 
						|
      // If we don't have filter-functions, we must have filter-URLs, which
 | 
						|
      // we can't add or interpolate.
 | 
						|
      return nullptr;
 | 
						|
    }
 | 
						|
 | 
						|
    UniquePtr<nsCSSValueList> resultFunction =
 | 
						|
      AddWeightedFilterFunction(aCoeff1, aList1, aCoeff2, aList2,
 | 
						|
                                aColorAdditionType);
 | 
						|
    if (!resultFunction) {
 | 
						|
      // filter function mismatch
 | 
						|
      return nullptr;
 | 
						|
    }
 | 
						|
 | 
						|
    AppendToCSSValueList(result, Move(resultFunction), &tail);
 | 
						|
 | 
						|
    // move to next aList items
 | 
						|
    if (aList1) {
 | 
						|
      aList1 = aList1->mNext;
 | 
						|
    }
 | 
						|
    if (aList2) {
 | 
						|
      aList2 = aList2->mNext;
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  return result;
 | 
						|
}
 | 
						|
 | 
						|
bool
 | 
						|
StyleAnimationValue::AddWeighted(nsCSSPropertyID aProperty,
 | 
						|
                                 double aCoeff1,
 | 
						|
                                 const StyleAnimationValue& aValue1,
 | 
						|
                                 double aCoeff2,
 | 
						|
                                 const StyleAnimationValue& aValue2,
 | 
						|
                                 StyleAnimationValue& aResultValue)
 | 
						|
{
 | 
						|
  Unit commonUnit =
 | 
						|
    GetCommonUnit(aProperty, aValue1.GetUnit(), aValue2.GetUnit());
 | 
						|
  // Maybe need a followup method to convert the inputs into the common
 | 
						|
  // unit-type, if they don't already match it. (Or would it make sense to do
 | 
						|
  // that in GetCommonUnit? in which case maybe ConvertToCommonUnit would be
 | 
						|
  // better.)
 | 
						|
 | 
						|
  switch (commonUnit) {
 | 
						|
    case eUnit_Null:
 | 
						|
    case eUnit_Auto:
 | 
						|
    case eUnit_None:
 | 
						|
    case eUnit_Normal:
 | 
						|
    case eUnit_UnparsedString:
 | 
						|
    case eUnit_URL:
 | 
						|
    case eUnit_DiscreteCSSValue:
 | 
						|
      return false;
 | 
						|
 | 
						|
    case eUnit_Enumerated:
 | 
						|
      switch (aProperty) {
 | 
						|
        case eCSSProperty_font_stretch: {
 | 
						|
          // https://drafts.csswg.org/css-fonts-3/#font-stretch-animation
 | 
						|
          double interpolatedValue = aCoeff1 * double(aValue1.GetIntValue()) +
 | 
						|
                                     aCoeff2 * double(aValue2.GetIntValue());
 | 
						|
          int32_t result = floor(interpolatedValue + 0.5);
 | 
						|
          if (result < NS_STYLE_FONT_STRETCH_ULTRA_CONDENSED) {
 | 
						|
            result = NS_STYLE_FONT_STRETCH_ULTRA_CONDENSED;
 | 
						|
          } else if (result > NS_STYLE_FONT_STRETCH_ULTRA_EXPANDED) {
 | 
						|
            result = NS_STYLE_FONT_STRETCH_ULTRA_EXPANDED;
 | 
						|
          }
 | 
						|
          aResultValue.SetIntValue(result, eUnit_Enumerated);
 | 
						|
          return true;
 | 
						|
        }
 | 
						|
        default:
 | 
						|
          return false;
 | 
						|
      }
 | 
						|
    case eUnit_Visibility: {
 | 
						|
      int32_t enum1 = aValue1.GetIntValue();
 | 
						|
      int32_t enum2 = aValue2.GetIntValue();
 | 
						|
      if (enum1 == enum2) {
 | 
						|
        aResultValue.SetIntValue(enum1, eUnit_Visibility);
 | 
						|
        return true;
 | 
						|
      }
 | 
						|
      if ((enum1 == NS_STYLE_VISIBILITY_VISIBLE) ==
 | 
						|
          (enum2 == NS_STYLE_VISIBILITY_VISIBLE)) {
 | 
						|
        return false;
 | 
						|
      }
 | 
						|
      int32_t val1 = enum1 == NS_STYLE_VISIBILITY_VISIBLE;
 | 
						|
      int32_t val2 = enum2 == NS_STYLE_VISIBILITY_VISIBLE;
 | 
						|
      double interp = aCoeff1 * val1 + aCoeff2 * val2;
 | 
						|
      int32_t result = interp > 0.0 ? NS_STYLE_VISIBILITY_VISIBLE
 | 
						|
                                    : (val1 ? enum2 : enum1);
 | 
						|
      aResultValue.SetIntValue(result, eUnit_Visibility);
 | 
						|
      return true;
 | 
						|
    }
 | 
						|
    case eUnit_Integer: {
 | 
						|
      // https://drafts.csswg.org/css-transitions/#animtype-integer
 | 
						|
      double interpolatedValue = aCoeff1 * double(aValue1.GetIntValue()) +
 | 
						|
                                 aCoeff2 * double(aValue2.GetIntValue());
 | 
						|
      int32_t result = floor(interpolatedValue + 0.5);
 | 
						|
      if (aProperty == eCSSProperty_font_weight) {
 | 
						|
        // https://drafts.csswg.org/css-transitions/#animtype-font-weight
 | 
						|
        result += 50;
 | 
						|
        result -= result % 100;
 | 
						|
        result = Clamp(result, 100, 900);
 | 
						|
      } else {
 | 
						|
        result = RestrictValue(aProperty, result);
 | 
						|
      }
 | 
						|
      aResultValue.SetIntValue(result, eUnit_Integer);
 | 
						|
      return true;
 | 
						|
    }
 | 
						|
    case eUnit_Coord: {
 | 
						|
      aResultValue.SetCoordValue(RestrictValue(aProperty, NSToCoordRound(
 | 
						|
        aCoeff1 * aValue1.GetCoordValue() +
 | 
						|
        aCoeff2 * aValue2.GetCoordValue())));
 | 
						|
      return true;
 | 
						|
    }
 | 
						|
    case eUnit_Percent: {
 | 
						|
      aResultValue.SetPercentValue(RestrictValue(aProperty,
 | 
						|
        aCoeff1 * aValue1.GetPercentValue() +
 | 
						|
        aCoeff2 * aValue2.GetPercentValue()));
 | 
						|
      return true;
 | 
						|
    }
 | 
						|
    case eUnit_Float: {
 | 
						|
      aResultValue.SetFloatValue(RestrictValue(aProperty,
 | 
						|
        aCoeff1 * aValue1.GetFloatValue() +
 | 
						|
        aCoeff2 * aValue2.GetFloatValue()));
 | 
						|
      return true;
 | 
						|
    }
 | 
						|
    case eUnit_Color: {
 | 
						|
      RGBAColorData color1 = ExtractColor(aValue1);
 | 
						|
      RGBAColorData color2 = ExtractColor(aValue2);
 | 
						|
      auto resultColor = MakeUnique<nsCSSValue>();
 | 
						|
      resultColor->SetColorValue(
 | 
						|
        AddWeightedColorsAndClamp(aCoeff1, color1, aCoeff2, color2));
 | 
						|
      aResultValue.SetAndAdoptCSSValueValue(resultColor.release(), eUnit_Color);
 | 
						|
      return true;
 | 
						|
    }
 | 
						|
    case eUnit_CurrentColor: {
 | 
						|
      aResultValue.SetCurrentColorValue();
 | 
						|
      return true;
 | 
						|
    }
 | 
						|
    case eUnit_ComplexColor: {
 | 
						|
      ComplexColorData color1 = ExtractComplexColor(aValue1);
 | 
						|
      ComplexColorData color2 = ExtractComplexColor(aValue2);
 | 
						|
      RefPtr<ComplexColorValue> result = new ComplexColorValue;
 | 
						|
      // Common case is interpolating between a color and a currentcolor.
 | 
						|
      if (color1.IsNumericColor() && color2.IsCurrentColor()) {
 | 
						|
        result->mColor = color1.mColor;
 | 
						|
        result->mForegroundRatio = aCoeff2;
 | 
						|
      } else if (color1.IsCurrentColor() && color2.IsNumericColor()) {
 | 
						|
        result->mColor = color2.mColor;
 | 
						|
        result->mForegroundRatio = aCoeff1;
 | 
						|
      } else {
 | 
						|
        float ratio1 = 1.0f - color1.mForegroundRatio;
 | 
						|
        float ratio2 = 1.0f - color2.mForegroundRatio;
 | 
						|
        float alpha1 = color1.mColor.mA * ratio1;
 | 
						|
        float alpha2 = color2.mColor.mA * ratio2;
 | 
						|
        RGBAColorData resultColor =
 | 
						|
          AddWeightedColors(aCoeff1, color1.mColor.WithAlpha(alpha1),
 | 
						|
                            aCoeff2, color2.mColor.WithAlpha(alpha2));
 | 
						|
        float resultRatio = color1.mForegroundRatio * aCoeff1 +
 | 
						|
                            color2.mForegroundRatio * aCoeff2;
 | 
						|
        float resultAlpha = resultColor.mA / (1.0f - resultRatio);
 | 
						|
        result->mColor = resultColor.WithAlpha(resultAlpha);
 | 
						|
        result->mForegroundRatio = resultRatio;
 | 
						|
      }
 | 
						|
      aResultValue.SetComplexColorValue(result.forget());
 | 
						|
      return true;
 | 
						|
    }
 | 
						|
    case eUnit_Calc: {
 | 
						|
      PixelCalcValue v1 = ExtractCalcValue(aValue1);
 | 
						|
      PixelCalcValue v2 = ExtractCalcValue(aValue2);
 | 
						|
      double len = aCoeff1 * v1.mLength + aCoeff2 * v2.mLength;
 | 
						|
      double pct = aCoeff1 * v1.mPercent + aCoeff2 * v2.mPercent;
 | 
						|
      bool hasPct = (aCoeff1 != 0.0 && v1.mHasPercent) ||
 | 
						|
                      (aCoeff2 != 0.0 && v2.mHasPercent);
 | 
						|
      nsCSSValue *val = new nsCSSValue();
 | 
						|
      nsCSSValue::Array *arr = nsCSSValue::Array::Create(1);
 | 
						|
      val->SetArrayValue(arr, eCSSUnit_Calc);
 | 
						|
      if (hasPct) {
 | 
						|
        nsCSSValue::Array *arr2 = nsCSSValue::Array::Create(2);
 | 
						|
        arr2->Item(0).SetFloatValue(len, eCSSUnit_Pixel);
 | 
						|
        arr2->Item(1).SetPercentValue(pct);
 | 
						|
        arr->Item(0).SetArrayValue(arr2, eCSSUnit_Calc_Plus);
 | 
						|
      } else {
 | 
						|
        arr->Item(0).SetFloatValue(len, eCSSUnit_Pixel);
 | 
						|
      }
 | 
						|
      aResultValue.SetAndAdoptCSSValueValue(val, eUnit_Calc);
 | 
						|
      return true;
 | 
						|
    }
 | 
						|
    case eUnit_ObjectPosition: {
 | 
						|
      const nsCSSValue* position1 = aValue1.GetCSSValueValue();
 | 
						|
      const nsCSSValue* position2 = aValue2.GetCSSValueValue();
 | 
						|
 | 
						|
      nsAutoPtr<nsCSSValue> result(new nsCSSValue);
 | 
						|
      AddPositions(aCoeff1, *position1,
 | 
						|
                   aCoeff2, *position2, *result);
 | 
						|
 | 
						|
      aResultValue.SetAndAdoptCSSValueValue(result.forget(),
 | 
						|
                                            eUnit_ObjectPosition);
 | 
						|
      return true;
 | 
						|
    }
 | 
						|
    case eUnit_CSSValuePair: {
 | 
						|
      uint32_t restrictions = nsCSSProps::ValueRestrictions(aProperty);
 | 
						|
      Maybe<nsCSSValuePair> result =
 | 
						|
        AddCSSValuePair(aProperty, restrictions,
 | 
						|
                        aCoeff1, aValue1.GetCSSValuePairValue(),
 | 
						|
                        aCoeff2, aValue2.GetCSSValuePairValue());
 | 
						|
      if (!result) {
 | 
						|
        return false;
 | 
						|
      }
 | 
						|
 | 
						|
      // We need a heap allocated object to adopt here:
 | 
						|
      auto heapResult = MakeUnique<nsCSSValuePair>(*result);
 | 
						|
      aResultValue.SetAndAdoptCSSValuePairValue(heapResult.release(),
 | 
						|
                                                eUnit_CSSValuePair);
 | 
						|
      return true;
 | 
						|
    }
 | 
						|
    case eUnit_CSSValueTriplet: {
 | 
						|
      nsCSSValueTriplet triplet1(*aValue1.GetCSSValueTripletValue());
 | 
						|
      nsCSSValueTriplet triplet2(*aValue2.GetCSSValueTripletValue());
 | 
						|
 | 
						|
      nsCSSUnit unit[3];
 | 
						|
      unit[0] = GetCommonUnit(aProperty, triplet1.mXValue.GetUnit(),
 | 
						|
                              triplet2.mXValue.GetUnit());
 | 
						|
      unit[1] = GetCommonUnit(aProperty, triplet1.mYValue.GetUnit(),
 | 
						|
                               triplet2.mYValue.GetUnit());
 | 
						|
      unit[2] = GetCommonUnit(aProperty, triplet1.mZValue.GetUnit(),
 | 
						|
                              triplet2.mZValue.GetUnit());
 | 
						|
      if (unit[0] == eCSSUnit_Null || unit[1] == eCSSUnit_Null ||
 | 
						|
          unit[2] == eCSSUnit_Null) {
 | 
						|
        return false;
 | 
						|
      }
 | 
						|
 | 
						|
      nsAutoPtr<nsCSSValueTriplet> result(new nsCSSValueTriplet);
 | 
						|
      static nsCSSValue nsCSSValueTriplet::* const tripletValues[3] = {
 | 
						|
        &nsCSSValueTriplet::mXValue, &nsCSSValueTriplet::mYValue, &nsCSSValueTriplet::mZValue
 | 
						|
      };
 | 
						|
      uint32_t restrictions = nsCSSProps::ValueRestrictions(aProperty);
 | 
						|
      for (uint32_t i = 0; i < 3; ++i) {
 | 
						|
        nsCSSValue nsCSSValueTriplet::*member = tripletValues[i];
 | 
						|
        if (!AddCSSValuePixelPercentCalc(restrictions, unit[i],
 | 
						|
                                         aCoeff1, &triplet1->*member,
 | 
						|
                                         aCoeff2, &triplet2->*member,
 | 
						|
                                         result->*member) ) {
 | 
						|
          MOZ_ASSERT(false, "unexpected unit");
 | 
						|
          return false;
 | 
						|
        }
 | 
						|
      }
 | 
						|
 | 
						|
      aResultValue.SetAndAdoptCSSValueTripletValue(result.forget(),
 | 
						|
                                                   eUnit_CSSValueTriplet);
 | 
						|
      return true;
 | 
						|
    }
 | 
						|
    case eUnit_CSSRect: {
 | 
						|
      MOZ_ASSERT(nsCSSProps::ValueRestrictions(aProperty) == 0,
 | 
						|
                 "must add code for handling value restrictions");
 | 
						|
      const nsCSSRect *rect1 = aValue1.GetCSSRectValue();
 | 
						|
      const nsCSSRect *rect2 = aValue2.GetCSSRectValue();
 | 
						|
      if (rect1->mTop.GetUnit() != rect2->mTop.GetUnit() ||
 | 
						|
          rect1->mRight.GetUnit() != rect2->mRight.GetUnit() ||
 | 
						|
          rect1->mBottom.GetUnit() != rect2->mBottom.GetUnit() ||
 | 
						|
          rect1->mLeft.GetUnit() != rect2->mLeft.GetUnit()) {
 | 
						|
        // At least until we have calc()
 | 
						|
        return false;
 | 
						|
      }
 | 
						|
 | 
						|
      nsAutoPtr<nsCSSRect> result(new nsCSSRect);
 | 
						|
      for (uint32_t i = 0; i < ArrayLength(nsCSSRect::sides); ++i) {
 | 
						|
        nsCSSValue nsCSSRect::*member = nsCSSRect::sides[i];
 | 
						|
        MOZ_ASSERT((rect1->*member).GetUnit() == (rect2->*member).GetUnit(),
 | 
						|
                   "should have returned above");
 | 
						|
        switch ((rect1->*member).GetUnit()) {
 | 
						|
          case eCSSUnit_Pixel:
 | 
						|
            AddCSSValuePixel(aCoeff1, rect1->*member, aCoeff2, rect2->*member,
 | 
						|
                             result->*member);
 | 
						|
            break;
 | 
						|
          case eCSSUnit_Auto:
 | 
						|
            if (float(aCoeff1 + aCoeff2) != 1.0f) {
 | 
						|
              // Interpolating between two auto values makes sense;
 | 
						|
              // adding in other ratios does not.
 | 
						|
              return false;
 | 
						|
            }
 | 
						|
            (result->*member).SetAutoValue();
 | 
						|
            break;
 | 
						|
          default:
 | 
						|
            MOZ_ASSERT(false, "unexpected unit");
 | 
						|
            return false;
 | 
						|
        }
 | 
						|
      }
 | 
						|
 | 
						|
      aResultValue.SetAndAdoptCSSRectValue(result.forget(), eUnit_CSSRect);
 | 
						|
      return true;
 | 
						|
    }
 | 
						|
    case eUnit_Dasharray: {
 | 
						|
      const nsCSSValueList *list1 = aValue1.GetCSSValueListValue();
 | 
						|
      const nsCSSValueList *list2 = aValue2.GetCSSValueListValue();
 | 
						|
 | 
						|
      uint32_t len1 = 0, len2 = 0;
 | 
						|
      for (const nsCSSValueList *v = list1; v; v = v->mNext) {
 | 
						|
        ++len1;
 | 
						|
      }
 | 
						|
      for (const nsCSSValueList *v = list2; v; v = v->mNext) {
 | 
						|
        ++len2;
 | 
						|
      }
 | 
						|
      MOZ_ASSERT(len1 > 0 && len2 > 0, "unexpected length");
 | 
						|
 | 
						|
      nsAutoPtr<nsCSSValueList> result;
 | 
						|
      nsCSSValueList **resultTail = getter_Transfers(result);
 | 
						|
      for (uint32_t i = 0, i_end = EuclidLCM<uint32_t>(len1, len2); i != i_end; ++i) {
 | 
						|
        const nsCSSValue &v1 = list1->mValue;
 | 
						|
        const nsCSSValue &v2 = list2->mValue;
 | 
						|
        MOZ_ASSERT(v1.GetUnit() == eCSSUnit_Number ||
 | 
						|
                   v1.GetUnit() == eCSSUnit_Percent, "unexpected");
 | 
						|
        MOZ_ASSERT(v2.GetUnit() == eCSSUnit_Number ||
 | 
						|
                   v2.GetUnit() == eCSSUnit_Percent, "unexpected");
 | 
						|
        if (v1.GetUnit() != v2.GetUnit()) {
 | 
						|
          // Can't animate between lengths and percentages (until calc()).
 | 
						|
          return false;
 | 
						|
        }
 | 
						|
 | 
						|
        nsCSSValueList *item = new nsCSSValueList;
 | 
						|
        *resultTail = item;
 | 
						|
        resultTail = &item->mNext;
 | 
						|
 | 
						|
        if (v1.GetUnit() == eCSSUnit_Number) {
 | 
						|
          AddCSSValueNumber(aCoeff1, v1, aCoeff2, v2, item->mValue,
 | 
						|
                            CSS_PROPERTY_VALUE_NONNEGATIVE);
 | 
						|
        } else {
 | 
						|
          AddCSSValuePercent(aCoeff1, v1, aCoeff2, v2, item->mValue,
 | 
						|
                             CSS_PROPERTY_VALUE_NONNEGATIVE);
 | 
						|
        }
 | 
						|
 | 
						|
        list1 = list1->mNext;
 | 
						|
        if (!list1) {
 | 
						|
          list1 = aValue1.GetCSSValueListValue();
 | 
						|
        }
 | 
						|
        list2 = list2->mNext;
 | 
						|
        if (!list2) {
 | 
						|
          list2 = aValue2.GetCSSValueListValue();
 | 
						|
        }
 | 
						|
      }
 | 
						|
 | 
						|
      aResultValue.SetAndAdoptCSSValueListValue(result.forget(),
 | 
						|
                                                eUnit_Dasharray);
 | 
						|
      return true;
 | 
						|
    }
 | 
						|
    case eUnit_Shadow: {
 | 
						|
      UniquePtr<nsCSSValueList> result =
 | 
						|
        AddWeightedShadowList(aCoeff1,
 | 
						|
                              aValue1.GetCSSValueListValue(),
 | 
						|
                              aCoeff2,
 | 
						|
                              aValue2.GetCSSValueListValue(),
 | 
						|
                              ColorAdditionType::Clamped,
 | 
						|
                              aProperty);
 | 
						|
      if (!result) {
 | 
						|
        return false;
 | 
						|
      }
 | 
						|
      aResultValue.SetAndAdoptCSSValueListValue(result.release(), eUnit_Shadow);
 | 
						|
      return true;
 | 
						|
    }
 | 
						|
    case eUnit_Shape: {
 | 
						|
      RefPtr<nsCSSValue::Array> result =
 | 
						|
        AddShapeFunction(aProperty,
 | 
						|
                         aCoeff1, aValue1.GetCSSValueArrayValue(),
 | 
						|
                         aCoeff2, aValue2.GetCSSValueArrayValue());
 | 
						|
      if (!result) {
 | 
						|
        return false;
 | 
						|
      }
 | 
						|
      aResultValue.SetCSSValueArrayValue(result, eUnit_Shape);
 | 
						|
      return true;
 | 
						|
    }
 | 
						|
    case eUnit_Filter: {
 | 
						|
      UniquePtr<nsCSSValueList> result =
 | 
						|
        AddWeightedFilterList(aCoeff1, aValue1.GetCSSValueListValue(),
 | 
						|
                              aCoeff2, aValue2.GetCSSValueListValue(),
 | 
						|
                              ColorAdditionType::Clamped);
 | 
						|
      if (!result) {
 | 
						|
        return false;
 | 
						|
      }
 | 
						|
 | 
						|
      aResultValue.SetAndAdoptCSSValueListValue(result.release(),
 | 
						|
                                                eUnit_Filter);
 | 
						|
      return true;
 | 
						|
    }
 | 
						|
 | 
						|
    case eUnit_Transform: {
 | 
						|
      const nsCSSValueList* list1 = aValue1.GetCSSValueSharedListValue()->mHead;
 | 
						|
      const nsCSSValueList* list2 = aValue2.GetCSSValueSharedListValue()->mHead;
 | 
						|
 | 
						|
      MOZ_ASSERT(list1);
 | 
						|
      MOZ_ASSERT(list2);
 | 
						|
 | 
						|
      // We want to avoid the matrix decomposition when we can, since
 | 
						|
      // avoiding it can produce better results both for compound
 | 
						|
      // transforms and for skew and skewY (see below).  We can do this
 | 
						|
      // in two cases:
 | 
						|
      //   (1) if one of the transforms is 'none'
 | 
						|
      //   (2) if the lists have the same length and the transform
 | 
						|
      //       functions match
 | 
						|
      nsAutoPtr<nsCSSValueList> result;
 | 
						|
      if (list1->mValue.GetUnit() == eCSSUnit_None) {
 | 
						|
        if (list2->mValue.GetUnit() == eCSSUnit_None) {
 | 
						|
          result = new nsCSSValueList;
 | 
						|
          if (result) {
 | 
						|
            result->mValue.SetNoneValue();
 | 
						|
          }
 | 
						|
        } else if (HasAccumulateMatrix(list2)) {
 | 
						|
          result = AddDifferentTransformLists(0, list2, aCoeff2, list2,
 | 
						|
                                              eCSSKeyword_interpolatematrix);
 | 
						|
        } else {
 | 
						|
          result = AddTransformLists(0, list2, aCoeff2, list2);
 | 
						|
        }
 | 
						|
      } else {
 | 
						|
        if (list2->mValue.GetUnit() == eCSSUnit_None) {
 | 
						|
          if (HasAccumulateMatrix(list1)) {
 | 
						|
            result = AddDifferentTransformLists(0, list1,
 | 
						|
                                                aCoeff1, list1,
 | 
						|
                                                eCSSKeyword_interpolatematrix);
 | 
						|
          } else {
 | 
						|
            result = AddTransformLists(0, list1, aCoeff1, list1);
 | 
						|
          }
 | 
						|
        } else if (TransformFunctionListsMatch(list1, list2)) {
 | 
						|
          result = AddTransformLists(aCoeff1, list1, aCoeff2, list2,
 | 
						|
                                     eCSSKeyword_interpolatematrix);
 | 
						|
        } else {
 | 
						|
          result = AddDifferentTransformLists(aCoeff1, list1,
 | 
						|
                                              aCoeff2, list2,
 | 
						|
                                              eCSSKeyword_interpolatematrix);
 | 
						|
        }
 | 
						|
      }
 | 
						|
 | 
						|
      aResultValue.SetTransformValue(new nsCSSValueSharedList(result.forget()));
 | 
						|
      return true;
 | 
						|
    }
 | 
						|
    case eUnit_BackgroundPositionCoord: {
 | 
						|
      const nsCSSValueList *position1 = aValue1.GetCSSValueListValue();
 | 
						|
      const nsCSSValueList *position2 = aValue2.GetCSSValueListValue();
 | 
						|
      nsAutoPtr<nsCSSValueList> result;
 | 
						|
      nsCSSValueList **resultTail = getter_Transfers(result);
 | 
						|
      while (position1 && position2) {
 | 
						|
        nsCSSValueList *item = new nsCSSValueList;
 | 
						|
        *resultTail = item;
 | 
						|
        resultTail = &item->mNext;
 | 
						|
 | 
						|
        AddPositionCoords(aCoeff1, position1->mValue,
 | 
						|
                          aCoeff2, position2->mValue, item->mValue);
 | 
						|
 | 
						|
        position1 = position1->mNext;
 | 
						|
        position2 = position2->mNext;
 | 
						|
      }
 | 
						|
 | 
						|
      // Check for different lengths
 | 
						|
      if (position1 || position2) {
 | 
						|
        return false;
 | 
						|
      }
 | 
						|
 | 
						|
      aResultValue.SetAndAdoptCSSValueListValue(result.forget(),
 | 
						|
                                                eUnit_BackgroundPositionCoord);
 | 
						|
      return true;
 | 
						|
    }
 | 
						|
    case eUnit_CSSValuePairList: {
 | 
						|
      const nsCSSValuePairList *list1 = aValue1.GetCSSValuePairListValue();
 | 
						|
      const nsCSSValuePairList *list2 = aValue2.GetCSSValuePairListValue();
 | 
						|
      UniquePtr<nsCSSValuePairList> result =
 | 
						|
        AddCSSValuePairList(aProperty, aCoeff1, list1, aCoeff2, list2);
 | 
						|
      if (!result) {
 | 
						|
        return false;
 | 
						|
      }
 | 
						|
      aResultValue.SetAndAdoptCSSValuePairListValue(result.release());
 | 
						|
      return true;
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  MOZ_ASSERT(false, "Can't interpolate using the given common unit");
 | 
						|
  return false;
 | 
						|
}
 | 
						|
 | 
						|
StyleAnimationValue
 | 
						|
StyleAnimationValue::Accumulate(nsCSSPropertyID aProperty,
 | 
						|
                                const StyleAnimationValue& aA,
 | 
						|
                                StyleAnimationValue&& aB,
 | 
						|
                                uint64_t aCount)
 | 
						|
{
 | 
						|
  StyleAnimationValue result(Move(aB));
 | 
						|
 | 
						|
  if (aCount == 0) {
 | 
						|
    return result;
 | 
						|
  }
 | 
						|
 | 
						|
  Unit commonUnit =
 | 
						|
    GetCommonUnit(aProperty, result.GetUnit(), aA.GetUnit());
 | 
						|
  switch (commonUnit) {
 | 
						|
    case eUnit_Filter: {
 | 
						|
      UniquePtr<nsCSSValueList> resultList =
 | 
						|
        AddWeightedFilterList(1.0, result.GetCSSValueListValue(),
 | 
						|
                              aCount, aA.GetCSSValueListValue(),
 | 
						|
                              ColorAdditionType::Unclamped);
 | 
						|
      if (resultList) {
 | 
						|
        result.SetAndAdoptCSSValueListValue(resultList.release(), eUnit_Filter);
 | 
						|
      }
 | 
						|
      break;
 | 
						|
    }
 | 
						|
    case eUnit_Shadow: {
 | 
						|
      UniquePtr<nsCSSValueList> resultList =
 | 
						|
        AddWeightedShadowList(1.0, result.GetCSSValueListValue(),
 | 
						|
                              aCount, aA.GetCSSValueListValue(),
 | 
						|
                              ColorAdditionType::Unclamped,
 | 
						|
                              aProperty);
 | 
						|
      if (resultList) {
 | 
						|
        result.SetAndAdoptCSSValueListValue(resultList.release(), eUnit_Shadow);
 | 
						|
      }
 | 
						|
      break;
 | 
						|
    }
 | 
						|
    case eUnit_Color: {
 | 
						|
      RGBAColorData color1 = ExtractColor(result);
 | 
						|
      RGBAColorData color2 = ExtractColor(aA);
 | 
						|
      result.mValue.mCSSValue->SetRGBAColorValue(
 | 
						|
        AddWeightedColors(1.0, color1, aCount, color2));
 | 
						|
      break;
 | 
						|
    }
 | 
						|
    case eUnit_Transform: {
 | 
						|
      const nsCSSValueList* listA =
 | 
						|
        aA.GetCSSValueSharedListValue()->mHead;
 | 
						|
      const nsCSSValueList* listB =
 | 
						|
        result.GetCSSValueSharedListValue()->mHead;
 | 
						|
 | 
						|
      MOZ_ASSERT(listA);
 | 
						|
      MOZ_ASSERT(listB);
 | 
						|
 | 
						|
      nsAutoPtr<nsCSSValueList> resultList;
 | 
						|
      if (listA->mValue.GetUnit() == eCSSUnit_None) {
 | 
						|
        // If |aA| is 'none' then we are calculating:
 | 
						|
        //
 | 
						|
        //    none * |aCount| + |aB|
 | 
						|
        //    = none + |aB|
 | 
						|
        //    = |aB|
 | 
						|
        //
 | 
						|
        // Hence the result should just be |aB|, even if |aB| is also 'none'.
 | 
						|
        // Since |result| is already initialized to |aB|, we just return that.
 | 
						|
        break;
 | 
						|
      } else if (listB->mValue.GetUnit() == eCSSUnit_None) {
 | 
						|
        resultList = AddTransformLists(0.0, listA, aCount, listA,
 | 
						|
                                       eCSSKeyword_accumulatematrix);
 | 
						|
      } else if (TransformFunctionListsMatch(listA, listB)) {
 | 
						|
        resultList = AddTransformLists(1.0, listB, aCount, listA,
 | 
						|
                                       eCSSKeyword_accumulatematrix);
 | 
						|
      } else {
 | 
						|
        resultList = AddDifferentTransformLists(1.0, listB,
 | 
						|
                                                aCount, listA,
 | 
						|
                                                eCSSKeyword_accumulatematrix);
 | 
						|
      }
 | 
						|
      result.SetTransformValue(new nsCSSValueSharedList(resultList.forget()));
 | 
						|
      break;
 | 
						|
    }
 | 
						|
    default:
 | 
						|
      Unused << AddWeighted(aProperty,
 | 
						|
                            1.0, result,
 | 
						|
                            aCount, aA,
 | 
						|
                            result);
 | 
						|
      break;
 | 
						|
  }
 | 
						|
  return result;
 | 
						|
}
 | 
						|
 | 
						|
static already_AddRefed<css::StyleRule>
 | 
						|
BuildStyleRule(nsCSSPropertyID aProperty,
 | 
						|
               dom::Element* aTargetElement,
 | 
						|
               const nsAString& aSpecifiedValue,
 | 
						|
               bool aUseSVGMode)
 | 
						|
{
 | 
						|
  // Set up an empty CSS Declaration
 | 
						|
  RefPtr<css::Declaration> declaration(new css::Declaration());
 | 
						|
  declaration->InitializeEmpty();
 | 
						|
 | 
						|
  bool changed; // ignored, but needed as outparam for ParseProperty
 | 
						|
  nsIDocument* doc = aTargetElement->OwnerDoc();
 | 
						|
  nsCOMPtr<nsIURI> baseURI = aTargetElement->GetBaseURI();
 | 
						|
  nsCSSParser parser(doc->CSSLoader());
 | 
						|
 | 
						|
  nsCSSPropertyID propertyToCheck = nsCSSProps::IsShorthand(aProperty) ?
 | 
						|
    nsCSSProps::SubpropertyEntryFor(aProperty)[0] : aProperty;
 | 
						|
 | 
						|
  // Get a parser, parse the property, and check for CSS parsing errors.
 | 
						|
  // If this fails, we bail out and delete the declaration.
 | 
						|
  parser.ParseProperty(aProperty, aSpecifiedValue, doc->GetDocumentURI(),
 | 
						|
                       baseURI, aTargetElement->NodePrincipal(), declaration,
 | 
						|
                       &changed, false, aUseSVGMode);
 | 
						|
 | 
						|
  // check whether property parsed without CSS parsing errors
 | 
						|
  if (!declaration->HasNonImportantValueFor(propertyToCheck)) {
 | 
						|
    return nullptr;
 | 
						|
  }
 | 
						|
 | 
						|
  RefPtr<css::StyleRule> rule = new css::StyleRule(nullptr,
 | 
						|
                                                     declaration,
 | 
						|
                                                     0, 0);
 | 
						|
  return rule.forget();
 | 
						|
}
 | 
						|
 | 
						|
static already_AddRefed<css::StyleRule>
 | 
						|
BuildStyleRule(nsCSSPropertyID aProperty,
 | 
						|
               dom::Element* aTargetElement,
 | 
						|
               const nsCSSValue& aSpecifiedValue,
 | 
						|
               bool aUseSVGMode)
 | 
						|
{
 | 
						|
  MOZ_ASSERT(!nsCSSProps::IsShorthand(aProperty),
 | 
						|
             "Should be a longhand property");
 | 
						|
 | 
						|
  // Check if longhand failed to parse correctly.
 | 
						|
  if (aSpecifiedValue.GetUnit() == eCSSUnit_Null) {
 | 
						|
    return nullptr;
 | 
						|
  }
 | 
						|
 | 
						|
  // Set up an empty CSS Declaration
 | 
						|
  RefPtr<css::Declaration> declaration(new css::Declaration());
 | 
						|
  declaration->InitializeEmpty();
 | 
						|
 | 
						|
  // Add our longhand value
 | 
						|
  nsCSSExpandedDataBlock block;
 | 
						|
  declaration->ExpandTo(&block);
 | 
						|
  block.AddLonghandProperty(aProperty, aSpecifiedValue);
 | 
						|
  declaration->ValueAppended(aProperty);
 | 
						|
  declaration->CompressFrom(&block);
 | 
						|
 | 
						|
  RefPtr<css::StyleRule> rule = new css::StyleRule(nullptr, declaration, 0, 0);
 | 
						|
  return rule.forget();
 | 
						|
}
 | 
						|
 | 
						|
static bool
 | 
						|
ComputeValuesFromStyleContext(
 | 
						|
  nsCSSPropertyID aProperty,
 | 
						|
  CSSEnabledState aEnabledState,
 | 
						|
  GeckoStyleContext* aStyleContext,
 | 
						|
  nsTArray<PropertyStyleAnimationValuePair>& aValues)
 | 
						|
{
 | 
						|
  // Extract computed value of our property (or all longhand components, if
 | 
						|
  // aProperty is a shorthand) from the temporary style context
 | 
						|
  if (nsCSSProps::IsShorthand(aProperty)) {
 | 
						|
    CSSPROPS_FOR_SHORTHAND_SUBPROPERTIES(p, aProperty, aEnabledState) {
 | 
						|
      if (nsCSSProps::kAnimTypeTable[*p] == eStyleAnimType_None) {
 | 
						|
        // Skip non-animatable component longhands.
 | 
						|
        continue;
 | 
						|
      }
 | 
						|
      PropertyStyleAnimationValuePair* pair = aValues.AppendElement();
 | 
						|
      pair->mProperty = *p;
 | 
						|
      if (!StyleAnimationValue::ExtractComputedValue(*p, aStyleContext,
 | 
						|
                                                     pair->mValue.mGecko)) {
 | 
						|
        return false;
 | 
						|
      }
 | 
						|
    }
 | 
						|
    return true;
 | 
						|
  }
 | 
						|
 | 
						|
  PropertyStyleAnimationValuePair* pair = aValues.AppendElement();
 | 
						|
  pair->mProperty = aProperty;
 | 
						|
  return StyleAnimationValue::ExtractComputedValue(aProperty, aStyleContext,
 | 
						|
                                                   pair->mValue.mGecko);
 | 
						|
}
 | 
						|
 | 
						|
static bool
 | 
						|
ComputeValuesFromStyleRule(nsCSSPropertyID aProperty,
 | 
						|
                           CSSEnabledState aEnabledState,
 | 
						|
                           GeckoStyleContext* aStyleContext,
 | 
						|
                           css::StyleRule* aStyleRule,
 | 
						|
                           nsTArray<PropertyStyleAnimationValuePair>& aValues,
 | 
						|
                           bool* aIsContextSensitive)
 | 
						|
{
 | 
						|
  MOZ_ASSERT(aStyleContext);
 | 
						|
  if (!nsCSSProps::IsEnabled(aProperty, aEnabledState)) {
 | 
						|
    return false;
 | 
						|
  }
 | 
						|
 | 
						|
  MOZ_ASSERT(aStyleContext->PresContext()->StyleSet()->IsGecko(),
 | 
						|
             "ServoStyleSet should not use StyleAnimationValue for animations");
 | 
						|
  nsStyleSet* styleSet = aStyleContext->PresContext()->StyleSet()->AsGecko();
 | 
						|
 | 
						|
  RefPtr<GeckoStyleContext> tmpStyleContext;
 | 
						|
  if (aIsContextSensitive) {
 | 
						|
    MOZ_ASSERT(!nsCSSProps::IsShorthand(aProperty),
 | 
						|
               "to correctly set aIsContextSensitive for shorthand properties, "
 | 
						|
               "this code must be adjusted");
 | 
						|
 | 
						|
    nsCOMArray<nsIStyleRule> ruleArray;
 | 
						|
    ruleArray.AppendObject(styleSet->InitialStyleRule());
 | 
						|
    css::Declaration* declaration = aStyleRule->GetDeclaration();
 | 
						|
    ruleArray.AppendObject(declaration);
 | 
						|
    declaration->SetImmutable();
 | 
						|
    tmpStyleContext =
 | 
						|
      styleSet->ResolveStyleByAddingRules(aStyleContext, ruleArray);
 | 
						|
    if (!tmpStyleContext) {
 | 
						|
      return false;
 | 
						|
    }
 | 
						|
 | 
						|
    // Force walk of rule tree
 | 
						|
    nsStyleStructID sid = nsCSSProps::kSIDTable[aProperty];
 | 
						|
    tmpStyleContext->StyleData(sid);
 | 
						|
 | 
						|
    // The rule node will have unconditional cached style data if the value is
 | 
						|
    // not context-sensitive.  So if there's nothing cached, it's not context
 | 
						|
    // sensitive.
 | 
						|
    *aIsContextSensitive =
 | 
						|
      !tmpStyleContext->RuleNode()->NodeHasCachedUnconditionalData(sid);
 | 
						|
  }
 | 
						|
 | 
						|
  // If we're not concerned whether the property is context sensitive then just
 | 
						|
  // add the rule to a new temporary style context alongside the target
 | 
						|
  // element's style context.
 | 
						|
  // Also, if we previously discovered that this property IS context-sensitive
 | 
						|
  // then we need to throw the temporary style context out since the property's
 | 
						|
  // value may have been biased by the 'initial' values supplied.
 | 
						|
  if (!aIsContextSensitive || *aIsContextSensitive) {
 | 
						|
    nsCOMArray<nsIStyleRule> ruleArray;
 | 
						|
    css::Declaration* declaration = aStyleRule->GetDeclaration();
 | 
						|
    ruleArray.AppendObject(declaration);
 | 
						|
    declaration->SetImmutable();
 | 
						|
    tmpStyleContext =
 | 
						|
      styleSet->ResolveStyleByAddingRules(aStyleContext, ruleArray);
 | 
						|
    if (!tmpStyleContext) {
 | 
						|
      return false;
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  return ComputeValuesFromStyleContext(aProperty, aEnabledState,
 | 
						|
                                       tmpStyleContext, aValues);
 | 
						|
}
 | 
						|
 | 
						|
/* static */ bool
 | 
						|
StyleAnimationValue::ComputeValue(nsCSSPropertyID aProperty,
 | 
						|
                                  dom::Element* aTargetElement,
 | 
						|
                                  GeckoStyleContext* aStyleContext,
 | 
						|
                                  const nsAString& aSpecifiedValue,
 | 
						|
                                  bool aUseSVGMode,
 | 
						|
                                  StyleAnimationValue& aComputedValue,
 | 
						|
                                  bool* aIsContextSensitive)
 | 
						|
{
 | 
						|
  MOZ_ASSERT(aTargetElement, "null target element");
 | 
						|
 | 
						|
  // Parse specified value into a temporary css::StyleRule
 | 
						|
  // Note: BuildStyleRule needs an element's OwnerDoc, BaseURI, and Principal.
 | 
						|
  // If it is a pseudo element, use its parent element's OwnerDoc, BaseURI,
 | 
						|
  // and Principal.
 | 
						|
  RefPtr<css::StyleRule> styleRule =
 | 
						|
    BuildStyleRule(aProperty, aTargetElement, aSpecifiedValue, aUseSVGMode);
 | 
						|
  if (!styleRule) {
 | 
						|
    return false;
 | 
						|
  }
 | 
						|
 | 
						|
  if (nsCSSProps::IsShorthand(aProperty) ||
 | 
						|
      nsCSSProps::kAnimTypeTable[aProperty] == eStyleAnimType_None) {
 | 
						|
    // Just capture the specified value
 | 
						|
    aComputedValue.SetUnparsedStringValue(nsString(aSpecifiedValue));
 | 
						|
    if (aIsContextSensitive) {
 | 
						|
      // Since we're just returning the string as-is, aComputedValue isn't going
 | 
						|
      // to change depending on the context
 | 
						|
      *aIsContextSensitive = false;
 | 
						|
    }
 | 
						|
    return true;
 | 
						|
  }
 | 
						|
 | 
						|
  AutoTArray<PropertyStyleAnimationValuePair,1> values;
 | 
						|
  bool ok = ComputeValuesFromStyleRule(aProperty,
 | 
						|
                                       CSSEnabledState::eIgnoreEnabledState,
 | 
						|
                                       aStyleContext, styleRule,
 | 
						|
                                       values, aIsContextSensitive);
 | 
						|
  if (!ok) {
 | 
						|
    return false;
 | 
						|
  }
 | 
						|
 | 
						|
  MOZ_ASSERT(values.Length() == 1);
 | 
						|
  MOZ_ASSERT(values[0].mProperty == aProperty);
 | 
						|
 | 
						|
  aComputedValue = values[0].mValue.mGecko;
 | 
						|
  return true;
 | 
						|
}
 | 
						|
 | 
						|
template <class T>
 | 
						|
bool
 | 
						|
ComputeValuesFromSpecifiedValue(
 | 
						|
    nsCSSPropertyID aProperty,
 | 
						|
    CSSEnabledState aEnabledState,
 | 
						|
    dom::Element* aTargetElement,
 | 
						|
    GeckoStyleContext* aStyleContext,
 | 
						|
    T& aSpecifiedValue,
 | 
						|
    bool aUseSVGMode,
 | 
						|
    nsTArray<PropertyStyleAnimationValuePair>& aResult)
 | 
						|
{
 | 
						|
  MOZ_ASSERT(aTargetElement, "null target element");
 | 
						|
 | 
						|
  // Parse specified value into a temporary css::StyleRule
 | 
						|
  // Note: BuildStyleRule needs an element's OwnerDoc, BaseURI, and Principal.
 | 
						|
  // If it is a pseudo element, use its parent element's OwnerDoc, BaseURI,
 | 
						|
  // and Principal.
 | 
						|
  RefPtr<css::StyleRule> styleRule =
 | 
						|
    BuildStyleRule(aProperty, aTargetElement, aSpecifiedValue, aUseSVGMode);
 | 
						|
  if (!styleRule) {
 | 
						|
    return false;
 | 
						|
  }
 | 
						|
 | 
						|
  aResult.Clear();
 | 
						|
  return ComputeValuesFromStyleRule(aProperty, aEnabledState,
 | 
						|
                                    aStyleContext, styleRule, aResult,
 | 
						|
                                    /* aIsContextSensitive */ nullptr);
 | 
						|
}
 | 
						|
 | 
						|
/* static */ bool
 | 
						|
StyleAnimationValue::ComputeValues(
 | 
						|
    nsCSSPropertyID aProperty,
 | 
						|
    CSSEnabledState aEnabledState,
 | 
						|
    dom::Element* aTargetElement,
 | 
						|
    GeckoStyleContext* aStyleContext,
 | 
						|
    const nsAString& aSpecifiedValue,
 | 
						|
    bool aUseSVGMode,
 | 
						|
    nsTArray<PropertyStyleAnimationValuePair>& aResult)
 | 
						|
{
 | 
						|
  return ComputeValuesFromSpecifiedValue(aProperty, aEnabledState,
 | 
						|
                                         aTargetElement, aStyleContext,
 | 
						|
                                         aSpecifiedValue, aUseSVGMode,
 | 
						|
                                         aResult);
 | 
						|
}
 | 
						|
 | 
						|
/* static */ bool
 | 
						|
StyleAnimationValue::ComputeValues(
 | 
						|
    nsCSSPropertyID aProperty,
 | 
						|
    CSSEnabledState aEnabledState,
 | 
						|
    dom::Element* aTargetElement,
 | 
						|
    GeckoStyleContext* aStyleContext,
 | 
						|
    const nsCSSValue& aSpecifiedValue,
 | 
						|
    bool aUseSVGMode,
 | 
						|
    nsTArray<PropertyStyleAnimationValuePair>& aResult)
 | 
						|
{
 | 
						|
  return ComputeValuesFromSpecifiedValue(aProperty, aEnabledState,
 | 
						|
                                         aTargetElement, aStyleContext,
 | 
						|
                                         aSpecifiedValue, aUseSVGMode,
 | 
						|
                                         aResult);
 | 
						|
}
 | 
						|
 | 
						|
bool
 | 
						|
StyleAnimationValue::UncomputeValue(nsCSSPropertyID aProperty,
 | 
						|
                                    const StyleAnimationValue& aComputedValue,
 | 
						|
                                    nsCSSValue& aSpecifiedValue)
 | 
						|
{
 | 
						|
  Unit unit = aComputedValue.GetUnit();
 | 
						|
  switch (unit) {
 | 
						|
    case eUnit_Normal:
 | 
						|
      aSpecifiedValue.SetNormalValue();
 | 
						|
      break;
 | 
						|
    case eUnit_Auto:
 | 
						|
      aSpecifiedValue.SetAutoValue();
 | 
						|
      break;
 | 
						|
    case eUnit_None:
 | 
						|
      aSpecifiedValue.SetNoneValue();
 | 
						|
      break;
 | 
						|
    case eUnit_Enumerated:
 | 
						|
    case eUnit_Visibility:
 | 
						|
      aSpecifiedValue.
 | 
						|
        SetIntValue(aComputedValue.GetIntValue(), eCSSUnit_Enumerated);
 | 
						|
      break;
 | 
						|
    case eUnit_Integer:
 | 
						|
      aSpecifiedValue.
 | 
						|
        SetIntValue(aComputedValue.GetIntValue(), eCSSUnit_Integer);
 | 
						|
      break;
 | 
						|
    case eUnit_Coord:
 | 
						|
      aSpecifiedValue.SetIntegerCoordValue(aComputedValue.GetCoordValue());
 | 
						|
      break;
 | 
						|
    case eUnit_Percent:
 | 
						|
      aSpecifiedValue.SetPercentValue(aComputedValue.GetPercentValue());
 | 
						|
      break;
 | 
						|
    case eUnit_Float:
 | 
						|
      aSpecifiedValue.
 | 
						|
        SetFloatValue(aComputedValue.GetFloatValue(), eCSSUnit_Number);
 | 
						|
      break;
 | 
						|
    case eUnit_CurrentColor:
 | 
						|
      aSpecifiedValue.SetIntValue(NS_COLOR_CURRENTCOLOR, eCSSUnit_EnumColor);
 | 
						|
      break;
 | 
						|
    case eUnit_Calc:
 | 
						|
    case eUnit_Color:
 | 
						|
    case eUnit_ObjectPosition:
 | 
						|
    case eUnit_URL:
 | 
						|
    case eUnit_DiscreteCSSValue: {
 | 
						|
      nsCSSValue* val = aComputedValue.GetCSSValueValue();
 | 
						|
      // Sanity-check that the underlying unit in the nsCSSValue is what we
 | 
						|
      // expect for our StyleAnimationValue::Unit:
 | 
						|
      MOZ_ASSERT((unit == eUnit_Calc && val->GetUnit() == eCSSUnit_Calc) ||
 | 
						|
                 (unit == eUnit_Color &&
 | 
						|
                  nsCSSValue::IsNumericColorUnit(val->GetUnit())) ||
 | 
						|
                 (unit == eUnit_ObjectPosition &&
 | 
						|
                  val->GetUnit() == eCSSUnit_Array) ||
 | 
						|
                 (unit == eUnit_URL && val->GetUnit() == eCSSUnit_URL) ||
 | 
						|
                 unit == eUnit_DiscreteCSSValue,
 | 
						|
                 "unexpected unit");
 | 
						|
      aSpecifiedValue = *val;
 | 
						|
      break;
 | 
						|
    }
 | 
						|
    case eUnit_ComplexColor: {
 | 
						|
      aSpecifiedValue.SetComplexColorValue(
 | 
						|
        do_AddRef(aComputedValue.mValue.mComplexColor));
 | 
						|
      break;
 | 
						|
    }
 | 
						|
    case eUnit_CSSValuePair: {
 | 
						|
      // Rule node processing expects pair values to be collapsed to a
 | 
						|
      // single value if both halves would be equal, for most but not
 | 
						|
      // all properties.  At present, all animatable properties that
 | 
						|
      // use pairs do expect collapsing.
 | 
						|
      const nsCSSValuePair* pair = aComputedValue.GetCSSValuePairValue();
 | 
						|
      if (pair->mXValue == pair->mYValue) {
 | 
						|
        aSpecifiedValue = pair->mXValue;
 | 
						|
      } else {
 | 
						|
        aSpecifiedValue.SetPairValue(pair);
 | 
						|
      }
 | 
						|
    } break;
 | 
						|
    case eUnit_CSSValueTriplet: {
 | 
						|
      // Rule node processing expects triplet values to be collapsed to a
 | 
						|
      // single value if both halves would be equal, for most but not
 | 
						|
      // all properties.  At present, all animatable properties that
 | 
						|
      // use pairs do expect collapsing.
 | 
						|
      const nsCSSValueTriplet* triplet = aComputedValue.GetCSSValueTripletValue();
 | 
						|
      if (triplet->mXValue == triplet->mYValue && triplet->mYValue == triplet->mZValue) {
 | 
						|
        aSpecifiedValue = triplet->mXValue;
 | 
						|
      } else {
 | 
						|
        aSpecifiedValue.SetTripletValue(triplet);
 | 
						|
      }
 | 
						|
    } break;
 | 
						|
    case eUnit_CSSRect: {
 | 
						|
      nsCSSRect& rect = aSpecifiedValue.SetRectValue();
 | 
						|
      rect = *aComputedValue.GetCSSRectValue();
 | 
						|
    } break;
 | 
						|
    case eUnit_Dasharray:
 | 
						|
    case eUnit_Shadow:
 | 
						|
    case eUnit_Filter:
 | 
						|
    case eUnit_BackgroundPositionCoord:
 | 
						|
      {
 | 
						|
        nsCSSValueList* computedList = aComputedValue.GetCSSValueListValue();
 | 
						|
        if (computedList) {
 | 
						|
          aSpecifiedValue.SetDependentListValue(computedList);
 | 
						|
        } else {
 | 
						|
          aSpecifiedValue.SetNoneValue();
 | 
						|
        }
 | 
						|
      }
 | 
						|
      break;
 | 
						|
    case eUnit_Shape: {
 | 
						|
      nsCSSValue::Array* computedArray = aComputedValue.GetCSSValueArrayValue();
 | 
						|
      aSpecifiedValue.SetArrayValue(computedArray, eCSSUnit_Array);
 | 
						|
      break;
 | 
						|
    }
 | 
						|
    case eUnit_Transform:
 | 
						|
      aSpecifiedValue.
 | 
						|
        SetSharedListValue(aComputedValue.GetCSSValueSharedListValue());
 | 
						|
      break;
 | 
						|
    case eUnit_CSSValuePairList:
 | 
						|
      aSpecifiedValue.
 | 
						|
        SetDependentPairListValue(aComputedValue.GetCSSValuePairListValue());
 | 
						|
      break;
 | 
						|
    default:
 | 
						|
      return false;
 | 
						|
  }
 | 
						|
  return true;
 | 
						|
}
 | 
						|
 | 
						|
bool
 | 
						|
StyleAnimationValue::UncomputeValue(nsCSSPropertyID aProperty,
 | 
						|
                                    StyleAnimationValue&& aComputedValue,
 | 
						|
                                    nsCSSValue& aSpecifiedValue)
 | 
						|
{
 | 
						|
  Unit unit = aComputedValue.GetUnit();
 | 
						|
  switch (unit) {
 | 
						|
    case eUnit_Dasharray:
 | 
						|
    case eUnit_Shadow:
 | 
						|
    case eUnit_Filter:
 | 
						|
    case eUnit_BackgroundPositionCoord:
 | 
						|
      {
 | 
						|
        UniquePtr<nsCSSValueList> computedList =
 | 
						|
          aComputedValue.TakeCSSValueListValue();
 | 
						|
        if (computedList) {
 | 
						|
          aSpecifiedValue.AdoptListValue(Move(computedList));
 | 
						|
        } else {
 | 
						|
          aSpecifiedValue.SetNoneValue();
 | 
						|
        }
 | 
						|
      }
 | 
						|
      break;
 | 
						|
    case eUnit_CSSValuePairList:
 | 
						|
      {
 | 
						|
        UniquePtr<nsCSSValuePairList> computedList =
 | 
						|
          aComputedValue.TakeCSSValuePairListValue();
 | 
						|
        MOZ_ASSERT(computedList, "Pair list should never be null");
 | 
						|
        aSpecifiedValue.AdoptPairListValue(Move(computedList));
 | 
						|
      }
 | 
						|
      break;
 | 
						|
    default:
 | 
						|
      return UncomputeValue(aProperty, aComputedValue, aSpecifiedValue);
 | 
						|
  }
 | 
						|
  return true;
 | 
						|
}
 | 
						|
 | 
						|
bool
 | 
						|
StyleAnimationValue::UncomputeValue(nsCSSPropertyID aProperty,
 | 
						|
                                    const StyleAnimationValue& aComputedValue,
 | 
						|
                                    nsAString& aSpecifiedValue)
 | 
						|
{
 | 
						|
  aSpecifiedValue.Truncate(); // Clear outparam, if it's not already empty
 | 
						|
 | 
						|
  if (aComputedValue.GetUnit() == eUnit_UnparsedString) {
 | 
						|
    aComputedValue.GetStringValue(aSpecifiedValue);
 | 
						|
    return true;
 | 
						|
  }
 | 
						|
  nsCSSValue val;
 | 
						|
  if (!StyleAnimationValue::UncomputeValue(aProperty, aComputedValue, val)) {
 | 
						|
    return false;
 | 
						|
  }
 | 
						|
 | 
						|
  val.AppendToString(aProperty, aSpecifiedValue);
 | 
						|
  return true;
 | 
						|
}
 | 
						|
 | 
						|
template<typename T>
 | 
						|
inline const T&
 | 
						|
StyleDataAtOffset(const void* aStyleStruct, ptrdiff_t aOffset)
 | 
						|
{
 | 
						|
  return *reinterpret_cast<const T*>(
 | 
						|
    reinterpret_cast<const uint8_t*>(aStyleStruct) + aOffset);
 | 
						|
}
 | 
						|
 | 
						|
static bool
 | 
						|
StyleCoordToValue(const nsStyleCoord& aCoord, StyleAnimationValue& aValue)
 | 
						|
{
 | 
						|
  switch (aCoord.GetUnit()) {
 | 
						|
    case eStyleUnit_Normal:
 | 
						|
      aValue.SetNormalValue();
 | 
						|
      break;
 | 
						|
    case eStyleUnit_Auto:
 | 
						|
      aValue.SetAutoValue();
 | 
						|
      break;
 | 
						|
    case eStyleUnit_None:
 | 
						|
      aValue.SetNoneValue();
 | 
						|
      break;
 | 
						|
    case eStyleUnit_Percent:
 | 
						|
      aValue.SetPercentValue(aCoord.GetPercentValue());
 | 
						|
      break;
 | 
						|
    case eStyleUnit_Factor:
 | 
						|
      aValue.SetFloatValue(aCoord.GetFactorValue());
 | 
						|
      break;
 | 
						|
    case eStyleUnit_Coord:
 | 
						|
      aValue.SetCoordValue(aCoord.GetCoordValue());
 | 
						|
      break;
 | 
						|
    case eStyleUnit_Enumerated:
 | 
						|
      aValue.SetIntValue(aCoord.GetIntValue(),
 | 
						|
                         StyleAnimationValue::eUnit_Enumerated);
 | 
						|
      break;
 | 
						|
    case eStyleUnit_Integer:
 | 
						|
      aValue.SetIntValue(aCoord.GetIntValue(),
 | 
						|
                         StyleAnimationValue::eUnit_Integer);
 | 
						|
      break;
 | 
						|
    case eStyleUnit_Calc: {
 | 
						|
      nsAutoPtr<nsCSSValue> val(new nsCSSValue);
 | 
						|
      CalcValueToCSSValue(aCoord.GetCalcValue(), *val);
 | 
						|
      aValue.SetAndAdoptCSSValueValue(val.forget(),
 | 
						|
                                      StyleAnimationValue::eUnit_Calc);
 | 
						|
      break;
 | 
						|
    }
 | 
						|
    default:
 | 
						|
      return false;
 | 
						|
  }
 | 
						|
  return true;
 | 
						|
}
 | 
						|
 | 
						|
static bool
 | 
						|
StyleCoordToCSSValue(const nsStyleCoord& aCoord, nsCSSValue& aCSSValue)
 | 
						|
{
 | 
						|
  switch (aCoord.GetUnit()) {
 | 
						|
    case eStyleUnit_Coord:
 | 
						|
      aCSSValue.SetIntegerCoordValue(aCoord.GetCoordValue());
 | 
						|
      break;
 | 
						|
    case eStyleUnit_Factor:
 | 
						|
      aCSSValue.SetFloatValue(aCoord.GetFactorValue(), eCSSUnit_Number);
 | 
						|
      break;
 | 
						|
    case eStyleUnit_Percent:
 | 
						|
      aCSSValue.SetPercentValue(aCoord.GetPercentValue());
 | 
						|
      break;
 | 
						|
    case eStyleUnit_Calc:
 | 
						|
      CalcValueToCSSValue(aCoord.GetCalcValue(), aCSSValue);
 | 
						|
      break;
 | 
						|
    case eStyleUnit_Degree:
 | 
						|
      aCSSValue.SetFloatValue(aCoord.GetAngleValue(), eCSSUnit_Degree);
 | 
						|
      break;
 | 
						|
    case eStyleUnit_Grad:
 | 
						|
      aCSSValue.SetFloatValue(aCoord.GetAngleValue(), eCSSUnit_Grad);
 | 
						|
      break;
 | 
						|
    case eStyleUnit_Radian:
 | 
						|
      aCSSValue.SetFloatValue(aCoord.GetAngleValue(), eCSSUnit_Radian);
 | 
						|
      break;
 | 
						|
    case eStyleUnit_Turn:
 | 
						|
      aCSSValue.SetFloatValue(aCoord.GetAngleValue(), eCSSUnit_Turn);
 | 
						|
      break;
 | 
						|
    default:
 | 
						|
      MOZ_ASSERT(false, "unexpected unit");
 | 
						|
      return false;
 | 
						|
  }
 | 
						|
  return true;
 | 
						|
}
 | 
						|
 | 
						|
static void
 | 
						|
SetPositionValue(const Position& aPos, nsCSSValue& aCSSValue)
 | 
						|
{
 | 
						|
  RefPtr<nsCSSValue::Array> posArray = nsCSSValue::Array::Create(4);
 | 
						|
  aCSSValue.SetArrayValue(posArray.get(), eCSSUnit_Array);
 | 
						|
 | 
						|
  // NOTE: Array entries #0 and #2 here are intentionally left untouched, with
 | 
						|
  // eCSSUnit_Null.  The purpose of these entries in our specified-style
 | 
						|
  // <position> representation is to store edge names.  But for values
 | 
						|
  // extracted from computed style (which is what we're dealing with here),
 | 
						|
  // we'll just have a normalized "x,y" position, with no edge names needed.
 | 
						|
  nsCSSValue& xValue = posArray->Item(1);
 | 
						|
  nsCSSValue& yValue = posArray->Item(3);
 | 
						|
 | 
						|
  CalcValueToCSSValue(&aPos.mXPosition, xValue);
 | 
						|
  CalcValueToCSSValue(&aPos.mYPosition, yValue);
 | 
						|
}
 | 
						|
 | 
						|
static void
 | 
						|
SetPositionCoordValue(const Position::Coord& aPosCoord,
 | 
						|
                      nsCSSValue& aCSSValue)
 | 
						|
{
 | 
						|
  RefPtr<nsCSSValue::Array> posArray = nsCSSValue::Array::Create(2);
 | 
						|
  aCSSValue.SetArrayValue(posArray.get(), eCSSUnit_Array);
 | 
						|
 | 
						|
  // NOTE: Array entry #0 here is intentionally left untouched, with
 | 
						|
  // eCSSUnit_Null.  The purpose of this entry in our specified-style
 | 
						|
  // <position-coord> representation is to store edge names.  But for values
 | 
						|
  // extracted from computed style (which is what we're dealing with here),
 | 
						|
  // we'll just have a normalized "x"/"y" position, with no edge names needed.
 | 
						|
  nsCSSValue& value = posArray->Item(1);
 | 
						|
 | 
						|
  CalcValueToCSSValue(&aPosCoord, value);
 | 
						|
}
 | 
						|
 | 
						|
/*
 | 
						|
 * Assign |aOutput = aInput|, except with any non-pixel lengths
 | 
						|
 * replaced with the equivalent in pixels, and any non-canonical calc()
 | 
						|
 * expressions replaced with canonical ones.
 | 
						|
 */
 | 
						|
static void
 | 
						|
SubstitutePixelValues(GeckoStyleContext* aStyleContext,
 | 
						|
                      const nsCSSValue& aInput, nsCSSValue& aOutput)
 | 
						|
{
 | 
						|
  if (aInput.IsCalcUnit()) {
 | 
						|
    RuleNodeCacheConditions conditions;
 | 
						|
    nsRuleNode::ComputedCalc c =
 | 
						|
      nsRuleNode::SpecifiedCalcToComputedCalc(aInput, aStyleContext,
 | 
						|
                                              aStyleContext->PresContext(),
 | 
						|
                                              conditions);
 | 
						|
    nsStyleCoord::CalcValue c2;
 | 
						|
    c2.mLength = c.mLength;
 | 
						|
    c2.mPercent = c.mPercent;
 | 
						|
    c2.mHasPercent = true; // doesn't matter for transform translate
 | 
						|
    CalcValueToCSSValue(&c2, aOutput);
 | 
						|
  } else if (aInput.UnitHasArrayValue()) {
 | 
						|
    const nsCSSValue::Array *inputArray = aInput.GetArrayValue();
 | 
						|
    RefPtr<nsCSSValue::Array> outputArray =
 | 
						|
      nsCSSValue::Array::Create(inputArray->Count());
 | 
						|
    for (size_t i = 0, i_end = inputArray->Count(); i < i_end; ++i) {
 | 
						|
      SubstitutePixelValues(aStyleContext,
 | 
						|
                            inputArray->Item(i), outputArray->Item(i));
 | 
						|
    }
 | 
						|
    aOutput.SetArrayValue(outputArray, aInput.GetUnit());
 | 
						|
  } else if (aInput.IsLengthUnit() &&
 | 
						|
             aInput.GetUnit() != eCSSUnit_Pixel) {
 | 
						|
    RuleNodeCacheConditions conditions;
 | 
						|
    nscoord len = nsRuleNode::CalcLength(aInput, aStyleContext,
 | 
						|
                                         aStyleContext->PresContext(),
 | 
						|
                                         conditions);
 | 
						|
    aOutput.SetFloatValue(nsPresContext::AppUnitsToFloatCSSPixels(len),
 | 
						|
                          eCSSUnit_Pixel);
 | 
						|
  } else {
 | 
						|
    aOutput = aInput;
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
static void
 | 
						|
ExtractImageLayerPositionXList(const nsStyleImageLayers& aLayer,
 | 
						|
                               StyleAnimationValue& aComputedValue)
 | 
						|
{
 | 
						|
  MOZ_ASSERT(aLayer.mPositionXCount > 0, "unexpected count");
 | 
						|
 | 
						|
  nsAutoPtr<nsCSSValueList> result;
 | 
						|
  nsCSSValueList **resultTail = getter_Transfers(result);
 | 
						|
  for (uint32_t i = 0, i_end = aLayer.mPositionXCount; i != i_end; ++i) {
 | 
						|
    nsCSSValueList *item = new nsCSSValueList;
 | 
						|
    *resultTail = item;
 | 
						|
    resultTail = &item->mNext;
 | 
						|
    SetPositionCoordValue(aLayer.mLayers[i].mPosition.mXPosition,
 | 
						|
                          item->mValue);
 | 
						|
  }
 | 
						|
 | 
						|
  aComputedValue.SetAndAdoptCSSValueListValue(result.forget(),
 | 
						|
    StyleAnimationValue::eUnit_BackgroundPositionCoord);
 | 
						|
}
 | 
						|
 | 
						|
static void
 | 
						|
ExtractImageLayerPositionYList(const nsStyleImageLayers& aLayer,
 | 
						|
                               StyleAnimationValue& aComputedValue)
 | 
						|
{
 | 
						|
  MOZ_ASSERT(aLayer.mPositionYCount > 0, "unexpected count");
 | 
						|
 | 
						|
  nsAutoPtr<nsCSSValueList> result;
 | 
						|
  nsCSSValueList **resultTail = getter_Transfers(result);
 | 
						|
  for (uint32_t i = 0, i_end = aLayer.mPositionYCount; i != i_end; ++i) {
 | 
						|
    nsCSSValueList *item = new nsCSSValueList;
 | 
						|
    *resultTail = item;
 | 
						|
    resultTail = &item->mNext;
 | 
						|
    SetPositionCoordValue(aLayer.mLayers[i].mPosition.mYPosition,
 | 
						|
                          item->mValue);
 | 
						|
  }
 | 
						|
 | 
						|
  aComputedValue.SetAndAdoptCSSValueListValue(result.forget(),
 | 
						|
    StyleAnimationValue::eUnit_BackgroundPositionCoord);
 | 
						|
}
 | 
						|
 | 
						|
static void
 | 
						|
ExtractImageLayerSizePairList(const nsStyleImageLayers& aLayer,
 | 
						|
                              StyleAnimationValue& aComputedValue)
 | 
						|
{
 | 
						|
  MOZ_ASSERT(aLayer.mSizeCount > 0, "unexpected count");
 | 
						|
 | 
						|
  nsAutoPtr<nsCSSValuePairList> result;
 | 
						|
  nsCSSValuePairList **resultTail = getter_Transfers(result);
 | 
						|
  for (uint32_t i = 0, i_end = aLayer.mSizeCount; i != i_end; ++i) {
 | 
						|
    nsCSSValuePairList *item = new nsCSSValuePairList;
 | 
						|
    *resultTail = item;
 | 
						|
    resultTail = &item->mNext;
 | 
						|
 | 
						|
    const nsStyleImageLayers::Size &size = aLayer.mLayers[i].mSize;
 | 
						|
    switch (size.mWidthType) {
 | 
						|
      case nsStyleImageLayers::Size::eContain:
 | 
						|
      case nsStyleImageLayers::Size::eCover:
 | 
						|
        item->mXValue.SetIntValue(size.mWidthType,
 | 
						|
                                  eCSSUnit_Enumerated);
 | 
						|
        break;
 | 
						|
      case nsStyleImageLayers::Size::eAuto:
 | 
						|
        item->mXValue.SetAutoValue();
 | 
						|
        break;
 | 
						|
      case nsStyleImageLayers::Size::eLengthPercentage:
 | 
						|
        // XXXbz is there a good reason we can't just
 | 
						|
        // CalcValueToCSSValue(&size.mWidth, item->mXValue) here?
 | 
						|
        if (!size.mWidth.mHasPercent &&
 | 
						|
            // negative values must have come from calc()
 | 
						|
            size.mWidth.mLength >= 0) {
 | 
						|
          MOZ_ASSERT(size.mWidth.mPercent == 0.0f,
 | 
						|
                     "Shouldn't have mPercent");
 | 
						|
          item->mXValue.SetIntegerCoordValue(size.mWidth.mLength);
 | 
						|
        } else if (size.mWidth.mLength == 0 &&
 | 
						|
                   // negative values must have come from calc()
 | 
						|
                   size.mWidth.mPercent >= 0.0f) {
 | 
						|
          item->mXValue.SetPercentValue(size.mWidth.mPercent);
 | 
						|
        } else {
 | 
						|
          CalcValueToCSSValue(&size.mWidth, item->mXValue);
 | 
						|
        }
 | 
						|
        break;
 | 
						|
    }
 | 
						|
 | 
						|
    switch (size.mHeightType) {
 | 
						|
      case nsStyleImageLayers::Size::eContain:
 | 
						|
      case nsStyleImageLayers::Size::eCover:
 | 
						|
        // leave it null
 | 
						|
        break;
 | 
						|
      case nsStyleImageLayers::Size::eAuto:
 | 
						|
        item->mYValue.SetAutoValue();
 | 
						|
        break;
 | 
						|
      case nsStyleImageLayers::Size::eLengthPercentage:
 | 
						|
        // XXXbz is there a good reason we can't just
 | 
						|
        // CalcValueToCSSValue(&size.mHeight, item->mYValue) here?
 | 
						|
        if (!size.mHeight.mHasPercent &&
 | 
						|
            // negative values must have come from calc()
 | 
						|
            size.mHeight.mLength >= 0) {
 | 
						|
          MOZ_ASSERT(size.mHeight.mPercent == 0.0f,
 | 
						|
                     "Shouldn't have mPercent");
 | 
						|
          item->mYValue.SetIntegerCoordValue(size.mHeight.mLength);
 | 
						|
        } else if (size.mHeight.mLength == 0 &&
 | 
						|
                   // negative values must have come from calc()
 | 
						|
                   size.mHeight.mPercent >= 0.0f) {
 | 
						|
          item->mYValue.SetPercentValue(size.mHeight.mPercent);
 | 
						|
        } else {
 | 
						|
          CalcValueToCSSValue(&size.mHeight, item->mYValue);
 | 
						|
        }
 | 
						|
        break;
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  aComputedValue.SetAndAdoptCSSValuePairListValue(result.forget());
 | 
						|
}
 | 
						|
 | 
						|
static bool
 | 
						|
StyleShapeSourceToCSSArray(const StyleShapeSource& aShapeSource,
 | 
						|
                           nsCSSValue::Array* aResult)
 | 
						|
{
 | 
						|
  MOZ_ASSERT(aResult->Count() == 2,
 | 
						|
             "Expected array to be presized for a function and the sizing-box");
 | 
						|
 | 
						|
  const UniquePtr<StyleBasicShape>& shape = aShapeSource.GetBasicShape();
 | 
						|
  nsCSSKeyword functionName = shape->GetShapeTypeName();
 | 
						|
  RefPtr<nsCSSValue::Array> functionArray;
 | 
						|
  switch (shape->GetShapeType()) {
 | 
						|
    case StyleBasicShapeType::Circle:
 | 
						|
    case StyleBasicShapeType::Ellipse: {
 | 
						|
      const nsTArray<nsStyleCoord>& coords = shape->Coordinates();
 | 
						|
      MOZ_ASSERT(coords.Length() == ShapeArgumentCount(functionName) - 1,
 | 
						|
                 "Unexpected radii count");
 | 
						|
      // The "+1" is for the center point:
 | 
						|
      functionArray = aResult->Item(0).InitFunction(functionName,
 | 
						|
                                                    coords.Length() + 1);
 | 
						|
      for (size_t i = 0; i < coords.Length(); ++i) {
 | 
						|
        if (coords[i].GetUnit() == eStyleUnit_Enumerated) {
 | 
						|
          functionArray->Item(i + 1).SetIntValue(coords[i].GetIntValue(),
 | 
						|
                                                 eCSSUnit_Enumerated);
 | 
						|
        } else if (!StyleCoordToCSSValue(coords[i],
 | 
						|
                                         functionArray->Item(i + 1))) {
 | 
						|
          return false;
 | 
						|
        }
 | 
						|
      }
 | 
						|
      // Set functionArray's last item to the circle or ellipse's center point:
 | 
						|
      SetPositionValue(shape->GetPosition(),
 | 
						|
                       functionArray->Item(functionArray->Count() - 1));
 | 
						|
      break;
 | 
						|
    }
 | 
						|
    case StyleBasicShapeType::Polygon: {
 | 
						|
      functionArray =
 | 
						|
        aResult->Item(0).InitFunction(functionName,
 | 
						|
                                      ShapeArgumentCount(functionName));
 | 
						|
      functionArray->Item(1).SetEnumValue(shape->GetFillRule());
 | 
						|
      nsCSSValuePairList* list = functionArray->Item(2).SetPairListValue();
 | 
						|
      const nsTArray<nsStyleCoord>& coords = shape->Coordinates();
 | 
						|
      MOZ_ASSERT((coords.Length() % 2) == 0);
 | 
						|
      for (size_t i = 0; i < coords.Length(); i += 2) {
 | 
						|
        if (i > 0) {
 | 
						|
          list->mNext = new nsCSSValuePairList;
 | 
						|
          list = list->mNext;
 | 
						|
        }
 | 
						|
        if (!StyleCoordToCSSValue(coords[i], list->mXValue) ||
 | 
						|
            !StyleCoordToCSSValue(coords[i + 1], list->mYValue)) {
 | 
						|
          return false;
 | 
						|
        }
 | 
						|
      }
 | 
						|
      break;
 | 
						|
    }
 | 
						|
    case StyleBasicShapeType::Inset: {
 | 
						|
      const nsTArray<nsStyleCoord>& coords = shape->Coordinates();
 | 
						|
      MOZ_ASSERT(coords.Length() == ShapeArgumentCount(functionName) - 1,
 | 
						|
                 "Unexpected offset count");
 | 
						|
      functionArray =
 | 
						|
        aResult->Item(0).InitFunction(functionName, coords.Length() + 1);
 | 
						|
      for (size_t i = 0; i < coords.Length(); ++i) {
 | 
						|
        if (!StyleCoordToCSSValue(coords[i], functionArray->Item(i + 1))) {
 | 
						|
          return false;
 | 
						|
        }
 | 
						|
      }
 | 
						|
      RefPtr<nsCSSValue::Array> radiusArray = nsCSSValue::Array::Create(4);
 | 
						|
      const nsStyleCorners& radii = shape->GetRadius();
 | 
						|
      NS_FOR_CSS_FULL_CORNERS(corner) {
 | 
						|
        auto pair = MakeUnique<nsCSSValuePair>();
 | 
						|
        if (!StyleCoordToCSSValue(radii.Get(FullToHalfCorner(corner, false)),
 | 
						|
                                  pair->mXValue) ||
 | 
						|
            !StyleCoordToCSSValue(radii.Get(FullToHalfCorner(corner, true)),
 | 
						|
                                  pair->mYValue)) {
 | 
						|
          return false;
 | 
						|
        }
 | 
						|
        radiusArray->Item(corner).SetPairValue(pair.get());
 | 
						|
      }
 | 
						|
      // Set the last item in functionArray to the radius array:
 | 
						|
      functionArray->Item(functionArray->Count() - 1).
 | 
						|
                       SetArrayValue(radiusArray, eCSSUnit_Array);
 | 
						|
      break;
 | 
						|
    }
 | 
						|
    default:
 | 
						|
      MOZ_ASSERT_UNREACHABLE("Unknown shape type");
 | 
						|
      return false;
 | 
						|
  }
 | 
						|
  aResult->Item(1).SetEnumValue(aShapeSource.GetReferenceBox());
 | 
						|
  return true;
 | 
						|
}
 | 
						|
 | 
						|
static bool
 | 
						|
ExtractComputedValueFromShapeSource(const StyleShapeSource& aShapeSource,
 | 
						|
                                    StyleAnimationValue& aComputedValue)
 | 
						|
{
 | 
						|
  const StyleShapeSourceType type = aShapeSource.GetType();
 | 
						|
 | 
						|
  if (type == StyleShapeSourceType::URL) {
 | 
						|
    auto result = MakeUnique<nsCSSValue>();
 | 
						|
    result->SetURLValue(aShapeSource.GetURL());
 | 
						|
    aComputedValue.SetAndAdoptCSSValueValue(result.release(),
 | 
						|
                                            StyleAnimationValue::eUnit_URL);
 | 
						|
  } else if (type == StyleShapeSourceType::Box) {
 | 
						|
    aComputedValue.SetEnumValue(aShapeSource.GetReferenceBox());
 | 
						|
  } else if (type == StyleShapeSourceType::Shape) {
 | 
						|
    RefPtr<nsCSSValue::Array> result = nsCSSValue::Array::Create(2);
 | 
						|
    if (!StyleShapeSourceToCSSArray(aShapeSource, result)) {
 | 
						|
      return false;
 | 
						|
    }
 | 
						|
    aComputedValue.SetCSSValueArrayValue(result,
 | 
						|
                                         StyleAnimationValue::eUnit_Shape);
 | 
						|
 | 
						|
  } else if (type == StyleShapeSourceType::Image) {
 | 
						|
    // XXX: Won't implement because Gecko style system will be removed.
 | 
						|
    return false;
 | 
						|
  } else {
 | 
						|
    MOZ_ASSERT(type == StyleShapeSourceType::None, "unknown type");
 | 
						|
    aComputedValue.SetNoneValue();
 | 
						|
  }
 | 
						|
 | 
						|
  return true;
 | 
						|
}
 | 
						|
 | 
						|
static void
 | 
						|
SetFallbackValue(nsCSSValuePair* aPair, const nsStyleSVGPaint& aPaint)
 | 
						|
{
 | 
						|
  if (aPaint.GetFallbackType() == eStyleSVGFallbackType_Color) {
 | 
						|
    aPair->mYValue.SetColorValue(aPaint.GetFallbackColor());
 | 
						|
  } else {
 | 
						|
    aPair->mYValue.SetNoneValue();
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
bool
 | 
						|
StyleAnimationValue::ExtractComputedValue(nsCSSPropertyID aProperty,
 | 
						|
                                          GeckoStyleContext* aStyleContext,
 | 
						|
                                          StyleAnimationValue& aComputedValue)
 | 
						|
{
 | 
						|
  MOZ_ASSERT(0 <= aProperty && aProperty < eCSSProperty_COUNT_no_shorthands,
 | 
						|
             "bad property");
 | 
						|
  const void* styleStruct =
 | 
						|
    aStyleContext->StyleData(nsCSSProps::kSIDTable[aProperty]);
 | 
						|
  ptrdiff_t ssOffset = nsCSSProps::kStyleStructOffsetTable[aProperty];
 | 
						|
  nsStyleAnimType animType = nsCSSProps::kAnimTypeTable[aProperty];
 | 
						|
  MOZ_ASSERT(0 <= ssOffset ||
 | 
						|
             animType == eStyleAnimType_Custom ||
 | 
						|
             animType == eStyleAnimType_Discrete,
 | 
						|
             "all animation types other than Custom and Discrete must " \
 | 
						|
             "specify a style struct offset to extract values from");
 | 
						|
  switch (animType) {
 | 
						|
    case eStyleAnimType_Custom:
 | 
						|
      switch (aProperty) {
 | 
						|
        // For border-width, ignore the border-image business (which
 | 
						|
        // only exists until we update our implementation to the current
 | 
						|
        // spec) and use GetComputedBorder
 | 
						|
 | 
						|
        #define BORDER_WIDTH_CASE(prop_, side_)                               \
 | 
						|
        case prop_:                                                           \
 | 
						|
          aComputedValue.SetCoordValue(                                       \
 | 
						|
            static_cast<const nsStyleBorder*>(styleStruct)->                  \
 | 
						|
              GetComputedBorder().side_);                                     \
 | 
						|
          break;
 | 
						|
        BORDER_WIDTH_CASE(eCSSProperty_border_bottom_width, bottom)
 | 
						|
        BORDER_WIDTH_CASE(eCSSProperty_border_left_width, left)
 | 
						|
        BORDER_WIDTH_CASE(eCSSProperty_border_right_width, right)
 | 
						|
        BORDER_WIDTH_CASE(eCSSProperty_border_top_width, top)
 | 
						|
        #undef BORDER_WIDTH_CASE
 | 
						|
 | 
						|
        case eCSSProperty_column_rule_width:
 | 
						|
          aComputedValue.SetCoordValue(
 | 
						|
            static_cast<const nsStyleColumn*>(styleStruct)->
 | 
						|
              GetComputedColumnRuleWidth());
 | 
						|
          break;
 | 
						|
 | 
						|
        case eCSSProperty_column_count: {
 | 
						|
          const nsStyleColumn *styleColumn =
 | 
						|
            static_cast<const nsStyleColumn*>(styleStruct);
 | 
						|
          if (styleColumn->mColumnCount == NS_STYLE_COLUMN_COUNT_AUTO) {
 | 
						|
            aComputedValue.SetAutoValue();
 | 
						|
          } else {
 | 
						|
            aComputedValue.SetIntValue(styleColumn->mColumnCount,
 | 
						|
                                       eUnit_Integer);
 | 
						|
          }
 | 
						|
          break;
 | 
						|
        }
 | 
						|
 | 
						|
        case eCSSProperty_order: {
 | 
						|
          const nsStylePosition *stylePosition =
 | 
						|
            static_cast<const nsStylePosition*>(styleStruct);
 | 
						|
          aComputedValue.SetIntValue(stylePosition->mOrder,
 | 
						|
                                     eUnit_Integer);
 | 
						|
          break;
 | 
						|
        }
 | 
						|
 | 
						|
        case eCSSProperty_border_spacing: {
 | 
						|
          const nsStyleTableBorder *styleTableBorder =
 | 
						|
            static_cast<const nsStyleTableBorder*>(styleStruct);
 | 
						|
          nsAutoPtr<nsCSSValuePair> pair(new nsCSSValuePair);
 | 
						|
          pair->mXValue.SetIntegerCoordValue(styleTableBorder->mBorderSpacingCol);
 | 
						|
          pair->mYValue.SetIntegerCoordValue(styleTableBorder->mBorderSpacingRow);
 | 
						|
          aComputedValue.SetAndAdoptCSSValuePairValue(pair.forget(),
 | 
						|
                                                      eUnit_CSSValuePair);
 | 
						|
          break;
 | 
						|
        }
 | 
						|
 | 
						|
        case eCSSProperty_transform_origin: {
 | 
						|
          const nsStyleDisplay *styleDisplay =
 | 
						|
            static_cast<const nsStyleDisplay*>(styleStruct);
 | 
						|
          nsAutoPtr<nsCSSValueTriplet> triplet(new nsCSSValueTriplet);
 | 
						|
          if (!StyleCoordToCSSValue(styleDisplay->mTransformOrigin[0],
 | 
						|
                                    triplet->mXValue) ||
 | 
						|
              !StyleCoordToCSSValue(styleDisplay->mTransformOrigin[1],
 | 
						|
                                    triplet->mYValue) ||
 | 
						|
              !StyleCoordToCSSValue(styleDisplay->mTransformOrigin[2],
 | 
						|
                                    triplet->mZValue)) {
 | 
						|
            return false;
 | 
						|
          }
 | 
						|
          aComputedValue.SetAndAdoptCSSValueTripletValue(triplet.forget(),
 | 
						|
                                                         eUnit_CSSValueTriplet);
 | 
						|
          break;
 | 
						|
        }
 | 
						|
 | 
						|
        case eCSSProperty_perspective_origin: {
 | 
						|
          const nsStyleDisplay *styleDisplay =
 | 
						|
            static_cast<const nsStyleDisplay*>(styleStruct);
 | 
						|
          nsAutoPtr<nsCSSValuePair> pair(new nsCSSValuePair);
 | 
						|
          if (!StyleCoordToCSSValue(styleDisplay->mPerspectiveOrigin[0],
 | 
						|
                                    pair->mXValue) ||
 | 
						|
              !StyleCoordToCSSValue(styleDisplay->mPerspectiveOrigin[1],
 | 
						|
                                    pair->mYValue)) {
 | 
						|
            return false;
 | 
						|
          }
 | 
						|
          aComputedValue.SetAndAdoptCSSValuePairValue(pair.forget(),
 | 
						|
                                                      eUnit_CSSValuePair);
 | 
						|
          break;
 | 
						|
        }
 | 
						|
 | 
						|
        case eCSSProperty__moz_window_transform_origin: {
 | 
						|
          const nsStyleUIReset *styleUIReset =
 | 
						|
            static_cast<const nsStyleUIReset*>(styleStruct);
 | 
						|
          nsAutoPtr<nsCSSValuePair> pair(new nsCSSValuePair);
 | 
						|
          if (!StyleCoordToCSSValue(styleUIReset->mWindowTransformOrigin[0],
 | 
						|
                                    pair->mXValue) ||
 | 
						|
              !StyleCoordToCSSValue(styleUIReset->mWindowTransformOrigin[1],
 | 
						|
                                    pair->mYValue)) {
 | 
						|
            return false;
 | 
						|
          }
 | 
						|
          aComputedValue.SetAndAdoptCSSValuePairValue(pair.forget(),
 | 
						|
                                                      eUnit_CSSValuePair);
 | 
						|
          break;
 | 
						|
        }
 | 
						|
 | 
						|
        case eCSSProperty_stroke_dasharray: {
 | 
						|
          const nsStyleSVG *svg = static_cast<const nsStyleSVG*>(styleStruct);
 | 
						|
          if (!svg->mStrokeDasharray.IsEmpty()) {
 | 
						|
            nsAutoPtr<nsCSSValueList> result;
 | 
						|
            nsCSSValueList **resultTail = getter_Transfers(result);
 | 
						|
            for (uint32_t i = 0, i_end = svg->mStrokeDasharray.Length();
 | 
						|
                 i != i_end; ++i) {
 | 
						|
              nsCSSValueList *item = new nsCSSValueList;
 | 
						|
              *resultTail = item;
 | 
						|
              resultTail = &item->mNext;
 | 
						|
 | 
						|
              const nsStyleCoord &coord = svg->mStrokeDasharray[i];
 | 
						|
              nsCSSValue &value = item->mValue;
 | 
						|
              switch (coord.GetUnit()) {
 | 
						|
                case eStyleUnit_Coord:
 | 
						|
                  // Number means the same thing as length; we want to
 | 
						|
                  // animate them the same way.  Normalize both to number
 | 
						|
                  // since it has more accuracy (float vs nscoord).
 | 
						|
                  value.SetFloatValue(nsPresContext::
 | 
						|
                    AppUnitsToFloatCSSPixels(coord.GetCoordValue()),
 | 
						|
                    eCSSUnit_Number);
 | 
						|
                  break;
 | 
						|
                case eStyleUnit_Factor:
 | 
						|
                  value.SetFloatValue(coord.GetFactorValue(),
 | 
						|
                                      eCSSUnit_Number);
 | 
						|
                  break;
 | 
						|
                case eStyleUnit_Percent:
 | 
						|
                  value.SetPercentValue(coord.GetPercentValue());
 | 
						|
                  break;
 | 
						|
                default:
 | 
						|
                  MOZ_ASSERT(false, "unexpected unit");
 | 
						|
                  return false;
 | 
						|
              }
 | 
						|
            }
 | 
						|
            aComputedValue.SetAndAdoptCSSValueListValue(result.forget(),
 | 
						|
                                                        eUnit_Dasharray);
 | 
						|
          } else if (svg->StrokeDasharrayFromObject()) {
 | 
						|
            // An empty dasharray with StrokeDasharrayFromObject() == true
 | 
						|
            // corresponds to the "context-value" keyword.
 | 
						|
            aComputedValue.SetIntValue(NS_STYLE_STROKE_PROP_CONTEXT_VALUE,
 | 
						|
                                       eUnit_Enumerated);
 | 
						|
          } else {
 | 
						|
            // Otherwise, an empty dasharray corresponds to the "none" keyword.
 | 
						|
            aComputedValue.SetNoneValue();
 | 
						|
          }
 | 
						|
          break;
 | 
						|
        }
 | 
						|
 | 
						|
        case eCSSProperty_font_stretch: {
 | 
						|
          int16_t stretch =
 | 
						|
            static_cast<const nsStyleFont*>(styleStruct)->mFont.stretch;
 | 
						|
          static_assert(NS_STYLE_FONT_STRETCH_ULTRA_CONDENSED == -4 &&
 | 
						|
                        NS_STYLE_FONT_STRETCH_ULTRA_EXPANDED == 4,
 | 
						|
                        "font stretch constants not as expected");
 | 
						|
          if (stretch < NS_STYLE_FONT_STRETCH_ULTRA_CONDENSED ||
 | 
						|
              stretch > NS_STYLE_FONT_STRETCH_ULTRA_EXPANDED) {
 | 
						|
            return false;
 | 
						|
          }
 | 
						|
          aComputedValue.SetIntValue(stretch, eUnit_Enumerated);
 | 
						|
          return true;
 | 
						|
        }
 | 
						|
 | 
						|
        case eCSSProperty_font_weight: {
 | 
						|
          uint16_t weight =
 | 
						|
            static_cast<const nsStyleFont*>(styleStruct)->mFont.weight;
 | 
						|
          if (weight % 100 != 0) {
 | 
						|
            return false;
 | 
						|
          }
 | 
						|
          aComputedValue.SetIntValue(weight, eUnit_Integer);
 | 
						|
          return true;
 | 
						|
        }
 | 
						|
 | 
						|
        case eCSSProperty__moz_image_region: {
 | 
						|
          const nsStyleList *list =
 | 
						|
            static_cast<const nsStyleList*>(styleStruct);
 | 
						|
          const nsRect &srect = list->mImageRegion;
 | 
						|
          if (srect.IsEmpty()) {
 | 
						|
            aComputedValue.SetAutoValue();
 | 
						|
            break;
 | 
						|
          }
 | 
						|
 | 
						|
          nsCSSRect *vrect = new nsCSSRect;
 | 
						|
          vrect->mLeft.SetIntegerCoordValue(srect.x);
 | 
						|
          vrect->mTop.SetIntegerCoordValue(srect.y);
 | 
						|
          vrect->mRight.SetIntegerCoordValue(srect.XMost());
 | 
						|
          vrect->mBottom.SetIntegerCoordValue(srect.YMost());
 | 
						|
          aComputedValue.SetAndAdoptCSSRectValue(vrect, eUnit_CSSRect);
 | 
						|
          break;
 | 
						|
        }
 | 
						|
 | 
						|
        case eCSSProperty_clip: {
 | 
						|
          const nsStyleEffects* effects =
 | 
						|
            static_cast<const nsStyleEffects*>(styleStruct);
 | 
						|
          if (!(effects->mClipFlags & NS_STYLE_CLIP_RECT)) {
 | 
						|
            aComputedValue.SetAutoValue();
 | 
						|
          } else {
 | 
						|
            nsCSSRect *vrect = new nsCSSRect;
 | 
						|
            const nsRect &srect = effects->mClip;
 | 
						|
            if (effects->mClipFlags & NS_STYLE_CLIP_TOP_AUTO) {
 | 
						|
              vrect->mTop.SetAutoValue();
 | 
						|
            } else {
 | 
						|
              vrect->mTop.SetIntegerCoordValue(srect.y);
 | 
						|
            }
 | 
						|
            if (effects->mClipFlags & NS_STYLE_CLIP_RIGHT_AUTO) {
 | 
						|
              vrect->mRight.SetAutoValue();
 | 
						|
            } else {
 | 
						|
              vrect->mRight.SetIntegerCoordValue(srect.XMost());
 | 
						|
            }
 | 
						|
            if (effects->mClipFlags & NS_STYLE_CLIP_BOTTOM_AUTO) {
 | 
						|
              vrect->mBottom.SetAutoValue();
 | 
						|
            } else {
 | 
						|
              vrect->mBottom.SetIntegerCoordValue(srect.YMost());
 | 
						|
            }
 | 
						|
            if (effects->mClipFlags & NS_STYLE_CLIP_LEFT_AUTO) {
 | 
						|
              vrect->mLeft.SetAutoValue();
 | 
						|
            } else {
 | 
						|
              vrect->mLeft.SetIntegerCoordValue(srect.x);
 | 
						|
            }
 | 
						|
            aComputedValue.SetAndAdoptCSSRectValue(vrect, eUnit_CSSRect);
 | 
						|
          }
 | 
						|
          break;
 | 
						|
        }
 | 
						|
 | 
						|
        case eCSSProperty_object_position: {
 | 
						|
          const nsStylePosition* stylePos =
 | 
						|
            static_cast<const nsStylePosition*>(styleStruct);
 | 
						|
 | 
						|
          nsAutoPtr<nsCSSValue> val(new nsCSSValue);
 | 
						|
          SetPositionValue(stylePos->mObjectPosition, *val);
 | 
						|
 | 
						|
          aComputedValue.SetAndAdoptCSSValueValue(val.forget(),
 | 
						|
                                                  eUnit_ObjectPosition);
 | 
						|
          break;
 | 
						|
        }
 | 
						|
 | 
						|
        case eCSSProperty_background_position_x: {
 | 
						|
          const nsStyleImageLayers& layers =
 | 
						|
            static_cast<const nsStyleBackground*>(styleStruct)->mImage;
 | 
						|
          ExtractImageLayerPositionXList(layers, aComputedValue);
 | 
						|
          break;
 | 
						|
        }
 | 
						|
        case eCSSProperty_background_position_y: {
 | 
						|
          const nsStyleImageLayers& layers =
 | 
						|
            static_cast<const nsStyleBackground*>(styleStruct)->mImage;
 | 
						|
          ExtractImageLayerPositionYList(layers, aComputedValue);
 | 
						|
          break;
 | 
						|
        }
 | 
						|
 | 
						|
        case eCSSProperty_mask_position_x: {
 | 
						|
          const nsStyleImageLayers& layers =
 | 
						|
            static_cast<const nsStyleSVGReset*>(styleStruct)->mMask;
 | 
						|
          ExtractImageLayerPositionXList(layers, aComputedValue);
 | 
						|
          break;
 | 
						|
        }
 | 
						|
 | 
						|
        case eCSSProperty_mask_position_y: {
 | 
						|
          const nsStyleImageLayers& layers =
 | 
						|
            static_cast<const nsStyleSVGReset*>(styleStruct)->mMask;
 | 
						|
          ExtractImageLayerPositionYList(layers, aComputedValue);
 | 
						|
 | 
						|
          break;
 | 
						|
        }
 | 
						|
 | 
						|
        case eCSSProperty_background_size: {
 | 
						|
          const nsStyleImageLayers& layers =
 | 
						|
            static_cast<const nsStyleBackground*>(styleStruct)->mImage;
 | 
						|
          ExtractImageLayerSizePairList(layers, aComputedValue);
 | 
						|
          break;
 | 
						|
        }
 | 
						|
 | 
						|
        case eCSSProperty_mask_size: {
 | 
						|
          const nsStyleImageLayers& layers =
 | 
						|
            static_cast<const nsStyleSVGReset*>(styleStruct)->mMask;
 | 
						|
          ExtractImageLayerSizePairList(layers, aComputedValue);
 | 
						|
          break;
 | 
						|
        }
 | 
						|
 | 
						|
        case eCSSProperty_clip_path: {
 | 
						|
          const nsStyleSVGReset* svgReset =
 | 
						|
            static_cast<const nsStyleSVGReset*>(styleStruct);
 | 
						|
          if (!ExtractComputedValueFromShapeSource(svgReset->mClipPath,
 | 
						|
                                                   aComputedValue)) {
 | 
						|
            return false;
 | 
						|
          }
 | 
						|
          break;
 | 
						|
        }
 | 
						|
 | 
						|
        case eCSSProperty_shape_outside: {
 | 
						|
          const nsStyleDisplay* styleDisplay =
 | 
						|
            static_cast<const nsStyleDisplay*>(styleStruct);
 | 
						|
          if (!ExtractComputedValueFromShapeSource(styleDisplay->mShapeOutside,
 | 
						|
                                                   aComputedValue)) {
 | 
						|
            return false;
 | 
						|
          };
 | 
						|
          break;
 | 
						|
        }
 | 
						|
 | 
						|
        case eCSSProperty_filter: {
 | 
						|
          const nsStyleEffects* effects =
 | 
						|
            static_cast<const nsStyleEffects*>(styleStruct);
 | 
						|
          const nsTArray<nsStyleFilter>& filters = effects->mFilters;
 | 
						|
          nsAutoPtr<nsCSSValueList> result;
 | 
						|
          nsCSSValueList **resultTail = getter_Transfers(result);
 | 
						|
          for (uint32_t i = 0; i < filters.Length(); ++i) {
 | 
						|
            nsCSSValueList *item = new nsCSSValueList;
 | 
						|
            *resultTail = item;
 | 
						|
            resultTail = &item->mNext;
 | 
						|
            const nsStyleFilter& filter = filters[i];
 | 
						|
            int32_t type = filter.GetType();
 | 
						|
            if (type == NS_STYLE_FILTER_URL) {
 | 
						|
              item->mValue.SetURLValue(filter.GetURL());
 | 
						|
            } else {
 | 
						|
              nsCSSKeyword functionName =
 | 
						|
                nsCSSProps::ValueToKeywordEnum(type,
 | 
						|
                  nsCSSProps::kFilterFunctionKTable);
 | 
						|
              nsCSSValue::Array* filterArray =
 | 
						|
                item->mValue.InitFunction(functionName, 1);
 | 
						|
              if (type >= NS_STYLE_FILTER_BLUR && type <= NS_STYLE_FILTER_HUE_ROTATE) {
 | 
						|
                if (!StyleCoordToCSSValue(
 | 
						|
                      filter.GetFilterParameter(),
 | 
						|
                      filterArray->Item(1))) {
 | 
						|
                  return false;
 | 
						|
                }
 | 
						|
              } else if (type == NS_STYLE_FILTER_DROP_SHADOW) {
 | 
						|
                nsCSSValueList* shadowResult = filterArray->Item(1).SetListValue();
 | 
						|
                nsAutoPtr<nsCSSValueList> tmpShadowValue;
 | 
						|
                nsCSSValueList **tmpShadowResultTail = getter_Transfers(tmpShadowValue);
 | 
						|
                nsCSSShadowArray* shadowArray = filter.GetDropShadow();
 | 
						|
                MOZ_ASSERT(shadowArray->Length() == 1,
 | 
						|
                           "expected exactly one shadow");
 | 
						|
                AppendCSSShadowValue(shadowArray->ShadowAt(0),
 | 
						|
                                     tmpShadowResultTail, aProperty);
 | 
						|
                *shadowResult = *tmpShadowValue;
 | 
						|
              } else {
 | 
						|
                // We checked all possible nsStyleFilter types but
 | 
						|
                // NS_STYLE_FILTER_NULL before. We should never enter this path.
 | 
						|
                NS_NOTREACHED("no other filter functions defined");
 | 
						|
                return false;
 | 
						|
              }
 | 
						|
            }
 | 
						|
          }
 | 
						|
 | 
						|
          aComputedValue.SetAndAdoptCSSValueListValue(result.forget(),
 | 
						|
                                                      eUnit_Filter);
 | 
						|
          break;
 | 
						|
        }
 | 
						|
 | 
						|
        case eCSSProperty_transform: {
 | 
						|
          const nsStyleDisplay *display =
 | 
						|
            static_cast<const nsStyleDisplay*>(styleStruct);
 | 
						|
          nsAutoPtr<nsCSSValueList> result;
 | 
						|
          RefPtr<nsCSSValueSharedList> transformList = display->GetCombinedTransform();
 | 
						|
          if (transformList) {
 | 
						|
            // Clone, and convert all lengths (not percents) to pixels.
 | 
						|
            nsCSSValueList **resultTail = getter_Transfers(result);
 | 
						|
            for (const nsCSSValueList *l = transformList->mHead;
 | 
						|
                 l; l = l->mNext) {
 | 
						|
              nsCSSValueList *clone = new nsCSSValueList;
 | 
						|
              *resultTail = clone;
 | 
						|
              resultTail = &clone->mNext;
 | 
						|
 | 
						|
              SubstitutePixelValues(aStyleContext, l->mValue, clone->mValue);
 | 
						|
            }
 | 
						|
          } else {
 | 
						|
            result = new nsCSSValueList();
 | 
						|
            result->mValue.SetNoneValue();
 | 
						|
          }
 | 
						|
 | 
						|
          aComputedValue.SetTransformValue(
 | 
						|
              new nsCSSValueSharedList(result.forget()));
 | 
						|
          break;
 | 
						|
        }
 | 
						|
 | 
						|
        case eCSSProperty__moz_window_transform: {
 | 
						|
          const nsStyleUIReset *uiReset =
 | 
						|
            static_cast<const nsStyleUIReset*>(styleStruct);
 | 
						|
          nsAutoPtr<nsCSSValueList> result;
 | 
						|
          if (uiReset->mSpecifiedWindowTransform) {
 | 
						|
            // Clone, and convert all lengths (not percents) to pixels.
 | 
						|
            nsCSSValueList **resultTail = getter_Transfers(result);
 | 
						|
            for (const nsCSSValueList *l = uiReset->mSpecifiedWindowTransform->mHead;
 | 
						|
                 l; l = l->mNext) {
 | 
						|
              nsCSSValueList *clone = new nsCSSValueList;
 | 
						|
              *resultTail = clone;
 | 
						|
              resultTail = &clone->mNext;
 | 
						|
 | 
						|
              SubstitutePixelValues(aStyleContext, l->mValue, clone->mValue);
 | 
						|
            }
 | 
						|
          } else {
 | 
						|
            result = new nsCSSValueList();
 | 
						|
            result->mValue.SetNoneValue();
 | 
						|
          }
 | 
						|
 | 
						|
          aComputedValue.SetTransformValue(
 | 
						|
              new nsCSSValueSharedList(result.forget()));
 | 
						|
          break;
 | 
						|
        }
 | 
						|
 | 
						|
        case eCSSProperty_font_variation_settings: {
 | 
						|
          auto font = static_cast<const nsStyleFont*>(styleStruct);
 | 
						|
          UniquePtr<nsCSSValuePairList> result;
 | 
						|
          if (!font->mFont.fontVariationSettings.IsEmpty()) {
 | 
						|
            // Make a new list that clones the current settings
 | 
						|
            nsCSSValuePairList* tail = nullptr;
 | 
						|
            for (auto v : font->mFont.fontVariationSettings) {
 | 
						|
              auto clone = MakeUnique<nsCSSValuePairList>();
 | 
						|
              // OpenType font tags are stored in nsFont as 32-bit unsigned
 | 
						|
              // values, but represented in CSS as 4-character ASCII strings,
 | 
						|
              // beginning with the high byte of the value. So to clone the
 | 
						|
              // tag here, we append each of its 4 bytes to a string.
 | 
						|
              nsAutoString tagString;
 | 
						|
              tagString.Append(char(v.mTag >> 24));
 | 
						|
              tagString.Append(char(v.mTag >> 16));
 | 
						|
              tagString.Append(char(v.mTag >> 8));
 | 
						|
              tagString.Append(char(v.mTag));
 | 
						|
              clone->mXValue.SetStringValue(tagString, eCSSUnit_String);
 | 
						|
              clone->mYValue.SetFloatValue(v.mValue, eCSSUnit_Number);
 | 
						|
              AppendToCSSValuePairList(result, Move(clone), &tail);
 | 
						|
            }
 | 
						|
            aComputedValue.SetAndAdoptCSSValuePairListValue(result.release());
 | 
						|
          } else {
 | 
						|
            aComputedValue.SetNormalValue();
 | 
						|
          }
 | 
						|
          break;
 | 
						|
        }
 | 
						|
 | 
						|
        default:
 | 
						|
          MOZ_ASSERT(false, "missing property implementation");
 | 
						|
          return false;
 | 
						|
      };
 | 
						|
      return true;
 | 
						|
    case eStyleAnimType_Coord: {
 | 
						|
      const nsStyleCoord& coord =
 | 
						|
        StyleDataAtOffset<nsStyleCoord>(styleStruct, ssOffset);
 | 
						|
      if (nsCSSProps::PropHasFlags(aProperty, CSS_PROPERTY_NUMBERS_ARE_PIXELS) &&
 | 
						|
          coord.GetUnit() == eStyleUnit_Coord) {
 | 
						|
        // For SVG properties where number means the same thing as length,
 | 
						|
        // we want to animate them the same way.  Normalize both to number
 | 
						|
        // since it has more accuracy (float vs nscoord).
 | 
						|
        aComputedValue.SetFloatValue(nsPresContext::
 | 
						|
          AppUnitsToFloatCSSPixels(coord.GetCoordValue()));
 | 
						|
        return true;
 | 
						|
      }
 | 
						|
      return StyleCoordToValue(coord, aComputedValue);
 | 
						|
    }
 | 
						|
    case eStyleAnimType_Sides_Top:
 | 
						|
    case eStyleAnimType_Sides_Right:
 | 
						|
    case eStyleAnimType_Sides_Bottom:
 | 
						|
    case eStyleAnimType_Sides_Left: {
 | 
						|
      static_assert(
 | 
						|
       eSideTop    == eStyleAnimType_Sides_Top   -eStyleAnimType_Sides_Top &&
 | 
						|
       eSideRight  == eStyleAnimType_Sides_Right -eStyleAnimType_Sides_Top &&
 | 
						|
       eSideBottom == eStyleAnimType_Sides_Bottom-eStyleAnimType_Sides_Top &&
 | 
						|
       eSideLeft   == eStyleAnimType_Sides_Left  -eStyleAnimType_Sides_Top,
 | 
						|
       "box side constants out of sync with animation side constants");
 | 
						|
 | 
						|
      const nsStyleCoord &coord =
 | 
						|
        StyleDataAtOffset<nsStyleSides>(styleStruct, ssOffset).
 | 
						|
          Get(mozilla::Side(animType - eStyleAnimType_Sides_Top));
 | 
						|
      return StyleCoordToValue(coord, aComputedValue);
 | 
						|
    }
 | 
						|
    case eStyleAnimType_Corner_TopLeft:
 | 
						|
    case eStyleAnimType_Corner_TopRight:
 | 
						|
    case eStyleAnimType_Corner_BottomRight:
 | 
						|
    case eStyleAnimType_Corner_BottomLeft: {
 | 
						|
      static_assert(
 | 
						|
       eCornerTopLeft     == eStyleAnimType_Corner_TopLeft -
 | 
						|
                             eStyleAnimType_Corner_TopLeft        &&
 | 
						|
       eCornerTopRight    == eStyleAnimType_Corner_TopRight -
 | 
						|
                             eStyleAnimType_Corner_TopLeft        &&
 | 
						|
       eCornerBottomRight == eStyleAnimType_Corner_BottomRight -
 | 
						|
                             eStyleAnimType_Corner_TopLeft        &&
 | 
						|
       eCornerBottomLeft  == eStyleAnimType_Corner_BottomLeft -
 | 
						|
                             eStyleAnimType_Corner_TopLeft,
 | 
						|
       "box corner constants out of sync with animation corner constants");
 | 
						|
 | 
						|
      const nsStyleCorners& corners =
 | 
						|
        StyleDataAtOffset<nsStyleCorners>(styleStruct, ssOffset);
 | 
						|
      Corner fullCorner = Corner(animType - eStyleAnimType_Corner_TopLeft);
 | 
						|
      const nsStyleCoord &horiz =
 | 
						|
        corners.Get(FullToHalfCorner(fullCorner, false));
 | 
						|
      const nsStyleCoord &vert =
 | 
						|
        corners.Get(FullToHalfCorner(fullCorner, true));
 | 
						|
      nsAutoPtr<nsCSSValuePair> pair(new nsCSSValuePair);
 | 
						|
      if (!StyleCoordToCSSValue(horiz, pair->mXValue) ||
 | 
						|
          !StyleCoordToCSSValue(vert, pair->mYValue)) {
 | 
						|
        return false;
 | 
						|
      }
 | 
						|
      aComputedValue.SetAndAdoptCSSValuePairValue(pair.forget(),
 | 
						|
                                                  eUnit_CSSValuePair);
 | 
						|
      return true;
 | 
						|
    }
 | 
						|
    case eStyleAnimType_nscoord:
 | 
						|
      aComputedValue.SetCoordValue(
 | 
						|
        StyleDataAtOffset<nscoord>(styleStruct, ssOffset));
 | 
						|
      return true;
 | 
						|
    case eStyleAnimType_float:
 | 
						|
      aComputedValue.SetFloatValue(
 | 
						|
        StyleDataAtOffset<float>(styleStruct, ssOffset));
 | 
						|
      if (aProperty == eCSSProperty_font_size_adjust &&
 | 
						|
          aComputedValue.GetFloatValue() == -1.0f) {
 | 
						|
        // In nsStyleFont, we set mFont.sizeAdjust to -1.0 to represent
 | 
						|
        // font-size-adjust: none.  Here, we have to treat this as a keyword
 | 
						|
        // instead of a float value, to make sure we don't end up doing
 | 
						|
        // interpolation with it.
 | 
						|
        aComputedValue.SetNoneValue();
 | 
						|
      }
 | 
						|
      return true;
 | 
						|
    case eStyleAnimType_Color:
 | 
						|
      aComputedValue.SetColorValue(
 | 
						|
        StyleDataAtOffset<nscolor>(styleStruct, ssOffset));
 | 
						|
      return true;
 | 
						|
    case eStyleAnimType_ComplexColor: {
 | 
						|
      auto& color = StyleDataAtOffset<StyleComplexColor>(styleStruct, ssOffset);
 | 
						|
      if (color.mIsAuto) {
 | 
						|
        aComputedValue.SetAutoValue();
 | 
						|
      } else {
 | 
						|
        aComputedValue.SetComplexColorValue(color);
 | 
						|
      }
 | 
						|
      return true;
 | 
						|
    }
 | 
						|
    case eStyleAnimType_PaintServer: {
 | 
						|
      const nsStyleSVGPaint& paint =
 | 
						|
        StyleDataAtOffset<nsStyleSVGPaint>(styleStruct, ssOffset);
 | 
						|
      switch (paint.Type()) {
 | 
						|
        case eStyleSVGPaintType_Color:
 | 
						|
          aComputedValue.SetColorValue(paint.GetColor());
 | 
						|
          return true;
 | 
						|
        case eStyleSVGPaintType_Server: {
 | 
						|
          css::URLValue* url = paint.GetPaintServer();
 | 
						|
          if (!url) {
 | 
						|
            NS_WARNING("Null paint server");
 | 
						|
            return false;
 | 
						|
          }
 | 
						|
          if (paint.GetFallbackType() != eStyleSVGFallbackType_NotSet) {
 | 
						|
            nsAutoPtr<nsCSSValuePair> pair(new nsCSSValuePair);
 | 
						|
            pair->mXValue.SetURLValue(url);
 | 
						|
            SetFallbackValue(pair, paint);
 | 
						|
            aComputedValue.SetAndAdoptCSSValuePairValue(pair.forget(),
 | 
						|
                                                        eUnit_CSSValuePair);
 | 
						|
          } else {
 | 
						|
            auto result = MakeUnique<nsCSSValue>();
 | 
						|
            result->SetURLValue(url);
 | 
						|
            aComputedValue.SetAndAdoptCSSValueValue(
 | 
						|
              result.release(), eUnit_URL);
 | 
						|
          }
 | 
						|
          return true;
 | 
						|
        }
 | 
						|
        case eStyleSVGPaintType_ContextFill:
 | 
						|
        case eStyleSVGPaintType_ContextStroke: {
 | 
						|
          int32_t value = paint.Type() == eStyleSVGPaintType_ContextFill ?
 | 
						|
                            NS_COLOR_CONTEXT_FILL : NS_COLOR_CONTEXT_STROKE;
 | 
						|
          if (paint.GetFallbackType() != eStyleSVGFallbackType_NotSet) {
 | 
						|
            nsAutoPtr<nsCSSValuePair> pair(new nsCSSValuePair);
 | 
						|
            pair->mXValue.SetIntValue(value, eCSSUnit_Enumerated);
 | 
						|
            SetFallbackValue(pair, paint);
 | 
						|
            aComputedValue.SetAndAdoptCSSValuePairValue(pair.forget(),
 | 
						|
                                                        eUnit_CSSValuePair);
 | 
						|
          } else {
 | 
						|
            aComputedValue.SetIntValue(value, eUnit_Enumerated);
 | 
						|
          }
 | 
						|
          return true;
 | 
						|
        }
 | 
						|
        default:
 | 
						|
          MOZ_ASSERT(paint.Type() == eStyleSVGPaintType_None,
 | 
						|
                     "Unexpected SVG paint type");
 | 
						|
          aComputedValue.SetNoneValue();
 | 
						|
          return true;
 | 
						|
      }
 | 
						|
    }
 | 
						|
    case eStyleAnimType_Shadow: {
 | 
						|
      const nsCSSShadowArray* shadowArray =
 | 
						|
        StyleDataAtOffset<RefPtr<nsCSSShadowArray>>(styleStruct, ssOffset);
 | 
						|
      if (!shadowArray) {
 | 
						|
        aComputedValue.SetAndAdoptCSSValueListValue(nullptr, eUnit_Shadow);
 | 
						|
        return true;
 | 
						|
      }
 | 
						|
      nsAutoPtr<nsCSSValueList> result;
 | 
						|
      nsCSSValueList **resultTail = getter_Transfers(result);
 | 
						|
      for (uint32_t i = 0, i_end = shadowArray->Length(); i < i_end; ++i) {
 | 
						|
        AppendCSSShadowValue(shadowArray->ShadowAt(i), resultTail, aProperty);
 | 
						|
      }
 | 
						|
      aComputedValue.SetAndAdoptCSSValueListValue(result.forget(),
 | 
						|
                                                  eUnit_Shadow);
 | 
						|
      return true;
 | 
						|
    }
 | 
						|
    case eStyleAnimType_Discrete: {
 | 
						|
      if (aProperty == eCSSProperty_visibility) {
 | 
						|
        aComputedValue.SetIntValue(
 | 
						|
          static_cast<const nsStyleVisibility*>(styleStruct)->mVisible,
 | 
						|
          eUnit_Visibility);
 | 
						|
        return true;
 | 
						|
      }
 | 
						|
      if (aStyleContext->IsServo()) {
 | 
						|
        NS_ERROR("stylo: extracting discretely animated values not supported");
 | 
						|
        return false;
 | 
						|
      }
 | 
						|
      auto cssValue = MakeUnique<nsCSSValue>(eCSSUnit_Unset);
 | 
						|
      aStyleContext->RuleNode()->GetDiscretelyAnimatedCSSValue(aProperty,
 | 
						|
                                                               cssValue.get());
 | 
						|
      aComputedValue.SetAndAdoptCSSValueValue(cssValue.release(),
 | 
						|
                                              eUnit_DiscreteCSSValue);
 | 
						|
      return true;
 | 
						|
    }
 | 
						|
    case eStyleAnimType_None:
 | 
						|
      NS_NOTREACHED("shouldn't use on non-animatable properties");
 | 
						|
  }
 | 
						|
  return false;
 | 
						|
}
 | 
						|
 | 
						|
Size
 | 
						|
StyleAnimationValue::GetScaleValue(const nsIFrame* aForFrame) const
 | 
						|
{
 | 
						|
  MOZ_ASSERT(GetUnit() == StyleAnimationValue::eUnit_Transform);
 | 
						|
 | 
						|
  nsCSSValueSharedList* list = GetCSSValueSharedListValue();
 | 
						|
  return nsStyleTransformMatrix::GetScaleValue(list, aForFrame);
 | 
						|
}
 | 
						|
 | 
						|
StyleAnimationValue::StyleAnimationValue(int32_t aInt, Unit aUnit,
 | 
						|
                                         IntegerConstructorType)
 | 
						|
{
 | 
						|
  NS_ASSERTION(IsIntUnit(aUnit), "unit must be of integer type");
 | 
						|
  mUnit = aUnit;
 | 
						|
  mValue.mInt = aInt;
 | 
						|
}
 | 
						|
 | 
						|
StyleAnimationValue::StyleAnimationValue(nscoord aLength, CoordConstructorType)
 | 
						|
{
 | 
						|
  mUnit = eUnit_Coord;
 | 
						|
  mValue.mCoord = aLength;
 | 
						|
}
 | 
						|
 | 
						|
StyleAnimationValue::StyleAnimationValue(float aPercent,
 | 
						|
                                         PercentConstructorType)
 | 
						|
{
 | 
						|
  mUnit = eUnit_Percent;
 | 
						|
  mValue.mFloat = aPercent;
 | 
						|
  MOZ_ASSERT(!mozilla::IsNaN(mValue.mFloat));
 | 
						|
}
 | 
						|
 | 
						|
StyleAnimationValue::StyleAnimationValue(float aFloat, FloatConstructorType)
 | 
						|
{
 | 
						|
  mUnit = eUnit_Float;
 | 
						|
  mValue.mFloat = aFloat;
 | 
						|
  MOZ_ASSERT(!mozilla::IsNaN(mValue.mFloat));
 | 
						|
}
 | 
						|
 | 
						|
StyleAnimationValue::StyleAnimationValue(nscolor aColor, ColorConstructorType)
 | 
						|
{
 | 
						|
  mUnit = eUnit_Color;
 | 
						|
  mValue.mCSSValue = new nsCSSValue();
 | 
						|
  mValue.mCSSValue->SetColorValue(aColor);
 | 
						|
}
 | 
						|
 | 
						|
StyleAnimationValue&
 | 
						|
StyleAnimationValue::operator=(const StyleAnimationValue& aOther)
 | 
						|
{
 | 
						|
  if (this == &aOther) {
 | 
						|
    return *this;
 | 
						|
  }
 | 
						|
 | 
						|
  FreeValue();
 | 
						|
 | 
						|
  mUnit = aOther.mUnit;
 | 
						|
  switch (mUnit) {
 | 
						|
    case eUnit_Null:
 | 
						|
    case eUnit_Normal:
 | 
						|
    case eUnit_Auto:
 | 
						|
    case eUnit_None:
 | 
						|
    case eUnit_CurrentColor:
 | 
						|
      break;
 | 
						|
    case eUnit_Enumerated:
 | 
						|
    case eUnit_Visibility:
 | 
						|
    case eUnit_Integer:
 | 
						|
      mValue.mInt = aOther.mValue.mInt;
 | 
						|
      break;
 | 
						|
    case eUnit_Coord:
 | 
						|
      mValue.mCoord = aOther.mValue.mCoord;
 | 
						|
      break;
 | 
						|
    case eUnit_Percent:
 | 
						|
    case eUnit_Float:
 | 
						|
      mValue.mFloat = aOther.mValue.mFloat;
 | 
						|
      MOZ_ASSERT(!mozilla::IsNaN(mValue.mFloat));
 | 
						|
      break;
 | 
						|
    case eUnit_Calc:
 | 
						|
    case eUnit_Color:
 | 
						|
    case eUnit_ObjectPosition:
 | 
						|
    case eUnit_URL:
 | 
						|
    case eUnit_DiscreteCSSValue:
 | 
						|
      MOZ_ASSERT(IsCSSValueUnit(mUnit),
 | 
						|
                 "This clause is for handling nsCSSValue-backed units");
 | 
						|
      MOZ_ASSERT(aOther.mValue.mCSSValue, "values may not be null");
 | 
						|
      mValue.mCSSValue = new nsCSSValue(*aOther.mValue.mCSSValue);
 | 
						|
      break;
 | 
						|
    case eUnit_CSSValuePair:
 | 
						|
      MOZ_ASSERT(aOther.mValue.mCSSValuePair,
 | 
						|
                 "value pairs may not be null");
 | 
						|
      mValue.mCSSValuePair = new nsCSSValuePair(*aOther.mValue.mCSSValuePair);
 | 
						|
      break;
 | 
						|
    case eUnit_CSSValueTriplet:
 | 
						|
      MOZ_ASSERT(aOther.mValue.mCSSValueTriplet,
 | 
						|
                 "value triplets may not be null");
 | 
						|
      mValue.mCSSValueTriplet = new nsCSSValueTriplet(*aOther.mValue.mCSSValueTriplet);
 | 
						|
      break;
 | 
						|
    case eUnit_CSSRect:
 | 
						|
      MOZ_ASSERT(aOther.mValue.mCSSRect, "rects may not be null");
 | 
						|
      mValue.mCSSRect = new nsCSSRect(*aOther.mValue.mCSSRect);
 | 
						|
      break;
 | 
						|
    case eUnit_Dasharray:
 | 
						|
    case eUnit_Shadow:
 | 
						|
    case eUnit_Filter:
 | 
						|
    case eUnit_BackgroundPositionCoord:
 | 
						|
      MOZ_ASSERT(mUnit == eUnit_Shadow || mUnit == eUnit_Filter ||
 | 
						|
                 aOther.mValue.mCSSValueList,
 | 
						|
                 "value lists other than shadows and filters may not be null");
 | 
						|
      if (aOther.mValue.mCSSValueList) {
 | 
						|
        mValue.mCSSValueList = aOther.mValue.mCSSValueList->Clone();
 | 
						|
      } else {
 | 
						|
        mValue.mCSSValueList = nullptr;
 | 
						|
      }
 | 
						|
      break;
 | 
						|
    case eUnit_Shape:
 | 
						|
      MOZ_ASSERT(aOther.mValue.mCSSValueArray,
 | 
						|
                 "value arrays may not be null");
 | 
						|
      mValue.mCSSValueArray = aOther.mValue.mCSSValueArray;
 | 
						|
      mValue.mCSSValueArray->AddRef();
 | 
						|
      break;
 | 
						|
    case eUnit_Transform:
 | 
						|
      mValue.mCSSValueSharedList = aOther.mValue.mCSSValueSharedList;
 | 
						|
      mValue.mCSSValueSharedList->AddRef();
 | 
						|
      break;
 | 
						|
    case eUnit_CSSValuePairList:
 | 
						|
      MOZ_ASSERT(aOther.mValue.mCSSValuePairList,
 | 
						|
                 "value pair lists may not be null");
 | 
						|
      mValue.mCSSValuePairList = aOther.mValue.mCSSValuePairList->Clone();
 | 
						|
      break;
 | 
						|
    case eUnit_UnparsedString:
 | 
						|
      MOZ_ASSERT(aOther.mValue.mString, "expecting non-null string");
 | 
						|
      mValue.mString = aOther.mValue.mString;
 | 
						|
      mValue.mString->AddRef();
 | 
						|
      break;
 | 
						|
    case eUnit_ComplexColor:
 | 
						|
      MOZ_ASSERT(aOther.mValue.mComplexColor);
 | 
						|
      mValue.mComplexColor = aOther.mValue.mComplexColor;
 | 
						|
      mValue.mComplexColor->AddRef();
 | 
						|
      break;
 | 
						|
  }
 | 
						|
 | 
						|
  return *this;
 | 
						|
}
 | 
						|
 | 
						|
void
 | 
						|
StyleAnimationValue::SetNormalValue()
 | 
						|
{
 | 
						|
  FreeValue();
 | 
						|
  mUnit = eUnit_Normal;
 | 
						|
}
 | 
						|
 | 
						|
void
 | 
						|
StyleAnimationValue::SetAutoValue()
 | 
						|
{
 | 
						|
  FreeValue();
 | 
						|
  mUnit = eUnit_Auto;
 | 
						|
}
 | 
						|
 | 
						|
void
 | 
						|
StyleAnimationValue::SetNoneValue()
 | 
						|
{
 | 
						|
  FreeValue();
 | 
						|
  mUnit = eUnit_None;
 | 
						|
}
 | 
						|
 | 
						|
void
 | 
						|
StyleAnimationValue::SetIntValue(int32_t aInt, Unit aUnit)
 | 
						|
{
 | 
						|
  NS_ASSERTION(IsIntUnit(aUnit), "unit must be of integer type");
 | 
						|
  FreeValue();
 | 
						|
  mUnit = aUnit;
 | 
						|
  mValue.mInt = aInt;
 | 
						|
}
 | 
						|
 | 
						|
void
 | 
						|
StyleAnimationValue::SetCoordValue(nscoord aLength)
 | 
						|
{
 | 
						|
  FreeValue();
 | 
						|
  mUnit = eUnit_Coord;
 | 
						|
  mValue.mCoord = aLength;
 | 
						|
}
 | 
						|
 | 
						|
void
 | 
						|
StyleAnimationValue::SetPercentValue(float aPercent)
 | 
						|
{
 | 
						|
  FreeValue();
 | 
						|
  mUnit = eUnit_Percent;
 | 
						|
  mValue.mFloat = aPercent;
 | 
						|
  MOZ_ASSERT(!mozilla::IsNaN(mValue.mFloat));
 | 
						|
}
 | 
						|
 | 
						|
void
 | 
						|
StyleAnimationValue::SetFloatValue(float aFloat)
 | 
						|
{
 | 
						|
  FreeValue();
 | 
						|
  mUnit = eUnit_Float;
 | 
						|
  mValue.mFloat = aFloat;
 | 
						|
  MOZ_ASSERT(!mozilla::IsNaN(mValue.mFloat));
 | 
						|
}
 | 
						|
 | 
						|
void
 | 
						|
StyleAnimationValue::SetColorValue(nscolor aColor)
 | 
						|
{
 | 
						|
  FreeValue();
 | 
						|
  mUnit = eUnit_Color;
 | 
						|
  mValue.mCSSValue = new nsCSSValue();
 | 
						|
  mValue.mCSSValue->SetColorValue(aColor);
 | 
						|
}
 | 
						|
 | 
						|
void
 | 
						|
StyleAnimationValue::SetCurrentColorValue()
 | 
						|
{
 | 
						|
  FreeValue();
 | 
						|
  mUnit = eUnit_CurrentColor;
 | 
						|
}
 | 
						|
 | 
						|
void
 | 
						|
StyleAnimationValue::SetComplexColorValue(const StyleComplexColor& aColor)
 | 
						|
{
 | 
						|
  if (aColor.mIsAuto) {
 | 
						|
    SetAutoValue();
 | 
						|
  } else if (aColor.IsCurrentColor()) {
 | 
						|
    SetCurrentColorValue();
 | 
						|
  } else if (aColor.IsNumericColor()) {
 | 
						|
    SetColorValue(aColor.mColor);
 | 
						|
  } else {
 | 
						|
    SetComplexColorValue(do_AddRef(new ComplexColorValue(aColor)));
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
void
 | 
						|
StyleAnimationValue::SetComplexColorValue(
 | 
						|
  already_AddRefed<ComplexColorValue> aValue)
 | 
						|
{
 | 
						|
  FreeValue();
 | 
						|
  mUnit = eUnit_ComplexColor;
 | 
						|
  mValue.mComplexColor = aValue.take();
 | 
						|
}
 | 
						|
 | 
						|
void
 | 
						|
StyleAnimationValue::SetUnparsedStringValue(const nsString& aString)
 | 
						|
{
 | 
						|
  FreeValue();
 | 
						|
  mUnit = eUnit_UnparsedString;
 | 
						|
  mValue.mString = nsCSSValue::BufferFromString(aString).take();
 | 
						|
}
 | 
						|
 | 
						|
void
 | 
						|
StyleAnimationValue::SetAndAdoptCSSValueValue(nsCSSValue *aValue,
 | 
						|
                                              Unit aUnit)
 | 
						|
{
 | 
						|
  FreeValue();
 | 
						|
  MOZ_ASSERT(IsCSSValueUnit(aUnit), "bad unit");
 | 
						|
  MOZ_ASSERT(aValue != nullptr, "values may not be null");
 | 
						|
  mUnit = aUnit;
 | 
						|
  mValue.mCSSValue = aValue; // take ownership
 | 
						|
}
 | 
						|
 | 
						|
void
 | 
						|
StyleAnimationValue::SetAndAdoptCSSValuePairValue(nsCSSValuePair *aValuePair,
 | 
						|
                                                  Unit aUnit)
 | 
						|
{
 | 
						|
  FreeValue();
 | 
						|
  MOZ_ASSERT(IsCSSValuePairUnit(aUnit), "bad unit");
 | 
						|
  MOZ_ASSERT(aValuePair != nullptr, "value pairs may not be null");
 | 
						|
  mUnit = aUnit;
 | 
						|
  mValue.mCSSValuePair = aValuePair; // take ownership
 | 
						|
}
 | 
						|
 | 
						|
void
 | 
						|
StyleAnimationValue::SetAndAdoptCSSValueTripletValue(
 | 
						|
                       nsCSSValueTriplet *aValueTriplet, Unit aUnit)
 | 
						|
{
 | 
						|
  FreeValue();
 | 
						|
  MOZ_ASSERT(IsCSSValueTripletUnit(aUnit), "bad unit");
 | 
						|
  MOZ_ASSERT(aValueTriplet != nullptr, "value pairs may not be null");
 | 
						|
  mUnit = aUnit;
 | 
						|
  mValue.mCSSValueTriplet = aValueTriplet; // take ownership
 | 
						|
}
 | 
						|
 | 
						|
void
 | 
						|
StyleAnimationValue::SetAndAdoptCSSRectValue(nsCSSRect *aRect, Unit aUnit)
 | 
						|
{
 | 
						|
  FreeValue();
 | 
						|
  MOZ_ASSERT(IsCSSRectUnit(aUnit), "bad unit");
 | 
						|
  MOZ_ASSERT(aRect != nullptr, "value pairs may not be null");
 | 
						|
  mUnit = aUnit;
 | 
						|
  mValue.mCSSRect = aRect; // take ownership
 | 
						|
}
 | 
						|
 | 
						|
void
 | 
						|
StyleAnimationValue::SetCSSValueArrayValue(nsCSSValue::Array* aValue,
 | 
						|
                                           Unit aUnit)
 | 
						|
{
 | 
						|
  FreeValue();
 | 
						|
  MOZ_ASSERT(IsCSSValueArrayUnit(aUnit), "bad unit");
 | 
						|
  MOZ_ASSERT(aValue != nullptr,
 | 
						|
             "not currently expecting any arrays to be null");
 | 
						|
  mUnit = aUnit;
 | 
						|
  mValue.mCSSValueArray = aValue;
 | 
						|
  mValue.mCSSValueArray->AddRef();
 | 
						|
}
 | 
						|
 | 
						|
void
 | 
						|
StyleAnimationValue::SetAndAdoptCSSValueListValue(nsCSSValueList *aValueList,
 | 
						|
                                                  Unit aUnit)
 | 
						|
{
 | 
						|
  FreeValue();
 | 
						|
  MOZ_ASSERT(IsCSSValueListUnit(aUnit), "bad unit");
 | 
						|
  MOZ_ASSERT(aUnit == eUnit_Shadow || aUnit == eUnit_Filter ||
 | 
						|
             aValueList != nullptr,
 | 
						|
             "value lists other than shadows and filters may not be null");
 | 
						|
  mUnit = aUnit;
 | 
						|
  mValue.mCSSValueList = aValueList; // take ownership
 | 
						|
}
 | 
						|
 | 
						|
void
 | 
						|
StyleAnimationValue::SetTransformValue(nsCSSValueSharedList* aList)
 | 
						|
{
 | 
						|
  FreeValue();
 | 
						|
  mUnit = eUnit_Transform;
 | 
						|
  mValue.mCSSValueSharedList = aList;
 | 
						|
  mValue.mCSSValueSharedList->AddRef();
 | 
						|
}
 | 
						|
 | 
						|
void
 | 
						|
StyleAnimationValue::SetAndAdoptCSSValuePairListValue(
 | 
						|
                       nsCSSValuePairList *aValuePairList)
 | 
						|
{
 | 
						|
  FreeValue();
 | 
						|
  MOZ_ASSERT(aValuePairList, "may not be null");
 | 
						|
  mUnit = eUnit_CSSValuePairList;
 | 
						|
  mValue.mCSSValuePairList = aValuePairList; // take ownership
 | 
						|
}
 | 
						|
 | 
						|
void
 | 
						|
StyleAnimationValue::FreeValue()
 | 
						|
{
 | 
						|
  if (IsCSSValueUnit(mUnit)) {
 | 
						|
    delete mValue.mCSSValue;
 | 
						|
  } else if (IsCSSValueListUnit(mUnit)) {
 | 
						|
    delete mValue.mCSSValueList;
 | 
						|
  } else if (IsCSSValueSharedListValue(mUnit)) {
 | 
						|
    mValue.mCSSValueSharedList->Release();
 | 
						|
  } else if (IsCSSValuePairUnit(mUnit)) {
 | 
						|
    delete mValue.mCSSValuePair;
 | 
						|
  } else if (IsCSSValueTripletUnit(mUnit)) {
 | 
						|
    delete mValue.mCSSValueTriplet;
 | 
						|
  } else if (IsCSSRectUnit(mUnit)) {
 | 
						|
    delete mValue.mCSSRect;
 | 
						|
  } else if (IsCSSValuePairListUnit(mUnit)) {
 | 
						|
    delete mValue.mCSSValuePairList;
 | 
						|
  } else if (IsCSSValueArrayUnit(mUnit)) {
 | 
						|
    mValue.mCSSValueArray->Release();
 | 
						|
  } else if (IsStringUnit(mUnit)) {
 | 
						|
    MOZ_ASSERT(mValue.mString, "expecting non-null string");
 | 
						|
    mValue.mString->Release();
 | 
						|
  } else if (mUnit == eUnit_ComplexColor) {
 | 
						|
    mValue.mComplexColor->Release();
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
bool
 | 
						|
StyleAnimationValue::operator==(const StyleAnimationValue& aOther) const
 | 
						|
{
 | 
						|
  if (mUnit != aOther.mUnit) {
 | 
						|
    return false;
 | 
						|
  }
 | 
						|
 | 
						|
  switch (mUnit) {
 | 
						|
    case eUnit_Null:
 | 
						|
    case eUnit_Normal:
 | 
						|
    case eUnit_Auto:
 | 
						|
    case eUnit_None:
 | 
						|
    case eUnit_CurrentColor:
 | 
						|
      return true;
 | 
						|
    case eUnit_Enumerated:
 | 
						|
    case eUnit_Visibility:
 | 
						|
    case eUnit_Integer:
 | 
						|
      return mValue.mInt == aOther.mValue.mInt;
 | 
						|
    case eUnit_Coord:
 | 
						|
      return mValue.mCoord == aOther.mValue.mCoord;
 | 
						|
    case eUnit_Percent:
 | 
						|
    case eUnit_Float:
 | 
						|
      return mValue.mFloat == aOther.mValue.mFloat;
 | 
						|
    case eUnit_Calc:
 | 
						|
    case eUnit_Color:
 | 
						|
    case eUnit_ObjectPosition:
 | 
						|
    case eUnit_URL:
 | 
						|
    case eUnit_DiscreteCSSValue:
 | 
						|
      MOZ_ASSERT(IsCSSValueUnit(mUnit),
 | 
						|
                 "This clause is for handling nsCSSValue-backed units");
 | 
						|
      return *mValue.mCSSValue == *aOther.mValue.mCSSValue;
 | 
						|
    case eUnit_CSSValuePair:
 | 
						|
      return *mValue.mCSSValuePair == *aOther.mValue.mCSSValuePair;
 | 
						|
    case eUnit_CSSValueTriplet:
 | 
						|
      return *mValue.mCSSValueTriplet == *aOther.mValue.mCSSValueTriplet;
 | 
						|
    case eUnit_CSSRect:
 | 
						|
      return *mValue.mCSSRect == *aOther.mValue.mCSSRect;
 | 
						|
    case eUnit_Dasharray:
 | 
						|
    case eUnit_Shadow:
 | 
						|
    case eUnit_Filter:
 | 
						|
    case eUnit_BackgroundPositionCoord:
 | 
						|
      return nsCSSValueList::Equal(mValue.mCSSValueList,
 | 
						|
                                   aOther.mValue.mCSSValueList);
 | 
						|
    case eUnit_Shape:
 | 
						|
      return *mValue.mCSSValueArray == *aOther.mValue.mCSSValueArray;
 | 
						|
    case eUnit_Transform:
 | 
						|
      return *mValue.mCSSValueSharedList == *aOther.mValue.mCSSValueSharedList;
 | 
						|
    case eUnit_CSSValuePairList:
 | 
						|
      return nsCSSValuePairList::Equal(mValue.mCSSValuePairList,
 | 
						|
                                       aOther.mValue.mCSSValuePairList);
 | 
						|
    case eUnit_UnparsedString:
 | 
						|
      return (NS_strcmp(GetStringBufferValue(),
 | 
						|
                        aOther.GetStringBufferValue()) == 0);
 | 
						|
    case eUnit_ComplexColor:
 | 
						|
      return *mValue.mComplexColor == *aOther.mValue.mComplexColor;
 | 
						|
  }
 | 
						|
 | 
						|
  NS_NOTREACHED("incomplete case");
 | 
						|
  return false;
 | 
						|
}
 | 
						|
#endif
 | 
						|
 | 
						|
 | 
						|
// AnimationValue Implementation
 | 
						|
 | 
						|
bool
 | 
						|
AnimationValue::operator==(const AnimationValue& aOther) const
 | 
						|
{
 | 
						|
#ifdef MOZ_OLD_STYLE
 | 
						|
  // It is possible to compare an empty AnimationValue with others, so both
 | 
						|
  // mServo and mGecko could be null while comparing.
 | 
						|
  MOZ_ASSERT(!mServo || mGecko.IsNull());
 | 
						|
#endif
 | 
						|
  if (mServo && aOther.mServo) {
 | 
						|
    return Servo_AnimationValue_DeepEqual(mServo, aOther.mServo);
 | 
						|
  }
 | 
						|
  if (!mServo && !aOther.mServo) {
 | 
						|
#ifdef MOZ_OLD_STYLE
 | 
						|
    return mGecko == aOther.mGecko;
 | 
						|
#else
 | 
						|
    return true;
 | 
						|
#endif
 | 
						|
  }
 | 
						|
  return false;
 | 
						|
}
 | 
						|
 | 
						|
bool
 | 
						|
AnimationValue::operator!=(const AnimationValue& aOther) const
 | 
						|
{
 | 
						|
  return !operator==(aOther);
 | 
						|
}
 | 
						|
 | 
						|
float
 | 
						|
AnimationValue::GetOpacity() const
 | 
						|
{
 | 
						|
#ifdef MOZ_OLD_STYLE
 | 
						|
  MOZ_ASSERT(!mServo != mGecko.IsNull());
 | 
						|
  MOZ_ASSERT(mServo || mGecko.GetUnit() == StyleAnimationValue::eUnit_Float,
 | 
						|
             "Should have the correct unit on Gecko backend");
 | 
						|
#else
 | 
						|
  MOZ_ASSERT(mServo);
 | 
						|
#endif
 | 
						|
  if (mServo) {
 | 
						|
    return Servo_AnimationValue_GetOpacity(mServo);
 | 
						|
  }
 | 
						|
#ifdef MOZ_OLD_STYLE
 | 
						|
  return mGecko.GetFloatValue();
 | 
						|
#else
 | 
						|
  MOZ_CRASH("old style system disabled");
 | 
						|
#endif
 | 
						|
}
 | 
						|
 | 
						|
already_AddRefed<const nsCSSValueSharedList>
 | 
						|
AnimationValue::GetTransformList() const
 | 
						|
{
 | 
						|
#ifdef MOZ_OLD_STYLE
 | 
						|
  MOZ_ASSERT(!mServo != mGecko.IsNull());
 | 
						|
  MOZ_ASSERT(mServo || mGecko.GetUnit() == StyleAnimationValue::eUnit_Transform,
 | 
						|
             "The unit of interpolated value for transform should be "
 | 
						|
             "transform on Gecko backend");
 | 
						|
#else
 | 
						|
  MOZ_ASSERT(mServo);
 | 
						|
#endif
 | 
						|
 | 
						|
  RefPtr<nsCSSValueSharedList> transform;
 | 
						|
  if (mServo) {
 | 
						|
    Servo_AnimationValue_GetTransform(mServo, &transform);
 | 
						|
  } else {
 | 
						|
#ifdef MOZ_OLD_STYLE
 | 
						|
    transform = mGecko.GetCSSValueSharedListValue();
 | 
						|
#else
 | 
						|
    MOZ_CRASH("old style system disabled");
 | 
						|
#endif
 | 
						|
  }
 | 
						|
  return transform.forget();
 | 
						|
}
 | 
						|
 | 
						|
Size
 | 
						|
AnimationValue::GetScaleValue(const nsIFrame* aFrame) const
 | 
						|
{
 | 
						|
#ifdef MOZ_OLD_STYLE
 | 
						|
  MOZ_ASSERT(!mServo != mGecko.IsNull());
 | 
						|
#else
 | 
						|
  MOZ_ASSERT(mServo);
 | 
						|
#endif
 | 
						|
 | 
						|
  if (mServo) {
 | 
						|
    RefPtr<nsCSSValueSharedList> list;
 | 
						|
    Servo_AnimationValue_GetTransform(mServo, &list);
 | 
						|
    return nsStyleTransformMatrix::GetScaleValue(list, aFrame);
 | 
						|
  }
 | 
						|
#ifdef MOZ_OLD_STYLE
 | 
						|
  return mGecko.GetScaleValue(aFrame);
 | 
						|
#else
 | 
						|
  MOZ_CRASH("old style system disabled");
 | 
						|
#endif
 | 
						|
}
 | 
						|
 | 
						|
void
 | 
						|
AnimationValue::SerializeSpecifiedValue(nsCSSPropertyID aProperty,
 | 
						|
                                        nsAString& aString) const
 | 
						|
{
 | 
						|
#ifdef MOZ_OLD_STYLE
 | 
						|
  MOZ_ASSERT(!mServo != mGecko.IsNull());
 | 
						|
#else
 | 
						|
  MOZ_ASSERT(mServo);
 | 
						|
#endif
 | 
						|
 | 
						|
  if (mServo) {
 | 
						|
    Servo_AnimationValue_Serialize(mServo, aProperty, &aString);
 | 
						|
    return;
 | 
						|
  }
 | 
						|
 | 
						|
#ifdef MOZ_OLD_STYLE
 | 
						|
  DebugOnly<bool> uncomputeResult =
 | 
						|
    StyleAnimationValue::UncomputeValue(aProperty, mGecko, aString);
 | 
						|
  MOZ_ASSERT(uncomputeResult, "failed to uncompute StyleAnimationValue");
 | 
						|
#else
 | 
						|
  MOZ_CRASH("old style system disabled");
 | 
						|
#endif
 | 
						|
}
 | 
						|
 | 
						|
bool
 | 
						|
AnimationValue::IsInterpolableWith(nsCSSPropertyID aProperty,
 | 
						|
                                   const AnimationValue& aToValue) const
 | 
						|
{
 | 
						|
  if (IsNull() || aToValue.IsNull()) {
 | 
						|
    return false;
 | 
						|
  }
 | 
						|
 | 
						|
#ifdef MOZ_OLD_STYLE
 | 
						|
  MOZ_ASSERT(!mServo != mGecko.IsNull());
 | 
						|
  MOZ_ASSERT(mGecko.IsNull() == aToValue.mGecko.IsNull() &&
 | 
						|
             !mServo == !aToValue.mServo,
 | 
						|
             "Animation values should have the same style engine");
 | 
						|
#else
 | 
						|
  MOZ_ASSERT(mServo);
 | 
						|
  MOZ_ASSERT(aToValue.mServo);
 | 
						|
#endif
 | 
						|
 | 
						|
  if (mServo) {
 | 
						|
    return Servo_AnimationValues_IsInterpolable(mServo, aToValue.mServo);
 | 
						|
  }
 | 
						|
 | 
						|
#ifdef MOZ_OLD_STYLE
 | 
						|
  // If this is ever a performance problem, we could add a
 | 
						|
  // StyleAnimationValue::IsInterpolatable method, but it seems fine for now.
 | 
						|
  StyleAnimationValue dummy;
 | 
						|
  return StyleAnimationValue::Interpolate(
 | 
						|
           aProperty, mGecko, aToValue.mGecko, 0.5, dummy);
 | 
						|
#else
 | 
						|
  MOZ_CRASH("old style system disabled");
 | 
						|
#endif
 | 
						|
}
 | 
						|
 | 
						|
double
 | 
						|
AnimationValue::ComputeDistance(nsCSSPropertyID aProperty,
 | 
						|
                                const AnimationValue& aOther,
 | 
						|
                                nsStyleContext* aStyleContext) const
 | 
						|
{
 | 
						|
  if (IsNull() || aOther.IsNull()) {
 | 
						|
    return 0.0;
 | 
						|
  }
 | 
						|
 | 
						|
#ifdef MOZ_OLD_STYLE
 | 
						|
  MOZ_ASSERT(!mServo != mGecko.IsNull());
 | 
						|
  MOZ_ASSERT(mGecko.IsNull() == aOther.mGecko.IsNull() &&
 | 
						|
             !mServo == !aOther.mServo,
 | 
						|
             "Animation values should have the same style engine");
 | 
						|
#else
 | 
						|
  MOZ_ASSERT(mServo);
 | 
						|
  MOZ_ASSERT(aOther.mServo);
 | 
						|
#endif
 | 
						|
 | 
						|
  double distance= 0.0;
 | 
						|
  if (mServo) {
 | 
						|
    distance = Servo_AnimationValues_ComputeDistance(mServo, aOther.mServo);
 | 
						|
    return distance < 0.0
 | 
						|
           ? 0.0
 | 
						|
           : distance;
 | 
						|
  }
 | 
						|
 | 
						|
#ifdef MOZ_OLD_STYLE
 | 
						|
  return StyleAnimationValue::ComputeDistance(aProperty,
 | 
						|
                                              mGecko,
 | 
						|
                                              aOther.mGecko,
 | 
						|
                                              aStyleContext->AsGecko(),
 | 
						|
                                              distance)
 | 
						|
         ? distance
 | 
						|
         : 0.0;
 | 
						|
#else
 | 
						|
  MOZ_CRASH("old style system disabled");
 | 
						|
#endif
 | 
						|
}
 | 
						|
 | 
						|
/* static */ AnimationValue
 | 
						|
AnimationValue::FromString(nsCSSPropertyID aProperty,
 | 
						|
                           const nsAString& aValue,
 | 
						|
                           Element* aElement)
 | 
						|
{
 | 
						|
  MOZ_ASSERT(aElement);
 | 
						|
 | 
						|
  AnimationValue result;
 | 
						|
 | 
						|
  nsCOMPtr<nsIDocument> doc = aElement->GetComposedDoc();
 | 
						|
  if (!doc) {
 | 
						|
    return result;
 | 
						|
  }
 | 
						|
 | 
						|
  nsCOMPtr<nsIPresShell> shell = doc->GetShell();
 | 
						|
  if (!shell) {
 | 
						|
    return result;
 | 
						|
  }
 | 
						|
 | 
						|
  // GetStyleContext() flushes style, so we shouldn't assume that any
 | 
						|
  // non-owning references we have are still valid.
 | 
						|
  RefPtr<nsStyleContext> styleContext =
 | 
						|
    nsComputedDOMStyle::GetStyleContext(aElement, nullptr);
 | 
						|
  MOZ_ASSERT(styleContext);
 | 
						|
 | 
						|
  if (auto* servoContext = styleContext->GetAsServo()) {
 | 
						|
    RefPtr<RawServoDeclarationBlock> declarations =
 | 
						|
      ServoCSSParser::ParseProperty(aProperty, aValue,
 | 
						|
                                    ServoCSSParser::GetParsingEnvironment(doc));
 | 
						|
 | 
						|
    if (!declarations) {
 | 
						|
      return result;
 | 
						|
    }
 | 
						|
 | 
						|
    result.mServo =
 | 
						|
      shell->StyleSet()->AsServo()->ComputeAnimationValue(aElement,
 | 
						|
                                                          declarations,
 | 
						|
                                                          servoContext);
 | 
						|
    return result;
 | 
						|
  }
 | 
						|
 | 
						|
#ifdef MOZ_OLD_STYLE
 | 
						|
  if (!StyleAnimationValue::ComputeValue(aProperty, aElement,
 | 
						|
                                         styleContext->AsGecko(),
 | 
						|
                                         aValue, false /* |aUseSVGMode| */,
 | 
						|
                                         result.mGecko)) {
 | 
						|
    MOZ_ASSERT(result.IsNull());
 | 
						|
  }
 | 
						|
  return result;
 | 
						|
#else
 | 
						|
  MOZ_CRASH("old style system disabled");
 | 
						|
#endif
 | 
						|
}
 | 
						|
 | 
						|
/* static */ AnimationValue
 | 
						|
AnimationValue::Opacity(StyleBackendType aBackendType, float aOpacity)
 | 
						|
{
 | 
						|
  AnimationValue result;
 | 
						|
 | 
						|
  switch (aBackendType) {
 | 
						|
    case StyleBackendType::Servo:
 | 
						|
      result.mServo = Servo_AnimationValue_Opacity(aOpacity).Consume();
 | 
						|
      break;
 | 
						|
    case StyleBackendType::Gecko:
 | 
						|
#ifdef MOZ_OLD_STYLE
 | 
						|
      result.mGecko.SetFloatValue(aOpacity);
 | 
						|
#else
 | 
						|
      MOZ_CRASH("old style system disabled");
 | 
						|
#endif
 | 
						|
      break;
 | 
						|
    default:
 | 
						|
      MOZ_ASSERT_UNREACHABLE("Unsupported style backend");
 | 
						|
  }
 | 
						|
  return result;
 | 
						|
}
 | 
						|
 | 
						|
/* static */ AnimationValue
 | 
						|
AnimationValue::Transform(StyleBackendType aBackendType,
 | 
						|
                          nsCSSValueSharedList& aList)
 | 
						|
{
 | 
						|
  AnimationValue result;
 | 
						|
 | 
						|
  switch (aBackendType) {
 | 
						|
    case StyleBackendType::Servo:
 | 
						|
      result.mServo = Servo_AnimationValue_Transform(aList).Consume();
 | 
						|
      break;
 | 
						|
    case StyleBackendType::Gecko:
 | 
						|
#ifdef MOZ_OLD_STYLE
 | 
						|
      result.mGecko.SetTransformValue(&aList);
 | 
						|
#else
 | 
						|
      MOZ_CRASH("old style system disabled");
 | 
						|
#endif
 | 
						|
      break;
 | 
						|
    default:
 | 
						|
      MOZ_ASSERT_UNREACHABLE("Unsupported style backend");
 | 
						|
  }
 | 
						|
  return result;
 | 
						|
}
 | 
						|
 | 
						|
/* static */ already_AddRefed<nsCSSValue::Array>
 | 
						|
AnimationValue::AppendTransformFunction(nsCSSKeyword aTransformFunction,
 | 
						|
                                        nsCSSValueList**& aListTail)
 | 
						|
{
 | 
						|
  RefPtr<nsCSSValue::Array> arr = AppendFunction(aTransformFunction);
 | 
						|
  nsCSSValueList *item = new nsCSSValueList;
 | 
						|
  item->mValue.SetArrayValue(arr, eCSSUnit_Function);
 | 
						|
 | 
						|
  *aListTail = item;
 | 
						|
  aListTail = &item->mNext;
 | 
						|
 | 
						|
  return arr.forget();
 | 
						|
}
 |