Bug 515116. DeCOMify SVG length-list, and implement SMIL animation of length-list. r=longsonr, r=dholbert, sr=roc

This commit is contained in:
Jonathan Watt 2010-07-16 22:42:12 +01:00
parent 29bd32b24a
commit fe8a477f84
47 changed files with 5112 additions and 1645 deletions

View File

@ -52,9 +52,12 @@ class nsSMILValue;
// the data upon which it should operate.
//
// We keep the data and type separate rather than just providing different
// subclasses of nsSMILValue as this allows nsSMILValues to be allocated on the
// stack and directly assigned to one another provided performance benefits for
// the animation code.
// subclasses of nsSMILValue. This is so that sizeof(nsSMILValue) is the same
// for all value types, allowing us to have a type-agnostic nsTArray of
// nsSMILValue objects (actual objects, not pointers). It also allows most
// nsSMILValues (except those that need to allocate extra memory for their
// data) to be allocated on the stack and directly assigned to one another
// provided performance benefits for the animation code.
//
// Note that different types have different capabilities. Roughly speaking there
// are probably three main types:

View File

@ -42,6 +42,15 @@
#include "nsISMILType.h"
#include "nsSMILNullType.h"
/**
* Although objects of this type are generally only created on the stack and
* only exist during the taking of a new time sample, that's not always the
* case. The nsSMILValue objects obtained from attributes' base values are
* cached so that the SMIL engine can make certain optimizations during a
* sample if the base value has not changed since the last sample (potentially
* avoiding recomposing). These nsSMILValue objects typically live much longer
* than a single sample.
*/
class nsSMILValue
{
public:

View File

@ -0,0 +1,165 @@
/* -*- 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 Mozilla SVG Project code.
*
* The Initial Developer of the Original Code is the Mozilla Foundation.
* Portions created by the Initial Developer are Copyright (C) 2010
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
*
* Alternatively, the contents of this file may be used under the terms of
* either 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 ***** */
#include "DOMSVGAnimatedLengthList.h"
#include "DOMSVGLengthList.h"
#include "SVGAnimatedLengthList.h"
#include "nsSVGElement.h"
#include "nsCOMPtr.h"
#include "nsSVGAttrTearoffTable.h"
// See the architecture comment in this file's header.
namespace mozilla {
static nsSVGAttrTearoffTable<SVGAnimatedLengthList, DOMSVGAnimatedLengthList>
sSVGAnimatedLengthListTearoffTable;
NS_SVG_VAL_IMPL_CYCLE_COLLECTION(DOMSVGAnimatedLengthList, mElement)
NS_IMPL_CYCLE_COLLECTING_ADDREF(DOMSVGAnimatedLengthList)
NS_IMPL_CYCLE_COLLECTING_RELEASE(DOMSVGAnimatedLengthList)
}
DOMCI_DATA(SVGAnimatedLengthList, DOMSVGAnimatedLengthList)
namespace mozilla {
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(DOMSVGAnimatedLengthList)
NS_INTERFACE_MAP_ENTRY(nsIDOMSVGAnimatedLengthList)
NS_INTERFACE_MAP_ENTRY(nsISupports)
NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(SVGAnimatedLengthList)
NS_INTERFACE_MAP_END
NS_IMETHODIMP
DOMSVGAnimatedLengthList::GetBaseVal(nsIDOMSVGLengthList **_retval)
{
if (!mBaseVal) {
mBaseVal = new DOMSVGLengthList(this);
}
NS_ADDREF(*_retval = mBaseVal);
return NS_OK;
}
NS_IMETHODIMP
DOMSVGAnimatedLengthList::GetAnimVal(nsIDOMSVGLengthList **_retval)
{
if (!mAnimVal) {
mAnimVal = new DOMSVGLengthList(this);
}
NS_ADDREF(*_retval = mAnimVal);
return NS_OK;
}
/* static */ already_AddRefed<DOMSVGAnimatedLengthList>
DOMSVGAnimatedLengthList::GetDOMWrapper(SVGAnimatedLengthList *aList,
nsSVGElement *aElement,
PRUint8 aAttrEnum,
PRUint8 aAxis)
{
DOMSVGAnimatedLengthList *wrapper =
sSVGAnimatedLengthListTearoffTable.GetTearoff(aList);
if (!wrapper) {
wrapper = new DOMSVGAnimatedLengthList(aElement, aAttrEnum, aAxis);
sSVGAnimatedLengthListTearoffTable.AddTearoff(aList, wrapper);
}
NS_ADDREF(wrapper);
return wrapper;
}
/* static */ DOMSVGAnimatedLengthList*
DOMSVGAnimatedLengthList::GetDOMWrapperIfExists(SVGAnimatedLengthList *aList)
{
return sSVGAnimatedLengthListTearoffTable.GetTearoff(aList);
}
DOMSVGAnimatedLengthList::~DOMSVGAnimatedLengthList()
{
// Script no longer has any references to us, to our base/animVal objects, or
// to any of their list items.
sSVGAnimatedLengthListTearoffTable.RemoveTearoff(&InternalAList());
}
void
DOMSVGAnimatedLengthList::InternalBaseValListWillChangeTo(const SVGLengthList& aNewValue)
{
// When the number of items in our internal counterpart's baseVal changes,
// we MUST keep our baseVal in sync. If we don't, script will either see a
// list that is too short and be unable to access indexes that should be
// valid, or else, MUCH WORSE, script will see a list that is too long and be
// able to access "items" at indexes that are out of bounds (read/write to
// bad memory)!!
if (mBaseVal) {
mBaseVal->InternalListLengthWillChange(aNewValue.Length());
}
// If our attribute is not animating, then our animVal mirrors our baseVal
// and we must sync its length too. (If our attribute is animating, then the
// SMIL engine takes care of calling InternalAnimValListWillChangeTo() if
// necessary.)
if (!IsAnimating()) {
InternalAnimValListWillChangeTo(aNewValue);
}
}
void
DOMSVGAnimatedLengthList::InternalAnimValListWillChangeTo(const SVGLengthList& aNewValue)
{
if (mAnimVal) {
mAnimVal->InternalListLengthWillChange(aNewValue.Length());
}
}
PRBool
DOMSVGAnimatedLengthList::IsAnimating() const
{
return InternalAList().IsAnimating();
}
SVGAnimatedLengthList&
DOMSVGAnimatedLengthList::InternalAList()
{
return *mElement->GetAnimatedLengthList(mAttrEnum);
}
const SVGAnimatedLengthList&
DOMSVGAnimatedLengthList::InternalAList() const
{
return *mElement->GetAnimatedLengthList(mAttrEnum);
}
} // namespace mozilla

View File

@ -0,0 +1,229 @@
/* -*- 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 Mozilla SVG Project code.
*
* The Initial Developer of the Original Code is the Mozilla Foundation.
* Portions created by the Initial Developer are Copyright (C) 2010
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
*
* Alternatively, the contents of this file may be used under the terms of
* either 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 ***** */
#ifndef MOZILLA_DOMSVGANIMATEDLENGTHLIST_H__
#define MOZILLA_DOMSVGANIMATEDLENGTHLIST_H__
#include "nsIDOMSVGAnimatedLengthList.h"
#include "nsCycleCollectionParticipant.h"
#include "nsAutoPtr.h"
#include "nsTArray.h"
class nsSVGElement;
namespace mozilla {
class SVGAnimatedLengthList;
class SVGLengthList;
class DOMSVGLengthList;
/**
* Class DOMSVGAnimatedLengthList
*
* This class is used to create the DOM tearoff objects that wrap internal
* SVGAnimatedLengthList objects. We have this internal-DOM split because DOM
* classes are relatively heavy-weight objects with non-optimal interfaces for
* internal code, and they're relatively infrequently used. Having separate
* internal and DOM classes does add complexity - especially for lists where
* the internal list and DOM lists (and their items) need to be kept in sync -
* but it keeps the internal classes light and fast, and in 99% of cases
* they're all that's used. DOM wrappers are only instantiated when script
* demands it.
*
* Ownership model:
*
* The diagram below shows the ownership model between the various DOM objects
* in the tree of DOM objects that correspond to an SVG length list attribute.
* The angled brackets ">" and "<" denote a reference from one object to
* another, where the "!" character denotes a strong reference, and the "~"
* character denotes a weak reference.
*
* .----<!----. .----<!----. .----<!----.
* | | | | | |
* element ~> DOMSVGAnimatedLengthList ~> DOMSVGLengthList ~> DOMSVGLength
*
* Rational:
*
* The following three paragraphs explain the main three requirements that must
* be met by any design. These are followed by an explanation of the rational
* behind our particular design.
*
* 1: DOMSVGAnimatedLengthList, DOMSVGLengthLists and DOMSVGLength get to their
* internal counterparts via their element, and they use their element to send
* out appropriate notifications when they change. Because of this, having
* their element disappear out from under them would be very bad. To keep their
* element alive at least as long as themselves, each of these classes must
* contain a _strong_ reference (directly or indirectly) to their element.
*
* 2: Another central requirement of any design is the SVG specification's
* requirement that script must always be given the exact same objects each
* time it accesses a given object in a DOM object tree corresponding to an SVG
* length list attribute. In practice "always" actually means "whenever script
* has kept a references to a DOM object it previously accessed", since a
* script will only be able to detect any difference in object identity if it
* has a previous reference to compare against.
*
* 3: The wiggle room in the "same object" requirement leads us to a third
* (self imposed) requirement: if script no longer has a reference to a given
* DOM object from an object tree corresponding to an SVG length list
* attribute, and if that object doesn't currently have any descendants, then
* that object should be released to free up memory.
*
* To help in understanding our current design, consider this BROKEN design:
*
* .-------------------------------<!-------------------------.
* |--------------------<!----------------. |
* |----<!----. | |
* | | | |
* element ~> DOMSVGAnimatedLengthList !> DOMSVGLengthList !> DOMSVGLength
*
* Having all the objects keep a reference directly to their element like this
* would reduce the number of dereferences that they need to make to get their
* internal counterpart. Hovewer, this design does not meet the "same object"
* requirement of the SVG specification. If script keeps a reference to a
* DOMSVGLength or DOMSVGLengthList object, but not to that object's
* DOMSVGAnimatedLengthList, then the DOMSVGAnimatedLengthList may be garbage
* collected. We'd then have no way to return the same DOMSVGLength /
* DOMSVGLengthList object that the script has a reference to if the script
* went looking for it via the DOMSVGAnimatedLengthList property on the
* element - we'd end up creating a fresh DOMSVGAnimatedLengthList, with no
* knowlegde of the existing DOMSVGLengthList or DOMSVGLength object.
*
* The way we solve this problem is by making sure that parent objects cannot
* die until all their children are dead by having child objects hold a strong
* reference to their parent object. Note that this design means that the child
* objects hold a strong reference to their element too, albeit indirectly via
* the strong reference to their parent object:
*
* .----<!----. .----<!----. .----<!----.
* | | | | | |
* element ~> DOMSVGAnimatedLengthList ~> DOMSVGLengthList ~> DOMSVGLength
*
* One drawback of this design is that objects must look up their parent
* chain to find their element, but that overhead is relatively small.
*/
class DOMSVGAnimatedLengthList : public nsIDOMSVGAnimatedLengthList
{
friend class DOMSVGLengthList;
public:
NS_DECL_CYCLE_COLLECTING_ISUPPORTS
NS_DECL_CYCLE_COLLECTION_CLASS(DOMSVGAnimatedLengthList)
NS_DECL_NSIDOMSVGANIMATEDLENGTHLIST
/**
* Factory method to create and return a DOMSVGAnimatedLengthList wrapper
* for a given internal SVGAnimatedLengthList object. The factory takes care
* of caching the object that it returns so that the same object can be
* returned for the given SVGAnimatedLengthList each time it is requested.
* The cached object is only removed from the cache when it is destroyed due
* to there being no more references to it or to any of its descendant
* objects. If that happens, any subsequent call requesting the DOM wrapper
* for the SVGAnimatedLengthList will naturally result in a new
* DOMSVGAnimatedLengthList being returned.
*/
static already_AddRefed<DOMSVGAnimatedLengthList>
GetDOMWrapper(SVGAnimatedLengthList *aList,
nsSVGElement *aElement,
PRUint8 aAttrEnum,
PRUint8 aAxis);
/**
* This method returns the DOMSVGAnimatedLengthList wrapper for an internal
* SVGAnimatedLengthList object if it currently has a wrapper. If it does
* not, then nsnull is returned.
*/
static DOMSVGAnimatedLengthList*
GetDOMWrapperIfExists(SVGAnimatedLengthList *aList);
/**
* Called by internal code to notify us when we need to sync the length of
* our baseVal DOM list with its internal list. This is called just prior to
* the length of the internal baseVal list being changed so that any DOM list
* items that need to be removed from the DOM list can first get their values
* from their internal counterpart.
*
* The only time this method could fail is on OOM when trying to increase the
* length of the DOM list. If that happens then this method simply clears the
* list and returns. Callers just proceed as normal, and we simply accept
* that the DOM list will be empty (until successfully set to a new value).
*/
void InternalBaseValListWillChangeTo(const SVGLengthList& aNewValue);
void InternalAnimValListWillChangeTo(const SVGLengthList& aNewValue);
/**
* Returns true if our attribute is animating (in which case our animVal is
* not simply a mirror of our baseVal).
*/
PRBool IsAnimating() const;
private:
/**
* Only our static GetDOMWrapper() factory method may create objects of our
* type.
*/
DOMSVGAnimatedLengthList(nsSVGElement *aElement, PRUint8 aAttrEnum, PRUint8 aAxis)
: mBaseVal(nsnull)
, mAnimVal(nsnull)
, mElement(aElement)
, mAttrEnum(aAttrEnum)
, mAxis(aAxis)
{}
~DOMSVGAnimatedLengthList();
/// Get a reference to this DOM wrapper object's internal counterpart.
SVGAnimatedLengthList& InternalAList();
const SVGAnimatedLengthList& InternalAList() const;
// Weak refs to our DOMSVGLengthList baseVal/animVal objects. These objects
// are friends and take care of clearing these pointers when they die, making
// these true weak references.
DOMSVGLengthList *mBaseVal;
DOMSVGLengthList *mAnimVal;
// Strong ref to our element to keep it alive. We hold this not only for
// ourself, but also for our base/animVal and all of their items.
nsRefPtr<nsSVGElement> mElement;
PRUint8 mAttrEnum;
PRUint8 mAxis;
};
} // namespace mozilla
#endif // MOZILLA_DOMSVGANIMATEDLENGTHLIST_H__

View File

@ -0,0 +1,364 @@
/* -*- 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 Mozilla SVG Project code.
*
* The Initial Developer of the Original Code is the Mozilla Foundation.
* Portions created by the Initial Developer are Copyright (C) 2010
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
*
* Alternatively, the contents of this file may be used under the terms of
* either 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 ***** */
#include "DOMSVGLength.h"
#include "DOMSVGLengthList.h"
#include "DOMSVGAnimatedLengthList.h"
#include "SVGLength.h"
#include "SVGAnimatedLengthList.h"
#include "nsSVGElement.h"
#include "nsIDOMSVGLength.h"
#include "nsDOMError.h"
// See the architecture comment in DOMSVGAnimatedLengthList.h.
namespace mozilla {
// We could use NS_IMPL_CYCLE_COLLECTION_1, except that in Unlink() we need to
// clear our list's weak ref to us to be safe. (The other option would be to
// not unlink and rely on the breaking of the other edges in the cycle, as
// NS_SVG_VAL_IMPL_CYCLE_COLLECTION does.)
NS_IMPL_CYCLE_COLLECTION_CLASS(DOMSVGLength)
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(DOMSVGLength)
// We may not belong to a list, so we must null check tmp->mList.
if (tmp->mList) {
tmp->mList->mItems[tmp->mListIndex] = nsnull;
}
NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mList)
NS_IMPL_CYCLE_COLLECTION_UNLINK_END
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(DOMSVGLength)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mList)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
NS_IMPL_CYCLE_COLLECTING_ADDREF(DOMSVGLength)
NS_IMPL_CYCLE_COLLECTING_RELEASE(DOMSVGLength)
}
DOMCI_DATA(SVGLength, DOMSVGLength)
namespace mozilla {
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(DOMSVGLength)
NS_INTERFACE_MAP_ENTRY(DOMSVGLength) // pseudo-interface
NS_INTERFACE_MAP_ENTRY(nsIDOMSVGLength)
NS_INTERFACE_MAP_ENTRY(nsISupports)
NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(SVGLength)
NS_INTERFACE_MAP_END
DOMSVGLength::DOMSVGLength(DOMSVGLengthList *aList,
PRUint32 aAttrEnum,
PRUint8 aListIndex,
PRUint8 aIsAnimValItem)
: mList(aList)
, mListIndex(aListIndex)
, mAttrEnum(aAttrEnum)
, mIsAnimValItem(aIsAnimValItem)
, mUnit(nsIDOMSVGLength::SVG_LENGTHTYPE_NUMBER)
, mValue(0.0f)
{
#ifdef DEBUG
// These shifts are in sync with the flag member's in the header.
NS_ABORT_IF_FALSE(aList &&
aAttrEnum < (1 << 22) &&
aListIndex < (1 << 4) &&
aIsAnimValItem < (1 << 1), "bad arg");
if (aIsAnimValItem &&
mListIndex >= Element()->GetAnimatedLengthList(mAttrEnum)->GetAnimValue().Length() ||
!aIsAnimValItem &&
mListIndex >= Element()->GetAnimatedLengthList(mAttrEnum)->GetBaseValue().Length()) {
NS_ABORT_IF_FALSE(0, "Bad aListIndex!");
mList = nsnull;
}
#endif
}
DOMSVGLength::DOMSVGLength()
: mList(nsnull)
, mListIndex(0)
, mAttrEnum(0)
, mIsAnimValItem(0)
, mUnit(nsIDOMSVGLength::SVG_LENGTHTYPE_NUMBER)
, mValue(0.0f)
{
}
NS_IMETHODIMP
DOMSVGLength::GetUnitType(PRUint16* aUnit)
{
#ifdef MOZ_SMIL
if (mIsAnimValItem && HasOwner()) {
Element()->FlushAnimations(); // May make HasOwner() == PR_FALSE
}
#endif
*aUnit = HasOwner() ? InternalItem().GetUnit() : mUnit;
return NS_OK;
}
NS_IMETHODIMP
DOMSVGLength::GetValue(float* aValue)
{
#ifdef MOZ_SMIL
if (mIsAnimValItem && HasOwner()) {
Element()->FlushAnimations(); // May make HasOwner() == PR_FALSE
}
#endif
if (HasOwner()) {
*aValue = InternalItem().GetValueInUserUnits(Element(), Axis());
if (NS_FloatIsFinite(*aValue)) {
return NS_OK;
}
} else if (mUnit == nsIDOMSVGLength::SVG_LENGTHTYPE_NUMBER ||
mUnit == nsIDOMSVGLength::SVG_LENGTHTYPE_PX) {
*aValue = mValue;
return NS_OK;
}
// else [SVGWG issue] Can't convert this length's value to user units
// ReportToConsole
return NS_ERROR_FAILURE;
}
NS_IMETHODIMP
DOMSVGLength::SetValue(float aUserUnitValue)
{
if (mIsAnimValItem) {
return NS_ERROR_DOM_NO_MODIFICATION_ALLOWED_ERR;
}
NS_ENSURE_FINITE(aUserUnitValue, NS_ERROR_ILLEGAL_VALUE);
// Although the value passed in is in user units, this method does not turn
// this length into a user unit length. Instead it converts the user unit
// value to this length's current unit and sets that, leaving this length's
// unit as it is.
if (HasOwner()) {
if (InternalItem().SetFromUserUnitValue(aUserUnitValue, Element(), Axis())) {
Element()->DidChangeLengthList(mAttrEnum, PR_TRUE);
#ifdef MOZ_SMIL
if (mList->mAList->IsAnimating()) {
Element()->AnimationNeedsResample();
}
#endif
return NS_OK;
}
} else if (mUnit == nsIDOMSVGLength::SVG_LENGTHTYPE_NUMBER ||
mUnit == nsIDOMSVGLength::SVG_LENGTHTYPE_PX) {
mValue = aUserUnitValue;
return NS_OK;
}
// else [SVGWG issue] Can't convert user unit value to this length's unit
// ReportToConsole
return NS_ERROR_FAILURE;
}
NS_IMETHODIMP
DOMSVGLength::GetValueInSpecifiedUnits(float* aValue)
{
#ifdef MOZ_SMIL
if (mIsAnimValItem && HasOwner()) {
Element()->FlushAnimations(); // May make HasOwner() == PR_FALSE
}
#endif
*aValue = HasOwner() ? InternalItem().GetValueInCurrentUnits() : mValue;
return NS_OK;
}
NS_IMETHODIMP
DOMSVGLength::SetValueInSpecifiedUnits(float aValue)
{
if (mIsAnimValItem) {
return NS_ERROR_DOM_NO_MODIFICATION_ALLOWED_ERR;
}
NS_ENSURE_FINITE(aValue, NS_ERROR_ILLEGAL_VALUE);
if (HasOwner()) {
InternalItem().SetValueInCurrentUnits(aValue);
Element()->DidChangeLengthList(mAttrEnum, PR_TRUE);
#ifdef MOZ_SMIL
if (mList->mAList->IsAnimating()) {
Element()->AnimationNeedsResample();
}
#endif
return NS_OK;
}
mValue = aValue;
return NS_OK;
}
NS_IMETHODIMP
DOMSVGLength::SetValueAsString(const nsAString& aValue)
{
if (mIsAnimValItem) {
return NS_ERROR_DOM_NO_MODIFICATION_ALLOWED_ERR;
}
SVGLength value;
if (!value.SetValueFromString(aValue)) {
return NS_ERROR_DOM_SYNTAX_ERR;
}
if (HasOwner()) {
InternalItem() = value;
Element()->DidChangeLengthList(mAttrEnum, PR_TRUE);
#ifdef MOZ_SMIL
if (mList->mAList->IsAnimating()) {
Element()->AnimationNeedsResample();
}
#endif
return NS_OK;
}
mValue = value.GetValueInCurrentUnits();
mUnit = value.GetUnit();
return NS_OK;
}
NS_IMETHODIMP
DOMSVGLength::GetValueAsString(nsAString& aValue)
{
#ifdef MOZ_SMIL
if (mIsAnimValItem && HasOwner()) {
Element()->FlushAnimations(); // May make HasOwner() == PR_FALSE
}
#endif
if (HasOwner()) {
InternalItem().GetValueAsString(aValue);
return NS_OK;
}
SVGLength(mValue, mUnit).GetValueAsString(aValue);
return NS_OK;
}
NS_IMETHODIMP
DOMSVGLength::NewValueSpecifiedUnits(PRUint16 aUnit, float aValue)
{
if (mIsAnimValItem) {
return NS_ERROR_DOM_NO_MODIFICATION_ALLOWED_ERR;
}
NS_ENSURE_FINITE(aValue, NS_ERROR_ILLEGAL_VALUE);
if (!SVGLength::IsValidUnitType(aUnit)) {
return NS_ERROR_DOM_NOT_SUPPORTED_ERR;
}
if (HasOwner()) {
InternalItem().SetValueAndUnit(aValue, aUnit);
Element()->DidChangeLengthList(mAttrEnum, PR_TRUE);
#ifdef MOZ_SMIL
if (mList->mAList->IsAnimating()) {
Element()->AnimationNeedsResample();
}
#endif
return NS_OK;
}
mUnit = PRUint8(aUnit);
mValue = aValue;
return NS_OK;
}
NS_IMETHODIMP
DOMSVGLength::ConvertToSpecifiedUnits(PRUint16 aUnit)
{
if (mIsAnimValItem) {
return NS_ERROR_DOM_NO_MODIFICATION_ALLOWED_ERR;
}
if (!SVGLength::IsValidUnitType(aUnit)) {
return NS_ERROR_DOM_NOT_SUPPORTED_ERR;
}
if (HasOwner()) {
if (InternalItem().ConvertToUnit(PRUint8(aUnit), Element(), Axis())) {
return NS_OK;
}
} else {
SVGLength len(mValue, mUnit);
if (len.ConvertToUnit(PRUint8(aUnit), nsnull, 0)) {
mValue = len.GetValueInCurrentUnits();
mUnit = aUnit;
return NS_OK;
}
}
// else [SVGWG issue] Can't convert unit
// ReportToConsole
return NS_ERROR_FAILURE;
}
void
DOMSVGLength::InsertingIntoList(DOMSVGLengthList *aList,
PRUint32 aAttrEnum,
PRUint8 aListIndex,
PRUint8 aIsAnimValItem)
{
NS_ASSERTION(!HasOwner(), "Inserting item that is already in a list");
NS_ASSERTION(mIsAnimValItem &&
aListIndex < aList->Element()->GetAnimatedLengthList(aAttrEnum)->GetAnimValue().Length() ||
!aIsAnimValItem &&
aListIndex < aList->Element()->GetAnimatedLengthList(aAttrEnum)->GetBaseValue().Length(),
"mListIndex too big");
mList = aList;
mAttrEnum = aAttrEnum;
mListIndex = aListIndex;
mIsAnimValItem = aIsAnimValItem;
}
void
DOMSVGLength::RemovingFromList()
{
mValue = InternalItem().GetValueInCurrentUnits();
mUnit = InternalItem().GetUnit();
mList = nsnull;
mIsAnimValItem = 0;
}
SVGLength
DOMSVGLength::ToSVGLength()
{
if (HasOwner()) {
return SVGLength(InternalItem().GetValueInCurrentUnits(),
InternalItem().GetUnit());
}
return SVGLength(mValue, mUnit);
}
SVGLength&
DOMSVGLength::InternalItem()
{
SVGAnimatedLengthList *alist = Element()->GetAnimatedLengthList(mAttrEnum);
return mIsAnimValItem && alist->mAnimVal ?
(*alist->mAnimVal)[mListIndex] :
alist->mBaseVal[mListIndex];
}
} // namespace mozilla

View File

@ -0,0 +1,226 @@
/* -*- 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 Mozilla SVG Project code.
*
* The Initial Developer of the Original Code is the Mozilla Foundation.
* Portions created by the Initial Developer are Copyright (C) 2010
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
*
* Alternatively, the contents of this file may be used under the terms of
* either 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 ***** */
#ifndef MOZILLA_DOMSVGLENGTH_H__
#define MOZILLA_DOMSVGLENGTH_H__
#include "nsIDOMSVGLength.h"
#include "DOMSVGLengthList.h"
#include "SVGLength.h"
#include "nsCycleCollectionParticipant.h"
#include "nsAutoPtr.h"
class nsSVGElement;
// We make DOMSVGLength a pseudo-interface to allow us to QI to it in order to
// check that the objects that scripts pass to DOMSVGLengthList methods are our
// *native* length objects.
//
// {A8468350-7F7B-4976-9A7E-3765A1DADF9A}
#define MOZILLA_DOMSVGLENGTH_IID \
{ 0xA8468350, 0x7F7B, 0x4976, { 0x9A, 0x7E, 0x37, 0x65, 0xA1, 0xDA, 0xDF, 0x9A } }
namespace mozilla {
/**
* Class DOMSVGLength
*
* This class creates the DOM objects that wrap internal SVGLength objects that
* are in an SVGLengthList. It is also used to create the objects returned by
* SVGSVGElement.createSVGLength().
*
* For the DOM wrapper classes for non-list SVGLength, see nsSVGLength2.h.
*
* See the architecture comment in DOMSVGAnimatedLengthList.h.
*
* This class is strongly intertwined with DOMSVGAnimatedLengthList and
* DOMSVGLengthList. We are a friend of DOMSVGLengthList, and are responsible
* for nulling out our DOMSVGLengthList's pointer to us when we die, making it
* a real weak pointer.
*
* When objects of this type are in a DOMSVGLengthList they belong to an
* attribute. While they belong to an attribute, the objects' values come from
* their corresponding internal SVGLength objects in the internal SVGLengthList
* objects for the attribute. Getting and setting values of a DOMSVGLength
* requires reading and writing to its internal SVGLength. However, if the
* DOMSVGLength is detached from its DOMSVGLengthList then it first makes a
* copy of its internal SVGLength's value and unit so that it doesn't appear to
* "lose" its value from script's perspective on being removed from the list.
* This means that these DOM tearoffs have space to store these values, even
* though they're not used in the common case.
*
* This class also stores its current list index, attribute enum, and whether
* it belongs to a baseVal or animVal list. This is so that objects of this
* type can find their corresponding internal SVGLength.
*
* To use these classes for <length> attributes as well as <list-of-length>
* attributes, we would need to take a bit from mListIndex and use that to
* indicate whether the object belongs to a list or non-list attribute, then
* if-else as appropriate. The bug for doing that work is:
* https://bugzilla.mozilla.org/show_bug.cgi?id=571734
*/
class DOMSVGLength : public nsIDOMSVGLength
{
public:
NS_DECLARE_STATIC_IID_ACCESSOR(MOZILLA_DOMSVGLENGTH_IID)
NS_DECL_CYCLE_COLLECTING_ISUPPORTS
NS_DECL_CYCLE_COLLECTION_CLASS(DOMSVGLength)
NS_DECL_NSIDOMSVGLENGTH
/**
* Generic ctor for DOMSVGLength objects that are created for an attribute.
*/
DOMSVGLength(DOMSVGLengthList *aList,
PRUint32 aAttrEnum,
PRUint8 aListIndex,
PRUint8 aIsAnimValItem);
/**
* Ctor for creating the objects returned by SVGSVGElement.createSVGLength(),
* which do not initially belong to an attribute.
*/
DOMSVGLength();
~DOMSVGLength() {
// Our mList's weak ref to us must be nulled out when we die. If GC has
// unlinked us using the cycle collector code, then that has already
// happened, and mList is null.
if (mList) {
mList->mItems[mListIndex] = nsnull;
}
};
/**
* Create an unowned copy of an owned length. The caller is responsible for
* the first AddRef().
*/
DOMSVGLength* Copy() {
NS_ASSERTION(mList, "unexpected caller");
DOMSVGLength *copy = new DOMSVGLength();
SVGLength &length = InternalItem();
copy->NewValueSpecifiedUnits(length.GetUnit(), length.GetValueInCurrentUnits());
return copy;
}
PRBool IsInList() const {
return !!mList;
}
/**
* In future, if this class is used for non-list lengths, this will be
* different to IsInList().
*/
PRBool HasOwner() const {
return !!mList;
}
/**
* This method is called to notify this DOM object that it is being inserted
* into a list, and give it the information it needs as a result.
*
* This object MUST NOT already belong to a list when this method is called.
* That's not to say that script can't move these DOM objects between
* lists - it can - it's just that the logic to handle that (and send out
* the necessary notifications) is located elsewhere (in DOMSVGLengthList).)
*/
void InsertingIntoList(DOMSVGLengthList *aList,
PRUint32 aAttrEnum,
PRUint8 aListIndex,
PRUint8 aIsAnimValItem);
/// This method is called to notify this object that its list index changed.
void UpdateListIndex(PRUint8 aListIndex) {
mListIndex = aListIndex;
}
/**
* This method is called to notify this DOM object that it is about to be
* removed from its current DOM list so that it can first make a copy of its
* internal counterpart's values. (If it didn't do this, then it would
* "loose" its value on being removed.)
*/
void RemovingFromList();
SVGLength ToSVGLength();
private:
nsSVGElement* Element() {
return mList->Element();
}
PRUint8 AttrEnum() const {
return mAttrEnum;
}
/**
* Get the axis that this length lies along. This method must only be called
* when this object is associated with an element (HasOwner() returns true).
*/
PRUint8 Axis() const {
return mList->Axis();
}
/**
* Get a reference to the internal SVGLength list item that this DOM wrapper
* object currently wraps.
*
* To simplyfy the code we just have this one method for obtaining both
* baseVal and animVal internal items. This means that animVal items don't
* get const protection, but then our setter methods guard against changing
* animVal items.
*/
SVGLength& InternalItem();
nsRefPtr<DOMSVGLengthList> mList;
// Bounds for the following are checked in the ctor, so be sure to update
// that if you change the capacity of any of the following.
PRUint32 mListIndex:22; // supports > 4 million list items
PRUint32 mAttrEnum:4; // supports up to 16 attributes
PRUint32 mIsAnimValItem:1;
// The following members are only used when we're not in a list:
PRUint32 mUnit:5; // can handle 31 units (the 10 SVG 1.1 units + rem, vw, vh, wm, calc + future additions)
float mValue;
};
NS_DEFINE_STATIC_IID_ACCESSOR(DOMSVGLength, MOZILLA_DOMSVGLENGTH_IID)
} // namespace mozilla
#endif // MOZILLA_DOMSVGLENGTH_H__

View File

@ -0,0 +1,329 @@
/* -*- 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 Mozilla SVG Project code.
*
* The Initial Developer of the Original Code is the Mozilla Foundation.
* Portions created by the Initial Developer are Copyright (C) 2010
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
*
* Alternatively, the contents of this file may be used under the terms of
* either 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 ***** */
#include "nsSVGElement.h"
#include "DOMSVGLengthList.h"
#include "DOMSVGLength.h"
#include "nsDOMError.h"
#include "SVGAnimatedLengthList.h"
#include "nsCOMPtr.h"
// See the comment in this file's header.
namespace mozilla {
// We could use NS_IMPL_CYCLE_COLLECTION_1, except that in Unlink() we need to
// clear our DOMSVGAnimatedLengthList's weak ref to us to be safe. (The other
// option would be to not unlink and rely on the breaking of the other edges in
// the cycle, as NS_SVG_VAL_IMPL_CYCLE_COLLECTION does.)
NS_IMPL_CYCLE_COLLECTION_CLASS(DOMSVGLengthList)
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(DOMSVGLengthList)
// No need to null check tmp - script/SMIL can't detach us from mAList
( tmp->IsAnimValList() ? tmp->mAList->mAnimVal : tmp->mAList->mBaseVal ) = nsnull;
NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mAList)
NS_IMPL_CYCLE_COLLECTION_UNLINK_END
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(DOMSVGLengthList)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mAList)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
NS_IMPL_CYCLE_COLLECTING_ADDREF(DOMSVGLengthList)
NS_IMPL_CYCLE_COLLECTING_RELEASE(DOMSVGLengthList)
}
DOMCI_DATA(SVGLengthList, DOMSVGLengthList)
namespace mozilla {
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(DOMSVGLengthList)
NS_INTERFACE_MAP_ENTRY(nsIDOMSVGLengthList)
NS_INTERFACE_MAP_ENTRY(nsISupports)
NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(SVGLengthList)
NS_INTERFACE_MAP_END
void
DOMSVGLengthList::InternalListLengthWillChange(PRUint32 aNewLength)
{
PRUint32 oldLength = mItems.Length();
// If our length will decrease, notify the items that will be removed:
for (PRUint32 i = aNewLength; i < oldLength; ++i) {
if (mItems[i]) {
mItems[i]->RemovingFromList();
}
}
if (!mItems.SetLength(aNewLength)) { // OOM
mItems.Clear();
return;
}
// If our length has increased, null out the new pointers:
for (PRUint32 i = oldLength; i < aNewLength; ++i) {
mItems[i] = nsnull;
}
}
SVGLengthList&
DOMSVGLengthList::InternalList()
{
SVGAnimatedLengthList *alist = Element()->GetAnimatedLengthList(AttrEnum());
return IsAnimValList() && alist->mAnimVal ? *alist->mAnimVal : alist->mBaseVal;
}
// ----------------------------------------------------------------------------
// nsIDOMSVGLengthList implementation:
NS_IMETHODIMP
DOMSVGLengthList::GetNumberOfItems(PRUint32 *aNumberOfItems)
{
#ifdef MOZ_SMIL
if (IsAnimValList()) {
Element()->FlushAnimations();
}
#endif
*aNumberOfItems = Length();
return NS_OK;
}
NS_IMETHODIMP
DOMSVGLengthList::Clear()
{
if (IsAnimValList()) {
return NS_ERROR_DOM_NO_MODIFICATION_ALLOWED_ERR;
}
if (Length() > 0) {
// Notify any existing DOM items of removal *before* truncating the lists
// so that they can find their SVGLength internal counterparts and copy
// their values:
mAList->InternalBaseValListWillChangeTo(SVGLengthList());
mItems.Clear();
InternalList().Clear();
Element()->DidChangeLengthList(AttrEnum(), PR_TRUE);
#ifdef MOZ_SMIL
if (mAList->IsAnimating()) {
Element()->AnimationNeedsResample();
}
#endif
}
return NS_OK;
}
NS_IMETHODIMP
DOMSVGLengthList::Initialize(nsIDOMSVGLength *newItem,
nsIDOMSVGLength **_retval)
{
*_retval = nsnull;
if (IsAnimValList()) {
return NS_ERROR_DOM_NO_MODIFICATION_ALLOWED_ERR;
}
// If newItem is already in a list we should insert a clone of newItem, and
// for consistency, this should happen even if *this* is the list that
// newItem is currently in. Note that in the case of newItem being in this
// list, the Clear() call before the InsertItemBefore() call would remove it
// from this list, and so the InsertItemBefore() call would not insert a
// clone of newItem, it would actually insert newItem. To prevent that from
// happening we have to do the clone here, if necessary.
nsCOMPtr<DOMSVGLength> domItem = do_QueryInterface(newItem);
if (!domItem) {
return NS_ERROR_DOM_SVG_WRONG_TYPE_ERR;
}
if (domItem->HasOwner()) {
newItem = domItem->Copy();
}
Clear();
return InsertItemBefore(newItem, 0, _retval);
}
NS_IMETHODIMP
DOMSVGLengthList::GetItem(PRUint32 index,
nsIDOMSVGLength **_retval)
{
#ifdef MOZ_SMIL
if (IsAnimValList()) {
Element()->FlushAnimations();
}
#endif
if (index < Length()) {
EnsureItemAt(index);
NS_ADDREF(*_retval = mItems[index]);
return NS_OK;
}
*_retval = nsnull;
return NS_ERROR_DOM_INDEX_SIZE_ERR;
}
NS_IMETHODIMP
DOMSVGLengthList::InsertItemBefore(nsIDOMSVGLength *newItem,
PRUint32 index,
nsIDOMSVGLength **_retval)
{
*_retval = nsnull;
if (IsAnimValList()) {
return NS_ERROR_DOM_NO_MODIFICATION_ALLOWED_ERR;
}
nsCOMPtr<DOMSVGLength> domItem = do_QueryInterface(newItem);
if (!domItem) {
return NS_ERROR_DOM_SVG_WRONG_TYPE_ERR;
}
index = NS_MIN(index, Length());
SVGLength length = domItem->ToSVGLength(); // get before setting domItem
if (domItem->HasOwner()) {
domItem = new DOMSVGLength();
}
PRBool ok = !!InternalList().InsertItem(index, length);
if (!ok) {
return NS_ERROR_OUT_OF_MEMORY;
}
domItem->InsertingIntoList(this, AttrEnum(), index, IsAnimValList());
ok = !!mItems.InsertElementAt(index, domItem.get());
if (!ok) {
InternalList().RemoveItem(index);
return NS_ERROR_OUT_OF_MEMORY;
}
for (PRUint32 i = index + 1; i < Length(); ++i) {
if (mItems[i]) {
mItems[i]->UpdateListIndex(i);
}
}
Element()->DidChangeLengthList(AttrEnum(), PR_TRUE);
#ifdef MOZ_SMIL
if (mAList->IsAnimating()) {
Element()->AnimationNeedsResample();
}
#endif
*_retval = domItem.forget().get();
return NS_OK;
}
NS_IMETHODIMP
DOMSVGLengthList::ReplaceItem(nsIDOMSVGLength *newItem,
PRUint32 index,
nsIDOMSVGLength **_retval)
{
*_retval = nsnull;
if (IsAnimValList()) {
return NS_ERROR_DOM_NO_MODIFICATION_ALLOWED_ERR;
}
nsCOMPtr<DOMSVGLength> domItem = do_QueryInterface(newItem);
if (!domItem) {
return NS_ERROR_DOM_SVG_WRONG_TYPE_ERR;
}
if (index >= Length()) {
return NS_ERROR_DOM_INDEX_SIZE_ERR;
}
SVGLength length = domItem->ToSVGLength(); // get before setting domItem
if (domItem->HasOwner()) {
domItem = new DOMSVGLength();
}
if (mItems[index]) {
// Notify any existing DOM item of removal *before* modifying the lists so
// that the DOM item can copy the *old* value at its index:
mItems[index]->RemovingFromList();
}
InternalList()[index] = length;
domItem->InsertingIntoList(this, AttrEnum(), index, IsAnimValList());
mItems[index] = domItem;
Element()->DidChangeLengthList(AttrEnum(), PR_TRUE);
#ifdef MOZ_SMIL
if (mAList->IsAnimating()) {
Element()->AnimationNeedsResample();
}
#endif
NS_ADDREF(*_retval = domItem.get());
return NS_OK;
}
NS_IMETHODIMP
DOMSVGLengthList::RemoveItem(PRUint32 index,
nsIDOMSVGLength **_retval)
{
*_retval = nsnull;
if (IsAnimValList()) {
return NS_ERROR_DOM_NO_MODIFICATION_ALLOWED_ERR;
}
if (index >= Length()) {
return NS_ERROR_DOM_INDEX_SIZE_ERR;
}
// We have to return the removed item, so make sure it exists:
EnsureItemAt(index);
// Notify the DOM item of removal *before* modifying the lists so that the
// DOM item can copy its *old* value:
mItems[index]->RemovingFromList();
InternalList().RemoveItem(index);
NS_ADDREF(*_retval = mItems[index]);
mItems.RemoveElementAt(index);
for (PRUint32 i = index; i < Length(); ++i) {
if (mItems[i]) {
mItems[i]->UpdateListIndex(i);
}
}
Element()->DidChangeLengthList(AttrEnum(), PR_TRUE);
#ifdef MOZ_SMIL
if (mAList->IsAnimating()) {
Element()->AnimationNeedsResample();
}
#endif
return NS_OK;
}
NS_IMETHODIMP
DOMSVGLengthList::AppendItem(nsIDOMSVGLength *newItem,
nsIDOMSVGLength **_retval)
{
return InsertItemBefore(newItem, Length(), _retval);
}
void
DOMSVGLengthList::EnsureItemAt(PRUint32 aIndex)
{
if (!mItems[aIndex]) {
mItems[aIndex] = new DOMSVGLength(this, AttrEnum(), aIndex, IsAnimValList());
}
}
} // namespace mozilla

View File

@ -0,0 +1,157 @@
/* -*- 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 Mozilla SVG Project code.
*
* The Initial Developer of the Original Code is the Mozilla Foundation.
* Portions created by the Initial Developer are Copyright (C) 2010
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
*
* Alternatively, the contents of this file may be used under the terms of
* either 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 ***** */
#ifndef MOZILLA_DOMSVGLENGTHLIST_H__
#define MOZILLA_DOMSVGLENGTHLIST_H__
#include "nsIDOMSVGLengthList.h"
#include "SVGLengthList.h"
#include "SVGLength.h"
#include "DOMSVGAnimatedLengthList.h"
#include "nsCOMArray.h"
#include "nsAutoPtr.h"
class nsSVGElement;
namespace mozilla {
class DOMSVGLength;
/**
* Class DOMSVGLengthList
*
* This class is used to create the DOM tearoff objects that wrap internal
* SVGLengthList objects.
*
* See the architecture comment in DOMSVGAnimatedLengthList.h.
*
* This class is strongly intertwined with DOMSVGAnimatedLengthList and
* DOMSVGLength. We are a friend of DOMSVGAnimatedLengthList, and are
* responsible for nulling out our DOMSVGAnimatedLengthList's pointer to us
* when we die, essentially making its pointer to us a weak pointer. Similarly,
* our DOMSVGLength items are friends of us and responsible for nulling out our
* pointers to them.
*
* Our DOM items are created lazily on demand as and when script requests them.
*/
class DOMSVGLengthList : public nsIDOMSVGLengthList
{
friend class DOMSVGLength;
public:
NS_DECL_CYCLE_COLLECTING_ISUPPORTS
NS_DECL_CYCLE_COLLECTION_CLASS(DOMSVGLengthList)
NS_DECL_NSIDOMSVGLENGTHLIST
DOMSVGLengthList(DOMSVGAnimatedLengthList *aAList)
: mAList(aAList)
{
// We silently ignore SetLength OOM failure since being out of sync is safe
// so long as we have *fewer* items than our internal list.
mItems.SetLength(InternalList().Length());
for (PRUint32 i = 0; i < Length(); ++i) {
// null out all the pointers - items are created on-demand
mItems[i] = nsnull;
}
}
~DOMSVGLengthList() {
// Our mAList's weak ref to us must be nulled out when we die. If GC has
// unlinked us using the cycle collector code, then that has already
// happened, and mAList is null.
if (mAList) {
( IsAnimValList() ? mAList->mAnimVal : mAList->mBaseVal ) = nsnull;
}
};
/**
* This will normally be the same as InternalList().Length(), except if we've
* hit OOM in which case our length will be zero.
*/
PRUint32 Length() const {
NS_ABORT_IF_FALSE(mItems.Length() == 0 ||
mItems.Length() ==
const_cast<DOMSVGLengthList*>(this)->InternalList().Length(),
"DOM wrapper's list length is out of sync");
return mItems.Length();
}
/// Called to notify us to syncronize our length and detach excess items.
void InternalListLengthWillChange(PRUint32 aNewLength);
private:
nsSVGElement* Element() {
return mAList->mElement;
}
PRUint8 AttrEnum() const {
return mAList->mAttrEnum;
}
PRUint8 Axis() const {
return mAList->mAxis;
}
/// Used to determine if this list is the baseVal or animVal list.
PRBool IsAnimValList() const {
return this == mAList->mAnimVal;
}
/**
* Get a reference to this object's corresponding internal SVGLengthList.
*
* To simplyfy the code we just have this one method for obtaining both
* baseVal and animVal internal lists. This means that animVal lists don't
* get const protection, but our setter methods guard against changing
* animVal lists.
*/
SVGLengthList& InternalList();
/// Creates a DOMSVGLength for aIndex, if it doesn't already exist.
void EnsureItemAt(PRUint32 aIndex);
// Weak refs to our DOMSVGLength items. The items are friends and take care
// of clearing our pointer to them when they die.
nsTArray<DOMSVGLength*> mItems;
nsRefPtr<DOMSVGAnimatedLengthList> mAList;
};
} // namespace mozilla
#endif // MOZILLA_DOMSVGLENGTHLIST_H__

View File

@ -49,12 +49,14 @@ LIBRARY_NAME = gkcontentsvg_s
LIBXUL_LIBRARY = 1
CPPSRCS = \
DOMSVGAnimatedLengthList.cpp \
DOMSVGLength.cpp \
DOMSVGLengthList.cpp \
nsDOMSVGZoomEvent.cpp \
nsDOMSVGEvent.cpp \
nsSVGAElement.cpp \
nsSVGAltGlyphElement.cpp \
nsSVGAngle.cpp \
nsSVGAnimatedLengthList.cpp \
nsSVGAnimatedNumberList.cpp \
nsSVGAnimatedTransformList.cpp \
nsSVGBoolean.cpp \
@ -76,9 +78,7 @@ CPPSRCS = \
nsSVGGraphicElement.cpp \
nsSVGImageElement.cpp \
nsSVGInteger.cpp \
nsSVGLength.cpp \
nsSVGLength2.cpp \
nsSVGLengthList.cpp \
nsSVGLineElement.cpp \
nsSVGMarkerElement.cpp \
nsSVGMaskElement.cpp \
@ -122,6 +122,9 @@ CPPSRCS = \
nsSVGUseElement.cpp \
nsSVGValue.cpp \
nsSVGViewBox.cpp \
SVGAnimatedLengthList.cpp \
SVGLength.cpp \
SVGLengthList.cpp \
$(NULL)
ifdef MOZ_SMIL
@ -133,6 +136,7 @@ CPPSRCS += nsSVGAnimateElement.cpp \
nsSVGSetElement.cpp \
nsSVGTransformSMILType.cpp \
nsSVGTransformSMILAttr.cpp \
SVGLengthListSMILType.cpp \
SVGMotionSMILType.cpp \
SVGMotionSMILAttr.cpp \
SVGMotionSMILAnimationFunction.cpp \

View File

@ -0,0 +1,244 @@
/* -*- 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 Mozilla SVG Project code.
*
* The Initial Developer of the Original Code is the Mozilla Foundation.
* Portions created by the Initial Developer are Copyright (C) 2010
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
*
* Alternatively, the contents of this file may be used under the terms of
* either 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 ***** */
#include "SVGAnimatedLengthList.h"
#include "DOMSVGAnimatedLengthList.h"
#include "nsSVGElement.h"
#include "nsSVGAttrTearoffTable.h"
#ifdef MOZ_SMIL
#include "nsSMILValue.h"
#include "SVGLengthListSMILType.h"
#endif // MOZ_SMIL
namespace mozilla {
nsresult
SVGAnimatedLengthList::SetBaseValueString(const nsAString& aValue)
{
SVGLengthList newBaseValue;
nsresult rv = newBaseValue.SetValueFromString(aValue);
if (NS_FAILED(rv)) {
return rv;
}
DOMSVGAnimatedLengthList *domWrapper =
DOMSVGAnimatedLengthList::GetDOMWrapperIfExists(this);
if (domWrapper) {
// We must send this notification *before* changing mBaseVal! If the length
// of our baseVal is being reduced, our baseVal's DOM wrapper list may have
// to remove DOM items from itself, and any removed DOM items need to copy
// their internal counterpart values *before* we change them.
//
domWrapper->InternalBaseValListWillChangeTo(newBaseValue);
}
// We don't need to call DidChange* here - we're only called by
// nsSVGElement::ParseAttribute under nsGenericElement::SetAttr,
// which takes care of notifying.
rv = mBaseVal.CopyFrom(newBaseValue);
if (NS_FAILED(rv)) {
// Attempting to increase mBaseVal's length failed - reduce domWrapper
// back to the same length:
domWrapper->InternalBaseValListWillChangeTo(mBaseVal);
}
return rv;
}
void
SVGAnimatedLengthList::ClearBaseValue(PRUint32 aAttrEnum)
{
DOMSVGAnimatedLengthList *domWrapper =
DOMSVGAnimatedLengthList::GetDOMWrapperIfExists(this);
if (domWrapper) {
// We must send this notification *before* changing mBaseVal! (See above.)
domWrapper->InternalAnimValListWillChangeTo(SVGLengthList());
}
mBaseVal.Clear();
// Caller notifies
}
nsresult
SVGAnimatedLengthList::SetAnimValue(const SVGLengthList& aNewAnimValue,
nsSVGElement *aElement,
PRUint32 aAttrEnum)
{
DOMSVGAnimatedLengthList *domWrapper =
DOMSVGAnimatedLengthList::GetDOMWrapperIfExists(this);
if (domWrapper) {
// A new animation may totally change the number of items in the animVal
// list, replacing what was essentially a mirror of the baseVal list, or
// else replacing and overriding an existing animation. When this happens
// we must try and keep our animVal's DOM wrapper in sync (see the comment
// in DOMSVGAnimatedLengthList::InternalBaseValListWillChangeTo).
//
// It's not possible for us to reliably distinguish between calls to this
// method that are setting a new sample for an existing animation, and
// calls that are setting the first sample of an animation that will
// override an existing animation. Happily it's cheap to just blindly
// notify our animVal's DOM wrapper of its internal counterpart's new value
// each time this method is called, so that's what we do.
//
// Note that we must send this notification *before* setting or changing
// mAnimVal! (See the comment in SetBaseValueString above.)
//
domWrapper->InternalAnimValListWillChangeTo(aNewAnimValue);
}
if (!mAnimVal) {
mAnimVal = new SVGLengthList();
}
nsresult rv = mAnimVal->CopyFrom(aNewAnimValue);
if (NS_FAILED(rv)) {
// OOM. We clear the animation, and, importantly, ClearAnimValue() ensures
// that mAnimVal and its DOM wrapper (if any) will have the same length!
ClearAnimValue(aElement, aAttrEnum);
return rv;
}
aElement->DidAnimateLengthList(aAttrEnum);
return NS_OK;
}
void
SVGAnimatedLengthList::ClearAnimValue(nsSVGElement *aElement,
PRUint32 aAttrEnum)
{
DOMSVGAnimatedLengthList *domWrapper =
DOMSVGAnimatedLengthList::GetDOMWrapperIfExists(this);
if (domWrapper) {
// When all animation ends, animVal simply mirrors baseVal, which may have
// a different number of items to the last active animated value. We must
// keep the length of our animVal's DOM wrapper list in sync, and again we
// must do that before touching mAnimVal. See comments above.
//
domWrapper->InternalAnimValListWillChangeTo(mBaseVal);
}
mAnimVal = nsnull;
aElement->DidAnimateLengthList(aAttrEnum);
}
#ifdef MOZ_SMIL
nsISMILAttr*
SVGAnimatedLengthList::ToSMILAttr(nsSVGElement *aSVGElement,
PRUint8 aAttrEnum,
PRUint8 aAxis,
PRBool aCanZeroPadList)
{
return new SMILAnimatedLengthList(this, aSVGElement, aAttrEnum, aAxis, aCanZeroPadList);
}
nsresult
SVGAnimatedLengthList::
SMILAnimatedLengthList::ValueFromString(const nsAString& aStr,
const nsISMILAnimationElement* /*aSrcElement*/,
nsSMILValue& aValue,
PRBool& aPreventCachingOfSandwich) const
{
nsSMILValue val(&SVGLengthListSMILType::sSingleton);
SVGLengthListAndInfo *llai = static_cast<SVGLengthListAndInfo*>(val.mU.mPtr);
nsresult rv = llai->SetValueFromString(aStr);
if (NS_SUCCEEDED(rv)) {
llai->SetInfo(mElement, mAxis, mCanZeroPadList);
aValue.Swap(val);
// If any of the lengths in the list depend on their context, then we must
// prevent caching of the entire animation sandwich. This is because the
// units of a length at a given index can change from sandwich layer to
// layer, and indeed even be different within a single sandwich layer. If
// any length in the result of an animation sandwich is the result of the
// addition of lengths where one or more of those lengths is context
// dependent, then naturally the resultant length is also context
// dependent, regardless of whether its actual unit is context dependent or
// not. Unfortunately normal invalidation mechanisms won't cause us to
// recalculate the result of the sandwich if the context changes, so we
// take the (substantial) performance hit of preventing caching of the
// sandwich layer, causing the animation sandwich to be recalculated every
// single sample.
aPreventCachingOfSandwich = PR_FALSE;
for (PRUint32 i = 0; i < llai->Length(); ++i) {
PRUint8 unit = (*llai)[i].GetUnit();
if (unit == nsIDOMSVGLength::SVG_LENGTHTYPE_PERCENTAGE ||
unit == nsIDOMSVGLength::SVG_LENGTHTYPE_EMS ||
unit == nsIDOMSVGLength::SVG_LENGTHTYPE_EXS) {
aPreventCachingOfSandwich = PR_TRUE;
break;
}
}
}
return rv;
}
nsSMILValue
SVGAnimatedLengthList::SMILAnimatedLengthList::GetBaseValue() const
{
// To benefit from Return Value Optimization and avoid copy constructor calls
// due to our use of return-by-value, we must return the exact same object
// from ALL return points. This function must only return THIS variable:
nsSMILValue val;
nsSMILValue tmp(&SVGLengthListSMILType::sSingleton);
SVGLengthListAndInfo *llai = static_cast<SVGLengthListAndInfo*>(tmp.mU.mPtr);
nsresult rv = llai->CopyFrom(mVal->mBaseVal);
if (NS_SUCCEEDED(rv)) {
llai->SetInfo(mElement, mAxis, mCanZeroPadList);
val.Swap(tmp);
}
return val;
}
nsresult
SVGAnimatedLengthList::SMILAnimatedLengthList::SetAnimValue(const nsSMILValue& aValue)
{
NS_ASSERTION(aValue.mType == &SVGLengthListSMILType::sSingleton,
"Unexpected type to assign animated value");
if (aValue.mType == &SVGLengthListSMILType::sSingleton) {
mVal->SetAnimValue(*static_cast<SVGLengthListAndInfo*>(aValue.mU.mPtr),
mElement,
mAttrEnum);
}
return NS_OK;
}
void
SVGAnimatedLengthList::SMILAnimatedLengthList::ClearAnimValue()
{
if (mVal->mAnimVal) {
mVal->ClearAnimValue(mElement, mAttrEnum);
}
}
#endif // MOZ_SMIL
} // namespace mozilla

View File

@ -0,0 +1,158 @@
/* -*- 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 Mozilla SVG Project code.
*
* The Initial Developer of the Original Code is the Mozilla Foundation.
* Portions created by the Initial Developer are Copyright (C) 2010
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
*
* Alternatively, the contents of this file may be used under the terms of
* either 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 ***** */
#ifndef MOZILLA_SVGANIMATEDLENGTHLIST_H__
#define MOZILLA_SVGANIMATEDLENGTHLIST_H__
#include "SVGLengthList.h"
class nsSVGElement;
#ifdef MOZ_SMIL
#include "nsISMILAttr.h"
#endif // MOZ_SMIL
namespace mozilla {
/**
* Class SVGAnimatedLengthList
*
* This class is very different to the SVG DOM interface of the same name found
* in the SVG specification. This is a lightweight internal class - see
* DOMSVGAnimatedLengthList for the heavier DOM class that wraps instances of
* this class and implements the SVG specification's SVGAnimatedLengthList DOM
* interface.
*
* Except where noted otherwise, this class' methods take care of keeping the
* appropriate DOM wrappers in sync (see the comment in
* DOMSVGAnimatedLengthList::InternalBaseValListWillChangeTo) so that their
* consumers don't need to concern themselves with that.
*/
class SVGAnimatedLengthList
{
// friends so that they can get write access to mBaseVal
friend class DOMSVGLength;
friend class DOMSVGLengthList;
public:
SVGAnimatedLengthList() {}
/**
* Because it's so important that mBaseVal and its DOMSVGLengthList wrapper
* (if any) be kept in sync (see the comment in
* DOMSVGAnimatedLengthList::InternalBaseValListWillChangeTo), this method
* returns a const reference. Only our friend classes may get mutable
* references to mBaseVal.
*/
const SVGLengthList& GetBaseValue() const {
return mBaseVal;
}
nsresult SetBaseValueString(const nsAString& aValue);
void ClearBaseValue(PRUint32 aAttrEnum);
const SVGLengthList& GetAnimValue() const {
return mAnimVal ? *mAnimVal : mBaseVal;
}
nsresult SetAnimValue(const SVGLengthList& aValue,
nsSVGElement *aElement,
PRUint32 aAttrEnum);
void ClearAnimValue(nsSVGElement *aElement,
PRUint32 aAttrEnum);
PRBool IsAnimating() const {
return !!mAnimVal;
}
#ifdef MOZ_SMIL
/// Callers own the returned nsISMILAttr
nsISMILAttr* ToSMILAttr(nsSVGElement* aSVGElement, PRUint8 aAttrEnum,
PRUint8 aAxis, PRBool aCanZeroPadList);
#endif // MOZ_SMIL
private:
// mAnimVal is a pointer to allow us to determine if we're being animated or
// not. Making it a non-pointer member and using mAnimVal.IsEmpty() to check
// if we're animating is not an option, since that would break animation *to*
// the empty string (<set to="">).
SVGLengthList mBaseVal;
nsAutoPtr<SVGLengthList> mAnimVal;
#ifdef MOZ_SMIL
struct SMILAnimatedLengthList : public nsISMILAttr
{
public:
SMILAnimatedLengthList(SVGAnimatedLengthList* aVal,
nsSVGElement* aSVGElement,
PRUint8 aAttrEnum,
PRUint8 aAxis,
PRBool aCanZeroPadList)
: mVal(aVal)
, mElement(aSVGElement)
, mAttrEnum(aAttrEnum)
, mAxis(aAxis)
, mCanZeroPadList(aCanZeroPadList)
{}
// These will stay alive because a nsISMILAttr only lives as long
// as the Compositing step, and DOM elements don't get a chance to
// die during that.
SVGAnimatedLengthList* mVal;
nsSVGElement* mElement;
PRUint8 mAttrEnum;
PRUint8 mAxis;
PRPackedBool mCanZeroPadList; // See SVGLengthListAndInfo::CanZeroPadList
// nsISMILAttr methods
virtual nsresult ValueFromString(const nsAString& aStr,
const nsISMILAnimationElement* aSrcElement,
nsSMILValue& aValue,
PRBool& aPreventCachingOfSandwich) const;
virtual nsSMILValue GetBaseValue() const;
virtual void ClearAnimValue();
virtual nsresult SetAnimValue(const nsSMILValue& aValue);
};
#endif // MOZ_SMIL
};
} // namespace mozilla
#endif // MOZILLA_SVGANIMATEDLENGTHLIST_H__

View File

@ -0,0 +1,295 @@
/* -*- 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 Mozilla SVG Project code.
*
* The Initial Developer of the Original Code is the Mozilla Foundation.
* Portions created by the Initial Developer are Copyright (C) 2010
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
*
* Alternatively, the contents of this file may be used under the terms of
* either 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 ***** */
#include "SVGLength.h"
#include "nsSVGElement.h"
#include "nsSVGSVGElement.h"
#include "nsString.h"
#include "nsSVGUtils.h"
#include "nsContentUtils.h"
#include "nsTextFormatter.h"
#include "prdtoa.h"
#include <limits>
namespace mozilla {
// Declare some helpers defined below:
static void GetUnitString(nsAString& unit, PRUint16 unitType);
static PRUint16 GetUnitTypeForString(const char* unitStr);
void
SVGLength::GetValueAsString(nsAString &aValue) const
{
PRUnichar buf[24];
nsTextFormatter::snprintf(buf, sizeof(buf)/sizeof(PRUnichar),
NS_LITERAL_STRING("%g").get(),
(double)mValue);
aValue.Assign(buf);
nsAutoString unitString;
GetUnitString(unitString, mUnit);
aValue.Append(unitString);
}
PRBool
SVGLength::SetValueFromString(const nsAString &aValue)
{
float tmpValue;
PRUint16 tmpUnit;
NS_ConvertUTF16toUTF8 value(aValue);
const char *str = value.get();
while (*str != '\0' && IsSVGWhitespace(*str)) {
++str;
}
char *unit;
tmpValue = float(PR_strtod(str, &unit));
if (unit != str && NS_FloatIsFinite(tmpValue)) {
char *theRest = unit;
if (*unit != '\0' && !IsSVGWhitespace(*unit)) {
while (*theRest != '\0' && !IsSVGWhitespace(*theRest)) {
++theRest;
}
nsCAutoString unitStr(unit, theRest - unit);
tmpUnit = GetUnitTypeForString(unitStr.get());
} else {
tmpUnit = nsIDOMSVGLength::SVG_LENGTHTYPE_NUMBER;
}
while (*theRest && IsSVGWhitespace(*theRest)) {
++theRest;
}
if (!*theRest) {
mValue = tmpValue;
mUnit = tmpUnit;
return PR_TRUE;
}
}
return PR_FALSE;
}
inline static PRBool
IsAbsoluteUnit(PRUint8 aUnit)
{
return aUnit >= nsIDOMSVGLength::SVG_LENGTHTYPE_CM &&
aUnit <= nsIDOMSVGLength::SVG_LENGTHTYPE_PC;
}
/**
* Helper to convert between different CSS absolute units without the need for
* an element, which provides more flexibility at the DOM level (and without
* the need for an intermediary conversion to user units, which avoids
* unnecessary overhead and rounding error).
*
* Example usage: to find out how many centimeters there are per inch:
*
* GetAbsUnitsPerAbsUnit(nsIDOMSVGLength::SVG_LENGTHTYPE_CM,
* nsIDOMSVGLength::SVG_LENGTHTYPE_IN)
*/
inline static float GetAbsUnitsPerAbsUnit(PRUint8 aUnits, PRUint8 aPerUnit)
{
NS_ABORT_IF_FALSE(IsAbsoluteUnit(aUnits), "Not a CSS absolute unit");
NS_ABORT_IF_FALSE(IsAbsoluteUnit(aPerUnit), "Not a CSS absolute unit");
float CSSAbsoluteUnitConversionFactors[5][5] = { // columns: cm, mm, in, pt, pc
// cm per...:
{ 1.0, 0.1, 2.54, 0.035277777777777778, 0.42333333333333333 },
// mm per...:
{ 10.0, 1.0, 25.4, 0.35277777777777778, 4.2333333333333333 },
// in per...:
{ 0.39370078740157481, 0.039370078740157481, 1.0, 0.013888888888888889, 0.16666666666666667 },
// pt per...:
{ 28.346456692913386, 2.8346456692913386, 72.0, 1.0, 12.0 },
// pc per...:
{ 2.3622047244094489, 0.23622047244094489, 6.0, 0.083333333333333333, 1.0 }
};
// First absolute unit is SVG_LENGTHTYPE_CM = 6
return CSSAbsoluteUnitConversionFactors[aUnits - 6][aPerUnit - 6];
}
float
SVGLength::GetValueInSpecifiedUnit(PRUint8 aUnit,
const nsSVGElement *aElement,
PRUint8 aAxis) const
{
if (aUnit == mUnit) {
return mValue;
}
if ((aUnit == nsIDOMSVGLength::SVG_LENGTHTYPE_NUMBER &&
mUnit == nsIDOMSVGLength::SVG_LENGTHTYPE_PX) ||
(aUnit == nsIDOMSVGLength::SVG_LENGTHTYPE_PX &&
mUnit == nsIDOMSVGLength::SVG_LENGTHTYPE_NUMBER)) {
return mValue;
}
if (IsAbsoluteUnit(aUnit) && IsAbsoluteUnit(mUnit)) {
return mValue * GetAbsUnitsPerAbsUnit(aUnit, mUnit);
}
// Otherwise we do a two step convertion via user units. This can only
// succeed if aElement is non-null (although that's not sufficent to
// guarantee success).
float userUnitsPerCurrentUnit = GetUserUnitsPerUnit(aElement, aAxis);
float userUnitsPerNewUnit =
SVGLength(0.0f, aUnit).GetUserUnitsPerUnit(aElement, aAxis);
NS_ASSERTION(userUnitsPerCurrentUnit >= 0 ||
!NS_FloatIsFinite(userUnitsPerCurrentUnit),
"bad userUnitsPerCurrentUnit");
NS_ASSERTION(userUnitsPerNewUnit >= 0 ||
!NS_FloatIsFinite(userUnitsPerNewUnit),
"bad userUnitsPerNewUnit");
float value = mValue * userUnitsPerCurrentUnit / userUnitsPerNewUnit;
// userUnitsPerCurrentUnit could be infinity, or userUnitsPerNewUnit could
// be zero.
if (NS_FloatIsFinite(value)) {
return value;
}
return std::numeric_limits<float>::quiet_NaN();
}
#define INCHES_PER_MM_FLOAT float(0.0393700787)
#define INCHES_PER_CM_FLOAT float(0.393700787)
float
SVGLength::GetUserUnitsPerUnit(const nsSVGElement *aElement, PRUint8 aAxis) const
{
switch (mUnit) {
case nsIDOMSVGLength::SVG_LENGTHTYPE_NUMBER:
case nsIDOMSVGLength::SVG_LENGTHTYPE_PX:
return 1.0f;
case nsIDOMSVGLength::SVG_LENGTHTYPE_MM:
return INCHES_PER_MM_FLOAT * GetUserUnitsPerInch(aElement);
case nsIDOMSVGLength::SVG_LENGTHTYPE_CM:
return INCHES_PER_CM_FLOAT * GetUserUnitsPerInch(aElement);
case nsIDOMSVGLength::SVG_LENGTHTYPE_IN:
return GetUserUnitsPerInch(aElement);
case nsIDOMSVGLength::SVG_LENGTHTYPE_PT:
return (1.0f/POINTS_PER_INCH_FLOAT) * GetUserUnitsPerInch(aElement);
case nsIDOMSVGLength::SVG_LENGTHTYPE_PC:
return (12.0f/POINTS_PER_INCH_FLOAT) * GetUserUnitsPerInch(aElement);
case nsIDOMSVGLength::SVG_LENGTHTYPE_PERCENTAGE:
return GetUserUnitsPerPercent(aElement, aAxis);
case nsIDOMSVGLength::SVG_LENGTHTYPE_EMS:
return nsSVGUtils::GetFontSize(const_cast<nsSVGElement*>(aElement));
case nsIDOMSVGLength::SVG_LENGTHTYPE_EXS:
return nsSVGUtils::GetFontXHeight(const_cast<nsSVGElement*>(aElement));
default:
NS_NOTREACHED("Unknown unit type");
return std::numeric_limits<float>::quiet_NaN();
}
}
/* static */ float
SVGLength::GetUserUnitsPerInch(const nsIContent *aContent)
{
if (aContent) {
nsPresContext *context = nsContentUtils::GetContextForContent(
const_cast<nsIContent*>(aContent));
if (context) {
float uuPerInch = float(context->AppUnitsPerInch()) /
float(nsPresContext::AppUnitsPerCSSPixel());
NS_ASSERTION(uuPerInch > 0.0f, "Non-positive user units per inch");
return uuPerInch;
}
}
return std::numeric_limits<float>::quiet_NaN();
}
/* static */ float
SVGLength::GetUserUnitsPerPercent(const nsSVGElement *aElement, PRUint8 aAxis)
{
if (aElement) {
nsSVGSVGElement *viewportElement = const_cast<nsSVGElement*>(aElement)->GetCtx();
if (viewportElement) {
return NS_MAX(viewportElement->GetLength(aAxis) / 100.0f, 0.0f);
}
}
return std::numeric_limits<float>::quiet_NaN();
}
// Helpers:
// These items must be at the same index as the nsIDOMSVGLength constants!
static nsIAtom** const unitMap[] =
{
nsnull, /* SVG_LENGTHTYPE_UNKNOWN */
nsnull, /* SVG_LENGTHTYPE_NUMBER */
&nsGkAtoms::percentage,
&nsGkAtoms::em,
&nsGkAtoms::ex,
&nsGkAtoms::px,
&nsGkAtoms::cm,
&nsGkAtoms::mm,
&nsGkAtoms::in,
&nsGkAtoms::pt,
&nsGkAtoms::pc
};
static void
GetUnitString(nsAString& unit, PRUint16 unitType)
{
if (SVGLength::IsValidUnitType(unitType)) {
if (unitMap[unitType]) {
(*unitMap[unitType])->ToString(unit);
}
return;
}
NS_NOTREACHED("Unknown unit type"); // Someone's using an SVGLength with an invalid unit?
return;
}
static PRUint16
GetUnitTypeForString(const char* unitStr)
{
if (!unitStr || *unitStr == '\0')
return nsIDOMSVGLength::SVG_LENGTHTYPE_NUMBER;
nsCOMPtr<nsIAtom> unitAtom = do_GetAtom(unitStr);
for (PRUint32 i = 1 ; i < NS_ARRAY_LENGTH(unitMap) ; i++) {
if (unitMap[i] && *unitMap[i] == unitAtom) {
return i;
}
}
NS_NOTREACHED("Returning unknown unit type");
return nsIDOMSVGLength::SVG_LENGTHTYPE_UNKNOWN;
}
} // namespace mozilla

View File

@ -0,0 +1,255 @@
/* -*- 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 Mozilla SVG Project code.
*
* The Initial Developer of the Original Code is the Mozilla Foundation.
* Portions created by the Initial Developer are Copyright (C) 2010
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
*
* Alternatively, the contents of this file may be used under the terms of
* either 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 ***** */
#ifndef MOZILLA_SVGLENGTH_H__
#define MOZILLA_SVGLENGTH_H__
#include "nsIDOMSVGLength.h"
#include "nsIContent.h"
#include "nsAString.h"
#include "nsContentUtils.h"
class nsSVGElement;
namespace mozilla {
/**
* This SVGLength class is currently used for SVGLength *list* attributes only.
* The class that is currently used for <length> attributes is nsSVGLength2.
*
* The member mUnit should always be valid, but the member mValue may be
* numeric_limits<float>::quiet_NaN() under one circumstances (see the comment
* in SetValueAndUnit below). Even if mValue is valid, some methods may return
* numeric_limits<float>::quiet_NaN() if they involve a unit conversion that
* fails - see comments below.
*
* The DOM wrapper class for this class is DOMSVGLength.
*/
class SVGLength
{
public:
SVGLength()
#ifdef DEBUG
: mValue(0.0f)
, mUnit(nsIDOMSVGLength::SVG_LENGTHTYPE_UNKNOWN) // caught by IsValid()
#endif
{}
SVGLength(float aValue, PRUint8 aUnit)
: mValue(aValue)
, mUnit(aUnit)
{
NS_ASSERTION(IsValid(), "Constructed an invalid length");
}
SVGLength(const SVGLength &aOther)
: mValue(aOther.mValue)
, mUnit(aOther.mUnit)
{}
SVGLength& operator=(const SVGLength &rhs) {
mValue = rhs.mValue;
mUnit = rhs.mUnit;
return *this;
}
PRBool operator==(const SVGLength &rhs) const {
return mValue == rhs.mValue && mUnit == rhs.mUnit;
}
void GetValueAsString(nsAString& aValue) const;
/**
* This method returns PR_TRUE, unless there was a parse failure, in which
* case it returns PR_FALSE (and the length is left unchanged).
*/
PRBool SetValueFromString(const nsAString& aValue);
/**
* This will usually return a valid, finite number. There is one exception
* though - see the comment in SetValueAndUnit().
*/
float GetValueInCurrentUnits() const {
return mValue;
}
PRUint8 GetUnit() const {
return mUnit;
}
void SetValueInCurrentUnits(float aValue) {
mValue = aValue;
NS_ASSERTION(IsValid(), "Set invalid SVGLength");
}
void SetValueAndUnit(float aValue, PRUint8 aUnit) {
mValue = aValue;
mUnit = aUnit;
// IsValid() should always be true, with one exception: if
// SVGLengthListSMILType has to convert between unit types and the unit
// conversion is undefined, it will end up passing in and setting
// numeric_limits<float>::quiet_NaN(). Because of that we only check the
// unit here, and allow mValue to be invalid. The painting code has to be
// able to handle NaN anyway, since conversion to user units may fail in
// general.
NS_ASSERTION(IsValidUnitType(mUnit), "Set invalid SVGLength");
}
/**
* If it's not possible to convert this length's value to user units, then
* this method will return numeric_limits<float>::quiet_NaN().
*/
float GetValueInUserUnits(const nsSVGElement *aElement, PRUint8 aAxis) const {
return mValue * GetUserUnitsPerUnit(aElement, aAxis);
}
/**
* Sets this length's value, converting the supplied user unit value to this
* lengths *current* unit (i.e. leaving the length's unit unchanged).
*
* This method returns PR_TRUE, unless the user unit value couldn't be
* converted to this length's current unit, in which case it returns PR_FALSE
* (and the length is left unchanged).
*/
PRBool SetFromUserUnitValue(float aUserUnitValue,
nsSVGElement *aElement,
PRUint8 aAxis) {
float uuPerUnit = GetUserUnitsPerUnit(aElement, aAxis);
float value = aUserUnitValue / uuPerUnit;
if (uuPerUnit > 0 && NS_FloatIsFinite(value)) {
mValue = value;
NS_ASSERTION(IsValid(), "Set invalid SVGLength");
return PR_TRUE;
}
return PR_FALSE;
}
/**
* Get this length's value in the units specified.
*
* This method returns numeric_limits<float>::quiet_NaN() if it is not
* possible to convert the value to the specified unit.
*/
float GetValueInSpecifiedUnit(PRUint8 aUnit,
const nsSVGElement *aElement,
PRUint8 aAxis) const;
/**
* Convert this length's value to the unit specified.
*
* This method returns PR_TRUE, unless it isn't possible to convert the
* length to the specified unit. In that case the length is left unchanged
* and this method returns PR_FALSE.
*/
PRBool ConvertToUnit(PRUint32 aUnit, nsSVGElement *aElement, PRUint8 aAxis) {
float val = GetValueInSpecifiedUnit(aUnit, aElement, aAxis);
if (NS_FloatIsFinite(val)) {
mValue = val;
mUnit = aUnit;
NS_ASSERTION(IsValid(), "Set invalid SVGLength");
return PR_TRUE;
}
return PR_FALSE;
}
PRBool IsPercentage() const {
return mUnit == nsIDOMSVGLength::SVG_LENGTHTYPE_PERCENTAGE;
}
static PRBool IsValidUnitType(PRUint16 unit) {
return unit > nsIDOMSVGLength::SVG_LENGTHTYPE_UNKNOWN &&
unit <= nsIDOMSVGLength::SVG_LENGTHTYPE_PC;
}
private:
#ifdef DEBUG
PRBool IsValid() const {
return NS_FloatIsFinite(mValue) && IsValidUnitType(mUnit);
}
#endif
/**
* Returns the number of user units per current unit.
*
* This method returns numeric_limits<float>::quiet_NaN() if the conversion
* factor between the length's current unit and user units is undefined (see
* the comments for GetUserUnitsPerInch and GetUserUnitsPerPercent).
*/
float GetUserUnitsPerUnit(const nsSVGElement *aElement, PRUint8 aAxis) const;
/**
* The conversion factor between user units (CSS px) and a CSS absolute unit
* (in this case inches) is the same for all elements in a given document,
* except for elements for which the factor is undefined. The conversion
* factor is undefined for elements that are not in a document tree, and for
* elements in a document tree that don't have a pres context (elements under
* a display:none iframe, or elements belonging to a data document (an XHR
* response document or a document created via createDocument()).
*
* This helper acts as the basis for conversion between user units and all
* CSS absolute units (the conversion factors between CSS absolute units are
* fixed). Inches are chosen as the canonical unit because that's what
* pres/device contexts store, so it makes sense for this helper to also work
* in those terms to eliminate unnecessary multiplications/divisions that
* must then be reversed.
*
* This function returns a positive value if the conversion factor is
* defined, otherwise it returns numeric_limits<float>::quiet_NaN().
*/
static float GetUserUnitsPerInch(const nsIContent *aContent);
/**
* The conversion factor between user units and percentage units depends on
* aElement being non-null, and on aElement having a viewport element
* ancestor with only appropriate SVG elements between aElement and that
* ancestor. If that's not the case, then the conversion factor is undefined.
*
* This function returns a non-negative value if the conversion factor is
* defined, otherwise it returns numeric_limits<float>::quiet_NaN().
*/
static float GetUserUnitsPerPercent(const nsSVGElement *aElement, PRUint8 aAxis);
float mValue;
PRUint8 mUnit;
};
} // namespace mozilla
#endif // MOZILLA_SVGLENGTH_H__

View File

@ -0,0 +1,132 @@
/* -*- 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 Mozilla SVG Project code.
*
* The Initial Developer of the Original Code is the Mozilla Foundation.
* Portions created by the Initial Developer are Copyright (C) 2010
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
*
* Alternatively, the contents of this file may be used under the terms of
* either 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 ***** */
#include "SVGLengthList.h"
#include "SVGAnimatedLengthList.h"
#include "SVGLength.h"
#include "nsSVGElement.h"
#include "nsISVGValueUtils.h"
#include "nsDOMError.h"
#include "nsContentUtils.h"
#include "nsString.h"
#include "nsSVGUtils.h"
#include "string.h"
namespace mozilla {
nsresult
SVGLengthList::CopyFrom(const SVGLengthList& rhs)
{
if (!mLengths.SetCapacity(rhs.Length())) {
// Yes, we do want fallible alloc here
return NS_ERROR_OUT_OF_MEMORY;
}
mLengths = rhs.mLengths;
return NS_OK;
}
void
SVGLengthList::GetValueAsString(nsAString& aValue) const
{
aValue.Truncate();
PRUint32 last = mLengths.Length() - 1;
for (PRUint32 i = 0; i < mLengths.Length(); ++i) {
nsAutoString length;
mLengths[i].GetValueAsString(length);
// We ignore OOM, since it's not useful for us to return an error.
aValue.Append(length);
if (i != last) {
aValue.Append(' ');
}
}
}
static inline char* SkipWhitespace(char* str)
{
while (IsSVGWhitespace(*str))
++str;
return str;
}
nsresult
SVGLengthList::SetValueFromString(const nsAString& aValue)
{
SVGLengthList temp;
NS_ConvertUTF16toUTF8 value(aValue);
char* start = SkipWhitespace(value.BeginWriting());
// We can't use strtok with SVG_COMMA_WSP_DELIM because to correctly handle
// invalid input in the form of two commas without a value between them, we
// would need to know if strtok overwrote a comma or not.
while (*start != '\0') {
int end = strcspn(start, SVG_COMMA_WSP_DELIM);
if (end == 0) {
// found comma in an invalid location
return NS_ERROR_DOM_SYNTAX_ERR;
}
SVGLength length;
if (!length.SetValueFromString(NS_ConvertUTF8toUTF16(start, PRUint32(end)))) {
return NS_ERROR_DOM_SYNTAX_ERR;
}
if (!temp.AppendItem(length)) {
return NS_ERROR_OUT_OF_MEMORY;
}
start = SkipWhitespace(start + end);
if (*start == ',') {
start = SkipWhitespace(start + 1);
}
}
return CopyFrom(temp);
}
PRBool
SVGLengthList::operator==(const SVGLengthList& rhs) const
{
if (Length() != rhs.Length()) {
return PR_FALSE;
}
for (PRUint32 i = 0; i < Length(); ++i) {
if (!(mLengths[i] == rhs.mLengths[i])) {
return PR_FALSE;
}
}
return PR_TRUE;
}
} // namespace mozilla

View File

@ -0,0 +1,352 @@
/* -*- 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 Mozilla SVG Project code.
*
* The Initial Developer of the Original Code is the Mozilla Foundation.
* Portions created by the Initial Developer are Copyright (C) 2010
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
*
* Alternatively, the contents of this file may be used under the terms of
* either 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 ***** */
#ifndef MOZILLA_SVGLENGTHLIST_H__
#define MOZILLA_SVGLENGTHLIST_H__
#include "SVGLength.h"
#include "nsTArray.h"
#include "nsSVGElement.h"
namespace mozilla {
/**
* ATTENTION! WARNING! WATCH OUT!!
*
* Consumers that modify objects of this type absolutely MUST keep the DOM
* wrappers for those lists (if any) in sync!! That's why this class is so
* locked down.
*
* The DOM wrapper class for this class is DOMSVGLengthList.
*/
class SVGLengthList
{
friend class SVGAnimatedLengthList;
friend class DOMSVGLengthList;
friend class DOMSVGLength;
public:
SVGLengthList(){}
~SVGLengthList(){}
// Only methods that don't make/permit modification to this list are public.
// Only our friend classes can access methods that may change us.
/// This may return an incomplete string on OOM, but that's acceptable.
void GetValueAsString(nsAString& aValue) const;
PRBool IsEmpty() const {
return mLengths.IsEmpty();
}
PRUint32 Length() const {
return mLengths.Length();
}
const SVGLength& operator[](PRUint32 aIndex) const {
return mLengths[aIndex];
}
PRBool operator==(const SVGLengthList& rhs) const;
PRBool SetCapacity(PRUint32 size) {
return mLengths.SetCapacity(size);
}
void Compact() {
mLengths.Compact();
}
// Access to methods that can modify objects of this type is deliberately
// limited. This is to reduce the chances of someone modifying objects of
// this type without taking the necessary steps to keep DOM wrappers in sync.
// If you need wider access to these methods, consider adding a method to
// SVGAnimatedLengthList and having that class act as an intermediary so it
// can take care of keeping DOM wrappers in sync.
protected:
/**
* This may fail on OOM if the internal capacity needs to be increased, in
* which case the list will be left unmodified.
*/
nsresult CopyFrom(const SVGLengthList& rhs);
SVGLength& operator[](PRUint32 aIndex) {
return mLengths[aIndex];
}
/**
* This may fail (return PR_FALSE) on OOM if the internal capacity is being
* increased, in which case the list will be left unmodified.
*/
PRBool SetLength(PRUint32 aNumberOfItems) {
return mLengths.SetLength(aNumberOfItems);
}
private:
// Marking the following private only serves to show which methods are only
// used by our friend classes (as opposed to our subclasses) - it doesn't
// really provide additional safety.
nsresult SetValueFromString(const nsAString& aValue);
void Clear() {
mLengths.Clear();
}
PRBool InsertItem(PRUint32 aIndex, const SVGLength &aLength) {
if (aIndex >= mLengths.Length()) aIndex = mLengths.Length();
return !!mLengths.InsertElementAt(aIndex, aLength);
}
void ReplaceItem(PRUint32 aIndex, const SVGLength &aLength) {
NS_ASSERTION(aIndex < mLengths.Length(),
"DOM wrapper caller should have raised INDEX_SIZE_ERR");
mLengths[aIndex] = aLength;
}
void RemoveItem(PRUint32 aIndex) {
NS_ASSERTION(aIndex < mLengths.Length(),
"DOM wrapper caller should have raised INDEX_SIZE_ERR");
mLengths.RemoveElementAt(aIndex);
}
PRBool AppendItem(SVGLength aLength) {
return !!mLengths.AppendElement(aLength);
}
protected:
/* Rationale for using nsTArray<SVGLength> and not nsTArray<SVGLength, 1>:
*
* It might seem like we should use nsAutoTArray<SVGLength, 1> instead of
* nsTArray<SVGLength>. That would preallocate space for one SVGLength and
* avoid an extra memory allocation call in the common case of the 'x'
* and 'y' attributes each containing a single length (and the 'dx' and 'dy'
* attributes being empty). However, consider this:
*
* An empty nsTArray uses sizeof(Header*). An nsAutoTArray<class E,
* PRUint32 N> on the other hand uses sizeof(Header*) +
* (2 * sizeof(PRUint32)) + (N * sizeof(E)), which for one SVGLength is
* sizeof(Header*) + 16 bytes.
*
* Now consider that for text elements we have four length list attributes
* (x, y, dx, dy), each of which can have a baseVal and an animVal list. If
* we were to go the nsAutoTArray<SVGLength, 1> route for each of these, we'd
* end up using at least 160 bytes for these four attributes alone, even
* though we only need storage for two SVGLengths (16 bytes) in the common
* case!!
*
* A compromise might be to template SVGLengthList to allow
* SVGAnimatedLengthList to preallocate space for an SVGLength for the
* baseVal lists only, and that would cut the space used by the four
* attributes to 96 bytes. Taking that even further and templating
* SVGAnimatedLengthList too in order to only use nsTArray for 'dx' and 'dy'
* would reduce the storage further to 64 bytes. Having different types makes
* things more complicated for code that needs to look at the lists though.
* In fact it also makes things more complicated when it comes to storing the
* lists.
*
* It may be worth considering using nsAttrValue for length lists instead of
* storing them directly on the element.
*/
nsTArray<SVGLength> mLengths;
};
/**
* This SVGLengthList subclass is for SVGLengthListSMILType which needs to know
* which element and attribute a length list belongs to so that it can convert
* between unit types if necessary.
*/
class SVGLengthListAndInfo : public SVGLengthList
{
public:
SVGLengthListAndInfo()
: mElement(nsnull)
, mAxis(0)
, mCanZeroPadList(PR_FALSE)
{}
SVGLengthListAndInfo(nsSVGElement *aElement, PRUint8 aAxis, PRBool aCanZeroPadList)
: mElement(aElement)
, mAxis(aAxis)
, mCanZeroPadList(aCanZeroPadList)
{}
void SetInfo(nsSVGElement *aElement, PRUint8 aAxis, PRBool aCanZeroPadList) {
mElement = aElement;
mAxis = aAxis;
mCanZeroPadList = aCanZeroPadList;
}
nsSVGElement* Element() const {
return mElement; // .get();
}
PRUint8 Axis() const {
NS_ASSERTION(mElement, "Axis() isn't valid");
return mAxis;
}
/**
* The value returned by this function depends on which attribute this object
* is for. If appending a list of zeros to the attribute's list would have no
* affect on rendering (e.g. the attributes 'dx' and 'dy' on <text>), then
* this method will return PR_TRUE. If appending a list of zeros to the
* attribute's list could *change* rendering (e.g. the attributes 'x' and 'y'
* on <text>), then this method will return PR_FALSE.
*
* The reason that this method exists is because the SMIL code needs to know
* what to do when it's asked to animate between lists of different length.
* If this method returns PR_TRUE, then it can zero pad the short list before
* carrying out its operations. However, in the case of the 'x' and 'y'
* attributes on <text>, zero would mean "zero in the current coordinate
* system", whereas we would want to pad shorter lists with the coordinates
* at which glyphs would otherwise lie, which is almost certainly not zero!
* Animating from/to zeros in this case would produce terrible results.
*
* Currently SVGLengthListSMILType simply disallows (drops) animation between
* lists of different length if it can't zero pad a list. This is to avoid
* having some authors create content that depends on undesirable behaviour
* (which would make it difficult for us to fix the behavior in future). At
* some point it would be nice to implement a callback to allow this code to
* determine padding values for lists that can't be zero padded. See
* https://bugzilla.mozilla.org/show_bug.cgi?id=573431
*/
PRBool CanZeroPadList() const {
//NS_ASSERTION(mElement, "CanZeroPadList() isn't valid");
return mCanZeroPadList;
}
// For the SMIL code. See comment in SVGLengthListSMILType::Add().
void SetCanZeroPadList(PRBool aCanZeroPadList) {
mCanZeroPadList = aCanZeroPadList;
}
nsresult CopyFrom(const SVGLengthListAndInfo& rhs) {
mElement = rhs.mElement;
mAxis = rhs.mAxis;
mCanZeroPadList = rhs.mCanZeroPadList;
return SVGLengthList::CopyFrom(rhs);
}
// Instances of this special subclass do not have DOM wrappers that we need
// to worry about keeping in sync, so it's safe to expose any hidden base
// class methods required by the SMIL code, as we do below.
/**
* Exposed so that SVGLengthList baseVals can be copied to
* SVGLengthListAndInfo objects. Note that callers should also call
* SetInfo() when using this method!
*/
nsresult CopyFrom(const SVGLengthList& rhs) {
return SVGLengthList::CopyFrom(rhs);
}
const SVGLength& operator[](PRUint32 aIndex) const {
return SVGLengthList::operator[](aIndex);
}
SVGLength& operator[](PRUint32 aIndex) {
return SVGLengthList::operator[](aIndex);
}
PRBool SetLength(PRUint32 aNumberOfItems) {
return SVGLengthList::SetLength(aNumberOfItems);
}
private:
// We must keep a strong reference to our element because we may belong to a
// cached baseVal nsSMILValue. See the comments starting at:
// https://bugzilla.mozilla.org/show_bug.cgi?id=515116#c15
nsRefPtr<nsSVGElement> mElement;
PRUint8 mAxis;
PRPackedBool mCanZeroPadList;
};
/**
* This class wraps SVGLengthList objects to allow frame consumers to process
* SVGLengthList objects as if they were simply a list of float values in user
* units. When consumers request the value at a given index, this class
* dynamically converts the corresponding SVGLength from its actual unit and
* returns its value in user units.
*
* Consumers should check that the user unit values returned are finite. Even
* if the consumer can guarantee the list's element has a valid viewport
* ancestor to resolve percentage units against, and a valid presContext and
* styleContext to resolve absolute and em/ex units against, unit conversions
* could still overflow. In that case the value returned will be
* numeric_limits<float>::quiet_NaN().
*/
class NS_STACK_CLASS SVGUserUnitList
{
public:
SVGUserUnitList()
: mList(nsnull)
{}
void Init(const SVGLengthList *aList, nsSVGElement *aElement, PRUint8 aAxis) {
mList = aList;
mElement = aElement;
mAxis = aAxis;
}
void Clear() {
mList = nsnull;
}
PRUint32 Length() {
return mList ? mList->Length() : 0;
}
/// This may return a non-finite value
float operator[](PRUint32 aIndex) {
return (*mList)[aIndex].GetValueInUserUnits(mElement, mAxis);
}
private:
const SVGLengthList *mList;
nsSVGElement *mElement;
PRUint8 mAxis;
};
} // namespace mozilla
#endif // MOZILLA_SVGLENGTHLIST_H__

View File

@ -0,0 +1,309 @@
/* -*- 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 SVG project.
*
* The Initial Developer of the Original Code is the Mozilla Foundation.
* Portions created by the Initial Developer are Copyright (C) 2010
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
*
* 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 ***** */
#include "SVGLengthListSMILType.h"
#include "nsSMILValue.h"
#include "SVGLengthList.h"
#include <math.h>
namespace mozilla {
/*static*/ SVGLengthListSMILType SVGLengthListSMILType::sSingleton;
//----------------------------------------------------------------------
// nsISMILType implementation
void
SVGLengthListSMILType::Init(nsSMILValue &aValue) const
{
NS_ABORT_IF_FALSE(aValue.IsNull(), "Unexpected value type");
SVGLengthListAndInfo* lengthList = new SVGLengthListAndInfo();
// See the comment documenting Init() in our header file:
lengthList->SetCanZeroPadList(PR_TRUE);
aValue.mU.mPtr = lengthList;
aValue.mType = this;
}
void
SVGLengthListSMILType::Destroy(nsSMILValue& aValue) const
{
NS_PRECONDITION(aValue.mType == this, "Unexpected SMIL value type");
delete static_cast<SVGLengthListAndInfo*>(aValue.mU.mPtr);
aValue.mU.mPtr = nsnull;
aValue.mType = &nsSMILNullType::sSingleton;
}
nsresult
SVGLengthListSMILType::Assign(nsSMILValue& aDest,
const nsSMILValue& aSrc) const
{
NS_PRECONDITION(aDest.mType == aSrc.mType, "Incompatible SMIL types");
NS_PRECONDITION(aDest.mType == this, "Unexpected SMIL value");
const SVGLengthListAndInfo* src =
static_cast<const SVGLengthListAndInfo*>(aSrc.mU.mPtr);
SVGLengthListAndInfo* dest =
static_cast<SVGLengthListAndInfo*>(aDest.mU.mPtr);
return dest->CopyFrom(*src);
}
PRBool
SVGLengthListSMILType::IsEqual(const nsSMILValue& aLeft,
const nsSMILValue& aRight) const
{
NS_PRECONDITION(aLeft.mType == aRight.mType, "Incompatible SMIL types");
NS_PRECONDITION(aLeft.mType == this, "Unexpected type for SMIL value");
return *static_cast<const SVGLengthListAndInfo*>(aLeft.mU.mPtr) ==
*static_cast<const SVGLengthListAndInfo*>(aRight.mU.mPtr);
}
nsresult
SVGLengthListSMILType::Add(nsSMILValue& aDest,
const nsSMILValue& aValueToAdd,
PRUint32 aCount) const
{
NS_PRECONDITION(aDest.mType == this, "Unexpected SMIL type");
NS_PRECONDITION(aValueToAdd.mType == this, "Incompatible SMIL type");
SVGLengthListAndInfo& dest =
*static_cast<SVGLengthListAndInfo*>(aDest.mU.mPtr);
const SVGLengthListAndInfo& valueToAdd =
*static_cast<const SVGLengthListAndInfo*>(aValueToAdd.mU.mPtr);
// To understand this code, see the comments documenting our Init() method,
// and documenting SVGLengthListAndInfo::CanZeroPadList().
// Note that *this* method actually may safely zero pad a shorter list
// regardless of the value returned by CanZeroPadList() for that list,
// just so long as the shorter list is being added *to* the longer list
// and *not* vice versa! It's okay in the case of adding a shorter list to a
// longer list because during the add operation we'll end up adding the
// zeros to actual specified values. It's *not* okay in the case of adding a
// longer list to a shorter list because then we end up adding to implicit
// zeros when we'd actually need to add to whatever the underlying values
// should be, not zeros, and those values are not explicit or otherwise
// available.
if (dest.Length() < valueToAdd.Length()) {
if (!dest.CanZeroPadList()) {
// nsSVGUtils::ReportToConsole
return NS_ERROR_FAILURE;
}
NS_ASSERTION(valueToAdd.CanZeroPadList() || dest.Length() == 0,
"Only \"zero\" nsSMILValues from the SMIL engine should "
"return PR_TRUE for CanZeroPadList() when the attribute "
"being animated can't be zero padded");
PRUint32 i = dest.Length();
if (!dest.SetLength(valueToAdd.Length())) {
return NS_ERROR_OUT_OF_MEMORY;
}
for (; i < valueToAdd.Length(); ++i) {
dest[i].SetValueAndUnit(0.0f, valueToAdd[i].GetUnit());
}
}
for (PRUint32 i = 0; i < valueToAdd.Length(); ++i) {
float valToAdd;
if (dest[i].GetUnit() == valueToAdd[i].GetUnit()) {
valToAdd = valueToAdd[i].GetValueInCurrentUnits();
} else {
// If units differ, we use the unit of the item in 'dest'.
// We leave it to the frame code to check that values are finite.
valToAdd = valueToAdd[i].GetValueInSpecifiedUnit(dest[i].GetUnit(),
dest.Element(),
dest.Axis());
}
dest[i].SetValueAndUnit(dest[i].GetValueInCurrentUnits() + valToAdd,
dest[i].GetUnit());
}
// propagate flag:
dest.SetCanZeroPadList(dest.CanZeroPadList() &&
valueToAdd.CanZeroPadList());
return NS_OK;
}
nsresult
SVGLengthListSMILType::ComputeDistance(const nsSMILValue& aFrom,
const nsSMILValue& aTo,
double& aDistance) const
{
NS_PRECONDITION(aFrom.mType == this, "Unexpected SMIL type");
NS_PRECONDITION(aTo.mType == this, "Incompatible SMIL type");
const SVGLengthListAndInfo& from =
*static_cast<const SVGLengthListAndInfo*>(aFrom.mU.mPtr);
const SVGLengthListAndInfo& to =
*static_cast<const SVGLengthListAndInfo*>(aTo.mU.mPtr);
// To understand this code, see the comments documenting our Init() method,
// and documenting SVGLengthListAndInfo::CanZeroPadList().
NS_ASSERTION(from.CanZeroPadList() == to.CanZeroPadList() ||
from.CanZeroPadList() && from.Length() == 0 ||
to.CanZeroPadList() && to.Length() == 0,
"Only \"zero\" nsSMILValues from the SMIL engine should "
"return PR_TRUE for CanZeroPadList() when the attribute "
"being animated can't be zero padded");
if ((from.Length() < to.Length() && !from.CanZeroPadList()) ||
(to.Length() < from.Length() && !to.CanZeroPadList())) {
// nsSVGUtils::ReportToConsole
return NS_ERROR_FAILURE;
}
// We return the root of the sum of the squares of the deltas between the
// user unit values of the lengths at each correspanding index. In the
// general case, paced animation is probably not useful, but this strategy at
// least does the right thing for paced animation in the face of simple
// 'values' lists such as:
//
// values="100 200 300; 101 201 301; 110 210 310"
//
// I.e. half way through the simple duration we'll get "105 205 305".
double total = 0.0;
PRUint32 i = 0;
for (; i < from.Length() && i < to.Length(); ++i) {
double f = from[i].GetValueInUserUnits(from.Element(), from.Axis());
double t = to[i].GetValueInUserUnits(to.Element(), to.Axis());
double delta = t - f;
total += delta * delta;
}
// In the case that from.Length() != to.Length(), one of the following loops
// will run. (OK since CanZeroPadList()==true for the other list.)
for (; i < from.Length(); ++i) {
double f = from[i].GetValueInUserUnits(from.Element(), from.Axis());
total += f * f;
}
for (; i < to.Length(); ++i) {
double t = to[i].GetValueInUserUnits(to.Element(), to.Axis());
total += t * t;
}
float distance = sqrt(total);
if (!NS_FloatIsFinite(distance)) {
return NS_ERROR_FAILURE;
}
aDistance = distance;
return NS_OK;
}
nsresult
SVGLengthListSMILType::Interpolate(const nsSMILValue& aStartVal,
const nsSMILValue& aEndVal,
double aUnitDistance,
nsSMILValue& aResult) const
{
NS_PRECONDITION(aStartVal.mType == aEndVal.mType,
"Trying to interpolate different types");
NS_PRECONDITION(aStartVal.mType == this,
"Unexpected types for interpolation");
NS_PRECONDITION(aResult.mType == this, "Unexpected result type");
const SVGLengthListAndInfo& start =
*static_cast<const SVGLengthListAndInfo*>(aStartVal.mU.mPtr);
const SVGLengthListAndInfo& end =
*static_cast<const SVGLengthListAndInfo*>(aEndVal.mU.mPtr);
SVGLengthListAndInfo& result =
*static_cast<SVGLengthListAndInfo*>(aResult.mU.mPtr);
// To understand this code, see the comments documenting our Init() method,
// and documenting SVGLengthListAndInfo::CanZeroPadList().
NS_ASSERTION(start.CanZeroPadList() == end.CanZeroPadList() ||
start.CanZeroPadList() && start.Length() == 0 ||
end.CanZeroPadList() && end.Length() == 0,
"Only \"zero\" nsSMILValues from the SMIL engine should "
"return PR_TRUE for CanZeroPadList() when the attribute "
"being animated can't be zero padded");
if ((start.Length() < end.Length() && !start.CanZeroPadList()) ||
(end.Length() < start.Length() && !end.CanZeroPadList())) {
// nsSVGUtils::ReportToConsole
return NS_ERROR_FAILURE;
}
if (!result.SetLength(NS_MAX(start.Length(), end.Length()))) {
return NS_ERROR_OUT_OF_MEMORY;
}
PRUint32 i = 0;
for (; i < start.Length() && i < end.Length(); ++i) {
float s;
if (start[i].GetUnit() == end[i].GetUnit()) {
s = start[i].GetValueInCurrentUnits();
} else {
// If units differ, we use the unit of the item in 'end'.
// We leave it to the frame code to check that values are finite.
s = start[i].GetValueInSpecifiedUnit(end[i].GetUnit(), end.Element(), end.Axis());
}
float e = end[i].GetValueInCurrentUnits();
result[i].SetValueAndUnit(s + (e - s) * aUnitDistance, end[i].GetUnit());
}
// In the case that start.Length() != end.Length(), one of the following
// loops will run. (Okay, since CanZeroPadList()==true for the other list.)
for (; i < start.Length(); ++i) {
result[i].SetValueAndUnit(start[i].GetValueInCurrentUnits() -
start[i].GetValueInCurrentUnits() * aUnitDistance,
start[i].GetUnit());
}
for (; i < end.Length(); ++i) {
result[i].SetValueAndUnit(end[i].GetValueInCurrentUnits() * aUnitDistance,
end[i].GetUnit());
}
// propagate flag:
result.SetCanZeroPadList(start.CanZeroPadList() &&
end.CanZeroPadList());
return NS_OK;
}
} // namespace mozilla

View File

@ -0,0 +1,132 @@
/* -*- 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 SVG project.
*
* The Initial Developer of the Original Code is the Mozilla Foundation.
* Portions created by the Initial Developer are Copyright (C) 2009
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
*
* 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 ***** */
#ifndef MOZILLA_SVGLENGTHLISTSMILTYPE_H_
#define MOZILLA_SVGLENGTHLISTSMILTYPE_H_
#include "nsISMILType.h"
class nsSMILValue;
namespace mozilla {
////////////////////////////////////////////////////////////////////////
// SVGLengthListSMILType
//
// Operations for animating an SVGLengthList.
//
class SVGLengthListSMILType : public nsISMILType
{
public:
// Singleton for nsSMILValue objects to hold onto.
static SVGLengthListSMILType sSingleton;
protected:
// nsISMILType Methods
// -------------------
/**
* When this method initializes the SVGLengthListAndInfo for its nsSMILValue
* argument, it has to blindly set its mCanZeroPadList to PR_TRUE despite
* the fact that some attributes can't be zero-padded. (See the explaination
* that follows.) SVGAnimatedLengthList::SMILAnimatedLengthList's
* GetBaseValue() and ValueFromString() methods then override this for the
* nsSMILValue objects that they create to set this flag to the appropriate
* value for the attribute in question.
*
* The reason that we default to setting the mCanZeroPadList to true is
* because the SMIL engine creates "zero" valued nsSMILValue objects for
* intermediary calculations, and may pass such an nsSMILValue (along with an
* nsSMILValue from an animation element - that is an nsSMILValue created by
* SVGAnimatedLengthList::SMILAnimatedLengthList's GetBaseValue() or
* ValueFromString() methods) into the Add(), ComputeDistance() or
* Interpolate() methods below. Even in the case of animation of list
* attributes that may *not* be padded with zeros (such as 'x' and 'y' on the
* <text> element), we need to allow zero-padding of these "zero" valued
* nsSMILValue's lists. One reason for this is illustrated by the following
* example:
*
* <text x="2 4">foo
* <animate by="2 2" .../>
* </text>
*
* In this example there are two SMIL animation layers to be sandwiched: the
* base layer, and the layer created for the <animate> element. The SMIL
* engine calculates the result of each layer *independently*, before
* compositing the results together. Thus for the <animate> sandwich layer
* the SMIL engine interpolates between a "zero" nsSMILValue that it creates
* (since there is no explicit "from") and the "2 2", before the result of
* that interpolation is added to the "2 4" from the base layer. Clearly for
* the interpolation between the "zero" nsSMILValue and "2 2" to work, the
* "zero" nsSMILValue's SVGLengthListAndInfo must be zero paddable - hence
* why this method always sets mCanZeroPadList to PR_TRUE.
*
* (Since the Add(), ComputeDistance() and Interpolate() methods may be
* passed two input nsSMILValue objects for which CanZeroPadList() returns
* opposite values, these methods must be careful what they set the flag to
* on the nsSMILValue that they output. If *either* of the input nsSMILValues
* has an SVGLengthListAndInfo for which CanZeroPadList() returns PR_FALSE,
* then they must set the flag to PR_FALSE on the output nsSMILValue too. If
* the methods failed to do that, then when the result nsSMILValue objects
* from each sandwich layer are composited together, we could end up allowing
* animation between lists of different length when we should not!)
*/
virtual void Init(nsSMILValue& aValue) const;
virtual void Destroy(nsSMILValue& aValue) const;
virtual nsresult Assign(nsSMILValue& aDest, const nsSMILValue& aSrc) const;
virtual PRBool IsEqual(const nsSMILValue& aLeft,
const nsSMILValue& aRight) const;
virtual nsresult Add(nsSMILValue& aDest, const nsSMILValue& aValueToAdd,
PRUint32 aCount) const;
virtual nsresult ComputeDistance(const nsSMILValue& aFrom,
const nsSMILValue& aTo,
double& aDistance) const;
virtual nsresult Interpolate(const nsSMILValue& aStartVal,
const nsSMILValue& aEndVal,
double aUnitDistance,
nsSMILValue& aResult) const;
private:
// Private constructor & destructor: prevent instances beyond my singleton,
// and prevent others from deleting my singleton.
SVGLengthListSMILType() {}
~SVGLengthListSMILType() {}
};
} // namespace mozilla
#endif // MOZILLA_SVGLENGTHLISTSMILTYPE_H_

View File

@ -1,206 +0,0 @@
/* -*- 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 SVG project.
*
* The Initial Developer of the Original Code is
* Crocodile Clips Ltd..
* Portions created by the Initial Developer are Copyright (C) 2002
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Alex Fritze <alex.fritze@crocodile-clips.com> (original author)
*
* 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 ***** */
#include "nsSVGAnimatedLengthList.h"
#include "nsSVGLengthList.h"
#include "nsSVGValue.h"
#include "nsWeakReference.h"
#include "nsContentUtils.h"
////////////////////////////////////////////////////////////////////////
// nsSVGAnimatedTransformList
class nsSVGAnimatedLengthList : public nsIDOMSVGAnimatedLengthList,
public nsSVGValue,
public nsISVGValueObserver
{
protected:
friend nsresult
NS_NewSVGAnimatedLengthList(nsIDOMSVGAnimatedLengthList** result,
nsIDOMSVGLengthList* baseVal);
nsSVGAnimatedLengthList();
~nsSVGAnimatedLengthList();
void Init(nsIDOMSVGLengthList* baseVal);
public:
// nsISupports interface:
NS_DECL_ISUPPORTS
// nsIDOMSVGAnimatedLengthList interface:
NS_DECL_NSIDOMSVGANIMATEDLENGTHLIST
// remainder of nsISVGValue interface:
NS_IMETHOD SetValueString(const nsAString& aValue);
NS_IMETHOD GetValueString(nsAString& aValue);
// nsISVGValueObserver
NS_IMETHOD WillModifySVGObservable(nsISVGValue* observable,
modificationType aModType);
NS_IMETHOD DidModifySVGObservable (nsISVGValue* observable,
modificationType aModType);
// nsISupportsWeakReference
// implementation inherited from nsSupportsWeakReference
protected:
nsCOMPtr<nsIDOMSVGLengthList> mBaseVal;
};
//----------------------------------------------------------------------
// Implementation
nsSVGAnimatedLengthList::nsSVGAnimatedLengthList()
{
}
nsSVGAnimatedLengthList::~nsSVGAnimatedLengthList()
{
if (!mBaseVal) return;
nsCOMPtr<nsISVGValue> val = do_QueryInterface(mBaseVal);
if (!val) return;
val->RemoveObserver(this);
}
void
nsSVGAnimatedLengthList::Init(nsIDOMSVGLengthList* baseVal)
{
mBaseVal = baseVal;
if (!mBaseVal) return;
nsCOMPtr<nsISVGValue> val = do_QueryInterface(mBaseVal);
if (!val) return;
val->AddObserver(this);
}
//----------------------------------------------------------------------
// nsISupports methods:
NS_IMPL_ADDREF(nsSVGAnimatedLengthList)
NS_IMPL_RELEASE(nsSVGAnimatedLengthList)
DOMCI_DATA(SVGAnimatedLengthList, nsSVGAnimatedLengthList)
NS_INTERFACE_MAP_BEGIN(nsSVGAnimatedLengthList)
NS_INTERFACE_MAP_ENTRY(nsISVGValue)
NS_INTERFACE_MAP_ENTRY(nsIDOMSVGAnimatedLengthList)
NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)
NS_INTERFACE_MAP_ENTRY(nsISVGValueObserver)
NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(SVGAnimatedLengthList)
NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsISVGValue)
NS_INTERFACE_MAP_END
//----------------------------------------------------------------------
// nsISVGValue methods:
NS_IMETHODIMP
nsSVGAnimatedLengthList::SetValueString(const nsAString& aValue)
{
nsCOMPtr<nsISVGValue> value = do_QueryInterface(mBaseVal);
return value->SetValueString(aValue);
}
NS_IMETHODIMP
nsSVGAnimatedLengthList::GetValueString(nsAString& aValue)
{
nsCOMPtr<nsISVGValue> value = do_QueryInterface(mBaseVal);
return value->GetValueString(aValue);
}
//----------------------------------------------------------------------
// nsIDOMSVGAnimatedLengthList methods:
/* readonly attribute nsIDOMSVGLengthList baseVal; */
NS_IMETHODIMP
nsSVGAnimatedLengthList::GetBaseVal(nsIDOMSVGLengthList * *aBaseVal)
{
*aBaseVal = mBaseVal;
NS_ADDREF(*aBaseVal);
return NS_OK;
}
/* readonly attribute nsIDOMSVGLengthList animVal; */
NS_IMETHODIMP
nsSVGAnimatedLengthList::GetAnimVal(nsIDOMSVGLengthList * *aAnimVal)
{
*aAnimVal = mBaseVal;
NS_ADDREF(*aAnimVal);
return NS_OK;
}
//----------------------------------------------------------------------
// nsISVGValueObserver methods
NS_IMETHODIMP
nsSVGAnimatedLengthList::WillModifySVGObservable(nsISVGValue* observable,
modificationType aModType)
{
WillModify(aModType);
return NS_OK;
}
NS_IMETHODIMP
nsSVGAnimatedLengthList::DidModifySVGObservable (nsISVGValue* observable,
modificationType aModType)
{
DidModify(aModType);
return NS_OK;
}
////////////////////////////////////////////////////////////////////////
// Exported creation functions:
nsresult
NS_NewSVGAnimatedLengthList(nsIDOMSVGAnimatedLengthList** result,
nsIDOMSVGLengthList* baseVal)
{
*result = nsnull;
nsSVGAnimatedLengthList* animatedLengthList = new nsSVGAnimatedLengthList();
if(!animatedLengthList) return NS_ERROR_OUT_OF_MEMORY;
NS_ADDREF(animatedLengthList);
animatedLengthList->Init(baseVal);
*result = (nsIDOMSVGAnimatedLengthList*) animatedLengthList;
return NS_OK;
}

View File

@ -1,49 +0,0 @@
/* -*- 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 SVG project.
*
* The Initial Developer of the Original Code is
* Crocodile Clips Ltd..
* Portions created by the Initial Developer are Copyright (C) 2001
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Alex Fritze <alex.fritze@crocodile-clips.com> (original author)
*
* 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 ***** */
#ifndef __NS_SVGANIMATEDLENGTHLIST_H__
#define __NS_SVGANIMATEDLENGTHLIST_H__
#include "nsIDOMSVGAnimatedLengthList.h"
#include "nsIDOMSVGLengthList.h"
nsresult
NS_NewSVGAnimatedLengthList(nsIDOMSVGAnimatedLengthList** result,
nsIDOMSVGLengthList* baseVal);
#endif //__NS_SVGANIMATEDLENGTHLIST_H__

View File

@ -74,15 +74,12 @@
#include "nsSVGEnum.h"
#include "nsSVGViewBox.h"
#include "nsSVGString.h"
#include "SVGAnimatedLengthList.h"
#include "nsIDOMSVGUnitTypes.h"
#include "nsIDOMSVGLengthList.h"
#include "nsIDOMSVGAnimatedLengthList.h"
#include "nsIDOMSVGNumberList.h"
#include "nsIDOMSVGAnimatedNumberList.h"
#include "nsIDOMSVGPointList.h"
#include "nsIDOMSVGAnimatedPoints.h"
#include "nsIDOMSVGPresAspectRatio.h"
#include "nsIDOMSVGAnimPresAspRatio.h"
#include "nsIDOMSVGTransformList.h"
#include "nsIDOMSVGAnimTransformList.h"
#include "nsIDOMSVGAnimatedRect.h"
@ -174,6 +171,12 @@ nsSVGElement::Init()
preserveAspectRatio->Init();
}
LengthListAttributesInfo lengthListInfo = GetLengthListInfo();
for (i = 0; i < lengthListInfo.mLengthListCount; i++) {
lengthListInfo.Reset(i);
}
StringAttributesInfo stringInfo = GetStringInfo();
for (i = 0; i < stringInfo.mStringCount; i++) {
@ -352,6 +355,22 @@ nsSVGElement::ParseAttribute(PRInt32 aNamespaceID,
}
}
if (!foundMatch) {
// Check for SVGAnimatedLengthList attribute
LengthListAttributesInfo lengthListInfo = GetLengthListInfo();
for (i = 0; i < lengthListInfo.mLengthListCount; i++) {
if (aAttribute == *lengthListInfo.mLengthListInfo[i].mName) {
rv = lengthListInfo.mLengthLists[i].SetBaseValueString(aValue);
if (NS_FAILED(rv)) {
// ReportToConsole
lengthListInfo.Reset(i);
}
foundMatch = PR_TRUE;
break;
}
}
}
if (!foundMatch) {
// Check for nsSVGNumber2 attribute
NumberAttributesInfo numberInfo = GetNumberInfo();
@ -533,6 +552,20 @@ nsSVGElement::UnsetAttr(PRInt32 aNamespaceID, nsIAtom* aName,
}
}
if (!foundMatch) {
// Check if this is a length list attribute going away
LengthListAttributesInfo lengthListInfo = GetLengthListInfo();
for (PRUint32 i = 0; i < lengthListInfo.mLengthListCount; i++) {
if (aName == *lengthListInfo.mLengthListInfo[i].mName) {
lengthListInfo.Reset(i);
DidChangeLengthList(i, PR_FALSE);
foundMatch = PR_TRUE;
break;
}
}
}
if (!foundMatch) {
// Check if this is a number attribute going away
NumberAttributesInfo numInfo = GetNumberInfo();
@ -669,12 +702,6 @@ nsSVGElement::UnsetAttr(PRInt32 aNamespaceID, nsIAtom* aName,
void
nsSVGElement::ResetOldStyleBaseType(nsISVGValue *svg_value)
{
nsCOMPtr<nsIDOMSVGAnimatedLengthList> ll = do_QueryInterface(svg_value);
if (ll) {
nsCOMPtr<nsIDOMSVGLengthList> lengthlist;
ll->GetBaseVal(getter_AddRefs(lengthlist));
lengthlist->Clear();
}
nsCOMPtr<nsIDOMSVGAnimatedNumberList> nl = do_QueryInterface(svg_value);
if (nl) {
nsCOMPtr<nsIDOMSVGNumberList> numberlist;
@ -1430,6 +1457,85 @@ nsSVGElement::GetAnimatedLengthValues(float *aFirst, ...)
va_end(args);
}
nsSVGElement::LengthListAttributesInfo
nsSVGElement::GetLengthListInfo()
{
return LengthListAttributesInfo(nsnull, nsnull, 0);
}
void
nsSVGElement::LengthListAttributesInfo::Reset(PRUint8 aAttrEnum)
{
mLengthLists[aAttrEnum].ClearBaseValue(aAttrEnum);
// caller notifies
}
void
nsSVGElement::DidChangeLengthList(PRUint8 aAttrEnum, PRBool aDoSetAttr)
{
if (!aDoSetAttr)
return;
LengthListAttributesInfo info = GetLengthListInfo();
NS_ASSERTION(info.mLengthListCount > 0,
"DidChangeLengthList on element with no length list attribs");
NS_ASSERTION(aAttrEnum < info.mLengthListCount, "aAttrEnum out of range");
nsAutoString newStr;
info.mLengthLists[aAttrEnum].GetBaseValue().GetValueAsString(newStr);
SetAttr(kNameSpaceID_None, *info.mLengthListInfo[aAttrEnum].mName,
newStr, PR_TRUE);
}
void
nsSVGElement::DidAnimateLengthList(PRUint8 aAttrEnum)
{
nsIFrame* frame = GetPrimaryFrame();
if (frame) {
LengthListAttributesInfo info = GetLengthListInfo();
frame->AttributeChanged(kNameSpaceID_None,
*info.mLengthListInfo[aAttrEnum].mName,
nsIDOMMutationEvent::MODIFICATION);
}
}
void
nsSVGElement::GetAnimatedLengthListValues(SVGUserUnitList *aFirst, ...)
{
LengthListAttributesInfo info = GetLengthListInfo();
NS_ASSERTION(info.mLengthListCount > 0,
"GetAnimatedLengthListValues on element with no length list attribs");
SVGUserUnitList *list = aFirst;
PRUint32 i = 0;
va_list args;
va_start(args, aFirst);
while (list && i < info.mLengthListCount) {
list->Init(&(info.mLengthLists[i].GetAnimValue()), this, info.mLengthListInfo[i].mAxis);
++i;
list = va_arg(args, SVGUserUnitList*);
}
va_end(args);
}
SVGAnimatedLengthList*
nsSVGElement::GetAnimatedLengthList(PRUint8 aAttrEnum)
{
LengthListAttributesInfo info = GetLengthListInfo();
if (aAttrEnum < info.mLengthListCount) {
return &(info.mLengthLists[aAttrEnum]);
}
NS_NOTREACHED("Bad attrEnum");
return nsnull;
}
nsSVGElement::NumberAttributesInfo
nsSVGElement::GetNumberInfo()
{
@ -2059,6 +2165,20 @@ nsSVGElement::GetAnimatedAttr(nsIAtom* aName)
return preserveAspectRatio ? preserveAspectRatio->ToSMILAttr(this) : nsnull;
}
// LengthLists:
{
LengthListAttributesInfo info = GetLengthListInfo();
for (PRUint32 i = 0; i < info.mLengthListCount; i++) {
if (aName == *info.mLengthListInfo[i].mName) {
NS_ABORT_IF_FALSE(i <= UCHAR_MAX, "Too many attributes");
return info.mLengthLists[i].ToSMILAttr(this,
PRUint8(i),
info.mLengthListInfo[i].mAxis,
info.mLengthListInfo[i].mCouldZeroPadList);
}
}
}
// Mapped attributes:
if (IsAttributeMapped(aName)) {
nsCSSProperty prop = nsCSSProps::LookupProperty(nsAtomString(aName));

View File

@ -71,6 +71,12 @@ class nsSVGViewBox;
class nsSVGPreserveAspectRatio;
class nsSVGString;
struct gfxMatrix;
namespace mozilla {
class SVGAnimatedLengthList;
class SVGUserUnitList;
}
using namespace mozilla;
typedef nsStyledElement nsSVGElementBase;
@ -159,6 +165,7 @@ public:
virtual void DidChangeEnum(PRUint8 aAttrEnum, PRBool aDoSetAttr);
virtual void DidChangeViewBox(PRBool aDoSetAttr);
virtual void DidChangePreserveAspectRatio(PRBool aDoSetAttr);
virtual void DidChangeLengthList(PRUint8 aAttrEnum, PRBool aDoSetAttr);
virtual void DidChangeString(PRUint8 aAttrEnum) {}
virtual void DidAnimateLength(PRUint8 aAttrEnum);
@ -169,11 +176,14 @@ public:
virtual void DidAnimateEnum(PRUint8 aAttrEnum);
virtual void DidAnimateViewBox();
virtual void DidAnimatePreserveAspectRatio();
virtual void DidAnimateLengthList(PRUint8 aAttrEnum);
virtual void DidAnimateTransform();
void GetAnimatedLengthValues(float *aFirst, ...);
void GetAnimatedNumberValues(float *aFirst, ...);
void GetAnimatedIntegerValues(PRInt32 *aFirst, ...);
void GetAnimatedLengthListValues(SVGUserUnitList *aFirst, ...);
SVGAnimatedLengthList* GetAnimatedLengthList(PRUint8 aAttrEnum);
#ifdef MOZ_SMIL
virtual nsISMILAttr* GetAnimatedAttr(nsIAtom* aName);
@ -330,6 +340,36 @@ protected:
void Reset(PRUint8 aAttrEnum);
};
struct LengthListInfo {
nsIAtom** mName;
PRUint8 mAxis;
/**
* Flag to indicate whether appending zeros to the end of the list would
* change the rendering of the SVG for the attribute in question. For x and
* y on the <text> element this is true, but for dx and dy on <text> this
* is false. This flag is fed down to SVGLengthListSMILType so it can
* determine if it can sensibly animate from-to lists of different lengths,
* which is desirable in the case of dx and dy.
*/
PRPackedBool mCouldZeroPadList;
};
struct LengthListAttributesInfo {
SVGAnimatedLengthList* mLengthLists;
LengthListInfo* mLengthListInfo;
PRUint32 mLengthListCount;
LengthListAttributesInfo(SVGAnimatedLengthList *aLengthLists,
LengthListInfo *aLengthListInfo,
PRUint32 aLengthListCount)
: mLengthLists(aLengthLists)
, mLengthListInfo(aLengthListInfo)
, mLengthListCount(aLengthListCount)
{}
void Reset(PRUint8 aAttrEnum);
};
struct StringInfo {
nsIAtom** mName;
PRInt32 mNamespaceID;
@ -359,6 +399,7 @@ protected:
// so we don't need to wrap the class
virtual nsSVGViewBox *GetViewBox();
virtual nsSVGPreserveAspectRatio *GetPreserveAspectRatio();
virtual LengthListAttributesInfo GetLengthListInfo();
virtual StringAttributesInfo GetStringInfo();
static nsSVGEnumMapping sSVGUnitTypesMap[];

View File

@ -58,7 +58,6 @@
#include "nsIFrame.h"
#include "gfxContext.h"
#include "gfxMatrix.h"
#include "nsSVGLengthList.h"
#include "nsIDOMSVGURIReference.h"
#include "nsImageLoadingContent.h"
#include "imgIContainer.h"

View File

@ -1,522 +0,0 @@
/* -*- 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 SVG project.
*
* The Initial Developer of the Original Code is
* Crocodile Clips Ltd..
* Portions created by the Initial Developer are Copyright (C) 2001
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Alex Fritze <alex.fritze@crocodile-clips.com> (original author)
* Jonathan Watt <jonathan.watt@strath.ac.uk>
*
* 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 ***** */
#include "nsSVGLength.h"
#include "nsSVGUtils.h"
#include "nsGkAtoms.h"
#include "nsSVGValue.h"
#include "nsTextFormatter.h"
#include "prdtoa.h"
#include "nsCRT.h"
#include "nsSVGSVGElement.h"
#include "nsIDOMSVGNumber.h"
#include "nsISVGValueUtils.h"
#include "nsWeakReference.h"
#include "nsContentUtils.h"
////////////////////////////////////////////////////////////////////////
// nsSVGLength class
class nsSVGLength : public nsISVGLength,
public nsSVGValue
{
protected:
friend nsresult NS_NewSVGLength(nsISVGLength** result,
float value,
PRUint16 unit);
friend nsresult NS_NewSVGLength(nsISVGLength** result,
const nsAString &value);
nsSVGLength(float value, PRUint16 unit);
nsSVGLength();
public:
// nsISupports interface:
NS_DECL_ISUPPORTS
// nsIDOMSVGLength interface:
NS_DECL_NSIDOMSVGLENGTH
// nsISVGLength interface:
NS_IMETHOD SetContext(nsIWeakReference *aContext, PRUint8 aCtxType);
// nsISVGValue interface:
NS_IMETHOD SetValueString(const nsAString& aValue);
NS_IMETHOD GetValueString(nsAString& aValue);
// nsISupportsWeakReference
// implementation inherited from nsSupportsWeakReference
protected:
// implementation helpers:
float mmPerPixel();
float AxisLength();
float EmLength();
float ExLength();
PRBool IsValidUnitType(PRUint16 unit);
nsWeakPtr mElement; // owning element - weakptr to avoid reference loop
float mValueInSpecifiedUnits;
PRUint16 mSpecifiedUnitType;
PRUint8 mCtxType;
};
//----------------------------------------------------------------------
// Implementation
nsresult
NS_NewSVGLength(nsISVGLength** result,
float value,
PRUint16 unit)
{
*result = new nsSVGLength(value, unit);
if (!*result)
return NS_ERROR_OUT_OF_MEMORY;
NS_ADDREF(*result);
return NS_OK;
}
nsresult
NS_NewSVGLength(nsISVGLength** result,
const nsAString &value)
{
*result = nsnull;
nsSVGLength *pl = new nsSVGLength();
if (!pl)
return NS_ERROR_OUT_OF_MEMORY;
NS_ADDREF(pl);
nsresult rv = pl->SetValueAsString(value);
if (NS_FAILED(rv)) {
NS_RELEASE(pl);
return rv;
}
*result = pl;
return NS_OK;
}
nsSVGLength::nsSVGLength(float value,
PRUint16 unit)
: mValueInSpecifiedUnits(value),
mSpecifiedUnitType(unit),
mCtxType(0)
{
}
nsSVGLength::nsSVGLength()
{
}
//----------------------------------------------------------------------
// nsISupports methods:
NS_IMPL_ADDREF(nsSVGLength)
NS_IMPL_RELEASE(nsSVGLength)
DOMCI_DATA(SVGLength, nsSVGLength)
NS_INTERFACE_MAP_BEGIN(nsSVGLength)
NS_INTERFACE_MAP_ENTRY(nsISVGValue)
NS_INTERFACE_MAP_ENTRY(nsISVGLength)
NS_INTERFACE_MAP_ENTRY(nsIDOMSVGLength)
NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(SVGLength)
NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsISVGValue)
NS_INTERFACE_MAP_END
//----------------------------------------------------------------------
// nsISVGValue methods:
NS_IMETHODIMP
nsSVGLength::SetValueString(const nsAString& aValue)
{
return SetValueAsString(aValue);
}
NS_IMETHODIMP
nsSVGLength::GetValueString(nsAString& aValue)
{
return GetValueAsString(aValue);
}
//----------------------------------------------------------------------
// nsIDOMSVGLength methods:
/* readonly attribute unsigned short unitType; */
NS_IMETHODIMP
nsSVGLength::GetUnitType(PRUint16 *aUnitType)
{
*aUnitType = mSpecifiedUnitType;
return NS_OK;
}
/* attribute float value; */
NS_IMETHODIMP
nsSVGLength::GetValue(float *aValue)
{
switch (mSpecifiedUnitType) {
case SVG_LENGTHTYPE_NUMBER:
case SVG_LENGTHTYPE_PX:
*aValue = mValueInSpecifiedUnits;
break;
case SVG_LENGTHTYPE_MM:
*aValue = mValueInSpecifiedUnits / mmPerPixel();
break;
case SVG_LENGTHTYPE_CM:
*aValue = mValueInSpecifiedUnits * 10.0f / mmPerPixel();
break;
case SVG_LENGTHTYPE_IN:
*aValue = mValueInSpecifiedUnits * 25.4f / mmPerPixel();
break;
case SVG_LENGTHTYPE_PT:
*aValue = mValueInSpecifiedUnits * 25.4f / POINTS_PER_INCH_FLOAT /
mmPerPixel();
break;
case SVG_LENGTHTYPE_PC:
*aValue = mValueInSpecifiedUnits * 25.4f * 12.0f / POINTS_PER_INCH_FLOAT /
mmPerPixel();
break;
case SVG_LENGTHTYPE_PERCENTAGE:
*aValue = mValueInSpecifiedUnits * AxisLength() / 100.0f;
break;
case SVG_LENGTHTYPE_EMS:
*aValue = mValueInSpecifiedUnits * EmLength();
break;
case SVG_LENGTHTYPE_EXS:
*aValue = mValueInSpecifiedUnits * ExLength();
break;
default:
NS_NOTREACHED("Unknown unit type");
*aValue = 0;
return NS_ERROR_UNEXPECTED;
}
return NS_OK;
}
NS_IMETHODIMP
nsSVGLength::SetValue(float aValue)
{
NS_ENSURE_FINITE(aValue, NS_ERROR_ILLEGAL_VALUE);
nsresult rv = NS_OK;
WillModify();
switch (mSpecifiedUnitType) {
case SVG_LENGTHTYPE_NUMBER:
case SVG_LENGTHTYPE_PX:
mValueInSpecifiedUnits = aValue;
break;
case SVG_LENGTHTYPE_MM:
mValueInSpecifiedUnits = aValue * mmPerPixel();
break;
case SVG_LENGTHTYPE_CM:
mValueInSpecifiedUnits = aValue * mmPerPixel() / 10.0f;
break;
case SVG_LENGTHTYPE_IN:
mValueInSpecifiedUnits = aValue * mmPerPixel() / 25.4f;
break;
case SVG_LENGTHTYPE_PT:
mValueInSpecifiedUnits = aValue * mmPerPixel() * POINTS_PER_INCH_FLOAT /
25.4f;
break;
case SVG_LENGTHTYPE_PC:
mValueInSpecifiedUnits = aValue * mmPerPixel() * POINTS_PER_INCH_FLOAT /
24.4f / 12.0f;
break;
case SVG_LENGTHTYPE_PERCENTAGE:
mValueInSpecifiedUnits = aValue * 100.0f / AxisLength();
break;
case SVG_LENGTHTYPE_EMS:
mValueInSpecifiedUnits = aValue / EmLength();
break;
case SVG_LENGTHTYPE_EXS:
mValueInSpecifiedUnits = aValue / ExLength();
break;
default:
NS_NOTREACHED("Unknown unit type");
mValueInSpecifiedUnits = 0;
rv = NS_ERROR_UNEXPECTED;
break;
}
DidModify();
return rv;
}
/* attribute float valueInSpecifiedUnits; */
NS_IMETHODIMP
nsSVGLength::GetValueInSpecifiedUnits(float *aValueInSpecifiedUnits)
{
*aValueInSpecifiedUnits = mValueInSpecifiedUnits;
return NS_OK;
}
NS_IMETHODIMP
nsSVGLength::SetValueInSpecifiedUnits(float aValueInSpecifiedUnits)
{
NS_ENSURE_FINITE(aValueInSpecifiedUnits, NS_ERROR_ILLEGAL_VALUE);
WillModify();
mValueInSpecifiedUnits = aValueInSpecifiedUnits;
DidModify();
return NS_OK;
}
/* attribute DOMString valueAsString; */
NS_IMETHODIMP
nsSVGLength::GetValueAsString(nsAString & aValueAsString)
{
PRUnichar buf[24];
nsTextFormatter::snprintf(buf, sizeof(buf)/sizeof(PRUnichar),
NS_LITERAL_STRING("%g").get(),
(double)mValueInSpecifiedUnits);
aValueAsString.Assign(buf);
nsIAtom* UnitAtom = nsnull;
switch (mSpecifiedUnitType) {
case SVG_LENGTHTYPE_NUMBER:
return NS_OK;
case SVG_LENGTHTYPE_PX:
UnitAtom = nsGkAtoms::px;
break;
case SVG_LENGTHTYPE_MM:
UnitAtom = nsGkAtoms::mm;
break;
case SVG_LENGTHTYPE_CM:
UnitAtom = nsGkAtoms::cm;
break;
case SVG_LENGTHTYPE_IN:
UnitAtom = nsGkAtoms::in;
break;
case SVG_LENGTHTYPE_PT:
UnitAtom = nsGkAtoms::pt;
break;
case SVG_LENGTHTYPE_PC:
UnitAtom = nsGkAtoms::pc;
break;
case SVG_LENGTHTYPE_EMS:
UnitAtom = nsGkAtoms::em;
break;
case SVG_LENGTHTYPE_EXS:
UnitAtom = nsGkAtoms::ex;
break;
case SVG_LENGTHTYPE_PERCENTAGE:
UnitAtom = nsGkAtoms::percentage;
break;
default:
NS_NOTREACHED("Unknown unit");
return NS_ERROR_UNEXPECTED;
}
aValueAsString.Append(nsDependentAtomString(UnitAtom));
return NS_OK;
}
NS_IMETHODIMP
nsSVGLength::SetValueAsString(const nsAString & aValueAsString)
{
nsresult rv = NS_ERROR_DOM_SYNTAX_ERR;
char *str = ToNewCString(aValueAsString);
char* number = str;
while (*number && IsSVGWhitespace(*number))
++number;
if (*number) {
char *rest;
float value = float(PR_strtod(number, &rest));
if (rest != number) {
const char* unitStr = nsCRT::strtok(rest, SVG_WSP_DELIM, &rest);
PRUint16 unitType = SVG_LENGTHTYPE_UNKNOWN;
if (!unitStr || *unitStr=='\0') {
unitType = SVG_LENGTHTYPE_NUMBER;
} else {
nsCOMPtr<nsIAtom> unitAtom = do_GetAtom(unitStr);
if (unitAtom == nsGkAtoms::px)
unitType = SVG_LENGTHTYPE_PX;
else if (unitAtom == nsGkAtoms::mm)
unitType = SVG_LENGTHTYPE_MM;
else if (unitAtom == nsGkAtoms::cm)
unitType = SVG_LENGTHTYPE_CM;
else if (unitAtom == nsGkAtoms::in)
unitType = SVG_LENGTHTYPE_IN;
else if (unitAtom == nsGkAtoms::pt)
unitType = SVG_LENGTHTYPE_PT;
else if (unitAtom == nsGkAtoms::pc)
unitType = SVG_LENGTHTYPE_PC;
else if (unitAtom == nsGkAtoms::em)
unitType = SVG_LENGTHTYPE_EMS;
else if (unitAtom == nsGkAtoms::ex)
unitType = SVG_LENGTHTYPE_EXS;
else if (unitAtom == nsGkAtoms::percentage)
unitType = SVG_LENGTHTYPE_PERCENTAGE;
}
if (IsValidUnitType(unitType) && NS_FloatIsFinite(value)) {
WillModify();
mValueInSpecifiedUnits = value;
mSpecifiedUnitType = unitType;
DidModify();
rv = NS_OK;
}
}
}
nsMemory::Free(str);
return rv;
}
/* void newValueSpecifiedUnits (in unsigned short unitType, in float valueInSpecifiedUnits); */
NS_IMETHODIMP
nsSVGLength::NewValueSpecifiedUnits(PRUint16 unitType, float valueInSpecifiedUnits)
{
NS_ENSURE_FINITE(valueInSpecifiedUnits, NS_ERROR_ILLEGAL_VALUE);
if (!IsValidUnitType(unitType))
return NS_ERROR_DOM_NOT_SUPPORTED_ERR;
WillModify();
mValueInSpecifiedUnits = valueInSpecifiedUnits;
mSpecifiedUnitType = unitType;
DidModify();
return NS_OK;
}
/* void convertToSpecifiedUnits (in unsigned short unitType); */
NS_IMETHODIMP
nsSVGLength::ConvertToSpecifiedUnits(PRUint16 unitType)
{
if (!IsValidUnitType(unitType))
return NS_ERROR_DOM_NOT_SUPPORTED_ERR;
WillModify();
float valueInUserUnits;
GetValue(&valueInUserUnits);
mSpecifiedUnitType = unitType;
SetValue(valueInUserUnits);
DidModify();
return NS_OK;
}
//----------------------------------------------------------------------
// nsISVGLength methods:
NS_IMETHODIMP
nsSVGLength::SetContext(nsIWeakReference *aContext, PRUint8 aCtxType)
{
mElement = aContext;
mCtxType = aCtxType;
return NS_OK;
}
//----------------------------------------------------------------------
// Implementation helpers:
float nsSVGLength::mmPerPixel()
{
nsCOMPtr<nsIContent> element = do_QueryReferent(mElement);
if (!element) {
NS_WARNING("no context in mmPerPixel()");
return 1.0f;
}
nsSVGSVGElement *ctx = static_cast<nsSVGElement*>(element.get())->GetCtx();
if (!ctx) {
return 1e-4f; // some small value
}
float mmPerPx = ctx->GetMMPerPx(mCtxType);
if (mmPerPx == 0.0f) {
NS_ASSERTION(mmPerPx != 0.0f, "invalid mm/pixels");
mmPerPx = 1e-4f; // some small value
}
return mmPerPx;
}
float nsSVGLength::AxisLength()
{
nsCOMPtr<nsIContent> element = do_QueryReferent(mElement);
if (!element) {
NS_WARNING("no context in AxisLength()");
return 1.0f;
}
nsSVGSVGElement *ctx = static_cast<nsSVGElement*>(element.get())->GetCtx();
if (!ctx) {
return 1e-20f; // some small value
}
float d = ctx->GetLength(mCtxType);
if (d == 0.0f) {
NS_WARNING("zero axis length");
d = 1e-20f;
}
return d;
}
float nsSVGLength::EmLength()
{
nsCOMPtr<nsIContent> element = do_QueryReferent(mElement);
return nsSVGUtils::GetFontSize(element->AsElement());
}
float nsSVGLength::ExLength()
{
nsCOMPtr<nsIContent> element = do_QueryReferent(mElement);
return nsSVGUtils::GetFontXHeight(element->AsElement());
}
PRBool nsSVGLength::IsValidUnitType(PRUint16 unit)
{
if (unit > SVG_LENGTHTYPE_UNKNOWN && unit <= SVG_LENGTHTYPE_PC)
return PR_TRUE;
return PR_FALSE;
}

View File

@ -1,59 +0,0 @@
/* -*- 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 SVG project.
*
* The Initial Developer of the Original Code is
* Crocodile Clips Ltd..
* Portions created by the Initial Developer are Copyright (C) 2001
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Alex Fritze <alex.fritze@crocodile-clips.com> (original author)
*
* 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 ***** */
#ifndef __NS_SVGLENGTH_H__
#define __NS_SVGLENGTH_H__
#include "nsISVGLength.h"
#include "nsAString.h"
nsresult
NS_NewSVGLength(nsISVGLength** result,
float value=0.0f,
PRUint16 unit=nsIDOMSVGLength::SVG_LENGTHTYPE_NUMBER);
nsresult
NS_NewSVGLength(nsISVGLength** result,
const nsAString &value);
// XXX we'll need this prototype-based stuff to support unsetting:
//nsresult NS_NewSVGLength(nsIDOMSVGLength** result,
// nsIDOMSVGLength* prototype);
#endif //__NS_SVGLENGTH_H__

View File

@ -1,407 +0,0 @@
/* -*- 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 SVG project.
*
* The Initial Developer of the Original Code is
* Crocodile Clips Ltd..
* Portions created by the Initial Developer are Copyright (C) 2002
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Alex Fritze <alex.fritze@crocodile-clips.com> (original author)
*
* 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 ***** */
#include "nsSVGLengthList.h"
#include "nsSVGLength.h"
#include "nsSVGValue.h"
#include "nsWeakReference.h"
#include "nsTArray.h"
#include "nsDOMError.h"
#include "nsReadableUtils.h"
#include "nsSVGSVGElement.h"
#include "nsCRT.h"
#include "nsISVGValueUtils.h"
#include "nsContentUtils.h"
////////////////////////////////////////////////////////////////////////
// nsSVGLengthList
class nsSVGLengthList : public nsIDOMSVGLengthList,
public nsSVGValue,
public nsISVGValueObserver
{
protected:
friend nsresult NS_NewSVGLengthList(nsIDOMSVGLengthList** result,
nsSVGElement *aContext,
PRUint8 aCtxType);
nsSVGLengthList(nsSVGElement *aContext, PRUint8 aCtxType);
~nsSVGLengthList();
// void Init();
public:
// nsISupports interface:
NS_DECL_ISUPPORTS
// nsIDOMSVGLengthList interface:
NS_DECL_NSIDOMSVGLENGTHLIST
// remainder of nsISVGValue interface:
NS_IMETHOD SetValueString(const nsAString& aValue);
NS_IMETHOD GetValueString(nsAString& aValue);
// nsISVGValueObserver interface:
NS_IMETHOD WillModifySVGObservable(nsISVGValue* observable,
modificationType aModType);
NS_IMETHOD DidModifySVGObservable (nsISVGValue* observable,
modificationType aModType);
// nsISupportsWeakReference
// implementation inherited from nsSupportsWeakReference
protected:
// implementation helpers:
nsISVGLength* ElementAt(PRInt32 index);
void AppendElement(nsISVGLength* aElement);
void RemoveElementAt(PRInt32 index);
void InsertElementAt(nsISVGLength* aElement, PRInt32 index);
void ReleaseLengths();
nsAutoTArray<nsISVGLength*, 1> mLengths;
nsWeakPtr mContext; // needs to be weak to avoid reference loop
PRUint8 mCtxType;
};
//----------------------------------------------------------------------
// Implementation
nsSVGLengthList::nsSVGLengthList(nsSVGElement *aContext, PRUint8 aCtxType)
: mCtxType(aCtxType)
{
mContext = do_GetWeakReference(static_cast<nsGenericElement*>(aContext));
}
nsSVGLengthList::~nsSVGLengthList()
{
ReleaseLengths();
}
//----------------------------------------------------------------------
// nsISupports methods:
NS_IMPL_ADDREF(nsSVGLengthList)
NS_IMPL_RELEASE(nsSVGLengthList)
DOMCI_DATA(SVGLengthList, nsSVGLengthList)
NS_INTERFACE_MAP_BEGIN(nsSVGLengthList)
NS_INTERFACE_MAP_ENTRY(nsISVGValue)
NS_INTERFACE_MAP_ENTRY(nsIDOMSVGLengthList)
NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)
NS_INTERFACE_MAP_ENTRY(nsISVGValueObserver)
NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(SVGLengthList)
NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsISVGValue)
NS_INTERFACE_MAP_END
//----------------------------------------------------------------------
// nsISVGValue methods:
NS_IMETHODIMP
nsSVGLengthList::SetValueString(const nsAString& aValue)
{
WillModify();
ReleaseLengths();
nsresult rv = NS_OK;
char* str;
str = ToNewCString(aValue);
char* rest = str;
char* token;
const char* delimiters = ",\x20\x9\xD\xA";
while ((token = nsCRT::strtok(rest, delimiters, &rest))) {
nsCOMPtr<nsISVGLength> length;
NS_NewSVGLength(getter_AddRefs(length), NS_ConvertASCIItoUTF16(token));
if (!length) {
rv = NS_ERROR_DOM_SYNTAX_ERR;
break;
}
AppendElement(length);
}
nsMemory::Free(str);
DidModify();
return rv;
}
NS_IMETHODIMP
nsSVGLengthList::GetValueString(nsAString& aValue)
{
aValue.Truncate();
PRUint32 count = mLengths.Length();
if (count == 0) return NS_OK;
PRUint32 i = 0;
while (1) {
nsISVGLength* length = ElementAt(i);
nsCOMPtr<nsISVGValue> val = do_QueryInterface(length);
NS_ASSERTION(val, "length doesn't implement required interface");
if (!val) continue;
nsAutoString str;
val->GetValueString(str);
aValue.Append(str);
if (++i >= count) break;
aValue.AppendLiteral(" ");
}
return NS_OK;
}
//----------------------------------------------------------------------
// nsIDOMSVGLengthList methods:
/* readonly attribute unsigned long numberOfItems; */
NS_IMETHODIMP nsSVGLengthList::GetNumberOfItems(PRUint32 *aNumberOfItems)
{
*aNumberOfItems = mLengths.Length();
return NS_OK;
}
/* void clear (); */
NS_IMETHODIMP nsSVGLengthList::Clear()
{
WillModify();
ReleaseLengths();
DidModify();
return NS_OK;
}
/* nsIDOMSVGLength initialize (in nsIDOMSVGLength newItem); */
NS_IMETHODIMP nsSVGLengthList::Initialize(nsIDOMSVGLength *newItem,
nsIDOMSVGLength **_retval)
{
if (!newItem) {
*_retval = nsnull;
return NS_ERROR_DOM_SVG_WRONG_TYPE_ERR;
}
Clear();
return AppendItem(newItem, _retval);
}
/* nsIDOMSVGLength getItem (in unsigned long index); */
NS_IMETHODIMP nsSVGLengthList::GetItem(PRUint32 index, nsIDOMSVGLength **_retval)
{
if (index >= mLengths.Length()) {
*_retval = nsnull;
return NS_ERROR_DOM_INDEX_SIZE_ERR;
}
*_retval = ElementAt(index);
NS_ADDREF(*_retval);
return NS_OK;
}
/* nsIDOMSVGLength insertItemBefore (in nsIDOMSVGLength newItem, in unsigned long index); */
NS_IMETHODIMP
nsSVGLengthList::InsertItemBefore(nsIDOMSVGLength *newItem,
PRUint32 index,
nsIDOMSVGLength **_retval)
{
// null check when implementing - this method can be used by scripts!
// if (!newItem)
// return NS_ERROR_DOM_SVG_WRONG_TYPE_ERR;
NS_NOTYETIMPLEMENTED("nsSVGLengthList::InsertItemBefore");
return NS_ERROR_NOT_IMPLEMENTED;
}
/* nsIDOMSVGLength replaceItem (in nsIDOMSVGLength newItem, in unsigned long index); */
NS_IMETHODIMP
nsSVGLengthList::ReplaceItem(nsIDOMSVGLength *newItem,
PRUint32 index,
nsIDOMSVGLength **_retval)
{
// null check when implementing - this method can be used by scripts!
// if (!newItem)
// return NS_ERROR_DOM_SVG_WRONG_TYPE_ERR;
NS_NOTYETIMPLEMENTED("nsSVGLengthList::ReplaceItem");
return NS_ERROR_NOT_IMPLEMENTED;
}
/* nsIDOMSVGLengthList removeItem (in unsigned long index); */
NS_IMETHODIMP nsSVGLengthList::RemoveItem(PRUint32 index, nsIDOMSVGLength **_retval)
{
if (index >= mLengths.Length()) {
*_retval = nsnull;
return NS_ERROR_DOM_INDEX_SIZE_ERR;
}
*_retval = ElementAt(index);
NS_ADDREF(*_retval);
WillModify();
RemoveElementAt(index);
DidModify();
return NS_OK;
}
/* nsIDOMSVGLengthList appendItem (in nsIDOMSVGLengthList newItem); */
NS_IMETHODIMP
nsSVGLengthList::AppendItem(nsIDOMSVGLength *newItem, nsIDOMSVGLength **_retval)
{
nsCOMPtr<nsISVGLength> length = do_QueryInterface(newItem);
if (!length) {
*_retval = nsnull;
return NS_ERROR_DOM_SVG_WRONG_TYPE_ERR;
}
AppendElement(length);
*_retval = newItem;
NS_ADDREF(*_retval);
return NS_OK;
}
//----------------------------------------------------------------------
// nsISVGValueObserver methods
NS_IMETHODIMP
nsSVGLengthList::WillModifySVGObservable(nsISVGValue* observable,
modificationType aModType)
{
WillModify(aModType);
return NS_OK;
}
NS_IMETHODIMP
nsSVGLengthList::DidModifySVGObservable(nsISVGValue* observable,
modificationType aModType)
{
DidModify(aModType);
return NS_OK;
}
//----------------------------------------------------------------------
// Implementation helpers
void
nsSVGLengthList::ReleaseLengths()
{
WillModify();
PRUint32 count = mLengths.Length();
for (PRUint32 i = 0; i < count; ++i) {
nsISVGLength* length = ElementAt(i);
length->SetContext(nsnull, 0);
NS_REMOVE_SVGVALUE_OBSERVER(length);
NS_RELEASE(length);
}
mLengths.Clear();
DidModify();
}
nsISVGLength*
nsSVGLengthList::ElementAt(PRInt32 index)
{
return mLengths.ElementAt(index);
}
void
nsSVGLengthList::AppendElement(nsISVGLength* aElement)
{
WillModify();
NS_ADDREF(aElement);
// The SVG specs state that 'if newItem is already in a list, it
// is removed from its previous list before it is inserted into this
// list':
// aElement->SetListOwner(this);
aElement->SetContext(mContext, mCtxType);
mLengths.AppendElement(aElement);
NS_ADD_SVGVALUE_OBSERVER(aElement);
DidModify();
}
void
nsSVGLengthList::RemoveElementAt(PRInt32 index)
{
WillModify();
nsISVGLength* length = ElementAt(index);
NS_ASSERTION(length, "null length");
NS_REMOVE_SVGVALUE_OBSERVER(length);
mLengths.RemoveElementAt(index);
NS_RELEASE(length);
DidModify();
}
void
nsSVGLengthList::InsertElementAt(nsISVGLength* aElement, PRInt32 index)
{
WillModify();
NS_ADDREF(aElement);
// The SVG specs state that 'if newItem is already in a list, it
// is removed from its previous list before it is inserted into this
// list':
// aElement->SetListOwner(this);
aElement->SetContext(mContext, mCtxType);
mLengths.InsertElementAt(index, aElement);
NS_ADD_SVGVALUE_OBSERVER(aElement);
DidModify();
}
////////////////////////////////////////////////////////////////////////
// Exported creation functions:
nsresult
NS_NewSVGLengthList(nsIDOMSVGLengthList** result, nsSVGElement *aContext, PRUint8 aCtxType)
{
*result = nsnull;
nsSVGLengthList* lengthList = new nsSVGLengthList(aContext, aCtxType);
if (!lengthList) return NS_ERROR_OUT_OF_MEMORY;
NS_ADDREF(lengthList);
*result = lengthList;
return NS_OK;
}

View File

@ -1,49 +0,0 @@
/* -*- 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 SVG project.
*
* The Initial Developer of the Original Code is
* Crocodile Clips Ltd..
* Portions created by the Initial Developer are Copyright (C) 2002
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Alex Fritze <alex.fritze@crocodile-clips.com> (original author)
*
* 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 ***** */
#ifndef __NS_SVGLENGTHLIST_H__
#define __NS_SVGLENGTHLIST_H__
#include "nsIDOMSVGLengthList.h"
class nsSVGElement;
nsresult
NS_NewSVGLengthList(nsIDOMSVGLengthList** result, nsSVGElement *aContext, PRUint8 aCtxType);
#endif //__NS_SVGLENGTHLIST_H__

View File

@ -39,7 +39,7 @@
* ***** END LICENSE BLOCK ***** */
#include "nsGkAtoms.h"
#include "nsSVGLength.h"
#include "DOMSVGLength.h"
#include "nsSVGAngle.h"
#include "nsCOMPtr.h"
#include "nsIPresShell.h"
@ -655,7 +655,8 @@ nsSVGSVGElement::CreateSVGNumber(nsIDOMSVGNumber **_retval)
NS_IMETHODIMP
nsSVGSVGElement::CreateSVGLength(nsIDOMSVGLength **_retval)
{
return NS_NewSVGLength(reinterpret_cast<nsISVGLength**>(_retval));
NS_IF_ADDREF(*_retval = new DOMSVGLength());
return NS_OK;
}
/* nsIDOMSVGAngle createSVGAngle (); */

View File

@ -115,7 +115,7 @@ NS_IMPL_ELEMENT_CLONE_WITH_INIT(nsSVGStopElement)
//----------------------------------------------------------------------
// nsIDOMSVGStopElement methods
/* readonly attribute nsIDOMSVGAnimatedLengthList x; */
/* readonly attribute nsIDOMSVGAnimatedNumber offset; */
NS_IMETHODIMP nsSVGStopElement::GetOffset(nsIDOMSVGAnimatedNumber * *aOffset)
{
return mOffset.ToDOMAnimatedNumber(aOffset,this);

View File

@ -44,11 +44,14 @@
#include "nsSVGTextPositioningElement.h"
#include "nsIFrame.h"
#include "nsDOMError.h"
#include "nsSVGLengthList.h"
#include "nsSVGAnimatedLengthList.h"
#include "SVGAnimatedLengthList.h"
#include "DOMSVGAnimatedLengthList.h"
#include "SVGLengthList.h"
#include "nsSVGNumberList.h"
#include "nsSVGAnimatedNumberList.h"
using namespace mozilla;
typedef nsSVGGraphicElement nsSVGTextElementBase;
/**
@ -96,11 +99,14 @@ protected:
return do_QueryFrame(GetPrimaryFrame(Flush_Layout));
}
virtual LengthListAttributesInfo GetLengthListInfo();
// nsIDOMSVGTextPositioning properties:
nsCOMPtr<nsIDOMSVGAnimatedLengthList> mX;
nsCOMPtr<nsIDOMSVGAnimatedLengthList> mY;
nsCOMPtr<nsIDOMSVGAnimatedLengthList> mdX;
nsCOMPtr<nsIDOMSVGAnimatedLengthList> mdY;
enum { X, Y, DX, DY };
SVGAnimatedLengthList mLengthListAttributes[4];
static LengthListInfo sLengthListInfo[4];
nsCOMPtr<nsIDOMSVGAnimatedNumberList> mRotate;
};
@ -139,54 +145,6 @@ nsSVGTextElement::Init()
nsresult rv = nsSVGTextElementBase::Init();
NS_ENSURE_SUCCESS(rv,rv);
// DOM property: nsIDOMSVGTextPositioningElement::x, #IMPLIED attrib: x
{
nsCOMPtr<nsIDOMSVGLengthList> lengthList;
rv = NS_NewSVGLengthList(getter_AddRefs(lengthList), this, nsSVGUtils::X);
NS_ENSURE_SUCCESS(rv,rv);
rv = NS_NewSVGAnimatedLengthList(getter_AddRefs(mX),
lengthList);
NS_ENSURE_SUCCESS(rv,rv);
rv = AddMappedSVGValue(nsGkAtoms::x, mX);
NS_ENSURE_SUCCESS(rv,rv);
}
// DOM property: nsIDOMSVGTextPositioningElement::y, #IMPLIED attrib: y
{
nsCOMPtr<nsIDOMSVGLengthList> lengthList;
rv = NS_NewSVGLengthList(getter_AddRefs(lengthList), this, nsSVGUtils::Y);
NS_ENSURE_SUCCESS(rv,rv);
rv = NS_NewSVGAnimatedLengthList(getter_AddRefs(mY),
lengthList);
NS_ENSURE_SUCCESS(rv,rv);
rv = AddMappedSVGValue(nsGkAtoms::y, mY);
NS_ENSURE_SUCCESS(rv,rv);
}
// DOM property: nsIDOMSVGTextPositioningElement::dx, #IMPLIED attrib: dx
{
nsCOMPtr<nsIDOMSVGLengthList> lengthList;
rv = NS_NewSVGLengthList(getter_AddRefs(lengthList), this, nsSVGUtils::X);
NS_ENSURE_SUCCESS(rv,rv);
rv = NS_NewSVGAnimatedLengthList(getter_AddRefs(mdX),
lengthList);
NS_ENSURE_SUCCESS(rv,rv);
rv = AddMappedSVGValue(nsGkAtoms::dx, mdX);
NS_ENSURE_SUCCESS(rv,rv);
}
// DOM property: nsIDOMSVGTextPositioningElement::dy, #IMPLIED attrib: dy
{
nsCOMPtr<nsIDOMSVGLengthList> lengthList;
rv = NS_NewSVGLengthList(getter_AddRefs(lengthList), this, nsSVGUtils::Y);
NS_ENSURE_SUCCESS(rv,rv);
rv = NS_NewSVGAnimatedLengthList(getter_AddRefs(mdY),
lengthList);
NS_ENSURE_SUCCESS(rv,rv);
rv = AddMappedSVGValue(nsGkAtoms::dy, mdY);
NS_ENSURE_SUCCESS(rv,rv);
}
// DOM property: nsIDOMSVGTextPositioningElement::rotate, #IMPLIED attrib: rotate
{
nsCOMPtr<nsIDOMSVGNumberList> numberList;
@ -222,8 +180,8 @@ NS_IMPL_ELEMENT_CLONE_WITH_INIT(nsSVGTextElement)
NS_IMETHODIMP
nsSVGTextElement::GetX(nsIDOMSVGAnimatedLengthList * *aX)
{
*aX = mX;
NS_IF_ADDREF(*aX);
*aX = DOMSVGAnimatedLengthList::GetDOMWrapper(&mLengthListAttributes[X],
this, X, nsSVGUtils::X).get();
return NS_OK;
}
@ -231,8 +189,8 @@ nsSVGTextElement::GetX(nsIDOMSVGAnimatedLengthList * *aX)
NS_IMETHODIMP
nsSVGTextElement::GetY(nsIDOMSVGAnimatedLengthList * *aY)
{
*aY = mY;
NS_IF_ADDREF(*aY);
*aY = DOMSVGAnimatedLengthList::GetDOMWrapper(&mLengthListAttributes[Y],
this, Y, nsSVGUtils::Y).get();
return NS_OK;
}
@ -240,8 +198,8 @@ nsSVGTextElement::GetY(nsIDOMSVGAnimatedLengthList * *aY)
NS_IMETHODIMP
nsSVGTextElement::GetDx(nsIDOMSVGAnimatedLengthList * *aDx)
{
*aDx = mdX;
NS_IF_ADDREF(*aDx);
*aDx = DOMSVGAnimatedLengthList::GetDOMWrapper(&mLengthListAttributes[DX],
this, DX, nsSVGUtils::X).get();
return NS_OK;
}
@ -249,8 +207,8 @@ nsSVGTextElement::GetDx(nsIDOMSVGAnimatedLengthList * *aDx)
NS_IMETHODIMP
nsSVGTextElement::GetDy(nsIDOMSVGAnimatedLengthList * *aDy)
{
*aDy = mdY;
NS_IF_ADDREF(*aDy);
*aDy = DOMSVGAnimatedLengthList::GetDOMWrapper(&mLengthListAttributes[DY],
this, DY, nsSVGUtils::Y).get();
return NS_OK;
}
@ -417,3 +375,22 @@ nsSVGTextElement::IsAttributeMapped(const nsIAtom* name) const
return FindAttributeDependence(name, map, NS_ARRAY_LENGTH(map)) ||
nsSVGTextElementBase::IsAttributeMapped(name);
}
//----------------------------------------------------------------------
// nsSVGElement methods
nsSVGElement::LengthListInfo nsSVGTextElement::sLengthListInfo[4] =
{
{ &nsGkAtoms::x, nsSVGUtils::X, PR_FALSE },
{ &nsGkAtoms::y, nsSVGUtils::Y, PR_FALSE },
{ &nsGkAtoms::dx, nsSVGUtils::X, PR_TRUE },
{ &nsGkAtoms::dy, nsSVGUtils::Y, PR_TRUE }
};
nsSVGElement::LengthListAttributesInfo
nsSVGTextElement::GetLengthListInfo()
{
return LengthListAttributesInfo(mLengthListAttributes, sLengthListInfo,
NS_ARRAY_LENGTH(sLengthListInfo));
}

View File

@ -34,11 +34,14 @@
* ***** END LICENSE BLOCK ***** */
#include "nsSVGTextPositioningElement.h"
#include "nsSVGAnimatedLengthList.h"
#include "SVGAnimatedLengthList.h"
#include "DOMSVGAnimatedLengthList.h"
#include "SVGLengthList.h"
#include "nsSVGAnimatedNumberList.h"
#include "nsSVGLengthList.h"
#include "nsSVGNumberList.h"
using namespace mozilla;
nsresult
nsSVGTextPositioningElement::Init()
{
@ -47,54 +50,6 @@ nsSVGTextPositioningElement::Init()
// Create mapped properties:
// DOM property: nsIDOMSVGTextPositioningElement::x, #IMPLIED attrib: x
{
nsCOMPtr<nsIDOMSVGLengthList> lengthList;
rv = NS_NewSVGLengthList(getter_AddRefs(lengthList), this, nsSVGUtils::X);
NS_ENSURE_SUCCESS(rv,rv);
rv = NS_NewSVGAnimatedLengthList(getter_AddRefs(mX),
lengthList);
NS_ENSURE_SUCCESS(rv,rv);
rv = AddMappedSVGValue(nsGkAtoms::x, mX);
NS_ENSURE_SUCCESS(rv,rv);
}
// DOM property: nsIDOMSVGTextPositioningElement::y, #IMPLIED attrib: y
{
nsCOMPtr<nsIDOMSVGLengthList> lengthList;
rv = NS_NewSVGLengthList(getter_AddRefs(lengthList), this, nsSVGUtils::Y);
NS_ENSURE_SUCCESS(rv,rv);
rv = NS_NewSVGAnimatedLengthList(getter_AddRefs(mY),
lengthList);
NS_ENSURE_SUCCESS(rv,rv);
rv = AddMappedSVGValue(nsGkAtoms::y, mY);
NS_ENSURE_SUCCESS(rv,rv);
}
// DOM property: nsIDOMSVGTextPositioningElement::dx, #IMPLIED attrib: dx
{
nsCOMPtr<nsIDOMSVGLengthList> lengthList;
rv = NS_NewSVGLengthList(getter_AddRefs(lengthList), this, nsSVGUtils::X);
NS_ENSURE_SUCCESS(rv,rv);
rv = NS_NewSVGAnimatedLengthList(getter_AddRefs(mdX),
lengthList);
NS_ENSURE_SUCCESS(rv,rv);
rv = AddMappedSVGValue(nsGkAtoms::dx, mdX);
NS_ENSURE_SUCCESS(rv,rv);
}
// DOM property: nsIDOMSVGTextPositioningElement::dy, #IMPLIED attrib: dy
{
nsCOMPtr<nsIDOMSVGLengthList> lengthList;
rv = NS_NewSVGLengthList(getter_AddRefs(lengthList), this, nsSVGUtils::Y);
NS_ENSURE_SUCCESS(rv,rv);
rv = NS_NewSVGAnimatedLengthList(getter_AddRefs(mdY),
lengthList);
NS_ENSURE_SUCCESS(rv,rv);
rv = AddMappedSVGValue(nsGkAtoms::dy, mdY);
NS_ENSURE_SUCCESS(rv,rv);
}
// DOM property: nsIDOMSVGTextPositioningElement::rotate, #IMPLIED attrib: rotate
{
nsCOMPtr<nsIDOMSVGNumberList> numberList;
@ -110,38 +65,54 @@ nsSVGTextPositioningElement::Init()
return rv;
}
nsSVGElement::LengthListInfo nsSVGTextPositioningElement::sLengthListInfo[4] =
{
{ &nsGkAtoms::x, nsSVGUtils::X, PR_FALSE },
{ &nsGkAtoms::y, nsSVGUtils::Y, PR_FALSE },
{ &nsGkAtoms::dx, nsSVGUtils::X, PR_TRUE },
{ &nsGkAtoms::dy, nsSVGUtils::Y, PR_TRUE }
};
nsSVGElement::LengthListAttributesInfo
nsSVGTextPositioningElement::GetLengthListInfo()
{
return LengthListAttributesInfo(mLengthListAttributes, sLengthListInfo,
NS_ARRAY_LENGTH(sLengthListInfo));
}
//----------------------------------------------------------------------
// nsIDOMSVGTextPositioningElement methods
/* readonly attribute nsIDOMSVGAnimatedLengthList x; */
NS_IMETHODIMP nsSVGTextPositioningElement::GetX(nsIDOMSVGAnimatedLengthList * *aX)
{
*aX = mX;
NS_IF_ADDREF(*aX);
*aX = DOMSVGAnimatedLengthList::GetDOMWrapper(&mLengthListAttributes[X],
this, X, nsSVGUtils::X).get();
return NS_OK;
}
/* readonly attribute nsIDOMSVGAnimatedLengthList y; */
NS_IMETHODIMP nsSVGTextPositioningElement::GetY(nsIDOMSVGAnimatedLengthList * *aY)
{
*aY = mY;
NS_IF_ADDREF(*aY);
*aY = DOMSVGAnimatedLengthList::GetDOMWrapper(&mLengthListAttributes[Y],
this, Y, nsSVGUtils::Y).get();
return NS_OK;
}
/* readonly attribute nsIDOMSVGAnimatedLengthList dx; */
NS_IMETHODIMP nsSVGTextPositioningElement::GetDx(nsIDOMSVGAnimatedLengthList * *aDx)
{
*aDx = mdX;
NS_IF_ADDREF(*aDx);
*aDx = DOMSVGAnimatedLengthList::GetDOMWrapper(&mLengthListAttributes[DX],
this, DX, nsSVGUtils::X).get();
return NS_OK;
}
/* readonly attribute nsIDOMSVGAnimatedLengthList dy; */
NS_IMETHODIMP nsSVGTextPositioningElement::GetDy(nsIDOMSVGAnimatedLengthList * *aDy)
{
*aDy = mdY;
NS_IF_ADDREF(*aDy);
*aDy = DOMSVGAnimatedLengthList::GetDOMWrapper(&mLengthListAttributes[DY],
this, DY, nsSVGUtils::Y).get();
return NS_OK;
}

View File

@ -40,9 +40,14 @@
#include "nsSVGTextContentElement.h"
#include "nsIDOMSVGAnimatedLengthList.h"
#include "nsIDOMSVGAnimatedNumberList.h"
#include "SVGAnimatedLengthList.h"
class nsSVGElement;
namespace mozilla {
class SVGAnimatedLengthList;
}
typedef nsSVGTextContentElement nsSVGTextPositioningElementBase;
/**
@ -63,11 +68,14 @@ protected:
nsresult Init();
virtual LengthListAttributesInfo GetLengthListInfo();
// nsIDOMSVGTextPositioning properties:
nsCOMPtr<nsIDOMSVGAnimatedLengthList> mX;
nsCOMPtr<nsIDOMSVGAnimatedLengthList> mY;
nsCOMPtr<nsIDOMSVGAnimatedLengthList> mdX;
nsCOMPtr<nsIDOMSVGAnimatedLengthList> mdY;
enum { X, Y, DX, DY };
SVGAnimatedLengthList mLengthListAttributes[4];
static LengthListInfo sLengthListInfo[4];
nsCOMPtr<nsIDOMSVGAnimatedNumberList> mRotate;
};

View File

@ -64,6 +64,7 @@ _TEST_FILES = \
test_scientific.html \
scientific-helper.svg \
test_SVGStyleElement.xhtml \
test_SVGxxxList.xhtml \
test_switch.xhtml \
switch-helper.svg \
test_text.html \

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,23 @@
<svg xmlns="http://www.w3.org/2000/svg">
<text transform="translate(20, 20)"
x="20 10mm 3pc 72pt 2em 3% 1ex"
dy="20 10mm 3pc 36pt 2em 3% 1ex 1">ABCDEFGH
</text>
<text transform="translate(120, 20)"
x="20 10mm 3pc 72pt 2em 3% 1ex"
dy="20 10mm 3pc 36pt 2em 3% 1ex">IJKLMNOP
</text>
<text transform="translate(220, 20)"
x="20 10mm 3pc 72pt 2em 3% 1ex"
dy="20 10mm 3pc 36pt 2em 3% 1ex 1">QRSTUVWX
</text>
<text transform="translate(320, 20)"
x="20 10mm 3pc 72pt 2em 3% 1ex"
dy="20 10mm 3pc 36pt 2em 3% 1ex">YZ123456
</text>
</svg>

After

Width:  |  Height:  |  Size: 612 B

View File

@ -0,0 +1,179 @@
<svg xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
class="reftest-wait"
onload="setTimeAndSnapshot(5, true)">
<title>Test animation of the &lt;length-list&gt; attributes on the 'text' element</title>
<script xlink:href="smil-util.js" type="text/javascript"/>
<style type="text/css">
:root { font-size: 16px; } /* see comment below - sets 1em == 16px */
</style>
<!-- If we start getting random orange on this test, see:
https://bugzilla.mozilla.org/show_bug.cgi?id=515116#c34
-->
<!-- One of the things that this file tests is animation between lengths
of _different unit_. One difficulty this creates is knowing the
values to use in the reference file. For example, what length should
be used in the reference to comparing against an animation that's mid
way between 50px and 10in? The SMIL engine will convert the start
length to the unit of the end length and then interpolate, but the
number of inches in 50px is not always the same, so we can't fix how
many inches is midway between 50px and 10in in the reference file. To
get around this problem this test mainly pairs different units with a
knows, fixed, relationship. For example, animating between cm and mm,
or between 'in' and pt (72 pt/in) or between 'in' and pc (6 pc/in).
Note that we can animate between px and em by fixing the relationship
between these units by setting the CSS 'font-size' property to a fixed
number of px units as we've done above.
The problem with only testing pairs with a fixed relationship is that
implementations may only implement interpolation between those pairs
because it's easy. To test interpolation between pairs of units
without a fixed relationship we use another strategy: we animate from
zero of the start unit. Since zero is zero regardless of the unit, we
then know what to use in the reference. In theory implementations might
specialize for zero, but that's unlikely (hopefully!).
(An alternative would be to only test end points of the animation, but
implementations may use discrete animation rather than interpolation
when lengths of different units are encountered, so that would be a bad
approach.)
(Another better alternative would be to use
SVGLength.convertToSpecifiedUnits() in the reference file to figure out
the conversion of the start unit and set values in the reference file
dynamically.)
-->
<!-- Another thing that this file test is animation between lists
of _different length_. One implementation strategy when faced with such
an animation is to pad the shorter list with zeros for the purposes of
animation. This works exactly as you would hope in the case of the
<text> element's 'dx' and 'dy' attributes, since for those attributes
lengths are offsets from the coordinate at which respective glyph's
would otherwise be positioned. In other words, appending a list of
zeros to any 'dx' or 'dy' attribute will never have an affect on
rendering. However, in the case of 'x' and 'y' attributes on <text>,
any lengths given are distances from zero along the relevant axis of
the current coordinate system, and zero is not likely to be the
position that all glyphs would otherwise be given. Their position will
actually depend on CSS parameters, the implementation's text layout
algorithm, and the layout of the characters that came before it. Hence
zero padding any 'x' or 'y' attribute will likely drastically alter
the rendering of the text, and in the case of animation would cause
glyphs to animate from/to zero in their coordinate system instead of
from/to their natural position. Clearly this is neither what authors
would expect, or want, so Mozilla currently disallows animation of 'x'
and 'y' if lists of different length are encountered to prevent content
being created that relies on such undesirable behavior.
Ideally the implementation would provide the SMIL engine with the
natural position of each glyph so that it can animate from/to those
positions. That's tricky, but we do have a bug open to try and
implement that. See:
https://bugzilla.mozilla.org/show_bug.cgi?id=573431
-->
<!-- Test calcMode="linear". -->
<text transform="translate(20, 20)"
x="10px 0.5cm 0.25in 0.5in 16px 0cm 0%">ABCDEFGH
<!-- At 5s the animVal should be "20 10mm 3pc 72pt 2em 3% 1ex". -->
<animate attributeName="x"
calcMode="linear"
begin="0s" dur="15s"
to="40 20mm 6pc 144pt 4em 9% 3ex"
fill="freeze"/>
<!-- At 5s the animVal should be "20 10mm 3pc 36pt 2em 3% 1ex 1".
Note that the 'to' list has one extra list item on purpose! -->
<animate attributeName="dy"
calcMode="linear"
begin="0s" dur="15s"
from="10px 0.5cm 0.25in 0.25in 16px 0cm 0%"
to="40 20mm 6pc 72pt 4em 9% 3ex 3"
fill="freeze"/>
</text>
<!-- Test 'by' animation. -->
<text transform="translate(120, 20)"
x="10px 0.5cm 0.25in 0.5in 16px 0cm 0%">IJKLMNOP
<!-- At 5s the animVal should be "20 10mm 3pc 72pt 2em 3% 1ex". -->
<animate attributeName="x"
calcMode="linear"
begin="0s" dur="15s"
by="30 15mm 4.5pc 108pt 3em 9% 3ex"
fill="freeze"/>
<!-- At 5s the animVal should be "20 10mm 3pc 36pt 2em 3% 1ex".
Note that the 'from' list is essentially zero length! -->
<animate attributeName="dy"
calcMode="linear"
begin="0s" dur="15s"
by="60 30mm 9pc 108pt 6em 9% 3ex"
fill="freeze"/>
</text>
<!-- Test calcMode="paced". It doesn't make a lot of sense to use paced
animation with a length list, but since we support it, we test it.
-->
<text transform="translate(220, 20)">QRSTUVWX
<!-- At 5s the animVal should be "20 10mm 3pc 72pt 2em 3% 1ex". -->
<animate attributeName="x"
calcMode="paced"
begin="0s" dur="15s"
values="10px 0.5cm 0.25in 0.5in 16px 0cm 0%;
30 15mm 4.5pc 108pt 3em 6% 2ex;
40 20mm 6pc 144pt 4em 9% 3ex"
fill="freeze"/>
<!-- At 5s the animVal should be "20 10mm 3pc 36pt 2em 3% 1ex 1".
Note that the 'to' lists have one extra list item on purpose! -->
<animate attributeName="dy"
calcMode="paced"
begin="0s" dur="15s"
values="10px 0.5cm 0.25in 0.25in 16px 0cm 0%;
30 15mm 4.5pc 54pt 3em 6% 2ex 2;
40 20mm 6pc 72pt 4em 9% 3ex 3"
fill="freeze"/>
</text>
<!-- Test calcMode="discrete". In this case SMIL treats the 'from' and 'to'
as two discrete values to jump between. Some authors may expect
discrete animation to jump from integer to integer in the unit of the
list item in question (or the unit of the 'to' item if the units of
corresponding 'from' and 'to' items differ), but that's not the case.
-->
<text transform="translate(320, 20)">YZ123456
<!-- At 5s the animVal should be "20 10mm 3pc 72pt 2em 3% 1ex". -->
<animate attributeName="x"
calcMode="discrete"
begin="0s" dur="10s"
from="10px 0.5cm 0.25in 0.5in 16px 0cm 0%"
to="20 10mm 3pc 72pt 2em 3% 1ex"
fill="freeze"/>
<!-- At 5s the animVal should be "20 10mm 3pc 36pt 2em 3% 1ex".
Note that the 'to' list has one extra list item on purpose! -->
<animate attributeName="dy"
calcMode="discrete"
begin="0s" dur="10.1s"
from="20 10mm 3pc 36pt 2em 3% 1ex"
to="40px 2cm 1in 1in 16px 0cm 0% 3"
fill="freeze"/>
</text>
</svg>

After

Width:  |  Height:  |  Size: 7.9 KiB

View File

@ -136,6 +136,8 @@ fails == anim-strokecolor-1.svg anim-standard-ref.svg # bug 436296
== anim-targethref-8.svg anim-standard-ref.svg
== anim-targethref-9.svg anim-standard-ref.svg
== anim-text-x-y-dx-dy-01.svg anim-text-x-y-dx-dy-01-ref.svg
== anim-width-done-1a.svg anim-standard-ref.svg
== anim-width-done-1b.svg anim-standard-ref.svg

View File

@ -40,11 +40,13 @@
#define __NS_ISVGGLYPHFRAGMENTLEAF_H__
#include "nsISVGGlyphFragmentNode.h"
#include "nsIDOMSVGLengthList.h"
class nsIDOMSVGPoint;
class nsIDOMSVGRect;
class nsSVGTextPathFrame;
namespace mozilla {
class SVGUserUnitList;
}
class nsISVGGlyphFragmentLeaf : public nsISVGGlyphFragmentNode
{
@ -62,8 +64,7 @@ public:
NS_IMETHOD_(nsSVGTextPathFrame*) FindTextPathParent()=0;
NS_IMETHOD_(PRBool) IsStartOfChunk()=0; // == is new absolutely positioned chunk.
NS_IMETHOD_(already_AddRefed<nsIDOMSVGLengthList>) GetX()=0;
NS_IMETHOD_(already_AddRefed<nsIDOMSVGLengthList>) GetY()=0;
NS_IMETHOD_(void) GetXY(mozilla::SVGUserUnitList *aX, mozilla::SVGUserUnitList *aY)=0;
NS_IMETHOD_(PRUint16) GetTextAnchor()=0;
NS_IMETHOD_(PRBool) IsAbsolutelyPositioned()=0;
};

View File

@ -43,12 +43,15 @@
#include "nsSVGAElement.h"
#include "nsSVGUtils.h"
#include "gfxMatrix.h"
#include "SVGLengthList.h"
// <a> elements can contain text. nsSVGGlyphFrames expect to have
// a class derived from nsSVGTextContainerFrame as a parent. We
// also need something that implements nsISVGGlyphFragmentNode to get
// the text DOM to work.
using namespace mozilla;
typedef nsSVGTSpanFrame nsSVGAFrameBase;
class nsSVGAFrame : public nsSVGAFrameBase
@ -91,7 +94,11 @@ public:
// nsSVGContainerFrame methods:
virtual gfxMatrix GetCanvasTM();
// nsSVGTextContainerFrame methods:
virtual void GetXY(mozilla::SVGUserUnitList *aX, mozilla::SVGUserUnitList *aY);
virtual void GetDxDy(mozilla::SVGUserUnitList *aDx, mozilla::SVGUserUnitList *aDy);
private:
nsCOMPtr<nsIDOMSVGMatrix> mCanvasTM;
};
@ -183,3 +190,20 @@ nsSVGAFrame::GetCanvasTM()
return nsSVGUtils::ConvertSVGMatrixToThebes(mCanvasTM);
}
//----------------------------------------------------------------------
// nsSVGTextContainerFrame methods:
void
nsSVGAFrame::GetXY(SVGUserUnitList *aX, SVGUserUnitList *aY)
{
aX->Clear();
aY->Clear();
}
void
nsSVGAFrame::GetDxDy(SVGUserUnitList *aDx, SVGUserUnitList *aDy)
{
aDx->Clear();
aDy->Clear();
}

View File

@ -40,7 +40,7 @@
#include "nsILookAndFeel.h"
#include "nsTextFragment.h"
#include "nsSVGUtils.h"
#include "nsIDOMSVGLengthList.h"
#include "SVGLengthList.h"
#include "nsIDOMSVGLength.h"
#include "nsIDOMSVGRect.h"
#include "nsIDOMSVGPoint.h"
@ -55,6 +55,8 @@
#include "gfxPlatform.h"
#include "gfxTextRunWordCache.h"
using namespace mozilla;
struct CharacterPosition {
gfxPoint pos;
gfxFloat angle;
@ -655,16 +657,6 @@ nsSVGGlyphFrame::GetCharacterData(nsAString & aCharacterData)
return !characterData.IsEmpty();
}
static PRUint32
GetNumberOfLengthListItems(nsIDOMSVGLengthList *aList)
{
PRUint32 items = 0;
if (aList) {
aList->GetNumberOfItems(&items);
}
return items;
}
static PRUint32
GetNumberOfNumberListItems(nsIDOMSVGNumberList *aList)
{
@ -675,21 +667,6 @@ GetNumberOfNumberListItems(nsIDOMSVGNumberList *aList)
return items;
}
static float
GetLengthListValue(nsIDOMSVGLengthList *aList, PRUint32 aIndex)
{
if (!aList) {
return 0.0f;
}
nsCOMPtr<nsIDOMSVGLength> length;
nsresult rv = aList->GetItem(aIndex, getter_AddRefs(length));
float value = 0.0f;
if (NS_SUCCEEDED(rv)) {
length->GetValue(&value);
}
return value;
}
static float
GetNumberListValue(nsIDOMSVGNumberList *aList, PRUint32 aIndex)
{
@ -714,8 +691,8 @@ nsSVGGlyphFrame::GetCharacterPositions(nsTArray<CharacterPosition>* aCharacterPo
const gfxFloat radPerDeg = M_PI / 180.0;
nsCOMPtr<nsIDOMSVGLengthList> dxList = GetDx();
nsCOMPtr<nsIDOMSVGLengthList> dyList = GetDy();
SVGUserUnitList dxList, dyList;
GetDxDy(&dxList, &dyList);
nsCOMPtr<nsIDOMSVGNumberList> rotateList = GetRotate();
PRBool rotateAllGlyphs = (GetNumberOfNumberListItems(rotateList) == 1);
@ -746,8 +723,8 @@ nsSVGGlyphFrame::GetCharacterPositions(nsTArray<CharacterPosition>* aCharacterPo
gfxFloat halfAdvance =
mTextRun->GetAdvanceWidth(i, 1, nsnull)*aMetricsScale / 2.0;
pos.x += GetLengthListValue(dxList, i) * pathScale;
pos.y += GetLengthListValue(dyList, i) * pathScale;
pos.x += i < dxList.Length() ? dxList[i] * pathScale : 0.0;
pos.y += i < dyList.Length() ? dyList[i] * pathScale : 0.0;
// check that we're within the path boundaries
cp[i].draw = (pos.x + halfAdvance >= 0.0 &&
@ -770,16 +747,16 @@ nsSVGGlyphFrame::GetCharacterPositions(nsTArray<CharacterPosition>* aCharacterPo
return PR_TRUE;
}
nsCOMPtr<nsIDOMSVGLengthList> xList = GetX();
nsCOMPtr<nsIDOMSVGLengthList> yList = GetY();
SVGUserUnitList xList, yList;
GetXY(&xList, &yList);
PRUint32 xListCount = GetNumberOfLengthListItems(xList);
PRUint32 yListCount = GetNumberOfLengthListItems(yList);
PRUint32 xListCount = xList.Length();
PRUint32 yListCount = yList.Length();
if (xListCount <= 1 &&
yListCount <= 1 &&
GetNumberOfLengthListItems(dxList) == 0 &&
GetNumberOfLengthListItems(dyList) == 0 &&
dxList.Length() == 0 &&
dyList.Length() == 0 &&
GetNumberOfNumberListItems(rotateList) == 0) {
// simple text without individual positioning
return PR_TRUE;
@ -797,7 +774,7 @@ nsSVGGlyphFrame::GetCharacterPositions(nsTArray<CharacterPosition>* aCharacterPo
gfxFloat advance = mTextRun->GetAdvanceWidth(i, 1, nsnull)*aMetricsScale;
if (xListCount > 1 && i < xListCount) {
pos.x = GetLengthListValue(xList, i);
pos.x = xList[i];
// apply text-anchor to character
if (anchor == NS_STYLE_TEXT_ANCHOR_MIDDLE)
pos.x -= advance/2.0;
@ -805,10 +782,10 @@ nsSVGGlyphFrame::GetCharacterPositions(nsTArray<CharacterPosition>* aCharacterPo
pos.x -= advance;
}
if (yListCount > 1 && i < yListCount) {
pos.y = GetLengthListValue(yList, i);
pos.y = yList[i];
}
pos.x += GetLengthListValue(dxList, i);
pos.y += GetLengthListValue(dyList, i);
pos.x += i < dxList.Length() ? dxList[i] : 0.0;
pos.y += i < dyList.Length() ? dyList[i] : 0.0;
cp[i].pos = pos;
pos.x += advance;
cp[i].angle = rotateAllGlyphs ? overallGlyphRotation :
@ -828,8 +805,9 @@ nsSVGGlyphFrame::GetSubStringAdvance(PRUint32 aCharnum,
gfxFloat advance =
mTextRun->GetAdvanceWidth(aCharnum, aFragmentChars, nsnull) * aMetricsScale;
nsCOMPtr<nsIDOMSVGLengthList> dxlist = GetDx();
PRUint32 dxcount = GetNumberOfLengthListItems(dxlist);
SVGUserUnitList dxlist, notUsed;
GetDxDy(&dxlist, &notUsed);
PRUint32 dxcount = dxlist.Length();
if (dxcount) {
gfxFloat pathScale = 1.0;
nsSVGTextPathFrame *textPath = FindTextPathParent();
@ -838,7 +816,7 @@ nsSVGGlyphFrame::GetSubStringAdvance(PRUint32 aCharnum,
if (dxcount > aFragmentChars)
dxcount = aFragmentChars;
for (PRUint32 i = aCharnum; i < dxcount; i++) {
advance += GetLengthListValue(dxlist, i) * pathScale;
advance += dxlist[i] * pathScale;
}
}
@ -1044,20 +1022,20 @@ nsSVGGlyphFrame::SetGlyphPosition(gfxPoint *aPosition, PRBool aForceGlobalTransf
if (textPath)
pathScale = textPath->GetPathScale();
nsCOMPtr<nsIDOMSVGLengthList> dxList = GetDx();
nsCOMPtr<nsIDOMSVGLengthList> dyList = GetDy();
SVGUserUnitList dxList, dyList;
GetDxDy(&dxList, &dyList);
PRUint32 dxcount = GetNumberOfLengthListItems(dxList);
PRUint32 dxcount = dxList.Length();
if (dxcount > strLength)
dxcount = strLength;
for (PRUint32 i = 0; i < dxcount; i++) {
aPosition->x += GetLengthListValue(dxList, i) * pathScale;
aPosition->x += dxList[i] * pathScale;
}
PRUint32 dycount = GetNumberOfLengthListItems(dyList);
PRUint32 dycount = dyList.Length();
if (dycount > strLength)
dycount = strLength;
for (PRUint32 i = 0; i < dycount; i++) {
aPosition->y += GetLengthListValue(dyList, i) * pathScale;
aPosition->y += dyList[i] * pathScale;
}
}
@ -1173,44 +1151,22 @@ nsSVGGlyphFrame::IsStartOfChunk()
return PR_FALSE;
}
NS_IMETHODIMP_(already_AddRefed<nsIDOMSVGLengthList>)
nsSVGGlyphFrame::GetX()
NS_IMETHODIMP_(void)
nsSVGGlyphFrame::GetXY(SVGUserUnitList *aX, SVGUserUnitList *aY)
{
nsSVGTextContainerFrame *containerFrame;
containerFrame = static_cast<nsSVGTextContainerFrame *>(mParent);
if (containerFrame)
return containerFrame->GetX();
return nsnull;
containerFrame->GetXY(aX, aY);
}
NS_IMETHODIMP_(already_AddRefed<nsIDOMSVGLengthList>)
nsSVGGlyphFrame::GetY()
void
nsSVGGlyphFrame::GetDxDy(SVGUserUnitList *aDx, SVGUserUnitList *aDy)
{
nsSVGTextContainerFrame *containerFrame;
containerFrame = static_cast<nsSVGTextContainerFrame *>(mParent);
if (containerFrame)
return containerFrame->GetY();
return nsnull;
}
already_AddRefed<nsIDOMSVGLengthList>
nsSVGGlyphFrame::GetDx()
{
nsSVGTextContainerFrame *containerFrame;
containerFrame = static_cast<nsSVGTextContainerFrame *>(mParent);
if (containerFrame)
return containerFrame->GetDx();
return nsnull;
}
already_AddRefed<nsIDOMSVGLengthList>
nsSVGGlyphFrame::GetDy()
{
nsSVGTextContainerFrame *containerFrame;
containerFrame = static_cast<nsSVGTextContainerFrame *>(mParent);
if (containerFrame)
return containerFrame->GetDy();
return nsnull;
containerFrame->GetDxDy(aDx, aDy);
}
already_AddRefed<nsIDOMSVGNumberList>

View File

@ -149,8 +149,7 @@ public:
NS_IMETHOD_(nsSVGTextPathFrame*) FindTextPathParent();
NS_IMETHOD_(PRBool) IsStartOfChunk(); // == is new absolutely positioned chunk.
NS_IMETHOD_(already_AddRefed<nsIDOMSVGLengthList>) GetX();
NS_IMETHOD_(already_AddRefed<nsIDOMSVGLengthList>) GetY();
NS_IMETHOD_(void) GetXY(SVGUserUnitList *aX, SVGUserUnitList *aY);
NS_IMETHOD_(PRUint16) GetTextAnchor();
NS_IMETHOD_(PRBool) IsAbsolutelyPositioned();
@ -207,8 +206,7 @@ protected:
float aMetricsScale);
gfxFloat GetBaselineOffset(float aMetricsScale);
already_AddRefed<nsIDOMSVGLengthList> GetDx();
already_AddRefed<nsIDOMSVGLengthList> GetDy();
virtual void GetDxDy(SVGUserUnitList *aDx, SVGUserUnitList *aDy);
already_AddRefed<nsIDOMSVGNumberList> GetRotate();
// Used to support GetBBoxContribution by making GetConvasTM use this as the

View File

@ -43,6 +43,9 @@
#include "nsIDOMSVGAnimatedNumberList.h"
#include "nsISVGGlyphFragmentLeaf.h"
#include "nsDOMError.h"
#include "SVGLengthList.h"
using namespace mozilla;
//----------------------------------------------------------------------
// nsQueryFrame methods
@ -61,68 +64,21 @@ nsSVGTextContainerFrame::NotifyGlyphMetricsChange()
textFrame->NotifyGlyphMetricsChange();
}
already_AddRefed<nsIDOMSVGLengthList>
nsSVGTextContainerFrame::GetX()
void
nsSVGTextContainerFrame::GetXY(SVGUserUnitList *aX, SVGUserUnitList *aY)
{
nsCOMPtr<nsIDOMSVGTextPositioningElement> tpElement =
do_QueryInterface(mContent);
if (!tpElement)
return nsnull;
nsCOMPtr<nsIDOMSVGAnimatedLengthList> animLengthList;
tpElement->GetX(getter_AddRefs(animLengthList));
nsIDOMSVGLengthList *retval;
animLengthList->GetAnimVal(&retval);
return retval;
static_cast<nsSVGElement*>(mContent)->
GetAnimatedLengthListValues(aX, aY, nsnull);
}
already_AddRefed<nsIDOMSVGLengthList>
nsSVGTextContainerFrame::GetY()
void
nsSVGTextContainerFrame::GetDxDy(SVGUserUnitList *aDx, SVGUserUnitList *aDy)
{
nsCOMPtr<nsIDOMSVGTextPositioningElement> tpElement =
do_QueryInterface(mContent);
if (!tpElement)
return nsnull;
nsCOMPtr<nsIDOMSVGAnimatedLengthList> animLengthList;
tpElement->GetY(getter_AddRefs(animLengthList));
nsIDOMSVGLengthList *retval;
animLengthList->GetAnimVal(&retval);
return retval;
}
already_AddRefed<nsIDOMSVGLengthList>
nsSVGTextContainerFrame::GetDx()
{
nsCOMPtr<nsIDOMSVGTextPositioningElement> tpElement =
do_QueryInterface(mContent);
if (!tpElement)
return nsnull;
nsCOMPtr<nsIDOMSVGAnimatedLengthList> animLengthList;
tpElement->GetDx(getter_AddRefs(animLengthList));
nsIDOMSVGLengthList *retval;
animLengthList->GetAnimVal(&retval);
return retval;
}
already_AddRefed<nsIDOMSVGLengthList>
nsSVGTextContainerFrame::GetDy()
{
nsCOMPtr<nsIDOMSVGTextPositioningElement> tpElement =
do_QueryInterface(mContent);
if (!tpElement)
return nsnull;
nsCOMPtr<nsIDOMSVGAnimatedLengthList> animLengthList;
tpElement->GetDy(getter_AddRefs(animLengthList));
nsIDOMSVGLengthList *retval;
animLengthList->GetAnimVal(&retval);
return retval;
// SVGUserUnitList is lazy, so there's little overhead it getting the x
// and y lists even though we ignore them.
SVGUserUnitList xLengthList, yLengthList;
static_cast<nsSVGElement*>(mContent)->
GetAnimatedLengthListValues(&xLengthList, &yLengthList, aDx, aDy, nsnull);
}
already_AddRefed<nsIDOMSVGNumberList>

View File

@ -38,13 +38,14 @@
#define NS_SVGTEXTCONTAINERFRAME_H
#include "nsSVGContainerFrame.h"
#include "nsIDOMSVGLengthList.h"
#include "nsIDOMSVGNumberList.h"
class nsISVGGlyphFragmentNode;
class nsISVGGlyphFragmentLeaf;
class nsSVGTextFrame;
namespace mozilla {
class SVGUserUnitList;
}
class nsSVGTextContainerFrame : public nsSVGDisplayContainerFrame
{
@ -53,10 +54,8 @@ public:
nsSVGDisplayContainerFrame(aContext) {}
void NotifyGlyphMetricsChange();
virtual already_AddRefed<nsIDOMSVGLengthList> GetX();
virtual already_AddRefed<nsIDOMSVGLengthList> GetY();
virtual already_AddRefed<nsIDOMSVGLengthList> GetDx();
virtual already_AddRefed<nsIDOMSVGLengthList> GetDy();
virtual void GetXY(mozilla::SVGUserUnitList *aX, mozilla::SVGUserUnitList *aY);
virtual void GetDxDy(mozilla::SVGUserUnitList *aDx, mozilla::SVGUserUnitList *aDy);
virtual already_AddRefed<nsIDOMSVGNumberList> GetRotate();
public:

View File

@ -39,7 +39,7 @@
#include "nsIDOMSVGTextElement.h"
#include "nsSVGTextFrame.h"
#include "nsWeakReference.h"
#include "nsIDOMSVGLengthList.h"
#include "SVGLengthList.h"
#include "nsIDOMSVGLength.h"
#include "nsIDOMSVGAnimatedNumber.h"
#include "nsISVGGlyphFragmentNode.h"
@ -54,6 +54,8 @@
#include "nsSVGUtils.h"
#include "nsSVGGraphicElement.h"
using namespace mozilla;
//----------------------------------------------------------------------
// Implementation
@ -291,23 +293,6 @@ nsSVGTextFrame::NotifyGlyphMetricsChange()
UpdateGlyphPositioning(PR_FALSE);
}
static void
GetSingleValue(nsIDOMSVGLengthList *list, gfxFloat *val)
{
if (!list)
return;
PRUint32 count = 0;
list->GetNumberOfItems(&count);
if (count) {
nsCOMPtr<nsIDOMSVGLength> length;
list->GetItem(0, getter_AddRefs(length));
float value;
length->GetValue(&value);
*val = value;
}
}
void
nsSVGTextFrame::UpdateGlyphPositioning(PRBool aForceGlobalTransform)
{
@ -330,25 +315,16 @@ nsSVGTextFrame::UpdateGlyphPositioning(PRBool aForceGlobalTransform)
gfxPoint ctp(0.0, 0.0);
{
nsCOMPtr<nsIDOMSVGLengthList> list = GetX();
GetSingleValue(list, &ctp.x);
}
{
nsCOMPtr<nsIDOMSVGLengthList> list = GetY();
GetSingleValue(list, &ctp.y);
}
SVGUserUnitList xLengthList, yLengthList;
GetXY(&xLengthList, &yLengthList);
if (xLengthList.Length() > 0) ctp.x = xLengthList[0];
if (yLengthList.Length() > 0) ctp.y = yLengthList[0];
// loop over chunks
while (firstFragment) {
{
nsCOMPtr<nsIDOMSVGLengthList> list = firstFragment->GetX();
GetSingleValue(list, &ctp.x);
}
{
nsCOMPtr<nsIDOMSVGLengthList> list = firstFragment->GetY();
GetSingleValue(list, &ctp.y);
}
firstFragment->GetXY(&xLengthList, &yLengthList);
if (xLengthList.Length() > 0) ctp.x = xLengthList[0];
if (yLengthList.Length() > 0) ctp.y = yLengthList[0];
// check for startOffset on textPath
nsSVGTextPathFrame *textPath = firstFragment->FindTextPathParent();

View File

@ -43,6 +43,8 @@
#include "nsSVGPathElement.h"
#include "nsSVGTextPathElement.h"
using namespace mozilla;
//----------------------------------------------------------------------
// Implementation
@ -83,29 +85,20 @@ nsSVGTextPathFrame::GetType() const
return nsGkAtoms::svgTextPathFrame;
}
already_AddRefed<nsIDOMSVGLengthList>
nsSVGTextPathFrame::GetX()
void
nsSVGTextPathFrame::GetXY(SVGUserUnitList *aX, SVGUserUnitList *aY)
{
return nsnull;
// 'x' and 'y' don't apply to 'textPath'
aX->Clear();
aY->Clear();
}
already_AddRefed<nsIDOMSVGLengthList>
nsSVGTextPathFrame::GetY()
void
nsSVGTextPathFrame::GetDxDy(SVGUserUnitList *aDx, SVGUserUnitList *aDy)
{
return nsnull;
}
already_AddRefed<nsIDOMSVGLengthList>
nsSVGTextPathFrame::GetDx()
{
return nsnull;
}
already_AddRefed<nsIDOMSVGLengthList>
nsSVGTextPathFrame::GetDy()
{
return nsnull;
// 'dx' and 'dy' don't apply to 'textPath'
aDx->Clear();
aDy->Clear();
}
already_AddRefed<nsIDOMSVGNumberList>

View File

@ -38,7 +38,7 @@
#define NSSVGTEXTPATHFRAME_H
#include "nsSVGTSpanFrame.h"
#include "nsSVGLengthList.h"
#include "SVGLengthList.h"
#include "nsSVGNumberList.h"
typedef nsSVGTSpanFrame nsSVGTextPathFrameBase;
@ -85,10 +85,8 @@ public:
gfxFloat GetPathScale();
protected:
virtual already_AddRefed<nsIDOMSVGLengthList> GetX();
virtual already_AddRefed<nsIDOMSVGLengthList> GetY();
virtual already_AddRefed<nsIDOMSVGLengthList> GetDx();
virtual already_AddRefed<nsIDOMSVGLengthList> GetDy();
virtual void GetXY(SVGUserUnitList *aX, SVGUserUnitList *aY);
virtual void GetDxDy(SVGUserUnitList *aDx, SVGUserUnitList *aDy);
virtual already_AddRefed<nsIDOMSVGNumberList> GetRotate();
private:

View File

@ -264,6 +264,7 @@ nsSVGUtils::GetFontSize(Element *aElement)
nsComputedDOMStyle::GetStyleContextForElementNoFlush(aElement,
nsnull, nsnull);
if (!styleContext) {
// ReportToConsole
NS_WARNING("Couldn't get style context for content in GetFontStyle");
return 1.0f;
}
@ -301,6 +302,7 @@ nsSVGUtils::GetFontXHeight(Element *aElement)
nsComputedDOMStyle::GetStyleContextForElementNoFlush(aElement,
nsnull, nsnull);
if (!styleContext) {
// ReportToConsole
NS_WARNING("Couldn't get style context for content in GetFontStyle");
return 1.0f;
}
@ -328,6 +330,7 @@ nsSVGUtils::GetFontXHeight(nsStyleContext *aStyleContext)
getter_AddRefs(fontMetrics));
if (!fontMetrics) {
// ReportToConsole
NS_WARNING("no FontMetrics in GetFontXHeight()");
return 1.0f;
}

View File

@ -213,13 +213,21 @@ public:
static mozilla::dom::Element *GetParentElement(nsIContent *aContent);
/*
* Get a font-size (em) of an nsIContent
* Get the number of CSS px (user units) per em (i.e. the em-height in user
* units) for an nsIContent
*
* XXX document the conditions under which these may fail, and what they
* return in those cases.
*/
static float GetFontSize(mozilla::dom::Element *aElement);
static float GetFontSize(nsIFrame *aFrame);
static float GetFontSize(nsStyleContext *aStyleContext);
/*
* Get an x-height of of an nsIContent
* Get the number of CSS px (user units) per ex (i.e. the x-height in user
* units) for an nsIContent
*
* XXX document the conditions under which these may fail, and what they
* return in those cases.
*/
static float GetFontXHeight(mozilla::dom::Element *aElement);
static float GetFontXHeight(nsIFrame *aFrame);