gecko/content/smil/nsSMILCSSValueType.cpp

354 lines
13 KiB
C++

/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is the Mozilla SMIL module.
*
* The Initial Developer of the Original Code is the Mozilla Corporation.
* Portions created by the Initial Developer are Copyright (C) 2009
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Daniel Holbert <dholbert@mozilla.com>
*
* Alternatively, the contents of this file may be used under the terms of
* either of the GNU General Public License Version 2 or later (the "GPL"),
* or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
/* representation of a value for a SMIL-animated CSS property */
#include "nsSMILCSSValueType.h"
#include "nsString.h"
#include "nsStyleAnimation.h"
#include "nsStyleCoord.h"
#include "nsSMILParserUtils.h"
#include "nsSMILValue.h"
#include "nsCSSValue.h"
#include "nsCSSDeclaration.h"
#include "nsColor.h"
#include "nsPresContext.h"
#include "nsIContent.h"
#include "nsDebug.h"
/*static*/ nsSMILCSSValueType nsSMILCSSValueType::sSingleton;
struct ValueWrapper {
ValueWrapper() : mCSSValue(), mPropID(eCSSProperty_UNKNOWN),
mPresContext(nsnull) {}
nsStyleCoord mCSSValue;
nsCSSProperty mPropID;
nsPresContext* mPresContext;
};
// Helper "zero" values of various types
// -------------------------------------
static const nsStyleCoord sZeroCoord(0, nsStyleCoord::CoordConstructor);
static const nsStyleCoord sZeroPercent(0.0f, eStyleUnit_Percent);
static const nsStyleCoord sZeroFactor(0.0f, eStyleUnit_Factor);
static const nsStyleCoord sZeroColor(NS_RGB(0,0,0),
nsStyleCoord::ColorConstructor);
// Helper Methods
// --------------
static const nsStyleCoord*
GetZeroValueForUnit(nsStyleUnit aUnit)
{
NS_ABORT_IF_FALSE(aUnit != eStyleUnit_Null,
"Need non-null unit for a zero value.");
switch (aUnit) {
case eStyleUnit_Coord:
return &sZeroCoord;
case eStyleUnit_Percent:
return &sZeroPercent;
case eStyleUnit_Factor:
return &sZeroFactor;
case eStyleUnit_Color:
return &sZeroColor;
default:
NS_NOTREACHED("Calling GetZeroValueForUnit with an unsupported unit");
return nsnull;
}
}
static void
InvertStyleCoordSign(nsStyleCoord& aStyleCoord)
{
switch (aStyleCoord.GetUnit()) {
case eStyleUnit_Coord:
aStyleCoord.SetCoordValue(-aStyleCoord.GetCoordValue());
break;
case eStyleUnit_Percent:
aStyleCoord.SetPercentValue(-aStyleCoord.GetPercentValue());
break;
case eStyleUnit_Factor:
aStyleCoord.SetFactorValue(-aStyleCoord.GetFactorValue());
break;
default:
NS_NOTREACHED("Calling InvertStyleCoordSign with an unsupported unit");
break;
}
}
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
// -------------
nsresult
nsSMILCSSValueType::Init(nsSMILValue& aValue) const
{
NS_ABORT_IF_FALSE(aValue.IsNull(),
"Unexpected value type");
aValue.mU.mPtr = new ValueWrapper();
if (!aValue.mU.mPtr) {
return NS_ERROR_OUT_OF_MEMORY;
}
aValue.mType = this;
return NS_OK;
}
void
nsSMILCSSValueType::Destroy(nsSMILValue& aValue) const
{
NS_ABORT_IF_FALSE(aValue.mType == this, "Unexpected SMIL value type");
NS_ABORT_IF_FALSE(aValue.mU.mPtr,
"nsSMILValue for CSS val should have non-null pointer");
delete static_cast<ValueWrapper*>(aValue.mU.mPtr);
aValue.mU.mPtr = nsnull;
aValue.mType = &nsSMILNullType::sSingleton;
}
nsresult
nsSMILCSSValueType::Assign(nsSMILValue& aDest, const nsSMILValue& aSrc) const
{
NS_ABORT_IF_FALSE(aDest.mType == aSrc.mType, "Incompatible SMIL types");
NS_ABORT_IF_FALSE(aDest.mType == this, "Unexpected SMIL value type");
NS_ABORT_IF_FALSE(aDest.mU.mPtr,
"nsSMILValue for CSS val should have non-null pointer");
NS_ABORT_IF_FALSE(aSrc.mU.mPtr,
"nsSMILValue for CSS val should have non-null pointer");
const ValueWrapper* srcWrapper = ExtractValueWrapper(aSrc);
ValueWrapper* destWrapper = ExtractValueWrapper(aDest);
*destWrapper = *srcWrapper; // Directly copy prop ID & CSS value
return NS_OK;
}
nsresult
nsSMILCSSValueType::Add(nsSMILValue& aDest, const nsSMILValue& aValueToAdd,
PRUint32 aCount) const
{
NS_ABORT_IF_FALSE(aValueToAdd.mType == aDest.mType,
"Trying to add invalid types");
NS_ABORT_IF_FALSE(aValueToAdd.mType == this, "Unexpected source type");
ValueWrapper* destWrapper = ExtractValueWrapper(aDest);
const ValueWrapper* valueToAddWrapper = ExtractValueWrapper(aValueToAdd);
NS_ABORT_IF_FALSE(destWrapper && valueToAddWrapper,
"these pointers shouldn't be null");
if (destWrapper->mPropID == eCSSProperty_UNKNOWN) {
NS_ABORT_IF_FALSE(destWrapper->mCSSValue.IsNull(),
"If property ID is unset, then the unit should be, too");
// We need to update destWrapper, since it's part of an outparam.
destWrapper->mCSSValue =
*GetZeroValueForUnit(valueToAddWrapper->mCSSValue.GetUnit());
destWrapper->mPropID = valueToAddWrapper->mPropID;
destWrapper->mPresContext = valueToAddWrapper->mPresContext;
}
NS_ABORT_IF_FALSE(valueToAddWrapper->mPropID != eCSSProperty_UNKNOWN &&
!valueToAddWrapper->mCSSValue.IsNull(),
"Added amount should be a parsed value");
// Special case: font-size-adjust is explicitly non-additive
if (destWrapper->mPropID == eCSSProperty_font_size_adjust) {
return NS_ERROR_FAILURE;
}
return nsStyleAnimation::Add(destWrapper->mCSSValue,
valueToAddWrapper->mCSSValue, aCount) ?
NS_OK : NS_ERROR_FAILURE;
}
nsresult
nsSMILCSSValueType::ComputeDistance(const nsSMILValue& aFrom,
const nsSMILValue& aTo,
double& aDistance) const
{
NS_ABORT_IF_FALSE(aFrom.mType == aTo.mType,
"Trying to compare different types");
NS_ABORT_IF_FALSE(aFrom.mType == this, "Unexpected source type");
const ValueWrapper* fromWrapper = ExtractValueWrapper(aFrom);
const ValueWrapper* toWrapper = ExtractValueWrapper(aTo);
NS_ABORT_IF_FALSE(fromWrapper && toWrapper,
"These pointers shouldn't be null");
const nsStyleCoord* fromCSSValue;
if (fromWrapper->mPropID == eCSSProperty_UNKNOWN) {
NS_ABORT_IF_FALSE(fromWrapper->mCSSValue.IsNull(),
"If property ID is unset, then the unit should be, too");
fromCSSValue = GetZeroValueForUnit(toWrapper->mCSSValue.GetUnit());
} else {
fromCSSValue = &fromWrapper->mCSSValue;
}
NS_ABORT_IF_FALSE(toWrapper->mPropID != eCSSProperty_UNKNOWN &&
!toWrapper->mCSSValue.IsNull(),
"ComputeDistance endpoint should be a parsed value");
return nsStyleAnimation::ComputeDistance(*fromCSSValue, toWrapper->mCSSValue,
aDistance) ?
NS_OK : NS_ERROR_FAILURE;
}
nsresult
nsSMILCSSValueType::Interpolate(const nsSMILValue& aStartVal,
const nsSMILValue& aEndVal,
double aUnitDistance,
nsSMILValue& aResult) const
{
NS_ABORT_IF_FALSE(aStartVal.mType == aEndVal.mType,
"Trying to interpolate different types");
NS_ABORT_IF_FALSE(aStartVal.mType == this,
"Unexpected types for interpolation");
NS_ABORT_IF_FALSE(aResult.mType == this, "Unexpected result type");
NS_ABORT_IF_FALSE(aUnitDistance >= 0.0 && aUnitDistance <= 1.0,
"unit distance value out of bounds");
const ValueWrapper* startWrapper = ExtractValueWrapper(aStartVal);
const ValueWrapper* endWrapper = ExtractValueWrapper(aEndVal);
ValueWrapper* resultWrapper = ExtractValueWrapper(aResult);
NS_ABORT_IF_FALSE(startWrapper && endWrapper && resultWrapper,
"These pointers shouldn't be null");
const nsStyleCoord* startCSSValue;
if (startWrapper->mPropID == eCSSProperty_UNKNOWN) {
NS_ABORT_IF_FALSE(startWrapper->mCSSValue.IsNull(),
"If property ID is unset, then the unit should be, too");
startCSSValue = GetZeroValueForUnit(endWrapper->mCSSValue.GetUnit());
} else {
startCSSValue = &startWrapper->mCSSValue;
}
NS_ABORT_IF_FALSE(endWrapper->mPropID != eCSSProperty_UNKNOWN &&
!endWrapper->mCSSValue.IsNull(),
"Interpolate endpoint should be a parsed value");
if (nsStyleAnimation::Interpolate(*startCSSValue,
endWrapper->mCSSValue,
aUnitDistance,
resultWrapper->mCSSValue)) {
resultWrapper->mPropID = endWrapper->mPropID;
resultWrapper->mPresContext = endWrapper->mPresContext;
return NS_OK;
}
return NS_ERROR_FAILURE;
}
PRBool
nsSMILCSSValueType::ValueFromString(nsCSSProperty aPropID,
nsIContent* aTargetElement,
const nsAString& aString,
nsSMILValue& aValue) const
{
NS_ABORT_IF_FALSE(aValue.mType == &nsSMILCSSValueType::sSingleton,
"Passed-in value is wrong type");
NS_ABORT_IF_FALSE(aPropID < eCSSProperty_COUNT_no_shorthands,
"nsSMILCSSValueType shouldn't be used with "
"shorthand properties");
// 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.)
PRBool isNegative = PR_FALSE;
PRUint32 subStringBegin = 0;
PRInt32 absValuePos = nsSMILParserUtils::CheckForNegativeNumber(aString);
if (absValuePos > 0) {
subStringBegin = (PRUint32)absValuePos;
isNegative = PR_TRUE;
}
nsDependentSubstring subString(aString, subStringBegin);
ValueWrapper* wrapper = ExtractValueWrapper(aValue);
NS_ABORT_IF_FALSE(wrapper, "Wrapper should be non-null if type is set.");
if (nsStyleAnimation::ComputeValue(aPropID, aTargetElement,
subString, wrapper->mCSSValue)) {
wrapper->mPropID = aPropID;
if (isNegative) {
InvertStyleCoordSign(wrapper->mCSSValue);
}
// Cache a reference to the PresContext, if we've got one
nsIDocument* doc = aTargetElement->GetCurrentDoc();
NS_ABORT_IF_FALSE(doc, "active animations should only be able to "
"target elements that are in a document");
nsIPresShell* shell = doc->GetPrimaryShell();
if (shell) {
wrapper->mPresContext = shell->GetPresContext();
}
if (wrapper->mPresContext) {
// Divide out text-zoom, since SVG is supposed to ignore it
if (aPropID == eCSSProperty_font_size) {
NS_ABORT_IF_FALSE(wrapper->mCSSValue.GetUnit() == eStyleUnit_Coord,
"'font-size' value with unexpected style unit");
wrapper->mCSSValue.SetCoordValue(wrapper->mCSSValue.GetCoordValue() /
wrapper->mPresContext->TextZoom());
}
return PR_TRUE;
}
// Crap! We lack a PresContext or a PresShell
NS_NOTREACHED("Not parsing animation value; unable to get PresContext");
// Destroy & re-initialize aValue to make sure we leave it in a
// consistent state
Destroy(aValue);
Init(aValue);
}
return PR_FALSE;
}
PRBool
nsSMILCSSValueType::ValueToString(const nsSMILValue& aValue,
nsAString& aString) const
{
NS_ABORT_IF_FALSE(aValue.mType == &nsSMILCSSValueType::sSingleton,
"Passed-in value is wrong type");
const ValueWrapper* wrapper = ExtractValueWrapper(aValue);
return nsStyleAnimation::UncomputeValue(wrapper->mPropID,
wrapper->mPresContext,
wrapper->mCSSValue, aString);
}