gecko-dev/dom/smil/nsSMILCSSValueType.cpp

960 строки
32 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: */
2012-05-21 15:12:37 +04:00
/* 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/. */
/* representation of a value for a SMIL-animated CSS property */
#include "nsSMILCSSValueType.h"
#include "nsComputedDOMStyle.h"
#include "nsString.h"
#include "nsSMILParserUtils.h"
#include "nsSMILValue.h"
#include "nsCSSProps.h"
#include "nsCSSValue.h"
#include "nsColor.h"
#include "nsPresContext.h"
#include "mozilla/ServoBindings.h"
#include "mozilla/StyleAnimationValue.h" // For AnimationValue
#include "mozilla/ServoCSSParser.h"
#include "mozilla/StyleSetHandleInlines.h"
#include "mozilla/dom/BaseKeyframeTypesBinding.h" // For CompositeOperation
#include "mozilla/dom/Element.h"
#include "nsDebug.h"
#include "nsStyleUtil.h"
#include "nsIDocument.h"
using namespace mozilla::dom;
using mozilla::StyleAnimationValue;
typedef AutoTArray<RefPtr<RawServoAnimationValue>, 1> ServoAnimationValues;
/*static*/ nsSMILCSSValueType nsSMILCSSValueType::sSingleton;
struct ValueWrapper {
ValueWrapper(nsCSSPropertyID aPropID, const AnimationValue& aValue)
: mPropID(aPropID)
{
if (aValue.mServo) {
mServoValues.AppendElement(aValue.mServo);
return;
}
#ifdef MOZ_OLD_STYLE
mGeckoValue = aValue.mGecko;
#else
MOZ_CRASH("old style system disabled");
#endif
}
#ifdef MOZ_OLD_STYLE
ValueWrapper(nsCSSPropertyID aPropID, const StyleAnimationValue& aValue)
: mPropID(aPropID), mGeckoValue(aValue) {}
#endif
ValueWrapper(nsCSSPropertyID aPropID,
const RefPtr<RawServoAnimationValue>& aValue)
: mPropID(aPropID), mServoValues{(aValue)} {}
ValueWrapper(nsCSSPropertyID aPropID, ServoAnimationValues&& aValues)
: mPropID(aPropID), mServoValues{aValues} {}
bool operator==(const ValueWrapper& aOther) const
{
if (mPropID != aOther.mPropID) {
return false;
}
if (!mServoValues.IsEmpty()) {
size_t len = mServoValues.Length();
if (len != aOther.mServoValues.Length()) {
return false;
}
for (size_t i = 0; i < len; i++) {
if (!Servo_AnimationValue_DeepEqual(mServoValues[i],
aOther.mServoValues[i])) {
return false;
}
}
return true;
}
#ifdef MOZ_OLD_STYLE
return mGeckoValue == aOther.mGeckoValue;
#else
MOZ_CRASH("old style system disabled");
#endif
}
bool operator!=(const ValueWrapper& aOther) const
{
return !(*this == aOther);
}
nsCSSPropertyID mPropID;
ServoAnimationValues mServoValues;
#ifdef MOZ_OLD_STYLE
StyleAnimationValue mGeckoValue;
#endif
};
// Helper Methods
// --------------
#ifdef MOZ_OLD_STYLE
static const StyleAnimationValue*
GetZeroValueForUnit(StyleAnimationValue::Unit aUnit)
{
static const StyleAnimationValue
sZeroCoord(0, StyleAnimationValue::CoordConstructor);
static const StyleAnimationValue
sZeroPercent(0.0f, StyleAnimationValue::PercentConstructor);
static const StyleAnimationValue
sZeroFloat(0.0f, StyleAnimationValue::FloatConstructor);
static const StyleAnimationValue
sZeroColor(NS_RGB(0,0,0), StyleAnimationValue::ColorConstructor);
MOZ_ASSERT(aUnit != StyleAnimationValue::eUnit_Null,
"Need non-null unit for a zero value");
switch (aUnit) {
case StyleAnimationValue::eUnit_Coord:
return &sZeroCoord;
case StyleAnimationValue::eUnit_Percent:
return &sZeroPercent;
case StyleAnimationValue::eUnit_Float:
return &sZeroFloat;
case StyleAnimationValue::eUnit_Color:
return &sZeroColor;
default:
return nullptr;
}
}
#endif
// If one argument is null, this method updates it to point to "zero"
// for the other argument's Unit (if applicable; otherwise, we return false).
//
// If neither argument is null, this method simply returns true.
//
// If both arguments are null, this method returns false.
//
// |aZeroValueStorage| should be a reference to a RefPtr<RawServoAnimationValue>.
// This is used where we may need to allocate a new ServoAnimationValue to
// represent the appropriate zero value.
//
// Returns true on success, or otherwise.
static bool
FinalizeServoAnimationValues(const RefPtr<RawServoAnimationValue>*& aValue1,
const RefPtr<RawServoAnimationValue>*& aValue2,
RefPtr<RawServoAnimationValue>& aZeroValueStorage)
{
if (!aValue1 && !aValue2) {
return false;
}
// Are we missing either val? (If so, it's an implied 0 in other val's units)
if (!aValue1) {
aZeroValueStorage = Servo_AnimationValues_GetZeroValue(*aValue2).Consume();
aValue1 = &aZeroValueStorage;
} else if (!aValue2) {
aZeroValueStorage = Servo_AnimationValues_GetZeroValue(*aValue1).Consume();
aValue2 = &aZeroValueStorage;
}
return *aValue1 && *aValue2;
}
#ifdef MOZ_OLD_STYLE
static bool
FinalizeStyleAnimationValues(const StyleAnimationValue*& aValue1,
const StyleAnimationValue*& aValue2)
{
if (!aValue1 && !aValue2) {
return false;
}
if (!aValue1) {
aValue1 = GetZeroValueForUnit(aValue2->GetUnit());
return !!aValue1; // Fail if we have no zero value for this unit.
}
if (!aValue2) {
aValue2 = GetZeroValueForUnit(aValue1->GetUnit());
return !!aValue2; // Fail if we have no zero value for this unit.
}
// Ok, both values were specified.
// Need to handle a special-case, though: unitless nonzero length (parsed as
// eUnit_Float) mixed with unitless 0 length (parsed as eUnit_Coord). These
// won't interoperate in StyleAnimationValue, since their Units don't match.
// In this case, we replace the eUnit_Coord 0 value with eUnit_Float 0 value.
const StyleAnimationValue& zeroCoord =
*GetZeroValueForUnit(StyleAnimationValue::eUnit_Coord);
if (*aValue1 == zeroCoord &&
aValue2->GetUnit() == StyleAnimationValue::eUnit_Float) {
aValue1 = GetZeroValueForUnit(StyleAnimationValue::eUnit_Float);
} else if (*aValue2 == zeroCoord &&
aValue1->GetUnit() == StyleAnimationValue::eUnit_Float) {
aValue2 = GetZeroValueForUnit(StyleAnimationValue::eUnit_Float);
}
return true;
}
static void
InvertSign(StyleAnimationValue& aValue)
{
switch (aValue.GetUnit()) {
case StyleAnimationValue::eUnit_Coord:
aValue.SetCoordValue(-aValue.GetCoordValue());
break;
case StyleAnimationValue::eUnit_Percent:
aValue.SetPercentValue(-aValue.GetPercentValue());
break;
case StyleAnimationValue::eUnit_Float:
aValue.SetFloatValue(-aValue.GetFloatValue());
break;
default:
NS_NOTREACHED("Calling InvertSign with an unsupported unit");
break;
}
}
#endif
static ValueWrapper*
ExtractValueWrapper(nsSMILValue& aValue)
{
return static_cast<ValueWrapper*>(aValue.mU.mPtr);
}
static const ValueWrapper*
ExtractValueWrapper(const nsSMILValue& aValue)
{
return static_cast<const ValueWrapper*>(aValue.mU.mPtr);
}
// Class methods
// -------------
void
nsSMILCSSValueType::Init(nsSMILValue& aValue) const
{
MOZ_ASSERT(aValue.IsNull(), "Unexpected SMIL value type");
aValue.mU.mPtr = nullptr;
aValue.mType = this;
}
void
nsSMILCSSValueType::Destroy(nsSMILValue& aValue) const
{
MOZ_ASSERT(aValue.mType == this, "Unexpected SMIL value type");
delete static_cast<ValueWrapper*>(aValue.mU.mPtr);
aValue.mType = nsSMILNullType::Singleton();
}
nsresult
nsSMILCSSValueType::Assign(nsSMILValue& aDest, const nsSMILValue& aSrc) const
{
MOZ_ASSERT(aDest.mType == aSrc.mType, "Incompatible SMIL types");
MOZ_ASSERT(aDest.mType == this, "Unexpected SMIL value type");
const ValueWrapper* srcWrapper = ExtractValueWrapper(aSrc);
ValueWrapper* destWrapper = ExtractValueWrapper(aDest);
if (srcWrapper) {
if (!destWrapper) {
// barely-initialized dest -- need to alloc & copy
aDest.mU.mPtr = new ValueWrapper(*srcWrapper);
} else {
// both already fully-initialized -- just copy straight across
*destWrapper = *srcWrapper;
}
} else if (destWrapper) {
// fully-initialized dest, barely-initialized src -- clear dest
delete destWrapper;
aDest.mU.mPtr = destWrapper = nullptr;
} // else, both are barely-initialized -- nothing to do.
return NS_OK;
}
bool
nsSMILCSSValueType::IsEqual(const nsSMILValue& aLeft,
const nsSMILValue& aRight) const
{
MOZ_ASSERT(aLeft.mType == aRight.mType, "Incompatible SMIL types");
MOZ_ASSERT(aLeft.mType == this, "Unexpected SMIL value");
const ValueWrapper* leftWrapper = ExtractValueWrapper(aLeft);
const ValueWrapper* rightWrapper = ExtractValueWrapper(aRight);
if (leftWrapper) {
if (rightWrapper) {
// Both non-null
NS_WARNING_ASSERTION(leftWrapper != rightWrapper,
"Two nsSMILValues with matching ValueWrapper ptr");
return *leftWrapper == *rightWrapper;
}
// Left non-null, right null
return false;
}
if (rightWrapper) {
// Left null, right non-null
return false;
}
// Both null
return true;
}
static bool
AddOrAccumulateForServo(nsSMILValue& aDest,
const ValueWrapper* aValueToAddWrapper,
ValueWrapper* aDestWrapper,
CompositeOperation aCompositeOp,
uint64_t aCount)
{
nsCSSPropertyID property = aValueToAddWrapper
? aValueToAddWrapper->mPropID
: aDestWrapper->mPropID;
size_t len = aValueToAddWrapper
? aValueToAddWrapper->mServoValues.Length()
: aDestWrapper->mServoValues.Length();
MOZ_ASSERT(!aValueToAddWrapper || !aDestWrapper ||
aValueToAddWrapper->mServoValues.Length() ==
aDestWrapper->mServoValues.Length(),
"Both of values' length in the wrappers should be the same if "
"both of them exist");
for (size_t i = 0; i < len; i++) {
const RefPtr<RawServoAnimationValue>* valueToAdd =
aValueToAddWrapper
? &aValueToAddWrapper->mServoValues[i]
: nullptr;
const RefPtr<RawServoAnimationValue>* destValue =
aDestWrapper
? &aDestWrapper->mServoValues[i]
: nullptr;
RefPtr<RawServoAnimationValue> zeroValueStorage;
if (!FinalizeServoAnimationValues(valueToAdd, destValue, zeroValueStorage)) {
return false;
}
// FinalizeServoAnimationValues may have updated destValue so we should make
// sure the aDest and aDestWrapper outparams are up-to-date.
if (aDestWrapper) {
aDestWrapper->mServoValues[i] = *destValue;
} else {
// aDest may be a barely-initialized "zero" destination.
aDest.mU.mPtr = aDestWrapper = new ValueWrapper(property, *destValue);
aDestWrapper->mServoValues.SetLength(len);
}
RefPtr<RawServoAnimationValue> result;
if (aCompositeOp == CompositeOperation::Add) {
result = Servo_AnimationValues_Add(*destValue, *valueToAdd).Consume();
} else {
result = Servo_AnimationValues_Accumulate(*destValue,
*valueToAdd,
aCount).Consume();
}
if (!result) {
return false;
}
aDestWrapper->mServoValues[i] = result;
}
return true;
}
static bool
AddOrAccumulate(nsSMILValue& aDest, const nsSMILValue& aValueToAdd,
CompositeOperation aCompositeOp, uint64_t aCount)
{
MOZ_ASSERT(aValueToAdd.mType == aDest.mType,
"Trying to add mismatching types");
MOZ_ASSERT(aValueToAdd.mType == &nsSMILCSSValueType::sSingleton,
"Unexpected SMIL value type");
MOZ_ASSERT(aCompositeOp == CompositeOperation::Add ||
aCompositeOp == CompositeOperation::Accumulate,
"Composite operation should be add or accumulate");
MOZ_ASSERT(aCompositeOp != CompositeOperation::Add || aCount == 1,
"Count should be 1 if composite operation is add");
ValueWrapper* destWrapper = ExtractValueWrapper(aDest);
const ValueWrapper* valueToAddWrapper = ExtractValueWrapper(aValueToAdd);
// If both of the values are empty just fail. This can happen in rare cases
// such as when the underlying animation produced an empty value.
//
// Technically, it doesn't matter what we return here since in either case it
// will produce the same result: an empty value.
if (!destWrapper && !valueToAddWrapper) {
return false;
}
nsCSSPropertyID property = valueToAddWrapper
? valueToAddWrapper->mPropID
: destWrapper->mPropID;
// Special case: font-size-adjust and stroke-dasharray are explicitly
// non-additive (even though StyleAnimationValue *could* support adding them)
if (property == eCSSProperty_font_size_adjust ||
property == eCSSProperty_stroke_dasharray) {
return false;
}
// Skip font shorthand since it includes font-size-adjust.
if (property == eCSSProperty_font) {
return false;
}
bool isServo = valueToAddWrapper
? !valueToAddWrapper->mServoValues.IsEmpty()
: !destWrapper->mServoValues.IsEmpty();
if (isServo) {
return AddOrAccumulateForServo(aDest,
valueToAddWrapper,
destWrapper,
aCompositeOp,
aCount);
}
#ifdef MOZ_OLD_STYLE
const StyleAnimationValue* valueToAdd = valueToAddWrapper ?
&valueToAddWrapper->mGeckoValue : nullptr;
const StyleAnimationValue* destValue = destWrapper ?
&destWrapper->mGeckoValue : nullptr;
if (!FinalizeStyleAnimationValues(valueToAdd, destValue)) {
return false;
}
// Did FinalizeStyleAnimationValues change destValue?
// If so, update outparam to use the new value.
if (destWrapper && &destWrapper->mGeckoValue != destValue) {
destWrapper->mGeckoValue = *destValue;
}
// Handle barely-initialized "zero" destination.
if (!destWrapper) {
aDest.mU.mPtr = destWrapper = new ValueWrapper(property, *destValue);
}
// For Gecko, we currently call Add for either composite mode.
//
// This is not ideal, but it doesn't make any difference for the set of
// properties we currently allow adding in SMIL and this code path will
// hopefully become obsolete before we expand that set.
return StyleAnimationValue::Add(property,
destWrapper->mGeckoValue,
*valueToAdd, aCount);
#else
MOZ_CRASH("old style system disabled");
#endif
}
nsresult
nsSMILCSSValueType::SandwichAdd(nsSMILValue& aDest,
const nsSMILValue& aValueToAdd) const
{
return AddOrAccumulate(aDest, aValueToAdd, CompositeOperation::Add, 1)
? NS_OK
: NS_ERROR_FAILURE;
}
nsresult
nsSMILCSSValueType::Add(nsSMILValue& aDest, const nsSMILValue& aValueToAdd,
uint32_t aCount) const
{
return AddOrAccumulate(aDest, aValueToAdd, CompositeOperation::Accumulate,
aCount)
? NS_OK
: NS_ERROR_FAILURE;
}
static nsresult
ComputeDistanceForServo(const ValueWrapper* aFromWrapper,
const ValueWrapper& aToWrapper,
double& aDistance)
{
size_t len = aToWrapper.mServoValues.Length();
MOZ_ASSERT(!aFromWrapper || aFromWrapper->mServoValues.Length() == len,
"From and to values length should be the same if "
"The start value exists");
double squareDistance = 0;
for (size_t i = 0; i < len; i++) {
const RefPtr<RawServoAnimationValue>* fromValue =
aFromWrapper ? &aFromWrapper->mServoValues[0] : nullptr;
const RefPtr<RawServoAnimationValue>* toValue = &aToWrapper.mServoValues[0];
RefPtr<RawServoAnimationValue> zeroValueStorage;
if (!FinalizeServoAnimationValues(fromValue, toValue, zeroValueStorage)) {
return NS_ERROR_FAILURE;
}
double distance = Servo_AnimationValues_ComputeDistance(*fromValue, *toValue);
if (distance < 0.0) {
return NS_ERROR_FAILURE;
}
if (len == 1) {
aDistance = distance;
return NS_OK;
}
squareDistance += distance * distance;
}
aDistance = sqrt(squareDistance);
return NS_OK;
}
nsresult
nsSMILCSSValueType::ComputeDistance(const nsSMILValue& aFrom,
const nsSMILValue& aTo,
double& aDistance) const
{
MOZ_ASSERT(aFrom.mType == aTo.mType,
"Trying to compare different types");
MOZ_ASSERT(aFrom.mType == this, "Unexpected source type");
const ValueWrapper* fromWrapper = ExtractValueWrapper(aFrom);
const ValueWrapper* toWrapper = ExtractValueWrapper(aTo);
MOZ_ASSERT(toWrapper, "expecting non-null endpoint");
if (!toWrapper->mServoValues.IsEmpty()) {
return ComputeDistanceForServo(fromWrapper, *toWrapper, aDistance);
}
#ifdef MOZ_OLD_STYLE
const StyleAnimationValue* fromCSSValue = fromWrapper ?
&fromWrapper->mGeckoValue : nullptr;
const StyleAnimationValue* toCSSValue = &toWrapper->mGeckoValue;
if (!FinalizeStyleAnimationValues(fromCSSValue, toCSSValue)) {
return NS_ERROR_FAILURE;
}
return StyleAnimationValue::ComputeDistance(toWrapper->mPropID,
fromWrapper->mGeckoValue,
toWrapper->mGeckoValue,
nullptr,
aDistance)
? NS_OK
: NS_ERROR_FAILURE;
#else
MOZ_CRASH("old style system disabled");
#endif
}
#ifdef MOZ_OLD_STYLE
static nsresult
InterpolateForGecko(const ValueWrapper* aStartWrapper,
const ValueWrapper& aEndWrapper,
double aUnitDistance,
nsSMILValue& aResult)
{
const StyleAnimationValue* startCSSValue = aStartWrapper
? &aStartWrapper->mGeckoValue
: nullptr;
const StyleAnimationValue* endCSSValue = &aEndWrapper.mGeckoValue;
if (!FinalizeStyleAnimationValues(startCSSValue, endCSSValue)) {
return NS_ERROR_FAILURE;
}
StyleAnimationValue resultValue;
if (StyleAnimationValue::Interpolate(aEndWrapper.mPropID,
*startCSSValue,
*endCSSValue,
aUnitDistance, resultValue)) {
aResult.mU.mPtr = new ValueWrapper(aEndWrapper.mPropID, resultValue);
return NS_OK;
}
return NS_ERROR_FAILURE;
}
#endif
static nsresult
InterpolateForServo(const ValueWrapper* aStartWrapper,
const ValueWrapper& aEndWrapper,
double aUnitDistance,
nsSMILValue& aResult)
{
// For discretely-animated properties Servo_AnimationValues_Interpolate will
// perform the discrete animation (i.e. 50% flip) and return a success result.
// However, SMIL has its own special discrete animation behavior that it uses
// when keyTimes are specified, but we won't run that unless that this method
// returns a failure to indicate that the property cannot be smoothly
// interpolated, i.e. that we need to use a discrete calcMode.
//
// For shorthands, Servo_Property_IsDiscreteAnimatable will always return
// false. That's fine since most shorthands (like 'font' and
// 'text-decoration') include non-discrete components. If authors want to
// treat all components as discrete then they should use calcMode="discrete".
if (Servo_Property_IsDiscreteAnimatable(aEndWrapper.mPropID)) {
return NS_ERROR_FAILURE;
}
ServoAnimationValues results;
size_t len = aEndWrapper.mServoValues.Length();
results.SetCapacity(len);
MOZ_ASSERT(!aStartWrapper || aStartWrapper->mServoValues.Length() == len,
"Start and end values length should be the same if "
"the start value exists");
for (size_t i = 0; i < len; i++) {
const RefPtr<RawServoAnimationValue>*
startValue = aStartWrapper
? &aStartWrapper->mServoValues[i]
: nullptr;
const RefPtr<RawServoAnimationValue>* endValue = &aEndWrapper.mServoValues[i];
RefPtr<RawServoAnimationValue> zeroValueStorage;
if (!FinalizeServoAnimationValues(startValue, endValue, zeroValueStorage)) {
return NS_ERROR_FAILURE;
}
RefPtr<RawServoAnimationValue> result =
Servo_AnimationValues_Interpolate(*startValue,
*endValue,
aUnitDistance).Consume();
if (!result) {
return NS_ERROR_FAILURE;
}
results.AppendElement(result);
}
aResult.mU.mPtr = new ValueWrapper(aEndWrapper.mPropID, Move(results));
return NS_OK;
}
nsresult
nsSMILCSSValueType::Interpolate(const nsSMILValue& aStartVal,
const nsSMILValue& aEndVal,
double aUnitDistance,
nsSMILValue& aResult) const
{
MOZ_ASSERT(aStartVal.mType == aEndVal.mType,
"Trying to interpolate different types");
MOZ_ASSERT(aStartVal.mType == this,
"Unexpected types for interpolation");
MOZ_ASSERT(aResult.mType == this, "Unexpected result type");
MOZ_ASSERT(aUnitDistance >= 0.0 && aUnitDistance <= 1.0,
"unit distance value out of bounds");
MOZ_ASSERT(!aResult.mU.mPtr, "expecting barely-initialized outparam");
const ValueWrapper* startWrapper = ExtractValueWrapper(aStartVal);
const ValueWrapper* endWrapper = ExtractValueWrapper(aEndVal);
MOZ_ASSERT(endWrapper, "expecting non-null endpoint");
if (!endWrapper->mServoValues.IsEmpty()) {
return InterpolateForServo(startWrapper,
*endWrapper,
aUnitDistance,
aResult);
}
#ifdef MOZ_OLD_STYLE
return InterpolateForGecko(startWrapper,
*endWrapper,
aUnitDistance,
aResult);
#else
MOZ_CRASH("old style system disabled");
#endif
}
// Helper function to extract presContext
static nsPresContext*
GetPresContextForElement(Element* aElem)
{
nsIDocument* doc = aElem->GetUncomposedDoc();
if (!doc) {
// This can happen if we process certain types of restyles mid-sample
// and remove anonymous animated content from the document as a result.
// See bug 534975.
return nullptr;
}
return doc->GetPresContext();
}
#ifdef MOZ_OLD_STYLE
static const nsDependentSubstring
GetNonNegativePropValue(const nsAString& aString, nsCSSPropertyID aPropID,
bool& aIsNegative)
{
// If value is negative, we'll strip off the "-" so the CSS parser won't
// barf, and then manually make the parsed value negative.
// (This is a partial solution to let us accept some otherwise out-of-bounds
// CSS values. Bug 501188 will provide a more complete fix.)
aIsNegative = false;
uint32_t subStringBegin = 0;
// NOTE: We need to opt-out 'stroke-dasharray' from the negative-number
// check. Its values might look negative (e.g. by starting with "-1"), but
// they're more complicated than our simple negation logic here can handle.
if (aPropID != eCSSProperty_stroke_dasharray) {
int32_t absValuePos = nsSMILParserUtils::CheckForNegativeNumber(aString);
if (absValuePos > 0) {
aIsNegative = true;
subStringBegin = (uint32_t)absValuePos; // Start parsing after '-' sign
}
}
return Substring(aString, subStringBegin);
}
// Helper function to parse a string into a StyleAnimationValue
static bool
ValueFromStringHelper(nsCSSPropertyID aPropID,
Element* aTargetElement,
nsPresContext* aPresContext,
mozilla::GeckoStyleContext* aStyleContext,
const nsAString& aString,
StyleAnimationValue& aStyleAnimValue,
bool* aIsContextSensitive)
{
bool isNegative = false;
const nsDependentSubstring subString =
GetNonNegativePropValue(aString, aPropID, isNegative);
if (!StyleAnimationValue::ComputeValue(aPropID, aTargetElement, aStyleContext,
subString, true, aStyleAnimValue,
aIsContextSensitive)) {
return false;
}
if (isNegative) {
InvertSign(aStyleAnimValue);
}
if (aPropID == eCSSProperty_font_size) {
// Divide out text-zoom, since SVG is supposed to ignore it
MOZ_ASSERT(aStyleAnimValue.GetUnit() == StyleAnimationValue::eUnit_Coord,
"'font-size' value with unexpected style unit");
aStyleAnimValue.SetCoordValue(aStyleAnimValue.GetCoordValue() /
Bug 1328868 - Part 2 - Apply the system font scale as an additional text zoom factor to all pages that are not font inflated. r=tnikkel We want to use a similar model as Chrome on Android does for scaling our display of web content, that is use font inflation for desktop pages and plain text zooming for everything else. Since we don't want to simply clobber any text zoom that might have been set by the user/front-end code, we allow setting and storing the system font scale separately on the PresContext. We then calculate the effective text zoom value as the product of the system font scale and the current text zoom value. Any function that is using the PresContext's TextZoom value for layouting/rendering is switched over to this new EffectiveTextZoom value, whereas functions that are interested in the text zoom as actually set by the user/front-end (e.g. the nsDocumentViewer, or the code responsible for copying text and full zoom settings into the new PresContext on page navigation) continue using the plain TextZoom value. As long as font inflation is enabled in principle (e.g. font.size.inflation.minTwips != 0), every page starts out as eligible for font inflation until the relevant meta viewport tags marking the page as "mobile friendly" have been detected. Since the PresShell caches the font inflation state and only recalculates it when necessary, we make use of that and set the PresContext's system font scale as necessary whenever the font inflation state has been refreshed. MozReview-Commit-ID: 2InyE04wKAW --HG-- extra : rebase_source : 3f6d7128f37c1dc18f67a6655f86d9a3003fe90b extra : source : 6100458b97289f9aea5ac8fda57ded045e6860b7
2017-02-25 15:22:52 +03:00
aPresContext->EffectiveTextZoom());
}
return true;
}
#endif
static ServoAnimationValues
ValueFromStringHelper(nsCSSPropertyID aPropID,
Element* aTargetElement,
nsPresContext* aPresContext,
nsStyleContext* aStyleContext,
const nsAString& aString)
{
ServoAnimationValues result;
nsIDocument* doc = aTargetElement->GetUncomposedDoc();
if (!doc) {
return result;
}
// Parse property
ServoCSSParser::ParsingEnvironment env =
ServoCSSParser::GetParsingEnvironment(doc);
RefPtr<RawServoDeclarationBlock> servoDeclarationBlock =
ServoCSSParser::ParseProperty(aPropID, aString, env,
ParsingMode::AllowUnitlessLength |
ParsingMode::AllowAllNumericValues);
if (!servoDeclarationBlock) {
return result;
}
// Compute value
aPresContext->StyleSet()->AsServo()->GetAnimationValues(servoDeclarationBlock,
aTargetElement,
aStyleContext->AsServo(),
result);
return result;
}
// static
void
nsSMILCSSValueType::ValueFromString(nsCSSPropertyID aPropID,
Element* aTargetElement,
const nsAString& aString,
nsSMILValue& aValue,
bool* aIsContextSensitive)
{
MOZ_ASSERT(aValue.IsNull(), "Outparam should be null-typed");
nsPresContext* presContext = GetPresContextForElement(aTargetElement);
if (!presContext) {
NS_WARNING("Not parsing animation value; unable to get PresContext");
return;
}
nsIDocument* doc = aTargetElement->GetUncomposedDoc();
if (doc && !nsStyleUtil::CSPAllowsInlineStyle(nullptr,
doc->NodePrincipal(),
nullptr,
doc->GetDocumentURI(),
0, aString, nullptr)) {
return;
}
RefPtr<nsStyleContext> styleContext =
nsComputedDOMStyle::GetStyleContext(aTargetElement, nullptr);
if (!styleContext) {
return;
}
if (styleContext->IsServo()) {
ServoAnimationValues parsedValues =
ValueFromStringHelper(aPropID, aTargetElement, presContext,
styleContext, aString);
if (aIsContextSensitive) {
// FIXME: Bug 1358955 - detect context-sensitive values and set this value
// appropriately.
*aIsContextSensitive = false;
}
if (!parsedValues.IsEmpty()) {
sSingleton.Init(aValue);
aValue.mU.mPtr = new ValueWrapper(aPropID, Move(parsedValues));
}
return;
}
#ifdef MOZ_OLD_STYLE
StyleAnimationValue parsedValue;
if (ValueFromStringHelper(aPropID, aTargetElement, presContext,
styleContext->AsGecko(), aString, parsedValue,
aIsContextSensitive)) {
sSingleton.Init(aValue);
aValue.mU.mPtr = new ValueWrapper(aPropID, parsedValue);
}
#else
MOZ_CRASH("old style system disabled");
#endif
}
// static
nsSMILValue
nsSMILCSSValueType::ValueFromAnimationValue(nsCSSPropertyID aPropID,
Element* aTargetElement,
const AnimationValue& aValue)
{
nsSMILValue result;
nsIDocument* doc = aTargetElement->GetUncomposedDoc();
// We'd like to avoid serializing |aValue| if possible, and since the
// string passed to CSPAllowsInlineStyle is only used for reporting violations
// and an intermediate CSS value is not likely to be particularly useful
// in that case, we just use a generic placeholder string instead.
static const nsLiteralString kPlaceholderText =
NS_LITERAL_STRING("[SVG animation of CSS]");
if (doc && !nsStyleUtil::CSPAllowsInlineStyle(nullptr,
doc->NodePrincipal(),
nullptr,
doc->GetDocumentURI(),
0, kPlaceholderText, nullptr)) {
return result;
}
sSingleton.Init(result);
result.mU.mPtr = new ValueWrapper(aPropID, aValue);
return result;
}
// static
void
nsSMILCSSValueType::ValueToString(const nsSMILValue& aValue,
nsAString& aString)
{
MOZ_ASSERT(aValue.mType == &nsSMILCSSValueType::sSingleton,
"Unexpected SMIL value type");
const ValueWrapper* wrapper = ExtractValueWrapper(aValue);
if (!wrapper) {
return;
}
if (wrapper->mServoValues.IsEmpty()) {
#ifdef MOZ_OLD_STYLE
DebugOnly<bool> uncomputeResult =
StyleAnimationValue::UncomputeValue(wrapper->mPropID,
wrapper->mGeckoValue,
aString);
return;
#else
MOZ_CRASH("old style system disabled");
#endif
}
if (nsCSSProps::IsShorthand(wrapper->mPropID)) {
// In case of shorthand on servo, we iterate over all mServoValues array
// since we have multiple AnimationValues in the array for each longhand
// component.
Servo_Shorthand_AnimationValues_Serialize(wrapper->mPropID,
&wrapper->mServoValues,
&aString);
return;
}
Servo_AnimationValue_Serialize(wrapper->mServoValues[0],
wrapper->mPropID,
&aString);
}
// static
nsCSSPropertyID
nsSMILCSSValueType::PropertyFromValue(const nsSMILValue& aValue)
{
if (aValue.mType != &nsSMILCSSValueType::sSingleton) {
return eCSSProperty_UNKNOWN;
}
const ValueWrapper* wrapper = ExtractValueWrapper(aValue);
if (!wrapper) {
return eCSSProperty_UNKNOWN;
}
return wrapper->mPropID;
}
// static
void
nsSMILCSSValueType::FinalizeValue(nsSMILValue& aValue,
const nsSMILValue& aValueToMatch)
{
MOZ_ASSERT(aValue.mType == aValueToMatch.mType, "Incompatible SMIL types");
MOZ_ASSERT(aValue.mType == &nsSMILCSSValueType::sSingleton,
"Unexpected SMIL value type");
ValueWrapper* valueWrapper = ExtractValueWrapper(aValue);
// If |aValue| already has a value, there's nothing to do here.
if (valueWrapper) {
return;
}
const ValueWrapper* valueToMatchWrapper = ExtractValueWrapper(aValueToMatch);
if (!valueToMatchWrapper) {
MOZ_ASSERT_UNREACHABLE("Value to match is empty");
return;
}
bool isServo = !valueToMatchWrapper->mServoValues.IsEmpty();
if (isServo) {
ServoAnimationValues zeroValues;
zeroValues.SetCapacity(valueToMatchWrapper->mServoValues.Length());
for (auto& valueToMatch : valueToMatchWrapper->mServoValues) {
RefPtr<RawServoAnimationValue> zeroValue =
Servo_AnimationValues_GetZeroValue(valueToMatch).Consume();
if (!zeroValue) {
return;
}
zeroValues.AppendElement(Move(zeroValue));
}
aValue.mU.mPtr = new ValueWrapper(valueToMatchWrapper->mPropID,
Move(zeroValues));
} else {
#ifdef MOZ_OLD_STYLE
const StyleAnimationValue* zeroValue =
GetZeroValueForUnit(valueToMatchWrapper->mGeckoValue.GetUnit());
if (!zeroValue) {
return;
}
aValue.mU.mPtr = new ValueWrapper(valueToMatchWrapper->mPropID,
*zeroValue);
#else
MOZ_CRASH("old style system disabled");
#endif
}
}