diff options
author | Matt A. Tobin <mattatobin@localhost.localdomain> | 2018-02-02 04:16:08 -0500 |
---|---|---|
committer | Matt A. Tobin <mattatobin@localhost.localdomain> | 2018-02-02 04:16:08 -0500 |
commit | 5f8de423f190bbb79a62f804151bc24824fa32d8 (patch) | |
tree | 10027f336435511475e392454359edea8e25895d /dom/svg | |
parent | 49ee0794b5d912db1f95dce6eb52d781dc210db5 (diff) | |
download | uxp-5f8de423f190bbb79a62f804151bc24824fa32d8.tar.gz |
Add m-esr52 at 52.6.0
Diffstat (limited to 'dom/svg')
491 files changed, 67264 insertions, 0 deletions
diff --git a/dom/svg/DOMSVGAnimatedLengthList.cpp b/dom/svg/DOMSVGAnimatedLengthList.cpp new file mode 100644 index 0000000000..955d59602d --- /dev/null +++ b/dom/svg/DOMSVGAnimatedLengthList.cpp @@ -0,0 +1,143 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "DOMSVGAnimatedLengthList.h" +#include "DOMSVGLengthList.h" +#include "SVGAnimatedLengthList.h" +#include "nsSVGElement.h" +#include "nsCOMPtr.h" +#include "nsSVGAttrTearoffTable.h" +#include "mozilla/dom/SVGAnimatedLengthListBinding.h" + +// See the architecture comment in this file's header. + +namespace mozilla { + +static inline +nsSVGAttrTearoffTable<SVGAnimatedLengthList, DOMSVGAnimatedLengthList>& +SVGAnimatedLengthListTearoffTable() +{ + static nsSVGAttrTearoffTable<SVGAnimatedLengthList, DOMSVGAnimatedLengthList> + sSVGAnimatedLengthListTearoffTable; + return sSVGAnimatedLengthListTearoffTable; +} + +NS_SVG_VAL_IMPL_CYCLE_COLLECTION_WRAPPERCACHED(DOMSVGAnimatedLengthList, mElement) + +NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(DOMSVGAnimatedLengthList, AddRef) +NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(DOMSVGAnimatedLengthList, Release) + +JSObject* +DOMSVGAnimatedLengthList::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) +{ + return dom::SVGAnimatedLengthListBinding::Wrap(aCx, this, aGivenProto); +} + +already_AddRefed<DOMSVGLengthList> +DOMSVGAnimatedLengthList::BaseVal() +{ + if (!mBaseVal) { + mBaseVal = new DOMSVGLengthList(this, InternalAList().GetBaseValue()); + } + RefPtr<DOMSVGLengthList> baseVal = mBaseVal; + return baseVal.forget(); +} + +already_AddRefed<DOMSVGLengthList> +DOMSVGAnimatedLengthList::AnimVal() +{ + if (!mAnimVal) { + mAnimVal = new DOMSVGLengthList(this, InternalAList().GetAnimValue()); + } + RefPtr<DOMSVGLengthList> animVal = mAnimVal; + return animVal.forget(); +} + +/* static */ already_AddRefed<DOMSVGAnimatedLengthList> +DOMSVGAnimatedLengthList::GetDOMWrapper(SVGAnimatedLengthList *aList, + nsSVGElement *aElement, + uint8_t aAttrEnum, + uint8_t aAxis) +{ + RefPtr<DOMSVGAnimatedLengthList> wrapper = + SVGAnimatedLengthListTearoffTable().GetTearoff(aList); + if (!wrapper) { + wrapper = new DOMSVGAnimatedLengthList(aElement, aAttrEnum, aAxis); + SVGAnimatedLengthListTearoffTable().AddTearoff(aList, wrapper); + } + return wrapper.forget(); +} + +/* static */ DOMSVGAnimatedLengthList* +DOMSVGAnimatedLengthList::GetDOMWrapperIfExists(SVGAnimatedLengthList *aList) +{ + return SVGAnimatedLengthListTearoffTable().GetTearoff(aList); +} + +DOMSVGAnimatedLengthList::~DOMSVGAnimatedLengthList() +{ + // Script no longer has any references to us, to our base/animVal objects, or + // to any of their list items. + SVGAnimatedLengthListTearoffTable().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)!! + + RefPtr<DOMSVGAnimatedLengthList> kungFuDeathGrip; + if (mBaseVal) { + if (aNewValue.Length() < mBaseVal->LengthNoFlush()) { + // InternalListLengthWillChange might clear last reference to |this|. + // Retain a temporary reference to keep from dying before returning. + kungFuDeathGrip = this; + } + 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()); + } +} + +bool +DOMSVGAnimatedLengthList::IsAnimating() const +{ + return InternalAList().IsAnimating(); +} + +SVGAnimatedLengthList& +DOMSVGAnimatedLengthList::InternalAList() +{ + return *mElement->GetAnimatedLengthList(mAttrEnum); +} + +const SVGAnimatedLengthList& +DOMSVGAnimatedLengthList::InternalAList() const +{ + return *mElement->GetAnimatedLengthList(mAttrEnum); +} + +} // namespace mozilla diff --git a/dom/svg/DOMSVGAnimatedLengthList.h b/dom/svg/DOMSVGAnimatedLengthList.h new file mode 100644 index 0000000000..cb502058d8 --- /dev/null +++ b/dom/svg/DOMSVGAnimatedLengthList.h @@ -0,0 +1,204 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef MOZILLA_DOMSVGANIMATEDLENGTHLIST_H__ +#define MOZILLA_DOMSVGANIMATEDLENGTHLIST_H__ + +#include "nsCOMPtr.h" +#include "nsCycleCollectionParticipant.h" +#include "nsSVGElement.h" +#include "mozilla/Attributes.h" + +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 + * + * Rationale: + * + * The following three paragraphs explain the main three requirements that must + * be met by any design. These are followed by an explanation of the rationale + * 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 final : public nsWrapperCache +{ + friend class DOMSVGLengthList; + +public: + NS_INLINE_DECL_CYCLE_COLLECTING_NATIVE_REFCOUNTING(DOMSVGAnimatedLengthList) + NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_NATIVE_CLASS(DOMSVGAnimatedLengthList) + + /** + * 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, + uint8_t aAttrEnum, + uint8_t aAxis); + + /** + * This method returns the DOMSVGAnimatedLengthList wrapper for an internal + * SVGAnimatedLengthList object if it currently has a wrapper. If it does + * not, then nullptr 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). + */ + bool IsAnimating() const; + + // WebIDL + nsSVGElement* GetParentObject() const { return mElement; } + virtual JSObject* WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override; + // These aren't weak refs because mBaseVal and mAnimVal are weak + already_AddRefed<DOMSVGLengthList> BaseVal(); + already_AddRefed<DOMSVGLengthList> AnimVal(); + +private: + + /** + * Only our static GetDOMWrapper() factory method may create objects of our + * type. + */ + DOMSVGAnimatedLengthList(nsSVGElement *aElement, uint8_t aAttrEnum, uint8_t aAxis) + : mBaseVal(nullptr) + , mAnimVal(nullptr) + , 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. + RefPtr<nsSVGElement> mElement; + + uint8_t mAttrEnum; + uint8_t mAxis; +}; + +} // namespace mozilla + +#endif // MOZILLA_DOMSVGANIMATEDLENGTHLIST_H__ diff --git a/dom/svg/DOMSVGAnimatedNumberList.cpp b/dom/svg/DOMSVGAnimatedNumberList.cpp new file mode 100644 index 0000000000..938216f2a8 --- /dev/null +++ b/dom/svg/DOMSVGAnimatedNumberList.cpp @@ -0,0 +1,147 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "DOMSVGAnimatedNumberList.h" +#include "DOMSVGNumberList.h" +#include "SVGAnimatedNumberList.h" +#include "nsSVGElement.h" +#include "nsCOMPtr.h" +#include "nsSVGAttrTearoffTable.h" +#include "mozilla/dom/SVGAnimatedNumberListBinding.h" + +// See the architecture comment in this file's header. + +namespace mozilla { + + static inline +nsSVGAttrTearoffTable<SVGAnimatedNumberList, DOMSVGAnimatedNumberList>& +SVGAnimatedNumberListTearoffTable() +{ + static nsSVGAttrTearoffTable<SVGAnimatedNumberList, DOMSVGAnimatedNumberList> + sSVGAnimatedNumberListTearoffTable; + return sSVGAnimatedNumberListTearoffTable; +} + +NS_SVG_VAL_IMPL_CYCLE_COLLECTION_WRAPPERCACHED(DOMSVGAnimatedNumberList, mElement) + +NS_IMPL_CYCLE_COLLECTING_ADDREF(DOMSVGAnimatedNumberList) +NS_IMPL_CYCLE_COLLECTING_RELEASE(DOMSVGAnimatedNumberList) + +NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(DOMSVGAnimatedNumberList) + NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY + NS_INTERFACE_MAP_ENTRY(nsISupports) +NS_INTERFACE_MAP_END + +JSObject* +DOMSVGAnimatedNumberList::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) +{ + return mozilla::dom::SVGAnimatedNumberListBinding::Wrap(aCx, this, aGivenProto); +} + +already_AddRefed<DOMSVGNumberList> +DOMSVGAnimatedNumberList::BaseVal() +{ + if (!mBaseVal) { + mBaseVal = new DOMSVGNumberList(this, InternalAList().GetBaseValue()); + } + RefPtr<DOMSVGNumberList> baseVal = mBaseVal; + return baseVal.forget(); +} + +already_AddRefed<DOMSVGNumberList> +DOMSVGAnimatedNumberList::AnimVal() +{ + if (!mAnimVal) { + mAnimVal = new DOMSVGNumberList(this, InternalAList().GetAnimValue()); + } + RefPtr<DOMSVGNumberList> animVal = mAnimVal; + return animVal.forget(); +} + +/* static */ already_AddRefed<DOMSVGAnimatedNumberList> +DOMSVGAnimatedNumberList::GetDOMWrapper(SVGAnimatedNumberList *aList, + nsSVGElement *aElement, + uint8_t aAttrEnum) +{ + RefPtr<DOMSVGAnimatedNumberList> wrapper = + SVGAnimatedNumberListTearoffTable().GetTearoff(aList); + if (!wrapper) { + wrapper = new DOMSVGAnimatedNumberList(aElement, aAttrEnum); + SVGAnimatedNumberListTearoffTable().AddTearoff(aList, wrapper); + } + return wrapper.forget(); +} + +/* static */ DOMSVGAnimatedNumberList* +DOMSVGAnimatedNumberList::GetDOMWrapperIfExists(SVGAnimatedNumberList *aList) +{ + return SVGAnimatedNumberListTearoffTable().GetTearoff(aList); +} + +DOMSVGAnimatedNumberList::~DOMSVGAnimatedNumberList() +{ + // Script no longer has any references to us, to our base/animVal objects, or + // to any of their list items. + SVGAnimatedNumberListTearoffTable().RemoveTearoff(&InternalAList()); +} + +void +DOMSVGAnimatedNumberList::InternalBaseValListWillChangeTo(const SVGNumberList& 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)!! + + RefPtr<DOMSVGAnimatedNumberList> kungFuDeathGrip; + if (mBaseVal) { + if (aNewValue.Length() < mBaseVal->LengthNoFlush()) { + // InternalListLengthWillChange might clear last reference to |this|. + // Retain a temporary reference to keep from dying before returning. + kungFuDeathGrip = this; + } + 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 +DOMSVGAnimatedNumberList::InternalAnimValListWillChangeTo(const SVGNumberList& aNewValue) +{ + if (mAnimVal) { + mAnimVal->InternalListLengthWillChange(aNewValue.Length()); + } +} + +bool +DOMSVGAnimatedNumberList::IsAnimating() const +{ + return InternalAList().IsAnimating(); +} + +SVGAnimatedNumberList& +DOMSVGAnimatedNumberList::InternalAList() +{ + return *mElement->GetAnimatedNumberList(mAttrEnum); +} + +const SVGAnimatedNumberList& +DOMSVGAnimatedNumberList::InternalAList() const +{ + return *mElement->GetAnimatedNumberList(mAttrEnum); +} + +} // namespace mozilla diff --git a/dom/svg/DOMSVGAnimatedNumberList.h b/dom/svg/DOMSVGAnimatedNumberList.h new file mode 100644 index 0000000000..b26076634c --- /dev/null +++ b/dom/svg/DOMSVGAnimatedNumberList.h @@ -0,0 +1,133 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef MOZILLA_DOMSVGANIMATEDNUMBERLIST_H__ +#define MOZILLA_DOMSVGANIMATEDNUMBERLIST_H__ + +#include "nsCOMPtr.h" +#include "nsCycleCollectionParticipant.h" +#include "nsSVGElement.h" +#include "nsWrapperCache.h" +#include "mozilla/Attributes.h" + +namespace mozilla { + +class DOMSVGNumberList; +class SVGAnimatedNumberList; +class SVGNumberList; + +/** + * Class DOMSVGAnimatedNumberList + * + * This class is used to create the DOM tearoff objects that wrap internal + * SVGAnimatedNumberList objects. + * + * See the architecture comment in DOMSVGAnimatedLengthList.h (that's + * LENGTH list). The comment for that class largly applies to this one too + * and will go a long way to helping you understand the architecture here. + * + * This class is strongly intertwined with DOMSVGNumberList and DOMSVGNumber. + * Our DOMSVGNumberList base and anim vals are friends and take care of nulling + * out our pointers to them when they die (making our pointers to them true + * weak refs). + */ +class DOMSVGAnimatedNumberList final : public nsISupports, + public nsWrapperCache +{ + friend class DOMSVGNumberList; + +public: + NS_DECL_CYCLE_COLLECTING_ISUPPORTS + NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(DOMSVGAnimatedNumberList) + + /** + * Factory method to create and return a DOMSVGAnimatedNumberList wrapper + * for a given internal SVGAnimatedNumberList object. The factory takes care + * of caching the object that it returns so that the same object can be + * returned for the given SVGAnimatedNumberList 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 SVGAnimatedNumberList will naturally result in a new + * DOMSVGAnimatedNumberList being returned. + */ + static already_AddRefed<DOMSVGAnimatedNumberList> + GetDOMWrapper(SVGAnimatedNumberList *aList, + nsSVGElement *aElement, + uint8_t aAttrEnum); + + /** + * This method returns the DOMSVGAnimatedNumberList wrapper for an internal + * SVGAnimatedNumberList object if it currently has a wrapper. If it does + * not, then nullptr is returned. + */ + static DOMSVGAnimatedNumberList* + GetDOMWrapperIfExists(SVGAnimatedNumberList *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 SVGNumberList& aNewValue); + void InternalAnimValListWillChangeTo(const SVGNumberList& aNewValue); + + /** + * Returns true if our attribute is animating (in which case our animVal is + * not simply a mirror of our baseVal). + */ + bool IsAnimating() const; + + // WebIDL + nsSVGElement* GetParentObject() const { return mElement; } + virtual JSObject* WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override; + // These aren't weak refs because mBaseVal and mAnimVal are weak + already_AddRefed<DOMSVGNumberList> BaseVal(); + already_AddRefed<DOMSVGNumberList> AnimVal(); + +private: + + /** + * Only our static GetDOMWrapper() factory method may create objects of our + * type. + */ + DOMSVGAnimatedNumberList(nsSVGElement *aElement, uint8_t aAttrEnum) + : mBaseVal(nullptr) + , mAnimVal(nullptr) + , mElement(aElement) + , mAttrEnum(aAttrEnum) + { + } + + ~DOMSVGAnimatedNumberList(); + + /// Get a reference to this DOM wrapper object's internal counterpart. + SVGAnimatedNumberList& InternalAList(); + const SVGAnimatedNumberList& InternalAList() const; + + // Weak refs to our DOMSVGNumberList baseVal/animVal objects. These objects + // are friends and take care of clearing these pointers when they die, making + // these true weak references. + DOMSVGNumberList *mBaseVal; + DOMSVGNumberList *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. + RefPtr<nsSVGElement> mElement; + + uint8_t mAttrEnum; +}; + +} // namespace mozilla + +#endif // MOZILLA_DOMSVGANIMATEDNUMBERLIST_H__ diff --git a/dom/svg/DOMSVGLength.cpp b/dom/svg/DOMSVGLength.cpp new file mode 100644 index 0000000000..574617045e --- /dev/null +++ b/dom/svg/DOMSVGLength.cpp @@ -0,0 +1,592 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "DOMSVGLength.h" +#include "DOMSVGLengthList.h" +#include "DOMSVGAnimatedLengthList.h" +#include "SVGLength.h" +#include "SVGAnimatedLengthList.h" +#include "nsSVGElement.h" +#include "nsSVGLength2.h" +#include "nsIDOMSVGLength.h" +#include "nsError.h" +#include "nsMathUtils.h" +#include "mozilla/dom/SVGLengthBinding.h" +#include "mozilla/FloatingPoint.h" +#include "nsSVGAttrTearoffTable.h" + +// See the architecture comment in DOMSVGAnimatedLengthList.h. + +namespace mozilla { + +static nsSVGAttrTearoffTable<nsSVGLength2, DOMSVGLength> + sBaseSVGLengthTearOffTable, + sAnimSVGLengthTearOffTable; + +// We could use NS_IMPL_CYCLE_COLLECTION(, 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) + tmp->CleanupWeakRefs(); + tmp->mVal = nullptr; // (owned by mSVGElement, which we drop our ref to here) +NS_IMPL_CYCLE_COLLECTION_UNLINK(mList) +NS_IMPL_CYCLE_COLLECTION_UNLINK(mSVGElement) +NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER +NS_IMPL_CYCLE_COLLECTION_UNLINK_END + +NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(DOMSVGLength) +NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mList) +NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mSVGElement) +NS_IMPL_CYCLE_COLLECTION_TRAVERSE_SCRIPT_OBJECTS +NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END + +NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(DOMSVGLength) +NS_IMPL_CYCLE_COLLECTION_TRACE_PRESERVED_WRAPPER +NS_IMPL_CYCLE_COLLECTION_TRACE_END + +NS_IMPL_CYCLE_COLLECTING_ADDREF(DOMSVGLength) +NS_IMPL_CYCLE_COLLECTING_RELEASE(DOMSVGLength) + +NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(DOMSVGLength) + NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY + NS_INTERFACE_MAP_ENTRY(mozilla::DOMSVGLength) // pseudo-interface + NS_INTERFACE_MAP_ENTRY(nsIDOMSVGLength) + NS_INTERFACE_MAP_ENTRY(nsISupports) +NS_INTERFACE_MAP_END + +//---------------------------------------------------------------------- +// Helper class: AutoChangeLengthNotifier +// Stack-based helper class to pair calls to WillChangeLengthList and +// DidChangeLengthList. +class MOZ_RAII AutoChangeLengthNotifier +{ +public: + explicit AutoChangeLengthNotifier(DOMSVGLength* aLength MOZ_GUARD_OBJECT_NOTIFIER_PARAM) + : mLength(aLength) + { + MOZ_GUARD_OBJECT_NOTIFIER_INIT; + MOZ_ASSERT(mLength, "Expecting non-null length"); + MOZ_ASSERT(mLength->HasOwner(), + "Expecting list to have an owner for notification"); + mEmptyOrOldValue = + mLength->Element()->WillChangeLengthList(mLength->mAttrEnum); + } + + ~AutoChangeLengthNotifier() + { + mLength->Element()->DidChangeLengthList(mLength->mAttrEnum, + mEmptyOrOldValue); + if (mLength->mList->IsAnimating()) { + mLength->Element()->AnimationNeedsResample(); + } + } + +private: + DOMSVGLength* const mLength; + nsAttrValue mEmptyOrOldValue; + MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER +}; + +DOMSVGLength::DOMSVGLength(DOMSVGLengthList *aList, + uint8_t aAttrEnum, + uint32_t aListIndex, + bool aIsAnimValItem) + : mList(aList) + , mListIndex(aListIndex) + , mAttrEnum(aAttrEnum) + , mIsAnimValItem(aIsAnimValItem) + , mUnit(nsIDOMSVGLength::SVG_LENGTHTYPE_NUMBER) + , mValue(0.0f) + , mVal(nullptr) +{ + // These shifts are in sync with the members in the header. + MOZ_ASSERT(aList && + aAttrEnum < (1 << 4) && + aListIndex <= MaxListIndex(), + "bad arg"); + + MOZ_ASSERT(IndexIsValid(), "Bad index for DOMSVGNumber!"); +} + +DOMSVGLength::DOMSVGLength() + : mList(nullptr) + , mListIndex(0) + , mAttrEnum(0) + , mIsAnimValItem(false) + , mUnit(nsIDOMSVGLength::SVG_LENGTHTYPE_NUMBER) + , mValue(0.0f) + , mVal(nullptr) +{ +} + +DOMSVGLength::DOMSVGLength(nsSVGLength2* aVal, nsSVGElement* aSVGElement, + bool aAnimVal) + : mList(nullptr) + , mListIndex(0) + , mAttrEnum(0) + , mIsAnimValItem(aAnimVal) + , mUnit(nsIDOMSVGLength::SVG_LENGTHTYPE_NUMBER) + , mValue(0.0f) + , mVal(aVal) + , mSVGElement(aSVGElement) +{ +} + +void +DOMSVGLength::CleanupWeakRefs() +{ + // Our mList's weak ref to us must be nulled out when we die (or when we're + // cycle collected), so we that don't leave behind a pointer to + // free / soon-to-be-free memory. + if (mList) { + MOZ_ASSERT(mList->mItems[mListIndex] == this, + "Clearing out the wrong list index...?"); + mList->mItems[mListIndex] = nullptr; + } + + // Similarly, we must update the tearoff table to remove its (non-owning) + // pointer to mVal. + if (mVal) { + auto& table = mIsAnimValItem ? + sAnimSVGLengthTearOffTable : sBaseSVGLengthTearOffTable; + table.RemoveTearoff(mVal); + } +} + +DOMSVGLength::~DOMSVGLength() +{ + CleanupWeakRefs(); +} + +already_AddRefed<DOMSVGLength> +DOMSVGLength::GetTearOff(nsSVGLength2* aVal, nsSVGElement* aSVGElement, + bool aAnimVal) +{ + auto& table = aAnimVal ? sAnimSVGLengthTearOffTable : sBaseSVGLengthTearOffTable; + RefPtr<DOMSVGLength> domLength = table.GetTearoff(aVal); + if (!domLength) { + domLength = new DOMSVGLength(aVal, aSVGElement, aAnimVal); + table.AddTearoff(aVal, domLength); + } + + return domLength.forget(); +} + +DOMSVGLength* +DOMSVGLength::Copy() +{ + NS_ASSERTION(HasOwner() || IsReflectingAttribute(), "unexpected caller"); + DOMSVGLength *copy = new DOMSVGLength(); + uint16_t unit; + float value; + if (mVal) { + unit = mVal->mSpecifiedUnitType; + value = mIsAnimValItem ? mVal->mAnimVal : mVal->mBaseVal; + } else { + SVGLength &length = InternalItem(); + unit = length.GetUnit(); + value = length.GetValueInCurrentUnits(); + } + copy->NewValueSpecifiedUnits(unit, value); + return copy; +} + +uint16_t +DOMSVGLength::UnitType() +{ + if (mVal) { + if (mIsAnimValItem) { + mSVGElement->FlushAnimations(); + } + return mVal->mSpecifiedUnitType; + } + + if (mIsAnimValItem && HasOwner()) { + Element()->FlushAnimations(); // May make HasOwner() == false + } + return HasOwner() ? InternalItem().GetUnit() : mUnit; +} + +NS_IMETHODIMP +DOMSVGLength::GetUnitType(uint16_t* aUnit) +{ + *aUnit = UnitType(); + return NS_OK; +} + +float +DOMSVGLength::GetValue(ErrorResult& aRv) +{ + if (mVal) { + if (mIsAnimValItem) { + mSVGElement->FlushAnimations(); + return mVal->GetAnimValue(mSVGElement); + } + return mVal->GetBaseValue(mSVGElement); + } + + if (mIsAnimValItem && HasOwner()) { + Element()->FlushAnimations(); // May make HasOwner() == false + } + if (HasOwner()) { + float value = InternalItem().GetValueInUserUnits(Element(), Axis()); + if (!IsFinite(value)) { + aRv.Throw(NS_ERROR_FAILURE); + } + return value; + } else if (mUnit == nsIDOMSVGLength::SVG_LENGTHTYPE_NUMBER || + mUnit == nsIDOMSVGLength::SVG_LENGTHTYPE_PX) { + return mValue; + } + // else [SVGWG issue] Can't convert this length's value to user units + // ReportToConsole + aRv.Throw(NS_ERROR_FAILURE); + return 0.0f; +} + +NS_IMETHODIMP +DOMSVGLength::GetValue(float* aValue) +{ + ErrorResult rv; + *aValue = GetValue(rv); + return rv.StealNSResult(); +} + +void +DOMSVGLength::SetValue(float aUserUnitValue, ErrorResult& aRv) +{ + if (mIsAnimValItem) { + aRv.Throw(NS_ERROR_DOM_NO_MODIFICATION_ALLOWED_ERR); + return; + } + + if (mVal) { + mVal->SetBaseValue(aUserUnitValue, mSVGElement, true); + return; + } + + // 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().GetValueInUserUnits(Element(), Axis()) == + aUserUnitValue) { + return; + } + float uuPerUnit = InternalItem().GetUserUnitsPerUnit(Element(), Axis()); + if (uuPerUnit > 0) { + float newValue = aUserUnitValue / uuPerUnit; + if (IsFinite(newValue)) { + AutoChangeLengthNotifier notifier(this); + InternalItem().SetValueAndUnit(newValue, InternalItem().GetUnit()); + return; + } + } + } else if (mUnit == nsIDOMSVGLength::SVG_LENGTHTYPE_NUMBER || + mUnit == nsIDOMSVGLength::SVG_LENGTHTYPE_PX) { + mValue = aUserUnitValue; + return; + } + // else [SVGWG issue] Can't convert user unit value to this length's unit + // ReportToConsole + aRv.Throw(NS_ERROR_FAILURE); +} + +NS_IMETHODIMP +DOMSVGLength::SetValue(float aUserUnitValue) +{ + if (!IsFinite(aUserUnitValue)) { + return NS_ERROR_ILLEGAL_VALUE; + } + + ErrorResult rv; + SetValue(aUserUnitValue, rv); + return rv.StealNSResult(); +} + +float +DOMSVGLength::ValueInSpecifiedUnits() +{ + if (mVal) { + if (mIsAnimValItem) { + mSVGElement->FlushAnimations(); + return mVal->mAnimVal; + } + return mVal->mBaseVal; + } + + if (mIsAnimValItem && HasOwner()) { + Element()->FlushAnimations(); // May make HasOwner() == false + } + return HasOwner() ? InternalItem().GetValueInCurrentUnits() : mValue; +} + +NS_IMETHODIMP +DOMSVGLength::GetValueInSpecifiedUnits(float* aValue) +{ + *aValue = ValueInSpecifiedUnits(); + return NS_OK; +} + +void +DOMSVGLength::SetValueInSpecifiedUnits(float aValue, ErrorResult& aRv) +{ + if (mIsAnimValItem) { + aRv.Throw(NS_ERROR_DOM_NO_MODIFICATION_ALLOWED_ERR); + return; + } + + if (mVal) { + mVal->SetBaseValueInSpecifiedUnits(aValue, mSVGElement, true); + return; + } + + if (HasOwner()) { + if (InternalItem().GetValueInCurrentUnits() == aValue) { + return; + } + AutoChangeLengthNotifier notifier(this); + InternalItem().SetValueInCurrentUnits(aValue); + return; + } + mValue = aValue; +} + +NS_IMETHODIMP +DOMSVGLength::SetValueInSpecifiedUnits(float aValue) +{ + if (!IsFinite(aValue)) { + return NS_ERROR_ILLEGAL_VALUE; + } + + ErrorResult rv; + SetValueInSpecifiedUnits(aValue, rv); + return rv.StealNSResult(); +} + +void +DOMSVGLength::SetValueAsString(const nsAString& aValue, ErrorResult& aRv) +{ + if (mIsAnimValItem) { + aRv.Throw(NS_ERROR_DOM_NO_MODIFICATION_ALLOWED_ERR); + return; + } + + if (mVal) { + aRv = mVal->SetBaseValueString(aValue, mSVGElement, true); + return; + } + + SVGLength value; + if (!value.SetValueFromString(aValue)) { + aRv.Throw(NS_ERROR_DOM_SYNTAX_ERR); + return; + } + if (HasOwner()) { + if (InternalItem() == value) { + return; + } + AutoChangeLengthNotifier notifier(this); + InternalItem() = value; + return; + } + mValue = value.GetValueInCurrentUnits(); + mUnit = value.GetUnit(); +} + +NS_IMETHODIMP +DOMSVGLength::SetValueAsString(const nsAString& aValue) +{ + ErrorResult rv; + SetValueAsString(aValue, rv); + return rv.StealNSResult(); +} + +NS_IMETHODIMP +DOMSVGLength::GetValueAsString(nsAString& aValue) +{ + if (mVal) { + if (mIsAnimValItem) { + mSVGElement->FlushAnimations(); + mVal->GetAnimValueString(aValue); + } else { + mVal->GetBaseValueString(aValue); + } + return NS_OK; + } + + if (mIsAnimValItem && HasOwner()) { + Element()->FlushAnimations(); // May make HasOwner() == false + } + if (HasOwner()) { + InternalItem().GetValueAsString(aValue); + return NS_OK; + } + SVGLength(mValue, mUnit).GetValueAsString(aValue); + return NS_OK; +} + +void +DOMSVGLength::NewValueSpecifiedUnits(uint16_t aUnit, float aValue, + ErrorResult& aRv) +{ + if (mIsAnimValItem) { + aRv.Throw(NS_ERROR_DOM_NO_MODIFICATION_ALLOWED_ERR); + return; + } + + if (mVal) { + mVal->NewValueSpecifiedUnits(aUnit, aValue, mSVGElement); + return; + } + + if (!SVGLength::IsValidUnitType(aUnit)) { + aRv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR); + return; + } + if (HasOwner()) { + if (InternalItem().GetUnit() == aUnit && + InternalItem().GetValueInCurrentUnits() == aValue) { + return; + } + AutoChangeLengthNotifier notifier(this); + InternalItem().SetValueAndUnit(aValue, uint8_t(aUnit)); + return; + } + mUnit = uint8_t(aUnit); + mValue = aValue; +} + +NS_IMETHODIMP +DOMSVGLength::NewValueSpecifiedUnits(uint16_t aUnit, float aValue) +{ + if (!IsFinite(aValue)) { + return NS_ERROR_ILLEGAL_VALUE; + } + + ErrorResult rv; + NewValueSpecifiedUnits(aUnit, aValue, rv); + return rv.StealNSResult(); +} + +void +DOMSVGLength::ConvertToSpecifiedUnits(uint16_t aUnit, ErrorResult& aRv) +{ + if (mIsAnimValItem) { + aRv.Throw(NS_ERROR_DOM_NO_MODIFICATION_ALLOWED_ERR); + return; + } + + if (mVal) { + mVal->ConvertToSpecifiedUnits(aUnit, mSVGElement); + return; + } + + if (!SVGLength::IsValidUnitType(aUnit)) { + aRv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR); + return; + } + if (HasOwner()) { + if (InternalItem().GetUnit() == aUnit) { + return; + } + float val = InternalItem().GetValueInSpecifiedUnit( + aUnit, Element(), Axis()); + if (IsFinite(val)) { + AutoChangeLengthNotifier notifier(this); + InternalItem().SetValueAndUnit(val, aUnit); + return; + } + } else { + SVGLength len(mValue, mUnit); + float val = len.GetValueInSpecifiedUnit(aUnit, nullptr, 0); + if (IsFinite(val)) { + mValue = val; + mUnit = aUnit; + return; + } + } + // else [SVGWG issue] Can't convert unit + // ReportToConsole + aRv.Throw(NS_ERROR_FAILURE); +} + +NS_IMETHODIMP +DOMSVGLength::ConvertToSpecifiedUnits(uint16_t aUnit) +{ + ErrorResult rv; + ConvertToSpecifiedUnits(aUnit, rv); + return rv.StealNSResult(); +} + +JSObject* +DOMSVGLength::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) +{ + return dom::SVGLengthBinding::Wrap(aCx, this, aGivenProto); +} + +void +DOMSVGLength::InsertingIntoList(DOMSVGLengthList *aList, + uint8_t aAttrEnum, + uint32_t aListIndex, + bool aIsAnimValItem) +{ + NS_ASSERTION(!HasOwner(), "Inserting item that is already in a list"); + + mList = aList; + mAttrEnum = aAttrEnum; + mListIndex = aListIndex; + mIsAnimValItem = aIsAnimValItem; + + MOZ_ASSERT(IndexIsValid(), "Bad index for DOMSVGLength!"); +} + +void +DOMSVGLength::RemovingFromList() +{ + mValue = InternalItem().GetValueInCurrentUnits(); + mUnit = InternalItem().GetUnit(); + mList = nullptr; + mIsAnimValItem = false; +} + +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]; +} + +#ifdef DEBUG +bool +DOMSVGLength::IndexIsValid() +{ + SVGAnimatedLengthList *alist = Element()->GetAnimatedLengthList(mAttrEnum); + return (mIsAnimValItem && + mListIndex < alist->GetAnimValue().Length()) || + (!mIsAnimValItem && + mListIndex < alist->GetBaseValue().Length()); +} +#endif + +} // namespace mozilla diff --git a/dom/svg/DOMSVGLength.h b/dom/svg/DOMSVGLength.h new file mode 100644 index 0000000000..d20c0ffa84 --- /dev/null +++ b/dom/svg/DOMSVGLength.h @@ -0,0 +1,257 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef MOZILLA_DOMSVGLENGTH_H__ +#define MOZILLA_DOMSVGLENGTH_H__ + +#include "DOMSVGLengthList.h" +#include "nsCycleCollectionParticipant.h" +#include "nsDebug.h" +#include "nsIDOMSVGLength.h" +#include "nsTArray.h" +#include "SVGLength.h" +#include "mozilla/Attributes.h" +#include "nsWrapperCache.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 } } + +#define MOZ_SVG_LIST_INDEX_BIT_COUNT 22 // supports > 4 million list items + +namespace mozilla { + +class ErrorResult; + +/** + * 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. + * + * Objects of this type are also used to reflect the baseVal and animVal of + * a single, non-list SVGLength attribute. Getting and settings values of the + * DOMSVGLength in this case requires reading and writing to the corresponding + * nsSVGLength2 object. + * + * 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 final : public nsIDOMSVGLength, + public nsWrapperCache +{ + friend class AutoChangeLengthNotifier; + + /** + * Ctor for creating the object returned by nsSVGLength2::ToDOMBaseVal/ToDOMAnimVal + */ + DOMSVGLength(nsSVGLength2* aVal, nsSVGElement* aSVGElement, bool aAnimVal); + + ~DOMSVGLength(); + +public: + NS_DECLARE_STATIC_IID_ACCESSOR(MOZILLA_DOMSVGLENGTH_IID) + NS_DECL_CYCLE_COLLECTING_ISUPPORTS + NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(DOMSVGLength) + NS_DECL_NSIDOMSVGLENGTH + + /** + * Generic ctor for DOMSVGLength objects that are created for an attribute. + */ + DOMSVGLength(DOMSVGLengthList *aList, + uint8_t aAttrEnum, + uint32_t aListIndex, + bool aIsAnimValItem); + + /** + * Ctor for creating the objects returned by SVGSVGElement.createSVGLength(), + * which do not initially belong to an attribute. + */ + DOMSVGLength(); + + static already_AddRefed<DOMSVGLength> GetTearOff(nsSVGLength2* aVal, + nsSVGElement* aSVGElement, + bool aAnimVal); + + /** + * Create an unowned copy of a length that is owned or is reflecting a single + * attribute. The caller is responsible for the first AddRef(). + */ + DOMSVGLength* Copy(); + + bool IsInList() const { + return !!mList; + } + + /** + * In future, if this class is used for non-list lengths, this will be + * different to IsInList(). + */ + bool HasOwner() const { + return !!mList; + } + + /** + * Returns whether this length object is reflecting a single SVG element + * attribute. This includes the baseVal or animVal of SVGRectElement.x, for + * example, but not an item in an SVGLengthList, such as those in the + * baseVal or animVal of SVGTextElement.x. + */ + bool IsReflectingAttribute() const { + return mVal; + } + + /** + * 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, + uint8_t aAttrEnum, + uint32_t aListIndex, + bool aIsAnimValItem); + + static uint32_t MaxListIndex() { + return (1U << MOZ_SVG_LIST_INDEX_BIT_COUNT) - 1; + } + + /// This method is called to notify this object that its list index changed. + void UpdateListIndex(uint32_t 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 + * "lose" its value on being removed.) + */ + void RemovingFromList(); + + SVGLength ToSVGLength(); + + // WebIDL + uint16_t UnitType(); + float GetValue(ErrorResult& aRv); + void SetValue(float aValue, ErrorResult& aRv); + float ValueInSpecifiedUnits(); + void SetValueInSpecifiedUnits(float aValue, ErrorResult& aRv); + // The XPCOM GetValueAsString is good + void SetValueAsString(const nsAString& aValue, ErrorResult& aRv); + void NewValueSpecifiedUnits(uint16_t aUnit, float aValue, + ErrorResult& aRv); + void ConvertToSpecifiedUnits(uint16_t aUnit, ErrorResult& aRv); + + nsISupports* GetParentObject() const { + auto svgElement = mList ? Element() : mSVGElement.get(); + return static_cast<nsIDOMSVGElement*> (svgElement); + } + + JSObject* WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override; + +private: + + nsSVGElement* Element() const { + return mList->Element(); + } + + uint8_t 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). + */ + uint8_t Axis() const { + return mList->Axis(); + } + + /** + * Get a reference to the internal SVGLength list item that this DOM wrapper + * object currently wraps. + * + * To simplify 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(); + +#ifdef DEBUG + bool IndexIsValid(); +#endif + + /** + * Clears soon-to-be-invalid weak references in external objects that were + * set up during the creation of this object. This should be called during + * destruction and during cycle collection. + */ + void CleanupWeakRefs(); + + RefPtr<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. + + uint32_t mListIndex:MOZ_SVG_LIST_INDEX_BIT_COUNT; + uint32_t mAttrEnum:4; // supports up to 16 attributes + uint32_t mIsAnimValItem:1; + + // The following members are only used when we're not in a list: + uint32_t mUnit:5; // can handle 31 units (the 10 SVG 1.1 units + rem, vw, vh, wm, calc + future additions) + float mValue; + + // The following members are only used when we have an nsSVGLength2 + nsSVGLength2* mVal; // kept alive because it belongs to mSVGElement + RefPtr<nsSVGElement> mSVGElement; +}; + +NS_DEFINE_STATIC_IID_ACCESSOR(DOMSVGLength, MOZILLA_DOMSVGLENGTH_IID) + +} // namespace mozilla + +#undef MOZ_SVG_LIST_INDEX_BIT_COUNT + +#endif // MOZILLA_DOMSVGLENGTH_H__ diff --git a/dom/svg/DOMSVGLengthList.cpp b/dom/svg/DOMSVGLengthList.cpp new file mode 100644 index 0000000000..9d93546d98 --- /dev/null +++ b/dom/svg/DOMSVGLengthList.cpp @@ -0,0 +1,419 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "nsSVGElement.h" +#include "DOMSVGLengthList.h" +#include "DOMSVGLength.h" +#include "nsError.h" +#include "SVGAnimatedLengthList.h" +#include "nsCOMPtr.h" +#include "mozilla/dom/SVGLengthListBinding.h" +#include <algorithm> + +// See the comment in this file's header. + +// local helper functions +namespace { + +using mozilla::DOMSVGLength; + +void UpdateListIndicesFromIndex(FallibleTArray<DOMSVGLength*>& aItemsArray, + uint32_t aStartingIndex) +{ + uint32_t length = aItemsArray.Length(); + + for (uint32_t i = aStartingIndex; i < length; ++i) { + if (aItemsArray[i]) { + aItemsArray[i]->UpdateListIndex(i); + } + } +} + +} // namespace + +namespace mozilla { + +// We could use NS_IMPL_CYCLE_COLLECTION(, 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) + if (tmp->mAList) { + if (tmp->IsAnimValList()) { + tmp->mAList->mAnimVal = nullptr; + } else { + tmp->mAList->mBaseVal = nullptr; + } + NS_IMPL_CYCLE_COLLECTION_UNLINK(mAList) + } + NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER +NS_IMPL_CYCLE_COLLECTION_UNLINK_END +NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(DOMSVGLengthList) + NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mAList) + NS_IMPL_CYCLE_COLLECTION_TRAVERSE_SCRIPT_OBJECTS +NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END +NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(DOMSVGLengthList) + NS_IMPL_CYCLE_COLLECTION_TRACE_PRESERVED_WRAPPER +NS_IMPL_CYCLE_COLLECTION_TRACE_END + +NS_IMPL_CYCLE_COLLECTING_ADDREF(DOMSVGLengthList) +NS_IMPL_CYCLE_COLLECTING_RELEASE(DOMSVGLengthList) + +NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(DOMSVGLengthList) + NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY + NS_INTERFACE_MAP_ENTRY(nsISupports) +NS_INTERFACE_MAP_END + +JSObject* +DOMSVGLengthList::WrapObject(JSContext *cx, JS::Handle<JSObject*> aGivenProto) +{ + return mozilla::dom::SVGLengthListBinding::Wrap(cx, this, aGivenProto); +} + +//---------------------------------------------------------------------- +// Helper class: AutoChangeLengthListNotifier +// Stack-based helper class to pair calls to WillChangeLengthList and +// DidChangeLengthList. +class MOZ_RAII AutoChangeLengthListNotifier +{ +public: + explicit AutoChangeLengthListNotifier(DOMSVGLengthList* aLengthList MOZ_GUARD_OBJECT_NOTIFIER_PARAM) + : mLengthList(aLengthList) + { + MOZ_GUARD_OBJECT_NOTIFIER_INIT; + MOZ_ASSERT(mLengthList, "Expecting non-null lengthList"); + mEmptyOrOldValue = + mLengthList->Element()->WillChangeLengthList(mLengthList->AttrEnum()); + } + + ~AutoChangeLengthListNotifier() + { + mLengthList->Element()->DidChangeLengthList(mLengthList->AttrEnum(), + mEmptyOrOldValue); + if (mLengthList->IsAnimating()) { + mLengthList->Element()->AnimationNeedsResample(); + } + } + +private: + DOMSVGLengthList* const mLengthList; + nsAttrValue mEmptyOrOldValue; + MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER +}; + +void +DOMSVGLengthList::InternalListLengthWillChange(uint32_t aNewLength) +{ + uint32_t oldLength = mItems.Length(); + + if (aNewLength > DOMSVGLength::MaxListIndex()) { + // It's safe to get out of sync with our internal list as long as we have + // FEWER items than it does. + aNewLength = DOMSVGLength::MaxListIndex(); + } + + RefPtr<DOMSVGLengthList> kungFuDeathGrip; + if (aNewLength < oldLength) { + // RemovingFromList() might clear last reference to |this|. + // Retain a temporary reference to keep from dying before returning. + kungFuDeathGrip = this; + } + + // If our length will decrease, notify the items that will be removed: + for (uint32_t i = aNewLength; i < oldLength; ++i) { + if (mItems[i]) { + mItems[i]->RemovingFromList(); + } + } + + if (!mItems.SetLength(aNewLength, fallible)) { + // 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.Clear(); + return; + } + + // If our length has increased, null out the new pointers: + for (uint32_t i = oldLength; i < aNewLength; ++i) { + mItems[i] = nullptr; + } +} + +SVGLengthList& +DOMSVGLengthList::InternalList() const +{ + SVGAnimatedLengthList *alist = Element()->GetAnimatedLengthList(AttrEnum()); + return IsAnimValList() && alist->mAnimVal ? *alist->mAnimVal : alist->mBaseVal; +} + +// ---------------------------------------------------------------------------- + +void +DOMSVGLengthList::Clear(ErrorResult& aError) +{ + if (IsAnimValList()) { + aError.Throw(NS_ERROR_DOM_NO_MODIFICATION_ALLOWED_ERR); + return; + } + + if (LengthNoFlush() > 0) { + AutoChangeLengthListNotifier notifier(this); + // Notify any existing DOM items of removal *before* truncating the lists + // so that they can find their SVGLength internal counterparts and copy + // their values. This also notifies the animVal list: + mAList->InternalBaseValListWillChangeTo(SVGLengthList()); + + mItems.Clear(); + InternalList().Clear(); + } +} + +already_AddRefed<DOMSVGLength> +DOMSVGLengthList::Initialize(DOMSVGLength& newItem, + ErrorResult& error) +{ + if (IsAnimValList()) { + error.Throw(NS_ERROR_DOM_NO_MODIFICATION_ALLOWED_ERR); + return nullptr; + } + + // If newItem already has an owner or is reflecting an attribute, 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. + + RefPtr<DOMSVGLength> domItem = &newItem; + if (!domItem) { + error.Throw(NS_ERROR_DOM_SVG_WRONG_TYPE_ERR); + return nullptr; + } + if (domItem->HasOwner() || domItem->IsReflectingAttribute()) { + domItem = domItem->Copy(); + } + + ErrorResult rv; + Clear(rv); + MOZ_ASSERT(!rv.Failed()); + return InsertItemBefore(*domItem, 0, error); +} + +already_AddRefed<DOMSVGLength> +DOMSVGLengthList::GetItem(uint32_t index, ErrorResult& error) +{ + bool found; + RefPtr<DOMSVGLength> item = IndexedGetter(index, found, error); + if (!found) { + error.Throw(NS_ERROR_DOM_INDEX_SIZE_ERR); + } + return item.forget(); +} + +already_AddRefed<DOMSVGLength> +DOMSVGLengthList::IndexedGetter(uint32_t index, bool& found, ErrorResult& error) +{ + if (IsAnimValList()) { + Element()->FlushAnimations(); + } + found = index < LengthNoFlush(); + if (found) { + return GetItemAt(index); + } + return nullptr; +} + +already_AddRefed<DOMSVGLength> +DOMSVGLengthList::InsertItemBefore(DOMSVGLength& newItem, + uint32_t index, + ErrorResult& error) +{ + if (IsAnimValList()) { + error.Throw(NS_ERROR_DOM_NO_MODIFICATION_ALLOWED_ERR); + return nullptr; + } + + index = std::min(index, LengthNoFlush()); + if (index >= DOMSVGLength::MaxListIndex()) { + error.Throw(NS_ERROR_DOM_INDEX_SIZE_ERR); + return nullptr; + } + + RefPtr<DOMSVGLength> domItem = &newItem; + if (!domItem) { + error.Throw(NS_ERROR_DOM_SVG_WRONG_TYPE_ERR); + return nullptr; + } + if (domItem->HasOwner() || domItem->IsReflectingAttribute()) { + domItem = domItem->Copy(); // must do this before changing anything! + } + + // Ensure we have enough memory so we can avoid complex error handling below: + if (!mItems.SetCapacity(mItems.Length() + 1, fallible) || + !InternalList().SetCapacity(InternalList().Length() + 1)) { + error.Throw(NS_ERROR_OUT_OF_MEMORY); + return nullptr; + } + if (AnimListMirrorsBaseList()) { + if (!mAList->mAnimVal->mItems.SetCapacity( + mAList->mAnimVal->mItems.Length() + 1, fallible)) { + error.Throw(NS_ERROR_OUT_OF_MEMORY); + return nullptr; + } + } + + AutoChangeLengthListNotifier notifier(this); + // Now that we know we're inserting, keep animVal list in sync as necessary. + MaybeInsertNullInAnimValListAt(index); + + InternalList().InsertItem(index, domItem->ToSVGLength()); + MOZ_ALWAYS_TRUE(mItems.InsertElementAt(index, domItem.get(), fallible)); + + // This MUST come after the insertion into InternalList(), or else under the + // insertion into InternalList() the values read from domItem would be bad + // data from InternalList() itself!: + domItem->InsertingIntoList(this, AttrEnum(), index, IsAnimValList()); + + UpdateListIndicesFromIndex(mItems, index + 1); + + return domItem.forget(); +} + +already_AddRefed<DOMSVGLength> +DOMSVGLengthList::ReplaceItem(DOMSVGLength& newItem, + uint32_t index, + ErrorResult& error) +{ + if (IsAnimValList()) { + error.Throw(NS_ERROR_DOM_NO_MODIFICATION_ALLOWED_ERR); + return nullptr; + } + + RefPtr<DOMSVGLength> domItem = &newItem; + if (!domItem) { + error.Throw(NS_ERROR_DOM_SVG_WRONG_TYPE_ERR); + return nullptr; + } + if (index >= LengthNoFlush()) { + error.Throw(NS_ERROR_DOM_INDEX_SIZE_ERR); + return nullptr; + } + if (domItem->HasOwner() || domItem->IsReflectingAttribute()) { + domItem = domItem->Copy(); // must do this before changing anything! + } + + AutoChangeLengthListNotifier notifier(this); + 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] = domItem->ToSVGLength(); + mItems[index] = domItem; + + // This MUST come after the ToSVGPoint() call, otherwise that call + // would end up reading bad data from InternalList()! + domItem->InsertingIntoList(this, AttrEnum(), index, IsAnimValList()); + + return domItem.forget(); +} + +already_AddRefed<DOMSVGLength> +DOMSVGLengthList::RemoveItem(uint32_t index, + ErrorResult& error) +{ + if (IsAnimValList()) { + error.Throw(NS_ERROR_DOM_NO_MODIFICATION_ALLOWED_ERR); + return nullptr; + } + + if (index >= LengthNoFlush()) { + error.Throw(NS_ERROR_DOM_INDEX_SIZE_ERR); + return nullptr; + } + + AutoChangeLengthListNotifier notifier(this); + // Now that we know we're removing, keep animVal list in sync as necessary. + // Do this *before* touching InternalList() so the removed item can get its + // internal value. + MaybeRemoveItemFromAnimValListAt(index); + + // We have to return the removed item, so get it, creating it if necessary: + nsCOMPtr<DOMSVGLength> result = GetItemAt(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); + mItems.RemoveElementAt(index); + + UpdateListIndicesFromIndex(mItems, index); + + return result.forget(); +} + +already_AddRefed<DOMSVGLength> +DOMSVGLengthList::GetItemAt(uint32_t aIndex) +{ + MOZ_ASSERT(aIndex < mItems.Length()); + + if (!mItems[aIndex]) { + mItems[aIndex] = new DOMSVGLength(this, AttrEnum(), aIndex, IsAnimValList()); + } + RefPtr<DOMSVGLength> result = mItems[aIndex]; + return result.forget(); +} + +void +DOMSVGLengthList::MaybeInsertNullInAnimValListAt(uint32_t aIndex) +{ + MOZ_ASSERT(!IsAnimValList(), "call from baseVal to animVal"); + + if (!AnimListMirrorsBaseList()) { + return; + } + + DOMSVGLengthList* animVal = mAList->mAnimVal; + + MOZ_ASSERT(animVal, "AnimListMirrorsBaseList() promised a non-null animVal"); + MOZ_ASSERT(animVal->mItems.Length() == mItems.Length(), + "animVal list not in sync!"); + MOZ_ALWAYS_TRUE(animVal->mItems.InsertElementAt(aIndex, nullptr, fallible)); + + UpdateListIndicesFromIndex(animVal->mItems, aIndex + 1); +} + +void +DOMSVGLengthList::MaybeRemoveItemFromAnimValListAt(uint32_t aIndex) +{ + MOZ_ASSERT(!IsAnimValList(), "call from baseVal to animVal"); + + if (!AnimListMirrorsBaseList()) { + return; + } + + // This needs to be a strong reference; otherwise, the RemovingFromList call + // below might drop the last reference to animVal before we're done with it. + RefPtr<DOMSVGLengthList> animVal = mAList->mAnimVal; + + MOZ_ASSERT(animVal, "AnimListMirrorsBaseList() promised a non-null animVal"); + MOZ_ASSERT(animVal->mItems.Length() == mItems.Length(), + "animVal list not in sync!"); + + if (animVal->mItems[aIndex]) { + animVal->mItems[aIndex]->RemovingFromList(); + } + animVal->mItems.RemoveElementAt(aIndex); + + UpdateListIndicesFromIndex(animVal->mItems, aIndex); +} + +} // namespace mozilla diff --git a/dom/svg/DOMSVGLengthList.h b/dom/svg/DOMSVGLengthList.h new file mode 100644 index 0000000000..842d291345 --- /dev/null +++ b/dom/svg/DOMSVGLengthList.h @@ -0,0 +1,185 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef MOZILLA_DOMSVGLENGTHLIST_H__ +#define MOZILLA_DOMSVGLENGTHLIST_H__ + +#include "DOMSVGAnimatedLengthList.h" +#include "nsCycleCollectionParticipant.h" +#include "nsDebug.h" +#include "nsTArray.h" +#include "SVGLengthList.h" +#include "mozilla/Attributes.h" +#include "mozilla/ErrorResult.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 final : public nsISupports, + public nsWrapperCache +{ + friend class AutoChangeLengthListNotifier; + friend class DOMSVGLength; + + ~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 ) = nullptr; + } + } + +public: + NS_DECL_CYCLE_COLLECTING_ISUPPORTS + NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(DOMSVGLengthList) + + DOMSVGLengthList(DOMSVGAnimatedLengthList *aAList, + const SVGLengthList &aInternalList) + : mAList(aAList) + { + // aInternalList must be passed in explicitly because we can't use + // InternalList() here. (Because it depends on IsAnimValList, which depends + // on this object having been assigned to aAList's mBaseVal or mAnimVal, + // which hasn't happend yet.) + + InternalListLengthWillChange(aInternalList.Length()); // Sync mItems + } + + virtual JSObject* WrapObject(JSContext *cx, JS::Handle<JSObject*> aGivenProto) override; + + nsISupports* GetParentObject() + { + return static_cast<nsIContent*>(Element()); + } + + /** + * This will normally be the same as InternalList().Length(), except if we've + * hit OOM in which case our length will be zero. + */ + uint32_t LengthNoFlush() const { + MOZ_ASSERT(mItems.Length() == 0 || + mItems.Length() == 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(uint32_t aNewLength); + + /** + * Returns true if our attribute is animating (in which case our animVal is + * not simply a mirror of our baseVal). + */ + bool IsAnimating() const { + return mAList->IsAnimating(); + } + /** + * Returns true if there is an animated list mirroring the base list. + */ + bool AnimListMirrorsBaseList() const { + return mAList->mAnimVal && !mAList->IsAnimating(); + } + + uint32_t NumberOfItems() const + { + if (IsAnimValList()) { + Element()->FlushAnimations(); + } + return LengthNoFlush(); + } + void Clear(ErrorResult& aError); + already_AddRefed<DOMSVGLength> Initialize(DOMSVGLength& newItem, + ErrorResult& error); + already_AddRefed<DOMSVGLength> GetItem(uint32_t index, + ErrorResult& error); + already_AddRefed<DOMSVGLength> IndexedGetter(uint32_t index, bool& found, + ErrorResult& error); + already_AddRefed<DOMSVGLength> InsertItemBefore(DOMSVGLength& newItem, + uint32_t index, + ErrorResult& error); + already_AddRefed<DOMSVGLength> ReplaceItem(DOMSVGLength& newItem, + uint32_t index, + ErrorResult& error); + already_AddRefed<DOMSVGLength> RemoveItem(uint32_t index, + ErrorResult& error); + already_AddRefed<DOMSVGLength> AppendItem(DOMSVGLength& newItem, + ErrorResult& error) + { + return InsertItemBefore(newItem, LengthNoFlush(), error); + } + uint32_t Length() const + { + return NumberOfItems(); + } + +private: + + nsSVGElement* Element() const { + return mAList->mElement; + } + + uint8_t AttrEnum() const { + return mAList->mAttrEnum; + } + + uint8_t Axis() const { + return mAList->mAxis; + } + + /// Used to determine if this list is the baseVal or animVal list. + bool IsAnimValList() const { + MOZ_ASSERT(this == mAList->mBaseVal || this == mAList->mAnimVal, + "Calling IsAnimValList() too early?!"); + return this == mAList->mAnimVal; + } + + /** + * Get a reference to this object's corresponding internal SVGLengthList. + * + * To simplify 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() const; + + /// Returns the DOMSVGLength at aIndex, creating it if necessary. + already_AddRefed<DOMSVGLength> GetItemAt(uint32_t aIndex); + + void MaybeInsertNullInAnimValListAt(uint32_t aIndex); + void MaybeRemoveItemFromAnimValListAt(uint32_t aIndex); + + // Weak refs to our DOMSVGLength items. The items are friends and take care + // of clearing our pointer to them when they die. + FallibleTArray<DOMSVGLength*> mItems; + + RefPtr<DOMSVGAnimatedLengthList> mAList; +}; + +} // namespace mozilla + +#endif // MOZILLA_DOMSVGLENGTHLIST_H__ diff --git a/dom/svg/DOMSVGNumber.cpp b/dom/svg/DOMSVGNumber.cpp new file mode 100644 index 0000000000..97d80d4d01 --- /dev/null +++ b/dom/svg/DOMSVGNumber.cpp @@ -0,0 +1,231 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "DOMSVGNumber.h" +#include "DOMSVGNumberList.h" +#include "DOMSVGAnimatedNumberList.h" +#include "SVGAnimatedNumberList.h" +#include "nsSVGElement.h" +#include "nsError.h" +#include "nsContentUtils.h" // for NS_ENSURE_FINITE +#include "mozilla/dom/SVGNumberBinding.h" + +// See the architecture comment in DOMSVGAnimatedNumberList.h. + +namespace mozilla { + +// We could use NS_IMPL_CYCLE_COLLECTION(, 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(DOMSVGNumber) + +NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(DOMSVGNumber) + // We may not belong to a list, so we must null check tmp->mList. + if (tmp->mList) { + tmp->mList->mItems[tmp->mListIndex] = nullptr; + } + NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER + NS_IMPL_CYCLE_COLLECTION_UNLINK(mList) + NS_IMPL_CYCLE_COLLECTION_UNLINK(mParent) +NS_IMPL_CYCLE_COLLECTION_UNLINK_END +NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(DOMSVGNumber) + NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mList) + NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mParent) + NS_IMPL_CYCLE_COLLECTION_TRAVERSE_SCRIPT_OBJECTS +NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END +NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(DOMSVGNumber) + NS_IMPL_CYCLE_COLLECTION_TRACE_PRESERVED_WRAPPER +NS_IMPL_CYCLE_COLLECTION_TRACE_END + +NS_IMPL_CYCLE_COLLECTING_ADDREF(DOMSVGNumber) +NS_IMPL_CYCLE_COLLECTING_RELEASE(DOMSVGNumber) + +NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(DOMSVGNumber) + NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY + NS_INTERFACE_MAP_ENTRY(nsISupports) +NS_INTERFACE_MAP_END + +//---------------------------------------------------------------------- +// Helper class: AutoChangeNumberNotifier +// Stack-based helper class to pair calls to WillChangeNumberList and +// DidChangeNumberList. +class MOZ_RAII AutoChangeNumberNotifier +{ +public: + explicit AutoChangeNumberNotifier(DOMSVGNumber* aNumber MOZ_GUARD_OBJECT_NOTIFIER_PARAM) + : mNumber(aNumber) + { + MOZ_GUARD_OBJECT_NOTIFIER_INIT; + MOZ_ASSERT(mNumber, "Expecting non-null number"); + MOZ_ASSERT(mNumber->HasOwner(), + "Expecting list to have an owner for notification"); + mEmptyOrOldValue = + mNumber->Element()->WillChangeNumberList(mNumber->mAttrEnum); + } + + ~AutoChangeNumberNotifier() + { + mNumber->Element()->DidChangeNumberList(mNumber->mAttrEnum, + mEmptyOrOldValue); + if (mNumber->mList->IsAnimating()) { + mNumber->Element()->AnimationNeedsResample(); + } + } + +private: + DOMSVGNumber* const mNumber; + nsAttrValue mEmptyOrOldValue; + MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER +}; + +DOMSVGNumber::DOMSVGNumber(DOMSVGNumberList *aList, + uint8_t aAttrEnum, + uint32_t aListIndex, + bool aIsAnimValItem) + : mList(aList) + , mParent(aList) + , mListIndex(aListIndex) + , mAttrEnum(aAttrEnum) + , mIsAnimValItem(aIsAnimValItem) + , mValue(0.0f) +{ + // These shifts are in sync with the members in the header. + MOZ_ASSERT(aList && + aAttrEnum < (1 << 4) && + aListIndex <= MaxListIndex(), + "bad arg"); + + MOZ_ASSERT(IndexIsValid(), "Bad index for DOMSVGNumber!"); +} + +DOMSVGNumber::DOMSVGNumber(nsISupports* aParent) + : mList(nullptr) + , mParent(aParent) + , mListIndex(0) + , mAttrEnum(0) + , mIsAnimValItem(false) + , mValue(0.0f) +{ +} + +/* static */ already_AddRefed<DOMSVGNumber> +DOMSVGNumber::Constructor(const dom::GlobalObject& aGlobal, ErrorResult& aRv) +{ + nsCOMPtr<nsPIDOMWindowInner> window = + do_QueryInterface(aGlobal.GetAsSupports()); + if (!window) { + aRv.Throw(NS_ERROR_UNEXPECTED); + return nullptr; + } + + RefPtr<DOMSVGNumber> number = new DOMSVGNumber(window); + return number.forget(); +} + +/* static */ already_AddRefed<DOMSVGNumber> +DOMSVGNumber::Constructor(const dom::GlobalObject& aGlobal, float aValue, + ErrorResult& aRv) +{ + nsCOMPtr<nsPIDOMWindowInner> window = + do_QueryInterface(aGlobal.GetAsSupports()); + if (!window) { + aRv.Throw(NS_ERROR_UNEXPECTED); + return nullptr; + } + + RefPtr<DOMSVGNumber> number = new DOMSVGNumber(window); + number->SetValue(aValue, aRv); + return number.forget(); +} + +float +DOMSVGNumber::Value() +{ + if (mIsAnimValItem && HasOwner()) { + Element()->FlushAnimations(); // May make HasOwner() == false + } + return HasOwner() ? InternalItem() : mValue; +} + +void +DOMSVGNumber::SetValue(float aValue, ErrorResult& aRv) +{ + if (mIsAnimValItem) { + aRv.Throw(NS_ERROR_DOM_NO_MODIFICATION_ALLOWED_ERR); + return; + } + + if (HasOwner()) { + if (InternalItem() == aValue) { + return; + } + AutoChangeNumberNotifier notifier(this); + InternalItem() = aValue; + return; + } + + mValue = aValue; +} + +void +DOMSVGNumber::InsertingIntoList(DOMSVGNumberList *aList, + uint8_t aAttrEnum, + uint32_t aListIndex, + bool aIsAnimValItem) +{ + NS_ASSERTION(!HasOwner(), "Inserting item that is already in a list"); + + mList = aList; + mAttrEnum = aAttrEnum; + mListIndex = aListIndex; + mIsAnimValItem = aIsAnimValItem; + + MOZ_ASSERT(IndexIsValid(), "Bad index for DOMSVGNumber!"); +} + +void +DOMSVGNumber::RemovingFromList() +{ + mValue = InternalItem(); + mList = nullptr; + mIsAnimValItem = false; +} + +float +DOMSVGNumber::ToSVGNumber() +{ + return HasOwner() ? InternalItem() : mValue; +} + +float& +DOMSVGNumber::InternalItem() +{ + SVGAnimatedNumberList *alist = Element()->GetAnimatedNumberList(mAttrEnum); + return mIsAnimValItem && alist->mAnimVal ? + (*alist->mAnimVal)[mListIndex] : + alist->mBaseVal[mListIndex]; +} + +#ifdef DEBUG +bool +DOMSVGNumber::IndexIsValid() +{ + SVGAnimatedNumberList *alist = Element()->GetAnimatedNumberList(mAttrEnum); + return (mIsAnimValItem && + mListIndex < alist->GetAnimValue().Length()) || + (!mIsAnimValItem && + mListIndex < alist->GetBaseValue().Length()); +} +#endif + +JSObject* +DOMSVGNumber::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) +{ + return dom::SVGNumberBinding::Wrap(aCx, this, aGivenProto); +} + +} // namespace mozilla diff --git a/dom/svg/DOMSVGNumber.h b/dom/svg/DOMSVGNumber.h new file mode 100644 index 0000000000..36bbc489c0 --- /dev/null +++ b/dom/svg/DOMSVGNumber.h @@ -0,0 +1,182 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef MOZILLA_DOMSVGNUMBER_H__ +#define MOZILLA_DOMSVGNUMBER_H__ + +#include "DOMSVGNumberList.h" +#include "nsCycleCollectionParticipant.h" +#include "nsTArray.h" +#include "mozilla/Attributes.h" +#include "mozilla/ErrorResult.h" +#include "nsWrapperCache.h" + +class nsSVGElement; + +#define MOZ_SVG_LIST_INDEX_BIT_COUNT 27 // supports > 134 million list items + +namespace mozilla { + +/** + * Class DOMSVGNumber + * + * This class creates the DOM objects that wrap internal SVGNumber objects that + * are in an SVGNumberList. It is also used to create the objects returned by + * SVGSVGElement.createSVGNumber(). + * + * For the DOM wrapper classes for non-list SVGNumber, see nsSVGNumber2.h. + * + * See the architecture comment in DOMSVGAnimatedNumberList.h. + * + * See the comment in DOMSVGLength.h (yes, LENGTH), which applies here too. + */ +class DOMSVGNumber final : public nsISupports + , public nsWrapperCache +{ + friend class AutoChangeNumberNotifier; + + ~DOMSVGNumber() { + // 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] = nullptr; + } + } + +public: + NS_DECL_CYCLE_COLLECTING_ISUPPORTS + NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(DOMSVGNumber) + + /** + * Generic ctor for DOMSVGNumber objects that are created for an attribute. + */ + DOMSVGNumber(DOMSVGNumberList *aList, + uint8_t aAttrEnum, + uint32_t aListIndex, + bool aIsAnimValItem); + + /** + * Ctor for creating the objects returned by SVGSVGElement.createSVGNumber(), + * which do not initially belong to an attribute. + */ + explicit DOMSVGNumber(nsISupports* aParent); + + /** + * Create an unowned copy. The caller is responsible for the first AddRef(). + */ + DOMSVGNumber* Clone() { + DOMSVGNumber *clone = new DOMSVGNumber(mParent); + clone->mValue = ToSVGNumber(); + return clone; + } + + bool IsInList() const { + return !!mList; + } + + /** + * In future, if this class is used for non-list numbers, this will be + * different to IsInList(). + */ + bool 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 DOMSVGNumberList).) + */ + void InsertingIntoList(DOMSVGNumberList *aList, + uint8_t aAttrEnum, + uint32_t aListIndex, + bool aIsAnimValItem); + + static uint32_t MaxListIndex() { + return (1U << MOZ_SVG_LIST_INDEX_BIT_COUNT) - 1; + } + + /// This method is called to notify this object that its list index changed. + void UpdateListIndex(uint32_t 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 value. (If it didn't do this, then it would + * "lose" its value on being removed.) + */ + void RemovingFromList(); + + float ToSVGNumber(); + + nsISupports* GetParentObject() + { + return mParent; + } + + virtual JSObject* WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override; + + static already_AddRefed<DOMSVGNumber> + Constructor(const dom::GlobalObject& aGlobal, ErrorResult& aRv); + + static already_AddRefed<DOMSVGNumber> + Constructor(const dom::GlobalObject& aGlobal, float aValue, ErrorResult& aRv); + + float Value(); + + void SetValue(float aValue, ErrorResult& aRv); + +private: + + nsSVGElement* Element() { + return mList->Element(); + } + + uint8_t AttrEnum() const { + return mAttrEnum; + } + + /** + * Get a reference to the internal SVGNumber list item that this DOM wrapper + * object currently wraps. + * + * To simplify 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. + */ + float& InternalItem(); + +#ifdef DEBUG + bool IndexIsValid(); +#endif + + RefPtr<DOMSVGNumberList> mList; + nsCOMPtr<nsISupports> mParent; + + // 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. + + uint32_t mListIndex:MOZ_SVG_LIST_INDEX_BIT_COUNT; + uint32_t mAttrEnum:4; // supports up to 16 attributes + uint32_t mIsAnimValItem:1; + + // The following member is only used when we're not in a list: + float mValue; +}; + +} // namespace mozilla + +#undef MOZ_SVG_LIST_INDEX_BIT_COUNT + +#endif // MOZILLA_DOMSVGNUMBER_H__ diff --git a/dom/svg/DOMSVGNumberList.cpp b/dom/svg/DOMSVGNumberList.cpp new file mode 100644 index 0000000000..00fb7ba4d0 --- /dev/null +++ b/dom/svg/DOMSVGNumberList.cpp @@ -0,0 +1,398 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "nsSVGElement.h" +#include "DOMSVGNumberList.h" +#include "DOMSVGNumber.h" +#include "nsError.h" +#include "SVGAnimatedNumberList.h" +#include "nsCOMPtr.h" +#include "mozilla/dom/SVGNumberListBinding.h" +#include <algorithm> + +// See the comment in this file's header. + +// local helper functions +namespace { + +using mozilla::DOMSVGNumber; + +void UpdateListIndicesFromIndex(FallibleTArray<DOMSVGNumber*>& aItemsArray, + uint32_t aStartingIndex) +{ + uint32_t length = aItemsArray.Length(); + + for (uint32_t i = aStartingIndex; i < length; ++i) { + if (aItemsArray[i]) { + aItemsArray[i]->UpdateListIndex(i); + } + } +} + +} // namespace + +namespace mozilla { + +// We could use NS_IMPL_CYCLE_COLLECTION(, except that in Unlink() we need to +// clear our DOMSVGAnimatedNumberList'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(DOMSVGNumberList) + +NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(DOMSVGNumberList) + if (tmp->mAList) { + if (tmp->IsAnimValList()) { + tmp->mAList->mAnimVal = nullptr; + } else { + tmp->mAList->mBaseVal = nullptr; + } + NS_IMPL_CYCLE_COLLECTION_UNLINK(mAList) + } + NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER +NS_IMPL_CYCLE_COLLECTION_UNLINK_END +NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(DOMSVGNumberList) + NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mAList) + NS_IMPL_CYCLE_COLLECTION_TRAVERSE_SCRIPT_OBJECTS +NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END +NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(DOMSVGNumberList) + NS_IMPL_CYCLE_COLLECTION_TRACE_PRESERVED_WRAPPER +NS_IMPL_CYCLE_COLLECTION_TRACE_END + +NS_IMPL_CYCLE_COLLECTING_ADDREF(DOMSVGNumberList) +NS_IMPL_CYCLE_COLLECTING_RELEASE(DOMSVGNumberList) + +NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(DOMSVGNumberList) + NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY + NS_INTERFACE_MAP_ENTRY(nsISupports) +NS_INTERFACE_MAP_END + + +JSObject* +DOMSVGNumberList::WrapObject(JSContext *cx, JS::Handle<JSObject*> aGivenProto) +{ + return mozilla::dom::SVGNumberListBinding::Wrap(cx, this, aGivenProto); +} + +//---------------------------------------------------------------------- +// Helper class: AutoChangeNumberListNotifier +// Stack-based helper class to pair calls to WillChangeNumberList and +// DidChangeNumberList. +class MOZ_RAII AutoChangeNumberListNotifier +{ +public: + explicit AutoChangeNumberListNotifier(DOMSVGNumberList* aNumberList MOZ_GUARD_OBJECT_NOTIFIER_PARAM) + : mNumberList(aNumberList) + { + MOZ_GUARD_OBJECT_NOTIFIER_INIT; + MOZ_ASSERT(mNumberList, "Expecting non-null numberList"); + mEmptyOrOldValue = + mNumberList->Element()->WillChangeNumberList(mNumberList->AttrEnum()); + } + + ~AutoChangeNumberListNotifier() + { + mNumberList->Element()->DidChangeNumberList(mNumberList->AttrEnum(), + mEmptyOrOldValue); + if (mNumberList->IsAnimating()) { + mNumberList->Element()->AnimationNeedsResample(); + } + } + +private: + DOMSVGNumberList* const mNumberList; + nsAttrValue mEmptyOrOldValue; + MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER +}; + +void +DOMSVGNumberList::InternalListLengthWillChange(uint32_t aNewLength) +{ + uint32_t oldLength = mItems.Length(); + + if (aNewLength > DOMSVGNumber::MaxListIndex()) { + // It's safe to get out of sync with our internal list as long as we have + // FEWER items than it does. + aNewLength = DOMSVGNumber::MaxListIndex(); + } + + RefPtr<DOMSVGNumberList> kungFuDeathGrip; + if (aNewLength < oldLength) { + // RemovingFromList() might clear last reference to |this|. + // Retain a temporary reference to keep from dying before returning. + kungFuDeathGrip = this; + } + + // If our length will decrease, notify the items that will be removed: + for (uint32_t i = aNewLength; i < oldLength; ++i) { + if (mItems[i]) { + mItems[i]->RemovingFromList(); + } + } + + if (!mItems.SetLength(aNewLength, fallible)) { + // 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.Clear(); + return; + } + + // If our length has increased, null out the new pointers: + for (uint32_t i = oldLength; i < aNewLength; ++i) { + mItems[i] = nullptr; + } +} + +SVGNumberList& +DOMSVGNumberList::InternalList() const +{ + SVGAnimatedNumberList *alist = Element()->GetAnimatedNumberList(AttrEnum()); + return IsAnimValList() && alist->mAnimVal ? *alist->mAnimVal : alist->mBaseVal; +} + +void +DOMSVGNumberList::Clear(ErrorResult& error) +{ + if (IsAnimValList()) { + error.Throw(NS_ERROR_DOM_NO_MODIFICATION_ALLOWED_ERR); + return; + } + + if (LengthNoFlush() > 0) { + AutoChangeNumberListNotifier notifier(this); + // Notify any existing DOM items of removal *before* truncating the lists + // so that they can find their SVGNumber internal counterparts and copy + // their values. This also notifies the animVal list: + mAList->InternalBaseValListWillChangeTo(SVGNumberList()); + + mItems.Clear(); + InternalList().Clear(); + } +} + +already_AddRefed<DOMSVGNumber> +DOMSVGNumberList::Initialize(DOMSVGNumber& aItem, + ErrorResult& error) +{ + if (IsAnimValList()) { + error.Throw(NS_ERROR_DOM_NO_MODIFICATION_ALLOWED_ERR); + return nullptr; + } + + // 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. + RefPtr<DOMSVGNumber> domItem = aItem.HasOwner() ? aItem.Clone() : &aItem; + + Clear(error); + MOZ_ASSERT(!error.Failed()); + return InsertItemBefore(*domItem, 0, error); +} + +already_AddRefed<DOMSVGNumber> +DOMSVGNumberList::GetItem(uint32_t index, ErrorResult& error) +{ + bool found; + RefPtr<DOMSVGNumber> item = IndexedGetter(index, found, error); + if (!found) { + error.Throw(NS_ERROR_DOM_INDEX_SIZE_ERR); + } + return item.forget(); +} + +already_AddRefed<DOMSVGNumber> +DOMSVGNumberList::IndexedGetter(uint32_t index, bool& found, ErrorResult& error) +{ + if (IsAnimValList()) { + Element()->FlushAnimations(); + } + found = index < LengthNoFlush(); + if (found) { + return GetItemAt(index); + } + return nullptr; +} + +already_AddRefed<DOMSVGNumber> +DOMSVGNumberList::InsertItemBefore(DOMSVGNumber& aItem, + uint32_t index, + ErrorResult& error) +{ + if (IsAnimValList()) { + error.Throw(NS_ERROR_DOM_NO_MODIFICATION_ALLOWED_ERR); + return nullptr; + } + + index = std::min(index, LengthNoFlush()); + if (index >= DOMSVGNumber::MaxListIndex()) { + error.Throw(NS_ERROR_DOM_INDEX_SIZE_ERR); + return nullptr; + } + + // must do this before changing anything! + RefPtr<DOMSVGNumber> domItem = aItem.HasOwner() ? aItem.Clone() : &aItem; + + // Ensure we have enough memory so we can avoid complex error handling below: + if (!mItems.SetCapacity(mItems.Length() + 1, fallible) || + !InternalList().SetCapacity(InternalList().Length() + 1)) { + error.Throw(NS_ERROR_OUT_OF_MEMORY); + return nullptr; + } + if (AnimListMirrorsBaseList()) { + if (!mAList->mAnimVal->mItems.SetCapacity( + mAList->mAnimVal->mItems.Length() + 1, fallible)) { + error.Throw(NS_ERROR_OUT_OF_MEMORY); + return nullptr; + } + } + + AutoChangeNumberListNotifier notifier(this); + // Now that we know we're inserting, keep animVal list in sync as necessary. + MaybeInsertNullInAnimValListAt(index); + + InternalList().InsertItem(index, domItem->ToSVGNumber()); + MOZ_ALWAYS_TRUE(mItems.InsertElementAt(index, domItem, fallible)); + + // This MUST come after the insertion into InternalList(), or else under the + // insertion into InternalList() the values read from domItem would be bad + // data from InternalList() itself!: + domItem->InsertingIntoList(this, AttrEnum(), index, IsAnimValList()); + + UpdateListIndicesFromIndex(mItems, index + 1); + + return domItem.forget(); +} + +already_AddRefed<DOMSVGNumber> +DOMSVGNumberList::ReplaceItem(DOMSVGNumber& aItem, + uint32_t index, + ErrorResult& error) +{ + if (IsAnimValList()) { + error.Throw(NS_ERROR_DOM_NO_MODIFICATION_ALLOWED_ERR); + return nullptr; + } + + if (index >= LengthNoFlush()) { + error.Throw(NS_ERROR_DOM_INDEX_SIZE_ERR); + return nullptr; + } + + // must do this before changing anything! + RefPtr<DOMSVGNumber> domItem = aItem.HasOwner() ? aItem.Clone() : &aItem; + + AutoChangeNumberListNotifier notifier(this); + 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] = domItem->ToSVGNumber(); + mItems[index] = domItem; + + // This MUST come after the ToSVGPoint() call, otherwise that call + // would end up reading bad data from InternalList()! + domItem->InsertingIntoList(this, AttrEnum(), index, IsAnimValList()); + + return domItem.forget(); +} + +already_AddRefed<DOMSVGNumber> +DOMSVGNumberList::RemoveItem(uint32_t index, + ErrorResult& error) +{ + if (IsAnimValList()) { + error.Throw(NS_ERROR_DOM_NO_MODIFICATION_ALLOWED_ERR); + return nullptr; + } + + if (index >= LengthNoFlush()) { + error.Throw(NS_ERROR_DOM_INDEX_SIZE_ERR); + return nullptr; + } + + // Now that we know we're removing, keep animVal list in sync as necessary. + // Do this *before* touching InternalList() so the removed item can get its + // internal value. + MaybeRemoveItemFromAnimValListAt(index); + + // We have to return the removed item, so get it, creating it if necessary: + RefPtr<DOMSVGNumber> result = GetItemAt(index); + + AutoChangeNumberListNotifier notifier(this); + // 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); + mItems.RemoveElementAt(index); + + UpdateListIndicesFromIndex(mItems, index); + + return result.forget(); +} + +already_AddRefed<DOMSVGNumber> +DOMSVGNumberList::GetItemAt(uint32_t aIndex) +{ + MOZ_ASSERT(aIndex < mItems.Length()); + + if (!mItems[aIndex]) { + mItems[aIndex] = new DOMSVGNumber(this, AttrEnum(), aIndex, IsAnimValList()); + } + RefPtr<DOMSVGNumber> result = mItems[aIndex]; + return result.forget(); +} + +void +DOMSVGNumberList::MaybeInsertNullInAnimValListAt(uint32_t aIndex) +{ + MOZ_ASSERT(!IsAnimValList(), "call from baseVal to animVal"); + + if (!AnimListMirrorsBaseList()) { + return; + } + + DOMSVGNumberList* animVal = mAList->mAnimVal; + + MOZ_ASSERT(animVal, "AnimListMirrorsBaseList() promised a non-null animVal"); + MOZ_ASSERT(animVal->mItems.Length() == mItems.Length(), + "animVal list not in sync!"); + MOZ_ALWAYS_TRUE(animVal->mItems.InsertElementAt(aIndex, nullptr, fallible)); + + UpdateListIndicesFromIndex(animVal->mItems, aIndex + 1); +} + +void +DOMSVGNumberList::MaybeRemoveItemFromAnimValListAt(uint32_t aIndex) +{ + MOZ_ASSERT(!IsAnimValList(), "call from baseVal to animVal"); + + if (!AnimListMirrorsBaseList()) { + return; + } + + // This needs to be a strong reference; otherwise, the RemovingFromList call + // below might drop the last reference to animVal before we're done with it. + RefPtr<DOMSVGNumberList> animVal = mAList->mAnimVal; + + MOZ_ASSERT(animVal, "AnimListMirrorsBaseList() promised a non-null animVal"); + MOZ_ASSERT(animVal->mItems.Length() == mItems.Length(), + "animVal list not in sync!"); + + if (animVal->mItems[aIndex]) { + animVal->mItems[aIndex]->RemovingFromList(); + } + animVal->mItems.RemoveElementAt(aIndex); + + UpdateListIndicesFromIndex(animVal->mItems, aIndex); +} + +} // namespace mozilla diff --git a/dom/svg/DOMSVGNumberList.h b/dom/svg/DOMSVGNumberList.h new file mode 100644 index 0000000000..5cbfb57da8 --- /dev/null +++ b/dom/svg/DOMSVGNumberList.h @@ -0,0 +1,178 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef MOZILLA_DOMSVGNUMBERLIST_H__ +#define MOZILLA_DOMSVGNUMBERLIST_H__ + +#include "DOMSVGAnimatedNumberList.h" +#include "nsCycleCollectionParticipant.h" +#include "nsDebug.h" +#include "nsTArray.h" +#include "SVGNumberList.h" +#include "mozilla/Attributes.h" +#include "mozilla/ErrorResult.h" + +class nsSVGElement; + +namespace mozilla { + +class DOMSVGNumber; + +/** + * Class DOMSVGNumberList + * + * This class is used to create the DOM tearoff objects that wrap internal + * SVGNumberList objects. + * + * See the architecture comment in DOMSVGAnimatedNumberList.h. + * + * This class is strongly intertwined with DOMSVGAnimatedNumberList and + * DOMSVGNumber. We are a friend of DOMSVGAnimatedNumberList, and are + * responsible for nulling out our DOMSVGAnimatedNumberList's pointer to us + * when we die, essentially making its pointer to us a weak pointer. Similarly, + * our DOMSVGNumber 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 DOMSVGNumberList final : public nsISupports, + public nsWrapperCache +{ + friend class AutoChangeNumberListNotifier; + friend class DOMSVGNumber; + + ~DOMSVGNumberList() { + // 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 ) = nullptr; + } + } + +public: + NS_DECL_CYCLE_COLLECTING_ISUPPORTS + NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(DOMSVGNumberList) + + DOMSVGNumberList(DOMSVGAnimatedNumberList *aAList, + const SVGNumberList &aInternalList) + : mAList(aAList) + { + // aInternalList must be passed in explicitly because we can't use + // InternalList() here. (Because it depends on IsAnimValList, which depends + // on this object having been assigned to aAList's mBaseVal or mAnimVal, + // which hasn't happend yet.) + + InternalListLengthWillChange(aInternalList.Length()); // Sync mItems + } + + virtual JSObject* WrapObject(JSContext *cx, JS::Handle<JSObject*> aGivenProto) override; + + nsISupports* GetParentObject() + { + return static_cast<nsIContent*>(Element()); + } + + /** + * This will normally be the same as InternalList().Length(), except if we've + * hit OOM in which case our length will be zero. + */ + uint32_t LengthNoFlush() const { + MOZ_ASSERT(mItems.Length() == 0 || + mItems.Length() == 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(uint32_t aNewLength); + + /** + * Returns true if our attribute is animating (in which case our animVal is + * not simply a mirror of our baseVal). + */ + bool IsAnimating() const { + return mAList->IsAnimating(); + } + /** + * Returns true if there is an animated list mirroring the base list. + */ + bool AnimListMirrorsBaseList() const { + return mAList->mAnimVal && !mAList->IsAnimating(); + } + + uint32_t NumberOfItems() const + { + if (IsAnimValList()) { + Element()->FlushAnimations(); + } + return LengthNoFlush(); + } + void Clear(ErrorResult& error); + already_AddRefed<DOMSVGNumber> Initialize(DOMSVGNumber& newItem, + ErrorResult& error); + already_AddRefed<DOMSVGNumber> GetItem(uint32_t index, ErrorResult& error); + already_AddRefed<DOMSVGNumber> IndexedGetter(uint32_t index, bool& found, + ErrorResult& error); + already_AddRefed<DOMSVGNumber> InsertItemBefore(DOMSVGNumber& newItem, + uint32_t index, ErrorResult& error); + already_AddRefed<DOMSVGNumber> ReplaceItem(DOMSVGNumber& newItem, uint32_t index, + ErrorResult& error); + already_AddRefed<DOMSVGNumber> RemoveItem(uint32_t index, + ErrorResult& error); + already_AddRefed<DOMSVGNumber> AppendItem(DOMSVGNumber& newItem, + ErrorResult& error) + { + return InsertItemBefore(newItem, LengthNoFlush(), error); + } + uint32_t Length() const + { + return NumberOfItems(); + } + +private: + + nsSVGElement* Element() const { + return mAList->mElement; + } + + uint8_t AttrEnum() const { + return mAList->mAttrEnum; + } + + /// Used to determine if this list is the baseVal or animVal list. + bool IsAnimValList() const { + MOZ_ASSERT(this == mAList->mBaseVal || this == mAList->mAnimVal, + "Calling IsAnimValList() too early?!"); + return this == mAList->mAnimVal; + } + + /** + * Get a reference to this object's corresponding internal SVGNumberList. + * + * To simplify 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. + */ + SVGNumberList& InternalList() const; + + /// Returns the DOMSVGNumber at aIndex, creating it if necessary. + already_AddRefed<DOMSVGNumber> GetItemAt(uint32_t aIndex); + + void MaybeInsertNullInAnimValListAt(uint32_t aIndex); + void MaybeRemoveItemFromAnimValListAt(uint32_t aIndex); + + // Weak refs to our DOMSVGNumber items. The items are friends and take care + // of clearing our pointer to them when they die. + FallibleTArray<DOMSVGNumber*> mItems; + + RefPtr<DOMSVGAnimatedNumberList> mAList; +}; + +} // namespace mozilla + +#endif // MOZILLA_DOMSVGNUMBERLIST_H__ diff --git a/dom/svg/DOMSVGPathSeg.cpp b/dom/svg/DOMSVGPathSeg.cpp new file mode 100644 index 0000000000..95be3521e2 --- /dev/null +++ b/dom/svg/DOMSVGPathSeg.cpp @@ -0,0 +1,383 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "DOMSVGPathSeg.h" +#include "DOMSVGPathSegList.h" +#include "SVGAnimatedPathSegList.h" +#include "nsSVGElement.h" +#include "nsError.h" + +// See the architecture comment in DOMSVGPathSegList.h. + +namespace mozilla { + +// We could use NS_IMPL_CYCLE_COLLECTION(, 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(DOMSVGPathSeg) + +NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(DOMSVGPathSeg) + // We may not belong to a list, so we must null check tmp->mList. + if (tmp->mList) { + tmp->mList->ItemAt(tmp->mListIndex) = nullptr; + } +NS_IMPL_CYCLE_COLLECTION_UNLINK(mList) +NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER +NS_IMPL_CYCLE_COLLECTION_UNLINK_END + +NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(DOMSVGPathSeg) +NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mList) +NS_IMPL_CYCLE_COLLECTION_TRAVERSE_SCRIPT_OBJECTS +NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END + +NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(DOMSVGPathSeg) +NS_IMPL_CYCLE_COLLECTION_TRACE_PRESERVED_WRAPPER +NS_IMPL_CYCLE_COLLECTION_TRACE_END + +NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(DOMSVGPathSeg, AddRef) +NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(DOMSVGPathSeg, Release) + +//---------------------------------------------------------------------- +// Helper class: AutoChangePathSegNotifier +// Stack-based helper class to pair calls to WillChangePathSegList +// and DidChangePathSegList. +class MOZ_RAII AutoChangePathSegNotifier +{ +public: + explicit AutoChangePathSegNotifier(DOMSVGPathSeg* aPathSeg MOZ_GUARD_OBJECT_NOTIFIER_PARAM) + : mPathSeg(aPathSeg) + { + MOZ_GUARD_OBJECT_NOTIFIER_INIT; + MOZ_ASSERT(mPathSeg, "Expecting non-null pathSeg"); + MOZ_ASSERT(mPathSeg->HasOwner(), + "Expecting list to have an owner for notification"); + mEmptyOrOldValue = + mPathSeg->Element()->WillChangePathSegList(); + } + + ~AutoChangePathSegNotifier() + { + mPathSeg->Element()->DidChangePathSegList(mEmptyOrOldValue); + if (mPathSeg->mList->AttrIsAnimating()) { + mPathSeg->Element()->AnimationNeedsResample(); + } + } + +private: + DOMSVGPathSeg* const mPathSeg; + nsAttrValue mEmptyOrOldValue; + MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER +}; + +DOMSVGPathSeg::DOMSVGPathSeg(DOMSVGPathSegList *aList, + uint32_t aListIndex, + bool aIsAnimValItem) + : mList(aList) + , mListIndex(aListIndex) + , mIsAnimValItem(aIsAnimValItem) +{ + // These shifts are in sync with the members in the header. + MOZ_ASSERT(aList && aListIndex <= MaxListIndex(), "bad arg"); + + MOZ_ASSERT(IndexIsValid(), "Bad index for DOMSVGPathSeg!"); +} + +DOMSVGPathSeg::DOMSVGPathSeg() + : mList(nullptr) + , mListIndex(0) + , mIsAnimValItem(false) +{ +} + +void +DOMSVGPathSeg::InsertingIntoList(DOMSVGPathSegList *aList, + uint32_t aListIndex, + bool aIsAnimValItem) +{ + MOZ_ASSERT(!HasOwner(), "Inserting item that is already in a list"); + + mList = aList; + mListIndex = aListIndex; + mIsAnimValItem = aIsAnimValItem; + + MOZ_ASSERT(IndexIsValid(), "Bad index for DOMSVGPathSeg!"); +} + +void +DOMSVGPathSeg::RemovingFromList() +{ + uint32_t argCount = SVGPathSegUtils::ArgCountForType(Type()); + // InternalItem() + 1, because the args come after the encoded seg type + memcpy(PtrToMemberArgs(), InternalItem() + 1, argCount * sizeof(float)); + mList = nullptr; + mIsAnimValItem = false; +} + +void +DOMSVGPathSeg::ToSVGPathSegEncodedData(float* aRaw) +{ + MOZ_ASSERT(aRaw, "null pointer"); + uint32_t argCount = SVGPathSegUtils::ArgCountForType(Type()); + if (IsInList()) { + // 1 + argCount, because we're copying the encoded seg type and args + memcpy(aRaw, InternalItem(), (1 + argCount) * sizeof(float)); + } else { + aRaw[0] = SVGPathSegUtils::EncodeType(Type()); + // aRaw + 1, because the args go after the encoded seg type + memcpy(aRaw + 1, PtrToMemberArgs(), argCount * sizeof(float)); + } +} + +float* +DOMSVGPathSeg::InternalItem() +{ + uint32_t dataIndex = mList->mItems[mListIndex].mInternalDataIndex; + return &(mList->InternalList().mData[dataIndex]); +} + +#ifdef DEBUG +bool +DOMSVGPathSeg::IndexIsValid() +{ + SVGAnimatedPathSegList *alist = Element()->GetAnimPathSegList(); + return (mIsAnimValItem && + mListIndex < alist->GetAnimValue().CountItems()) || + (!mIsAnimValItem && + mListIndex < alist->GetBaseValue().CountItems()); +} +#endif + + +//////////////////////////////////////////////////////////////////////// +// Implementation of DOMSVGPathSeg sub-classes below this point + +#define IMPL_PROP_WITH_TYPE(segName, propName, index, type) \ + type \ + DOMSVGPathSeg##segName::propName() \ + { \ + if (mIsAnimValItem && HasOwner()) { \ + Element()->FlushAnimations(); /* May make HasOwner() == false */ \ + } \ + return type(HasOwner() ? InternalItem()[1+index] : mArgs[index]); \ + } \ + void \ + DOMSVGPathSeg##segName::Set##propName(type a##propName, ErrorResult& rv) \ + { \ + if (mIsAnimValItem) { \ + rv.Throw(NS_ERROR_DOM_NO_MODIFICATION_ALLOWED_ERR); \ + return; \ + } \ + if (HasOwner()) { \ + if (InternalItem()[1+index] == float(a##propName)) { \ + return; \ + } \ + AutoChangePathSegNotifier notifier(this); \ + InternalItem()[1+index] = float(a##propName); \ + } else { \ + mArgs[index] = float(a##propName); \ + } \ + } + +// For float, the normal type of arguments +#define IMPL_FLOAT_PROP(segName, propName, index) \ + IMPL_PROP_WITH_TYPE(segName, propName, index, float) + +// For the boolean flags in arc commands +#define IMPL_BOOL_PROP(segName, propName, index) \ + IMPL_PROP_WITH_TYPE(segName, propName, index, bool) + + +/////////////////////////////////////////////////////////////////////// + +IMPL_FLOAT_PROP(MovetoAbs, X, 0) +IMPL_FLOAT_PROP(MovetoAbs, Y, 1) + + +//////////////////////////////////////////////////////////////////////// + +IMPL_FLOAT_PROP(MovetoRel, X, 0) +IMPL_FLOAT_PROP(MovetoRel, Y, 1) + + + +//////////////////////////////////////////////////////////////////////// + +IMPL_FLOAT_PROP(LinetoAbs, X, 0) +IMPL_FLOAT_PROP(LinetoAbs, Y, 1) + + +//////////////////////////////////////////////////////////////////////// + +IMPL_FLOAT_PROP(LinetoRel, X, 0) +IMPL_FLOAT_PROP(LinetoRel, Y, 1) + + +//////////////////////////////////////////////////////////////////////// + +IMPL_FLOAT_PROP(CurvetoCubicAbs, X1, 0) +IMPL_FLOAT_PROP(CurvetoCubicAbs, Y1, 1) +IMPL_FLOAT_PROP(CurvetoCubicAbs, X2, 2) +IMPL_FLOAT_PROP(CurvetoCubicAbs, Y2, 3) +IMPL_FLOAT_PROP(CurvetoCubicAbs, X, 4) +IMPL_FLOAT_PROP(CurvetoCubicAbs, Y, 5) + + +//////////////////////////////////////////////////////////////////////// + +IMPL_FLOAT_PROP(CurvetoCubicRel, X1, 0) +IMPL_FLOAT_PROP(CurvetoCubicRel, Y1, 1) +IMPL_FLOAT_PROP(CurvetoCubicRel, X2, 2) +IMPL_FLOAT_PROP(CurvetoCubicRel, Y2, 3) +IMPL_FLOAT_PROP(CurvetoCubicRel, X, 4) +IMPL_FLOAT_PROP(CurvetoCubicRel, Y, 5) + + +//////////////////////////////////////////////////////////////////////// + +IMPL_FLOAT_PROP(CurvetoQuadraticAbs, X1, 0) +IMPL_FLOAT_PROP(CurvetoQuadraticAbs, Y1, 1) +IMPL_FLOAT_PROP(CurvetoQuadraticAbs, X, 2) +IMPL_FLOAT_PROP(CurvetoQuadraticAbs, Y, 3) + + +//////////////////////////////////////////////////////////////////////// + +IMPL_FLOAT_PROP(CurvetoQuadraticRel, X1, 0) +IMPL_FLOAT_PROP(CurvetoQuadraticRel, Y1, 1) +IMPL_FLOAT_PROP(CurvetoQuadraticRel, X, 2) +IMPL_FLOAT_PROP(CurvetoQuadraticRel, Y, 3) + + +//////////////////////////////////////////////////////////////////////// + +IMPL_FLOAT_PROP(ArcAbs, R1, 0) +IMPL_FLOAT_PROP(ArcAbs, R2, 1) +IMPL_FLOAT_PROP(ArcAbs, Angle, 2) +IMPL_BOOL_PROP(ArcAbs, LargeArcFlag, 3) +IMPL_BOOL_PROP(ArcAbs, SweepFlag, 4) +IMPL_FLOAT_PROP(ArcAbs, X, 5) +IMPL_FLOAT_PROP(ArcAbs, Y, 6) + + +//////////////////////////////////////////////////////////////////////// + +IMPL_FLOAT_PROP(ArcRel, R1, 0) +IMPL_FLOAT_PROP(ArcRel, R2, 1) +IMPL_FLOAT_PROP(ArcRel, Angle, 2) +IMPL_BOOL_PROP(ArcRel, LargeArcFlag, 3) +IMPL_BOOL_PROP(ArcRel, SweepFlag, 4) +IMPL_FLOAT_PROP(ArcRel, X, 5) +IMPL_FLOAT_PROP(ArcRel, Y, 6) + + +//////////////////////////////////////////////////////////////////////// + +IMPL_FLOAT_PROP(LinetoHorizontalAbs, X, 0) + + +//////////////////////////////////////////////////////////////////////// + +IMPL_FLOAT_PROP(LinetoHorizontalRel, X, 0) + + +//////////////////////////////////////////////////////////////////////// + +IMPL_FLOAT_PROP(LinetoVerticalAbs, Y, 0) + + +//////////////////////////////////////////////////////////////////////// + +IMPL_FLOAT_PROP(LinetoVerticalRel, Y, 0) + + +//////////////////////////////////////////////////////////////////////// + +IMPL_FLOAT_PROP(CurvetoCubicSmoothAbs, X2, 0) +IMPL_FLOAT_PROP(CurvetoCubicSmoothAbs, Y2, 1) +IMPL_FLOAT_PROP(CurvetoCubicSmoothAbs, X, 2) +IMPL_FLOAT_PROP(CurvetoCubicSmoothAbs, Y, 3) + + +//////////////////////////////////////////////////////////////////////// + +IMPL_FLOAT_PROP(CurvetoCubicSmoothRel, X2, 0) +IMPL_FLOAT_PROP(CurvetoCubicSmoothRel, Y2, 1) +IMPL_FLOAT_PROP(CurvetoCubicSmoothRel, X, 2) +IMPL_FLOAT_PROP(CurvetoCubicSmoothRel, Y, 3) + + +//////////////////////////////////////////////////////////////////////// + +IMPL_FLOAT_PROP(CurvetoQuadraticSmoothAbs, X, 0) +IMPL_FLOAT_PROP(CurvetoQuadraticSmoothAbs, Y, 1) + + +//////////////////////////////////////////////////////////////////////// + + +IMPL_FLOAT_PROP(CurvetoQuadraticSmoothRel, X, 0) +IMPL_FLOAT_PROP(CurvetoQuadraticSmoothRel, Y, 1) + + + +// This must come after DOMSVGPathSegClosePath et. al. have been declared. +/* static */ DOMSVGPathSeg* +DOMSVGPathSeg::CreateFor(DOMSVGPathSegList *aList, + uint32_t aListIndex, + bool aIsAnimValItem) +{ + uint32_t dataIndex = aList->mItems[aListIndex].mInternalDataIndex; + float *data = &aList->InternalList().mData[dataIndex]; + uint32_t type = SVGPathSegUtils::DecodeType(data[0]); + + switch (type) + { + case PATHSEG_CLOSEPATH: + return new DOMSVGPathSegClosePath(aList, aListIndex, aIsAnimValItem); + case PATHSEG_MOVETO_ABS: + return new DOMSVGPathSegMovetoAbs(aList, aListIndex, aIsAnimValItem); + case PATHSEG_MOVETO_REL: + return new DOMSVGPathSegMovetoRel(aList, aListIndex, aIsAnimValItem); + case PATHSEG_LINETO_ABS: + return new DOMSVGPathSegLinetoAbs(aList, aListIndex, aIsAnimValItem); + case PATHSEG_LINETO_REL: + return new DOMSVGPathSegLinetoRel(aList, aListIndex, aIsAnimValItem); + case PATHSEG_CURVETO_CUBIC_ABS: + return new DOMSVGPathSegCurvetoCubicAbs(aList, aListIndex, aIsAnimValItem); + case PATHSEG_CURVETO_CUBIC_REL: + return new DOMSVGPathSegCurvetoCubicRel(aList, aListIndex, aIsAnimValItem); + case PATHSEG_CURVETO_QUADRATIC_ABS: + return new DOMSVGPathSegCurvetoQuadraticAbs(aList, aListIndex, aIsAnimValItem); + case PATHSEG_CURVETO_QUADRATIC_REL: + return new DOMSVGPathSegCurvetoQuadraticRel(aList, aListIndex, aIsAnimValItem); + case PATHSEG_ARC_ABS: + return new DOMSVGPathSegArcAbs(aList, aListIndex, aIsAnimValItem); + case PATHSEG_ARC_REL: + return new DOMSVGPathSegArcRel(aList, aListIndex, aIsAnimValItem); + case PATHSEG_LINETO_HORIZONTAL_ABS: + return new DOMSVGPathSegLinetoHorizontalAbs(aList, aListIndex, aIsAnimValItem); + case PATHSEG_LINETO_HORIZONTAL_REL: + return new DOMSVGPathSegLinetoHorizontalRel(aList, aListIndex, aIsAnimValItem); + case PATHSEG_LINETO_VERTICAL_ABS: + return new DOMSVGPathSegLinetoVerticalAbs(aList, aListIndex, aIsAnimValItem); + case PATHSEG_LINETO_VERTICAL_REL: + return new DOMSVGPathSegLinetoVerticalRel(aList, aListIndex, aIsAnimValItem); + case PATHSEG_CURVETO_CUBIC_SMOOTH_ABS: + return new DOMSVGPathSegCurvetoCubicSmoothAbs(aList, aListIndex, aIsAnimValItem); + case PATHSEG_CURVETO_CUBIC_SMOOTH_REL: + return new DOMSVGPathSegCurvetoCubicSmoothRel(aList, aListIndex, aIsAnimValItem); + case PATHSEG_CURVETO_QUADRATIC_SMOOTH_ABS: + return new DOMSVGPathSegCurvetoQuadraticSmoothAbs(aList, aListIndex, aIsAnimValItem); + case PATHSEG_CURVETO_QUADRATIC_SMOOTH_REL: + return new DOMSVGPathSegCurvetoQuadraticSmoothRel(aList, aListIndex, aIsAnimValItem); + default: + NS_NOTREACHED("Invalid path segment type"); + return nullptr; + } +} + +} // namespace mozilla diff --git a/dom/svg/DOMSVGPathSeg.h b/dom/svg/DOMSVGPathSeg.h new file mode 100644 index 0000000000..33efe719b0 --- /dev/null +++ b/dom/svg/DOMSVGPathSeg.h @@ -0,0 +1,723 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef MOZILLA_DOMSVGPATHSEG_H__ +#define MOZILLA_DOMSVGPATHSEG_H__ + +#include "DOMSVGPathSegList.h" +#include "nsCycleCollectionParticipant.h" +#include "nsWrapperCache.h" +#include "SVGPathSegUtils.h" +#include "mozilla/dom/SVGPathSegBinding.h" + +class nsSVGElement; + +#define MOZ_SVG_LIST_INDEX_BIT_COUNT 31 + +namespace mozilla { + +#define CHECK_ARG_COUNT_IN_SYNC(segType) \ + MOZ_ASSERT(ArrayLength(mArgs) == \ + SVGPathSegUtils::ArgCountForType(uint32_t(segType)) || \ + uint32_t(segType) == PATHSEG_CLOSEPATH, \ + "Arg count/array size out of sync") + +#define IMPL_SVGPATHSEG_SUBCLASS_COMMON(segName, segType) \ + explicit DOMSVGPathSeg##segName(const float *aArgs) \ + : DOMSVGPathSeg() \ + { \ + CHECK_ARG_COUNT_IN_SYNC(segType); \ + memcpy(mArgs, aArgs, \ + SVGPathSegUtils::ArgCountForType(uint32_t(segType)) * sizeof(float)); \ + } \ + DOMSVGPathSeg##segName(DOMSVGPathSegList *aList, \ + uint32_t aListIndex, \ + bool aIsAnimValItem) \ + : DOMSVGPathSeg(aList, aListIndex, aIsAnimValItem) \ + { \ + CHECK_ARG_COUNT_IN_SYNC(segType); \ + } \ + /* From DOMSVGPathSeg: */ \ + virtual uint32_t \ + Type() const override \ + { \ + return segType; \ + } \ + virtual DOMSVGPathSeg* \ + Clone() override \ + { \ + /* InternalItem() + 1, because we're skipping the encoded seg type */ \ + float *args = IsInList() ? InternalItem() + 1 : mArgs; \ + return new DOMSVGPathSeg##segName(args); \ + } \ + virtual float* \ + PtrToMemberArgs() override \ + { \ + return mArgs; \ + } \ + \ + virtual JSObject* \ + WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override \ + { \ + return dom::SVGPathSeg##segName##Binding::Wrap(aCx, this, aGivenProto); \ + } + + +/** + * Class DOMSVGPathSeg + * + * This class is the base class of the classes that create the DOM objects that + * wrap the internal path segments that are encoded in an SVGPathData. Its + * sub-classes are also used to create the objects returned by + * SVGPathElement.createSVGPathSegXxx(). + * + * See the architecture comment in DOMSVGPathSegList.h for an overview of the + * important points regarding these DOM wrapper structures. + * + * See the architecture comment in DOMSVGLength.h (yes, LENGTH) for an overview + * of the important points regarding how this specific class works. + * + * The main differences between this class and DOMSVGLength is that we have + * sub-classes (it does not), and the "internal counterpart" that we provide a + * DOM wrapper for is a list of floats, not an instance of an internal class. + */ +class DOMSVGPathSeg : public nsWrapperCache +{ + friend class AutoChangePathSegNotifier; + +public: + NS_INLINE_DECL_CYCLE_COLLECTING_NATIVE_REFCOUNTING(DOMSVGPathSeg) + NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_NATIVE_CLASS(DOMSVGPathSeg) + + /** + * Unlike the other list classes, we hide our ctor (because no one should be + * creating instances of this class directly). This factory method in exposed + * instead to take care of creating instances of the correct sub-class. + */ + static DOMSVGPathSeg *CreateFor(DOMSVGPathSegList *aList, + uint32_t aListIndex, + bool aIsAnimValItem); + + /** + * Create an unowned copy of this object. The caller is responsible for the + * first AddRef()! + */ + virtual DOMSVGPathSeg* Clone() = 0; + + bool IsInList() const { + return !!mList; + } + + /** + * In future, if this class is used for non-list segments, this will be + * different to IsInList(). + */ + bool 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 DOMSVGPathSegList).) + */ + void InsertingIntoList(DOMSVGPathSegList *aList, + uint32_t aListIndex, + bool aIsAnimValItem); + + static uint32_t MaxListIndex() { + return (1U << MOZ_SVG_LIST_INDEX_BIT_COUNT) - 1; + } + + /// This method is called to notify this object that its list index changed. + void UpdateListIndex(uint32_t 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 + * "lose" its value on being removed.) + */ + void RemovingFromList(); + + /** + * This method converts the segment to a string of floats as found in + * SVGPathData (i.e. the first float contains the type of the segment, + * encoded into a float, followed by its arguments in the same order as they + * are given in the <path> element's 'd' attribute). + */ + void ToSVGPathSegEncodedData(float *aData); + + /** + * The type of this path segment. + */ + virtual uint32_t Type() const = 0; + + // WebIDL + DOMSVGPathSegList* GetParentObject() { return mList; } + uint16_t PathSegType() const { return Type(); } + void GetPathSegTypeAsLetter(nsAString &aPathSegTypeAsLetter) + { aPathSegTypeAsLetter = SVGPathSegUtils::GetPathSegTypeAsLetter(Type()); } + virtual JSObject* WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override = 0; + +protected: + + /** + * Generic ctor for DOMSVGPathSeg objects that are created for an attribute. + */ + DOMSVGPathSeg(DOMSVGPathSegList *aList, + uint32_t aListIndex, + bool aIsAnimValItem); + + /** + * Ctor for creating the objects returned by + * SVGPathElement.createSVGPathSegXxx(), which do not initially belong to an + * attribute. + */ + DOMSVGPathSeg(); + + virtual ~DOMSVGPathSeg() { + // 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->ItemAt(mListIndex) = nullptr; + } + } + + nsSVGElement* Element() { + return mList->Element(); + } + + /** + * Get a reference to the internal SVGPathSeg list item that this DOM wrapper + * object currently wraps. + * + * To simplify 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. + */ + float* InternalItem(); + + virtual float* PtrToMemberArgs() = 0; + +#ifdef DEBUG + bool IndexIsValid(); +#endif + + RefPtr<DOMSVGPathSegList> 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. + + uint32_t mListIndex:MOZ_SVG_LIST_INDEX_BIT_COUNT; + uint32_t mIsAnimValItem:1; // uint32_t because MSVC won't pack otherwise +}; + +class DOMSVGPathSegClosePath + : public DOMSVGPathSeg +{ +public: + DOMSVGPathSegClosePath() + : DOMSVGPathSeg() + { + } + + IMPL_SVGPATHSEG_SUBCLASS_COMMON(ClosePath, PATHSEG_CLOSEPATH) + +protected: + // To allow IMPL_SVGPATHSEG_SUBCLASS_COMMON above to compile we need an + // mArgs, but since C++ doesn't allow zero-sized arrays we need to give it + // one (unused) element. + float mArgs[1]; +}; + +class DOMSVGPathSegMovetoAbs + : public DOMSVGPathSeg +{ +public: + DOMSVGPathSegMovetoAbs(float x, float y) + : DOMSVGPathSeg() + { + mArgs[0] = x; + mArgs[1] = y; + } + + IMPL_SVGPATHSEG_SUBCLASS_COMMON(MovetoAbs, PATHSEG_MOVETO_ABS) + + float X(); + void SetX(float aX, ErrorResult& rv); + float Y(); + void SetY(float aY, ErrorResult& rv); + +protected: + float mArgs[2]; +}; + +class DOMSVGPathSegMovetoRel + : public DOMSVGPathSeg +{ +public: + DOMSVGPathSegMovetoRel(float x, float y) + : DOMSVGPathSeg() + { + mArgs[0] = x; + mArgs[1] = y; + } + + IMPL_SVGPATHSEG_SUBCLASS_COMMON(MovetoRel, PATHSEG_MOVETO_REL) + + float X(); + void SetX(float aX, ErrorResult& rv); + float Y(); + void SetY(float aY, ErrorResult& rv); + +protected: + float mArgs[2]; +}; + +class DOMSVGPathSegLinetoAbs + : public DOMSVGPathSeg +{ +public: + DOMSVGPathSegLinetoAbs(float x, float y) + : DOMSVGPathSeg() + { + mArgs[0] = x; + mArgs[1] = y; + } + + IMPL_SVGPATHSEG_SUBCLASS_COMMON(LinetoAbs, PATHSEG_LINETO_ABS) + + float X(); + void SetX(float aX, ErrorResult& rv); + float Y(); + void SetY(float aY, ErrorResult& rv); + +protected: + float mArgs[2]; +}; + +class DOMSVGPathSegLinetoRel + : public DOMSVGPathSeg +{ +public: + DOMSVGPathSegLinetoRel(float x, float y) + : DOMSVGPathSeg() + { + mArgs[0] = x; + mArgs[1] = y; + } + + IMPL_SVGPATHSEG_SUBCLASS_COMMON(LinetoRel, PATHSEG_LINETO_REL) + + float X(); + void SetX(float aX, ErrorResult& rv); + float Y(); + void SetY(float aY, ErrorResult& rv); + +protected: + float mArgs[2]; +}; + +class DOMSVGPathSegCurvetoCubicAbs + : public DOMSVGPathSeg +{ +public: + DOMSVGPathSegCurvetoCubicAbs(float x1, float y1, + float x2, float y2, + float x, float y) + : DOMSVGPathSeg() + { + mArgs[0] = x1; + mArgs[1] = y1; + mArgs[2] = x2; + mArgs[3] = y2; + mArgs[4] = x; + mArgs[5] = y; + } + + float X(); + void SetX(float aX, ErrorResult& rv); + float Y(); + void SetY(float aY, ErrorResult& rv); + float X1(); + void SetX1(float aX1, ErrorResult& rv); + float Y1(); + void SetY1(float aY1, ErrorResult& rv); + float X2(); + void SetX2(float aX2, ErrorResult& rv); + float Y2(); + void SetY2(float aY2, ErrorResult& rv); + + IMPL_SVGPATHSEG_SUBCLASS_COMMON(CurvetoCubicAbs, PATHSEG_CURVETO_CUBIC_ABS) + +protected: + float mArgs[6]; +}; + +class DOMSVGPathSegCurvetoCubicRel + : public DOMSVGPathSeg +{ +public: + DOMSVGPathSegCurvetoCubicRel(float x1, float y1, + float x2, float y2, + float x, float y) + : DOMSVGPathSeg() + { + mArgs[0] = x1; + mArgs[1] = y1; + mArgs[2] = x2; + mArgs[3] = y2; + mArgs[4] = x; + mArgs[5] = y; + } + + IMPL_SVGPATHSEG_SUBCLASS_COMMON(CurvetoCubicRel, PATHSEG_CURVETO_CUBIC_REL) + + float X(); + void SetX(float aX, ErrorResult& rv); + float Y(); + void SetY(float aY, ErrorResult& rv); + float X1(); + void SetX1(float aX1, ErrorResult& rv); + float Y1(); + void SetY1(float aY1, ErrorResult& rv); + float X2(); + void SetX2(float aX2, ErrorResult& rv); + float Y2(); + void SetY2(float aY2, ErrorResult& rv); + +protected: + float mArgs[6]; +}; + +class DOMSVGPathSegCurvetoQuadraticAbs + : public DOMSVGPathSeg +{ +public: + DOMSVGPathSegCurvetoQuadraticAbs(float x1, float y1, + float x, float y) + : DOMSVGPathSeg() + { + mArgs[0] = x1; + mArgs[1] = y1; + mArgs[2] = x; + mArgs[3] = y; + } + + IMPL_SVGPATHSEG_SUBCLASS_COMMON(CurvetoQuadraticAbs, PATHSEG_CURVETO_QUADRATIC_ABS) + + float X(); + void SetX(float aX, ErrorResult& rv); + float Y(); + void SetY(float aY, ErrorResult& rv); + float X1(); + void SetX1(float aX1, ErrorResult& rv); + float Y1(); + void SetY1(float aY1, ErrorResult& rv); + +protected: + float mArgs[4]; +}; + +class DOMSVGPathSegCurvetoQuadraticRel + : public DOMSVGPathSeg +{ +public: + DOMSVGPathSegCurvetoQuadraticRel(float x1, float y1, + float x, float y) + : DOMSVGPathSeg() + { + mArgs[0] = x1; + mArgs[1] = y1; + mArgs[2] = x; + mArgs[3] = y; + } + + IMPL_SVGPATHSEG_SUBCLASS_COMMON(CurvetoQuadraticRel, PATHSEG_CURVETO_QUADRATIC_REL) + + float X(); + void SetX(float aX, ErrorResult& rv); + float Y(); + void SetY(float aY, ErrorResult& rv); + float X1(); + void SetX1(float aX1, ErrorResult& rv); + float Y1(); + void SetY1(float aY1, ErrorResult& rv); + +protected: + float mArgs[4]; +}; + +class DOMSVGPathSegArcAbs + : public DOMSVGPathSeg +{ +public: + DOMSVGPathSegArcAbs(float r1, float r2, float angle, + bool largeArcFlag, bool sweepFlag, + float x, float y) + : DOMSVGPathSeg() + { + mArgs[0] = r1; + mArgs[1] = r2; + mArgs[2] = angle; + mArgs[3] = largeArcFlag; + mArgs[4] = sweepFlag; + mArgs[5] = x; + mArgs[6] = y; + } + + IMPL_SVGPATHSEG_SUBCLASS_COMMON(ArcAbs, PATHSEG_ARC_ABS) + + float X(); + void SetX(float aX, ErrorResult& rv); + float Y(); + void SetY(float aY, ErrorResult& rv); + float R1(); + void SetR1(float aR1, ErrorResult& rv); + float R2(); + void SetR2(float aR2, ErrorResult& rv); + float Angle(); + void SetAngle(float aAngle, ErrorResult& rv); + bool LargeArcFlag(); + void SetLargeArcFlag(bool aFlag, ErrorResult& rv); + bool SweepFlag(); + void SetSweepFlag(bool aFlag, ErrorResult& rv); + +protected: + float mArgs[7]; +}; + +class DOMSVGPathSegArcRel + : public DOMSVGPathSeg +{ +public: + DOMSVGPathSegArcRel(float r1, float r2, float angle, + bool largeArcFlag, bool sweepFlag, + float x, float y) + : DOMSVGPathSeg() + { + mArgs[0] = r1; + mArgs[1] = r2; + mArgs[2] = angle; + mArgs[3] = largeArcFlag; + mArgs[4] = sweepFlag; + mArgs[5] = x; + mArgs[6] = y; + } + + IMPL_SVGPATHSEG_SUBCLASS_COMMON(ArcRel, PATHSEG_ARC_REL) + + float X(); + void SetX(float aX, ErrorResult& rv); + float Y(); + void SetY(float aY, ErrorResult& rv); + float R1(); + void SetR1(float aR1, ErrorResult& rv); + float R2(); + void SetR2(float aR2, ErrorResult& rv); + float Angle(); + void SetAngle(float aAngle, ErrorResult& rv); + bool LargeArcFlag(); + void SetLargeArcFlag(bool aFlag, ErrorResult& rv); + bool SweepFlag(); + void SetSweepFlag(bool aFlag, ErrorResult& rv); + +protected: + float mArgs[7]; +}; + +class DOMSVGPathSegLinetoHorizontalAbs + : public DOMSVGPathSeg +{ +public: + explicit DOMSVGPathSegLinetoHorizontalAbs(float x) + : DOMSVGPathSeg() + { + mArgs[0] = x; + } + + IMPL_SVGPATHSEG_SUBCLASS_COMMON(LinetoHorizontalAbs, PATHSEG_LINETO_HORIZONTAL_ABS) + + float X(); + void SetX(float aX, ErrorResult& rv); + +protected: + float mArgs[1]; +}; + +class DOMSVGPathSegLinetoHorizontalRel + : public DOMSVGPathSeg +{ +public: + explicit DOMSVGPathSegLinetoHorizontalRel(float x) + : DOMSVGPathSeg() + { + mArgs[0] = x; + } + + IMPL_SVGPATHSEG_SUBCLASS_COMMON(LinetoHorizontalRel, PATHSEG_LINETO_HORIZONTAL_REL) + + float X(); + void SetX(float aX, ErrorResult& rv); + +protected: + float mArgs[1]; +}; + +class DOMSVGPathSegLinetoVerticalAbs + : public DOMSVGPathSeg +{ +public: + explicit DOMSVGPathSegLinetoVerticalAbs(float y) + : DOMSVGPathSeg() + { + mArgs[0] = y; + } + + IMPL_SVGPATHSEG_SUBCLASS_COMMON(LinetoVerticalAbs, PATHSEG_LINETO_VERTICAL_ABS) + + float Y(); + void SetY(float aY, ErrorResult& rv); + +protected: + float mArgs[1]; +}; + +class DOMSVGPathSegLinetoVerticalRel + : public DOMSVGPathSeg +{ +public: + explicit DOMSVGPathSegLinetoVerticalRel(float y) + : DOMSVGPathSeg() + { + mArgs[0] = y; + } + + IMPL_SVGPATHSEG_SUBCLASS_COMMON(LinetoVerticalRel, PATHSEG_LINETO_VERTICAL_REL) + + float Y(); + void SetY(float aY, ErrorResult& rv); + +protected: + float mArgs[1]; +}; + +class DOMSVGPathSegCurvetoCubicSmoothAbs + : public DOMSVGPathSeg +{ +public: + DOMSVGPathSegCurvetoCubicSmoothAbs(float x2, float y2, + float x, float y) + : DOMSVGPathSeg() + { + mArgs[0] = x2; + mArgs[1] = y2; + mArgs[2] = x; + mArgs[3] = y; + } + + IMPL_SVGPATHSEG_SUBCLASS_COMMON(CurvetoCubicSmoothAbs, PATHSEG_CURVETO_CUBIC_SMOOTH_ABS) + + float X(); + void SetX(float aX, ErrorResult& rv); + float Y(); + void SetY(float aY, ErrorResult& rv); + float X2(); + void SetX2(float aX2, ErrorResult& rv); + float Y2(); + void SetY2(float aY2, ErrorResult& rv); + +protected: + float mArgs[4]; +}; + +class DOMSVGPathSegCurvetoCubicSmoothRel + : public DOMSVGPathSeg +{ +public: + DOMSVGPathSegCurvetoCubicSmoothRel(float x2, float y2, + float x, float y) + : DOMSVGPathSeg() + { + mArgs[0] = x2; + mArgs[1] = y2; + mArgs[2] = x; + mArgs[3] = y; + } + + IMPL_SVGPATHSEG_SUBCLASS_COMMON(CurvetoCubicSmoothRel, PATHSEG_CURVETO_CUBIC_SMOOTH_REL) + + float X(); + void SetX(float aX, ErrorResult& rv); + float Y(); + void SetY(float aY, ErrorResult& rv); + float X2(); + void SetX2(float aX2, ErrorResult& rv); + float Y2(); + void SetY2(float aY2, ErrorResult& rv); + +protected: + float mArgs[4]; +}; + +class DOMSVGPathSegCurvetoQuadraticSmoothAbs + : public DOMSVGPathSeg +{ +public: + DOMSVGPathSegCurvetoQuadraticSmoothAbs(float x, float y) + : DOMSVGPathSeg() + { + mArgs[0] = x; + mArgs[1] = y; + } + + IMPL_SVGPATHSEG_SUBCLASS_COMMON(CurvetoQuadraticSmoothAbs, PATHSEG_CURVETO_QUADRATIC_SMOOTH_ABS) + + float X(); + void SetX(float aX, ErrorResult& rv); + float Y(); + void SetY(float aY, ErrorResult& rv); + +protected: + float mArgs[2]; +}; + +class DOMSVGPathSegCurvetoQuadraticSmoothRel + : public DOMSVGPathSeg +{ +public: + DOMSVGPathSegCurvetoQuadraticSmoothRel(float x, float y) + : DOMSVGPathSeg() + { + mArgs[0] = x; + mArgs[1] = y; + } + + IMPL_SVGPATHSEG_SUBCLASS_COMMON(CurvetoQuadraticSmoothRel, PATHSEG_CURVETO_QUADRATIC_SMOOTH_REL) + + float X(); + void SetX(float aX, ErrorResult& rv); + float Y(); + void SetY(float aY, ErrorResult& rv); + +protected: + float mArgs[2]; +}; + +} // namespace mozilla + +#undef MOZ_SVG_LIST_INDEX_BIT_COUNT + +#endif // MOZILLA_DOMSVGPATHSEG_H__ diff --git a/dom/svg/DOMSVGPathSegList.cpp b/dom/svg/DOMSVGPathSegList.cpp new file mode 100644 index 0000000000..edacdfc93e --- /dev/null +++ b/dom/svg/DOMSVGPathSegList.cpp @@ -0,0 +1,607 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "nsSVGElement.h" +#include "DOMSVGPathSegList.h" +#include "DOMSVGPathSeg.h" +#include "nsError.h" +#include "SVGAnimatedPathSegList.h" +#include "nsCOMPtr.h" +#include "nsSVGAttrTearoffTable.h" +#include "SVGPathSegUtils.h" +#include "mozilla/dom/SVGPathSegListBinding.h" + +// See the comment in this file's header. + +namespace mozilla { + + static inline +nsSVGAttrTearoffTable<void, DOMSVGPathSegList>& +SVGPathSegListTearoffTable() +{ + static nsSVGAttrTearoffTable<void, DOMSVGPathSegList> + sSVGPathSegListTearoffTable; + return sSVGPathSegListTearoffTable; +} + +NS_IMPL_CYCLE_COLLECTION_CLASS(DOMSVGPathSegList) + +NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(DOMSVGPathSegList) + // No unlinking of mElement, we'd need to null out the value pointer (the + // object it points to is held by the element) and null-check it everywhere. + NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER +NS_IMPL_CYCLE_COLLECTION_UNLINK_END +NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(DOMSVGPathSegList) + NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mElement) + NS_IMPL_CYCLE_COLLECTION_TRAVERSE_SCRIPT_OBJECTS +NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END +NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(DOMSVGPathSegList) + NS_IMPL_CYCLE_COLLECTION_TRACE_PRESERVED_WRAPPER +NS_IMPL_CYCLE_COLLECTION_TRACE_END + +NS_IMPL_CYCLE_COLLECTING_ADDREF(DOMSVGPathSegList) +NS_IMPL_CYCLE_COLLECTING_RELEASE(DOMSVGPathSegList) + +NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(DOMSVGPathSegList) + NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY + NS_INTERFACE_MAP_ENTRY(nsISupports) +NS_INTERFACE_MAP_END + + +//---------------------------------------------------------------------- +// Helper class: AutoChangePathSegListNotifier +// Stack-based helper class to pair calls to WillChangePathSegList and +// DidChangePathSegList. +class MOZ_RAII AutoChangePathSegListNotifier +{ +public: + explicit AutoChangePathSegListNotifier(DOMSVGPathSegList* aPathSegList MOZ_GUARD_OBJECT_NOTIFIER_PARAM) + : mPathSegList(aPathSegList) + { + MOZ_GUARD_OBJECT_NOTIFIER_INIT; + MOZ_ASSERT(mPathSegList, "Expecting non-null pathSegList"); + mEmptyOrOldValue = + mPathSegList->Element()->WillChangePathSegList(); + } + + ~AutoChangePathSegListNotifier() + { + mPathSegList->Element()->DidChangePathSegList(mEmptyOrOldValue); + if (mPathSegList->AttrIsAnimating()) { + mPathSegList->Element()->AnimationNeedsResample(); + } + } + +private: + DOMSVGPathSegList* const mPathSegList; + nsAttrValue mEmptyOrOldValue; + MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER +}; + +/* static */ already_AddRefed<DOMSVGPathSegList> +DOMSVGPathSegList::GetDOMWrapper(void *aList, + nsSVGElement *aElement, + bool aIsAnimValList) +{ + RefPtr<DOMSVGPathSegList> wrapper = + SVGPathSegListTearoffTable().GetTearoff(aList); + if (!wrapper) { + wrapper = new DOMSVGPathSegList(aElement, aIsAnimValList); + SVGPathSegListTearoffTable().AddTearoff(aList, wrapper); + } + return wrapper.forget(); +} + +/* static */ DOMSVGPathSegList* +DOMSVGPathSegList::GetDOMWrapperIfExists(void *aList) +{ + return SVGPathSegListTearoffTable().GetTearoff(aList); +} + +DOMSVGPathSegList::~DOMSVGPathSegList() +{ + // There are now no longer any references to us held by script or list items. + // Note we must use GetAnimValKey/GetBaseValKey here, NOT InternalList()! + void *key = mIsAnimValList ? + InternalAList().GetAnimValKey() : + InternalAList().GetBaseValKey(); + SVGPathSegListTearoffTable().RemoveTearoff(key); +} + +JSObject* +DOMSVGPathSegList::WrapObject(JSContext *cx, JS::Handle<JSObject*> aGivenProto) +{ + return mozilla::dom::SVGPathSegListBinding::Wrap(cx, this, aGivenProto); +} + +void +DOMSVGPathSegList::InternalListWillChangeTo(const SVGPathData& aNewValue) +{ + // When the number of items in our internal counterpart changes, we MUST stay + // in sync. Everything in the scary comment in + // DOMSVGLengthList::InternalBaseValListWillChangeTo applies here just as + // much, but we have the additional issue that failing to stay in sync would + // mean that - assuming we aren't reading bad memory - we would likely end up + // decoding command types from argument floats when looking in our + // SVGPathData's data array! Either way, we'll likely then go down + // NS_NOTREACHED code paths, or end up reading/setting more bad memory!! + + // The only time that our other DOM list type implementations remove items is + // if those items become surplus items due to an attribute change or SMIL + // animation sample shortening the list. In general though, they try to keep + // their existing DOM items, even when things change. To be consistent, we'd + // really like to do the same thing. However, because different types of path + // segment correspond to different DOMSVGPathSeg subclasses, the type of + // items in our list are generally not the same, which makes this harder for + // us. We have to remove DOM segments if their type is not the same as the + // type of the new internal segment at their index. + // + // We also need to sync up mInternalDataIndex, but since we need to loop over + // all the items in the new list checking types anyway, that's almost + // insignificant in terms of overhead. + // + // Note that this method is called on every single SMIL animation resample + // and we have no way to short circuit the overhead since we don't have a + // way to tell if the call is due to a new animation, or a resample of an + // existing animation (when the number and type of items would be the same). + // (Note that a new animation could start overriding an existing animation at + // any time, so checking IsAnimating() wouldn't work.) Because we get called + // on every sample, it would not be acceptable alternative to throw away all + // our items and let them be recreated lazily, since that would break what + // script sees! + + uint32_t length = mItems.Length(); + uint32_t index = 0; + + uint32_t dataLength = aNewValue.mData.Length(); + uint32_t dataIndex = 0; // index into aNewValue's raw data array + + uint32_t newSegType; + + RefPtr<DOMSVGPathSegList> kungFuDeathGrip; + if (length) { + // RemovingFromList() might clear last reference to |this|. + // Retain a temporary reference to keep from dying before returning. + // + // NOTE: For path-seg lists (unlike other list types), we have to do this + // *whenever our list is nonempty* (even if we're growing in length). + // That's because the path-seg-type of any segment could differ between old + // list vs. new list, which will make us destroy & recreate that segment, + // which could remove the last reference to us. + // + // (We explicitly *don't* want to create a kungFuDeathGrip in the length=0 + // case, though, because we do hit this code inside our constructor before + // any other owning references have been added, and at that point, the + // deathgrip-removal would make us die before we exit our constructor.) + kungFuDeathGrip = this; + } + + while (index < length && dataIndex < dataLength) { + newSegType = SVGPathSegUtils::DecodeType(aNewValue.mData[dataIndex]); + if (ItemAt(index) && ItemAt(index)->Type() != newSegType) { + ItemAt(index)->RemovingFromList(); + ItemAt(index) = nullptr; + } + // Only after the RemovingFromList() can we touch mInternalDataIndex! + mItems[index].mInternalDataIndex = dataIndex; + ++index; + dataIndex += 1 + SVGPathSegUtils::ArgCountForType(newSegType); + } + + MOZ_ASSERT((index == length && dataIndex <= dataLength) || + (index <= length && dataIndex == dataLength), + "very bad - list corruption?"); + + if (index < length) { + // aNewValue has fewer items than our previous internal counterpart + + uint32_t newLength = index; + + // Remove excess items from the list: + for (; index < length; ++index) { + if (ItemAt(index)) { + ItemAt(index)->RemovingFromList(); + ItemAt(index) = nullptr; + } + } + + // Only now may we truncate mItems + mItems.TruncateLength(newLength); + } else if (dataIndex < dataLength) { + // aNewValue has more items than our previous internal counterpart + + // Sync mItems: + while (dataIndex < dataLength) { + if (mItems.Length() && + mItems.Length() - 1 > DOMSVGPathSeg::MaxListIndex()) { + // It's safe to get out of sync with our internal list as long as we + // have FEWER items than it does. + return; + } + if (!mItems.AppendElement(ItemProxy(nullptr, dataIndex), fallible)) { + // OOM + ErrorResult rv; + Clear(rv); + MOZ_ASSERT(!rv.Failed()); + return; + } + dataIndex += 1 + SVGPathSegUtils::ArgCountForType(SVGPathSegUtils::DecodeType(aNewValue.mData[dataIndex])); + } + } + + MOZ_ASSERT(dataIndex == dataLength, "Serious processing error"); + MOZ_ASSERT(index == length, "Serious counting error"); +} + +bool +DOMSVGPathSegList::AttrIsAnimating() const +{ + return InternalAList().IsAnimating(); +} + +bool +DOMSVGPathSegList::AnimListMirrorsBaseList() const +{ + return GetDOMWrapperIfExists(InternalAList().GetAnimValKey()) && + !AttrIsAnimating(); +} + +SVGPathData& +DOMSVGPathSegList::InternalList() const +{ + SVGAnimatedPathSegList *alist = mElement->GetAnimPathSegList(); + return mIsAnimValList && alist->IsAnimating() ? *alist->mAnimVal : alist->mBaseVal; +} + +SVGAnimatedPathSegList& +DOMSVGPathSegList::InternalAList() const +{ + MOZ_ASSERT(mElement->GetAnimPathSegList(), "Internal error"); + return *mElement->GetAnimPathSegList(); +} + +// ---------------------------------------------------------------------------- +// nsIDOMSVGPathSegList implementation: + +void +DOMSVGPathSegList::Clear(ErrorResult& aError) +{ + if (IsAnimValList()) { + aError.Throw(NS_ERROR_DOM_NO_MODIFICATION_ALLOWED_ERR); + return; + } + + if (LengthNoFlush() > 0) { + AutoChangePathSegListNotifier notifier(this); + // DOM list items that are to be removed must be removed before we change + // the internal list, otherwise they wouldn't be able to copy their + // internal counterparts' values! + + InternalListWillChangeTo(SVGPathData()); // clears mItems + + if (!AttrIsAnimating()) { + // The anim val list is in sync with the base val list + DOMSVGPathSegList *animList = + GetDOMWrapperIfExists(InternalAList().GetAnimValKey()); + if (animList) { + animList->InternalListWillChangeTo(SVGPathData()); // clears its mItems + } + } + + InternalList().Clear(); + } +} + +already_AddRefed<DOMSVGPathSeg> +DOMSVGPathSegList::Initialize(DOMSVGPathSeg& aNewItem, ErrorResult& aError) +{ + if (IsAnimValList()) { + aError.Throw(NS_ERROR_DOM_NO_MODIFICATION_ALLOWED_ERR); + return nullptr; + } + + // If aNewItem is already in a list we should insert a clone of aNewItem, + // and for consistency, this should happen even if *this* is the list that + // aNewItem is currently in. Note that in the case of aNewItem 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 aNewItem, it would actually insert aNewItem. To prevent that + // from happening we have to do the clone here, if necessary. + + RefPtr<DOMSVGPathSeg> domItem = &aNewItem; + if (aNewItem.HasOwner()) { + domItem = aNewItem.Clone(); + } + + Clear(aError); + MOZ_ASSERT(!aError.Failed(), "How could this fail?"); + return InsertItemBefore(*domItem, 0, aError); +} + +already_AddRefed<DOMSVGPathSeg> +DOMSVGPathSegList::GetItem(uint32_t index, ErrorResult& error) +{ + bool found; + RefPtr<DOMSVGPathSeg> item = IndexedGetter(index, found, error); + if (!found) { + error.Throw(NS_ERROR_DOM_INDEX_SIZE_ERR); + } + return item.forget(); +} + +already_AddRefed<DOMSVGPathSeg> +DOMSVGPathSegList::IndexedGetter(uint32_t aIndex, bool& aFound, + ErrorResult& aError) +{ + if (IsAnimValList()) { + Element()->FlushAnimations(); + } + aFound = aIndex < LengthNoFlush(); + if (aFound) { + return GetItemAt(aIndex); + } + return nullptr; +} + +already_AddRefed<DOMSVGPathSeg> +DOMSVGPathSegList::InsertItemBefore(DOMSVGPathSeg& aNewItem, + uint32_t aIndex, + ErrorResult& aError) +{ + if (IsAnimValList()) { + aError.Throw(NS_ERROR_DOM_NO_MODIFICATION_ALLOWED_ERR); + return nullptr; + } + + uint32_t internalIndex; + if (aIndex < LengthNoFlush()) { + internalIndex = mItems[aIndex].mInternalDataIndex; + } else { + aIndex = LengthNoFlush(); + internalIndex = InternalList().mData.Length(); + } + if (aIndex >= DOMSVGPathSeg::MaxListIndex()) { + aError.Throw(NS_ERROR_DOM_INDEX_SIZE_ERR); + return nullptr; + } + + RefPtr<DOMSVGPathSeg> domItem = &aNewItem; + if (domItem->HasOwner()) { + domItem = domItem->Clone(); // must do this before changing anything! + } + + uint32_t argCount = SVGPathSegUtils::ArgCountForType(domItem->Type()); + + // Ensure we have enough memory so we can avoid complex error handling below: + if (!mItems.SetCapacity(mItems.Length() + 1, fallible) || + !InternalList().mData.SetCapacity(InternalList().mData.Length() + 1 + argCount, + fallible)) { + aError.Throw(NS_ERROR_OUT_OF_MEMORY); + return nullptr; + } + if (AnimListMirrorsBaseList()) { + DOMSVGPathSegList *animVal = + GetDOMWrapperIfExists(InternalAList().GetAnimValKey()); + MOZ_ASSERT(animVal, "animVal should be a valid pointer"); + if (!animVal->mItems.SetCapacity( + animVal->mItems.Length() + 1, fallible)) { + aError.Throw(NS_ERROR_OUT_OF_MEMORY); + return nullptr; + } + } + + AutoChangePathSegListNotifier notifier(this); + // Now that we know we're inserting, keep animVal list in sync as necessary. + MaybeInsertNullInAnimValListAt(aIndex, internalIndex, argCount); + + float segAsRaw[1 + NS_SVG_PATH_SEG_MAX_ARGS]; + domItem->ToSVGPathSegEncodedData(segAsRaw); + + MOZ_ALWAYS_TRUE(InternalList().mData.InsertElementsAt(internalIndex, + segAsRaw, + 1 + argCount, + fallible)); + MOZ_ALWAYS_TRUE(mItems.InsertElementAt(aIndex, + ItemProxy(domItem.get(), + internalIndex), + fallible)); + + // This MUST come after the insertion into InternalList(), or else under the + // insertion into InternalList() the values read from domItem would be bad + // data from InternalList() itself!: + domItem->InsertingIntoList(this, aIndex, IsAnimValList()); + + UpdateListIndicesFromIndex(aIndex + 1, argCount + 1); + + return domItem.forget(); +} + +already_AddRefed<DOMSVGPathSeg> +DOMSVGPathSegList::ReplaceItem(DOMSVGPathSeg& aNewItem, + uint32_t aIndex, + ErrorResult& aError) +{ + if (IsAnimValList()) { + aError.Throw(NS_ERROR_DOM_NO_MODIFICATION_ALLOWED_ERR); + return nullptr; + } + + if (aIndex >= LengthNoFlush()) { + aError.Throw(NS_ERROR_DOM_INDEX_SIZE_ERR); + return nullptr; + } + + RefPtr<DOMSVGPathSeg> domItem = &aNewItem; + if (domItem->HasOwner()) { + domItem = domItem->Clone(); // must do this before changing anything! + } + + AutoChangePathSegListNotifier notifier(this); + if (ItemAt(aIndex)) { + // Notify any existing DOM item of removal *before* modifying the lists so + // that the DOM item can copy the *old* value at its index: + ItemAt(aIndex)->RemovingFromList(); + } + + uint32_t internalIndex = mItems[aIndex].mInternalDataIndex; + // We use InternalList() to get oldArgCount since we may not have a DOM + // wrapper at the index being replaced. + uint32_t oldType = SVGPathSegUtils::DecodeType(InternalList().mData[internalIndex]); + + // NOTE: ArgCountForType returns a (small) unsigned value, but we're + // intentionally putting it in a signed variable, because we're going to + // subtract these values and might produce something negative. + int32_t oldArgCount = SVGPathSegUtils::ArgCountForType(oldType); + int32_t newArgCount = SVGPathSegUtils::ArgCountForType(domItem->Type()); + + float segAsRaw[1 + NS_SVG_PATH_SEG_MAX_ARGS]; + domItem->ToSVGPathSegEncodedData(segAsRaw); + + if (!InternalList().mData.ReplaceElementsAt(internalIndex, 1 + oldArgCount, + segAsRaw, 1 + newArgCount, + fallible)) { + aError.Throw(NS_ERROR_OUT_OF_MEMORY); + return nullptr; + } + ItemAt(aIndex) = domItem; + + // This MUST come after the ToSVGPathSegEncodedData call, otherwise that call + // would end up reading bad data from InternalList()! + domItem->InsertingIntoList(this, aIndex, IsAnimValList()); + + int32_t delta = newArgCount - oldArgCount; + if (delta != 0) { + for (uint32_t i = aIndex + 1; i < LengthNoFlush(); ++i) { + mItems[i].mInternalDataIndex += delta; + } + } + + return domItem.forget(); +} + +already_AddRefed<DOMSVGPathSeg> +DOMSVGPathSegList::RemoveItem(uint32_t aIndex, + ErrorResult& aError) +{ + if (IsAnimValList()) { + aError.Throw(NS_ERROR_DOM_NO_MODIFICATION_ALLOWED_ERR); + return nullptr; + } + + if (aIndex >= LengthNoFlush()) { + aError.Throw(NS_ERROR_DOM_INDEX_SIZE_ERR); + return nullptr; + } + // We have to return the removed item, so get it, creating it if necessary: + RefPtr<DOMSVGPathSeg> result = GetItemAt(aIndex); + + AutoChangePathSegListNotifier notifier(this); + // Notify the DOM item of removal *before* modifying the lists so that the + // DOM item can copy its *old* value: + ItemAt(aIndex)->RemovingFromList(); + + uint32_t internalIndex = mItems[aIndex].mInternalDataIndex; + uint32_t segType = SVGPathSegUtils::DecodeType(InternalList().mData[internalIndex]); + // NOTE: ArgCountForType returns a (small) unsigned value, but we're + // intentionally putting it in a signed value, because we're going to + // negate it, and you can't negate an unsigned value. + int32_t argCount = SVGPathSegUtils::ArgCountForType(segType); + + // Now that we know we're removing, keep animVal list in sync as necessary. + // Do this *before* touching InternalList() so the removed item can get its + // internal value. + MaybeRemoveItemFromAnimValListAt(aIndex, argCount); + + InternalList().mData.RemoveElementsAt(internalIndex, 1 + argCount); + mItems.RemoveElementAt(aIndex); + + UpdateListIndicesFromIndex(aIndex, -(argCount + 1)); + + return result.forget(); +} + +already_AddRefed<DOMSVGPathSeg> +DOMSVGPathSegList::GetItemAt(uint32_t aIndex) +{ + MOZ_ASSERT(aIndex < mItems.Length()); + + if (!ItemAt(aIndex)) { + ItemAt(aIndex) = DOMSVGPathSeg::CreateFor(this, aIndex, IsAnimValList()); + } + RefPtr<DOMSVGPathSeg> result = ItemAt(aIndex); + return result.forget(); +} + +void +DOMSVGPathSegList:: + MaybeInsertNullInAnimValListAt(uint32_t aIndex, + uint32_t aInternalIndex, + uint32_t aArgCountForItem) +{ + MOZ_ASSERT(!IsAnimValList(), "call from baseVal to animVal"); + + if (!AnimListMirrorsBaseList()) { + return; + } + + // The anim val list is in sync with the base val list + DOMSVGPathSegList *animVal = + GetDOMWrapperIfExists(InternalAList().GetAnimValKey()); + + MOZ_ASSERT(animVal, "AnimListMirrorsBaseList() promised a non-null animVal"); + MOZ_ASSERT(animVal->mItems.Length() == mItems.Length(), + "animVal list not in sync!"); + MOZ_ALWAYS_TRUE(animVal->mItems.InsertElementAt(aIndex, + ItemProxy(nullptr, + aInternalIndex), + fallible)); + + animVal->UpdateListIndicesFromIndex(aIndex + 1, 1 + aArgCountForItem); +} + +void +DOMSVGPathSegList:: + MaybeRemoveItemFromAnimValListAt(uint32_t aIndex, + int32_t aArgCountForItem) +{ + MOZ_ASSERT(!IsAnimValList(), "call from baseVal to animVal"); + + if (!AnimListMirrorsBaseList()) { + return; + } + + // This needs to be a strong reference; otherwise, the RemovingFromList call + // below might drop the last reference to animVal before we're done with it. + RefPtr<DOMSVGPathSegList> animVal = + GetDOMWrapperIfExists(InternalAList().GetAnimValKey()); + + MOZ_ASSERT(animVal, "AnimListMirrorsBaseList() promised a non-null animVal"); + MOZ_ASSERT(animVal->mItems.Length() == mItems.Length(), + "animVal list not in sync!"); + + if (animVal->ItemAt(aIndex)) { + animVal->ItemAt(aIndex)->RemovingFromList(); + } + animVal->mItems.RemoveElementAt(aIndex); + + animVal->UpdateListIndicesFromIndex(aIndex, -(1 + aArgCountForItem)); +} + +void +DOMSVGPathSegList::UpdateListIndicesFromIndex(uint32_t aStartingIndex, + int32_t aInternalDataIndexDelta) +{ + uint32_t length = mItems.Length(); + + for (uint32_t i = aStartingIndex; i < length; ++i) { + mItems[i].mInternalDataIndex += aInternalDataIndexDelta; + if (ItemAt(i)) { + ItemAt(i)->UpdateListIndex(i); + } + } +} + +} // namespace mozilla diff --git a/dom/svg/DOMSVGPathSegList.h b/dom/svg/DOMSVGPathSegList.h new file mode 100644 index 0000000000..150c920354 --- /dev/null +++ b/dom/svg/DOMSVGPathSegList.h @@ -0,0 +1,256 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef MOZILLA_DOMSVGPATHSEGLIST_H__ +#define MOZILLA_DOMSVGPATHSEGLIST_H__ + +#include "nsCOMPtr.h" +#include "nsCycleCollectionParticipant.h" +#include "nsDebug.h" +#include "nsSVGElement.h" +#include "nsTArray.h" +#include "SVGPathData.h" // IWYU pragma: keep +#include "mozilla/Attributes.h" +#include "mozilla/ErrorResult.h" + +namespace mozilla { + +class DOMSVGPathSeg; +class SVGAnimatedPathSegList; + +/** + * Class DOMSVGPathSegList + * + * This class is used to create the DOM tearoff objects that wrap internal + * SVGPathData objects. + * + * See the architecture comment in DOMSVGAnimatedLengthList.h first (that's + * LENGTH list), then continue reading the remainder of this comment. + * + * The architecture of this class is very similar to that of DOMSVGLengthList + * except that, since there is no nsIDOMSVGAnimatedPathSegList interface + * in SVG, we have no parent DOMSVGAnimatedPathSegList (unlike DOMSVGLengthList + * which has a parent DOMSVGAnimatedLengthList class). (There is an + * SVGAnimatedPathData interface, but that is quite different to + * DOMSVGAnimatedLengthList, since it is inherited by elements rather than + * elements having members of that type.) As a consequence, much of the logic + * that would otherwise be in DOMSVGAnimatedPathSegList (and is in + * DOMSVGAnimatedLengthList) is contained in this class. + * + * This class is strongly intertwined with DOMSVGPathSeg. Our DOMSVGPathSeg + * items are friends of us and responsible for nulling out our pointers to + * them when they die. + * + * Our DOM items are created lazily on demand as and when script requests them. + */ +class DOMSVGPathSegList final : public nsISupports, + public nsWrapperCache +{ + friend class AutoChangePathSegListNotifier; + friend class DOMSVGPathSeg; + +public: + NS_DECL_CYCLE_COLLECTING_ISUPPORTS + NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(DOMSVGPathSegList) + + virtual JSObject* WrapObject(JSContext *cx, JS::Handle<JSObject*> aGivenProto) override; + + nsISupports* GetParentObject() + { + return static_cast<nsIContent*>(mElement); + } + + /** + * Factory method to create and return a DOMSVGPathSegList wrapper + * for a given internal SVGPathData object. The factory takes care + * of caching the object that it returns so that the same object can be + * returned for the given SVGPathData 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 SVGPathData will naturally result in a new + * DOMSVGPathSegList being returned. + * + * It's unfortunate that aList is a void* instead of a typed argument. This + * is because the mBaseVal and mAnimVal members of SVGAnimatedPathSegList are + * of different types - a plain SVGPathData, and a SVGPathData*. We + * use the addresses of these members as the key for the hash table, and + * clearly SVGPathData* and a SVGPathData** are not the same type. + */ + static already_AddRefed<DOMSVGPathSegList> + GetDOMWrapper(void *aList, + nsSVGElement *aElement, + bool aIsAnimValList); + + /** + * This method returns the DOMSVGPathSegList wrapper for an internal + * SVGPathData object if it currently has a wrapper. If it does + * not, then nullptr is returned. + */ + static DOMSVGPathSegList* + GetDOMWrapperIfExists(void *aList); + + /** + * This will normally be the same as InternalList().CountItems(), except if + * we've hit OOM, in which case our length will be zero. + */ + uint32_t LengthNoFlush() const { + MOZ_ASSERT(mItems.Length() == 0 || + mItems.Length() == InternalList().CountItems(), + "DOM wrapper's list length is out of sync"); + return mItems.Length(); + } + + /** + * WATCH OUT! If you add code to call this on a baseVal wrapper, then you + * must also call it on the animVal wrapper too if necessary!! See other + * callers! + * + * Called by internal code to notify us when we need to sync the length of + * this DOM list with its internal list. This is called immediately prior to + * the length of the internal list being changed so that any DOM list items + * that need to be removed from the DOM list can first copy 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 InternalListWillChangeTo(const SVGPathData& aNewValue); + + /** + * Returns true if our attribute is animating (in which case our animVal is + * not simply a mirror of our baseVal). + */ + bool AttrIsAnimating() const; + /** + * Returns true if there is an animated list mirroring the base list. + */ + bool AnimListMirrorsBaseList() const; + + uint32_t NumberOfItems() const + { + if (IsAnimValList()) { + Element()->FlushAnimations(); + } + return LengthNoFlush(); + } + void Clear(ErrorResult& aError); + already_AddRefed<DOMSVGPathSeg> Initialize(DOMSVGPathSeg& aNewItem, + ErrorResult& aError); + already_AddRefed<DOMSVGPathSeg> GetItem(uint32_t index, + ErrorResult& error); + already_AddRefed<DOMSVGPathSeg> IndexedGetter(uint32_t index, bool& found, + ErrorResult& error); + already_AddRefed<DOMSVGPathSeg> InsertItemBefore(DOMSVGPathSeg& aNewItem, + uint32_t aIndex, + ErrorResult& aError); + already_AddRefed<DOMSVGPathSeg> ReplaceItem(DOMSVGPathSeg& aNewItem, + uint32_t aIndex, + ErrorResult& aError); + already_AddRefed<DOMSVGPathSeg> RemoveItem(uint32_t aIndex, + ErrorResult& aError); + already_AddRefed<DOMSVGPathSeg> AppendItem(DOMSVGPathSeg& aNewItem, + ErrorResult& aError) + { + return InsertItemBefore(aNewItem, LengthNoFlush(), aError); + } + uint32_t Length() const + { + return NumberOfItems(); + } + +private: + + /** + * Only our static GetDOMWrapper() factory method may create objects of our + * type. + */ + DOMSVGPathSegList(nsSVGElement *aElement, bool aIsAnimValList) + : mElement(aElement) + , mIsAnimValList(aIsAnimValList) + { + InternalListWillChangeTo(InternalList()); // Sync mItems + } + + ~DOMSVGPathSegList(); + + nsSVGElement* Element() const { + return mElement.get(); + } + + /// Used to determine if this list is the baseVal or animVal list. + bool IsAnimValList() const { + return mIsAnimValList; + } + + /** + * Get a reference to this object's corresponding internal SVGPathData. + * + * To simplify the code we just have this one method for obtaining both + * base val and anim val internal lists. This means that anim val lists don't + * get const protection, but our setter methods guard against changing + * anim val lists. + */ + SVGPathData& InternalList() const; + + SVGAnimatedPathSegList& InternalAList() const; + + /// Creates an instance of the appropriate DOMSVGPathSeg sub-class for + // aIndex, if it doesn't already exist, and then returs it. + already_AddRefed<DOMSVGPathSeg> GetItemAt(uint32_t aIndex); + + void MaybeInsertNullInAnimValListAt(uint32_t aIndex, + uint32_t aInternalIndex, + uint32_t aArgCountForItem); + void MaybeRemoveItemFromAnimValListAt(uint32_t aIndex, + int32_t aArgCountForItem); + + // Calls UpdateListIndex on all elements in |mItems| that satisfy ItemAt(), + // from |aStartingIndex| to the end of |mItems|. Also adjusts + // |mItems.mInternalDataIndex| by the requested amount. + void UpdateListIndicesFromIndex(uint32_t aStartingIndex, + int32_t aInternalDataIndexDelta); + + DOMSVGPathSeg*& ItemAt(uint32_t aIndex) { + return mItems[aIndex].mItem; + } + + /** + * This struct is used in our array of mItems to provide us with somewhere to + * store the indexes into the internal SVGPathData of the internal seg data + * that our DOMSVGPathSeg items wrap (the internal segment data is or varying + * length, so we can't just use the index of our DOMSVGPathSeg items + * themselves). The reason that we have this separate struct rather than + * just storing the internal indexes in the DOMSVGPathSeg items is because we + * want to create the DOMSVGPathSeg items lazily on demand. + */ + struct ItemProxy { + ItemProxy(){} + ItemProxy(DOMSVGPathSeg *aItem, uint32_t aInternalDataIndex) + : mItem(aItem) + , mInternalDataIndex(aInternalDataIndex) + {} + + DOMSVGPathSeg *mItem; + uint32_t mInternalDataIndex; + }; + + // Weak refs to our DOMSVGPathSeg items. The items are friends and take care + // of clearing our pointer to them when they die. + FallibleTArray<ItemProxy> mItems; + + // Strong ref to our element to keep it alive. We hold this not only for + // ourself, but also for our DOMSVGPathSeg items too. + RefPtr<nsSVGElement> mElement; + + bool mIsAnimValList; +}; + +} // namespace mozilla + +#endif // MOZILLA_DOMSVGPATHSEGLIST_H__ diff --git a/dom/svg/DOMSVGPoint.cpp b/dom/svg/DOMSVGPoint.cpp new file mode 100644 index 0000000000..fa87ee1899 --- /dev/null +++ b/dom/svg/DOMSVGPoint.cpp @@ -0,0 +1,121 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "DOMSVGPoint.h" +#include "DOMSVGPointList.h" +#include "SVGPoint.h" +#include "gfx2DGlue.h" +#include "nsSVGElement.h" +#include "nsError.h" +#include "mozilla/dom/SVGMatrix.h" + +// See the architecture comment in DOMSVGPointList.h. + +using namespace mozilla; +using namespace mozilla::gfx; + +namespace mozilla { + +//---------------------------------------------------------------------- +// Helper class: AutoChangePointNotifier +// Stack-based helper class to pair calls to WillChangePointList and +// DidChangePointList. +class MOZ_RAII AutoChangePointNotifier +{ +public: + explicit AutoChangePointNotifier(DOMSVGPoint* aPoint MOZ_GUARD_OBJECT_NOTIFIER_PARAM) + : mPoint(aPoint) + { + MOZ_GUARD_OBJECT_NOTIFIER_INIT; + MOZ_ASSERT(mPoint, "Expecting non-null point"); + MOZ_ASSERT(mPoint->HasOwner(), + "Expecting list to have an owner for notification"); + mEmptyOrOldValue = + mPoint->Element()->WillChangePointList(); + } + + ~AutoChangePointNotifier() + { + mPoint->Element()->DidChangePointList(mEmptyOrOldValue); + if (mPoint->mList->AttrIsAnimating()) { + mPoint->Element()->AnimationNeedsResample(); + } + } + +private: + DOMSVGPoint* const mPoint; + nsAttrValue mEmptyOrOldValue; + MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER +}; + +} // namespace mozilla + +float +DOMSVGPoint::X() +{ + if (mIsAnimValItem && HasOwner()) { + Element()->FlushAnimations(); // May make HasOwner() == false + } + return HasOwner() ? InternalItem().mX : mPt.mX; +} + +void +DOMSVGPoint::SetX(float aX, ErrorResult& rv) +{ + if (mIsAnimValItem || mIsReadonly) { + rv.Throw(NS_ERROR_DOM_NO_MODIFICATION_ALLOWED_ERR); + return; + } + + if (HasOwner()) { + if (InternalItem().mX == aX) { + return; + } + AutoChangePointNotifier notifier(this); + InternalItem().mX = aX; + return; + } + mPt.mX = aX; +} + +float +DOMSVGPoint::Y() +{ + if (mIsAnimValItem && HasOwner()) { + Element()->FlushAnimations(); // May make HasOwner() == false + } + return HasOwner() ? InternalItem().mY : mPt.mY; +} + +void +DOMSVGPoint::SetY(float aY, ErrorResult& rv) +{ + if (mIsAnimValItem || mIsReadonly) { + rv.Throw(NS_ERROR_DOM_NO_MODIFICATION_ALLOWED_ERR); + return; + } + + if (HasOwner()) { + if (InternalItem().mY == aY) { + return; + } + AutoChangePointNotifier notifier(this); + InternalItem().mY = aY; + return; + } + mPt.mY = aY; +} + +already_AddRefed<nsISVGPoint> +DOMSVGPoint::MatrixTransform(dom::SVGMatrix& matrix) +{ + float x = HasOwner() ? InternalItem().mX : mPt.mX; + float y = HasOwner() ? InternalItem().mY : mPt.mY; + + Point pt = ToMatrix(matrix.GetMatrix()).TransformPoint(Point(x, y)); + nsCOMPtr<nsISVGPoint> newPoint = new DOMSVGPoint(pt); + return newPoint.forget(); +} diff --git a/dom/svg/DOMSVGPoint.h b/dom/svg/DOMSVGPoint.h new file mode 100644 index 0000000000..3c8780a202 --- /dev/null +++ b/dom/svg/DOMSVGPoint.h @@ -0,0 +1,113 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef MOZILLA_DOMSVGPOINT_H__ +#define MOZILLA_DOMSVGPOINT_H__ + +#include "DOMSVGPointList.h" +#include "mozilla/gfx/2D.h" +#include "nsDebug.h" +#include "nsISVGPoint.h" +#include "SVGPoint.h" +#include "mozilla/Attributes.h" +#include "mozilla/FloatingPoint.h" + +class nsSVGElement; + +namespace mozilla { + +namespace dom { +class SVGMatrix; +} // namespace dom + +/** + * Class DOMSVGPoint + * + * This class creates the DOM objects that wrap internal SVGPoint objects that + * are in an SVGPointList. It is also used to create the objects returned by + * SVGSVGElement.createSVGPoint() and other functions that return DOM SVGPoint + * objects. + * + * See the architecture comment in DOMSVGPointList.h for an overview of the + * important points regarding these DOM wrapper structures. + * + * See the architecture comment in DOMSVGLength.h (yes, LENGTH) for an overview + * of the important points regarding how this specific class works. + */ +class DOMSVGPoint final : public nsISVGPoint +{ + friend class AutoChangePointNotifier; + + typedef mozilla::gfx::Point Point; + +public: + /** + * Generic ctor for DOMSVGPoint objects that are created for an attribute. + */ + DOMSVGPoint(DOMSVGPointList *aList, + uint32_t aListIndex, + bool aIsAnimValItem) + : nsISVGPoint() + { + mList = aList; + mListIndex = aListIndex; + mIsAnimValItem = aIsAnimValItem; + + // These shifts are in sync with the members. + MOZ_ASSERT(aList && aListIndex <= MaxListIndex(), "bad arg"); + + MOZ_ASSERT(IndexIsValid(), "Bad index for DOMSVGPoint!"); + } + + explicit DOMSVGPoint(const DOMSVGPoint *aPt = nullptr) + : nsISVGPoint() + { + if (aPt) { + mPt = aPt->ToSVGPoint(); + } + } + + DOMSVGPoint(float aX, float aY) + : nsISVGPoint() + { + mPt.mX = aX; + mPt.mY = aY; + } + + explicit DOMSVGPoint(const Point& aPt) + : nsISVGPoint() + { + mPt.mX = aPt.x; + mPt.mY = aPt.y; + NS_ASSERTION(IsFinite(mPt.mX) && IsFinite(mPt.mX), + "DOMSVGPoint coords are not finite"); + } + + + // WebIDL + virtual float X() override; + virtual void SetX(float aX, ErrorResult& rv) override; + virtual float Y() override; + virtual void SetY(float aY, ErrorResult& rv) override; + virtual already_AddRefed<nsISVGPoint> MatrixTransform(dom::SVGMatrix& matrix) override; + nsISupports* GetParentObject() override { + return mList; + } + + virtual DOMSVGPoint* Copy() override { + return new DOMSVGPoint(this); + } + +protected: + + nsSVGElement* Element() { + return mList->Element(); + } +}; + +} // namespace mozilla + +#endif // MOZILLA_DOMSVGPOINT_H__ diff --git a/dom/svg/DOMSVGPointList.cpp b/dom/svg/DOMSVGPointList.cpp new file mode 100644 index 0000000000..966ef476e7 --- /dev/null +++ b/dom/svg/DOMSVGPointList.cpp @@ -0,0 +1,479 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "nsSVGElement.h" +#include "DOMSVGPointList.h" +#include "DOMSVGPoint.h" +#include "nsError.h" +#include "SVGAnimatedPointList.h" +#include "nsCOMPtr.h" +#include "nsSVGAttrTearoffTable.h" +#include "nsContentUtils.h" +#include "mozilla/dom/SVGPointListBinding.h" +#include <algorithm> + +// See the comment in this file's header. + +// local helper functions +namespace { + +void +UpdateListIndicesFromIndex(FallibleTArray<mozilla::nsISVGPoint*>& aItemsArray, + uint32_t aStartingIndex) +{ + uint32_t length = aItemsArray.Length(); + + for (uint32_t i = aStartingIndex; i < length; ++i) { + if (aItemsArray[i]) { + aItemsArray[i]->UpdateListIndex(i); + } + } +} + +} // namespace + +namespace mozilla { + + static inline +nsSVGAttrTearoffTable<void, DOMSVGPointList>& +SVGPointListTearoffTable() +{ + static nsSVGAttrTearoffTable<void, DOMSVGPointList> + sSVGPointListTearoffTable; + return sSVGPointListTearoffTable; +} + +NS_IMPL_CYCLE_COLLECTION_CLASS(DOMSVGPointList) + +NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(DOMSVGPointList) + // No unlinking of mElement, we'd need to null out the value pointer (the + // object it points to is held by the element) and null-check it everywhere. + NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER +NS_IMPL_CYCLE_COLLECTION_UNLINK_END +NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(DOMSVGPointList) + NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mElement) + NS_IMPL_CYCLE_COLLECTION_TRAVERSE_SCRIPT_OBJECTS +NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END +NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(DOMSVGPointList) + NS_IMPL_CYCLE_COLLECTION_TRACE_PRESERVED_WRAPPER +NS_IMPL_CYCLE_COLLECTION_TRACE_END + +NS_IMPL_CYCLE_COLLECTING_ADDREF(DOMSVGPointList) +NS_IMPL_CYCLE_COLLECTING_RELEASE(DOMSVGPointList) + +NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(DOMSVGPointList) + NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY + NS_INTERFACE_MAP_ENTRY(nsISupports) +NS_INTERFACE_MAP_END + +//---------------------------------------------------------------------- +// Helper class: AutoChangePointListNotifier +// Stack-based helper class to pair calls to WillChangePointList and +// DidChangePointList. +class MOZ_RAII AutoChangePointListNotifier +{ +public: + explicit AutoChangePointListNotifier(DOMSVGPointList* aPointList MOZ_GUARD_OBJECT_NOTIFIER_PARAM) + : mPointList(aPointList) + { + MOZ_GUARD_OBJECT_NOTIFIER_INIT; + MOZ_ASSERT(mPointList, "Expecting non-null pointList"); + mEmptyOrOldValue = + mPointList->Element()->WillChangePointList(); + } + + ~AutoChangePointListNotifier() + { + mPointList->Element()->DidChangePointList(mEmptyOrOldValue); + if (mPointList->AttrIsAnimating()) { + mPointList->Element()->AnimationNeedsResample(); + } + } + +private: + DOMSVGPointList* const mPointList; + nsAttrValue mEmptyOrOldValue; + MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER +}; + + +/* static */ already_AddRefed<DOMSVGPointList> +DOMSVGPointList::GetDOMWrapper(void *aList, + nsSVGElement *aElement, + bool aIsAnimValList) +{ + RefPtr<DOMSVGPointList> wrapper = + SVGPointListTearoffTable().GetTearoff(aList); + if (!wrapper) { + wrapper = new DOMSVGPointList(aElement, aIsAnimValList); + SVGPointListTearoffTable().AddTearoff(aList, wrapper); + } + return wrapper.forget(); +} + +/* static */ DOMSVGPointList* +DOMSVGPointList::GetDOMWrapperIfExists(void *aList) +{ + return SVGPointListTearoffTable().GetTearoff(aList); +} + +DOMSVGPointList::~DOMSVGPointList() +{ + // There are now no longer any references to us held by script or list items. + // Note we must use GetAnimValKey/GetBaseValKey here, NOT InternalList()! + void *key = mIsAnimValList ? + InternalAList().GetAnimValKey() : + InternalAList().GetBaseValKey(); + SVGPointListTearoffTable().RemoveTearoff(key); +} + +JSObject* +DOMSVGPointList::WrapObject(JSContext *cx, JS::Handle<JSObject*> aGivenProto) +{ + return mozilla::dom::SVGPointListBinding::Wrap(cx, this, aGivenProto); +} + +void +DOMSVGPointList::InternalListWillChangeTo(const SVGPointList& aNewValue) +{ + // When the number of items in our internal counterpart changes, we MUST stay + // in sync. Everything in the scary comment in + // DOMSVGLengthList::InternalBaseValListWillChangeTo applies here too! + + uint32_t oldLength = mItems.Length(); + + uint32_t newLength = aNewValue.Length(); + if (newLength > nsISVGPoint::MaxListIndex()) { + // It's safe to get out of sync with our internal list as long as we have + // FEWER items than it does. + newLength = nsISVGPoint::MaxListIndex(); + } + + RefPtr<DOMSVGPointList> kungFuDeathGrip; + if (newLength < oldLength) { + // RemovingFromList() might clear last reference to |this|. + // Retain a temporary reference to keep from dying before returning. + kungFuDeathGrip = this; + } + + // If our length will decrease, notify the items that will be removed: + for (uint32_t i = newLength; i < oldLength; ++i) { + if (mItems[i]) { + mItems[i]->RemovingFromList(); + } + } + + if (!mItems.SetLength(newLength, fallible)) { + // 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.Clear(); + return; + } + + // If our length has increased, null out the new pointers: + for (uint32_t i = oldLength; i < newLength; ++i) { + mItems[i] = nullptr; + } +} + +bool +DOMSVGPointList::AttrIsAnimating() const +{ + return InternalAList().IsAnimating(); +} + +bool +DOMSVGPointList::AnimListMirrorsBaseList() const +{ + return GetDOMWrapperIfExists(InternalAList().GetAnimValKey()) && + !AttrIsAnimating(); +} + +SVGPointList& +DOMSVGPointList::InternalList() const +{ + SVGAnimatedPointList *alist = mElement->GetAnimatedPointList(); + return mIsAnimValList && alist->IsAnimating() ? *alist->mAnimVal : alist->mBaseVal; +} + +SVGAnimatedPointList& +DOMSVGPointList::InternalAList() const +{ + MOZ_ASSERT(mElement->GetAnimatedPointList(), "Internal error"); + return *mElement->GetAnimatedPointList(); +} + +// ---------------------------------------------------------------------------- +// nsIDOMSVGPointList implementation: + +void +DOMSVGPointList::Clear(ErrorResult& aError) +{ + if (IsAnimValList()) { + aError.Throw(NS_ERROR_DOM_NO_MODIFICATION_ALLOWED_ERR); + return; + } + + if (LengthNoFlush() > 0) { + AutoChangePointListNotifier notifier(this); + // DOM list items that are to be removed must be removed before we change + // the internal list, otherwise they wouldn't be able to copy their + // internal counterparts' values! + + InternalListWillChangeTo(SVGPointList()); // clears mItems + + if (!AttrIsAnimating()) { + // The anim val list is in sync with the base val list + DOMSVGPointList *animList = + GetDOMWrapperIfExists(InternalAList().GetAnimValKey()); + if (animList) { + animList->InternalListWillChangeTo(SVGPointList()); // clears its mItems + } + } + + InternalList().Clear(); + } +} + +already_AddRefed<nsISVGPoint> +DOMSVGPointList::Initialize(nsISVGPoint& aNewItem, ErrorResult& aError) +{ + if (IsAnimValList()) { + aError.Throw(NS_ERROR_DOM_NO_MODIFICATION_ALLOWED_ERR); + return nullptr; + } + + // If aNewItem is already in a list we should insert a clone of aNewItem, + // and for consistency, this should happen even if *this* is the list that + // aNewItem is currently in. Note that in the case of aNewItem 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 aNewItem, it would actually insert aNewItem. To prevent that + // from happening we have to do the clone here, if necessary. + + nsCOMPtr<nsISVGPoint> domItem = &aNewItem; + if (domItem->HasOwner() || domItem->IsReadonly() || + domItem->IsTranslatePoint()) { + domItem = domItem->Copy(); // must do this before changing anything! + } + + ErrorResult rv; + Clear(rv); + MOZ_ASSERT(!rv.Failed()); + return InsertItemBefore(*domItem, 0, aError); +} + +already_AddRefed<nsISVGPoint> +DOMSVGPointList::GetItem(uint32_t index, ErrorResult& error) +{ + bool found; + RefPtr<nsISVGPoint> item = IndexedGetter(index, found, error); + if (!found) { + error.Throw(NS_ERROR_DOM_INDEX_SIZE_ERR); + } + return item.forget(); +} + +already_AddRefed<nsISVGPoint> +DOMSVGPointList::IndexedGetter(uint32_t aIndex, bool& aFound, + ErrorResult& aError) +{ + if (IsAnimValList()) { + Element()->FlushAnimations(); + } + aFound = aIndex < LengthNoFlush(); + if (aFound) { + return GetItemAt(aIndex); + } + return nullptr; +} + +already_AddRefed<nsISVGPoint> +DOMSVGPointList::InsertItemBefore(nsISVGPoint& aNewItem, uint32_t aIndex, + ErrorResult& aError) +{ + if (IsAnimValList()) { + aError.Throw(NS_ERROR_DOM_NO_MODIFICATION_ALLOWED_ERR); + return nullptr; + } + + aIndex = std::min(aIndex, LengthNoFlush()); + if (aIndex >= nsISVGPoint::MaxListIndex()) { + aError.Throw(NS_ERROR_DOM_INDEX_SIZE_ERR); + return nullptr; + } + + nsCOMPtr<nsISVGPoint> domItem = &aNewItem; + if (domItem->HasOwner() || domItem->IsReadonly() || + domItem->IsTranslatePoint()) { + domItem = domItem->Copy(); // must do this before changing anything! + } + + // Ensure we have enough memory so we can avoid complex error handling below: + if (!mItems.SetCapacity(mItems.Length() + 1, fallible) || + !InternalList().SetCapacity(InternalList().Length() + 1)) { + aError.Throw(NS_ERROR_OUT_OF_MEMORY); + return nullptr; + } + if (AnimListMirrorsBaseList()) { + DOMSVGPointList *animVal = + GetDOMWrapperIfExists(InternalAList().GetAnimValKey()); + MOZ_ASSERT(animVal, "animVal must be a valid pointer"); + if (!animVal->mItems.SetCapacity( + animVal->mItems.Length() + 1, fallible)) { + aError.Throw(NS_ERROR_OUT_OF_MEMORY); + return nullptr; + } + } + + AutoChangePointListNotifier notifier(this); + // Now that we know we're inserting, keep animVal list in sync as necessary. + MaybeInsertNullInAnimValListAt(aIndex); + + InternalList().InsertItem(aIndex, domItem->ToSVGPoint()); + MOZ_ALWAYS_TRUE(mItems.InsertElementAt(aIndex, domItem, fallible)); + + // This MUST come after the insertion into InternalList(), or else under the + // insertion into InternalList() the values read from domItem would be bad + // data from InternalList() itself!: + domItem->InsertingIntoList(this, aIndex, IsAnimValList()); + + UpdateListIndicesFromIndex(mItems, aIndex + 1); + + return domItem.forget(); +} + +already_AddRefed<nsISVGPoint> +DOMSVGPointList::ReplaceItem(nsISVGPoint& aNewItem, uint32_t aIndex, + ErrorResult& aError) +{ + if (IsAnimValList()) { + aError.Throw(NS_ERROR_DOM_NO_MODIFICATION_ALLOWED_ERR); + return nullptr; + } + + if (aIndex >= LengthNoFlush()) { + aError.Throw(NS_ERROR_DOM_INDEX_SIZE_ERR); + return nullptr; + } + + nsCOMPtr<nsISVGPoint> domItem = &aNewItem; + if (domItem->HasOwner() || domItem->IsReadonly() || + domItem->IsTranslatePoint()) { + domItem = domItem->Copy(); // must do this before changing anything! + } + + AutoChangePointListNotifier notifier(this); + if (mItems[aIndex]) { + // 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[aIndex]->RemovingFromList(); + } + + InternalList()[aIndex] = domItem->ToSVGPoint(); + mItems[aIndex] = domItem; + + // This MUST come after the ToSVGPoint() call, otherwise that call + // would end up reading bad data from InternalList()! + domItem->InsertingIntoList(this, aIndex, IsAnimValList()); + + return domItem.forget(); +} + +already_AddRefed<nsISVGPoint> +DOMSVGPointList::RemoveItem(uint32_t aIndex, ErrorResult& aError) +{ + if (IsAnimValList()) { + aError.Throw(NS_ERROR_DOM_NO_MODIFICATION_ALLOWED_ERR); + return nullptr; + } + + if (aIndex >= LengthNoFlush()) { + aError.Throw(NS_ERROR_DOM_INDEX_SIZE_ERR); + return nullptr; + } + + AutoChangePointListNotifier notifier(this); + // Now that we know we're removing, keep animVal list in sync as necessary. + // Do this *before* touching InternalList() so the removed item can get its + // internal value. + MaybeRemoveItemFromAnimValListAt(aIndex); + + // We have to return the removed item, so get it, creating it if necessary: + RefPtr<nsISVGPoint> result = GetItemAt(aIndex); + + // Notify the DOM item of removal *before* modifying the lists so that the + // DOM item can copy its *old* value: + mItems[aIndex]->RemovingFromList(); + + InternalList().RemoveItem(aIndex); + mItems.RemoveElementAt(aIndex); + + UpdateListIndicesFromIndex(mItems, aIndex); + + return result.forget(); +} + +already_AddRefed<nsISVGPoint> +DOMSVGPointList::GetItemAt(uint32_t aIndex) +{ + MOZ_ASSERT(aIndex < mItems.Length()); + + if (!mItems[aIndex]) { + mItems[aIndex] = new DOMSVGPoint(this, aIndex, IsAnimValList()); + } + RefPtr<nsISVGPoint> result = mItems[aIndex]; + return result.forget(); +} + +void +DOMSVGPointList::MaybeInsertNullInAnimValListAt(uint32_t aIndex) +{ + MOZ_ASSERT(!IsAnimValList(), "call from baseVal to animVal"); + + if (!AnimListMirrorsBaseList()) { + return; + } + + // The anim val list is in sync with the base val list + DOMSVGPointList *animVal = + GetDOMWrapperIfExists(InternalAList().GetAnimValKey()); + + MOZ_ASSERT(animVal, "AnimListMirrorsBaseList() promised a non-null animVal"); + MOZ_ASSERT(animVal->mItems.Length() == mItems.Length(), + "animVal list not in sync!"); + MOZ_ALWAYS_TRUE(animVal->mItems.InsertElementAt(aIndex, nullptr, fallible)); + + UpdateListIndicesFromIndex(animVal->mItems, aIndex + 1); +} + +void +DOMSVGPointList::MaybeRemoveItemFromAnimValListAt(uint32_t aIndex) +{ + MOZ_ASSERT(!IsAnimValList(), "call from baseVal to animVal"); + + if (!AnimListMirrorsBaseList()) { + return; + } + + // This needs to be a strong reference; otherwise, the RemovingFromList call + // below might drop the last reference to animVal before we're done with it. + RefPtr<DOMSVGPointList> animVal = + GetDOMWrapperIfExists(InternalAList().GetAnimValKey()); + + MOZ_ASSERT(animVal, "AnimListMirrorsBaseList() promised a non-null animVal"); + MOZ_ASSERT(animVal->mItems.Length() == mItems.Length(), + "animVal list not in sync!"); + + if (animVal->mItems[aIndex]) { + animVal->mItems[aIndex]->RemovingFromList(); + } + animVal->mItems.RemoveElementAt(aIndex); + + UpdateListIndicesFromIndex(animVal->mItems, aIndex); +} + +} // namespace mozilla diff --git a/dom/svg/DOMSVGPointList.h b/dom/svg/DOMSVGPointList.h new file mode 100644 index 0000000000..499a49cf58 --- /dev/null +++ b/dom/svg/DOMSVGPointList.h @@ -0,0 +1,224 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef MOZILLA_DOMSVGPOINTLIST_H__ +#define MOZILLA_DOMSVGPOINTLIST_H__ + +#include "nsCOMPtr.h" +#include "nsCycleCollectionParticipant.h" +#include "nsDebug.h" +#include "nsSVGElement.h" +#include "nsTArray.h" +#include "SVGPointList.h" // IWYU pragma: keep +#include "mozilla/Attributes.h" +#include "mozilla/ErrorResult.h" + +namespace mozilla { + +class DOMSVGPoint; +class nsISVGPoint; +class SVGAnimatedPointList; + +/** + * Class DOMSVGPointList + * + * This class is used to create the DOM tearoff objects that wrap internal + * SVGPointList objects. + * + * See the architecture comment in DOMSVGAnimatedLengthList.h first (that's + * LENGTH list), then continue reading the remainder of this comment. + * + * The architecture of this class is very similar to that of DOMSVGLengthList + * except that, since there is no nsIDOMSVGAnimatedPointList interface + * in SVG, we have no parent DOMSVGAnimatedPointList (unlike DOMSVGLengthList + * which has a parent DOMSVGAnimatedLengthList class). (There is an + * SVGAnimatedPoints interface, but that is quite different to + * DOMSVGAnimatedLengthList, since it is inherited by elements rather than + * elements having members of that type.) As a consequence, much of the logic + * that would otherwise be in DOMSVGAnimatedPointList (and is in + * DOMSVGAnimatedLengthList) is contained in this class. + * + * This class is strongly intertwined with DOMSVGPoint. Our DOMSVGPoint + * items are friends of us and responsible for nulling out our pointers to + * them when they die. + * + * Our DOM items are created lazily on demand as and when script requests them. + */ +class DOMSVGPointList final : public nsISupports, + public nsWrapperCache +{ + friend class AutoChangePointListNotifier; + friend class nsISVGPoint; + friend class mozilla::DOMSVGPoint; + +public: + NS_DECL_CYCLE_COLLECTING_ISUPPORTS + NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(DOMSVGPointList) + + virtual JSObject* WrapObject(JSContext *cx, JS::Handle<JSObject*> aGivenProto) override; + + nsISupports* GetParentObject() + { + return static_cast<nsIContent*>(mElement); + } + + /** + * Factory method to create and return a DOMSVGPointList wrapper + * for a given internal SVGPointList object. The factory takes care + * of caching the object that it returns so that the same object can be + * returned for the given SVGPointList 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 SVGPointList will naturally result in a new + * DOMSVGPointList being returned. + * + * It's unfortunate that aList is a void* instead of a typed argument. This + * is because the mBaseVal and mAnimVal members of SVGAnimatedPointList are + * of different types - a plain SVGPointList, and a SVGPointList*. We + * use the addresses of these members as the key for the hash table, and + * clearly SVGPointList* and a SVGPointList** are not the same type. + */ + static already_AddRefed<DOMSVGPointList> + GetDOMWrapper(void *aList, + nsSVGElement *aElement, + bool aIsAnimValList); + + /** + * This method returns the DOMSVGPointList wrapper for an internal + * SVGPointList object if it currently has a wrapper. If it does + * not, then nullptr is returned. + */ + static DOMSVGPointList* + GetDOMWrapperIfExists(void *aList); + + /** + * This will normally be the same as InternalList().Length(), except if + * we've hit OOM, in which case our length will be zero. + */ + uint32_t LengthNoFlush() const { + MOZ_ASSERT(mItems.Length() == 0 || + mItems.Length() == InternalList().Length(), + "DOM wrapper's list length is out of sync"); + return mItems.Length(); + } + + /** + * WATCH OUT! If you add code to call this on a baseVal wrapper, then you + * must also call it on the animVal wrapper too if necessary!! See other + * callers! + * + * Called by internal code to notify us when we need to sync the length of + * this DOM list with its internal list. This is called immediately prior to + * the length of the internal list being changed so that any DOM list items + * that need to be removed from the DOM list can first copy 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 InternalListWillChangeTo(const SVGPointList& aNewValue); + + /** + * Returns true if our attribute is animating (in which case our animVal is + * not simply a mirror of our baseVal). + */ + bool AttrIsAnimating() const; + /** + * Returns true if there is an animated list mirroring the base list. + */ + bool AnimListMirrorsBaseList() const; + + uint32_t NumberOfItems() const + { + if (IsAnimValList()) { + Element()->FlushAnimations(); + } + return LengthNoFlush(); + } + void Clear(ErrorResult& aError); + already_AddRefed<nsISVGPoint> Initialize(nsISVGPoint& aNewItem, + ErrorResult& aError); + already_AddRefed<nsISVGPoint> GetItem(uint32_t index, + ErrorResult& error); + already_AddRefed<nsISVGPoint> IndexedGetter(uint32_t index, bool& found, + ErrorResult& error); + already_AddRefed<nsISVGPoint> InsertItemBefore(nsISVGPoint& aNewItem, + uint32_t aIndex, + ErrorResult& aError); + already_AddRefed<nsISVGPoint> ReplaceItem(nsISVGPoint& aNewItem, + uint32_t aIndex, + ErrorResult& aError); + already_AddRefed<nsISVGPoint> RemoveItem(uint32_t aIndex, + ErrorResult& aError); + already_AddRefed<nsISVGPoint> AppendItem(nsISVGPoint& aNewItem, + ErrorResult& aError) + { + return InsertItemBefore(aNewItem, LengthNoFlush(), aError); + } + uint32_t Length() const + { + return NumberOfItems(); + } + +private: + + /** + * Only our static GetDOMWrapper() factory method may create objects of our + * type. + */ + DOMSVGPointList(nsSVGElement *aElement, bool aIsAnimValList) + : mElement(aElement) + , mIsAnimValList(aIsAnimValList) + { + InternalListWillChangeTo(InternalList()); // Sync mItems + } + + ~DOMSVGPointList(); + + nsSVGElement* Element() const { + return mElement.get(); + } + + /// Used to determine if this list is the baseVal or animVal list. + bool IsAnimValList() const { + return mIsAnimValList; + } + + /** + * Get a reference to this object's corresponding internal SVGPointList. + * + * To simplify the code we just have this one method for obtaining both + * base val and anim val internal lists. This means that anim val lists don't + * get const protection, but our setter methods guard against changing + * anim val lists. + */ + SVGPointList& InternalList() const; + + SVGAnimatedPointList& InternalAList() const; + + /// Returns the nsISVGPoint at aIndex, creating it if necessary. + already_AddRefed<nsISVGPoint> GetItemAt(uint32_t aIndex); + + void MaybeInsertNullInAnimValListAt(uint32_t aIndex); + void MaybeRemoveItemFromAnimValListAt(uint32_t aIndex); + + // Weak refs to our nsISVGPoint items. The items are friends and take care + // of clearing our pointer to them when they die. + FallibleTArray<nsISVGPoint*> mItems; + + // Strong ref to our element to keep it alive. We hold this not only for + // ourself, but also for our nsISVGPoint items too. + RefPtr<nsSVGElement> mElement; + + bool mIsAnimValList; +}; + +} // namespace mozilla + +#endif // MOZILLA_DOMSVGPOINTLIST_H__ diff --git a/dom/svg/DOMSVGStringList.cpp b/dom/svg/DOMSVGStringList.cpp new file mode 100644 index 0000000000..df358d9909 --- /dev/null +++ b/dom/svg/DOMSVGStringList.cpp @@ -0,0 +1,223 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "DOMSVGStringList.h" + +#include "mozilla/dom/SVGStringListBinding.h" +#include "mozilla/dom/SVGTests.h" +#include "nsError.h" +#include "nsCOMPtr.h" +#include "nsSVGAttrTearoffTable.h" +#include "nsQueryObject.h" +#include <algorithm> + +// See the architecture comment in this file's header. + +namespace mozilla { + +using namespace dom; + +static inline +nsSVGAttrTearoffTable<SVGStringList, DOMSVGStringList>& +SVGStringListTearoffTable() +{ + static nsSVGAttrTearoffTable<SVGStringList, DOMSVGStringList> + sSVGStringListTearoffTable; + return sSVGStringListTearoffTable; +} + +NS_SVG_VAL_IMPL_CYCLE_COLLECTION_WRAPPERCACHED(DOMSVGStringList, mElement) + +NS_IMPL_CYCLE_COLLECTING_ADDREF(DOMSVGStringList) +NS_IMPL_CYCLE_COLLECTING_RELEASE(DOMSVGStringList) + +NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(DOMSVGStringList) + NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY + NS_INTERFACE_MAP_ENTRY(nsISupports) +NS_INTERFACE_MAP_END + +//---------------------------------------------------------------------- +// Helper class: AutoChangeStringListNotifier +// Stack-based helper class to pair calls to WillChangeStringListList and +// DidChangeStringListList. +class MOZ_RAII AutoChangeStringListNotifier +{ +public: + explicit AutoChangeStringListNotifier(DOMSVGStringList* aStringList MOZ_GUARD_OBJECT_NOTIFIER_PARAM) + : mStringList(aStringList) + { + MOZ_GUARD_OBJECT_NOTIFIER_INIT; + MOZ_ASSERT(mStringList, "Expecting non-null stringList"); + mEmptyOrOldValue = + mStringList->mElement->WillChangeStringList(mStringList->mIsConditionalProcessingAttribute, + mStringList->mAttrEnum); + } + + ~AutoChangeStringListNotifier() + { + mStringList->mElement->DidChangeStringList(mStringList->mIsConditionalProcessingAttribute, + mStringList->mAttrEnum, mEmptyOrOldValue); + } + +private: + DOMSVGStringList* const mStringList; + nsAttrValue mEmptyOrOldValue; + MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER +}; + +/* static */ already_AddRefed<DOMSVGStringList> +DOMSVGStringList::GetDOMWrapper(SVGStringList *aList, + nsSVGElement *aElement, + bool aIsConditionalProcessingAttribute, + uint8_t aAttrEnum) +{ + RefPtr<DOMSVGStringList> wrapper = + SVGStringListTearoffTable().GetTearoff(aList); + if (!wrapper) { + wrapper = new DOMSVGStringList(aElement, + aIsConditionalProcessingAttribute, + aAttrEnum); + SVGStringListTearoffTable().AddTearoff(aList, wrapper); + } + return wrapper.forget(); +} + +DOMSVGStringList::~DOMSVGStringList() +{ + // Script no longer has any references to us. + SVGStringListTearoffTable().RemoveTearoff(&InternalList()); +} + +/* virtual */ JSObject* +DOMSVGStringList::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) +{ + return SVGStringListBinding::Wrap(aCx, this, aGivenProto); +} + +// ---------------------------------------------------------------------------- +// SVGStringList implementation: + +uint32_t +DOMSVGStringList::NumberOfItems() const +{ + return InternalList().Length(); +} + +uint32_t +DOMSVGStringList::Length() const +{ + return NumberOfItems(); +} + +void +DOMSVGStringList::Clear() +{ + if (InternalList().IsExplicitlySet()) { + AutoChangeStringListNotifier notifier(this); + InternalList().Clear(); + } +} + +void +DOMSVGStringList::Initialize(const nsAString& aNewItem, nsAString& aRetval, + ErrorResult& aRv) +{ + if (InternalList().IsExplicitlySet()) { + InternalList().Clear(); + } + InsertItemBefore(aNewItem, 0, aRetval, aRv); +} + +void +DOMSVGStringList::GetItem(uint32_t aIndex, nsAString& aRetval, ErrorResult& aRv) +{ + bool found; + IndexedGetter(aIndex, found, aRetval); + if (!found) { + aRv.Throw(NS_ERROR_DOM_INDEX_SIZE_ERR); + } +} + +void +DOMSVGStringList::IndexedGetter(uint32_t aIndex, bool& aFound, + nsAString& aRetval) +{ + aFound = aIndex < InternalList().Length(); + if (aFound) { + aRetval = InternalList()[aIndex]; + } +} + +void +DOMSVGStringList::InsertItemBefore(const nsAString& aNewItem, uint32_t aIndex, + nsAString& aRetval, ErrorResult& aRv) +{ + if (aNewItem.IsEmpty()) { + aRv.Throw(NS_ERROR_DOM_SYNTAX_ERR); + return; + } + aIndex = std::min(aIndex, InternalList().Length()); + + // Ensure we have enough memory so we can avoid complex error handling below: + if (!InternalList().SetCapacity(InternalList().Length() + 1)) { + aRv.Throw(NS_ERROR_OUT_OF_MEMORY); + return; + } + + AutoChangeStringListNotifier notifier(this); + InternalList().InsertItem(aIndex, aNewItem); + aRetval = aNewItem; +} + +void +DOMSVGStringList::ReplaceItem(const nsAString& aNewItem, uint32_t aIndex, + nsAString& aRetval, ErrorResult& aRv) +{ + if (aNewItem.IsEmpty()) { + aRv.Throw(NS_ERROR_DOM_SYNTAX_ERR); + return; + } + if (aIndex >= InternalList().Length()) { + aRv.Throw(NS_ERROR_DOM_INDEX_SIZE_ERR); + return; + } + + aRetval = InternalList()[aIndex]; + AutoChangeStringListNotifier notifier(this); + InternalList().ReplaceItem(aIndex, aNewItem); +} + +void +DOMSVGStringList::RemoveItem(uint32_t aIndex, nsAString& aRetval, + ErrorResult& aRv) +{ + if (aIndex >= InternalList().Length()) { + aRv.Throw(NS_ERROR_DOM_INDEX_SIZE_ERR); + return; + } + + AutoChangeStringListNotifier notifier(this); + InternalList().RemoveItem(aIndex); +} + +void +DOMSVGStringList::AppendItem(const nsAString& aNewItem, nsAString& aRetval, + ErrorResult& aRv) +{ + InsertItemBefore(aNewItem, InternalList().Length(), aRetval, aRv); +} + +SVGStringList & +DOMSVGStringList::InternalList() const +{ + if (mIsConditionalProcessingAttribute) { + nsCOMPtr<dom::SVGTests> tests = do_QueryObject(mElement.get()); + return tests->mStringListAttributes[mAttrEnum]; + } + return mElement->GetStringListInfo().mStringLists[mAttrEnum]; +} + +} // namespace mozilla diff --git a/dom/svg/DOMSVGStringList.h b/dom/svg/DOMSVGStringList.h new file mode 100644 index 0000000000..642f07180e --- /dev/null +++ b/dom/svg/DOMSVGStringList.h @@ -0,0 +1,119 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef MOZILLA_DOMSVGSTRINGLIST_H__ +#define MOZILLA_DOMSVGSTRINGLIST_H__ + +#include "nsCOMPtr.h" +#include "nsCycleCollectionParticipant.h" +#include "nsSVGElement.h" +#include "mozilla/Attributes.h" + +namespace mozilla { + +class ErrorResult; +class SVGStringList; + +/** + * Class DOMSVGStringList + * + * This class is used to create the DOM tearoff objects that wrap internal + * SVGPathData objects. + * + * See the architecture comment in DOMSVGAnimatedLengthList.h first (that's + * LENGTH list), then continue reading the remainder of this comment. + * + * The architecture of this class is similar to that of DOMSVGLengthList + * except for two important aspects: + * + * First, since there is no nsIDOMSVGAnimatedStringList interface in SVG, we + * have no parent DOMSVGAnimatedStringList (unlike DOMSVGLengthList which has + * a parent DOMSVGAnimatedLengthList class). As a consequence, much of the + * logic that would otherwise be in DOMSVGAnimatedStringList (and is in + * DOMSVGAnimatedLengthList) is contained in this class. + * + * Second, since there is no nsIDOMSVGString interface in SVG, we have no + * DOMSVGString items to maintain. As far as script is concerned, objects + * of this class contain a list of strings, not a list of mutable objects + * like the other SVG list types. As a result, unlike the other SVG list + * types, this class does not create its items lazily on demand and store + * them so it can return the same objects each time. It simply returns a new + * string each time any given item is requested. + */ +class DOMSVGStringList final : public nsISupports + , public nsWrapperCache +{ + friend class AutoChangeStringListNotifier; + +public: + NS_DECL_CYCLE_COLLECTING_ISUPPORTS + NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(DOMSVGStringList) + + nsSVGElement* GetParentObject() const + { + return mElement; + } + virtual JSObject* WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override; + + uint32_t NumberOfItems() const; + uint32_t Length() const; + void Clear(); + void Initialize(const nsAString& aNewItem, nsAString& aRetval, + ErrorResult& aRv); + void GetItem(uint32_t aIndex, nsAString& aRetval, ErrorResult& aRv); + void IndexedGetter(uint32_t aIndex, bool& aFound, nsAString& aRetval); + void InsertItemBefore(const nsAString& aNewItem, uint32_t aIndex, + nsAString& aRetval, ErrorResult& aRv); + void ReplaceItem(const nsAString& aNewItem, uint32_t aIndex, + nsAString& aRetval, ErrorResult& aRv); + void RemoveItem(uint32_t aIndex, nsAString& aRetval, ErrorResult& aRv); + void AppendItem(const nsAString& aNewItem, nsAString& aRetval, + ErrorResult& aRv); + + /** + * Factory method to create and return a DOMSVGStringList wrapper + * for a given internal SVGStringList object. The factory takes care + * of caching the object that it returns so that the same object can be + * returned for the given SVGStringList 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. If that happens, any subsequent + * call requesting the DOM wrapper for the SVGStringList will naturally + * result in a new DOMSVGStringList being returned. + */ + static already_AddRefed<DOMSVGStringList> + GetDOMWrapper(SVGStringList *aList, + nsSVGElement *aElement, + bool aIsConditionalProcessingAttribute, + uint8_t aAttrEnum); + +private: + /** + * Only our static GetDOMWrapper() factory method may create objects of our + * type. + */ + DOMSVGStringList(nsSVGElement *aElement, + bool aIsConditionalProcessingAttribute, uint8_t aAttrEnum) + : mElement(aElement) + , mAttrEnum(aAttrEnum) + , mIsConditionalProcessingAttribute(aIsConditionalProcessingAttribute) + { + } + + ~DOMSVGStringList(); + + SVGStringList &InternalList() const; + + // Strong ref to our element to keep it alive. + RefPtr<nsSVGElement> mElement; + + uint8_t mAttrEnum; + + bool mIsConditionalProcessingAttribute; +}; + +} // namespace mozilla + +#endif // MOZILLA_DOMSVGSTRINGLIST_H__ diff --git a/dom/svg/DOMSVGTransformList.cpp b/dom/svg/DOMSVGTransformList.cpp new file mode 100644 index 0000000000..30760bc6d1 --- /dev/null +++ b/dom/svg/DOMSVGTransformList.cpp @@ -0,0 +1,445 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "DOMSVGTransformList.h" +#include "mozilla/dom/SVGTransform.h" +#include "mozilla/dom/SVGMatrix.h" +#include "nsSVGAnimatedTransformList.h" +#include "nsSVGElement.h" +#include "mozilla/dom/SVGTransformListBinding.h" +#include "nsError.h" +#include <algorithm> + +// local helper functions +namespace { + +void UpdateListIndicesFromIndex( + FallibleTArray<mozilla::dom::SVGTransform*>& aItemsArray, + uint32_t aStartingIndex) +{ + uint32_t length = aItemsArray.Length(); + + for (uint32_t i = aStartingIndex; i < length; ++i) { + if (aItemsArray[i]) { + aItemsArray[i]->UpdateListIndex(i); + } + } +} + +} // namespace + +namespace mozilla { + +using namespace dom; + +// We could use NS_IMPL_CYCLE_COLLECTION(, except that in Unlink() we need to +// clear our SVGAnimatedTransformList'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(DOMSVGTransformList) + +NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(DOMSVGTransformList) + if (tmp->mAList) { + if (tmp->IsAnimValList()) { + tmp->mAList->mAnimVal = nullptr; + } else { + tmp->mAList->mBaseVal = nullptr; + } + NS_IMPL_CYCLE_COLLECTION_UNLINK(mAList) + } + NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER +NS_IMPL_CYCLE_COLLECTION_UNLINK_END +NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(DOMSVGTransformList) + NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mAList) + NS_IMPL_CYCLE_COLLECTION_TRAVERSE_SCRIPT_OBJECTS +NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END +NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(DOMSVGTransformList) + NS_IMPL_CYCLE_COLLECTION_TRACE_PRESERVED_WRAPPER +NS_IMPL_CYCLE_COLLECTION_TRACE_END + +NS_IMPL_CYCLE_COLLECTING_ADDREF(DOMSVGTransformList) +NS_IMPL_CYCLE_COLLECTING_RELEASE(DOMSVGTransformList) + +NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(DOMSVGTransformList) + NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY + NS_INTERFACE_MAP_ENTRY(nsISupports) +NS_INTERFACE_MAP_END + +//---------------------------------------------------------------------- +// DOMSVGTransformList methods: + +JSObject* +DOMSVGTransformList::WrapObject(JSContext *cx, JS::Handle<JSObject*> aGivenProto) +{ + return mozilla::dom::SVGTransformListBinding::Wrap(cx, this, aGivenProto); +} + +//---------------------------------------------------------------------- +// Helper class: AutoChangeTransformListNotifier +// Stack-based helper class to pair calls to WillChangeTransformList and +// DidChangeTransformList. +class MOZ_RAII AutoChangeTransformListNotifier +{ +public: + explicit AutoChangeTransformListNotifier(DOMSVGTransformList* aTransformList MOZ_GUARD_OBJECT_NOTIFIER_PARAM) + : mTransformList(aTransformList) + { + MOZ_GUARD_OBJECT_NOTIFIER_INIT; + MOZ_ASSERT(mTransformList, "Expecting non-null transformList"); + mEmptyOrOldValue = + mTransformList->Element()->WillChangeTransformList(); + } + + ~AutoChangeTransformListNotifier() + { + mTransformList->Element()->DidChangeTransformList(mEmptyOrOldValue); + if (mTransformList->IsAnimating()) { + mTransformList->Element()->AnimationNeedsResample(); + } + } + +private: + DOMSVGTransformList* const mTransformList; + nsAttrValue mEmptyOrOldValue; + MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER +}; + +void +DOMSVGTransformList::InternalListLengthWillChange(uint32_t aNewLength) +{ + uint32_t oldLength = mItems.Length(); + + if (aNewLength > SVGTransform::MaxListIndex()) { + // It's safe to get out of sync with our internal list as long as we have + // FEWER items than it does. + aNewLength = SVGTransform::MaxListIndex(); + } + + RefPtr<DOMSVGTransformList> kungFuDeathGrip; + if (aNewLength < oldLength) { + // RemovingFromList() might clear last reference to |this|. + // Retain a temporary reference to keep from dying before returning. + kungFuDeathGrip = this; + } + + // If our length will decrease, notify the items that will be removed: + for (uint32_t i = aNewLength; i < oldLength; ++i) { + if (mItems[i]) { + mItems[i]->RemovingFromList(); + } + } + + if (!mItems.SetLength(aNewLength, fallible)) { + // 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.Clear(); + return; + } + + // If our length has increased, null out the new pointers: + for (uint32_t i = oldLength; i < aNewLength; ++i) { + mItems[i] = nullptr; + } +} + +SVGTransformList& +DOMSVGTransformList::InternalList() const +{ + nsSVGAnimatedTransformList *alist = Element()->GetAnimatedTransformList(); + return IsAnimValList() && alist->mAnimVal ? + *alist->mAnimVal : + alist->mBaseVal; +} + +//---------------------------------------------------------------------- +void +DOMSVGTransformList::Clear(ErrorResult& error) +{ + if (IsAnimValList()) { + error.Throw(NS_ERROR_DOM_NO_MODIFICATION_ALLOWED_ERR); + return; + } + + if (LengthNoFlush() > 0) { + AutoChangeTransformListNotifier notifier(this); + // Notify any existing DOM items of removal *before* truncating the lists + // so that they can find their SVGTransform internal counterparts and copy + // their values. This also notifies the animVal list: + mAList->InternalBaseValListWillChangeLengthTo(0); + + mItems.Clear(); + InternalList().Clear(); + } +} + +already_AddRefed<SVGTransform> +DOMSVGTransformList::Initialize(SVGTransform& newItem, ErrorResult& error) +{ + if (IsAnimValList()) { + error.Throw(NS_ERROR_DOM_NO_MODIFICATION_ALLOWED_ERR); + return nullptr; + } + + // 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. + + RefPtr<SVGTransform> domItem = &newItem; + if (domItem->HasOwner()) { + domItem = newItem.Clone(); + } + + Clear(error); + MOZ_ASSERT(!error.Failed(), "How could this fail?"); + return InsertItemBefore(*domItem, 0, error); +} + +already_AddRefed<SVGTransform> +DOMSVGTransformList::GetItem(uint32_t index, ErrorResult& error) +{ + bool found; + RefPtr<SVGTransform> item = IndexedGetter(index, found, error); + if (!found) { + error.Throw(NS_ERROR_DOM_INDEX_SIZE_ERR); + } + return item.forget(); +} + +already_AddRefed<SVGTransform> +DOMSVGTransformList::IndexedGetter(uint32_t index, bool& found, + ErrorResult& error) +{ + if (IsAnimValList()) { + Element()->FlushAnimations(); + } + found = index < LengthNoFlush(); + if (found) { + return GetItemAt(index); + } + return nullptr; +} + +already_AddRefed<SVGTransform> +DOMSVGTransformList::InsertItemBefore(SVGTransform& newItem, + uint32_t index, ErrorResult& error) +{ + if (IsAnimValList()) { + error.Throw(NS_ERROR_DOM_NO_MODIFICATION_ALLOWED_ERR); + return nullptr; + } + + index = std::min(index, LengthNoFlush()); + if (index >= SVGTransform::MaxListIndex()) { + error.Throw(NS_ERROR_DOM_INDEX_SIZE_ERR); + return nullptr; + } + + RefPtr<SVGTransform> domItem = &newItem; + if (newItem.HasOwner()) { + domItem = newItem.Clone(); // must do this before changing anything! + } + + // Ensure we have enough memory so we can avoid complex error handling below: + if (!mItems.SetCapacity(mItems.Length() + 1, fallible) || + !InternalList().SetCapacity(InternalList().Length() + 1)) { + error.Throw(NS_ERROR_OUT_OF_MEMORY); + return nullptr; + } + if (AnimListMirrorsBaseList()) { + if (!mAList->mAnimVal->mItems.SetCapacity( + mAList->mAnimVal->mItems.Length() + 1, fallible)) { + error.Throw(NS_ERROR_OUT_OF_MEMORY); + return nullptr; + } + } + + AutoChangeTransformListNotifier notifier(this); + // Now that we know we're inserting, keep animVal list in sync as necessary. + MaybeInsertNullInAnimValListAt(index); + + InternalList().InsertItem(index, domItem->ToSVGTransform()); + MOZ_ALWAYS_TRUE(mItems.InsertElementAt(index, domItem.get(), fallible)); + + // This MUST come after the insertion into InternalList(), or else under the + // insertion into InternalList() the values read from domItem would be bad + // data from InternalList() itself!: + domItem->InsertingIntoList(this, index, IsAnimValList()); + + UpdateListIndicesFromIndex(mItems, index + 1); + + return domItem.forget(); +} + +already_AddRefed<SVGTransform> +DOMSVGTransformList::ReplaceItem(SVGTransform& newItem, + uint32_t index, ErrorResult& error) +{ + if (IsAnimValList()) { + error.Throw(NS_ERROR_DOM_NO_MODIFICATION_ALLOWED_ERR); + return nullptr; + } + + if (index >= LengthNoFlush()) { + error.Throw(NS_ERROR_DOM_INDEX_SIZE_ERR); + return nullptr; + } + + RefPtr<SVGTransform> domItem = &newItem; + if (newItem.HasOwner()) { + domItem = newItem.Clone(); // must do this before changing anything! + } + + AutoChangeTransformListNotifier notifier(this); + 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] = domItem->ToSVGTransform(); + mItems[index] = domItem; + + // This MUST come after the ToSVGPoint() call, otherwise that call + // would end up reading bad data from InternalList()! + domItem->InsertingIntoList(this, index, IsAnimValList()); + + return domItem.forget(); +} + +already_AddRefed<SVGTransform> +DOMSVGTransformList::RemoveItem(uint32_t index, ErrorResult& error) +{ + if (IsAnimValList()) { + error.Throw(NS_ERROR_DOM_NO_MODIFICATION_ALLOWED_ERR); + return nullptr; + } + + if (index >= LengthNoFlush()) { + error.Throw(NS_ERROR_DOM_INDEX_SIZE_ERR); + return nullptr; + } + + AutoChangeTransformListNotifier notifier(this); + // Now that we know we're removing, keep animVal list in sync as necessary. + // Do this *before* touching InternalList() so the removed item can get its + // internal value. + MaybeRemoveItemFromAnimValListAt(index); + + // We have to return the removed item, so get it, creating it if necessary: + RefPtr<SVGTransform> result = GetItemAt(index); + + // Notify the DOM item of removal *before* modifying the lists so that the + // DOM item can copy its *old* value: + result->RemovingFromList(); + + InternalList().RemoveItem(index); + mItems.RemoveElementAt(index); + + UpdateListIndicesFromIndex(mItems, index); + + return result.forget(); +} + +already_AddRefed<SVGTransform> +DOMSVGTransformList::CreateSVGTransformFromMatrix(dom::SVGMatrix& matrix) +{ + RefPtr<SVGTransform> result = new SVGTransform(matrix.GetMatrix()); + return result.forget(); +} + +already_AddRefed<SVGTransform> +DOMSVGTransformList::Consolidate(ErrorResult& error) +{ + if (IsAnimValList()) { + error.Throw(NS_ERROR_DOM_NO_MODIFICATION_ALLOWED_ERR); + return nullptr; + } + + if (LengthNoFlush() == 0) { + return nullptr; + } + + // Note that SVG 1.1 says, "The consolidation operation creates new + // SVGTransform object as the first and only item in the list" hence, even if + // LengthNoFlush() == 1 we can't return that one item (after making it a + // matrix type). We must orphan the existing item and then make a new one. + + // First calculate our matrix + gfxMatrix mx = InternalList().GetConsolidationMatrix(); + + // Then orphan the existing items + Clear(error); + MOZ_ASSERT(!error.Failed(), "How could this fail?"); + + // And append the new transform + RefPtr<SVGTransform> transform = new SVGTransform(mx); + return InsertItemBefore(*transform, LengthNoFlush(), error); +} + +//---------------------------------------------------------------------- +// Implementation helpers: + +already_AddRefed<SVGTransform> +DOMSVGTransformList::GetItemAt(uint32_t aIndex) +{ + MOZ_ASSERT(aIndex < mItems.Length()); + + if (!mItems[aIndex]) { + mItems[aIndex] = new SVGTransform(this, aIndex, IsAnimValList()); + } + RefPtr<SVGTransform> result = mItems[aIndex]; + return result.forget(); +} + +void +DOMSVGTransformList::MaybeInsertNullInAnimValListAt(uint32_t aIndex) +{ + MOZ_ASSERT(!IsAnimValList(), "call from baseVal to animVal"); + + if (!AnimListMirrorsBaseList()) { + return; + } + + DOMSVGTransformList* animVal = mAList->mAnimVal; + + MOZ_ASSERT(animVal, "AnimListMirrorsBaseList() promised a non-null animVal"); + MOZ_ASSERT(animVal->mItems.Length() == mItems.Length(), + "animVal list not in sync!"); + MOZ_ALWAYS_TRUE(animVal->mItems.InsertElementAt(aIndex, nullptr, fallible)); + + UpdateListIndicesFromIndex(animVal->mItems, aIndex + 1); +} + +void +DOMSVGTransformList::MaybeRemoveItemFromAnimValListAt(uint32_t aIndex) +{ + MOZ_ASSERT(!IsAnimValList(), "call from baseVal to animVal"); + + if (!AnimListMirrorsBaseList()) { + return; + } + + // This needs to be a strong reference; otherwise, the RemovingFromList call + // below might drop the last reference to animVal before we're done with it. + RefPtr<DOMSVGTransformList> animVal = mAList->mAnimVal; + + MOZ_ASSERT(animVal, "AnimListMirrorsBaseList() promised a non-null animVal"); + MOZ_ASSERT(animVal->mItems.Length() == mItems.Length(), + "animVal list not in sync!"); + + if (animVal->mItems[aIndex]) { + animVal->mItems[aIndex]->RemovingFromList(); + } + animVal->mItems.RemoveElementAt(aIndex); + + UpdateListIndicesFromIndex(animVal->mItems, aIndex); +} + +} // namespace mozilla diff --git a/dom/svg/DOMSVGTransformList.h b/dom/svg/DOMSVGTransformList.h new file mode 100644 index 0000000000..6825151cab --- /dev/null +++ b/dom/svg/DOMSVGTransformList.h @@ -0,0 +1,172 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef MOZILLA_DOMSVGTRANSFORMLIST_H__ +#define MOZILLA_DOMSVGTRANSFORMLIST_H__ + +#include "mozilla/dom/SVGAnimatedTransformList.h" +#include "nsCycleCollectionParticipant.h" +#include "nsDebug.h" +#include "nsTArray.h" +#include "SVGTransformList.h" +#include "mozilla/Attributes.h" +#include "mozilla/ErrorResult.h" + +class nsSVGElement; + +namespace mozilla { + +namespace dom { +class SVGMatrix; +class SVGTransform; +} // namespace dom + +/** + * Class DOMSVGTransformList + * + * This class is used to create the DOM tearoff objects that wrap internal + * SVGTransformList objects. + * + * See the architecture comment in SVGAnimatedTransformList.h. + */ +class DOMSVGTransformList final : public nsISupports, + public nsWrapperCache +{ + friend class AutoChangeTransformListNotifier; + friend class dom::SVGTransform; + + ~DOMSVGTransformList() { + // 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 ) = nullptr; + } + } + +public: + NS_DECL_CYCLE_COLLECTING_ISUPPORTS + NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(DOMSVGTransformList) + + DOMSVGTransformList(dom::SVGAnimatedTransformList *aAList, + const SVGTransformList &aInternalList) + : mAList(aAList) + { + // aInternalList must be passed in explicitly because we can't use + // InternalList() here. (Because it depends on IsAnimValList, which depends + // on this object having been assigned to aAList's mBaseVal or mAnimVal, + // which hasn't happend yet.) + + InternalListLengthWillChange(aInternalList.Length()); // Sync mItems + } + + virtual JSObject* WrapObject(JSContext *cx, JS::Handle<JSObject*> aGivenProto) override; + + nsISupports* GetParentObject() + { + return static_cast<nsIContent*>(Element()); + } + + /** + * This will normally be the same as InternalList().Length(), except if we've + * hit OOM in which case our length will be zero. + */ + uint32_t LengthNoFlush() const { + MOZ_ASSERT(mItems.IsEmpty() || mItems.Length() == InternalList().Length(), + "DOM wrapper's list length is out of sync"); + return mItems.Length(); + } + + /// Called to notify us to synchronize our length and detach excess items. + void InternalListLengthWillChange(uint32_t aNewLength); + + /** + * Returns true if our attribute is animating (in which case our animVal is + * not simply a mirror of our baseVal). + */ + bool IsAnimating() const { + return mAList->IsAnimating(); + } + /** + * Returns true if there is an animated list mirroring the base list. + */ + bool AnimListMirrorsBaseList() const { + return mAList->mAnimVal && !mAList->IsAnimating(); + } + + uint32_t NumberOfItems() const + { + if (IsAnimValList()) { + Element()->FlushAnimations(); + } + return LengthNoFlush(); + } + void Clear(ErrorResult& error); + already_AddRefed<dom::SVGTransform> Initialize(dom::SVGTransform& newItem, + ErrorResult& error); + already_AddRefed<dom::SVGTransform> GetItem(uint32_t index, + ErrorResult& error); + already_AddRefed<dom::SVGTransform> IndexedGetter(uint32_t index, bool& found, + ErrorResult& error); + already_AddRefed<dom::SVGTransform> InsertItemBefore(dom::SVGTransform& newItem, + uint32_t index, + ErrorResult& error); + already_AddRefed<dom::SVGTransform> ReplaceItem(dom::SVGTransform& newItem, + uint32_t index, + ErrorResult& error); + already_AddRefed<dom::SVGTransform> RemoveItem(uint32_t index, + ErrorResult& error); + already_AddRefed<dom::SVGTransform> AppendItem(dom::SVGTransform& newItem, + ErrorResult& error) + { + return InsertItemBefore(newItem, LengthNoFlush(), error); + } + already_AddRefed<dom::SVGTransform> CreateSVGTransformFromMatrix(dom::SVGMatrix& matrix); + already_AddRefed<dom::SVGTransform> Consolidate(ErrorResult& error); + uint32_t Length() const + { + return NumberOfItems(); + } + +private: + + nsSVGElement* Element() const { + return mAList->mElement; + } + + /// Used to determine if this list is the baseVal or animVal list. + bool IsAnimValList() const { + MOZ_ASSERT(this == mAList->mBaseVal || this == mAList->mAnimVal, + "Calling IsAnimValList() too early?!"); + return this == mAList->mAnimVal; + } + + /** + * Get a reference to this object's corresponding internal SVGTransformList. + * + * To simplify 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. + */ + SVGTransformList& InternalList() const; + + /// Returns the SVGTransform at aIndex, creating it if necessary. + already_AddRefed<dom::SVGTransform> GetItemAt(uint32_t aIndex); + + void MaybeInsertNullInAnimValListAt(uint32_t aIndex); + void MaybeRemoveItemFromAnimValListAt(uint32_t aIndex); + + // Weak refs to our SVGTransform items. The items are friends and take care + // of clearing our pointer to them when they die. + FallibleTArray<dom::SVGTransform*> mItems; + + RefPtr<dom::SVGAnimatedTransformList> mAList; +}; + +} // namespace mozilla + +#endif // MOZILLA_DOMSVGTRANSFORMLIST_H__ diff --git a/dom/svg/SVGAElement.cpp b/dom/svg/SVGAElement.cpp new file mode 100644 index 0000000000..a69c60ee4e --- /dev/null +++ b/dom/svg/SVGAElement.cpp @@ -0,0 +1,349 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "mozilla/dom/SVGAElement.h" + +#include "mozilla/Attributes.h" +#include "mozilla/EventDispatcher.h" +#include "mozilla/EventStates.h" +#include "mozilla/dom/SVGAElementBinding.h" +#include "nsCOMPtr.h" +#include "nsContentUtils.h" +#include "nsGkAtoms.h" +#include "nsSVGString.h" +#include "nsIURI.h" + +NS_IMPL_NS_NEW_NAMESPACED_SVG_ELEMENT(A) + +namespace mozilla { +namespace dom { + +JSObject* +SVGAElement::WrapNode(JSContext *aCx, JS::Handle<JSObject*> aGivenProto) +{ + return SVGAElementBinding::Wrap(aCx, this, aGivenProto); +} + +nsSVGElement::StringInfo SVGAElement::sStringInfo[3] = +{ + { &nsGkAtoms::href, kNameSpaceID_None, true }, + { &nsGkAtoms::href, kNameSpaceID_XLink, true }, + { &nsGkAtoms::target, kNameSpaceID_None, true } +}; + + +//---------------------------------------------------------------------- +// nsISupports methods + +NS_INTERFACE_TABLE_HEAD_CYCLE_COLLECTION_INHERITED(SVGAElement) + NS_INTERFACE_TABLE_INHERITED(SVGAElement, + nsIDOMNode, + nsIDOMElement, + nsIDOMSVGElement, + Link) +NS_INTERFACE_TABLE_TAIL_INHERITING(SVGAElementBase) + +NS_IMPL_CYCLE_COLLECTION_CLASS(SVGAElement) + +NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(SVGAElement, + SVGAElementBase) +NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END + +NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(SVGAElement, + SVGAElementBase) +NS_IMPL_CYCLE_COLLECTION_UNLINK_END + +NS_IMPL_ADDREF_INHERITED(SVGAElement, SVGAElementBase) +NS_IMPL_RELEASE_INHERITED(SVGAElement, SVGAElementBase) + +//---------------------------------------------------------------------- +// Implementation + +SVGAElement::SVGAElement(already_AddRefed<mozilla::dom::NodeInfo>& aNodeInfo) + : SVGAElementBase(aNodeInfo) + , Link(this) +{ +} + +SVGAElement::~SVGAElement() +{ +} + +already_AddRefed<SVGAnimatedString> +SVGAElement::Href() +{ + return mStringAttributes[HREF].IsExplicitlySet() + ? mStringAttributes[HREF].ToDOMAnimatedString(this) + : mStringAttributes[XLINK_HREF].ToDOMAnimatedString(this); +} + +//---------------------------------------------------------------------- +// nsINode methods + +nsresult +SVGAElement::PreHandleEvent(EventChainPreVisitor& aVisitor) +{ + nsresult rv = Element::PreHandleEvent(aVisitor); + NS_ENSURE_SUCCESS(rv, rv); + + return PreHandleEventForLinks(aVisitor); +} + +nsresult +SVGAElement::PostHandleEvent(EventChainPostVisitor& aVisitor) +{ + return PostHandleEventForLinks(aVisitor); +} + +NS_IMPL_ELEMENT_CLONE_WITH_INIT(SVGAElement) + + +//---------------------------------------------------------------------- + +already_AddRefed<SVGAnimatedString> +SVGAElement::Target() +{ + return mStringAttributes[TARGET].ToDOMAnimatedString(this); +} + +void +SVGAElement::GetDownload(nsAString & aDownload) +{ + GetAttr(kNameSpaceID_None, nsGkAtoms::download, aDownload); +} + +void +SVGAElement::SetDownload(const nsAString & aDownload, ErrorResult& rv) +{ + rv = SetAttr(kNameSpaceID_None, nsGkAtoms::download, aDownload, true); +} + +//---------------------------------------------------------------------- +// nsIContent methods + +nsresult +SVGAElement::BindToTree(nsIDocument *aDocument, nsIContent *aParent, + nsIContent *aBindingParent, + bool aCompileEventHandlers) +{ + Link::ResetLinkState(false, Link::ElementHasHref()); + + nsresult rv = SVGAElementBase::BindToTree(aDocument, aParent, + aBindingParent, + aCompileEventHandlers); + NS_ENSURE_SUCCESS(rv, rv); + + nsIDocument* doc = GetComposedDoc(); + if (doc) { + doc->RegisterPendingLinkUpdate(this); + } + + return NS_OK; +} + +void +SVGAElement::UnbindFromTree(bool aDeep, bool aNullParent) +{ + // If this link is ever reinserted into a document, it might + // be under a different xml:base, so forget the cached state now. + Link::ResetLinkState(false, Link::ElementHasHref()); + + // Note, we need to use OwnerDoc() here since GetComposedDoc() may + // return null already at this point. + nsIDocument* doc = OwnerDoc(); + if (doc) { + doc->UnregisterPendingLinkUpdate(this); + } + + SVGAElementBase::UnbindFromTree(aDeep, aNullParent); +} + +already_AddRefed<nsIURI> +SVGAElement::GetHrefURI() const +{ + nsCOMPtr<nsIURI> hrefURI; + return IsLink(getter_AddRefs(hrefURI)) ? hrefURI.forget() : nullptr; +} + + +NS_IMETHODIMP_(bool) +SVGAElement::IsAttributeMapped(const nsIAtom* name) const +{ + static const MappedAttributeEntry* const map[] = { + sFEFloodMap, + sFiltersMap, + sFontSpecificationMap, + sGradientStopMap, + sLightingEffectsMap, + sMarkersMap, + sTextContentElementsMap, + sViewportsMap + }; + + return FindAttributeDependence(name, map) || + SVGAElementBase::IsAttributeMapped(name); +} + +bool +SVGAElement::IsFocusableInternal(int32_t *aTabIndex, bool aWithMouse) +{ + nsCOMPtr<nsIURI> uri; + if (IsLink(getter_AddRefs(uri))) { + if (aTabIndex) { + *aTabIndex = ((sTabFocusModel & eTabFocus_linksMask) == 0 ? -1 : 0); + } + return true; + } + if (nsSVGElement::IsFocusableInternal(aTabIndex, aWithMouse)) { + return true; + } + + if (aTabIndex) { + *aTabIndex = -1; + } + + return false; +} + +bool +SVGAElement::IsLink(nsIURI** aURI) const +{ + // To be a clickable XLink for styling and interaction purposes, we require: + // + // xlink:href - must be set + // xlink:type - must be unset or set to "" or set to "simple" + // xlink:show - must be unset or set to "", "new" or "replace" + // xlink:actuate - must be unset or set to "" or "onRequest" + // + // For any other values, we're either not a *clickable* XLink, or the end + // result is poorly specified. Either way, we return false. + + static nsIContent::AttrValuesArray sTypeVals[] = + { &nsGkAtoms::_empty, &nsGkAtoms::simple, nullptr }; + + static nsIContent::AttrValuesArray sShowVals[] = + { &nsGkAtoms::_empty, &nsGkAtoms::_new, &nsGkAtoms::replace, nullptr }; + + static nsIContent::AttrValuesArray sActuateVals[] = + { &nsGkAtoms::_empty, &nsGkAtoms::onRequest, nullptr }; + + // Optimization: check for href first for early return + bool useXLink = !HasAttr(kNameSpaceID_None, nsGkAtoms::href); + const nsAttrValue* href = + useXLink + ? mAttrsAndChildren.GetAttr(nsGkAtoms::href, kNameSpaceID_XLink) + : mAttrsAndChildren.GetAttr(nsGkAtoms::href, kNameSpaceID_None); + + if (href && + FindAttrValueIn(kNameSpaceID_XLink, nsGkAtoms::type, + sTypeVals, eCaseMatters) != + nsIContent::ATTR_VALUE_NO_MATCH && + FindAttrValueIn(kNameSpaceID_XLink, nsGkAtoms::show, + sShowVals, eCaseMatters) != + nsIContent::ATTR_VALUE_NO_MATCH && + FindAttrValueIn(kNameSpaceID_XLink, nsGkAtoms::actuate, + sActuateVals, eCaseMatters) != + nsIContent::ATTR_VALUE_NO_MATCH) { + nsCOMPtr<nsIURI> baseURI = GetBaseURI(); + // Get absolute URI + nsAutoString str; + const uint8_t idx = useXLink ? XLINK_HREF : HREF; + mStringAttributes[idx].GetAnimValue(str, this); + nsContentUtils::NewURIWithDocumentCharset(aURI, str, OwnerDoc(), baseURI); + // must promise out param is non-null if we return true + return !!*aURI; + } + + *aURI = nullptr; + return false; +} + +void +SVGAElement::GetLinkTarget(nsAString& aTarget) +{ + mStringAttributes[TARGET].GetAnimValue(aTarget, this); + if (aTarget.IsEmpty()) { + + static nsIContent::AttrValuesArray sShowVals[] = + { &nsGkAtoms::_new, &nsGkAtoms::replace, nullptr }; + + switch (FindAttrValueIn(kNameSpaceID_XLink, nsGkAtoms::show, + sShowVals, eCaseMatters)) { + case 0: + aTarget.AssignLiteral("_blank"); + return; + case 1: + return; + } + nsIDocument* ownerDoc = OwnerDoc(); + if (ownerDoc) { + ownerDoc->GetBaseTarget(aTarget); + } + } +} + +EventStates +SVGAElement::IntrinsicState() const +{ + return Link::LinkState() | SVGAElementBase::IntrinsicState(); +} + +nsresult +SVGAElement::SetAttr(int32_t aNameSpaceID, nsIAtom* aName, + nsIAtom* aPrefix, const nsAString& aValue, + bool aNotify) +{ + nsresult rv = SVGAElementBase::SetAttr(aNameSpaceID, aName, aPrefix, + aValue, aNotify); + + // The ordering of the parent class's SetAttr call and Link::ResetLinkState + // is important here! The attribute is not set until SetAttr returns, and + // we will need the updated attribute value because notifying the document + // that content states have changed will call IntrinsicState, which will try + // to get updated information about the visitedness from Link. + if (aName == nsGkAtoms::href && + (aNameSpaceID == kNameSpaceID_XLink || + aNameSpaceID == kNameSpaceID_None)) { + Link::ResetLinkState(!!aNotify, true); + } + + return rv; +} + +nsresult +SVGAElement::UnsetAttr(int32_t aNameSpaceID, nsIAtom* aAttr, + bool aNotify) +{ + nsresult rv = nsSVGElement::UnsetAttr(aNameSpaceID, aAttr, aNotify); + + // The ordering of the parent class's UnsetAttr call and Link::ResetLinkState + // is important here! The attribute is not unset until UnsetAttr returns, and + // we will need the updated attribute value because notifying the document + // that content states have changed will call IntrinsicState, which will try + // to get updated information about the visitedness from Link. + if (aAttr == nsGkAtoms::href && + (aNameSpaceID == kNameSpaceID_XLink || + aNameSpaceID == kNameSpaceID_None)) { + bool hasHref = HasAttr(kNameSpaceID_None, nsGkAtoms::href) || + HasAttr(kNameSpaceID_XLink, nsGkAtoms::href); + Link::ResetLinkState(!!aNotify, hasHref); + } + + return rv; +} + +//---------------------------------------------------------------------- +// nsSVGElement methods + +nsSVGElement::StringAttributesInfo +SVGAElement::GetStringInfo() +{ + return StringAttributesInfo(mStringAttributes, sStringInfo, + ArrayLength(sStringInfo)); +} + +} // namespace dom +} // namespace mozilla diff --git a/dom/svg/SVGAElement.h b/dom/svg/SVGAElement.h new file mode 100644 index 0000000000..4f7df9e507 --- /dev/null +++ b/dom/svg/SVGAElement.h @@ -0,0 +1,87 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef mozilla_dom_SVGAElement_h +#define mozilla_dom_SVGAElement_h + +#include "Link.h" +#include "nsSVGString.h" +#include "mozilla/dom/SVGGraphicsElement.h" + +nsresult NS_NewSVGAElement(nsIContent **aResult, + already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo); + +namespace mozilla { + +class EventChainPostVisitor; +class EventChainPreVisitor; + +namespace dom { + +typedef SVGGraphicsElement SVGAElementBase; + +class SVGAElement final : public SVGAElementBase, + public Link +{ +protected: + explicit SVGAElement(already_AddRefed<mozilla::dom::NodeInfo>& aNodeInfo); + friend nsresult (::NS_NewSVGAElement(nsIContent **aResult, + already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo)); + virtual JSObject* WrapNode(JSContext *cx, JS::Handle<JSObject*> aGivenProto) override; + +public: + NS_DECL_ISUPPORTS_INHERITED + NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(SVGAElement, SVGAElementBase) + + // nsINode interface methods + virtual nsresult PreHandleEvent(EventChainPreVisitor& aVisitor) override; + virtual nsresult PostHandleEvent( + EventChainPostVisitor& aVisitor) override; + virtual nsresult Clone(mozilla::dom::NodeInfo *aNodeInfo, nsINode **aResult) const override; + + // nsIContent + virtual nsresult BindToTree(nsIDocument *aDocument, nsIContent *aParent, + nsIContent *aBindingParent, + bool aCompileEventHandlers) override; + virtual void UnbindFromTree(bool aDeep = true, + bool aNullParent = true) override; + NS_IMETHOD_(bool) IsAttributeMapped(const nsIAtom* aAttribute) const override; + virtual bool IsFocusableInternal(int32_t* aTabIndex, bool aWithMouse) override; + virtual bool IsLink(nsIURI** aURI) const override; + virtual void GetLinkTarget(nsAString& aTarget) override; + virtual already_AddRefed<nsIURI> GetHrefURI() const override; + virtual EventStates IntrinsicState() const override; + nsresult SetAttr(int32_t aNameSpaceID, nsIAtom* aName, + const nsAString& aValue, bool aNotify) + { + return SetAttr(aNameSpaceID, aName, nullptr, aValue, aNotify); + } + virtual nsresult SetAttr(int32_t aNameSpaceID, nsIAtom* aName, + nsIAtom* aPrefix, const nsAString& aValue, + bool aNotify) override; + virtual nsresult UnsetAttr(int32_t aNameSpaceID, nsIAtom* aAttribute, + bool aNotify) override; + + // WebIDL + already_AddRefed<SVGAnimatedString> Href(); + already_AddRefed<SVGAnimatedString> Target(); + void GetDownload(nsAString & aDownload); + void SetDownload(const nsAString & aDownload, ErrorResult& rv); + +protected: + virtual ~SVGAElement(); + + virtual StringAttributesInfo GetStringInfo() override; + + enum { HREF, XLINK_HREF, TARGET }; + nsSVGString mStringAttributes[3]; + static StringInfo sStringInfo[3]; +}; + +} // namespace dom +} // namespace mozilla + +#endif // mozilla_dom_SVGAElement_h diff --git a/dom/svg/SVGAngle.cpp b/dom/svg/SVGAngle.cpp new file mode 100644 index 0000000000..83d5f7ce15 --- /dev/null +++ b/dom/svg/SVGAngle.cpp @@ -0,0 +1,123 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "SVGAngle.h" +#include "nsSVGAngle.h" +#include "mozilla/dom/SVGAngleBinding.h" + +using namespace mozilla; +using namespace mozilla::dom; + +NS_SVG_VAL_IMPL_CYCLE_COLLECTION_WRAPPERCACHED(SVGAngle, mSVGElement) + +NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(SVGAngle, AddRef) +NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(SVGAngle, Release) + +JSObject* +SVGAngle::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) +{ + return SVGAngleBinding::Wrap(aCx, this, aGivenProto); +} + +uint16_t +SVGAngle::UnitType() const +{ + if (mType == AnimValue) { + return mVal->mAnimValUnit; + } + return mVal->mBaseValUnit; +} + +float +SVGAngle::Value() const +{ + if (mType == AnimValue) { + return mVal->GetAnimValue(); + } + return mVal->GetBaseValue(); +} + +void +SVGAngle::SetValue(float aValue, ErrorResult& rv) +{ + if (mType == AnimValue) { + rv.Throw(NS_ERROR_DOM_NO_MODIFICATION_ALLOWED_ERR); + return; + } + bool isBaseVal = mType == BaseValue; + mVal->SetBaseValue(aValue, isBaseVal ? mSVGElement.get() : nullptr, + isBaseVal); +} + +float +SVGAngle::ValueInSpecifiedUnits() const +{ + if (mType == AnimValue) { + return mVal->mAnimVal; + } + return mVal->mBaseVal; +} + +void +SVGAngle::SetValueInSpecifiedUnits(float aValue, ErrorResult& rv) +{ + if (mType == AnimValue) { + rv.Throw(NS_ERROR_DOM_NO_MODIFICATION_ALLOWED_ERR); + return; + } else if (mType == BaseValue) { + mVal->SetBaseValueInSpecifiedUnits(aValue, mSVGElement); + } else { + mVal->mBaseVal = aValue; + } +} + +void +SVGAngle::NewValueSpecifiedUnits(uint16_t unitType, + float valueInSpecifiedUnits, + ErrorResult& rv) +{ + if (mType == AnimValue) { + rv.Throw(NS_ERROR_DOM_NO_MODIFICATION_ALLOWED_ERR); + return; + } + rv = mVal->NewValueSpecifiedUnits(unitType, valueInSpecifiedUnits, + mType == BaseValue ? mSVGElement.get() + : nullptr); +} + +void +SVGAngle::ConvertToSpecifiedUnits(uint16_t unitType, ErrorResult& rv) +{ + if (mType == AnimValue) { + rv.Throw(NS_ERROR_DOM_NO_MODIFICATION_ALLOWED_ERR); + return; + } + rv = mVal->ConvertToSpecifiedUnits(unitType, mType == BaseValue ? + mSVGElement.get() : nullptr); +} + +void +SVGAngle::SetValueAsString(const nsAString& aValue, ErrorResult& rv) +{ + if (mType == AnimValue) { + rv.Throw(NS_ERROR_DOM_NO_MODIFICATION_ALLOWED_ERR); + return; + } + bool isBaseVal = mType == BaseValue; + rv = mVal->SetBaseValueString(aValue, isBaseVal ? mSVGElement.get() : nullptr, + isBaseVal); +} + +void +SVGAngle::GetValueAsString(nsAString& aValue) +{ + if (mType == AnimValue) { + mVal->GetAnimValueString(aValue); + } else { + mVal->GetBaseValueString(aValue); + } +} + diff --git a/dom/svg/SVGAngle.h b/dom/svg/SVGAngle.h new file mode 100644 index 0000000000..d91fd6c57b --- /dev/null +++ b/dom/svg/SVGAngle.h @@ -0,0 +1,60 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef mozilla_dom_SVGAngle_h +#define mozilla_dom_SVGAngle_h + +#include "nsWrapperCache.h" +#include "nsSVGElement.h" +#include "mozilla/Attributes.h" + +class nsSVGAngle; + +namespace mozilla { +namespace dom { + +class SVGAngle final : public nsWrapperCache +{ +public: + typedef enum { + BaseValue, + AnimValue, + CreatedValue + } AngleType; + + NS_INLINE_DECL_CYCLE_COLLECTING_NATIVE_REFCOUNTING(SVGAngle) + NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_NATIVE_CLASS(SVGAngle) + + SVGAngle(nsSVGAngle* aVal, nsSVGElement *aSVGElement, AngleType aType) + : mVal(aVal), mSVGElement(aSVGElement), mType(aType) + { + } + + // WebIDL + nsSVGElement* GetParentObject() { return mSVGElement; } + virtual JSObject* WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override; + uint16_t UnitType() const; + float Value() const; + void GetValueAsString(nsAString& aValue); + void SetValue(float aValue, ErrorResult& rv); + float ValueInSpecifiedUnits() const; + void SetValueInSpecifiedUnits(float aValue, ErrorResult& rv); + void SetValueAsString(const nsAString& aValue, ErrorResult& rv); + void NewValueSpecifiedUnits(uint16_t unitType, float value, ErrorResult& rv); + void ConvertToSpecifiedUnits(uint16_t unitType, ErrorResult& rv); + +protected: + ~SVGAngle(); + + nsSVGAngle* mVal; // if mType is CreatedValue, we own the angle. Otherwise, the element does. + RefPtr<nsSVGElement> mSVGElement; + AngleType mType; +}; + +} //namespace dom +} //namespace mozilla + +#endif // mozilla_dom_SVGAngle_h diff --git a/dom/svg/SVGAnimateElement.cpp b/dom/svg/SVGAnimateElement.cpp new file mode 100644 index 0000000000..2ba4c5703e --- /dev/null +++ b/dom/svg/SVGAnimateElement.cpp @@ -0,0 +1,44 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "mozilla/dom/SVGAnimateElement.h" +#include "mozilla/dom/SVGAnimateElementBinding.h" + +NS_IMPL_NS_NEW_NAMESPACED_SVG_ELEMENT(Animate) + +namespace mozilla { +namespace dom { + +JSObject* +SVGAnimateElement::WrapNode(JSContext *aCx, JS::Handle<JSObject*> aGivenProto) +{ + return SVGAnimateElementBinding::Wrap(aCx, this, aGivenProto); +} + +//---------------------------------------------------------------------- +// Implementation + +SVGAnimateElement::SVGAnimateElement(already_AddRefed<mozilla::dom::NodeInfo>& aNodeInfo) + : SVGAnimationElement(aNodeInfo) +{ +} + +//---------------------------------------------------------------------- +// nsIDOMNode methods + +NS_IMPL_ELEMENT_CLONE_WITH_INIT(SVGAnimateElement) + +//---------------------------------------------------------------------- + +nsSMILAnimationFunction& +SVGAnimateElement::AnimationFunction() +{ + return mAnimationFunction; +} + +} // namespace dom +} // namespace mozilla + diff --git a/dom/svg/SVGAnimateElement.h b/dom/svg/SVGAnimateElement.h new file mode 100644 index 0000000000..ed029ef49f --- /dev/null +++ b/dom/svg/SVGAnimateElement.h @@ -0,0 +1,43 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef mozilla_dom_SVGAnimateElement_h +#define mozilla_dom_SVGAnimateElement_h + +#include "mozilla/Attributes.h" +#include "mozilla/dom/SVGAnimationElement.h" +#include "nsSMILAnimationFunction.h" + +nsresult NS_NewSVGAnimateElement(nsIContent **aResult, + already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo); + +namespace mozilla { +namespace dom { + +class SVGAnimateElement final : public SVGAnimationElement +{ +protected: + explicit SVGAnimateElement(already_AddRefed<mozilla::dom::NodeInfo>& aNodeInfo); + + nsSMILAnimationFunction mAnimationFunction; + friend nsresult + (::NS_NewSVGAnimateElement(nsIContent **aResult, + already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo)); + + virtual JSObject* WrapNode(JSContext *aCx, JS::Handle<JSObject*> aGivenProto) override; + +public: + // nsIDOMNode + virtual nsresult Clone(mozilla::dom::NodeInfo *aNodeInfo, nsINode **aResult) const override; + + // SVGAnimationElement + virtual nsSMILAnimationFunction& AnimationFunction() override; +}; + +} // namespace dom +} // namespace mozilla + +#endif // mozilla_dom_SVGAnimateElement_h diff --git a/dom/svg/SVGAnimateMotionElement.cpp b/dom/svg/SVGAnimateMotionElement.cpp new file mode 100644 index 0000000000..ed8eb7ccd6 --- /dev/null +++ b/dom/svg/SVGAnimateMotionElement.cpp @@ -0,0 +1,65 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "mozilla/dom/SVGAnimateMotionElement.h" +#include "mozilla/dom/SVGAnimateMotionElementBinding.h" + +NS_IMPL_NS_NEW_NAMESPACED_SVG_ELEMENT(AnimateMotion) + +namespace mozilla { +namespace dom { + +JSObject* +SVGAnimateMotionElement::WrapNode(JSContext *aCx, JS::Handle<JSObject*> aGivenProto) +{ + return SVGAnimateMotionElementBinding::Wrap(aCx, this, aGivenProto); +} + +//---------------------------------------------------------------------- +// Implementation + +SVGAnimateMotionElement::SVGAnimateMotionElement(already_AddRefed<mozilla::dom::NodeInfo>& aNodeInfo) + : SVGAnimationElement(aNodeInfo) +{ +} + +//---------------------------------------------------------------------- +// nsIDOMNode methods + +NS_IMPL_ELEMENT_CLONE_WITH_INIT(SVGAnimateMotionElement) + +//---------------------------------------------------------------------- + +nsSMILAnimationFunction& +SVGAnimateMotionElement::AnimationFunction() +{ + return mAnimationFunction; +} + +bool +SVGAnimateMotionElement::GetTargetAttributeName(int32_t *aNamespaceID, + nsIAtom **aLocalName) const +{ + // <animateMotion> doesn't take an attributeName, since it doesn't target an + // 'attribute' per se. We'll use a unique dummy attribute-name so that our + // nsSMILTargetIdentifier logic (which requires a attribute name) still works. + *aNamespaceID = kNameSpaceID_None; + *aLocalName = nsGkAtoms::mozAnimateMotionDummyAttr; + return true; +} + +nsSMILTargetAttrType +SVGAnimateMotionElement::GetTargetAttributeType() const +{ + // <animateMotion> doesn't take an attributeType, since it doesn't target an + // 'attribute' per se. We'll just return 'XML' for simplicity. (This just + // needs to match what we expect in nsSVGElement::GetAnimAttr.) + return eSMILTargetAttrType_XML; +} + +} // namespace dom +} // namespace mozilla + diff --git a/dom/svg/SVGAnimateMotionElement.h b/dom/svg/SVGAnimateMotionElement.h new file mode 100644 index 0000000000..7cc0556306 --- /dev/null +++ b/dom/svg/SVGAnimateMotionElement.h @@ -0,0 +1,55 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef mozilla_dom_SVGAnimateMotionElement_h +#define mozilla_dom_SVGAnimateMotionElement_h + +#include "mozilla/Attributes.h" +#include "mozilla/dom/SVGAnimationElement.h" +#include "SVGMotionSMILAnimationFunction.h" + +nsresult NS_NewSVGAnimateMotionElement(nsIContent **aResult, + already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo); + +namespace mozilla { +namespace dom { + +class SVGAnimateMotionElement final : public SVGAnimationElement +{ +protected: + explicit SVGAnimateMotionElement(already_AddRefed<mozilla::dom::NodeInfo>& aNodeInfo); + + SVGMotionSMILAnimationFunction mAnimationFunction; + friend nsresult + (::NS_NewSVGAnimateMotionElement(nsIContent **aResult, + already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo)); + + virtual JSObject* WrapNode(JSContext *aCx, JS::Handle<JSObject*> aGivenProto) override; + +public: + // nsIDOMNode specializations + virtual nsresult Clone(mozilla::dom::NodeInfo *aNodeInfo, nsINode **aResult) const override; + + // SVGAnimationElement + virtual nsSMILAnimationFunction& AnimationFunction() override; + virtual bool GetTargetAttributeName(int32_t *aNamespaceID, + nsIAtom **aLocalName) const override; + virtual nsSMILTargetAttrType GetTargetAttributeType() const override; + + // nsSVGElement + virtual nsIAtom* GetPathDataAttrName() const override { + return nsGkAtoms::path; + } + + // Utility method to let our <mpath> children tell us when they've changed, + // so we can make sure our mAnimationFunction is marked as having changed. + void MpathChanged() { mAnimationFunction.MpathChanged(); } +}; + +} // namespace dom +} // namespace mozilla + +#endif // mozilla_dom_SVGAnimateMotionElement_h diff --git a/dom/svg/SVGAnimateTransformElement.cpp b/dom/svg/SVGAnimateTransformElement.cpp new file mode 100644 index 0000000000..d3355cf809 --- /dev/null +++ b/dom/svg/SVGAnimateTransformElement.cpp @@ -0,0 +1,70 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "mozilla/dom/SVGAnimateTransformElement.h" +#include "mozilla/dom/SVGAnimateTransformElementBinding.h" + +NS_IMPL_NS_NEW_NAMESPACED_SVG_ELEMENT(AnimateTransform) + +namespace mozilla { +namespace dom { + +JSObject* +SVGAnimateTransformElement::WrapNode(JSContext *aCx, JS::Handle<JSObject*> aGivenProto) +{ + return SVGAnimateTransformElementBinding::Wrap(aCx, this, aGivenProto); +} + +//---------------------------------------------------------------------- +// Implementation + +SVGAnimateTransformElement::SVGAnimateTransformElement(already_AddRefed<mozilla::dom::NodeInfo>& aNodeInfo) + : SVGAnimationElement(aNodeInfo) +{ +} + +bool +SVGAnimateTransformElement::ParseAttribute(int32_t aNamespaceID, + nsIAtom* aAttribute, + const nsAString& aValue, + nsAttrValue& aResult) +{ + // 'type' is an <animateTransform>-specific attribute, and we'll handle it + // specially. + if (aNamespaceID == kNameSpaceID_None && aAttribute == nsGkAtoms::type) { + aResult.ParseAtom(aValue); + nsIAtom* atom = aResult.GetAtomValue(); + if (atom != nsGkAtoms::translate && + atom != nsGkAtoms::scale && + atom != nsGkAtoms::rotate && + atom != nsGkAtoms::skewX && + atom != nsGkAtoms::skewY) { + ReportAttributeParseFailure(OwnerDoc(), aAttribute, aValue); + } + return true; + } + + return SVGAnimationElement::ParseAttribute(aNamespaceID, + aAttribute, aValue, + aResult); +} + +//---------------------------------------------------------------------- +// nsIDOMNode methods + +NS_IMPL_ELEMENT_CLONE_WITH_INIT(SVGAnimateTransformElement) + +//---------------------------------------------------------------------- + +nsSMILAnimationFunction& +SVGAnimateTransformElement::AnimationFunction() +{ + return mAnimationFunction; +} + +} // namespace dom +} // namespace mozilla + diff --git a/dom/svg/SVGAnimateTransformElement.h b/dom/svg/SVGAnimateTransformElement.h new file mode 100644 index 0000000000..eacfe06c8d --- /dev/null +++ b/dom/svg/SVGAnimateTransformElement.h @@ -0,0 +1,49 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef mozilla_dom_SVGAnimateTransformElement_h +#define mozilla_dom_SVGAnimateTransformElement_h + +#include "mozilla/Attributes.h" +#include "mozilla/dom/SVGAnimationElement.h" +#include "nsSMILAnimationFunction.h" + +nsresult NS_NewSVGAnimateTransformElement(nsIContent **aResult, + already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo); + +namespace mozilla { +namespace dom { + +class SVGAnimateTransformElement final : public SVGAnimationElement +{ +protected: + explicit SVGAnimateTransformElement(already_AddRefed<mozilla::dom::NodeInfo>& aNodeInfo); + + nsSMILAnimationFunction mAnimationFunction; + friend nsresult + (::NS_NewSVGAnimateTransformElement(nsIContent **aResult, + already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo)); + + virtual JSObject* WrapNode(JSContext *aCx, JS::Handle<JSObject*> aGivenProto) override; + +public: + // nsIDOMNode specializations + virtual nsresult Clone(mozilla::dom::NodeInfo *aNodeInfo, nsINode **aResult) const override; + + // Element specializations + bool ParseAttribute(int32_t aNamespaceID, + nsIAtom* aAttribute, + const nsAString& aValue, + nsAttrValue& aResult) override; + + // SVGAnimationElement + virtual nsSMILAnimationFunction& AnimationFunction() override; +}; + +} // namespace dom +} // namespace mozilla + +#endif // mozilla_dom_SVGAnimateTransformElement_h diff --git a/dom/svg/SVGAnimatedAngle.cpp b/dom/svg/SVGAnimatedAngle.cpp new file mode 100644 index 0000000000..febc8ead37 --- /dev/null +++ b/dom/svg/SVGAnimatedAngle.cpp @@ -0,0 +1,36 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "SVGAnimatedAngle.h" +#include "nsSVGAngle.h" +#include "mozilla/dom/SVGAnimatedAngleBinding.h" + +using namespace mozilla; +using namespace mozilla::dom; + +NS_SVG_VAL_IMPL_CYCLE_COLLECTION_WRAPPERCACHED(SVGAnimatedAngle, mSVGElement) + +NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(SVGAnimatedAngle, AddRef) +NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(SVGAnimatedAngle, Release) + +JSObject* +SVGAnimatedAngle::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) +{ + return SVGAnimatedAngleBinding::Wrap(aCx, this, aGivenProto); +} + +already_AddRefed<SVGAngle> +SVGAnimatedAngle::BaseVal() +{ + return mVal->ToDOMBaseVal(mSVGElement); +} + +already_AddRefed<SVGAngle> +SVGAnimatedAngle::AnimVal() +{ + return mVal->ToDOMAnimVal(mSVGElement); +} + diff --git a/dom/svg/SVGAnimatedAngle.h b/dom/svg/SVGAnimatedAngle.h new file mode 100644 index 0000000000..50ea1d6181 --- /dev/null +++ b/dom/svg/SVGAnimatedAngle.h @@ -0,0 +1,48 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef mozilla_dom_SVGAnimatedAngle_h +#define mozilla_dom_SVGAnimatedAngle_h + +#include "nsWrapperCache.h" +#include "nsSVGElement.h" +#include "mozilla/Attributes.h" + +class nsSVGAngle; + +namespace mozilla { +namespace dom { + +class SVGAngle; + +class SVGAnimatedAngle final : public nsWrapperCache +{ +public: + NS_INLINE_DECL_CYCLE_COLLECTING_NATIVE_REFCOUNTING(SVGAnimatedAngle) + NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_NATIVE_CLASS(SVGAnimatedAngle) + + SVGAnimatedAngle(nsSVGAngle* aVal, nsSVGElement *aSVGElement) + : mVal(aVal), mSVGElement(aSVGElement) + { + } + + // WebIDL + nsSVGElement* GetParentObject() { return mSVGElement; } + virtual JSObject* WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override; + already_AddRefed<SVGAngle> BaseVal(); + already_AddRefed<SVGAngle> AnimVal(); + +protected: + ~SVGAnimatedAngle(); + + nsSVGAngle* mVal; // kept alive because it belongs to content + RefPtr<nsSVGElement> mSVGElement; +}; + +} //namespace dom +} //namespace mozilla + +#endif // mozilla_dom_SVGAnimatedAngle_h diff --git a/dom/svg/SVGAnimatedBoolean.cpp b/dom/svg/SVGAnimatedBoolean.cpp new file mode 100644 index 0000000000..102846e36b --- /dev/null +++ b/dom/svg/SVGAnimatedBoolean.cpp @@ -0,0 +1,23 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "SVGAnimatedBoolean.h" +#include "mozilla/dom/SVGAnimatedBooleanBinding.h" + +using namespace mozilla; +using namespace mozilla::dom; + +NS_SVG_VAL_IMPL_CYCLE_COLLECTION_WRAPPERCACHED(SVGAnimatedBoolean, mSVGElement) + +NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(SVGAnimatedBoolean, AddRef) +NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(SVGAnimatedBoolean, Release) + +JSObject* +SVGAnimatedBoolean::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) +{ + return SVGAnimatedBooleanBinding::Wrap(aCx, this, aGivenProto); +} + diff --git a/dom/svg/SVGAnimatedBoolean.h b/dom/svg/SVGAnimatedBoolean.h new file mode 100644 index 0000000000..b5d4d7993d --- /dev/null +++ b/dom/svg/SVGAnimatedBoolean.h @@ -0,0 +1,45 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef mozilla_dom_SVGAnimatedBoolean_h +#define mozilla_dom_SVGAnimatedBoolean_h + +#include "nsWrapperCache.h" +#include "nsSVGElement.h" +#include "mozilla/Attributes.h" +#include "nsSVGBoolean.h" + +namespace mozilla { +namespace dom { + +class SVGAnimatedBoolean final : public nsWrapperCache +{ + NS_INLINE_DECL_CYCLE_COLLECTING_NATIVE_REFCOUNTING(SVGAnimatedBoolean) + NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_NATIVE_CLASS(SVGAnimatedBoolean) + + SVGAnimatedBoolean(nsSVGBoolean* aVal, nsSVGElement *aSVGElement) + : mVal(aVal), mSVGElement(aSVGElement) + { + } + + // WebIDL + nsSVGElement* GetParentObject() const { return mSVGElement; } + virtual JSObject* WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override; + bool BaseVal() const { return mVal->GetBaseValue(); } + void SetBaseVal(bool aValue) { mVal->SetBaseValue(aValue, mSVGElement); } + bool AnimVal() const { mSVGElement->FlushAnimations(); return mVal->GetAnimValue(); } + +protected: + ~SVGAnimatedBoolean(); + + nsSVGBoolean* mVal; // kept alive because it belongs to content + RefPtr<nsSVGElement> mSVGElement; +}; + +} //namespace dom +} //namespace mozilla + +#endif // mozilla_dom_SVGAnimatedBoolean_h diff --git a/dom/svg/SVGAnimatedEnumeration.cpp b/dom/svg/SVGAnimatedEnumeration.cpp new file mode 100644 index 0000000000..103985f060 --- /dev/null +++ b/dom/svg/SVGAnimatedEnumeration.cpp @@ -0,0 +1,32 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "mozilla/dom/SVGAnimatedEnumeration.h" + +#include "mozilla/dom/SVGAnimatedEnumerationBinding.h" + +namespace mozilla { +namespace dom { + +NS_SVG_VAL_IMPL_CYCLE_COLLECTION_WRAPPERCACHED(SVGAnimatedEnumeration, + mSVGElement) + +NS_IMPL_CYCLE_COLLECTING_ADDREF(SVGAnimatedEnumeration) +NS_IMPL_CYCLE_COLLECTING_RELEASE(SVGAnimatedEnumeration) + +NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(SVGAnimatedEnumeration) + NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY + NS_INTERFACE_MAP_ENTRY(nsISupports) +NS_INTERFACE_MAP_END + +JSObject* +SVGAnimatedEnumeration::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) +{ + return SVGAnimatedEnumerationBinding::Wrap(aCx, this, aGivenProto); +} + +} // namespace dom +} // namespace mozilla diff --git a/dom/svg/SVGAnimatedEnumeration.h b/dom/svg/SVGAnimatedEnumeration.h new file mode 100644 index 0000000000..e65169b7bb --- /dev/null +++ b/dom/svg/SVGAnimatedEnumeration.h @@ -0,0 +1,49 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef mozilla_dom_SVGAnimatedEnumeration_h +#define mozilla_dom_SVGAnimatedEnumeration_h + +#include "nsWrapperCache.h" + +#include "nsSVGElement.h" + +namespace mozilla { +namespace dom { + +class SVGAnimatedEnumeration : public nsISupports + , public nsWrapperCache +{ +public: + NS_DECL_CYCLE_COLLECTING_ISUPPORTS + NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(SVGAnimatedEnumeration) + + nsSVGElement* GetParentObject() const + { + return mSVGElement; + } + + virtual JSObject* WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) + override final; + + virtual uint16_t BaseVal() = 0; + virtual void SetBaseVal(uint16_t aBaseVal, ErrorResult& aRv) = 0; + virtual uint16_t AnimVal() = 0; + +protected: + explicit SVGAnimatedEnumeration(nsSVGElement* aSVGElement) + : mSVGElement(aSVGElement) + { + } + virtual ~SVGAnimatedEnumeration() {}; + + RefPtr<nsSVGElement> mSVGElement; +}; + +} // namespace dom +} // namespace mozilla + +#endif // mozilla_dom_SVGAnimatedEnumeration_h diff --git a/dom/svg/SVGAnimatedInteger.cpp b/dom/svg/SVGAnimatedInteger.cpp new file mode 100644 index 0000000000..b7eb41ec09 --- /dev/null +++ b/dom/svg/SVGAnimatedInteger.cpp @@ -0,0 +1,32 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "mozilla/dom/SVGAnimatedInteger.h" + +#include "mozilla/dom/SVGAnimatedIntegerBinding.h" + +namespace mozilla { +namespace dom { + +NS_SVG_VAL_IMPL_CYCLE_COLLECTION_WRAPPERCACHED(SVGAnimatedInteger, + mSVGElement) + +NS_IMPL_CYCLE_COLLECTING_ADDREF(SVGAnimatedInteger) +NS_IMPL_CYCLE_COLLECTING_RELEASE(SVGAnimatedInteger) + +NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(SVGAnimatedInteger) + NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY + NS_INTERFACE_MAP_ENTRY(nsISupports) +NS_INTERFACE_MAP_END + +JSObject* +SVGAnimatedInteger::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) +{ + return SVGAnimatedIntegerBinding::Wrap(aCx, this, aGivenProto); +} + +} // namespace dom +} // namespace mozilla diff --git a/dom/svg/SVGAnimatedInteger.h b/dom/svg/SVGAnimatedInteger.h new file mode 100644 index 0000000000..d0b562679e --- /dev/null +++ b/dom/svg/SVGAnimatedInteger.h @@ -0,0 +1,49 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef mozilla_dom_SVGAnimatedInteger_h +#define mozilla_dom_SVGAnimatedInteger_h + +#include "nsWrapperCache.h" + +#include "nsSVGElement.h" + +namespace mozilla { +namespace dom { + +class SVGAnimatedInteger : public nsISupports + , public nsWrapperCache +{ +public: + NS_DECL_CYCLE_COLLECTING_ISUPPORTS + NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(SVGAnimatedInteger) + + nsSVGElement* GetParentObject() const + { + return mSVGElement; + } + + virtual JSObject* WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) + override final; + + virtual int32_t BaseVal() = 0; + virtual void SetBaseVal(int32_t aBaseVal) = 0; + virtual int32_t AnimVal() = 0; + +protected: + explicit SVGAnimatedInteger(nsSVGElement* aSVGElement) + : mSVGElement(aSVGElement) + { + } + virtual ~SVGAnimatedInteger() {}; + + RefPtr<nsSVGElement> mSVGElement; +}; + +} // namespace dom +} // namespace mozilla + +#endif // mozilla_dom_SVGAnimatedInteger_h diff --git a/dom/svg/SVGAnimatedLength.cpp b/dom/svg/SVGAnimatedLength.cpp new file mode 100644 index 0000000000..9efea0c485 --- /dev/null +++ b/dom/svg/SVGAnimatedLength.cpp @@ -0,0 +1,43 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "mozilla/dom/SVGAnimatedLength.h" +#include "mozilla/dom/SVGAnimatedLengthBinding.h" +#include "nsSVGLength2.h" +#include "DOMSVGLength.h" + +namespace mozilla { +namespace dom { + +NS_SVG_VAL_IMPL_CYCLE_COLLECTION_WRAPPERCACHED(SVGAnimatedLength, mSVGElement) + +NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(SVGAnimatedLength, AddRef) +NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(SVGAnimatedLength, Release) + +JSObject* +SVGAnimatedLength::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) +{ + return SVGAnimatedLengthBinding::Wrap(aCx, this, aGivenProto); +} + +already_AddRefed<DOMSVGLength> +SVGAnimatedLength::BaseVal() +{ + RefPtr<DOMSVGLength> angle; + mVal->ToDOMBaseVal(getter_AddRefs(angle), mSVGElement); + return angle.forget(); +} + +already_AddRefed<DOMSVGLength> +SVGAnimatedLength::AnimVal() +{ + RefPtr<DOMSVGLength> angle; + mVal->ToDOMAnimVal(getter_AddRefs(angle), mSVGElement); + return angle.forget(); +} + +} // namespace dom +} // namespace mozilla diff --git a/dom/svg/SVGAnimatedLength.h b/dom/svg/SVGAnimatedLength.h new file mode 100644 index 0000000000..b1e02fa4e2 --- /dev/null +++ b/dom/svg/SVGAnimatedLength.h @@ -0,0 +1,48 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef mozilla_dom_SVGAnimatedLength_h +#define mozilla_dom_SVGAnimatedLength_h + +#include "mozilla/Attributes.h" +#include "nsSVGElement.h" + +class nsSVGLength2; + +namespace mozilla { + +class DOMSVGLength; + +namespace dom { + +class SVGAnimatedLength final : public nsWrapperCache +{ +public: + NS_INLINE_DECL_CYCLE_COLLECTING_NATIVE_REFCOUNTING(SVGAnimatedLength) + NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_NATIVE_CLASS(SVGAnimatedLength) + + SVGAnimatedLength(nsSVGLength2* aVal, nsSVGElement *aSVGElement) + : mVal(aVal), mSVGElement(aSVGElement) + { + } + + // WebIDL + nsSVGElement* GetParentObject() { return mSVGElement; } + virtual JSObject* WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override; + already_AddRefed<DOMSVGLength> BaseVal(); + already_AddRefed<DOMSVGLength> AnimVal(); + +protected: + ~SVGAnimatedLength(); + + nsSVGLength2* mVal; // kept alive because it belongs to content + RefPtr<nsSVGElement> mSVGElement; +}; + +} // namespace dom +} // namespace mozilla + +#endif // mozilla_dom_SVGAnimatedLength_h diff --git a/dom/svg/SVGAnimatedLengthList.cpp b/dom/svg/SVGAnimatedLengthList.cpp new file mode 100644 index 0000000000..91702a16b4 --- /dev/null +++ b/dom/svg/SVGAnimatedLengthList.cpp @@ -0,0 +1,212 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "SVGAnimatedLengthList.h" + +#include "DOMSVGAnimatedLengthList.h" +#include "mozilla/Move.h" +#include "nsSVGElement.h" +#include "nsSVGAttrTearoffTable.h" +#include "nsSMILValue.h" +#include "SVGLengthListSMILType.h" + +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 Element::SetAttr, + // which takes care of notifying. + + rv = mBaseVal.CopyFrom(newBaseValue); + if (NS_FAILED(rv) && domWrapper) { + // Attempting to increase mBaseVal's length failed - reduce domWrapper + // back to the same length: + domWrapper->InternalBaseValListWillChangeTo(mBaseVal); + } + return rv; +} + +void +SVGAnimatedLengthList::ClearBaseValue(uint32_t aAttrEnum) +{ + DOMSVGAnimatedLengthList *domWrapper = + DOMSVGAnimatedLengthList::GetDOMWrapperIfExists(this); + if (domWrapper) { + // We must send this notification *before* changing mBaseVal! (See above.) + domWrapper->InternalBaseValListWillChangeTo(SVGLengthList()); + } + mBaseVal.Clear(); + // Caller notifies +} + +nsresult +SVGAnimatedLengthList::SetAnimValue(const SVGLengthList& aNewAnimValue, + nsSVGElement *aElement, + uint32_t 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, + uint32_t 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 = nullptr; + aElement->DidAnimateLengthList(aAttrEnum); +} + +nsISMILAttr* +SVGAnimatedLengthList::ToSMILAttr(nsSVGElement *aSVGElement, + uint8_t aAttrEnum, + uint8_t aAxis, + bool aCanZeroPadList) +{ + return new SMILAnimatedLengthList(this, aSVGElement, aAttrEnum, aAxis, aCanZeroPadList); +} + +nsresult +SVGAnimatedLengthList:: + SMILAnimatedLengthList::ValueFromString(const nsAString& aStr, + const dom::SVGAnimationElement* /*aSrcElement*/, + nsSMILValue& aValue, + bool& 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 = Move(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 = false; + for (uint32_t i = 0; i < llai->Length(); ++i) { + uint8_t unit = (*llai)[i].GetUnit(); + if (unit == nsIDOMSVGLength::SVG_LENGTHTYPE_PERCENTAGE || + unit == nsIDOMSVGLength::SVG_LENGTHTYPE_EMS || + unit == nsIDOMSVGLength::SVG_LENGTHTYPE_EXS) { + aPreventCachingOfSandwich = 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 = Move(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); + } +} + +} // namespace mozilla diff --git a/dom/svg/SVGAnimatedLengthList.h b/dom/svg/SVGAnimatedLengthList.h new file mode 100644 index 0000000000..447e947a0d --- /dev/null +++ b/dom/svg/SVGAnimatedLengthList.h @@ -0,0 +1,128 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef MOZILLA_SVGANIMATEDLENGTHLIST_H__ +#define MOZILLA_SVGANIMATEDLENGTHLIST_H__ + +#include "mozilla/Attributes.h" +#include "nsAutoPtr.h" +#include "nsISMILAttr.h" +#include "SVGLengthList.h" + +class nsSMILValue; +class nsSVGElement; + +namespace mozilla { + +namespace dom { +class SVGAnimationElement; +} // namespace dom + +/** + * 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(uint32_t aAttrEnum); + + const SVGLengthList& GetAnimValue() const { + return mAnimVal ? *mAnimVal : mBaseVal; + } + + nsresult SetAnimValue(const SVGLengthList& aValue, + nsSVGElement *aElement, + uint32_t aAttrEnum); + + void ClearAnimValue(nsSVGElement *aElement, + uint32_t aAttrEnum); + + bool IsAnimating() const { + return !!mAnimVal; + } + + /// Callers own the returned nsISMILAttr + nsISMILAttr* ToSMILAttr(nsSVGElement* aSVGElement, uint8_t aAttrEnum, + uint8_t aAxis, bool aCanZeroPadList); + +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; + + struct SMILAnimatedLengthList : public nsISMILAttr + { + public: + SMILAnimatedLengthList(SVGAnimatedLengthList* aVal, + nsSVGElement* aSVGElement, + uint8_t aAttrEnum, + uint8_t aAxis, + bool 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; + uint8_t mAttrEnum; + uint8_t mAxis; + bool mCanZeroPadList; // See SVGLengthListAndInfo::CanZeroPadList + + // nsISMILAttr methods + virtual nsresult ValueFromString(const nsAString& aStr, + const dom::SVGAnimationElement* aSrcElement, + nsSMILValue& aValue, + bool& aPreventCachingOfSandwich) const override; + virtual nsSMILValue GetBaseValue() const override; + virtual void ClearAnimValue() override; + virtual nsresult SetAnimValue(const nsSMILValue& aValue) override; + }; +}; + +} // namespace mozilla + +#endif // MOZILLA_SVGANIMATEDLENGTHLIST_H__ diff --git a/dom/svg/SVGAnimatedNumber.cpp b/dom/svg/SVGAnimatedNumber.cpp new file mode 100644 index 0000000000..b14bfb5582 --- /dev/null +++ b/dom/svg/SVGAnimatedNumber.cpp @@ -0,0 +1,32 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "mozilla/dom/SVGAnimatedNumber.h" + +#include "mozilla/dom/SVGAnimatedNumberBinding.h" + +namespace mozilla { +namespace dom { + +NS_SVG_VAL_IMPL_CYCLE_COLLECTION_WRAPPERCACHED(SVGAnimatedNumber, + mSVGElement) + +NS_IMPL_CYCLE_COLLECTING_ADDREF(SVGAnimatedNumber) +NS_IMPL_CYCLE_COLLECTING_RELEASE(SVGAnimatedNumber) + +NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(SVGAnimatedNumber) + NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY + NS_INTERFACE_MAP_ENTRY(nsISupports) +NS_INTERFACE_MAP_END + +JSObject* +SVGAnimatedNumber::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) +{ + return SVGAnimatedNumberBinding::Wrap(aCx, this, aGivenProto); +} + +} // namespace dom +} // namespace mozilla diff --git a/dom/svg/SVGAnimatedNumber.h b/dom/svg/SVGAnimatedNumber.h new file mode 100644 index 0000000000..c992b2e54e --- /dev/null +++ b/dom/svg/SVGAnimatedNumber.h @@ -0,0 +1,50 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef mozilla_dom_SVGAnimatedNumber_h +#define mozilla_dom_SVGAnimatedNumber_h + +#include "nsISupports.h" +#include "nsWrapperCache.h" + +#include "nsSVGElement.h" + +namespace mozilla { +namespace dom { + +class SVGAnimatedNumber : public nsISupports + , public nsWrapperCache +{ +public: + NS_DECL_CYCLE_COLLECTING_ISUPPORTS + NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(SVGAnimatedNumber) + + nsSVGElement* GetParentObject() const + { + return mSVGElement; + } + + virtual JSObject* WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) + override final; + + virtual float BaseVal() = 0; + virtual void SetBaseVal(float aBaseVal) = 0; + virtual float AnimVal() = 0; + +protected: + explicit SVGAnimatedNumber(nsSVGElement* aSVGElement) + : mSVGElement(aSVGElement) + { + } + virtual ~SVGAnimatedNumber() {}; + + RefPtr<nsSVGElement> mSVGElement; +}; + +} // namespace dom +} // namespace mozilla + +#endif // mozilla_dom_SVGAnimatedNumber_h diff --git a/dom/svg/SVGAnimatedNumberList.cpp b/dom/svg/SVGAnimatedNumberList.cpp new file mode 100644 index 0000000000..9be7ffadd8 --- /dev/null +++ b/dom/svg/SVGAnimatedNumberList.cpp @@ -0,0 +1,188 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "SVGAnimatedNumberList.h" + +#include "DOMSVGAnimatedNumberList.h" +#include "mozilla/Move.h" +#include "nsSVGElement.h" +#include "nsSVGAttrTearoffTable.h" +#include "nsSMILValue.h" +#include "SVGNumberListSMILType.h" + +namespace mozilla { + +nsresult +SVGAnimatedNumberList::SetBaseValueString(const nsAString& aValue) +{ + SVGNumberList newBaseValue; + nsresult rv = newBaseValue.SetValueFromString(aValue); + if (NS_FAILED(rv)) { + return rv; + } + + DOMSVGAnimatedNumberList *domWrapper = + DOMSVGAnimatedNumberList::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 Element::SetAttr, + // which takes care of notifying. + + mIsBaseSet = true; + rv = mBaseVal.CopyFrom(newBaseValue); + if (NS_FAILED(rv) && domWrapper) { + // Attempting to increase mBaseVal's length failed - reduce domWrapper + // back to the same length: + domWrapper->InternalBaseValListWillChangeTo(mBaseVal); + } + return rv; +} + +void +SVGAnimatedNumberList::ClearBaseValue(uint32_t aAttrEnum) +{ + DOMSVGAnimatedNumberList *domWrapper = + DOMSVGAnimatedNumberList::GetDOMWrapperIfExists(this); + if (domWrapper) { + // We must send this notification *before* changing mBaseVal! (See above.) + domWrapper->InternalBaseValListWillChangeTo(SVGNumberList()); + } + mBaseVal.Clear(); + mIsBaseSet = false; + // Caller notifies +} + +nsresult +SVGAnimatedNumberList::SetAnimValue(const SVGNumberList& aNewAnimValue, + nsSVGElement *aElement, + uint32_t aAttrEnum) +{ + DOMSVGAnimatedNumberList *domWrapper = + DOMSVGAnimatedNumberList::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 DOMSVGAnimatedNumberList::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 SVGNumberList(); + } + 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->DidAnimateNumberList(aAttrEnum); + return NS_OK; +} + +void +SVGAnimatedNumberList::ClearAnimValue(nsSVGElement *aElement, + uint32_t aAttrEnum) +{ + DOMSVGAnimatedNumberList *domWrapper = + DOMSVGAnimatedNumberList::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 = nullptr; + aElement->DidAnimateNumberList(aAttrEnum); +} + +nsISMILAttr* +SVGAnimatedNumberList::ToSMILAttr(nsSVGElement *aSVGElement, + uint8_t aAttrEnum) +{ + return new SMILAnimatedNumberList(this, aSVGElement, aAttrEnum); +} + +nsresult +SVGAnimatedNumberList:: + SMILAnimatedNumberList::ValueFromString(const nsAString& aStr, + const dom::SVGAnimationElement* /*aSrcElement*/, + nsSMILValue& aValue, + bool& aPreventCachingOfSandwich) const +{ + nsSMILValue val(&SVGNumberListSMILType::sSingleton); + SVGNumberListAndInfo *nlai = static_cast<SVGNumberListAndInfo*>(val.mU.mPtr); + nsresult rv = nlai->SetValueFromString(aStr); + if (NS_SUCCEEDED(rv)) { + nlai->SetInfo(mElement); + aValue = Move(val); + } + aPreventCachingOfSandwich = false; + return rv; +} + +nsSMILValue +SVGAnimatedNumberList::SMILAnimatedNumberList::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(&SVGNumberListSMILType::sSingleton); + SVGNumberListAndInfo *nlai = static_cast<SVGNumberListAndInfo*>(tmp.mU.mPtr); + nsresult rv = nlai->CopyFrom(mVal->mBaseVal); + if (NS_SUCCEEDED(rv)) { + nlai->SetInfo(mElement); + Swap(val, tmp); + } + return val; +} + +nsresult +SVGAnimatedNumberList::SMILAnimatedNumberList::SetAnimValue(const nsSMILValue& aValue) +{ + NS_ASSERTION(aValue.mType == &SVGNumberListSMILType::sSingleton, + "Unexpected type to assign animated value"); + if (aValue.mType == &SVGNumberListSMILType::sSingleton) { + mVal->SetAnimValue(*static_cast<SVGNumberListAndInfo*>(aValue.mU.mPtr), + mElement, + mAttrEnum); + } + return NS_OK; +} + +void +SVGAnimatedNumberList::SMILAnimatedNumberList::ClearAnimValue() +{ + if (mVal->mAnimVal) { + mVal->ClearAnimValue(mElement, mAttrEnum); + } +} + +} // namespace mozilla diff --git a/dom/svg/SVGAnimatedNumberList.h b/dom/svg/SVGAnimatedNumberList.h new file mode 100644 index 0000000000..6ef713c9d5 --- /dev/null +++ b/dom/svg/SVGAnimatedNumberList.h @@ -0,0 +1,130 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef MOZILLA_SVGANIMATEDNUMBERLIST_H__ +#define MOZILLA_SVGANIMATEDNUMBERLIST_H__ + +#include "mozilla/Attributes.h" +#include "nsAutoPtr.h" +#include "nsISMILAttr.h" +#include "SVGNumberList.h" + +class nsSMILValue; +class nsSVGElement; + +namespace mozilla { + +namespace dom { +class SVGAnimationElement; +} // namespace dom + +/** + * Class SVGAnimatedNumberList + * + * 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 + * DOMSVGAnimatedNumberList for the heavier DOM class that wraps instances of + * this class and implements the SVG specification's SVGAnimatedNumberList DOM + * interface. + * + * Except where noted otherwise, this class' methods take care of keeping the + * appropriate DOM wrappers in sync (see the comment in + * DOMSVGAnimatedNumberList::InternalBaseValListWillChangeTo) so that their + * consumers don't need to concern themselves with that. + */ +class SVGAnimatedNumberList +{ + // friends so that they can get write access to mBaseVal + friend class DOMSVGNumber; + friend class DOMSVGNumberList; + +public: + SVGAnimatedNumberList() {} + + /** + * Because it's so important that mBaseVal and its DOMSVGNumberList wrapper + * (if any) be kept in sync (see the comment in + * DOMSVGAnimatedNumberList::InternalBaseValListWillChangeTo), this method + * returns a const reference. Only our friend classes may get mutable + * references to mBaseVal. + */ + const SVGNumberList& GetBaseValue() const { + return mBaseVal; + } + + nsresult SetBaseValueString(const nsAString& aValue); + + void ClearBaseValue(uint32_t aAttrEnum); + + const SVGNumberList& GetAnimValue() const { + return mAnimVal ? *mAnimVal : mBaseVal; + } + + nsresult SetAnimValue(const SVGNumberList& aValue, + nsSVGElement *aElement, + uint32_t aAttrEnum); + + void ClearAnimValue(nsSVGElement *aElement, + uint32_t aAttrEnum); + + // Returns true if the animated value of this list has been explicitly + // set (either by animation, or by taking on the base value which has been + // explicitly set by markup or a DOM call), false otherwise. + // If this returns false, the animated value is still valid, that is, + // useable, and represents the default base value of the attribute. + bool IsExplicitlySet() const + { return !!mAnimVal || mIsBaseSet; } + + bool IsAnimating() const { + return !!mAnimVal; + } + + /// Callers own the returned nsISMILAttr + nsISMILAttr* ToSMILAttr(nsSVGElement* aSVGElement, uint8_t aAttrEnum); + +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="">). + + SVGNumberList mBaseVal; + nsAutoPtr<SVGNumberList> mAnimVal; + bool mIsBaseSet; + + struct SMILAnimatedNumberList : public nsISMILAttr + { + public: + SMILAnimatedNumberList(SVGAnimatedNumberList* aVal, + nsSVGElement* aSVGElement, + uint8_t aAttrEnum) + : mVal(aVal) + , mElement(aSVGElement) + , mAttrEnum(aAttrEnum) + {} + + // 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. + SVGAnimatedNumberList* mVal; + nsSVGElement* mElement; + uint8_t mAttrEnum; + + // nsISMILAttr methods + virtual nsresult ValueFromString(const nsAString& aStr, + const dom::SVGAnimationElement* aSrcElement, + nsSMILValue& aValue, + bool& aPreventCachingOfSandwich) const override; + virtual nsSMILValue GetBaseValue() const override; + virtual void ClearAnimValue() override; + virtual nsresult SetAnimValue(const nsSMILValue& aValue) override; + }; +}; + +} // namespace mozilla + +#endif // MOZILLA_SVGANIMATEDNUMBERLIST_H__ diff --git a/dom/svg/SVGAnimatedPathSegList.cpp b/dom/svg/SVGAnimatedPathSegList.cpp new file mode 100644 index 0000000000..4f07d20404 --- /dev/null +++ b/dom/svg/SVGAnimatedPathSegList.cpp @@ -0,0 +1,219 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "SVGAnimatedPathSegList.h" + +#include "DOMSVGPathSegList.h" +#include "mozilla/Move.h" +#include "nsSVGElement.h" +#include "nsSVGAttrTearoffTable.h" +#include "nsSMILValue.h" +#include "SVGPathSegListSMILType.h" + +// See the comments in this file's header! + +namespace mozilla { + +nsresult +SVGAnimatedPathSegList::SetBaseValueString(const nsAString& aValue) +{ + SVGPathData newBaseValue; + + // The spec says that the path data is parsed and accepted up to the first + // error encountered, so we don't return early if an error occurs. However, + // we do want to throw any error code from setAttribute if there's a problem. + + nsresult rv = newBaseValue.SetValueFromString(aValue); + + // We must send these notifications *before* changing mBaseVal! 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's values *before* we + // change them. See the comments in + // DOMSVGPathSegList::InternalListWillChangeTo(). + + DOMSVGPathSegList *baseValWrapper = + DOMSVGPathSegList::GetDOMWrapperIfExists(GetBaseValKey()); + if (baseValWrapper) { + baseValWrapper->InternalListWillChangeTo(newBaseValue); + } + + DOMSVGPathSegList* animValWrapper = nullptr; + if (!IsAnimating()) { // DOM anim val wraps our base val too! + animValWrapper = DOMSVGPathSegList::GetDOMWrapperIfExists(GetAnimValKey()); + if (animValWrapper) { + animValWrapper->InternalListWillChangeTo(newBaseValue); + } + } + + // Only now may we modify mBaseVal! + + // We don't need to call DidChange* here - we're only called by + // nsSVGElement::ParseAttribute under Element::SetAttr, + // which takes care of notifying. + + nsresult rv2 = mBaseVal.CopyFrom(newBaseValue); + if (NS_FAILED(rv2)) { + // Attempting to increase mBaseVal's length failed (mBaseVal is left + // unmodified). We MUST keep any DOM wrappers in sync: + if (baseValWrapper) { + baseValWrapper->InternalListWillChangeTo(mBaseVal); + } + if (animValWrapper) { + animValWrapper->InternalListWillChangeTo(mBaseVal); + } + return rv2; + } + return rv; +} + +void +SVGAnimatedPathSegList::ClearBaseValue() +{ + // We must send these notifications *before* changing mBaseVal! (See above.) + + DOMSVGPathSegList *baseValWrapper = + DOMSVGPathSegList::GetDOMWrapperIfExists(GetBaseValKey()); + if (baseValWrapper) { + baseValWrapper->InternalListWillChangeTo(SVGPathData()); + } + + if (!IsAnimating()) { // DOM anim val wraps our base val too! + DOMSVGPathSegList *animValWrapper = + DOMSVGPathSegList::GetDOMWrapperIfExists(GetAnimValKey()); + if (animValWrapper) { + animValWrapper->InternalListWillChangeTo(SVGPathData()); + } + } + + mBaseVal.Clear(); + // Caller notifies +} + +nsresult +SVGAnimatedPathSegList::SetAnimValue(const SVGPathData& aNewAnimValue, + nsSVGElement *aElement) +{ + // Note that a new animation may totally change the number of items in the + // animVal list, either replacing what was essentially a mirror of the + // baseVal list, or else replacing and overriding an existing animation. + // Unfortunately it is 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. In the case of DOMSVGPathSegList + // the InternalListWillChangeTo method is not virtually free as it is for the + // other DOM list classes, so this is a shame. We'd quite like to be able to + // skip the call if possible. + + // We must send these notifications *before* changing mAnimVal! (See above.) + + DOMSVGPathSegList *domWrapper = + DOMSVGPathSegList::GetDOMWrapperIfExists(GetAnimValKey()); + if (domWrapper) { + domWrapper->InternalListWillChangeTo(aNewAnimValue); + } + if (!mAnimVal) { + mAnimVal = new SVGPathData(); + } + nsresult rv = mAnimVal->CopyFrom(aNewAnimValue); + if (NS_FAILED(rv)) { + // OOM. We clear the animation and, importantly, ClearAnimValue() ensures + // that mAnimVal's DOM wrapper (if any) is kept in sync! + ClearAnimValue(aElement); + } + aElement->DidAnimatePathSegList(); + return rv; +} + +void +SVGAnimatedPathSegList::ClearAnimValue(nsSVGElement *aElement) +{ + // We must send these notifications *before* changing mAnimVal! (See above.) + + DOMSVGPathSegList *domWrapper = + DOMSVGPathSegList::GetDOMWrapperIfExists(GetAnimValKey()); + if (domWrapper) { + // When all animation ends, animVal simply mirrors baseVal, which may have + // a different number of items to the last active animated value. + // + domWrapper->InternalListWillChangeTo(mBaseVal); + } + mAnimVal = nullptr; + aElement->DidAnimatePathSegList(); +} + +nsISMILAttr* +SVGAnimatedPathSegList::ToSMILAttr(nsSVGElement *aElement) +{ + return new SMILAnimatedPathSegList(this, aElement); +} + +nsresult +SVGAnimatedPathSegList:: + SMILAnimatedPathSegList::ValueFromString(const nsAString& aStr, + const dom::SVGAnimationElement* /*aSrcElement*/, + nsSMILValue& aValue, + bool& aPreventCachingOfSandwich) const +{ + nsSMILValue val(SVGPathSegListSMILType::Singleton()); + SVGPathDataAndInfo *list = static_cast<SVGPathDataAndInfo*>(val.mU.mPtr); + nsresult rv = list->SetValueFromString(aStr); + if (NS_SUCCEEDED(rv)) { + list->SetElement(mElement); + aValue = Move(val); + } + aPreventCachingOfSandwich = false; + return rv; +} + +nsSMILValue +SVGAnimatedPathSegList::SMILAnimatedPathSegList::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(SVGPathSegListSMILType::Singleton()); + SVGPathDataAndInfo *list = static_cast<SVGPathDataAndInfo*>(tmp.mU.mPtr); + nsresult rv = list->CopyFrom(mVal->mBaseVal); + if (NS_SUCCEEDED(rv)) { + list->SetElement(mElement); + val = Move(tmp); + } + return val; +} + +nsresult +SVGAnimatedPathSegList::SMILAnimatedPathSegList::SetAnimValue(const nsSMILValue& aValue) +{ + NS_ASSERTION(aValue.mType == SVGPathSegListSMILType::Singleton(), + "Unexpected type to assign animated value"); + if (aValue.mType == SVGPathSegListSMILType::Singleton()) { + mVal->SetAnimValue(*static_cast<SVGPathDataAndInfo*>(aValue.mU.mPtr), + mElement); + } + return NS_OK; +} + +void +SVGAnimatedPathSegList::SMILAnimatedPathSegList::ClearAnimValue() +{ + if (mVal->mAnimVal) { + mVal->ClearAnimValue(mElement); + } +} + +size_t +SVGAnimatedPathSegList::SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const +{ + size_t total = mBaseVal.SizeOfExcludingThis(aMallocSizeOf); + if (mAnimVal) { + mAnimVal->SizeOfIncludingThis(aMallocSizeOf); + } + return total; +} + +} // namespace mozilla diff --git a/dom/svg/SVGAnimatedPathSegList.h b/dom/svg/SVGAnimatedPathSegList.h new file mode 100644 index 0000000000..42136db92c --- /dev/null +++ b/dom/svg/SVGAnimatedPathSegList.h @@ -0,0 +1,133 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef MOZILLA_SVGANIMATEDPATHSEGLIST_H__ +#define MOZILLA_SVGANIMATEDPATHSEGLIST_H__ + +#include "mozilla/Attributes.h" +#include "mozilla/MemoryReporting.h" +#include "nsAutoPtr.h" +#include "nsISMILAttr.h" +#include "SVGPathData.h" + +class nsSMILValue; +class nsSVGElement; + +namespace mozilla { + +namespace dom { +class SVGAnimationElement; +} // namespace dom + +/** + * Class SVGAnimatedPathSegList + * + * Despite the fact that no SVGAnimatedPathSegList interface or objects exist + * in the SVG specification (unlike e.g. SVGAnimated*Length*List), we + * nevertheless have this internal class. (Note that there is an + * SVGAnimatedPathData interface, but that's quite different to + * SVGAnimatedLengthList since it is inherited by elements, as opposed to + * elements having members of that type.) The reason that we have this class is + * to provide a single locked down point of entry to the SVGPathData objects, + * which helps ensure that the DOM wrappers for SVGPathData objects' are always + * kept in sync. This is vitally important (see the comment in + * DOMSVGPathSegList::InternalListWillChangeTo) and frees consumers from having + * to know or worry about wrappers (or forget about them!) for the most part. + */ +class SVGAnimatedPathSegList final +{ + // friends so that they can get write access to mBaseVal and mAnimVal + friend class DOMSVGPathSeg; + friend class DOMSVGPathSegList; + +public: + SVGAnimatedPathSegList() {} + + /** + * Because it's so important that mBaseVal and its DOMSVGPathSegList wrapper + * (if any) be kept in sync (see the comment in + * DOMSVGPathSegList::InternalListWillChangeTo), this method returns a const + * reference. Only our friend classes may get mutable references to mBaseVal. + */ + const SVGPathData& GetBaseValue() const { + return mBaseVal; + } + + nsresult SetBaseValueString(const nsAString& aValue); + + void ClearBaseValue(); + + /** + * const! See comment for GetBaseValue! + */ + const SVGPathData& GetAnimValue() const { + return mAnimVal ? *mAnimVal : mBaseVal; + } + + nsresult SetAnimValue(const SVGPathData& aValue, + nsSVGElement *aElement); + + void ClearAnimValue(nsSVGElement *aElement); + + /** + * Needed for correct DOM wrapper construction since GetAnimValue may + * actually return the baseVal! + */ + void *GetBaseValKey() const { + return (void*)&mBaseVal; + } + void *GetAnimValKey() const { + return (void*)&mAnimVal; + } + + bool IsAnimating() const { + return !!mAnimVal; + } + + /// Callers own the returned nsISMILAttr + nsISMILAttr* ToSMILAttr(nsSVGElement* aElement); + + size_t SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const; + +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="">). + + SVGPathData mBaseVal; + nsAutoPtr<SVGPathData> mAnimVal; + + struct SMILAnimatedPathSegList : public nsISMILAttr + { + public: + SMILAnimatedPathSegList(SVGAnimatedPathSegList* aVal, + nsSVGElement* aElement) + : mVal(aVal) + , mElement(aElement) + {} + + // 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. + SVGAnimatedPathSegList *mVal; + nsSVGElement *mElement; + + // nsISMILAttr methods + virtual nsresult ValueFromString(const nsAString& aStr, + const dom::SVGAnimationElement* aSrcElement, + nsSMILValue& aValue, + bool& aPreventCachingOfSandwich) const override; + virtual nsSMILValue GetBaseValue() const override; + virtual void ClearAnimValue() override; + virtual nsresult SetAnimValue(const nsSMILValue& aValue) override; + }; +}; + +} // namespace mozilla + +#endif // MOZILLA_SVGANIMATEDPATHSEGLIST_H__ diff --git a/dom/svg/SVGAnimatedPointList.cpp b/dom/svg/SVGAnimatedPointList.cpp new file mode 100644 index 0000000000..13031901d7 --- /dev/null +++ b/dom/svg/SVGAnimatedPointList.cpp @@ -0,0 +1,212 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "SVGAnimatedPointList.h" + +#include "DOMSVGPointList.h" +#include "mozilla/Move.h" +#include "nsSVGElement.h" +#include "nsSVGAttrTearoffTable.h" +#include "nsSMILValue.h" +#include "SVGPointListSMILType.h" + +// See the comments in this file's header! + +namespace mozilla { + +nsresult +SVGAnimatedPointList::SetBaseValueString(const nsAString& aValue) +{ + SVGPointList newBaseValue; + + // The spec says that the point data is parsed and accepted up to the first + // error encountered, so we don't return early if an error occurs. However, + // we do want to throw any error code from setAttribute if there's a problem. + + nsresult rv = newBaseValue.SetValueFromString(aValue); + + // We must send these notifications *before* changing mBaseVal! 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's values *before* we + // change them. See the comments in + // DOMSVGPointList::InternalListWillChangeTo(). + + DOMSVGPointList *baseValWrapper = + DOMSVGPointList::GetDOMWrapperIfExists(GetBaseValKey()); + if (baseValWrapper) { + baseValWrapper->InternalListWillChangeTo(newBaseValue); + } + + DOMSVGPointList* animValWrapper = nullptr; + if (!IsAnimating()) { // DOM anim val wraps our base val too! + animValWrapper = DOMSVGPointList::GetDOMWrapperIfExists(GetAnimValKey()); + if (animValWrapper) { + animValWrapper->InternalListWillChangeTo(newBaseValue); + } + } + + // Only now may we modify mBaseVal! + + // We don't need to call DidChange* here - we're only called by + // nsSVGElement::ParseAttribute under Element::SetAttr, + // which takes care of notifying. + + nsresult rv2 = mBaseVal.CopyFrom(newBaseValue); + if (NS_FAILED(rv2)) { + // Attempting to increase mBaseVal's length failed (mBaseVal is left + // unmodified). We MUST keep any DOM wrappers in sync: + if (baseValWrapper) { + baseValWrapper->InternalListWillChangeTo(mBaseVal); + } + if (animValWrapper) { + animValWrapper->InternalListWillChangeTo(mBaseVal); + } + return rv2; + } + return rv; +} + +void +SVGAnimatedPointList::ClearBaseValue() +{ + // We must send these notifications *before* changing mBaseVal! (See above.) + + DOMSVGPointList *baseValWrapper = + DOMSVGPointList::GetDOMWrapperIfExists(GetBaseValKey()); + if (baseValWrapper) { + baseValWrapper->InternalListWillChangeTo(SVGPointList()); + } + + if (!IsAnimating()) { // DOM anim val wraps our base val too! + DOMSVGPointList *animValWrapper = + DOMSVGPointList::GetDOMWrapperIfExists(GetAnimValKey()); + if (animValWrapper) { + animValWrapper->InternalListWillChangeTo(SVGPointList()); + } + } + + mBaseVal.Clear(); + // Caller notifies +} + +nsresult +SVGAnimatedPointList::SetAnimValue(const SVGPointList& aNewAnimValue, + nsSVGElement *aElement) +{ + // Note that a new animation may totally change the number of items in the + // animVal list, either replacing what was essentially a mirror of the + // baseVal list, or else replacing and overriding an existing animation. + // It is not possible for us to reliably distinguish between calls to this + // method that are setting a new sample for an existing animation (in which + // case our list length isn't changing and we wouldn't need to notify our DOM + // wrapper to keep its length in sync), and calls to this method that are + // setting the first sample of a new animation that will override the base + // value/an existing animation (in which case our length may be changing and + // our DOM wrapper may need to be notified). Happily though, it's cheap to + // just blindly notify our animVal's DOM wrapper of our new value each time + // this method is called, so that's what we do. + + // We must send this notification *before* changing mAnimVal! (See above.) + + DOMSVGPointList *domWrapper = + DOMSVGPointList::GetDOMWrapperIfExists(GetAnimValKey()); + if (domWrapper) { + domWrapper->InternalListWillChangeTo(aNewAnimValue); + } + if (!mAnimVal) { + mAnimVal = new SVGPointList(); + } + nsresult rv = mAnimVal->CopyFrom(aNewAnimValue); + if (NS_FAILED(rv)) { + // OOM. We clear the animation and, importantly, ClearAnimValue() ensures + // that mAnimVal's DOM wrapper (if any) is kept in sync! + ClearAnimValue(aElement); + return rv; + } + aElement->DidAnimatePointList(); + return NS_OK; +} + +void +SVGAnimatedPointList::ClearAnimValue(nsSVGElement *aElement) +{ + // We must send these notifications *before* changing mAnimVal! (See above.) + + DOMSVGPointList *domWrapper = + DOMSVGPointList::GetDOMWrapperIfExists(GetAnimValKey()); + if (domWrapper) { + // When all animation ends, animVal simply mirrors baseVal, which may have + // a different number of items to the last active animated value. + // + domWrapper->InternalListWillChangeTo(mBaseVal); + } + mAnimVal = nullptr; + aElement->DidAnimatePointList(); +} + +nsISMILAttr* +SVGAnimatedPointList::ToSMILAttr(nsSVGElement *aElement) +{ + return new SMILAnimatedPointList(this, aElement); +} + +nsresult +SVGAnimatedPointList:: + SMILAnimatedPointList::ValueFromString(const nsAString& aStr, + const dom::SVGAnimationElement* /*aSrcElement*/, + nsSMILValue& aValue, + bool& aPreventCachingOfSandwich) const +{ + nsSMILValue val(&SVGPointListSMILType::sSingleton); + SVGPointListAndInfo *list = static_cast<SVGPointListAndInfo*>(val.mU.mPtr); + nsresult rv = list->SetValueFromString(aStr); + if (NS_SUCCEEDED(rv)) { + list->SetInfo(mElement); + aValue = Move(val); + } + aPreventCachingOfSandwich = false; + return rv; +} + +nsSMILValue +SVGAnimatedPointList::SMILAnimatedPointList::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(&SVGPointListSMILType::sSingleton); + SVGPointListAndInfo *list = static_cast<SVGPointListAndInfo*>(tmp.mU.mPtr); + nsresult rv = list->CopyFrom(mVal->mBaseVal); + if (NS_SUCCEEDED(rv)) { + list->SetInfo(mElement); + Swap(val, tmp); + } + return val; +} + +nsresult +SVGAnimatedPointList::SMILAnimatedPointList::SetAnimValue(const nsSMILValue& aValue) +{ + NS_ASSERTION(aValue.mType == &SVGPointListSMILType::sSingleton, + "Unexpected type to assign animated value"); + if (aValue.mType == &SVGPointListSMILType::sSingleton) { + mVal->SetAnimValue(*static_cast<SVGPointListAndInfo*>(aValue.mU.mPtr), + mElement); + } + return NS_OK; +} + +void +SVGAnimatedPointList::SMILAnimatedPointList::ClearAnimValue() +{ + if (mVal->mAnimVal) { + mVal->ClearAnimValue(mElement); + } +} + +} // namespace mozilla diff --git a/dom/svg/SVGAnimatedPointList.h b/dom/svg/SVGAnimatedPointList.h new file mode 100644 index 0000000000..57f9bc06bf --- /dev/null +++ b/dom/svg/SVGAnimatedPointList.h @@ -0,0 +1,130 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef MOZILLA_SVGANIMATEDPOINTLIST_H__ +#define MOZILLA_SVGANIMATEDPOINTLIST_H__ + +#include "mozilla/Attributes.h" +#include "nsAutoPtr.h" +#include "nsISMILAttr.h" +#include "SVGPointList.h" + +class nsSMILValue; +class nsSVGElement; + +namespace mozilla { + +namespace dom { +class SVGAnimationElement; +} // namespace dom + +/** + * Class SVGAnimatedPointList + * + * Despite the fact that no SVGAnimatedPointList interface or objects exist + * in the SVG specification (unlike e.g. SVGAnimated*Length*List), we + * nevertheless have this internal class. (Note that there is an + * SVGAnimatedPoints interface, but that's quite different to + * SVGAnimatedLengthList since it is inherited by elements, as opposed to + * elements having members of that type.) The reason that we have this class is + * to provide a single locked down point of entry to the SVGPointList objects, + * which helps ensure that the DOM wrappers for SVGPointList objects' are + * always kept in sync. This is vitally important (see the comment in + * DOMSVGPointList::InternalListWillChangeTo) and frees consumers from having + * to know or worry about wrappers (or forget about them!) for the most part. + */ +class SVGAnimatedPointList +{ + // friends so that they can get write access to mBaseVal and mAnimVal + friend class DOMSVGPoint; + friend class DOMSVGPointList; + +public: + SVGAnimatedPointList() {} + + /** + * Because it's so important that mBaseVal and its DOMSVGPointList wrapper + * (if any) be kept in sync (see the comment in + * DOMSVGPointList::InternalListWillChangeTo), this method returns a const + * reference. Only our friend classes may get mutable references to mBaseVal. + */ + const SVGPointList& GetBaseValue() const { + return mBaseVal; + } + + nsresult SetBaseValueString(const nsAString& aValue); + + void ClearBaseValue(); + + /** + * const! See comment for GetBaseValue! + */ + const SVGPointList& GetAnimValue() const { + return mAnimVal ? *mAnimVal : mBaseVal; + } + + nsresult SetAnimValue(const SVGPointList& aValue, + nsSVGElement *aElement); + + void ClearAnimValue(nsSVGElement *aElement); + + /** + * Needed for correct DOM wrapper construction since GetAnimValue may + * actually return the baseVal! + */ + void *GetBaseValKey() const { + return (void*)&mBaseVal; + } + void *GetAnimValKey() const { + return (void*)&mAnimVal; + } + + bool IsAnimating() const { + return !!mAnimVal; + } + + /// Callers own the returned nsISMILAttr + nsISMILAttr* ToSMILAttr(nsSVGElement* aElement); + +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="">). + + SVGPointList mBaseVal; + nsAutoPtr<SVGPointList> mAnimVal; + + struct SMILAnimatedPointList : public nsISMILAttr + { + public: + SMILAnimatedPointList(SVGAnimatedPointList* aVal, + nsSVGElement* aElement) + : mVal(aVal) + , mElement(aElement) + {} + + // 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. + SVGAnimatedPointList *mVal; + nsSVGElement *mElement; + + // nsISMILAttr methods + virtual nsresult ValueFromString(const nsAString& aStr, + const dom::SVGAnimationElement* aSrcElement, + nsSMILValue& aValue, + bool& aPreventCachingOfSandwich) const override; + virtual nsSMILValue GetBaseValue() const override; + virtual void ClearAnimValue() override; + virtual nsresult SetAnimValue(const nsSMILValue& aValue) override; + }; +}; + +} // namespace mozilla + +#endif // MOZILLA_SVGANIMATEDPOINTLIST_H__ diff --git a/dom/svg/SVGAnimatedPreserveAspectRatio.cpp b/dom/svg/SVGAnimatedPreserveAspectRatio.cpp new file mode 100644 index 0000000000..3676d0e82b --- /dev/null +++ b/dom/svg/SVGAnimatedPreserveAspectRatio.cpp @@ -0,0 +1,338 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "mozilla/ArrayUtils.h" + +#include "SVGAnimatedPreserveAspectRatio.h" +#include "mozilla/dom/SVGAnimatedPreserveAspectRatioBinding.h" +#include "nsSMILValue.h" +#include "nsSVGAttrTearoffTable.h" +#include "nsWhitespaceTokenizer.h" +#include "SMILEnumType.h" +#include "SVGContentUtils.h" + +using namespace mozilla; +using namespace mozilla::dom; + +//////////////////////////////////////////////////////////////////////// +// SVGAnimatedPreserveAspectRatio class +NS_SVG_VAL_IMPL_CYCLE_COLLECTION_WRAPPERCACHED(DOMSVGAnimatedPreserveAspectRatio, mSVGElement) + +NS_IMPL_CYCLE_COLLECTING_ADDREF(DOMSVGAnimatedPreserveAspectRatio) +NS_IMPL_CYCLE_COLLECTING_RELEASE(DOMSVGAnimatedPreserveAspectRatio) + +NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(DOMSVGAnimatedPreserveAspectRatio) + NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY + NS_INTERFACE_MAP_ENTRY(nsISupports) +NS_INTERFACE_MAP_END + +JSObject* +DOMSVGAnimatedPreserveAspectRatio::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) +{ + return SVGAnimatedPreserveAspectRatioBinding::Wrap(aCx, this, aGivenProto); +} + +/* Implementation */ + +static const char *sAlignStrings[] = + { "none", "xMinYMin", "xMidYMin", "xMaxYMin", "xMinYMid", "xMidYMid", + "xMaxYMid", "xMinYMax", "xMidYMax", "xMaxYMax" }; + +static const char *sMeetOrSliceStrings[] = { "meet", "slice" }; + +static nsSVGAttrTearoffTable<SVGAnimatedPreserveAspectRatio, DOMSVGAnimatedPreserveAspectRatio> + sSVGAnimatedPAspectRatioTearoffTable; +static nsSVGAttrTearoffTable<SVGAnimatedPreserveAspectRatio, DOMSVGPreserveAspectRatio> + sBaseSVGPAspectRatioTearoffTable; +static nsSVGAttrTearoffTable<SVGAnimatedPreserveAspectRatio, DOMSVGPreserveAspectRatio> + sAnimSVGPAspectRatioTearoffTable; + +static uint16_t +GetAlignForString(const nsAString &aAlignString) +{ + for (uint32_t i = 0 ; i < ArrayLength(sAlignStrings) ; i++) { + if (aAlignString.EqualsASCII(sAlignStrings[i])) { + return (i + SVG_ALIGN_MIN_VALID); + } + } + + return SVG_PRESERVEASPECTRATIO_UNKNOWN; +} + +static void +GetAlignString(nsAString& aAlignString, uint16_t aAlign) +{ + NS_ASSERTION( + aAlign >= SVG_ALIGN_MIN_VALID && aAlign <= SVG_ALIGN_MAX_VALID, + "Unknown align"); + + aAlignString.AssignASCII( + sAlignStrings[aAlign - SVG_ALIGN_MIN_VALID]); +} + +static uint16_t +GetMeetOrSliceForString(const nsAString &aMeetOrSlice) +{ + for (uint32_t i = 0 ; i < ArrayLength(sMeetOrSliceStrings) ; i++) { + if (aMeetOrSlice.EqualsASCII(sMeetOrSliceStrings[i])) { + return (i + SVG_MEETORSLICE_MIN_VALID); + } + } + + return SVG_MEETORSLICE_UNKNOWN; +} + +static void +GetMeetOrSliceString(nsAString& aMeetOrSliceString, uint16_t aMeetOrSlice) +{ + NS_ASSERTION( + aMeetOrSlice >= SVG_MEETORSLICE_MIN_VALID && + aMeetOrSlice <= SVG_MEETORSLICE_MAX_VALID, + "Unknown meetOrSlice"); + + aMeetOrSliceString.AssignASCII( + sMeetOrSliceStrings[aMeetOrSlice - SVG_MEETORSLICE_MIN_VALID]); +} + +already_AddRefed<DOMSVGPreserveAspectRatio> +DOMSVGAnimatedPreserveAspectRatio::BaseVal() +{ + RefPtr<DOMSVGPreserveAspectRatio> domBaseVal = + sBaseSVGPAspectRatioTearoffTable.GetTearoff(mVal); + if (!domBaseVal) { + domBaseVal = new DOMSVGPreserveAspectRatio(mVal, mSVGElement, true); + sBaseSVGPAspectRatioTearoffTable.AddTearoff(mVal, domBaseVal); + } + + return domBaseVal.forget(); +} + +DOMSVGPreserveAspectRatio::~DOMSVGPreserveAspectRatio() +{ + if (mIsBaseValue) { + sBaseSVGPAspectRatioTearoffTable.RemoveTearoff(mVal); + } else { + sAnimSVGPAspectRatioTearoffTable.RemoveTearoff(mVal); + } +} + +already_AddRefed<DOMSVGPreserveAspectRatio> +DOMSVGAnimatedPreserveAspectRatio::AnimVal() +{ + RefPtr<DOMSVGPreserveAspectRatio> domAnimVal = + sAnimSVGPAspectRatioTearoffTable.GetTearoff(mVal); + if (!domAnimVal) { + domAnimVal = new DOMSVGPreserveAspectRatio(mVal, mSVGElement, false); + sAnimSVGPAspectRatioTearoffTable.AddTearoff(mVal, domAnimVal); + } + + return domAnimVal.forget(); +} + +static nsresult +ToPreserveAspectRatio(const nsAString &aString, + SVGPreserveAspectRatio *aValue) +{ + nsWhitespaceTokenizerTemplate<IsSVGWhitespace> tokenizer(aString); + if (tokenizer.whitespaceBeforeFirstToken() || + !tokenizer.hasMoreTokens()) { + return NS_ERROR_DOM_SYNTAX_ERR; + } + const nsAString &token = tokenizer.nextToken(); + + nsresult rv; + SVGPreserveAspectRatio val; + + rv = val.SetAlign(GetAlignForString(token)); + + if (NS_FAILED(rv)) { + return NS_ERROR_DOM_SYNTAX_ERR; + } + + if (tokenizer.hasMoreTokens()) { + rv = val.SetMeetOrSlice(GetMeetOrSliceForString(tokenizer.nextToken())); + if (NS_FAILED(rv)) { + return NS_ERROR_DOM_SYNTAX_ERR; + } + } else { + val.SetMeetOrSlice(SVG_MEETORSLICE_MEET); + } + + if (tokenizer.whitespaceAfterCurrentToken()) { + return NS_ERROR_DOM_SYNTAX_ERR; + } + + *aValue = val; + return NS_OK; +} + +nsresult +SVGAnimatedPreserveAspectRatio::SetBaseValueString( + const nsAString &aValueAsString, nsSVGElement *aSVGElement, bool aDoSetAttr) +{ + SVGPreserveAspectRatio val; + nsresult res = ToPreserveAspectRatio(aValueAsString, &val); + if (NS_FAILED(res)) { + return res; + } + + nsAttrValue emptyOrOldValue; + if (aDoSetAttr) { + emptyOrOldValue = aSVGElement->WillChangePreserveAspectRatio(); + } + + mBaseVal = val; + mIsBaseSet = true; + + if (!mIsAnimated) { + mAnimVal = mBaseVal; + } + if (aDoSetAttr) { + aSVGElement->DidChangePreserveAspectRatio(emptyOrOldValue); + } + if (mIsAnimated) { + aSVGElement->AnimationNeedsResample(); + } + return NS_OK; +} + +void +SVGAnimatedPreserveAspectRatio::GetBaseValueString( + nsAString& aValueAsString) const +{ + nsAutoString tmpString; + + aValueAsString.Truncate(); + + GetAlignString(tmpString, mBaseVal.mAlign); + aValueAsString.Append(tmpString); + + if (mBaseVal.mAlign != uint8_t(SVG_PRESERVEASPECTRATIO_NONE)) { + + aValueAsString.Append(' '); + GetMeetOrSliceString(tmpString, mBaseVal.mMeetOrSlice); + aValueAsString.Append(tmpString); + } +} + +void +SVGAnimatedPreserveAspectRatio::SetBaseValue(const SVGPreserveAspectRatio &aValue, + nsSVGElement *aSVGElement) +{ + if (mIsBaseSet && mBaseVal == aValue) { + return; + } + + nsAttrValue emptyOrOldValue = aSVGElement->WillChangePreserveAspectRatio(); + mBaseVal = aValue; + mIsBaseSet = true; + + if (!mIsAnimated) { + mAnimVal = mBaseVal; + } + aSVGElement->DidChangePreserveAspectRatio(emptyOrOldValue); + if (mIsAnimated) { + aSVGElement->AnimationNeedsResample(); + } +} + +static uint64_t +PackPreserveAspectRatio(const SVGPreserveAspectRatio& par) +{ + // All preserveAspectRatio values are enum values (do not interpolate), so we + // can safely collate them and treat them as a single enum as for SMIL. + uint64_t packed = 0; + packed |= uint64_t(par.GetAlign()) << 8; + packed |= uint64_t(par.GetMeetOrSlice()); + return packed; +} + +void +SVGAnimatedPreserveAspectRatio::SetAnimValue(uint64_t aPackedValue, + nsSVGElement *aSVGElement) +{ + if (mIsAnimated && PackPreserveAspectRatio(mAnimVal) == aPackedValue) { + return; + } + mAnimVal.SetAlign(uint16_t((aPackedValue & 0xff00) >> 8)); + mAnimVal.SetMeetOrSlice(uint16_t(aPackedValue & 0xff)); + mIsAnimated = true; + aSVGElement->DidAnimatePreserveAspectRatio(); +} + +already_AddRefed<DOMSVGAnimatedPreserveAspectRatio> +SVGAnimatedPreserveAspectRatio::ToDOMAnimatedPreserveAspectRatio( + nsSVGElement *aSVGElement) +{ + RefPtr<DOMSVGAnimatedPreserveAspectRatio> domAnimatedPAspectRatio = + sSVGAnimatedPAspectRatioTearoffTable.GetTearoff(this); + if (!domAnimatedPAspectRatio) { + domAnimatedPAspectRatio = new DOMSVGAnimatedPreserveAspectRatio(this, aSVGElement); + sSVGAnimatedPAspectRatioTearoffTable.AddTearoff(this, domAnimatedPAspectRatio); + } + return domAnimatedPAspectRatio.forget(); +} + +DOMSVGAnimatedPreserveAspectRatio::~DOMSVGAnimatedPreserveAspectRatio() +{ + sSVGAnimatedPAspectRatioTearoffTable.RemoveTearoff(mVal); +} + +nsISMILAttr* +SVGAnimatedPreserveAspectRatio::ToSMILAttr(nsSVGElement *aSVGElement) +{ + return new SMILPreserveAspectRatio(this, aSVGElement); +} + +// typedef for inner class, to make function signatures shorter below: +typedef SVGAnimatedPreserveAspectRatio::SMILPreserveAspectRatio + SMILPreserveAspectRatio; + +nsresult +SMILPreserveAspectRatio::ValueFromString(const nsAString& aStr, + const SVGAnimationElement* /*aSrcElement*/, + nsSMILValue& aValue, + bool& aPreventCachingOfSandwich) const +{ + SVGPreserveAspectRatio par; + nsresult res = ToPreserveAspectRatio(aStr, &par); + NS_ENSURE_SUCCESS(res, res); + + nsSMILValue val(SMILEnumType::Singleton()); + val.mU.mUint = PackPreserveAspectRatio(par); + aValue = val; + aPreventCachingOfSandwich = false; + return NS_OK; +} + +nsSMILValue +SMILPreserveAspectRatio::GetBaseValue() const +{ + nsSMILValue val(SMILEnumType::Singleton()); + val.mU.mUint = PackPreserveAspectRatio(mVal->GetBaseValue()); + return val; +} + +void +SMILPreserveAspectRatio::ClearAnimValue() +{ + if (mVal->mIsAnimated) { + mVal->mIsAnimated = false; + mVal->mAnimVal = mVal->mBaseVal; + mSVGElement->DidAnimatePreserveAspectRatio(); + } +} + +nsresult +SMILPreserveAspectRatio::SetAnimValue(const nsSMILValue& aValue) +{ + NS_ASSERTION(aValue.mType == SMILEnumType::Singleton(), + "Unexpected type to assign animated value"); + if (aValue.mType == SMILEnumType::Singleton()) { + mVal->SetAnimValue(aValue.mU.mUint, mSVGElement); + } + return NS_OK; +} diff --git a/dom/svg/SVGAnimatedPreserveAspectRatio.h b/dom/svg/SVGAnimatedPreserveAspectRatio.h new file mode 100644 index 0000000000..6b0a08b7a5 --- /dev/null +++ b/dom/svg/SVGAnimatedPreserveAspectRatio.h @@ -0,0 +1,142 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef MOZILLA_SVGANIMATEDPRESERVEASPECTRATIO_H__ +#define MOZILLA_SVGANIMATEDPRESERVEASPECTRATIO_H__ + +#include "nsCycleCollectionParticipant.h" +#include "nsError.h" +#include "nsISMILAttr.h" +#include "nsSVGElement.h" +#include "SVGPreserveAspectRatio.h" +#include "mozilla/Attributes.h" + +class nsSMILValue; + +namespace mozilla { +namespace dom { +class DOMSVGAnimatedPreserveAspectRatio; +class SVGAnimationElement; +} // namespace dom + +class SVGAnimatedPreserveAspectRatio final +{ +public: + void Init() { + mBaseVal.mAlign = SVG_PRESERVEASPECTRATIO_XMIDYMID; + mBaseVal.mMeetOrSlice = SVG_MEETORSLICE_MEET; + mAnimVal = mBaseVal; + mIsAnimated = false; + mIsBaseSet = false; + } + + nsresult SetBaseValueString(const nsAString& aValue, + nsSVGElement *aSVGElement, + bool aDoSetAttr); + void GetBaseValueString(nsAString& aValue) const; + + void SetBaseValue(const SVGPreserveAspectRatio &aValue, + nsSVGElement *aSVGElement); + nsresult SetBaseAlign(uint16_t aAlign, nsSVGElement *aSVGElement) { + if (aAlign < SVG_ALIGN_MIN_VALID || aAlign > SVG_ALIGN_MAX_VALID) { + return NS_ERROR_FAILURE; + } + SetBaseValue(SVGPreserveAspectRatio( + static_cast<SVGAlign>(aAlign), mBaseVal.GetMeetOrSlice()), + aSVGElement); + return NS_OK; + } + nsresult SetBaseMeetOrSlice(uint16_t aMeetOrSlice, nsSVGElement *aSVGElement) { + if (aMeetOrSlice < SVG_MEETORSLICE_MIN_VALID || + aMeetOrSlice > SVG_MEETORSLICE_MAX_VALID) { + return NS_ERROR_FAILURE; + } + SetBaseValue(SVGPreserveAspectRatio( + mBaseVal.GetAlign(), static_cast<SVGMeetOrSlice>(aMeetOrSlice)), + aSVGElement); + return NS_OK; + } + void SetAnimValue(uint64_t aPackedValue, nsSVGElement *aSVGElement); + + const SVGPreserveAspectRatio &GetBaseValue() const + { return mBaseVal; } + const SVGPreserveAspectRatio &GetAnimValue() const + { return mAnimVal; } + bool IsAnimated() const + { return mIsAnimated; } + bool IsExplicitlySet() const + { return mIsAnimated || mIsBaseSet; } + + already_AddRefed<mozilla::dom::DOMSVGAnimatedPreserveAspectRatio> + ToDOMAnimatedPreserveAspectRatio(nsSVGElement* aSVGElement); + // Returns a new nsISMILAttr object that the caller must delete + nsISMILAttr* ToSMILAttr(nsSVGElement* aSVGElement); + +private: + + SVGPreserveAspectRatio mAnimVal; + SVGPreserveAspectRatio mBaseVal; + bool mIsAnimated; + bool mIsBaseSet; + +public: + struct SMILPreserveAspectRatio final : public nsISMILAttr + { + public: + SMILPreserveAspectRatio(SVGAnimatedPreserveAspectRatio* aVal, + nsSVGElement* aSVGElement) + : mVal(aVal), mSVGElement(aSVGElement) {} + + // 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. + SVGAnimatedPreserveAspectRatio* mVal; + nsSVGElement* mSVGElement; + + // nsISMILAttr methods + virtual nsresult ValueFromString(const nsAString& aStr, + const dom::SVGAnimationElement* aSrcElement, + nsSMILValue& aValue, + bool& aPreventCachingOfSandwich) const override; + virtual nsSMILValue GetBaseValue() const override; + virtual void ClearAnimValue() override; + virtual nsresult SetAnimValue(const nsSMILValue& aValue) override; + }; +}; + +namespace dom { +class DOMSVGAnimatedPreserveAspectRatio final : public nsISupports, + public nsWrapperCache +{ + ~DOMSVGAnimatedPreserveAspectRatio(); + + NS_DECL_CYCLE_COLLECTING_ISUPPORTS + NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(DOMSVGAnimatedPreserveAspectRatio) + + DOMSVGAnimatedPreserveAspectRatio(SVGAnimatedPreserveAspectRatio* aVal, + nsSVGElement *aSVGElement) + : mVal(aVal), mSVGElement(aSVGElement) + { + } + + // WebIDL + nsSVGElement* GetParentObject() const { return mSVGElement; } + virtual JSObject* WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override; + + // These aren't weak refs because new objects are returned each time + already_AddRefed<DOMSVGPreserveAspectRatio> BaseVal(); + already_AddRefed<DOMSVGPreserveAspectRatio> AnimVal(); + +protected: + // kept alive because it belongs to content: + SVGAnimatedPreserveAspectRatio* mVal; + RefPtr<nsSVGElement> mSVGElement; +}; + +} // namespace dom +} // namespace mozilla + +#endif // MOZILLA_SVGANIMATEDPRESERVEASPECTRATIO_H__ diff --git a/dom/svg/SVGAnimatedRect.cpp b/dom/svg/SVGAnimatedRect.cpp new file mode 100644 index 0000000000..f2406a7263 --- /dev/null +++ b/dom/svg/SVGAnimatedRect.cpp @@ -0,0 +1,51 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "SVGAnimatedRect.h" +#include "mozilla/dom/SVGAnimatedRectBinding.h" +#include "nsSVGElement.h" +#include "nsSVGViewBox.h" +#include "SVGIRect.h" + +namespace mozilla { +namespace dom { + +NS_SVG_VAL_IMPL_CYCLE_COLLECTION_WRAPPERCACHED(SVGAnimatedRect, mSVGElement) + +NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(SVGAnimatedRect, AddRef) +NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(SVGAnimatedRect, Release) + +SVGAnimatedRect::SVGAnimatedRect(nsSVGViewBox* aVal, nsSVGElement* aSVGElement) + : mVal(aVal) + , mSVGElement(aSVGElement) +{ +} + +SVGAnimatedRect::~SVGAnimatedRect() +{ + nsSVGViewBox::sSVGAnimatedRectTearoffTable.RemoveTearoff(mVal); +} + +already_AddRefed<SVGIRect> +SVGAnimatedRect::GetBaseVal() +{ + return mVal->ToDOMBaseVal(mSVGElement); +} + +already_AddRefed<SVGIRect> +SVGAnimatedRect::GetAnimVal() +{ + return mVal->ToDOMAnimVal(mSVGElement); +} + +JSObject* +SVGAnimatedRect::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) +{ + return SVGAnimatedRectBinding::Wrap(aCx, this, aGivenProto); +} + +} // namespace dom +} // namespace mozilla diff --git a/dom/svg/SVGAnimatedRect.h b/dom/svg/SVGAnimatedRect.h new file mode 100644 index 0000000000..1f39c21c7c --- /dev/null +++ b/dom/svg/SVGAnimatedRect.h @@ -0,0 +1,52 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef mozilla_dom_SVGAnimatedRect_h +#define mozilla_dom_SVGAnimatedRect_h + +#include "nsCycleCollectionParticipant.h" +#include "mozilla/dom/SVGRectBinding.h" +#include "mozilla/Attributes.h" +#include "mozilla/ErrorResult.h" +#include "nsWrapperCache.h" +#include "nsSVGElement.h" + +class nsSVGViewBox; + +namespace mozilla { +namespace dom { + +class SVGAnimatedRect final : public nsWrapperCache +{ +public: + NS_INLINE_DECL_CYCLE_COLLECTING_NATIVE_REFCOUNTING(SVGAnimatedRect) + NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_NATIVE_CLASS(SVGAnimatedRect) + + SVGAnimatedRect(nsSVGViewBox* aVal, nsSVGElement* aSVGElement); + + nsSVGElement* GetParentObject() const + { + return mSVGElement; + } + + virtual JSObject* WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override; + + already_AddRefed<SVGIRect> GetBaseVal(); + + already_AddRefed<SVGIRect> GetAnimVal(); + +private: + virtual ~SVGAnimatedRect(); + + nsSVGViewBox* mVal; // kept alive because it belongs to content + RefPtr<nsSVGElement> mSVGElement; +}; + +} // namespace dom +} // namespace mozilla + +#endif // mozilla_dom_SVGAnimatedRect_h + diff --git a/dom/svg/SVGAnimatedString.cpp b/dom/svg/SVGAnimatedString.cpp new file mode 100644 index 0000000000..040d6484ee --- /dev/null +++ b/dom/svg/SVGAnimatedString.cpp @@ -0,0 +1,20 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "mozilla/dom/SVGAnimatedString.h" +#include "mozilla/dom/SVGAnimatedStringBinding.h" + +namespace mozilla { +namespace dom { + +JSObject* +SVGAnimatedString::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) +{ + return SVGAnimatedStringBinding::Wrap(aCx, this, aGivenProto); +} + +} // namespace dom +} // namespace mozilla diff --git a/dom/svg/SVGAnimatedString.h b/dom/svg/SVGAnimatedString.h new file mode 100644 index 0000000000..c730bb8efa --- /dev/null +++ b/dom/svg/SVGAnimatedString.h @@ -0,0 +1,42 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef mozilla_dom_SVGAnimatedString_h +#define mozilla_dom_SVGAnimatedString_h + +#include "nsSVGElement.h" + +namespace mozilla { +namespace dom { + +class SVGAnimatedString : public nsISupports, + public nsWrapperCache +{ +public: + explicit SVGAnimatedString(nsSVGElement* aSVGElement) + : mSVGElement(aSVGElement) + { + } + + virtual JSObject* WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override; + + // WebIDL + nsSVGElement* GetParentObject() const + { + return mSVGElement; + } + + virtual void GetBaseVal(nsAString& aResult) = 0; + virtual void SetBaseVal(const nsAString& aValue) = 0; + virtual void GetAnimVal(nsAString& aResult) = 0; + + RefPtr<nsSVGElement> mSVGElement; +}; + +} // namespace dom +} // namespace mozilla + +#endif // mozilla_dom_SVGAnimatedString_h diff --git a/dom/svg/SVGAnimatedTransformList.cpp b/dom/svg/SVGAnimatedTransformList.cpp new file mode 100644 index 0000000000..1e4451a986 --- /dev/null +++ b/dom/svg/SVGAnimatedTransformList.cpp @@ -0,0 +1,138 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "mozilla/dom/SVGAnimatedTransformList.h" +#include "DOMSVGTransformList.h" +#include "nsSVGAnimatedTransformList.h" +#include "nsSVGAttrTearoffTable.h" +#include "mozilla/dom/SVGAnimatedTransformListBinding.h" + +namespace mozilla { +namespace dom { + +static + nsSVGAttrTearoffTable<nsSVGAnimatedTransformList, SVGAnimatedTransformList> + sSVGAnimatedTransformListTearoffTable; + +NS_SVG_VAL_IMPL_CYCLE_COLLECTION_WRAPPERCACHED(SVGAnimatedTransformList, mElement) + +NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(SVGAnimatedTransformList, AddRef) +NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(SVGAnimatedTransformList, Release) + +JSObject* +SVGAnimatedTransformList::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) +{ + return SVGAnimatedTransformListBinding::Wrap(aCx, this, aGivenProto); +} + +//---------------------------------------------------------------------- +already_AddRefed<DOMSVGTransformList> +SVGAnimatedTransformList::BaseVal() +{ + if (!mBaseVal) { + mBaseVal = new DOMSVGTransformList(this, InternalAList().GetBaseValue()); + } + RefPtr<DOMSVGTransformList> baseVal = mBaseVal; + return baseVal.forget(); +} + +already_AddRefed<DOMSVGTransformList> +SVGAnimatedTransformList::AnimVal() +{ + if (!mAnimVal) { + mAnimVal = new DOMSVGTransformList(this, InternalAList().GetAnimValue()); + } + RefPtr<DOMSVGTransformList> animVal = mAnimVal; + return animVal.forget(); +} + +/* static */ already_AddRefed<SVGAnimatedTransformList> +SVGAnimatedTransformList::GetDOMWrapper(nsSVGAnimatedTransformList *aList, + nsSVGElement *aElement) +{ + RefPtr<SVGAnimatedTransformList> wrapper = + sSVGAnimatedTransformListTearoffTable.GetTearoff(aList); + if (!wrapper) { + wrapper = new SVGAnimatedTransformList(aElement); + sSVGAnimatedTransformListTearoffTable.AddTearoff(aList, wrapper); + } + return wrapper.forget(); +} + +/* static */ SVGAnimatedTransformList* +SVGAnimatedTransformList::GetDOMWrapperIfExists( + nsSVGAnimatedTransformList *aList) +{ + return sSVGAnimatedTransformListTearoffTable.GetTearoff(aList); +} + +SVGAnimatedTransformList::~SVGAnimatedTransformList() +{ + // Script no longer has any references to us, to our base/animVal objects, or + // to any of their list items. + sSVGAnimatedTransformListTearoffTable.RemoveTearoff(&InternalAList()); +} + +void +SVGAnimatedTransformList::InternalBaseValListWillChangeLengthTo( + uint32_t aNewLength) +{ + // 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)!! + + RefPtr<SVGAnimatedTransformList> kungFuDeathGrip; + if (mBaseVal) { + if (aNewLength < mBaseVal->LengthNoFlush()) { + // InternalListLengthWillChange might clear last reference to |this|. + // Retain a temporary reference to keep from dying before returning. + kungFuDeathGrip = this; + } + mBaseVal->InternalListLengthWillChange(aNewLength); + } + + // 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 InternalAnimValListWillChangeLengthTo() + // if necessary.) + + if (!IsAnimating()) { + InternalAnimValListWillChangeLengthTo(aNewLength); + } +} + +void +SVGAnimatedTransformList::InternalAnimValListWillChangeLengthTo( + uint32_t aNewLength) +{ + if (mAnimVal) { + mAnimVal->InternalListLengthWillChange(aNewLength); + } +} + +bool +SVGAnimatedTransformList::IsAnimating() const +{ + return InternalAList().IsAnimating(); +} + +nsSVGAnimatedTransformList& +SVGAnimatedTransformList::InternalAList() +{ + return *mElement->GetAnimatedTransformList(); +} + +const nsSVGAnimatedTransformList& +SVGAnimatedTransformList::InternalAList() const +{ + return *mElement->GetAnimatedTransformList(); +} + +} // namespace dom +} // namespace mozilla diff --git a/dom/svg/SVGAnimatedTransformList.h b/dom/svg/SVGAnimatedTransformList.h new file mode 100644 index 0000000000..245d8d52d9 --- /dev/null +++ b/dom/svg/SVGAnimatedTransformList.h @@ -0,0 +1,131 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef mozilla_dom_SVGAnimatedTransformList_h +#define mozilla_dom_SVGAnimatedTransformList_h + +#include "nsAutoPtr.h" +#include "nsCOMPtr.h" +#include "nsCycleCollectionParticipant.h" +#include "nsSVGElement.h" +#include "nsWrapperCache.h" +#include "mozilla/Attributes.h" + +namespace mozilla { + +class DOMSVGTransformList; +class nsSVGAnimatedTransformList; + +namespace dom { + +/** + * Class SVGAnimatedTransformList + * + * This class is used to create the DOM tearoff objects that wrap internal + * nsSVGAnimatedTransformList objects. + * + * See the architecture comment in DOMSVGAnimatedLengthList.h (that's + * LENGTH list). The comment for that class largly applies to this one too + * and will go a long way to helping you understand the architecture here. + * + * This class is strongly intertwined with DOMSVGTransformList and + * DOMSVGTransform. + * Our DOMSVGTransformList base and anim vals are friends and take care of + * nulling out our pointers to them when they die (making our pointers to them + * true weak refs). + */ +class SVGAnimatedTransformList final : public nsWrapperCache +{ + friend class mozilla::DOMSVGTransformList; + +public: + NS_INLINE_DECL_CYCLE_COLLECTING_NATIVE_REFCOUNTING(SVGAnimatedTransformList) + NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_NATIVE_CLASS(SVGAnimatedTransformList) + + /** + * Factory method to create and return a SVGAnimatedTransformList wrapper + * for a given internal nsSVGAnimatedTransformList object. The factory takes + * care of caching the object that it returns so that the same object can be + * returned for the given nsSVGAnimatedTransformList 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 nsSVGAnimatedTransformList will naturally result in a new + * SVGAnimatedTransformList being returned. + */ + static already_AddRefed<SVGAnimatedTransformList> + GetDOMWrapper(nsSVGAnimatedTransformList *aList, nsSVGElement *aElement); + + /** + * This method returns the SVGAnimatedTransformList wrapper for an internal + * nsSVGAnimatedTransformList object if it currently has a wrapper. If it does + * not, then nullptr is returned. + */ + static SVGAnimatedTransformList* + GetDOMWrapperIfExists(nsSVGAnimatedTransformList *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 InternalBaseValListWillChangeLengthTo(uint32_t aNewLength); + void InternalAnimValListWillChangeLengthTo(uint32_t aNewLength); + + /** + * Returns true if our attribute is animating (in which case our animVal is + * not simply a mirror of our baseVal). + */ + bool IsAnimating() const; + + // WebIDL + nsSVGElement* GetParentObject() const { return mElement; } + virtual JSObject* WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override; + // These aren't weak refs because mBaseVal and mAnimVal are weak + already_AddRefed<DOMSVGTransformList> BaseVal(); + already_AddRefed<DOMSVGTransformList> AnimVal(); + +private: + + /** + * Only our static GetDOMWrapper() factory method may create objects of our + * type. + */ + explicit SVGAnimatedTransformList(nsSVGElement *aElement) + : mBaseVal(nullptr) + , mAnimVal(nullptr) + , mElement(aElement) + { + } + + ~SVGAnimatedTransformList(); + + /// Get a reference to this DOM wrapper object's internal counterpart. + nsSVGAnimatedTransformList& InternalAList(); + const nsSVGAnimatedTransformList& InternalAList() const; + + // Weak refs to our DOMSVGTransformList baseVal/animVal objects. These objects + // are friends and take care of clearing these pointers when they die, making + // these true weak references. + DOMSVGTransformList *mBaseVal; + DOMSVGTransformList *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. + RefPtr<nsSVGElement> mElement; +}; + +} // namespace dom +} // namespace mozilla + +#endif // mozilla_dom_SVGAnimatedTransformList_h diff --git a/dom/svg/SVGAnimationElement.cpp b/dom/svg/SVGAnimationElement.cpp new file mode 100644 index 0000000000..d6550c96ee --- /dev/null +++ b/dom/svg/SVGAnimationElement.cpp @@ -0,0 +1,488 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "mozilla/dom/SVGAnimationElement.h" +#include "mozilla/dom/SVGSVGElement.h" +#include "nsSMILTimeContainer.h" +#include "nsSMILAnimationController.h" +#include "nsSMILAnimationFunction.h" +#include "nsContentUtils.h" +#include "nsIContentInlines.h" +#include "nsIURI.h" +#include "prtime.h" + +namespace mozilla { +namespace dom { + +//---------------------------------------------------------------------- +// nsISupports methods + +NS_IMPL_ADDREF_INHERITED(SVGAnimationElement, SVGAnimationElementBase) +NS_IMPL_RELEASE_INHERITED(SVGAnimationElement, SVGAnimationElementBase) + +NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(SVGAnimationElement) + NS_INTERFACE_MAP_ENTRY(mozilla::dom::SVGTests) +NS_INTERFACE_MAP_END_INHERITING(SVGAnimationElementBase) + +NS_IMPL_CYCLE_COLLECTION_INHERITED(SVGAnimationElement, + SVGAnimationElementBase, + mHrefTarget, mTimedElement) + +//---------------------------------------------------------------------- +// Implementation + +SVGAnimationElement::SVGAnimationElement(already_AddRefed<mozilla::dom::NodeInfo>& aNodeInfo) + : SVGAnimationElementBase(aNodeInfo), + mHrefTarget(this) +{ +} + +SVGAnimationElement::~SVGAnimationElement() +{ +} + +nsresult +SVGAnimationElement::Init() +{ + nsresult rv = SVGAnimationElementBase::Init(); + NS_ENSURE_SUCCESS(rv, rv); + + mTimedElement.SetAnimationElement(this); + AnimationFunction().SetAnimationElement(this); + mTimedElement.SetTimeClient(&AnimationFunction()); + + return NS_OK; +} + +//---------------------------------------------------------------------- + +const nsAttrValue* +SVGAnimationElement::GetAnimAttr(nsIAtom* aName) const +{ + return mAttrsAndChildren.GetAttr(aName, kNameSpaceID_None); +} + +bool +SVGAnimationElement::GetAnimAttr(nsIAtom* aAttName, + nsAString& aResult) const +{ + return GetAttr(kNameSpaceID_None, aAttName, aResult); +} + +bool +SVGAnimationElement::HasAnimAttr(nsIAtom* aAttName) const +{ + return HasAttr(kNameSpaceID_None, aAttName); +} + +Element* +SVGAnimationElement::GetTargetElementContent() +{ + if (HasAttr(kNameSpaceID_XLink, nsGkAtoms::href) || + HasAttr(kNameSpaceID_None, nsGkAtoms::href)) { + return mHrefTarget.get(); + } + MOZ_ASSERT(!mHrefTarget.get(), + "We shouldn't have a href target " + "if we don't have an xlink:href or href attribute"); + + // No "href" or "xlink:href" attribute --> I should target my parent. + nsIContent* parent = GetFlattenedTreeParent(); + return parent && parent->IsElement() ? parent->AsElement() : nullptr; +} + +bool +SVGAnimationElement::GetTargetAttributeName(int32_t *aNamespaceID, + nsIAtom **aLocalName) const +{ + const nsAttrValue* nameAttr + = mAttrsAndChildren.GetAttr(nsGkAtoms::attributeName); + + if (!nameAttr) + return false; + + NS_ASSERTION(nameAttr->Type() == nsAttrValue::eAtom, + "attributeName should have been parsed as an atom"); + + return NS_SUCCEEDED(nsContentUtils::SplitQName( + this, nsDependentAtomString(nameAttr->GetAtomValue()), + aNamespaceID, aLocalName)); +} + +nsSMILTargetAttrType +SVGAnimationElement::GetTargetAttributeType() const +{ + nsIContent::AttrValuesArray typeValues[] = { &nsGkAtoms::css, + &nsGkAtoms::XML, + nullptr}; + nsSMILTargetAttrType smilTypes[] = { eSMILTargetAttrType_CSS, + eSMILTargetAttrType_XML }; + int32_t index = FindAttrValueIn(kNameSpaceID_None, + nsGkAtoms::attributeType, + typeValues, + eCaseMatters); + return (index >= 0) ? smilTypes[index] : eSMILTargetAttrType_auto; +} + +nsSMILTimedElement& +SVGAnimationElement::TimedElement() +{ + return mTimedElement; +} + +nsSVGElement* +SVGAnimationElement::GetTargetElement() +{ + FlushAnimations(); + + // We'll just call the other GetTargetElement method, and QI to the right type + nsIContent* target = GetTargetElementContent(); + + return (target && target->IsSVGElement()) + ? static_cast<nsSVGElement*>(target) : nullptr; +} + +float +SVGAnimationElement::GetStartTime(ErrorResult& rv) +{ + FlushAnimations(); + + nsSMILTimeValue startTime = mTimedElement.GetStartTime(); + if (!startTime.IsDefinite()) { + rv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR); + return 0.f; + } + + return float(double(startTime.GetMillis()) / PR_MSEC_PER_SEC); +} + +float +SVGAnimationElement::GetCurrentTime() +{ + // Not necessary to call FlushAnimations() for this + + nsSMILTimeContainer* root = GetTimeContainer(); + if (root) { + return float(double(root->GetCurrentTime()) / PR_MSEC_PER_SEC); + } + + return 0.0f; +} + +float +SVGAnimationElement::GetSimpleDuration(ErrorResult& rv) +{ + // Not necessary to call FlushAnimations() for this + + nsSMILTimeValue simpleDur = mTimedElement.GetSimpleDuration(); + if (!simpleDur.IsDefinite()) { + rv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR); + return 0.f; + } + + return float(double(simpleDur.GetMillis()) / PR_MSEC_PER_SEC); +} + +//---------------------------------------------------------------------- +// nsIContent methods + +nsresult +SVGAnimationElement::BindToTree(nsIDocument* aDocument, + nsIContent* aParent, + nsIContent* aBindingParent, + bool aCompileEventHandlers) +{ + MOZ_ASSERT(!mHrefTarget.get(), + "Shouldn't have href-target yet (or it should've been cleared)"); + nsresult rv = SVGAnimationElementBase::BindToTree(aDocument, aParent, + aBindingParent, + aCompileEventHandlers); + NS_ENSURE_SUCCESS(rv,rv); + + // XXXdholbert is GetCtx (as a check for SVG parent) still needed here? + if (!GetCtx()) { + // No use proceeding. We don't have an SVG parent (yet) so we won't be able + // to register ourselves etc. Maybe next time we'll have more luck. + // (This sort of situation will arise a lot when trees are being constructed + // piece by piece via script) + return NS_OK; + } + + // Add myself to the animation controller's master set of animation elements. + if (aDocument) { + nsSMILAnimationController *controller = aDocument->GetAnimationController(); + if (controller) { + controller->RegisterAnimationElement(this); + } + const nsAttrValue* href = + HasAttr(kNameSpaceID_None, nsGkAtoms::href) + ? mAttrsAndChildren.GetAttr(nsGkAtoms::href, kNameSpaceID_None) + : mAttrsAndChildren.GetAttr(nsGkAtoms::href, kNameSpaceID_XLink); + if (href) { + nsAutoString hrefStr; + href->ToString(hrefStr); + + // Pass in |aParent| instead of |this| -- first argument is only used + // for a call to GetComposedDoc(), and |this| might not have a current + // document yet. + UpdateHrefTarget(aParent, hrefStr); + } + + mTimedElement.BindToTree(aParent); + } + + AnimationNeedsResample(); + + return NS_OK; +} + +void +SVGAnimationElement::UnbindFromTree(bool aDeep, bool aNullParent) +{ + nsSMILAnimationController *controller = OwnerDoc()->GetAnimationController(); + if (controller) { + controller->UnregisterAnimationElement(this); + } + + mHrefTarget.Unlink(); + mTimedElement.DissolveReferences(); + + AnimationNeedsResample(); + + SVGAnimationElementBase::UnbindFromTree(aDeep, aNullParent); +} + +bool +SVGAnimationElement::ParseAttribute(int32_t aNamespaceID, + nsIAtom* aAttribute, + const nsAString& aValue, + nsAttrValue& aResult) +{ + if (aNamespaceID == kNameSpaceID_None) { + // Deal with target-related attributes here + if (aAttribute == nsGkAtoms::attributeName || + aAttribute == nsGkAtoms::attributeType) { + aResult.ParseAtom(aValue); + AnimationNeedsResample(); + return true; + } + + nsresult rv = NS_ERROR_FAILURE; + + // First let the animation function try to parse it... + bool foundMatch = + AnimationFunction().SetAttr(aAttribute, aValue, aResult, &rv); + + // ... and if that didn't recognize the attribute, let the timed element + // try to parse it. + if (!foundMatch) { + foundMatch = + mTimedElement.SetAttr(aAttribute, aValue, aResult, this, &rv); + } + + if (foundMatch) { + AnimationNeedsResample(); + if (NS_FAILED(rv)) { + ReportAttributeParseFailure(OwnerDoc(), aAttribute, aValue); + return false; + } + return true; + } + } + + return SVGAnimationElementBase::ParseAttribute(aNamespaceID, aAttribute, + aValue, aResult); +} + +nsresult +SVGAnimationElement::AfterSetAttr(int32_t aNamespaceID, nsIAtom* aName, + const nsAttrValue* aValue, bool aNotify) +{ + nsresult rv = + SVGAnimationElementBase::AfterSetAttr(aNamespaceID, aName, aValue, + aNotify); + + if (SVGTests::IsConditionalProcessingAttribute(aName)) { + bool isDisabled = !SVGTests::PassesConditionalProcessingTests(); + if (mTimedElement.SetIsDisabled(isDisabled)) { + AnimationNeedsResample(); + } + } + + if (!((aNamespaceID == kNameSpaceID_None || + aNamespaceID == kNameSpaceID_XLink) && + aName == nsGkAtoms::href)) { + return rv; + } + + if (!aValue) { + if (aNamespaceID == kNameSpaceID_None) { + mHrefTarget.Unlink(); + AnimationTargetChanged(); + + // After unsetting href, we may still have xlink:href, so we + // should try to add it back. + const nsAttrValue* xlinkHref = + mAttrsAndChildren.GetAttr(nsGkAtoms::href, kNameSpaceID_XLink); + if (xlinkHref) { + UpdateHrefTarget(this, xlinkHref->GetStringValue()); + } + } else if (!HasAttr(kNameSpaceID_None, nsGkAtoms::href)) { + mHrefTarget.Unlink(); + AnimationTargetChanged(); + } // else: we unset xlink:href, but we still have href attribute, so keep + // mHrefTarget linking to href. + } else if (IsInUncomposedDoc() && + !(aNamespaceID == kNameSpaceID_XLink && + HasAttr(kNameSpaceID_None, nsGkAtoms::href))) { + // Note: "href" takes priority over xlink:href. So if "xlink:href" is being + // set here, we only let that update our target if "href" is *unset*. + MOZ_ASSERT(aValue->Type() == nsAttrValue::eString, + "Expected href attribute to be string type"); + UpdateHrefTarget(this, aValue->GetStringValue()); + } // else: we're not yet in a document -- we'll update the target on + // next BindToTree call. + + return rv; +} + +nsresult +SVGAnimationElement::UnsetAttr(int32_t aNamespaceID, + nsIAtom* aAttribute, bool aNotify) +{ + nsresult rv = SVGAnimationElementBase::UnsetAttr(aNamespaceID, aAttribute, + aNotify); + NS_ENSURE_SUCCESS(rv,rv); + + if (aNamespaceID == kNameSpaceID_None) { + if (AnimationFunction().UnsetAttr(aAttribute) || + mTimedElement.UnsetAttr(aAttribute)) { + AnimationNeedsResample(); + } + } + + return NS_OK; +} + +bool +SVGAnimationElement::IsNodeOfType(uint32_t aFlags) const +{ + return !(aFlags & ~(eCONTENT | eANIMATION)); +} + +//---------------------------------------------------------------------- +// SVGTests methods + +bool +SVGAnimationElement::IsInChromeDoc() const +{ + return nsContentUtils::IsChromeDoc(OwnerDoc()); +} + +//---------------------------------------------------------------------- +// SVG utility methods + +void +SVGAnimationElement::ActivateByHyperlink() +{ + FlushAnimations(); + + // The behavior for when the target is an animation element is defined in + // SMIL Animation: + // http://www.w3.org/TR/smil-animation/#HyperlinkSemantics + nsSMILTimeValue seekTime = mTimedElement.GetHyperlinkTime(); + if (seekTime.IsDefinite()) { + nsSMILTimeContainer* timeContainer = GetTimeContainer(); + if (timeContainer) { + timeContainer->SetCurrentTime(seekTime.GetMillis()); + AnimationNeedsResample(); + // As with SVGSVGElement::SetCurrentTime, we need to trigger + // a synchronous sample now. + FlushAnimations(); + } + // else, silently fail. We mustn't be part of an SVG document fragment that + // is attached to the document tree so there's nothing we can do here + } else { + IgnoredErrorResult rv; + BeginElement(rv); + } +} + +//---------------------------------------------------------------------- +// Implementation helpers + +nsSMILTimeContainer* +SVGAnimationElement::GetTimeContainer() +{ + SVGSVGElement *element = SVGContentUtils::GetOuterSVGElement(this); + + if (element) { + return element->GetTimedDocumentRoot(); + } + + return nullptr; +} + +void +SVGAnimationElement::BeginElementAt(float offset, ErrorResult& rv) +{ + // Make sure the timegraph is up-to-date + FlushAnimations(); + + // This will fail if we're not attached to a time container (SVG document + // fragment). + rv = mTimedElement.BeginElementAt(offset); + if (rv.Failed()) + return; + + AnimationNeedsResample(); + // Force synchronous sample so that events resulting from this call arrive in + // the expected order and we get an up-to-date paint. + FlushAnimations(); +} + +void +SVGAnimationElement::EndElementAt(float offset, ErrorResult& rv) +{ + // Make sure the timegraph is up-to-date + FlushAnimations(); + + rv = mTimedElement.EndElementAt(offset); + if (rv.Failed()) + return; + + AnimationNeedsResample(); + // Force synchronous sample + FlushAnimations(); +} + +bool +SVGAnimationElement::IsEventAttributeName(nsIAtom* aName) +{ + return nsContentUtils::IsEventAttributeName(aName, EventNameType_SMIL); +} + +void +SVGAnimationElement::UpdateHrefTarget(nsIContent* aNodeForContext, + const nsAString& aHrefStr) +{ + nsCOMPtr<nsIURI> targetURI; + nsCOMPtr<nsIURI> baseURI = GetBaseURI(); + nsContentUtils::NewURIWithDocumentCharset(getter_AddRefs(targetURI), + aHrefStr, OwnerDoc(), baseURI); + mHrefTarget.Reset(aNodeForContext, targetURI); + AnimationTargetChanged(); +} + +void +SVGAnimationElement::AnimationTargetChanged() +{ + mTimedElement.HandleTargetElementChange(GetTargetElementContent()); + AnimationNeedsResample(); +} + +} // namespace dom +} // namespace mozilla diff --git a/dom/svg/SVGAnimationElement.h b/dom/svg/SVGAnimationElement.h new file mode 100644 index 0000000000..9bcbdf0c22 --- /dev/null +++ b/dom/svg/SVGAnimationElement.h @@ -0,0 +1,127 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef mozilla_dom_SVGAnimationElement_h +#define mozilla_dom_SVGAnimationElement_h + +#include "mozilla/Attributes.h" +#include "mozilla/dom/SVGTests.h" +#include "nsReferencedElement.h" +#include "nsSMILTimedElement.h" +#include "nsSVGElement.h" + +typedef nsSVGElement SVGAnimationElementBase; + +namespace mozilla { +namespace dom { + +enum nsSMILTargetAttrType { + eSMILTargetAttrType_auto, + eSMILTargetAttrType_CSS, + eSMILTargetAttrType_XML +}; + +class SVGAnimationElement : public SVGAnimationElementBase, + public SVGTests +{ +protected: + explicit SVGAnimationElement(already_AddRefed<mozilla::dom::NodeInfo>& aNodeInfo); + nsresult Init(); + virtual ~SVGAnimationElement(); + +public: + // interfaces: + NS_DECL_ISUPPORTS_INHERITED + + NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(SVGAnimationElement, + SVGAnimationElementBase) + + virtual nsresult Clone(mozilla::dom::NodeInfo *aNodeInfo, nsINode **aResult) const override = 0; + + // nsIContent specializations + virtual nsresult BindToTree(nsIDocument* aDocument, nsIContent* aParent, + nsIContent* aBindingParent, + bool aCompileEventHandlers) override; + virtual void UnbindFromTree(bool aDeep, bool aNullParent) override; + + virtual nsresult UnsetAttr(int32_t aNamespaceID, nsIAtom* aAttribute, + bool aNotify) override; + + virtual bool IsNodeOfType(uint32_t aFlags) const override; + + // Element specializations + virtual bool ParseAttribute(int32_t aNamespaceID, + nsIAtom* aAttribute, + const nsAString& aValue, + nsAttrValue& aResult) override; + virtual nsresult AfterSetAttr(int32_t aNamespaceID, nsIAtom* aName, + const nsAttrValue* aValue, bool aNotify) override; + + const nsAttrValue* GetAnimAttr(nsIAtom* aName) const; + bool GetAnimAttr(nsIAtom* aAttName, nsAString& aResult) const; + bool HasAnimAttr(nsIAtom* aAttName) const; + Element* GetTargetElementContent(); + virtual bool GetTargetAttributeName(int32_t* aNamespaceID, + nsIAtom** aLocalName) const; + virtual nsSMILTargetAttrType GetTargetAttributeType() const; + nsSMILTimedElement& TimedElement(); + nsSMILTimeContainer* GetTimeContainer(); + virtual nsSMILAnimationFunction& AnimationFunction() = 0; + + virtual bool IsEventAttributeName(nsIAtom* aName) override; + + // Utility methods for within SVG + void ActivateByHyperlink(); + + // WebIDL + nsSVGElement* GetTargetElement(); + float GetStartTime(ErrorResult& rv); + float GetCurrentTime(); + float GetSimpleDuration(ErrorResult& rv); + void BeginElement(ErrorResult& rv) { BeginElementAt(0.f, rv); } + void BeginElementAt(float offset, ErrorResult& rv); + void EndElement(ErrorResult& rv) { EndElementAt(0.f, rv); } + void EndElementAt(float offset, ErrorResult& rv); + + // SVGTests + virtual bool IsInChromeDoc() const override; + + + protected: + // nsSVGElement overrides + + void UpdateHrefTarget(nsIContent* aNodeForContext, + const nsAString& aHrefStr); + void AnimationTargetChanged(); + + class TargetReference : public nsReferencedElement { + public: + explicit TargetReference(SVGAnimationElement* aAnimationElement) : + mAnimationElement(aAnimationElement) {} + protected: + // We need to be notified when target changes, in order to request a + // sample (which will clear animation effects from old target and apply + // them to the new target) and update any event registrations. + virtual void ElementChanged(Element* aFrom, Element* aTo) override { + nsReferencedElement::ElementChanged(aFrom, aTo); + mAnimationElement->AnimationTargetChanged(); + } + + // We need to override IsPersistent to get persistent tracking (beyond the + // first time the target changes) + virtual bool IsPersistent() override { return true; } + private: + SVGAnimationElement* const mAnimationElement; + }; + + TargetReference mHrefTarget; + nsSMILTimedElement mTimedElement; +}; + +} // namespace dom +} // namespace mozilla + +#endif // mozilla_dom_SVGAnimationElement_h diff --git a/dom/svg/SVGAttrValueWrapper.cpp b/dom/svg/SVGAttrValueWrapper.cpp new file mode 100644 index 0000000000..5561397dc4 --- /dev/null +++ b/dom/svg/SVGAttrValueWrapper.cpp @@ -0,0 +1,102 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "SVGAttrValueWrapper.h" +#include "nsSVGAngle.h" +#include "nsSVGIntegerPair.h" +#include "nsSVGLength2.h" +#include "nsSVGNumberPair.h" +#include "nsSVGViewBox.h" +#include "SVGAnimatedPreserveAspectRatio.h" +#include "SVGLengthList.h" +#include "SVGNumberList.h" +#include "SVGPathData.h" +#include "SVGPointList.h" +#include "SVGStringList.h" +#include "SVGTransformList.h" + +using namespace mozilla; + +/*static*/ void +SVGAttrValueWrapper::ToString(const nsSVGAngle* aAngle, nsAString& aResult) +{ + aAngle->GetBaseValueString(aResult); +} + +/*static*/ void +SVGAttrValueWrapper::ToString(const nsSVGIntegerPair* aIntegerPair, + nsAString& aResult) +{ + aIntegerPair->GetBaseValueString(aResult); +} + +/*static*/ void +SVGAttrValueWrapper::ToString(const nsSVGLength2* aLength, nsAString& aResult) +{ + aLength->GetBaseValueString(aResult); +} + +/*static*/ void +SVGAttrValueWrapper::ToString(const SVGLengthList* aLengthList, + nsAString& aResult) +{ + aLengthList->GetValueAsString(aResult); +} + +/*static*/ void +SVGAttrValueWrapper::ToString(const SVGNumberList* aNumberList, + nsAString& aResult) +{ + aNumberList->GetValueAsString(aResult); +} + +/*static*/ void +SVGAttrValueWrapper::ToString(const nsSVGNumberPair* aNumberPair, + nsAString& aResult) +{ + aNumberPair->GetBaseValueString(aResult); +} + +/*static*/ void +SVGAttrValueWrapper::ToString(const SVGPathData* aPathData, nsAString& aResult) +{ + aPathData->GetValueAsString(aResult); +} + +/*static*/ void +SVGAttrValueWrapper::ToString(const SVGPointList* aPointList, + nsAString& aResult) +{ + aPointList->GetValueAsString(aResult); +} + +/*static*/ void +SVGAttrValueWrapper::ToString( + const SVGAnimatedPreserveAspectRatio* aPreserveAspectRatio, + nsAString& aResult) +{ + aPreserveAspectRatio->GetBaseValueString(aResult); +} + +/*static*/ void +SVGAttrValueWrapper::ToString(const SVGStringList* aStringList, + nsAString& aResult) +{ + aStringList->GetValue(aResult); +} + +/*static*/ void +SVGAttrValueWrapper::ToString(const SVGTransformList* aTransformList, + nsAString& aResult) +{ + aTransformList->GetValueAsString(aResult); +} + +/*static*/ void +SVGAttrValueWrapper::ToString(const nsSVGViewBox* aViewBox, nsAString& aResult) +{ + aViewBox->GetBaseValueString(aResult); +} diff --git a/dom/svg/SVGAttrValueWrapper.h b/dom/svg/SVGAttrValueWrapper.h new file mode 100644 index 0000000000..27ec9cb30d --- /dev/null +++ b/dom/svg/SVGAttrValueWrapper.h @@ -0,0 +1,63 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef MOZILLA_SVGATTRVALUEWRAPPER_H__ +#define MOZILLA_SVGATTRVALUEWRAPPER_H__ + +/** + * Utility wrapper for handling SVG types used inside nsAttrValue so that these + * types don't need to be exported outside the SVG module. + */ + +#include "nsStringGlue.h" + +class nsSVGAngle; +class nsSVGIntegerPair; +class nsSVGLength2; +class nsSVGNumberPair; +class nsSVGViewBox; + +namespace mozilla { +class SVGLengthList; +class SVGNumberList; +class SVGPathData; +class SVGPointList; +class SVGAnimatedPreserveAspectRatio; +class SVGStringList; +class SVGTransformList; +} // namespace mozilla + +namespace mozilla { + +class SVGAttrValueWrapper +{ +public: + static void ToString(const nsSVGAngle* aAngle, nsAString& aResult); + static void ToString(const nsSVGIntegerPair* aIntegerPair, + nsAString& aResult); + static void ToString(const nsSVGLength2* aLength, nsAString& aResult); + static void ToString(const mozilla::SVGLengthList* aLengthList, + nsAString& aResult); + static void ToString(const mozilla::SVGNumberList* aNumberList, + nsAString& aResult); + static void ToString(const nsSVGNumberPair* aNumberPair, nsAString& aResult); + static void ToString(const mozilla::SVGPathData* aPathData, + nsAString& aResult); + static void ToString(const mozilla::SVGPointList* aPointList, + nsAString& aResult); + static void ToString( + const mozilla::SVGAnimatedPreserveAspectRatio* aPreserveAspectRatio, + nsAString& aResult); + static void ToString(const mozilla::SVGStringList* aStringList, + nsAString& aResult); + static void ToString(const mozilla::SVGTransformList* aTransformList, + nsAString& aResult); + static void ToString(const nsSVGViewBox* aViewBox, nsAString& aResult); +}; + +} /* namespace mozilla */ + +#endif // MOZILLA_SVGATTRVALUEWRAPPER_H__ diff --git a/dom/svg/SVGCircleElement.cpp b/dom/svg/SVGCircleElement.cpp new file mode 100644 index 0000000000..fa04824f1f --- /dev/null +++ b/dom/svg/SVGCircleElement.cpp @@ -0,0 +1,141 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "mozilla/dom/SVGCircleElement.h" +#include "mozilla/gfx/2D.h" +#include "nsGkAtoms.h" +#include "mozilla/dom/SVGCircleElementBinding.h" + +NS_IMPL_NS_NEW_NAMESPACED_SVG_ELEMENT(Circle) + +using namespace mozilla::gfx; + +namespace mozilla { +namespace dom { + +JSObject* +SVGCircleElement::WrapNode(JSContext *aCx, JS::Handle<JSObject*> aGivenProto) +{ + return SVGCircleElementBinding::Wrap(aCx, this, aGivenProto); +} + +nsSVGElement::LengthInfo SVGCircleElement::sLengthInfo[3] = +{ + { &nsGkAtoms::cx, 0, nsIDOMSVGLength::SVG_LENGTHTYPE_NUMBER, SVGContentUtils::X }, + { &nsGkAtoms::cy, 0, nsIDOMSVGLength::SVG_LENGTHTYPE_NUMBER, SVGContentUtils::Y }, + { &nsGkAtoms::r, 0, nsIDOMSVGLength::SVG_LENGTHTYPE_NUMBER, SVGContentUtils::XY } +}; + +//---------------------------------------------------------------------- +// Implementation + +SVGCircleElement::SVGCircleElement(already_AddRefed<mozilla::dom::NodeInfo>& aNodeInfo) + : SVGCircleElementBase(aNodeInfo) +{ +} + +//---------------------------------------------------------------------- +// nsIDOMNode methods + +NS_IMPL_ELEMENT_CLONE_WITH_INIT(SVGCircleElement) + +//---------------------------------------------------------------------- + +already_AddRefed<SVGAnimatedLength> +SVGCircleElement::Cx() +{ + return mLengthAttributes[ATTR_CX].ToDOMAnimatedLength(this); +} + +already_AddRefed<SVGAnimatedLength> +SVGCircleElement::Cy() +{ + return mLengthAttributes[ATTR_CY].ToDOMAnimatedLength(this); +} + +already_AddRefed<SVGAnimatedLength> +SVGCircleElement::R() +{ + return mLengthAttributes[ATTR_R].ToDOMAnimatedLength(this); +} + +//---------------------------------------------------------------------- +// nsSVGElement methods + +/* virtual */ bool +SVGCircleElement::HasValidDimensions() const +{ + return mLengthAttributes[ATTR_R].IsExplicitlySet() && + mLengthAttributes[ATTR_R].GetAnimValInSpecifiedUnits() > 0; +} + +nsSVGElement::LengthAttributesInfo +SVGCircleElement::GetLengthInfo() +{ + return LengthAttributesInfo(mLengthAttributes, sLengthInfo, + ArrayLength(sLengthInfo)); +} + +//---------------------------------------------------------------------- +// nsSVGPathGeometryElement methods + +bool +SVGCircleElement::GetGeometryBounds(Rect* aBounds, + const StrokeOptions& aStrokeOptions, + const Matrix& aToBoundsSpace, + const Matrix* aToNonScalingStrokeSpace) +{ + float x, y, r; + GetAnimatedLengthValues(&x, &y, &r, nullptr); + + if (r <= 0.f) { + // Rendering of the element is disabled + *aBounds = Rect(aToBoundsSpace.TransformPoint(Point(x, y)), Size()); + return true; + } + + if (aToBoundsSpace.IsRectilinear()) { + // Optimize the case where we can treat the circle as a rectangle and + // still get tight bounds. + if (aStrokeOptions.mLineWidth > 0.f) { + if (aToNonScalingStrokeSpace) { + if (aToNonScalingStrokeSpace->IsRectilinear()) { + MOZ_ASSERT(!aToNonScalingStrokeSpace->IsSingular()); + Rect userBounds(x - r, y - r, 2 * r, 2 * r); + SVGContentUtils::RectilinearGetStrokeBounds( + userBounds, aToBoundsSpace, *aToNonScalingStrokeSpace, + aStrokeOptions.mLineWidth, aBounds); + return true; + } + return false; + } + r += aStrokeOptions.mLineWidth / 2.f; + } + Rect rect(x - r, y - r, 2 * r, 2 * r); + *aBounds = aToBoundsSpace.TransformBounds(rect); + return true; + } + + return false; +} + +already_AddRefed<Path> +SVGCircleElement::BuildPath(PathBuilder* aBuilder) +{ + float x, y, r; + GetAnimatedLengthValues(&x, &y, &r, nullptr); + + if (r <= 0.0f) { + return nullptr; + } + + aBuilder->Arc(Point(x, y), r, 0, Float(2*M_PI)); + + return aBuilder->Finish(); +} + +} // namespace dom +} // namespace mozilla diff --git a/dom/svg/SVGCircleElement.h b/dom/svg/SVGCircleElement.h new file mode 100644 index 0000000000..ff7bfe9024 --- /dev/null +++ b/dom/svg/SVGCircleElement.h @@ -0,0 +1,58 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef mozilla_dom_SVGCircleElement_h +#define mozilla_dom_SVGCircleElement_h + +#include "nsSVGPathGeometryElement.h" +#include "nsSVGLength2.h" + +nsresult NS_NewSVGCircleElement(nsIContent **aResult, + already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo); + +typedef nsSVGPathGeometryElement SVGCircleElementBase; + +namespace mozilla { +namespace dom { + +class SVGCircleElement final : public SVGCircleElementBase +{ +protected: + explicit SVGCircleElement(already_AddRefed<mozilla::dom::NodeInfo>& aNodeInfo); + virtual JSObject* WrapNode(JSContext *cx, JS::Handle<JSObject*> aGivenProto) override; + friend nsresult (::NS_NewSVGCircleElement(nsIContent **aResult, + already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo)); + +public: + // nsSVGSVGElement methods: + virtual bool HasValidDimensions() const override; + + // nsSVGPathGeometryElement methods: + virtual bool GetGeometryBounds(Rect* aBounds, const StrokeOptions& aStrokeOptions, + const Matrix& aToBoundsSpace, + const Matrix* aToNonScalingStrokeSpace = nullptr) override; + virtual already_AddRefed<Path> BuildPath(PathBuilder* aBuilder) override; + + virtual nsresult Clone(mozilla::dom::NodeInfo *aNodeInfo, nsINode **aResult) const override; + + // WebIDL + already_AddRefed<SVGAnimatedLength> Cx(); + already_AddRefed<SVGAnimatedLength> Cy(); + already_AddRefed<SVGAnimatedLength> R(); + +protected: + + virtual LengthAttributesInfo GetLengthInfo() override; + + enum { ATTR_CX, ATTR_CY, ATTR_R }; + nsSVGLength2 mLengthAttributes[3]; + static LengthInfo sLengthInfo[3]; +}; + +} // namespace dom +} // namespace mozilla + +#endif // mozilla_dom_SVGCircleElement_h diff --git a/dom/svg/SVGClipPathElement.cpp b/dom/svg/SVGClipPathElement.cpp new file mode 100644 index 0000000000..60d72fdf04 --- /dev/null +++ b/dom/svg/SVGClipPathElement.cpp @@ -0,0 +1,59 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "mozilla/ArrayUtils.h" + +#include "mozilla/dom/SVGClipPathElement.h" +#include "mozilla/dom/SVGClipPathElementBinding.h" +#include "nsGkAtoms.h" + +NS_IMPL_NS_NEW_NAMESPACED_SVG_ELEMENT(ClipPath) + +namespace mozilla { +namespace dom { + +JSObject* +SVGClipPathElement::WrapNode(JSContext *aCx, JS::Handle<JSObject*> aGivenProto) +{ + return SVGClipPathElementBinding::Wrap(aCx, this, aGivenProto); +} + +nsSVGElement::EnumInfo SVGClipPathElement::sEnumInfo[1] = +{ + { &nsGkAtoms::clipPathUnits, + sSVGUnitTypesMap, + SVG_UNIT_TYPE_USERSPACEONUSE + } +}; + +//---------------------------------------------------------------------- +// Implementation + +SVGClipPathElement::SVGClipPathElement(already_AddRefed<mozilla::dom::NodeInfo>& aNodeInfo) + : SVGClipPathElementBase(aNodeInfo) +{ +} + +already_AddRefed<SVGAnimatedEnumeration> +SVGClipPathElement::ClipPathUnits() +{ + return mEnumAttributes[CLIPPATHUNITS].ToDOMAnimatedEnum(this); +} + +nsSVGElement::EnumAttributesInfo +SVGClipPathElement::GetEnumInfo() +{ + return EnumAttributesInfo(mEnumAttributes, sEnumInfo, + ArrayLength(sEnumInfo)); +} + +//---------------------------------------------------------------------- +// nsIDOMNode methods + +NS_IMPL_ELEMENT_CLONE_WITH_INIT(SVGClipPathElement) + +} // namespace dom +} // namespace mozilla diff --git a/dom/svg/SVGClipPathElement.h b/dom/svg/SVGClipPathElement.h new file mode 100644 index 0000000000..d84f5b30fb --- /dev/null +++ b/dom/svg/SVGClipPathElement.h @@ -0,0 +1,51 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef mozilla_dom_SVGClipPathElement_h +#define mozilla_dom_SVGClipPathElement_h + +#include "nsSVGEnum.h" +#include "mozilla/dom/SVGTransformableElement.h" + +class nsSVGClipPathFrame; + +nsresult NS_NewSVGClipPathElement(nsIContent **aResult, + already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo); + +namespace mozilla { +namespace dom { + +typedef SVGTransformableElement SVGClipPathElementBase; + +class SVGClipPathElement final : public SVGClipPathElementBase +{ + friend class ::nsSVGClipPathFrame; + +protected: + friend nsresult (::NS_NewSVGClipPathElement(nsIContent **aResult, + already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo)); + explicit SVGClipPathElement(already_AddRefed<mozilla::dom::NodeInfo>& aNodeInfo); + virtual JSObject* WrapNode(JSContext *cx, JS::Handle<JSObject*> aGivenProto) override; + +public: + virtual nsresult Clone(mozilla::dom::NodeInfo *aNodeInfo, nsINode **aResult) const override; + + // WebIDL + already_AddRefed<SVGAnimatedEnumeration> ClipPathUnits(); + +protected: + + enum { CLIPPATHUNITS }; + nsSVGEnum mEnumAttributes[1]; + static EnumInfo sEnumInfo[1]; + + virtual EnumAttributesInfo GetEnumInfo() override; +}; + +} // namespace dom +} // namespace mozilla + +#endif // mozilla_dom_SVGClipPathElement_h diff --git a/dom/svg/SVGComponentTransferFunctionElement.h b/dom/svg/SVGComponentTransferFunctionElement.h new file mode 100644 index 0000000000..5dd32ce4cb --- /dev/null +++ b/dom/svg/SVGComponentTransferFunctionElement.h @@ -0,0 +1,187 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef mozilla_dom_SVGComponentTransferFunctionElement_h +#define mozilla_dom_SVGComponentTransferFunctionElement_h + +#include "nsSVGEnum.h" +#include "nsSVGFilters.h" +#include "nsSVGNumber2.h" +#include "SVGAnimatedNumberList.h" + + +#define NS_SVG_FE_COMPONENT_TRANSFER_FUNCTION_ELEMENT_CID \ +{ 0xafab106d, 0xbc18, 0x4f7f, \ + { 0x9e, 0x29, 0xfe, 0xb4, 0xb0, 0x16, 0x5f, 0xf4 } } + +namespace mozilla { + +class DOMSVGAnimatedNumberList; + +namespace dom { + +typedef SVGFEUnstyledElement SVGComponentTransferFunctionElementBase; + +class SVGComponentTransferFunctionElement : public SVGComponentTransferFunctionElementBase +{ +protected: + explicit SVGComponentTransferFunctionElement(already_AddRefed<mozilla::dom::NodeInfo>& aNodeInfo) + : SVGComponentTransferFunctionElementBase(aNodeInfo) + { + } + + virtual ~SVGComponentTransferFunctionElement() {} + +public: + typedef gfx::AttributeMap AttributeMap; + + // interfaces: + NS_DECLARE_STATIC_IID_ACCESSOR(NS_SVG_FE_COMPONENT_TRANSFER_FUNCTION_ELEMENT_CID) + + NS_DECL_ISUPPORTS_INHERITED + + virtual bool AttributeAffectsRendering( + int32_t aNameSpaceID, nsIAtom* aAttribute) const override; + + virtual int32_t GetChannel() = 0; + + AttributeMap ComputeAttributes(); + + // WebIDL + virtual JSObject* WrapNode(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override = 0; + already_AddRefed<SVGAnimatedEnumeration> Type(); + already_AddRefed<DOMSVGAnimatedNumberList> TableValues(); + already_AddRefed<SVGAnimatedNumber> Slope(); + already_AddRefed<SVGAnimatedNumber> Intercept(); + already_AddRefed<SVGAnimatedNumber> Amplitude(); + already_AddRefed<SVGAnimatedNumber> Exponent(); + already_AddRefed<SVGAnimatedNumber> Offset(); + +protected: + virtual NumberAttributesInfo GetNumberInfo() override; + virtual EnumAttributesInfo GetEnumInfo() override; + virtual NumberListAttributesInfo GetNumberListInfo() override; + + enum { TABLEVALUES }; + SVGAnimatedNumberList mNumberListAttributes[1]; + static NumberListInfo sNumberListInfo[1]; + + enum { SLOPE, INTERCEPT, AMPLITUDE, EXPONENT, OFFSET }; + nsSVGNumber2 mNumberAttributes[5]; + static NumberInfo sNumberInfo[5]; + + enum { TYPE }; + nsSVGEnum mEnumAttributes[1]; + static nsSVGEnumMapping sTypeMap[]; + static EnumInfo sEnumInfo[1]; +}; + +NS_DEFINE_STATIC_IID_ACCESSOR(SVGComponentTransferFunctionElement, NS_SVG_FE_COMPONENT_TRANSFER_FUNCTION_ELEMENT_CID) + +} // namespace dom +} // namespace mozilla + +nsresult NS_NewSVGFEFuncRElement( + nsIContent** aResult, already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo); + +namespace mozilla { +namespace dom { + +class SVGFEFuncRElement : public SVGComponentTransferFunctionElement +{ + friend nsresult (::NS_NewSVGFEFuncRElement( + nsIContent** aResult, already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo)); +protected: + explicit SVGFEFuncRElement(already_AddRefed<mozilla::dom::NodeInfo>& aNodeInfo) + : SVGComponentTransferFunctionElement(aNodeInfo) {} + +public: + virtual int32_t GetChannel() override { return 0; } + + virtual nsresult Clone(mozilla::dom::NodeInfo *aNodeInfo, nsINode **aResult) const override; + + virtual JSObject* WrapNode(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override; +}; + +} // namespace dom +} // namespace mozilla + +nsresult NS_NewSVGFEFuncGElement( + nsIContent** aResult, already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo); + +namespace mozilla { +namespace dom { + +class SVGFEFuncGElement : public SVGComponentTransferFunctionElement +{ + friend nsresult (::NS_NewSVGFEFuncGElement( + nsIContent** aResult, already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo)); +protected: + explicit SVGFEFuncGElement(already_AddRefed<mozilla::dom::NodeInfo>& aNodeInfo) + : SVGComponentTransferFunctionElement(aNodeInfo) {} + +public: + virtual int32_t GetChannel() override { return 1; } + + virtual nsresult Clone(mozilla::dom::NodeInfo *aNodeInfo, nsINode **aResult) const override; + + virtual JSObject* WrapNode(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override; +}; + +} // namespace dom +} // namespace mozilla + +nsresult NS_NewSVGFEFuncBElement( + nsIContent** aResult, already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo); + +namespace mozilla { +namespace dom { + +class SVGFEFuncBElement : public SVGComponentTransferFunctionElement +{ + friend nsresult (::NS_NewSVGFEFuncBElement( + nsIContent** aResult, already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo)); +protected: + explicit SVGFEFuncBElement(already_AddRefed<mozilla::dom::NodeInfo>& aNodeInfo) + : SVGComponentTransferFunctionElement(aNodeInfo) {} + +public: + virtual int32_t GetChannel() override { return 2; } + + virtual nsresult Clone(mozilla::dom::NodeInfo *aNodeInfo, nsINode **aResult) const override; + + virtual JSObject* WrapNode(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override; +}; + +} // namespace dom +} // namespace mozilla + +nsresult NS_NewSVGFEFuncAElement( + nsIContent** aResult, already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo); + +namespace mozilla { +namespace dom { + +class SVGFEFuncAElement : public SVGComponentTransferFunctionElement +{ + friend nsresult (::NS_NewSVGFEFuncAElement( + nsIContent** aResult, already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo)); +protected: + explicit SVGFEFuncAElement(already_AddRefed<mozilla::dom::NodeInfo>& aNodeInfo) + : SVGComponentTransferFunctionElement(aNodeInfo) {} + +public: + virtual int32_t GetChannel() override { return 3; } + + virtual nsresult Clone(mozilla::dom::NodeInfo *aNodeInfo, nsINode **aResult) const override; + + virtual JSObject* WrapNode(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override; +}; + +} // namespace dom +} // namespace mozilla + +#endif // mozilla_dom_SVGComponentTransferFunctionElement_h diff --git a/dom/svg/SVGContentUtils.cpp b/dom/svg/SVGContentUtils.cpp new file mode 100644 index 0000000000..ed3b6032e5 --- /dev/null +++ b/dom/svg/SVGContentUtils.cpp @@ -0,0 +1,898 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +// Main header first: +// This is also necessary to ensure our definition of M_SQRT1_2 is picked up +#include "SVGContentUtils.h" + +// Keep others in (case-insensitive) order: +#include "gfx2DGlue.h" +#include "gfxMatrix.h" +#include "gfxPlatform.h" +#include "mozilla/gfx/2D.h" +#include "mozilla/dom/SVGSVGElement.h" +#include "mozilla/RefPtr.h" +#include "mozilla/SVGContextPaint.h" +#include "nsComputedDOMStyle.h" +#include "nsFontMetrics.h" +#include "nsIFrame.h" +#include "nsIScriptError.h" +#include "nsLayoutUtils.h" +#include "SVGAnimationElement.h" +#include "SVGAnimatedPreserveAspectRatio.h" +#include "nsContentUtils.h" +#include "mozilla/gfx/2D.h" +#include "mozilla/gfx/Types.h" +#include "mozilla/FloatingPoint.h" +#include "nsStyleContext.h" +#include "nsSVGPathDataParser.h" +#include "SVGPathData.h" +#include "SVGPathElement.h" + +using namespace mozilla; +using namespace mozilla::dom; +using namespace mozilla::gfx; + +SVGSVGElement* +SVGContentUtils::GetOuterSVGElement(nsSVGElement *aSVGElement) +{ + nsIContent *element = nullptr; + nsIContent *ancestor = aSVGElement->GetFlattenedTreeParent(); + + while (ancestor && ancestor->IsSVGElement() && + !ancestor->IsSVGElement(nsGkAtoms::foreignObject)) { + element = ancestor; + ancestor = element->GetFlattenedTreeParent(); + } + + if (element && element->IsSVGElement(nsGkAtoms::svg)) { + return static_cast<SVGSVGElement*>(element); + } + return nullptr; +} + +void +SVGContentUtils::ActivateByHyperlink(nsIContent *aContent) +{ + MOZ_ASSERT(aContent->IsNodeOfType(nsINode::eANIMATION), + "Expecting an animation element"); + + static_cast<SVGAnimationElement*>(aContent)->ActivateByHyperlink(); +} + +enum DashState { + eDashedStroke, + eContinuousStroke, //< all dashes, no gaps + eNoStroke //< all gaps, no dashes +}; + +static DashState +GetStrokeDashData(SVGContentUtils::AutoStrokeOptions* aStrokeOptions, + nsSVGElement* aElement, + const nsStyleSVG* aStyleSVG, + SVGContextPaint* aContextPaint) +{ + size_t dashArrayLength; + Float totalLengthOfDashes = 0.0, totalLengthOfGaps = 0.0; + Float pathScale = 1.0; + + if (aContextPaint && aStyleSVG->StrokeDasharrayFromObject()) { + const FallibleTArray<gfxFloat>& dashSrc = aContextPaint->GetStrokeDashArray(); + dashArrayLength = dashSrc.Length(); + if (dashArrayLength <= 0) { + return eContinuousStroke; + } + Float* dashPattern = aStrokeOptions->InitDashPattern(dashArrayLength); + if (!dashPattern) { + return eContinuousStroke; + } + for (size_t i = 0; i < dashArrayLength; i++) { + if (dashSrc[i] < 0.0) { + return eContinuousStroke; // invalid + } + dashPattern[i] = Float(dashSrc[i]); + (i % 2 ? totalLengthOfGaps : totalLengthOfDashes) += dashSrc[i]; + } + } else { + const nsTArray<nsStyleCoord>& dasharray = aStyleSVG->mStrokeDasharray; + dashArrayLength = aStyleSVG->mStrokeDasharray.Length(); + if (dashArrayLength <= 0) { + return eContinuousStroke; + } + if (aElement->IsSVGElement(nsGkAtoms::path)) { + pathScale = static_cast<SVGPathElement*>(aElement)-> + GetPathLengthScale(SVGPathElement::eForStroking); + if (pathScale <= 0) { + return eContinuousStroke; + } + } + Float* dashPattern = aStrokeOptions->InitDashPattern(dashArrayLength); + if (!dashPattern) { + return eContinuousStroke; + } + for (uint32_t i = 0; i < dashArrayLength; i++) { + Float dashLength = + SVGContentUtils::CoordToFloat(aElement, dasharray[i]) * pathScale; + if (dashLength < 0.0) { + return eContinuousStroke; // invalid + } + dashPattern[i] = dashLength; + (i % 2 ? totalLengthOfGaps : totalLengthOfDashes) += dashLength; + } + } + + // Now that aStrokeOptions.mDashPattern is fully initialized (we didn't + // return early above) we can safely set mDashLength: + aStrokeOptions->mDashLength = dashArrayLength; + + if ((dashArrayLength % 2) == 1) { + // If we have a dash pattern with an odd number of lengths the pattern + // repeats a second time, per the SVG spec., and as implemented by Moz2D. + // When deciding whether to return eNoStroke or eContinuousStroke below we + // need to take into account that in the repeat pattern the dashes become + // gaps, and the gaps become dashes. + Float origTotalLengthOfDashes = totalLengthOfDashes; + totalLengthOfDashes += totalLengthOfGaps; + totalLengthOfGaps += origTotalLengthOfDashes; + } + + // Stroking using dashes is much slower than stroking a continuous line + // (see bug 609361 comment 40), and much, much slower than not stroking the + // line at all. Here we check for cases when the dash pattern causes the + // stroke to essentially be continuous or to be nonexistent in which case + // we can avoid expensive stroking operations (the underlying platform + // graphics libraries don't seem to optimize for this). + if (totalLengthOfGaps <= 0) { + return eContinuousStroke; + } + // We can only return eNoStroke if the value of stroke-linecap isn't + // adding caps to zero length dashes. + if (totalLengthOfDashes <= 0 && + aStyleSVG->mStrokeLinecap == NS_STYLE_STROKE_LINECAP_BUTT) { + return eNoStroke; + } + + if (aContextPaint && aStyleSVG->StrokeDashoffsetFromObject()) { + aStrokeOptions->mDashOffset = Float(aContextPaint->GetStrokeDashOffset()); + } else { + aStrokeOptions->mDashOffset = + SVGContentUtils::CoordToFloat(aElement, aStyleSVG->mStrokeDashoffset) * + pathScale; + } + + return eDashedStroke; +} + +void +SVGContentUtils::GetStrokeOptions(AutoStrokeOptions* aStrokeOptions, + nsSVGElement* aElement, + nsStyleContext* aStyleContext, + SVGContextPaint* aContextPaint, + StrokeOptionFlags aFlags) +{ + RefPtr<nsStyleContext> styleContext; + if (aStyleContext) { + styleContext = aStyleContext; + } else { + styleContext = + nsComputedDOMStyle::GetStyleContextForElementNoFlush(aElement, nullptr, + nullptr); + } + + if (!styleContext) { + return; + } + + const nsStyleSVG* styleSVG = styleContext->StyleSVG(); + + bool checkedDashAndStrokeIsDashed = false; + if (aFlags != eIgnoreStrokeDashing) { + DashState dashState = + GetStrokeDashData(aStrokeOptions, aElement, styleSVG, aContextPaint); + + if (dashState == eNoStroke) { + // Hopefully this will shortcircuit any stroke operations: + aStrokeOptions->mLineWidth = 0; + return; + } + if (dashState == eContinuousStroke && aStrokeOptions->mDashPattern) { + // Prevent our caller from wasting time looking at a pattern without gaps: + aStrokeOptions->DiscardDashPattern(); + } + checkedDashAndStrokeIsDashed = (dashState == eDashedStroke); + } + + aStrokeOptions->mLineWidth = + GetStrokeWidth(aElement, styleContext, aContextPaint); + + aStrokeOptions->mMiterLimit = Float(styleSVG->mStrokeMiterlimit); + + switch (styleSVG->mStrokeLinejoin) { + case NS_STYLE_STROKE_LINEJOIN_MITER: + aStrokeOptions->mLineJoin = JoinStyle::MITER_OR_BEVEL; + break; + case NS_STYLE_STROKE_LINEJOIN_ROUND: + aStrokeOptions->mLineJoin = JoinStyle::ROUND; + break; + case NS_STYLE_STROKE_LINEJOIN_BEVEL: + aStrokeOptions->mLineJoin = JoinStyle::BEVEL; + break; + } + + if (ShapeTypeHasNoCorners(aElement) && !checkedDashAndStrokeIsDashed) { + // Note: if aFlags == eIgnoreStrokeDashing then we may be returning the + // wrong linecap value here, since the actual linecap used on render in this + // case depends on whether the stroke is dashed or not. + aStrokeOptions->mLineCap = CapStyle::BUTT; + } else { + switch (styleSVG->mStrokeLinecap) { + case NS_STYLE_STROKE_LINECAP_BUTT: + aStrokeOptions->mLineCap = CapStyle::BUTT; + break; + case NS_STYLE_STROKE_LINECAP_ROUND: + aStrokeOptions->mLineCap = CapStyle::ROUND; + break; + case NS_STYLE_STROKE_LINECAP_SQUARE: + aStrokeOptions->mLineCap = CapStyle::SQUARE; + break; + } + } +} + +Float +SVGContentUtils::GetStrokeWidth(nsSVGElement* aElement, + nsStyleContext* aStyleContext, + SVGContextPaint* aContextPaint) +{ + RefPtr<nsStyleContext> styleContext; + if (aStyleContext) { + styleContext = aStyleContext; + } else { + styleContext = + nsComputedDOMStyle::GetStyleContextForElementNoFlush(aElement, nullptr, + nullptr); + } + + if (!styleContext) { + return 0.0f; + } + + const nsStyleSVG* styleSVG = styleContext->StyleSVG(); + + if (aContextPaint && styleSVG->StrokeWidthFromObject()) { + return aContextPaint->GetStrokeWidth(); + } + + return SVGContentUtils::CoordToFloat(aElement, styleSVG->mStrokeWidth); +} + +float +SVGContentUtils::GetFontSize(Element *aElement) +{ + if (!aElement) + return 1.0f; + + RefPtr<nsStyleContext> styleContext = + nsComputedDOMStyle::GetStyleContextForElementNoFlush(aElement, + nullptr, nullptr); + if (!styleContext) { + // ReportToConsole + NS_WARNING("Couldn't get style context for content in GetFontStyle"); + return 1.0f; + } + + return GetFontSize(styleContext); +} + +float +SVGContentUtils::GetFontSize(nsIFrame *aFrame) +{ + MOZ_ASSERT(aFrame, "NULL frame in GetFontSize"); + return GetFontSize(aFrame->StyleContext()); +} + +float +SVGContentUtils::GetFontSize(nsStyleContext *aStyleContext) +{ + MOZ_ASSERT(aStyleContext, "NULL style context in GetFontSize"); + + nsPresContext *presContext = aStyleContext->PresContext(); + MOZ_ASSERT(presContext, "NULL pres context in GetFontSize"); + + nscoord fontSize = aStyleContext->StyleFont()->mSize; + return nsPresContext::AppUnitsToFloatCSSPixels(fontSize) / + presContext->TextZoom(); +} + +float +SVGContentUtils::GetFontXHeight(Element *aElement) +{ + if (!aElement) + return 1.0f; + + RefPtr<nsStyleContext> styleContext = + nsComputedDOMStyle::GetStyleContextForElementNoFlush(aElement, + nullptr, nullptr); + if (!styleContext) { + // ReportToConsole + NS_WARNING("Couldn't get style context for content in GetFontStyle"); + return 1.0f; + } + + return GetFontXHeight(styleContext); +} + +float +SVGContentUtils::GetFontXHeight(nsIFrame *aFrame) +{ + MOZ_ASSERT(aFrame, "NULL frame in GetFontXHeight"); + return GetFontXHeight(aFrame->StyleContext()); +} + +float +SVGContentUtils::GetFontXHeight(nsStyleContext *aStyleContext) +{ + MOZ_ASSERT(aStyleContext, "NULL style context in GetFontXHeight"); + + nsPresContext *presContext = aStyleContext->PresContext(); + MOZ_ASSERT(presContext, "NULL pres context in GetFontXHeight"); + + RefPtr<nsFontMetrics> fontMetrics = + nsLayoutUtils::GetFontMetricsForStyleContext(aStyleContext); + + if (!fontMetrics) { + // ReportToConsole + NS_WARNING("no FontMetrics in GetFontXHeight()"); + return 1.0f; + } + + nscoord xHeight = fontMetrics->XHeight(); + return nsPresContext::AppUnitsToFloatCSSPixels(xHeight) / + presContext->TextZoom(); +} +nsresult +SVGContentUtils::ReportToConsole(nsIDocument* doc, + const char* aWarning, + const char16_t **aParams, + uint32_t aParamsLength) +{ + return nsContentUtils::ReportToConsole(nsIScriptError::warningFlag, + NS_LITERAL_CSTRING("SVG"), doc, + nsContentUtils::eSVG_PROPERTIES, + aWarning, + aParams, aParamsLength); +} + +bool +SVGContentUtils::EstablishesViewport(nsIContent *aContent) +{ + // Although SVG 1.1 states that <image> is an element that establishes a + // viewport, this is really only for the document it references, not + // for any child content, which is what this function is used for. + return aContent && aContent->IsAnyOfSVGElements(nsGkAtoms::svg, + nsGkAtoms::foreignObject, + nsGkAtoms::symbol); +} + +nsSVGElement* +SVGContentUtils::GetNearestViewportElement(nsIContent *aContent) +{ + nsIContent *element = aContent->GetFlattenedTreeParent(); + + while (element && element->IsSVGElement()) { + if (EstablishesViewport(element)) { + if (element->IsSVGElement(nsGkAtoms::foreignObject)) { + return nullptr; + } + return static_cast<nsSVGElement*>(element); + } + element = element->GetFlattenedTreeParent(); + } + return nullptr; +} + +static gfx::Matrix +GetCTMInternal(nsSVGElement *aElement, bool aScreenCTM, bool aHaveRecursed) +{ + gfxMatrix matrix = aElement->PrependLocalTransformsTo(gfxMatrix(), + aHaveRecursed ? eAllTransforms : eUserSpaceToParent); + nsSVGElement *element = aElement; + nsIContent *ancestor = aElement->GetFlattenedTreeParent(); + + while (ancestor && ancestor->IsSVGElement() && + !ancestor->IsSVGElement(nsGkAtoms::foreignObject)) { + element = static_cast<nsSVGElement*>(ancestor); + matrix *= element->PrependLocalTransformsTo(gfxMatrix()); // i.e. *A*ppend + if (!aScreenCTM && SVGContentUtils::EstablishesViewport(element)) { + if (!element->NodeInfo()->Equals(nsGkAtoms::svg, kNameSpaceID_SVG) && + !element->NodeInfo()->Equals(nsGkAtoms::symbol, kNameSpaceID_SVG)) { + NS_ERROR("New (SVG > 1.1) SVG viewport establishing element?"); + return gfx::Matrix(0.0, 0.0, 0.0, 0.0, 0.0, 0.0); // singular + } + // XXX spec seems to say x,y translation should be undone for IsInnerSVG + return gfx::ToMatrix(matrix); + } + ancestor = ancestor->GetFlattenedTreeParent(); + } + if (!aScreenCTM) { + // didn't find a nearestViewportElement + return gfx::Matrix(0.0, 0.0, 0.0, 0.0, 0.0, 0.0); // singular + } + if (!element->IsSVGElement(nsGkAtoms::svg)) { + // Not a valid SVG fragment + return gfx::Matrix(0.0, 0.0, 0.0, 0.0, 0.0, 0.0); // singular + } + if (element == aElement && !aHaveRecursed) { + // We get here when getScreenCTM() is called on an outer-<svg>. + // Consistency with other elements would have us include only the + // eFromUserSpace transforms, but we include the eAllTransforms + // transforms in this case since that's what we've been doing for + // a while, and it keeps us consistent with WebKit and Opera (if not + // really with the ambiguous spec). + matrix = aElement->PrependLocalTransformsTo(gfxMatrix()); + } + if (!ancestor || !ancestor->IsElement()) { + return gfx::ToMatrix(matrix); + } + if (ancestor->IsSVGElement()) { + return + gfx::ToMatrix(matrix) * GetCTMInternal(static_cast<nsSVGElement*>(ancestor), true, true); + } + + // XXX this does not take into account CSS transform, or that the non-SVG + // content that we've hit may itself be inside an SVG foreignObject higher up + nsIDocument* currentDoc = aElement->GetComposedDoc(); + float x = 0.0f, y = 0.0f; + if (currentDoc && element->NodeInfo()->Equals(nsGkAtoms::svg, kNameSpaceID_SVG)) { + nsIPresShell *presShell = currentDoc->GetShell(); + if (presShell) { + nsIFrame* frame = element->GetPrimaryFrame(); + nsIFrame* ancestorFrame = presShell->GetRootFrame(); + if (frame && ancestorFrame) { + nsPoint point = frame->GetOffsetTo(ancestorFrame); + x = nsPresContext::AppUnitsToFloatCSSPixels(point.x); + y = nsPresContext::AppUnitsToFloatCSSPixels(point.y); + } + } + } + return ToMatrix(matrix).PostTranslate(x, y); +} + +gfx::Matrix +SVGContentUtils::GetCTM(nsSVGElement *aElement, bool aScreenCTM) +{ + return GetCTMInternal(aElement, aScreenCTM, false); +} + +void +SVGContentUtils::RectilinearGetStrokeBounds(const Rect& aRect, + const Matrix& aToBoundsSpace, + const Matrix& aToNonScalingStrokeSpace, + float aStrokeWidth, + Rect* aBounds) +{ + MOZ_ASSERT(aToBoundsSpace.IsRectilinear(), + "aToBoundsSpace must be rectilinear"); + MOZ_ASSERT(aToNonScalingStrokeSpace.IsRectilinear(), + "aToNonScalingStrokeSpace must be rectilinear"); + + Matrix nonScalingToSource = aToNonScalingStrokeSpace.Inverse(); + Matrix nonScalingToBounds = nonScalingToSource * aToBoundsSpace; + + *aBounds = aToBoundsSpace.TransformBounds(aRect); + + // Compute the amounts dx and dy that nonScalingToBounds scales a half-width + // stroke in the x and y directions, and then inflate aBounds by those amounts + // so that when aBounds is transformed back to non-scaling-stroke space + // it will map onto the correct stroked bounds. + + Float dx = 0.0f; + Float dy = 0.0f; + // nonScalingToBounds is rectilinear, so either _12 and _21 are zero or _11 + // and _22 are zero, and in each case the non-zero entries (from among _11, + // _12, _21, _22) simply scale the stroke width in the x and y directions. + if (FuzzyEqual(nonScalingToBounds._12, 0) && + FuzzyEqual(nonScalingToBounds._21, 0)) { + dx = (aStrokeWidth / 2.0f) * std::abs(nonScalingToBounds._11); + dy = (aStrokeWidth / 2.0f) * std::abs(nonScalingToBounds._22); + } else { + dx = (aStrokeWidth / 2.0f) * std::abs(nonScalingToBounds._21); + dy = (aStrokeWidth / 2.0f) * std::abs(nonScalingToBounds._12); + } + + aBounds->Inflate(dx, dy); +} + +double +SVGContentUtils::ComputeNormalizedHypotenuse(double aWidth, double aHeight) +{ + return sqrt((aWidth*aWidth + aHeight*aHeight)/2); +} + +float +SVGContentUtils::AngleBisect(float a1, float a2) +{ + float delta = fmod(a2 - a1, static_cast<float>(2*M_PI)); + if (delta < 0) { + delta += static_cast<float>(2*M_PI); + } + /* delta is now the angle from a1 around to a2, in the range [0, 2*M_PI) */ + float r = a1 + delta/2; + if (delta >= M_PI) { + /* the arc from a2 to a1 is smaller, so use the ray on that side */ + r += static_cast<float>(M_PI); + } + return r; +} + +gfx::Matrix +SVGContentUtils::GetViewBoxTransform(float aViewportWidth, float aViewportHeight, + float aViewboxX, float aViewboxY, + float aViewboxWidth, float aViewboxHeight, + const SVGAnimatedPreserveAspectRatio &aPreserveAspectRatio) +{ + return GetViewBoxTransform(aViewportWidth, aViewportHeight, + aViewboxX, aViewboxY, + aViewboxWidth, aViewboxHeight, + aPreserveAspectRatio.GetAnimValue()); +} + +gfx::Matrix +SVGContentUtils::GetViewBoxTransform(float aViewportWidth, float aViewportHeight, + float aViewboxX, float aViewboxY, + float aViewboxWidth, float aViewboxHeight, + const SVGPreserveAspectRatio &aPreserveAspectRatio) +{ + NS_ASSERTION(aViewportWidth >= 0, "viewport width must be nonnegative!"); + NS_ASSERTION(aViewportHeight >= 0, "viewport height must be nonnegative!"); + NS_ASSERTION(aViewboxWidth > 0, "viewBox width must be greater than zero!"); + NS_ASSERTION(aViewboxHeight > 0, "viewBox height must be greater than zero!"); + + SVGAlign align = aPreserveAspectRatio.GetAlign(); + SVGMeetOrSlice meetOrSlice = aPreserveAspectRatio.GetMeetOrSlice(); + + // default to the defaults + if (align == SVG_PRESERVEASPECTRATIO_UNKNOWN) + align = SVG_PRESERVEASPECTRATIO_XMIDYMID; + if (meetOrSlice == SVG_MEETORSLICE_UNKNOWN) + meetOrSlice = SVG_MEETORSLICE_MEET; + + float a, d, e, f; + a = aViewportWidth / aViewboxWidth; + d = aViewportHeight / aViewboxHeight; + e = 0.0f; + f = 0.0f; + + if (align != SVG_PRESERVEASPECTRATIO_NONE && + a != d) { + if ((meetOrSlice == SVG_MEETORSLICE_MEET && a < d) || + (meetOrSlice == SVG_MEETORSLICE_SLICE && d < a)) { + d = a; + switch (align) { + case SVG_PRESERVEASPECTRATIO_XMINYMIN: + case SVG_PRESERVEASPECTRATIO_XMIDYMIN: + case SVG_PRESERVEASPECTRATIO_XMAXYMIN: + break; + case SVG_PRESERVEASPECTRATIO_XMINYMID: + case SVG_PRESERVEASPECTRATIO_XMIDYMID: + case SVG_PRESERVEASPECTRATIO_XMAXYMID: + f = (aViewportHeight - a * aViewboxHeight) / 2.0f; + break; + case SVG_PRESERVEASPECTRATIO_XMINYMAX: + case SVG_PRESERVEASPECTRATIO_XMIDYMAX: + case SVG_PRESERVEASPECTRATIO_XMAXYMAX: + f = aViewportHeight - a * aViewboxHeight; + break; + default: + NS_NOTREACHED("Unknown value for align"); + } + } + else if ( + (meetOrSlice == SVG_MEETORSLICE_MEET && + d < a) || + (meetOrSlice == SVG_MEETORSLICE_SLICE && + a < d)) { + a = d; + switch (align) { + case SVG_PRESERVEASPECTRATIO_XMINYMIN: + case SVG_PRESERVEASPECTRATIO_XMINYMID: + case SVG_PRESERVEASPECTRATIO_XMINYMAX: + break; + case SVG_PRESERVEASPECTRATIO_XMIDYMIN: + case SVG_PRESERVEASPECTRATIO_XMIDYMID: + case SVG_PRESERVEASPECTRATIO_XMIDYMAX: + e = (aViewportWidth - a * aViewboxWidth) / 2.0f; + break; + case SVG_PRESERVEASPECTRATIO_XMAXYMIN: + case SVG_PRESERVEASPECTRATIO_XMAXYMID: + case SVG_PRESERVEASPECTRATIO_XMAXYMAX: + e = aViewportWidth - a * aViewboxWidth; + break; + default: + NS_NOTREACHED("Unknown value for align"); + } + } + else NS_NOTREACHED("Unknown value for meetOrSlice"); + } + + if (aViewboxX) e += -a * aViewboxX; + if (aViewboxY) f += -d * aViewboxY; + + return gfx::Matrix(a, 0.0f, 0.0f, d, e, f); +} + +static bool +ParseNumber(RangedPtr<const char16_t>& aIter, + const RangedPtr<const char16_t>& aEnd, + double& aValue) +{ + int32_t sign; + if (!SVGContentUtils::ParseOptionalSign(aIter, aEnd, sign)) { + return false; + } + + // Absolute value of the integer part of the mantissa. + double intPart = 0.0; + + bool gotDot = *aIter == '.'; + + if (!gotDot) { + if (!SVGContentUtils::IsDigit(*aIter)) { + return false; + } + do { + intPart = 10.0 * intPart + SVGContentUtils::DecimalDigitValue(*aIter); + ++aIter; + } while (aIter != aEnd && SVGContentUtils::IsDigit(*aIter)); + + if (aIter != aEnd) { + gotDot = *aIter == '.'; + } + } + + // Fractional part of the mantissa. + double fracPart = 0.0; + + if (gotDot) { + ++aIter; + if (aIter == aEnd || !SVGContentUtils::IsDigit(*aIter)) { + return false; + } + + // Power of ten by which we need to divide the fraction + double divisor = 1.0; + + do { + fracPart = 10.0 * fracPart + SVGContentUtils::DecimalDigitValue(*aIter); + divisor *= 10.0; + ++aIter; + } while (aIter != aEnd && SVGContentUtils::IsDigit(*aIter)); + + fracPart /= divisor; + } + + bool gotE = false; + int32_t exponent = 0; + int32_t expSign; + + if (aIter != aEnd && (*aIter == 'e' || *aIter == 'E')) { + + RangedPtr<const char16_t> expIter(aIter); + + ++expIter; + if (expIter != aEnd) { + expSign = *expIter == '-' ? -1 : 1; + if (*expIter == '-' || *expIter == '+') { + ++expIter; + } + if (expIter != aEnd && SVGContentUtils::IsDigit(*expIter)) { + // At this point we're sure this is an exponent + // and not the start of a unit such as em or ex. + gotE = true; + } + } + + if (gotE) { + aIter = expIter; + do { + exponent = 10.0 * exponent + SVGContentUtils::DecimalDigitValue(*aIter); + ++aIter; + } while (aIter != aEnd && SVGContentUtils::IsDigit(*aIter)); + } + } + + // Assemble the number + aValue = sign * (intPart + fracPart); + if (gotE) { + aValue *= pow(10.0, expSign * exponent); + } + return true; +} + +template<class floatType> +bool +SVGContentUtils::ParseNumber(RangedPtr<const char16_t>& aIter, + const RangedPtr<const char16_t>& aEnd, + floatType& aValue) +{ + RangedPtr<const char16_t> iter(aIter); + + double value; + if (!::ParseNumber(iter, aEnd, value)) { + return false; + } + floatType floatValue = floatType(value); + if (!IsFinite(floatValue)) { + return false; + } + aValue = floatValue; + aIter = iter; + return true; +} + +template bool +SVGContentUtils::ParseNumber<float>(RangedPtr<const char16_t>& aIter, + const RangedPtr<const char16_t>& aEnd, + float& aValue); + +template bool +SVGContentUtils::ParseNumber<double>(RangedPtr<const char16_t>& aIter, + const RangedPtr<const char16_t>& aEnd, + double& aValue); + +RangedPtr<const char16_t> +SVGContentUtils::GetStartRangedPtr(const nsAString& aString) +{ + return RangedPtr<const char16_t>(aString.Data(), aString.Length()); +} + +RangedPtr<const char16_t> +SVGContentUtils::GetEndRangedPtr(const nsAString& aString) +{ + return RangedPtr<const char16_t>(aString.Data() + aString.Length(), + aString.Data(), aString.Length()); +} + +template<class floatType> +bool +SVGContentUtils::ParseNumber(const nsAString& aString, + floatType& aValue) +{ + RangedPtr<const char16_t> iter = GetStartRangedPtr(aString); + const RangedPtr<const char16_t> end = GetEndRangedPtr(aString); + + return ParseNumber(iter, end, aValue) && iter == end; +} + +template bool +SVGContentUtils::ParseNumber<float>(const nsAString& aString, + float& aValue); +template bool +SVGContentUtils::ParseNumber<double>(const nsAString& aString, + double& aValue); + +/* static */ +bool +SVGContentUtils::ParseInteger(RangedPtr<const char16_t>& aIter, + const RangedPtr<const char16_t>& aEnd, + int32_t& aValue) +{ + RangedPtr<const char16_t> iter(aIter); + + int32_t sign; + if (!ParseOptionalSign(iter, aEnd, sign)) { + return false; + } + + if (!IsDigit(*iter)) { + return false; + } + + int64_t value = 0; + + do { + if (value <= std::numeric_limits<int32_t>::max()) { + value = 10 * value + DecimalDigitValue(*iter); + } + ++iter; + } while (iter != aEnd && IsDigit(*iter)); + + aIter = iter; + aValue = int32_t(clamped(sign * value, + int64_t(std::numeric_limits<int32_t>::min()), + int64_t(std::numeric_limits<int32_t>::max()))); + return true; +} + +/* static */ +bool +SVGContentUtils::ParseInteger(const nsAString& aString, + int32_t& aValue) +{ + RangedPtr<const char16_t> iter = GetStartRangedPtr(aString); + const RangedPtr<const char16_t> end = GetEndRangedPtr(aString); + + return ParseInteger(iter, end, aValue) && iter == end; +} + +float +SVGContentUtils::CoordToFloat(nsSVGElement *aContent, + const nsStyleCoord &aCoord) +{ + switch (aCoord.GetUnit()) { + case eStyleUnit_Factor: + // user units + return aCoord.GetFactorValue(); + + case eStyleUnit_Coord: + return nsPresContext::AppUnitsToFloatCSSPixels(aCoord.GetCoordValue()); + + case eStyleUnit_Percent: { + SVGSVGElement* ctx = aContent->GetCtx(); + return ctx ? aCoord.GetPercentValue() * ctx->GetLength(SVGContentUtils::XY) : 0.0f; + } + default: + return 0.0f; + } +} + +already_AddRefed<gfx::Path> +SVGContentUtils::GetPath(const nsAString& aPathString) +{ + SVGPathData pathData; + nsSVGPathDataParser parser(aPathString, &pathData); + if (!parser.Parse()) { + return NULL; + } + + RefPtr<DrawTarget> drawTarget = + gfxPlatform::GetPlatform()->ScreenReferenceDrawTarget(); + RefPtr<PathBuilder> builder = + drawTarget->CreatePathBuilder(FillRule::FILL_WINDING); + + return pathData.BuildPath(builder, NS_STYLE_STROKE_LINECAP_BUTT, 1); +} + +bool +SVGContentUtils::ShapeTypeHasNoCorners(const nsIContent* aContent) { + return aContent && aContent->IsAnyOfSVGElements(nsGkAtoms::circle, + nsGkAtoms::ellipse); +} + +gfxMatrix +SVGContentUtils::PrependLocalTransformsTo( + const gfxMatrix &aMatrix, + SVGTransformTypes aWhich, + const gfx::Matrix* aAnimateMotionTransform, + const nsSVGAnimatedTransformList* aTransforms) +{ + gfxMatrix result(aMatrix); + + if (aWhich == eChildToUserSpace) { + // We don't have anything to prepend. + // eChildToUserSpace is not the common case, which is why we return + // 'result' to benefit from NRVO rather than returning aMatrix before + // creating 'result'. + return result; + } + + MOZ_ASSERT(aWhich == eAllTransforms || aWhich == eUserSpaceToParent, + "Unknown TransformTypes"); + + // animateMotion's resulting transform is supposed to apply *on top of* + // any transformations from the |transform| attribute. So since we're + // PRE-multiplying, we need to apply the animateMotion transform *first*. + if (aAnimateMotionTransform) { + result.PreMultiply(ThebesMatrix(*aAnimateMotionTransform)); + } + + if (aTransforms) { + result.PreMultiply( + aTransforms->GetAnimValue().GetConsolidationMatrix()); + } + + return result; +} diff --git a/dom/svg/SVGContentUtils.h b/dom/svg/SVGContentUtils.h new file mode 100644 index 0000000000..373adf9f05 --- /dev/null +++ b/dom/svg/SVGContentUtils.h @@ -0,0 +1,400 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef MOZILLA_SVGCONTENTUTILS_H +#define MOZILLA_SVGCONTENTUTILS_H + +// include math.h to pick up definition of M_ maths defines e.g. M_PI +#include <math.h> + +#include "mozilla/gfx/2D.h" // for StrokeOptions +#include "mozilla/gfx/Matrix.h" +#include "mozilla/RangedPtr.h" +#include "nsError.h" +#include "nsStringFwd.h" +#include "gfx2DGlue.h" + +class nsIContent; +class nsIDocument; +class nsIFrame; +class nsStyleContext; +class nsStyleCoord; +class nsSVGElement; + +namespace mozilla { +class nsSVGAnimatedTransformList; +class SVGAnimatedPreserveAspectRatio; +class SVGContextPaint; +class SVGPreserveAspectRatio; +namespace dom { +class Element; +class SVGSVGElement; +} // namespace dom + +namespace gfx { +class Matrix; +} // namespace gfx +} // namespace mozilla + +#define SVG_ZERO_LENGTH_PATH_FIX_FACTOR 512 + +/** + * SVGTransformTypes controls the transforms that PrependLocalTransformsTo + * applies. + * + * If aWhich is eAllTransforms, then all the transforms from the coordinate + * space established by this element for its children to the coordinate + * space established by this element's parent element for this element, are + * included. + * + * If aWhich is eUserSpaceToParent, then only the transforms from this + * element's userspace to the coordinate space established by its parent is + * included. This includes any transforms introduced by the 'transform' + * attribute, transform animations and animateMotion, but not any offsets + * due to e.g. 'x'/'y' attributes, or any transform due to a 'viewBox' + * attribute. (SVG userspace is defined to be the coordinate space in which + * coordinates on an element apply.) + * + * If aWhich is eChildToUserSpace, then only the transforms from the + * coordinate space established by this element for its childre to this + * elements userspace are included. This includes any offsets due to e.g. + * 'x'/'y' attributes, and any transform due to a 'viewBox' attribute, but + * does not include any transforms due to the 'transform' attribute. + */ +enum SVGTransformTypes { + eAllTransforms, + eUserSpaceToParent, + eChildToUserSpace +}; + +inline bool +IsSVGWhitespace(char aChar) +{ + return aChar == '\x20' || aChar == '\x9' || + aChar == '\xD' || aChar == '\xA'; +} + +inline bool +IsSVGWhitespace(char16_t aChar) +{ + return aChar == char16_t('\x20') || aChar == char16_t('\x9') || + aChar == char16_t('\xD') || aChar == char16_t('\xA'); +} + +/** + * Functions generally used by SVG Content classes. Functions here + * should not generally depend on layout methods/classes e.g. nsSVGUtils + */ +class SVGContentUtils +{ +public: + typedef mozilla::gfx::Float Float; + typedef mozilla::gfx::Matrix Matrix; + typedef mozilla::gfx::Rect Rect; + typedef mozilla::gfx::StrokeOptions StrokeOptions; + typedef mozilla::SVGAnimatedPreserveAspectRatio SVGAnimatedPreserveAspectRatio; + typedef mozilla::SVGPreserveAspectRatio SVGPreserveAspectRatio; + + /* + * Get the outer SVG element of an nsIContent + */ + static mozilla::dom::SVGSVGElement *GetOuterSVGElement(nsSVGElement *aSVGElement); + + /** + * Activates the animation element aContent as a result of navigation to the + * fragment identifier that identifies aContent. aContent must be an instance + * of nsSVGAnimationElement. + * + * This is just a shim to allow nsSVGAnimationElement::ActivateByHyperlink to + * be called from layout/base without adding to that directory's include paths. + */ + static void ActivateByHyperlink(nsIContent *aContent); + + /** + * Moz2D's StrokeOptions requires someone else to own its mDashPattern + * buffer, which is a pain when you want to initialize a StrokeOptions object + * in a helper function and pass it out. This sub-class owns the mDashPattern + * buffer so that consumers of such a helper function don't need to worry + * about creating it, passing it in, or deleting it. (An added benefit is + * that in the typical case when stroke-dasharray is short it will avoid + * allocating.) + */ + struct AutoStrokeOptions : public StrokeOptions { + AutoStrokeOptions() + { + MOZ_ASSERT(mDashLength == 0, "InitDashPattern() depends on this"); + } + ~AutoStrokeOptions() { + if (mDashPattern && mDashPattern != mSmallArray) { + delete [] mDashPattern; + } + } + /** + * Creates the buffer to store the stroke-dasharray, assuming out-of-memory + * does not occur. The buffer's address is assigned to mDashPattern and + * returned to the caller as a non-const pointer (so that the caller can + * initialize the values in the buffer, since mDashPattern is const). + */ + Float* InitDashPattern(size_t aDashCount) { + if (aDashCount <= MOZ_ARRAY_LENGTH(mSmallArray)) { + mDashPattern = mSmallArray; + return mSmallArray; + } + Float* nonConstArray = new (mozilla::fallible) Float[aDashCount]; + mDashPattern = nonConstArray; + return nonConstArray; + } + void DiscardDashPattern() { + if (mDashPattern && mDashPattern != mSmallArray) { + delete [] mDashPattern; + } + mDashLength = 0; + mDashPattern = nullptr; + } + private: + // Most dasharrays will fit in this and save us allocating + Float mSmallArray[16]; + }; + + enum StrokeOptionFlags { + eAllStrokeOptions, + eIgnoreStrokeDashing + }; + /** + * Note: the linecap style returned in aStrokeOptions is not valid when + * ShapeTypeHasNoCorners(aElement) == true && aFlags == eIgnoreStrokeDashing, + * since when aElement has no corners the rendered linecap style depends on + * whether or not the stroke is dashed. + */ + static void GetStrokeOptions(AutoStrokeOptions* aStrokeOptions, + nsSVGElement* aElement, + nsStyleContext* aStyleContext, + mozilla::SVGContextPaint* aContextPaint, + StrokeOptionFlags aFlags = eAllStrokeOptions); + + /** + * Returns the current computed value of the CSS property 'stroke-width' for + * the given element. aStyleContext may be provided as an optimization. + * aContextPaint is also optional. + * + * Note that this function does NOT take account of the value of the 'stroke' + * and 'stroke-opacity' properties to, say, return zero if they are "none" or + * "0", respectively. + */ + static Float GetStrokeWidth(nsSVGElement* aElement, + nsStyleContext* aStyleContext, + mozilla::SVGContextPaint* aContextPaint); + + /* + * 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 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); + static float GetFontXHeight(nsStyleContext *aStyleContext); + + /* + * Report a localized error message to the error console. + */ + static nsresult ReportToConsole(nsIDocument* doc, + const char* aWarning, + const char16_t **aParams, + uint32_t aParamsLength); + + static Matrix GetCTM(nsSVGElement *aElement, bool aScreenCTM); + + /** + * Gets the tight bounds-space stroke bounds of the non-scaling-stroked rect + * aRect. + * @param aToBoundsSpace transforms from source space to the space aBounds + * should be computed in. Must be rectilinear. + * @param aToNonScalingStrokeSpace transforms from source + * space to the space in which non-scaling stroke should be applied. + * Must be rectilinear. + */ + static void + RectilinearGetStrokeBounds(const Rect& aRect, + const Matrix& aToBoundsSpace, + const Matrix& aToNonScalingStrokeSpace, + float aStrokeWidth, + Rect* aBounds); + + /** + * Check if this is one of the SVG elements that SVG 1.1 Full says + * establishes a viewport: svg, symbol, image or foreignObject. + */ + static bool EstablishesViewport(nsIContent *aContent); + + static nsSVGElement* + GetNearestViewportElement(nsIContent *aContent); + + /* enum for specifying coordinate direction for ObjectSpace/UserSpace */ + enum ctxDirection { X, Y, XY }; + + /** + * Computes sqrt((aWidth^2 + aHeight^2)/2); + */ + static double ComputeNormalizedHypotenuse(double aWidth, double aHeight); + + /* Returns the angle halfway between the two specified angles */ + static float + AngleBisect(float a1, float a2); + + /* Generate a viewbox to viewport tranformation matrix */ + + static Matrix + GetViewBoxTransform(float aViewportWidth, float aViewportHeight, + float aViewboxX, float aViewboxY, + float aViewboxWidth, float aViewboxHeight, + const SVGAnimatedPreserveAspectRatio &aPreserveAspectRatio); + + static Matrix + GetViewBoxTransform(float aViewportWidth, float aViewportHeight, + float aViewboxX, float aViewboxY, + float aViewboxWidth, float aViewboxHeight, + const SVGPreserveAspectRatio &aPreserveAspectRatio); + + static mozilla::RangedPtr<const char16_t> + GetStartRangedPtr(const nsAString& aString); + + static mozilla::RangedPtr<const char16_t> + GetEndRangedPtr(const nsAString& aString); + + /** + * True if 'aCh' is a decimal digit. + */ + static inline bool IsDigit(char16_t aCh) + { + return aCh >= '0' && aCh <= '9'; + } + + /** + * Assuming that 'aCh' is a decimal digit, return its numeric value. + */ + static inline uint32_t DecimalDigitValue(char16_t aCh) + { + MOZ_ASSERT(IsDigit(aCh), "Digit expected"); + return aCh - '0'; + } + + /** + * Parses the sign (+ or -) of a number and moves aIter to the next + * character if a sign is found. + * @param aSignMultiplier [outparam] -1 if the sign is negative otherwise 1 + * @return false if we hit the end of the string (i.e. if aIter is initially + * at aEnd, or if we reach aEnd right after the sign character). + */ + static inline bool + ParseOptionalSign(mozilla::RangedPtr<const char16_t>& aIter, + const mozilla::RangedPtr<const char16_t>& aEnd, + int32_t& aSignMultiplier) + { + if (aIter == aEnd) { + return false; + } + aSignMultiplier = *aIter == '-' ? -1 : 1; + + mozilla::RangedPtr<const char16_t> iter(aIter); + + if (*iter == '-' || *iter == '+') { + ++iter; + if (iter == aEnd) { + return false; + } + } + aIter = iter; + return true; + } + + /** + * Parse a number of the form: + * number ::= integer ([Ee] integer)? | [+-]? [0-9]* "." [0-9]+ ([Ee] integer)? + * Parsing fails if the number cannot be represented by a floatType. + * If parsing succeeds, aIter is updated so that it points to the character + * after the end of the number, otherwise it is left unchanged + */ + template<class floatType> + static bool + ParseNumber(mozilla::RangedPtr<const char16_t>& aIter, + const mozilla::RangedPtr<const char16_t>& aEnd, + floatType& aValue); + + /** + * Parse a number of the form: + * number ::= integer ([Ee] integer)? | [+-]? [0-9]* "." [0-9]+ ([Ee] integer)? + * Parsing fails if there is anything left over after the number, + * or the number cannot be represented by a floatType. + */ + template<class floatType> + static bool + ParseNumber(const nsAString& aString, floatType& aValue); + + /** + * Parse an integer of the form: + * integer ::= [+-]? [0-9]+ + * The returned number is clamped to an int32_t if outside that range. + * If parsing succeeds, aIter is updated so that it points to the character + * after the end of the number, otherwise it is left unchanged + */ + static bool ParseInteger(mozilla::RangedPtr<const char16_t>& aIter, + const mozilla::RangedPtr<const char16_t>& aEnd, + int32_t& aValue); + + /** + * Parse an integer of the form: + * integer ::= [+-]? [0-9]+ + * The returned number is clamped to an int32_t if outside that range. + * Parsing fails if there is anything left over after the number. + */ + static bool ParseInteger(const nsAString& aString, int32_t& aValue); + + /** + * Converts an nsStyleCoord into a userspace value. Handles units + * Factor (straight userspace), Coord (dimensioned), and Percent (of + * aContent's SVG viewport) + */ + static float CoordToFloat(nsSVGElement *aContent, + const nsStyleCoord &aCoord); + /** + * Parse the SVG path string + * Returns a path + * string formatted as an SVG path + */ + static already_AddRefed<mozilla::gfx::Path> + GetPath(const nsAString& aPathString); + + /** + * Returns true if aContent is one of the elements whose stroke is guaranteed + * to have no corners: circle or ellipse + */ + static bool ShapeTypeHasNoCorners(const nsIContent* aContent); + + /** + * Prepends an element's local transforms to the transform matrix. + * This is a helper for nsSVGElement::PrependLocalTransformsTo. + * Any callers probably really want to call that method instead of this one. + */ + static gfxMatrix PrependLocalTransformsTo( + const gfxMatrix &aMatrix, + SVGTransformTypes aWhich, + const Matrix* aAnimateMotionTransform, + const mozilla::nsSVGAnimatedTransformList* aTransforms); +}; + +#endif diff --git a/dom/svg/SVGDefsElement.cpp b/dom/svg/SVGDefsElement.cpp new file mode 100644 index 0000000000..c0491e9764 --- /dev/null +++ b/dom/svg/SVGDefsElement.cpp @@ -0,0 +1,59 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "mozilla/dom/SVGDefsElement.h" +#include "mozilla/dom/SVGDefsElementBinding.h" + +NS_IMPL_NS_NEW_NAMESPACED_SVG_ELEMENT(Defs) + +namespace mozilla { +namespace dom { + +JSObject* +SVGDefsElement::WrapNode(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) +{ + return SVGDefsElementBinding::Wrap(aCx, this, aGivenProto); +} + +//---------------------------------------------------------------------- +// Implementation + +SVGDefsElement::SVGDefsElement(already_AddRefed<mozilla::dom::NodeInfo>& aNodeInfo) + : SVGGraphicsElement(aNodeInfo) +{ +} + +//---------------------------------------------------------------------- +// nsIDOMNode methods + + +NS_IMPL_ELEMENT_CLONE_WITH_INIT(SVGDefsElement) + + +//---------------------------------------------------------------------- +// nsIContent methods + +NS_IMETHODIMP_(bool) +SVGDefsElement::IsAttributeMapped(const nsIAtom* name) const +{ + static const MappedAttributeEntry* const map[] = { + sFEFloodMap, + sFiltersMap, + sFontSpecificationMap, + sGradientStopMap, + sLightingEffectsMap, + sMarkersMap, + sTextContentElementsMap, + sViewportsMap + }; + + return FindAttributeDependence(name, map) || + SVGGraphicsElement::IsAttributeMapped(name); +} + +} // namespace dom +} // namespace mozilla + diff --git a/dom/svg/SVGDefsElement.h b/dom/svg/SVGDefsElement.h new file mode 100644 index 0000000000..260459090a --- /dev/null +++ b/dom/svg/SVGDefsElement.h @@ -0,0 +1,36 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef mozilla_dom_SVGDefsElement_h +#define mozilla_dom_SVGDefsElement_h + +#include "SVGGraphicsElement.h" + +nsresult NS_NewSVGDefsElement(nsIContent **aResult, + already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo); + +namespace mozilla { +namespace dom { + +class SVGDefsElement final : public SVGGraphicsElement +{ +protected: + friend nsresult (::NS_NewSVGDefsElement(nsIContent **aResult, + already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo)); + explicit SVGDefsElement(already_AddRefed<mozilla::dom::NodeInfo>& aNodeInfo); + virtual JSObject* WrapNode(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override; + +public: + // nsIContent + NS_IMETHOD_(bool) IsAttributeMapped(const nsIAtom* aAttribute) const override; + + virtual nsresult Clone(mozilla::dom::NodeInfo *aNodeInfo, nsINode **aResult) const override; +}; + +} // namespace dom +} // namespace mozilla + +#endif // mozilla_dom_SVGDefsElement_h diff --git a/dom/svg/SVGDescElement.cpp b/dom/svg/SVGDescElement.cpp new file mode 100644 index 0000000000..2a6e1434df --- /dev/null +++ b/dom/svg/SVGDescElement.cpp @@ -0,0 +1,36 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "mozilla/dom/SVGDescElement.h" +#include "mozilla/dom/SVGDescElementBinding.h" + +NS_IMPL_NS_NEW_NAMESPACED_SVG_ELEMENT(Desc) + +namespace mozilla { +namespace dom { + +JSObject* +SVGDescElement::WrapNode(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) +{ + return SVGDescElementBinding::Wrap(aCx, this, aGivenProto); +} + +//---------------------------------------------------------------------- +// Implementation + +SVGDescElement::SVGDescElement(already_AddRefed<mozilla::dom::NodeInfo>& aNodeInfo) + : SVGDescElementBase(aNodeInfo) +{ +} + +//---------------------------------------------------------------------- +// nsIDOMNode methods + +NS_IMPL_ELEMENT_CLONE_WITH_INIT(SVGDescElement) + +} // namespace dom +} // namespace mozilla + diff --git a/dom/svg/SVGDescElement.h b/dom/svg/SVGDescElement.h new file mode 100644 index 0000000000..8cf7171b62 --- /dev/null +++ b/dom/svg/SVGDescElement.h @@ -0,0 +1,38 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef mozilla_dom_SVGDescElement_h +#define mozilla_dom_SVGDescElement_h + +#include "mozilla/Attributes.h" +#include "nsSVGElement.h" + +nsresult NS_NewSVGDescElement(nsIContent **aResult, + already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo); + +typedef nsSVGElement SVGDescElementBase; + +namespace mozilla { +namespace dom { + +class SVGDescElement final : public SVGDescElementBase +{ +protected: + friend nsresult (::NS_NewSVGDescElement(nsIContent **aResult, + already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo)); + explicit SVGDescElement(already_AddRefed<mozilla::dom::NodeInfo>& aNodeInfo); + + virtual JSObject* WrapNode(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override; + +public: + virtual nsresult Clone(mozilla::dom::NodeInfo *aNodeInfo, nsINode **aResult) const override; +}; + +} // namespace dom +} // namespace mozilla + +#endif // mozilla_dom_SVGDescElement_h + diff --git a/dom/svg/SVGDocument.cpp b/dom/svg/SVGDocument.cpp new file mode 100644 index 0000000000..f992e52f5c --- /dev/null +++ b/dom/svg/SVGDocument.cpp @@ -0,0 +1,179 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "mozilla/dom/SVGDocument.h" + +#include "mozilla/css/Loader.h" +#include "nsICategoryManager.h" +#include "nsISimpleEnumerator.h" +#include "nsIStyleSheetService.h" +#include "nsISupportsPrimitives.h" +#include "nsLayoutStylesheetCache.h" +#include "nsNetUtil.h" +#include "nsServiceManagerUtils.h" +#include "nsString.h" +#include "nsLiteralString.h" +#include "nsIDOMSVGElement.h" +#include "mozilla/dom/Element.h" +#include "nsSVGElement.h" +#include "mozilla/StyleSheet.h" +#include "mozilla/StyleSheetInlines.h" + +using namespace mozilla::css; +using namespace mozilla::dom; + +namespace mozilla { +namespace dom { + +//---------------------------------------------------------------------- +// Implementation + +//---------------------------------------------------------------------- +// nsISupports methods: + +nsresult +SVGDocument::InsertChildAt(nsIContent* aKid, uint32_t aIndex, bool aNotify) +{ + if (aKid->IsElement() && !aKid->IsSVGElement()) { + // We can get here when well formed XML with a non-SVG root element is + // served with the SVG MIME type, for example. In that case we need to load + // the non-SVG UA sheets or else we can get bugs like bug 1016145. Note + // that we have to do this _before_ the XMLDocument::InsertChildAt call, + // since that can try to construct frames, and we need to have the sheets + // loaded by then. + EnsureNonSVGUserAgentStyleSheetsLoaded(); + } + + return XMLDocument::InsertChildAt(aKid, aIndex, aNotify); +} + +nsresult +SVGDocument::Clone(mozilla::dom::NodeInfo *aNodeInfo, nsINode **aResult) const +{ + NS_ASSERTION(aNodeInfo->NodeInfoManager() == mNodeInfoManager, + "Can't import this document into another document!"); + + RefPtr<SVGDocument> clone = new SVGDocument(); + nsresult rv = CloneDocHelper(clone.get()); + NS_ENSURE_SUCCESS(rv, rv); + + return CallQueryInterface(clone.get(), aResult); +} + +void +SVGDocument::EnsureNonSVGUserAgentStyleSheetsLoaded() +{ + if (mHasLoadedNonSVGUserAgentStyleSheets) { + return; + } + + if (IsStaticDocument()) { + // If we're a static clone of a document, then + // nsIDocument::CreateStaticClone will handle cloning the original + // document's sheets, including the on-demand non-SVG UA sheets, + // for us. + return; + } + + mHasLoadedNonSVGUserAgentStyleSheets = true; + + BeginUpdate(UPDATE_STYLE); + + if (IsBeingUsedAsImage()) { + // nsDocumentViewer::CreateStyleSet skipped loading all user-agent/user + // style sheets in this case, but we'll need B2G/Fennec's + // content.css. We could load all the sheets registered with the + // nsIStyleSheetService (and maybe we should) but most likely it isn't + // desirable or necessary for foreignObject in SVG-as-an-image. Instead we + // only load the "agent-style-sheets" that nsStyleSheetService::Init() + // pulls in from the category manager. That keeps memory use of + // SVG-as-an-image down. + // + // We do this before adding UASheet() etc. below because + // EnsureOnDemandBuiltInUASheet prepends, and B2G/Fennec's + // content.css must come after UASheet() etc. + nsCOMPtr<nsICategoryManager> catMan = + do_GetService(NS_CATEGORYMANAGER_CONTRACTID); + if (catMan) { + nsCOMPtr<nsISimpleEnumerator> sheets; + catMan->EnumerateCategory("agent-style-sheets", getter_AddRefs(sheets)); + if (sheets) { + bool hasMore; + while (NS_SUCCEEDED(sheets->HasMoreElements(&hasMore)) && hasMore) { + nsCOMPtr<nsISupports> sheet; + if (NS_FAILED(sheets->GetNext(getter_AddRefs(sheet)))) + break; + + nsCOMPtr<nsISupportsCString> icStr = do_QueryInterface(sheet); + MOZ_ASSERT(icStr, + "category manager entries must be nsISupportsCStrings"); + + nsAutoCString name; + icStr->GetData(name); + + nsXPIDLCString spec; + catMan->GetCategoryEntry("agent-style-sheets", name.get(), + getter_Copies(spec)); + + mozilla::css::Loader* cssLoader = CSSLoader(); + if (cssLoader->GetEnabled()) { + nsCOMPtr<nsIURI> uri; + NS_NewURI(getter_AddRefs(uri), spec); + if (uri) { + RefPtr<StyleSheet> sheet; + cssLoader->LoadSheetSync(uri, + mozilla::css::eAgentSheetFeatures, + true, &sheet); + if (sheet) { + EnsureOnDemandBuiltInUASheet(sheet); + } + } + } + } + } + } + } + + auto cache = nsLayoutStylesheetCache::For(GetStyleBackendType()); + + StyleSheet* sheet = cache->NumberControlSheet(); + if (sheet) { + // number-control.css can be behind a pref + EnsureOnDemandBuiltInUASheet(sheet); + } + EnsureOnDemandBuiltInUASheet(cache->FormsSheet()); + EnsureOnDemandBuiltInUASheet(cache->CounterStylesSheet()); + EnsureOnDemandBuiltInUASheet(cache->HTMLSheet()); + if (nsLayoutUtils::ShouldUseNoFramesSheet(this)) { + EnsureOnDemandBuiltInUASheet(cache->NoFramesSheet()); + } + if (nsLayoutUtils::ShouldUseNoScriptSheet(this)) { + EnsureOnDemandBuiltInUASheet(cache->NoScriptSheet()); + } + EnsureOnDemandBuiltInUASheet(cache->UASheet()); + + EndUpdate(UPDATE_STYLE); +} + +} // namespace dom +} // namespace mozilla + +//////////////////////////////////////////////////////////////////////// +// Exported creation functions + +nsresult +NS_NewSVGDocument(nsIDocument** aInstancePtrResult) +{ + RefPtr<SVGDocument> doc = new SVGDocument(); + + nsresult rv = doc->Init(); + if (NS_FAILED(rv)) { + return rv; + } + + doc.forget(aInstancePtrResult); + return rv; +} diff --git a/dom/svg/SVGDocument.h b/dom/svg/SVGDocument.h new file mode 100644 index 0000000000..5ec7143dae --- /dev/null +++ b/dom/svg/SVGDocument.h @@ -0,0 +1,49 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef mozilla_dom_SVGDocument_h +#define mozilla_dom_SVGDocument_h + +#include "mozilla/Attributes.h" +#include "mozilla/dom/XMLDocument.h" + +class nsSVGElement; + +namespace mozilla { +namespace dom { + +class SVGForeignObjectElement; + +class SVGDocument final : public XMLDocument +{ + friend class SVGForeignObjectElement; // To call EnsureNonSVGUserAgentStyleSheetsLoaded + +public: + SVGDocument() + : XMLDocument("image/svg+xml") + , mHasLoadedNonSVGUserAgentStyleSheets(false) + { + mType = eSVG; + } + + virtual nsresult InsertChildAt(nsIContent* aKid, uint32_t aIndex, + bool aNotify) override; + virtual nsresult Clone(mozilla::dom::NodeInfo *aNodeInfo, nsINode **aResult) const override; + + virtual SVGDocument* AsSVGDocument() override { + return this; + } + +private: + void EnsureNonSVGUserAgentStyleSheetsLoaded(); + + bool mHasLoadedNonSVGUserAgentStyleSheets; +}; + +} // namespace dom +} // namespace mozilla + +#endif // mozilla_dom_SVGDocument_h diff --git a/dom/svg/SVGElementFactory.cpp b/dom/svg/SVGElementFactory.cpp new file mode 100644 index 0000000000..abbb0a8651 --- /dev/null +++ b/dom/svg/SVGElementFactory.cpp @@ -0,0 +1,137 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "SVGElementFactory.h" +#include "nsGkAtoms.h" +#include "nsIContent.h" +#include "mozilla/dom/NodeInfo.h" +#include "mozilla/dom/Element.h" +#include "mozilla/dom/FromParser.h" + +using namespace mozilla; +using namespace mozilla::dom; + +// Hash table that maps nsIAtom* SVG tags to an offset index +// within the array sContentCreatorCallbacks (offset by TABLE_VALUE_OFFSET) +static PLHashTable* sTagAtomTable = nullptr; + +// We don't want to store 0 in the hash table as a return value of 0 from +// PL_HashTableLookupConst indicates that the value is not found +#define TABLE_VALUE_OFFSET 1 + +#define SVG_TAG(_tag, _classname) \ +nsresult \ +NS_NewSVG##_classname##Element(nsIContent** aResult, \ + already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo); \ +\ +static inline nsresult \ +Create##_classname##Element(nsIContent** aResult, \ + already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo, \ + FromParser aFromParser) \ +{ \ + return NS_NewSVG##_classname##Element(aResult, mozilla::Move(aNodeInfo)); \ +} + +#define SVG_FROM_PARSER_TAG(_tag, _classname) \ +nsresult \ +NS_NewSVG##_classname##Element(nsIContent** aResult, \ + already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo, \ + FromParser aFromParser); +#include "SVGTagList.h" +#undef SVG_TAG +#undef SVG_FROM_PARSER_TAG + +nsresult +NS_NewSVGElement(Element** aResult, + already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo); + +typedef nsresult + (*contentCreatorCallback)(nsIContent** aResult, + already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo, + FromParser aFromParser); + +static const contentCreatorCallback sContentCreatorCallbacks[] = { +#define SVG_TAG(_tag, _classname) Create##_classname##Element, +#define SVG_FROM_PARSER_TAG(_tag, _classname) NS_NewSVG##_classname##Element, +#include "SVGTagList.h" +#undef SVG_TAG +#undef SVG_FROM_PARSER_TAG +}; + +enum SVGTag { +#define SVG_TAG(_tag, _classname) eSVGTag_##_tag, +#define SVG_FROM_PARSER_TAG(_tag, _classname) eSVGTag_##_tag, +#include "SVGTagList.h" +#undef SVG_TAG +#undef SVG_FROM_PARSER_TAG + eSVGTag_Count +}; + +// nsIAtom* -> id hash +static PLHashNumber +SVGTagsHashCodeAtom(const void* key) +{ + return NS_PTR_TO_INT32(key) >> 2; +} + +void +SVGElementFactory::Init() +{ + sTagAtomTable = PL_NewHashTable(64, SVGTagsHashCodeAtom, + PL_CompareValues, PL_CompareValues, + nullptr, nullptr); + +#define SVG_TAG(_tag, _classname) \ + PL_HashTableAdd(sTagAtomTable, nsGkAtoms::_tag,\ + NS_INT32_TO_PTR(eSVGTag_##_tag + TABLE_VALUE_OFFSET)); +#define SVG_FROM_PARSER_TAG(_tag, _classname) \ + PL_HashTableAdd(sTagAtomTable, nsGkAtoms::_tag,\ + NS_INT32_TO_PTR(eSVGTag_##_tag + TABLE_VALUE_OFFSET)); +#include "SVGTagList.h" +#undef SVG_TAG +#undef SVG_FROM_PARSER_TAG +} + +void +SVGElementFactory::Shutdown() +{ + if (sTagAtomTable) { + PL_HashTableDestroy(sTagAtomTable); + sTagAtomTable = nullptr; + } +} + +nsresult +NS_NewSVGElement(Element** aResult, already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo, + FromParser aFromParser) +{ + NS_ASSERTION(sTagAtomTable, "no lookup table, needs SVGElementFactory::Init"); + + RefPtr<mozilla::dom::NodeInfo> ni = aNodeInfo; + nsIAtom* name = ni->NameAtom(); + + NS_ASSERTION(ni->NamespaceEquals(kNameSpaceID_SVG), + "Trying to create SVG elements that aren't in the SVG namespace"); + + void* tag = PL_HashTableLookupConst(sTagAtomTable, name); + if (tag) { + int32_t index = NS_PTR_TO_INT32(tag) - TABLE_VALUE_OFFSET; + if (index < 0 || index >= eSVGTag_Count) { + NS_WARNING("About to index out of array bounds - crashing instead"); + MOZ_CRASH(); + } + + contentCreatorCallback cb = sContentCreatorCallbacks[index]; + + nsCOMPtr<nsIContent> content; + nsresult rv = cb(getter_AddRefs(content), ni.forget(), aFromParser); + *aResult = content.forget().take()->AsElement(); + return rv; + } + + // if we don't know what to create, just create a standard svg element: + return NS_NewSVGElement(aResult, ni.forget()); +} diff --git a/dom/svg/SVGElementFactory.h b/dom/svg/SVGElementFactory.h new file mode 100644 index 0000000000..3a75ef750b --- /dev/null +++ b/dom/svg/SVGElementFactory.h @@ -0,0 +1,24 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef mozilla_dom_SVGElementFactory_h +#define mozilla_dom_SVGElementFactory_h + +class nsIAtom; + +namespace mozilla { +namespace dom { + +class SVGElementFactory { +public: + static void Init(); + static void Shutdown(); +}; + +} // namespace dom +} // namespace mozilla + +#endif /* mozilla_dom_SVGElementFactory_h */ diff --git a/dom/svg/SVGEllipseElement.cpp b/dom/svg/SVGEllipseElement.cpp new file mode 100644 index 0000000000..f841e9646a --- /dev/null +++ b/dom/svg/SVGEllipseElement.cpp @@ -0,0 +1,153 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "mozilla/dom/SVGEllipseElement.h" +#include "mozilla/dom/SVGEllipseElementBinding.h" +#include "mozilla/gfx/2D.h" +#include "mozilla/gfx/PathHelpers.h" +#include "mozilla/RefPtr.h" + +NS_IMPL_NS_NEW_NAMESPACED_SVG_ELEMENT(Ellipse) + +using namespace mozilla::gfx; + +namespace mozilla { +namespace dom { + +JSObject* +SVGEllipseElement::WrapNode(JSContext *aCx, JS::Handle<JSObject*> aGivenProto) +{ + return SVGEllipseElementBinding::Wrap(aCx, this, aGivenProto); +} + +nsSVGElement::LengthInfo SVGEllipseElement::sLengthInfo[4] = +{ + { &nsGkAtoms::cx, 0, nsIDOMSVGLength::SVG_LENGTHTYPE_NUMBER, SVGContentUtils::X }, + { &nsGkAtoms::cy, 0, nsIDOMSVGLength::SVG_LENGTHTYPE_NUMBER, SVGContentUtils::Y }, + { &nsGkAtoms::rx, 0, nsIDOMSVGLength::SVG_LENGTHTYPE_NUMBER, SVGContentUtils::X }, + { &nsGkAtoms::ry, 0, nsIDOMSVGLength::SVG_LENGTHTYPE_NUMBER, SVGContentUtils::Y }, +}; + +//---------------------------------------------------------------------- +// Implementation + +SVGEllipseElement::SVGEllipseElement(already_AddRefed<mozilla::dom::NodeInfo>& aNodeInfo) + : SVGEllipseElementBase(aNodeInfo) +{ +} + +//---------------------------------------------------------------------- +// nsIDOMNode methods + +NS_IMPL_ELEMENT_CLONE_WITH_INIT(SVGEllipseElement) + +//---------------------------------------------------------------------- +// nsIDOMSVGEllipseElement methods + +already_AddRefed<SVGAnimatedLength> +SVGEllipseElement::Cx() +{ + return mLengthAttributes[CX].ToDOMAnimatedLength(this); +} + +already_AddRefed<SVGAnimatedLength> +SVGEllipseElement::Cy() +{ + return mLengthAttributes[CY].ToDOMAnimatedLength(this); +} + +already_AddRefed<SVGAnimatedLength> +SVGEllipseElement::Rx() +{ + return mLengthAttributes[RX].ToDOMAnimatedLength(this); +} + +already_AddRefed<SVGAnimatedLength> +SVGEllipseElement::Ry() +{ + return mLengthAttributes[RY].ToDOMAnimatedLength(this); +} + +//---------------------------------------------------------------------- +// nsSVGElement methods + +/* virtual */ bool +SVGEllipseElement::HasValidDimensions() const +{ + return mLengthAttributes[RX].IsExplicitlySet() && + mLengthAttributes[RX].GetAnimValInSpecifiedUnits() > 0 && + mLengthAttributes[RY].IsExplicitlySet() && + mLengthAttributes[RY].GetAnimValInSpecifiedUnits() > 0; +} + +nsSVGElement::LengthAttributesInfo +SVGEllipseElement::GetLengthInfo() +{ + return LengthAttributesInfo(mLengthAttributes, sLengthInfo, + ArrayLength(sLengthInfo)); +} + +//---------------------------------------------------------------------- +// nsSVGPathGeometryElement methods + +bool +SVGEllipseElement::GetGeometryBounds(Rect* aBounds, + const StrokeOptions& aStrokeOptions, + const Matrix& aToBoundsSpace, + const Matrix* aToNonScalingStrokeSpace) +{ + float x, y, rx, ry; + GetAnimatedLengthValues(&x, &y, &rx, &ry, nullptr); + + if (rx <= 0.f || ry <= 0.f) { + // Rendering of the element is disabled + *aBounds = Rect(aToBoundsSpace.TransformPoint(Point(x, y)), Size()); + return true; + } + + if (aToBoundsSpace.IsRectilinear()) { + // Optimize the case where we can treat the ellipse as a rectangle and + // still get tight bounds. + if (aStrokeOptions.mLineWidth > 0.f) { + if (aToNonScalingStrokeSpace) { + if (aToNonScalingStrokeSpace->IsRectilinear()) { + MOZ_ASSERT(!aToNonScalingStrokeSpace->IsSingular()); + Rect userBounds(x - rx, y - ry, 2 * rx, 2 * ry); + SVGContentUtils::RectilinearGetStrokeBounds( + userBounds, aToBoundsSpace, *aToNonScalingStrokeSpace, + aStrokeOptions.mLineWidth, aBounds); + return true; + } + return false; + } + rx += aStrokeOptions.mLineWidth / 2.f; + ry += aStrokeOptions.mLineWidth / 2.f; + } + Rect rect(x - rx, y - ry, 2 * rx, 2 * ry); + *aBounds = aToBoundsSpace.TransformBounds(rect); + return true; + } + + return false; +} + +already_AddRefed<Path> +SVGEllipseElement::BuildPath(PathBuilder* aBuilder) +{ + float x, y, rx, ry; + GetAnimatedLengthValues(&x, &y, &rx, &ry, nullptr); + + if (rx <= 0.0f || ry <= 0.0f) { + return nullptr; + } + + EllipseToBezier(aBuilder, Point(x, y), Size(rx, ry)); + + return aBuilder->Finish(); +} + +} // namespace dom +} // namespace mozilla diff --git a/dom/svg/SVGEllipseElement.h b/dom/svg/SVGEllipseElement.h new file mode 100644 index 0000000000..27c132fd96 --- /dev/null +++ b/dom/svg/SVGEllipseElement.h @@ -0,0 +1,59 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef mozilla_dom_SVGEllipseElement_h +#define mozilla_dom_SVGEllipseElement_h + +#include "nsSVGPathGeometryElement.h" +#include "nsSVGLength2.h" + +nsresult NS_NewSVGEllipseElement(nsIContent **aResult, + already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo); + +namespace mozilla { +namespace dom { + +typedef nsSVGPathGeometryElement SVGEllipseElementBase; + +class SVGEllipseElement final : public SVGEllipseElementBase +{ +protected: + explicit SVGEllipseElement(already_AddRefed<mozilla::dom::NodeInfo>& aNodeInfo); + virtual JSObject* WrapNode(JSContext *cx, JS::Handle<JSObject*> aGivenProto) override; + friend nsresult (::NS_NewSVGEllipseElement(nsIContent **aResult, + already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo)); + +public: + // nsSVGSVGElement methods: + virtual bool HasValidDimensions() const override; + + // nsSVGPathGeometryElement methods: + virtual bool GetGeometryBounds(Rect* aBounds, const StrokeOptions& aStrokeOptions, + const Matrix& aToBoundsSpace, + const Matrix* aToNonScalingStrokeSpace = nullptr) override; + virtual already_AddRefed<Path> BuildPath(PathBuilder* aBuilder) override; + + virtual nsresult Clone(mozilla::dom::NodeInfo *aNodeInfo, nsINode **aResult) const override; + + // WebIDL + already_AddRefed<SVGAnimatedLength> Cx(); + already_AddRefed<SVGAnimatedLength> Cy(); + already_AddRefed<SVGAnimatedLength> Rx(); + already_AddRefed<SVGAnimatedLength> Ry(); + +protected: + + virtual LengthAttributesInfo GetLengthInfo() override; + + enum { CX, CY, RX, RY }; + nsSVGLength2 mLengthAttributes[4]; + static LengthInfo sLengthInfo[4]; +}; + +} // namespace dom +} // namespace mozilla + +#endif // mozilla_dom_SVGEllipseElement_h diff --git a/dom/svg/SVGFEBlendElement.cpp b/dom/svg/SVGFEBlendElement.cpp new file mode 100644 index 0000000000..7fea66b611 --- /dev/null +++ b/dom/svg/SVGFEBlendElement.cpp @@ -0,0 +1,133 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "mozilla/dom/SVGFEBlendElement.h" +#include "mozilla/dom/SVGFEBlendElementBinding.h" +#include "nsSVGUtils.h" + +NS_IMPL_NS_NEW_NAMESPACED_SVG_ELEMENT(FEBlend) + +using namespace mozilla::gfx; + +namespace mozilla { +namespace dom { + +JSObject* +SVGFEBlendElement::WrapNode(JSContext *aCx, JS::Handle<JSObject*> aGivenProto) +{ + return SVGFEBlendElementBinding::Wrap(aCx, this, aGivenProto); +} + +nsSVGEnumMapping SVGFEBlendElement::sModeMap[] = { + {&nsGkAtoms::normal, SVG_FEBLEND_MODE_NORMAL}, + {&nsGkAtoms::multiply, SVG_FEBLEND_MODE_MULTIPLY}, + {&nsGkAtoms::screen, SVG_FEBLEND_MODE_SCREEN}, + {&nsGkAtoms::darken, SVG_FEBLEND_MODE_DARKEN}, + {&nsGkAtoms::lighten, SVG_FEBLEND_MODE_LIGHTEN}, + {&nsGkAtoms::overlay, SVG_FEBLEND_MODE_OVERLAY}, + {&nsGkAtoms::colorDodge, SVG_FEBLEND_MODE_COLOR_DODGE}, + {&nsGkAtoms::colorBurn, SVG_FEBLEND_MODE_COLOR_BURN}, + {&nsGkAtoms::hardLight, SVG_FEBLEND_MODE_HARD_LIGHT}, + {&nsGkAtoms::softLight, SVG_FEBLEND_MODE_SOFT_LIGHT}, + {&nsGkAtoms::difference, SVG_FEBLEND_MODE_DIFFERENCE}, + {&nsGkAtoms::exclusion, SVG_FEBLEND_MODE_EXCLUSION}, + {&nsGkAtoms::hue, SVG_FEBLEND_MODE_HUE}, + {&nsGkAtoms::saturation, SVG_FEBLEND_MODE_SATURATION}, + {&nsGkAtoms::color, SVG_FEBLEND_MODE_COLOR}, + {&nsGkAtoms::luminosity, SVG_FEBLEND_MODE_LUMINOSITY}, + {nullptr, 0} +}; + +nsSVGElement::EnumInfo SVGFEBlendElement::sEnumInfo[1] = +{ + { &nsGkAtoms::mode, + sModeMap, + SVG_FEBLEND_MODE_NORMAL + } +}; + +nsSVGElement::StringInfo SVGFEBlendElement::sStringInfo[3] = +{ + { &nsGkAtoms::result, kNameSpaceID_None, true }, + { &nsGkAtoms::in, kNameSpaceID_None, true }, + { &nsGkAtoms::in2, kNameSpaceID_None, true } +}; + +//---------------------------------------------------------------------- +// nsIDOMNode methods + +NS_IMPL_ELEMENT_CLONE_WITH_INIT(SVGFEBlendElement) + +//---------------------------------------------------------------------- +// nsIDOMSVGFEBlendElement methods + +already_AddRefed<SVGAnimatedString> +SVGFEBlendElement::In1() +{ + return mStringAttributes[IN1].ToDOMAnimatedString(this); +} + +already_AddRefed<SVGAnimatedString> +SVGFEBlendElement::In2() +{ + return mStringAttributes[IN2].ToDOMAnimatedString(this); +} + +already_AddRefed<SVGAnimatedEnumeration> +SVGFEBlendElement::Mode() +{ + return mEnumAttributes[MODE].ToDOMAnimatedEnum(this); +} + +FilterPrimitiveDescription +SVGFEBlendElement::GetPrimitiveDescription(nsSVGFilterInstance* aInstance, + const IntRect& aFilterSubregion, + const nsTArray<bool>& aInputsAreTainted, + nsTArray<RefPtr<SourceSurface>>& aInputImages) +{ + uint32_t mode = mEnumAttributes[MODE].GetAnimValue(); + FilterPrimitiveDescription descr(PrimitiveType::Blend); + descr.Attributes().Set(eBlendBlendmode, mode); + return descr; +} + +bool +SVGFEBlendElement::AttributeAffectsRendering(int32_t aNameSpaceID, + nsIAtom* aAttribute) const +{ + return SVGFEBlendElementBase::AttributeAffectsRendering(aNameSpaceID, aAttribute) || + (aNameSpaceID == kNameSpaceID_None && + (aAttribute == nsGkAtoms::in || + aAttribute == nsGkAtoms::in2 || + aAttribute == nsGkAtoms::mode)); +} + +void +SVGFEBlendElement::GetSourceImageNames(nsTArray<nsSVGStringInfo>& aSources) +{ + aSources.AppendElement(nsSVGStringInfo(&mStringAttributes[IN1], this)); + aSources.AppendElement(nsSVGStringInfo(&mStringAttributes[IN2], this)); +} + +//---------------------------------------------------------------------- +// nsSVGElement methods + +nsSVGElement::EnumAttributesInfo +SVGFEBlendElement::GetEnumInfo() +{ + return EnumAttributesInfo(mEnumAttributes, sEnumInfo, + ArrayLength(sEnumInfo)); +} + +nsSVGElement::StringAttributesInfo +SVGFEBlendElement::GetStringInfo() +{ + return StringAttributesInfo(mStringAttributes, sStringInfo, + ArrayLength(sStringInfo)); +} + +} // namespace dom +} // namespace mozilla diff --git a/dom/svg/SVGFEBlendElement.h b/dom/svg/SVGFEBlendElement.h new file mode 100644 index 0000000000..3c5c84523f --- /dev/null +++ b/dom/svg/SVGFEBlendElement.h @@ -0,0 +1,67 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef mozilla_dom_SVGFEBlendElement_h +#define mozilla_dom_SVGFEBlendElement_h + +#include "nsSVGFilters.h" +#include "nsSVGEnum.h" + +nsresult NS_NewSVGFEBlendElement(nsIContent **aResult, + already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo); +namespace mozilla { +namespace dom { + +typedef nsSVGFE SVGFEBlendElementBase; + +class SVGFEBlendElement : public SVGFEBlendElementBase +{ + friend nsresult (::NS_NewSVGFEBlendElement(nsIContent **aResult, + already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo)); +protected: + explicit SVGFEBlendElement(already_AddRefed<mozilla::dom::NodeInfo>& aNodeInfo) + : SVGFEBlendElementBase(aNodeInfo) + { + } + virtual JSObject* WrapNode(JSContext *cx, JS::Handle<JSObject*> aGivenProto) override; + +public: + virtual FilterPrimitiveDescription + GetPrimitiveDescription(nsSVGFilterInstance* aInstance, + const IntRect& aFilterSubregion, + const nsTArray<bool>& aInputsAreTainted, + nsTArray<RefPtr<SourceSurface>>& aInputImages) override; + virtual bool AttributeAffectsRendering( + int32_t aNameSpaceID, nsIAtom* aAttribute) const override; + virtual nsSVGString& GetResultImageName() override { return mStringAttributes[RESULT]; } + virtual void GetSourceImageNames(nsTArray<nsSVGStringInfo>& aSources) override; + + virtual nsresult Clone(mozilla::dom::NodeInfo *aNodeInfo, nsINode **aResult) const override; + + // WebIDL + already_AddRefed<SVGAnimatedString> In1(); + already_AddRefed<SVGAnimatedString> In2(); + already_AddRefed<SVGAnimatedEnumeration> Mode(); + +protected: + + virtual EnumAttributesInfo GetEnumInfo() override; + virtual StringAttributesInfo GetStringInfo() override; + + enum { MODE }; + nsSVGEnum mEnumAttributes[1]; + static nsSVGEnumMapping sModeMap[]; + static EnumInfo sEnumInfo[1]; + + enum { RESULT, IN1, IN2 }; + nsSVGString mStringAttributes[3]; + static StringInfo sStringInfo[3]; +}; + +} // namespace dom +} // namespace mozilla + +#endif // mozilla_dom_SVGFEBlendElement_h diff --git a/dom/svg/SVGFEColorMatrixElement.cpp b/dom/svg/SVGFEColorMatrixElement.cpp new file mode 100644 index 0000000000..0402a6dea2 --- /dev/null +++ b/dom/svg/SVGFEColorMatrixElement.cpp @@ -0,0 +1,156 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "DOMSVGAnimatedNumberList.h" +#include "mozilla/dom/SVGFEColorMatrixElement.h" +#include "mozilla/dom/SVGFEColorMatrixElementBinding.h" +#include "nsSVGUtils.h" + +#define NUM_ENTRIES_IN_4x5_MATRIX 20 + +NS_IMPL_NS_NEW_NAMESPACED_SVG_ELEMENT(FEColorMatrix) + +using namespace mozilla::gfx; + +namespace mozilla { +namespace dom { + +JSObject* +SVGFEColorMatrixElement::WrapNode(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) +{ + return SVGFEColorMatrixElementBinding::Wrap(aCx, this, aGivenProto); +} + +nsSVGEnumMapping SVGFEColorMatrixElement::sTypeMap[] = { + {&nsGkAtoms::matrix, SVG_FECOLORMATRIX_TYPE_MATRIX}, + {&nsGkAtoms::saturate, SVG_FECOLORMATRIX_TYPE_SATURATE}, + {&nsGkAtoms::hueRotate, SVG_FECOLORMATRIX_TYPE_HUE_ROTATE}, + {&nsGkAtoms::luminanceToAlpha, SVG_FECOLORMATRIX_TYPE_LUMINANCE_TO_ALPHA}, + {nullptr, 0} +}; + +nsSVGElement::EnumInfo SVGFEColorMatrixElement::sEnumInfo[1] = +{ + { &nsGkAtoms::type, + sTypeMap, + SVG_FECOLORMATRIX_TYPE_MATRIX + } +}; + +nsSVGElement::StringInfo SVGFEColorMatrixElement::sStringInfo[2] = +{ + { &nsGkAtoms::result, kNameSpaceID_None, true }, + { &nsGkAtoms::in, kNameSpaceID_None, true } +}; + +nsSVGElement::NumberListInfo SVGFEColorMatrixElement::sNumberListInfo[1] = +{ + { &nsGkAtoms::values } +}; + +//---------------------------------------------------------------------- +// nsIDOMNode methods + + +NS_IMPL_ELEMENT_CLONE_WITH_INIT(SVGFEColorMatrixElement) + + +//---------------------------------------------------------------------- + +already_AddRefed<SVGAnimatedString> +SVGFEColorMatrixElement::In1() +{ + return mStringAttributes[IN1].ToDOMAnimatedString(this); +} + +already_AddRefed<SVGAnimatedEnumeration> +SVGFEColorMatrixElement::Type() +{ + return mEnumAttributes[TYPE].ToDOMAnimatedEnum(this); +} + +already_AddRefed<DOMSVGAnimatedNumberList> +SVGFEColorMatrixElement::Values() +{ + return DOMSVGAnimatedNumberList::GetDOMWrapper(&mNumberListAttributes[VALUES], + this, VALUES); +} + +void +SVGFEColorMatrixElement::GetSourceImageNames(nsTArray<nsSVGStringInfo>& aSources) +{ + aSources.AppendElement(nsSVGStringInfo(&mStringAttributes[IN1], this)); +} + +FilterPrimitiveDescription +SVGFEColorMatrixElement::GetPrimitiveDescription(nsSVGFilterInstance* aInstance, + const IntRect& aFilterSubregion, + const nsTArray<bool>& aInputsAreTainted, + nsTArray<RefPtr<SourceSurface>>& aInputImages) +{ + uint32_t type = mEnumAttributes[TYPE].GetAnimValue(); + const SVGNumberList &values = mNumberListAttributes[VALUES].GetAnimValue(); + + FilterPrimitiveDescription descr(PrimitiveType::ColorMatrix); + if (!mNumberListAttributes[VALUES].IsExplicitlySet() && + (type == SVG_FECOLORMATRIX_TYPE_MATRIX || + type == SVG_FECOLORMATRIX_TYPE_SATURATE || + type == SVG_FECOLORMATRIX_TYPE_HUE_ROTATE)) { + descr.Attributes().Set(eColorMatrixType, (uint32_t)SVG_FECOLORMATRIX_TYPE_MATRIX); + static const float identityMatrix[] = + { 1, 0, 0, 0, 0, + 0, 1, 0, 0, 0, + 0, 0, 1, 0, 0, + 0, 0, 0, 1, 0 }; + descr.Attributes().Set(eColorMatrixValues, identityMatrix, 20); + } else { + descr.Attributes().Set(eColorMatrixType, type); + if (values.Length()) { + descr.Attributes().Set(eColorMatrixValues, &values[0], values.Length()); + } else { + descr.Attributes().Set(eColorMatrixValues, nullptr, 0); + } + } + return descr; +} + +bool +SVGFEColorMatrixElement::AttributeAffectsRendering(int32_t aNameSpaceID, + nsIAtom* aAttribute) const +{ + return SVGFEColorMatrixElementBase::AttributeAffectsRendering(aNameSpaceID, aAttribute) || + (aNameSpaceID == kNameSpaceID_None && + (aAttribute == nsGkAtoms::in || + aAttribute == nsGkAtoms::type || + aAttribute == nsGkAtoms::values)); +} + +//---------------------------------------------------------------------- +// nsSVGElement methods + +nsSVGElement::EnumAttributesInfo +SVGFEColorMatrixElement::GetEnumInfo() +{ + return EnumAttributesInfo(mEnumAttributes, sEnumInfo, + ArrayLength(sEnumInfo)); +} + +nsSVGElement::StringAttributesInfo +SVGFEColorMatrixElement::GetStringInfo() +{ + return StringAttributesInfo(mStringAttributes, sStringInfo, + ArrayLength(sStringInfo)); +} + +nsSVGElement::NumberListAttributesInfo +SVGFEColorMatrixElement::GetNumberListInfo() +{ + return NumberListAttributesInfo(mNumberListAttributes, sNumberListInfo, + ArrayLength(sNumberListInfo)); +} + +} // namespace dom +} // namespace mozilla diff --git a/dom/svg/SVGFEColorMatrixElement.h b/dom/svg/SVGFEColorMatrixElement.h new file mode 100644 index 0000000000..54f290964f --- /dev/null +++ b/dom/svg/SVGFEColorMatrixElement.h @@ -0,0 +1,73 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef mozilla_dom_SVGFEColorMatrixElement_h +#define mozilla_dom_SVGFEColorMatrixElement_h + +#include "nsSVGEnum.h" +#include "nsSVGFilters.h" +#include "SVGAnimatedNumberList.h" + +nsresult NS_NewSVGFEColorMatrixElement(nsIContent **aResult, + already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo); + +namespace mozilla { +namespace dom { + +typedef nsSVGFE SVGFEColorMatrixElementBase; + +class SVGFEColorMatrixElement : public SVGFEColorMatrixElementBase +{ + friend nsresult (::NS_NewSVGFEColorMatrixElement(nsIContent **aResult, + already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo)); +protected: + explicit SVGFEColorMatrixElement(already_AddRefed<mozilla::dom::NodeInfo>& aNodeInfo) + : SVGFEColorMatrixElementBase(aNodeInfo) + { + } + virtual JSObject* WrapNode(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override; + +public: + virtual FilterPrimitiveDescription + GetPrimitiveDescription(nsSVGFilterInstance* aInstance, + const IntRect& aFilterSubregion, + const nsTArray<bool>& aInputsAreTainted, + nsTArray<RefPtr<SourceSurface>>& aInputImages) override; + virtual bool AttributeAffectsRendering( + int32_t aNameSpaceID, nsIAtom* aAttribute) const override; + virtual nsSVGString& GetResultImageName() override { return mStringAttributes[RESULT]; } + virtual void GetSourceImageNames(nsTArray<nsSVGStringInfo>& aSources) override; + + virtual nsresult Clone(mozilla::dom::NodeInfo *aNodeInfo, nsINode **aResult) const override; + + // WebIDL + already_AddRefed<SVGAnimatedString> In1(); + already_AddRefed<SVGAnimatedEnumeration> Type(); + already_AddRefed<DOMSVGAnimatedNumberList> Values(); + + protected: + virtual EnumAttributesInfo GetEnumInfo() override; + virtual StringAttributesInfo GetStringInfo() override; + virtual NumberListAttributesInfo GetNumberListInfo() override; + + enum { TYPE }; + nsSVGEnum mEnumAttributes[1]; + static nsSVGEnumMapping sTypeMap[]; + static EnumInfo sEnumInfo[1]; + + enum { RESULT, IN1 }; + nsSVGString mStringAttributes[2]; + static StringInfo sStringInfo[2]; + + enum { VALUES }; + SVGAnimatedNumberList mNumberListAttributes[1]; + static NumberListInfo sNumberListInfo[1]; +}; + +} // namespace dom +} // namespace mozilla + +#endif // mozilla_dom_SVGFEColorMatrixElement_h diff --git a/dom/svg/SVGFEComponentTransferElement.cpp b/dom/svg/SVGFEComponentTransferElement.cpp new file mode 100644 index 0000000000..25764f4181 --- /dev/null +++ b/dom/svg/SVGFEComponentTransferElement.cpp @@ -0,0 +1,112 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "mozilla/dom/SVGComponentTransferFunctionElement.h" +#include "mozilla/dom/SVGFEComponentTransferElement.h" +#include "mozilla/dom/SVGFEComponentTransferElementBinding.h" +#include "nsSVGUtils.h" +#include "mozilla/gfx/2D.h" + +NS_IMPL_NS_NEW_NAMESPACED_SVG_ELEMENT(FEComponentTransfer) + +using namespace mozilla::gfx;; + +namespace mozilla { +namespace dom { + +JSObject* +SVGFEComponentTransferElement::WrapNode(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) +{ + return SVGFEComponentTransferElementBinding::Wrap(aCx, this, aGivenProto); +} + +nsSVGElement::StringInfo SVGFEComponentTransferElement::sStringInfo[2] = +{ + { &nsGkAtoms::result, kNameSpaceID_None, true }, + { &nsGkAtoms::in, kNameSpaceID_None, true } +}; + +//---------------------------------------------------------------------- +// nsIDOMNode methods + +NS_IMPL_ELEMENT_CLONE_WITH_INIT(SVGFEComponentTransferElement) + +already_AddRefed<SVGAnimatedString> +SVGFEComponentTransferElement::In1() +{ + return mStringAttributes[IN1].ToDOMAnimatedString(this); +} + +//---------------------------------------------------------------------- +// nsSVGElement methods + +nsSVGElement::StringAttributesInfo +SVGFEComponentTransferElement::GetStringInfo() +{ + return StringAttributesInfo(mStringAttributes, sStringInfo, + ArrayLength(sStringInfo)); +} + +//-------------------------------------------- + +FilterPrimitiveDescription +SVGFEComponentTransferElement::GetPrimitiveDescription(nsSVGFilterInstance* aInstance, + const IntRect& aFilterSubregion, + const nsTArray<bool>& aInputsAreTainted, + nsTArray<RefPtr<SourceSurface>>& aInputImages) +{ + RefPtr<SVGComponentTransferFunctionElement> childForChannel[4]; + + for (nsIContent* childContent = nsINode::GetFirstChild(); + childContent; + childContent = childContent->GetNextSibling()) { + + RefPtr<SVGComponentTransferFunctionElement> child; + CallQueryInterface(childContent, + (SVGComponentTransferFunctionElement**)getter_AddRefs(child)); + if (child) { + childForChannel[child->GetChannel()] = child; + } + } + + static const AttributeName attributeNames[4] = { + eComponentTransferFunctionR, + eComponentTransferFunctionG, + eComponentTransferFunctionB, + eComponentTransferFunctionA + }; + + FilterPrimitiveDescription descr(PrimitiveType::ComponentTransfer); + for (int32_t i = 0; i < 4; i++) { + if (childForChannel[i]) { + descr.Attributes().Set(attributeNames[i], childForChannel[i]->ComputeAttributes()); + } else { + AttributeMap functionAttributes; + functionAttributes.Set(eComponentTransferFunctionType, + (uint32_t)SVG_FECOMPONENTTRANSFER_TYPE_IDENTITY); + descr.Attributes().Set(attributeNames[i], functionAttributes); + } + } + return descr; +} + +bool +SVGFEComponentTransferElement::AttributeAffectsRendering(int32_t aNameSpaceID, + nsIAtom* aAttribute) const +{ + return SVGFEComponentTransferElementBase::AttributeAffectsRendering(aNameSpaceID, aAttribute) || + (aNameSpaceID == kNameSpaceID_None && + aAttribute == nsGkAtoms::in); +} + +void +SVGFEComponentTransferElement::GetSourceImageNames(nsTArray<nsSVGStringInfo>& aSources) +{ + aSources.AppendElement(nsSVGStringInfo(&mStringAttributes[IN1], this)); +} + +} // namespace dom +} // namespace mozilla diff --git a/dom/svg/SVGFEComponentTransferElement.h b/dom/svg/SVGFEComponentTransferElement.h new file mode 100644 index 0000000000..7881e8ced7 --- /dev/null +++ b/dom/svg/SVGFEComponentTransferElement.h @@ -0,0 +1,59 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef mozilla_dom_SVGFEComponentTransferElement_h +#define mozilla_dom_SVGFEComponentTransferElement_h + +#include "nsSVGFilters.h" + +typedef nsSVGFE SVGFEComponentTransferElementBase; + +nsresult NS_NewSVGFEComponentTransferElement(nsIContent **aResult, + already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo); + +namespace mozilla { +namespace dom { + +class SVGFEComponentTransferElement : public SVGFEComponentTransferElementBase +{ + friend nsresult (::NS_NewSVGFEComponentTransferElement(nsIContent **aResult, + already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo)); +protected: + explicit SVGFEComponentTransferElement(already_AddRefed<mozilla::dom::NodeInfo>& aNodeInfo) + : SVGFEComponentTransferElementBase(aNodeInfo) + { + } + virtual JSObject* WrapNode(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override; + +public: + virtual FilterPrimitiveDescription + GetPrimitiveDescription(nsSVGFilterInstance* aInstance, + const IntRect& aFilterSubregion, + const nsTArray<bool>& aInputsAreTainted, + nsTArray<RefPtr<SourceSurface>>& aInputImages) override; + virtual bool AttributeAffectsRendering( + int32_t aNameSpaceID, nsIAtom* aAttribute) const override; + virtual nsSVGString& GetResultImageName() override { return mStringAttributes[RESULT]; } + virtual void GetSourceImageNames(nsTArray<nsSVGStringInfo>& aSources) override; + + // nsIContent + virtual nsresult Clone(mozilla::dom::NodeInfo *aNodeInfo, nsINode **aResult) const override; + + // WebIDL + already_AddRefed<SVGAnimatedString> In1(); + +protected: + virtual StringAttributesInfo GetStringInfo() override; + + enum { RESULT, IN1 }; + nsSVGString mStringAttributes[2]; + static StringInfo sStringInfo[2]; +}; + +} // namespace dom +} // namespace mozilla + +#endif // mozilla_dom_SVGFEComponentTransferElement_h diff --git a/dom/svg/SVGFECompositeElement.cpp b/dom/svg/SVGFECompositeElement.cpp new file mode 100644 index 0000000000..07a1e2934c --- /dev/null +++ b/dom/svg/SVGFECompositeElement.cpp @@ -0,0 +1,178 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "mozilla/dom/SVGFECompositeElement.h" +#include "mozilla/dom/SVGFECompositeElementBinding.h" + +NS_IMPL_NS_NEW_NAMESPACED_SVG_ELEMENT(FEComposite) + +using namespace mozilla::gfx; + +namespace mozilla { +namespace dom { + +JSObject* +SVGFECompositeElement::WrapNode(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) +{ + return SVGFECompositeElementBinding::Wrap(aCx, this, aGivenProto); +} + +nsSVGElement::NumberInfo SVGFECompositeElement::sNumberInfo[4] = +{ + { &nsGkAtoms::k1, 0, false }, + { &nsGkAtoms::k2, 0, false }, + { &nsGkAtoms::k3, 0, false }, + { &nsGkAtoms::k4, 0, false } +}; + +nsSVGEnumMapping SVGFECompositeElement::sOperatorMap[] = { + {&nsGkAtoms::over, SVG_FECOMPOSITE_OPERATOR_OVER}, + {&nsGkAtoms::in, SVG_FECOMPOSITE_OPERATOR_IN}, + {&nsGkAtoms::out, SVG_FECOMPOSITE_OPERATOR_OUT}, + {&nsGkAtoms::atop, SVG_FECOMPOSITE_OPERATOR_ATOP}, + {&nsGkAtoms::xor_, SVG_FECOMPOSITE_OPERATOR_XOR}, + {&nsGkAtoms::arithmetic, SVG_FECOMPOSITE_OPERATOR_ARITHMETIC}, + {nullptr, 0} +}; + +nsSVGElement::EnumInfo SVGFECompositeElement::sEnumInfo[1] = +{ + { &nsGkAtoms::_operator, + sOperatorMap, + SVG_FECOMPOSITE_OPERATOR_OVER + } +}; + +nsSVGElement::StringInfo SVGFECompositeElement::sStringInfo[3] = +{ + { &nsGkAtoms::result, kNameSpaceID_None, true }, + { &nsGkAtoms::in, kNameSpaceID_None, true }, + { &nsGkAtoms::in2, kNameSpaceID_None, true } +}; + +//---------------------------------------------------------------------- +// nsIDOMNode methods + +NS_IMPL_ELEMENT_CLONE_WITH_INIT(SVGFECompositeElement) + +already_AddRefed<SVGAnimatedString> +SVGFECompositeElement::In1() +{ + return mStringAttributes[IN1].ToDOMAnimatedString(this); +} + +already_AddRefed<SVGAnimatedString> +SVGFECompositeElement::In2() +{ + return mStringAttributes[IN2].ToDOMAnimatedString(this); +} + +already_AddRefed<SVGAnimatedEnumeration> +SVGFECompositeElement::Operator() +{ + return mEnumAttributes[OPERATOR].ToDOMAnimatedEnum(this); +} + +already_AddRefed<SVGAnimatedNumber> +SVGFECompositeElement::K1() +{ + return mNumberAttributes[ATTR_K1].ToDOMAnimatedNumber(this); +} + +already_AddRefed<SVGAnimatedNumber> +SVGFECompositeElement::K2() +{ + return mNumberAttributes[ATTR_K2].ToDOMAnimatedNumber(this); +} + +already_AddRefed<SVGAnimatedNumber> +SVGFECompositeElement::K3() +{ + return mNumberAttributes[ATTR_K3].ToDOMAnimatedNumber(this); +} + +already_AddRefed<SVGAnimatedNumber> +SVGFECompositeElement::K4() +{ + return mNumberAttributes[ATTR_K4].ToDOMAnimatedNumber(this); +} + +void +SVGFECompositeElement::SetK(float k1, float k2, float k3, float k4) +{ + mNumberAttributes[ATTR_K1].SetBaseValue(k1, this); + mNumberAttributes[ATTR_K2].SetBaseValue(k2, this); + mNumberAttributes[ATTR_K3].SetBaseValue(k3, this); + mNumberAttributes[ATTR_K4].SetBaseValue(k4, this); +} + +FilterPrimitiveDescription +SVGFECompositeElement::GetPrimitiveDescription(nsSVGFilterInstance* aInstance, + const IntRect& aFilterSubregion, + const nsTArray<bool>& aInputsAreTainted, + nsTArray<RefPtr<SourceSurface>>& aInputImages) +{ + FilterPrimitiveDescription descr(PrimitiveType::Composite); + uint32_t op = mEnumAttributes[OPERATOR].GetAnimValue(); + descr.Attributes().Set(eCompositeOperator, op); + + if (op == SVG_FECOMPOSITE_OPERATOR_ARITHMETIC) { + float k[4]; + GetAnimatedNumberValues(k, k+1, k+2, k+3, nullptr); + descr.Attributes().Set(eCompositeCoefficients, k, 4); + } + + return descr; +} + +bool +SVGFECompositeElement::AttributeAffectsRendering(int32_t aNameSpaceID, + nsIAtom* aAttribute) const +{ + return SVGFECompositeElementBase::AttributeAffectsRendering(aNameSpaceID, aAttribute) || + (aNameSpaceID == kNameSpaceID_None && + (aAttribute == nsGkAtoms::in || + aAttribute == nsGkAtoms::in2 || + aAttribute == nsGkAtoms::k1 || + aAttribute == nsGkAtoms::k2 || + aAttribute == nsGkAtoms::k3 || + aAttribute == nsGkAtoms::k4 || + aAttribute == nsGkAtoms::_operator)); +} + +void +SVGFECompositeElement::GetSourceImageNames(nsTArray<nsSVGStringInfo>& aSources) +{ + aSources.AppendElement(nsSVGStringInfo(&mStringAttributes[IN1], this)); + aSources.AppendElement(nsSVGStringInfo(&mStringAttributes[IN2], this)); +} + +//---------------------------------------------------------------------- +// nsSVGElement methods + +nsSVGElement::NumberAttributesInfo +SVGFECompositeElement::GetNumberInfo() +{ + return NumberAttributesInfo(mNumberAttributes, sNumberInfo, + ArrayLength(sNumberInfo)); +} + +nsSVGElement::EnumAttributesInfo +SVGFECompositeElement::GetEnumInfo() +{ + return EnumAttributesInfo(mEnumAttributes, sEnumInfo, + ArrayLength(sEnumInfo)); +} + +nsSVGElement::StringAttributesInfo +SVGFECompositeElement::GetStringInfo() +{ + return StringAttributesInfo(mStringAttributes, sStringInfo, + ArrayLength(sStringInfo)); +} + +} // namespace dom +} // namespace mozilla diff --git a/dom/svg/SVGFECompositeElement.h b/dom/svg/SVGFECompositeElement.h new file mode 100644 index 0000000000..4718a8c01b --- /dev/null +++ b/dom/svg/SVGFECompositeElement.h @@ -0,0 +1,79 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef mozilla_dom_SVGFECompositeElement_h +#define mozilla_dom_SVGFECompositeElement_h + +#include "nsSVGEnum.h" +#include "nsSVGFilters.h" +#include "nsSVGNumber2.h" + +nsresult NS_NewSVGFECompositeElement(nsIContent **aResult, + already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo); + +namespace mozilla { +namespace dom { + +typedef nsSVGFE SVGFECompositeElementBase; + +class SVGFECompositeElement : public SVGFECompositeElementBase +{ + friend nsresult (::NS_NewSVGFECompositeElement(nsIContent **aResult, + already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo)); +protected: + explicit SVGFECompositeElement(already_AddRefed<mozilla::dom::NodeInfo>& aNodeInfo) + : SVGFECompositeElementBase(aNodeInfo) + { + } + virtual JSObject* WrapNode(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override; + +public: + virtual FilterPrimitiveDescription + GetPrimitiveDescription(nsSVGFilterInstance* aInstance, + const IntRect& aFilterSubregion, + const nsTArray<bool>& aInputsAreTainted, + nsTArray<RefPtr<SourceSurface>>& aInputImages) override; + virtual bool AttributeAffectsRendering( + int32_t aNameSpaceID, nsIAtom* aAttribute) const override; + virtual nsSVGString& GetResultImageName() override { return mStringAttributes[RESULT]; } + virtual void GetSourceImageNames(nsTArray<nsSVGStringInfo>& aSources) override; + + + virtual nsresult Clone(mozilla::dom::NodeInfo *aNodeInfo, nsINode **aResult) const override; + + // WebIDL + already_AddRefed<SVGAnimatedString> In1(); + already_AddRefed<SVGAnimatedString> In2(); + already_AddRefed<SVGAnimatedEnumeration> Operator(); + already_AddRefed<SVGAnimatedNumber> K1(); + already_AddRefed<SVGAnimatedNumber> K2(); + already_AddRefed<SVGAnimatedNumber> K3(); + already_AddRefed<SVGAnimatedNumber> K4(); + void SetK(float k1, float k2, float k3, float k4); + +protected: + virtual NumberAttributesInfo GetNumberInfo() override; + virtual EnumAttributesInfo GetEnumInfo() override; + virtual StringAttributesInfo GetStringInfo() override; + + enum { ATTR_K1, ATTR_K2, ATTR_K3, ATTR_K4 }; + nsSVGNumber2 mNumberAttributes[4]; + static NumberInfo sNumberInfo[4]; + + enum { OPERATOR }; + nsSVGEnum mEnumAttributes[1]; + static nsSVGEnumMapping sOperatorMap[]; + static EnumInfo sEnumInfo[1]; + + enum { RESULT, IN1, IN2 }; + nsSVGString mStringAttributes[3]; + static StringInfo sStringInfo[3]; +}; + +} // namespace dom +} // namespace mozilla + +#endif // mozilla_dom_SVGFECompositeElement_h diff --git a/dom/svg/SVGFEConvolveMatrixElement.cpp b/dom/svg/SVGFEConvolveMatrixElement.cpp new file mode 100644 index 0000000000..3d387ca6aa --- /dev/null +++ b/dom/svg/SVGFEConvolveMatrixElement.cpp @@ -0,0 +1,333 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "mozilla/dom/SVGFEConvolveMatrixElement.h" +#include "mozilla/dom/SVGFEConvolveMatrixElementBinding.h" +#include "mozilla/UniquePtr.h" +#include "mozilla/UniquePtrExtensions.h" +#include "DOMSVGAnimatedNumberList.h" +#include "nsSVGUtils.h" +#include "nsSVGFilterInstance.h" + +NS_IMPL_NS_NEW_NAMESPACED_SVG_ELEMENT(FEConvolveMatrix) + +using namespace mozilla::gfx; + +namespace mozilla { +namespace dom { + +JSObject* +SVGFEConvolveMatrixElement::WrapNode(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) +{ + return SVGFEConvolveMatrixElementBinding::Wrap(aCx, this, aGivenProto); +} + +nsSVGElement::NumberInfo SVGFEConvolveMatrixElement::sNumberInfo[2] = +{ + { &nsGkAtoms::divisor, 1, false }, + { &nsGkAtoms::bias, 0, false } +}; + +nsSVGElement::NumberPairInfo SVGFEConvolveMatrixElement::sNumberPairInfo[1] = +{ + { &nsGkAtoms::kernelUnitLength, 0, 0 } +}; + +nsSVGElement::IntegerInfo SVGFEConvolveMatrixElement::sIntegerInfo[2] = +{ + { &nsGkAtoms::targetX, 0 }, + { &nsGkAtoms::targetY, 0 } +}; + +nsSVGElement::IntegerPairInfo SVGFEConvolveMatrixElement::sIntegerPairInfo[1] = +{ + { &nsGkAtoms::order, 3, 3 } +}; + +nsSVGElement::BooleanInfo SVGFEConvolveMatrixElement::sBooleanInfo[1] = +{ + { &nsGkAtoms::preserveAlpha, false } +}; + +nsSVGEnumMapping SVGFEConvolveMatrixElement::sEdgeModeMap[] = { + {&nsGkAtoms::duplicate, SVG_EDGEMODE_DUPLICATE}, + {&nsGkAtoms::wrap, SVG_EDGEMODE_WRAP}, + {&nsGkAtoms::none, SVG_EDGEMODE_NONE}, + {nullptr, 0} +}; + +nsSVGElement::EnumInfo SVGFEConvolveMatrixElement::sEnumInfo[1] = +{ + { &nsGkAtoms::edgeMode, + sEdgeModeMap, + SVG_EDGEMODE_DUPLICATE + } +}; + +nsSVGElement::StringInfo SVGFEConvolveMatrixElement::sStringInfo[2] = +{ + { &nsGkAtoms::result, kNameSpaceID_None, true }, + { &nsGkAtoms::in, kNameSpaceID_None, true } +}; + +nsSVGElement::NumberListInfo SVGFEConvolveMatrixElement::sNumberListInfo[1] = +{ + { &nsGkAtoms::kernelMatrix } +}; + +//---------------------------------------------------------------------- +// nsIDOMNode methods + +NS_IMPL_ELEMENT_CLONE_WITH_INIT(SVGFEConvolveMatrixElement) + +//---------------------------------------------------------------------- + +already_AddRefed<SVGAnimatedString> +SVGFEConvolveMatrixElement::In1() +{ + return mStringAttributes[IN1].ToDOMAnimatedString(this); +} + +already_AddRefed<SVGAnimatedInteger> +SVGFEConvolveMatrixElement::OrderX() +{ + return mIntegerPairAttributes[ORDER].ToDOMAnimatedInteger(nsSVGIntegerPair::eFirst, this); +} + +already_AddRefed<SVGAnimatedInteger> +SVGFEConvolveMatrixElement::OrderY() +{ + return mIntegerPairAttributes[ORDER].ToDOMAnimatedInteger(nsSVGIntegerPair::eSecond, this); +} + +already_AddRefed<DOMSVGAnimatedNumberList> +SVGFEConvolveMatrixElement::KernelMatrix() +{ + return DOMSVGAnimatedNumberList::GetDOMWrapper(&mNumberListAttributes[KERNELMATRIX], + this, KERNELMATRIX); +} + +already_AddRefed<SVGAnimatedInteger> +SVGFEConvolveMatrixElement::TargetX() +{ + return mIntegerAttributes[TARGET_X].ToDOMAnimatedInteger(this); +} + +already_AddRefed<SVGAnimatedInteger> +SVGFEConvolveMatrixElement::TargetY() +{ + return mIntegerAttributes[TARGET_Y].ToDOMAnimatedInteger(this); +} + +already_AddRefed<SVGAnimatedEnumeration> +SVGFEConvolveMatrixElement::EdgeMode() +{ + return mEnumAttributes[EDGEMODE].ToDOMAnimatedEnum(this); +} + +already_AddRefed<SVGAnimatedBoolean> +SVGFEConvolveMatrixElement::PreserveAlpha() +{ + return mBooleanAttributes[PRESERVEALPHA].ToDOMAnimatedBoolean(this); +} + +already_AddRefed<SVGAnimatedNumber> +SVGFEConvolveMatrixElement::Divisor() +{ + return mNumberAttributes[DIVISOR].ToDOMAnimatedNumber(this); +} + +already_AddRefed<SVGAnimatedNumber> +SVGFEConvolveMatrixElement::Bias() +{ + return mNumberAttributes[BIAS].ToDOMAnimatedNumber(this); +} + +already_AddRefed<SVGAnimatedNumber> +SVGFEConvolveMatrixElement::KernelUnitLengthX() +{ + return mNumberPairAttributes[KERNEL_UNIT_LENGTH].ToDOMAnimatedNumber(nsSVGNumberPair::eFirst, + this); +} + +already_AddRefed<SVGAnimatedNumber> +SVGFEConvolveMatrixElement::KernelUnitLengthY() +{ + return mNumberPairAttributes[KERNEL_UNIT_LENGTH].ToDOMAnimatedNumber(nsSVGNumberPair::eSecond, + this); +} + +void +SVGFEConvolveMatrixElement::GetSourceImageNames(nsTArray<nsSVGStringInfo>& aSources) +{ + aSources.AppendElement(nsSVGStringInfo(&mStringAttributes[IN1], this)); +} + +FilterPrimitiveDescription +SVGFEConvolveMatrixElement::GetPrimitiveDescription(nsSVGFilterInstance* aInstance, + const IntRect& aFilterSubregion, + const nsTArray<bool>& aInputsAreTainted, + nsTArray<RefPtr<SourceSurface>>& aInputImages) +{ + const FilterPrimitiveDescription failureDescription(PrimitiveType::Empty); + + const SVGNumberList &kernelMatrix = + mNumberListAttributes[KERNELMATRIX].GetAnimValue(); + uint32_t kmLength = kernelMatrix.Length(); + + int32_t orderX = mIntegerPairAttributes[ORDER].GetAnimValue(nsSVGIntegerPair::eFirst); + int32_t orderY = mIntegerPairAttributes[ORDER].GetAnimValue(nsSVGIntegerPair::eSecond); + + if (orderX <= 0 || orderY <= 0 || + static_cast<uint32_t>(orderX * orderY) != kmLength) { + return failureDescription; + } + + int32_t targetX, targetY; + GetAnimatedIntegerValues(&targetX, &targetY, nullptr); + + if (mIntegerAttributes[TARGET_X].IsExplicitlySet()) { + if (targetX < 0 || targetX >= orderX) + return failureDescription; + } else { + targetX = orderX / 2; + } + if (mIntegerAttributes[TARGET_Y].IsExplicitlySet()) { + if (targetY < 0 || targetY >= orderY) + return failureDescription; + } else { + targetY = orderY / 2; + } + + if (orderX > NS_SVG_OFFSCREEN_MAX_DIMENSION || + orderY > NS_SVG_OFFSCREEN_MAX_DIMENSION) + return failureDescription; + UniquePtr<float[]> kernel = MakeUniqueFallible<float[]>(orderX * orderY); + if (!kernel) + return failureDescription; + for (uint32_t i = 0; i < kmLength; i++) { + kernel[kmLength - 1 - i] = kernelMatrix[i]; + } + + float divisor; + if (mNumberAttributes[DIVISOR].IsExplicitlySet()) { + divisor = mNumberAttributes[DIVISOR].GetAnimValue(); + if (divisor == 0) + return failureDescription; + } else { + divisor = kernel[0]; + for (uint32_t i = 1; i < kmLength; i++) + divisor += kernel[i]; + if (divisor == 0) + divisor = 1; + } + + uint32_t edgeMode = mEnumAttributes[EDGEMODE].GetAnimValue(); + bool preserveAlpha = mBooleanAttributes[PRESERVEALPHA].GetAnimValue(); + float bias = mNumberAttributes[BIAS].GetAnimValue(); + + Size kernelUnitLength = + GetKernelUnitLength(aInstance, &mNumberPairAttributes[KERNEL_UNIT_LENGTH]); + + if (kernelUnitLength.width <= 0 || kernelUnitLength.height <= 0) { + // According to spec, A negative or zero value is an error. See link below for details. + // https://www.w3.org/TR/SVG/filters.html#feConvolveMatrixElementKernelUnitLengthAttribute + return failureDescription; + } + + FilterPrimitiveDescription descr(PrimitiveType::ConvolveMatrix); + AttributeMap& atts = descr.Attributes(); + atts.Set(eConvolveMatrixKernelSize, IntSize(orderX, orderY)); + atts.Set(eConvolveMatrixKernelMatrix, &kernelMatrix[0], kmLength); + atts.Set(eConvolveMatrixDivisor, divisor); + atts.Set(eConvolveMatrixBias, bias); + atts.Set(eConvolveMatrixTarget, IntPoint(targetX, targetY)); + atts.Set(eConvolveMatrixEdgeMode, edgeMode); + atts.Set(eConvolveMatrixKernelUnitLength, kernelUnitLength); + atts.Set(eConvolveMatrixPreserveAlpha, preserveAlpha); + + return descr; +} + +bool +SVGFEConvolveMatrixElement::AttributeAffectsRendering(int32_t aNameSpaceID, + nsIAtom* aAttribute) const +{ + return SVGFEConvolveMatrixElementBase::AttributeAffectsRendering(aNameSpaceID, aAttribute) || + (aNameSpaceID == kNameSpaceID_None && + (aAttribute == nsGkAtoms::in || + aAttribute == nsGkAtoms::divisor || + aAttribute == nsGkAtoms::bias || + aAttribute == nsGkAtoms::kernelUnitLength || + aAttribute == nsGkAtoms::targetX || + aAttribute == nsGkAtoms::targetY || + aAttribute == nsGkAtoms::order || + aAttribute == nsGkAtoms::preserveAlpha|| + aAttribute == nsGkAtoms::edgeMode || + aAttribute == nsGkAtoms::kernelMatrix)); +} + +//---------------------------------------------------------------------- +// nsSVGElement methods + +nsSVGElement::NumberAttributesInfo +SVGFEConvolveMatrixElement::GetNumberInfo() +{ + return NumberAttributesInfo(mNumberAttributes, sNumberInfo, + ArrayLength(sNumberInfo)); +} + +nsSVGElement::NumberPairAttributesInfo +SVGFEConvolveMatrixElement::GetNumberPairInfo() +{ + return NumberPairAttributesInfo(mNumberPairAttributes, sNumberPairInfo, + ArrayLength(sNumberPairInfo)); +} + +nsSVGElement::IntegerAttributesInfo +SVGFEConvolveMatrixElement::GetIntegerInfo() +{ + return IntegerAttributesInfo(mIntegerAttributes, sIntegerInfo, + ArrayLength(sIntegerInfo)); +} + +nsSVGElement::IntegerPairAttributesInfo +SVGFEConvolveMatrixElement::GetIntegerPairInfo() +{ + return IntegerPairAttributesInfo(mIntegerPairAttributes, sIntegerPairInfo, + ArrayLength(sIntegerPairInfo)); +} + +nsSVGElement::BooleanAttributesInfo +SVGFEConvolveMatrixElement::GetBooleanInfo() +{ + return BooleanAttributesInfo(mBooleanAttributes, sBooleanInfo, + ArrayLength(sBooleanInfo)); +} + +nsSVGElement::EnumAttributesInfo +SVGFEConvolveMatrixElement::GetEnumInfo() +{ + return EnumAttributesInfo(mEnumAttributes, sEnumInfo, + ArrayLength(sEnumInfo)); +} + +nsSVGElement::StringAttributesInfo +SVGFEConvolveMatrixElement::GetStringInfo() +{ + return StringAttributesInfo(mStringAttributes, sStringInfo, + ArrayLength(sStringInfo)); +} + +nsSVGElement::NumberListAttributesInfo +SVGFEConvolveMatrixElement::GetNumberListInfo() +{ + return NumberListAttributesInfo(mNumberListAttributes, sNumberListInfo, + ArrayLength(sNumberListInfo)); +} + +} // namespace dom +} // namespace mozilla diff --git a/dom/svg/SVGFEConvolveMatrixElement.h b/dom/svg/SVGFEConvolveMatrixElement.h new file mode 100644 index 0000000000..f1f95c5046 --- /dev/null +++ b/dom/svg/SVGFEConvolveMatrixElement.h @@ -0,0 +1,115 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef mozilla_dom_SVGFEConvolveMatrixElement_h +#define mozilla_dom_SVGFEConvolveMatrixElement_h + +#include "nsSVGBoolean.h" +#include "nsSVGEnum.h" +#include "nsSVGFilters.h" +#include "nsSVGInteger.h" +#include "nsSVGIntegerPair.h" +#include "nsSVGNumber2.h" +#include "nsSVGString.h" +#include "SVGAnimatedNumberList.h" + +nsresult NS_NewSVGFEConvolveMatrixElement(nsIContent **aResult, + already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo); + +namespace mozilla { +class DOMSVGAnimatedNumberList; + +namespace dom { +class SVGAnimatedBoolean; + +typedef nsSVGFE SVGFEConvolveMatrixElementBase; + +class SVGFEConvolveMatrixElement : public SVGFEConvolveMatrixElementBase +{ + friend nsresult (::NS_NewSVGFEConvolveMatrixElement(nsIContent **aResult, + already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo)); +protected: + explicit SVGFEConvolveMatrixElement(already_AddRefed<mozilla::dom::NodeInfo>& aNodeInfo) + : SVGFEConvolveMatrixElementBase(aNodeInfo) + { + } + virtual JSObject* WrapNode(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override; + +public: + virtual FilterPrimitiveDescription + GetPrimitiveDescription(nsSVGFilterInstance* aInstance, + const IntRect& aFilterSubregion, + const nsTArray<bool>& aInputsAreTainted, + nsTArray<RefPtr<SourceSurface>>& aInputImages) override; + virtual bool AttributeAffectsRendering( + int32_t aNameSpaceID, nsIAtom* aAttribute) const override; + virtual nsSVGString& GetResultImageName() override { return mStringAttributes[RESULT]; } + virtual void GetSourceImageNames(nsTArray<nsSVGStringInfo>& aSources) override; + + virtual nsresult Clone(mozilla::dom::NodeInfo *aNodeInfo, nsINode **aResult) const override; + + // WebIDL + already_AddRefed<SVGAnimatedString> In1(); + already_AddRefed<SVGAnimatedInteger> OrderX(); + already_AddRefed<SVGAnimatedInteger> OrderY(); + already_AddRefed<DOMSVGAnimatedNumberList> KernelMatrix(); + already_AddRefed<SVGAnimatedInteger> TargetX(); + already_AddRefed<SVGAnimatedInteger> TargetY(); + already_AddRefed<SVGAnimatedEnumeration> EdgeMode(); + already_AddRefed<SVGAnimatedBoolean> PreserveAlpha(); + already_AddRefed<SVGAnimatedNumber> Divisor(); + already_AddRefed<SVGAnimatedNumber> Bias(); + already_AddRefed<SVGAnimatedNumber> KernelUnitLengthX(); + already_AddRefed<SVGAnimatedNumber> KernelUnitLengthY(); + +protected: + virtual NumberAttributesInfo GetNumberInfo() override; + virtual NumberPairAttributesInfo GetNumberPairInfo() override; + virtual IntegerAttributesInfo GetIntegerInfo() override; + virtual IntegerPairAttributesInfo GetIntegerPairInfo() override; + virtual BooleanAttributesInfo GetBooleanInfo() override; + virtual EnumAttributesInfo GetEnumInfo() override; + virtual StringAttributesInfo GetStringInfo() override; + virtual NumberListAttributesInfo GetNumberListInfo() override; + + enum { DIVISOR, BIAS }; + nsSVGNumber2 mNumberAttributes[2]; + static NumberInfo sNumberInfo[2]; + + enum { KERNEL_UNIT_LENGTH }; + nsSVGNumberPair mNumberPairAttributes[1]; + static NumberPairInfo sNumberPairInfo[1]; + + enum { TARGET_X, TARGET_Y }; + nsSVGInteger mIntegerAttributes[2]; + static IntegerInfo sIntegerInfo[2]; + + enum { ORDER }; + nsSVGIntegerPair mIntegerPairAttributes[1]; + static IntegerPairInfo sIntegerPairInfo[1]; + + enum { PRESERVEALPHA }; + nsSVGBoolean mBooleanAttributes[1]; + static BooleanInfo sBooleanInfo[1]; + + enum { EDGEMODE }; + nsSVGEnum mEnumAttributes[1]; + static nsSVGEnumMapping sEdgeModeMap[]; + static EnumInfo sEnumInfo[1]; + + enum { RESULT, IN1 }; + nsSVGString mStringAttributes[2]; + static StringInfo sStringInfo[2]; + + enum { KERNELMATRIX }; + SVGAnimatedNumberList mNumberListAttributes[1]; + static NumberListInfo sNumberListInfo[1]; +}; + +} // namespace dom +} // namespace mozilla + +#endif // mozilla_dom_SVGFEConvolveMatrixElement_h diff --git a/dom/svg/SVGFEDiffuseLightingElement.cpp b/dom/svg/SVGFEDiffuseLightingElement.cpp new file mode 100644 index 0000000000..010cf605f7 --- /dev/null +++ b/dom/svg/SVGFEDiffuseLightingElement.cpp @@ -0,0 +1,87 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "mozilla/dom/SVGFEDiffuseLightingElement.h" +#include "mozilla/dom/SVGFEDiffuseLightingElementBinding.h" +#include "nsSVGUtils.h" +#include "nsSVGFilterInstance.h" + +NS_IMPL_NS_NEW_NAMESPACED_SVG_ELEMENT(FEDiffuseLighting) + +using namespace mozilla::gfx; + +namespace mozilla { +namespace dom { + +JSObject* +SVGFEDiffuseLightingElement::WrapNode(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) +{ + return SVGFEDiffuseLightingElementBinding::Wrap(aCx, this, aGivenProto); +} + +//---------------------------------------------------------------------- +// nsIDOMNode methods + +NS_IMPL_ELEMENT_CLONE_WITH_INIT(SVGFEDiffuseLightingElement) + +//---------------------------------------------------------------------- + +already_AddRefed<SVGAnimatedString> +SVGFEDiffuseLightingElement::In1() +{ + return mStringAttributes[IN1].ToDOMAnimatedString(this); +} + +already_AddRefed<SVGAnimatedNumber> +SVGFEDiffuseLightingElement::SurfaceScale() +{ + return mNumberAttributes[SURFACE_SCALE].ToDOMAnimatedNumber(this); +} + +already_AddRefed<SVGAnimatedNumber> +SVGFEDiffuseLightingElement::DiffuseConstant() +{ + return mNumberAttributes[DIFFUSE_CONSTANT].ToDOMAnimatedNumber(this); +} + +already_AddRefed<SVGAnimatedNumber> +SVGFEDiffuseLightingElement::KernelUnitLengthX() +{ + return mNumberPairAttributes[KERNEL_UNIT_LENGTH].ToDOMAnimatedNumber( + nsSVGNumberPair::eFirst, this); +} + +already_AddRefed<SVGAnimatedNumber> +SVGFEDiffuseLightingElement::KernelUnitLengthY() +{ + return mNumberPairAttributes[KERNEL_UNIT_LENGTH].ToDOMAnimatedNumber( + nsSVGNumberPair::eSecond, this); +} + +FilterPrimitiveDescription +SVGFEDiffuseLightingElement::GetPrimitiveDescription(nsSVGFilterInstance* aInstance, + const IntRect& aFilterSubregion, + const nsTArray<bool>& aInputsAreTainted, + nsTArray<RefPtr<SourceSurface>>& aInputImages) +{ + float diffuseConstant = mNumberAttributes[DIFFUSE_CONSTANT].GetAnimValue(); + + FilterPrimitiveDescription descr(PrimitiveType::DiffuseLighting); + descr.Attributes().Set(eDiffuseLightingDiffuseConstant, diffuseConstant); + return AddLightingAttributes(descr, aInstance); +} + +bool +SVGFEDiffuseLightingElement::AttributeAffectsRendering(int32_t aNameSpaceID, + nsIAtom* aAttribute) const +{ + return SVGFEDiffuseLightingElementBase::AttributeAffectsRendering(aNameSpaceID, aAttribute) || + (aNameSpaceID == kNameSpaceID_None && + aAttribute == nsGkAtoms::diffuseConstant); +} + +} // namespace dom +} // namespace mozilla diff --git a/dom/svg/SVGFEDiffuseLightingElement.h b/dom/svg/SVGFEDiffuseLightingElement.h new file mode 100644 index 0000000000..584ab56499 --- /dev/null +++ b/dom/svg/SVGFEDiffuseLightingElement.h @@ -0,0 +1,53 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef mozilla_dom_SVGFEDiffuseLightingElement_h +#define mozilla_dom_SVGFEDiffuseLightingElement_h + +#include "nsSVGFilters.h" + +nsresult NS_NewSVGFEDiffuseLightingElement(nsIContent **aResult, + already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo); + +namespace mozilla { +namespace dom { + +typedef nsSVGFELightingElement SVGFEDiffuseLightingElementBase; + +class SVGFEDiffuseLightingElement : public SVGFEDiffuseLightingElementBase +{ + friend nsresult (::NS_NewSVGFEDiffuseLightingElement(nsIContent **aResult, + already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo)); +protected: + explicit SVGFEDiffuseLightingElement(already_AddRefed<mozilla::dom::NodeInfo>& aNodeInfo) + : SVGFEDiffuseLightingElementBase(aNodeInfo) + { + } + virtual JSObject* WrapNode(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override; + +public: + virtual FilterPrimitiveDescription + GetPrimitiveDescription(nsSVGFilterInstance* aInstance, + const IntRect& aFilterSubregion, + const nsTArray<bool>& aInputsAreTainted, + nsTArray<RefPtr<SourceSurface>>& aInputImages) override; + virtual bool AttributeAffectsRendering( + int32_t aNameSpaceID, nsIAtom* aAttribute) const override; + + virtual nsresult Clone(mozilla::dom::NodeInfo *aNodeInfo, nsINode **aResult) const override; + + // WebIDL + already_AddRefed<SVGAnimatedString> In1(); + already_AddRefed<SVGAnimatedNumber> SurfaceScale(); + already_AddRefed<SVGAnimatedNumber> DiffuseConstant(); + already_AddRefed<SVGAnimatedNumber> KernelUnitLengthX(); + already_AddRefed<SVGAnimatedNumber> KernelUnitLengthY(); +}; + +} // namespace dom +} // namespace mozilla + +#endif // mozilla_dom_SVGFEDiffuseLightingElement_h diff --git a/dom/svg/SVGFEDisplacementMapElement.cpp b/dom/svg/SVGFEDisplacementMapElement.cpp new file mode 100644 index 0000000000..84e59d75df --- /dev/null +++ b/dom/svg/SVGFEDisplacementMapElement.cpp @@ -0,0 +1,164 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "mozilla/dom/SVGFEDisplacementMapElement.h" +#include "mozilla/dom/SVGFEDisplacementMapElementBinding.h" +#include "nsSVGFilterInstance.h" +#include "nsSVGUtils.h" + +NS_IMPL_NS_NEW_NAMESPACED_SVG_ELEMENT(FEDisplacementMap) + +using namespace mozilla::gfx; + +namespace mozilla { +namespace dom { + +JSObject* +SVGFEDisplacementMapElement::WrapNode(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) +{ + return SVGFEDisplacementMapElementBinding::Wrap(aCx, this, aGivenProto); +} + +nsSVGElement::NumberInfo SVGFEDisplacementMapElement::sNumberInfo[1] = +{ + { &nsGkAtoms::scale, 0, false }, +}; + +nsSVGEnumMapping SVGFEDisplacementMapElement::sChannelMap[] = { + {&nsGkAtoms::R, SVG_CHANNEL_R}, + {&nsGkAtoms::G, SVG_CHANNEL_G}, + {&nsGkAtoms::B, SVG_CHANNEL_B}, + {&nsGkAtoms::A, SVG_CHANNEL_A}, + {nullptr, 0} +}; + +nsSVGElement::EnumInfo SVGFEDisplacementMapElement::sEnumInfo[2] = +{ + { &nsGkAtoms::xChannelSelector, + sChannelMap, + SVG_CHANNEL_A + }, + { &nsGkAtoms::yChannelSelector, + sChannelMap, + SVG_CHANNEL_A + } +}; + +nsSVGElement::StringInfo SVGFEDisplacementMapElement::sStringInfo[3] = +{ + { &nsGkAtoms::result, kNameSpaceID_None, true }, + { &nsGkAtoms::in, kNameSpaceID_None, true }, + { &nsGkAtoms::in2, kNameSpaceID_None, true } +}; + +//---------------------------------------------------------------------- +// nsIDOMNode methods + +NS_IMPL_ELEMENT_CLONE_WITH_INIT(SVGFEDisplacementMapElement) + +//---------------------------------------------------------------------- + +already_AddRefed<SVGAnimatedString> +SVGFEDisplacementMapElement::In1() +{ + return mStringAttributes[IN1].ToDOMAnimatedString(this); +} + +already_AddRefed<SVGAnimatedString> +SVGFEDisplacementMapElement::In2() +{ + return mStringAttributes[IN2].ToDOMAnimatedString(this); +} + +already_AddRefed<SVGAnimatedNumber> +SVGFEDisplacementMapElement::Scale() +{ + return mNumberAttributes[SCALE].ToDOMAnimatedNumber(this); +} + +already_AddRefed<SVGAnimatedEnumeration> +SVGFEDisplacementMapElement::XChannelSelector() +{ + return mEnumAttributes[CHANNEL_X].ToDOMAnimatedEnum(this); +} + +already_AddRefed<SVGAnimatedEnumeration> +SVGFEDisplacementMapElement::YChannelSelector() +{ + return mEnumAttributes[CHANNEL_Y].ToDOMAnimatedEnum(this); +} + +FilterPrimitiveDescription +SVGFEDisplacementMapElement::GetPrimitiveDescription(nsSVGFilterInstance* aInstance, + const IntRect& aFilterSubregion, + const nsTArray<bool>& aInputsAreTainted, + nsTArray<RefPtr<SourceSurface>>& aInputImages) +{ + if (aInputsAreTainted[1]) { + // If the map is tainted, refuse to apply the effect and act as a + // pass-through filter instead, as required by the spec. + FilterPrimitiveDescription descr(PrimitiveType::Offset); + descr.Attributes().Set(eOffsetOffset, IntPoint(0, 0)); + return descr; + } + + float scale = aInstance->GetPrimitiveNumber(SVGContentUtils::XY, + &mNumberAttributes[SCALE]); + uint32_t xChannel = mEnumAttributes[CHANNEL_X].GetAnimValue(); + uint32_t yChannel = mEnumAttributes[CHANNEL_Y].GetAnimValue(); + FilterPrimitiveDescription descr(PrimitiveType::DisplacementMap); + descr.Attributes().Set(eDisplacementMapScale, scale); + descr.Attributes().Set(eDisplacementMapXChannel, xChannel); + descr.Attributes().Set(eDisplacementMapYChannel, yChannel); + return descr; +} + +bool +SVGFEDisplacementMapElement::AttributeAffectsRendering(int32_t aNameSpaceID, + nsIAtom* aAttribute) const +{ + return SVGFEDisplacementMapElementBase::AttributeAffectsRendering(aNameSpaceID, aAttribute) || + (aNameSpaceID == kNameSpaceID_None && + (aAttribute == nsGkAtoms::in || + aAttribute == nsGkAtoms::in2 || + aAttribute == nsGkAtoms::scale || + aAttribute == nsGkAtoms::xChannelSelector || + aAttribute == nsGkAtoms::yChannelSelector)); +} + +void +SVGFEDisplacementMapElement::GetSourceImageNames(nsTArray<nsSVGStringInfo>& aSources) +{ + aSources.AppendElement(nsSVGStringInfo(&mStringAttributes[IN1], this)); + aSources.AppendElement(nsSVGStringInfo(&mStringAttributes[IN2], this)); +} + +//---------------------------------------------------------------------- +// nsSVGElement methods + +nsSVGElement::NumberAttributesInfo +SVGFEDisplacementMapElement::GetNumberInfo() +{ + return NumberAttributesInfo(mNumberAttributes, sNumberInfo, + ArrayLength(sNumberInfo)); +} + +nsSVGElement::EnumAttributesInfo +SVGFEDisplacementMapElement::GetEnumInfo() +{ + return EnumAttributesInfo(mEnumAttributes, sEnumInfo, + ArrayLength(sEnumInfo)); +} + +nsSVGElement::StringAttributesInfo +SVGFEDisplacementMapElement::GetStringInfo() +{ + return StringAttributesInfo(mStringAttributes, sStringInfo, + ArrayLength(sStringInfo)); +} + +} // namespace dom +} // namespace mozilla diff --git a/dom/svg/SVGFEDisplacementMapElement.h b/dom/svg/SVGFEDisplacementMapElement.h new file mode 100644 index 0000000000..941eda1992 --- /dev/null +++ b/dom/svg/SVGFEDisplacementMapElement.h @@ -0,0 +1,87 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef mozilla_dom_SVGFEDisplacementMapElement_h +#define mozilla_dom_SVGFEDisplacementMapElement_h + +#include "nsSVGEnum.h" +#include "nsSVGFilters.h" + +nsresult NS_NewSVGFEDisplacementMapElement(nsIContent **aResult, + already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo); + +namespace mozilla { +namespace dom { + +typedef nsSVGFE SVGFEDisplacementMapElementBase; + +class SVGFEDisplacementMapElement : public SVGFEDisplacementMapElementBase +{ +protected: + friend nsresult (::NS_NewSVGFEDisplacementMapElement(nsIContent **aResult, + already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo)); + explicit SVGFEDisplacementMapElement(already_AddRefed<mozilla::dom::NodeInfo>& aNodeInfo) + : SVGFEDisplacementMapElementBase(aNodeInfo) + { + } + virtual JSObject* WrapNode(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override; + +public: + virtual FilterPrimitiveDescription + GetPrimitiveDescription(nsSVGFilterInstance* aInstance, + const IntRect& aFilterSubregion, + const nsTArray<bool>& aInputsAreTainted, + nsTArray<RefPtr<SourceSurface>>& aInputImages) override; + virtual bool AttributeAffectsRendering( + int32_t aNameSpaceID, nsIAtom* aAttribute) const override; + virtual nsSVGString& GetResultImageName() override { return mStringAttributes[RESULT]; } + virtual void GetSourceImageNames(nsTArray<nsSVGStringInfo>& aSources) override; + + virtual nsresult Clone(mozilla::dom::NodeInfo *aNodeInfo, nsINode **aResult) const override; + + // WebIDL + already_AddRefed<SVGAnimatedString> In1(); + already_AddRefed<SVGAnimatedString> In2(); + already_AddRefed<SVGAnimatedNumber> Scale(); + already_AddRefed<SVGAnimatedEnumeration> XChannelSelector(); + already_AddRefed<SVGAnimatedEnumeration> YChannelSelector(); + +protected: + virtual bool OperatesOnSRGB(int32_t aInputIndex, + bool aInputIsAlreadySRGB) override { + switch (aInputIndex) { + case 0: + return aInputIsAlreadySRGB; + case 1: + return SVGFEDisplacementMapElementBase::OperatesOnSRGB(aInputIndex, aInputIsAlreadySRGB); + default: + NS_ERROR("Will not give correct color model"); + return false; + } + } + + virtual NumberAttributesInfo GetNumberInfo() override; + virtual EnumAttributesInfo GetEnumInfo() override; + virtual StringAttributesInfo GetStringInfo() override; + + enum { SCALE }; + nsSVGNumber2 mNumberAttributes[1]; + static NumberInfo sNumberInfo[1]; + + enum { CHANNEL_X, CHANNEL_Y }; + nsSVGEnum mEnumAttributes[2]; + static nsSVGEnumMapping sChannelMap[]; + static EnumInfo sEnumInfo[2]; + + enum { RESULT, IN1, IN2 }; + nsSVGString mStringAttributes[3]; + static StringInfo sStringInfo[3]; +}; + +} // namespace dom +} // namespace mozilla + +#endif // mozilla_dom_SVGFEDisplacementMapElement_h diff --git a/dom/svg/SVGFEDistantLightElement.cpp b/dom/svg/SVGFEDistantLightElement.cpp new file mode 100644 index 0000000000..2a4dd446fd --- /dev/null +++ b/dom/svg/SVGFEDistantLightElement.cpp @@ -0,0 +1,83 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "mozilla/dom/SVGFEDistantLightElement.h" +#include "mozilla/dom/SVGFEDistantLightElementBinding.h" +#include "nsSVGFilterInstance.h" + +NS_IMPL_NS_NEW_NAMESPACED_SVG_ELEMENT(FEDistantLight) + +using namespace mozilla::gfx; + +namespace mozilla { +namespace dom { + +JSObject* +SVGFEDistantLightElement::WrapNode(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) +{ + return SVGFEDistantLightElementBinding::Wrap(aCx, this, aGivenProto); +} + +nsSVGElement::NumberInfo SVGFEDistantLightElement::sNumberInfo[2] = +{ + { &nsGkAtoms::azimuth, 0, false }, + { &nsGkAtoms::elevation, 0, false } +}; + +//---------------------------------------------------------------------- +//---------------------------------------------------------------------- +// nsIDOMNode methods + +NS_IMPL_ELEMENT_CLONE_WITH_INIT(SVGFEDistantLightElement) + +// nsFEUnstyledElement methods + +bool +SVGFEDistantLightElement::AttributeAffectsRendering(int32_t aNameSpaceID, + nsIAtom* aAttribute) const +{ + return aNameSpaceID == kNameSpaceID_None && + (aAttribute == nsGkAtoms::azimuth || + aAttribute == nsGkAtoms::elevation); +} + +AttributeMap +SVGFEDistantLightElement::ComputeLightAttributes(nsSVGFilterInstance* aInstance) +{ + float azimuth, elevation; + GetAnimatedNumberValues(&azimuth, &elevation, nullptr); + + AttributeMap map; + map.Set(eLightType, (uint32_t)eLightTypeDistant); + map.Set(eDistantLightAzimuth, azimuth); + map.Set(eDistantLightElevation, elevation); + return map; +} + +already_AddRefed<SVGAnimatedNumber> +SVGFEDistantLightElement::Azimuth() +{ + return mNumberAttributes[AZIMUTH].ToDOMAnimatedNumber(this); +} + +already_AddRefed<SVGAnimatedNumber> +SVGFEDistantLightElement::Elevation() +{ + return mNumberAttributes[ELEVATION].ToDOMAnimatedNumber(this); +} + +//---------------------------------------------------------------------- +// nsSVGElement methods + +nsSVGElement::NumberAttributesInfo +SVGFEDistantLightElement::GetNumberInfo() +{ + return NumberAttributesInfo(mNumberAttributes, sNumberInfo, + ArrayLength(sNumberInfo)); +} + +} // namespace dom +} // namespace mozilla diff --git a/dom/svg/SVGFEDistantLightElement.h b/dom/svg/SVGFEDistantLightElement.h new file mode 100644 index 0000000000..f069a9cb16 --- /dev/null +++ b/dom/svg/SVGFEDistantLightElement.h @@ -0,0 +1,54 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef mozilla_dom_SVGFEDistantLightElement_h +#define mozilla_dom_SVGFEDistantLightElement_h + +#include "nsSVGFilters.h" +#include "nsSVGNumber2.h" + +nsresult NS_NewSVGFEDistantLightElement(nsIContent **aResult, + already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo); + +namespace mozilla { +namespace dom { + +typedef SVGFELightElement SVGFEDistantLightElementBase; + +class SVGFEDistantLightElement : public SVGFEDistantLightElementBase +{ + friend nsresult (::NS_NewSVGFEDistantLightElement(nsIContent **aResult, + already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo)); +protected: + explicit SVGFEDistantLightElement(already_AddRefed<mozilla::dom::NodeInfo>& aNodeInfo) + : SVGFEDistantLightElementBase(aNodeInfo) + { + } + virtual JSObject* WrapNode(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override; + +public: + virtual AttributeMap ComputeLightAttributes(nsSVGFilterInstance* aInstance) override; + virtual bool AttributeAffectsRendering( + int32_t aNameSpaceID, nsIAtom* aAttribute) const override; + + virtual nsresult Clone(mozilla::dom::NodeInfo *aNodeInfo, nsINode **aResult) const override; + + // WebIDL + already_AddRefed<SVGAnimatedNumber> Azimuth(); + already_AddRefed<SVGAnimatedNumber> Elevation(); + +protected: + virtual NumberAttributesInfo GetNumberInfo() override; + + enum { AZIMUTH, ELEVATION }; + nsSVGNumber2 mNumberAttributes[2]; + static NumberInfo sNumberInfo[2]; +}; + +} // namespace dom +} // namespace mozilla + +#endif // mozilla_dom_SVGFEDistantLightElement_h diff --git a/dom/svg/SVGFEDropShadowElement.cpp b/dom/svg/SVGFEDropShadowElement.cpp new file mode 100644 index 0000000000..f15d43fad2 --- /dev/null +++ b/dom/svg/SVGFEDropShadowElement.cpp @@ -0,0 +1,179 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "mozilla/dom/SVGFEDropShadowElement.h" +#include "mozilla/dom/SVGFEDropShadowElementBinding.h" +#include "nsIFrame.h" +#include "nsSVGFilterInstance.h" + +NS_IMPL_NS_NEW_NAMESPACED_SVG_ELEMENT(FEDropShadow) + +using namespace mozilla::gfx; + +namespace mozilla { +namespace dom { + +JSObject* +SVGFEDropShadowElement::WrapNode(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) +{ + return SVGFEDropShadowElementBinding::Wrap(aCx, this, aGivenProto); +} + +nsSVGElement::NumberInfo SVGFEDropShadowElement::sNumberInfo[2] = +{ + { &nsGkAtoms::dx, 2, false }, + { &nsGkAtoms::dy, 2, false } +}; + +nsSVGElement::NumberPairInfo SVGFEDropShadowElement::sNumberPairInfo[1] = +{ + { &nsGkAtoms::stdDeviation, 2, 2 } +}; + +nsSVGElement::StringInfo SVGFEDropShadowElement::sStringInfo[2] = +{ + { &nsGkAtoms::result, kNameSpaceID_None, true }, + { &nsGkAtoms::in, kNameSpaceID_None, true } +}; + +//---------------------------------------------------------------------- +// nsIDOMNode methods + +NS_IMPL_ELEMENT_CLONE_WITH_INIT(SVGFEDropShadowElement) + +//---------------------------------------------------------------------- + +already_AddRefed<SVGAnimatedString> +SVGFEDropShadowElement::In1() +{ + return mStringAttributes[IN1].ToDOMAnimatedString(this); +} + +already_AddRefed<SVGAnimatedNumber> +SVGFEDropShadowElement::Dx() +{ + return mNumberAttributes[DX].ToDOMAnimatedNumber(this); +} + +already_AddRefed<SVGAnimatedNumber> +SVGFEDropShadowElement::Dy() +{ + return mNumberAttributes[DY].ToDOMAnimatedNumber(this); +} + +already_AddRefed<SVGAnimatedNumber> +SVGFEDropShadowElement::StdDeviationX() +{ + return mNumberPairAttributes[STD_DEV].ToDOMAnimatedNumber(nsSVGNumberPair::eFirst, this); +} + +already_AddRefed<SVGAnimatedNumber> +SVGFEDropShadowElement::StdDeviationY() +{ + return mNumberPairAttributes[STD_DEV].ToDOMAnimatedNumber(nsSVGNumberPair::eSecond, this); +} + +void +SVGFEDropShadowElement::SetStdDeviation(float stdDeviationX, float stdDeviationY) +{ + mNumberPairAttributes[STD_DEV].SetBaseValues(stdDeviationX, stdDeviationY, this); +} + +FilterPrimitiveDescription +SVGFEDropShadowElement::GetPrimitiveDescription(nsSVGFilterInstance* aInstance, + const IntRect& aFilterSubregion, + const nsTArray<bool>& aInputsAreTainted, + nsTArray<RefPtr<SourceSurface>>& aInputImages) +{ + float stdX = aInstance->GetPrimitiveNumber(SVGContentUtils::X, + &mNumberPairAttributes[STD_DEV], + nsSVGNumberPair::eFirst); + float stdY = aInstance->GetPrimitiveNumber(SVGContentUtils::Y, + &mNumberPairAttributes[STD_DEV], + nsSVGNumberPair::eSecond); + if (stdX < 0 || stdY < 0) { + return FilterPrimitiveDescription(PrimitiveType::Empty); + } + + IntPoint offset(int32_t(aInstance->GetPrimitiveNumber( + SVGContentUtils::X, &mNumberAttributes[DX])), + int32_t(aInstance->GetPrimitiveNumber( + SVGContentUtils::Y, &mNumberAttributes[DY]))); + + FilterPrimitiveDescription descr(PrimitiveType::DropShadow); + descr.Attributes().Set(eDropShadowStdDeviation, Size(stdX, stdY)); + descr.Attributes().Set(eDropShadowOffset, offset); + + nsIFrame* frame = GetPrimaryFrame(); + if (frame) { + nsStyleContext* style = frame->StyleContext(); + Color color(Color::FromABGR(style->StyleSVGReset()->mFloodColor)); + color.a *= style->StyleSVGReset()->mFloodOpacity; + descr.Attributes().Set(eDropShadowColor, color); + } else { + descr.Attributes().Set(eDropShadowColor, Color()); + } + return descr; +} + +bool +SVGFEDropShadowElement::AttributeAffectsRendering(int32_t aNameSpaceID, + nsIAtom* aAttribute) const +{ + return SVGFEDropShadowElementBase::AttributeAffectsRendering(aNameSpaceID, aAttribute) || + (aNameSpaceID == kNameSpaceID_None && + (aAttribute == nsGkAtoms::in || + aAttribute == nsGkAtoms::stdDeviation || + aAttribute == nsGkAtoms::dx || + aAttribute == nsGkAtoms::dy)); +} + +void +SVGFEDropShadowElement::GetSourceImageNames(nsTArray<nsSVGStringInfo>& aSources) +{ + aSources.AppendElement(nsSVGStringInfo(&mStringAttributes[IN1], this)); +} + +//---------------------------------------------------------------------- +// nsIContent methods + +NS_IMETHODIMP_(bool) +SVGFEDropShadowElement::IsAttributeMapped(const nsIAtom* name) const +{ + static const MappedAttributeEntry* const map[] = { + sFEFloodMap + }; + + return FindAttributeDependence(name, map) || + SVGFEDropShadowElementBase::IsAttributeMapped(name); +} + +//---------------------------------------------------------------------- +// nsSVGElement methods + +nsSVGElement::NumberAttributesInfo +SVGFEDropShadowElement::GetNumberInfo() +{ + return NumberAttributesInfo(mNumberAttributes, sNumberInfo, + ArrayLength(sNumberInfo)); +} + +nsSVGElement::NumberPairAttributesInfo +SVGFEDropShadowElement::GetNumberPairInfo() +{ + return NumberPairAttributesInfo(mNumberPairAttributes, sNumberPairInfo, + ArrayLength(sNumberPairInfo)); +} + +nsSVGElement::StringAttributesInfo +SVGFEDropShadowElement::GetStringInfo() +{ + return StringAttributesInfo(mStringAttributes, sStringInfo, + ArrayLength(sStringInfo)); +} + +} // namespace dom +} // namespace mozilla diff --git a/dom/svg/SVGFEDropShadowElement.h b/dom/svg/SVGFEDropShadowElement.h new file mode 100644 index 0000000000..fcaf701bdf --- /dev/null +++ b/dom/svg/SVGFEDropShadowElement.h @@ -0,0 +1,79 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef mozilla_dom_SVGFEDropShadowElement_h +#define mozilla_dom_SVGFEDropShadowElement_h + +#include "nsSVGFilters.h" +#include "nsSVGNumber2.h" +#include "nsSVGNumberPair.h" +#include "nsSVGString.h" + +nsresult NS_NewSVGFEDropShadowElement(nsIContent **aResult, + already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo); + +namespace mozilla { +namespace dom { + +typedef nsSVGFE SVGFEDropShadowElementBase; + +class SVGFEDropShadowElement : public SVGFEDropShadowElementBase +{ + friend nsresult (::NS_NewSVGFEDropShadowElement(nsIContent **aResult, + already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo)); +protected: + explicit SVGFEDropShadowElement(already_AddRefed<mozilla::dom::NodeInfo>& aNodeInfo) + : SVGFEDropShadowElementBase(aNodeInfo) + { + } + virtual JSObject* WrapNode(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override; + +public: + virtual FilterPrimitiveDescription + GetPrimitiveDescription(nsSVGFilterInstance* aInstance, + const IntRect& aFilterSubregion, + const nsTArray<bool>& aInputsAreTainted, + nsTArray<RefPtr<SourceSurface>>& aInputImages) override; + virtual bool AttributeAffectsRendering( + int32_t aNameSpaceID, nsIAtom* aAttribute) const override; + virtual nsSVGString& GetResultImageName() override { return mStringAttributes[RESULT]; } + virtual void GetSourceImageNames(nsTArray<nsSVGStringInfo >& aSources) override; + + // nsIContent interface + NS_IMETHOD_(bool) IsAttributeMapped(const nsIAtom* aAttribute) const override; + + virtual nsresult Clone(mozilla::dom::NodeInfo *aNodeInfo, nsINode **aResult) const override; + + // WebIDL + already_AddRefed<SVGAnimatedString> In1(); + already_AddRefed<SVGAnimatedNumber> Dx(); + already_AddRefed<SVGAnimatedNumber> Dy(); + already_AddRefed<SVGAnimatedNumber> StdDeviationX(); + already_AddRefed<SVGAnimatedNumber> StdDeviationY(); + void SetStdDeviation(float stdDeviationX, float stdDeviationY); + +protected: + virtual NumberAttributesInfo GetNumberInfo() override; + virtual NumberPairAttributesInfo GetNumberPairInfo() override; + virtual StringAttributesInfo GetStringInfo() override; + + enum { DX, DY }; + nsSVGNumber2 mNumberAttributes[2]; + static NumberInfo sNumberInfo[2]; + + enum { STD_DEV }; + nsSVGNumberPair mNumberPairAttributes[1]; + static NumberPairInfo sNumberPairInfo[1]; + + enum { RESULT, IN1 }; + nsSVGString mStringAttributes[2]; + static StringInfo sStringInfo[2]; +}; + +} // namespace dom +} // namespace mozilla + +#endif // mozilla_dom_SVGFEDropShadowElement_h diff --git a/dom/svg/SVGFEFloodElement.cpp b/dom/svg/SVGFEFloodElement.cpp new file mode 100644 index 0000000000..5cacda78e9 --- /dev/null +++ b/dom/svg/SVGFEFloodElement.cpp @@ -0,0 +1,81 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "mozilla/dom/SVGFEFloodElement.h" + +#include "FilterSupport.h" +#include "mozilla/dom/SVGFEFloodElementBinding.h" +#include "nsColor.h" +#include "nsIFrame.h" + +NS_IMPL_NS_NEW_NAMESPACED_SVG_ELEMENT(FEFlood) + +using namespace mozilla::gfx; + +namespace mozilla { +namespace dom { + +JSObject* +SVGFEFloodElement::WrapNode(JSContext *aCx, JS::Handle<JSObject*> aGivenProto) +{ + return SVGFEFloodElementBinding::Wrap(aCx, this, aGivenProto); +} + +nsSVGElement::StringInfo SVGFEFloodElement::sStringInfo[1] = +{ + { &nsGkAtoms::result, kNameSpaceID_None, true } +}; + +//---------------------------------------------------------------------- +// nsIDOMNode methods + +NS_IMPL_ELEMENT_CLONE_WITH_INIT(SVGFEFloodElement) + +FilterPrimitiveDescription +SVGFEFloodElement::GetPrimitiveDescription(nsSVGFilterInstance* aInstance, + const IntRect& aFilterSubregion, + const nsTArray<bool>& aInputsAreTainted, + nsTArray<RefPtr<SourceSurface>>& aInputImages) +{ + FilterPrimitiveDescription descr(PrimitiveType::Flood); + nsIFrame* frame = GetPrimaryFrame(); + if (frame) { + nsStyleContext* style = frame->StyleContext(); + Color color(Color::FromABGR(style->StyleSVGReset()->mFloodColor)); + color.a *= style->StyleSVGReset()->mFloodOpacity; + descr.Attributes().Set(eFloodColor, color); + } else { + descr.Attributes().Set(eFloodColor, Color()); + } + return descr; +} + +//---------------------------------------------------------------------- +// nsIContent methods + +NS_IMETHODIMP_(bool) +SVGFEFloodElement::IsAttributeMapped(const nsIAtom* name) const +{ + static const MappedAttributeEntry* const map[] = { + sFEFloodMap + }; + + return FindAttributeDependence(name, map) || + SVGFEFloodElementBase::IsAttributeMapped(name); +} + +//---------------------------------------------------------------------- +// nsSVGElement methods + +nsSVGElement::StringAttributesInfo +SVGFEFloodElement::GetStringInfo() +{ + return StringAttributesInfo(mStringAttributes, sStringInfo, + ArrayLength(sStringInfo)); +} + +} // namespace dom +} // namespace mozilla diff --git a/dom/svg/SVGFEFloodElement.h b/dom/svg/SVGFEFloodElement.h new file mode 100644 index 0000000000..1390e58aaf --- /dev/null +++ b/dom/svg/SVGFEFloodElement.h @@ -0,0 +1,59 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef mozilla_dom_SVGFEFloodElement_h +#define mozilla_dom_SVGFEFloodElement_h + +#include "nsSVGFilters.h" + +nsresult NS_NewSVGFEFloodElement(nsIContent **aResult, + already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo); + +namespace mozilla { +namespace dom { + +typedef nsSVGFE SVGFEFloodElementBase; + +class SVGFEFloodElement : public SVGFEFloodElementBase +{ + friend nsresult (::NS_NewSVGFEFloodElement(nsIContent **aResult, + already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo)); +protected: + explicit SVGFEFloodElement(already_AddRefed<mozilla::dom::NodeInfo>& aNodeInfo) + : SVGFEFloodElementBase(aNodeInfo) + { + } + virtual JSObject* WrapNode(JSContext *cx, JS::Handle<JSObject*> aGivenProto) override; + +public: + virtual bool SubregionIsUnionOfRegions() override { return false; } + + virtual FilterPrimitiveDescription + GetPrimitiveDescription(nsSVGFilterInstance* aInstance, + const IntRect& aFilterSubregion, + const nsTArray<bool>& aInputsAreTainted, + nsTArray<RefPtr<SourceSurface>>& aInputImages) override; + virtual nsSVGString& GetResultImageName() override { return mStringAttributes[RESULT]; } + + // nsIContent interface + NS_IMETHOD_(bool) IsAttributeMapped(const nsIAtom* aAttribute) const override; + + virtual nsresult Clone(mozilla::dom::NodeInfo *aNodeInfo, nsINode **aResult) const override; + +protected: + virtual bool ProducesSRGB() override { return true; } + + virtual StringAttributesInfo GetStringInfo() override; + + enum { RESULT }; + nsSVGString mStringAttributes[1]; + static StringInfo sStringInfo[1]; +}; + +} // namespace dom +} // namespace mozilla + +#endif // mozilla_dom_SVGFEFloodElement_h diff --git a/dom/svg/SVGFEGaussianBlurElement.cpp b/dom/svg/SVGFEGaussianBlurElement.cpp new file mode 100644 index 0000000000..7d9beeec5f --- /dev/null +++ b/dom/svg/SVGFEGaussianBlurElement.cpp @@ -0,0 +1,122 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "mozilla/dom/SVGFEGaussianBlurElement.h" +#include "mozilla/dom/SVGFEGaussianBlurElementBinding.h" +#include "nsSVGFilterInstance.h" +#include "nsSVGUtils.h" + +NS_IMPL_NS_NEW_NAMESPACED_SVG_ELEMENT(FEGaussianBlur) + +using namespace mozilla::gfx; + +namespace mozilla { +namespace dom { + +JSObject* +SVGFEGaussianBlurElement::WrapNode(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) +{ + return SVGFEGaussianBlurElementBinding::Wrap(aCx, this, aGivenProto); +} + +nsSVGElement::NumberPairInfo SVGFEGaussianBlurElement::sNumberPairInfo[1] = +{ + { &nsGkAtoms::stdDeviation, 0, 0 } +}; + +nsSVGElement::StringInfo SVGFEGaussianBlurElement::sStringInfo[2] = +{ + { &nsGkAtoms::result, kNameSpaceID_None, true }, + { &nsGkAtoms::in, kNameSpaceID_None, true } +}; + +//---------------------------------------------------------------------- +// nsIDOMNode methods + +NS_IMPL_ELEMENT_CLONE_WITH_INIT(SVGFEGaussianBlurElement) + +//---------------------------------------------------------------------- + +already_AddRefed<SVGAnimatedString> +SVGFEGaussianBlurElement::In1() +{ + return mStringAttributes[IN1].ToDOMAnimatedString(this); +} + +already_AddRefed<SVGAnimatedNumber> +SVGFEGaussianBlurElement::StdDeviationX() +{ + return mNumberPairAttributes[STD_DEV].ToDOMAnimatedNumber(nsSVGNumberPair::eFirst, this); +} + +already_AddRefed<SVGAnimatedNumber> +SVGFEGaussianBlurElement::StdDeviationY() +{ + return mNumberPairAttributes[STD_DEV].ToDOMAnimatedNumber(nsSVGNumberPair::eSecond, this); +} + +void +SVGFEGaussianBlurElement::SetStdDeviation(float stdDeviationX, float stdDeviationY) +{ + mNumberPairAttributes[STD_DEV].SetBaseValues(stdDeviationX, stdDeviationY, this); +} + +FilterPrimitiveDescription +SVGFEGaussianBlurElement::GetPrimitiveDescription(nsSVGFilterInstance* aInstance, + const IntRect& aFilterSubregion, + const nsTArray<bool>& aInputsAreTainted, + nsTArray<RefPtr<SourceSurface>>& aInputImages) +{ + float stdX = aInstance->GetPrimitiveNumber(SVGContentUtils::X, + &mNumberPairAttributes[STD_DEV], + nsSVGNumberPair::eFirst); + float stdY = aInstance->GetPrimitiveNumber(SVGContentUtils::Y, + &mNumberPairAttributes[STD_DEV], + nsSVGNumberPair::eSecond); + if (stdX < 0 || stdY < 0) { + return FilterPrimitiveDescription(PrimitiveType::Empty); + } + + FilterPrimitiveDescription descr(PrimitiveType::GaussianBlur); + descr.Attributes().Set(eGaussianBlurStdDeviation, Size(stdX, stdY)); + return descr; +} + +bool +SVGFEGaussianBlurElement::AttributeAffectsRendering(int32_t aNameSpaceID, + nsIAtom* aAttribute) const +{ + return SVGFEGaussianBlurElementBase::AttributeAffectsRendering(aNameSpaceID, aAttribute) || + (aNameSpaceID == kNameSpaceID_None && + (aAttribute == nsGkAtoms::in || + aAttribute == nsGkAtoms::stdDeviation)); +} + +void +SVGFEGaussianBlurElement::GetSourceImageNames(nsTArray<nsSVGStringInfo>& aSources) +{ + aSources.AppendElement(nsSVGStringInfo(&mStringAttributes[IN1], this)); +} + +//---------------------------------------------------------------------- +// nsSVGElement methods + +nsSVGElement::NumberPairAttributesInfo +SVGFEGaussianBlurElement::GetNumberPairInfo() +{ + return NumberPairAttributesInfo(mNumberPairAttributes, sNumberPairInfo, + ArrayLength(sNumberPairInfo)); +} + +nsSVGElement::StringAttributesInfo +SVGFEGaussianBlurElement::GetStringInfo() +{ + return StringAttributesInfo(mStringAttributes, sStringInfo, + ArrayLength(sStringInfo)); +} + +} // namespace dom +} // namespace mozilla diff --git a/dom/svg/SVGFEGaussianBlurElement.h b/dom/svg/SVGFEGaussianBlurElement.h new file mode 100644 index 0000000000..e8a49044fe --- /dev/null +++ b/dom/svg/SVGFEGaussianBlurElement.h @@ -0,0 +1,68 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef mozilla_dom_SVGFEGaussianBlurElement_h +#define mozilla_dom_SVGFEGaussianBlurElement_h + +#include "nsSVGFilters.h" +#include "nsSVGNumberPair.h" +#include "nsSVGString.h" + +nsresult NS_NewSVGFEGaussianBlurElement(nsIContent **aResult, + already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo); + +namespace mozilla { +namespace dom { + +typedef nsSVGFE SVGFEGaussianBlurElementBase; + +class SVGFEGaussianBlurElement : public SVGFEGaussianBlurElementBase +{ + friend nsresult (::NS_NewSVGFEGaussianBlurElement(nsIContent **aResult, + already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo)); +protected: + explicit SVGFEGaussianBlurElement(already_AddRefed<mozilla::dom::NodeInfo>& aNodeInfo) + : SVGFEGaussianBlurElementBase(aNodeInfo) + { + } + virtual JSObject* WrapNode(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override; + +public: + virtual FilterPrimitiveDescription + GetPrimitiveDescription(nsSVGFilterInstance* aInstance, + const IntRect& aFilterSubregion, + const nsTArray<bool>& aInputsAreTainted, + nsTArray<RefPtr<SourceSurface>>& aInputImages) override; + virtual bool AttributeAffectsRendering( + int32_t aNameSpaceID, nsIAtom* aAttribute) const override; + virtual nsSVGString& GetResultImageName() override { return mStringAttributes[RESULT]; } + virtual void GetSourceImageNames(nsTArray<nsSVGStringInfo >& aSources) override; + + virtual nsresult Clone(mozilla::dom::NodeInfo *aNodeInfo, nsINode **aResult) const override; + + // WebIDL + already_AddRefed<SVGAnimatedString> In1(); + already_AddRefed<SVGAnimatedNumber> StdDeviationX(); + already_AddRefed<SVGAnimatedNumber> StdDeviationY(); + void SetStdDeviation(float stdDeviationX, float stdDeviationY); + +protected: + virtual NumberPairAttributesInfo GetNumberPairInfo() override; + virtual StringAttributesInfo GetStringInfo() override; + + enum { STD_DEV }; + nsSVGNumberPair mNumberPairAttributes[1]; + static NumberPairInfo sNumberPairInfo[1]; + + enum { RESULT, IN1 }; + nsSVGString mStringAttributes[2]; + static StringInfo sStringInfo[2]; +}; + +} // namespace dom +} // namespace mozilla + +#endif // mozilla_dom_SVGFEGaussianBlurElement_h diff --git a/dom/svg/SVGFEImageElement.cpp b/dom/svg/SVGFEImageElement.cpp new file mode 100644 index 0000000000..f235d5ccbf --- /dev/null +++ b/dom/svg/SVGFEImageElement.cpp @@ -0,0 +1,378 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "mozilla/dom/SVGFEImageElement.h" + +#include "mozilla/EventStates.h" +#include "mozilla/dom/SVGFEImageElementBinding.h" +#include "mozilla/dom/SVGFilterElement.h" +#include "mozilla/gfx/2D.h" +#include "mozilla/RefPtr.h" +#include "nsContentUtils.h" +#include "nsLayoutUtils.h" +#include "nsSVGUtils.h" +#include "nsNetUtil.h" +#include "imgIContainer.h" +#include "gfx2DGlue.h" + +NS_IMPL_NS_NEW_NAMESPACED_SVG_ELEMENT(FEImage) + +using namespace mozilla::gfx; + +namespace mozilla { +namespace dom { + +JSObject* +SVGFEImageElement::WrapNode(JSContext *aCx, JS::Handle<JSObject*> aGivenProto) +{ + return SVGFEImageElementBinding::Wrap(aCx, this, aGivenProto); +} + +nsSVGElement::StringInfo SVGFEImageElement::sStringInfo[3] = +{ + { &nsGkAtoms::result, kNameSpaceID_None, true }, + { &nsGkAtoms::href, kNameSpaceID_None, true }, + { &nsGkAtoms::href, kNameSpaceID_XLink, true } +}; + +//---------------------------------------------------------------------- +// nsISupports methods + +NS_IMPL_ISUPPORTS_INHERITED(SVGFEImageElement, SVGFEImageElementBase, + nsIDOMNode, nsIDOMElement, nsIDOMSVGElement, + imgINotificationObserver, nsIImageLoadingContent, + imgIOnloadBlocker) + +//---------------------------------------------------------------------- +// Implementation + +SVGFEImageElement::SVGFEImageElement(already_AddRefed<mozilla::dom::NodeInfo>& aNodeInfo) + : SVGFEImageElementBase(aNodeInfo) +{ + // We start out broken + AddStatesSilently(NS_EVENT_STATE_BROKEN); +} + +SVGFEImageElement::~SVGFEImageElement() +{ + DestroyImageLoadingContent(); +} + +//---------------------------------------------------------------------- + +nsresult +SVGFEImageElement::LoadSVGImage(bool aForce, bool aNotify) +{ + // resolve href attribute + nsCOMPtr<nsIURI> baseURI = GetBaseURI(); + + nsAutoString href; + if (mStringAttributes[HREF].IsExplicitlySet()) { + mStringAttributes[HREF].GetAnimValue(href, this); + } else { + mStringAttributes[XLINK_HREF].GetAnimValue(href, this); + } + href.Trim(" \t\n\r"); + + if (baseURI && !href.IsEmpty()) + NS_MakeAbsoluteURI(href, href, baseURI); + + // Make sure we don't get in a recursive death-spiral + nsIDocument* doc = OwnerDoc(); + nsCOMPtr<nsIURI> hrefAsURI; + if (NS_SUCCEEDED(StringToURI(href, doc, getter_AddRefs(hrefAsURI)))) { + bool isEqual; + if (NS_SUCCEEDED(hrefAsURI->Equals(baseURI, &isEqual)) && isEqual) { + // Image URI matches our URI exactly! Bail out. + return NS_OK; + } + } + + return LoadImage(href, aForce, aNotify, eImageLoadType_Normal); +} + +//---------------------------------------------------------------------- +// EventTarget methods: + +void +SVGFEImageElement::AsyncEventRunning(AsyncEventDispatcher* aEvent) +{ + nsImageLoadingContent::AsyncEventRunning(aEvent); +} + +//---------------------------------------------------------------------- +// nsIContent methods: + +NS_IMETHODIMP_(bool) +SVGFEImageElement::IsAttributeMapped(const nsIAtom* name) const +{ + static const MappedAttributeEntry* const map[] = { + sGraphicsMap + }; + + return FindAttributeDependence(name, map) || + SVGFEImageElementBase::IsAttributeMapped(name); +} + +nsresult +SVGFEImageElement::AfterSetAttr(int32_t aNamespaceID, nsIAtom* aName, + const nsAttrValue* aValue, bool aNotify) +{ + if (aName == nsGkAtoms::href && + (aNamespaceID == kNameSpaceID_XLink || + aNamespaceID == kNameSpaceID_None)) { + + // If there isn't a frame we still need to load the image in case + // the frame is created later e.g. by attaching to a document. + // If there is a frame then it should deal with loading as the image + // url may be animated. + if (!GetPrimaryFrame()) { + if (aValue) { + LoadSVGImage(true, aNotify); + } else { + CancelImageRequests(aNotify); + } + } + } + + return SVGFEImageElementBase::AfterSetAttr(aNamespaceID, aName, + aValue, aNotify); +} + +void +SVGFEImageElement::MaybeLoadSVGImage() +{ + if ((mStringAttributes[HREF].IsExplicitlySet() || + mStringAttributes[XLINK_HREF].IsExplicitlySet() ) && + (NS_FAILED(LoadSVGImage(false, true)) || + !LoadingEnabled())) { + CancelImageRequests(true); + } +} + +nsresult +SVGFEImageElement::BindToTree(nsIDocument* aDocument, nsIContent* aParent, + nsIContent* aBindingParent, + bool aCompileEventHandlers) +{ + nsresult rv = SVGFEImageElementBase::BindToTree(aDocument, aParent, + aBindingParent, + aCompileEventHandlers); + NS_ENSURE_SUCCESS(rv, rv); + + nsImageLoadingContent::BindToTree(aDocument, aParent, aBindingParent, + aCompileEventHandlers); + + if (mStringAttributes[HREF].IsExplicitlySet() || + mStringAttributes[XLINK_HREF].IsExplicitlySet()) { + // FIXME: Bug 660963 it would be nice if we could just have + // ClearBrokenState update our state and do it fast... + ClearBrokenState(); + RemoveStatesSilently(NS_EVENT_STATE_BROKEN); + nsContentUtils::AddScriptRunner( + NewRunnableMethod(this, &SVGFEImageElement::MaybeLoadSVGImage)); + } + + return rv; +} + +void +SVGFEImageElement::UnbindFromTree(bool aDeep, bool aNullParent) +{ + nsImageLoadingContent::UnbindFromTree(aDeep, aNullParent); + SVGFEImageElementBase::UnbindFromTree(aDeep, aNullParent); +} + +EventStates +SVGFEImageElement::IntrinsicState() const +{ + return SVGFEImageElementBase::IntrinsicState() | + nsImageLoadingContent::ImageState(); +} + +//---------------------------------------------------------------------- +// nsIDOMNode methods + +NS_IMPL_ELEMENT_CLONE_WITH_INIT(SVGFEImageElement) + +already_AddRefed<SVGAnimatedString> +SVGFEImageElement::Href() +{ + return mStringAttributes[HREF].IsExplicitlySet() + ? mStringAttributes[HREF].ToDOMAnimatedString(this) + : mStringAttributes[XLINK_HREF].ToDOMAnimatedString(this); +} + +//---------------------------------------------------------------------- +// nsIDOMSVGFEImageElement methods + +FilterPrimitiveDescription +SVGFEImageElement::GetPrimitiveDescription(nsSVGFilterInstance* aInstance, + const IntRect& aFilterSubregion, + const nsTArray<bool>& aInputsAreTainted, + nsTArray<RefPtr<SourceSurface>>& aInputImages) +{ + nsIFrame* frame = GetPrimaryFrame(); + if (!frame) { + return FilterPrimitiveDescription(PrimitiveType::Empty); + } + + nsCOMPtr<imgIRequest> currentRequest; + GetRequest(nsIImageLoadingContent::CURRENT_REQUEST, + getter_AddRefs(currentRequest)); + + nsCOMPtr<imgIContainer> imageContainer; + if (currentRequest) { + currentRequest->GetImage(getter_AddRefs(imageContainer)); + } + + RefPtr<SourceSurface> image; + if (imageContainer) { + image = imageContainer->GetFrame(imgIContainer::FRAME_CURRENT, + imgIContainer::FLAG_SYNC_DECODE); + } + + if (!image) { + return FilterPrimitiveDescription(PrimitiveType::Empty); + } + + IntSize nativeSize; + imageContainer->GetWidth(&nativeSize.width); + imageContainer->GetHeight(&nativeSize.height); + + Matrix viewBoxTM = + SVGContentUtils::GetViewBoxTransform(aFilterSubregion.width, aFilterSubregion.height, + 0, 0, nativeSize.width, nativeSize.height, + mPreserveAspectRatio); + Matrix TM = viewBoxTM; + TM.PostTranslate(aFilterSubregion.x, aFilterSubregion.y); + + SamplingFilter samplingFilter = nsLayoutUtils::GetSamplingFilterForFrame(frame); + + FilterPrimitiveDescription descr(PrimitiveType::Image); + descr.Attributes().Set(eImageFilter, (uint32_t)samplingFilter); + descr.Attributes().Set(eImageTransform, TM); + + // Append the image to aInputImages and store its index in the description. + size_t imageIndex = aInputImages.Length(); + aInputImages.AppendElement(image); + descr.Attributes().Set(eImageInputIndex, (uint32_t)imageIndex); + + return descr; +} + +bool +SVGFEImageElement::AttributeAffectsRendering(int32_t aNameSpaceID, + nsIAtom* aAttribute) const +{ + // nsGkAtoms::href is deliberately omitted as the frame has special + // handling to load the image + return SVGFEImageElementBase::AttributeAffectsRendering(aNameSpaceID, aAttribute) || + (aNameSpaceID == kNameSpaceID_None && + aAttribute == nsGkAtoms::preserveAspectRatio); +} + +bool +SVGFEImageElement::OutputIsTainted(const nsTArray<bool>& aInputsAreTainted, + nsIPrincipal* aReferencePrincipal) +{ + nsresult rv; + nsCOMPtr<imgIRequest> currentRequest; + GetRequest(nsIImageLoadingContent::CURRENT_REQUEST, + getter_AddRefs(currentRequest)); + + if (!currentRequest) { + return false; + } + + uint32_t status; + currentRequest->GetImageStatus(&status); + if ((status & imgIRequest::STATUS_LOAD_COMPLETE) == 0) { + // The load has not completed yet. + return false; + } + + nsCOMPtr<nsIPrincipal> principal; + rv = currentRequest->GetImagePrincipal(getter_AddRefs(principal)); + if (NS_FAILED(rv) || !principal) { + return true; + } + + int32_t corsmode; + if (NS_SUCCEEDED(currentRequest->GetCORSMode(&corsmode)) && + corsmode != imgIRequest::CORS_NONE) { + // If CORS was used to load the image, the page is allowed to read from it. + return false; + } + + if (aReferencePrincipal->Subsumes(principal)) { + // The page is allowed to read from the image. + return false; + } + + return true; +} + +//---------------------------------------------------------------------- +// nsSVGElement methods + +already_AddRefed<DOMSVGAnimatedPreserveAspectRatio> +SVGFEImageElement::PreserveAspectRatio() +{ + return mPreserveAspectRatio.ToDOMAnimatedPreserveAspectRatio(this); +} + +SVGAnimatedPreserveAspectRatio * +SVGFEImageElement::GetPreserveAspectRatio() +{ + return &mPreserveAspectRatio; +} + +nsSVGElement::StringAttributesInfo +SVGFEImageElement::GetStringInfo() +{ + return StringAttributesInfo(mStringAttributes, sStringInfo, + ArrayLength(sStringInfo)); +} + +//---------------------------------------------------------------------- +// imgINotificationObserver methods + +NS_IMETHODIMP +SVGFEImageElement::Notify(imgIRequest* aRequest, int32_t aType, const nsIntRect* aData) +{ + nsresult rv = nsImageLoadingContent::Notify(aRequest, aType, aData); + + if (aType == imgINotificationObserver::SIZE_AVAILABLE) { + // Request a decode + nsCOMPtr<imgIContainer> container; + aRequest->GetImage(getter_AddRefs(container)); + MOZ_ASSERT(container, "who sent the notification then?"); + container->StartDecoding(); + } + + if (aType == imgINotificationObserver::LOAD_COMPLETE || + aType == imgINotificationObserver::FRAME_UPDATE || + aType == imgINotificationObserver::SIZE_AVAILABLE) { + Invalidate(); + } + + return rv; +} + +//---------------------------------------------------------------------- +// helper methods + +void +SVGFEImageElement::Invalidate() +{ + if (GetParent() && GetParent()->IsSVGElement(nsGkAtoms::filter)) { + static_cast<SVGFilterElement*>(GetParent())->Invalidate(); + } +} + +} // namespace dom +} // namespace mozilla diff --git a/dom/svg/SVGFEImageElement.h b/dom/svg/SVGFEImageElement.h new file mode 100644 index 0000000000..edf6f065db --- /dev/null +++ b/dom/svg/SVGFEImageElement.h @@ -0,0 +1,98 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef mozilla_dom_SVGFEImageElement_h +#define mozilla_dom_SVGFEImageElement_h + +#include "nsSVGFilters.h" +#include "SVGAnimatedPreserveAspectRatio.h" + +class SVGFEImageFrame; + +nsresult NS_NewSVGFEImageElement(nsIContent **aResult, + already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo); + +namespace mozilla { +namespace dom { + +typedef nsSVGFE SVGFEImageElementBase; + +class SVGFEImageElement final : public SVGFEImageElementBase, + public nsImageLoadingContent +{ + friend class ::SVGFEImageFrame; + +protected: + friend nsresult (::NS_NewSVGFEImageElement(nsIContent **aResult, + already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo)); + explicit SVGFEImageElement(already_AddRefed<mozilla::dom::NodeInfo>& aNodeInfo); + virtual ~SVGFEImageElement(); + virtual JSObject* WrapNode(JSContext *aCx, JS::Handle<JSObject*> aGivenProto) override; + +public: + virtual bool SubregionIsUnionOfRegions() override { return false; } + + // interfaces: + NS_DECL_ISUPPORTS_INHERITED + + // EventTarget + virtual void AsyncEventRunning(AsyncEventDispatcher* aEvent) override; + + virtual FilterPrimitiveDescription + GetPrimitiveDescription(nsSVGFilterInstance* aInstance, + const IntRect& aFilterSubregion, + const nsTArray<bool>& aInputsAreTainted, + nsTArray<RefPtr<SourceSurface>>& aInputImages) override; + virtual bool AttributeAffectsRendering( + int32_t aNameSpaceID, nsIAtom* aAttribute) const override; + virtual nsSVGString& GetResultImageName() override { return mStringAttributes[RESULT]; } + virtual bool OutputIsTainted(const nsTArray<bool>& aInputsAreTainted, + nsIPrincipal* aReferencePrincipal) override; + + // nsIContent + NS_IMETHOD_(bool) IsAttributeMapped(const nsIAtom* aAttribute) const override; + + virtual nsresult Clone(mozilla::dom::NodeInfo *aNodeInfo, nsINode **aResult) const override; + + virtual nsresult AfterSetAttr(int32_t aNamespaceID, nsIAtom* aName, + const nsAttrValue* aValue, bool aNotify) override; + virtual nsresult BindToTree(nsIDocument* aDocument, nsIContent* aParent, + nsIContent* aBindingParent, + bool aCompileEventHandlers) override; + virtual void UnbindFromTree(bool aDeep, bool aNullParent) override; + virtual EventStates IntrinsicState() const override; + + NS_IMETHOD Notify(imgIRequest *aRequest, int32_t aType, const nsIntRect* aData) override; + + void MaybeLoadSVGImage(); + + // WebIDL + already_AddRefed<SVGAnimatedString> Href(); + already_AddRefed<DOMSVGAnimatedPreserveAspectRatio> PreserveAspectRatio(); + +private: + // Invalidate users of the filter containing this element. + void Invalidate(); + + nsresult LoadSVGImage(bool aForce, bool aNotify); + +protected: + virtual bool ProducesSRGB() override { return true; } + + virtual SVGAnimatedPreserveAspectRatio *GetPreserveAspectRatio() override; + virtual StringAttributesInfo GetStringInfo() override; + + enum { RESULT, HREF, XLINK_HREF }; + nsSVGString mStringAttributes[3]; + static StringInfo sStringInfo[3]; + + SVGAnimatedPreserveAspectRatio mPreserveAspectRatio; +}; + +} // namespace dom +} // namespace mozilla + +#endif diff --git a/dom/svg/SVGFEMergeElement.cpp b/dom/svg/SVGFEMergeElement.cpp new file mode 100644 index 0000000000..3147648b55 --- /dev/null +++ b/dom/svg/SVGFEMergeElement.cpp @@ -0,0 +1,64 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "mozilla/dom/SVGFEMergeElement.h" +#include "mozilla/dom/SVGFEMergeElementBinding.h" +#include "mozilla/dom/SVGFEMergeNodeElement.h" + +NS_IMPL_NS_NEW_NAMESPACED_SVG_ELEMENT(FEMerge) + +using namespace mozilla::gfx; + +namespace mozilla { +namespace dom { + +JSObject* +SVGFEMergeElement::WrapNode(JSContext *aCx, JS::Handle<JSObject*> aGivenProto) +{ + return SVGFEMergeElementBinding::Wrap(aCx, this, aGivenProto); +} + +nsSVGElement::StringInfo SVGFEMergeElement::sStringInfo[1] = +{ + { &nsGkAtoms::result, kNameSpaceID_None, true } +}; + +NS_IMPL_ELEMENT_CLONE_WITH_INIT(SVGFEMergeElement) + +FilterPrimitiveDescription +SVGFEMergeElement::GetPrimitiveDescription(nsSVGFilterInstance* aInstance, + const IntRect& aFilterSubregion, + const nsTArray<bool>& aInputsAreTainted, + nsTArray<RefPtr<SourceSurface>>& aInputImages) +{ + return FilterPrimitiveDescription(PrimitiveType::Merge); +} + +void +SVGFEMergeElement::GetSourceImageNames(nsTArray<nsSVGStringInfo>& aSources) +{ + for (nsIContent* child = nsINode::GetFirstChild(); + child; + child = child->GetNextSibling()) { + if (child->IsSVGElement(nsGkAtoms::feMergeNode)) { + SVGFEMergeNodeElement* node = static_cast<SVGFEMergeNodeElement*>(child); + aSources.AppendElement(nsSVGStringInfo(node->GetIn1(), node)); + } + } +} + +//---------------------------------------------------------------------- +// nsSVGElement methods + +nsSVGElement::StringAttributesInfo +SVGFEMergeElement::GetStringInfo() +{ + return StringAttributesInfo(mStringAttributes, sStringInfo, + ArrayLength(sStringInfo)); +} + +} // namespace dom +} // namespace mozilla diff --git a/dom/svg/SVGFEMergeElement.h b/dom/svg/SVGFEMergeElement.h new file mode 100644 index 0000000000..07305c28d2 --- /dev/null +++ b/dom/svg/SVGFEMergeElement.h @@ -0,0 +1,53 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef mozilla_dom_SVGFEMergeElement_h +#define mozilla_dom_SVGFEMergeElement_h + +#include "nsSVGFilters.h" + +nsresult NS_NewSVGFEMergeElement(nsIContent **aResult, + already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo); + +namespace mozilla { +namespace dom { + +typedef nsSVGFE SVGFEMergeElementBase; + +class SVGFEMergeElement : public SVGFEMergeElementBase +{ + friend nsresult (::NS_NewSVGFEMergeElement(nsIContent **aResult, + already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo)); +protected: + explicit SVGFEMergeElement(already_AddRefed<mozilla::dom::NodeInfo>& aNodeInfo) + : SVGFEMergeElementBase(aNodeInfo) + { + } + virtual JSObject* WrapNode(JSContext *cx, JS::Handle<JSObject*> aGivenProto) override; + +public: + virtual FilterPrimitiveDescription + GetPrimitiveDescription(nsSVGFilterInstance* aInstance, + const IntRect& aFilterSubregion, + const nsTArray<bool>& aInputsAreTainted, + nsTArray<RefPtr<SourceSurface>>& aInputImages) override; + virtual nsSVGString& GetResultImageName() override { return mStringAttributes[RESULT]; } + virtual void GetSourceImageNames(nsTArray<nsSVGStringInfo>& aSources) override; + + // nsIContent + virtual nsresult Clone(mozilla::dom::NodeInfo *aNodeInfo, nsINode **aResult) const override; +protected: + virtual StringAttributesInfo GetStringInfo() override; + + enum { RESULT }; + nsSVGString mStringAttributes[1]; + static StringInfo sStringInfo[1]; +}; + +} // namespace dom +} // namespace mozilla + +#endif // mozilla_dom_SVGFEMergeElement_h diff --git a/dom/svg/SVGFEMergeNodeElement.cpp b/dom/svg/SVGFEMergeNodeElement.cpp new file mode 100644 index 0000000000..5a567ef8ab --- /dev/null +++ b/dom/svg/SVGFEMergeNodeElement.cpp @@ -0,0 +1,58 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "mozilla/dom/SVGFEMergeNodeElement.h" +#include "mozilla/dom/SVGFEMergeNodeElementBinding.h" + +NS_IMPL_NS_NEW_NAMESPACED_SVG_ELEMENT(FEMergeNode) + +namespace mozilla { +namespace dom { + +JSObject* +SVGFEMergeNodeElement::WrapNode(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) +{ + return SVGFEMergeNodeElementBinding::Wrap(aCx, this, aGivenProto); +} + +nsSVGElement::StringInfo SVGFEMergeNodeElement::sStringInfo[1] = +{ + { &nsGkAtoms::in, kNameSpaceID_None, true } +}; + +//---------------------------------------------------------------------- +// nsIDOMNode methods + +NS_IMPL_ELEMENT_CLONE_WITH_INIT(SVGFEMergeNodeElement) + +//---------------------------------------------------------------------- +// nsFEUnstyledElement methods + +bool +SVGFEMergeNodeElement::AttributeAffectsRendering(int32_t aNameSpaceID, + nsIAtom* aAttribute) const +{ + return aNameSpaceID == kNameSpaceID_None && aAttribute == nsGkAtoms::in; +} + +already_AddRefed<SVGAnimatedString> +SVGFEMergeNodeElement::In1() +{ + return mStringAttributes[IN1].ToDOMAnimatedString(this); +} + +//---------------------------------------------------------------------- +// nsSVGElement methods + +nsSVGElement::StringAttributesInfo +SVGFEMergeNodeElement::GetStringInfo() +{ + return StringAttributesInfo(mStringAttributes, sStringInfo, + ArrayLength(sStringInfo)); +} + +} // namespace dom +} // namespace mozilla diff --git a/dom/svg/SVGFEMergeNodeElement.h b/dom/svg/SVGFEMergeNodeElement.h new file mode 100644 index 0000000000..04c623ea4a --- /dev/null +++ b/dom/svg/SVGFEMergeNodeElement.h @@ -0,0 +1,53 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef mozilla_dom_SVGFEMergeNodeElement_h +#define mozilla_dom_SVGFEMergeNodeElement_h + +#include "nsSVGFilters.h" + +nsresult NS_NewSVGFEMergeNodeElement(nsIContent** aResult, + already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo); + +namespace mozilla { +namespace dom { + +typedef SVGFEUnstyledElement SVGFEMergeNodeElementBase; + +class SVGFEMergeNodeElement : public SVGFEMergeNodeElementBase +{ + friend nsresult (::NS_NewSVGFEMergeNodeElement(nsIContent **aResult, + already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo)); +protected: + explicit SVGFEMergeNodeElement(already_AddRefed<mozilla::dom::NodeInfo>& aNodeInfo) + : SVGFEMergeNodeElementBase(aNodeInfo) + { + } + virtual JSObject* WrapNode(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override; + +public: + virtual nsresult Clone(mozilla::dom::NodeInfo *aNodeInfo, nsINode **aResult) const override; + + virtual bool AttributeAffectsRendering( + int32_t aNameSpaceID, nsIAtom* aAttribute) const override; + + const nsSVGString* GetIn1() { return &mStringAttributes[IN1]; } + + // WebIDL + already_AddRefed<SVGAnimatedString> In1(); + +protected: + virtual StringAttributesInfo GetStringInfo() override; + + enum { IN1 }; + nsSVGString mStringAttributes[1]; + static StringInfo sStringInfo[1]; +}; + +} // namespace dom +} // namespace mozilla + +#endif // mozilla_dom_SVGFEMergeNodeElement_h diff --git a/dom/svg/SVGFEMorphologyElement.cpp b/dom/svg/SVGFEMorphologyElement.cpp new file mode 100644 index 0000000000..3d2de3a0f5 --- /dev/null +++ b/dom/svg/SVGFEMorphologyElement.cpp @@ -0,0 +1,166 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "mozilla/dom/SVGFEMorphologyElement.h" +#include "mozilla/dom/SVGFEMorphologyElementBinding.h" +#include "nsSVGFilterInstance.h" + +NS_IMPL_NS_NEW_NAMESPACED_SVG_ELEMENT(FEMorphology) + +using namespace mozilla::gfx; + +namespace mozilla { +namespace dom { + +JSObject* +SVGFEMorphologyElement::WrapNode(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) +{ + return SVGFEMorphologyElementBinding::Wrap(aCx, this, aGivenProto); +} + +nsSVGElement::NumberPairInfo SVGFEMorphologyElement::sNumberPairInfo[1] = +{ + { &nsGkAtoms::radius, 0, 0 } +}; + +nsSVGEnumMapping SVGFEMorphologyElement::sOperatorMap[] = { + {&nsGkAtoms::erode, SVG_OPERATOR_ERODE}, + {&nsGkAtoms::dilate, SVG_OPERATOR_DILATE}, + {nullptr, 0} +}; + +nsSVGElement::EnumInfo SVGFEMorphologyElement::sEnumInfo[1] = +{ + { &nsGkAtoms::_operator, + sOperatorMap, + SVG_OPERATOR_ERODE + } +}; + +nsSVGElement::StringInfo SVGFEMorphologyElement::sStringInfo[2] = +{ + { &nsGkAtoms::result, kNameSpaceID_None, true }, + { &nsGkAtoms::in, kNameSpaceID_None, true } +}; + +//---------------------------------------------------------------------- +// nsIDOMNode methods + + +NS_IMPL_ELEMENT_CLONE_WITH_INIT(SVGFEMorphologyElement) + + +//---------------------------------------------------------------------- +// SVGFEMorphologyElement methods + +already_AddRefed<SVGAnimatedString> +SVGFEMorphologyElement::In1() +{ + return mStringAttributes[IN1].ToDOMAnimatedString(this); +} + +already_AddRefed<SVGAnimatedEnumeration> +SVGFEMorphologyElement::Operator() +{ + return mEnumAttributes[OPERATOR].ToDOMAnimatedEnum(this); +} + +already_AddRefed<SVGAnimatedNumber> +SVGFEMorphologyElement::RadiusX() +{ + return mNumberPairAttributes[RADIUS].ToDOMAnimatedNumber(nsSVGNumberPair::eFirst, this); +} + +already_AddRefed<SVGAnimatedNumber> +SVGFEMorphologyElement::RadiusY() +{ + return mNumberPairAttributes[RADIUS].ToDOMAnimatedNumber(nsSVGNumberPair::eSecond, this); +} + +void +SVGFEMorphologyElement::SetRadius(float rx, float ry) +{ + mNumberPairAttributes[RADIUS].SetBaseValues(rx, ry, this); +} + +void +SVGFEMorphologyElement::GetSourceImageNames(nsTArray<nsSVGStringInfo>& aSources) +{ + aSources.AppendElement(nsSVGStringInfo(&mStringAttributes[IN1], this)); +} + +#define MORPHOLOGY_EPSILON 0.0001 + +void +SVGFEMorphologyElement::GetRXY(int32_t *aRX, int32_t *aRY, + const nsSVGFilterInstance& aInstance) +{ + // Subtract an epsilon here because we don't want a value that's just + // slightly larger than an integer to round up to the next integer; it's + // probably meant to be the integer it's close to, modulo machine precision + // issues. + *aRX = NSToIntCeil(aInstance.GetPrimitiveNumber(SVGContentUtils::X, + &mNumberPairAttributes[RADIUS], + nsSVGNumberPair::eFirst) - + MORPHOLOGY_EPSILON); + *aRY = NSToIntCeil(aInstance.GetPrimitiveNumber(SVGContentUtils::Y, + &mNumberPairAttributes[RADIUS], + nsSVGNumberPair::eSecond) - + MORPHOLOGY_EPSILON); +} + +FilterPrimitiveDescription +SVGFEMorphologyElement::GetPrimitiveDescription(nsSVGFilterInstance* aInstance, + const IntRect& aFilterSubregion, + const nsTArray<bool>& aInputsAreTainted, + nsTArray<RefPtr<SourceSurface>>& aInputImages) +{ + int32_t rx, ry; + GetRXY(&rx, &ry, *aInstance); + FilterPrimitiveDescription descr(PrimitiveType::Morphology); + descr.Attributes().Set(eMorphologyRadii, Size(rx, ry)); + descr.Attributes().Set(eMorphologyOperator, + (uint32_t)mEnumAttributes[OPERATOR].GetAnimValue()); + return descr; +} + +bool +SVGFEMorphologyElement::AttributeAffectsRendering(int32_t aNameSpaceID, + nsIAtom* aAttribute) const +{ + return SVGFEMorphologyElementBase::AttributeAffectsRendering(aNameSpaceID, aAttribute) || + (aNameSpaceID == kNameSpaceID_None && + (aAttribute == nsGkAtoms::in || + aAttribute == nsGkAtoms::radius || + aAttribute == nsGkAtoms::_operator)); +} + +//---------------------------------------------------------------------- +// nsSVGElement methods + +nsSVGElement::NumberPairAttributesInfo +SVGFEMorphologyElement::GetNumberPairInfo() +{ + return NumberPairAttributesInfo(mNumberPairAttributes, sNumberPairInfo, + ArrayLength(sNumberPairInfo)); +} + +nsSVGElement::EnumAttributesInfo +SVGFEMorphologyElement::GetEnumInfo() +{ + return EnumAttributesInfo(mEnumAttributes, sEnumInfo, + ArrayLength(sEnumInfo)); +} + +nsSVGElement::StringAttributesInfo +SVGFEMorphologyElement::GetStringInfo() +{ + return StringAttributesInfo(mStringAttributes, sStringInfo, + ArrayLength(sStringInfo)); +} + +} // namespace dom +} // namespace mozilla diff --git a/dom/svg/SVGFEMorphologyElement.h b/dom/svg/SVGFEMorphologyElement.h new file mode 100644 index 0000000000..bc137eaaba --- /dev/null +++ b/dom/svg/SVGFEMorphologyElement.h @@ -0,0 +1,78 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef mozilla_dom_SVGFEMorphologyElement_h +#define mozilla_dom_SVGFEMorphologyElement_h + +#include "nsSVGEnum.h" +#include "nsSVGFilters.h" +#include "nsSVGNumberPair.h" +#include "nsSVGString.h" + +nsresult NS_NewSVGFEMorphologyElement(nsIContent **aResult, + already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo); + +namespace mozilla { +namespace dom { + +typedef nsSVGFE SVGFEMorphologyElementBase; + +class SVGFEMorphologyElement : public SVGFEMorphologyElementBase +{ + friend nsresult (::NS_NewSVGFEMorphologyElement(nsIContent **aResult, + already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo)); +protected: + explicit SVGFEMorphologyElement(already_AddRefed<mozilla::dom::NodeInfo>& aNodeInfo) + : SVGFEMorphologyElementBase(aNodeInfo) + { + } + virtual JSObject* WrapNode(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override; + +public: + virtual FilterPrimitiveDescription + GetPrimitiveDescription(nsSVGFilterInstance* aInstance, + const IntRect& aFilterSubregion, + const nsTArray<bool>& aInputsAreTainted, + nsTArray<RefPtr<SourceSurface>>& aInputImages) override; + virtual bool AttributeAffectsRendering( + int32_t aNameSpaceID, nsIAtom* aAttribute) const override; + virtual nsSVGString& GetResultImageName() override { return mStringAttributes[RESULT]; } + virtual void GetSourceImageNames(nsTArray<nsSVGStringInfo>& aSources) override; + + virtual nsresult Clone(mozilla::dom::NodeInfo *aNodeInfo, nsINode **aResult) const override; + + // WebIDL + already_AddRefed<SVGAnimatedString> In1(); + already_AddRefed<SVGAnimatedEnumeration> Operator(); + already_AddRefed<SVGAnimatedNumber> RadiusX(); + already_AddRefed<SVGAnimatedNumber> RadiusY(); + void SetRadius(float rx, float ry); + +protected: + void GetRXY(int32_t *aRX, int32_t *aRY, const nsSVGFilterInstance& aInstance); + + virtual NumberPairAttributesInfo GetNumberPairInfo() override; + virtual EnumAttributesInfo GetEnumInfo() override; + virtual StringAttributesInfo GetStringInfo() override; + + enum { RADIUS }; + nsSVGNumberPair mNumberPairAttributes[1]; + static NumberPairInfo sNumberPairInfo[1]; + + enum { OPERATOR }; + nsSVGEnum mEnumAttributes[1]; + static nsSVGEnumMapping sOperatorMap[]; + static EnumInfo sEnumInfo[1]; + + enum { RESULT, IN1 }; + nsSVGString mStringAttributes[2]; + static StringInfo sStringInfo[2]; +}; + +} // namespace dom +} // namespace mozilla + +#endif // mozilla_dom_SVGFEMorphologyElement_h diff --git a/dom/svg/SVGFEOffsetElement.cpp b/dom/svg/SVGFEOffsetElement.cpp new file mode 100644 index 0000000000..f7d9b9cbe2 --- /dev/null +++ b/dom/svg/SVGFEOffsetElement.cpp @@ -0,0 +1,113 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "mozilla/dom/SVGFEOffsetElement.h" +#include "mozilla/dom/SVGFEOffsetElementBinding.h" +#include "nsSVGFilterInstance.h" + +NS_IMPL_NS_NEW_NAMESPACED_SVG_ELEMENT(FEOffset) + +using namespace mozilla::gfx; + +namespace mozilla { +namespace dom { + +JSObject* +SVGFEOffsetElement::WrapNode(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) +{ + return SVGFEOffsetElementBinding::Wrap(aCx, this, aGivenProto); +} + +nsSVGElement::NumberInfo SVGFEOffsetElement::sNumberInfo[2] = +{ + { &nsGkAtoms::dx, 0, false }, + { &nsGkAtoms::dy, 0, false } +}; + +nsSVGElement::StringInfo SVGFEOffsetElement::sStringInfo[2] = +{ + { &nsGkAtoms::result, kNameSpaceID_None, true }, + { &nsGkAtoms::in, kNameSpaceID_None, true } +}; + +//---------------------------------------------------------------------- +// nsIDOMNode methods + + +NS_IMPL_ELEMENT_CLONE_WITH_INIT(SVGFEOffsetElement) + + +//---------------------------------------------------------------------- + +already_AddRefed<SVGAnimatedString> +SVGFEOffsetElement::In1() +{ + return mStringAttributes[IN1].ToDOMAnimatedString(this); +} + +already_AddRefed<SVGAnimatedNumber> +SVGFEOffsetElement::Dx() +{ + return mNumberAttributes[DX].ToDOMAnimatedNumber(this); +} + +already_AddRefed<SVGAnimatedNumber> +SVGFEOffsetElement::Dy() +{ + return mNumberAttributes[DY].ToDOMAnimatedNumber(this); +} + +FilterPrimitiveDescription +SVGFEOffsetElement::GetPrimitiveDescription(nsSVGFilterInstance* aInstance, + const IntRect& aFilterSubregion, + const nsTArray<bool>& aInputsAreTainted, + nsTArray<RefPtr<SourceSurface>>& aInputImages) +{ + FilterPrimitiveDescription descr(PrimitiveType::Offset); + IntPoint offset(int32_t(aInstance->GetPrimitiveNumber( + SVGContentUtils::X, &mNumberAttributes[DX])), + int32_t(aInstance->GetPrimitiveNumber( + SVGContentUtils::Y, &mNumberAttributes[DY]))); + descr.Attributes().Set(eOffsetOffset, offset); + return descr; +} + +bool +SVGFEOffsetElement::AttributeAffectsRendering(int32_t aNameSpaceID, + nsIAtom* aAttribute) const +{ + return SVGFEOffsetElementBase::AttributeAffectsRendering(aNameSpaceID, aAttribute) || + (aNameSpaceID == kNameSpaceID_None && + (aAttribute == nsGkAtoms::in || + aAttribute == nsGkAtoms::dx || + aAttribute == nsGkAtoms::dy)); +} + +void +SVGFEOffsetElement::GetSourceImageNames(nsTArray<nsSVGStringInfo>& aSources) +{ + aSources.AppendElement(nsSVGStringInfo(&mStringAttributes[IN1], this)); +} + +//---------------------------------------------------------------------- +// nsSVGElement methods + +nsSVGElement::NumberAttributesInfo +SVGFEOffsetElement::GetNumberInfo() +{ + return NumberAttributesInfo(mNumberAttributes, sNumberInfo, + ArrayLength(sNumberInfo)); +} + +nsSVGElement::StringAttributesInfo +SVGFEOffsetElement::GetStringInfo() +{ + return StringAttributesInfo(mStringAttributes, sStringInfo, + ArrayLength(sStringInfo)); +} + +} // namespace dom +} // namespace mozilla diff --git a/dom/svg/SVGFEOffsetElement.h b/dom/svg/SVGFEOffsetElement.h new file mode 100644 index 0000000000..4e61d95aaf --- /dev/null +++ b/dom/svg/SVGFEOffsetElement.h @@ -0,0 +1,67 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef mozilla_dom_SVGFEOffsetElement_h +#define mozilla_dom_SVGFEOffsetElement_h + +#include "nsSVGFilters.h" +#include "nsSVGNumber2.h" +#include "nsSVGString.h" + +nsresult NS_NewSVGFEOffsetElement(nsIContent **aResult, + already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo); + +namespace mozilla { +namespace dom { + +typedef nsSVGFE SVGFEOffsetElementBase; + +class SVGFEOffsetElement : public SVGFEOffsetElementBase +{ + friend nsresult (::NS_NewSVGFEOffsetElement(nsIContent **aResult, + already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo)); +protected: + explicit SVGFEOffsetElement(already_AddRefed<mozilla::dom::NodeInfo>& aNodeInfo) + : SVGFEOffsetElementBase(aNodeInfo) + { + } + virtual JSObject* WrapNode(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override; + +public: + virtual FilterPrimitiveDescription + GetPrimitiveDescription(nsSVGFilterInstance* aInstance, + const IntRect& aFilterSubregion, + const nsTArray<bool>& aInputsAreTainted, + nsTArray<RefPtr<SourceSurface>>& aInputImages) override; + virtual bool AttributeAffectsRendering( + int32_t aNameSpaceID, nsIAtom* aAttribute) const override; + virtual nsSVGString& GetResultImageName() override { return mStringAttributes[RESULT]; } + virtual void GetSourceImageNames(nsTArray<nsSVGStringInfo>& aSources) override; + + virtual nsresult Clone(mozilla::dom::NodeInfo *aNodeInfo, nsINode **aResult) const override; + + // WebIDL + already_AddRefed<SVGAnimatedString> In1(); + already_AddRefed<SVGAnimatedNumber> Dx(); + already_AddRefed<SVGAnimatedNumber> Dy(); + +protected: + virtual NumberAttributesInfo GetNumberInfo() override; + virtual StringAttributesInfo GetStringInfo() override; + + enum { DX, DY }; + nsSVGNumber2 mNumberAttributes[2]; + static NumberInfo sNumberInfo[2]; + + enum { RESULT, IN1 }; + nsSVGString mStringAttributes[2]; + static StringInfo sStringInfo[2]; +}; + +} // namespace dom +} // namespace mozilla + +#endif // mozilla_dom_SVGFEOffsetElement_h diff --git a/dom/svg/SVGFEPointLightElement.cpp b/dom/svg/SVGFEPointLightElement.cpp new file mode 100644 index 0000000000..8f80bb71ff --- /dev/null +++ b/dom/svg/SVGFEPointLightElement.cpp @@ -0,0 +1,92 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "mozilla/dom/SVGFEPointLightElement.h" +#include "mozilla/dom/SVGFEPointLightElementBinding.h" +#include "nsSVGFilterInstance.h" + +NS_IMPL_NS_NEW_NAMESPACED_SVG_ELEMENT(FEPointLight) + +using namespace mozilla::gfx; + +namespace mozilla { +namespace dom { + +JSObject* +SVGFEPointLightElement::WrapNode(JSContext *aCx, JS::Handle<JSObject*> aGivenProto) +{ + return SVGFEPointLightElementBinding::Wrap(aCx, this, aGivenProto); +} + +nsSVGElement::NumberInfo SVGFEPointLightElement::sNumberInfo[3] = +{ + { &nsGkAtoms::x, 0, false }, + { &nsGkAtoms::y, 0, false }, + { &nsGkAtoms::z, 0, false } +}; + +//---------------------------------------------------------------------- +// nsIDOMNode methods + +NS_IMPL_ELEMENT_CLONE_WITH_INIT(SVGFEPointLightElement) + +//---------------------------------------------------------------------- +// nsFEUnstyledElement methods + +bool +SVGFEPointLightElement::AttributeAffectsRendering(int32_t aNameSpaceID, + nsIAtom* aAttribute) const +{ + return aNameSpaceID == kNameSpaceID_None && + (aAttribute == nsGkAtoms::x || + aAttribute == nsGkAtoms::y || + aAttribute == nsGkAtoms::z); +} + +//---------------------------------------------------------------------- + +AttributeMap +SVGFEPointLightElement::ComputeLightAttributes(nsSVGFilterInstance* aInstance) +{ + Point3D lightPos; + GetAnimatedNumberValues(&lightPos.x, &lightPos.y, &lightPos.z, nullptr); + + AttributeMap map; + map.Set(eLightType, (uint32_t)eLightTypePoint); + map.Set(ePointLightPosition, aInstance->ConvertLocation(lightPos)); + return map; +} + +already_AddRefed<SVGAnimatedNumber> +SVGFEPointLightElement::X() +{ + return mNumberAttributes[ATTR_X].ToDOMAnimatedNumber(this); +} + +already_AddRefed<SVGAnimatedNumber> +SVGFEPointLightElement::Y() +{ + return mNumberAttributes[ATTR_Y].ToDOMAnimatedNumber(this); +} + +already_AddRefed<SVGAnimatedNumber> +SVGFEPointLightElement::Z() +{ + return mNumberAttributes[ATTR_Z].ToDOMAnimatedNumber(this); +} + +//---------------------------------------------------------------------- +// nsSVGElement methods + +nsSVGElement::NumberAttributesInfo +SVGFEPointLightElement::GetNumberInfo() +{ + return NumberAttributesInfo(mNumberAttributes, sNumberInfo, + ArrayLength(sNumberInfo)); +} + +} // namespace dom +} // namespace mozilla diff --git a/dom/svg/SVGFEPointLightElement.h b/dom/svg/SVGFEPointLightElement.h new file mode 100644 index 0000000000..89918bec67 --- /dev/null +++ b/dom/svg/SVGFEPointLightElement.h @@ -0,0 +1,55 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef mozilla_dom_SVGFEPointLightElement_h +#define mozilla_dom_SVGFEPointLightElement_h + +#include "nsSVGFilters.h" +#include "nsSVGNumber2.h" + +nsresult NS_NewSVGFEPointLightElement(nsIContent **aResult, + already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo); + +namespace mozilla { +namespace dom { + +typedef SVGFELightElement SVGFEPointLightElementBase; + +class SVGFEPointLightElement : public SVGFEPointLightElementBase +{ + friend nsresult (::NS_NewSVGFEPointLightElement(nsIContent **aResult, + already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo)); +protected: + explicit SVGFEPointLightElement(already_AddRefed<mozilla::dom::NodeInfo>& aNodeInfo) + : SVGFEPointLightElementBase(aNodeInfo) + { + } + virtual JSObject* WrapNode(JSContext *cx, JS::Handle<JSObject*> aGivenProto) override; + +public: + virtual AttributeMap ComputeLightAttributes(nsSVGFilterInstance* aInstance) override; + virtual bool AttributeAffectsRendering( + int32_t aNameSpaceID, nsIAtom* aAttribute) const override; + + virtual nsresult Clone(mozilla::dom::NodeInfo *aNodeInfo, nsINode **aResult) const override; + + // WebIDL + already_AddRefed<SVGAnimatedNumber> X(); + already_AddRefed<SVGAnimatedNumber> Y(); + already_AddRefed<SVGAnimatedNumber> Z(); + +protected: + virtual NumberAttributesInfo GetNumberInfo() override; + + enum { ATTR_X, ATTR_Y, ATTR_Z }; + nsSVGNumber2 mNumberAttributes[3]; + static NumberInfo sNumberInfo[3]; +}; + +} // namespace dom +} // namespace mozilla + +#endif // mozilla_dom_SVGFEPointLightElement_h diff --git a/dom/svg/SVGFESpecularLightingElement.cpp b/dom/svg/SVGFESpecularLightingElement.cpp new file mode 100644 index 0000000000..ae583352e1 --- /dev/null +++ b/dom/svg/SVGFESpecularLightingElement.cpp @@ -0,0 +1,102 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "mozilla/dom/SVGFESpecularLightingElement.h" +#include "mozilla/dom/SVGFESpecularLightingElementBinding.h" +#include "nsSVGUtils.h" +#include "nsSVGFilterInstance.h" + +NS_IMPL_NS_NEW_NAMESPACED_SVG_ELEMENT(FESpecularLighting) + +using namespace mozilla::gfx; + +namespace mozilla { +namespace dom { + +JSObject* +SVGFESpecularLightingElement::WrapNode(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) +{ + return SVGFESpecularLightingElementBinding::Wrap(aCx, this, aGivenProto); +} + +//---------------------------------------------------------------------- +// nsIDOMNode methods + +NS_IMPL_ELEMENT_CLONE_WITH_INIT(SVGFESpecularLightingElement) + +already_AddRefed<SVGAnimatedString> +SVGFESpecularLightingElement::In1() +{ + return mStringAttributes[IN1].ToDOMAnimatedString(this); +} + +already_AddRefed<SVGAnimatedNumber> +SVGFESpecularLightingElement::SurfaceScale() +{ + return mNumberAttributes[SURFACE_SCALE].ToDOMAnimatedNumber(this); +} + +already_AddRefed<SVGAnimatedNumber> +SVGFESpecularLightingElement::SpecularConstant() +{ + return mNumberAttributes[SPECULAR_CONSTANT].ToDOMAnimatedNumber(this); +} + +already_AddRefed<SVGAnimatedNumber> +SVGFESpecularLightingElement::SpecularExponent() +{ + return mNumberAttributes[SPECULAR_EXPONENT].ToDOMAnimatedNumber(this); +} + +already_AddRefed<SVGAnimatedNumber> +SVGFESpecularLightingElement::KernelUnitLengthX() +{ + return mNumberPairAttributes[KERNEL_UNIT_LENGTH].ToDOMAnimatedNumber( + nsSVGNumberPair::eFirst, this); +} + +already_AddRefed<SVGAnimatedNumber> +SVGFESpecularLightingElement::KernelUnitLengthY() +{ + return mNumberPairAttributes[KERNEL_UNIT_LENGTH].ToDOMAnimatedNumber( + nsSVGNumberPair::eSecond, this); +} + +//---------------------------------------------------------------------- +// nsSVGElement methods + +FilterPrimitiveDescription +SVGFESpecularLightingElement::GetPrimitiveDescription(nsSVGFilterInstance* aInstance, + const IntRect& aFilterSubregion, + const nsTArray<bool>& aInputsAreTainted, + nsTArray<RefPtr<SourceSurface>>& aInputImages) +{ + float specularExponent = mNumberAttributes[SPECULAR_EXPONENT].GetAnimValue(); + float specularConstant = mNumberAttributes[SPECULAR_CONSTANT].GetAnimValue(); + + // specification defined range (15.22) + if (specularExponent < 1 || specularExponent > 128) { + return FilterPrimitiveDescription(PrimitiveType::Empty); + } + + FilterPrimitiveDescription descr(PrimitiveType::SpecularLighting); + descr.Attributes().Set(eSpecularLightingSpecularConstant, specularConstant); + descr.Attributes().Set(eSpecularLightingSpecularExponent, specularExponent); + return AddLightingAttributes(descr, aInstance); +} + +bool +SVGFESpecularLightingElement::AttributeAffectsRendering(int32_t aNameSpaceID, + nsIAtom* aAttribute) const +{ + return SVGFESpecularLightingElementBase::AttributeAffectsRendering(aNameSpaceID, aAttribute) || + (aNameSpaceID == kNameSpaceID_None && + (aAttribute == nsGkAtoms::specularConstant || + aAttribute == nsGkAtoms::specularExponent)); +} + +} // namespace dom +} // namespace mozilla diff --git a/dom/svg/SVGFESpecularLightingElement.h b/dom/svg/SVGFESpecularLightingElement.h new file mode 100644 index 0000000000..0d81a4c91d --- /dev/null +++ b/dom/svg/SVGFESpecularLightingElement.h @@ -0,0 +1,56 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef mozilla_dom_SVGFESpecularLightingElement_h +#define mozilla_dom_SVGFESpecularLightingElement_h + +#include "nsSVGFilters.h" + +nsresult NS_NewSVGFESpecularLightingElement(nsIContent **aResult, + already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo); + +namespace mozilla { +namespace dom { + +//---------------------SpecularLighting------------------------ + +typedef nsSVGFELightingElement SVGFESpecularLightingElementBase; + +class SVGFESpecularLightingElement : public SVGFESpecularLightingElementBase +{ + friend nsresult (::NS_NewSVGFESpecularLightingElement(nsIContent **aResult, + already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo)); +protected: + explicit SVGFESpecularLightingElement(already_AddRefed<mozilla::dom::NodeInfo>& aNodeInfo) + : SVGFESpecularLightingElementBase(aNodeInfo) + { + } + virtual JSObject* WrapNode(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override; + +public: + virtual nsresult Clone(mozilla::dom::NodeInfo *aNodeInfo, nsINode **aResult) const override; + + virtual FilterPrimitiveDescription + GetPrimitiveDescription(nsSVGFilterInstance* aInstance, + const IntRect& aFilterSubregion, + const nsTArray<bool>& aInputsAreTainted, + nsTArray<RefPtr<SourceSurface>>& aInputImages) override; + virtual bool AttributeAffectsRendering( + int32_t aNameSpaceID, nsIAtom* aAttribute) const override; + + // WebIDL + already_AddRefed<SVGAnimatedString> In1(); + already_AddRefed<SVGAnimatedNumber> SurfaceScale(); + already_AddRefed<SVGAnimatedNumber> SpecularConstant(); + already_AddRefed<SVGAnimatedNumber> SpecularExponent(); + already_AddRefed<SVGAnimatedNumber> KernelUnitLengthX(); + already_AddRefed<SVGAnimatedNumber> KernelUnitLengthY(); +}; + +} // namespace dom +} // namespace mozilla + +#endif // mozilla_dom_SVGFESpecularLightingElement_h diff --git a/dom/svg/SVGFESpotLightElement.cpp b/dom/svg/SVGFESpotLightElement.cpp new file mode 100644 index 0000000000..222421cfd7 --- /dev/null +++ b/dom/svg/SVGFESpotLightElement.cpp @@ -0,0 +1,142 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "mozilla/dom/SVGFESpotLightElement.h" +#include "mozilla/dom/SVGFESpotLightElementBinding.h" +#include "nsSVGFilterInstance.h" + +NS_IMPL_NS_NEW_NAMESPACED_SVG_ELEMENT(FESpotLight) + +using namespace mozilla::gfx; + +namespace mozilla { +namespace dom { + +JSObject* +SVGFESpotLightElement::WrapNode(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) +{ + return SVGFESpotLightElementBinding::Wrap(aCx, this, aGivenProto); +} + +nsSVGElement::NumberInfo SVGFESpotLightElement::sNumberInfo[8] = +{ + { &nsGkAtoms::x, 0, false }, + { &nsGkAtoms::y, 0, false }, + { &nsGkAtoms::z, 0, false }, + { &nsGkAtoms::pointsAtX, 0, false }, + { &nsGkAtoms::pointsAtY, 0, false }, + { &nsGkAtoms::pointsAtZ, 0, false }, + { &nsGkAtoms::specularExponent, 1, false }, + { &nsGkAtoms::limitingConeAngle, 0, false } +}; + +//---------------------------------------------------------------------- +// nsIDOMNode methods + +NS_IMPL_ELEMENT_CLONE_WITH_INIT(SVGFESpotLightElement) + +//---------------------------------------------------------------------- +// nsFEUnstyledElement methods + +bool +SVGFESpotLightElement::AttributeAffectsRendering(int32_t aNameSpaceID, + nsIAtom* aAttribute) const +{ + return aNameSpaceID == kNameSpaceID_None && + (aAttribute == nsGkAtoms::x || + aAttribute == nsGkAtoms::y || + aAttribute == nsGkAtoms::z || + aAttribute == nsGkAtoms::pointsAtX || + aAttribute == nsGkAtoms::pointsAtY || + aAttribute == nsGkAtoms::pointsAtZ || + aAttribute == nsGkAtoms::specularExponent || + aAttribute == nsGkAtoms::limitingConeAngle); +} + +//---------------------------------------------------------------------- + +AttributeMap +SVGFESpotLightElement::ComputeLightAttributes(nsSVGFilterInstance* aInstance) +{ + Point3D lightPos, pointsAt; + float specularExponent, limitingConeAngle; + GetAnimatedNumberValues(&lightPos.x, &lightPos.y, &lightPos.z, + &pointsAt.x, &pointsAt.y, &pointsAt.z, + &specularExponent, &limitingConeAngle, + nullptr); + if (!mNumberAttributes[SVGFESpotLightElement::LIMITING_CONE_ANGLE].IsExplicitlySet()) { + limitingConeAngle = 90; + } + + AttributeMap map; + map.Set(eLightType, (uint32_t)eLightTypeSpot); + map.Set(eSpotLightPosition, aInstance->ConvertLocation(lightPos)); + map.Set(eSpotLightPointsAt, aInstance->ConvertLocation(pointsAt)); + map.Set(eSpotLightFocus, specularExponent); + map.Set(eSpotLightLimitingConeAngle, limitingConeAngle); + return map; +} + +already_AddRefed<SVGAnimatedNumber> +SVGFESpotLightElement::X() +{ + return mNumberAttributes[ATTR_X].ToDOMAnimatedNumber(this); +} + +already_AddRefed<SVGAnimatedNumber> +SVGFESpotLightElement::Y() +{ + return mNumberAttributes[ATTR_Y].ToDOMAnimatedNumber(this); +} + +already_AddRefed<SVGAnimatedNumber> +SVGFESpotLightElement::Z() +{ + return mNumberAttributes[ATTR_Z].ToDOMAnimatedNumber(this); +} + +already_AddRefed<SVGAnimatedNumber> +SVGFESpotLightElement::PointsAtX() +{ + return mNumberAttributes[POINTS_AT_X].ToDOMAnimatedNumber(this); +} + +already_AddRefed<SVGAnimatedNumber> +SVGFESpotLightElement::PointsAtY() +{ + return mNumberAttributes[POINTS_AT_Y].ToDOMAnimatedNumber(this); +} + +already_AddRefed<SVGAnimatedNumber> +SVGFESpotLightElement::PointsAtZ() +{ + return mNumberAttributes[POINTS_AT_Z].ToDOMAnimatedNumber(this); +} + +already_AddRefed<SVGAnimatedNumber> +SVGFESpotLightElement::SpecularExponent() +{ + return mNumberAttributes[SPECULAR_EXPONENT].ToDOMAnimatedNumber(this); +} + +already_AddRefed<SVGAnimatedNumber> +SVGFESpotLightElement::LimitingConeAngle() +{ + return mNumberAttributes[LIMITING_CONE_ANGLE].ToDOMAnimatedNumber(this); +} + +//---------------------------------------------------------------------- +// nsSVGElement methods + +nsSVGElement::NumberAttributesInfo +SVGFESpotLightElement::GetNumberInfo() +{ + return NumberAttributesInfo(mNumberAttributes, sNumberInfo, + ArrayLength(sNumberInfo)); +} + +} // namespace dom +} // namespace mozilla diff --git a/dom/svg/SVGFESpotLightElement.h b/dom/svg/SVGFESpotLightElement.h new file mode 100644 index 0000000000..829ae63f10 --- /dev/null +++ b/dom/svg/SVGFESpotLightElement.h @@ -0,0 +1,62 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef mozilla_dom_SVGFESpotLightElement_h +#define mozilla_dom_SVGFESpotLightElement_h + +#include "nsSVGFilters.h" +#include "nsSVGNumber2.h" + +nsresult NS_NewSVGFESpotLightElement(nsIContent **aResult, + already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo); + +namespace mozilla { +namespace dom { + +typedef SVGFELightElement SVGFESpotLightElementBase; + +class SVGFESpotLightElement : public SVGFESpotLightElementBase +{ + friend nsresult (::NS_NewSVGFESpotLightElement(nsIContent **aResult, + already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo)); + friend class ::nsSVGFELightingElement; +protected: + explicit SVGFESpotLightElement(already_AddRefed<mozilla::dom::NodeInfo>& aNodeInfo) + : SVGFESpotLightElementBase(aNodeInfo) + { + } + virtual JSObject* WrapNode(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override; + +public: + virtual AttributeMap ComputeLightAttributes(nsSVGFilterInstance* aInstance) override; + virtual bool AttributeAffectsRendering( + int32_t aNameSpaceID, nsIAtom* aAttribute) const override; + + virtual nsresult Clone(mozilla::dom::NodeInfo *aNodeInfo, nsINode **aResult) const override; + + // WebIDL + already_AddRefed<SVGAnimatedNumber> X(); + already_AddRefed<SVGAnimatedNumber> Y(); + already_AddRefed<SVGAnimatedNumber> Z(); + already_AddRefed<SVGAnimatedNumber> PointsAtX(); + already_AddRefed<SVGAnimatedNumber> PointsAtY(); + already_AddRefed<SVGAnimatedNumber> PointsAtZ(); + already_AddRefed<SVGAnimatedNumber> SpecularExponent(); + already_AddRefed<SVGAnimatedNumber> LimitingConeAngle(); + +protected: + virtual NumberAttributesInfo GetNumberInfo() override; + + enum { ATTR_X, ATTR_Y, ATTR_Z, POINTS_AT_X, POINTS_AT_Y, POINTS_AT_Z, + SPECULAR_EXPONENT, LIMITING_CONE_ANGLE }; + nsSVGNumber2 mNumberAttributes[8]; + static NumberInfo sNumberInfo[8]; +}; + +} // namespace dom +} // namespace mozilla + +#endif // mozilla_dom_SVGFESpotLightElement_h diff --git a/dom/svg/SVGFETileElement.cpp b/dom/svg/SVGFETileElement.cpp new file mode 100644 index 0000000000..b649ee6367 --- /dev/null +++ b/dom/svg/SVGFETileElement.cpp @@ -0,0 +1,80 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "mozilla/dom/SVGFETileElement.h" +#include "mozilla/dom/SVGFETileElementBinding.h" +#include "nsSVGFilterInstance.h" + +NS_IMPL_NS_NEW_NAMESPACED_SVG_ELEMENT(FETile) + +using namespace mozilla::gfx; + +namespace mozilla { +namespace dom { + +JSObject* +SVGFETileElement::WrapNode(JSContext *aCx, JS::Handle<JSObject*> aGivenProto) +{ + return SVGFETileElementBinding::Wrap(aCx, this, aGivenProto); +} + +nsSVGElement::StringInfo SVGFETileElement::sStringInfo[2] = +{ + { &nsGkAtoms::result, kNameSpaceID_None, true }, + { &nsGkAtoms::in, kNameSpaceID_None, true } +}; + +//---------------------------------------------------------------------- +// nsIDOMNode methods + + +NS_IMPL_ELEMENT_CLONE_WITH_INIT(SVGFETileElement) + +already_AddRefed<SVGAnimatedString> +SVGFETileElement::In1() +{ + return mStringAttributes[IN1].ToDOMAnimatedString(this); +} + +void +SVGFETileElement::GetSourceImageNames(nsTArray<nsSVGStringInfo>& aSources) +{ + aSources.AppendElement(nsSVGStringInfo(&mStringAttributes[IN1], this)); +} + +//---------------------------------------------------------------------- +// nsSVGElement methods + +FilterPrimitiveDescription +SVGFETileElement::GetPrimitiveDescription(nsSVGFilterInstance* aInstance, + const IntRect& aFilterSubregion, + const nsTArray<bool>& aInputsAreTainted, + nsTArray<RefPtr<SourceSurface>>& aInputImages) +{ + return FilterPrimitiveDescription(PrimitiveType::Tile); +} + +bool +SVGFETileElement::AttributeAffectsRendering(int32_t aNameSpaceID, + nsIAtom* aAttribute) const +{ + return SVGFETileElementBase::AttributeAffectsRendering(aNameSpaceID, + aAttribute) || + (aNameSpaceID == kNameSpaceID_None && aAttribute == nsGkAtoms::in); +} + +//---------------------------------------------------------------------- +// nsSVGElement methods + +nsSVGElement::StringAttributesInfo +SVGFETileElement::GetStringInfo() +{ + return StringAttributesInfo(mStringAttributes, sStringInfo, + ArrayLength(sStringInfo)); +} + +} // namespace dom +} // namespace mozilla diff --git a/dom/svg/SVGFETileElement.h b/dom/svg/SVGFETileElement.h new file mode 100644 index 0000000000..52efde4724 --- /dev/null +++ b/dom/svg/SVGFETileElement.h @@ -0,0 +1,60 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef mozilla_dom_SVGFETileElement_h +#define mozilla_dom_SVGFETileElement_h + +#include "nsSVGFilters.h" + +nsresult NS_NewSVGFETileElement(nsIContent **aResult, + already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo); + +namespace mozilla { +namespace dom { + +typedef nsSVGFE SVGFETileElementBase; + +class SVGFETileElement : public SVGFETileElementBase +{ + friend nsresult (::NS_NewSVGFETileElement(nsIContent **aResult, + already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo)); +protected: + explicit SVGFETileElement(already_AddRefed<mozilla::dom::NodeInfo>& aNodeInfo) + : SVGFETileElementBase(aNodeInfo) + { + } + virtual JSObject* WrapNode(JSContext *cx, JS::Handle<JSObject*> aGivenProto) override; + +public: + virtual bool SubregionIsUnionOfRegions() override { return false; } + + virtual FilterPrimitiveDescription + GetPrimitiveDescription(nsSVGFilterInstance* aInstance, + const IntRect& aFilterSubregion, + const nsTArray<bool>& aInputsAreTainted, + nsTArray<RefPtr<SourceSurface>>& aInputImages) override; + virtual bool AttributeAffectsRendering( + int32_t aNameSpaceID, nsIAtom* aAttribute) const override; + virtual nsSVGString& GetResultImageName() override { return mStringAttributes[RESULT]; } + virtual void GetSourceImageNames(nsTArray<nsSVGStringInfo>& aSources) override; + + virtual nsresult Clone(mozilla::dom::NodeInfo *aNodeInfo, nsINode **aResult) const override; + + // WebIDL + already_AddRefed<SVGAnimatedString> In1(); + +protected: + virtual StringAttributesInfo GetStringInfo() override; + + enum { RESULT, IN1 }; + nsSVGString mStringAttributes[2]; + static StringInfo sStringInfo[2]; +}; + +} // namespace dom +} // namespace mozilla + +#endif // mozilla_dom_SVGFETileElement_h diff --git a/dom/svg/SVGFETurbulenceElement.cpp b/dom/svg/SVGFETurbulenceElement.cpp new file mode 100644 index 0000000000..908e735065 --- /dev/null +++ b/dom/svg/SVGFETurbulenceElement.cpp @@ -0,0 +1,211 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "mozilla/dom/SVGFETurbulenceElement.h" +#include "mozilla/dom/SVGFETurbulenceElementBinding.h" +#include "nsSVGFilterInstance.h" +#include "nsSVGUtils.h" + +NS_IMPL_NS_NEW_NAMESPACED_SVG_ELEMENT(FETurbulence) + +using namespace mozilla::gfx; + +namespace mozilla { +namespace dom { + +// Stitch Options +static const unsigned short SVG_STITCHTYPE_STITCH = 1; +static const unsigned short SVG_STITCHTYPE_NOSTITCH = 2; + +static const int32_t MAX_OCTAVES = 10; + +JSObject* +SVGFETurbulenceElement::WrapNode(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) +{ + return SVGFETurbulenceElementBinding::Wrap(aCx, this, aGivenProto); +} + +nsSVGElement::NumberInfo SVGFETurbulenceElement::sNumberInfo[1] = +{ + { &nsGkAtoms::seed, 0, false } +}; + +nsSVGElement::NumberPairInfo SVGFETurbulenceElement::sNumberPairInfo[1] = +{ + { &nsGkAtoms::baseFrequency, 0, 0 } +}; + +nsSVGElement::IntegerInfo SVGFETurbulenceElement::sIntegerInfo[1] = +{ + { &nsGkAtoms::numOctaves, 1 } +}; + +nsSVGEnumMapping SVGFETurbulenceElement::sTypeMap[] = { + {&nsGkAtoms::fractalNoise, + SVG_TURBULENCE_TYPE_FRACTALNOISE}, + {&nsGkAtoms::turbulence, + SVG_TURBULENCE_TYPE_TURBULENCE}, + {nullptr, 0} +}; + +nsSVGEnumMapping SVGFETurbulenceElement::sStitchTilesMap[] = { + {&nsGkAtoms::stitch, + SVG_STITCHTYPE_STITCH}, + {&nsGkAtoms::noStitch, + SVG_STITCHTYPE_NOSTITCH}, + {nullptr, 0} +}; + +nsSVGElement::EnumInfo SVGFETurbulenceElement::sEnumInfo[2] = +{ + { &nsGkAtoms::type, + sTypeMap, + SVG_TURBULENCE_TYPE_TURBULENCE + }, + { &nsGkAtoms::stitchTiles, + sStitchTilesMap, + SVG_STITCHTYPE_NOSTITCH + } +}; + +nsSVGElement::StringInfo SVGFETurbulenceElement::sStringInfo[1] = +{ + { &nsGkAtoms::result, kNameSpaceID_None, true } +}; + +//---------------------------------------------------------------------- +// nsIDOMNode methods + +NS_IMPL_ELEMENT_CLONE_WITH_INIT(SVGFETurbulenceElement) + +//---------------------------------------------------------------------- + +already_AddRefed<SVGAnimatedNumber> +SVGFETurbulenceElement::BaseFrequencyX() +{ + return mNumberPairAttributes[BASE_FREQ].ToDOMAnimatedNumber(nsSVGNumberPair::eFirst, this); +} + +already_AddRefed<SVGAnimatedNumber> +SVGFETurbulenceElement::BaseFrequencyY() +{ + return mNumberPairAttributes[BASE_FREQ].ToDOMAnimatedNumber(nsSVGNumberPair::eSecond, this); +} + +already_AddRefed<SVGAnimatedInteger> +SVGFETurbulenceElement::NumOctaves() +{ + return mIntegerAttributes[OCTAVES].ToDOMAnimatedInteger(this); +} + +already_AddRefed<SVGAnimatedNumber> +SVGFETurbulenceElement::Seed() +{ + return mNumberAttributes[SEED].ToDOMAnimatedNumber(this); +} + +already_AddRefed<SVGAnimatedEnumeration> +SVGFETurbulenceElement::StitchTiles() +{ + return mEnumAttributes[STITCHTILES].ToDOMAnimatedEnum(this); +} + +already_AddRefed<SVGAnimatedEnumeration> +SVGFETurbulenceElement::Type() +{ + return mEnumAttributes[TYPE].ToDOMAnimatedEnum(this); +} + +FilterPrimitiveDescription +SVGFETurbulenceElement::GetPrimitiveDescription(nsSVGFilterInstance* aInstance, + const IntRect& aFilterSubregion, + const nsTArray<bool>& aInputsAreTainted, + nsTArray<RefPtr<SourceSurface>>& aInputImages) +{ + float fX = mNumberPairAttributes[BASE_FREQ].GetAnimValue(nsSVGNumberPair::eFirst); + float fY = mNumberPairAttributes[BASE_FREQ].GetAnimValue(nsSVGNumberPair::eSecond); + float seed = mNumberAttributes[OCTAVES].GetAnimValue(); + uint32_t octaves = clamped(mIntegerAttributes[OCTAVES].GetAnimValue(), 0, MAX_OCTAVES); + uint32_t type = mEnumAttributes[TYPE].GetAnimValue(); + uint16_t stitch = mEnumAttributes[STITCHTILES].GetAnimValue(); + + if (fX == 0 || fY == 0) { + return FilterPrimitiveDescription(PrimitiveType::Empty); + } + + // We interpret the base frequency as relative to user space units. In other + // words, we consider one turbulence base period to be 1 / fX user space + // units wide and 1 / fY user space units high. We do not scale the frequency + // depending on the filter primitive region. + gfxRect firstPeriodInUserSpace(0, 0, 1 / fX, 1 / fY); + gfxRect firstPeriodInFilterSpace = aInstance->UserSpaceToFilterSpace(firstPeriodInUserSpace); + Size frequencyInFilterSpace(1 / firstPeriodInFilterSpace.width, + 1 / firstPeriodInFilterSpace.height); + gfxPoint offset = firstPeriodInFilterSpace.TopLeft(); + + FilterPrimitiveDescription descr(PrimitiveType::Turbulence); + descr.Attributes().Set(eTurbulenceOffset, IntPoint::Truncate(offset.x, offset.y)); + descr.Attributes().Set(eTurbulenceBaseFrequency, frequencyInFilterSpace); + descr.Attributes().Set(eTurbulenceSeed, seed); + descr.Attributes().Set(eTurbulenceNumOctaves, octaves); + descr.Attributes().Set(eTurbulenceStitchable, stitch == SVG_STITCHTYPE_STITCH); + descr.Attributes().Set(eTurbulenceType, type); + return descr; +} + +bool +SVGFETurbulenceElement::AttributeAffectsRendering(int32_t aNameSpaceID, + nsIAtom* aAttribute) const +{ + return SVGFETurbulenceElementBase::AttributeAffectsRendering(aNameSpaceID, aAttribute) || + (aNameSpaceID == kNameSpaceID_None && + (aAttribute == nsGkAtoms::seed || + aAttribute == nsGkAtoms::baseFrequency || + aAttribute == nsGkAtoms::numOctaves || + aAttribute == nsGkAtoms::type || + aAttribute == nsGkAtoms::stitchTiles)); +} + +//---------------------------------------------------------------------- +// nsSVGElement methods + +nsSVGElement::NumberAttributesInfo +SVGFETurbulenceElement::GetNumberInfo() +{ + return NumberAttributesInfo(mNumberAttributes, sNumberInfo, + ArrayLength(sNumberInfo)); +} + +nsSVGElement::NumberPairAttributesInfo +SVGFETurbulenceElement::GetNumberPairInfo() +{ + return NumberPairAttributesInfo(mNumberPairAttributes, sNumberPairInfo, + ArrayLength(sNumberPairInfo)); +} + +nsSVGElement::IntegerAttributesInfo +SVGFETurbulenceElement::GetIntegerInfo() +{ + return IntegerAttributesInfo(mIntegerAttributes, sIntegerInfo, + ArrayLength(sIntegerInfo)); +} + +nsSVGElement::EnumAttributesInfo +SVGFETurbulenceElement::GetEnumInfo() +{ + return EnumAttributesInfo(mEnumAttributes, sEnumInfo, + ArrayLength(sEnumInfo)); +} + +nsSVGElement::StringAttributesInfo +SVGFETurbulenceElement::GetStringInfo() +{ + return StringAttributesInfo(mStringAttributes, sStringInfo, + ArrayLength(sStringInfo)); +} + +} // namespace dom +} // namespace mozilla diff --git a/dom/svg/SVGFETurbulenceElement.h b/dom/svg/SVGFETurbulenceElement.h new file mode 100644 index 0000000000..d6ca2bc478 --- /dev/null +++ b/dom/svg/SVGFETurbulenceElement.h @@ -0,0 +1,90 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef mozilla_dom_SVGFETurbulenceElement_h +#define mozilla_dom_SVGFETurbulenceElement_h + +#include "nsSVGEnum.h" +#include "nsSVGFilters.h" +#include "nsSVGNumber2.h" +#include "nsSVGInteger.h" +#include "nsSVGString.h" + +nsresult NS_NewSVGFETurbulenceElement(nsIContent **aResult, + already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo); + +namespace mozilla { +namespace dom { + +typedef nsSVGFE SVGFETurbulenceElementBase; + +class SVGFETurbulenceElement : public SVGFETurbulenceElementBase +{ + friend nsresult (::NS_NewSVGFETurbulenceElement(nsIContent **aResult, + already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo)); +protected: + explicit SVGFETurbulenceElement(already_AddRefed<mozilla::dom::NodeInfo>& aNodeInfo) + : SVGFETurbulenceElementBase(aNodeInfo) + { + } + virtual JSObject* WrapNode(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override; + +public: + virtual bool SubregionIsUnionOfRegions() override { return false; } + + virtual FilterPrimitiveDescription + GetPrimitiveDescription(nsSVGFilterInstance* aInstance, + const IntRect& aFilterSubregion, + const nsTArray<bool>& aInputsAreTainted, + nsTArray<RefPtr<SourceSurface>>& aInputImages) override; + virtual bool AttributeAffectsRendering( + int32_t aNameSpaceID, nsIAtom* aAttribute) const override; + virtual nsSVGString& GetResultImageName() override { return mStringAttributes[RESULT]; } + + virtual nsresult Clone(mozilla::dom::NodeInfo *aNodeInfo, nsINode **aResult) const override; + + // WebIDL + already_AddRefed<SVGAnimatedNumber> BaseFrequencyX(); + already_AddRefed<SVGAnimatedNumber> BaseFrequencyY(); + already_AddRefed<SVGAnimatedInteger> NumOctaves(); + already_AddRefed<SVGAnimatedNumber> Seed(); + already_AddRefed<SVGAnimatedEnumeration> StitchTiles(); + already_AddRefed<SVGAnimatedEnumeration> Type(); + +protected: + virtual NumberAttributesInfo GetNumberInfo() override; + virtual NumberPairAttributesInfo GetNumberPairInfo() override; + virtual IntegerAttributesInfo GetIntegerInfo() override; + virtual EnumAttributesInfo GetEnumInfo() override; + virtual StringAttributesInfo GetStringInfo() override; + + enum { SEED }; // floating point seed?! + nsSVGNumber2 mNumberAttributes[1]; + static NumberInfo sNumberInfo[1]; + + enum { BASE_FREQ }; + nsSVGNumberPair mNumberPairAttributes[1]; + static NumberPairInfo sNumberPairInfo[1]; + + enum { OCTAVES }; + nsSVGInteger mIntegerAttributes[1]; + static IntegerInfo sIntegerInfo[1]; + + enum { TYPE, STITCHTILES }; + nsSVGEnum mEnumAttributes[2]; + static nsSVGEnumMapping sTypeMap[]; + static nsSVGEnumMapping sStitchTilesMap[]; + static EnumInfo sEnumInfo[2]; + + enum { RESULT }; + nsSVGString mStringAttributes[1]; + static StringInfo sStringInfo[1]; +}; + +} // namespace dom +} // namespace mozilla + +#endif // mozilla_dom_SVGFETurbulenceElement_h diff --git a/dom/svg/SVGFilterElement.cpp b/dom/svg/SVGFilterElement.cpp new file mode 100644 index 0000000000..65b32dfb71 --- /dev/null +++ b/dom/svg/SVGFilterElement.cpp @@ -0,0 +1,183 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "mozilla/ArrayUtils.h" + +#include "nsGkAtoms.h" +#include "nsCOMPtr.h" +#include "mozilla/dom/SVGFilterElement.h" +#include "mozilla/dom/SVGFilterElementBinding.h" +#include "nsSVGUtils.h" + +NS_IMPL_NS_NEW_NAMESPACED_SVG_ELEMENT(Filter) + +namespace mozilla { +namespace dom { + +JSObject* +SVGFilterElement::WrapNode(JSContext *aCx, JS::Handle<JSObject*> aGivenProto) +{ + return SVGFilterElementBinding::Wrap(aCx, this, aGivenProto); +} + +nsSVGElement::LengthInfo SVGFilterElement::sLengthInfo[4] = +{ + { &nsGkAtoms::x, -10, nsIDOMSVGLength::SVG_LENGTHTYPE_PERCENTAGE, SVGContentUtils::X }, + { &nsGkAtoms::y, -10, nsIDOMSVGLength::SVG_LENGTHTYPE_PERCENTAGE, SVGContentUtils::Y }, + { &nsGkAtoms::width, 120, nsIDOMSVGLength::SVG_LENGTHTYPE_PERCENTAGE, SVGContentUtils::X }, + { &nsGkAtoms::height, 120, nsIDOMSVGLength::SVG_LENGTHTYPE_PERCENTAGE, SVGContentUtils::Y }, +}; + +nsSVGElement::EnumInfo SVGFilterElement::sEnumInfo[2] = +{ + { &nsGkAtoms::filterUnits, + sSVGUnitTypesMap, + SVG_UNIT_TYPE_OBJECTBOUNDINGBOX + }, + { &nsGkAtoms::primitiveUnits, + sSVGUnitTypesMap, + SVG_UNIT_TYPE_USERSPACEONUSE + } +}; + +nsSVGElement::StringInfo SVGFilterElement::sStringInfo[2] = +{ + { &nsGkAtoms::href, kNameSpaceID_None, true }, + { &nsGkAtoms::href, kNameSpaceID_XLink, true } +}; + +//---------------------------------------------------------------------- +// Implementation + +SVGFilterElement::SVGFilterElement(already_AddRefed<mozilla::dom::NodeInfo>& aNodeInfo) + : SVGFilterElementBase(aNodeInfo) +{ +} + +//---------------------------------------------------------------------- +// nsIDOMNode methods + + +NS_IMPL_ELEMENT_CLONE_WITH_INIT(SVGFilterElement) + + +//---------------------------------------------------------------------- + +already_AddRefed<SVGAnimatedLength> +SVGFilterElement::X() +{ + return mLengthAttributes[ATTR_X].ToDOMAnimatedLength(this); +} + +already_AddRefed<SVGAnimatedLength> +SVGFilterElement::Y() +{ + return mLengthAttributes[ATTR_Y].ToDOMAnimatedLength(this); +} + +already_AddRefed<SVGAnimatedLength> +SVGFilterElement::Width() +{ + return mLengthAttributes[ATTR_WIDTH].ToDOMAnimatedLength(this); +} + +already_AddRefed<SVGAnimatedLength> +SVGFilterElement::Height() +{ + return mLengthAttributes[ATTR_HEIGHT].ToDOMAnimatedLength(this); +} + +already_AddRefed<SVGAnimatedEnumeration> +SVGFilterElement::FilterUnits() +{ + return mEnumAttributes[FILTERUNITS].ToDOMAnimatedEnum(this); +} + +already_AddRefed<SVGAnimatedEnumeration> +SVGFilterElement::PrimitiveUnits() +{ + return mEnumAttributes[PRIMITIVEUNITS].ToDOMAnimatedEnum(this); +} + +already_AddRefed<SVGAnimatedString> +SVGFilterElement::Href() +{ + return mStringAttributes[HREF].IsExplicitlySet() + ? mStringAttributes[HREF].ToDOMAnimatedString(this) + : mStringAttributes[XLINK_HREF].ToDOMAnimatedString(this); +} + +//---------------------------------------------------------------------- +// nsIContent methods + +NS_IMETHODIMP_(bool) +SVGFilterElement::IsAttributeMapped(const nsIAtom* name) const +{ + static const MappedAttributeEntry* const map[] = { + sFEFloodMap, + sFiltersMap, + sFontSpecificationMap, + sGradientStopMap, + sLightingEffectsMap, + sMarkersMap, + sTextContentElementsMap, + sViewportsMap + }; + return FindAttributeDependence(name, map) || + SVGFilterElementBase::IsAttributeMapped(name); +} + +void +SVGFilterElement::Invalidate() +{ + nsTObserverArray<nsIMutationObserver*> *observers = GetMutationObservers(); + + if (observers && !observers->IsEmpty()) { + nsTObserverArray<nsIMutationObserver*>::ForwardIterator iter(*observers); + while (iter.HasMore()) { + nsCOMPtr<nsIMutationObserver> obs(iter.GetNext()); + nsCOMPtr<nsISVGFilterReference> filter = do_QueryInterface(obs); + if (filter) + filter->Invalidate(); + } + } +} + +//---------------------------------------------------------------------- +// nsSVGElement methods + +/* virtual */ bool +SVGFilterElement::HasValidDimensions() const +{ + return (!mLengthAttributes[ATTR_WIDTH].IsExplicitlySet() || + mLengthAttributes[ATTR_WIDTH].GetAnimValInSpecifiedUnits() > 0) && + (!mLengthAttributes[ATTR_HEIGHT].IsExplicitlySet() || + mLengthAttributes[ATTR_HEIGHT].GetAnimValInSpecifiedUnits() > 0); +} + +nsSVGElement::LengthAttributesInfo +SVGFilterElement::GetLengthInfo() +{ + return LengthAttributesInfo(mLengthAttributes, sLengthInfo, + ArrayLength(sLengthInfo)); +} + +nsSVGElement::EnumAttributesInfo +SVGFilterElement::GetEnumInfo() +{ + return EnumAttributesInfo(mEnumAttributes, sEnumInfo, + ArrayLength(sEnumInfo)); +} + +nsSVGElement::StringAttributesInfo +SVGFilterElement::GetStringInfo() +{ + return StringAttributesInfo(mStringAttributes, sStringInfo, + ArrayLength(sStringInfo)); +} + +} // namespace dom +} // namespace mozilla diff --git a/dom/svg/SVGFilterElement.h b/dom/svg/SVGFilterElement.h new file mode 100644 index 0000000000..126865590b --- /dev/null +++ b/dom/svg/SVGFilterElement.h @@ -0,0 +1,81 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef mozilla_dom_SVGFilterElement_h +#define mozilla_dom_SVGFilterElement_h + +#include "nsSVGEnum.h" +#include "nsSVGElement.h" +#include "nsSVGIntegerPair.h" +#include "nsSVGLength2.h" +#include "nsSVGString.h" + +typedef nsSVGElement SVGFilterElementBase; + +class nsSVGFilterFrame; +class nsSVGFilterInstance; + +nsresult NS_NewSVGFilterElement(nsIContent **aResult, + already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo); + +namespace mozilla { +namespace dom { +class SVGAnimatedLength; + +class SVGFilterElement : public SVGFilterElementBase +{ + friend class ::nsSVGFilterFrame; + friend class ::nsSVGFilterInstance; + +protected: + friend nsresult (::NS_NewSVGFilterElement(nsIContent **aResult, + already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo)); + explicit SVGFilterElement(already_AddRefed<mozilla::dom::NodeInfo>& aNodeInfo); + virtual JSObject* WrapNode(JSContext *cx, JS::Handle<JSObject*> aGivenProto) override; + +public: + // nsIContent + virtual nsresult Clone(mozilla::dom::NodeInfo *aNodeInfo, nsINode **aResult) const override; + NS_IMETHOD_(bool) IsAttributeMapped(const nsIAtom* aAttribute) const override; + + // Invalidate users of this filter + void Invalidate(); + + // nsSVGSVGElement methods: + virtual bool HasValidDimensions() const override; + + // WebIDL + already_AddRefed<SVGAnimatedLength> X(); + already_AddRefed<SVGAnimatedLength> Y(); + already_AddRefed<SVGAnimatedLength> Width(); + already_AddRefed<SVGAnimatedLength> Height(); + already_AddRefed<SVGAnimatedEnumeration> FilterUnits(); + already_AddRefed<SVGAnimatedEnumeration> PrimitiveUnits(); + already_AddRefed<SVGAnimatedString> Href(); + +protected: + + virtual LengthAttributesInfo GetLengthInfo() override; + virtual EnumAttributesInfo GetEnumInfo() override; + virtual StringAttributesInfo GetStringInfo() override; + + enum { ATTR_X, ATTR_Y, ATTR_WIDTH, ATTR_HEIGHT }; + nsSVGLength2 mLengthAttributes[4]; + static LengthInfo sLengthInfo[4]; + + enum { FILTERUNITS, PRIMITIVEUNITS }; + nsSVGEnum mEnumAttributes[2]; + static EnumInfo sEnumInfo[2]; + + enum { HREF, XLINK_HREF }; + nsSVGString mStringAttributes[2]; + static StringInfo sStringInfo[2]; +}; + +} // namespace dom +} // namespace mozilla + +#endif // mozilla_dom_SVGFilterElement_h diff --git a/dom/svg/SVGForeignObjectElement.cpp b/dom/svg/SVGForeignObjectElement.cpp new file mode 100644 index 0000000000..8a643d79ac --- /dev/null +++ b/dom/svg/SVGForeignObjectElement.cpp @@ -0,0 +1,165 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "mozilla/ArrayUtils.h" + +#include "nsCOMPtr.h" +#include "mozilla/dom/SVGDocument.h" +#include "mozilla/dom/SVGForeignObjectElement.h" +#include "mozilla/dom/SVGForeignObjectElementBinding.h" + +NS_IMPL_NS_NEW_NAMESPACED_SVG_ELEMENT(ForeignObject) + +namespace mozilla { +namespace dom { + +JSObject* +SVGForeignObjectElement::WrapNode(JSContext *aCx, JS::Handle<JSObject*> aGivenProto) +{ + return SVGForeignObjectElementBinding::Wrap(aCx, this, aGivenProto); +} + +nsSVGElement::LengthInfo SVGForeignObjectElement::sLengthInfo[4] = +{ + { &nsGkAtoms::x, 0, nsIDOMSVGLength::SVG_LENGTHTYPE_NUMBER, SVGContentUtils::X }, + { &nsGkAtoms::y, 0, nsIDOMSVGLength::SVG_LENGTHTYPE_NUMBER, SVGContentUtils::Y }, + { &nsGkAtoms::width, 0, nsIDOMSVGLength::SVG_LENGTHTYPE_NUMBER, SVGContentUtils::X }, + { &nsGkAtoms::height, 0, nsIDOMSVGLength::SVG_LENGTHTYPE_NUMBER, SVGContentUtils::Y }, +}; + +//---------------------------------------------------------------------- +// Implementation + +SVGForeignObjectElement::SVGForeignObjectElement(already_AddRefed<mozilla::dom::NodeInfo>& aNodeInfo) + : SVGGraphicsElement(aNodeInfo) +{ +} + +//---------------------------------------------------------------------- +// nsIDOMNode methods + +NS_IMPL_ELEMENT_CLONE_WITH_INIT(SVGForeignObjectElement) + +//---------------------------------------------------------------------- + +already_AddRefed<SVGAnimatedLength> +SVGForeignObjectElement::X() +{ + return mLengthAttributes[ATTR_X].ToDOMAnimatedLength(this); +} + +already_AddRefed<SVGAnimatedLength> +SVGForeignObjectElement::Y() +{ + return mLengthAttributes[ATTR_Y].ToDOMAnimatedLength(this); +} + +already_AddRefed<SVGAnimatedLength> +SVGForeignObjectElement::Width() +{ + return mLengthAttributes[ATTR_WIDTH].ToDOMAnimatedLength(this); +} + +already_AddRefed<SVGAnimatedLength> +SVGForeignObjectElement::Height() +{ + return mLengthAttributes[ATTR_HEIGHT].ToDOMAnimatedLength(this); +} + +//---------------------------------------------------------------------- +// nsSVGElement methods + +/* virtual */ gfxMatrix +SVGForeignObjectElement::PrependLocalTransformsTo( + const gfxMatrix &aMatrix, SVGTransformTypes aWhich) const +{ + // 'transform' attribute: + gfxMatrix fromUserSpace = + SVGGraphicsElement::PrependLocalTransformsTo(aMatrix, aWhich); + if (aWhich == eUserSpaceToParent) { + return fromUserSpace; + } + // our 'x' and 'y' attributes: + float x, y; + const_cast<SVGForeignObjectElement*>(this)-> + GetAnimatedLengthValues(&x, &y, nullptr); + gfxMatrix toUserSpace = gfxMatrix::Translation(x, y); + if (aWhich == eChildToUserSpace) { + return toUserSpace * aMatrix; + } + MOZ_ASSERT(aWhich == eAllTransforms, "Unknown TransformTypes"); + return toUserSpace * fromUserSpace; +} + +/* virtual */ bool +SVGForeignObjectElement::HasValidDimensions() const +{ + return mLengthAttributes[ATTR_WIDTH].IsExplicitlySet() && + mLengthAttributes[ATTR_WIDTH].GetAnimValInSpecifiedUnits() > 0 && + mLengthAttributes[ATTR_HEIGHT].IsExplicitlySet() && + mLengthAttributes[ATTR_HEIGHT].GetAnimValInSpecifiedUnits() > 0; +} + +//---------------------------------------------------------------------- +// nsIContent methods + +nsresult +SVGForeignObjectElement::BindToTree(nsIDocument* aDocument, + nsIContent* aParent, + nsIContent* aBindingParent, + bool aCompileEventHandlers) +{ + nsresult rv = SVGGraphicsElement::BindToTree(aDocument, aParent, + aBindingParent, + aCompileEventHandlers); + NS_ENSURE_SUCCESS(rv, rv); + + nsIDocument* doc = GetComposedDoc(); + if (doc && doc->IsSVGDocument()) { + // We assume that we're going to have HTML content, so we ensure that the + // UA style sheets that nsDocumentViewer::CreateStyleSet skipped when + // it saw the document was an SVG document are loaded. + // + // We setup these style sheets during binding, not element construction, + // because elements can be moved from the document that creates them to + // another document. + doc->AsSVGDocument()->EnsureNonSVGUserAgentStyleSheetsLoaded(); + } + + return rv; +} + +NS_IMETHODIMP_(bool) +SVGForeignObjectElement::IsAttributeMapped(const nsIAtom* name) const +{ + static const MappedAttributeEntry* const map[] = { + sFEFloodMap, + sFiltersMap, + sFontSpecificationMap, + sGradientStopMap, + sLightingEffectsMap, + sMarkersMap, + sTextContentElementsMap, + sViewportsMap + }; + + return FindAttributeDependence(name, map) || + SVGGraphicsElement::IsAttributeMapped(name); +} + +//---------------------------------------------------------------------- +// nsSVGElement methods + +nsSVGElement::LengthAttributesInfo +SVGForeignObjectElement::GetLengthInfo() +{ + return LengthAttributesInfo(mLengthAttributes, sLengthInfo, + ArrayLength(sLengthInfo)); +} + +} // namespace dom +} // namespace mozilla + diff --git a/dom/svg/SVGForeignObjectElement.h b/dom/svg/SVGForeignObjectElement.h new file mode 100644 index 0000000000..095bcd56ca --- /dev/null +++ b/dom/svg/SVGForeignObjectElement.h @@ -0,0 +1,65 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef mozilla_dom_SVGForeignObjectElement_h +#define mozilla_dom_SVGForeignObjectElement_h + +#include "mozilla/dom/SVGGraphicsElement.h" +#include "nsSVGLength2.h" + +nsresult NS_NewSVGForeignObjectElement(nsIContent **aResult, + already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo); + +class nsSVGForeignObjectFrame; + +namespace mozilla { +namespace dom { + +class SVGForeignObjectElement final : public SVGGraphicsElement +{ + friend class ::nsSVGForeignObjectFrame; + +protected: + friend nsresult (::NS_NewSVGForeignObjectElement(nsIContent **aResult, + already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo)); + explicit SVGForeignObjectElement(already_AddRefed<mozilla::dom::NodeInfo>& aNodeInfo); + virtual JSObject* WrapNode(JSContext *cx, JS::Handle<JSObject*> aGivenProto) override; + +public: + // nsSVGElement specializations: + virtual gfxMatrix PrependLocalTransformsTo( + const gfxMatrix &aMatrix, + SVGTransformTypes aWhich = eAllTransforms) const override; + virtual bool HasValidDimensions() const override; + + // nsIContent interface + virtual nsresult BindToTree(nsIDocument* aDocument, nsIContent* aParent, + nsIContent* aBindingParent, + bool aCompileEventHandlers) override; + NS_IMETHOD_(bool) IsAttributeMapped(const nsIAtom* name) const override; + + virtual nsresult Clone(mozilla::dom::NodeInfo *aNodeInfo, nsINode **aResult) const override; + + // WebIDL + already_AddRefed<SVGAnimatedLength> X(); + already_AddRefed<SVGAnimatedLength> Y(); + already_AddRefed<SVGAnimatedLength> Width(); + already_AddRefed<SVGAnimatedLength> Height(); + +protected: + + virtual LengthAttributesInfo GetLengthInfo() override; + + enum { ATTR_X, ATTR_Y, ATTR_WIDTH, ATTR_HEIGHT }; + nsSVGLength2 mLengthAttributes[4]; + static LengthInfo sLengthInfo[4]; +}; + +} // namespace dom +} // namespace mozilla + +#endif // mozilla_dom_SVGForeignObjectElement_h + diff --git a/dom/svg/SVGFragmentIdentifier.cpp b/dom/svg/SVGFragmentIdentifier.cpp new file mode 100644 index 0000000000..3adad54447 --- /dev/null +++ b/dom/svg/SVGFragmentIdentifier.cpp @@ -0,0 +1,202 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "SVGFragmentIdentifier.h" + +#include "mozilla/dom/SVGSVGElement.h" +#include "mozilla/dom/SVGViewElement.h" +#include "nsContentUtils.h" // for nsCharSeparatedTokenizerTemplate +#include "nsSVGAnimatedTransformList.h" +#include "nsCharSeparatedTokenizer.h" + +namespace mozilla { + +using namespace dom; + +static bool +IsMatchingParameter(const nsAString& aString, const nsAString& aParameterName) +{ + // The first two tests ensure aString.Length() > aParameterName.Length() + // so it's then safe to do the third test + return StringBeginsWith(aString, aParameterName) && + aString.Last() == ')' && + aString.CharAt(aParameterName.Length()) == '('; +} + +inline bool +IgnoreWhitespace(char16_t aChar) +{ + return false; +} + +static SVGViewElement* +GetViewElement(nsIDocument* aDocument, const nsAString& aId) +{ + Element* element = aDocument->GetElementById(aId); + return (element && element->IsSVGElement(nsGkAtoms::view)) ? + static_cast<SVGViewElement*>(element) : nullptr; +} + +// Handles setting/clearing the root's mSVGView pointer. +class MOZ_RAII AutoSVGViewHandler +{ +public: + explicit AutoSVGViewHandler(SVGSVGElement* aRoot + MOZ_GUARD_OBJECT_NOTIFIER_PARAM) + : mRoot(aRoot), mValid(false) { + MOZ_GUARD_OBJECT_NOTIFIER_INIT; + mWasOverridden = mRoot->UseCurrentView(); + mRoot->mSVGView = nullptr; + mRoot->mCurrentViewID = nullptr; + } + + ~AutoSVGViewHandler() { + if (!mWasOverridden && !mValid) { + // we weren't overridden before and we aren't + // overridden now so nothing has changed. + return; + } + if (mValid) { + mRoot->mSVGView = mSVGView; + } + mRoot->InvalidateTransformNotifyFrame(); + } + + void CreateSVGView() { + MOZ_ASSERT(!mSVGView, "CreateSVGView should not be called multiple times"); + mSVGView = new SVGView(); + } + + bool ProcessAttr(const nsAString& aToken, const nsAString &aParams) { + + MOZ_ASSERT(mSVGView, "CreateSVGView should have been called"); + + // SVGViewAttributes may occur in any order, but each type may only occur + // at most one time in a correctly formed SVGViewSpec. + // If we encounter any attribute more than once or get any syntax errors + // we're going to return false and cancel any changes. + + if (IsMatchingParameter(aToken, NS_LITERAL_STRING("viewBox"))) { + if (mSVGView->mViewBox.IsExplicitlySet() || + NS_FAILED(mSVGView->mViewBox.SetBaseValueString( + aParams, mRoot, false))) { + return false; + } + } else if (IsMatchingParameter(aToken, NS_LITERAL_STRING("preserveAspectRatio"))) { + if (mSVGView->mPreserveAspectRatio.IsExplicitlySet() || + NS_FAILED(mSVGView->mPreserveAspectRatio.SetBaseValueString( + aParams, mRoot, false))) { + return false; + } + } else if (IsMatchingParameter(aToken, NS_LITERAL_STRING("transform"))) { + if (mSVGView->mTransforms) { + return false; + } + mSVGView->mTransforms = new nsSVGAnimatedTransformList(); + if (NS_FAILED(mSVGView->mTransforms->SetBaseValueString(aParams))) { + return false; + } + } else if (IsMatchingParameter(aToken, NS_LITERAL_STRING("zoomAndPan"))) { + if (mSVGView->mZoomAndPan.IsExplicitlySet()) { + return false; + } + nsIAtom* valAtom = NS_GetStaticAtom(aParams); + if (!valAtom || + NS_FAILED(mSVGView->mZoomAndPan.SetBaseValueAtom( + valAtom, mRoot))) { + return false; + } + } else { + // We don't support viewTarget currently + return false; + } + return true; + } + + void SetValid() { + mValid = true; + } + +private: + SVGSVGElement* mRoot; + nsAutoPtr<SVGView> mSVGView; + bool mValid; + bool mWasOverridden; + MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER +}; + +bool +SVGFragmentIdentifier::ProcessSVGViewSpec(const nsAString& aViewSpec, + SVGSVGElement* aRoot) +{ + AutoSVGViewHandler viewHandler(aRoot); + + if (!IsMatchingParameter(aViewSpec, NS_LITERAL_STRING("svgView"))) { + return false; + } + + // Each token is a SVGViewAttribute + int32_t bracketPos = aViewSpec.FindChar('('); + uint32_t lengthOfViewSpec = aViewSpec.Length() - bracketPos - 2; + nsCharSeparatedTokenizerTemplate<IgnoreWhitespace> tokenizer( + Substring(aViewSpec, bracketPos + 1, lengthOfViewSpec), ';'); + + if (!tokenizer.hasMoreTokens()) { + return false; + } + viewHandler.CreateSVGView(); + + do { + + nsAutoString token(tokenizer.nextToken()); + + bracketPos = token.FindChar('('); + if (bracketPos < 1 || token.Last() != ')') { + // invalid SVGViewAttribute syntax + return false; + } + + const nsAString ¶ms = + Substring(token, bracketPos + 1, token.Length() - bracketPos - 2); + + if (!viewHandler.ProcessAttr(token, params)) { + return false; + } + + } while (tokenizer.hasMoreTokens()); + + viewHandler.SetValid(); + return true; +} + +bool +SVGFragmentIdentifier::ProcessFragmentIdentifier(nsIDocument* aDocument, + const nsAString& aAnchorName) +{ + MOZ_ASSERT(aDocument->GetRootElement()->IsSVGElement(nsGkAtoms::svg), + "expecting an SVG root element"); + + SVGSVGElement* rootElement = + static_cast<SVGSVGElement*>(aDocument->GetRootElement()); + + const SVGViewElement* viewElement = GetViewElement(aDocument, aAnchorName); + + if (viewElement) { + if (!rootElement->mCurrentViewID) { + rootElement->mCurrentViewID = new nsString(); + } + *rootElement->mCurrentViewID = aAnchorName; + rootElement->mSVGView = nullptr; + rootElement->InvalidateTransformNotifyFrame(); + // not an svgView()-style fragment identifier, return false so the caller + // continues processing to match any :target pseudo elements + return false; + } + + return ProcessSVGViewSpec(aAnchorName, rootElement); +} + +} // namespace mozilla diff --git a/dom/svg/SVGFragmentIdentifier.h b/dom/svg/SVGFragmentIdentifier.h new file mode 100644 index 0000000000..0fb6fb1a78 --- /dev/null +++ b/dom/svg/SVGFragmentIdentifier.h @@ -0,0 +1,49 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef MOZILLA_SVGFRAGMENTIDENTIFIER_H__ +#define MOZILLA_SVGFRAGMENTIDENTIFIER_H__ + +#include "nsString.h" + +class nsIDocument; + +namespace mozilla { + +namespace dom { +class SVGSVGElement; +} // namespace dom + +/** + * Implements support for parsing SVG fragment identifiers + * http://www.w3.org/TR/SVG/linking.html#SVGFragmentIdentifiers + */ +class SVGFragmentIdentifier +{ + // To prevent the class being instantiated + SVGFragmentIdentifier() = delete; + +public: + /** + * Process the SVG fragment identifier, if there is one. + * @return true if we found a valid svgView()-style fragment identifier, + * in which case further processing by the caller can stop. Otherwise return + * false as we may have an ordinary anchor which needs to be :target matched. + */ + static bool ProcessFragmentIdentifier(nsIDocument *aDocument, + const nsAString &aAnchorName); + +private: + /** + * Parse an SVG ViewSpec and set applicable attributes on the root element. + * @return true if there is a valid ViewSpec + */ + static bool ProcessSVGViewSpec(const nsAString &aViewSpec, dom::SVGSVGElement *root); +}; + +} // namespace mozilla + +#endif // MOZILLA_SVGFRAGMENTIDENTIFIER_H__ diff --git a/dom/svg/SVGGElement.cpp b/dom/svg/SVGGElement.cpp new file mode 100644 index 0000000000..9b052eea57 --- /dev/null +++ b/dom/svg/SVGGElement.cpp @@ -0,0 +1,60 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "mozilla/dom/SVGGElement.h" +#include "mozilla/dom/SVGGElementBinding.h" + +NS_IMPL_NS_NEW_NAMESPACED_SVG_ELEMENT(G) + +namespace mozilla { +namespace dom { + +JSObject* +SVGGElement::WrapNode(JSContext *aCx, JS::Handle<JSObject*> aGivenProto) +{ + return SVGGElementBinding::Wrap(aCx, this, aGivenProto); +} + +//---------------------------------------------------------------------- +// Implementation + +SVGGElement::SVGGElement(already_AddRefed<mozilla::dom::NodeInfo>& aNodeInfo) + : SVGGraphicsElement(aNodeInfo) +{ +} + + +//---------------------------------------------------------------------- +// nsIDOMNode methods + + +NS_IMPL_ELEMENT_CLONE_WITH_INIT(SVGGElement) + + +//---------------------------------------------------------------------- +// nsIContent methods + +NS_IMETHODIMP_(bool) +SVGGElement::IsAttributeMapped(const nsIAtom* name) const +{ + static const MappedAttributeEntry* const map[] = { + sFEFloodMap, + sFiltersMap, + sFontSpecificationMap, + sGradientStopMap, + sLightingEffectsMap, + sMarkersMap, + sTextContentElementsMap, + sViewportsMap + }; + + return FindAttributeDependence(name, map) || + SVGGraphicsElement::IsAttributeMapped(name); +} + +} // namespace dom +} // namespace mozilla + diff --git a/dom/svg/SVGGElement.h b/dom/svg/SVGGElement.h new file mode 100644 index 0000000000..ee0d9c9da0 --- /dev/null +++ b/dom/svg/SVGGElement.h @@ -0,0 +1,36 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef mozilla_dom_SVGGElement_h +#define mozilla_dom_SVGGElement_h + +#include "mozilla/dom/SVGGraphicsElement.h" + +nsresult NS_NewSVGGElement(nsIContent **aResult, + already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo); + +namespace mozilla { +namespace dom { + +class SVGGElement final : public SVGGraphicsElement +{ +protected: + explicit SVGGElement(already_AddRefed<mozilla::dom::NodeInfo>& aNodeInfo); + virtual JSObject* WrapNode(JSContext *cx, JS::Handle<JSObject*> aGivenProto) override; + friend nsresult (::NS_NewSVGGElement(nsIContent **aResult, + already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo)); + +public: + // nsIContent + NS_IMETHOD_(bool) IsAttributeMapped(const nsIAtom* aAttribute) const override; + + virtual nsresult Clone(mozilla::dom::NodeInfo *aNodeInfo, nsINode **aResult) const override; +}; + +} // namespace dom +} // namespace mozilla + +#endif // mozilla_dom_SVGGElement_h diff --git a/dom/svg/SVGGradientElement.cpp b/dom/svg/SVGGradientElement.cpp new file mode 100644 index 0000000000..1bcac67e6e --- /dev/null +++ b/dom/svg/SVGGradientElement.cpp @@ -0,0 +1,266 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "mozilla/dom/SVGGradientElement.h" + +#include "mozilla/ArrayUtils.h" +#include "mozilla/dom/SVGAnimatedTransformList.h" +#include "mozilla/dom/SVGRadialGradientElementBinding.h" +#include "mozilla/dom/SVGLinearGradientElementBinding.h" +#include "nsCOMPtr.h" +#include "nsGkAtoms.h" +#include "nsSVGElement.h" + +NS_IMPL_NS_NEW_NAMESPACED_SVG_ELEMENT(LinearGradient) +NS_IMPL_NS_NEW_NAMESPACED_SVG_ELEMENT(RadialGradient) + +namespace mozilla { +namespace dom { + +//--------------------- Gradients------------------------ + +nsSVGEnumMapping SVGGradientElement::sSpreadMethodMap[] = { + {&nsGkAtoms::pad, SVG_SPREADMETHOD_PAD}, + {&nsGkAtoms::reflect, SVG_SPREADMETHOD_REFLECT}, + {&nsGkAtoms::repeat, SVG_SPREADMETHOD_REPEAT}, + {nullptr, 0} +}; + +nsSVGElement::EnumInfo SVGGradientElement::sEnumInfo[2] = +{ + { &nsGkAtoms::gradientUnits, + sSVGUnitTypesMap, + SVG_UNIT_TYPE_OBJECTBOUNDINGBOX + }, + { &nsGkAtoms::spreadMethod, + sSpreadMethodMap, + SVG_SPREADMETHOD_PAD + } +}; + +nsSVGElement::StringInfo SVGGradientElement::sStringInfo[2] = +{ + { &nsGkAtoms::href, kNameSpaceID_None, true }, + { &nsGkAtoms::href, kNameSpaceID_XLink, true } +}; + +//---------------------------------------------------------------------- +// Implementation + +SVGGradientElement::SVGGradientElement(already_AddRefed<mozilla::dom::NodeInfo>& aNodeInfo) + : SVGGradientElementBase(aNodeInfo) +{ +} + +//---------------------------------------------------------------------- +// nsSVGElement methods + +nsSVGElement::EnumAttributesInfo +SVGGradientElement::GetEnumInfo() +{ + return EnumAttributesInfo(mEnumAttributes, sEnumInfo, + ArrayLength(sEnumInfo)); +} + +nsSVGElement::StringAttributesInfo +SVGGradientElement::GetStringInfo() +{ + return StringAttributesInfo(mStringAttributes, sStringInfo, + ArrayLength(sStringInfo)); +} + +already_AddRefed<SVGAnimatedEnumeration> +SVGGradientElement::GradientUnits() +{ + return mEnumAttributes[GRADIENTUNITS].ToDOMAnimatedEnum(this); +} + +already_AddRefed<SVGAnimatedTransformList> +SVGGradientElement::GradientTransform() +{ + // We're creating a DOM wrapper, so we must tell GetAnimatedTransformList + // to allocate the SVGAnimatedTransformList if it hasn't already done so: + return SVGAnimatedTransformList::GetDOMWrapper( + GetAnimatedTransformList(DO_ALLOCATE), this); +} + +already_AddRefed<SVGAnimatedEnumeration> +SVGGradientElement::SpreadMethod() +{ + return mEnumAttributes[SPREADMETHOD].ToDOMAnimatedEnum(this); +} + +already_AddRefed<SVGAnimatedString> +SVGGradientElement::Href() +{ + return mStringAttributes[HREF].IsExplicitlySet() + ? mStringAttributes[HREF].ToDOMAnimatedString(this) + : mStringAttributes[XLINK_HREF].ToDOMAnimatedString(this); +} + +//---------------------------------------------------------------------- +// nsIContent methods + +NS_IMETHODIMP_(bool) +SVGGradientElement::IsAttributeMapped(const nsIAtom* name) const +{ + static const MappedAttributeEntry* const map[] = { + sColorMap, + sGradientStopMap + }; + + return FindAttributeDependence(name, map) || + SVGGradientElementBase::IsAttributeMapped(name); +} + +//---------------------Linear Gradients------------------------ + +JSObject* +SVGLinearGradientElement::WrapNode(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) +{ + return SVGLinearGradientElementBinding::Wrap(aCx, this, aGivenProto); +} + +nsSVGElement::LengthInfo SVGLinearGradientElement::sLengthInfo[4] = +{ + { &nsGkAtoms::x1, 0, nsIDOMSVGLength::SVG_LENGTHTYPE_PERCENTAGE, SVGContentUtils::X }, + { &nsGkAtoms::y1, 0, nsIDOMSVGLength::SVG_LENGTHTYPE_PERCENTAGE, SVGContentUtils::Y }, + { &nsGkAtoms::x2, 100, nsIDOMSVGLength::SVG_LENGTHTYPE_PERCENTAGE, SVGContentUtils::X }, + { &nsGkAtoms::y2, 0, nsIDOMSVGLength::SVG_LENGTHTYPE_PERCENTAGE, SVGContentUtils::Y }, +}; + +//---------------------------------------------------------------------- +// Implementation + +SVGLinearGradientElement::SVGLinearGradientElement(already_AddRefed<mozilla::dom::NodeInfo>& aNodeInfo) + : SVGLinearGradientElementBase(aNodeInfo) +{ +} + +//---------------------------------------------------------------------- +// nsIDOMNode methods + +NS_IMPL_ELEMENT_CLONE_WITH_INIT(SVGLinearGradientElement) + +//---------------------------------------------------------------------- + +already_AddRefed<SVGAnimatedLength> +SVGLinearGradientElement::X1() +{ + return mLengthAttributes[ATTR_X1].ToDOMAnimatedLength(this); +} + +already_AddRefed<SVGAnimatedLength> +SVGLinearGradientElement::Y1() +{ + return mLengthAttributes[ATTR_Y1].ToDOMAnimatedLength(this); +} + +already_AddRefed<SVGAnimatedLength> +SVGLinearGradientElement::X2() +{ + return mLengthAttributes[ATTR_X2].ToDOMAnimatedLength(this); +} + +already_AddRefed<SVGAnimatedLength> +SVGLinearGradientElement::Y2() +{ + return mLengthAttributes[ATTR_Y2].ToDOMAnimatedLength(this); +} + +//---------------------------------------------------------------------- +// nsSVGElement methods + +nsSVGAnimatedTransformList* +SVGGradientElement::GetAnimatedTransformList(uint32_t aFlags) +{ + if (!mGradientTransform && (aFlags & DO_ALLOCATE)) { + mGradientTransform = new nsSVGAnimatedTransformList(); + } + return mGradientTransform; +} + +nsSVGElement::LengthAttributesInfo +SVGLinearGradientElement::GetLengthInfo() +{ + return LengthAttributesInfo(mLengthAttributes, sLengthInfo, + ArrayLength(sLengthInfo)); +} + +//-------------------------- Radial Gradients ---------------------------- + +JSObject* +SVGRadialGradientElement::WrapNode(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) +{ + return SVGRadialGradientElementBinding::Wrap(aCx, this, aGivenProto); +} + +nsSVGElement::LengthInfo SVGRadialGradientElement::sLengthInfo[5] = +{ + { &nsGkAtoms::cx, 50, nsIDOMSVGLength::SVG_LENGTHTYPE_PERCENTAGE, SVGContentUtils::X }, + { &nsGkAtoms::cy, 50, nsIDOMSVGLength::SVG_LENGTHTYPE_PERCENTAGE, SVGContentUtils::Y }, + { &nsGkAtoms::r, 50, nsIDOMSVGLength::SVG_LENGTHTYPE_PERCENTAGE, SVGContentUtils::XY }, + { &nsGkAtoms::fx, 50, nsIDOMSVGLength::SVG_LENGTHTYPE_PERCENTAGE, SVGContentUtils::X }, + { &nsGkAtoms::fy, 50, nsIDOMSVGLength::SVG_LENGTHTYPE_PERCENTAGE, SVGContentUtils::Y }, +}; + +//---------------------------------------------------------------------- +// Implementation + +SVGRadialGradientElement::SVGRadialGradientElement(already_AddRefed<mozilla::dom::NodeInfo>& aNodeInfo) + : SVGRadialGradientElementBase(aNodeInfo) +{ +} + +//---------------------------------------------------------------------- +// nsIDOMNode methods + +NS_IMPL_ELEMENT_CLONE_WITH_INIT(SVGRadialGradientElement) + +//---------------------------------------------------------------------- + +already_AddRefed<SVGAnimatedLength> +SVGRadialGradientElement::Cx() +{ + return mLengthAttributes[ATTR_CX].ToDOMAnimatedLength(this); +} + +already_AddRefed<SVGAnimatedLength> +SVGRadialGradientElement::Cy() +{ + return mLengthAttributes[ATTR_CY].ToDOMAnimatedLength(this); +} + +already_AddRefed<SVGAnimatedLength> +SVGRadialGradientElement::R() +{ + return mLengthAttributes[ATTR_R].ToDOMAnimatedLength(this); +} + +already_AddRefed<SVGAnimatedLength> +SVGRadialGradientElement::Fx() +{ + return mLengthAttributes[ATTR_FX].ToDOMAnimatedLength(this); +} + +already_AddRefed<SVGAnimatedLength> +SVGRadialGradientElement::Fy() +{ + return mLengthAttributes[ATTR_FY].ToDOMAnimatedLength(this); +} + +//---------------------------------------------------------------------- +// nsSVGElement methods + +nsSVGElement::LengthAttributesInfo +SVGRadialGradientElement::GetLengthInfo() +{ + return LengthAttributesInfo(mLengthAttributes, sLengthInfo, + ArrayLength(sLengthInfo)); +} + +} // namespace dom +} // namespace mozilla diff --git a/dom/svg/SVGGradientElement.h b/dom/svg/SVGGradientElement.h new file mode 100644 index 0000000000..7b2c6b2200 --- /dev/null +++ b/dom/svg/SVGGradientElement.h @@ -0,0 +1,154 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef __NS_SVGGRADIENTELEMENT_H__ +#define __NS_SVGGRADIENTELEMENT_H__ + +#include "nsAutoPtr.h" +#include "nsSVGAnimatedTransformList.h" +#include "nsSVGElement.h" +#include "nsSVGLength2.h" +#include "nsSVGEnum.h" +#include "nsSVGString.h" + +static const unsigned short SVG_SPREADMETHOD_UNKNOWN = 0; +static const unsigned short SVG_SPREADMETHOD_PAD = 1; +static const unsigned short SVG_SPREADMETHOD_REFLECT = 2; +static const unsigned short SVG_SPREADMETHOD_REPEAT = 3; + +class nsSVGGradientFrame; +class nsSVGLinearGradientFrame; +class nsSVGRadialGradientFrame; + +nsresult +NS_NewSVGLinearGradientElement(nsIContent** aResult, + already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo); +nsresult +NS_NewSVGRadialGradientElement(nsIContent** aResult, + already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo); + +namespace mozilla { +namespace dom { + +class SVGAnimatedTransformList; + +//--------------------- Gradients------------------------ + +typedef nsSVGElement SVGGradientElementBase; + +class SVGGradientElement : public SVGGradientElementBase +{ + friend class ::nsSVGGradientFrame; + +protected: + explicit SVGGradientElement(already_AddRefed<mozilla::dom::NodeInfo>& aNodeInfo); + virtual JSObject* WrapNode(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override = 0; + +public: + virtual nsresult Clone(mozilla::dom::NodeInfo *aNodeInfo, nsINode **aResult) const override = 0; + + // nsIContent + NS_IMETHOD_(bool) IsAttributeMapped(const nsIAtom* aAttribute) const override; + + virtual nsSVGAnimatedTransformList* + GetAnimatedTransformList(uint32_t aFlags = 0) override; + virtual nsIAtom* GetTransformListAttrName() const override { + return nsGkAtoms::gradientTransform; + } + + // WebIDL + already_AddRefed<SVGAnimatedEnumeration> GradientUnits(); + already_AddRefed<SVGAnimatedTransformList> GradientTransform(); + already_AddRefed<SVGAnimatedEnumeration> SpreadMethod(); + already_AddRefed<SVGAnimatedString> Href(); + +protected: + virtual EnumAttributesInfo GetEnumInfo() override; + virtual StringAttributesInfo GetStringInfo() override; + + enum { GRADIENTUNITS, SPREADMETHOD }; + nsSVGEnum mEnumAttributes[2]; + static nsSVGEnumMapping sSpreadMethodMap[]; + static EnumInfo sEnumInfo[2]; + + enum { HREF, XLINK_HREF }; + nsSVGString mStringAttributes[2]; + static StringInfo sStringInfo[2]; + + // SVGGradientElement values + nsAutoPtr<nsSVGAnimatedTransformList> mGradientTransform; +}; + +//---------------------Linear Gradients------------------------ + +typedef SVGGradientElement SVGLinearGradientElementBase; + +class SVGLinearGradientElement : public SVGLinearGradientElementBase +{ + friend class ::nsSVGLinearGradientFrame; + friend nsresult + (::NS_NewSVGLinearGradientElement(nsIContent** aResult, + already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo)); + +protected: + explicit SVGLinearGradientElement(already_AddRefed<mozilla::dom::NodeInfo>& aNodeInfo); + virtual JSObject* WrapNode(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override; + +public: + virtual nsresult Clone(mozilla::dom::NodeInfo *aNodeInfo, nsINode **aResult) const override; + + // WebIDL + already_AddRefed<SVGAnimatedLength> X1(); + already_AddRefed<SVGAnimatedLength> Y1(); + already_AddRefed<SVGAnimatedLength> X2(); + already_AddRefed<SVGAnimatedLength> Y2(); + +protected: + + virtual LengthAttributesInfo GetLengthInfo() override; + + enum { ATTR_X1, ATTR_Y1, ATTR_X2, ATTR_Y2 }; + nsSVGLength2 mLengthAttributes[4]; + static LengthInfo sLengthInfo[4]; +}; + +//-------------------------- Radial Gradients ---------------------------- + +typedef SVGGradientElement SVGRadialGradientElementBase; + +class SVGRadialGradientElement : public SVGRadialGradientElementBase +{ + friend class ::nsSVGRadialGradientFrame; + friend nsresult + (::NS_NewSVGRadialGradientElement(nsIContent** aResult, + already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo)); + +protected: + explicit SVGRadialGradientElement(already_AddRefed<mozilla::dom::NodeInfo>& aNodeInfo); + virtual JSObject* WrapNode(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override; + +public: + virtual nsresult Clone(mozilla::dom::NodeInfo *aNodeInfo, nsINode **aResult) const override; + + // WebIDL + already_AddRefed<SVGAnimatedLength> Cx(); + already_AddRefed<SVGAnimatedLength> Cy(); + already_AddRefed<SVGAnimatedLength> R(); + already_AddRefed<SVGAnimatedLength> Fx(); + already_AddRefed<SVGAnimatedLength> Fy(); +protected: + + virtual LengthAttributesInfo GetLengthInfo() override; + + enum { ATTR_CX, ATTR_CY, ATTR_R, ATTR_FX, ATTR_FY }; + nsSVGLength2 mLengthAttributes[5]; + static LengthInfo sLengthInfo[5]; +}; + +} // namespace dom +} // namespace mozilla + +#endif diff --git a/dom/svg/SVGGraphicsElement.cpp b/dom/svg/SVGGraphicsElement.cpp new file mode 100644 index 0000000000..ba63066803 --- /dev/null +++ b/dom/svg/SVGGraphicsElement.cpp @@ -0,0 +1,41 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "mozilla/dom/SVGGraphicsElement.h" + +namespace mozilla { +namespace dom { + +//---------------------------------------------------------------------- +// nsISupports methods + +NS_IMPL_ADDREF_INHERITED(SVGGraphicsElement, SVGGraphicsElementBase) +NS_IMPL_RELEASE_INHERITED(SVGGraphicsElement, SVGGraphicsElementBase) + +NS_INTERFACE_MAP_BEGIN(SVGGraphicsElement) + NS_INTERFACE_MAP_ENTRY(mozilla::dom::SVGTests) +NS_INTERFACE_MAP_END_INHERITING(SVGGraphicsElementBase) + +//---------------------------------------------------------------------- +// Implementation + +SVGGraphicsElement::SVGGraphicsElement(already_AddRefed<mozilla::dom::NodeInfo>& aNodeInfo) + : SVGGraphicsElementBase(aNodeInfo) +{ +} + +SVGGraphicsElement::~SVGGraphicsElement() +{ +} + +bool +SVGGraphicsElement::IsInChromeDoc() const +{ + return nsContentUtils::IsChromeDoc(OwnerDoc()); +} + +} // namespace dom +} // namespace mozilla diff --git a/dom/svg/SVGGraphicsElement.h b/dom/svg/SVGGraphicsElement.h new file mode 100644 index 0000000000..a6437dba9e --- /dev/null +++ b/dom/svg/SVGGraphicsElement.h @@ -0,0 +1,35 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef mozilla_dom_SVGGraphicsElement_h +#define mozilla_dom_SVGGraphicsElement_h + +#include "mozilla/dom/SVGTests.h" +#include "mozilla/dom/SVGTransformableElement.h" + +namespace mozilla { +namespace dom { + +typedef SVGTransformableElement SVGGraphicsElementBase; + +class SVGGraphicsElement : public SVGGraphicsElementBase, + public SVGTests +{ +protected: + explicit SVGGraphicsElement(already_AddRefed<mozilla::dom::NodeInfo>& aNodeInfo); + ~SVGGraphicsElement(); + +public: + // interfaces: + NS_DECL_ISUPPORTS_INHERITED + + bool IsInChromeDoc() const override; +}; + +} // namespace dom +} // namespace mozilla + +#endif // mozilla_dom_SVGGraphicsElement_h diff --git a/dom/svg/SVGIRect.h b/dom/svg/SVGIRect.h new file mode 100644 index 0000000000..d34860095c --- /dev/null +++ b/dom/svg/SVGIRect.h @@ -0,0 +1,57 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef mozilla_dom_SVGIRect_h +#define mozilla_dom_SVGIRect_h + +#include "nsCycleCollectionParticipant.h" +#include "mozilla/dom/SVGRectBinding.h" +#include "mozilla/Attributes.h" +#include "mozilla/ErrorResult.h" +#include "nsWrapperCache.h" + +class nsIContent; + +namespace mozilla { +namespace dom { + +class SVGIRect : public nsISupports, + public nsWrapperCache +{ +public: + virtual ~SVGIRect() + { + } + + JSObject* WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override + { + return SVGRectBinding::Wrap(aCx, this, aGivenProto); + } + + virtual nsIContent* GetParentObject() const = 0; + + virtual float X() const = 0; + + virtual void SetX(float aX, ErrorResult& aRv) = 0; + + virtual float Y() const = 0; + + virtual void SetY(float aY, ErrorResult& aRv) = 0; + + virtual float Width() const = 0; + + virtual void SetWidth(float aWidth, ErrorResult& aRv) = 0; + + virtual float Height() const = 0; + + virtual void SetHeight(float aHeight, ErrorResult& aRv) = 0; +}; + +} // namespace dom +} // namespace mozilla + +#endif //mozilla_dom_SVGIRect_h + diff --git a/dom/svg/SVGImageElement.cpp b/dom/svg/SVGImageElement.cpp new file mode 100644 index 0000000000..524485dee8 --- /dev/null +++ b/dom/svg/SVGImageElement.cpp @@ -0,0 +1,327 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "mozilla/ArrayUtils.h" +#include "mozilla/EventStates.h" + +#include "mozilla/dom/SVGImageElement.h" +#include "mozilla/gfx/2D.h" +#include "nsCOMPtr.h" +#include "nsIURI.h" +#include "nsNetUtil.h" +#include "imgINotificationObserver.h" +#include "mozilla/dom/SVGImageElementBinding.h" +#include "nsContentUtils.h" + +NS_IMPL_NS_NEW_NAMESPACED_SVG_ELEMENT(Image) + +using namespace mozilla::gfx; + +namespace mozilla { +namespace dom { + +JSObject* +SVGImageElement::WrapNode(JSContext *aCx, JS::Handle<JSObject*> aGivenProto) +{ + return SVGImageElementBinding::Wrap(aCx, this, aGivenProto); +} + +nsSVGElement::LengthInfo SVGImageElement::sLengthInfo[4] = +{ + { &nsGkAtoms::x, 0, nsIDOMSVGLength::SVG_LENGTHTYPE_NUMBER, SVGContentUtils::X }, + { &nsGkAtoms::y, 0, nsIDOMSVGLength::SVG_LENGTHTYPE_NUMBER, SVGContentUtils::Y }, + { &nsGkAtoms::width, 0, nsIDOMSVGLength::SVG_LENGTHTYPE_NUMBER, SVGContentUtils::X }, + { &nsGkAtoms::height, 0, nsIDOMSVGLength::SVG_LENGTHTYPE_NUMBER, SVGContentUtils::Y }, +}; + +nsSVGElement::StringInfo SVGImageElement::sStringInfo[2] = +{ + { &nsGkAtoms::href, kNameSpaceID_None, true }, + { &nsGkAtoms::href, kNameSpaceID_XLink, true } +}; + +//---------------------------------------------------------------------- +// nsISupports methods + +NS_IMPL_ISUPPORTS_INHERITED(SVGImageElement, SVGImageElementBase, + nsIDOMNode, nsIDOMElement, + nsIDOMSVGElement, + imgINotificationObserver, + nsIImageLoadingContent, imgIOnloadBlocker) + +//---------------------------------------------------------------------- +// Implementation + +SVGImageElement::SVGImageElement(already_AddRefed<mozilla::dom::NodeInfo>& aNodeInfo) + : SVGImageElementBase(aNodeInfo) +{ + // We start out broken + AddStatesSilently(NS_EVENT_STATE_BROKEN); +} + +SVGImageElement::~SVGImageElement() +{ + DestroyImageLoadingContent(); +} + +//---------------------------------------------------------------------- +// nsIDOMNode methods + + +NS_IMPL_ELEMENT_CLONE_WITH_INIT(SVGImageElement) + + +//---------------------------------------------------------------------- + +already_AddRefed<SVGAnimatedLength> +SVGImageElement::X() +{ + return mLengthAttributes[ATTR_X].ToDOMAnimatedLength(this); +} + +already_AddRefed<SVGAnimatedLength> +SVGImageElement::Y() +{ + return mLengthAttributes[ATTR_Y].ToDOMAnimatedLength(this); +} + +already_AddRefed<SVGAnimatedLength> +SVGImageElement::Width() +{ + return mLengthAttributes[ATTR_WIDTH].ToDOMAnimatedLength(this); +} + +already_AddRefed<SVGAnimatedLength> +SVGImageElement::Height() +{ + return mLengthAttributes[ATTR_HEIGHT].ToDOMAnimatedLength(this); +} + +already_AddRefed<DOMSVGAnimatedPreserveAspectRatio> +SVGImageElement::PreserveAspectRatio() +{ + return mPreserveAspectRatio.ToDOMAnimatedPreserveAspectRatio(this); +} + +already_AddRefed<SVGAnimatedString> +SVGImageElement::Href() +{ + return mStringAttributes[HREF].IsExplicitlySet() + ? mStringAttributes[HREF].ToDOMAnimatedString(this) + : mStringAttributes[XLINK_HREF].ToDOMAnimatedString(this); +} + +//---------------------------------------------------------------------- + +nsresult +SVGImageElement::LoadSVGImage(bool aForce, bool aNotify) +{ + // resolve href attribute + nsCOMPtr<nsIURI> baseURI = GetBaseURI(); + + nsAutoString href; + if (mStringAttributes[HREF].IsExplicitlySet()) { + mStringAttributes[HREF].GetAnimValue(href, this); + } else { + mStringAttributes[XLINK_HREF].GetAnimValue(href, this); + } + href.Trim(" \t\n\r"); + + if (baseURI && !href.IsEmpty()) + NS_MakeAbsoluteURI(href, href, baseURI); + + return LoadImage(href, aForce, aNotify, eImageLoadType_Normal); +} + +//---------------------------------------------------------------------- +// EventTarget methods: + +void +SVGImageElement::AsyncEventRunning(AsyncEventDispatcher* aEvent) +{ + nsImageLoadingContent::AsyncEventRunning(aEvent); +} + +//---------------------------------------------------------------------- +// nsIContent methods: + +nsresult +SVGImageElement::AfterSetAttr(int32_t aNamespaceID, nsIAtom* aName, + const nsAttrValue* aValue, bool aNotify) +{ + if (aName == nsGkAtoms::href && + (aNamespaceID == kNameSpaceID_None || + aNamespaceID == kNameSpaceID_XLink)) { + + // If there isn't a frame we still need to load the image in case + // the frame is created later e.g. by attaching to a document. + // If there is a frame then it should deal with loading as the image + // url may be animated + if (!GetPrimaryFrame()) { + if (aValue) { + LoadSVGImage(true, aNotify); + } else { + CancelImageRequests(aNotify); + } + } + } + return SVGImageElementBase::AfterSetAttr(aNamespaceID, aName, + aValue, aNotify); +} + +void +SVGImageElement::MaybeLoadSVGImage() +{ + if ((mStringAttributes[HREF].IsExplicitlySet() || + mStringAttributes[XLINK_HREF].IsExplicitlySet()) && + (NS_FAILED(LoadSVGImage(false, true)) || + !LoadingEnabled())) { + CancelImageRequests(true); + } +} + +nsresult +SVGImageElement::BindToTree(nsIDocument* aDocument, nsIContent* aParent, + nsIContent* aBindingParent, + bool aCompileEventHandlers) +{ + nsresult rv = SVGImageElementBase::BindToTree(aDocument, aParent, + aBindingParent, + aCompileEventHandlers); + NS_ENSURE_SUCCESS(rv, rv); + + nsImageLoadingContent::BindToTree(aDocument, aParent, aBindingParent, + aCompileEventHandlers); + + if (mStringAttributes[HREF].IsExplicitlySet() || + mStringAttributes[XLINK_HREF].IsExplicitlySet()) { + // FIXME: Bug 660963 it would be nice if we could just have + // ClearBrokenState update our state and do it fast... + ClearBrokenState(); + RemoveStatesSilently(NS_EVENT_STATE_BROKEN); + nsContentUtils::AddScriptRunner( + NewRunnableMethod(this, &SVGImageElement::MaybeLoadSVGImage)); + } + + return rv; +} + +void +SVGImageElement::UnbindFromTree(bool aDeep, bool aNullParent) +{ + nsImageLoadingContent::UnbindFromTree(aDeep, aNullParent); + SVGImageElementBase::UnbindFromTree(aDeep, aNullParent); +} + +EventStates +SVGImageElement::IntrinsicState() const +{ + return SVGImageElementBase::IntrinsicState() | + nsImageLoadingContent::ImageState(); +} + +NS_IMETHODIMP_(bool) +SVGImageElement::IsAttributeMapped(const nsIAtom* name) const +{ + static const MappedAttributeEntry* const map[] = { + sViewportsMap, + }; + + return FindAttributeDependence(name, map) || + SVGImageElementBase::IsAttributeMapped(name); +} + +//---------------------------------------------------------------------- +// nsSVGPathGeometryElement methods + +/* For the purposes of the update/invalidation logic pretend to + be a rectangle. */ +bool +SVGImageElement::GetGeometryBounds(Rect* aBounds, + const StrokeOptions& aStrokeOptions, + const Matrix& aToBoundsSpace, + const Matrix* aToNonScalingStrokeSpace) +{ + Rect rect; + GetAnimatedLengthValues(&rect.x, &rect.y, &rect.width, + &rect.height, nullptr); + + if (rect.IsEmpty()) { + // Rendering of the element disabled + rect.SetEmpty(); // Make sure width/height are zero and not negative + } + + *aBounds = aToBoundsSpace.TransformBounds(rect); + return true; +} + +already_AddRefed<Path> +SVGImageElement::BuildPath(PathBuilder* aBuilder) +{ + // We get called in order to get bounds for this element, and for + // hit-testing against it. For that we just pretend to be a rectangle. + + float x, y, width, height; + GetAnimatedLengthValues(&x, &y, &width, &height, nullptr); + + if (width <= 0 || height <= 0) { + return nullptr; + } + + Rect r(x, y, width, height); + aBuilder->MoveTo(r.TopLeft()); + aBuilder->LineTo(r.TopRight()); + aBuilder->LineTo(r.BottomRight()); + aBuilder->LineTo(r.BottomLeft()); + aBuilder->Close(); + + return aBuilder->Finish(); +} + +//---------------------------------------------------------------------- +// nsSVGElement methods + +/* virtual */ bool +SVGImageElement::HasValidDimensions() const +{ + return mLengthAttributes[ATTR_WIDTH].IsExplicitlySet() && + mLengthAttributes[ATTR_WIDTH].GetAnimValInSpecifiedUnits() > 0 && + mLengthAttributes[ATTR_HEIGHT].IsExplicitlySet() && + mLengthAttributes[ATTR_HEIGHT].GetAnimValInSpecifiedUnits() > 0; +} + +nsSVGElement::LengthAttributesInfo +SVGImageElement::GetLengthInfo() +{ + return LengthAttributesInfo(mLengthAttributes, sLengthInfo, + ArrayLength(sLengthInfo)); +} + +SVGAnimatedPreserveAspectRatio * +SVGImageElement::GetPreserveAspectRatio() +{ + return &mPreserveAspectRatio; +} + +nsSVGElement::StringAttributesInfo +SVGImageElement::GetStringInfo() +{ + return StringAttributesInfo(mStringAttributes, sStringInfo, + ArrayLength(sStringInfo)); +} + +nsresult +SVGImageElement::CopyInnerTo(Element* aDest) +{ + if (aDest->OwnerDoc()->IsStaticDocument()) { + CreateStaticImageClone(static_cast<SVGImageElement*>(aDest)); + } + return SVGImageElementBase::CopyInnerTo(aDest); +} + +} // namespace dom +} // namespace mozilla + diff --git a/dom/svg/SVGImageElement.h b/dom/svg/SVGImageElement.h new file mode 100644 index 0000000000..85730212b4 --- /dev/null +++ b/dom/svg/SVGImageElement.h @@ -0,0 +1,103 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef mozilla_dom_SVGImageElement_h +#define mozilla_dom_SVGImageElement_h + +#include "nsImageLoadingContent.h" +#include "nsSVGLength2.h" +#include "nsSVGPathGeometryElement.h" +#include "nsSVGString.h" +#include "SVGAnimatedPreserveAspectRatio.h" + +nsresult NS_NewSVGImageElement(nsIContent **aResult, + already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo); + +typedef nsSVGPathGeometryElement SVGImageElementBase; + +class nsSVGImageFrame; + +namespace mozilla { +namespace dom { +class DOMSVGAnimatedPreserveAspectRatio; + +class SVGImageElement : public SVGImageElementBase, + public nsImageLoadingContent +{ + friend class ::nsSVGImageFrame; + +protected: + explicit SVGImageElement(already_AddRefed<mozilla::dom::NodeInfo>& aNodeInfo); + virtual ~SVGImageElement(); + virtual JSObject* WrapNode(JSContext *aCx, JS::Handle<JSObject*> aGivenProto) override; + friend nsresult (::NS_NewSVGImageElement(nsIContent **aResult, + already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo)); + +public: + // interfaces: + + NS_DECL_ISUPPORTS_INHERITED + + // EventTarget + virtual void AsyncEventRunning(AsyncEventDispatcher* aEvent) override; + + // nsIContent interface + virtual nsresult AfterSetAttr(int32_t aNamespaceID, nsIAtom* aName, + const nsAttrValue* aValue, bool aNotify) override; + virtual nsresult BindToTree(nsIDocument* aDocument, nsIContent* aParent, + nsIContent* aBindingParent, + bool aCompileEventHandlers) override; + virtual void UnbindFromTree(bool aDeep, bool aNullParent) override; + + virtual EventStates IntrinsicState() const override; + + NS_IMETHOD_(bool) IsAttributeMapped(const nsIAtom* name) const override; + + // nsSVGPathGeometryElement methods: + virtual bool GetGeometryBounds(Rect* aBounds, const StrokeOptions& aStrokeOptions, + const Matrix& aToBoundsSpace, + const Matrix* aToNonScalingStrokeSpace = nullptr) override; + virtual already_AddRefed<Path> BuildPath(PathBuilder* aBuilder) override; + + // nsSVGSVGElement methods: + virtual bool HasValidDimensions() const override; + + virtual nsresult Clone(mozilla::dom::NodeInfo *aNodeInfo, nsINode **aResult) const override; + + nsresult CopyInnerTo(mozilla::dom::Element* aDest); + + void MaybeLoadSVGImage(); + + // WebIDL + already_AddRefed<SVGAnimatedLength> X(); + already_AddRefed<SVGAnimatedLength> Y(); + already_AddRefed<SVGAnimatedLength> Width(); + already_AddRefed<SVGAnimatedLength> Height(); + already_AddRefed<DOMSVGAnimatedPreserveAspectRatio> PreserveAspectRatio(); + already_AddRefed<SVGAnimatedString> Href(); + +protected: + nsresult LoadSVGImage(bool aForce, bool aNotify); + + virtual LengthAttributesInfo GetLengthInfo() override; + virtual SVGAnimatedPreserveAspectRatio *GetPreserveAspectRatio() override; + virtual StringAttributesInfo GetStringInfo() override; + + enum { ATTR_X, ATTR_Y, ATTR_WIDTH, ATTR_HEIGHT }; + nsSVGLength2 mLengthAttributes[4]; + static LengthInfo sLengthInfo[4]; + + SVGAnimatedPreserveAspectRatio mPreserveAspectRatio; + + enum { HREF, XLINK_HREF }; + nsSVGString mStringAttributes[2]; + static StringInfo sStringInfo[2]; +}; + +} // namespace dom +} // namespace mozilla + +#endif // mozilla_dom_SVGImageElement_h diff --git a/dom/svg/SVGIntegerPairSMILType.cpp b/dom/svg/SVGIntegerPairSMILType.cpp new file mode 100644 index 0000000000..e13fae3f58 --- /dev/null +++ b/dom/svg/SVGIntegerPairSMILType.cpp @@ -0,0 +1,108 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "SVGIntegerPairSMILType.h" +#include "nsSMILValue.h" +#include "nsMathUtils.h" +#include "nsDebug.h" + +namespace mozilla { + +void +SVGIntegerPairSMILType::Init(nsSMILValue& aValue) const +{ + MOZ_ASSERT(aValue.IsNull(), "Unexpected value type"); + + aValue.mU.mIntPair[0] = 0; + aValue.mU.mIntPair[1] = 0; + aValue.mType = this; +} + +void +SVGIntegerPairSMILType::Destroy(nsSMILValue& aValue) const +{ + NS_PRECONDITION(aValue.mType == this, "Unexpected SMIL value"); + aValue.mU.mIntPair[0] = 0; + aValue.mU.mIntPair[1] = 0; + aValue.mType = nsSMILNullType::Singleton(); +} + +nsresult +SVGIntegerPairSMILType::Assign(nsSMILValue& aDest, const nsSMILValue& aSrc) const +{ + NS_PRECONDITION(aDest.mType == aSrc.mType, "Incompatible SMIL types"); + NS_PRECONDITION(aDest.mType == this, "Unexpected SMIL value"); + + aDest.mU.mIntPair[0] = aSrc.mU.mIntPair[0]; + aDest.mU.mIntPair[1] = aSrc.mU.mIntPair[1]; + return NS_OK; +} + +bool +SVGIntegerPairSMILType::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 aLeft.mU.mIntPair[0] == aRight.mU.mIntPair[0] && + aLeft.mU.mIntPair[1] == aRight.mU.mIntPair[1]; +} + +nsresult +SVGIntegerPairSMILType::Add(nsSMILValue& aDest, const nsSMILValue& aValueToAdd, + uint32_t aCount) const +{ + NS_PRECONDITION(aValueToAdd.mType == aDest.mType, + "Trying to add invalid types"); + NS_PRECONDITION(aValueToAdd.mType == this, "Unexpected source type"); + + aDest.mU.mIntPair[0] += aValueToAdd.mU.mIntPair[0] * aCount; + aDest.mU.mIntPair[1] += aValueToAdd.mU.mIntPair[1] * aCount; + + return NS_OK; +} + +nsresult +SVGIntegerPairSMILType::ComputeDistance(const nsSMILValue& aFrom, + const nsSMILValue& aTo, + double& aDistance) const +{ + NS_PRECONDITION(aFrom.mType == aTo.mType,"Trying to compare different types"); + NS_PRECONDITION(aFrom.mType == this, "Unexpected source type"); + + double delta[2]; + delta[0] = aTo.mU.mIntPair[0] - aFrom.mU.mIntPair[0]; + delta[1] = aTo.mU.mIntPair[1] - aFrom.mU.mIntPair[1]; + + aDistance = NS_hypot(delta[0], delta[1]); + return NS_OK; +} + +nsresult +SVGIntegerPairSMILType::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"); + + double currentVal[2]; + currentVal[0] = aStartVal.mU.mIntPair[0] + + (aEndVal.mU.mIntPair[0] - aStartVal.mU.mIntPair[0]) * aUnitDistance; + currentVal[1] = aStartVal.mU.mIntPair[1] + + (aEndVal.mU.mIntPair[1] - aStartVal.mU.mIntPair[1]) * aUnitDistance; + + aResult.mU.mIntPair[0] = NS_lround(currentVal[0]); + aResult.mU.mIntPair[1] = NS_lround(currentVal[1]); + return NS_OK; +} + +} // namespace mozilla diff --git a/dom/svg/SVGIntegerPairSMILType.h b/dom/svg/SVGIntegerPairSMILType.h new file mode 100644 index 0000000000..52af5d3dc6 --- /dev/null +++ b/dom/svg/SVGIntegerPairSMILType.h @@ -0,0 +1,53 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef MOZILLA_SVGINTEGERPAIRSMILTYPE_H_ +#define MOZILLA_SVGINTEGERPAIRSMILTYPE_H_ + +#include "mozilla/Attributes.h" +#include "nsISMILType.h" + +class nsSMILValue; + +namespace mozilla { + +class SVGIntegerPairSMILType : public nsISMILType +{ +public: + // Singleton for nsSMILValue objects to hold onto. + static SVGIntegerPairSMILType* + Singleton() + { + static SVGIntegerPairSMILType sSingleton; + return &sSingleton; + } + +protected: + // nsISMILType Methods + // ------------------- + virtual void Init(nsSMILValue& aValue) const override; + virtual void Destroy(nsSMILValue&) const override; + virtual nsresult Assign(nsSMILValue& aDest, const nsSMILValue& aSrc) const override; + virtual bool IsEqual(const nsSMILValue& aLeft, + const nsSMILValue& aRight) const override; + virtual nsresult Add(nsSMILValue& aDest, const nsSMILValue& aValueToAdd, + uint32_t aCount) const override; + virtual nsresult ComputeDistance(const nsSMILValue& aFrom, + const nsSMILValue& aTo, + double& aDistance) const override; + virtual nsresult Interpolate(const nsSMILValue& aStartVal, + const nsSMILValue& aEndVal, + double aUnitDistance, + nsSMILValue& aResult) const override; + +private: + // Private constructor: prevent instances beyond my singleton. + constexpr SVGIntegerPairSMILType() {} +}; + +} // namespace mozilla + +#endif // MOZILLA_SVGINTEGERPAIRSMILTYPE_H_ diff --git a/dom/svg/SVGLength.cpp b/dom/svg/SVGLength.cpp new file mode 100644 index 0000000000..988c1e89aa --- /dev/null +++ b/dom/svg/SVGLength.cpp @@ -0,0 +1,237 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "mozilla/ArrayUtils.h" + +#include "SVGLength.h" +#include "nsSVGElement.h" +#include "mozilla/dom/SVGSVGElement.h" +#include "nsTextFormatter.h" +#include "SVGContentUtils.h" +#include <limits> +#include <algorithm> + +namespace mozilla { + +// Declare some helpers defined below: +static void GetUnitString(nsAString& unit, uint16_t unitType); +static uint16_t GetUnitTypeForString(const nsAString& unitStr); + +void +SVGLength::GetValueAsString(nsAString &aValue) const +{ + char16_t buf[24]; + nsTextFormatter::snprintf(buf, sizeof(buf)/sizeof(char16_t), + u"%g", + (double)mValue); + aValue.Assign(buf); + + nsAutoString unitString; + GetUnitString(unitString, mUnit); + aValue.Append(unitString); +} + +bool +SVGLength::SetValueFromString(const nsAString &aString) +{ + RangedPtr<const char16_t> iter = + SVGContentUtils::GetStartRangedPtr(aString); + const RangedPtr<const char16_t> end = + SVGContentUtils::GetEndRangedPtr(aString); + + float value; + + if (!SVGContentUtils::ParseNumber(iter, end, value)) { + return false; + } + + const nsAString& units = Substring(iter.get(), end.get()); + uint16_t unitType = GetUnitTypeForString(units); + if (!IsValidUnitType(unitType)) { + return false; + } + mValue = value; + mUnit = uint8_t(unitType); + return true; +} + +inline static bool +IsAbsoluteUnit(uint8_t 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(uint8_t aUnits, uint8_t aPerUnit) +{ + MOZ_ASSERT(IsAbsoluteUnit(aUnits), "Not a CSS absolute unit"); + MOZ_ASSERT(IsAbsoluteUnit(aPerUnit), "Not a CSS absolute unit"); + + float CSSAbsoluteUnitConversionFactors[5][5] = { // columns: cm, mm, in, pt, pc + // cm per...: + { 1.0f, 0.1f, 2.54f, 0.035277777777777778f, 0.42333333333333333f }, + // mm per...: + { 10.0f, 1.0f, 25.4f, 0.35277777777777778f, 4.2333333333333333f }, + // in per...: + { 0.39370078740157481f, 0.039370078740157481f, 1.0f, 0.013888888888888889f, 0.16666666666666667f }, + // pt per...: + { 28.346456692913386f, 2.8346456692913386f, 72.0f, 1.0f, 12.0f }, + // pc per...: + { 2.3622047244094489f, 0.23622047244094489f, 6.0f, 0.083333333333333333f, 1.0f } + }; + + // First absolute unit is SVG_LENGTHTYPE_CM = 6 + return CSSAbsoluteUnitConversionFactors[aUnits - 6][aPerUnit - 6]; +} + +float +SVGLength::GetValueInSpecifiedUnit(uint8_t aUnit, + const nsSVGElement *aElement, + uint8_t 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 || + !IsFinite(userUnitsPerCurrentUnit), + "bad userUnitsPerCurrentUnit"); + NS_ASSERTION(userUnitsPerNewUnit >= 0 || + !IsFinite(userUnitsPerNewUnit), + "bad userUnitsPerNewUnit"); + + float value = mValue * userUnitsPerCurrentUnit / userUnitsPerNewUnit; + + // userUnitsPerCurrentUnit could be infinity, or userUnitsPerNewUnit could + // be zero. + if (IsFinite(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, uint8_t 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(); + case nsIDOMSVGLength::SVG_LENGTHTYPE_CM: + return INCHES_PER_CM_FLOAT * GetUserUnitsPerInch(); + case nsIDOMSVGLength::SVG_LENGTHTYPE_IN: + return GetUserUnitsPerInch(); + case nsIDOMSVGLength::SVG_LENGTHTYPE_PT: + return (1.0f/POINTS_PER_INCH_FLOAT) * GetUserUnitsPerInch(); + case nsIDOMSVGLength::SVG_LENGTHTYPE_PC: + return (12.0f/POINTS_PER_INCH_FLOAT) * GetUserUnitsPerInch(); + case nsIDOMSVGLength::SVG_LENGTHTYPE_PERCENTAGE: + return GetUserUnitsPerPercent(aElement, aAxis); + case nsIDOMSVGLength::SVG_LENGTHTYPE_EMS: + return SVGContentUtils::GetFontSize(const_cast<nsSVGElement*>(aElement)); + case nsIDOMSVGLength::SVG_LENGTHTYPE_EXS: + return SVGContentUtils::GetFontXHeight(const_cast<nsSVGElement*>(aElement)); + default: + NS_NOTREACHED("Unknown unit type"); + return std::numeric_limits<float>::quiet_NaN(); + } +} + +/* static */ float +SVGLength::GetUserUnitsPerPercent(const nsSVGElement *aElement, uint8_t aAxis) +{ + if (aElement) { + dom::SVGSVGElement *viewportElement = aElement->GetCtx(); + if (viewportElement) { + return std::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[] = +{ + nullptr, /* SVG_LENGTHTYPE_UNKNOWN */ + nullptr, /* 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, uint16_t 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 uint16_t +GetUnitTypeForString(const nsAString& unitStr) +{ + if (unitStr.IsEmpty()) + return nsIDOMSVGLength::SVG_LENGTHTYPE_NUMBER; + + nsIAtom* unitAtom = NS_GetStaticAtom(unitStr); + + if (unitAtom) { + for (uint32_t i = 1 ; i < ArrayLength(unitMap) ; i++) { + if (unitMap[i] && *unitMap[i] == unitAtom) { + return i; + } + } + } + return nsIDOMSVGLength::SVG_LENGTHTYPE_UNKNOWN; +} + +} // namespace mozilla diff --git a/dom/svg/SVGLength.h b/dom/svg/SVGLength.h new file mode 100644 index 0000000000..5cfcf9472b --- /dev/null +++ b/dom/svg/SVGLength.h @@ -0,0 +1,174 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef MOZILLA_SVGLENGTH_H__ +#define MOZILLA_SVGLENGTH_H__ + +#include "nsDebug.h" +#include "nsIDOMSVGLength.h" +#include "nsMathUtils.h" +#include "mozilla/FloatingPoint.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, uint8_t 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; + } + + bool operator==(const SVGLength &rhs) const { + return mValue == rhs.mValue && mUnit == rhs.mUnit; + } + + void GetValueAsString(nsAString& aValue) const; + + /** + * This method returns true, unless there was a parse failure, in which + * case it returns false (and the length is left unchanged). + */ + bool 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; + } + + uint8_t GetUnit() const { + return mUnit; + } + + void SetValueInCurrentUnits(float aValue) { + mValue = aValue; + NS_ASSERTION(IsValid(), "Set invalid SVGLength"); + } + + void SetValueAndUnit(float aValue, uint8_t 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, uint8_t aAxis) const { + return mValue * GetUserUnitsPerUnit(aElement, aAxis); + } + + /** + * 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(uint8_t aUnit, + const nsSVGElement *aElement, + uint8_t aAxis) const; + + bool IsPercentage() const { + return mUnit == nsIDOMSVGLength::SVG_LENGTHTYPE_PERCENTAGE; + } + + static bool IsValidUnitType(uint16_t unit) { + return unit > nsIDOMSVGLength::SVG_LENGTHTYPE_UNKNOWN && + unit <= nsIDOMSVGLength::SVG_LENGTHTYPE_PC; + } + + /** + * 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, uint8_t aAxis) const; + +private: + +#ifdef DEBUG + bool IsValid() const { + return IsFinite(mValue) && IsValidUnitType(mUnit); + } +#endif + + /** + * The conversion factor between user units (CSS px) and CSS inches is + * constant: 96 px per inch. + */ + static float GetUserUnitsPerInch() + { + return 96.0; + } + + /** + * 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, uint8_t aAxis); + + float mValue; + uint8_t mUnit; +}; + +} // namespace mozilla + +#endif // MOZILLA_SVGLENGTH_H__ diff --git a/dom/svg/SVGLengthList.cpp b/dom/svg/SVGLengthList.cpp new file mode 100644 index 0000000000..0c5b7752e5 --- /dev/null +++ b/dom/svg/SVGLengthList.cpp @@ -0,0 +1,80 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "SVGLengthList.h" +#include "nsCharSeparatedTokenizer.h" +#include "nsError.h" +#include "nsString.h" +#include "nsSVGElement.h" +#include "SVGAnimatedLengthList.h" +#include "SVGContentUtils.h" +#include "SVGLength.h" + +namespace mozilla { + +nsresult +SVGLengthList::CopyFrom(const SVGLengthList& rhs) +{ + if (!mLengths.Assign(rhs.mLengths, fallible)) { + return NS_ERROR_OUT_OF_MEMORY; + } + return NS_OK; +} + +void +SVGLengthList::GetValueAsString(nsAString& aValue) const +{ + aValue.Truncate(); + uint32_t last = mLengths.Length() - 1; + for (uint32_t 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(' '); + } + } +} + +nsresult +SVGLengthList::SetValueFromString(const nsAString& aValue) +{ + SVGLengthList temp; + + nsCharSeparatedTokenizerTemplate<IsSVGWhitespace> + tokenizer(aValue, ',', nsCharSeparatedTokenizer::SEPARATOR_OPTIONAL); + + while (tokenizer.hasMoreTokens()) { + SVGLength length; + if (!length.SetValueFromString(tokenizer.nextToken())) { + return NS_ERROR_DOM_SYNTAX_ERR; + } + if (!temp.AppendItem(length)) { + return NS_ERROR_OUT_OF_MEMORY; + } + } + if (tokenizer.separatorAfterCurrentToken()) { + return NS_ERROR_DOM_SYNTAX_ERR; // trailing comma + } + return CopyFrom(temp); +} + +bool +SVGLengthList::operator==(const SVGLengthList& rhs) const +{ + if (Length() != rhs.Length()) { + return false; + } + for (uint32_t i = 0; i < Length(); ++i) { + if (!(mLengths[i] == rhs.mLengths[i])) { + return false; + } + } + return true; +} + +} // namespace mozilla diff --git a/dom/svg/SVGLengthList.h b/dom/svg/SVGLengthList.h new file mode 100644 index 0000000000..7ca07d1da3 --- /dev/null +++ b/dom/svg/SVGLengthList.h @@ -0,0 +1,351 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef MOZILLA_SVGLENGTHLIST_H__ +#define MOZILLA_SVGLENGTHLIST_H__ + +#include "nsCOMPtr.h" +#include "nsDebug.h" +#include "nsIContent.h" +#include "nsINode.h" +#include "nsIWeakReferenceUtils.h" +#include "nsSVGElement.h" +#include "nsTArray.h" +#include "SVGLength.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; + + bool IsEmpty() const { + return mLengths.IsEmpty(); + } + + uint32_t Length() const { + return mLengths.Length(); + } + + const SVGLength& operator[](uint32_t aIndex) const { + return mLengths[aIndex]; + } + + bool operator==(const SVGLengthList& rhs) const; + + bool SetCapacity(uint32_t size) { + return mLengths.SetCapacity(size, fallible); + } + + 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[](uint32_t aIndex) { + return mLengths[aIndex]; + } + + /** + * This may fail (return false) on OOM if the internal capacity is being + * increased, in which case the list will be left unmodified. + */ + bool SetLength(uint32_t aNumberOfItems) { + return mLengths.SetLength(aNumberOfItems, fallible); + } + +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(); + } + + bool InsertItem(uint32_t aIndex, const SVGLength &aLength) { + if (aIndex >= mLengths.Length()) aIndex = mLengths.Length(); + return !!mLengths.InsertElementAt(aIndex, aLength, fallible); + } + + void ReplaceItem(uint32_t aIndex, const SVGLength &aLength) { + MOZ_ASSERT(aIndex < mLengths.Length(), + "DOM wrapper caller should have raised INDEX_SIZE_ERR"); + mLengths[aIndex] = aLength; + } + + void RemoveItem(uint32_t aIndex) { + MOZ_ASSERT(aIndex < mLengths.Length(), + "DOM wrapper caller should have raised INDEX_SIZE_ERR"); + mLengths.RemoveElementAt(aIndex); + } + + bool AppendItem(SVGLength aLength) { + return !!mLengths.AppendElement(aLength, fallible); + } + +protected: + + /* Rationale for using nsTArray<SVGLength> and not nsTArray<SVGLength, 1>: + * + * It might seem like we should use AutoTArray<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 AutoTArray<class E, + * uint32_t N> on the other hand uses sizeof(Header*) + + * (2 * sizeof(uint32_t)) + (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 AutoTArray<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. + */ + FallibleTArray<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(nullptr) + , mAxis(0) + , mCanZeroPadList(false) + {} + + SVGLengthListAndInfo(nsSVGElement *aElement, uint8_t aAxis, bool aCanZeroPadList) + : mElement(do_GetWeakReference(static_cast<nsINode*>(aElement))) + , mAxis(aAxis) + , mCanZeroPadList(aCanZeroPadList) + {} + + void SetInfo(nsSVGElement *aElement, uint8_t aAxis, bool aCanZeroPadList) { + mElement = do_GetWeakReference(static_cast<nsINode*>(aElement)); + mAxis = aAxis; + mCanZeroPadList = aCanZeroPadList; + } + + nsSVGElement* Element() const { + nsCOMPtr<nsIContent> e = do_QueryReferent(mElement); + return static_cast<nsSVGElement*>(e.get()); + } + + /** + * Returns true if this object is an "identity" value, from the perspective + * of SMIL. In other words, returns true until the initial value set up in + * SVGLengthListSMILType::Init() has been changed with a SetInfo() call. + */ + bool IsIdentity() const { + if (!mElement) { + MOZ_ASSERT(IsEmpty(), "target element propagation failure"); + return true; + } + return false; + } + + uint8_t Axis() const { + MOZ_ASSERT(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 + * effect on rendering (e.g. the attributes 'dx' and 'dy' on <text>), then + * this method will return 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 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 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 + */ + bool CanZeroPadList() const { + //NS_ASSERTION(mElement, "CanZeroPadList() isn't valid"); + return mCanZeroPadList; + } + + // For the SMIL code. See comment in SVGLengthListSMILType::Add(). + void SetCanZeroPadList(bool 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[](uint32_t aIndex) const { + return SVGLengthList::operator[](aIndex); + } + SVGLength& operator[](uint32_t aIndex) { + return SVGLengthList::operator[](aIndex); + } + bool SetLength(uint32_t aNumberOfItems) { + return SVGLengthList::SetLength(aNumberOfItems); + } + +private: + // We must keep a weak 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 + // See also https://bugzilla.mozilla.org/show_bug.cgi?id=653497 + nsWeakPtr mElement; + uint8_t mAxis; + bool 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 MOZ_STACK_CLASS SVGUserUnitList +{ +public: + + SVGUserUnitList() + : mList(nullptr) + {} + + void Init(const SVGLengthList *aList, nsSVGElement *aElement, uint8_t aAxis) { + mList = aList; + mElement = aElement; + mAxis = aAxis; + } + + void Clear() { + mList = nullptr; + } + + bool IsEmpty() const { + return !mList || mList->IsEmpty(); + } + + uint32_t Length() const { + return mList ? mList->Length() : 0; + } + + /// This may return a non-finite value + float operator[](uint32_t aIndex) const { + return (*mList)[aIndex].GetValueInUserUnits(mElement, mAxis); + } + + bool HasPercentageValueAt(uint32_t aIndex) const { + const SVGLength& length = (*mList)[aIndex]; + return length.GetUnit() == nsIDOMSVGLength::SVG_LENGTHTYPE_PERCENTAGE; + } + +private: + const SVGLengthList *mList; + nsSVGElement *mElement; + uint8_t mAxis; +}; + +} // namespace mozilla + +#endif // MOZILLA_SVGLENGTHLIST_H__ diff --git a/dom/svg/SVGLengthListSMILType.cpp b/dom/svg/SVGLengthListSMILType.cpp new file mode 100644 index 0000000000..8664965a25 --- /dev/null +++ b/dom/svg/SVGLengthListSMILType.cpp @@ -0,0 +1,301 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "SVGLengthListSMILType.h" +#include "nsSMILValue.h" +#include "SVGLengthList.h" +#include "nsMathUtils.h" +#include "mozilla/FloatingPoint.h" +#include <math.h> +#include <algorithm> + +namespace mozilla { + +/*static*/ SVGLengthListSMILType SVGLengthListSMILType::sSingleton; + +//---------------------------------------------------------------------- +// nsISMILType implementation + +void +SVGLengthListSMILType::Init(nsSMILValue &aValue) const +{ + MOZ_ASSERT(aValue.IsNull(), "Unexpected value type"); + + SVGLengthListAndInfo* lengthList = new SVGLengthListAndInfo(); + + // See the comment documenting Init() in our header file: + lengthList->SetCanZeroPadList(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 = nullptr; + aValue.mType = nsSMILNullType::Singleton(); +} + +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); +} + +bool +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, + uint32_t 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 (valueToAdd.IsIdentity()) { // Adding identity value - no-op + return NS_OK; + } + + if (dest.IsIdentity()) { // Adding *to* an identity value + if (!dest.SetLength(valueToAdd.Length())) { + return NS_ERROR_OUT_OF_MEMORY; + } + for (uint32_t i = 0; i < dest.Length(); ++i) { + dest[i].SetValueAndUnit(valueToAdd[i].GetValueInCurrentUnits() * aCount, + valueToAdd[i].GetUnit()); + } + dest.SetInfo(valueToAdd.Element(), valueToAdd.Axis(), + valueToAdd.CanZeroPadList()); // propagate target element info! + return NS_OK; + } + MOZ_ASSERT(dest.Element() == valueToAdd.Element(), + "adding values from different elements...?"); + + // Zero-pad our |dest| list, if necessary. + if (dest.Length() < valueToAdd.Length()) { + if (!dest.CanZeroPadList()) { + // SVGContentUtils::ReportToConsole + return NS_ERROR_FAILURE; + } + + MOZ_ASSERT(valueToAdd.CanZeroPadList(), + "values disagree about attribute's zero-paddibility"); + + uint32_t 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 (uint32_t 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 * aCount, + dest[i].GetUnit()); + } + + // propagate target element info! + dest.SetInfo(valueToAdd.Element(), valueToAdd.Axis(), + 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.IsEmpty()) || + (to.CanZeroPadList() && to.IsEmpty()), + "Only \"zero\" nsSMILValues from the SMIL engine should " + "return 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())) { + // SVGContentUtils::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; + + uint32_t 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 (!IsFinite(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.IsEmpty()) || + (end.CanZeroPadList() && end.IsEmpty()), + "Only \"zero\" nsSMILValues from the SMIL engine should " + "return 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())) { + // SVGContentUtils::ReportToConsole + return NS_ERROR_FAILURE; + } + + if (!result.SetLength(std::max(start.Length(), end.Length()))) { + return NS_ERROR_OUT_OF_MEMORY; + } + + uint32_t 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 target element info! + result.SetInfo(end.Element(), end.Axis(), + start.CanZeroPadList() && end.CanZeroPadList()); + + return NS_OK; +} + +} // namespace mozilla diff --git a/dom/svg/SVGLengthListSMILType.h b/dom/svg/SVGLengthListSMILType.h new file mode 100644 index 0000000000..10321b585b --- /dev/null +++ b/dom/svg/SVGLengthListSMILType.h @@ -0,0 +1,101 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef MOZILLA_SVGLENGTHLISTSMILTYPE_H_ +#define MOZILLA_SVGLENGTHLISTSMILTYPE_H_ + +#include "mozilla/Attributes.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 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 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 false, + * then they must set the flag to 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 override; + + virtual void Destroy(nsSMILValue& aValue) const override; + virtual nsresult Assign(nsSMILValue& aDest, const nsSMILValue& aSrc) const override; + virtual bool IsEqual(const nsSMILValue& aLeft, + const nsSMILValue& aRight) const override; + virtual nsresult Add(nsSMILValue& aDest, const nsSMILValue& aValueToAdd, + uint32_t aCount) const override; + virtual nsresult ComputeDistance(const nsSMILValue& aFrom, + const nsSMILValue& aTo, + double& aDistance) const override; + virtual nsresult Interpolate(const nsSMILValue& aStartVal, + const nsSMILValue& aEndVal, + double aUnitDistance, + nsSMILValue& aResult) const override; + +private: + // Private constructor: prevent instances beyond my singleton. + constexpr SVGLengthListSMILType() {} +}; + +} // namespace mozilla + +#endif // MOZILLA_SVGLENGTHLISTSMILTYPE_H_ diff --git a/dom/svg/SVGLineElement.cpp b/dom/svg/SVGLineElement.cpp new file mode 100644 index 0000000000..e4ebd741d2 --- /dev/null +++ b/dom/svg/SVGLineElement.cpp @@ -0,0 +1,257 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "mozilla/dom/SVGLineElement.h" +#include "mozilla/dom/SVGLineElementBinding.h" +#include "mozilla/gfx/2D.h" + +NS_IMPL_NS_NEW_NAMESPACED_SVG_ELEMENT(Line) + +using namespace mozilla::gfx; + +namespace mozilla { +namespace dom { + +JSObject* +SVGLineElement::WrapNode(JSContext *aCx, JS::Handle<JSObject*> aGivenProto) +{ + return SVGLineElementBinding::Wrap(aCx, this, aGivenProto); +} + +nsSVGElement::LengthInfo SVGLineElement::sLengthInfo[4] = +{ + { &nsGkAtoms::x1, 0, nsIDOMSVGLength::SVG_LENGTHTYPE_NUMBER, SVGContentUtils::X }, + { &nsGkAtoms::y1, 0, nsIDOMSVGLength::SVG_LENGTHTYPE_NUMBER, SVGContentUtils::Y }, + { &nsGkAtoms::x2, 0, nsIDOMSVGLength::SVG_LENGTHTYPE_NUMBER, SVGContentUtils::X }, + { &nsGkAtoms::y2, 0, nsIDOMSVGLength::SVG_LENGTHTYPE_NUMBER, SVGContentUtils::Y }, +}; + +//---------------------------------------------------------------------- +// Implementation + +SVGLineElement::SVGLineElement(already_AddRefed<mozilla::dom::NodeInfo>& aNodeInfo) + : SVGLineElementBase(aNodeInfo) +{ +} + +void +SVGLineElement::MaybeAdjustForZeroLength(float aX1, float aY1, + float& aX2, float aY2) +{ + if (aX1 == aX2 && aY1 == aY2) { + SVGContentUtils::AutoStrokeOptions strokeOptions; + SVGContentUtils::GetStrokeOptions(&strokeOptions, this, nullptr, nullptr, + SVGContentUtils::eIgnoreStrokeDashing); + + if (strokeOptions.mLineCap != CapStyle::BUTT) { + float tinyLength = + strokeOptions.mLineWidth / SVG_ZERO_LENGTH_PATH_FIX_FACTOR; + aX2 += tinyLength; + } + } +} + +//---------------------------------------------------------------------- +// nsIDOMNode methods + +NS_IMPL_ELEMENT_CLONE_WITH_INIT(SVGLineElement) + +//---------------------------------------------------------------------- + +already_AddRefed<SVGAnimatedLength> +SVGLineElement::X1() +{ + return mLengthAttributes[ATTR_X1].ToDOMAnimatedLength(this); +} + +already_AddRefed<SVGAnimatedLength> +SVGLineElement::Y1() +{ + return mLengthAttributes[ATTR_Y1].ToDOMAnimatedLength(this); +} + +already_AddRefed<SVGAnimatedLength> +SVGLineElement::X2() +{ + return mLengthAttributes[ATTR_X2].ToDOMAnimatedLength(this); +} + +already_AddRefed<SVGAnimatedLength> +SVGLineElement::Y2() +{ + return mLengthAttributes[ATTR_Y2].ToDOMAnimatedLength(this); +} + +//---------------------------------------------------------------------- +// nsIContent methods + +NS_IMETHODIMP_(bool) +SVGLineElement::IsAttributeMapped(const nsIAtom* name) const +{ + static const MappedAttributeEntry* const map[] = { + sMarkersMap + }; + + return FindAttributeDependence(name, map) || + SVGLineElementBase::IsAttributeMapped(name); +} + +//---------------------------------------------------------------------- +// nsSVGElement methods + +nsSVGElement::LengthAttributesInfo +SVGLineElement::GetLengthInfo() +{ + return LengthAttributesInfo(mLengthAttributes, sLengthInfo, + ArrayLength(sLengthInfo)); +} + +//---------------------------------------------------------------------- +// nsSVGPathGeometryElement methods + +void +SVGLineElement::GetMarkPoints(nsTArray<nsSVGMark> *aMarks) { + float x1, y1, x2, y2; + + GetAnimatedLengthValues(&x1, &y1, &x2, &y2, nullptr); + + float angle = atan2(y2 - y1, x2 - x1); + + aMarks->AppendElement(nsSVGMark(x1, y1, angle, nsSVGMark::eStart)); + aMarks->AppendElement(nsSVGMark(x2, y2, angle, nsSVGMark::eEnd)); +} + +void +SVGLineElement::GetAsSimplePath(SimplePath* aSimplePath) +{ + float x1, y1, x2, y2; + GetAnimatedLengthValues(&x1, &y1, &x2, &y2, nullptr); + + MaybeAdjustForZeroLength(x1, y1, x2, y2); + aSimplePath->SetLine(x1, y1, x2, y2); +} + +already_AddRefed<Path> +SVGLineElement::BuildPath(PathBuilder* aBuilder) +{ + float x1, y1, x2, y2; + GetAnimatedLengthValues(&x1, &y1, &x2, &y2, nullptr); + + MaybeAdjustForZeroLength(x1, y1, x2, y2); + aBuilder->MoveTo(Point(x1, y1)); + aBuilder->LineTo(Point(x2, y2)); + + return aBuilder->Finish(); +} + +bool +SVGLineElement::GetGeometryBounds(Rect* aBounds, + const StrokeOptions& aStrokeOptions, + const Matrix& aToBoundsSpace, + const Matrix* aToNonScalingStrokeSpace) +{ + float x1, y1, x2, y2; + GetAnimatedLengthValues(&x1, &y1, &x2, &y2, nullptr); + + if (aStrokeOptions.mLineWidth <= 0) { + *aBounds = Rect(aToBoundsSpace.TransformPoint(Point(x1, y1)), Size()); + aBounds->ExpandToEnclose(aToBoundsSpace.TransformPoint(Point(x2, y2))); + return true; + } + + // transform from non-scaling-stroke space to the space in which we compute + // bounds + Matrix nonScalingToBounds; + if (aToNonScalingStrokeSpace) { + MOZ_ASSERT(!aToNonScalingStrokeSpace->IsSingular()); + Matrix nonScalingToUser = aToNonScalingStrokeSpace->Inverse(); + nonScalingToBounds = nonScalingToUser * aToBoundsSpace; + } + + if (aStrokeOptions.mLineCap == CapStyle::ROUND) { + if (!aToBoundsSpace.IsRectilinear() || + (aToNonScalingStrokeSpace && + !aToNonScalingStrokeSpace->IsRectilinear())) { + // TODO: handle this case. + return false; + } + Rect bounds(Point(x1, y1), Size()); + bounds.ExpandToEnclose(Point(x2, y2)); + if (aToNonScalingStrokeSpace) { + bounds = aToNonScalingStrokeSpace->TransformBounds(bounds); + bounds.Inflate(aStrokeOptions.mLineWidth / 2.f); + *aBounds = nonScalingToBounds.TransformBounds(bounds); + } else { + bounds.Inflate(aStrokeOptions.mLineWidth / 2.f); + *aBounds = aToBoundsSpace.TransformBounds(bounds); + } + return true; + } + + // Handle butt and square linecap, normal and non-scaling stroke cases + // together: start with endpoints (x1, y1), (x2, y2) in the stroke space, + // compute the four corners of the stroked line, transform the corners to + // bounds space, and compute bounds there. + + if (aToNonScalingStrokeSpace) { + Point nonScalingSpaceP1, nonScalingSpaceP2; + nonScalingSpaceP1 = aToNonScalingStrokeSpace->TransformPoint(Point(x1, y1)); + nonScalingSpaceP2 = aToNonScalingStrokeSpace->TransformPoint(Point(x2, y2)); + x1 = nonScalingSpaceP1.x; + y1 = nonScalingSpaceP1.y; + x2 = nonScalingSpaceP2.x; + y2 = nonScalingSpaceP2.y; + } + + Float length = Float(NS_hypot(x2 - x1, y2 - y1)); + Float xDelta; + Float yDelta; + Point points[4]; + + if (aStrokeOptions.mLineCap == CapStyle::BUTT) { + if (length == 0.f) { + xDelta = yDelta = 0.f; + } else { + Float ratio = aStrokeOptions.mLineWidth / 2.f / length; + xDelta = ratio * (y2 - y1); + yDelta = ratio * (x2 - x1); + } + points[0] = Point(x1 - xDelta, y1 + yDelta); + points[1] = Point(x1 + xDelta, y1 - yDelta); + points[2] = Point(x2 + xDelta, y2 - yDelta); + points[3] = Point(x2 - xDelta, y2 + yDelta); + } else { + MOZ_ASSERT(aStrokeOptions.mLineCap == CapStyle::SQUARE); + if (length == 0.f) { + xDelta = yDelta = aStrokeOptions.mLineWidth / 2.f; + points[0] = Point(x1 - xDelta, y1 + yDelta); + points[1] = Point(x1 - xDelta, y1 - yDelta); + points[2] = Point(x1 + xDelta, y1 - yDelta); + points[3] = Point(x1 + xDelta, y1 + yDelta); + } else { + Float ratio = aStrokeOptions.mLineWidth / 2.f / length; + yDelta = ratio * (x2 - x1); + xDelta = ratio * (y2 - y1); + points[0] = Point(x1 - yDelta - xDelta, y1 - xDelta + yDelta); + points[1] = Point(x1 - yDelta + xDelta, y1 - xDelta - yDelta); + points[2] = Point(x2 + yDelta + xDelta, y2 + xDelta - yDelta); + points[3] = Point(x2 + yDelta - xDelta, y2 + xDelta + yDelta); + } + } + + const Matrix& toBoundsSpace = aToNonScalingStrokeSpace ? + nonScalingToBounds : aToBoundsSpace; + + *aBounds = Rect(toBoundsSpace.TransformPoint(points[0]), Size()); + for (uint32_t i = 1; i < 4; ++i) { + aBounds->ExpandToEnclose(toBoundsSpace.TransformPoint(points[i])); + } + + return true; +} + +} // namespace dom +} // namespace mozilla diff --git a/dom/svg/SVGLineElement.h b/dom/svg/SVGLineElement.h new file mode 100644 index 0000000000..f2bafa4a36 --- /dev/null +++ b/dom/svg/SVGLineElement.h @@ -0,0 +1,66 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef mozilla_dom_SVGLineElement_h +#define mozilla_dom_SVGLineElement_h + +#include "nsSVGPathGeometryElement.h" +#include "nsSVGLength2.h" + +nsresult NS_NewSVGLineElement(nsIContent **aResult, + already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo); + +namespace mozilla { +namespace dom { + +typedef nsSVGPathGeometryElement SVGLineElementBase; + +class SVGLineElement final : public SVGLineElementBase +{ +protected: + explicit SVGLineElement(already_AddRefed<mozilla::dom::NodeInfo>& aNodeInfo); + virtual JSObject* WrapNode(JSContext *cx, JS::Handle<JSObject*> aGivenProto) override; + friend nsresult (::NS_NewSVGLineElement(nsIContent **aResult, + already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo)); + // If the input line has length zero and linecaps aren't butt, adjust |aX2| by + // a tiny amount to a barely-nonzero-length line that all of our draw targets + // will render + void MaybeAdjustForZeroLength(float aX1, float aY1, float& aX2, float aY2); + +public: + // nsIContent interface + NS_IMETHOD_(bool) IsAttributeMapped(const nsIAtom* name) const override; + + // nsSVGPathGeometryElement methods: + virtual bool IsMarkable() override { return true; } + virtual void GetMarkPoints(nsTArray<nsSVGMark> *aMarks) override; + virtual void GetAsSimplePath(SimplePath* aSimplePath) override; + virtual already_AddRefed<Path> BuildPath(PathBuilder* aBuilder) override; + virtual bool GetGeometryBounds(Rect* aBounds, const StrokeOptions& aStrokeOptions, + const Matrix& aToBoundsSpace, + const Matrix* aToNonScalingStrokeSpace = nullptr) override; + + virtual nsresult Clone(mozilla::dom::NodeInfo *aNodeInfo, nsINode **aResult) const override; + + // WebIDL + already_AddRefed<SVGAnimatedLength> X1(); + already_AddRefed<SVGAnimatedLength> Y1(); + already_AddRefed<SVGAnimatedLength> X2(); + already_AddRefed<SVGAnimatedLength> Y2(); + +protected: + + virtual LengthAttributesInfo GetLengthInfo() override; + + enum { ATTR_X1, ATTR_Y1, ATTR_X2, ATTR_Y2 }; + nsSVGLength2 mLengthAttributes[4]; + static LengthInfo sLengthInfo[4]; +}; + +} // namespace dom +} // namespace mozilla + +#endif // mozilla_dom_SVGLineElement_h diff --git a/dom/svg/SVGMPathElement.cpp b/dom/svg/SVGMPathElement.cpp new file mode 100644 index 0000000000..4a137f8fca --- /dev/null +++ b/dom/svg/SVGMPathElement.cpp @@ -0,0 +1,288 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "mozilla/ArrayUtils.h" + +#include "mozilla/dom/SVGMPathElement.h" +#include "nsDebug.h" +#include "mozilla/dom/SVGAnimateMotionElement.h" +#include "mozilla/dom/SVGPathElement.h" +#include "nsContentUtils.h" +#include "mozilla/dom/SVGMPathElementBinding.h" +#include "nsIURI.h" + +NS_IMPL_NS_NEW_NAMESPACED_SVG_ELEMENT(MPath) + +namespace mozilla { +namespace dom { + +JSObject* +SVGMPathElement::WrapNode(JSContext *aCx, JS::Handle<JSObject*> aGivenProto) +{ + return SVGMPathElementBinding::Wrap(aCx, this, aGivenProto); +} + +nsSVGElement::StringInfo SVGMPathElement::sStringInfo[2] = +{ + { &nsGkAtoms::href, kNameSpaceID_None, false }, + { &nsGkAtoms::href, kNameSpaceID_XLink, false } +}; + +// Cycle collection magic -- based on SVGUseElement +NS_IMPL_CYCLE_COLLECTION_CLASS(SVGMPathElement) + +NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(SVGMPathElement, + SVGMPathElementBase) + tmp->UnlinkHrefTarget(false); +NS_IMPL_CYCLE_COLLECTION_UNLINK_END + +NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(SVGMPathElement, + SVGMPathElementBase) + tmp->mHrefTarget.Traverse(&cb); +NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END + +//---------------------------------------------------------------------- +// nsISupports methods + +NS_IMPL_ADDREF_INHERITED(SVGMPathElement,SVGMPathElementBase) +NS_IMPL_RELEASE_INHERITED(SVGMPathElement,SVGMPathElementBase) + +NS_INTERFACE_TABLE_HEAD_CYCLE_COLLECTION_INHERITED(SVGMPathElement) + NS_INTERFACE_TABLE_INHERITED(SVGMPathElement, nsIDOMNode, nsIDOMElement, + nsIDOMSVGElement, + nsIMutationObserver) +NS_INTERFACE_TABLE_TAIL_INHERITING(SVGMPathElementBase) + +// Constructor +SVGMPathElement::SVGMPathElement(already_AddRefed<mozilla::dom::NodeInfo>& aNodeInfo) + : SVGMPathElementBase(aNodeInfo), + mHrefTarget(this) +{ +} + +SVGMPathElement::~SVGMPathElement() +{ + UnlinkHrefTarget(false); +} + +//---------------------------------------------------------------------- +// nsIDOMNode methods + +NS_IMPL_ELEMENT_CLONE_WITH_INIT(SVGMPathElement) + +already_AddRefed<SVGAnimatedString> +SVGMPathElement::Href() +{ + return mStringAttributes[HREF].IsExplicitlySet() + ? mStringAttributes[HREF].ToDOMAnimatedString(this) + : mStringAttributes[XLINK_HREF].ToDOMAnimatedString(this); +} + +//---------------------------------------------------------------------- +// nsIContent methods + +nsresult +SVGMPathElement::BindToTree(nsIDocument* aDocument, + nsIContent* aParent, + nsIContent* aBindingParent, + bool aCompileEventHandlers) +{ + MOZ_ASSERT(!mHrefTarget.get(), + "Shouldn't have href-target yet (or it should've been cleared)"); + nsresult rv = SVGMPathElementBase::BindToTree(aDocument, aParent, + aBindingParent, + aCompileEventHandlers); + NS_ENSURE_SUCCESS(rv,rv); + + if (aDocument) { + const nsAttrValue* hrefAttrValue = + HasAttr(kNameSpaceID_None, nsGkAtoms::href) + ? mAttrsAndChildren.GetAttr(nsGkAtoms::href, kNameSpaceID_None) + : mAttrsAndChildren.GetAttr(nsGkAtoms::href, kNameSpaceID_XLink); + if (hrefAttrValue) { + UpdateHrefTarget(aParent, hrefAttrValue->GetStringValue()); + } + } + + return NS_OK; +} + +void +SVGMPathElement::UnbindFromTree(bool aDeep, bool aNullParent) +{ + UnlinkHrefTarget(true); + SVGMPathElementBase::UnbindFromTree(aDeep, aNullParent); +} + +bool +SVGMPathElement::ParseAttribute(int32_t aNamespaceID, + nsIAtom* aAttribute, + const nsAString& aValue, + nsAttrValue& aResult) +{ + bool returnVal = + SVGMPathElementBase::ParseAttribute(aNamespaceID, aAttribute, + aValue, aResult); + if ((aNamespaceID == kNameSpaceID_XLink || + aNamespaceID == kNameSpaceID_None ) && + aAttribute == nsGkAtoms::href && + IsInUncomposedDoc()) { + // Note: If we fail the IsInDoc call, it's ok -- we'll update the target + // on next BindToTree call. + + // Note: "href" takes priority over xlink:href. So if "xlink:href" is being + // set here, we only let that update our target if "href" is *unset*. + if (aNamespaceID != kNameSpaceID_XLink || + !mStringAttributes[HREF].IsExplicitlySet()) { + UpdateHrefTarget(GetParent(), aValue); + } + } + return returnVal; +} + +nsresult +SVGMPathElement::UnsetAttr(int32_t aNamespaceID, + nsIAtom* aAttribute, bool aNotify) +{ + nsresult rv = SVGMPathElementBase::UnsetAttr(aNamespaceID, aAttribute, + aNotify); + NS_ENSURE_SUCCESS(rv, rv); + + if (aAttribute == nsGkAtoms::href) { + if (aNamespaceID == kNameSpaceID_None) { + UnlinkHrefTarget(true); + + // After unsetting href, we may still have xlink:href, so we should + // try to add it back. + const nsAttrValue* xlinkHref = + mAttrsAndChildren.GetAttr(nsGkAtoms::href, kNameSpaceID_XLink); + if (xlinkHref) { + UpdateHrefTarget(GetParent(), xlinkHref->GetStringValue()); + } + } else if (!HasAttr(kNameSpaceID_None, nsGkAtoms::href)) { + UnlinkHrefTarget(true); + } // else: we unset xlink:href, but we still have href attribute, so keep + // the target linking to href. + } + + return NS_OK; +} + +//---------------------------------------------------------------------- +// nsSVGElement methods + +nsSVGElement::StringAttributesInfo +SVGMPathElement::GetStringInfo() +{ + return StringAttributesInfo(mStringAttributes, sStringInfo, + ArrayLength(sStringInfo)); +} + +//---------------------------------------------------------------------- +// nsIMutationObserver methods + +void +SVGMPathElement::AttributeChanged(nsIDocument* aDocument, + Element* aElement, + int32_t aNameSpaceID, + nsIAtom* aAttribute, + int32_t aModType, + const nsAttrValue* aOldValue) +{ + if (aNameSpaceID == kNameSpaceID_None) { + if (aAttribute == nsGkAtoms::d) { + NotifyParentOfMpathChange(GetParent()); + } + } +} + +//---------------------------------------------------------------------- +// Public helper methods + +SVGPathElement* +SVGMPathElement::GetReferencedPath() +{ + if (!HasAttr(kNameSpaceID_XLink, nsGkAtoms::href) && + !HasAttr(kNameSpaceID_None, nsGkAtoms::href)) { + MOZ_ASSERT(!mHrefTarget.get(), + "We shouldn't have a href target " + "if we don't have an xlink:href or href attribute"); + return nullptr; + } + + nsIContent* genericTarget = mHrefTarget.get(); + if (genericTarget && genericTarget->IsSVGElement(nsGkAtoms::path)) { + return static_cast<SVGPathElement*>(genericTarget); + } + return nullptr; +} + +//---------------------------------------------------------------------- +// Protected helper methods + +void +SVGMPathElement::UpdateHrefTarget(nsIContent* aParent, + const nsAString& aHrefStr) +{ + nsCOMPtr<nsIURI> targetURI; + nsCOMPtr<nsIURI> baseURI = GetBaseURI(); + nsContentUtils::NewURIWithDocumentCharset(getter_AddRefs(targetURI), + aHrefStr, OwnerDoc(), baseURI); + + // Stop observing old target (if any) + if (mHrefTarget.get()) { + mHrefTarget.get()->RemoveMutationObserver(this); + } + + if (aParent) { + // Pass in |aParent| instead of |this| -- first argument is only used + // for a call to GetComposedDoc(), and |this| might not have a current + // document yet (if our caller is BindToTree). + mHrefTarget.Reset(aParent, targetURI); + } else { + // if we don't have a parent, then there's no animateMotion element + // depending on our target, so there's no point tracking it right now. + mHrefTarget.Unlink(); + } + + // Start observing new target (if any) + if (mHrefTarget.get()) { + mHrefTarget.get()->AddMutationObserver(this); + } + + NotifyParentOfMpathChange(aParent); +} + +void +SVGMPathElement::UnlinkHrefTarget(bool aNotifyParent) +{ + // Stop observing old target (if any) + if (mHrefTarget.get()) { + mHrefTarget.get()->RemoveMutationObserver(this); + } + mHrefTarget.Unlink(); + + if (aNotifyParent) { + NotifyParentOfMpathChange(GetParent()); + } +} + +void +SVGMPathElement::NotifyParentOfMpathChange(nsIContent* aParent) +{ + if (aParent && aParent->IsSVGElement(nsGkAtoms::animateMotion)) { + + SVGAnimateMotionElement* animateMotionParent = + static_cast<SVGAnimateMotionElement*>(aParent); + + animateMotionParent->MpathChanged(); + AnimationNeedsResample(); + } +} + +} // namespace dom +} // namespace mozilla + diff --git a/dom/svg/SVGMPathElement.h b/dom/svg/SVGMPathElement.h new file mode 100644 index 0000000000..fe0894a542 --- /dev/null +++ b/dom/svg/SVGMPathElement.h @@ -0,0 +1,109 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef mozilla_dom_SVGMPathElement_h +#define mozilla_dom_SVGMPathElement_h + +#include "nsSVGElement.h" +#include "nsStubMutationObserver.h" +#include "nsSVGString.h" +#include "nsReferencedElement.h" + +nsresult NS_NewSVGMPathElement(nsIContent **aResult, + already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo); + +typedef nsSVGElement SVGMPathElementBase; + +namespace mozilla { +namespace dom { +class SVGPathElement; + +class SVGMPathElement final : public SVGMPathElementBase, + public nsStubMutationObserver +{ +protected: + friend nsresult (::NS_NewSVGMPathElement(nsIContent **aResult, + already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo)); + explicit SVGMPathElement(already_AddRefed<mozilla::dom::NodeInfo>& aNodeInfo); + ~SVGMPathElement(); + + virtual JSObject* WrapNode(JSContext *aCx, JS::Handle<JSObject*> aGivenProto) override; + +public: + // interfaces: + NS_DECL_ISUPPORTS_INHERITED + + NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(SVGMPathElement, + SVGMPathElementBase) + + NS_DECL_NSIMUTATIONOBSERVER_ATTRIBUTECHANGED + + // nsIContent interface + virtual nsresult Clone(mozilla::dom::NodeInfo *aNodeInfo, nsINode **aResult) const override; + virtual nsresult BindToTree(nsIDocument* aDocument, nsIContent* aParent, + nsIContent* aBindingParent, + bool aCompileEventHandlers) override; + virtual void UnbindFromTree(bool aDeep, bool aNullParent) override; + + virtual nsresult UnsetAttr(int32_t aNamespaceID, nsIAtom* aAttribute, + bool aNotify) override; + // Element specializations + virtual bool ParseAttribute(int32_t aNamespaceID, + nsIAtom* aAttribute, + const nsAString& aValue, + nsAttrValue& aResult) override; + + // Public helper method: If our xlink:href attribute links to a <path> + // element, this method returns a pointer to that element. Otherwise, + // this returns nullptr. + SVGPathElement* GetReferencedPath(); + + // WebIDL + already_AddRefed<SVGAnimatedString> Href(); + +protected: + class PathReference : public nsReferencedElement { + public: + explicit PathReference(SVGMPathElement* aMpathElement) : + mMpathElement(aMpathElement) {} + protected: + // We need to be notified when target changes, in order to request a sample + // (which will clear animation effects that used the old target-path + // and recompute the animation effects using the new target-path). + virtual void ElementChanged(Element* aFrom, Element* aTo) override { + nsReferencedElement::ElementChanged(aFrom, aTo); + if (aFrom) { + aFrom->RemoveMutationObserver(mMpathElement); + } + if (aTo) { + aTo->AddMutationObserver(mMpathElement); + } + mMpathElement->NotifyParentOfMpathChange(mMpathElement->GetParent()); + } + + // We need to override IsPersistent to get persistent tracking (beyond the + // first time the target changes) + virtual bool IsPersistent() override { return true; } + private: + SVGMPathElement* const mMpathElement; + }; + + virtual StringAttributesInfo GetStringInfo() override; + + void UpdateHrefTarget(nsIContent* aParent, const nsAString& aHrefStr); + void UnlinkHrefTarget(bool aNotifyParent); + void NotifyParentOfMpathChange(nsIContent* aParent); + + enum { HREF, XLINK_HREF }; + nsSVGString mStringAttributes[2]; + static StringInfo sStringInfo[2]; + PathReference mHrefTarget; +}; + +} // namespace dom +} // namespace mozilla + +#endif // mozilla_dom_SVGMPathElement_h diff --git a/dom/svg/SVGMarkerElement.cpp b/dom/svg/SVGMarkerElement.cpp new file mode 100644 index 0000000000..b1f2071969 --- /dev/null +++ b/dom/svg/SVGMarkerElement.cpp @@ -0,0 +1,383 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "mozilla/ArrayUtils.h" + +#include "nsGkAtoms.h" +#include "nsCOMPtr.h" +#include "SVGAnimatedPreserveAspectRatio.h" +#include "nsError.h" +#include "mozilla/dom/SVGAngle.h" +#include "mozilla/dom/SVGMarkerElement.h" +#include "mozilla/dom/SVGMarkerElementBinding.h" +#include "mozilla/Preferences.h" +#include "mozilla/gfx/Matrix.h" +#include "mozilla/FloatingPoint.h" +#include "SVGContentUtils.h" + +using namespace mozilla::gfx; + +NS_IMPL_NS_NEW_NAMESPACED_SVG_ELEMENT(Marker) + +namespace mozilla { +namespace dom { + +JSObject* +SVGMarkerElement::WrapNode(JSContext *aCx, JS::Handle<JSObject*> aGivenProto) +{ + return SVGMarkerElementBinding::Wrap(aCx, this, aGivenProto); +} + +nsSVGElement::LengthInfo SVGMarkerElement::sLengthInfo[4] = +{ + { &nsGkAtoms::refX, 0, nsIDOMSVGLength::SVG_LENGTHTYPE_NUMBER, SVGContentUtils::X }, + { &nsGkAtoms::refY, 0, nsIDOMSVGLength::SVG_LENGTHTYPE_NUMBER, SVGContentUtils::Y }, + { &nsGkAtoms::markerWidth, 3, nsIDOMSVGLength::SVG_LENGTHTYPE_NUMBER, SVGContentUtils::X }, + { &nsGkAtoms::markerHeight, 3, nsIDOMSVGLength::SVG_LENGTHTYPE_NUMBER, SVGContentUtils::Y }, +}; + +nsSVGEnumMapping SVGMarkerElement::sUnitsMap[] = { + {&nsGkAtoms::strokeWidth, SVG_MARKERUNITS_STROKEWIDTH}, + {&nsGkAtoms::userSpaceOnUse, SVG_MARKERUNITS_USERSPACEONUSE}, + {nullptr, 0} +}; + +nsSVGElement::EnumInfo SVGMarkerElement::sEnumInfo[1] = +{ + { &nsGkAtoms::markerUnits, + sUnitsMap, + SVG_MARKERUNITS_STROKEWIDTH + } +}; + +nsSVGElement::AngleInfo SVGMarkerElement::sAngleInfo[1] = +{ + { &nsGkAtoms::orient, 0, SVG_ANGLETYPE_UNSPECIFIED } +}; + +//---------------------------------------------------------------------- +// Implementation + +nsresult +nsSVGOrientType::SetBaseValue(uint16_t aValue, + nsSVGElement *aSVGElement) +{ + if (aValue == SVG_MARKER_ORIENT_AUTO_START_REVERSE && + !SVGMarkerElement::MarkerImprovementsPrefEnabled()) { + return NS_ERROR_DOM_SYNTAX_ERR; + } + + if (aValue == SVG_MARKER_ORIENT_AUTO || + aValue == SVG_MARKER_ORIENT_ANGLE || + aValue == SVG_MARKER_ORIENT_AUTO_START_REVERSE) { + SetBaseValue(aValue); + aSVGElement->SetAttr( + kNameSpaceID_None, nsGkAtoms::orient, nullptr, + (aValue == SVG_MARKER_ORIENT_AUTO ? + NS_LITERAL_STRING("auto") : + aValue == SVG_MARKER_ORIENT_ANGLE ? + NS_LITERAL_STRING("0") : + NS_LITERAL_STRING("auto-start-reverse")), + true); + return NS_OK; + } + return NS_ERROR_DOM_SYNTAX_ERR; +} + +already_AddRefed<SVGAnimatedEnumeration> +nsSVGOrientType::ToDOMAnimatedEnum(nsSVGElement *aSVGElement) +{ + RefPtr<SVGAnimatedEnumeration> toReturn = + new DOMAnimatedEnum(this, aSVGElement); + return toReturn.forget(); +} + +SVGMarkerElement::SVGMarkerElement(already_AddRefed<mozilla::dom::NodeInfo>& aNodeInfo) + : SVGMarkerElementBase(aNodeInfo), mCoordCtx(nullptr) +{ +} + +//---------------------------------------------------------------------- +// nsIDOMNode methods + +NS_IMPL_ELEMENT_CLONE_WITH_INIT(SVGMarkerElement) + +//---------------------------------------------------------------------- + +already_AddRefed<SVGAnimatedRect> +SVGMarkerElement::ViewBox() +{ + return mViewBox.ToSVGAnimatedRect(this); +} + +already_AddRefed<DOMSVGAnimatedPreserveAspectRatio> +SVGMarkerElement::PreserveAspectRatio() +{ + return mPreserveAspectRatio.ToDOMAnimatedPreserveAspectRatio(this); +} + +//---------------------------------------------------------------------- + +already_AddRefed<SVGAnimatedLength> +SVGMarkerElement::RefX() +{ + return mLengthAttributes[REFX].ToDOMAnimatedLength(this); +} + +already_AddRefed<SVGAnimatedLength> +SVGMarkerElement::RefY() +{ + return mLengthAttributes[REFY].ToDOMAnimatedLength(this); +} + +already_AddRefed<SVGAnimatedEnumeration> +SVGMarkerElement::MarkerUnits() +{ + return mEnumAttributes[MARKERUNITS].ToDOMAnimatedEnum(this); +} + +already_AddRefed<SVGAnimatedLength> +SVGMarkerElement::MarkerWidth() +{ + return mLengthAttributes[MARKERWIDTH].ToDOMAnimatedLength(this); +} + +already_AddRefed<SVGAnimatedLength> +SVGMarkerElement::MarkerHeight() +{ + return mLengthAttributes[MARKERHEIGHT].ToDOMAnimatedLength(this); +} + +already_AddRefed<SVGAnimatedEnumeration> +SVGMarkerElement::OrientType() +{ + return mOrientType.ToDOMAnimatedEnum(this); +} + +already_AddRefed<SVGAnimatedAngle> +SVGMarkerElement::OrientAngle() +{ + return mAngleAttributes[ORIENT].ToDOMAnimatedAngle(this); +} + +void SVGMarkerElement::SetOrientToAuto() +{ + SetAttr(kNameSpaceID_None, nsGkAtoms::orient, nullptr, + NS_LITERAL_STRING("auto"), true); +} + +void +SVGMarkerElement::SetOrientToAngle(SVGAngle& angle, ErrorResult& rv) +{ + float f = angle.Value(); + if (!IsFinite(f)) { + rv.Throw(NS_ERROR_DOM_SVG_WRONG_TYPE_ERR); + return; + } + mOrientType.SetBaseValue(SVG_MARKER_ORIENT_ANGLE); + mAngleAttributes[ORIENT].SetBaseValue(f, this, true); +} + +//---------------------------------------------------------------------- +// nsIContent methods + +NS_IMETHODIMP_(bool) +SVGMarkerElement::IsAttributeMapped(const nsIAtom* name) const +{ + static const MappedAttributeEntry* const map[] = { + sFEFloodMap, + sFiltersMap, + sFontSpecificationMap, + sGradientStopMap, + sLightingEffectsMap, + sMarkersMap, + sTextContentElementsMap, + sViewportsMap, + sColorMap, + sFillStrokeMap, + sGraphicsMap + }; + + return FindAttributeDependence(name, map) || + SVGMarkerElementBase::IsAttributeMapped(name); +} + +//---------------------------------------------------------------------- +// nsSVGElement methods + +bool +SVGMarkerElement::ParseAttribute(int32_t aNameSpaceID, nsIAtom* aName, + const nsAString& aValue, + nsAttrValue& aResult) +{ + if (aNameSpaceID == kNameSpaceID_None && aName == nsGkAtoms::orient) { + if (aValue.EqualsLiteral("auto")) { + mOrientType.SetBaseValue(SVG_MARKER_ORIENT_AUTO); + aResult.SetTo(aValue); + mAngleAttributes[ORIENT].SetBaseValue(0.f, this, false); + return true; + } + if (aValue.EqualsLiteral("auto-start-reverse") && + MarkerImprovementsPrefEnabled()) { + mOrientType.SetBaseValue(SVG_MARKER_ORIENT_AUTO_START_REVERSE); + aResult.SetTo(aValue); + mAngleAttributes[ORIENT].SetBaseValue(0.f, this, false); + return true; + } + mOrientType.SetBaseValue(SVG_MARKER_ORIENT_ANGLE); + } + return SVGMarkerElementBase::ParseAttribute(aNameSpaceID, aName, + aValue, aResult); +} + +nsresult +SVGMarkerElement::UnsetAttr(int32_t aNamespaceID, nsIAtom* aName, + bool aNotify) +{ + if (aNamespaceID == kNameSpaceID_None) { + if (aName == nsGkAtoms::orient) { + mOrientType.SetBaseValue(SVG_MARKER_ORIENT_ANGLE); + } + } + + return nsSVGElement::UnsetAttr(aNamespaceID, aName, aNotify); +} + +//---------------------------------------------------------------------- +// nsSVGElement methods + +void +SVGMarkerElement::SetParentCoordCtxProvider(SVGSVGElement *aContext) +{ + mCoordCtx = aContext; + mViewBoxToViewportTransform = nullptr; +} + +/* virtual */ bool +SVGMarkerElement::HasValidDimensions() const +{ + return (!mLengthAttributes[MARKERWIDTH].IsExplicitlySet() || + mLengthAttributes[MARKERWIDTH].GetAnimValInSpecifiedUnits() > 0) && + (!mLengthAttributes[MARKERHEIGHT].IsExplicitlySet() || + mLengthAttributes[MARKERHEIGHT].GetAnimValInSpecifiedUnits() > 0); +} + +nsSVGElement::LengthAttributesInfo +SVGMarkerElement::GetLengthInfo() +{ + return LengthAttributesInfo(mLengthAttributes, sLengthInfo, + ArrayLength(sLengthInfo)); +} + +nsSVGElement::AngleAttributesInfo +SVGMarkerElement::GetAngleInfo() +{ + return AngleAttributesInfo(mAngleAttributes, sAngleInfo, + ArrayLength(sAngleInfo)); +} + +nsSVGElement::EnumAttributesInfo +SVGMarkerElement::GetEnumInfo() +{ + return EnumAttributesInfo(mEnumAttributes, sEnumInfo, + ArrayLength(sEnumInfo)); +} + +nsSVGViewBox * +SVGMarkerElement::GetViewBox() +{ + return &mViewBox; +} + +SVGAnimatedPreserveAspectRatio * +SVGMarkerElement::GetPreserveAspectRatio() +{ + return &mPreserveAspectRatio; +} + +//---------------------------------------------------------------------- +// public helpers + +gfx::Matrix +SVGMarkerElement::GetMarkerTransform(float aStrokeWidth, + float aX, float aY, float aAutoAngle, + bool aIsStart) +{ + float scale = mEnumAttributes[MARKERUNITS].GetAnimValue() == + SVG_MARKERUNITS_STROKEWIDTH ? aStrokeWidth : 1.0f; + + float angle; + switch (mOrientType.GetAnimValueInternal()) { + case SVG_MARKER_ORIENT_AUTO: + angle = aAutoAngle; + break; + case SVG_MARKER_ORIENT_AUTO_START_REVERSE: + angle = aAutoAngle + (aIsStart ? M_PI : 0.0f); + break; + default: // SVG_MARKER_ORIENT_ANGLE + angle = mAngleAttributes[ORIENT].GetAnimValue() * M_PI / 180.0f; + break; + } + + return gfx::Matrix(cos(angle) * scale, sin(angle) * scale, + -sin(angle) * scale, cos(angle) * scale, + aX, aY); +} + +nsSVGViewBoxRect +SVGMarkerElement::GetViewBoxRect() +{ + if (mViewBox.HasRect()) { + return mViewBox.GetAnimValue(); + } + return nsSVGViewBoxRect( + 0, 0, + mLengthAttributes[MARKERWIDTH].GetAnimValue(mCoordCtx), + mLengthAttributes[MARKERHEIGHT].GetAnimValue(mCoordCtx)); +} + +gfx::Matrix +SVGMarkerElement::GetViewBoxTransform() +{ + if (!mViewBoxToViewportTransform) { + float viewportWidth = + mLengthAttributes[MARKERWIDTH].GetAnimValue(mCoordCtx); + float viewportHeight = + mLengthAttributes[MARKERHEIGHT].GetAnimValue(mCoordCtx); + + nsSVGViewBoxRect viewbox = GetViewBoxRect(); + + MOZ_ASSERT(viewbox.width > 0.0f && viewbox.height > 0.0f, + "Rendering should be disabled"); + + gfx::Matrix viewBoxTM = + SVGContentUtils::GetViewBoxTransform(viewportWidth, viewportHeight, + viewbox.x, viewbox.y, + viewbox.width, viewbox.height, + mPreserveAspectRatio); + + float refX = mLengthAttributes[REFX].GetAnimValue(mCoordCtx); + float refY = mLengthAttributes[REFY].GetAnimValue(mCoordCtx); + + gfx::Point ref = viewBoxTM.TransformPoint(gfx::Point(refX, refY)); + + Matrix TM = viewBoxTM; + TM.PostTranslate(-ref.x, -ref.y); + + mViewBoxToViewportTransform = new gfx::Matrix(TM); + } + + return *mViewBoxToViewportTransform; +} + +/* static */ bool +SVGMarkerElement::MarkerImprovementsPrefEnabled() +{ + return Preferences::GetBool("svg.marker-improvements.enabled", false); +} + +} // namespace dom +} // namespace mozilla diff --git a/dom/svg/SVGMarkerElement.h b/dom/svg/SVGMarkerElement.h new file mode 100644 index 0000000000..04eabafa4c --- /dev/null +++ b/dom/svg/SVGMarkerElement.h @@ -0,0 +1,190 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef mozilla_dom_SVGMarkerElement_h +#define mozilla_dom_SVGMarkerElement_h + +#include "nsAutoPtr.h" +#include "nsSVGAngle.h" +#include "nsSVGEnum.h" +#include "nsSVGLength2.h" +#include "nsSVGViewBox.h" +#include "SVGAnimatedPreserveAspectRatio.h" +#include "nsSVGElement.h" +#include "mozilla/Attributes.h" +#include "mozilla/dom/SVGAnimatedEnumeration.h" + +class nsSVGMarkerFrame; + +nsresult NS_NewSVGMarkerElement(nsIContent **aResult, + already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo); + +namespace mozilla { +namespace dom { + +// Marker Unit Types +static const unsigned short SVG_MARKERUNITS_UNKNOWN = 0; +static const unsigned short SVG_MARKERUNITS_USERSPACEONUSE = 1; +static const unsigned short SVG_MARKERUNITS_STROKEWIDTH = 2; + +// Marker Orientation Types +static const unsigned short SVG_MARKER_ORIENT_UNKNOWN = 0; +static const unsigned short SVG_MARKER_ORIENT_AUTO = 1; +static const unsigned short SVG_MARKER_ORIENT_ANGLE = 2; +static const unsigned short SVG_MARKER_ORIENT_AUTO_START_REVERSE = 3; + +class nsSVGOrientType +{ +public: + nsSVGOrientType() + : mAnimVal(SVG_MARKER_ORIENT_ANGLE), + mBaseVal(SVG_MARKER_ORIENT_ANGLE) {} + + nsresult SetBaseValue(uint16_t aValue, + nsSVGElement *aSVGElement); + + // XXX FIXME like https://bugzilla.mozilla.org/show_bug.cgi?id=545550 but + // without adding an mIsAnimated member...? + void SetBaseValue(uint16_t aValue) + { mAnimVal = mBaseVal = uint8_t(aValue); } + // no need to notify, since nsSVGAngle does that + void SetAnimValue(uint16_t aValue) + { mAnimVal = uint8_t(aValue); } + + // we want to avoid exposing SVG_MARKER_ORIENT_AUTO_START_REVERSE to + // Web content + uint16_t GetBaseValue() const + { return mAnimVal == SVG_MARKER_ORIENT_AUTO_START_REVERSE ? + SVG_MARKER_ORIENT_UNKNOWN : mBaseVal; } + uint16_t GetAnimValue() const + { return mAnimVal == SVG_MARKER_ORIENT_AUTO_START_REVERSE ? + SVG_MARKER_ORIENT_UNKNOWN : mAnimVal; } + uint16_t GetAnimValueInternal() const + { return mAnimVal; } + + already_AddRefed<SVGAnimatedEnumeration> + ToDOMAnimatedEnum(nsSVGElement* aSVGElement); + +private: + nsSVGEnumValue mAnimVal; + nsSVGEnumValue mBaseVal; + + struct DOMAnimatedEnum final : public SVGAnimatedEnumeration + { + DOMAnimatedEnum(nsSVGOrientType* aVal, + nsSVGElement *aSVGElement) + : SVGAnimatedEnumeration(aSVGElement) + , mVal(aVal) + {} + + nsSVGOrientType *mVal; // kept alive because it belongs to content + + using mozilla::dom::SVGAnimatedEnumeration::SetBaseVal; + virtual uint16_t BaseVal() override + { + return mVal->GetBaseValue(); + } + virtual void SetBaseVal(uint16_t aBaseVal, ErrorResult& aRv) override + { + aRv = mVal->SetBaseValue(aBaseVal, mSVGElement); + } + virtual uint16_t AnimVal() override + { + return mVal->GetAnimValue(); + } + }; +}; + +typedef nsSVGElement SVGMarkerElementBase; + +class SVGMarkerElement : public SVGMarkerElementBase +{ + friend class ::nsSVGMarkerFrame; + +protected: + friend nsresult (::NS_NewSVGMarkerElement(nsIContent **aResult, + already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo)); + explicit SVGMarkerElement(already_AddRefed<mozilla::dom::NodeInfo>& aNodeInfo); + virtual JSObject* WrapNode(JSContext *cx, JS::Handle<JSObject*> aGivenProto) override; + +public: + // nsIContent interface + NS_IMETHOD_(bool) IsAttributeMapped(const nsIAtom* name) const override; + + virtual nsresult UnsetAttr(int32_t aNameSpaceID, nsIAtom* aAttribute, + bool aNotify) override; + + // nsSVGSVGElement methods: + virtual bool HasValidDimensions() const override; + + // public helpers + gfx::Matrix GetMarkerTransform(float aStrokeWidth, + float aX, float aY, float aAutoAngle, + bool aIsStart); + nsSVGViewBoxRect GetViewBoxRect(); + gfx::Matrix GetViewBoxTransform(); + + virtual nsresult Clone(mozilla::dom::NodeInfo *aNodeInfo, nsINode **aResult) const override; + + nsSVGOrientType* GetOrientType() { return &mOrientType; } + + // Returns the value of svg.marker-improvements.enabled. + static bool MarkerImprovementsPrefEnabled(); + + // WebIDL + already_AddRefed<SVGAnimatedRect> ViewBox(); + already_AddRefed<DOMSVGAnimatedPreserveAspectRatio> PreserveAspectRatio(); + already_AddRefed<SVGAnimatedLength> RefX(); + already_AddRefed<SVGAnimatedLength> RefY(); + already_AddRefed<SVGAnimatedEnumeration> MarkerUnits(); + already_AddRefed<SVGAnimatedLength> MarkerWidth(); + already_AddRefed<SVGAnimatedLength> MarkerHeight(); + already_AddRefed<SVGAnimatedEnumeration> OrientType(); + already_AddRefed<SVGAnimatedAngle> OrientAngle(); + void SetOrientToAuto(); + void SetOrientToAngle(SVGAngle& angle, ErrorResult& rv); + +protected: + + virtual bool ParseAttribute(int32_t aNameSpaceID, nsIAtom* aName, + const nsAString& aValue, + nsAttrValue& aResult) override; + + void SetParentCoordCtxProvider(SVGSVGElement *aContext); + + virtual LengthAttributesInfo GetLengthInfo() override; + virtual AngleAttributesInfo GetAngleInfo() override; + virtual EnumAttributesInfo GetEnumInfo() override; + virtual nsSVGViewBox *GetViewBox() override; + virtual SVGAnimatedPreserveAspectRatio *GetPreserveAspectRatio() override; + + enum { REFX, REFY, MARKERWIDTH, MARKERHEIGHT }; + nsSVGLength2 mLengthAttributes[4]; + static LengthInfo sLengthInfo[4]; + + enum { MARKERUNITS }; + nsSVGEnum mEnumAttributes[1]; + static nsSVGEnumMapping sUnitsMap[]; + static EnumInfo sEnumInfo[1]; + + enum { ORIENT }; + nsSVGAngle mAngleAttributes[1]; + static AngleInfo sAngleInfo[1]; + + nsSVGViewBox mViewBox; + SVGAnimatedPreserveAspectRatio mPreserveAspectRatio; + + // derived properties (from 'orient') handled separately + nsSVGOrientType mOrientType; + + SVGSVGElement *mCoordCtx; + nsAutoPtr<gfx::Matrix> mViewBoxToViewportTransform; +}; + +} // namespace dom +} // namespace mozilla + +#endif // mozilla_dom_SVGMarkerElement_h diff --git a/dom/svg/SVGMaskElement.cpp b/dom/svg/SVGMaskElement.cpp new file mode 100644 index 0000000000..c22896d1e6 --- /dev/null +++ b/dom/svg/SVGMaskElement.cpp @@ -0,0 +1,149 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "mozilla/ArrayUtils.h" + +#include "nsCOMPtr.h" +#include "nsGkAtoms.h" +#include "mozilla/dom/SVGMaskElement.h" +#include "mozilla/dom/SVGMaskElementBinding.h" + +NS_IMPL_NS_NEW_NAMESPACED_SVG_ELEMENT(Mask) + +namespace mozilla { +namespace dom { + +JSObject* +SVGMaskElement::WrapNode(JSContext *aCx, JS::Handle<JSObject*> aGivenProto) +{ + return SVGMaskElementBinding::Wrap(aCx, this, aGivenProto); +} + +//--------------------- Masks ------------------------ + +nsSVGElement::LengthInfo SVGMaskElement::sLengthInfo[4] = +{ + { &nsGkAtoms::x, -10, nsIDOMSVGLength::SVG_LENGTHTYPE_PERCENTAGE, SVGContentUtils::X }, + { &nsGkAtoms::y, -10, nsIDOMSVGLength::SVG_LENGTHTYPE_PERCENTAGE, SVGContentUtils::Y }, + { &nsGkAtoms::width, 120, nsIDOMSVGLength::SVG_LENGTHTYPE_PERCENTAGE, SVGContentUtils::X }, + { &nsGkAtoms::height, 120, nsIDOMSVGLength::SVG_LENGTHTYPE_PERCENTAGE, SVGContentUtils::Y }, +}; + +nsSVGElement::EnumInfo SVGMaskElement::sEnumInfo[2] = +{ + { &nsGkAtoms::maskUnits, + sSVGUnitTypesMap, + SVG_UNIT_TYPE_OBJECTBOUNDINGBOX + }, + { &nsGkAtoms::maskContentUnits, + sSVGUnitTypesMap, + SVG_UNIT_TYPE_USERSPACEONUSE + } +}; + +//---------------------------------------------------------------------- +// Implementation + +SVGMaskElement::SVGMaskElement(already_AddRefed<mozilla::dom::NodeInfo>& aNodeInfo) + : SVGMaskElementBase(aNodeInfo) +{ +} + +//---------------------------------------------------------------------- +// nsIDOMNode method + +NS_IMPL_ELEMENT_CLONE_WITH_INIT(SVGMaskElement) + +//---------------------------------------------------------------------- + +already_AddRefed<SVGAnimatedEnumeration> +SVGMaskElement::MaskUnits() +{ + return mEnumAttributes[MASKUNITS].ToDOMAnimatedEnum(this); +} + +already_AddRefed<SVGAnimatedEnumeration> +SVGMaskElement::MaskContentUnits() +{ + return mEnumAttributes[MASKCONTENTUNITS].ToDOMAnimatedEnum(this); +} + +already_AddRefed<SVGAnimatedLength> +SVGMaskElement::X() +{ + return mLengthAttributes[ATTR_X].ToDOMAnimatedLength(this); +} + +already_AddRefed<SVGAnimatedLength> +SVGMaskElement::Y() +{ + return mLengthAttributes[ATTR_Y].ToDOMAnimatedLength(this); +} + +already_AddRefed<SVGAnimatedLength> +SVGMaskElement::Width() +{ + return mLengthAttributes[ATTR_WIDTH].ToDOMAnimatedLength(this); +} + +already_AddRefed<SVGAnimatedLength> +SVGMaskElement::Height() +{ + return mLengthAttributes[ATTR_HEIGHT].ToDOMAnimatedLength(this); +} + +//---------------------------------------------------------------------- +// nsSVGElement methods + +/* virtual */ bool +SVGMaskElement::HasValidDimensions() const +{ + return (!mLengthAttributes[ATTR_WIDTH].IsExplicitlySet() || + mLengthAttributes[ATTR_WIDTH].GetAnimValInSpecifiedUnits() > 0) && + (!mLengthAttributes[ATTR_HEIGHT].IsExplicitlySet() || + mLengthAttributes[ATTR_HEIGHT].GetAnimValInSpecifiedUnits() > 0); +} + +nsSVGElement::LengthAttributesInfo +SVGMaskElement::GetLengthInfo() +{ + return LengthAttributesInfo(mLengthAttributes, sLengthInfo, + ArrayLength(sLengthInfo)); +} + +nsSVGElement::EnumAttributesInfo +SVGMaskElement::GetEnumInfo() +{ + return EnumAttributesInfo(mEnumAttributes, sEnumInfo, + ArrayLength(sEnumInfo)); +} + +//---------------------------------------------------------------------- +// nsIContent methods + +NS_IMETHODIMP_(bool) +SVGMaskElement::IsAttributeMapped(const nsIAtom* name) const +{ + static const MappedAttributeEntry* const map[] = { + sColorMap, + sFEFloodMap, + sFillStrokeMap, + sFiltersMap, + sFontSpecificationMap, + sGradientStopMap, + sGraphicsMap, + sMarkersMap, + sMaskMap, + sTextContentElementsMap, + sViewportsMap + }; + + return FindAttributeDependence(name, map) || + SVGMaskElementBase::IsAttributeMapped(name); +} + +} // namespace dom +} // namespace mozilla diff --git a/dom/svg/SVGMaskElement.h b/dom/svg/SVGMaskElement.h new file mode 100644 index 0000000000..f0eb8869d6 --- /dev/null +++ b/dom/svg/SVGMaskElement.h @@ -0,0 +1,69 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef mozilla_dom_SVGMaskElement_h +#define mozilla_dom_SVGMaskElement_h + +#include "nsSVGEnum.h" +#include "nsSVGLength2.h" +#include "nsSVGElement.h" + +class nsSVGMaskFrame; + +nsresult NS_NewSVGMaskElement(nsIContent **aResult, + already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo); + +namespace mozilla { +namespace dom { + +//--------------------- Masks ------------------------ + +typedef nsSVGElement SVGMaskElementBase; + +class SVGMaskElement final : public SVGMaskElementBase +{ + friend class ::nsSVGMaskFrame; + +protected: + friend nsresult (::NS_NewSVGMaskElement(nsIContent **aResult, + already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo)); + explicit SVGMaskElement(already_AddRefed<mozilla::dom::NodeInfo>& aNodeInfo); + virtual JSObject* WrapNode(JSContext *cx, JS::Handle<JSObject*> aGivenProto) override; + +public: + // nsIContent interface + virtual nsresult Clone(mozilla::dom::NodeInfo *aNodeInfo, nsINode **aResult) const override; + NS_IMETHOD_(bool) IsAttributeMapped(const nsIAtom* aAttribute) const override; + + // nsSVGSVGElement methods: + virtual bool HasValidDimensions() const override; + + // WebIDL + already_AddRefed<SVGAnimatedEnumeration> MaskUnits(); + already_AddRefed<SVGAnimatedEnumeration> MaskContentUnits(); + already_AddRefed<SVGAnimatedLength> X(); + already_AddRefed<SVGAnimatedLength> Y(); + already_AddRefed<SVGAnimatedLength> Width(); + already_AddRefed<SVGAnimatedLength> Height(); + +protected: + + virtual LengthAttributesInfo GetLengthInfo() override; + virtual EnumAttributesInfo GetEnumInfo() override; + + enum { ATTR_X, ATTR_Y, ATTR_WIDTH, ATTR_HEIGHT }; + nsSVGLength2 mLengthAttributes[4]; + static LengthInfo sLengthInfo[4]; + + enum { MASKUNITS, MASKCONTENTUNITS }; + nsSVGEnum mEnumAttributes[2]; + static EnumInfo sEnumInfo[2]; +}; + +} // namespace dom +} // namespace mozilla + +#endif // mozilla_dom_SVGMaskElement_h diff --git a/dom/svg/SVGMatrix.cpp b/dom/svg/SVGMatrix.cpp new file mode 100644 index 0000000000..3fb0de5faa --- /dev/null +++ b/dom/svg/SVGMatrix.cpp @@ -0,0 +1,230 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "mozilla/dom/SVGMatrix.h" +#include "nsError.h" +#include <math.h> +#include "mozilla/dom/SVGMatrixBinding.h" +#include "mozilla/FloatingPoint.h" + +const double radPerDegree = 2.0 * M_PI / 360.0; + +namespace mozilla { +namespace dom { + +NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(SVGMatrix, mTransform) + +NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(SVGMatrix, AddRef) +NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(SVGMatrix, Release) + +SVGTransform* +SVGMatrix::GetParentObject() const +{ + return mTransform; +} + +JSObject* +SVGMatrix::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) +{ + return SVGMatrixBinding::Wrap(aCx, this, aGivenProto); +} + +void +SVGMatrix::SetA(float aA, ErrorResult& rv) +{ + if (IsAnimVal()) { + rv.Throw(NS_ERROR_DOM_NO_MODIFICATION_ALLOWED_ERR); + return; + } + + gfxMatrix mx = GetMatrix(); + mx._11 = aA; + SetMatrix(mx); +} + +void +SVGMatrix::SetB(float aB, ErrorResult& rv) +{ + if (IsAnimVal()) { + rv.Throw(NS_ERROR_DOM_NO_MODIFICATION_ALLOWED_ERR); + return; + } + + gfxMatrix mx = GetMatrix(); + mx._12 = aB; + SetMatrix(mx); +} + +void +SVGMatrix::SetC(float aC, ErrorResult& rv) +{ + if (IsAnimVal()) { + rv.Throw(NS_ERROR_DOM_NO_MODIFICATION_ALLOWED_ERR); + return; + } + + gfxMatrix mx = GetMatrix(); + mx._21 = aC; + SetMatrix(mx); +} + +void +SVGMatrix::SetD(float aD, ErrorResult& rv) +{ + if (IsAnimVal()) { + rv.Throw(NS_ERROR_DOM_NO_MODIFICATION_ALLOWED_ERR); + return; + } + + gfxMatrix mx = GetMatrix(); + mx._22 = aD; + SetMatrix(mx); +} + +void +SVGMatrix::SetE(float aE, ErrorResult& rv) +{ + if (IsAnimVal()) { + rv.Throw(NS_ERROR_DOM_NO_MODIFICATION_ALLOWED_ERR); + return; + } + + gfxMatrix mx = GetMatrix(); + mx._31 = aE; + SetMatrix(mx); +} + +void +SVGMatrix::SetF(float aF, ErrorResult& rv) +{ + if (IsAnimVal()) { + rv.Throw(NS_ERROR_DOM_NO_MODIFICATION_ALLOWED_ERR); + return; + } + + gfxMatrix mx = GetMatrix(); + mx._32 = aF; + SetMatrix(mx); +} + +already_AddRefed<SVGMatrix> +SVGMatrix::Multiply(SVGMatrix& aMatrix) +{ + RefPtr<SVGMatrix> matrix = new SVGMatrix(aMatrix.GetMatrix() * GetMatrix()); + return matrix.forget(); +} + +already_AddRefed<SVGMatrix> +SVGMatrix::Inverse(ErrorResult& rv) +{ + gfxMatrix mat = GetMatrix(); + if (!mat.Invert()) { + rv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR); + return nullptr; + } + RefPtr<SVGMatrix> matrix = new SVGMatrix(mat); + return matrix.forget(); +} + +already_AddRefed<SVGMatrix> +SVGMatrix::Translate(float x, float y) +{ + RefPtr<SVGMatrix> matrix = + new SVGMatrix(gfxMatrix(GetMatrix()).Translate(gfxPoint(x, y))); + return matrix.forget(); +} + +already_AddRefed<SVGMatrix> +SVGMatrix::Scale(float scaleFactor) +{ + return ScaleNonUniform(scaleFactor, scaleFactor); +} + +already_AddRefed<SVGMatrix> +SVGMatrix::ScaleNonUniform(float scaleFactorX, + float scaleFactorY) +{ + RefPtr<SVGMatrix> matrix = + new SVGMatrix(gfxMatrix(GetMatrix()).Scale(scaleFactorX, scaleFactorY)); + return matrix.forget(); +} + +already_AddRefed<SVGMatrix> +SVGMatrix::Rotate(float angle) +{ + RefPtr<SVGMatrix> matrix = + new SVGMatrix(gfxMatrix(GetMatrix()).Rotate(angle*radPerDegree)); + return matrix.forget(); +} + +already_AddRefed<SVGMatrix> +SVGMatrix::RotateFromVector(float x, float y, ErrorResult& rv) +{ + if (x == 0.0 || y == 0.0) { + rv.Throw(NS_ERROR_DOM_INVALID_ACCESS_ERR); + return nullptr; + } + + RefPtr<SVGMatrix> matrix = + new SVGMatrix(gfxMatrix(GetMatrix()).Rotate(atan2(y, x))); + return matrix.forget(); +} + +already_AddRefed<SVGMatrix> +SVGMatrix::FlipX() +{ + const gfxMatrix& mx = GetMatrix(); + RefPtr<SVGMatrix> matrix = + new SVGMatrix(gfxMatrix(-mx._11, -mx._12, mx._21, mx._22, mx._31, mx._32)); + return matrix.forget(); +} + +already_AddRefed<SVGMatrix> +SVGMatrix::FlipY() +{ + const gfxMatrix& mx = GetMatrix(); + RefPtr<SVGMatrix> matrix = + new SVGMatrix(gfxMatrix(mx._11, mx._12, -mx._21, -mx._22, mx._31, mx._32)); + return matrix.forget(); +} + +already_AddRefed<SVGMatrix> +SVGMatrix::SkewX(float angle, ErrorResult& rv) +{ + double ta = tan( angle*radPerDegree ); + if (!IsFinite(ta)) { + rv.Throw(NS_ERROR_DOM_INVALID_ACCESS_ERR); + return nullptr; + } + + const gfxMatrix& mx = GetMatrix(); + gfxMatrix skewMx(mx._11, mx._12, + (float) (mx._21 + mx._11*ta), (float) (mx._22 + mx._12*ta), + mx._31, mx._32); + RefPtr<SVGMatrix> matrix = new SVGMatrix(skewMx); + return matrix.forget(); +} + +already_AddRefed<SVGMatrix> +SVGMatrix::SkewY(float angle, ErrorResult& rv) +{ + double ta = tan( angle*radPerDegree ); + if (!IsFinite(ta)) { + rv.Throw(NS_ERROR_DOM_INVALID_ACCESS_ERR); + return nullptr; + } + + const gfxMatrix& mx = GetMatrix(); + gfxMatrix skewMx((float) (mx._11 + mx._21*ta), (float) (mx._12 + mx._22*ta), + mx._21, mx._22, + mx._31, mx._32); + + RefPtr<SVGMatrix> matrix = new SVGMatrix(skewMx); + return matrix.forget(); +} + +} // namespace dom +} // namespace mozilla diff --git a/dom/svg/SVGMatrix.h b/dom/svg/SVGMatrix.h new file mode 100644 index 0000000000..14646b5fac --- /dev/null +++ b/dom/svg/SVGMatrix.h @@ -0,0 +1,131 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/** + * Notes on transforms in Mozilla and the SVG code. + * + * It's important to note that the matrix convention used in the SVG standard + * is the opposite convention to the one used in the Mozilla code or, more + * specifically, the convention used in Thebes code (code using gfxMatrix). + * Whereas the SVG standard uses the column vector convention, Thebes code uses + * the row vector convention. Thus, whereas in the SVG standard you have + * [M1][M2][M3]|p|, in Thebes you have |p|'[M3]'[M2]'[M1]'. In other words, the + * following are equivalent: + * + * / a1 c1 tx1 \ / a2 c2 tx2 \ / a3 c3 tx3 \ / x \ + * SVG: | b1 d1 ty1 | | b2 d2 ty2 | | b3 d3 ty3 | | y | + * \ 0 0 1 / \ 0 0 1 / \ 0 0 1 / \ 1 / + * + * / a3 b3 0 \ / a2 b2 0 \ / a1 b1 0 \ + * Thebes: [ x y 1 ] | c3 d3 0 | | c2 d2 0 | | c1 d1 0 | + * \ tx3 ty3 1 / \ tx2 ty2 1 / \ tx1 ty1 1 / + * + * Because the Thebes representation of a transform is the transpose of the SVG + * representation, our transform order must be reversed when representing SVG + * transforms using gfxMatrix in the SVG code. Since the SVG implementation + * stores and obtains matrices in SVG order, to do this we must pre-multiply + * gfxMatrix objects that represent SVG transforms instead of post-multiplying + * them as we would for matrices using SVG's column vector convention. + * Pre-multiplying may look wrong if you're only familiar with the SVG + * convention, but in that case hopefully the above explanation clears things + * up. + */ + +#ifndef mozilla_dom_SVGMatrix_h +#define mozilla_dom_SVGMatrix_h + +#include "mozilla/dom/SVGTransform.h" +#include "gfxMatrix.h" +#include "nsCycleCollectionParticipant.h" +#include "nsWrapperCache.h" +#include "mozilla/Attributes.h" + +namespace mozilla { +namespace dom { + +/** + * DOM wrapper for an SVG matrix. + */ +class SVGMatrix final : public nsWrapperCache +{ +public: + NS_INLINE_DECL_CYCLE_COLLECTING_NATIVE_REFCOUNTING(SVGMatrix) + NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_NATIVE_CLASS(SVGMatrix) + + /** + * Ctor for SVGMatrix objects that belong to a SVGTransform. + */ + explicit SVGMatrix(SVGTransform& aTransform) : mTransform(&aTransform) {} + + /** + * Ctors for SVGMatrix objects created independently of a SVGTransform. + */ + // Default ctor for gfxMatrix will produce identity mx + SVGMatrix() {} + + explicit SVGMatrix(const gfxMatrix &aMatrix) : mMatrix(aMatrix) {} + + const gfxMatrix& GetMatrix() const { + return mTransform ? mTransform->Matrixgfx() : mMatrix; + } + + // WebIDL + SVGTransform* GetParentObject() const; + virtual JSObject* WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override; + + float A() const { return static_cast<float>(GetMatrix()._11); } + void SetA(float aA, ErrorResult& rv); + float B() const { return static_cast<float>(GetMatrix()._12); } + void SetB(float aB, ErrorResult& rv); + float C() const { return static_cast<float>(GetMatrix()._21); } + void SetC(float aC, ErrorResult& rv); + float D() const { return static_cast<float>(GetMatrix()._22); } + void SetD(float aD, ErrorResult& rv); + float E() const { return static_cast<float>(GetMatrix()._31); } + void SetE(float aE, ErrorResult& rv); + float F() const { return static_cast<float>(GetMatrix()._32); } + void SetF(float aF, ErrorResult& rv); + already_AddRefed<SVGMatrix> Multiply(SVGMatrix& aMatrix); + already_AddRefed<SVGMatrix> Inverse(ErrorResult& aRv); + already_AddRefed<SVGMatrix> Translate(float x, float y); + already_AddRefed<SVGMatrix> Scale(float scaleFactor); + already_AddRefed<SVGMatrix> ScaleNonUniform(float scaleFactorX, + float scaleFactorY); + already_AddRefed<SVGMatrix> Rotate(float angle); + already_AddRefed<SVGMatrix> RotateFromVector(float x, + float y, + ErrorResult& aRv); + already_AddRefed<SVGMatrix> FlipX(); + already_AddRefed<SVGMatrix> FlipY(); + already_AddRefed<SVGMatrix> SkewX(float angle, ErrorResult& rv); + already_AddRefed<SVGMatrix> SkewY(float angle, ErrorResult& rv); + +private: + ~SVGMatrix() {} + + void SetMatrix(const gfxMatrix& aMatrix) { + if (mTransform) { + mTransform->SetMatrix(aMatrix); + } else { + mMatrix = aMatrix; + } + } + + bool IsAnimVal() const { + return mTransform ? mTransform->IsAnimVal() : false; + } + + RefPtr<SVGTransform> mTransform; + + // Typically we operate on the matrix data accessed via mTransform but for + // matrices that exist independently of an SVGTransform we use mMatrix below. + gfxMatrix mMatrix; +}; + +} // namespace dom +} // namespace mozilla + +#endif // mozilla_dom_SVGMatrix_h diff --git a/dom/svg/SVGMetadataElement.cpp b/dom/svg/SVGMetadataElement.cpp new file mode 100644 index 0000000000..92ee9ddf11 --- /dev/null +++ b/dom/svg/SVGMetadataElement.cpp @@ -0,0 +1,44 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "mozilla/dom/SVGMetadataElement.h" +#include "mozilla/dom/SVGMetadataElementBinding.h" + +NS_IMPL_NS_NEW_NAMESPACED_SVG_ELEMENT(Metadata) + +namespace mozilla { +namespace dom { + +JSObject* +SVGMetadataElement::WrapNode(JSContext *aCx, JS::Handle<JSObject*> aGivenProto) +{ + return SVGMetadataElementBinding::Wrap(aCx, this, aGivenProto); +} + +//---------------------------------------------------------------------- +// Implementation + +SVGMetadataElement::SVGMetadataElement(already_AddRefed<mozilla::dom::NodeInfo>& aNodeInfo) + : SVGMetadataElementBase(aNodeInfo) +{ +} + + +nsresult +SVGMetadataElement::Init() +{ + return NS_OK; +} + + +//---------------------------------------------------------------------- +// nsIDOMNode methods + +NS_IMPL_ELEMENT_CLONE_WITH_INIT(SVGMetadataElement) + +} // namespace dom +} // namespace mozilla + diff --git a/dom/svg/SVGMetadataElement.h b/dom/svg/SVGMetadataElement.h new file mode 100644 index 0000000000..322552790d --- /dev/null +++ b/dom/svg/SVGMetadataElement.h @@ -0,0 +1,38 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef mozilla_dom_SVGMetadataElement_h +#define mozilla_dom_SVGMetadataElement_h + +#include "mozilla/Attributes.h" +#include "nsSVGElement.h" + +nsresult NS_NewSVGMetadataElement(nsIContent **aResult, + already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo); + +typedef nsSVGElement SVGMetadataElementBase; + +namespace mozilla { +namespace dom { + +class SVGMetadataElement final : public SVGMetadataElementBase +{ +protected: + friend nsresult (::NS_NewSVGMetadataElement(nsIContent **aResult, + already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo)); + explicit SVGMetadataElement(already_AddRefed<mozilla::dom::NodeInfo>& aNodeInfo); + + virtual JSObject* WrapNode(JSContext *aCx, JS::Handle<JSObject*> aGivenProto) override; + nsresult Init(); + +public: + virtual nsresult Clone(mozilla::dom::NodeInfo *aNodeInfo, nsINode **aResult) const override; +}; + +} // namespace dom +} // namespace mozilla + +#endif // mozilla_dom_SVGMetadataElement_h diff --git a/dom/svg/SVGMotionSMILAnimationFunction.cpp b/dom/svg/SVGMotionSMILAnimationFunction.cpp new file mode 100644 index 0000000000..972d1b6270 --- /dev/null +++ b/dom/svg/SVGMotionSMILAnimationFunction.cpp @@ -0,0 +1,464 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "SVGMotionSMILAnimationFunction.h" +#include "mozilla/dom/SVGAnimationElement.h" +#include "mozilla/dom/SVGPathElement.h" // for nsSVGPathList +#include "mozilla/dom/SVGMPathElement.h" +#include "mozilla/gfx/2D.h" +#include "nsAttrValue.h" +#include "nsAttrValueInlines.h" +#include "nsSMILParserUtils.h" +#include "nsSVGAngle.h" +#include "nsSVGPathDataParser.h" +#include "SVGMotionSMILType.h" +#include "SVGMotionSMILPathUtils.h" + +using namespace mozilla::dom; +using namespace mozilla::gfx; + +namespace mozilla { + +SVGMotionSMILAnimationFunction::SVGMotionSMILAnimationFunction() + : mRotateType(eRotateType_Explicit), + mRotateAngle(0.0f), + mPathSourceType(ePathSourceType_None), + mIsPathStale(true) // Try to initialize path on first GetValues call +{ +} + +void +SVGMotionSMILAnimationFunction::MarkStaleIfAttributeAffectsPath(nsIAtom* aAttribute) +{ + bool isAffected; + if (aAttribute == nsGkAtoms::path) { + isAffected = (mPathSourceType <= ePathSourceType_PathAttr); + } else if (aAttribute == nsGkAtoms::values) { + isAffected = (mPathSourceType <= ePathSourceType_ValuesAttr); + } else if (aAttribute == nsGkAtoms::from || + aAttribute == nsGkAtoms::to) { + isAffected = (mPathSourceType <= ePathSourceType_ToAttr); + } else if (aAttribute == nsGkAtoms::by) { + isAffected = (mPathSourceType <= ePathSourceType_ByAttr); + } else { + NS_NOTREACHED("Should only call this method for path-describing attrs"); + isAffected = false; + } + + if (isAffected) { + mIsPathStale = true; + mHasChanged = true; + } +} + +bool +SVGMotionSMILAnimationFunction::SetAttr(nsIAtom* aAttribute, + const nsAString& aValue, + nsAttrValue& aResult, + nsresult* aParseResult) +{ + // Handle motion-specific attrs + if (aAttribute == nsGkAtoms::keyPoints) { + nsresult rv = SetKeyPoints(aValue, aResult); + if (aParseResult) { + *aParseResult = rv; + } + } else if (aAttribute == nsGkAtoms::rotate) { + nsresult rv = SetRotate(aValue, aResult); + if (aParseResult) { + *aParseResult = rv; + } + } else if (aAttribute == nsGkAtoms::path || + aAttribute == nsGkAtoms::by || + aAttribute == nsGkAtoms::from || + aAttribute == nsGkAtoms::to || + aAttribute == nsGkAtoms::values) { + aResult.SetTo(aValue); + MarkStaleIfAttributeAffectsPath(aAttribute); + if (aParseResult) { + *aParseResult = NS_OK; + } + } else { + // Defer to superclass method + return nsSMILAnimationFunction::SetAttr(aAttribute, aValue, + aResult, aParseResult); + } + + return true; +} + +bool +SVGMotionSMILAnimationFunction::UnsetAttr(nsIAtom* aAttribute) +{ + if (aAttribute == nsGkAtoms::keyPoints) { + UnsetKeyPoints(); + } else if (aAttribute == nsGkAtoms::rotate) { + UnsetRotate(); + } else if (aAttribute == nsGkAtoms::path || + aAttribute == nsGkAtoms::by || + aAttribute == nsGkAtoms::from || + aAttribute == nsGkAtoms::to || + aAttribute == nsGkAtoms::values) { + MarkStaleIfAttributeAffectsPath(aAttribute); + } else { + // Defer to superclass method + return nsSMILAnimationFunction::UnsetAttr(aAttribute); + } + + return true; +} + +nsSMILAnimationFunction::nsSMILCalcMode +SVGMotionSMILAnimationFunction::GetCalcMode() const +{ + const nsAttrValue* value = GetAttr(nsGkAtoms::calcMode); + if (!value) { + return CALC_PACED; // animateMotion defaults to calcMode="paced" + } + + return nsSMILCalcMode(value->GetEnumValue()); +} + +//---------------------------------------------------------------------- +// Helpers for GetValues + +/* + * Returns the first <mpath> child of the given element + */ + +static SVGMPathElement* +GetFirstMPathChild(nsIContent* aElem) +{ + for (nsIContent* child = aElem->GetFirstChild(); + child; + child = child->GetNextSibling()) { + if (child->IsSVGElement(nsGkAtoms::mpath)) { + return static_cast<SVGMPathElement*>(child); + } + } + + return nullptr; +} + +void +SVGMotionSMILAnimationFunction:: + RebuildPathAndVerticesFromBasicAttrs(const nsIContent* aContextElem) +{ + MOZ_ASSERT(!HasAttr(nsGkAtoms::path), + "Should be using |path| attr if we have it"); + MOZ_ASSERT(!mPath, "regenerating when we aleady have path"); + MOZ_ASSERT(mPathVertices.IsEmpty(), + "regenerating when we already have vertices"); + + if (!aContextElem->IsSVGElement()) { + NS_ERROR("Uh oh, SVG animateMotion element targeting a non-SVG node"); + return; + } + + SVGMotionSMILPathUtils::PathGenerator + pathGenerator(static_cast<const nsSVGElement*>(aContextElem)); + + bool success = false; + if (HasAttr(nsGkAtoms::values)) { + // Generate path based on our values array + mPathSourceType = ePathSourceType_ValuesAttr; + const nsAString& valuesStr = GetAttr(nsGkAtoms::values)->GetStringValue(); + SVGMotionSMILPathUtils::MotionValueParser parser(&pathGenerator, + &mPathVertices); + success = nsSMILParserUtils::ParseValuesGeneric(valuesStr, parser); + } else if (HasAttr(nsGkAtoms::to) || HasAttr(nsGkAtoms::by)) { + // Apply 'from' value (or a dummy 0,0 'from' value) + if (HasAttr(nsGkAtoms::from)) { + const nsAString& fromStr = GetAttr(nsGkAtoms::from)->GetStringValue(); + success = pathGenerator.MoveToAbsolute(fromStr); + mPathVertices.AppendElement(0.0, fallible); + } else { + // Create dummy 'from' value at 0,0, if we're doing by-animation. + // (NOTE: We don't add the dummy 0-point to our list for *to-animation*, + // because the nsSMILAnimationFunction logic for to-animation doesn't + // expect a dummy value. It only expects one value: the final 'to' value.) + pathGenerator.MoveToOrigin(); + if (!HasAttr(nsGkAtoms::to)) { + mPathVertices.AppendElement(0.0, fallible); + } + success = true; + } + + // Apply 'to' or 'by' value + if (success) { + double dist; + if (HasAttr(nsGkAtoms::to)) { + mPathSourceType = ePathSourceType_ToAttr; + const nsAString& toStr = GetAttr(nsGkAtoms::to)->GetStringValue(); + success = pathGenerator.LineToAbsolute(toStr, dist); + } else { // HasAttr(nsGkAtoms::by) + mPathSourceType = ePathSourceType_ByAttr; + const nsAString& byStr = GetAttr(nsGkAtoms::by)->GetStringValue(); + success = pathGenerator.LineToRelative(byStr, dist); + } + if (success) { + mPathVertices.AppendElement(dist, fallible); + } + } + } + if (success) { + mPath = pathGenerator.GetResultingPath(); + } else { + // Parse failure. Leave path as null, and clear path-related member data. + mPathVertices.Clear(); + } +} + +void +SVGMotionSMILAnimationFunction:: + RebuildPathAndVerticesFromMpathElem(SVGMPathElement* aMpathElem) +{ + mPathSourceType = ePathSourceType_Mpath; + + // Use the path that's the target of our chosen <mpath> child. + SVGPathElement* pathElem = aMpathElem->GetReferencedPath(); + if (pathElem) { + const SVGPathData &path = pathElem->GetAnimPathSegList()->GetAnimValue(); + // Path data must contain of at least one path segment (if the path data + // doesn't begin with a valid "M", then it's invalid). + if (path.Length()) { + bool ok = + path.GetDistancesFromOriginToEndsOfVisibleSegments(&mPathVertices); + if (ok && mPathVertices.Length()) { + mPath = pathElem->GetOrBuildPathForMeasuring(); + } + } + } +} + +void +SVGMotionSMILAnimationFunction::RebuildPathAndVerticesFromPathAttr() +{ + const nsAString& pathSpec = GetAttr(nsGkAtoms::path)->GetStringValue(); + mPathSourceType = ePathSourceType_PathAttr; + + // Generate Path from |path| attr + SVGPathData path; + nsSVGPathDataParser pathParser(pathSpec, &path); + + // We ignore any failure returned from Parse() since the SVG spec says to + // accept all segments up to the first invalid token. Instead we must + // explicitly check that the parse produces at least one path segment (if + // the path data doesn't begin with a valid "M", then it's invalid). + pathParser.Parse(); + if (!path.Length()) { + return; + } + + mPath = path.BuildPathForMeasuring(); + bool ok = path.GetDistancesFromOriginToEndsOfVisibleSegments(&mPathVertices); + if (!ok || !mPathVertices.Length()) { + mPath = nullptr; + } +} + +// Helper to regenerate our path representation & its list of vertices +void +SVGMotionSMILAnimationFunction:: + RebuildPathAndVertices(const nsIContent* aTargetElement) +{ + MOZ_ASSERT(mIsPathStale, "rebuilding path when it isn't stale"); + + // Clear stale data + mPath = nullptr; + mPathVertices.Clear(); + mPathSourceType = ePathSourceType_None; + + // Do we have a mpath child? if so, it trumps everything. Otherwise, we look + // through our list of path-defining attributes, in order of priority. + SVGMPathElement* firstMpathChild = GetFirstMPathChild(mAnimationElement); + + if (firstMpathChild) { + RebuildPathAndVerticesFromMpathElem(firstMpathChild); + mValueNeedsReparsingEverySample = false; + } else if (HasAttr(nsGkAtoms::path)) { + RebuildPathAndVerticesFromPathAttr(); + mValueNeedsReparsingEverySample = false; + } else { + // Get path & vertices from basic SMIL attrs: from/by/to/values + + RebuildPathAndVerticesFromBasicAttrs(aTargetElement); + mValueNeedsReparsingEverySample = true; + } + mIsPathStale = false; +} + +bool +SVGMotionSMILAnimationFunction:: + GenerateValuesForPathAndPoints(Path* aPath, + bool aIsKeyPoints, + FallibleTArray<double>& aPointDistances, + nsSMILValueArray& aResult) +{ + MOZ_ASSERT(aResult.IsEmpty(), "outparam is non-empty"); + + // If we're using "keyPoints" as our list of input distances, then we need + // to de-normalize from the [0, 1] scale to the [0, totalPathLen] scale. + double distanceMultiplier = aIsKeyPoints ? aPath->ComputeLength() : 1.0; + const uint32_t numPoints = aPointDistances.Length(); + for (uint32_t i = 0; i < numPoints; ++i) { + double curDist = aPointDistances[i] * distanceMultiplier; + if (!aResult.AppendElement( + SVGMotionSMILType::ConstructSMILValue(aPath, curDist, + mRotateType, mRotateAngle), + fallible)) { + return false; + } + } + return true; +} + +nsresult +SVGMotionSMILAnimationFunction::GetValues(const nsISMILAttr& aSMILAttr, + nsSMILValueArray& aResult) +{ + if (mIsPathStale) { + RebuildPathAndVertices(aSMILAttr.GetTargetNode()); + } + MOZ_ASSERT(!mIsPathStale, "Forgot to clear 'is path stale' state"); + + if (!mPath) { + // This could be due to e.g. a parse error. + MOZ_ASSERT(mPathVertices.IsEmpty(), "have vertices but no path"); + return NS_ERROR_FAILURE; + } + MOZ_ASSERT(!mPathVertices.IsEmpty(), "have a path but no vertices"); + + // Now: Make the actual list of nsSMILValues (using keyPoints, if set) + bool isUsingKeyPoints = !mKeyPoints.IsEmpty(); + bool success = GenerateValuesForPathAndPoints(mPath, isUsingKeyPoints, + isUsingKeyPoints ? + mKeyPoints : mPathVertices, + aResult); + if (!success) { + return NS_ERROR_OUT_OF_MEMORY; + } + + return NS_OK; +} + +void +SVGMotionSMILAnimationFunction:: + CheckValueListDependentAttrs(uint32_t aNumValues) +{ + // Call superclass method. + nsSMILAnimationFunction::CheckValueListDependentAttrs(aNumValues); + + // Added behavior: Do checks specific to keyPoints. + CheckKeyPoints(); +} + +bool +SVGMotionSMILAnimationFunction::IsToAnimation() const +{ + // Rely on inherited method, but not if we have an <mpath> child or a |path| + // attribute, because they'll override any 'to' attr we might have. + // NOTE: We can't rely on mPathSourceType, because it might not have been + // set to a useful value yet (or it might be stale). + return !GetFirstMPathChild(mAnimationElement) && + !HasAttr(nsGkAtoms::path) && + nsSMILAnimationFunction::IsToAnimation(); +} + +void +SVGMotionSMILAnimationFunction::CheckKeyPoints() +{ + if (!HasAttr(nsGkAtoms::keyPoints)) + return; + + // attribute is ignored for calcMode="paced" (even if it's got errors) + if (GetCalcMode() == CALC_PACED) { + SetKeyPointsErrorFlag(false); + } + + if (mKeyPoints.Length() != mKeyTimes.Length()) { + // there must be exactly as many keyPoints as keyTimes + SetKeyPointsErrorFlag(true); + return; + } + + // Nothing else to check -- we can catch all keyPoints errors elsewhere. + // - Formatting & range issues will be caught in SetKeyPoints, and will + // result in an empty mKeyPoints array, which will drop us into the error + // case above. +} + +nsresult +SVGMotionSMILAnimationFunction::SetKeyPoints(const nsAString& aKeyPoints, + nsAttrValue& aResult) +{ + mKeyPoints.Clear(); + aResult.SetTo(aKeyPoints); + + mHasChanged = true; + + if (!nsSMILParserUtils::ParseSemicolonDelimitedProgressList(aKeyPoints, false, + mKeyPoints)) { + mKeyPoints.Clear(); + return NS_ERROR_FAILURE; + } + + return NS_OK; +} + +void +SVGMotionSMILAnimationFunction::UnsetKeyPoints() +{ + mKeyPoints.Clear(); + SetKeyPointsErrorFlag(false); + mHasChanged = true; +} + +nsresult +SVGMotionSMILAnimationFunction::SetRotate(const nsAString& aRotate, + nsAttrValue& aResult) +{ + mHasChanged = true; + + aResult.SetTo(aRotate); + if (aRotate.EqualsLiteral("auto")) { + mRotateType = eRotateType_Auto; + } else if (aRotate.EqualsLiteral("auto-reverse")) { + mRotateType = eRotateType_AutoReverse; + } else { + mRotateType = eRotateType_Explicit; + + // Parse numeric angle string, with the help of a temp nsSVGAngle. + nsSVGAngle svgAngle; + svgAngle.Init(); + nsresult rv = svgAngle.SetBaseValueString(aRotate, nullptr, false); + if (NS_FAILED(rv)) { // Parse error + mRotateAngle = 0.0f; // set default rotate angle + // XXX report to console? + return rv; + } + + mRotateAngle = svgAngle.GetBaseValInSpecifiedUnits(); + + // Convert to radian units, if we're not already in radians. + uint8_t angleUnit = svgAngle.GetBaseValueUnit(); + if (angleUnit != SVG_ANGLETYPE_RAD) { + mRotateAngle *= nsSVGAngle::GetDegreesPerUnit(angleUnit) / + nsSVGAngle::GetDegreesPerUnit(SVG_ANGLETYPE_RAD); + } + } + return NS_OK; +} + +void +SVGMotionSMILAnimationFunction::UnsetRotate() +{ + mRotateAngle = 0.0f; // default value + mRotateType = eRotateType_Explicit; + mHasChanged = true; +} + +} // namespace mozilla diff --git a/dom/svg/SVGMotionSMILAnimationFunction.h b/dom/svg/SVGMotionSMILAnimationFunction.h new file mode 100644 index 0000000000..a9855b3722 --- /dev/null +++ b/dom/svg/SVGMotionSMILAnimationFunction.h @@ -0,0 +1,105 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef MOZILLA_SVGMOTIONSMILANIMATIONFUNCTION_H_ +#define MOZILLA_SVGMOTIONSMILANIMATIONFUNCTION_H_ + +#include "mozilla/gfx/2D.h" +#include "mozilla/RefPtr.h" +#include "nsSMILAnimationFunction.h" +#include "nsTArray.h" +#include "SVGMotionSMILType.h" // for RotateType + +class nsAttrValue; +class nsIAtom; +class nsIContent; +class nsISMILAttr; +class nsSMILValue; + +namespace mozilla { + +namespace dom { +class SVGMPathElement; +} // namespace dom + +//---------------------------------------------------------------------- +// SVGMotionSMILAnimationFunction +// +// Subclass of nsSMILAnimationFunction to support a few extra features offered +// by the <animateMotion> element. +// +class SVGMotionSMILAnimationFunction final : public nsSMILAnimationFunction +{ + typedef mozilla::gfx::Path Path; + +public: + SVGMotionSMILAnimationFunction(); + virtual bool SetAttr(nsIAtom* aAttribute, + const nsAString& aValue, + nsAttrValue& aResult, + nsresult* aParseResult = nullptr) override; + virtual bool UnsetAttr(nsIAtom* aAttribute) override; + + // Method to allow our owner-element to signal us when our <mpath> + // has changed or been added/removed. When that happens, we need to + // mark ourselves as changed so we'll get recomposed, and mark our path data + // as stale so it'll get regenerated (regardless of mPathSourceType, since + // <mpath> trumps all the other sources of path data) + void MpathChanged() { mIsPathStale = mHasChanged = true; } + +protected: + enum PathSourceType { + // NOTE: Ordering matters here. Higher-priority path-descriptors should + // have higher enumerated values + ePathSourceType_None, // uninitialized or not applicable + ePathSourceType_ByAttr, // by or from-by animation + ePathSourceType_ToAttr, // to or from-to animation + ePathSourceType_ValuesAttr, + ePathSourceType_PathAttr, + ePathSourceType_Mpath + }; + + virtual nsSMILCalcMode GetCalcMode() const override; + virtual nsresult GetValues(const nsISMILAttr& aSMILAttr, + nsSMILValueArray& aResult) override; + virtual void CheckValueListDependentAttrs(uint32_t aNumValues) override; + + virtual bool IsToAnimation() const override; + + void CheckKeyPoints(); + nsresult SetKeyPoints(const nsAString& aKeyPoints, nsAttrValue& aResult); + void UnsetKeyPoints(); + nsresult SetRotate(const nsAString& aRotate, nsAttrValue& aResult); + void UnsetRotate(); + + // Helpers for GetValues + void MarkStaleIfAttributeAffectsPath(nsIAtom* aAttribute); + void RebuildPathAndVertices(const nsIContent* aContextElem); + void RebuildPathAndVerticesFromMpathElem(dom::SVGMPathElement* aMpathElem); + void RebuildPathAndVerticesFromPathAttr(); + void RebuildPathAndVerticesFromBasicAttrs(const nsIContent* aContextElem); + bool GenerateValuesForPathAndPoints(Path* aPath, + bool aIsKeyPoints, + FallibleTArray<double>& aPointDistances, + nsSMILValueArray& aResult); + + // Members + // ------- + FallibleTArray<double> mKeyPoints; // parsed from "keyPoints" attribute. + + RotateType mRotateType; // auto, auto-reverse, or explicit. + float mRotateAngle; // the angle value, if explicit. + + PathSourceType mPathSourceType; // source of our Path. + RefPtr<Path> mPath; // representation of motion path. + FallibleTArray<double> mPathVertices; // distances of vertices along path. + + bool mIsPathStale; +}; + +} // namespace mozilla + +#endif // MOZILLA_SVGMOTIONSMILANIMATIONFUNCTION_H_ diff --git a/dom/svg/SVGMotionSMILAttr.cpp b/dom/svg/SVGMotionSMILAttr.cpp new file mode 100644 index 0000000000..278a018be2 --- /dev/null +++ b/dom/svg/SVGMotionSMILAttr.cpp @@ -0,0 +1,56 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/* representation of a dummy attribute targeted by <animateMotion> element */ + +#include "SVGMotionSMILAttr.h" +#include "SVGMotionSMILType.h" +#include "mozilla/dom/SVGAnimationElement.h" +#include "nsSMILValue.h" +#include "nsDebug.h" +#include "nsSVGElement.h" +#include "gfx2DGlue.h" + +namespace mozilla { + +nsresult +SVGMotionSMILAttr::ValueFromString(const nsAString& aStr, + const dom::SVGAnimationElement* aSrcElement, + nsSMILValue& aValue, + bool& aPreventCachingOfSandwich) const +{ + NS_NOTREACHED("Shouldn't using nsISMILAttr::ValueFromString for parsing " + "animateMotion's SMIL values."); + return NS_ERROR_FAILURE; +} + +nsSMILValue +SVGMotionSMILAttr::GetBaseValue() const +{ + return nsSMILValue(&SVGMotionSMILType::sSingleton); +} + +void +SVGMotionSMILAttr::ClearAnimValue() +{ + mSVGElement->SetAnimateMotionTransform(nullptr); +} + +nsresult +SVGMotionSMILAttr::SetAnimValue(const nsSMILValue& aValue) +{ + gfx::Matrix matrix = SVGMotionSMILType::CreateMatrix(aValue); + mSVGElement->SetAnimateMotionTransform(&matrix); + return NS_OK; +} + +const nsIContent* +SVGMotionSMILAttr::GetTargetNode() const +{ + return mSVGElement; +} + +} // namespace mozilla diff --git a/dom/svg/SVGMotionSMILAttr.h b/dom/svg/SVGMotionSMILAttr.h new file mode 100644 index 0000000000..9fcf52dc21 --- /dev/null +++ b/dom/svg/SVGMotionSMILAttr.h @@ -0,0 +1,57 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/* representation of a dummy attribute targeted by <animateMotion> element */ + +#ifndef MOZILLA_SVGMOTIONSMILATTR_H_ +#define MOZILLA_SVGMOTIONSMILATTR_H_ + +#include "mozilla/Attributes.h" +#include "nsISMILAttr.h" + +class nsIContent; +class nsSMILValue; +class nsSVGElement; + +namespace mozilla { + +namespace dom { +class SVGAnimationElement; +} // namespace dom + +/** + * SVGMotionSMILAttr: Implements the nsISMILAttr interface for SMIL animations + * from <animateMotion>. + * + * NOTE: Even though there's technically no "motion" attribute, we behave in + * many ways as if there were, for simplicity. + */ +class SVGMotionSMILAttr : public nsISMILAttr +{ +public: + explicit SVGMotionSMILAttr(nsSVGElement* aSVGElement) + : mSVGElement(aSVGElement) {} + + // nsISMILAttr methods + virtual nsresult ValueFromString(const nsAString& aStr, + const dom::SVGAnimationElement* aSrcElement, + nsSMILValue& aValue, + bool& aPreventCachingOfSandwich) const override; + virtual nsSMILValue GetBaseValue() const override; + virtual nsresult SetAnimValue(const nsSMILValue& aValue) override; + virtual void ClearAnimValue() override; + virtual const nsIContent* GetTargetNode() const override; + +protected: + // Raw pointers are OK here because this SVGMotionSMILAttr is both + // created & destroyed during a SMIL sample-step, during which time the DOM + // isn't modified. + nsSVGElement* mSVGElement; +}; + +} // namespace mozilla + +#endif // MOZILLA_SVGMOTIONSMILATTR_H_ diff --git a/dom/svg/SVGMotionSMILPathUtils.cpp b/dom/svg/SVGMotionSMILPathUtils.cpp new file mode 100644 index 0000000000..e31c6a3862 --- /dev/null +++ b/dom/svg/SVGMotionSMILPathUtils.cpp @@ -0,0 +1,152 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "SVGMotionSMILPathUtils.h" + +#include "nsCharSeparatedTokenizer.h" +#include "nsContentUtils.h" // for NS_ENSURE_FINITE2 +#include "SVGContentUtils.h" +#include "SVGLength.h" + +using namespace mozilla::gfx; + +namespace mozilla { + +//---------------------------------------------------------------------- +// PathGenerator methods + +// For the dummy 'from' value used in pure by-animation & to-animation +void +SVGMotionSMILPathUtils::PathGenerator:: + MoveToOrigin() +{ + MOZ_ASSERT(!mHaveReceivedCommands, + "Not expecting requests for mid-path MoveTo commands"); + mHaveReceivedCommands = true; + mPathBuilder->MoveTo(Point(0, 0)); +} + +// For 'from' and the first entry in 'values'. +bool +SVGMotionSMILPathUtils::PathGenerator:: + MoveToAbsolute(const nsAString& aCoordPairStr) +{ + MOZ_ASSERT(!mHaveReceivedCommands, + "Not expecting requests for mid-path MoveTo commands"); + mHaveReceivedCommands = true; + + float xVal, yVal; + if (!ParseCoordinatePair(aCoordPairStr, xVal, yVal)) { + return false; + } + mPathBuilder->MoveTo(Point(xVal, yVal)); + return true; +} + +// For 'to' and every entry in 'values' except the first. +bool +SVGMotionSMILPathUtils::PathGenerator:: + LineToAbsolute(const nsAString& aCoordPairStr, double& aSegmentDistance) +{ + mHaveReceivedCommands = true; + + float xVal, yVal; + if (!ParseCoordinatePair(aCoordPairStr, xVal, yVal)) { + return false; + } + Point initialPoint = mPathBuilder->CurrentPoint(); + + mPathBuilder->LineTo(Point(xVal, yVal)); + aSegmentDistance = NS_hypot(initialPoint.x - xVal, initialPoint.y -yVal); + return true; +} + +// For 'by'. +bool +SVGMotionSMILPathUtils::PathGenerator:: + LineToRelative(const nsAString& aCoordPairStr, double& aSegmentDistance) +{ + mHaveReceivedCommands = true; + + float xVal, yVal; + if (!ParseCoordinatePair(aCoordPairStr, xVal, yVal)) { + return false; + } + mPathBuilder->LineTo(mPathBuilder->CurrentPoint() + Point(xVal, yVal)); + aSegmentDistance = NS_hypot(xVal, yVal); + return true; +} + +already_AddRefed<Path> +SVGMotionSMILPathUtils::PathGenerator::GetResultingPath() +{ + return mPathBuilder->Finish(); +} + +//---------------------------------------------------------------------- +// Helper / protected methods + +bool +SVGMotionSMILPathUtils::PathGenerator:: + ParseCoordinatePair(const nsAString& aCoordPairStr, + float& aXVal, float& aYVal) +{ + nsCharSeparatedTokenizerTemplate<IsSVGWhitespace> + tokenizer(aCoordPairStr, ',', + nsCharSeparatedTokenizer::SEPARATOR_OPTIONAL); + + SVGLength x, y; + + if (!tokenizer.hasMoreTokens() || + !x.SetValueFromString(tokenizer.nextToken())) { + return false; + } + + if (!tokenizer.hasMoreTokens() || + !y.SetValueFromString(tokenizer.nextToken())) { + return false; + } + + if (tokenizer.separatorAfterCurrentToken() || // Trailing comma. + tokenizer.hasMoreTokens()) { // More text remains + return false; + } + + float xRes = x.GetValueInUserUnits(mSVGElement, SVGContentUtils::X); + float yRes = y.GetValueInUserUnits(mSVGElement, SVGContentUtils::Y); + + NS_ENSURE_FINITE2(xRes, yRes, false); + + aXVal = xRes; + aYVal = yRes; + return true; +} + +//---------------------------------------------------------------------- +// MotionValueParser methods +bool +SVGMotionSMILPathUtils::MotionValueParser:: + Parse(const nsAString& aValueStr) +{ + bool success; + if (!mPathGenerator->HaveReceivedCommands()) { + // Interpret first value in "values" attribute as the path's initial MoveTo + success = mPathGenerator->MoveToAbsolute(aValueStr); + if (success) { + success = !!mPointDistances->AppendElement(0.0, fallible); + } + } else { + double dist; + success = mPathGenerator->LineToAbsolute(aValueStr, dist); + if (success) { + mDistanceSoFar += dist; + success = !!mPointDistances->AppendElement(mDistanceSoFar, fallible); + } + } + return success; +} + +} // namespace mozilla diff --git a/dom/svg/SVGMotionSMILPathUtils.h b/dom/svg/SVGMotionSMILPathUtils.h new file mode 100644 index 0000000000..8616d1dae6 --- /dev/null +++ b/dom/svg/SVGMotionSMILPathUtils.h @@ -0,0 +1,107 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/* Helper class to help with generating anonymous path elements for + <animateMotion> elements to use. */ + +#ifndef MOZILLA_SVGMOTIONSMILPATHUTILS_H_ +#define MOZILLA_SVGMOTIONSMILPATHUTILS_H_ + +#include "mozilla/Attributes.h" +#include "gfxPlatform.h" +#include "mozilla/gfx/2D.h" +#include "mozilla/RefPtr.h" +#include "nsDebug.h" +#include "nsSMILParserUtils.h" +#include "nsTArray.h" + +class nsAString; +class nsSVGElement; + +namespace mozilla { + +class SVGMotionSMILPathUtils +{ + typedef mozilla::gfx::DrawTarget DrawTarget; + typedef mozilla::gfx::Path Path; + typedef mozilla::gfx::PathBuilder PathBuilder; + +public: + // Class to assist in generating a Path, based on + // coordinates in the <animateMotion> from/by/to/values attributes. + class PathGenerator { + public: + explicit PathGenerator(const nsSVGElement* aSVGElement) + : mSVGElement(aSVGElement), + mHaveReceivedCommands(false) + { + RefPtr<DrawTarget> drawTarget = + gfxPlatform::GetPlatform()->ScreenReferenceDrawTarget(); + NS_ASSERTION(gfxPlatform::GetPlatform()-> + SupportsAzureContentForDrawTarget(drawTarget), + "Should support Moz2D content drawing"); + + mPathBuilder = drawTarget->CreatePathBuilder(); + } + + // Methods for adding various path commands to output path. + // Note: aCoordPairStr is expected to be a whitespace and/or + // comma-separated x,y coordinate-pair -- see description of + // "the specified values for from, by, to, and values" at + // http://www.w3.org/TR/SVG11/animate.html#AnimateMotionElement + void MoveToOrigin(); + bool MoveToAbsolute(const nsAString& aCoordPairStr); + bool LineToAbsolute(const nsAString& aCoordPairStr, + double& aSegmentDistance); + bool LineToRelative(const nsAString& aCoordPairStr, + double& aSegmentDistance); + + // Accessor to let clients check if we've received any commands yet. + inline bool HaveReceivedCommands() { return mHaveReceivedCommands; } + // Accessor to get the finalized path + already_AddRefed<Path> GetResultingPath(); + + protected: + // Helper methods + bool ParseCoordinatePair(const nsAString& aStr, + float& aXVal, float& aYVal); + + // Member data + const nsSVGElement* mSVGElement; // context for converting to user units + RefPtr<PathBuilder> mPathBuilder; + bool mHaveReceivedCommands; + }; + + // Class to assist in passing each subcomponent of a |values| attribute to + // a PathGenerator, for generating a corresponding Path. + class MOZ_STACK_CLASS MotionValueParser : + public nsSMILParserUtils::GenericValueParser + { + public: + MotionValueParser(PathGenerator* aPathGenerator, + FallibleTArray<double>* aPointDistances) + : mPathGenerator(aPathGenerator), + mPointDistances(aPointDistances), + mDistanceSoFar(0.0) + { + MOZ_ASSERT(mPointDistances->IsEmpty(), + "expecting point distances array to start empty"); + } + + // nsSMILParserUtils::GenericValueParser interface + virtual bool Parse(const nsAString& aValueStr) override; + + protected: + PathGenerator* mPathGenerator; + FallibleTArray<double>* mPointDistances; + double mDistanceSoFar; + }; + +}; + +} // namespace mozilla + +#endif // MOZILLA_SVGMOTIONSMILPATHUTILS_H_ diff --git a/dom/svg/SVGMotionSMILType.cpp b/dom/svg/SVGMotionSMILType.cpp new file mode 100644 index 0000000000..2e91d17746 --- /dev/null +++ b/dom/svg/SVGMotionSMILType.cpp @@ -0,0 +1,491 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/* implementation of nsISMILType for use by <animateMotion> element */ + +#include "SVGMotionSMILType.h" + +#include "gfx2DGlue.h" +#include "mozilla/gfx/Point.h" +#include "nsSMILValue.h" +#include "nsDebug.h" +#include "nsMathUtils.h" +#include "nsISupportsUtils.h" +#include "nsTArray.h" +#include <math.h> + +using namespace mozilla::gfx; + +namespace mozilla { + +/*static*/ SVGMotionSMILType SVGMotionSMILType::sSingleton; + + +// Helper enum, for distinguishing between types of MotionSegment structs +enum SegmentType { + eSegmentType_Translation, + eSegmentType_PathPoint +}; + +// Helper Structs: containers for params to define our MotionSegment +// (either simple translation or point-on-a-path) +struct TranslationParams { // Simple translation + float mX; + float mY; +}; +struct PathPointParams { // Point along a path + // Refcounted: need to AddRef/Release. This can't be an nsRefPtr because + // this struct is used inside a union so it can't have a default constructor. + Path* MOZ_OWNING_REF mPath; + float mDistToPoint; // Distance from path start to the point on the path that + // we're interested in. +}; + +/** + * Helper Struct: MotionSegment + * + * Instances of this class represent the points that we move between during + * <animateMotion>. Each nsSMILValue will get a nsTArray of these (generally + * with at most 1 entry in the array, except for in SandwichAdd). (This + * matches our behavior in nsSVGTransformSMILType.) + * + * NOTE: In general, MotionSegments are represented as points on a path + * (eSegmentType_PathPoint), so that we can easily interpolate and compute + * distance *along their path*. However, Add() outputs MotionSegments as + * simple translations (eSegmentType_Translation), because adding two points + * from a path (e.g. when accumulating a repeated animation) will generally + * take you to an arbitrary point *off* of the path. + */ +struct MotionSegment +{ + // Default constructor just locks us into being a Translation, and leaves + // other fields uninitialized (since client is presumably about to set them) + MotionSegment() + : mSegmentType(eSegmentType_Translation) + { } + + // Constructor for a translation + MotionSegment(float aX, float aY, float aRotateAngle) + : mRotateType(eRotateType_Explicit), mRotateAngle(aRotateAngle), + mSegmentType(eSegmentType_Translation) + { + mU.mTranslationParams.mX = aX; + mU.mTranslationParams.mY = aY; + } + + // Constructor for a point on a path (NOTE: AddRef's) + MotionSegment(Path* aPath, float aDistToPoint, + RotateType aRotateType, float aRotateAngle) + : mRotateType(aRotateType), mRotateAngle(aRotateAngle), + mSegmentType(eSegmentType_PathPoint) + { + mU.mPathPointParams.mPath = aPath; + mU.mPathPointParams.mDistToPoint = aDistToPoint; + + NS_ADDREF(mU.mPathPointParams.mPath); // Retain a reference to path + } + + // Copy constructor (NOTE: AddRef's if we're eSegmentType_PathPoint) + MotionSegment(const MotionSegment& aOther) + : mRotateType(aOther.mRotateType), mRotateAngle(aOther.mRotateAngle), + mSegmentType(aOther.mSegmentType) + { + if (mSegmentType == eSegmentType_Translation) { + mU.mTranslationParams = aOther.mU.mTranslationParams; + } else { // mSegmentType == eSegmentType_PathPoint + mU.mPathPointParams = aOther.mU.mPathPointParams; + NS_ADDREF(mU.mPathPointParams.mPath); // Retain a reference to path + } + } + + // Destructor (releases any reference we were holding onto) + ~MotionSegment() + { + if (mSegmentType == eSegmentType_PathPoint) { + NS_RELEASE(mU.mPathPointParams.mPath); + } + } + + // Comparison operators + bool operator==(const MotionSegment& aOther) const + { + // Compare basic params + if (mSegmentType != aOther.mSegmentType || + mRotateType != aOther.mRotateType || + (mRotateType == eRotateType_Explicit && // Technically, angle mismatch + mRotateAngle != aOther.mRotateAngle)) { // only matters for Explicit. + return false; + } + + // Compare translation params, if we're a translation. + if (mSegmentType == eSegmentType_Translation) { + return mU.mTranslationParams.mX == aOther.mU.mTranslationParams.mX && + mU.mTranslationParams.mY == aOther.mU.mTranslationParams.mY; + } + + // Else, compare path-point params, if we're a path point. + return (mU.mPathPointParams.mPath == aOther.mU.mPathPointParams.mPath) && + (mU.mPathPointParams.mDistToPoint == + aOther.mU.mPathPointParams.mDistToPoint); + } + + bool operator!=(const MotionSegment& aOther) const + { + return !(*this == aOther); + } + + // Member Data + // ----------- + RotateType mRotateType; // Explicit angle vs. auto vs. auto-reverse. + float mRotateAngle; // Only used if mRotateType == eRotateType_Explicit. + const SegmentType mSegmentType; // This determines how we interpret + // mU. (const for safety/sanity) + + union { // Union to let us hold the params for either segment-type. + TranslationParams mTranslationParams; + PathPointParams mPathPointParams; + } mU; +}; + +typedef FallibleTArray<MotionSegment> MotionSegmentArray; + +// Helper methods to cast nsSMILValue.mU.mPtr to the right pointer-type +static MotionSegmentArray& +ExtractMotionSegmentArray(nsSMILValue& aValue) +{ + return *static_cast<MotionSegmentArray*>(aValue.mU.mPtr); +} + +static const MotionSegmentArray& +ExtractMotionSegmentArray(const nsSMILValue& aValue) +{ + return *static_cast<const MotionSegmentArray*>(aValue.mU.mPtr); +} + +// nsISMILType Methods +// ------------------- + +void +SVGMotionSMILType::Init(nsSMILValue& aValue) const +{ + MOZ_ASSERT(aValue.IsNull(), "Unexpected SMIL type"); + + aValue.mType = this; + aValue.mU.mPtr = new MotionSegmentArray(1); +} + +void +SVGMotionSMILType::Destroy(nsSMILValue& aValue) const +{ + MOZ_ASSERT(aValue.mType == this, "Unexpected SMIL type"); + + MotionSegmentArray* arr = static_cast<MotionSegmentArray*>(aValue.mU.mPtr); + delete arr; + + aValue.mU.mPtr = nullptr; + aValue.mType = nsSMILNullType::Singleton(); +} + +nsresult +SVGMotionSMILType::Assign(nsSMILValue& aDest, const nsSMILValue& aSrc) const +{ + MOZ_ASSERT(aDest.mType == aSrc.mType, "Incompatible SMIL types"); + MOZ_ASSERT(aDest.mType == this, "Unexpected SMIL type"); + + const MotionSegmentArray& srcArr = ExtractMotionSegmentArray(aSrc); + MotionSegmentArray& dstArr = ExtractMotionSegmentArray(aDest); + if (!dstArr.Assign(srcArr, fallible)) { + return NS_ERROR_OUT_OF_MEMORY; + } + + return NS_OK; +} + +bool +SVGMotionSMILType::IsEqual(const nsSMILValue& aLeft, + const nsSMILValue& aRight) const +{ + MOZ_ASSERT(aLeft.mType == aRight.mType, "Incompatible SMIL types"); + MOZ_ASSERT(aLeft.mType == this, "Unexpected SMIL type"); + + const MotionSegmentArray& leftArr = ExtractMotionSegmentArray(aLeft); + const MotionSegmentArray& rightArr = ExtractMotionSegmentArray(aRight); + + // If array-lengths don't match, we're trivially non-equal. + if (leftArr.Length() != rightArr.Length()) { + return false; + } + + // Array-lengths match -- check each array-entry for equality. + uint32_t length = leftArr.Length(); // == rightArr->Length(), if we get here + for (uint32_t i = 0; i < length; ++i) { + if (leftArr[i] != rightArr[i]) { + return false; + } + } + + return true; // If we get here, we found no differences. +} + +// Helper method for Add & CreateMatrix +inline static void +GetAngleAndPointAtDistance(Path* aPath, float aDistance, + RotateType aRotateType, + float& aRotateAngle, // in & out-param. + Point& aPoint) // out-param. +{ + if (aRotateType == eRotateType_Explicit) { + // Leave aRotateAngle as-is. + aPoint = aPath->ComputePointAtLength(aDistance); + } else { + Point tangent; // Unit vector tangent to the point we find. + aPoint = aPath->ComputePointAtLength(aDistance, &tangent); + float tangentAngle = atan2(tangent.y, tangent.x); + if (aRotateType == eRotateType_Auto) { + aRotateAngle = tangentAngle; + } else { + MOZ_ASSERT(aRotateType == eRotateType_AutoReverse); + aRotateAngle = M_PI + tangentAngle; + } + } +} + +nsresult +SVGMotionSMILType::Add(nsSMILValue& aDest, const nsSMILValue& aValueToAdd, + uint32_t aCount) const +{ + MOZ_ASSERT(aDest.mType == aValueToAdd.mType, + "Incompatible SMIL types"); + MOZ_ASSERT(aDest.mType == this, "Unexpected SMIL type"); + + MotionSegmentArray& dstArr = ExtractMotionSegmentArray(aDest); + const MotionSegmentArray& srcArr = ExtractMotionSegmentArray(aValueToAdd); + + // We're doing a simple add here (as opposed to a sandwich add below). We + // only do this when we're accumulating a repeat result. + // NOTE: In other nsISMILTypes, we use this method with a barely-initialized + // |aDest| value to assist with "by" animation. (In this case, + // "barely-initialized" would mean dstArr.Length() would be empty.) However, + // we don't do this for <animateMotion>, because we instead use our "by" + // value to construct an equivalent "path" attribute, and we use *that* for + // our actual animation. + MOZ_ASSERT(srcArr.Length() == 1, "Invalid source segment arr to add"); + MOZ_ASSERT(dstArr.Length() == 1, "Invalid dest segment arr to add to"); + const MotionSegment& srcSeg = srcArr[0]; + const MotionSegment& dstSeg = dstArr[0]; + MOZ_ASSERT(srcSeg.mSegmentType == eSegmentType_PathPoint, + "expecting to be adding points from a motion path"); + MOZ_ASSERT(dstSeg.mSegmentType == eSegmentType_PathPoint, + "expecting to be adding points from a motion path"); + + const PathPointParams& srcParams = srcSeg.mU.mPathPointParams; + const PathPointParams& dstParams = dstSeg.mU.mPathPointParams; + + MOZ_ASSERT(srcSeg.mRotateType == dstSeg.mRotateType && + srcSeg.mRotateAngle == dstSeg.mRotateAngle, + "unexpected angle mismatch"); + MOZ_ASSERT(srcParams.mPath == dstParams.mPath, + "unexpected path mismatch"); + Path* path = srcParams.mPath; + + // Use destination to get our rotate angle. + float rotateAngle = dstSeg.mRotateAngle; + Point dstPt; + GetAngleAndPointAtDistance(path, dstParams.mDistToPoint, dstSeg.mRotateType, + rotateAngle, dstPt); + + Point srcPt = path->ComputePointAtLength(srcParams.mDistToPoint); + + float newX = dstPt.x + srcPt.x * aCount; + float newY = dstPt.y + srcPt.y * aCount; + + // Replace destination's current value -- a point-on-a-path -- with the + // translation that results from our addition. + dstArr.ReplaceElementAt(0, MotionSegment(newX, newY, rotateAngle)); + return NS_OK; +} + +nsresult +SVGMotionSMILType::SandwichAdd(nsSMILValue& aDest, + const nsSMILValue& aValueToAdd) const +{ + MOZ_ASSERT(aDest.mType == aValueToAdd.mType, + "Incompatible SMIL types"); + MOZ_ASSERT(aDest.mType == this, "Unexpected SMIL type"); + MotionSegmentArray& dstArr = ExtractMotionSegmentArray(aDest); + const MotionSegmentArray& srcArr = ExtractMotionSegmentArray(aValueToAdd); + + // We're only expecting to be adding 1 segment on to the list + MOZ_ASSERT(srcArr.Length() == 1, + "Trying to do sandwich add of more than one value"); + + if (!dstArr.AppendElement(srcArr[0], fallible)) { + return NS_ERROR_OUT_OF_MEMORY; + } + + return NS_OK; +} + +nsresult +SVGMotionSMILType::ComputeDistance(const nsSMILValue& aFrom, + const nsSMILValue& aTo, + double& aDistance) const +{ + MOZ_ASSERT(aFrom.mType == aTo.mType, "Incompatible SMIL types"); + MOZ_ASSERT(aFrom.mType == this, "Unexpected SMIL type"); + const MotionSegmentArray& fromArr = ExtractMotionSegmentArray(aFrom); + const MotionSegmentArray& toArr = ExtractMotionSegmentArray(aTo); + + // ComputeDistance is only used for calculating distances between single + // values in a values array. So we should only have one entry in each array. + MOZ_ASSERT(fromArr.Length() == 1, + "Wrong number of elements in from value"); + MOZ_ASSERT(toArr.Length() == 1, + "Wrong number of elements in to value"); + + const MotionSegment& from = fromArr[0]; + const MotionSegment& to = toArr[0]; + + MOZ_ASSERT(from.mSegmentType == to.mSegmentType, + "Mismatched MotionSegment types"); + if (from.mSegmentType == eSegmentType_PathPoint) { + const PathPointParams& fromParams = from.mU.mPathPointParams; + const PathPointParams& toParams = to.mU.mPathPointParams; + MOZ_ASSERT(fromParams.mPath == toParams.mPath, + "Interpolation endpoints should be from same path"); + MOZ_ASSERT(fromParams.mDistToPoint <= toParams.mDistToPoint, + "To value shouldn't be before from value on path"); + aDistance = fabs(toParams.mDistToPoint - fromParams.mDistToPoint); + } else { + const TranslationParams& fromParams = from.mU.mTranslationParams; + const TranslationParams& toParams = to.mU.mTranslationParams; + float dX = toParams.mX - fromParams.mX; + float dY = toParams.mY - fromParams.mY; + aDistance = NS_hypot(dX, dY); + } + + return NS_OK; +} + +// Helper method for Interpolate() +static inline float +InterpolateFloat(const float& aStartFlt, const float& aEndFlt, + const double& aUnitDistance) +{ + return aStartFlt + aUnitDistance * (aEndFlt - aStartFlt); +} + +nsresult +SVGMotionSMILType::Interpolate(const nsSMILValue& aStartVal, + const nsSMILValue& aEndVal, + double aUnitDistance, + nsSMILValue& aResult) const +{ + MOZ_ASSERT(aStartVal.mType == aEndVal.mType, + "Trying to interpolate different types"); + MOZ_ASSERT(aStartVal.mType == this, + "Unexpected types for interpolation"); + MOZ_ASSERT(aResult.mType == this, "Unexpected result type"); + MOZ_ASSERT(aUnitDistance >= 0.0 && aUnitDistance <= 1.0, + "unit distance value out of bounds"); + + const MotionSegmentArray& startArr = ExtractMotionSegmentArray(aStartVal); + const MotionSegmentArray& endArr = ExtractMotionSegmentArray(aEndVal); + MotionSegmentArray& resultArr = ExtractMotionSegmentArray(aResult); + + MOZ_ASSERT(startArr.Length() <= 1, + "Invalid start-point for animateMotion interpolation"); + MOZ_ASSERT(endArr.Length() == 1, + "Invalid end-point for animateMotion interpolation"); + MOZ_ASSERT(resultArr.IsEmpty(), + "Expecting result to be just-initialized w/ empty array"); + + const MotionSegment& endSeg = endArr[0]; + MOZ_ASSERT(endSeg.mSegmentType == eSegmentType_PathPoint, + "Expecting to be interpolating along a path"); + + const PathPointParams& endParams = endSeg.mU.mPathPointParams; + // NOTE: path & angle should match between start & end (since presumably + // start & end came from the same <animateMotion> element), unless start is + // empty. (as it would be for pure 'to' animation) + Path* path = endParams.mPath; + RotateType rotateType = endSeg.mRotateType; + float rotateAngle = endSeg.mRotateAngle; + + float startDist; + if (startArr.IsEmpty()) { + startDist = 0.0f; + } else { + const MotionSegment& startSeg = startArr[0]; + MOZ_ASSERT(startSeg.mSegmentType == eSegmentType_PathPoint, + "Expecting to be interpolating along a path"); + const PathPointParams& startParams = startSeg.mU.mPathPointParams; + MOZ_ASSERT(startSeg.mRotateType == endSeg.mRotateType && + startSeg.mRotateAngle == endSeg.mRotateAngle, + "unexpected angle mismatch"); + MOZ_ASSERT(startParams.mPath == endParams.mPath, + "unexpected path mismatch"); + startDist = startParams.mDistToPoint; + } + + // Get the interpolated distance along our path. + float resultDist = InterpolateFloat(startDist, endParams.mDistToPoint, + aUnitDistance); + + // Construct the intermediate result segment, and put it in our outparam. + // AppendElement has guaranteed success here, since Init() allocates 1 slot. + MOZ_ALWAYS_TRUE(resultArr.AppendElement(MotionSegment(path, resultDist, + rotateType, + rotateAngle), + fallible)); + return NS_OK; +} + +/* static */ gfx::Matrix +SVGMotionSMILType::CreateMatrix(const nsSMILValue& aSMILVal) +{ + const MotionSegmentArray& arr = ExtractMotionSegmentArray(aSMILVal); + + gfx::Matrix matrix; + uint32_t length = arr.Length(); + for (uint32_t i = 0; i < length; i++) { + Point point; // initialized below + float rotateAngle = arr[i].mRotateAngle; // might get updated below + if (arr[i].mSegmentType == eSegmentType_Translation) { + point.x = arr[i].mU.mTranslationParams.mX; + point.y = arr[i].mU.mTranslationParams.mY; + MOZ_ASSERT(arr[i].mRotateType == eRotateType_Explicit, + "'auto'/'auto-reverse' should have been converted to " + "explicit angles when we generated this translation"); + } else { + GetAngleAndPointAtDistance(arr[i].mU.mPathPointParams.mPath, + arr[i].mU.mPathPointParams.mDistToPoint, + arr[i].mRotateType, + rotateAngle, point); + } + matrix.PreTranslate(point.x, point.y); + matrix.PreRotate(rotateAngle); + } + return matrix; +} + +/* static */ nsSMILValue +SVGMotionSMILType::ConstructSMILValue(Path* aPath, + float aDist, + RotateType aRotateType, + float aRotateAngle) +{ + nsSMILValue smilVal(&SVGMotionSMILType::sSingleton); + MotionSegmentArray& arr = ExtractMotionSegmentArray(smilVal); + + // AppendElement has guaranteed success here, since Init() allocates 1 slot. + MOZ_ALWAYS_TRUE(arr.AppendElement(MotionSegment(aPath, aDist, + aRotateType, aRotateAngle), + fallible)); + return smilVal; +} + +} // namespace mozilla diff --git a/dom/svg/SVGMotionSMILType.h b/dom/svg/SVGMotionSMILType.h new file mode 100644 index 0000000000..7529d796d7 --- /dev/null +++ b/dom/svg/SVGMotionSMILType.h @@ -0,0 +1,86 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/* implementation of nsISMILType for use by <animateMotion> element */ + +#ifndef MOZILLA_SVGMOTIONSMILTYPE_H_ +#define MOZILLA_SVGMOTIONSMILTYPE_H_ + +#include "mozilla/gfx/2D.h" +#include "mozilla/Attributes.h" +#include "nsISMILType.h" + +class nsSMILValue; + +namespace mozilla { + +namespace gfx { +class Matrix; +} // namespace gfx + +/** + * MotionRotateType: Enum to indicate the type of our "rotate" attribute. + */ +enum RotateType { + eRotateType_Explicit, // for e.g. rotate="45"/"45deg"/"0.785rad" + eRotateType_Auto, // for rotate="auto" + eRotateType_AutoReverse // for rotate="auto-reverse" +}; + +/** + * SVGMotionSMILType: Implements the nsISMILType interface for SMIL animations + * from <animateMotion>. + * + * NOTE: Even though there's technically no "motion" attribute, we behave in + * many ways as if there were, for simplicity. + */ +class SVGMotionSMILType : public nsISMILType +{ + typedef mozilla::gfx::Path Path; + +public: + // Singleton for nsSMILValue objects to hold onto. + static SVGMotionSMILType sSingleton; + +protected: + // nsISMILType Methods + // ------------------- + virtual void Init(nsSMILValue& aValue) const override; + virtual void Destroy(nsSMILValue& aValue) const override; + virtual nsresult Assign(nsSMILValue& aDest, const nsSMILValue& aSrc) const override; + virtual bool IsEqual(const nsSMILValue& aLeft, + const nsSMILValue& aRight) const override; + virtual nsresult Add(nsSMILValue& aDest, + const nsSMILValue& aValueToAdd, + uint32_t aCount) const override; + virtual nsresult SandwichAdd(nsSMILValue& aDest, + const nsSMILValue& aValueToAdd) const override; + virtual nsresult ComputeDistance(const nsSMILValue& aFrom, + const nsSMILValue& aTo, + double& aDistance) const override; + virtual nsresult Interpolate(const nsSMILValue& aStartVal, + const nsSMILValue& aEndVal, + double aUnitDistance, + nsSMILValue& aResult) const override; +public: + // Used to generate a transform matrix from an <animateMotion> nsSMILValue. + static gfx::Matrix CreateMatrix(const nsSMILValue& aSMILVal); + + // Used to generate a nsSMILValue for the point at the given distance along + // the given path. + static nsSMILValue ConstructSMILValue(Path* aPath, + float aDist, + RotateType aRotateType, + float aRotateAngle); + +private: + // Private constructor: prevent instances beyond my singleton. + constexpr SVGMotionSMILType() {} +}; + +} // namespace mozilla + +#endif // MOZILLA_SVGMOTIONSMILTYPE_H_ diff --git a/dom/svg/SVGNumberList.cpp b/dom/svg/SVGNumberList.cpp new file mode 100644 index 0000000000..bd30fd3f84 --- /dev/null +++ b/dom/svg/SVGNumberList.cpp @@ -0,0 +1,69 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "mozilla/ArrayUtils.h" + +#include "SVGNumberList.h" +#include "nsCharSeparatedTokenizer.h" +#include "nsString.h" +#include "nsTextFormatter.h" +#include "SVGContentUtils.h" + +namespace mozilla { + +nsresult +SVGNumberList::CopyFrom(const SVGNumberList& rhs) +{ + if (!mNumbers.Assign(rhs.mNumbers, fallible)) { + return NS_ERROR_OUT_OF_MEMORY; + } + return NS_OK; +} + +void +SVGNumberList::GetValueAsString(nsAString& aValue) const +{ + aValue.Truncate(); + char16_t buf[24]; + uint32_t last = mNumbers.Length() - 1; + for (uint32_t i = 0; i < mNumbers.Length(); ++i) { + // Would like to use aValue.AppendPrintf("%f", mNumbers[i]), but it's not + // possible to always avoid trailing zeros. + nsTextFormatter::snprintf(buf, ArrayLength(buf), + u"%g", + double(mNumbers[i])); + // We ignore OOM, since it's not useful for us to return an error. + aValue.Append(buf); + if (i != last) { + aValue.Append(' '); + } + } +} + +nsresult +SVGNumberList::SetValueFromString(const nsAString& aValue) +{ + SVGNumberList temp; + + nsCharSeparatedTokenizerTemplate<IsSVGWhitespace> + tokenizer(aValue, ',', nsCharSeparatedTokenizer::SEPARATOR_OPTIONAL); + + while (tokenizer.hasMoreTokens()) { + float num; + if (!SVGContentUtils::ParseNumber(tokenizer.nextToken(), num)) { + return NS_ERROR_DOM_SYNTAX_ERR; + } + if (!temp.AppendItem(num)) { + return NS_ERROR_OUT_OF_MEMORY; + } + } + if (tokenizer.separatorAfterCurrentToken()) { + return NS_ERROR_DOM_SYNTAX_ERR; // trailing comma + } + return CopyFrom(temp); +} + +} // namespace mozilla diff --git a/dom/svg/SVGNumberList.h b/dom/svg/SVGNumberList.h new file mode 100644 index 0000000000..b6845ffd55 --- /dev/null +++ b/dom/svg/SVGNumberList.h @@ -0,0 +1,206 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef MOZILLA_SVGNUMBERLIST_H__ +#define MOZILLA_SVGNUMBERLIST_H__ + +#include "nsCOMPtr.h" +#include "nsDebug.h" +#include "nsIContent.h" +#include "nsINode.h" +#include "nsIWeakReferenceUtils.h" +#include "nsSVGElement.h" +#include "nsTArray.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 DOMSVGNumberList. + */ +class SVGNumberList +{ + friend class SVGAnimatedNumberList; + friend class DOMSVGNumberList; + friend class DOMSVGNumber; + +public: + + SVGNumberList(){} + ~SVGNumberList(){} + + // 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; + + bool IsEmpty() const { + return mNumbers.IsEmpty(); + } + + uint32_t Length() const { + return mNumbers.Length(); + } + + const float& operator[](uint32_t aIndex) const { + return mNumbers[aIndex]; + } + + bool operator==(const SVGNumberList& rhs) const { + return mNumbers == rhs.mNumbers; + } + + bool SetCapacity(uint32_t size) { + return mNumbers.SetCapacity(size, fallible); + } + + void Compact() { + mNumbers.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 + // SVGAnimatedNumberList 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 SVGNumberList& rhs); + + float& operator[](uint32_t aIndex) { + return mNumbers[aIndex]; + } + + /** + * This may fail (return false) on OOM if the internal capacity is being + * increased, in which case the list will be left unmodified. + */ + bool SetLength(uint32_t aNumberOfItems) { + return mNumbers.SetLength(aNumberOfItems, fallible); + } + +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() { + mNumbers.Clear(); + } + + bool InsertItem(uint32_t aIndex, const float &aNumber) { + if (aIndex >= mNumbers.Length()) { + aIndex = mNumbers.Length(); + } + return !!mNumbers.InsertElementAt(aIndex, aNumber, fallible); + } + + void ReplaceItem(uint32_t aIndex, const float &aNumber) { + MOZ_ASSERT(aIndex < mNumbers.Length(), + "DOM wrapper caller should have raised INDEX_SIZE_ERR"); + mNumbers[aIndex] = aNumber; + } + + void RemoveItem(uint32_t aIndex) { + MOZ_ASSERT(aIndex < mNumbers.Length(), + "DOM wrapper caller should have raised INDEX_SIZE_ERR"); + mNumbers.RemoveElementAt(aIndex); + } + + bool AppendItem(float aNumber) { + return !!mNumbers.AppendElement(aNumber, fallible); + } + +protected: + + /* See SVGLengthList for the rationale for using FallibleTArray<float> instead + * of FallibleTArray<float, 1>. + */ + FallibleTArray<float> mNumbers; +}; + + +/** + * This SVGNumberList subclass is used by the SMIL code when a number list + * is to be stored in an nsISMILValue instance. Since nsISMILValue objects may + * be cached, it is necessary for us to hold a strong reference to our element + * so that it doesn't disappear out from under us if, say, the element is + * removed from the DOM tree. + */ +class SVGNumberListAndInfo : public SVGNumberList +{ +public: + + SVGNumberListAndInfo() + : mElement(nullptr) + {} + + explicit SVGNumberListAndInfo(nsSVGElement *aElement) + : mElement(do_GetWeakReference(static_cast<nsINode*>(aElement))) + {} + + void SetInfo(nsSVGElement *aElement) { + mElement = do_GetWeakReference(static_cast<nsINode*>(aElement)); + } + + nsSVGElement* Element() const { + nsCOMPtr<nsIContent> e = do_QueryReferent(mElement); + return static_cast<nsSVGElement*>(e.get()); + } + + nsresult CopyFrom(const SVGNumberListAndInfo& rhs) { + mElement = rhs.mElement; + return SVGNumberList::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 SVGNumberList baseVals can be copied to + * SVGNumberListAndInfo objects. Note that callers should also call + * SetInfo() when using this method! + */ + nsresult CopyFrom(const SVGNumberList& rhs) { + return SVGNumberList::CopyFrom(rhs); + } + const float& operator[](uint32_t aIndex) const { + return SVGNumberList::operator[](aIndex); + } + float& operator[](uint32_t aIndex) { + return SVGNumberList::operator[](aIndex); + } + bool SetLength(uint32_t aNumberOfItems) { + return SVGNumberList::SetLength(aNumberOfItems); + } + +private: + // We must keep a weak 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 + // See also https://bugzilla.mozilla.org/show_bug.cgi?id=653497 + nsWeakPtr mElement; +}; + +} // namespace mozilla + +#endif // MOZILLA_SVGNUMBERLIST_H__ diff --git a/dom/svg/SVGNumberListSMILType.cpp b/dom/svg/SVGNumberListSMILType.cpp new file mode 100644 index 0000000000..51bc27a688 --- /dev/null +++ b/dom/svg/SVGNumberListSMILType.cpp @@ -0,0 +1,216 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "SVGNumberListSMILType.h" +#include "nsSMILValue.h" +#include "SVGNumberList.h" +#include "nsMathUtils.h" +#include "mozilla/FloatingPoint.h" +#include <math.h> + +/* The "identity" number list for a given number list attribute (the effective + * number list that is used if an attribute value is not specified) varies + * widely for different number list attributes, and can depend on the value of + * other attributes on the same element: + * + * http://www.w3.org/TR/SVG11/filters.html#feColorMatrixValuesAttribute + * http://www.w3.org/TR/SVG11/filters.html#feComponentTransferTableValuesAttribute + * http://www.w3.org/TR/SVG11/filters.html#feConvolveMatrixElementKernelMatrixAttribute + * http://www.w3.org/TR/SVG11/text.html#TextElementRotateAttribute + * + * Note that we don't need to worry about that variation here, however. The way + * that the SMIL engine creates and composites sandwich layers together allows + * us to treat "identity" nsSMILValue objects as a number list of zeros. Such + * identity nsSMILValues are identified by the fact that their + # SVGNumberListAndInfo has not been given an element yet. + */ + +namespace mozilla { + +/*static*/ SVGNumberListSMILType SVGNumberListSMILType::sSingleton; + +//---------------------------------------------------------------------- +// nsISMILType implementation + +void +SVGNumberListSMILType::Init(nsSMILValue &aValue) const +{ + MOZ_ASSERT(aValue.IsNull(), "Unexpected value type"); + + SVGNumberListAndInfo* numberList = new SVGNumberListAndInfo(); + + aValue.mU.mPtr = numberList; + aValue.mType = this; +} + +void +SVGNumberListSMILType::Destroy(nsSMILValue& aValue) const +{ + NS_PRECONDITION(aValue.mType == this, "Unexpected SMIL value type"); + delete static_cast<SVGNumberListAndInfo*>(aValue.mU.mPtr); + aValue.mU.mPtr = nullptr; + aValue.mType = nsSMILNullType::Singleton(); +} + +nsresult +SVGNumberListSMILType::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 SVGNumberListAndInfo* src = + static_cast<const SVGNumberListAndInfo*>(aSrc.mU.mPtr); + SVGNumberListAndInfo* dest = + static_cast<SVGNumberListAndInfo*>(aDest.mU.mPtr); + + return dest->CopyFrom(*src); +} + +bool +SVGNumberListSMILType::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 SVGNumberListAndInfo*>(aLeft.mU.mPtr) == + *static_cast<const SVGNumberListAndInfo*>(aRight.mU.mPtr); +} + +nsresult +SVGNumberListSMILType::Add(nsSMILValue& aDest, + const nsSMILValue& aValueToAdd, + uint32_t aCount) const +{ + NS_PRECONDITION(aDest.mType == this, "Unexpected SMIL type"); + NS_PRECONDITION(aValueToAdd.mType == this, "Incompatible SMIL type"); + + SVGNumberListAndInfo& dest = + *static_cast<SVGNumberListAndInfo*>(aDest.mU.mPtr); + const SVGNumberListAndInfo& valueToAdd = + *static_cast<const SVGNumberListAndInfo*>(aValueToAdd.mU.mPtr); + + MOZ_ASSERT(dest.Element() || valueToAdd.Element(), + "Target element propagation failure"); + + if (!valueToAdd.Element()) { + MOZ_ASSERT(valueToAdd.Length() == 0, + "Not identity value - target element propagation failure"); + return NS_OK; + } + if (!dest.Element()) { + MOZ_ASSERT(dest.Length() == 0, + "Not identity value - target element propagation failure"); + if (!dest.SetLength(valueToAdd.Length())) { + return NS_ERROR_OUT_OF_MEMORY; + } + for (uint32_t i = 0; i < dest.Length(); ++i) { + dest[i] = aCount * valueToAdd[i]; + } + dest.SetInfo(valueToAdd.Element()); // propagate target element info! + return NS_OK; + } + MOZ_ASSERT(dest.Element() == valueToAdd.Element(), + "adding values from different elements...?"); + if (dest.Length() != valueToAdd.Length()) { + // For now we only support animation between lists with the same number of + // items. SVGContentUtils::ReportToConsole + return NS_ERROR_FAILURE; + } + for (uint32_t i = 0; i < dest.Length(); ++i) { + dest[i] += aCount * valueToAdd[i]; + } + dest.SetInfo(valueToAdd.Element()); // propagate target element info! + return NS_OK; +} + +nsresult +SVGNumberListSMILType::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 SVGNumberListAndInfo& from = + *static_cast<const SVGNumberListAndInfo*>(aFrom.mU.mPtr); + const SVGNumberListAndInfo& to = + *static_cast<const SVGNumberListAndInfo*>(aTo.mU.mPtr); + + if (from.Length() != to.Length()) { + // Lists in the 'values' attribute must have the same length. + // SVGContentUtils::ReportToConsole + return NS_ERROR_FAILURE; + } + + // We return the root of the sum of the squares of the delta between the + // numbers at each correspanding index. + + double total = 0.0; + + for (uint32_t i = 0; i < to.Length(); ++i) { + double delta = to[i] - from[i]; + total += delta * delta; + } + double distance = sqrt(total); + if (!IsFinite(distance)) { + return NS_ERROR_FAILURE; + } + aDistance = distance; + + return NS_OK; +} + +nsresult +SVGNumberListSMILType::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 SVGNumberListAndInfo& start = + *static_cast<const SVGNumberListAndInfo*>(aStartVal.mU.mPtr); + const SVGNumberListAndInfo& end = + *static_cast<const SVGNumberListAndInfo*>(aEndVal.mU.mPtr); + SVGNumberListAndInfo& result = + *static_cast<SVGNumberListAndInfo*>(aResult.mU.mPtr); + + MOZ_ASSERT(end.Element(), "Can't propagate target element"); + MOZ_ASSERT(start.Element() == end.Element() || !start.Element(), + "Different target elements"); + + if (start.Element() && // 'start' is not an "identity" value + start.Length() != end.Length()) { + // For now we only support animation between lists of the same length. + // SVGContentUtils::ReportToConsole + return NS_ERROR_FAILURE; + } + if (!result.SetLength(end.Length())) { + return NS_ERROR_OUT_OF_MEMORY; + } + + result.SetInfo(end.Element()); // propagate target element info! + + if (start.Length() != end.Length()) { + MOZ_ASSERT(start.Length() == 0, "Not an identity value"); + for (uint32_t i = 0; i < end.Length(); ++i) { + result[i] = aUnitDistance * end[i]; + } + return NS_OK; + } + for (uint32_t i = 0; i < end.Length(); ++i) { + result[i] = start[i] + (end[i] - start[i]) * aUnitDistance; + } + return NS_OK; +} + +} // namespace mozilla diff --git a/dom/svg/SVGNumberListSMILType.h b/dom/svg/SVGNumberListSMILType.h new file mode 100644 index 0000000000..5e8db8afeb --- /dev/null +++ b/dom/svg/SVGNumberListSMILType.h @@ -0,0 +1,55 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef MOZILLA_SVGNUMBERLISTSMILTYPE_H_ +#define MOZILLA_SVGNUMBERLISTSMILTYPE_H_ + +#include "mozilla/Attributes.h" +#include "nsISMILType.h" + +class nsSMILValue; + +namespace mozilla { + +//////////////////////////////////////////////////////////////////////// +// SVGNumberListSMILType +// +// Operations for animating an SVGNumberList. +// +class SVGNumberListSMILType : public nsISMILType +{ +public: + // Singleton for nsSMILValue objects to hold onto. + static SVGNumberListSMILType sSingleton; + +protected: + // nsISMILType Methods + // ------------------- + + virtual void Init(nsSMILValue& aValue) const override; + + virtual void Destroy(nsSMILValue& aValue) const override; + virtual nsresult Assign(nsSMILValue& aDest, const nsSMILValue& aSrc) const override; + virtual bool IsEqual(const nsSMILValue& aLeft, + const nsSMILValue& aRight) const override; + virtual nsresult Add(nsSMILValue& aDest, const nsSMILValue& aValueToAdd, + uint32_t aCount) const override; + virtual nsresult ComputeDistance(const nsSMILValue& aFrom, + const nsSMILValue& aTo, + double& aDistance) const override; + virtual nsresult Interpolate(const nsSMILValue& aStartVal, + const nsSMILValue& aEndVal, + double aUnitDistance, + nsSMILValue& aResult) const override; + +private: + // Private constructor: prevent instances beyond my singleton. + constexpr SVGNumberListSMILType() {} +}; + +} // namespace mozilla + +#endif // MOZILLA_SVGNUMBERLISTSMILTYPE_H_ diff --git a/dom/svg/SVGNumberPairSMILType.cpp b/dom/svg/SVGNumberPairSMILType.cpp new file mode 100644 index 0000000000..e314dfcf1d --- /dev/null +++ b/dom/svg/SVGNumberPairSMILType.cpp @@ -0,0 +1,108 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "SVGNumberPairSMILType.h" +#include "nsSMILValue.h" +#include "nsMathUtils.h" +#include "nsDebug.h" + +namespace mozilla { + +/*static*/ SVGNumberPairSMILType SVGNumberPairSMILType::sSingleton; + +void +SVGNumberPairSMILType::Init(nsSMILValue& aValue) const +{ + MOZ_ASSERT(aValue.IsNull(), "Unexpected value type"); + + aValue.mU.mNumberPair[0] = 0; + aValue.mU.mNumberPair[1] = 0; + aValue.mType = this; +} + +void +SVGNumberPairSMILType::Destroy(nsSMILValue& aValue) const +{ + NS_PRECONDITION(aValue.mType == this, "Unexpected SMIL value"); + aValue.mU.mNumberPair[0] = 0; + aValue.mU.mNumberPair[1] = 0; + aValue.mType = nsSMILNullType::Singleton(); +} + +nsresult +SVGNumberPairSMILType::Assign(nsSMILValue& aDest, const nsSMILValue& aSrc) const +{ + NS_PRECONDITION(aDest.mType == aSrc.mType, "Incompatible SMIL types"); + NS_PRECONDITION(aDest.mType == this, "Unexpected SMIL value"); + + aDest.mU.mNumberPair[0] = aSrc.mU.mNumberPair[0]; + aDest.mU.mNumberPair[1] = aSrc.mU.mNumberPair[1]; + return NS_OK; +} + +bool +SVGNumberPairSMILType::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 aLeft.mU.mNumberPair[0] == aRight.mU.mNumberPair[0] && + aLeft.mU.mNumberPair[1] == aRight.mU.mNumberPair[1]; +} + +nsresult +SVGNumberPairSMILType::Add(nsSMILValue& aDest, const nsSMILValue& aValueToAdd, + uint32_t aCount) const +{ + NS_PRECONDITION(aValueToAdd.mType == aDest.mType, + "Trying to add invalid types"); + NS_PRECONDITION(aValueToAdd.mType == this, "Unexpected source type"); + + aDest.mU.mNumberPair[0] += aValueToAdd.mU.mNumberPair[0] * aCount; + aDest.mU.mNumberPair[1] += aValueToAdd.mU.mNumberPair[1] * aCount; + + return NS_OK; +} + +nsresult +SVGNumberPairSMILType::ComputeDistance(const nsSMILValue& aFrom, + const nsSMILValue& aTo, + double& aDistance) const +{ + NS_PRECONDITION(aFrom.mType == aTo.mType,"Trying to compare different types"); + NS_PRECONDITION(aFrom.mType == this, "Unexpected source type"); + + double delta[2]; + delta[0] = aTo.mU.mNumberPair[0] - aFrom.mU.mNumberPair[0]; + delta[1] = aTo.mU.mNumberPair[1] - aFrom.mU.mNumberPair[1]; + + aDistance = NS_hypot(delta[0], delta[1]); + return NS_OK; +} + +nsresult +SVGNumberPairSMILType::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"); + + aResult.mU.mNumberPair[0] = + float(aStartVal.mU.mNumberPair[0] + + (aEndVal.mU.mNumberPair[0] - aStartVal.mU.mNumberPair[0]) * aUnitDistance); + aResult.mU.mNumberPair[1] = + float(aStartVal.mU.mNumberPair[1] + + (aEndVal.mU.mNumberPair[1] - aStartVal.mU.mNumberPair[1]) * aUnitDistance); + return NS_OK; +} + +} // namespace mozilla diff --git a/dom/svg/SVGNumberPairSMILType.h b/dom/svg/SVGNumberPairSMILType.h new file mode 100644 index 0000000000..0f90163f58 --- /dev/null +++ b/dom/svg/SVGNumberPairSMILType.h @@ -0,0 +1,48 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef MOZILLA_SVGNUMBERPAIRSMILTYPE_H_ +#define MOZILLA_SVGNUMBERPAIRSMILTYPE_H_ + +#include "mozilla/Attributes.h" +#include "nsISMILType.h" + +class nsSMILValue; + +namespace mozilla { + +class SVGNumberPairSMILType : public nsISMILType +{ +public: + // Singleton for nsSMILValue objects to hold onto. + static SVGNumberPairSMILType sSingleton; + +protected: + // nsISMILType Methods + // ------------------- + virtual void Init(nsSMILValue& aValue) const override; + virtual void Destroy(nsSMILValue&) const override; + virtual nsresult Assign(nsSMILValue& aDest, const nsSMILValue& aSrc) const override; + virtual bool IsEqual(const nsSMILValue& aLeft, + const nsSMILValue& aRight) const override; + virtual nsresult Add(nsSMILValue& aDest, const nsSMILValue& aValueToAdd, + uint32_t aCount) const override; + virtual nsresult ComputeDistance(const nsSMILValue& aFrom, + const nsSMILValue& aTo, + double& aDistance) const override; + virtual nsresult Interpolate(const nsSMILValue& aStartVal, + const nsSMILValue& aEndVal, + double aUnitDistance, + nsSMILValue& aResult) const override; + +private: + // Private constructor: prevent instances beyond my singleton. + constexpr SVGNumberPairSMILType() {} +}; + +} // namespace mozilla + +#endif // MOZILLA_SVGNUMBERPAIRSMILTYPE_H_ diff --git a/dom/svg/SVGOrientSMILType.cpp b/dom/svg/SVGOrientSMILType.cpp new file mode 100644 index 0000000000..8e69ef1329 --- /dev/null +++ b/dom/svg/SVGOrientSMILType.cpp @@ -0,0 +1,150 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "SVGOrientSMILType.h" +#include "nsSMILValue.h" +#include "nsSVGAngle.h" +#include "nsDebug.h" +#include "mozilla/dom/SVGMarkerElement.h" +#include <math.h> + +namespace mozilla { + +/*static*/ SVGOrientSMILType SVGOrientSMILType::sSingleton; + +void +SVGOrientSMILType::Init(nsSMILValue& aValue) const +{ + MOZ_ASSERT(aValue.IsNull(), "Unexpected value type"); + + aValue.mU.mOrient.mAngle = 0.0f; + aValue.mU.mOrient.mUnit = SVG_ANGLETYPE_UNSPECIFIED; + aValue.mU.mOrient.mOrientType = dom::SVG_MARKER_ORIENT_ANGLE; + aValue.mType = this; +} + +void +SVGOrientSMILType::Destroy(nsSMILValue& aValue) const +{ + NS_PRECONDITION(aValue.mType == this, "Unexpected SMIL value."); + aValue.mU.mPtr = nullptr; + aValue.mType = nsSMILNullType::Singleton(); +} + +nsresult +SVGOrientSMILType::Assign(nsSMILValue& aDest, const nsSMILValue& aSrc) const +{ + NS_PRECONDITION(aDest.mType == aSrc.mType, "Incompatible SMIL types."); + NS_PRECONDITION(aDest.mType == this, "Unexpected SMIL value."); + + aDest.mU.mOrient.mAngle = aSrc.mU.mOrient.mAngle; + aDest.mU.mOrient.mUnit = aSrc.mU.mOrient.mUnit; + aDest.mU.mOrient.mOrientType = aSrc.mU.mOrient.mOrientType; + return NS_OK; +} + +bool +SVGOrientSMILType::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 + aLeft.mU.mOrient.mAngle == aRight.mU.mOrient.mAngle && + aLeft.mU.mOrient.mUnit == aRight.mU.mOrient.mUnit && + aLeft.mU.mOrient.mOrientType == aRight.mU.mOrient.mOrientType; +} + +nsresult +SVGOrientSMILType::Add(nsSMILValue& aDest, const nsSMILValue& aValueToAdd, + uint32_t aCount) const +{ + NS_PRECONDITION(aValueToAdd.mType == aDest.mType, + "Trying to add invalid types"); + NS_PRECONDITION(aValueToAdd.mType == this, "Unexpected source type"); + + if (aDest.mU.mOrient.mOrientType != dom::SVG_MARKER_ORIENT_ANGLE || + aValueToAdd.mU.mOrient.mOrientType != dom::SVG_MARKER_ORIENT_ANGLE) { + // TODO: it would be nice to be able to add to auto angles + return NS_ERROR_FAILURE; + } + + // We may be dealing with two different angle units, so we normalize to + // degrees for the add: + float currentAngle = aDest.mU.mOrient.mAngle * + nsSVGAngle::GetDegreesPerUnit(aDest.mU.mOrient.mUnit); + float angleToAdd = aValueToAdd.mU.mOrient.mAngle * + nsSVGAngle::GetDegreesPerUnit(aValueToAdd.mU.mOrient.mUnit) * + aCount; + + // And then we give the resulting animated value the same units as the value + // that we're animating to/by (i.e. the same as aValueToAdd): + aDest.mU.mOrient.mAngle = (currentAngle + angleToAdd) / + nsSVGAngle::GetDegreesPerUnit(aValueToAdd.mU.mOrient.mUnit); + aDest.mU.mOrient.mUnit = aValueToAdd.mU.mOrient.mUnit; + + return NS_OK; +} + +nsresult +SVGOrientSMILType::ComputeDistance(const nsSMILValue& aFrom, + const nsSMILValue& aTo, + double& aDistance) const +{ + NS_PRECONDITION(aFrom.mType == aTo.mType,"Trying to compare different types"); + NS_PRECONDITION(aFrom.mType == this, "Unexpected source type"); + + if (aFrom.mU.mOrient.mOrientType != dom::SVG_MARKER_ORIENT_ANGLE || + aTo.mU.mOrient.mOrientType != dom::SVG_MARKER_ORIENT_ANGLE) { + // TODO: it would be nice to be able to compute distance with auto angles + return NS_ERROR_FAILURE; + } + + // Normalize both to degrees in case they're different angle units: + double from = aFrom.mU.mOrient.mAngle * + nsSVGAngle::GetDegreesPerUnit(aFrom.mU.mOrient.mUnit); + double to = aTo.mU.mOrient.mAngle * + nsSVGAngle::GetDegreesPerUnit(aTo.mU.mOrient.mUnit); + + aDistance = fabs(to - from); + + return NS_OK; +} + +nsresult +SVGOrientSMILType::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."); + + if (aStartVal.mU.mOrient.mOrientType != dom::SVG_MARKER_ORIENT_ANGLE || + aEndVal.mU.mOrient.mOrientType != dom::SVG_MARKER_ORIENT_ANGLE) { + // TODO: it would be nice to be able to handle auto angles too. + return NS_ERROR_FAILURE; + } + + float start = aStartVal.mU.mOrient.mAngle * + nsSVGAngle::GetDegreesPerUnit(aStartVal.mU.mOrient.mUnit); + float end = aEndVal.mU.mOrient.mAngle * + nsSVGAngle::GetDegreesPerUnit(aEndVal.mU.mOrient.mUnit); + float result = (start + (end - start) * aUnitDistance); + + // Again, we use the unit of the to/by value for the result: + aResult.mU.mOrient.mAngle = result / + nsSVGAngle::GetDegreesPerUnit(aEndVal.mU.mOrient.mUnit); + aResult.mU.mOrient.mUnit = aEndVal.mU.mOrient.mUnit; + + return NS_OK; +} + +} // namespace mozilla diff --git a/dom/svg/SVGOrientSMILType.h b/dom/svg/SVGOrientSMILType.h new file mode 100644 index 0000000000..6b30cbee0f --- /dev/null +++ b/dom/svg/SVGOrientSMILType.h @@ -0,0 +1,68 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef MOZILLA_SVGORIENTSMILTYPE_H_ +#define MOZILLA_SVGORIENTSMILTYPE_H_ + +#include "mozilla/Attributes.h" +#include "nsISMILType.h" + +class nsSMILValue; + +/** + * This nsISMILType class is a special case for the 'orient' attribute on SVG's + * 'marker' element. + * + * orient = "auto | auto-start-reverse | <angle>" + * + * Unusually, this attribute doesn't have just a single corresponding DOM + * property, but rather is split into two properties: 'orientType' (of type + * SVGAnimatedEnumeration) and 'orientAngle' (of type SVGAnimatedAngle). If + * 'orientType.animVal' is SVG_MARKER_ORIENT_ANGLE, then + * 'orientAngle.animVal' contains the angle that is being used. The lacuna + * value is 0. + * + * The SVG 2 specification does not define a + * SVG_MARKER_ORIENT_AUTO_START_REVERSE constant value for orientType to use; + * instead, if the attribute is set to "auto-start-reverse", + * SVG_MARKER_ORIENT_UNKNOWN is used. Internally, however, we do use a + * constant with this name. + */ + +namespace mozilla { + +class SVGOrientSMILType : public nsISMILType +{ +public: + // Singleton for nsSMILValue objects to hold onto. + static SVGOrientSMILType sSingleton; + +protected: + // nsISMILType Methods + // ------------------- + virtual void Init(nsSMILValue& aValue) const override; + virtual void Destroy(nsSMILValue&) const override; + virtual nsresult Assign(nsSMILValue& aDest, const nsSMILValue& aSrc) const override; + virtual bool IsEqual(const nsSMILValue& aLeft, + const nsSMILValue& aRight) const override; + virtual nsresult Add(nsSMILValue& aDest, const nsSMILValue& aValueToAdd, + uint32_t aCount) const override; + virtual nsresult ComputeDistance(const nsSMILValue& aFrom, + const nsSMILValue& aTo, + double& aDistance) const override; + virtual nsresult Interpolate(const nsSMILValue& aStartVal, + const nsSMILValue& aEndVal, + double aUnitDistance, + nsSMILValue& aResult) const override; + +private: + // Private constructor: prevent instances beyond my singleton. + constexpr SVGOrientSMILType() {} +}; + +} // namespace mozilla + +#endif // MOZILLA_SVGORIENTSMILTYPE_H_ diff --git a/dom/svg/SVGPathData.cpp b/dom/svg/SVGPathData.cpp new file mode 100644 index 0000000000..2020798b4f --- /dev/null +++ b/dom/svg/SVGPathData.cpp @@ -0,0 +1,872 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "SVGPathData.h" + +#include "gfx2DGlue.h" +#include "gfxPlatform.h" +#include "mozilla/gfx/2D.h" +#include "mozilla/gfx/Types.h" +#include "mozilla/gfx/Point.h" +#include "mozilla/RefPtr.h" +#include "nsError.h" +#include "nsString.h" +#include "nsSVGPathDataParser.h" +#include "nsSVGPathGeometryElement.h" // for nsSVGMark +#include <stdarg.h> +#include "nsStyleConsts.h" +#include "SVGContentUtils.h" +#include "SVGPathSegUtils.h" +#include <algorithm> + +using namespace mozilla; +using namespace mozilla::gfx; + +static bool IsMoveto(uint16_t aSegType) +{ + return aSegType == PATHSEG_MOVETO_ABS || + aSegType == PATHSEG_MOVETO_REL; +} + +nsresult +SVGPathData::CopyFrom(const SVGPathData& rhs) +{ + if (!mData.Assign(rhs.mData, fallible)) { + return NS_ERROR_OUT_OF_MEMORY; + } + return NS_OK; +} + +void +SVGPathData::GetValueAsString(nsAString& aValue) const +{ + // we need this function in DidChangePathSegList + aValue.Truncate(); + if (!Length()) { + return; + } + uint32_t i = 0; + for (;;) { + nsAutoString segAsString; + SVGPathSegUtils::GetValueAsString(&mData[i], segAsString); + // We ignore OOM, since it's not useful for us to return an error. + aValue.Append(segAsString); + i += 1 + SVGPathSegUtils::ArgCountForType(mData[i]); + if (i >= mData.Length()) { + MOZ_ASSERT(i == mData.Length(), "Very, very bad - mData corrupt"); + return; + } + aValue.Append(' '); + } +} + +nsresult +SVGPathData::SetValueFromString(const nsAString& aValue) +{ + // We don't use a temp variable since the spec says to parse everything up to + // the first error. We still return any error though so that callers know if + // there's a problem. + + nsSVGPathDataParser pathParser(aValue, this); + return pathParser.Parse() ? NS_OK : NS_ERROR_DOM_SYNTAX_ERR; +} + +nsresult +SVGPathData::AppendSeg(uint32_t aType, ...) +{ + uint32_t oldLength = mData.Length(); + uint32_t newLength = oldLength + 1 + SVGPathSegUtils::ArgCountForType(aType); + if (!mData.SetLength(newLength, fallible)) { + return NS_ERROR_OUT_OF_MEMORY; + } + + mData[oldLength] = SVGPathSegUtils::EncodeType(aType); + va_list args; + va_start(args, aType); + for (uint32_t i = oldLength + 1; i < newLength; ++i) { + // NOTE! 'float' is promoted to 'double' when passed through '...'! + mData[i] = float(va_arg(args, double)); + } + va_end(args); + return NS_OK; +} + +float +SVGPathData::GetPathLength() const +{ + SVGPathTraversalState state; + + uint32_t i = 0; + while (i < mData.Length()) { + SVGPathSegUtils::TraversePathSegment(&mData[i], state); + i += 1 + SVGPathSegUtils::ArgCountForType(mData[i]); + } + + MOZ_ASSERT(i == mData.Length(), "Very, very bad - mData corrupt"); + + return state.length; +} + +#ifdef DEBUG +uint32_t +SVGPathData::CountItems() const +{ + uint32_t i = 0, count = 0; + + while (i < mData.Length()) { + i += 1 + SVGPathSegUtils::ArgCountForType(mData[i]); + count++; + } + + MOZ_ASSERT(i == mData.Length(), "Very, very bad - mData corrupt"); + + return count; +} +#endif + +bool +SVGPathData::GetSegmentLengths(nsTArray<double> *aLengths) const +{ + aLengths->Clear(); + SVGPathTraversalState state; + + uint32_t i = 0; + while (i < mData.Length()) { + state.length = 0.0; + SVGPathSegUtils::TraversePathSegment(&mData[i], state); + if (!aLengths->AppendElement(state.length)) { + aLengths->Clear(); + return false; + } + i += 1 + SVGPathSegUtils::ArgCountForType(mData[i]); + } + + MOZ_ASSERT(i == mData.Length(), "Very, very bad - mData corrupt"); + + return true; +} + +bool +SVGPathData::GetDistancesFromOriginToEndsOfVisibleSegments(FallibleTArray<double> *aOutput) const +{ + SVGPathTraversalState state; + + aOutput->Clear(); + + uint32_t i = 0; + while (i < mData.Length()) { + uint32_t segType = SVGPathSegUtils::DecodeType(mData[i]); + SVGPathSegUtils::TraversePathSegment(&mData[i], state); + + // We skip all moveto commands except an initial moveto. See the text 'A + // "move to" command does not count as an additional point when dividing up + // the duration...': + // + // http://www.w3.org/TR/SVG11/animate.html#AnimateMotionElement + // + // This is important in the non-default case of calcMode="linear". In + // this case an equal amount of time is spent on each path segment, + // except on moveto segments which are jumped over immediately. + + if (i == 0 || (segType != PATHSEG_MOVETO_ABS && + segType != PATHSEG_MOVETO_REL)) { + if (!aOutput->AppendElement(state.length, fallible)) { + return false; + } + } + i += 1 + SVGPathSegUtils::ArgCountForType(segType); + } + + MOZ_ASSERT(i == mData.Length(), "Very, very bad - mData corrupt?"); + + return true; +} + +uint32_t +SVGPathData::GetPathSegAtLength(float aDistance) const +{ + // TODO [SVGWG issue] get specified what happen if 'aDistance' < 0, or + // 'aDistance' > the length of the path, or the seg list is empty. + // Return -1? Throwing would better help authors avoid tricky bugs (DOM + // could do that if we return -1). + + uint32_t i = 0, segIndex = 0; + SVGPathTraversalState state; + + while (i < mData.Length()) { + SVGPathSegUtils::TraversePathSegment(&mData[i], state); + if (state.length >= aDistance) { + return segIndex; + } + i += 1 + SVGPathSegUtils::ArgCountForType(mData[i]); + segIndex++; + } + + MOZ_ASSERT(i == mData.Length(), "Very, very bad - mData corrupt"); + + return std::max(0U, segIndex - 1); // -1 because while loop takes us 1 too far +} + +/** + * The SVG spec says we have to paint stroke caps for zero length subpaths: + * + * http://www.w3.org/TR/SVG11/implnote.html#PathElementImplementationNotes + * + * Cairo only does this for |stroke-linecap: round| and not for + * |stroke-linecap: square| (since that's what Adobe Acrobat has always done). + * Most likely the other backends that DrawTarget uses have the same behavior. + * + * To help us conform to the SVG spec we have this helper function to draw an + * approximation of square caps for zero length subpaths. It does this by + * inserting a subpath containing a single user space axis aligned straight + * line that is as small as it can be while minimizing the risk of it being + * thrown away by the DrawTarget's backend for being too small to affect + * rendering. The idea is that we'll then get stroke caps drawn for this axis + * aligned line, creating an axis aligned rectangle that approximates the + * square that would ideally be drawn. + * + * Since we don't have any information about transforms from user space to + * device space, we choose the length of the small line that we insert by + * making it a small percentage of the stroke width of the path. This should + * hopefully allow us to make the line as long as possible (to avoid rounding + * issues in the backend resulting in the backend seeing it as having zero + * length) while still avoiding the small rectangle being noticably different + * from a square. + * + * Note that this function inserts a subpath into the current gfx path that + * will be present during both fill and stroke operations. + */ +static void +ApproximateZeroLengthSubpathSquareCaps(PathBuilder* aPB, + const Point& aPoint, + Float aStrokeWidth) +{ + // Note that caps are proportional to stroke width, so if stroke width is + // zero it's actually fine for |tinyLength| below to end up being zero. + // However, it would be a waste to inserting a LineTo in that case, so better + // not to. + MOZ_ASSERT(aStrokeWidth > 0.0f, + "Make the caller check for this, or check it here"); + + // The fraction of the stroke width that we choose for the length of the + // line is rather arbitrary, other than being chosen to meet the requirements + // described in the comment above. + + Float tinyLength = aStrokeWidth / SVG_ZERO_LENGTH_PATH_FIX_FACTOR; + + aPB->LineTo(aPoint + Point(tinyLength, 0)); + aPB->MoveTo(aPoint); +} + +#define MAYBE_APPROXIMATE_ZERO_LENGTH_SUBPATH_SQUARE_CAPS_TO_DT \ + do { \ + if (!subpathHasLength && hasLineCaps && aStrokeWidth > 0 && \ + subpathContainsNonMoveTo && \ + SVGPathSegUtils::IsValidType(prevSegType) && \ + (!IsMoveto(prevSegType) || segType == PATHSEG_CLOSEPATH)) { \ + ApproximateZeroLengthSubpathSquareCaps(builder, segStart, aStrokeWidth);\ + } \ + } while(0) + +already_AddRefed<Path> +SVGPathData::BuildPath(PathBuilder* builder, + uint8_t aStrokeLineCap, + Float aStrokeWidth) const +{ + if (mData.IsEmpty() || !IsMoveto(SVGPathSegUtils::DecodeType(mData[0]))) { + return nullptr; // paths without an initial moveto are invalid + } + + bool hasLineCaps = aStrokeLineCap != NS_STYLE_STROKE_LINECAP_BUTT; + bool subpathHasLength = false; // visual length + bool subpathContainsNonMoveTo = false; + + uint32_t segType = PATHSEG_UNKNOWN; + uint32_t prevSegType = PATHSEG_UNKNOWN; + Point pathStart(0.0, 0.0); // start point of [sub]path + Point segStart(0.0, 0.0); + Point segEnd; + Point cp1, cp2; // previous bezier's control points + Point tcp1, tcp2; // temporaries + + // Regarding cp1 and cp2: If the previous segment was a cubic bezier curve, + // then cp2 is its second control point. If the previous segment was a + // quadratic curve, then cp1 is its (only) control point. + + uint32_t i = 0; + while (i < mData.Length()) { + segType = SVGPathSegUtils::DecodeType(mData[i++]); + uint32_t argCount = SVGPathSegUtils::ArgCountForType(segType); + + switch (segType) + { + case PATHSEG_CLOSEPATH: + // set this early to allow drawing of square caps for "M{x},{y} Z": + subpathContainsNonMoveTo = true; + MAYBE_APPROXIMATE_ZERO_LENGTH_SUBPATH_SQUARE_CAPS_TO_DT; + segEnd = pathStart; + builder->Close(); + break; + + case PATHSEG_MOVETO_ABS: + MAYBE_APPROXIMATE_ZERO_LENGTH_SUBPATH_SQUARE_CAPS_TO_DT; + pathStart = segEnd = Point(mData[i], mData[i+1]); + builder->MoveTo(segEnd); + subpathHasLength = false; + break; + + case PATHSEG_MOVETO_REL: + MAYBE_APPROXIMATE_ZERO_LENGTH_SUBPATH_SQUARE_CAPS_TO_DT; + pathStart = segEnd = segStart + Point(mData[i], mData[i+1]); + builder->MoveTo(segEnd); + subpathHasLength = false; + break; + + case PATHSEG_LINETO_ABS: + segEnd = Point(mData[i], mData[i+1]); + if (segEnd != segStart) { + subpathHasLength = true; + builder->LineTo(segEnd); + } + break; + + case PATHSEG_LINETO_REL: + segEnd = segStart + Point(mData[i], mData[i+1]); + if (segEnd != segStart) { + subpathHasLength = true; + builder->LineTo(segEnd); + } + break; + + case PATHSEG_CURVETO_CUBIC_ABS: + cp1 = Point(mData[i], mData[i+1]); + cp2 = Point(mData[i+2], mData[i+3]); + segEnd = Point(mData[i+4], mData[i+5]); + if (segEnd != segStart || segEnd != cp1 || segEnd != cp2) { + subpathHasLength = true; + builder->BezierTo(cp1, cp2, segEnd); + } + break; + + case PATHSEG_CURVETO_CUBIC_REL: + cp1 = segStart + Point(mData[i], mData[i+1]); + cp2 = segStart + Point(mData[i+2], mData[i+3]); + segEnd = segStart + Point(mData[i+4], mData[i+5]); + if (segEnd != segStart || segEnd != cp1 || segEnd != cp2) { + subpathHasLength = true; + builder->BezierTo(cp1, cp2, segEnd); + } + break; + + case PATHSEG_CURVETO_QUADRATIC_ABS: + cp1 = Point(mData[i], mData[i+1]); + // Convert quadratic curve to cubic curve: + tcp1 = segStart + (cp1 - segStart) * 2 / 3; + segEnd = Point(mData[i+2], mData[i+3]); // set before setting tcp2! + tcp2 = cp1 + (segEnd - cp1) / 3; + if (segEnd != segStart || segEnd != cp1) { + subpathHasLength = true; + builder->BezierTo(tcp1, tcp2, segEnd); + } + break; + + case PATHSEG_CURVETO_QUADRATIC_REL: + cp1 = segStart + Point(mData[i], mData[i+1]); + // Convert quadratic curve to cubic curve: + tcp1 = segStart + (cp1 - segStart) * 2 / 3; + segEnd = segStart + Point(mData[i+2], mData[i+3]); // set before setting tcp2! + tcp2 = cp1 + (segEnd - cp1) / 3; + if (segEnd != segStart || segEnd != cp1) { + subpathHasLength = true; + builder->BezierTo(tcp1, tcp2, segEnd); + } + break; + + case PATHSEG_ARC_ABS: + case PATHSEG_ARC_REL: + { + Point radii(mData[i], mData[i+1]); + segEnd = Point(mData[i+5], mData[i+6]); + if (segType == PATHSEG_ARC_REL) { + segEnd += segStart; + } + if (segEnd != segStart) { + subpathHasLength = true; + if (radii.x == 0.0f || radii.y == 0.0f) { + builder->LineTo(segEnd); + } else { + nsSVGArcConverter converter(segStart, segEnd, radii, mData[i+2], + mData[i+3] != 0, mData[i+4] != 0); + while (converter.GetNextSegment(&cp1, &cp2, &segEnd)) { + builder->BezierTo(cp1, cp2, segEnd); + } + } + } + break; + } + + case PATHSEG_LINETO_HORIZONTAL_ABS: + segEnd = Point(mData[i], segStart.y); + if (segEnd != segStart) { + subpathHasLength = true; + builder->LineTo(segEnd); + } + break; + + case PATHSEG_LINETO_HORIZONTAL_REL: + segEnd = segStart + Point(mData[i], 0.0f); + if (segEnd != segStart) { + subpathHasLength = true; + builder->LineTo(segEnd); + } + break; + + case PATHSEG_LINETO_VERTICAL_ABS: + segEnd = Point(segStart.x, mData[i]); + if (segEnd != segStart) { + subpathHasLength = true; + builder->LineTo(segEnd); + } + break; + + case PATHSEG_LINETO_VERTICAL_REL: + segEnd = segStart + Point(0.0f, mData[i]); + if (segEnd != segStart) { + subpathHasLength = true; + builder->LineTo(segEnd); + } + break; + + case PATHSEG_CURVETO_CUBIC_SMOOTH_ABS: + cp1 = SVGPathSegUtils::IsCubicType(prevSegType) ? segStart * 2 - cp2 : segStart; + cp2 = Point(mData[i], mData[i+1]); + segEnd = Point(mData[i+2], mData[i+3]); + if (segEnd != segStart || segEnd != cp1 || segEnd != cp2) { + subpathHasLength = true; + builder->BezierTo(cp1, cp2, segEnd); + } + break; + + case PATHSEG_CURVETO_CUBIC_SMOOTH_REL: + cp1 = SVGPathSegUtils::IsCubicType(prevSegType) ? segStart * 2 - cp2 : segStart; + cp2 = segStart + Point(mData[i], mData[i+1]); + segEnd = segStart + Point(mData[i+2], mData[i+3]); + if (segEnd != segStart || segEnd != cp1 || segEnd != cp2) { + subpathHasLength = true; + builder->BezierTo(cp1, cp2, segEnd); + } + break; + + case PATHSEG_CURVETO_QUADRATIC_SMOOTH_ABS: + cp1 = SVGPathSegUtils::IsQuadraticType(prevSegType) ? segStart * 2 - cp1 : segStart; + // Convert quadratic curve to cubic curve: + tcp1 = segStart + (cp1 - segStart) * 2 / 3; + segEnd = Point(mData[i], mData[i+1]); // set before setting tcp2! + tcp2 = cp1 + (segEnd - cp1) / 3; + if (segEnd != segStart || segEnd != cp1) { + subpathHasLength = true; + builder->BezierTo(tcp1, tcp2, segEnd); + } + break; + + case PATHSEG_CURVETO_QUADRATIC_SMOOTH_REL: + cp1 = SVGPathSegUtils::IsQuadraticType(prevSegType) ? segStart * 2 - cp1 : segStart; + // Convert quadratic curve to cubic curve: + tcp1 = segStart + (cp1 - segStart) * 2 / 3; + segEnd = segStart + Point(mData[i], mData[i+1]); // changed before setting tcp2! + tcp2 = cp1 + (segEnd - cp1) / 3; + if (segEnd != segStart || segEnd != cp1) { + subpathHasLength = true; + builder->BezierTo(tcp1, tcp2, segEnd); + } + break; + + default: + NS_NOTREACHED("Bad path segment type"); + return nullptr; // according to spec we'd use everything up to the bad seg anyway + } + + subpathContainsNonMoveTo = segType != PATHSEG_MOVETO_ABS && + segType != PATHSEG_MOVETO_REL; + i += argCount; + prevSegType = segType; + segStart = segEnd; + } + + MOZ_ASSERT(i == mData.Length(), "Very, very bad - mData corrupt"); + MOZ_ASSERT(prevSegType == segType, + "prevSegType should be left at the final segType"); + + MAYBE_APPROXIMATE_ZERO_LENGTH_SUBPATH_SQUARE_CAPS_TO_DT; + + return builder->Finish(); +} + +already_AddRefed<Path> +SVGPathData::BuildPathForMeasuring() const +{ + // Since the path that we return will not be used for painting it doesn't + // matter what we pass to CreatePathBuilder as aFillRule. Hawever, we do want + // to pass something other than NS_STYLE_STROKE_LINECAP_SQUARE as + // aStrokeLineCap to avoid the insertion of extra little lines (by + // ApproximateZeroLengthSubpathSquareCaps), in which case the value that we + // pass as aStrokeWidth doesn't matter (since it's only used to determine the + // length of those extra little lines). + + RefPtr<DrawTarget> drawTarget = + gfxPlatform::GetPlatform()->ScreenReferenceDrawTarget(); + RefPtr<PathBuilder> builder = + drawTarget->CreatePathBuilder(FillRule::FILL_WINDING); + return BuildPath(builder, NS_STYLE_STROKE_LINECAP_BUTT, 0); +} + +static double +AngleOfVector(const Point& aVector) +{ + // C99 says about atan2 "A domain error may occur if both arguments are + // zero" and "On a domain error, the function returns an implementation- + // defined value". In the case of atan2 the implementation-defined value + // seems to commonly be zero, but it could just as easily be a NaN value. + // We specifically want zero in this case, hence the check: + + return (aVector != Point(0.0, 0.0)) ? atan2(aVector.y, aVector.x) : 0.0; +} + +static float +AngleOfVector(const Point& cp1, const Point& cp2) +{ + return static_cast<float>(AngleOfVector(cp1 - cp2)); +} + +void +SVGPathData::GetMarkerPositioningData(nsTArray<nsSVGMark> *aMarks) const +{ + // This code should assume that ANY type of segment can appear at ANY index. + // It should also assume that segments such as M and Z can appear in weird + // places, and repeat multiple times consecutively. + + // info on current [sub]path (reset every M command): + Point pathStart(0.0, 0.0); + float pathStartAngle = 0.0f; + + // info on previous segment: + uint16_t prevSegType = PATHSEG_UNKNOWN; + Point prevSegEnd(0.0, 0.0); + float prevSegEndAngle = 0.0f; + Point prevCP; // if prev seg was a bezier, this was its last control point + + uint32_t i = 0; + while (i < mData.Length()) { + + // info on current segment: + uint16_t segType = + SVGPathSegUtils::DecodeType(mData[i++]); // advances i to args + Point& segStart = prevSegEnd; + Point segEnd; + float segStartAngle, segEndAngle; + + switch (segType) // to find segStartAngle, segEnd and segEndAngle + { + case PATHSEG_CLOSEPATH: + segEnd = pathStart; + segStartAngle = segEndAngle = AngleOfVector(segEnd, segStart); + break; + + case PATHSEG_MOVETO_ABS: + case PATHSEG_MOVETO_REL: + if (segType == PATHSEG_MOVETO_ABS) { + segEnd = Point(mData[i], mData[i+1]); + } else { + segEnd = segStart + Point(mData[i], mData[i+1]); + } + pathStart = segEnd; + // If authors are going to specify multiple consecutive moveto commands + // with markers, me might as well make the angle do something useful: + segStartAngle = segEndAngle = AngleOfVector(segEnd, segStart); + i += 2; + break; + + case PATHSEG_LINETO_ABS: + case PATHSEG_LINETO_REL: + if (segType == PATHSEG_LINETO_ABS) { + segEnd = Point(mData[i], mData[i+1]); + } else { + segEnd = segStart + Point(mData[i], mData[i+1]); + } + segStartAngle = segEndAngle = AngleOfVector(segEnd, segStart); + i += 2; + break; + + case PATHSEG_CURVETO_CUBIC_ABS: + case PATHSEG_CURVETO_CUBIC_REL: + { + Point cp1, cp2; // control points + if (segType == PATHSEG_CURVETO_CUBIC_ABS) { + cp1 = Point(mData[i], mData[i+1]); + cp2 = Point(mData[i+2], mData[i+3]); + segEnd = Point(mData[i+4], mData[i+5]); + } else { + cp1 = segStart + Point(mData[i], mData[i+1]); + cp2 = segStart + Point(mData[i+2], mData[i+3]); + segEnd = segStart + Point(mData[i+4], mData[i+5]); + } + prevCP = cp2; + segStartAngle = + AngleOfVector(cp1 == segStart ? (cp1 == cp2 ? segEnd : cp2) : cp1, segStart); + segEndAngle = + AngleOfVector(segEnd, cp2 == segEnd ? (cp1 == cp2 ? segStart : cp1) : cp2); + i += 6; + break; + } + + case PATHSEG_CURVETO_QUADRATIC_ABS: + case PATHSEG_CURVETO_QUADRATIC_REL: + { + Point cp1; // control point + if (segType == PATHSEG_CURVETO_QUADRATIC_ABS) { + cp1 = Point(mData[i], mData[i+1]); + segEnd = Point(mData[i+2], mData[i+3]); + } else { + cp1 = segStart + Point(mData[i], mData[i+1]); + segEnd = segStart + Point(mData[i+2], mData[i+3]); + } + prevCP = cp1; + segStartAngle = AngleOfVector(cp1 == segStart ? segEnd : cp1, segStart); + segEndAngle = AngleOfVector(segEnd, cp1 == segEnd ? segStart : cp1); + i += 4; + break; + } + + case PATHSEG_ARC_ABS: + case PATHSEG_ARC_REL: + { + double rx = mData[i]; + double ry = mData[i+1]; + double angle = mData[i+2]; + bool largeArcFlag = mData[i+3] != 0.0f; + bool sweepFlag = mData[i+4] != 0.0f; + if (segType == PATHSEG_ARC_ABS) { + segEnd = Point(mData[i+5], mData[i+6]); + } else { + segEnd = segStart + Point(mData[i+5], mData[i+6]); + } + + // See section F.6 of SVG 1.1 for details on what we're doing here: + // http://www.w3.org/TR/SVG11/implnote.html#ArcImplementationNotes + + if (segStart == segEnd) { + // F.6.2 says "If the endpoints (x1, y1) and (x2, y2) are identical, + // then this is equivalent to omitting the elliptical arc segment + // entirely." We take that very literally here, not adding a mark, and + // not even setting any of the 'prev' variables so that it's as if this + // arc had never existed; note the difference this will make e.g. if + // the arc is proceeded by a bezier curve and followed by a "smooth" + // bezier curve of the same degree! + i += 7; + continue; + } + + // Below we have funny interleaving of F.6.6 (Correction of out-of-range + // radii) and F.6.5 (Conversion from endpoint to center parameterization) + // which is designed to avoid some unnecessary calculations. + + if (rx == 0.0 || ry == 0.0) { + // F.6.6 step 1 - straight line or coincidental points + segStartAngle = segEndAngle = AngleOfVector(segEnd, segStart); + i += 7; + break; + } + rx = fabs(rx); // F.6.6.1 + ry = fabs(ry); + + // F.6.5.1: + angle = angle * M_PI/180.0; + double x1p = cos(angle) * (segStart.x - segEnd.x) / 2.0 + + sin(angle) * (segStart.y - segEnd.y) / 2.0; + double y1p = -sin(angle) * (segStart.x - segEnd.x) / 2.0 + + cos(angle) * (segStart.y - segEnd.y) / 2.0; + + // This is the root in F.6.5.2 and the numerator under that root: + double root; + double numerator = rx*rx*ry*ry - rx*rx*y1p*y1p - ry*ry*x1p*x1p; + + if (numerator >= 0.0) { + root = sqrt(numerator/(rx*rx*y1p*y1p + ry*ry*x1p*x1p)); + if (largeArcFlag == sweepFlag) + root = -root; + } else { + // F.6.6 step 3 - |numerator < 0.0|. This is equivalent to the result + // of F.6.6.2 (lamedh) being greater than one. What we have here is + // ellipse radii that are too small for the ellipse to reach between + // segStart and segEnd. We scale the radii up uniformly so that the + // ellipse is just big enough to fit (i.e. to the point where there is + // exactly one solution). + + double lamedh = 1.0 - numerator/(rx*rx*ry*ry); // equiv to eqn F.6.6.2 + double s = sqrt(lamedh); + rx *= s; // F.6.6.3 + ry *= s; + root = 0.0; + } + + double cxp = root * rx * y1p / ry; // F.6.5.2 + double cyp = -root * ry * x1p / rx; + + double theta, delta; + theta = AngleOfVector(Point((x1p-cxp)/rx, (y1p-cyp)/ry)); // F.6.5.5 + delta = AngleOfVector(Point((-x1p-cxp)/rx, (-y1p-cyp)/ry)) - // F.6.5.6 + theta; + if (!sweepFlag && delta > 0) + delta -= 2.0 * M_PI; + else if (sweepFlag && delta < 0) + delta += 2.0 * M_PI; + + double tx1, ty1, tx2, ty2; + tx1 = -cos(angle)*rx*sin(theta) - sin(angle)*ry*cos(theta); + ty1 = -sin(angle)*rx*sin(theta) + cos(angle)*ry*cos(theta); + tx2 = -cos(angle)*rx*sin(theta+delta) - sin(angle)*ry*cos(theta+delta); + ty2 = -sin(angle)*rx*sin(theta+delta) + cos(angle)*ry*cos(theta+delta); + + if (delta < 0.0f) { + tx1 = -tx1; + ty1 = -ty1; + tx2 = -tx2; + ty2 = -ty2; + } + + segStartAngle = static_cast<float>(atan2(ty1, tx1)); + segEndAngle = static_cast<float>(atan2(ty2, tx2)); + i += 7; + break; + } + + case PATHSEG_LINETO_HORIZONTAL_ABS: + case PATHSEG_LINETO_HORIZONTAL_REL: + if (segType == PATHSEG_LINETO_HORIZONTAL_ABS) { + segEnd = Point(mData[i++], segStart.y); + } else { + segEnd = segStart + Point(mData[i++], 0.0f); + } + segStartAngle = segEndAngle = AngleOfVector(segEnd, segStart); + break; + + case PATHSEG_LINETO_VERTICAL_ABS: + case PATHSEG_LINETO_VERTICAL_REL: + if (segType == PATHSEG_LINETO_VERTICAL_ABS) { + segEnd = Point(segStart.x, mData[i++]); + } else { + segEnd = segStart + Point(0.0f, mData[i++]); + } + segStartAngle = segEndAngle = AngleOfVector(segEnd, segStart); + break; + + case PATHSEG_CURVETO_CUBIC_SMOOTH_ABS: + case PATHSEG_CURVETO_CUBIC_SMOOTH_REL: + { + Point cp1 = SVGPathSegUtils::IsCubicType(prevSegType) ? + segStart * 2 - prevCP : segStart; + Point cp2; + if (segType == PATHSEG_CURVETO_CUBIC_SMOOTH_ABS) { + cp2 = Point(mData[i], mData[i+1]); + segEnd = Point(mData[i+2], mData[i+3]); + } else { + cp2 = segStart + Point(mData[i], mData[i+1]); + segEnd = segStart + Point(mData[i+2], mData[i+3]); + } + prevCP = cp2; + segStartAngle = + AngleOfVector(cp1 == segStart ? (cp1 == cp2 ? segEnd : cp2) : cp1, segStart); + segEndAngle = + AngleOfVector(segEnd, cp2 == segEnd ? (cp1 == cp2 ? segStart : cp1) : cp2); + i += 4; + break; + } + + case PATHSEG_CURVETO_QUADRATIC_SMOOTH_ABS: + case PATHSEG_CURVETO_QUADRATIC_SMOOTH_REL: + { + Point cp1 = SVGPathSegUtils::IsQuadraticType(prevSegType) ? + segStart * 2 - prevCP : segStart; + if (segType == PATHSEG_CURVETO_QUADRATIC_SMOOTH_ABS) { + segEnd = Point(mData[i], mData[i+1]); + } else { + segEnd = segStart + Point(mData[i], mData[i+1]); + } + prevCP = cp1; + segStartAngle = AngleOfVector(cp1 == segStart ? segEnd : cp1, segStart); + segEndAngle = AngleOfVector(segEnd, cp1 == segEnd ? segStart : cp1); + i += 2; + break; + } + + default: + // Leave any existing marks in aMarks so we have a visual indication of + // when things went wrong. + MOZ_ASSERT(false, "Unknown segment type - path corruption?"); + return; + } + + // Set the angle of the mark at the start of this segment: + if (aMarks->Length()) { + nsSVGMark &mark = aMarks->LastElement(); + if (!IsMoveto(segType) && IsMoveto(prevSegType)) { + // start of new subpath + pathStartAngle = mark.angle = segStartAngle; + } else if (IsMoveto(segType) && !IsMoveto(prevSegType)) { + // end of a subpath + if (prevSegType != PATHSEG_CLOSEPATH) + mark.angle = prevSegEndAngle; + } else { + if (!(segType == PATHSEG_CLOSEPATH && + prevSegType == PATHSEG_CLOSEPATH)) + mark.angle = SVGContentUtils::AngleBisect(prevSegEndAngle, segStartAngle); + } + } + + // Add the mark at the end of this segment, and set its position: + if (!aMarks->AppendElement(nsSVGMark(static_cast<float>(segEnd.x), + static_cast<float>(segEnd.y), + 0.0f, + nsSVGMark::eMid))) { + aMarks->Clear(); // OOM, so try to free some + return; + } + + if (segType == PATHSEG_CLOSEPATH && + prevSegType != PATHSEG_CLOSEPATH) { + aMarks->LastElement().angle = + //aMarks->ElementAt(pathStartIndex).angle = + SVGContentUtils::AngleBisect(segEndAngle, pathStartAngle); + } + + prevSegType = segType; + prevSegEnd = segEnd; + prevSegEndAngle = segEndAngle; + } + + MOZ_ASSERT(i == mData.Length(), "Very, very bad - mData corrupt"); + + if (aMarks->Length()) { + if (prevSegType != PATHSEG_CLOSEPATH) { + aMarks->LastElement().angle = prevSegEndAngle; + } + aMarks->LastElement().type = nsSVGMark::eEnd; + aMarks->ElementAt(0).type = nsSVGMark::eStart; + } +} + +size_t +SVGPathData::SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const +{ + return mData.ShallowSizeOfExcludingThis(aMallocSizeOf); +} + +size_t +SVGPathData::SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const +{ + return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf); +} + diff --git a/dom/svg/SVGPathData.h b/dom/svg/SVGPathData.h new file mode 100644 index 0000000000..60dffc2533 --- /dev/null +++ b/dom/svg/SVGPathData.h @@ -0,0 +1,297 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef MOZILLA_SVGPATHDATA_H__ +#define MOZILLA_SVGPATHDATA_H__ + +#include "nsCOMPtr.h" +#include "nsDebug.h" +#include "nsIContent.h" +#include "nsINode.h" +#include "nsIWeakReferenceUtils.h" +#include "mozilla/gfx/2D.h" +#include "mozilla/gfx/Types.h" +#include "mozilla/MemoryReporting.h" +#include "mozilla/RefPtr.h" +#include "nsSVGElement.h" +#include "nsTArray.h" + +#include <string.h> + +class nsSVGPathDataParser; // IWYU pragma: keep + +struct nsSVGMark; + +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 DOMSVGPathSegList. + * + * This class is not called |class SVGPathSegList| for one very good reason; + * this class does not provide a list of "SVGPathSeg" items, it provides an + * array of floats into which path segments are encoded. See the paragraphs + * that follow for why. Note that the Length() method returns the number of + * floats in our array, not the number of encoded segments, and the index + * operator indexes floats in the array, not segments. If this class were + * called SVGPathSegList the names of these methods would be very misleading. + * + * The reason this class is designed in this way is because there are many + * different types of path segment, each taking a different numbers of + * arguments. We want to store the segments in an nsTArray to avoid individual + * allocations for each item, but the different size of segments means we can't + * have one single segment type for the nsTArray (not without using a space + * wasteful union or something similar). Since the internal code does not need + * to index into the list (the DOM wrapper does, but it handles that itself) + * the obvious solution is to have the items in this class take up variable + * width and have the internal code iterate over these lists rather than index + * into them. + * + * Implementing indexing to segments with O(1) performance would require us to + * allocate and maintain a separate segment index table (keeping that table in + * sync when items are inserted or removed from the list). So long as the + * internal code doesn't require indexing to segments, we can avoid that + * overhead and additional complexity. + * + * Segment encoding: the first float in the encoding of a segment contains the + * segment's type. The segment's type is encoded to/decoded from this float + * using the static methods SVGPathSegUtils::EncodeType(uint32_t)/ + * SVGPathSegUtils::DecodeType(float). If the path segment type in question + * takes any arguments then these follow the first float, and are in the same + * order as they are given in a <path> element's 'd' attribute (NOT in the + * order of the createSVGPathSegXxx() methods' arguments from the SVG DOM + * interface SVGPathElement, which are different...grr). Consumers can use + * SVGPathSegUtils::ArgCountForType(type) to determine how many arguments + * there are (if any), and thus where the current encoded segment ends, and + * where the next segment (if any) begins. + */ +class SVGPathData +{ + friend class SVGAnimatedPathSegList; + friend class DOMSVGPathSegList; + friend class DOMSVGPathSeg; + friend class ::nsSVGPathDataParser; + // nsSVGPathDataParser will not keep wrappers in sync, so consumers + // are responsible for that! + + typedef gfx::DrawTarget DrawTarget; + typedef gfx::Path Path; + typedef gfx::PathBuilder PathBuilder; + typedef gfx::FillRule FillRule; + typedef gfx::Float Float; + typedef gfx::CapStyle CapStyle; + +public: + typedef const float* const_iterator; + + SVGPathData(){} + ~SVGPathData(){} + + // 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; + + bool IsEmpty() const { + return mData.IsEmpty(); + } + +#ifdef DEBUG + /** + * This method iterates over the encoded segment data and counts the number + * of segments we currently have. + */ + uint32_t CountItems() const; +#endif + + /** + * Returns the number of *floats* in the encoding array, and NOT the number + * of segments encoded in this object. (For that, see CountItems() above.) + */ + uint32_t Length() const { + return mData.Length(); + } + + const float& operator[](uint32_t aIndex) const { + return mData[aIndex]; + } + + // Used by nsSMILCompositor to check if the cached base val is out of date + bool operator==(const SVGPathData& rhs) const { + // We use memcmp so that we don't need to worry that the data encoded in + // the first float may have the same bit pattern as a NaN. + return mData.Length() == rhs.mData.Length() && + memcmp(mData.Elements(), rhs.mData.Elements(), + mData.Length() * sizeof(float)) == 0; + } + + bool SetCapacity(uint32_t aSize) { + return mData.SetCapacity(aSize, fallible); + } + + void Compact() { + mData.Compact(); + } + + + float GetPathLength() const; + + uint32_t GetPathSegAtLength(float aLength) const; + + void GetMarkerPositioningData(nsTArray<nsSVGMark> *aMarks) const; + + /** + * Returns true, except on OOM, in which case returns false. + */ + bool GetSegmentLengths(nsTArray<double> *aLengths) const; + + /** + * Returns true, except on OOM, in which case returns false. + */ + bool GetDistancesFromOriginToEndsOfVisibleSegments(FallibleTArray<double> *aArray) const; + + /** + * This returns a path without the extra little line segments that + * ApproximateZeroLengthSubpathSquareCaps can insert if we have square-caps. + * See the comment for that function for more info on that. + */ + already_AddRefed<Path> BuildPathForMeasuring() const; + + already_AddRefed<Path> BuildPath(PathBuilder* aBuilder, + uint8_t aCapStyle, + Float aStrokeWidth) const; + + const_iterator begin() const { return mData.Elements(); } + const_iterator end() const { return mData.Elements() + mData.Length(); } + + // memory reporting methods + size_t SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf) const; + size_t SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const; + + // 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 + // SVGAnimatedPathSegList and having that class act as an intermediary so it + // can take care of keeping DOM wrappers in sync. + +protected: + typedef float* iterator; + + /** + * 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 SVGPathData& rhs); + + float& operator[](uint32_t aIndex) { + return mData[aIndex]; + } + + /** + * This may fail (return false) on OOM if the internal capacity is being + * increased, in which case the list will be left unmodified. + */ + bool SetLength(uint32_t aLength) { + return mData.SetLength(aLength, fallible); + } + + nsresult SetValueFromString(const nsAString& aValue); + + void Clear() { + mData.Clear(); + } + + // Our DOM wrappers have direct access to our mData, so they directly + // manipulate it rather than us implementing: + // + // * InsertItem(uint32_t aDataIndex, uint32_t aType, const float *aArgs); + // * ReplaceItem(uint32_t aDataIndex, uint32_t aType, const float *aArgs); + // * RemoveItem(uint32_t aDataIndex); + // * bool AppendItem(uint32_t aType, const float *aArgs); + + nsresult AppendSeg(uint32_t aType, ...); // variable number of float args + + iterator begin() { return mData.Elements(); } + iterator end() { return mData.Elements() + mData.Length(); } + + FallibleTArray<float> mData; +}; + + +/** + * This SVGPathData subclass is for SVGPathSegListSMILType which needs to + * have write access to the lists it works with. + * + * Instances of this class do not have DOM wrappers that need to be kept in + * sync, so we can safely expose any protected base class methods required by + * the SMIL code. + */ +class SVGPathDataAndInfo final : public SVGPathData +{ +public: + explicit SVGPathDataAndInfo(nsSVGElement *aElement = nullptr) + : mElement(do_GetWeakReference(static_cast<nsINode*>(aElement))) + {} + + void SetElement(nsSVGElement *aElement) { + mElement = do_GetWeakReference(static_cast<nsINode*>(aElement)); + } + + nsSVGElement* Element() const { + nsCOMPtr<nsIContent> e = do_QueryReferent(mElement); + return static_cast<nsSVGElement*>(e.get()); + } + + nsresult CopyFrom(const SVGPathDataAndInfo& rhs) { + mElement = rhs.mElement; + return SVGPathData::CopyFrom(rhs); + } + + /** + * Returns true if this object is an "identity" value, from the perspective + * of SMIL. In other words, returns true until the initial value set up in + * SVGPathSegListSMILType::Init() has been changed with a SetElement() call. + */ + bool IsIdentity() const { + if (!mElement) { + MOZ_ASSERT(IsEmpty(), "target element propagation failure"); + return true; + } + return false; + } + + /** + * Exposed so that SVGPathData baseVals can be copied to + * SVGPathDataAndInfo objects. Note that callers should also call + * SetElement() when using this method! + */ + using SVGPathData::CopyFrom; + + // Exposed since SVGPathData objects can be modified. + using SVGPathData::iterator; + using SVGPathData::operator[]; + using SVGPathData::SetLength; + using SVGPathData::begin; + using SVGPathData::end; + +private: + // We must keep a weak 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 + // See also https://bugzilla.mozilla.org/show_bug.cgi?id=653497 + nsWeakPtr mElement; +}; + +} // namespace mozilla + +#endif // MOZILLA_SVGPATHDATA_H__ diff --git a/dom/svg/SVGPathElement.cpp b/dom/svg/SVGPathElement.cpp new file mode 100644 index 0000000000..85fce0072d --- /dev/null +++ b/dom/svg/SVGPathElement.cpp @@ -0,0 +1,398 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "mozilla/dom/SVGPathElement.h" + +#include <algorithm> + +#include "DOMSVGPathSeg.h" +#include "DOMSVGPathSegList.h" +#include "DOMSVGPoint.h" +#include "gfx2DGlue.h" +#include "gfxPlatform.h" +#include "mozilla/dom/SVGPathElementBinding.h" +#include "mozilla/gfx/2D.h" +#include "mozilla/RefPtr.h" +#include "nsCOMPtr.h" +#include "nsComputedDOMStyle.h" +#include "nsGkAtoms.h" +#include "nsStyleConsts.h" +#include "nsStyleStruct.h" +#include "SVGContentUtils.h" + +NS_IMPL_NS_NEW_NAMESPACED_SVG_ELEMENT(Path) + +using namespace mozilla::gfx; + +namespace mozilla { +namespace dom { + +JSObject* +SVGPathElement::WrapNode(JSContext *aCx, JS::Handle<JSObject*> aGivenProto) +{ + return SVGPathElementBinding::Wrap(aCx, this, aGivenProto); +} + +nsSVGElement::NumberInfo SVGPathElement::sNumberInfo = +{ &nsGkAtoms::pathLength, 0, false }; + +//---------------------------------------------------------------------- +// Implementation + +SVGPathElement::SVGPathElement(already_AddRefed<mozilla::dom::NodeInfo>& aNodeInfo) + : SVGPathElementBase(aNodeInfo) +{ +} + +//---------------------------------------------------------------------- +// memory reporting methods + +size_t +SVGPathElement::SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf) const +{ + return SVGPathElementBase::SizeOfExcludingThis(aMallocSizeOf) + + mD.SizeOfExcludingThis(aMallocSizeOf); +} + +//---------------------------------------------------------------------- +// nsIDOMNode methods + +NS_IMPL_ELEMENT_CLONE_WITH_INIT(SVGPathElement) + +already_AddRefed<SVGAnimatedNumber> +SVGPathElement::PathLength() +{ + return mPathLength.ToDOMAnimatedNumber(this); +} + +float +SVGPathElement::GetTotalLength() +{ + RefPtr<Path> flat = GetOrBuildPathForMeasuring(); + return flat ? flat->ComputeLength() : 0.f; +} + +already_AddRefed<nsISVGPoint> +SVGPathElement::GetPointAtLength(float distance, ErrorResult& rv) +{ + RefPtr<Path> path = GetOrBuildPathForMeasuring(); + if (!path) { + rv.Throw(NS_ERROR_FAILURE); + return nullptr; + } + + float totalLength = path->ComputeLength(); + if (mPathLength.IsExplicitlySet()) { + float pathLength = mPathLength.GetAnimValue(); + if (pathLength <= 0) { + rv.Throw(NS_ERROR_FAILURE); + return nullptr; + } + distance *= totalLength / pathLength; + } + distance = std::max(0.f, distance); + distance = std::min(totalLength, distance); + + nsCOMPtr<nsISVGPoint> point = + new DOMSVGPoint(path->ComputePointAtLength(distance)); + return point.forget(); +} + +uint32_t +SVGPathElement::GetPathSegAtLength(float distance) +{ + return mD.GetAnimValue().GetPathSegAtLength(distance); +} + +already_AddRefed<DOMSVGPathSegClosePath> +SVGPathElement::CreateSVGPathSegClosePath() +{ + RefPtr<DOMSVGPathSegClosePath> pathSeg = new DOMSVGPathSegClosePath(); + return pathSeg.forget(); +} + +already_AddRefed<DOMSVGPathSegMovetoAbs> +SVGPathElement::CreateSVGPathSegMovetoAbs(float x, float y) +{ + RefPtr<DOMSVGPathSegMovetoAbs> pathSeg = new DOMSVGPathSegMovetoAbs(x, y); + return pathSeg.forget(); +} + +already_AddRefed<DOMSVGPathSegMovetoRel> +SVGPathElement::CreateSVGPathSegMovetoRel(float x, float y) +{ + RefPtr<DOMSVGPathSegMovetoRel> pathSeg = new DOMSVGPathSegMovetoRel(x, y); + return pathSeg.forget(); +} + +already_AddRefed<DOMSVGPathSegLinetoAbs> +SVGPathElement::CreateSVGPathSegLinetoAbs(float x, float y) +{ + RefPtr<DOMSVGPathSegLinetoAbs> pathSeg = new DOMSVGPathSegLinetoAbs(x, y); + return pathSeg.forget(); +} + +already_AddRefed<DOMSVGPathSegLinetoRel> +SVGPathElement::CreateSVGPathSegLinetoRel(float x, float y) +{ + RefPtr<DOMSVGPathSegLinetoRel> pathSeg = new DOMSVGPathSegLinetoRel(x, y); + return pathSeg.forget(); +} + +already_AddRefed<DOMSVGPathSegCurvetoCubicAbs> +SVGPathElement::CreateSVGPathSegCurvetoCubicAbs(float x, float y, float x1, float y1, float x2, float y2) +{ + // Note that we swap from DOM API argument order to the argument order used + // in the <path> element's 'd' attribute (i.e. we put the arguments for the + // end point of the segment last instead of first). + RefPtr<DOMSVGPathSegCurvetoCubicAbs> pathSeg = + new DOMSVGPathSegCurvetoCubicAbs(x1, y1, x2, y2, x, y); + return pathSeg.forget(); +} + +already_AddRefed<DOMSVGPathSegCurvetoCubicRel> +SVGPathElement::CreateSVGPathSegCurvetoCubicRel(float x, float y, float x1, float y1, float x2, float y2) +{ + // See comment in CreateSVGPathSegCurvetoCubicAbs + RefPtr<DOMSVGPathSegCurvetoCubicRel> pathSeg = + new DOMSVGPathSegCurvetoCubicRel(x1, y1, x2, y2, x, y); + return pathSeg.forget(); +} + +already_AddRefed<DOMSVGPathSegCurvetoQuadraticAbs> +SVGPathElement::CreateSVGPathSegCurvetoQuadraticAbs(float x, float y, float x1, float y1) +{ + // See comment in CreateSVGPathSegCurvetoCubicAbs + RefPtr<DOMSVGPathSegCurvetoQuadraticAbs> pathSeg = + new DOMSVGPathSegCurvetoQuadraticAbs(x1, y1, x, y); + return pathSeg.forget(); +} + +already_AddRefed<DOMSVGPathSegCurvetoQuadraticRel> +SVGPathElement::CreateSVGPathSegCurvetoQuadraticRel(float x, float y, float x1, float y1) +{ + // See comment in CreateSVGPathSegCurvetoCubicAbs + RefPtr<DOMSVGPathSegCurvetoQuadraticRel> pathSeg = + new DOMSVGPathSegCurvetoQuadraticRel(x1, y1, x, y); + return pathSeg.forget(); +} + +already_AddRefed<DOMSVGPathSegArcAbs> +SVGPathElement::CreateSVGPathSegArcAbs(float x, float y, float r1, float r2, float angle, bool largeArcFlag, bool sweepFlag) +{ + // See comment in CreateSVGPathSegCurvetoCubicAbs + RefPtr<DOMSVGPathSegArcAbs> pathSeg = + new DOMSVGPathSegArcAbs(r1, r2, angle, largeArcFlag, sweepFlag, x, y); + return pathSeg.forget(); +} + +already_AddRefed<DOMSVGPathSegArcRel> +SVGPathElement::CreateSVGPathSegArcRel(float x, float y, float r1, float r2, float angle, bool largeArcFlag, bool sweepFlag) +{ + // See comment in CreateSVGPathSegCurvetoCubicAbs + RefPtr<DOMSVGPathSegArcRel> pathSeg = + new DOMSVGPathSegArcRel(r1, r2, angle, largeArcFlag, sweepFlag, x, y); + return pathSeg.forget(); +} + +already_AddRefed<DOMSVGPathSegLinetoHorizontalAbs> +SVGPathElement::CreateSVGPathSegLinetoHorizontalAbs(float x) +{ + RefPtr<DOMSVGPathSegLinetoHorizontalAbs> pathSeg = + new DOMSVGPathSegLinetoHorizontalAbs(x); + return pathSeg.forget(); +} + +already_AddRefed<DOMSVGPathSegLinetoHorizontalRel> +SVGPathElement::CreateSVGPathSegLinetoHorizontalRel(float x) +{ + RefPtr<DOMSVGPathSegLinetoHorizontalRel> pathSeg = + new DOMSVGPathSegLinetoHorizontalRel(x); + return pathSeg.forget(); +} + +already_AddRefed<DOMSVGPathSegLinetoVerticalAbs> +SVGPathElement::CreateSVGPathSegLinetoVerticalAbs(float y) +{ + RefPtr<DOMSVGPathSegLinetoVerticalAbs> pathSeg = + new DOMSVGPathSegLinetoVerticalAbs(y); + return pathSeg.forget(); +} + +already_AddRefed<DOMSVGPathSegLinetoVerticalRel> +SVGPathElement::CreateSVGPathSegLinetoVerticalRel(float y) +{ + RefPtr<DOMSVGPathSegLinetoVerticalRel> pathSeg = + new DOMSVGPathSegLinetoVerticalRel(y); + return pathSeg.forget(); +} + +already_AddRefed<DOMSVGPathSegCurvetoCubicSmoothAbs> +SVGPathElement::CreateSVGPathSegCurvetoCubicSmoothAbs(float x, float y, float x2, float y2) +{ + // See comment in CreateSVGPathSegCurvetoCubicAbs + RefPtr<DOMSVGPathSegCurvetoCubicSmoothAbs> pathSeg = + new DOMSVGPathSegCurvetoCubicSmoothAbs(x2, y2, x, y); + return pathSeg.forget(); +} + +already_AddRefed<DOMSVGPathSegCurvetoCubicSmoothRel> +SVGPathElement::CreateSVGPathSegCurvetoCubicSmoothRel(float x, float y, float x2, float y2) +{ + // See comment in CreateSVGPathSegCurvetoCubicAbs + RefPtr<DOMSVGPathSegCurvetoCubicSmoothRel> pathSeg = + new DOMSVGPathSegCurvetoCubicSmoothRel(x2, y2, x, y); + return pathSeg.forget(); +} + +already_AddRefed<DOMSVGPathSegCurvetoQuadraticSmoothAbs> +SVGPathElement::CreateSVGPathSegCurvetoQuadraticSmoothAbs(float x, float y) +{ + RefPtr<DOMSVGPathSegCurvetoQuadraticSmoothAbs> pathSeg = + new DOMSVGPathSegCurvetoQuadraticSmoothAbs(x, y); + return pathSeg.forget(); +} + +already_AddRefed<DOMSVGPathSegCurvetoQuadraticSmoothRel> +SVGPathElement::CreateSVGPathSegCurvetoQuadraticSmoothRel(float x, float y) +{ + RefPtr<DOMSVGPathSegCurvetoQuadraticSmoothRel> pathSeg = + new DOMSVGPathSegCurvetoQuadraticSmoothRel(x, y); + return pathSeg.forget(); +} + +already_AddRefed<DOMSVGPathSegList> +SVGPathElement::PathSegList() +{ + return DOMSVGPathSegList::GetDOMWrapper(mD.GetBaseValKey(), this, false); +} + +already_AddRefed<DOMSVGPathSegList> +SVGPathElement::AnimatedPathSegList() +{ + return DOMSVGPathSegList::GetDOMWrapper(mD.GetAnimValKey(), this, true); +} + +//---------------------------------------------------------------------- +// nsSVGElement methods + +/* virtual */ bool +SVGPathElement::HasValidDimensions() const +{ + return !mD.GetAnimValue().IsEmpty(); +} + +nsSVGElement::NumberAttributesInfo +SVGPathElement::GetNumberInfo() +{ + return NumberAttributesInfo(&mPathLength, &sNumberInfo, 1); +} + +//---------------------------------------------------------------------- +// nsIContent methods + +NS_IMETHODIMP_(bool) +SVGPathElement::IsAttributeMapped(const nsIAtom* name) const +{ + static const MappedAttributeEntry* const map[] = { + sMarkersMap + }; + + return FindAttributeDependence(name, map) || + SVGPathElementBase::IsAttributeMapped(name); +} + +already_AddRefed<Path> +SVGPathElement::GetOrBuildPathForMeasuring() +{ + return mD.GetAnimValue().BuildPathForMeasuring(); +} + +//---------------------------------------------------------------------- +// nsSVGPathGeometryElement methods + +bool +SVGPathElement::AttributeDefinesGeometry(const nsIAtom *aName) +{ + return aName == nsGkAtoms::d || + aName == nsGkAtoms::pathLength; +} + +bool +SVGPathElement::IsMarkable() +{ + return true; +} + +void +SVGPathElement::GetMarkPoints(nsTArray<nsSVGMark> *aMarks) +{ + mD.GetAnimValue().GetMarkerPositioningData(aMarks); +} + +float +SVGPathElement::GetPathLengthScale(PathLengthScaleForType aFor) +{ + MOZ_ASSERT(aFor == eForTextPath || aFor == eForStroking, + "Unknown enum"); + if (mPathLength.IsExplicitlySet()) { + float authorsPathLengthEstimate = mPathLength.GetAnimValue(); + if (authorsPathLengthEstimate > 0) { + RefPtr<Path> path = GetOrBuildPathForMeasuring(); + if (!path) { + // The path is empty or invalid so its length must be zero and + // we know that 0 / authorsPathLengthEstimate = 0. + return 0.0; + } + if (aFor == eForTextPath) { + // For textPath, a transform on the referenced path affects the + // textPath layout, so when calculating the actual path length + // we need to take that into account. + gfxMatrix matrix = PrependLocalTransformsTo(gfxMatrix()); + if (!matrix.IsIdentity()) { + RefPtr<PathBuilder> builder = + path->TransformedCopyToBuilder(ToMatrix(matrix)); + path = builder->Finish(); + } + } + return path->ComputeLength() / authorsPathLengthEstimate; + } + } + return 1.0; +} + +already_AddRefed<Path> +SVGPathElement::BuildPath(PathBuilder* aBuilder) +{ + // The Moz2D PathBuilder that our SVGPathData will be using only cares about + // the fill rule. However, in order to fulfill the requirements of the SVG + // spec regarding zero length sub-paths when square line caps are in use, + // SVGPathData needs to know our stroke-linecap style and, if "square", then + // also our stroke width. See the comment for + // ApproximateZeroLengthSubpathSquareCaps for more info. + + uint8_t strokeLineCap = NS_STYLE_STROKE_LINECAP_BUTT; + Float strokeWidth = 0; + + RefPtr<nsStyleContext> styleContext = + nsComputedDOMStyle::GetStyleContextForElementNoFlush(this, nullptr, nullptr); + if (styleContext) { + const nsStyleSVG* style = styleContext->StyleSVG(); + // Note: the path that we return may be used for hit-testing, and SVG + // exposes hit-testing of strokes that are not actually painted. For that + // reason we do not check for eStyleSVGPaintType_None or check the stroke + // opacity here. + if (style->mStrokeLinecap != NS_STYLE_STROKE_LINECAP_BUTT) { + strokeLineCap = style->mStrokeLinecap; + strokeWidth = SVGContentUtils::GetStrokeWidth(this, styleContext, nullptr); + } + } + + return mD.GetAnimValue().BuildPath(aBuilder, strokeLineCap, strokeWidth); +} + +} // namespace dom +} // namespace mozilla diff --git a/dom/svg/SVGPathElement.h b/dom/svg/SVGPathElement.h new file mode 100644 index 0000000000..20af44905f --- /dev/null +++ b/dom/svg/SVGPathElement.h @@ -0,0 +1,136 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef mozilla_dom_SVGPathElement_h +#define mozilla_dom_SVGPathElement_h + +#include "mozilla/gfx/2D.h" +#include "mozilla/RefPtr.h" +#include "nsSVGNumber2.h" +#include "nsSVGPathGeometryElement.h" +#include "SVGAnimatedPathSegList.h" +#include "DOMSVGPathSeg.h" + +nsresult NS_NewSVGPathElement(nsIContent **aResult, + already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo); + +typedef nsSVGPathGeometryElement SVGPathElementBase; + +namespace mozilla { + +class nsISVGPoint; + +namespace dom { + +class SVGPathElement final : public SVGPathElementBase +{ +friend class nsSVGPathFrame; + + typedef mozilla::gfx::Path Path; + +protected: + friend nsresult (::NS_NewSVGPathElement(nsIContent **aResult, + already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo)); + virtual JSObject* WrapNode(JSContext *cx, JS::Handle<JSObject*> aGivenProto) override; + explicit SVGPathElement(already_AddRefed<mozilla::dom::NodeInfo>& aNodeInfo); + +public: + // DOM memory reporter participant + NS_DECL_SIZEOF_EXCLUDING_THIS + + // nsIContent interface + NS_IMETHOD_(bool) IsAttributeMapped(const nsIAtom* name) const override; + + // nsSVGSVGElement methods: + virtual bool HasValidDimensions() const override; + + // nsSVGPathGeometryElement methods: + virtual bool AttributeDefinesGeometry(const nsIAtom *aName) override; + virtual bool IsMarkable() override; + virtual void GetMarkPoints(nsTArray<nsSVGMark> *aMarks) override; + virtual already_AddRefed<Path> BuildPath(PathBuilder* aBuilder) override; + + /** + * This returns a path without the extra little line segments that + * ApproximateZeroLengthSubpathSquareCaps can insert if we have square-caps. + * See the comment for that function for more info on that. + */ + virtual already_AddRefed<Path> GetOrBuildPathForMeasuring() override; + + // nsIContent interface + virtual nsresult Clone(mozilla::dom::NodeInfo *aNodeInfo, nsINode **aResult) const override; + + virtual SVGAnimatedPathSegList* GetAnimPathSegList() override { + return &mD; + } + + virtual nsIAtom* GetPathDataAttrName() const override { + return nsGkAtoms::d; + } + + enum PathLengthScaleForType { + eForTextPath, + eForStroking + }; + + /** + * Gets the ratio of the actual path length to the content author's estimated + * length (as provided by the <path> element's 'pathLength' attribute). This + * is used to scale stroke dashing, and to scale offsets along a textPath. + */ + float GetPathLengthScale(PathLengthScaleForType aFor); + + // WebIDL + already_AddRefed<SVGAnimatedNumber> PathLength(); + float GetTotalLength(); + already_AddRefed<nsISVGPoint> GetPointAtLength(float distance, ErrorResult& rv); + uint32_t GetPathSegAtLength(float distance); + already_AddRefed<DOMSVGPathSegClosePath> CreateSVGPathSegClosePath(); + already_AddRefed<DOMSVGPathSegMovetoAbs> CreateSVGPathSegMovetoAbs(float x, float y); + already_AddRefed<DOMSVGPathSegMovetoRel> CreateSVGPathSegMovetoRel(float x, float y); + already_AddRefed<DOMSVGPathSegLinetoAbs> CreateSVGPathSegLinetoAbs(float x, float y); + already_AddRefed<DOMSVGPathSegLinetoRel> CreateSVGPathSegLinetoRel(float x, float y); + already_AddRefed<DOMSVGPathSegCurvetoCubicAbs> + CreateSVGPathSegCurvetoCubicAbs(float x, float y, float x1, float y1, float x2, float y2); + already_AddRefed<DOMSVGPathSegCurvetoCubicRel> + CreateSVGPathSegCurvetoCubicRel(float x, float y, float x1, float y1, float x2, float y2); + already_AddRefed<DOMSVGPathSegCurvetoQuadraticAbs> + CreateSVGPathSegCurvetoQuadraticAbs(float x, float y, float x1, float y1); + already_AddRefed<DOMSVGPathSegCurvetoQuadraticRel> + CreateSVGPathSegCurvetoQuadraticRel(float x, float y, float x1, float y1); + already_AddRefed<DOMSVGPathSegArcAbs> + CreateSVGPathSegArcAbs(float x, float y, float r1, float r2, float angle, bool largeArcFlag, bool sweepFlag); + already_AddRefed<DOMSVGPathSegArcRel> + CreateSVGPathSegArcRel(float x, float y, float r1, float r2, float angle, bool largeArcFlag, bool sweepFlag); + already_AddRefed<DOMSVGPathSegLinetoHorizontalAbs> CreateSVGPathSegLinetoHorizontalAbs(float x); + already_AddRefed<DOMSVGPathSegLinetoHorizontalRel> CreateSVGPathSegLinetoHorizontalRel(float x); + already_AddRefed<DOMSVGPathSegLinetoVerticalAbs> CreateSVGPathSegLinetoVerticalAbs(float y); + already_AddRefed<DOMSVGPathSegLinetoVerticalRel> CreateSVGPathSegLinetoVerticalRel(float y); + already_AddRefed<DOMSVGPathSegCurvetoCubicSmoothAbs> + CreateSVGPathSegCurvetoCubicSmoothAbs(float x, float y, float x2, float y2); + already_AddRefed<DOMSVGPathSegCurvetoCubicSmoothRel> + CreateSVGPathSegCurvetoCubicSmoothRel(float x, float y, float x2, float y2); + already_AddRefed<DOMSVGPathSegCurvetoQuadraticSmoothAbs> + CreateSVGPathSegCurvetoQuadraticSmoothAbs(float x, float y); + already_AddRefed<DOMSVGPathSegCurvetoQuadraticSmoothRel> + CreateSVGPathSegCurvetoQuadraticSmoothRel(float x, float y); + already_AddRefed<DOMSVGPathSegList> PathSegList(); + already_AddRefed<DOMSVGPathSegList> AnimatedPathSegList(); + +protected: + + // nsSVGElement method + virtual NumberAttributesInfo GetNumberInfo() override; + + SVGAnimatedPathSegList mD; + nsSVGNumber2 mPathLength; + static NumberInfo sNumberInfo; +}; + +} // namespace dom +} // namespace mozilla + +#endif // mozilla_dom_SVGPathElement_h diff --git a/dom/svg/SVGPathSegListSMILType.cpp b/dom/svg/SVGPathSegListSMILType.cpp new file mode 100644 index 0000000000..845781f9f0 --- /dev/null +++ b/dom/svg/SVGPathSegListSMILType.cpp @@ -0,0 +1,501 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "mozilla/DebugOnly.h" + +#include "SVGPathSegListSMILType.h" +#include "nsSMILValue.h" +#include "SVGPathSegUtils.h" +#include "SVGPathData.h" + +// Indices of boolean flags within 'arc' segment chunks in path-data arrays +// (where '0' would correspond to the index of the encoded segment type): +#define LARGE_ARC_FLAG_IDX 4 +#define SWEEP_FLAG_IDX 5 + +namespace mozilla { + +//---------------------------------------------------------------------- +// nsISMILType implementation + +void +SVGPathSegListSMILType::Init(nsSMILValue &aValue) const +{ + MOZ_ASSERT(aValue.IsNull(), "Unexpected value type"); + aValue.mU.mPtr = new SVGPathDataAndInfo(); + aValue.mType = this; +} + +void +SVGPathSegListSMILType::Destroy(nsSMILValue& aValue) const +{ + NS_PRECONDITION(aValue.mType == this, "Unexpected SMIL value type"); + delete static_cast<SVGPathDataAndInfo*>(aValue.mU.mPtr); + aValue.mU.mPtr = nullptr; + aValue.mType = nsSMILNullType::Singleton(); +} + +nsresult +SVGPathSegListSMILType::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 SVGPathDataAndInfo* src = + static_cast<const SVGPathDataAndInfo*>(aSrc.mU.mPtr); + SVGPathDataAndInfo* dest = + static_cast<SVGPathDataAndInfo*>(aDest.mU.mPtr); + + return dest->CopyFrom(*src); +} + +bool +SVGPathSegListSMILType::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 SVGPathDataAndInfo*>(aLeft.mU.mPtr) == + *static_cast<const SVGPathDataAndInfo*>(aRight.mU.mPtr); +} + +static bool +ArcFlagsDiffer(SVGPathDataAndInfo::const_iterator aPathData1, + SVGPathDataAndInfo::const_iterator aPathData2) +{ + MOZ_ASSERT + (SVGPathSegUtils::IsArcType(SVGPathSegUtils::DecodeType(aPathData1[0])), + "ArcFlagsDiffer called with non-arc segment"); + MOZ_ASSERT + (SVGPathSegUtils::IsArcType(SVGPathSegUtils::DecodeType(aPathData2[0])), + "ArcFlagsDiffer called with non-arc segment"); + + return aPathData1[LARGE_ARC_FLAG_IDX] != aPathData2[LARGE_ARC_FLAG_IDX] || + aPathData1[SWEEP_FLAG_IDX] != aPathData2[SWEEP_FLAG_IDX]; +} + +enum PathInterpolationResult { + eCannotInterpolate, + eRequiresConversion, + eCanInterpolate +}; + +static PathInterpolationResult +CanInterpolate(const SVGPathDataAndInfo& aStart, + const SVGPathDataAndInfo& aEnd) +{ + if (aStart.IsIdentity()) { + return eCanInterpolate; + } + + if (aStart.Length() != aEnd.Length()) { + return eCannotInterpolate; + } + + PathInterpolationResult result = eCanInterpolate; + + SVGPathDataAndInfo::const_iterator pStart = aStart.begin(); + SVGPathDataAndInfo::const_iterator pEnd = aEnd.begin(); + SVGPathDataAndInfo::const_iterator pStartDataEnd = aStart.end(); + SVGPathDataAndInfo::const_iterator pEndDataEnd = aEnd.end(); + + while (pStart < pStartDataEnd && pEnd < pEndDataEnd) { + uint32_t startType = SVGPathSegUtils::DecodeType(*pStart); + uint32_t endType = SVGPathSegUtils::DecodeType(*pEnd); + + if (SVGPathSegUtils::IsArcType(startType) && + SVGPathSegUtils::IsArcType(endType) && + ArcFlagsDiffer(pStart, pEnd)) { + return eCannotInterpolate; + } + + if (startType != endType) { + if (!SVGPathSegUtils::SameTypeModuloRelativeness(startType, endType)) { + return eCannotInterpolate; + } + + result = eRequiresConversion; + } + + pStart += 1 + SVGPathSegUtils::ArgCountForType(startType); + pEnd += 1 + SVGPathSegUtils::ArgCountForType(endType); + } + + MOZ_ASSERT(pStart <= pStartDataEnd && pEnd <= pEndDataEnd, + "Iterated past end of buffer! (Corrupt path data?)"); + + if (pStart != pStartDataEnd || pEnd != pEndDataEnd) { + return eCannotInterpolate; + } + + return result; +} + +enum RelativenessAdjustmentType { + eAbsoluteToRelative, + eRelativeToAbsolute +}; + +static inline void +AdjustSegmentForRelativeness(RelativenessAdjustmentType aAdjustmentType, + const SVGPathDataAndInfo::iterator& aSegmentToAdjust, + const SVGPathTraversalState& aState) +{ + if (aAdjustmentType == eAbsoluteToRelative) { + aSegmentToAdjust[0] -= aState.pos.x; + aSegmentToAdjust[1] -= aState.pos.y; + } else { + aSegmentToAdjust[0] += aState.pos.x; + aSegmentToAdjust[1] += aState.pos.y; + } +} + +/** + * Helper function for AddWeightedPathSegLists, to add multiples of two + * path-segments of the same type. + * + * NOTE: |aSeg1| is allowed to be nullptr, so we use |aSeg2| as the + * authoritative source of things like segment-type and boolean arc flags. + * + * @param aCoeff1 The coefficient to use on the first segment. + * @param aSeg1 An iterator pointing to the first segment. This can be + * null, which is treated as identity (zero). + * @param aCoeff2 The coefficient to use on the second segment. + * @param aSeg2 An iterator pointing to the second segment. + * @param [out] aResultSeg An iterator pointing to where we should write the + * result of this operation. + */ +static inline void +AddWeightedPathSegs(double aCoeff1, + SVGPathDataAndInfo::const_iterator& aSeg1, + double aCoeff2, + SVGPathDataAndInfo::const_iterator& aSeg2, + SVGPathDataAndInfo::iterator& aResultSeg) +{ + MOZ_ASSERT(aSeg2, "2nd segment must be non-null"); + MOZ_ASSERT(aResultSeg, "result segment must be non-null"); + + uint32_t segType = SVGPathSegUtils::DecodeType(aSeg2[0]); + MOZ_ASSERT(!aSeg1 || SVGPathSegUtils::DecodeType(*aSeg1) == segType, + "unexpected segment type"); + + // FIRST: Directly copy the arguments that don't make sense to add. + aResultSeg[0] = aSeg2[0]; // encoded segment type + + bool isArcType = SVGPathSegUtils::IsArcType(segType); + if (isArcType) { + // Copy boolean arc flags. + MOZ_ASSERT(!aSeg1 || !ArcFlagsDiffer(aSeg1, aSeg2), + "Expecting arc flags to match"); + aResultSeg[LARGE_ARC_FLAG_IDX] = aSeg2[LARGE_ARC_FLAG_IDX]; + aResultSeg[SWEEP_FLAG_IDX] = aSeg2[SWEEP_FLAG_IDX]; + } + + // SECOND: Add the arguments that are supposed to be added. + // (The 1's below are to account for segment type) + uint32_t numArgs = SVGPathSegUtils::ArgCountForType(segType); + for (uint32_t i = 1; i < 1 + numArgs; ++i) { + // Need to skip arc flags for arc-type segments. (already handled them) + if (!(isArcType && (i == LARGE_ARC_FLAG_IDX || i == SWEEP_FLAG_IDX))) { + aResultSeg[i] = (aSeg1 ? aCoeff1 * aSeg1[i] : 0.0) + aCoeff2 * aSeg2[i]; + } + } + + // FINALLY: Shift iterators forward. ("1+" is to include seg-type) + if (aSeg1) { + aSeg1 += 1 + numArgs; + } + aSeg2 += 1 + numArgs; + aResultSeg += 1 + numArgs; +} + +/** + * Helper function for Add & Interpolate, to add multiples of two path-segment + * lists. + * + * NOTE: aList1 and aList2 are assumed to have their segment-types and + * segment-count match exactly (unless aList1 is an identity value). + * + * NOTE: aResult, the output list, is expected to either be an identity value + * (in which case we'll grow it) *or* to already have the exactly right length + * (e.g. in cases where aList1 and aResult are actually the same list). + * + * @param aCoeff1 The coefficient to use on the first path segment list. + * @param aList1 The first path segment list. Allowed to be identity. + * @param aCoeff2 The coefficient to use on the second path segment list. + * @param aList2 The second path segment list. + * @param [out] aResultSeg The resulting path segment list. Allowed to be + * identity, in which case we'll grow it to the right + * size. Also allowed to be the same list as aList1. + */ +static nsresult +AddWeightedPathSegLists(double aCoeff1, const SVGPathDataAndInfo& aList1, + double aCoeff2, const SVGPathDataAndInfo& aList2, + SVGPathDataAndInfo& aResult) +{ + MOZ_ASSERT(aCoeff1 >= 0.0 && aCoeff2 >= 0.0, + "expecting non-negative coefficients"); + MOZ_ASSERT(!aList2.IsIdentity(), + "expecting 2nd list to be non-identity"); + MOZ_ASSERT(aList1.IsIdentity() || aList1.Length() == aList2.Length(), + "expecting 1st list to be identity or to have same " + "length as 2nd list"); + MOZ_ASSERT(aResult.IsIdentity() || aResult.Length() == aList2.Length(), + "expecting result list to be identity or to have same " + "length as 2nd list"); + + SVGPathDataAndInfo::const_iterator iter1, end1; + if (aList1.IsIdentity()) { + iter1 = end1 = nullptr; // indicate that this is an identity list + } else { + iter1 = aList1.begin(); + end1 = aList1.end(); + } + SVGPathDataAndInfo::const_iterator iter2 = aList2.begin(); + SVGPathDataAndInfo::const_iterator end2 = aList2.end(); + + // Grow |aResult| if necessary. (NOTE: It's possible that aResult and aList1 + // are the same list, so this may implicitly resize aList1. That's fine, + // because in that case, we will have already set iter1 to nullptr above, to + // record that our first operand is an identity value.) + if (aResult.IsIdentity()) { + if (!aResult.SetLength(aList2.Length())) { + return NS_ERROR_OUT_OF_MEMORY; + } + aResult.SetElement(aList2.Element()); // propagate target element info! + } + + SVGPathDataAndInfo::iterator resultIter = aResult.begin(); + + while ((!iter1 || iter1 != end1) && + iter2 != end2) { + AddWeightedPathSegs(aCoeff1, iter1, + aCoeff2, iter2, + resultIter); + } + MOZ_ASSERT((!iter1 || iter1 == end1) && + iter2 == end2 && + resultIter == aResult.end(), + "Very, very bad - path data corrupt"); + return NS_OK; +} + +static void +ConvertPathSegmentData(SVGPathDataAndInfo::const_iterator& aStart, + SVGPathDataAndInfo::const_iterator& aEnd, + SVGPathDataAndInfo::iterator& aResult, + SVGPathTraversalState& aState) +{ + uint32_t startType = SVGPathSegUtils::DecodeType(*aStart); + uint32_t endType = SVGPathSegUtils::DecodeType(*aEnd); + + uint32_t segmentLengthIncludingType = + 1 + SVGPathSegUtils::ArgCountForType(startType); + + SVGPathDataAndInfo::const_iterator pResultSegmentBegin = aResult; + + if (startType == endType) { + // No conversion need, just directly copy aStart. + aEnd += segmentLengthIncludingType; + while (segmentLengthIncludingType) { + *aResult++ = *aStart++; + --segmentLengthIncludingType; + } + SVGPathSegUtils::TraversePathSegment(pResultSegmentBegin, aState); + return; + } + + MOZ_ASSERT + (SVGPathSegUtils::SameTypeModuloRelativeness(startType, endType), + "Incompatible path segment types passed to ConvertPathSegmentData!"); + + RelativenessAdjustmentType adjustmentType = + SVGPathSegUtils::IsRelativeType(startType) ? eRelativeToAbsolute + : eAbsoluteToRelative; + + MOZ_ASSERT + (segmentLengthIncludingType == + 1 + SVGPathSegUtils::ArgCountForType(endType), + "Compatible path segment types for interpolation had different lengths!"); + + aResult[0] = aEnd[0]; + + switch (endType) { + case PATHSEG_LINETO_HORIZONTAL_ABS: + case PATHSEG_LINETO_HORIZONTAL_REL: + aResult[1] = aStart[1] + + (adjustmentType == eRelativeToAbsolute ? 1 : -1) * aState.pos.x; + break; + case PATHSEG_LINETO_VERTICAL_ABS: + case PATHSEG_LINETO_VERTICAL_REL: + aResult[1] = aStart[1] + + (adjustmentType == eRelativeToAbsolute ? 1 : -1) * aState.pos.y; + break; + case PATHSEG_ARC_ABS: + case PATHSEG_ARC_REL: + aResult[1] = aStart[1]; + aResult[2] = aStart[2]; + aResult[3] = aStart[3]; + aResult[4] = aStart[4]; + aResult[5] = aStart[5]; + aResult[6] = aStart[6]; + aResult[7] = aStart[7]; + AdjustSegmentForRelativeness(adjustmentType, aResult + 6, aState); + break; + case PATHSEG_CURVETO_CUBIC_ABS: + case PATHSEG_CURVETO_CUBIC_REL: + aResult[5] = aStart[5]; + aResult[6] = aStart[6]; + AdjustSegmentForRelativeness(adjustmentType, aResult + 5, aState); + MOZ_FALLTHROUGH; + case PATHSEG_CURVETO_QUADRATIC_ABS: + case PATHSEG_CURVETO_QUADRATIC_REL: + case PATHSEG_CURVETO_CUBIC_SMOOTH_ABS: + case PATHSEG_CURVETO_CUBIC_SMOOTH_REL: + aResult[3] = aStart[3]; + aResult[4] = aStart[4]; + AdjustSegmentForRelativeness(adjustmentType, aResult + 3, aState); + MOZ_FALLTHROUGH; + case PATHSEG_MOVETO_ABS: + case PATHSEG_MOVETO_REL: + case PATHSEG_LINETO_ABS: + case PATHSEG_LINETO_REL: + case PATHSEG_CURVETO_QUADRATIC_SMOOTH_ABS: + case PATHSEG_CURVETO_QUADRATIC_SMOOTH_REL: + aResult[1] = aStart[1]; + aResult[2] = aStart[2]; + AdjustSegmentForRelativeness(adjustmentType, aResult + 1, aState); + break; + } + + SVGPathSegUtils::TraversePathSegment(pResultSegmentBegin, aState); + aStart += segmentLengthIncludingType; + aEnd += segmentLengthIncludingType; + aResult += segmentLengthIncludingType; +} + +static void +ConvertAllPathSegmentData(SVGPathDataAndInfo::const_iterator aStart, + SVGPathDataAndInfo::const_iterator aStartDataEnd, + SVGPathDataAndInfo::const_iterator aEnd, + SVGPathDataAndInfo::const_iterator aEndDataEnd, + SVGPathDataAndInfo::iterator aResult) +{ + SVGPathTraversalState state; + state.mode = SVGPathTraversalState::eUpdateOnlyStartAndCurrentPos; + while (aStart < aStartDataEnd && aEnd < aEndDataEnd) { + ConvertPathSegmentData(aStart, aEnd, aResult, state); + } + MOZ_ASSERT(aStart == aStartDataEnd && aEnd == aEndDataEnd, + "Failed to convert all path segment data! (Corrupt?)"); +} + +nsresult +SVGPathSegListSMILType::Add(nsSMILValue& aDest, + const nsSMILValue& aValueToAdd, + uint32_t aCount) const +{ + NS_PRECONDITION(aDest.mType == this, "Unexpected SMIL type"); + NS_PRECONDITION(aValueToAdd.mType == this, "Incompatible SMIL type"); + + SVGPathDataAndInfo& dest = + *static_cast<SVGPathDataAndInfo*>(aDest.mU.mPtr); + const SVGPathDataAndInfo& valueToAdd = + *static_cast<const SVGPathDataAndInfo*>(aValueToAdd.mU.mPtr); + + if (valueToAdd.IsIdentity()) { // Adding identity value - no-op + return NS_OK; + } + + if (!dest.IsIdentity()) { + // Neither value is identity; make sure they're compatible. + MOZ_ASSERT(dest.Element() == valueToAdd.Element(), + "adding values from different elements...?"); + + PathInterpolationResult check = CanInterpolate(dest, valueToAdd); + if (check == eCannotInterpolate) { + // SVGContentUtils::ReportToConsole - can't add path segment lists with + // different numbers of segments, with arcs that have different flag + // values, or with incompatible segment types. + return NS_ERROR_FAILURE; + } + if (check == eRequiresConversion) { + // Convert dest, in-place, to match the types in valueToAdd: + ConvertAllPathSegmentData(dest.begin(), dest.end(), + valueToAdd.begin(), valueToAdd.end(), + dest.begin()); + } + } + + return AddWeightedPathSegLists(1.0, dest, aCount, valueToAdd, dest); +} + +nsresult +SVGPathSegListSMILType::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"); + + // See https://bugzilla.mozilla.org/show_bug.cgi?id=522306#c18 + + // SVGContentUtils::ReportToConsole + return NS_ERROR_NOT_IMPLEMENTED; +} + +nsresult +SVGPathSegListSMILType::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 SVGPathDataAndInfo& start = + *static_cast<const SVGPathDataAndInfo*>(aStartVal.mU.mPtr); + const SVGPathDataAndInfo& end = + *static_cast<const SVGPathDataAndInfo*>(aEndVal.mU.mPtr); + SVGPathDataAndInfo& result = + *static_cast<SVGPathDataAndInfo*>(aResult.mU.mPtr); + MOZ_ASSERT(result.IsIdentity(), + "expecting outparam to start out as identity"); + + PathInterpolationResult check = CanInterpolate(start, end); + + if (check == eCannotInterpolate) { + // SVGContentUtils::ReportToConsole - can't interpolate path segment lists with + // different numbers of segments, with arcs with different flag values, or + // with incompatible segment types. + return NS_ERROR_FAILURE; + } + + const SVGPathDataAndInfo* startListToUse = &start; + if (check == eRequiresConversion) { + // Can't convert |start| in-place, since it's const. Instead, we copy it + // into |result|, converting the types as we go, and use that as our start. + if (!result.SetLength(end.Length())) { + return NS_ERROR_OUT_OF_MEMORY; + } + result.SetElement(end.Element()); // propagate target element info! + + ConvertAllPathSegmentData(start.begin(), start.end(), + end.begin(), end.end(), + result.begin()); + startListToUse = &result; + } + + return AddWeightedPathSegLists(1.0 - aUnitDistance, *startListToUse, + aUnitDistance, end, result); +} + +} // namespace mozilla diff --git a/dom/svg/SVGPathSegListSMILType.h b/dom/svg/SVGPathSegListSMILType.h new file mode 100644 index 0000000000..6856ef2759 --- /dev/null +++ b/dom/svg/SVGPathSegListSMILType.h @@ -0,0 +1,59 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef MOZILLA_SVGPATHSEGLISTSMILTYPE_H_ +#define MOZILLA_SVGPATHSEGLISTSMILTYPE_H_ + +#include "mozilla/Attributes.h" +#include "nsISMILType.h" + +class nsSMILValue; + +namespace mozilla { + +//////////////////////////////////////////////////////////////////////// +// SVGPathSegListSMILType +// +// Operations for animating an SVGPathData. +// +class SVGPathSegListSMILType : public nsISMILType +{ +public: + // Singleton for nsSMILValue objects to hold onto. + static SVGPathSegListSMILType* Singleton() + { + static SVGPathSegListSMILType sSingleton; + return &sSingleton; + } + +protected: + // nsISMILType Methods + // ------------------- + + virtual void Init(nsSMILValue& aValue) const override; + + virtual void Destroy(nsSMILValue& aValue) const override; + virtual nsresult Assign(nsSMILValue& aDest, const nsSMILValue& aSrc) const override; + virtual bool IsEqual(const nsSMILValue& aLeft, + const nsSMILValue& aRight) const override; + virtual nsresult Add(nsSMILValue& aDest, const nsSMILValue& aValueToAdd, + uint32_t aCount) const override; + virtual nsresult ComputeDistance(const nsSMILValue& aFrom, + const nsSMILValue& aTo, + double& aDistance) const override; + virtual nsresult Interpolate(const nsSMILValue& aStartVal, + const nsSMILValue& aEndVal, + double aUnitDistance, + nsSMILValue& aResult) const override; + +private: + // Private constructor: prevent instances beyond my singleton. + constexpr SVGPathSegListSMILType() {} +}; + +} // namespace mozilla + +#endif // MOZILLA_SVGPATHSEGLISTSMILTYPE_H_ diff --git a/dom/svg/SVGPathSegUtils.cpp b/dom/svg/SVGPathSegUtils.cpp new file mode 100644 index 0000000000..97d34a650a --- /dev/null +++ b/dom/svg/SVGPathSegUtils.cpp @@ -0,0 +1,452 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "mozilla/ArrayUtils.h" // MOZ_ARRAY_LENGTH + +#include "SVGPathSegUtils.h" + +#include "gfx2DGlue.h" +#include "nsSVGPathDataParser.h" +#include "nsTextFormatter.h" + +using namespace mozilla; +using namespace mozilla::gfx; + +static const float PATH_SEG_LENGTH_TOLERANCE = 0.0000001f; +static const uint32_t MAX_RECURSION = 10; + + +/* static */ void +SVGPathSegUtils::GetValueAsString(const float* aSeg, nsAString& aValue) +{ + // Adding new seg type? Is the formatting below acceptable for the new types? + static_assert(NS_SVG_PATH_SEG_LAST_VALID_TYPE == + PATHSEG_CURVETO_QUADRATIC_SMOOTH_REL, + "Update GetValueAsString for the new value."); + static_assert(NS_SVG_PATH_SEG_MAX_ARGS == 7, + "Add another case to the switch below."); + + uint32_t type = DecodeType(aSeg[0]); + char16_t typeAsChar = GetPathSegTypeAsLetter(type); + + // Special case arcs: + if (IsArcType(type)) { + bool largeArcFlag = aSeg[4] != 0.0f; + bool sweepFlag = aSeg[5] != 0.0f; + nsTextFormatter::ssprintf(aValue, + u"%c%g,%g %g %d,%d %g,%g", + typeAsChar, aSeg[1], aSeg[2], aSeg[3], + largeArcFlag, sweepFlag, aSeg[6], aSeg[7]); + } else { + + switch (ArgCountForType(type)) { + case 0: + aValue = typeAsChar; + break; + + case 1: + nsTextFormatter::ssprintf(aValue, u"%c%g", + typeAsChar, aSeg[1]); + break; + + case 2: + nsTextFormatter::ssprintf(aValue, u"%c%g,%g", + typeAsChar, aSeg[1], aSeg[2]); + break; + + case 4: + nsTextFormatter::ssprintf(aValue, u"%c%g,%g %g,%g", + typeAsChar, aSeg[1], aSeg[2], aSeg[3], aSeg[4]); + break; + + case 6: + nsTextFormatter::ssprintf(aValue, + u"%c%g,%g %g,%g %g,%g", + typeAsChar, aSeg[1], aSeg[2], aSeg[3], aSeg[4], + aSeg[5], aSeg[6]); + break; + + default: + MOZ_ASSERT(false, "Unknown segment type"); + aValue = u"<unknown-segment-type>"; + return; + } + } + + // nsTextFormatter::ssprintf is one of the nsTextFormatter methods that + // randomly appends '\0' to its output string, which means that the length + // of the output string is one too long. We need to manually remove that '\0' + // until nsTextFormatter is fixed. + // + if (aValue[aValue.Length() - 1] == char16_t('\0')) { + aValue.SetLength(aValue.Length() - 1); + } +} + + +static float +CalcDistanceBetweenPoints(const Point& aP1, const Point& aP2) +{ + return NS_hypot(aP2.x - aP1.x, aP2.y - aP1.y); +} + + +static void +SplitQuadraticBezier(const Point* aCurve, Point* aLeft, Point* aRight) +{ + aLeft[0].x = aCurve[0].x; + aLeft[0].y = aCurve[0].y; + aRight[2].x = aCurve[2].x; + aRight[2].y = aCurve[2].y; + aLeft[1].x = (aCurve[0].x + aCurve[1].x) / 2; + aLeft[1].y = (aCurve[0].y + aCurve[1].y) / 2; + aRight[1].x = (aCurve[1].x + aCurve[2].x) / 2; + aRight[1].y = (aCurve[1].y + aCurve[2].y) / 2; + aLeft[2].x = aRight[0].x = (aLeft[1].x + aRight[1].x) / 2; + aLeft[2].y = aRight[0].y = (aLeft[1].y + aRight[1].y) / 2; +} + +static void +SplitCubicBezier(const Point* aCurve, Point* aLeft, Point* aRight) +{ + Point tmp; + tmp.x = (aCurve[1].x + aCurve[2].x) / 4; + tmp.y = (aCurve[1].y + aCurve[2].y) / 4; + aLeft[0].x = aCurve[0].x; + aLeft[0].y = aCurve[0].y; + aRight[3].x = aCurve[3].x; + aRight[3].y = aCurve[3].y; + aLeft[1].x = (aCurve[0].x + aCurve[1].x) / 2; + aLeft[1].y = (aCurve[0].y + aCurve[1].y) / 2; + aRight[2].x = (aCurve[2].x + aCurve[3].x) / 2; + aRight[2].y = (aCurve[2].y + aCurve[3].y) / 2; + aLeft[2].x = aLeft[1].x / 2 + tmp.x; + aLeft[2].y = aLeft[1].y / 2 + tmp.y; + aRight[1].x = aRight[2].x / 2 + tmp.x; + aRight[1].y = aRight[2].y / 2 + tmp.y; + aLeft[3].x = aRight[0].x = (aLeft[2].x + aRight[1].x) / 2; + aLeft[3].y = aRight[0].y = (aLeft[2].y + aRight[1].y) / 2; +} + +static float +CalcBezLengthHelper(const Point* aCurve, uint32_t aNumPts, + uint32_t aRecursionCount, + void (*aSplit)(const Point*, Point*, Point*)) +{ + Point left[4]; + Point right[4]; + float length = 0, dist; + for (uint32_t i = 0; i < aNumPts - 1; i++) { + length += CalcDistanceBetweenPoints(aCurve[i], aCurve[i+1]); + } + dist = CalcDistanceBetweenPoints(aCurve[0], aCurve[aNumPts - 1]); + if (length - dist > PATH_SEG_LENGTH_TOLERANCE && + aRecursionCount < MAX_RECURSION) { + aSplit(aCurve, left, right); + ++aRecursionCount; + return CalcBezLengthHelper(left, aNumPts, aRecursionCount, aSplit) + + CalcBezLengthHelper(right, aNumPts, aRecursionCount, aSplit); + } + return length; +} + +static inline float +CalcLengthOfCubicBezier(const Point& aPos, const Point &aCP1, + const Point& aCP2, const Point &aTo) +{ + Point curve[4] = { aPos, aCP1, aCP2, aTo }; + return CalcBezLengthHelper(curve, 4, 0, SplitCubicBezier); +} + +static inline float +CalcLengthOfQuadraticBezier(const Point& aPos, const Point& aCP, + const Point& aTo) +{ + Point curve[3] = { aPos, aCP, aTo }; + return CalcBezLengthHelper(curve, 3, 0, SplitQuadraticBezier); +} + + +static void +TraverseClosePath(const float* aArgs, SVGPathTraversalState& aState) +{ + if (aState.ShouldUpdateLengthAndControlPoints()) { + aState.length += CalcDistanceBetweenPoints(aState.pos, aState.start); + aState.cp1 = aState.cp2 = aState.start; + } + aState.pos = aState.start; +} + +static void +TraverseMovetoAbs(const float* aArgs, SVGPathTraversalState& aState) +{ + aState.start = aState.pos = Point(aArgs[0], aArgs[1]); + if (aState.ShouldUpdateLengthAndControlPoints()) { + // aState.length is unchanged, since move commands don't affect path length. + aState.cp1 = aState.cp2 = aState.start; + } +} + +static void +TraverseMovetoRel(const float* aArgs, SVGPathTraversalState& aState) +{ + aState.start = aState.pos += Point(aArgs[0], aArgs[1]); + if (aState.ShouldUpdateLengthAndControlPoints()) { + // aState.length is unchanged, since move commands don't affect path length. + aState.cp1 = aState.cp2 = aState.start; + } +} + +static void +TraverseLinetoAbs(const float* aArgs, SVGPathTraversalState& aState) +{ + Point to(aArgs[0], aArgs[1]); + if (aState.ShouldUpdateLengthAndControlPoints()) { + aState.length += CalcDistanceBetweenPoints(aState.pos, to); + aState.cp1 = aState.cp2 = to; + } + aState.pos = to; +} + +static void +TraverseLinetoRel(const float* aArgs, SVGPathTraversalState& aState) +{ + Point to = aState.pos + Point(aArgs[0], aArgs[1]); + if (aState.ShouldUpdateLengthAndControlPoints()) { + aState.length += CalcDistanceBetweenPoints(aState.pos, to); + aState.cp1 = aState.cp2 = to; + } + aState.pos = to; +} + +static void +TraverseLinetoHorizontalAbs(const float* aArgs, SVGPathTraversalState& aState) +{ + Point to(aArgs[0], aState.pos.y); + if (aState.ShouldUpdateLengthAndControlPoints()) { + aState.length += fabs(to.x - aState.pos.x); + aState.cp1 = aState.cp2 = to; + } + aState.pos = to; +} + +static void +TraverseLinetoHorizontalRel(const float* aArgs, SVGPathTraversalState& aState) +{ + aState.pos.x += aArgs[0]; + if (aState.ShouldUpdateLengthAndControlPoints()) { + aState.length += fabs(aArgs[0]); + aState.cp1 = aState.cp2 = aState.pos; + } +} + +static void +TraverseLinetoVerticalAbs(const float* aArgs, SVGPathTraversalState& aState) +{ + Point to(aState.pos.x, aArgs[0]); + if (aState.ShouldUpdateLengthAndControlPoints()) { + aState.length += fabs(to.y - aState.pos.y); + aState.cp1 = aState.cp2 = to; + } + aState.pos = to; +} + +static void +TraverseLinetoVerticalRel(const float* aArgs, SVGPathTraversalState& aState) +{ + aState.pos.y += aArgs[0]; + if (aState.ShouldUpdateLengthAndControlPoints()) { + aState.length += fabs(aArgs[0]); + aState.cp1 = aState.cp2 = aState.pos; + } +} + +static void +TraverseCurvetoCubicAbs(const float* aArgs, SVGPathTraversalState& aState) +{ + Point to(aArgs[4], aArgs[5]); + if (aState.ShouldUpdateLengthAndControlPoints()) { + Point cp1(aArgs[0], aArgs[1]); + Point cp2(aArgs[2], aArgs[3]); + aState.length += (float)CalcLengthOfCubicBezier(aState.pos, cp1, cp2, to); + aState.cp2 = cp2; + aState.cp1 = to; + } + aState.pos = to; +} + +static void +TraverseCurvetoCubicSmoothAbs(const float* aArgs, SVGPathTraversalState& aState) +{ + Point to(aArgs[2], aArgs[3]); + if (aState.ShouldUpdateLengthAndControlPoints()) { + Point cp1 = aState.pos - (aState.cp2 - aState.pos); + Point cp2(aArgs[0], aArgs[1]); + aState.length += (float)CalcLengthOfCubicBezier(aState.pos, cp1, cp2, to); + aState.cp2 = cp2; + aState.cp1 = to; + } + aState.pos = to; +} + +static void +TraverseCurvetoCubicRel(const float* aArgs, SVGPathTraversalState& aState) +{ + Point to = aState.pos + Point(aArgs[4], aArgs[5]); + if (aState.ShouldUpdateLengthAndControlPoints()) { + Point cp1 = aState.pos + Point(aArgs[0], aArgs[1]); + Point cp2 = aState.pos + Point(aArgs[2], aArgs[3]); + aState.length += (float)CalcLengthOfCubicBezier(aState.pos, cp1, cp2, to); + aState.cp2 = cp2; + aState.cp1 = to; + } + aState.pos = to; +} + +static void +TraverseCurvetoCubicSmoothRel(const float* aArgs, SVGPathTraversalState& aState) +{ + Point to = aState.pos + Point(aArgs[2], aArgs[3]); + if (aState.ShouldUpdateLengthAndControlPoints()) { + Point cp1 = aState.pos - (aState.cp2 - aState.pos); + Point cp2 = aState.pos + Point(aArgs[0], aArgs[1]); + aState.length += (float)CalcLengthOfCubicBezier(aState.pos, cp1, cp2, to); + aState.cp2 = cp2; + aState.cp1 = to; + } + aState.pos = to; +} + +static void +TraverseCurvetoQuadraticAbs(const float* aArgs, SVGPathTraversalState& aState) +{ + Point to(aArgs[2], aArgs[3]); + if (aState.ShouldUpdateLengthAndControlPoints()) { + Point cp(aArgs[0], aArgs[1]); + aState.length += (float)CalcLengthOfQuadraticBezier(aState.pos, cp, to); + aState.cp1 = cp; + aState.cp2 = to; + } + aState.pos = to; +} + +static void +TraverseCurvetoQuadraticSmoothAbs(const float* aArgs, + SVGPathTraversalState& aState) +{ + Point to(aArgs[0], aArgs[1]); + if (aState.ShouldUpdateLengthAndControlPoints()) { + Point cp = aState.pos - (aState.cp1 - aState.pos); + aState.length += (float)CalcLengthOfQuadraticBezier(aState.pos, cp, to); + aState.cp1 = cp; + aState.cp2 = to; + } + aState.pos = to; +} + +static void +TraverseCurvetoQuadraticRel(const float* aArgs, SVGPathTraversalState& aState) +{ + Point to = aState.pos + Point(aArgs[2], aArgs[3]); + if (aState.ShouldUpdateLengthAndControlPoints()) { + Point cp = aState.pos + Point(aArgs[0], aArgs[1]); + aState.length += (float)CalcLengthOfQuadraticBezier(aState.pos, cp, to); + aState.cp1 = cp; + aState.cp2 = to; + } + aState.pos = to; +} + +static void +TraverseCurvetoQuadraticSmoothRel(const float* aArgs, + SVGPathTraversalState& aState) +{ + Point to = aState.pos + Point(aArgs[0], aArgs[1]); + if (aState.ShouldUpdateLengthAndControlPoints()) { + Point cp = aState.pos - (aState.cp1 - aState.pos); + aState.length += (float)CalcLengthOfQuadraticBezier(aState.pos, cp, to); + aState.cp1 = cp; + aState.cp2 = to; + } + aState.pos = to; +} + +static void +TraverseArcAbs(const float* aArgs, SVGPathTraversalState& aState) +{ + Point to(aArgs[5], aArgs[6]); + if (aState.ShouldUpdateLengthAndControlPoints()) { + float dist = 0; + Point radii(aArgs[0], aArgs[1]); + Point bez[4] = { aState.pos, Point(0, 0), Point(0, 0), Point(0, 0) }; + nsSVGArcConverter converter(aState.pos, to, radii, aArgs[2], + aArgs[3] != 0, aArgs[4] != 0); + while (converter.GetNextSegment(&bez[1], &bez[2], &bez[3])) { + dist += CalcBezLengthHelper(bez, 4, 0, SplitCubicBezier); + bez[0] = bez[3]; + } + aState.length += dist; + aState.cp1 = aState.cp2 = to; + } + aState.pos = to; +} + +static void +TraverseArcRel(const float* aArgs, SVGPathTraversalState& aState) +{ + Point to = aState.pos + Point(aArgs[5], aArgs[6]); + if (aState.ShouldUpdateLengthAndControlPoints()) { + float dist = 0; + Point radii(aArgs[0], aArgs[1]); + Point bez[4] = { aState.pos, Point(0, 0), Point(0, 0), Point(0, 0) }; + nsSVGArcConverter converter(aState.pos, to, radii, aArgs[2], + aArgs[3] != 0, aArgs[4] != 0); + while (converter.GetNextSegment(&bez[1], &bez[2], &bez[3])) { + dist += CalcBezLengthHelper(bez, 4, 0, SplitCubicBezier); + bez[0] = bez[3]; + } + aState.length += dist; + aState.cp1 = aState.cp2 = to; + } + aState.pos = to; +} + + +typedef void (*TraverseFunc)(const float*, SVGPathTraversalState&); + +static TraverseFunc gTraverseFuncTable[NS_SVG_PATH_SEG_TYPE_COUNT] = { + nullptr, // 0 == PATHSEG_UNKNOWN + TraverseClosePath, + TraverseMovetoAbs, + TraverseMovetoRel, + TraverseLinetoAbs, + TraverseLinetoRel, + TraverseCurvetoCubicAbs, + TraverseCurvetoCubicRel, + TraverseCurvetoQuadraticAbs, + TraverseCurvetoQuadraticRel, + TraverseArcAbs, + TraverseArcRel, + TraverseLinetoHorizontalAbs, + TraverseLinetoHorizontalRel, + TraverseLinetoVerticalAbs, + TraverseLinetoVerticalRel, + TraverseCurvetoCubicSmoothAbs, + TraverseCurvetoCubicSmoothRel, + TraverseCurvetoQuadraticSmoothAbs, + TraverseCurvetoQuadraticSmoothRel +}; + +/* static */ void +SVGPathSegUtils::TraversePathSegment(const float* aData, + SVGPathTraversalState& aState) +{ + static_assert(MOZ_ARRAY_LENGTH(gTraverseFuncTable) == + NS_SVG_PATH_SEG_TYPE_COUNT, + "gTraverseFuncTable is out of date"); + uint32_t type = DecodeType(aData[0]); + gTraverseFuncTable[type](aData + 1, aState); +} diff --git a/dom/svg/SVGPathSegUtils.h b/dom/svg/SVGPathSegUtils.h new file mode 100644 index 0000000000..9600f5ca1d --- /dev/null +++ b/dom/svg/SVGPathSegUtils.h @@ -0,0 +1,277 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef MOZILLA_SVGPATHSEGUTILS_H__ +#define MOZILLA_SVGPATHSEGUTILS_H__ + +#include "mozilla/ArrayUtils.h" +#include "mozilla/gfx/Point.h" +#include "nsDebug.h" + +namespace mozilla { + +// Path Segment Types +static const unsigned short PATHSEG_UNKNOWN = 0; +static const unsigned short PATHSEG_CLOSEPATH = 1; +static const unsigned short PATHSEG_MOVETO_ABS = 2; +static const unsigned short PATHSEG_MOVETO_REL = 3; +static const unsigned short PATHSEG_LINETO_ABS = 4; +static const unsigned short PATHSEG_LINETO_REL = 5; +static const unsigned short PATHSEG_CURVETO_CUBIC_ABS = 6; +static const unsigned short PATHSEG_CURVETO_CUBIC_REL = 7; +static const unsigned short PATHSEG_CURVETO_QUADRATIC_ABS = 8; +static const unsigned short PATHSEG_CURVETO_QUADRATIC_REL = 9; +static const unsigned short PATHSEG_ARC_ABS = 10; +static const unsigned short PATHSEG_ARC_REL = 11; +static const unsigned short PATHSEG_LINETO_HORIZONTAL_ABS = 12; +static const unsigned short PATHSEG_LINETO_HORIZONTAL_REL = 13; +static const unsigned short PATHSEG_LINETO_VERTICAL_ABS = 14; +static const unsigned short PATHSEG_LINETO_VERTICAL_REL = 15; +static const unsigned short PATHSEG_CURVETO_CUBIC_SMOOTH_ABS = 16; +static const unsigned short PATHSEG_CURVETO_CUBIC_SMOOTH_REL = 17; +static const unsigned short PATHSEG_CURVETO_QUADRATIC_SMOOTH_ABS = 18; +static const unsigned short PATHSEG_CURVETO_QUADRATIC_SMOOTH_REL = 19; + +#define NS_SVG_PATH_SEG_MAX_ARGS 7 +#define NS_SVG_PATH_SEG_FIRST_VALID_TYPE mozilla::PATHSEG_CLOSEPATH +#define NS_SVG_PATH_SEG_LAST_VALID_TYPE mozilla::PATHSEG_CURVETO_QUADRATIC_SMOOTH_REL +#define NS_SVG_PATH_SEG_TYPE_COUNT (NS_SVG_PATH_SEG_LAST_VALID_TYPE + 1) + +/** + * Code that works with path segments can use an instance of this class to + * store/provide information about the start of the current subpath and the + * last path segment (if any). + */ +struct SVGPathTraversalState +{ + typedef gfx::Point Point; + + enum TraversalMode { + eUpdateAll, + eUpdateOnlyStartAndCurrentPos + }; + + SVGPathTraversalState() + : start(0.0, 0.0) + , pos(0.0, 0.0) + , cp1(0.0, 0.0) + , cp2(0.0, 0.0) + , length(0.0) + , mode(eUpdateAll) + {} + + bool ShouldUpdateLengthAndControlPoints() { return mode == eUpdateAll; } + + Point start; // start point of current sub path (reset each moveto) + + Point pos; // current position (end point of previous segment) + + Point cp1; // quadratic control point - if the previous segment was a + // quadratic bezier curve then this is set to the absolute + // position of its control point, otherwise its set to pos + + Point cp2; // cubic control point - if the previous segment was a cubic + // bezier curve then this is set to the absolute position of + // its second control point, otherwise it's set to pos + + float length; // accumulated path length + + TraversalMode mode; // indicates what to track while traversing a path +}; + + +/** + * This class is just a collection of static methods - it doesn't have any data + * members, and it's not possible to create instances of this class. This class + * exists purely as a convenient place to gather together a bunch of methods + * related to manipulating and answering questions about path segments. + * Internally we represent path segments purely as an array of floats. See the + * comment documenting SVGPathData for more info on that. + * + * The DOM wrapper classes for encoded path segments (data contained in + * instances of SVGPathData) is DOMSVGPathSeg and its sub-classes. Note that + * there are multiple different DOM classes for path segs - one for each of the + * 19 SVG 1.1 segment types. + */ +class SVGPathSegUtils +{ +private: + SVGPathSegUtils(){} // private to prevent instances + +public: + + static void GetValueAsString(const float *aSeg, nsAString& aValue); + + /** + * Encode a segment type enum to a float. + * + * At some point in the future we will likely want to encode other + * information into the float, such as whether the command was explicit or + * not. For now all this method does is save on int to float runtime + * conversion by requiring uint32_t and float to be of the same size so we + * can simply do a bitwise uint32_t<->float copy. + */ + static float EncodeType(uint32_t aType) { + static_assert(sizeof(uint32_t) == sizeof(float), "sizeof uint32_t and float must be the same"); + MOZ_ASSERT(IsValidType(aType), "Seg type not recognized"); + return *(reinterpret_cast<float*>(&aType)); + } + + static uint32_t DecodeType(float aType) { + static_assert(sizeof(uint32_t) == sizeof(float), "sizeof uint32_t and float must be the same"); + uint32_t type = *(reinterpret_cast<uint32_t*>(&aType)); + MOZ_ASSERT(IsValidType(type), "Seg type not recognized"); + return type; + } + + static char16_t GetPathSegTypeAsLetter(uint32_t aType) { + MOZ_ASSERT(IsValidType(aType), "Seg type not recognized"); + + static const char16_t table[] = { + char16_t('x'), // 0 == PATHSEG_UNKNOWN + char16_t('z'), // 1 == PATHSEG_CLOSEPATH + char16_t('M'), // 2 == PATHSEG_MOVETO_ABS + char16_t('m'), // 3 == PATHSEG_MOVETO_REL + char16_t('L'), // 4 == PATHSEG_LINETO_ABS + char16_t('l'), // 5 == PATHSEG_LINETO_REL + char16_t('C'), // 6 == PATHSEG_CURVETO_CUBIC_ABS + char16_t('c'), // 7 == PATHSEG_CURVETO_CUBIC_REL + char16_t('Q'), // 8 == PATHSEG_CURVETO_QUADRATIC_ABS + char16_t('q'), // 9 == PATHSEG_CURVETO_QUADRATIC_REL + char16_t('A'), // 10 == PATHSEG_ARC_ABS + char16_t('a'), // 11 == PATHSEG_ARC_REL + char16_t('H'), // 12 == PATHSEG_LINETO_HORIZONTAL_ABS + char16_t('h'), // 13 == PATHSEG_LINETO_HORIZONTAL_REL + char16_t('V'), // 14 == PATHSEG_LINETO_VERTICAL_ABS + char16_t('v'), // 15 == PATHSEG_LINETO_VERTICAL_REL + char16_t('S'), // 16 == PATHSEG_CURVETO_CUBIC_SMOOTH_ABS + char16_t('s'), // 17 == PATHSEG_CURVETO_CUBIC_SMOOTH_REL + char16_t('T'), // 18 == PATHSEG_CURVETO_QUADRATIC_SMOOTH_ABS + char16_t('t') // 19 == PATHSEG_CURVETO_QUADRATIC_SMOOTH_REL + }; + static_assert(MOZ_ARRAY_LENGTH(table) == NS_SVG_PATH_SEG_TYPE_COUNT, "Unexpected table size"); + + return table[aType]; + } + + static uint32_t ArgCountForType(uint32_t aType) { + MOZ_ASSERT(IsValidType(aType), "Seg type not recognized"); + + static const uint8_t table[] = { + 0, // 0 == PATHSEG_UNKNOWN + 0, // 1 == PATHSEG_CLOSEPATH + 2, // 2 == PATHSEG_MOVETO_ABS + 2, // 3 == PATHSEG_MOVETO_REL + 2, // 4 == PATHSEG_LINETO_ABS + 2, // 5 == PATHSEG_LINETO_REL + 6, // 6 == PATHSEG_CURVETO_CUBIC_ABS + 6, // 7 == PATHSEG_CURVETO_CUBIC_REL + 4, // 8 == PATHSEG_CURVETO_QUADRATIC_ABS + 4, // 9 == PATHSEG_CURVETO_QUADRATIC_REL + 7, // 10 == PATHSEG_ARC_ABS + 7, // 11 == PATHSEG_ARC_REL + 1, // 12 == PATHSEG_LINETO_HORIZONTAL_ABS + 1, // 13 == PATHSEG_LINETO_HORIZONTAL_REL + 1, // 14 == PATHSEG_LINETO_VERTICAL_ABS + 1, // 15 == PATHSEG_LINETO_VERTICAL_REL + 4, // 16 == PATHSEG_CURVETO_CUBIC_SMOOTH_ABS + 4, // 17 == PATHSEG_CURVETO_CUBIC_SMOOTH_REL + 2, // 18 == PATHSEG_CURVETO_QUADRATIC_SMOOTH_ABS + 2 // 19 == PATHSEG_CURVETO_QUADRATIC_SMOOTH_REL + }; + static_assert(MOZ_ARRAY_LENGTH(table) == NS_SVG_PATH_SEG_TYPE_COUNT, "Unexpected table size"); + + return table[aType]; + } + + /** + * Convenience so that callers can pass a float containing an encoded type + * and have it decoded implicitly. + */ + static uint32_t ArgCountForType(float aType) { + return ArgCountForType(DecodeType(aType)); + } + + static bool IsValidType(uint32_t aType) { + return aType >= NS_SVG_PATH_SEG_FIRST_VALID_TYPE && + aType <= NS_SVG_PATH_SEG_LAST_VALID_TYPE; + } + + static bool IsCubicType(uint32_t aType) { + return aType == PATHSEG_CURVETO_CUBIC_REL || + aType == PATHSEG_CURVETO_CUBIC_ABS || + aType == PATHSEG_CURVETO_CUBIC_SMOOTH_REL || + aType == PATHSEG_CURVETO_CUBIC_SMOOTH_ABS; + } + + static bool IsQuadraticType(uint32_t aType) { + return aType == PATHSEG_CURVETO_QUADRATIC_REL || + aType == PATHSEG_CURVETO_QUADRATIC_ABS || + aType == PATHSEG_CURVETO_QUADRATIC_SMOOTH_REL || + aType == PATHSEG_CURVETO_QUADRATIC_SMOOTH_ABS; + } + + static bool IsArcType(uint32_t aType) { + return aType == PATHSEG_ARC_ABS || + aType == PATHSEG_ARC_REL; + } + + static bool IsRelativeOrAbsoluteType(uint32_t aType) { + MOZ_ASSERT(IsValidType(aType), "Seg type not recognized"); + + // When adding a new path segment type, ensure that the returned condition + // below is still correct. + static_assert(NS_SVG_PATH_SEG_LAST_VALID_TYPE == PATHSEG_CURVETO_QUADRATIC_SMOOTH_REL, + "Unexpected type"); + + return aType >= PATHSEG_MOVETO_ABS; + } + + static bool IsRelativeType(uint32_t aType) { + MOZ_ASSERT + (IsRelativeOrAbsoluteType(aType), + "IsRelativeType called with segment type that does not come in relative and absolute forms"); + + // When adding a new path segment type, ensure that the returned condition + // below is still correct. + static_assert(NS_SVG_PATH_SEG_LAST_VALID_TYPE == PATHSEG_CURVETO_QUADRATIC_SMOOTH_REL, + "Unexpected type"); + + return aType & 1; + } + + static uint32_t RelativeVersionOfType(uint32_t aType) { + MOZ_ASSERT + (IsRelativeOrAbsoluteType(aType), + "RelativeVersionOfType called with segment type that does not come in relative and absolute forms"); + + // When adding a new path segment type, ensure that the returned condition + // below is still correct. + static_assert(NS_SVG_PATH_SEG_LAST_VALID_TYPE == PATHSEG_CURVETO_QUADRATIC_SMOOTH_REL, + "Unexpected type"); + + return aType | 1; + } + + static uint32_t SameTypeModuloRelativeness(uint32_t aType1, uint32_t aType2) { + if (!IsRelativeOrAbsoluteType(aType1)) { + return aType1 == aType2; + } + + return RelativeVersionOfType(aType1) == RelativeVersionOfType(aType2); + } + + /** + * Traverse the given path segment and update the SVGPathTraversalState + * object. + */ + static void TraversePathSegment(const float* aData, + SVGPathTraversalState& aState); +}; + +} // namespace mozilla + +#endif // MOZILLA_SVGPATHSEGUTILS_H__ diff --git a/dom/svg/SVGPatternElement.cpp b/dom/svg/SVGPatternElement.cpp new file mode 100644 index 0000000000..e940644599 --- /dev/null +++ b/dom/svg/SVGPatternElement.cpp @@ -0,0 +1,215 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "mozilla/ArrayUtils.h" + +#include "nsCOMPtr.h" +#include "nsGkAtoms.h" +#include "mozilla/dom/SVGAnimatedTransformList.h" +#include "mozilla/dom/SVGPatternElement.h" +#include "mozilla/dom/SVGPatternElementBinding.h" + +NS_IMPL_NS_NEW_NAMESPACED_SVG_ELEMENT(Pattern) + +namespace mozilla { +namespace dom { + +JSObject* +SVGPatternElement::WrapNode(JSContext *aCx, JS::Handle<JSObject*> aGivenProto) +{ + return SVGPatternElementBinding::Wrap(aCx, this, aGivenProto); +} + +//--------------------- Patterns ------------------------ + +nsSVGElement::LengthInfo SVGPatternElement::sLengthInfo[4] = +{ + { &nsGkAtoms::x, 0, nsIDOMSVGLength::SVG_LENGTHTYPE_PERCENTAGE, SVGContentUtils::X }, + { &nsGkAtoms::y, 0, nsIDOMSVGLength::SVG_LENGTHTYPE_PERCENTAGE, SVGContentUtils::Y }, + { &nsGkAtoms::width, 0, nsIDOMSVGLength::SVG_LENGTHTYPE_PERCENTAGE, SVGContentUtils::X }, + { &nsGkAtoms::height, 0, nsIDOMSVGLength::SVG_LENGTHTYPE_PERCENTAGE, SVGContentUtils::Y }, +}; + +nsSVGElement::EnumInfo SVGPatternElement::sEnumInfo[2] = +{ + { &nsGkAtoms::patternUnits, + sSVGUnitTypesMap, + SVG_UNIT_TYPE_OBJECTBOUNDINGBOX + }, + { &nsGkAtoms::patternContentUnits, + sSVGUnitTypesMap, + SVG_UNIT_TYPE_USERSPACEONUSE + } +}; + +nsSVGElement::StringInfo SVGPatternElement::sStringInfo[2] = +{ + { &nsGkAtoms::href, kNameSpaceID_None, true }, + { &nsGkAtoms::href, kNameSpaceID_XLink, true } +}; + +//---------------------------------------------------------------------- +// Implementation + +SVGPatternElement::SVGPatternElement(already_AddRefed<mozilla::dom::NodeInfo>& aNodeInfo) + : SVGPatternElementBase(aNodeInfo) +{ +} + +//---------------------------------------------------------------------- +// nsIDOMNode method + +NS_IMPL_ELEMENT_CLONE_WITH_INIT(SVGPatternElement) + +//---------------------------------------------------------------------- + +already_AddRefed<SVGAnimatedRect> +SVGPatternElement::ViewBox() +{ + return mViewBox.ToSVGAnimatedRect(this); +} + +already_AddRefed<DOMSVGAnimatedPreserveAspectRatio> +SVGPatternElement::PreserveAspectRatio() +{ + return mPreserveAspectRatio.ToDOMAnimatedPreserveAspectRatio(this); +} + +//---------------------------------------------------------------------- + +already_AddRefed<SVGAnimatedEnumeration> +SVGPatternElement::PatternUnits() +{ + return mEnumAttributes[PATTERNUNITS].ToDOMAnimatedEnum(this); +} + +already_AddRefed<SVGAnimatedEnumeration> +SVGPatternElement::PatternContentUnits() +{ + return mEnumAttributes[PATTERNCONTENTUNITS].ToDOMAnimatedEnum(this); +} + +already_AddRefed<SVGAnimatedTransformList> +SVGPatternElement::PatternTransform() +{ + // We're creating a DOM wrapper, so we must tell GetAnimatedTransformList + // to allocate the SVGAnimatedTransformList if it hasn't already done so: + return SVGAnimatedTransformList::GetDOMWrapper( + GetAnimatedTransformList(DO_ALLOCATE), this); +} + +already_AddRefed<SVGAnimatedLength> +SVGPatternElement::X() +{ + return mLengthAttributes[ATTR_X].ToDOMAnimatedLength(this); +} + +already_AddRefed<SVGAnimatedLength> +SVGPatternElement::Y() +{ + return mLengthAttributes[ATTR_Y].ToDOMAnimatedLength(this); +} + +already_AddRefed<SVGAnimatedLength> +SVGPatternElement::Width() +{ + return mLengthAttributes[ATTR_WIDTH].ToDOMAnimatedLength(this); +} + +already_AddRefed<SVGAnimatedLength> +SVGPatternElement::Height() +{ + return mLengthAttributes[ATTR_HEIGHT].ToDOMAnimatedLength(this); +} + +already_AddRefed<SVGAnimatedString> +SVGPatternElement::Href() +{ + return mStringAttributes[HREF].IsExplicitlySet() + ? mStringAttributes[HREF].ToDOMAnimatedString(this) + : mStringAttributes[XLINK_HREF].ToDOMAnimatedString(this); +} + +//---------------------------------------------------------------------- +// nsIContent methods + +NS_IMETHODIMP_(bool) +SVGPatternElement::IsAttributeMapped(const nsIAtom* name) const +{ + static const MappedAttributeEntry* const map[] = { + sColorMap, + sFEFloodMap, + sFillStrokeMap, + sFiltersMap, + sFontSpecificationMap, + sGradientStopMap, + sGraphicsMap, + sLightingEffectsMap, + sMarkersMap, + sTextContentElementsMap, + sViewportsMap + }; + + return FindAttributeDependence(name, map) || + SVGPatternElementBase::IsAttributeMapped(name); +} + +//---------------------------------------------------------------------- +// nsSVGElement methods + +nsSVGAnimatedTransformList* +SVGPatternElement::GetAnimatedTransformList(uint32_t aFlags) +{ + if (!mPatternTransform && (aFlags & DO_ALLOCATE)) { + mPatternTransform = new nsSVGAnimatedTransformList(); + } + return mPatternTransform; +} + +/* virtual */ bool +SVGPatternElement::HasValidDimensions() const +{ + return mLengthAttributes[ATTR_WIDTH].IsExplicitlySet() && + mLengthAttributes[ATTR_WIDTH].GetAnimValInSpecifiedUnits() > 0 && + mLengthAttributes[ATTR_HEIGHT].IsExplicitlySet() && + mLengthAttributes[ATTR_HEIGHT].GetAnimValInSpecifiedUnits() > 0; +} + +nsSVGElement::LengthAttributesInfo +SVGPatternElement::GetLengthInfo() +{ + return LengthAttributesInfo(mLengthAttributes, sLengthInfo, + ArrayLength(sLengthInfo)); +} + +nsSVGElement::EnumAttributesInfo +SVGPatternElement::GetEnumInfo() +{ + return EnumAttributesInfo(mEnumAttributes, sEnumInfo, + ArrayLength(sEnumInfo)); +} + +nsSVGViewBox * +SVGPatternElement::GetViewBox() +{ + return &mViewBox; +} + +SVGAnimatedPreserveAspectRatio * +SVGPatternElement::GetPreserveAspectRatio() +{ + return &mPreserveAspectRatio; +} + +nsSVGElement::StringAttributesInfo +SVGPatternElement::GetStringInfo() +{ + return StringAttributesInfo(mStringAttributes, sStringInfo, + ArrayLength(sStringInfo)); +} + +} // namespace dom +} // namespace mozilla diff --git a/dom/svg/SVGPatternElement.h b/dom/svg/SVGPatternElement.h new file mode 100644 index 0000000000..3a89ddd643 --- /dev/null +++ b/dom/svg/SVGPatternElement.h @@ -0,0 +1,99 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef mozilla_dom_SVGPatternElement_h +#define mozilla_dom_SVGPatternElement_h + +#include "nsAutoPtr.h" +#include "nsSVGEnum.h" +#include "nsSVGLength2.h" +#include "nsSVGString.h" +#include "nsSVGElement.h" +#include "nsSVGViewBox.h" +#include "SVGAnimatedPreserveAspectRatio.h" +#include "nsSVGAnimatedTransformList.h" + +class nsSVGPatternFrame; + +nsresult NS_NewSVGPatternElement(nsIContent **aResult, + already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo); + +namespace mozilla { +namespace dom { +class SVGAnimatedTransformList; + +typedef nsSVGElement SVGPatternElementBase; + +class SVGPatternElement final : public SVGPatternElementBase +{ + friend class ::nsSVGPatternFrame; + +protected: + friend nsresult (::NS_NewSVGPatternElement(nsIContent **aResult, + already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo)); + explicit SVGPatternElement(already_AddRefed<mozilla::dom::NodeInfo>& aNodeInfo); + virtual JSObject* WrapNode(JSContext *cx, JS::Handle<JSObject*> aGivenProto) override; + +public: + typedef mozilla::SVGAnimatedPreserveAspectRatio SVGAnimatedPreserveAspectRatio; + + // nsIContent interface + NS_IMETHOD_(bool) IsAttributeMapped(const nsIAtom* name) const override; + + virtual nsresult Clone(mozilla::dom::NodeInfo *aNodeInfo, nsINode **aResult) const override; + + // nsSVGSVGElement methods: + virtual bool HasValidDimensions() const override; + + virtual mozilla::nsSVGAnimatedTransformList* + GetAnimatedTransformList(uint32_t aFlags = 0) override; + virtual nsIAtom* GetTransformListAttrName() const override { + return nsGkAtoms::patternTransform; + } + + // WebIDL + already_AddRefed<SVGAnimatedRect> ViewBox(); + already_AddRefed<DOMSVGAnimatedPreserveAspectRatio> PreserveAspectRatio(); + already_AddRefed<SVGAnimatedEnumeration> PatternUnits(); + already_AddRefed<SVGAnimatedEnumeration> PatternContentUnits(); + already_AddRefed<SVGAnimatedTransformList> PatternTransform(); + already_AddRefed<SVGAnimatedLength> X(); + already_AddRefed<SVGAnimatedLength> Y(); + already_AddRefed<SVGAnimatedLength> Width(); + already_AddRefed<SVGAnimatedLength> Height(); + already_AddRefed<SVGAnimatedString> Href(); + +protected: + + virtual LengthAttributesInfo GetLengthInfo() override; + virtual EnumAttributesInfo GetEnumInfo() override; + virtual nsSVGViewBox *GetViewBox() override; + virtual SVGAnimatedPreserveAspectRatio *GetPreserveAspectRatio() override; + virtual StringAttributesInfo GetStringInfo() override; + + enum { ATTR_X, ATTR_Y, ATTR_WIDTH, ATTR_HEIGHT }; + nsSVGLength2 mLengthAttributes[4]; + static LengthInfo sLengthInfo[4]; + + enum { PATTERNUNITS, PATTERNCONTENTUNITS }; + nsSVGEnum mEnumAttributes[2]; + static EnumInfo sEnumInfo[2]; + + nsAutoPtr<mozilla::nsSVGAnimatedTransformList> mPatternTransform; + + enum { HREF, XLINK_HREF }; + nsSVGString mStringAttributes[2]; + static StringInfo sStringInfo[2]; + + // SVGFitToViewbox properties + nsSVGViewBox mViewBox; + SVGAnimatedPreserveAspectRatio mPreserveAspectRatio; +}; + +} // namespace dom +} // namespace mozilla + +#endif // mozilla_dom_SVGPatternElement_h diff --git a/dom/svg/SVGPoint.h b/dom/svg/SVGPoint.h new file mode 100644 index 0000000000..2f3b74c4a9 --- /dev/null +++ b/dom/svg/SVGPoint.h @@ -0,0 +1,118 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef MOZILLA_SVGPOINT_H__ +#define MOZILLA_SVGPOINT_H__ + +#include "nsDebug.h" +#include "gfxPoint.h" +#include "mozilla/gfx/Point.h" +#include "mozilla/FloatingPoint.h" + +namespace mozilla { + +/** + * This class is currently used for point list attributes. + * + * The DOM wrapper class for this class is DOMSVGPoint. + */ +class SVGPoint +{ + typedef mozilla::gfx::Point Point; + +public: + + SVGPoint() + : mX(0.0f) + , mY(0.0f) + {} + + SVGPoint(float aX, float aY) + : mX(aX) + , mY(aY) + { + NS_ASSERTION(IsValid(), "Constructed an invalid SVGPoint"); + } + + SVGPoint(const SVGPoint &aOther) + : mX(aOther.mX) + , mY(aOther.mY) + {} + + SVGPoint& operator=(const SVGPoint &rhs) { + mX = rhs.mX; + mY = rhs.mY; + return *this; + } + + bool operator==(const SVGPoint &rhs) const { + return mX == rhs.mX && mY == rhs.mY; + } + + SVGPoint& operator+=(const SVGPoint &rhs) { + mX += rhs.mX; + mY += rhs.mY; + return *this; + } + + operator gfxPoint() const { + return gfxPoint(mX, mY); + } + + operator Point() const { + return Point(mX, mY); + } + +#ifdef DEBUG + bool IsValid() const { + return IsFinite(mX) && IsFinite(mY); + } +#endif + + void SetX(float aX) + { mX = aX; } + void SetY(float aY) + { mY = aY; } + float GetX() const + { return mX; } + float GetY() const + { return mY; } + + bool operator!=(const SVGPoint &rhs) const { + return mX != rhs.mX || mY != rhs.mY; + } + + float mX; + float mY; +}; + +inline SVGPoint operator+(const SVGPoint& aP1, + const SVGPoint& aP2) +{ + return SVGPoint(aP1.mX + aP2.mX, aP1.mY + aP2.mY); +} + +inline SVGPoint operator-(const SVGPoint& aP1, + const SVGPoint& aP2) +{ + return SVGPoint(aP1.mX - aP2.mX, aP1.mY - aP2.mY); +} + +inline SVGPoint operator*(float aFactor, + const SVGPoint& aPoint) +{ + return SVGPoint(aFactor * aPoint.mX, aFactor * aPoint.mY); +} + +inline SVGPoint operator*(const SVGPoint& aPoint, + float aFactor) +{ + return SVGPoint(aFactor * aPoint.mX, aFactor * aPoint.mY); +} + +} // namespace mozilla + +#endif // MOZILLA_SVGPOINT_H__ diff --git a/dom/svg/SVGPointList.cpp b/dom/svg/SVGPointList.cpp new file mode 100644 index 0000000000..c41a109c29 --- /dev/null +++ b/dom/svg/SVGPointList.cpp @@ -0,0 +1,103 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "mozilla/ArrayUtils.h" + +#include "SVGPointList.h" +#include "nsCharSeparatedTokenizer.h" +#include "nsTextFormatter.h" +#include "SVGContentUtils.h" + +namespace mozilla { + +nsresult +SVGPointList::CopyFrom(const SVGPointList& rhs) +{ + if (!mItems.Assign(rhs.mItems, fallible)) { + return NS_ERROR_OUT_OF_MEMORY; + } + return NS_OK; +} + +void +SVGPointList::GetValueAsString(nsAString& aValue) const +{ + aValue.Truncate(); + char16_t buf[50]; + uint32_t last = mItems.Length() - 1; + for (uint32_t i = 0; i < mItems.Length(); ++i) { + // Would like to use aValue.AppendPrintf("%f,%f", item.mX, item.mY), + // but it's not possible to always avoid trailing zeros. + nsTextFormatter::snprintf(buf, ArrayLength(buf), + u"%g,%g", + double(mItems[i].mX), double(mItems[i].mY)); + // We ignore OOM, since it's not useful for us to return an error. + aValue.Append(buf); + if (i != last) { + aValue.Append(' '); + } + } +} + +nsresult +SVGPointList::SetValueFromString(const nsAString& aValue) +{ + // The spec says that the list is parsed and accepted up to the first error + // encountered, so we must call CopyFrom even if an error occurs. We still + // want to throw any error code from setAttribute if there's a problem + // though, so we must take care to return any error code. + + nsresult rv = NS_OK; + + SVGPointList temp; + + nsCharSeparatedTokenizerTemplate<IsSVGWhitespace> + tokenizer(aValue, ',', nsCharSeparatedTokenizer::SEPARATOR_OPTIONAL); + + while (tokenizer.hasMoreTokens()) { + + const nsAString& token = tokenizer.nextToken(); + + RangedPtr<const char16_t> iter = + SVGContentUtils::GetStartRangedPtr(token); + const RangedPtr<const char16_t> end = + SVGContentUtils::GetEndRangedPtr(token); + + float x; + if (!SVGContentUtils::ParseNumber(iter, end, x)) { + rv = NS_ERROR_DOM_SYNTAX_ERR; + break; + } + + float y; + if (iter == end) { + if (!tokenizer.hasMoreTokens() || + !SVGContentUtils::ParseNumber(tokenizer.nextToken(), y)) { + rv = NS_ERROR_DOM_SYNTAX_ERR; + break; + } + } else { + // It's possible for the token to be 10-30 which has + // no separator but needs to be parsed as 10, -30 + const nsAString& leftOver = Substring(iter.get(), end.get()); + if (leftOver[0] != '-' || !SVGContentUtils::ParseNumber(leftOver, y)) { + rv = NS_ERROR_DOM_SYNTAX_ERR; + break; + } + } + temp.AppendItem(SVGPoint(x, y)); + } + if (tokenizer.separatorAfterCurrentToken()) { + rv = NS_ERROR_DOM_SYNTAX_ERR; // trailing comma + } + nsresult rv2 = CopyFrom(temp); + if (NS_FAILED(rv2)) { + return rv2; // prioritize OOM error code over syntax errors + } + return rv; +} + +} // namespace mozilla diff --git a/dom/svg/SVGPointList.h b/dom/svg/SVGPointList.h new file mode 100644 index 0000000000..7f4008c6d8 --- /dev/null +++ b/dom/svg/SVGPointList.h @@ -0,0 +1,224 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef MOZILLA_SVGPOINTLIST_H__ +#define MOZILLA_SVGPOINTLIST_H__ + +#include "nsCOMPtr.h" +#include "nsDebug.h" +#include "nsIContent.h" +#include "nsINode.h" +#include "nsIWeakReferenceUtils.h" +#include "nsSVGElement.h" +#include "nsTArray.h" +#include "SVGPoint.h" + +#include <string.h> + +namespace mozilla { +class nsISVGPoint; + +/** + * 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 DOMSVGPointList. + */ +class SVGPointList +{ + friend class mozilla::nsISVGPoint; + friend class SVGAnimatedPointList; + friend class DOMSVGPointList; + friend class DOMSVGPoint; + +public: + + SVGPointList(){} + ~SVGPointList(){} + + // 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; + + bool IsEmpty() const { + return mItems.IsEmpty(); + } + + uint32_t Length() const { + return mItems.Length(); + } + + const SVGPoint& operator[](uint32_t aIndex) const { + return mItems[aIndex]; + } + + bool operator==(const SVGPointList& rhs) const { + // memcmp can be faster than |mItems == rhs.mItems| + return mItems.Length() == rhs.mItems.Length() && + memcmp(mItems.Elements(), rhs.mItems.Elements(), + mItems.Length() * sizeof(SVGPoint)) == 0; + } + + bool SetCapacity(uint32_t aSize) { + return mItems.SetCapacity(aSize, fallible); + } + + void Compact() { + mItems.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 + // SVGAnimatedPointList 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 SVGPointList& rhs); + + SVGPoint& operator[](uint32_t aIndex) { + return mItems[aIndex]; + } + + /** + * This may fail (return false) on OOM if the internal capacity is being + * increased, in which case the list will be left unmodified. + */ + bool SetLength(uint32_t aNumberOfItems) { + return mItems.SetLength(aNumberOfItems, fallible); + } + +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() { + mItems.Clear(); + } + + bool InsertItem(uint32_t aIndex, const SVGPoint &aPoint) { + if (aIndex >= mItems.Length()) { + aIndex = mItems.Length(); + } + return !!mItems.InsertElementAt(aIndex, aPoint, fallible); + } + + void ReplaceItem(uint32_t aIndex, const SVGPoint &aPoint) { + MOZ_ASSERT(aIndex < mItems.Length(), + "DOM wrapper caller should have raised INDEX_SIZE_ERR"); + mItems[aIndex] = aPoint; + } + + void RemoveItem(uint32_t aIndex) { + MOZ_ASSERT(aIndex < mItems.Length(), + "DOM wrapper caller should have raised INDEX_SIZE_ERR"); + mItems.RemoveElementAt(aIndex); + } + + bool AppendItem(SVGPoint aPoint) { + return !!mItems.AppendElement(aPoint, fallible); + } + +protected: + + /* See SVGLengthList for the rationale for using FallibleTArray<SVGPoint> instead + * of FallibleTArray<SVGPoint, 1>. + */ + FallibleTArray<SVGPoint> mItems; +}; + + +/** + * This SVGPointList subclass is for SVGPointListSMILType which needs a + * mutable version of SVGPointList. Instances of this class do not have + * DOM wrappers that need to be kept in sync, so we can safely expose any + * protected base class methods required by the SMIL code. + * + * This class contains a strong reference to the element that instances of + * this class are being used to animate. This is because the SMIL code stores + * instances of this class in nsSMILValue objects, some of which are cached. + * Holding a strong reference to the element here prevents the element from + * disappearing out from under the SMIL code unexpectedly. + */ +class SVGPointListAndInfo : public SVGPointList +{ +public: + + explicit SVGPointListAndInfo(nsSVGElement *aElement = nullptr) + : mElement(do_GetWeakReference(static_cast<nsINode*>(aElement))) + {} + + void SetInfo(nsSVGElement *aElement) { + mElement = do_GetWeakReference(static_cast<nsINode*>(aElement)); + } + + nsSVGElement* Element() const { + nsCOMPtr<nsIContent> e = do_QueryReferent(mElement); + return static_cast<nsSVGElement*>(e.get()); + } + + /** + * Returns true if this object is an "identity" value, from the perspective + * of SMIL. In other words, returns true until the initial value set up in + * SVGPointListSMILType::Init() has been changed with a SetInfo() call. + */ + bool IsIdentity() const { + if (!mElement) { + MOZ_ASSERT(IsEmpty(), "target element propagation failure"); + return true; + } + return false; + } + + nsresult CopyFrom(const SVGPointListAndInfo& rhs) { + mElement = rhs.mElement; + return SVGPointList::CopyFrom(rhs); + } + + /** + * Exposed so that SVGPointList baseVals can be copied to + * SVGPointListAndInfo objects. Note that callers should also call + * SetElement() when using this method! + */ + nsresult CopyFrom(const SVGPointList& rhs) { + return SVGPointList::CopyFrom(rhs); + } + const SVGPoint& operator[](uint32_t aIndex) const { + return SVGPointList::operator[](aIndex); + } + SVGPoint& operator[](uint32_t aIndex) { + return SVGPointList::operator[](aIndex); + } + bool SetLength(uint32_t aNumberOfItems) { + return SVGPointList::SetLength(aNumberOfItems); + } + +private: + // We must keep a weak 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 + // See also https://bugzilla.mozilla.org/show_bug.cgi?id=653497 + nsWeakPtr mElement; +}; + +} // namespace mozilla + +#endif // MOZILLA_SVGPOINTLIST_H__ diff --git a/dom/svg/SVGPointListSMILType.cpp b/dom/svg/SVGPointListSMILType.cpp new file mode 100644 index 0000000000..4b8c6abe71 --- /dev/null +++ b/dom/svg/SVGPointListSMILType.cpp @@ -0,0 +1,196 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "SVGPointListSMILType.h" +#include "nsSMILValue.h" +#include "SVGPointList.h" +#include "nsMathUtils.h" +#include "mozilla/FloatingPoint.h" +#include <math.h> + +namespace mozilla { + +/*static*/ SVGPointListSMILType SVGPointListSMILType::sSingleton; + +//---------------------------------------------------------------------- +// nsISMILType implementation + +void +SVGPointListSMILType::Init(nsSMILValue &aValue) const +{ + MOZ_ASSERT(aValue.IsNull(), "Unexpected value type"); + + SVGPointListAndInfo* pointList = new SVGPointListAndInfo(); + + aValue.mU.mPtr = pointList; + aValue.mType = this; +} + +void +SVGPointListSMILType::Destroy(nsSMILValue& aValue) const +{ + NS_PRECONDITION(aValue.mType == this, "Unexpected SMIL value type"); + delete static_cast<SVGPointListAndInfo*>(aValue.mU.mPtr); + aValue.mU.mPtr = nullptr; + aValue.mType = nsSMILNullType::Singleton(); +} + +nsresult +SVGPointListSMILType::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 SVGPointListAndInfo* src = + static_cast<const SVGPointListAndInfo*>(aSrc.mU.mPtr); + SVGPointListAndInfo* dest = + static_cast<SVGPointListAndInfo*>(aDest.mU.mPtr); + + return dest->CopyFrom(*src); +} + +bool +SVGPointListSMILType::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 SVGPointListAndInfo*>(aLeft.mU.mPtr) == + *static_cast<const SVGPointListAndInfo*>(aRight.mU.mPtr); +} + +nsresult +SVGPointListSMILType::Add(nsSMILValue& aDest, + const nsSMILValue& aValueToAdd, + uint32_t aCount) const +{ + NS_PRECONDITION(aDest.mType == this, "Unexpected SMIL type"); + NS_PRECONDITION(aValueToAdd.mType == this, "Incompatible SMIL type"); + + SVGPointListAndInfo& dest = + *static_cast<SVGPointListAndInfo*>(aDest.mU.mPtr); + const SVGPointListAndInfo& valueToAdd = + *static_cast<const SVGPointListAndInfo*>(aValueToAdd.mU.mPtr); + + MOZ_ASSERT(dest.Element() || valueToAdd.Element(), + "Target element propagation failure"); + + if (valueToAdd.IsIdentity()) { + return NS_OK; + } + if (dest.IsIdentity()) { + if (!dest.SetLength(valueToAdd.Length())) { + return NS_ERROR_OUT_OF_MEMORY; + } + for (uint32_t i = 0; i < dest.Length(); ++i) { + dest[i] = aCount * valueToAdd[i]; + } + dest.SetInfo(valueToAdd.Element()); // propagate target element info! + return NS_OK; + } + MOZ_ASSERT(dest.Element() == valueToAdd.Element(), + "adding values from different elements...?"); + if (dest.Length() != valueToAdd.Length()) { + // For now we only support animation between lists with the same number of + // items. SVGContentUtils::ReportToConsole + return NS_ERROR_FAILURE; + } + for (uint32_t i = 0; i < dest.Length(); ++i) { + dest[i] += aCount * valueToAdd[i]; + } + dest.SetInfo(valueToAdd.Element()); // propagate target element info! + return NS_OK; +} + +nsresult +SVGPointListSMILType::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 SVGPointListAndInfo& from = + *static_cast<const SVGPointListAndInfo*>(aFrom.mU.mPtr); + const SVGPointListAndInfo& to = + *static_cast<const SVGPointListAndInfo*>(aTo.mU.mPtr); + + if (from.Length() != to.Length()) { + // Lists in the 'values' attribute must have the same length. + // SVGContentUtils::ReportToConsole + return NS_ERROR_FAILURE; + } + + // We return the root of the sum of the squares of the distances between the + // points at each corresponding index. + + double total = 0.0; + + for (uint32_t i = 0; i < to.Length(); ++i) { + double dx = to[i].mX - from[i].mX; + double dy = to[i].mY - from[i].mY; + total += dx * dx + dy * dy; + } + double distance = sqrt(total); + if (!IsFinite(distance)) { + return NS_ERROR_FAILURE; + } + aDistance = distance; + + return NS_OK; +} + +nsresult +SVGPointListSMILType::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 SVGPointListAndInfo& start = + *static_cast<const SVGPointListAndInfo*>(aStartVal.mU.mPtr); + const SVGPointListAndInfo& end = + *static_cast<const SVGPointListAndInfo*>(aEndVal.mU.mPtr); + SVGPointListAndInfo& result = + *static_cast<SVGPointListAndInfo*>(aResult.mU.mPtr); + + MOZ_ASSERT(end.Element(), "Can't propagate target element"); + MOZ_ASSERT(start.Element() == end.Element() || !start.Element(), + "Different target elements"); + + if (start.Element() && // 'start' is not an "identity" value + start.Length() != end.Length()) { + // For now we only support animation between lists of the same length. + // SVGContentUtils::ReportToConsole + return NS_ERROR_FAILURE; + } + if (!result.SetLength(end.Length())) { + return NS_ERROR_OUT_OF_MEMORY; + } + + result.SetInfo(end.Element()); // propagate target element info! + + if (start.Length() != end.Length()) { + MOZ_ASSERT(start.Length() == 0, "Not an identity value"); + for (uint32_t i = 0; i < end.Length(); ++i) { + result[i] = aUnitDistance * end[i]; + } + return NS_OK; + } + for (uint32_t i = 0; i < end.Length(); ++i) { + result[i] = start[i] + (end[i] - start[i]) * aUnitDistance; + } + return NS_OK; +} + +} // namespace mozilla diff --git a/dom/svg/SVGPointListSMILType.h b/dom/svg/SVGPointListSMILType.h new file mode 100644 index 0000000000..6f58bd42e5 --- /dev/null +++ b/dom/svg/SVGPointListSMILType.h @@ -0,0 +1,55 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef MOZILLA_SVGPOINTLISTSMILTYPE_H_ +#define MOZILLA_SVGPOINTLISTSMILTYPE_H_ + +#include "mozilla/Attributes.h" +#include "nsISMILType.h" + +class nsSMILValue; + +namespace mozilla { + +//////////////////////////////////////////////////////////////////////// +// SVGPointListSMILType +// +// Operations for animating an SVGPointList. +// +class SVGPointListSMILType : public nsISMILType +{ +public: + // Singleton for nsSMILValue objects to hold onto. + static SVGPointListSMILType sSingleton; + +protected: + // nsISMILType Methods + // ------------------- + + virtual void Init(nsSMILValue& aValue) const override; + + virtual void Destroy(nsSMILValue& aValue) const override; + virtual nsresult Assign(nsSMILValue& aDest, const nsSMILValue& aSrc) const override; + virtual bool IsEqual(const nsSMILValue& aLeft, + const nsSMILValue& aRight) const override; + virtual nsresult Add(nsSMILValue& aDest, const nsSMILValue& aValueToAdd, + uint32_t aCount) const override; + virtual nsresult ComputeDistance(const nsSMILValue& aFrom, + const nsSMILValue& aTo, + double& aDistance) const override; + virtual nsresult Interpolate(const nsSMILValue& aStartVal, + const nsSMILValue& aEndVal, + double aUnitDistance, + nsSMILValue& aResult) const override; + +private: + // Private constructor: prevent instances beyond my singleton. + constexpr SVGPointListSMILType() {} +}; + +} // namespace mozilla + +#endif // MOZILLA_SVGPOINTLISTSMILTYPE_H_ diff --git a/dom/svg/SVGPolygonElement.cpp b/dom/svg/SVGPolygonElement.cpp new file mode 100644 index 0000000000..a3feaff6f5 --- /dev/null +++ b/dom/svg/SVGPolygonElement.cpp @@ -0,0 +1,85 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "mozilla/dom/SVGPolygonElement.h" +#include "mozilla/dom/SVGPolygonElementBinding.h" +#include "mozilla/gfx/2D.h" +#include "SVGContentUtils.h" + +using namespace mozilla; +using namespace mozilla::gfx; + +NS_IMPL_NS_NEW_NAMESPACED_SVG_ELEMENT(Polygon) + +namespace mozilla { +namespace dom { + +JSObject* +SVGPolygonElement::WrapNode(JSContext *aCx, JS::Handle<JSObject*> aGivenProto) +{ + return SVGPolygonElementBinding::Wrap(aCx, this, aGivenProto); +} + +//---------------------------------------------------------------------- +// Implementation + +SVGPolygonElement::SVGPolygonElement(already_AddRefed<mozilla::dom::NodeInfo>& aNodeInfo) + : SVGPolygonElementBase(aNodeInfo) +{ +} + +//---------------------------------------------------------------------- +// nsIDOMNode methods + +NS_IMPL_ELEMENT_CLONE_WITH_INIT(SVGPolygonElement) + +//---------------------------------------------------------------------- +// nsSVGPathGeometryElement methods + +void +SVGPolygonElement::GetMarkPoints(nsTArray<nsSVGMark> *aMarks) +{ + nsSVGPolyElement::GetMarkPoints(aMarks); + + if (aMarks->IsEmpty() || aMarks->LastElement().type != nsSVGMark::eEnd) { + return; + } + + nsSVGMark *endMark = &aMarks->LastElement(); + nsSVGMark *startMark = &aMarks->ElementAt(0); + float angle = atan2(startMark->y - endMark->y, startMark->x - endMark->x); + + endMark->type = nsSVGMark::eMid; + endMark->angle = SVGContentUtils::AngleBisect(angle, endMark->angle); + startMark->angle = SVGContentUtils::AngleBisect(angle, startMark->angle); + // for a polygon (as opposed to a polyline) there's an implicit extra point + // co-located with the start point that nsSVGPolyElement::GetMarkPoints + // doesn't return + aMarks->AppendElement(nsSVGMark(startMark->x, startMark->y, startMark->angle, + nsSVGMark::eEnd)); +} + +already_AddRefed<Path> +SVGPolygonElement::BuildPath(PathBuilder* aBuilder) +{ + const SVGPointList &points = mPoints.GetAnimValue(); + + if (points.IsEmpty()) { + return nullptr; + } + + aBuilder->MoveTo(points[0]); + for (uint32_t i = 1; i < points.Length(); ++i) { + aBuilder->LineTo(points[i]); + } + + aBuilder->Close(); + + return aBuilder->Finish(); +} + +} // namespace dom +} // namespace mozilla diff --git a/dom/svg/SVGPolygonElement.h b/dom/svg/SVGPolygonElement.h new file mode 100644 index 0000000000..113ff5820d --- /dev/null +++ b/dom/svg/SVGPolygonElement.h @@ -0,0 +1,40 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef mozilla_dom_SVGPolygonElement_h +#define mozilla_dom_SVGPolygonElement_h + +#include "mozilla/Attributes.h" +#include "nsSVGPolyElement.h" + +nsresult NS_NewSVGPolygonElement(nsIContent **aResult, + already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo); + +typedef nsSVGPolyElement SVGPolygonElementBase; + +namespace mozilla { +namespace dom { + +class SVGPolygonElement final : public SVGPolygonElementBase +{ +protected: + explicit SVGPolygonElement(already_AddRefed<mozilla::dom::NodeInfo>& aNodeInfo); + virtual JSObject* WrapNode(JSContext *cx, JS::Handle<JSObject*> aGivenProto) override; + friend nsresult (::NS_NewSVGPolygonElement(nsIContent **aResult, + already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo)); + +public: + // nsSVGPathGeometryElement methods: + virtual void GetMarkPoints(nsTArray<nsSVGMark> *aMarks) override; + virtual already_AddRefed<Path> BuildPath(PathBuilder* aBuilder) override; + + virtual nsresult Clone(mozilla::dom::NodeInfo *aNodeInfo, nsINode **aResult) const override; +}; + +} // namespace dom +} // namespace mozilla + +#endif // mozilla_dom_SVGPolygonElement_h diff --git a/dom/svg/SVGPolylineElement.cpp b/dom/svg/SVGPolylineElement.cpp new file mode 100644 index 0000000000..df8d49fec9 --- /dev/null +++ b/dom/svg/SVGPolylineElement.cpp @@ -0,0 +1,59 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "mozilla/dom/SVGPolylineElement.h" +#include "mozilla/dom/SVGPolylineElementBinding.h" +#include "mozilla/gfx/2D.h" + +using namespace mozilla; +using namespace mozilla::gfx; + +NS_IMPL_NS_NEW_NAMESPACED_SVG_ELEMENT(Polyline) + +namespace mozilla { +namespace dom { + +JSObject* +SVGPolylineElement::WrapNode(JSContext *aCx, JS::Handle<JSObject*> aGivenProto) +{ + return SVGPolylineElementBinding::Wrap(aCx, this, aGivenProto); +} + +//---------------------------------------------------------------------- +// Implementation + +SVGPolylineElement::SVGPolylineElement(already_AddRefed<mozilla::dom::NodeInfo>& aNodeInfo) + : SVGPolylineElementBase(aNodeInfo) +{ +} + +//---------------------------------------------------------------------- +// nsIDOMNode methods + +NS_IMPL_ELEMENT_CLONE_WITH_INIT(SVGPolylineElement) + +//---------------------------------------------------------------------- +// nsSVGPathGeometryElement methods + +already_AddRefed<Path> +SVGPolylineElement::BuildPath(PathBuilder* aBuilder) +{ + const SVGPointList &points = mPoints.GetAnimValue(); + + if (points.IsEmpty()) { + return nullptr; + } + + aBuilder->MoveTo(points[0]); + for (uint32_t i = 1; i < points.Length(); ++i) { + aBuilder->LineTo(points[i]); + } + + return aBuilder->Finish(); +} + +} // namespace dom +} // namespace mozilla diff --git a/dom/svg/SVGPolylineElement.h b/dom/svg/SVGPolylineElement.h new file mode 100644 index 0000000000..d5e1052d8b --- /dev/null +++ b/dom/svg/SVGPolylineElement.h @@ -0,0 +1,39 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef mozilla_dom_SVGPolylineElement_h +#define mozilla_dom_SVGPolylineElement_h + +#include "nsSVGPolyElement.h" + +nsresult NS_NewSVGPolylineElement(nsIContent **aResult, + already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo); + +typedef nsSVGPolyElement SVGPolylineElementBase; + +namespace mozilla { +namespace dom { + +class SVGPolylineElement final : public SVGPolylineElementBase +{ +protected: + explicit SVGPolylineElement(already_AddRefed<mozilla::dom::NodeInfo>& aNodeInfo); + virtual JSObject* WrapNode(JSContext *cx, JS::Handle<JSObject*> aGivenProto) override; + friend nsresult (::NS_NewSVGPolylineElement(nsIContent **aResult, + already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo)); + + // nsSVGPathGeometryElement methods: + virtual already_AddRefed<Path> BuildPath(PathBuilder* aBuilder) override; + +public: + // nsIContent interface + virtual nsresult Clone(mozilla::dom::NodeInfo *aNodeInfo, nsINode **aResult) const override; +}; + +} // namespace dom +} // namespace mozilla + +#endif // mozilla_dom_SVGPolylineElement_h diff --git a/dom/svg/SVGPreserveAspectRatio.cpp b/dom/svg/SVGPreserveAspectRatio.cpp new file mode 100644 index 0000000000..1bf3388499 --- /dev/null +++ b/dom/svg/SVGPreserveAspectRatio.cpp @@ -0,0 +1,78 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "SVGPreserveAspectRatio.h" +#include "SVGAnimatedPreserveAspectRatio.h" +#include "mozilla/dom/SVGPreserveAspectRatioBinding.h" + +using namespace mozilla; +using namespace dom; + +NS_SVG_VAL_IMPL_CYCLE_COLLECTION_WRAPPERCACHED(DOMSVGPreserveAspectRatio, mSVGElement) + +NS_IMPL_CYCLE_COLLECTING_ADDREF(DOMSVGPreserveAspectRatio) +NS_IMPL_CYCLE_COLLECTING_RELEASE(DOMSVGPreserveAspectRatio) + +NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(DOMSVGPreserveAspectRatio) + NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY + NS_INTERFACE_MAP_ENTRY(nsISupports) +NS_INTERFACE_MAP_END + +bool +SVGPreserveAspectRatio::operator==(const SVGPreserveAspectRatio& aOther) const +{ + return mAlign == aOther.mAlign && + mMeetOrSlice == aOther.mMeetOrSlice; +} + +JSObject* +DOMSVGPreserveAspectRatio::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) +{ + return mozilla::dom::SVGPreserveAspectRatioBinding::Wrap(aCx, this, aGivenProto); +} + +uint16_t +DOMSVGPreserveAspectRatio::Align() +{ + if (mIsBaseValue) { + return mVal->GetBaseValue().GetAlign(); + } + + mSVGElement->FlushAnimations(); + return mVal->GetAnimValue().GetAlign(); +} + +void +DOMSVGPreserveAspectRatio::SetAlign(uint16_t aAlign, ErrorResult& rv) +{ + if (!mIsBaseValue) { + rv.Throw(NS_ERROR_DOM_NO_MODIFICATION_ALLOWED_ERR); + return; + } + rv = mVal->SetBaseAlign(aAlign, mSVGElement); +} + +uint16_t +DOMSVGPreserveAspectRatio::MeetOrSlice() +{ + if (mIsBaseValue) { + return mVal->GetBaseValue().GetMeetOrSlice(); + } + + mSVGElement->FlushAnimations(); + return mVal->GetAnimValue().GetMeetOrSlice(); +} + +void +DOMSVGPreserveAspectRatio::SetMeetOrSlice(uint16_t aMeetOrSlice, ErrorResult& rv) +{ + if (!mIsBaseValue) { + rv.Throw(NS_ERROR_DOM_NO_MODIFICATION_ALLOWED_ERR); + return; + } + rv = mVal->SetBaseMeetOrSlice(aMeetOrSlice, mSVGElement); +} + diff --git a/dom/svg/SVGPreserveAspectRatio.h b/dom/svg/SVGPreserveAspectRatio.h new file mode 100644 index 0000000000..69fe860dfa --- /dev/null +++ b/dom/svg/SVGPreserveAspectRatio.h @@ -0,0 +1,137 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef MOZILLA_CONTENT_SVGPRESERVEASPECTRATIO_H_ +#define MOZILLA_CONTENT_SVGPRESERVEASPECTRATIO_H_ + +#include "mozilla/HashFunctions.h" // for HashGeneric + +#include "nsWrapperCache.h" +#include "nsCycleCollectionParticipant.h" +#include "mozilla/ErrorResult.h" +#include "nsSVGElement.h" + +namespace mozilla { +// Alignment Types +enum SVGAlign : uint8_t { + SVG_PRESERVEASPECTRATIO_UNKNOWN = 0, + SVG_PRESERVEASPECTRATIO_NONE = 1, + SVG_PRESERVEASPECTRATIO_XMINYMIN = 2, + SVG_PRESERVEASPECTRATIO_XMIDYMIN = 3, + SVG_PRESERVEASPECTRATIO_XMAXYMIN = 4, + SVG_PRESERVEASPECTRATIO_XMINYMID = 5, + SVG_PRESERVEASPECTRATIO_XMIDYMID = 6, + SVG_PRESERVEASPECTRATIO_XMAXYMID = 7, + SVG_PRESERVEASPECTRATIO_XMINYMAX = 8, + SVG_PRESERVEASPECTRATIO_XMIDYMAX = 9, + SVG_PRESERVEASPECTRATIO_XMAXYMAX = 10 +}; + +// These constants represent the range of valid enum values for the <align> +// parameter. They exclude the sentinel _UNKNOWN value. +const uint16_t SVG_ALIGN_MIN_VALID = SVG_PRESERVEASPECTRATIO_NONE; +const uint16_t SVG_ALIGN_MAX_VALID = SVG_PRESERVEASPECTRATIO_XMAXYMAX; + +// Meet-or-slice Types +enum SVGMeetOrSlice : uint8_t { + SVG_MEETORSLICE_UNKNOWN = 0, + SVG_MEETORSLICE_MEET = 1, + SVG_MEETORSLICE_SLICE = 2 +}; + +// These constants represent the range of valid enum values for the +// <meetOrSlice> parameter. They exclude the sentinel _UNKNOWN value. +const uint16_t SVG_MEETORSLICE_MIN_VALID = SVG_MEETORSLICE_MEET; +const uint16_t SVG_MEETORSLICE_MAX_VALID = SVG_MEETORSLICE_SLICE; + +class SVGAnimatedPreserveAspectRatio; + +class SVGPreserveAspectRatio final +{ + friend class SVGAnimatedPreserveAspectRatio; +public: + SVGPreserveAspectRatio(SVGAlign aAlign, SVGMeetOrSlice aMeetOrSlice) + : mAlign(aAlign) + , mMeetOrSlice(aMeetOrSlice) + {} + + bool operator==(const SVGPreserveAspectRatio& aOther) const; + + explicit SVGPreserveAspectRatio() + : mAlign(SVG_PRESERVEASPECTRATIO_UNKNOWN) + , mMeetOrSlice(SVG_MEETORSLICE_UNKNOWN) + {} + + nsresult SetAlign(uint16_t aAlign) { + if (aAlign < SVG_ALIGN_MIN_VALID || aAlign > SVG_ALIGN_MAX_VALID) + return NS_ERROR_FAILURE; + mAlign = static_cast<uint8_t>(aAlign); + return NS_OK; + } + + SVGAlign GetAlign() const { + return static_cast<SVGAlign>(mAlign); + } + + nsresult SetMeetOrSlice(uint16_t aMeetOrSlice) { + if (aMeetOrSlice < SVG_MEETORSLICE_MIN_VALID || + aMeetOrSlice > SVG_MEETORSLICE_MAX_VALID) + return NS_ERROR_FAILURE; + mMeetOrSlice = static_cast<uint8_t>(aMeetOrSlice); + return NS_OK; + } + + SVGMeetOrSlice GetMeetOrSlice() const { + return static_cast<SVGMeetOrSlice>(mMeetOrSlice); + } + + uint32_t Hash() const { + return HashGeneric(mAlign, mMeetOrSlice); + } + +private: + // We can't use enum types here because some compilers fail to pack them. + uint8_t mAlign; + uint8_t mMeetOrSlice; +}; + +namespace dom { + +class DOMSVGPreserveAspectRatio final : public nsISupports, + public nsWrapperCache +{ +public: + NS_DECL_CYCLE_COLLECTING_ISUPPORTS + NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(DOMSVGPreserveAspectRatio) + + DOMSVGPreserveAspectRatio(SVGAnimatedPreserveAspectRatio* aVal, + nsSVGElement *aSVGElement, + bool aIsBaseValue) + : mVal(aVal), mSVGElement(aSVGElement), mIsBaseValue(aIsBaseValue) + { + } + + // WebIDL + nsSVGElement* GetParentObject() const { return mSVGElement; } + virtual JSObject* WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override; + + uint16_t Align(); + void SetAlign(uint16_t aAlign, ErrorResult& rv); + uint16_t MeetOrSlice(); + void SetMeetOrSlice(uint16_t aMeetOrSlice, ErrorResult& rv); + +protected: + ~DOMSVGPreserveAspectRatio(); + + SVGAnimatedPreserveAspectRatio* mVal; // kept alive because it belongs to mSVGElement + RefPtr<nsSVGElement> mSVGElement; + const bool mIsBaseValue; +}; + +} //namespace dom +} //namespace mozilla + +#endif // MOZILLA_CONTENT_SVGPRESERVEASPECTRATIO_H_ diff --git a/dom/svg/SVGRect.cpp b/dom/svg/SVGRect.cpp new file mode 100644 index 0000000000..966b0ff4ef --- /dev/null +++ b/dom/svg/SVGRect.cpp @@ -0,0 +1,58 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "mozilla/dom/SVGRect.h" +#include "nsSVGElement.h" + +using namespace mozilla::gfx; + +namespace mozilla { +namespace dom { + +//---------------------------------------------------------------------- +// implementation: + +SVGRect::SVGRect(nsIContent* aParent, float x, float y, float w, float h) + : SVGIRect(), mParent(aParent), mX(x), mY(y), mWidth(w), mHeight(h) +{ +} + +//---------------------------------------------------------------------- +// nsISupports methods: + +NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(SVGRect, mParent) + +NS_IMPL_CYCLE_COLLECTING_ADDREF(SVGRect) +NS_IMPL_CYCLE_COLLECTING_RELEASE(SVGRect) + +NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(SVGRect) + NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY + NS_INTERFACE_MAP_ENTRY(nsISupports) +NS_INTERFACE_MAP_END + +} // namespace dom +} // namespace mozilla + +//////////////////////////////////////////////////////////////////////// +// Exported creation functions: + +already_AddRefed<mozilla::dom::SVGRect> +NS_NewSVGRect(nsIContent* aParent, float aX, float aY, float aWidth, + float aHeight) +{ + RefPtr<mozilla::dom::SVGRect> rect = + new mozilla::dom::SVGRect(aParent, aX, aY, aWidth, aHeight); + + return rect.forget(); +} + +already_AddRefed<mozilla::dom::SVGRect> +NS_NewSVGRect(nsIContent* aParent, const Rect& aRect) +{ + return NS_NewSVGRect(aParent, aRect.x, aRect.y, + aRect.width, aRect.height); +} + diff --git a/dom/svg/SVGRect.h b/dom/svg/SVGRect.h new file mode 100644 index 0000000000..b2e820b91e --- /dev/null +++ b/dom/svg/SVGRect.h @@ -0,0 +1,92 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef mozilla_dom_SVGRect_h +#define mozilla_dom_SVGRect_h + +#include "mozilla/dom/SVGIRect.h" +#include "mozilla/gfx/Rect.h" +#include "nsSVGElement.h" + +//////////////////////////////////////////////////////////////////////// +// SVGRect class + +namespace mozilla { +namespace dom { + +class SVGRect final : public SVGIRect +{ +public: + explicit SVGRect(nsIContent* aParent, float x=0.0f, float y=0.0f, float w=0.0f, + float h=0.0f); + + NS_DECL_CYCLE_COLLECTING_ISUPPORTS + NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(SVGRect) + + // WebIDL + virtual float X() const override final + { + return mX; + } + + virtual void SetX(float aX, ErrorResult& aRv) final + { + mX = aX; + } + + virtual float Y() const override final + { + return mY; + } + + virtual void SetY(float aY, ErrorResult& aRv) final + { + mY = aY; + } + + virtual float Width() const override final + { + return mWidth; + } + + virtual void SetWidth(float aWidth, ErrorResult& aRv) final + { + mWidth = aWidth; + } + + virtual float Height() const override final + { + return mHeight; + } + + virtual void SetHeight(float aHeight, ErrorResult& aRv) final + { + mHeight = aHeight; + } + + virtual nsIContent* GetParentObject() const override + { + return mParent; + } + +protected: + ~SVGRect() {} + + nsCOMPtr<nsIContent> mParent; + float mX, mY, mWidth, mHeight; +}; + +} // namespace dom +} // namespace mozilla + +already_AddRefed<mozilla::dom::SVGRect> +NS_NewSVGRect(nsIContent* aParent, float x=0.0f, float y=0.0f, + float width=0.0f, float height=0.0f); + +already_AddRefed<mozilla::dom::SVGRect> +NS_NewSVGRect(nsIContent* aParent, const mozilla::gfx::Rect& rect); + +#endif //mozilla_dom_SVGRect_h diff --git a/dom/svg/SVGRectElement.cpp b/dom/svg/SVGRectElement.cpp new file mode 100644 index 0000000000..808c7d8190 --- /dev/null +++ b/dom/svg/SVGRectElement.cpp @@ -0,0 +1,238 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "mozilla/dom/SVGRectElement.h" +#include "nsGkAtoms.h" +#include "mozilla/dom/SVGRectElementBinding.h" +#include "mozilla/gfx/2D.h" +#include "mozilla/gfx/Matrix.h" +#include "mozilla/gfx/Rect.h" +#include "mozilla/gfx/PathHelpers.h" +#include <algorithm> + +NS_IMPL_NS_NEW_NAMESPACED_SVG_ELEMENT(Rect) + +using namespace mozilla::gfx; + +namespace mozilla { +namespace dom { + +class SVGAnimatedLength; + +JSObject* +SVGRectElement::WrapNode(JSContext *aCx, JS::Handle<JSObject*> aGivenProto) +{ + return SVGRectElementBinding::Wrap(aCx, this, aGivenProto); +} + +nsSVGElement::LengthInfo SVGRectElement::sLengthInfo[6] = +{ + { &nsGkAtoms::x, 0, nsIDOMSVGLength::SVG_LENGTHTYPE_NUMBER, SVGContentUtils::X }, + { &nsGkAtoms::y, 0, nsIDOMSVGLength::SVG_LENGTHTYPE_NUMBER, SVGContentUtils::Y }, + { &nsGkAtoms::width, 0, nsIDOMSVGLength::SVG_LENGTHTYPE_NUMBER, SVGContentUtils::X }, + { &nsGkAtoms::height, 0, nsIDOMSVGLength::SVG_LENGTHTYPE_NUMBER, SVGContentUtils::Y }, + { &nsGkAtoms::rx, 0, nsIDOMSVGLength::SVG_LENGTHTYPE_NUMBER, SVGContentUtils::X }, + { &nsGkAtoms::ry, 0, nsIDOMSVGLength::SVG_LENGTHTYPE_NUMBER, SVGContentUtils::Y } +}; + +//---------------------------------------------------------------------- +// Implementation + +SVGRectElement::SVGRectElement(already_AddRefed<mozilla::dom::NodeInfo>& aNodeInfo) + : SVGRectElementBase(aNodeInfo) +{ +} + +//---------------------------------------------------------------------- +// nsIDOMNode methods + +NS_IMPL_ELEMENT_CLONE_WITH_INIT(SVGRectElement) + +//---------------------------------------------------------------------- + +already_AddRefed<SVGAnimatedLength> +SVGRectElement::X() +{ + return mLengthAttributes[ATTR_X].ToDOMAnimatedLength(this); +} + +already_AddRefed<SVGAnimatedLength> +SVGRectElement::Y() +{ + return mLengthAttributes[ATTR_Y].ToDOMAnimatedLength(this); +} + +already_AddRefed<SVGAnimatedLength> +SVGRectElement::Width() +{ + return mLengthAttributes[ATTR_WIDTH].ToDOMAnimatedLength(this); +} + +already_AddRefed<SVGAnimatedLength> +SVGRectElement::Height() +{ + return mLengthAttributes[ATTR_HEIGHT].ToDOMAnimatedLength(this); +} + +already_AddRefed<SVGAnimatedLength> +SVGRectElement::Rx() +{ + return mLengthAttributes[ATTR_RX].ToDOMAnimatedLength(this); +} + +already_AddRefed<SVGAnimatedLength> +SVGRectElement::Ry() +{ + return mLengthAttributes[ATTR_RY].ToDOMAnimatedLength(this); +} + +//---------------------------------------------------------------------- +// nsSVGElement methods + +/* virtual */ bool +SVGRectElement::HasValidDimensions() const +{ + return mLengthAttributes[ATTR_WIDTH].IsExplicitlySet() && + mLengthAttributes[ATTR_WIDTH].GetAnimValInSpecifiedUnits() > 0 && + mLengthAttributes[ATTR_HEIGHT].IsExplicitlySet() && + mLengthAttributes[ATTR_HEIGHT].GetAnimValInSpecifiedUnits() > 0; +} + +nsSVGElement::LengthAttributesInfo +SVGRectElement::GetLengthInfo() +{ + return LengthAttributesInfo(mLengthAttributes, sLengthInfo, + ArrayLength(sLengthInfo)); +} + +//---------------------------------------------------------------------- +// nsSVGPathGeometryElement methods + +bool +SVGRectElement::GetGeometryBounds(Rect* aBounds, + const StrokeOptions& aStrokeOptions, + const Matrix& aToBoundsSpace, + const Matrix* aToNonScalingStrokeSpace) +{ + Rect rect; + Float rx, ry; + GetAnimatedLengthValues(&rect.x, &rect.y, &rect.width, + &rect.height, &rx, &ry, nullptr); + + if (rect.IsEmpty()) { + // Rendering of the element disabled + rect.SetEmpty(); // Make sure width/height are zero and not negative + // We still want the x/y position from 'rect' + *aBounds = aToBoundsSpace.TransformBounds(rect); + return true; + } + + if (!aToBoundsSpace.IsRectilinear()) { + // We can't ignore the radii in this case if we want tight bounds + rx = std::max(rx, 0.0f); + ry = std::max(ry, 0.0f); + + if (rx != 0 || ry != 0) { + return false; + } + } + + if (aStrokeOptions.mLineWidth > 0.f) { + if (aToNonScalingStrokeSpace) { + if (aToNonScalingStrokeSpace->IsRectilinear()) { + MOZ_ASSERT(!aToNonScalingStrokeSpace->IsSingular()); + rect = aToNonScalingStrokeSpace->TransformBounds(rect); + // Note that, in principle, an author could cause the corners of the + // rect to be beveled by specifying stroke-linejoin or setting + // stroke-miterlimit to be less than sqrt(2). In that very unlikely + // event the bounds that we calculate here may be too big if + // aToBoundsSpace is non-rectilinear. This is likely to be so rare it's + // not worth handling though. + rect.Inflate(aStrokeOptions.mLineWidth / 2.f); + Matrix nonScalingToBounds = + aToNonScalingStrokeSpace->Inverse() * aToBoundsSpace; + *aBounds = nonScalingToBounds.TransformBounds(rect); + return true; + } + return false; + } + // The "beveled" comment above applies here too + rect.Inflate(aStrokeOptions.mLineWidth / 2.f); + } + + *aBounds = aToBoundsSpace.TransformBounds(rect); + return true; +} + +void +SVGRectElement::GetAsSimplePath(SimplePath* aSimplePath) +{ + float x, y, width, height, rx, ry; + GetAnimatedLengthValues(&x, &y, &width, &height, &rx, &ry, nullptr); + + if (width <= 0 || height <= 0) { + aSimplePath->Reset(); + return; + } + + rx = std::max(rx, 0.0f); + ry = std::max(ry, 0.0f); + + if (rx != 0 || ry != 0) { + aSimplePath->Reset(); + return; + } + + aSimplePath->SetRect(x, y, width, height); +} + +already_AddRefed<Path> +SVGRectElement::BuildPath(PathBuilder* aBuilder) +{ + float x, y, width, height, rx, ry; + GetAnimatedLengthValues(&x, &y, &width, &height, &rx, &ry, nullptr); + + if (width <= 0 || height <= 0) { + return nullptr; + } + + rx = std::max(rx, 0.0f); + ry = std::max(ry, 0.0f); + + if (rx == 0 && ry == 0) { + // Optimization for the no rounded corners case. + Rect r(x, y, width, height); + aBuilder->MoveTo(r.TopLeft()); + aBuilder->LineTo(r.TopRight()); + aBuilder->LineTo(r.BottomRight()); + aBuilder->LineTo(r.BottomLeft()); + aBuilder->Close(); + } else { + // If either the 'rx' or the 'ry' attribute isn't set, then we have to + // set it to the value of the other: + bool hasRx = mLengthAttributes[ATTR_RX].IsExplicitlySet(); + bool hasRy = mLengthAttributes[ATTR_RY].IsExplicitlySet(); + MOZ_ASSERT(hasRx || hasRy); + + if (hasRx && !hasRy) { + ry = rx; + } else if (hasRy && !hasRx) { + rx = ry; + } + + // Clamp rx and ry to half the rect's width and height respectively: + rx = std::min(rx, width / 2); + ry = std::min(ry, height / 2); + + RectCornerRadii radii(rx, ry); + AppendRoundedRectToPath(aBuilder, Rect(x, y, width, height), radii); + } + + return aBuilder->Finish(); +} + +} // namespace dom +} // namespace mozilla diff --git a/dom/svg/SVGRectElement.h b/dom/svg/SVGRectElement.h new file mode 100644 index 0000000000..fe32414eec --- /dev/null +++ b/dom/svg/SVGRectElement.h @@ -0,0 +1,62 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef mozilla_dom_SVGRectElement_h +#define mozilla_dom_SVGRectElement_h + +#include "nsSVGPathGeometryElement.h" +#include "nsSVGLength2.h" + +nsresult NS_NewSVGRectElement(nsIContent **aResult, + already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo); + +typedef nsSVGPathGeometryElement SVGRectElementBase; + +namespace mozilla { +namespace dom { + +class SVGRectElement final : public SVGRectElementBase +{ +protected: + explicit SVGRectElement(already_AddRefed<mozilla::dom::NodeInfo>& aNodeInfo); + virtual JSObject* WrapNode(JSContext *cx, JS::Handle<JSObject*> aGivenProto) override; + friend nsresult (::NS_NewSVGRectElement(nsIContent **aResult, + already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo)); + +public: + // nsSVGSVGElement methods: + virtual bool HasValidDimensions() const override; + + // nsSVGPathGeometryElement methods: + virtual bool GetGeometryBounds(Rect* aBounds, const StrokeOptions& aStrokeOptions, + const Matrix& aToBoundsSpace, + const Matrix* aToNonScalingStrokeSpace = nullptr) override; + virtual void GetAsSimplePath(SimplePath* aSimplePath) override; + virtual already_AddRefed<Path> BuildPath(PathBuilder* aBuilder = nullptr) override; + + virtual nsresult Clone(mozilla::dom::NodeInfo *aNodeInfo, nsINode **aResult) const override; + + // WebIDL + already_AddRefed<SVGAnimatedLength> X(); + already_AddRefed<SVGAnimatedLength> Y(); + already_AddRefed<SVGAnimatedLength> Height(); + already_AddRefed<SVGAnimatedLength> Width(); + already_AddRefed<SVGAnimatedLength> Rx(); + already_AddRefed<SVGAnimatedLength> Ry(); + +protected: + + virtual LengthAttributesInfo GetLengthInfo() override; + + enum { ATTR_X, ATTR_Y, ATTR_WIDTH, ATTR_HEIGHT, ATTR_RX, ATTR_RY }; + nsSVGLength2 mLengthAttributes[6]; + static LengthInfo sLengthInfo[6]; +}; + +} // namespace dom +} // namespace mozilla + +#endif // mozilla_dom_SVGRectElement_h diff --git a/dom/svg/SVGSVGElement.cpp b/dom/svg/SVGSVGElement.cpp new file mode 100644 index 0000000000..90c3c3fff3 --- /dev/null +++ b/dom/svg/SVGSVGElement.cpp @@ -0,0 +1,1192 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include <stdint.h> +#include "mozilla/ArrayUtils.h" +#include "mozilla/ContentEvents.h" +#include "mozilla/EventDispatcher.h" +#include "mozilla/Likely.h" + +#include "nsGkAtoms.h" +#include "nsLayoutUtils.h" +#include "nsLayoutStylesheetCache.h" +#include "DOMSVGNumber.h" +#include "DOMSVGLength.h" +#include "nsSVGAngle.h" +#include "nsCOMPtr.h" +#include "nsIPresShell.h" +#include "nsContentUtils.h" +#include "nsIDocument.h" +#include "mozilla/dom/SVGMatrix.h" +#include "DOMSVGPoint.h" +#include "nsIFrame.h" +#include "nsFrameSelection.h" +#include "nsISVGSVGFrame.h" //XXX +#include "mozilla/dom/SVGRect.h" +#include "nsError.h" +#include "nsISVGChildFrame.h" +#include "mozilla/dom/SVGSVGElement.h" +#include "mozilla/dom/SVGSVGElementBinding.h" +#include "nsSVGUtils.h" +#include "mozilla/dom/SVGViewElement.h" +#include "nsStyleUtil.h" +#include "SVGContentUtils.h" + +#include "nsSMILTimeContainer.h" +#include "nsSMILAnimationController.h" +#include "nsSMILTypes.h" +#include "SVGAngle.h" +#include <algorithm> +#include "prtime.h" + +NS_IMPL_NS_NEW_NAMESPACED_SVG_ELEMENT_CHECK_PARSER(SVG) + +using namespace mozilla::gfx; + +namespace mozilla { +namespace dom { + +class SVGAnimatedLength; + +JSObject* +SVGSVGElement::WrapNode(JSContext *aCx, JS::Handle<JSObject*> aGivenProto) +{ + return SVGSVGElementBinding::Wrap(aCx, this, aGivenProto); +} + +NS_IMPL_CYCLE_COLLECTION_INHERITED(DOMSVGTranslatePoint, nsISVGPoint, + mElement) + +NS_IMPL_ADDREF_INHERITED(DOMSVGTranslatePoint, nsISVGPoint) +NS_IMPL_RELEASE_INHERITED(DOMSVGTranslatePoint, nsISVGPoint) + +NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(DOMSVGTranslatePoint) + NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY + // We have to qualify nsISVGPoint because NS_GET_IID looks for a class in the + // global namespace + NS_INTERFACE_MAP_ENTRY(mozilla::nsISVGPoint) + NS_INTERFACE_MAP_ENTRY(nsISupports) +NS_INTERFACE_MAP_END + +SVGSVGElement::~SVGSVGElement() +{ +} + +DOMSVGPoint* +DOMSVGTranslatePoint::Copy() +{ + return new DOMSVGPoint(mPt.GetX(), mPt.GetY()); +} + +nsISupports* +DOMSVGTranslatePoint::GetParentObject() +{ + return static_cast<nsIDOMSVGElement*>(mElement); +} + +void +DOMSVGTranslatePoint::SetX(float aValue, ErrorResult& rv) +{ + mElement->SetCurrentTranslate(aValue, mPt.GetY()); +} + +void +DOMSVGTranslatePoint::SetY(float aValue, ErrorResult& rv) +{ + mElement->SetCurrentTranslate(mPt.GetX(), aValue); +} + +already_AddRefed<nsISVGPoint> +DOMSVGTranslatePoint::MatrixTransform(SVGMatrix& matrix) +{ + float a = matrix.A(), b = matrix.B(), c = matrix.C(); + float d = matrix.D(), e = matrix.E(), f = matrix.F(); + float x = mPt.GetX(); + float y = mPt.GetY(); + + nsCOMPtr<nsISVGPoint> point = new DOMSVGPoint(a*x + c*y + e, b*x + d*y + f); + return point.forget(); +} + +SVGView::SVGView() +{ + mZoomAndPan.Init(SVGSVGElement::ZOOMANDPAN, + SVG_ZOOMANDPAN_MAGNIFY); + mViewBox.Init(); + mPreserveAspectRatio.Init(); +} + +nsSVGElement::LengthInfo SVGSVGElement::sLengthInfo[4] = +{ + { &nsGkAtoms::x, 0, nsIDOMSVGLength::SVG_LENGTHTYPE_NUMBER, SVGContentUtils::X }, + { &nsGkAtoms::y, 0, nsIDOMSVGLength::SVG_LENGTHTYPE_NUMBER, SVGContentUtils::Y }, + { &nsGkAtoms::width, 100, nsIDOMSVGLength::SVG_LENGTHTYPE_PERCENTAGE, SVGContentUtils::X }, + { &nsGkAtoms::height, 100, nsIDOMSVGLength::SVG_LENGTHTYPE_PERCENTAGE, SVGContentUtils::Y }, +}; + +nsSVGEnumMapping SVGSVGElement::sZoomAndPanMap[] = { + {&nsGkAtoms::disable, SVG_ZOOMANDPAN_DISABLE}, + {&nsGkAtoms::magnify, SVG_ZOOMANDPAN_MAGNIFY}, + {nullptr, 0} +}; + +nsSVGElement::EnumInfo SVGSVGElement::sEnumInfo[1] = +{ + { &nsGkAtoms::zoomAndPan, + sZoomAndPanMap, + SVG_ZOOMANDPAN_MAGNIFY + } +}; + +//---------------------------------------------------------------------- +// nsISupports methods + +NS_IMPL_CYCLE_COLLECTION_CLASS(SVGSVGElement) + +NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(SVGSVGElement, + SVGSVGElementBase) + if (tmp->mTimedDocumentRoot) { + tmp->mTimedDocumentRoot->Unlink(); + } +NS_IMPL_CYCLE_COLLECTION_UNLINK_END +NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(SVGSVGElement, + SVGSVGElementBase) + if (tmp->mTimedDocumentRoot) { + tmp->mTimedDocumentRoot->Traverse(&cb); + } +NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END + +NS_IMPL_ADDREF_INHERITED(SVGSVGElement,SVGSVGElementBase) +NS_IMPL_RELEASE_INHERITED(SVGSVGElement,SVGSVGElementBase) + +NS_INTERFACE_TABLE_HEAD_CYCLE_COLLECTION_INHERITED(SVGSVGElement) + NS_INTERFACE_TABLE_INHERITED(SVGSVGElement, nsIDOMNode, nsIDOMElement, + nsIDOMSVGElement) +NS_INTERFACE_TABLE_TAIL_INHERITING(SVGSVGElementBase) + +//---------------------------------------------------------------------- +// Implementation + +SVGSVGElement::SVGSVGElement(already_AddRefed<mozilla::dom::NodeInfo>& aNodeInfo, + FromParser aFromParser) + : SVGSVGElementBase(aNodeInfo), + mViewportWidth(0), + mViewportHeight(0), + mCurrentTranslate(0.0f, 0.0f), + mCurrentScale(1.0f), + mPreviousTranslate(0.0f, 0.0f), + mPreviousScale(1.0f), + mStartAnimationOnBindToTree(aFromParser == NOT_FROM_PARSER || + aFromParser == FROM_PARSER_FRAGMENT || + aFromParser == FROM_PARSER_XSLT), + mImageNeedsTransformInvalidation(false), + mIsPaintingSVGImageElement(false), + mHasChildrenOnlyTransform(false) +{ +} + +//---------------------------------------------------------------------- +// nsIDOMNode methods + +// From NS_IMPL_ELEMENT_CLONE_WITH_INIT(SVGSVGElement) +nsresult +SVGSVGElement::Clone(mozilla::dom::NodeInfo *aNodeInfo, nsINode **aResult) const +{ + *aResult = nullptr; + already_AddRefed<mozilla::dom::NodeInfo> ni = RefPtr<mozilla::dom::NodeInfo>(aNodeInfo).forget(); + SVGSVGElement *it = new SVGSVGElement(ni, NOT_FROM_PARSER); + + nsCOMPtr<nsINode> kungFuDeathGrip = it; + nsresult rv1 = it->Init(); + nsresult rv2 = const_cast<SVGSVGElement*>(this)->CopyInnerTo(it); + if (NS_SUCCEEDED(rv1) && NS_SUCCEEDED(rv2)) { + kungFuDeathGrip.swap(*aResult); + } + + return NS_FAILED(rv1) ? rv1 : rv2; +} + + +//---------------------------------------------------------------------- +// nsIDOMSVGSVGElement methods: + +already_AddRefed<SVGAnimatedLength> +SVGSVGElement::X() +{ + return mLengthAttributes[ATTR_X].ToDOMAnimatedLength(this); +} + +already_AddRefed<SVGAnimatedLength> +SVGSVGElement::Y() +{ + return mLengthAttributes[ATTR_Y].ToDOMAnimatedLength(this); +} + +already_AddRefed<SVGAnimatedLength> +SVGSVGElement::Width() +{ + return mLengthAttributes[ATTR_WIDTH].ToDOMAnimatedLength(this); +} + +already_AddRefed<SVGAnimatedLength> +SVGSVGElement::Height() +{ + return mLengthAttributes[ATTR_HEIGHT].ToDOMAnimatedLength(this); +} + +float +SVGSVGElement::PixelUnitToMillimeterX() +{ + return MM_PER_INCH_FLOAT / 96; +} + +float +SVGSVGElement::PixelUnitToMillimeterY() +{ + return PixelUnitToMillimeterX(); +} + +float +SVGSVGElement::ScreenPixelToMillimeterX() +{ + return MM_PER_INCH_FLOAT / 96; +} + +float +SVGSVGElement::ScreenPixelToMillimeterY() +{ + return ScreenPixelToMillimeterX(); +} + +bool +SVGSVGElement::UseCurrentView() +{ + return mSVGView || mCurrentViewID; +} + +float +SVGSVGElement::CurrentScale() +{ + return mCurrentScale; +} + +#define CURRENT_SCALE_MAX 16.0f +#define CURRENT_SCALE_MIN 0.0625f + +void +SVGSVGElement::SetCurrentScale(float aCurrentScale) +{ + SetCurrentScaleTranslate(aCurrentScale, + mCurrentTranslate.GetX(), mCurrentTranslate.GetY()); +} + +already_AddRefed<nsISVGPoint> +SVGSVGElement::CurrentTranslate() +{ + nsCOMPtr<nsISVGPoint> point = new DOMSVGTranslatePoint(&mCurrentTranslate, this); + return point.forget(); +} + +uint32_t +SVGSVGElement::SuspendRedraw(uint32_t max_wait_milliseconds) +{ + // suspendRedraw is a no-op in Mozilla, so it doesn't matter what + // we return + return 1; +} + +void +SVGSVGElement::UnsuspendRedraw(uint32_t suspend_handle_id) +{ + // no-op +} + +void +SVGSVGElement::UnsuspendRedrawAll() +{ + // no-op +} + +void +SVGSVGElement::ForceRedraw() +{ + // no-op +} + +void +SVGSVGElement::PauseAnimations() +{ + if (mTimedDocumentRoot) { + mTimedDocumentRoot->Pause(nsSMILTimeContainer::PAUSE_SCRIPT); + } + // else we're not the outermost <svg> or not bound to a tree, so silently fail +} + +void +SVGSVGElement::UnpauseAnimations() +{ + if (mTimedDocumentRoot) { + mTimedDocumentRoot->Resume(nsSMILTimeContainer::PAUSE_SCRIPT); + } + // else we're not the outermost <svg> or not bound to a tree, so silently fail +} + +bool +SVGSVGElement::AnimationsPaused() +{ + nsSMILTimeContainer* root = GetTimedDocumentRoot(); + return root && root->IsPausedByType(nsSMILTimeContainer::PAUSE_SCRIPT); +} + +float +SVGSVGElement::GetCurrentTime() +{ + nsSMILTimeContainer* root = GetTimedDocumentRoot(); + if (root) { + double fCurrentTimeMs = double(root->GetCurrentTime()); + return (float)(fCurrentTimeMs / PR_MSEC_PER_SEC); + } else { + return 0.f; + } +} + +void +SVGSVGElement::SetCurrentTime(float seconds) +{ + if (mTimedDocumentRoot) { + // Make sure the timegraph is up-to-date + FlushAnimations(); + double fMilliseconds = double(seconds) * PR_MSEC_PER_SEC; + // Round to nearest whole number before converting, to avoid precision + // errors + nsSMILTime lMilliseconds = int64_t(NS_round(fMilliseconds)); + mTimedDocumentRoot->SetCurrentTime(lMilliseconds); + AnimationNeedsResample(); + // Trigger synchronous sample now, to: + // - Make sure we get an up-to-date paint after this method + // - re-enable event firing (it got disabled during seeking, and it + // doesn't get re-enabled until the first sample after the seek -- so + // let's make that happen now.) + FlushAnimations(); + } + // else we're not the outermost <svg> or not bound to a tree, so silently fail +} + +void +SVGSVGElement::DeselectAll() +{ + nsIFrame* frame = GetPrimaryFrame(); + if (frame) { + RefPtr<nsFrameSelection> frameSelection = frame->GetFrameSelection(); + frameSelection->ClearNormalSelection(); + } +} + +already_AddRefed<DOMSVGNumber> +SVGSVGElement::CreateSVGNumber() +{ + RefPtr<DOMSVGNumber> number = new DOMSVGNumber(ToSupports(this)); + return number.forget(); +} + +already_AddRefed<DOMSVGLength> +SVGSVGElement::CreateSVGLength() +{ + nsCOMPtr<DOMSVGLength> length = new DOMSVGLength(); + return length.forget(); +} + +already_AddRefed<SVGAngle> +SVGSVGElement::CreateSVGAngle() +{ + nsSVGAngle* angle = new nsSVGAngle(); + angle->Init(); + RefPtr<SVGAngle> svgangle = new SVGAngle(angle, this, SVGAngle::CreatedValue); + return svgangle.forget(); +} + +already_AddRefed<nsISVGPoint> +SVGSVGElement::CreateSVGPoint() +{ + nsCOMPtr<nsISVGPoint> point = new DOMSVGPoint(0, 0); + return point.forget(); +} + +already_AddRefed<SVGMatrix> +SVGSVGElement::CreateSVGMatrix() +{ + RefPtr<SVGMatrix> matrix = new SVGMatrix(); + return matrix.forget(); +} + +already_AddRefed<SVGIRect> +SVGSVGElement::CreateSVGRect() +{ + return NS_NewSVGRect(this); +} + +already_AddRefed<SVGTransform> +SVGSVGElement::CreateSVGTransform() +{ + RefPtr<SVGTransform> transform = new SVGTransform(); + return transform.forget(); +} + +already_AddRefed<SVGTransform> +SVGSVGElement::CreateSVGTransformFromMatrix(SVGMatrix& matrix) +{ + RefPtr<SVGTransform> transform = new SVGTransform(matrix.GetMatrix()); + return transform.forget(); +} + +//---------------------------------------------------------------------- + +already_AddRefed<SVGAnimatedRect> +SVGSVGElement::ViewBox() +{ + return mViewBox.ToSVGAnimatedRect(this); +} + +already_AddRefed<DOMSVGAnimatedPreserveAspectRatio> +SVGSVGElement::PreserveAspectRatio() +{ + return mPreserveAspectRatio.ToDOMAnimatedPreserveAspectRatio(this); +} + +uint16_t +SVGSVGElement::ZoomAndPan() +{ + return mEnumAttributes[ZOOMANDPAN].GetAnimValue(); +} + +void +SVGSVGElement::SetZoomAndPan(uint16_t aZoomAndPan, ErrorResult& rv) +{ + if (aZoomAndPan == SVG_ZOOMANDPAN_DISABLE || + aZoomAndPan == SVG_ZOOMANDPAN_MAGNIFY) { + mEnumAttributes[ZOOMANDPAN].SetBaseValue(aZoomAndPan, this); + return; + } + + rv.ThrowRangeError<MSG_INVALID_ZOOMANDPAN_VALUE_ERROR>(); +} + +//---------------------------------------------------------------------- +// helper methods for implementing SVGZoomEvent: + +void +SVGSVGElement::SetCurrentScaleTranslate(float s, float x, float y) +{ + if (s == mCurrentScale && + x == mCurrentTranslate.GetX() && y == mCurrentTranslate.GetY()) { + return; + } + + // Prevent bizarre behaviour and maxing out of CPU and memory by clamping + if (s < CURRENT_SCALE_MIN) + s = CURRENT_SCALE_MIN; + else if (s > CURRENT_SCALE_MAX) + s = CURRENT_SCALE_MAX; + + // IMPORTANT: If either mCurrentTranslate *or* mCurrentScale is changed then + // mPreviousTranslate_x, mPreviousTranslate_y *and* mPreviousScale must all + // be updated otherwise SVGZoomEvents will end up with invalid data. I.e. an + // SVGZoomEvent's properties previousScale and previousTranslate must contain + // the state of currentScale and currentTranslate immediately before the + // change that caused the event's dispatch, which is *not* necessarily the + // same thing as the values of currentScale and currentTranslate prior to + // their own last change. + mPreviousScale = mCurrentScale; + mPreviousTranslate = mCurrentTranslate; + + mCurrentScale = s; + mCurrentTranslate = SVGPoint(x, y); + + // now dispatch the appropriate event if we are the root element + nsIDocument* doc = GetUncomposedDoc(); + if (doc) { + nsCOMPtr<nsIPresShell> presShell = doc->GetShell(); + if (presShell && IsRoot()) { + nsEventStatus status = nsEventStatus_eIgnore; + if (mPreviousScale != mCurrentScale) { + InternalSVGZoomEvent svgZoomEvent(true, eSVGZoom); + presShell->HandleDOMEventWithTarget(this, &svgZoomEvent, &status); + } else { + WidgetEvent svgScrollEvent(true, eSVGScroll); + presShell->HandleDOMEventWithTarget(this, &svgScrollEvent, &status); + } + InvalidateTransformNotifyFrame(); + } + } +} + +void +SVGSVGElement::SetCurrentTranslate(float x, float y) +{ + SetCurrentScaleTranslate(mCurrentScale, x, y); +} + +nsSMILTimeContainer* +SVGSVGElement::GetTimedDocumentRoot() +{ + if (mTimedDocumentRoot) { + return mTimedDocumentRoot; + } + + // We must not be the outermost <svg> element, try to find it + SVGSVGElement *outerSVGElement = + SVGContentUtils::GetOuterSVGElement(this); + + if (outerSVGElement) { + return outerSVGElement->GetTimedDocumentRoot(); + } + // invalid structure + return nullptr; +} + +//---------------------------------------------------------------------- +// nsIContent methods + +NS_IMETHODIMP_(bool) +SVGSVGElement::IsAttributeMapped(const nsIAtom* name) const +{ + // We want to map the 'width' and 'height' attributes into style for + // outer-<svg>, except when the attributes aren't set (since their default + // values of '100%' can cause unexpected and undesirable behaviour for SVG + // inline in HTML). We rely on nsSVGElement::UpdateContentStyleRule() to + // prevent mapping of the default values into style (it only maps attributes + // that are set). We also rely on a check in nsSVGElement:: + // UpdateContentStyleRule() to prevent us mapping the attributes when they're + // given a <length> value that is not currently recognized by the SVG + // specification. + + if (!IsInner() && (name == nsGkAtoms::width || name == nsGkAtoms::height)) { + return true; + } + + static const MappedAttributeEntry* const map[] = { + sColorMap, + sFEFloodMap, + sFillStrokeMap, + sFiltersMap, + sFontSpecificationMap, + sGradientStopMap, + sGraphicsMap, + sLightingEffectsMap, + sMarkersMap, + sTextContentElementsMap, + sViewportsMap + }; + + return FindAttributeDependence(name, map) || + SVGSVGElementBase::IsAttributeMapped(name); +} + +//---------------------------------------------------------------------- +// nsIContent methods: + +nsresult +SVGSVGElement::PreHandleEvent(EventChainPreVisitor& aVisitor) +{ + if (aVisitor.mEvent->mMessage == eSVGLoad) { + if (mTimedDocumentRoot) { + mTimedDocumentRoot->Begin(); + // Set 'resample needed' flag, so that if any script calls a DOM method + // that requires up-to-date animations before our first sample callback, + // we'll force a synchronous sample. + AnimationNeedsResample(); + } + } + return SVGSVGElementBase::PreHandleEvent(aVisitor); +} + +bool +SVGSVGElement::IsEventAttributeName(nsIAtom* aName) +{ + /* The events in EventNameType_SVGSVG are for events that are only + applicable to outermost 'svg' elements. We don't check if we're an outer + 'svg' element in case we're not inserted into the document yet, but since + the target of the events in question will always be the outermost 'svg' + element, this shouldn't cause any real problems. + */ + return nsContentUtils::IsEventAttributeName(aName, + (EventNameType_SVGGraphic | EventNameType_SVGSVG)); +} + +//---------------------------------------------------------------------- +// nsSVGElement overrides + +// Helper for GetViewBoxTransform on root <svg> node +// * aLength: internal value for our <svg> width or height attribute. +// * aViewportLength: length of the corresponding dimension of the viewport. +// * aSelf: the outermost <svg> node itself. +// NOTE: aSelf is not an ancestor viewport element, so it can't be used to +// resolve percentage lengths. (It can only be used to resolve +// 'em'/'ex'-valued units). +inline float +ComputeSynthesizedViewBoxDimension(const nsSVGLength2& aLength, + float aViewportLength, + const SVGSVGElement* aSelf) +{ + if (aLength.IsPercentage()) { + return aViewportLength * aLength.GetAnimValInSpecifiedUnits() / 100.0f; + } + + return aLength.GetAnimValue(const_cast<SVGSVGElement*>(aSelf)); +} + +//---------------------------------------------------------------------- +// public helpers: + +gfx::Matrix +SVGSVGElement::GetViewBoxTransform() const +{ + float viewportWidth, viewportHeight; + if (IsInner()) { + SVGSVGElement *ctx = GetCtx(); + viewportWidth = mLengthAttributes[ATTR_WIDTH].GetAnimValue(ctx); + viewportHeight = mLengthAttributes[ATTR_HEIGHT].GetAnimValue(ctx); + } else { + viewportWidth = mViewportWidth; + viewportHeight = mViewportHeight; + } + + if (viewportWidth <= 0.0f || viewportHeight <= 0.0f) { + return gfx::Matrix(0.0, 0.0, 0.0, 0.0, 0.0, 0.0); // singular + } + + nsSVGViewBoxRect viewBox = + GetViewBoxWithSynthesis(viewportWidth, viewportHeight); + + if (viewBox.width <= 0.0f || viewBox.height <= 0.0f) { + return gfx::Matrix(0.0, 0.0, 0.0, 0.0, 0.0, 0.0); // singular + } + + return SVGContentUtils::GetViewBoxTransform(viewportWidth, viewportHeight, + viewBox.x, viewBox.y, + viewBox.width, viewBox.height, + GetPreserveAspectRatioWithOverride()); +} + +void +SVGSVGElement::UpdateHasChildrenOnlyTransform() +{ + bool hasChildrenOnlyTransform = + HasViewBoxOrSyntheticViewBox() || + (IsRoot() && (mCurrentTranslate != SVGPoint(0.0f, 0.0f) || + mCurrentScale != 1.0f)); + mHasChildrenOnlyTransform = hasChildrenOnlyTransform; +} + +void +SVGSVGElement::ChildrenOnlyTransformChanged(uint32_t aFlags) +{ + // Avoid wasteful calls: + MOZ_ASSERT(!(GetPrimaryFrame()->GetStateBits() & NS_FRAME_IS_NONDISPLAY), + "Non-display SVG frames don't maintain overflow rects"); + + nsChangeHint changeHint; + + bool hadChildrenOnlyTransform = mHasChildrenOnlyTransform; + + UpdateHasChildrenOnlyTransform(); + + if (hadChildrenOnlyTransform != mHasChildrenOnlyTransform) { + // Reconstruct the frame tree to handle stacking context changes: + // XXXjwatt don't do this for root-<svg> or even outer-<svg>? + changeHint = nsChangeHint_ReconstructFrame; + } else { + // We just assume the old and new transforms are different. + changeHint = nsChangeHint(nsChangeHint_UpdateOverflow | + nsChangeHint_ChildrenOnlyTransform); + } + + // If we're not reconstructing the frame tree, then we only call + // PostRestyleEvent if we're not being called under reflow to avoid recursing + // to death. See bug 767056 comments 10 and 12. Since our nsSVGOuterSVGFrame + // is being reflowed we're going to invalidate and repaint its entire area + // anyway (which will include our children). + if ((changeHint & nsChangeHint_ReconstructFrame) || + !(aFlags & eDuringReflow)) { + nsLayoutUtils::PostRestyleEvent(this, nsRestyleHint(0), changeHint); + } +} + +nsresult +SVGSVGElement::BindToTree(nsIDocument* aDocument, + nsIContent* aParent, + nsIContent* aBindingParent, + bool aCompileEventHandlers) +{ + nsSMILAnimationController* smilController = nullptr; + + if (aDocument) { + smilController = aDocument->GetAnimationController(); + if (smilController) { + // SMIL is enabled in this document + if (WillBeOutermostSVG(aParent, aBindingParent)) { + // We'll be the outermost <svg> element. We'll need a time container. + if (!mTimedDocumentRoot) { + mTimedDocumentRoot = new nsSMILTimeContainer(); + } + } else { + // We're a child of some other <svg> element, so we don't need our own + // time container. However, we need to make sure that we'll get a + // kick-start if we get promoted to be outermost later on. + mTimedDocumentRoot = nullptr; + mStartAnimationOnBindToTree = true; + } + } + } + + nsresult rv = SVGSVGElementBase::BindToTree(aDocument, aParent, + aBindingParent, + aCompileEventHandlers); + NS_ENSURE_SUCCESS(rv,rv); + + nsIDocument* doc = GetComposedDoc(); + if (doc) { + // Setup the style sheet during binding, not element construction, + // because we could move the root SVG element from the document + // that created it to another document. + auto cache = nsLayoutStylesheetCache::For(doc->GetStyleBackendType()); + doc->EnsureOnDemandBuiltInUASheet(cache->SVGSheet()); + } + + if (mTimedDocumentRoot && smilController) { + rv = mTimedDocumentRoot->SetParent(smilController); + if (mStartAnimationOnBindToTree) { + mTimedDocumentRoot->Begin(); + mStartAnimationOnBindToTree = false; + } + } + + return rv; +} + +void +SVGSVGElement::UnbindFromTree(bool aDeep, bool aNullParent) +{ + if (mTimedDocumentRoot) { + mTimedDocumentRoot->SetParent(nullptr); + } + + SVGSVGElementBase::UnbindFromTree(aDeep, aNullParent); +} + +//---------------------------------------------------------------------- +// implementation helpers + +bool +SVGSVGElement::WillBeOutermostSVG(nsIContent* aParent, + nsIContent* aBindingParent) const +{ + nsIContent* parent = aBindingParent ? aBindingParent : aParent; + + while (parent && parent->IsSVGElement()) { + if (parent->IsSVGElement(nsGkAtoms::foreignObject)) { + // SVG in a foreignObject must have its own <svg> (nsSVGOuterSVGFrame). + return false; + } + if (parent->IsSVGElement(nsGkAtoms::svg)) { + return false; + } + parent = parent->GetParent(); + } + + return true; +} + +void +SVGSVGElement::InvalidateTransformNotifyFrame() +{ + nsISVGSVGFrame* svgframe = do_QueryFrame(GetPrimaryFrame()); + // might fail this check if we've failed conditional processing + if (svgframe) { + svgframe->NotifyViewportOrTransformChanged( + nsISVGChildFrame::TRANSFORM_CHANGED); + } +} + +bool +SVGSVGElement::HasPreserveAspectRatio() +{ + return HasAttr(kNameSpaceID_None, nsGkAtoms::preserveAspectRatio) || + mPreserveAspectRatio.IsAnimated(); +} + +SVGViewElement* +SVGSVGElement::GetCurrentViewElement() const +{ + if (mCurrentViewID) { + //XXXsmaug It is unclear how this should work in case we're in Shadow DOM. + nsIDocument* doc = GetUncomposedDoc(); + if (doc) { + Element *element = doc->GetElementById(*mCurrentViewID); + if (element && element->IsSVGElement(nsGkAtoms::view)) { + return static_cast<SVGViewElement*>(element); + } + } + } + return nullptr; +} + +nsSVGViewBoxRect +SVGSVGElement::GetViewBoxWithSynthesis( + float aViewportWidth, float aViewportHeight) const +{ + // The logic here should match HasViewBoxRect(). + SVGViewElement* viewElement = GetCurrentViewElement(); + if (viewElement && viewElement->mViewBox.HasRect()) { + return viewElement->mViewBox.GetAnimValue(); + } + if (mSVGView && mSVGView->mViewBox.HasRect()) { + return mSVGView->mViewBox.GetAnimValue(); + } + if (mViewBox.HasRect()) { + return mViewBox.GetAnimValue(); + } + + if (ShouldSynthesizeViewBox()) { + // Special case -- fake a viewBox, using height & width attrs. + // (Use |this| as context, since if we get here, we're outermost <svg>.) + return nsSVGViewBoxRect(0, 0, + ComputeSynthesizedViewBoxDimension(mLengthAttributes[ATTR_WIDTH], + mViewportWidth, this), + ComputeSynthesizedViewBoxDimension(mLengthAttributes[ATTR_HEIGHT], + mViewportHeight, this)); + + } + + // No viewBox attribute, so we shouldn't auto-scale. This is equivalent + // to having a viewBox that exactly matches our viewport size. + return nsSVGViewBoxRect(0, 0, aViewportWidth, aViewportHeight); +} + +SVGPreserveAspectRatio +SVGSVGElement::GetPreserveAspectRatioWithOverride() const +{ + nsIDocument* doc = GetUncomposedDoc(); + if (doc && doc->IsBeingUsedAsImage()) { + const SVGPreserveAspectRatio *pAROverridePtr = GetPreserveAspectRatioProperty(); + if (pAROverridePtr) { + return *pAROverridePtr; + } + } + + SVGViewElement* viewElement = GetCurrentViewElement(); + + // This check is equivalent to "!HasViewBoxRect() && ShouldSynthesizeViewBox()". + // We're just holding onto the viewElement that HasViewBoxRect() would look up, + // so that we don't have to look it up again later. + if (!((viewElement && viewElement->mViewBox.HasRect()) || + (mSVGView && mSVGView->mViewBox.HasRect()) || + mViewBox.HasRect()) && + ShouldSynthesizeViewBox()) { + // If we're synthesizing a viewBox, use preserveAspectRatio="none"; + return SVGPreserveAspectRatio(SVG_PRESERVEASPECTRATIO_NONE, SVG_MEETORSLICE_SLICE); + } + + if (viewElement && viewElement->mPreserveAspectRatio.IsExplicitlySet()) { + return viewElement->mPreserveAspectRatio.GetAnimValue(); + } + if (mSVGView && mSVGView->mPreserveAspectRatio.IsExplicitlySet()) { + return mSVGView->mPreserveAspectRatio.GetAnimValue(); + } + return mPreserveAspectRatio.GetAnimValue(); +} + +//---------------------------------------------------------------------- +// SVGSVGElement + +float +SVGSVGElement::GetLength(uint8_t aCtxType) +{ + float h, w; + + SVGViewElement* viewElement = GetCurrentViewElement(); + const nsSVGViewBoxRect* viewbox = nullptr; + + // The logic here should match HasViewBoxRect(). + if (viewElement && viewElement->mViewBox.HasRect()) { + viewbox = &viewElement->mViewBox.GetAnimValue(); + } else if (mSVGView && mSVGView->mViewBox.HasRect()) { + viewbox = &mSVGView->mViewBox.GetAnimValue(); + } else if (mViewBox.HasRect()) { + viewbox = &mViewBox.GetAnimValue(); + } + + if (viewbox) { + w = viewbox->width; + h = viewbox->height; + } else if (IsInner()) { + SVGSVGElement *ctx = GetCtx(); + w = mLengthAttributes[ATTR_WIDTH].GetAnimValue(ctx); + h = mLengthAttributes[ATTR_HEIGHT].GetAnimValue(ctx); + } else if (ShouldSynthesizeViewBox()) { + w = ComputeSynthesizedViewBoxDimension(mLengthAttributes[ATTR_WIDTH], + mViewportWidth, this); + h = ComputeSynthesizedViewBoxDimension(mLengthAttributes[ATTR_HEIGHT], + mViewportHeight, this); + } else { + w = mViewportWidth; + h = mViewportHeight; + } + + w = std::max(w, 0.0f); + h = std::max(h, 0.0f); + + switch (aCtxType) { + case SVGContentUtils::X: + return w; + case SVGContentUtils::Y: + return h; + case SVGContentUtils::XY: + return float(SVGContentUtils::ComputeNormalizedHypotenuse(w, h)); + } + return 0; +} + +//---------------------------------------------------------------------- +// nsSVGElement methods + +/* virtual */ gfxMatrix +SVGSVGElement::PrependLocalTransformsTo( + const gfxMatrix &aMatrix, SVGTransformTypes aWhich) const +{ + // 'transform' attribute (or an override from a fragment identifier): + gfxMatrix fromUserSpace = + SVGContentUtils::PrependLocalTransformsTo( + aMatrix, aWhich, mAnimateMotionTransform, + mSVGView && mSVGView->mTransforms ? mSVGView->mTransforms : mTransforms); + + if (aWhich == eUserSpaceToParent) { + return fromUserSpace; + } + + if (IsInner()) { + float x, y; + const_cast<SVGSVGElement*>(this)->GetAnimatedLengthValues(&x, &y, nullptr); + if (aWhich == eAllTransforms) { + // the common case + return ThebesMatrix(GetViewBoxTransform()) * gfxMatrix::Translation(x, y) * fromUserSpace; + } + MOZ_ASSERT(aWhich == eChildToUserSpace, "Unknown TransformTypes"); + return ThebesMatrix(GetViewBoxTransform()) * gfxMatrix::Translation(x, y) * aMatrix; + } + + if (IsRoot()) { + gfxMatrix zoomPanTM; + zoomPanTM.Translate(gfxPoint(mCurrentTranslate.GetX(), mCurrentTranslate.GetY())); + zoomPanTM.Scale(mCurrentScale, mCurrentScale); + return ThebesMatrix(GetViewBoxTransform()) * zoomPanTM * fromUserSpace; + } + + // outer-<svg>, but inline in some other content: + return ThebesMatrix(GetViewBoxTransform()) * fromUserSpace; +} + +nsSVGAnimatedTransformList* +SVGSVGElement::GetAnimatedTransformList(uint32_t aFlags) +{ + if (!(aFlags & DO_ALLOCATE) && mSVGView && mSVGView->mTransforms) { + return mSVGView->mTransforms; + } + return SVGSVGElementBase::GetAnimatedTransformList(aFlags); +} + +/* virtual */ bool +SVGSVGElement::HasValidDimensions() const +{ + return !IsInner() || + ((!mLengthAttributes[ATTR_WIDTH].IsExplicitlySet() || + mLengthAttributes[ATTR_WIDTH].GetAnimValInSpecifiedUnits() > 0) && + (!mLengthAttributes[ATTR_HEIGHT].IsExplicitlySet() || + mLengthAttributes[ATTR_HEIGHT].GetAnimValInSpecifiedUnits() > 0)); +} + +nsSVGElement::LengthAttributesInfo +SVGSVGElement::GetLengthInfo() +{ + return LengthAttributesInfo(mLengthAttributes, sLengthInfo, + ArrayLength(sLengthInfo)); +} + +nsSVGElement::EnumAttributesInfo +SVGSVGElement::GetEnumInfo() +{ + return EnumAttributesInfo(mEnumAttributes, sEnumInfo, + ArrayLength(sEnumInfo)); +} + +nsSVGViewBox* +SVGSVGElement::GetViewBox() +{ + return &mViewBox; +} + +SVGAnimatedPreserveAspectRatio * +SVGSVGElement::GetPreserveAspectRatio() +{ + return &mPreserveAspectRatio; +} + +bool +SVGSVGElement::HasViewBoxRect() const +{ + SVGViewElement* viewElement = GetCurrentViewElement(); + if ((viewElement && viewElement->mViewBox.HasRect()) || + (mSVGView && mSVGView->mViewBox.HasRect())) { + return true; + } + return mViewBox.HasRect(); +} + +bool +SVGSVGElement::ShouldSynthesizeViewBox() const +{ + MOZ_ASSERT(!HasViewBoxRect(), + "Should only be called if we lack a viewBox"); + + nsIDocument* doc = GetUncomposedDoc(); + return doc && + doc->IsBeingUsedAsImage() && + !mIsPaintingSVGImageElement && + !GetParent(); +} + +bool +SVGSVGElement::SetPreserveAspectRatioProperty(const SVGPreserveAspectRatio& aPAR) +{ + SVGPreserveAspectRatio* pAROverridePtr = new SVGPreserveAspectRatio(aPAR); + nsresult rv = SetProperty(nsGkAtoms::overridePreserveAspectRatio, + pAROverridePtr, + nsINode::DeleteProperty<SVGPreserveAspectRatio>, + true); + MOZ_ASSERT(rv != NS_PROPTABLE_PROP_OVERWRITTEN, + "Setting override value when it's already set...?"); + + if (MOZ_UNLIKELY(NS_FAILED(rv))) { + // property-insertion failed (e.g. OOM in property-table code) + delete pAROverridePtr; + return false; + } + return true; +} + +const SVGPreserveAspectRatio* +SVGSVGElement::GetPreserveAspectRatioProperty() const +{ + void* valPtr = GetProperty(nsGkAtoms::overridePreserveAspectRatio); + if (valPtr) { + return static_cast<SVGPreserveAspectRatio*>(valPtr); + } + return nullptr; +} + +bool +SVGSVGElement::ClearPreserveAspectRatioProperty() +{ + void* valPtr = UnsetProperty(nsGkAtoms::overridePreserveAspectRatio); + delete static_cast<SVGPreserveAspectRatio*>(valPtr); + return valPtr; +} + +void +SVGSVGElement::SetIsPaintingForSVGImageElement(bool aIsPaintingSVGImageElement) +{ + mIsPaintingSVGImageElement = aIsPaintingSVGImageElement; +} + +void +SVGSVGElement:: + SetImageOverridePreserveAspectRatio(const SVGPreserveAspectRatio& aPAR) +{ +#ifdef DEBUG + MOZ_ASSERT(OwnerDoc()->IsBeingUsedAsImage(), + "should only override preserveAspectRatio in images"); +#endif + + bool hasViewBoxRect = HasViewBoxRect(); + if (!hasViewBoxRect && ShouldSynthesizeViewBox()) { + // My non-<svg:image> clients will have been painting me with a synthesized + // viewBox, but my <svg:image> client that's about to paint me now does NOT + // want that. Need to tell ourselves to flush our transform. + mImageNeedsTransformInvalidation = true; + } + + if (!hasViewBoxRect) { + return; // preserveAspectRatio irrelevant (only matters if we have viewBox) + } + + if (SetPreserveAspectRatioProperty(aPAR)) { + mImageNeedsTransformInvalidation = true; + } +} + +void +SVGSVGElement::ClearImageOverridePreserveAspectRatio() +{ +#ifdef DEBUG + MOZ_ASSERT(OwnerDoc()->IsBeingUsedAsImage(), + "should only override image preserveAspectRatio in images"); +#endif + + if (!HasViewBoxRect() && ShouldSynthesizeViewBox()) { + // My non-<svg:image> clients will want to paint me with a synthesized + // viewBox, but my <svg:image> client that just painted me did NOT + // use that. Need to tell ourselves to flush our transform. + mImageNeedsTransformInvalidation = true; + } + + if (ClearPreserveAspectRatioProperty()) { + mImageNeedsTransformInvalidation = true; + } +} + +void +SVGSVGElement::FlushImageTransformInvalidation() +{ + MOZ_ASSERT(!GetParent(), "Should only be called on root node"); + MOZ_ASSERT(OwnerDoc()->IsBeingUsedAsImage(), + "Should only be called on image documents"); + + if (mImageNeedsTransformInvalidation) { + InvalidateTransformNotifyFrame(); + mImageNeedsTransformInvalidation = false; + } +} + +int32_t +SVGSVGElement::GetIntrinsicWidth() +{ + if (mLengthAttributes[ATTR_WIDTH].IsPercentage()) { + return -1; + } + // Passing |this| as a SVGSVGElement* invokes the variant of GetAnimValue + // that uses the passed argument as the context, but that's fine since we + // know the length isn't a percentage so the context won't be used (and we + // need to pass the element to be able to resolve em/ex units). + float width = mLengthAttributes[ATTR_WIDTH].GetAnimValue(this); + return nsSVGUtils::ClampToInt(width); +} + +int32_t +SVGSVGElement::GetIntrinsicHeight() +{ + if (mLengthAttributes[ATTR_HEIGHT].IsPercentage()) { + return -1; + } + // Passing |this| as a SVGSVGElement* invokes the variant of GetAnimValue + // that uses the passed argument as the context, but that's fine since we + // know the length isn't a percentage so the context won't be used (and we + // need to pass the element to be able to resolve em/ex units). + float height = mLengthAttributes[ATTR_HEIGHT].GetAnimValue(this); + return nsSVGUtils::ClampToInt(height); +} + +} // namespace dom +} // namespace mozilla diff --git a/dom/svg/SVGSVGElement.h b/dom/svg/SVGSVGElement.h new file mode 100644 index 0000000000..da08ad770c --- /dev/null +++ b/dom/svg/SVGSVGElement.h @@ -0,0 +1,473 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef mozilla_dom_SVGSVGElement_h +#define mozilla_dom_SVGSVGElement_h + +#include "mozilla/dom/FromParser.h" +#include "nsAutoPtr.h" +#include "nsIContentInlines.h" +#include "nsISVGPoint.h" +#include "nsSVGEnum.h" +#include "nsSVGLength2.h" +#include "SVGGraphicsElement.h" +#include "SVGImageContext.h" +#include "nsSVGViewBox.h" +#include "SVGPreserveAspectRatio.h" +#include "SVGAnimatedPreserveAspectRatio.h" +#include "mozilla/Attributes.h" + +nsresult NS_NewSVGSVGElement(nsIContent **aResult, + already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo, + mozilla::dom::FromParser aFromParser); + +class nsSMILTimeContainer; +class nsSVGOuterSVGFrame; +class nsSVGInnerSVGFrame; + +namespace mozilla { +class AutoSVGRenderingState; +class DOMSVGAnimatedPreserveAspectRatio; +class DOMSVGLength; +class DOMSVGNumber; +class EventChainPreVisitor; +class SVGFragmentIdentifier; +class AutoSVGViewHandler; + +namespace dom { +class SVGAngle; +class SVGAnimatedRect; +class SVGMatrix; +class SVGTransform; +class SVGViewElement; +class SVGIRect; + +class SVGSVGElement; + +class DOMSVGTranslatePoint final : public nsISVGPoint { +public: + DOMSVGTranslatePoint(SVGPoint* aPt, SVGSVGElement *aElement) + : nsISVGPoint(aPt, true), mElement(aElement) {} + + explicit DOMSVGTranslatePoint(DOMSVGTranslatePoint* aPt) + : nsISVGPoint(&aPt->mPt, true), mElement(aPt->mElement) {} + + NS_DECL_ISUPPORTS_INHERITED + NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(DOMSVGTranslatePoint, nsISVGPoint) + + virtual DOMSVGPoint* Copy() override; + + // WebIDL + virtual float X() override { return mPt.GetX(); } + virtual float Y() override { return mPt.GetY(); } + virtual void SetX(float aValue, ErrorResult& rv) override; + virtual void SetY(float aValue, ErrorResult& rv) override; + virtual already_AddRefed<nsISVGPoint> MatrixTransform(SVGMatrix& matrix) override; + + virtual nsISupports* GetParentObject() override; + + RefPtr<SVGSVGElement> mElement; + +private: + ~DOMSVGTranslatePoint() {} +}; + +class svgFloatSize { +public: + svgFloatSize(float aWidth, float aHeight) + : width(aWidth) + , height(aHeight) + {} + bool operator!=(const svgFloatSize& rhs) { + return width != rhs.width || height != rhs.height; + } + float width; + float height; +}; + +// Stores svgView arguments of SVG fragment identifiers. +class SVGView { + friend class mozilla::AutoSVGViewHandler; + friend class mozilla::dom::SVGSVGElement; +public: + SVGView(); + +private: + nsSVGEnum mZoomAndPan; + nsSVGViewBox mViewBox; + SVGAnimatedPreserveAspectRatio mPreserveAspectRatio; + nsAutoPtr<nsSVGAnimatedTransformList> mTransforms; +}; + +typedef SVGGraphicsElement SVGSVGElementBase; + +class SVGSVGElement final : public SVGSVGElementBase +{ + friend class ::nsSVGOuterSVGFrame; + friend class ::nsSVGInnerSVGFrame; + friend class mozilla::dom::SVGView; + friend class mozilla::SVGFragmentIdentifier; + friend class mozilla::AutoSVGViewHandler; + friend class mozilla::AutoSVGRenderingState; + + SVGSVGElement(already_AddRefed<mozilla::dom::NodeInfo>& aNodeInfo, + FromParser aFromParser); + virtual JSObject* WrapNode(JSContext *aCx, JS::Handle<JSObject*> aGivenProto) override; + + friend nsresult (::NS_NewSVGSVGElement(nsIContent **aResult, + already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo, + mozilla::dom::FromParser aFromParser)); + + ~SVGSVGElement(); + +public: + // interfaces: + NS_DECL_ISUPPORTS_INHERITED + NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(SVGSVGElement, SVGSVGElementBase) + + /** + * For use by zoom controls to allow currentScale, currentTranslate.x and + * currentTranslate.y to be set by a single operation that dispatches a + * single SVGZoom event (instead of one SVGZoom and two SVGScroll events). + */ + void SetCurrentScaleTranslate(float s, float x, float y); + + /** + * Retrieve the value of currentScale and currentTranslate. + */ + const SVGPoint& GetCurrentTranslate() { return mCurrentTranslate; } + float GetCurrentScale() { return mCurrentScale; } + + /** + * Retrieve the value of currentScale, currentTranslate.x or + * currentTranslate.y prior to the last change made to any one of them. + */ + const SVGPoint& GetPreviousTranslate() { return mPreviousTranslate; } + float GetPreviousScale() { return mPreviousScale; } + + nsSMILTimeContainer* GetTimedDocumentRoot(); + + // nsIContent interface + NS_IMETHOD_(bool) IsAttributeMapped(const nsIAtom* aAttribute) const override; + virtual nsresult PreHandleEvent(EventChainPreVisitor& aVisitor) override; + + virtual bool IsEventAttributeName(nsIAtom* aName) override; + + // nsSVGElement specializations: + virtual gfxMatrix PrependLocalTransformsTo( + const gfxMatrix &aMatrix, + SVGTransformTypes aWhich = eAllTransforms) const override; + virtual nsSVGAnimatedTransformList* + GetAnimatedTransformList(uint32_t aFlags = 0) override; + virtual bool HasValidDimensions() const override; + + // SVGSVGElement methods: + float GetLength(uint8_t mCtxType); + + // public helpers: + + /** + * Returns -1 if the width/height is a percentage, else returns the user unit + * length clamped to fit in a int32_t. + * XXX see bug 1112533 comment 3 - we should fix drawImage so that we can + * change these methods to make zero the error flag for percentages. + */ + int32_t GetIntrinsicWidth(); + int32_t GetIntrinsicHeight(); + + /** + * Returns true if this element has a base/anim value for its "viewBox" + * attribute that defines a viewBox rectangle with finite values, or + * if there is a view element overriding this element's viewBox and it + * has a valid viewBox. + * + * Note that this does not check whether we need to synthesize a viewBox, + * so you must call ShouldSynthesizeViewBox() if you need to check that too. + * + * Note also that this method does not pay attention to whether the width or + * height values of the viewBox rect are positive! + */ + bool HasViewBoxRect() const; + + /** + * Returns true if we should synthesize a viewBox for ourselves (that is, if + * we're the root element in an image document, and we're not currently being + * painted for an <svg:image> element). + * + * Only call this method if HasViewBoxRect() returns false. + */ + bool ShouldSynthesizeViewBox() const; + + bool HasViewBoxOrSyntheticViewBox() const { + return HasViewBoxRect() || ShouldSynthesizeViewBox(); + } + + gfx::Matrix GetViewBoxTransform() const; + + bool HasChildrenOnlyTransform() const { + return mHasChildrenOnlyTransform; + } + + void UpdateHasChildrenOnlyTransform(); + + enum ChildrenOnlyTransformChangedFlags { + eDuringReflow = 1 + }; + + /** + * This method notifies the style system that the overflow rects of our + * immediate childrens' frames need to be updated. It is called by our own + * frame when changes (e.g. to currentScale) cause our children-only + * transform to change. + * + * The reason we have this method instead of overriding + * GetAttributeChangeHint is because we need to act on non-attribute (e.g. + * currentScale) changes in addition to attribute (e.g. viewBox) changes. + */ + void ChildrenOnlyTransformChanged(uint32_t aFlags = 0); + + // This services any pending notifications for the transform on on this root + // <svg> node needing to be recalculated. (Only applicable in + // SVG-as-an-image documents.) + virtual void FlushImageTransformInvalidation(); + + virtual nsresult Clone(mozilla::dom::NodeInfo *aNodeInfo, nsINode **aResult) const override; + + // Returns true IFF our attributes are currently overridden by a <view> + // element and that element's ID matches the passed-in string. + bool IsOverriddenBy(const nsAString &aViewID) const { + return mCurrentViewID && mCurrentViewID->Equals(aViewID); + } + + svgFloatSize GetViewportSize() const { + return svgFloatSize(mViewportWidth, mViewportHeight); + } + + void SetViewportSize(const svgFloatSize& aSize) { + mViewportWidth = aSize.width; + mViewportHeight = aSize.height; + } + + // WebIDL + already_AddRefed<SVGAnimatedLength> X(); + already_AddRefed<SVGAnimatedLength> Y(); + already_AddRefed<SVGAnimatedLength> Width(); + already_AddRefed<SVGAnimatedLength> Height(); + float PixelUnitToMillimeterX(); + float PixelUnitToMillimeterY(); + float ScreenPixelToMillimeterX(); + float ScreenPixelToMillimeterY(); + bool UseCurrentView(); + float CurrentScale(); + void SetCurrentScale(float aCurrentScale); + already_AddRefed<nsISVGPoint> CurrentTranslate(); + void SetCurrentTranslate(float x, float y); + uint32_t SuspendRedraw(uint32_t max_wait_milliseconds); + void UnsuspendRedraw(uint32_t suspend_handle_id); + void UnsuspendRedrawAll(); + void ForceRedraw(); + void PauseAnimations(); + void UnpauseAnimations(); + bool AnimationsPaused(); + float GetCurrentTime(); + void SetCurrentTime(float seconds); + void DeselectAll(); + already_AddRefed<DOMSVGNumber> CreateSVGNumber(); + already_AddRefed<DOMSVGLength> CreateSVGLength(); + already_AddRefed<SVGAngle> CreateSVGAngle(); + already_AddRefed<nsISVGPoint> CreateSVGPoint(); + already_AddRefed<SVGMatrix> CreateSVGMatrix(); + already_AddRefed<SVGIRect> CreateSVGRect(); + already_AddRefed<SVGTransform> CreateSVGTransform(); + already_AddRefed<SVGTransform> CreateSVGTransformFromMatrix(SVGMatrix& matrix); + using nsINode::GetElementById; // This does what we want + already_AddRefed<SVGAnimatedRect> ViewBox(); + already_AddRefed<DOMSVGAnimatedPreserveAspectRatio> PreserveAspectRatio(); + uint16_t ZoomAndPan(); + void SetZoomAndPan(uint16_t aZoomAndPan, ErrorResult& rv); + virtual nsSVGViewBox* GetViewBox() override; + +private: + // nsSVGElement overrides + + virtual nsresult BindToTree(nsIDocument* aDocument, nsIContent* aParent, + nsIContent* aBindingParent, + bool aCompileEventHandlers) override; + virtual void UnbindFromTree(bool aDeep, bool aNullParent) override; + + // implementation helpers: + + SVGViewElement* GetCurrentViewElement() const; + + // Methods for <image> elements to override my "PreserveAspectRatio" value. + // These are private so that only our friends (AutoSVGRenderingState in + // particular) have access. + void SetImageOverridePreserveAspectRatio(const SVGPreserveAspectRatio& aPAR); + void ClearImageOverridePreserveAspectRatio(); + + // Set/Clear properties to hold old version of preserveAspectRatio + // when it's being overridden by an <image> element that we are inside of. + bool SetPreserveAspectRatioProperty(const SVGPreserveAspectRatio& aPAR); + const SVGPreserveAspectRatio* GetPreserveAspectRatioProperty() const; + bool ClearPreserveAspectRatioProperty(); + + void SetIsPaintingForSVGImageElement(bool aIsPaintingSVGImageElement); + + bool IsRoot() const { + NS_ASSERTION((IsInUncomposedDoc() && !GetParent()) == + (OwnerDoc() && (OwnerDoc()->GetRootElement() == this)), + "Can't determine if we're root"); + return IsInUncomposedDoc() && !GetParent(); + } + + /** + * Returns true if this is an SVG <svg> element that is the child of + * another non-foreignObject SVG element. + */ + bool IsInner() const { + const nsIContent *parent = GetFlattenedTreeParent(); + return parent && parent->IsSVGElement() && + !parent->IsSVGElement(nsGkAtoms::foreignObject); + } + + /* + * While binding to the tree we need to determine if we will be the outermost + * <svg> element _before_ the children are bound (as they want to know what + * timed document root to register with) and therefore _before_ our parent is + * set (both actions are performed by Element::BindToTree) so we + * can't use GetOwnerSVGElement() as it relies on GetParent(). This code is + * basically a simplified version of GetOwnerSVGElement that uses the parent + * parameters passed in instead. + */ + bool WillBeOutermostSVG(nsIContent* aParent, + nsIContent* aBindingParent) const; + + // invalidate viewbox -> viewport xform & inform frames + void InvalidateTransformNotifyFrame(); + + // Returns true if we have at least one of the following: + // - a (valid or invalid) value for the preserveAspectRatio attribute + // - a SMIL-animated value for the preserveAspectRatio attribute + bool HasPreserveAspectRatio(); + + /** + * Returns the explicit viewBox rect, if specified, or else a synthesized + * viewBox, if appropriate, or else a viewBox matching the dimensions of the + * SVG viewport. + */ + nsSVGViewBoxRect GetViewBoxWithSynthesis( + float aViewportWidth, float aViewportHeight) const; + /** + * Returns the explicit or default preserveAspectRatio, unless we're + * synthesizing a viewBox, in which case it returns the "none" value. + */ + SVGPreserveAspectRatio GetPreserveAspectRatioWithOverride() const; + + virtual LengthAttributesInfo GetLengthInfo() override; + + enum { ATTR_X, ATTR_Y, ATTR_WIDTH, ATTR_HEIGHT }; + nsSVGLength2 mLengthAttributes[4]; + static LengthInfo sLengthInfo[4]; + + virtual EnumAttributesInfo GetEnumInfo() override; + + enum { ZOOMANDPAN }; + nsSVGEnum mEnumAttributes[1]; + static nsSVGEnumMapping sZoomAndPanMap[]; + static EnumInfo sEnumInfo[1]; + + virtual SVGAnimatedPreserveAspectRatio *GetPreserveAspectRatio() override; + + nsSVGViewBox mViewBox; + SVGAnimatedPreserveAspectRatio mPreserveAspectRatio; + + // mCurrentViewID and mSVGView are mutually exclusive; we can have + // at most one non-null. + nsAutoPtr<nsString> mCurrentViewID; + nsAutoPtr<SVGView> mSVGView; + + // The size of the rectangular SVG viewport into which we render. This is + // not (necessarily) the same as the content area. See: + // + // http://www.w3.org/TR/SVG11/coords.html#ViewportSpace + // + // XXXjwatt Currently only used for outer <svg>, but maybe we could use -1 to + // flag this as an inner <svg> to save the overhead of GetCtx calls? + // XXXjwatt our frame should probably reset these when it's destroyed. + float mViewportWidth, mViewportHeight; + + // The time container for animations within this SVG document fragment. Set + // for all outermost <svg> elements (not nested <svg> elements). + nsAutoPtr<nsSMILTimeContainer> mTimedDocumentRoot; + + // zoom and pan + // IMPORTANT: see the comment in RecordCurrentScaleTranslate before writing + // code to change any of these! + SVGPoint mCurrentTranslate; + float mCurrentScale; + SVGPoint mPreviousTranslate; + float mPreviousScale; + + // For outermost <svg> elements created from parsing, animation is started by + // the onload event in accordance with the SVG spec, but for <svg> elements + // created by script or promoted from inner <svg> to outermost <svg> we need + // to manually kick off animation when they are bound to the tree. + bool mStartAnimationOnBindToTree; + bool mImageNeedsTransformInvalidation; + bool mIsPaintingSVGImageElement; + bool mHasChildrenOnlyTransform; +}; + +} // namespace dom + +// Helper class to automatically manage temporary changes to an SVG document's +// state for rendering purposes. +class MOZ_RAII AutoSVGRenderingState +{ +public: + AutoSVGRenderingState(const Maybe<SVGImageContext>& aSVGContext, + float aFrameTime, + dom::SVGSVGElement* aRootElem + MOZ_GUARD_OBJECT_NOTIFIER_PARAM) + : mHaveOverrides(aSVGContext.isSome() && + aSVGContext->GetPreserveAspectRatio().isSome()) + , mRootElem(aRootElem) + { + MOZ_GUARD_OBJECT_NOTIFIER_INIT; + MOZ_ASSERT(mRootElem, "No SVG node to manage?"); + if (mHaveOverrides) { + // Override preserveAspectRatio in our helper document. + // XXXdholbert We should technically be overriding the helper doc's clip + // and overflow properties here, too. See bug 272288 comment 36. + mRootElem->SetImageOverridePreserveAspectRatio( + *aSVGContext->GetPreserveAspectRatio()); + mRootElem->SetIsPaintingForSVGImageElement( + aSVGContext->IsPaintingForSVGImageElement()); + } + + mOriginalTime = mRootElem->GetCurrentTime(); + mRootElem->SetCurrentTime(aFrameTime); // Does nothing if there's no change. + } + + ~AutoSVGRenderingState() + { + mRootElem->SetCurrentTime(mOriginalTime); + if (mHaveOverrides) { + mRootElem->ClearImageOverridePreserveAspectRatio(); + mRootElem->SetIsPaintingForSVGImageElement(false); + } + } + +private: + const bool mHaveOverrides; + float mOriginalTime; + const RefPtr<dom::SVGSVGElement> mRootElem; + MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER +}; + +} // namespace mozilla + +#endif // SVGSVGElement_h diff --git a/dom/svg/SVGScriptElement.cpp b/dom/svg/SVGScriptElement.cpp new file mode 100644 index 0000000000..ffc049c212 --- /dev/null +++ b/dom/svg/SVGScriptElement.cpp @@ -0,0 +1,248 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "nsGkAtoms.h" +#include "nsNetUtil.h" +#include "nsContentUtils.h" +#include "mozilla/dom/SVGScriptElement.h" +#include "mozilla/dom/SVGScriptElementBinding.h" + +NS_IMPL_NS_NEW_NAMESPACED_SVG_ELEMENT_CHECK_PARSER(Script) + +namespace mozilla { +namespace dom { + +JSObject* +SVGScriptElement::WrapNode(JSContext *aCx, JS::Handle<JSObject*> aGivenProto) +{ + return SVGScriptElementBinding::Wrap(aCx, this, aGivenProto); +} + +nsSVGElement::StringInfo SVGScriptElement::sStringInfo[2] = +{ + { &nsGkAtoms::href, kNameSpaceID_None, false }, + { &nsGkAtoms::href, kNameSpaceID_XLink, false } +}; + +//---------------------------------------------------------------------- +// nsISupports methods + +NS_IMPL_ISUPPORTS_INHERITED(SVGScriptElement, SVGScriptElementBase, + nsIDOMNode, nsIDOMElement, + nsIDOMSVGElement, + nsIScriptLoaderObserver, + nsIScriptElement, nsIMutationObserver) + +//---------------------------------------------------------------------- +// Implementation + +SVGScriptElement::SVGScriptElement(already_AddRefed<mozilla::dom::NodeInfo>& aNodeInfo, + FromParser aFromParser) + : SVGScriptElementBase(aNodeInfo) + , nsScriptElement(aFromParser) +{ + AddMutationObserver(this); +} + +SVGScriptElement::~SVGScriptElement() +{ +} + +//---------------------------------------------------------------------- +// nsIDOMNode methods + +nsresult +SVGScriptElement::Clone(mozilla::dom::NodeInfo *aNodeInfo, nsINode **aResult) const +{ + *aResult = nullptr; + + already_AddRefed<mozilla::dom::NodeInfo> ni = RefPtr<mozilla::dom::NodeInfo>(aNodeInfo).forget(); + SVGScriptElement* it = new SVGScriptElement(ni, NOT_FROM_PARSER); + + nsCOMPtr<nsINode> kungFuDeathGrip = it; + nsresult rv1 = it->Init(); + nsresult rv2 = const_cast<SVGScriptElement*>(this)->CopyInnerTo(it); + NS_ENSURE_SUCCESS(rv1, rv1); + NS_ENSURE_SUCCESS(rv2, rv2); + + // The clone should be marked evaluated if we are. + it->mAlreadyStarted = mAlreadyStarted; + it->mLineNumber = mLineNumber; + it->mMalformed = mMalformed; + + kungFuDeathGrip.swap(*aResult); + + return NS_OK; +} + +//---------------------------------------------------------------------- +void +SVGScriptElement::GetType(nsAString & aType) +{ + GetScriptType(aType); +} + +void +SVGScriptElement::SetType(const nsAString & aType, ErrorResult& rv) +{ + rv = SetAttr(kNameSpaceID_None, nsGkAtoms::type, aType, true); +} + +void +SVGScriptElement::GetCrossOrigin(nsAString & aCrossOrigin) +{ + // Null for both missing and invalid defaults is ok, since we + // always parse to an enum value, so we don't need an invalid + // default, and we _want_ the missing default to be null. + GetEnumAttr(nsGkAtoms::crossorigin, nullptr, aCrossOrigin); +} + +void +SVGScriptElement::SetCrossOrigin(const nsAString & aCrossOrigin, + ErrorResult& aError) +{ + SetOrRemoveNullableStringAttr(nsGkAtoms::crossorigin, aCrossOrigin, aError); +} + +already_AddRefed<SVGAnimatedString> +SVGScriptElement::Href() +{ + return mStringAttributes[HREF].IsExplicitlySet() + ? mStringAttributes[HREF].ToDOMAnimatedString(this) + : mStringAttributes[XLINK_HREF].ToDOMAnimatedString(this); +} + +//---------------------------------------------------------------------- +// nsIScriptElement methods + +bool +SVGScriptElement::GetScriptType(nsAString& type) +{ + return GetAttr(kNameSpaceID_None, nsGkAtoms::type, type); +} + +void +SVGScriptElement::GetScriptText(nsAString& text) +{ + nsContentUtils::GetNodeTextContent(this, false, text); +} + +void +SVGScriptElement::GetScriptCharset(nsAString& charset) +{ + charset.Truncate(); +} + +void +SVGScriptElement::FreezeUriAsyncDefer() +{ + if (mFrozen) { + return; + } + + if (mStringAttributes[HREF].IsExplicitlySet() || + mStringAttributes[XLINK_HREF].IsExplicitlySet()) { + // variation of this code in nsHTMLScriptElement - check if changes + // need to be transfered when modifying + nsAutoString src; + if (mStringAttributes[HREF].IsExplicitlySet()) { + mStringAttributes[HREF].GetAnimValue(src, this); + } else { + mStringAttributes[XLINK_HREF].GetAnimValue(src, this); + } + + // Empty src should be treated as invalid URL. + if (!src.IsEmpty()) { + nsCOMPtr<nsIURI> baseURI = GetBaseURI(); + NS_NewURI(getter_AddRefs(mUri), src, nullptr, baseURI); + } + + // At this point mUri will be null for invalid URLs. + mExternal = true; + } + + mFrozen = true; +} + +//---------------------------------------------------------------------- +// nsScriptElement methods + +bool +SVGScriptElement::HasScriptContent() +{ + return (mFrozen ? mExternal + : mStringAttributes[HREF].IsExplicitlySet() || + mStringAttributes[XLINK_HREF].IsExplicitlySet()) || + nsContentUtils::HasNonEmptyTextContent(this); +} + +//---------------------------------------------------------------------- +// nsSVGElement methods + +nsSVGElement::StringAttributesInfo +SVGScriptElement::GetStringInfo() +{ + return StringAttributesInfo(mStringAttributes, sStringInfo, + ArrayLength(sStringInfo)); +} + +//---------------------------------------------------------------------- +// nsIContent methods + +nsresult +SVGScriptElement::BindToTree(nsIDocument* aDocument, nsIContent* aParent, + nsIContent* aBindingParent, + bool aCompileEventHandlers) +{ + nsresult rv = SVGScriptElementBase::BindToTree(aDocument, aParent, + aBindingParent, + aCompileEventHandlers); + NS_ENSURE_SUCCESS(rv, rv); + + if (aDocument) { + MaybeProcessScript(); + } + + return NS_OK; +} + +nsresult +SVGScriptElement::AfterSetAttr(int32_t aNamespaceID, nsIAtom* aName, + const nsAttrValue* aValue, bool aNotify) +{ + if ((aNamespaceID == kNameSpaceID_XLink || + aNamespaceID == kNameSpaceID_None) && + aName == nsGkAtoms::href) { + MaybeProcessScript(); + } + return SVGScriptElementBase::AfterSetAttr(aNamespaceID, aName, + aValue, aNotify); +} + +bool +SVGScriptElement::ParseAttribute(int32_t aNamespaceID, + nsIAtom* aAttribute, + const nsAString& aValue, + nsAttrValue& aResult) +{ + if (aNamespaceID == kNameSpaceID_None && + aAttribute == nsGkAtoms::crossorigin) { + ParseCORSValue(aValue, aResult); + return true; + } + + return SVGScriptElementBase::ParseAttribute(aNamespaceID, aAttribute, + aValue, aResult); +} + +CORSMode +SVGScriptElement::GetCORSMode() const +{ + return AttrValueToCORSMode(GetParsedAttr(nsGkAtoms::crossorigin)); +} + +} // namespace dom +} // namespace mozilla diff --git a/dom/svg/SVGScriptElement.h b/dom/svg/SVGScriptElement.h new file mode 100644 index 0000000000..620a1bcde0 --- /dev/null +++ b/dom/svg/SVGScriptElement.h @@ -0,0 +1,86 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef mozilla_dom_SVGScriptElement_h +#define mozilla_dom_SVGScriptElement_h + +#include "nsSVGElement.h" +#include "nsCOMPtr.h" +#include "nsSVGString.h" +#include "nsScriptElement.h" + +class nsIDocument; + +nsresult NS_NewSVGScriptElement(nsIContent **aResult, + already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo, + mozilla::dom::FromParser aFromParser); + +namespace mozilla { +namespace dom { + +typedef nsSVGElement SVGScriptElementBase; + +class SVGScriptElement final : public SVGScriptElementBase, + public nsScriptElement +{ +protected: + friend nsresult (::NS_NewSVGScriptElement(nsIContent **aResult, + already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo, + mozilla::dom::FromParser aFromParser)); + SVGScriptElement(already_AddRefed<mozilla::dom::NodeInfo>& aNodeInfo, + FromParser aFromParser); + + virtual JSObject* WrapNode(JSContext *aCx, JS::Handle<JSObject*> aGivenProto) override; + +public: + // interfaces: + + NS_DECL_ISUPPORTS_INHERITED + + // nsIScriptElement + virtual bool GetScriptType(nsAString& type) override; + virtual void GetScriptText(nsAString& text) override; + virtual void GetScriptCharset(nsAString& charset) override; + virtual void FreezeUriAsyncDefer() override; + virtual CORSMode GetCORSMode() const override; + + // nsScriptElement + virtual bool HasScriptContent() override; + + // nsIContent specializations: + virtual nsresult BindToTree(nsIDocument* aDocument, nsIContent* aParent, + nsIContent* aBindingParent, + bool aCompileEventHandlers) override; + virtual nsresult AfterSetAttr(int32_t aNamespaceID, nsIAtom* aName, + const nsAttrValue* aValue, bool aNotify) override; + virtual bool ParseAttribute(int32_t aNamespaceID, + nsIAtom* aAttribute, + const nsAString& aValue, + nsAttrValue& aResult) override; + + virtual nsresult Clone(mozilla::dom::NodeInfo *aNodeInfo, nsINode **aResult) const override; + + // WebIDL + void GetType(nsAString & aType); + void SetType(const nsAString & aType, ErrorResult& rv); + void GetCrossOrigin(nsAString & aCrossOrigin); + void SetCrossOrigin(const nsAString & aCrossOrigin, ErrorResult& aError); + already_AddRefed<SVGAnimatedString> Href(); + +protected: + ~SVGScriptElement(); + + virtual StringAttributesInfo GetStringInfo() override; + + enum { HREF, XLINK_HREF }; + nsSVGString mStringAttributes[2]; + static StringInfo sStringInfo[2]; +}; + +} // namespace dom +} // namespace mozilla + +#endif // mozilla_dom_SVGScriptElement_h diff --git a/dom/svg/SVGSetElement.cpp b/dom/svg/SVGSetElement.cpp new file mode 100644 index 0000000000..5073d17b49 --- /dev/null +++ b/dom/svg/SVGSetElement.cpp @@ -0,0 +1,44 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "mozilla/dom/SVGSetElement.h" +#include "mozilla/dom/SVGSetElementBinding.h" + +NS_IMPL_NS_NEW_NAMESPACED_SVG_ELEMENT(Set) + +namespace mozilla { +namespace dom { + +JSObject* +SVGSetElement::WrapNode(JSContext *aCx, JS::Handle<JSObject*> aGivenProto) +{ + return SVGSetElementBinding::Wrap(aCx, this, aGivenProto); +} + +//---------------------------------------------------------------------- +// Implementation + +SVGSetElement::SVGSetElement(already_AddRefed<mozilla::dom::NodeInfo>& aNodeInfo) + : SVGAnimationElement(aNodeInfo) +{ +} + +//---------------------------------------------------------------------- +// nsIDOMNode methods + +NS_IMPL_ELEMENT_CLONE_WITH_INIT(SVGSetElement) + +//---------------------------------------------------------------------- + +nsSMILAnimationFunction& +SVGSetElement::AnimationFunction() +{ + return mAnimationFunction; +} + +} // namespace dom +} // namespace mozilla + diff --git a/dom/svg/SVGSetElement.h b/dom/svg/SVGSetElement.h new file mode 100644 index 0000000000..cc76114011 --- /dev/null +++ b/dom/svg/SVGSetElement.h @@ -0,0 +1,43 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef mozilla_dom_SVGSetElement_h +#define mozilla_dom_SVGSetElement_h + +#include "mozilla/Attributes.h" +#include "mozilla/dom/SVGAnimationElement.h" +#include "nsSMILSetAnimationFunction.h" + +nsresult NS_NewSVGSetElement(nsIContent **aResult, + already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo); + +namespace mozilla { +namespace dom { + +class SVGSetElement final : public SVGAnimationElement +{ +protected: + explicit SVGSetElement(already_AddRefed<mozilla::dom::NodeInfo>& aNodeInfo); + + nsSMILSetAnimationFunction mAnimationFunction; + + friend nsresult (::NS_NewSVGSetElement(nsIContent **aResult, + already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo)); + + virtual JSObject* WrapNode(JSContext *aCx, JS::Handle<JSObject*> aGivenProto) override; + +public: + // nsIDOMNode + virtual nsresult Clone(mozilla::dom::NodeInfo *aNodeInfo, nsINode **aResult) const override; + + // SVGAnimationElement + virtual nsSMILAnimationFunction& AnimationFunction() override; +}; + +} // namespace dom +} // namespace mozilla + +#endif // mozilla_dom_SVGSetElement_h diff --git a/dom/svg/SVGStopElement.cpp b/dom/svg/SVGStopElement.cpp new file mode 100644 index 0000000000..b10ae5ffd0 --- /dev/null +++ b/dom/svg/SVGStopElement.cpp @@ -0,0 +1,70 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "mozilla/dom/SVGStopElement.h" +#include "mozilla/dom/SVGStopElementBinding.h" + +NS_IMPL_NS_NEW_NAMESPACED_SVG_ELEMENT(Stop) + +namespace mozilla { +namespace dom { + +JSObject* +SVGStopElement::WrapNode(JSContext *aCx, JS::Handle<JSObject*> aGivenProto) +{ + return SVGStopElementBinding::Wrap(aCx, this, aGivenProto); +} + +nsSVGElement::NumberInfo SVGStopElement::sNumberInfo = +{ &nsGkAtoms::offset, 0, true }; + +//---------------------------------------------------------------------- +// Implementation + +SVGStopElement::SVGStopElement(already_AddRefed<mozilla::dom::NodeInfo>& aNodeInfo) + : SVGStopElementBase(aNodeInfo) +{ +} + +//---------------------------------------------------------------------- +// nsIDOMNode methods + +NS_IMPL_ELEMENT_CLONE_WITH_INIT(SVGStopElement) + +//---------------------------------------------------------------------- + +already_AddRefed<SVGAnimatedNumber> +SVGStopElement::Offset() +{ + return mOffset.ToDOMAnimatedNumber(this); +} + +//---------------------------------------------------------------------- +// sSVGElement methods + +nsSVGElement::NumberAttributesInfo +SVGStopElement::GetNumberInfo() +{ + return NumberAttributesInfo(&mOffset, &sNumberInfo, 1); +} + +//---------------------------------------------------------------------- +// nsIContent methods + +NS_IMETHODIMP_(bool) +SVGStopElement::IsAttributeMapped(const nsIAtom* name) const +{ + static const MappedAttributeEntry* const map[] = { + sGradientStopMap + }; + + return FindAttributeDependence(name, map) || + SVGStopElementBase::IsAttributeMapped(name); +} + +} // namespace dom +} // namespace mozilla + diff --git a/dom/svg/SVGStopElement.h b/dom/svg/SVGStopElement.h new file mode 100644 index 0000000000..cdf5204f4c --- /dev/null +++ b/dom/svg/SVGStopElement.h @@ -0,0 +1,48 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef mozilla_dom_SVGStopElement_h +#define mozilla_dom_SVGStopElement_h + +#include "nsSVGElement.h" +#include "nsSVGNumber2.h" + +nsresult NS_NewSVGStopElement(nsIContent **aResult, + already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo); + +typedef nsSVGElement SVGStopElementBase; + +namespace mozilla { +namespace dom { + +class SVGStopElement final : public SVGStopElementBase +{ +protected: + friend nsresult (::NS_NewSVGStopElement(nsIContent **aResult, + already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo)); + explicit SVGStopElement(already_AddRefed<mozilla::dom::NodeInfo>& aNodeInfo); + virtual JSObject* WrapNode(JSContext *aCx, JS::Handle<JSObject*> aGivenProto) override; + +public: + // nsIContent interface + NS_IMETHOD_(bool) IsAttributeMapped(const nsIAtom* aAttribute) const override; + + virtual nsresult Clone(mozilla::dom::NodeInfo *aNodeInfo, nsINode **aResult) const override; + + // WebIDL + already_AddRefed<SVGAnimatedNumber> Offset(); + +protected: + + virtual NumberAttributesInfo GetNumberInfo() override; + nsSVGNumber2 mOffset; + static NumberInfo sNumberInfo; +}; + +} // namespace dom +} // namespace mozilla + +#endif // mozilla_dom_SVGStopElement_h diff --git a/dom/svg/SVGStringList.cpp b/dom/svg/SVGStringList.cpp new file mode 100644 index 0000000000..dc00bc5b88 --- /dev/null +++ b/dom/svg/SVGStringList.cpp @@ -0,0 +1,72 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "SVGStringList.h" +#include "nsError.h" +#include "nsCharSeparatedTokenizer.h" +#include "nsString.h" +#include "nsWhitespaceTokenizer.h" +#include "SVGContentUtils.h" + +namespace mozilla { + +nsresult +SVGStringList::CopyFrom(const SVGStringList& rhs) +{ + if (!mStrings.Assign(rhs.mStrings, fallible)) { + return NS_ERROR_OUT_OF_MEMORY; + } + mIsSet = true; + return NS_OK; +} + +void +SVGStringList::GetValue(nsAString& aValue) const +{ + aValue.Truncate(); + uint32_t last = mStrings.Length() - 1; + for (uint32_t i = 0; i < mStrings.Length(); ++i) { + aValue.Append(mStrings[i]); + if (i != last) { + if (mIsCommaSeparated) { + aValue.Append(','); + } + aValue.Append(' '); + } + } +} + +nsresult +SVGStringList::SetValue(const nsAString& aValue) +{ + SVGStringList temp; + + if (mIsCommaSeparated) { + nsCharSeparatedTokenizerTemplate<IsSVGWhitespace> + tokenizer(aValue, ','); + + while (tokenizer.hasMoreTokens()) { + if (!temp.AppendItem(tokenizer.nextToken())) { + return NS_ERROR_OUT_OF_MEMORY; + } + } + if (tokenizer.separatorAfterCurrentToken()) { + return NS_ERROR_DOM_SYNTAX_ERR; // trailing comma + } + } else { + nsWhitespaceTokenizerTemplate<IsSVGWhitespace> tokenizer(aValue); + + while (tokenizer.hasMoreTokens()) { + if (!temp.AppendItem(tokenizer.nextToken())) { + return NS_ERROR_OUT_OF_MEMORY; + } + } + } + + return CopyFrom(temp); +} + +} // namespace mozilla diff --git a/dom/svg/SVGStringList.h b/dom/svg/SVGStringList.h new file mode 100644 index 0000000000..e10ac5da47 --- /dev/null +++ b/dom/svg/SVGStringList.h @@ -0,0 +1,147 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef MOZILLA_SVGSTRINGLIST_H__ +#define MOZILLA_SVGSTRINGLIST_H__ + +#include "nsDebug.h" +#include "nsTArray.h" +#include "nsString.h" // IWYU pragma: keep + +namespace mozilla { + +/** + * + * The DOM wrapper class for this class is DOMSVGStringList. + */ +class SVGStringList +{ + friend class DOMSVGStringList; + +public: + + SVGStringList() : mIsSet(false), mIsCommaSeparated(false) {} + ~SVGStringList(){} + + void SetIsCommaSeparated(bool aIsCommaSeparated) { + mIsCommaSeparated = aIsCommaSeparated; + } + nsresult SetValue(const nsAString& aValue); + + void Clear() { + mStrings.Clear(); + mIsSet = false; + } + + /// This may return an incomplete string on OOM, but that's acceptable. + void GetValue(nsAString& aValue) const; + + bool IsEmpty() const { + return mStrings.IsEmpty(); + } + + uint32_t Length() const { + return mStrings.Length(); + } + + const nsAString& operator[](uint32_t aIndex) const { + return mStrings[aIndex]; + } + + bool operator==(const SVGStringList& rhs) const { + return mStrings == rhs.mStrings; + } + + bool SetCapacity(uint32_t size) { + return mStrings.SetCapacity(size, fallible); + } + + void Compact() { + mStrings.Compact(); + } + + // Returns true if the value of this stringlist has been explicitly + // set by markup or a DOM call, false otherwise. + bool IsExplicitlySet() const + { return mIsSet; } + + // 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 + // SVGAnimatedStringList 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 SVGStringList& rhs); + + nsAString& operator[](uint32_t aIndex) { + return mStrings[aIndex]; + } + + /** + * This may fail (return false) on OOM if the internal capacity is being + * increased, in which case the list will be left unmodified. + */ + bool SetLength(uint32_t aStringOfItems) { + return mStrings.SetLength(aStringOfItems, fallible); + } + +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. + + bool InsertItem(uint32_t aIndex, const nsAString &aString) { + if (aIndex >= mStrings.Length()) { + aIndex = mStrings.Length(); + } + if (mStrings.InsertElementAt(aIndex, aString, fallible)) { + mIsSet = true; + return true; + } + return false; + } + + void ReplaceItem(uint32_t aIndex, const nsAString &aString) { + MOZ_ASSERT(aIndex < mStrings.Length(), + "DOM wrapper caller should have raised INDEX_SIZE_ERR"); + mStrings[aIndex] = aString; + } + + void RemoveItem(uint32_t aIndex) { + MOZ_ASSERT(aIndex < mStrings.Length(), + "DOM wrapper caller should have raised INDEX_SIZE_ERR"); + mStrings.RemoveElementAt(aIndex); + } + + bool AppendItem(const nsAString &aString) { + if (mStrings.AppendElement(aString, fallible)) { + mIsSet = true; + return true; + } + return false; + } + +protected: + + /* See SVGLengthList for the rationale for using FallibleTArray<float> instead + * of FallibleTArray<float, 1>. + */ + FallibleTArray<nsString> mStrings; + bool mIsSet; + bool mIsCommaSeparated; +}; + +} // namespace mozilla + +#endif // MOZILLA_SVGSTRINGLIST_H__ diff --git a/dom/svg/SVGStyleElement.cpp b/dom/svg/SVGStyleElement.cpp new file mode 100644 index 0000000000..22fb204dff --- /dev/null +++ b/dom/svg/SVGStyleElement.cpp @@ -0,0 +1,306 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "mozilla/dom/Element.h" +#include "mozilla/dom/SVGStyleElement.h" +#include "nsContentUtils.h" +#include "mozilla/dom/SVGStyleElementBinding.h" + +NS_IMPL_NS_NEW_NAMESPACED_SVG_ELEMENT(Style) + +namespace mozilla { +namespace dom { + +JSObject* +SVGStyleElement::WrapNode(JSContext *aCx, JS::Handle<JSObject*> aGivenProto) +{ + return SVGStyleElementBinding::Wrap(aCx, this, aGivenProto); +} + +//---------------------------------------------------------------------- +// nsISupports methods + +NS_IMPL_ADDREF_INHERITED(SVGStyleElement, SVGStyleElementBase) +NS_IMPL_RELEASE_INHERITED(SVGStyleElement, SVGStyleElementBase) + +NS_INTERFACE_TABLE_HEAD_CYCLE_COLLECTION_INHERITED(SVGStyleElement) + NS_INTERFACE_TABLE_INHERITED(SVGStyleElement, + nsIStyleSheetLinkingElement, + nsIMutationObserver) +NS_INTERFACE_TABLE_TAIL_INHERITING(SVGStyleElementBase) + +NS_IMPL_CYCLE_COLLECTION_CLASS(SVGStyleElement) + +NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(SVGStyleElement, + SVGStyleElementBase) + tmp->nsStyleLinkElement::Traverse(cb); +NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END + +NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(SVGStyleElement, + SVGStyleElementBase) + tmp->nsStyleLinkElement::Unlink(); +NS_IMPL_CYCLE_COLLECTION_UNLINK_END + +//---------------------------------------------------------------------- +// Implementation + +SVGStyleElement::SVGStyleElement(already_AddRefed<mozilla::dom::NodeInfo>& aNodeInfo) + : SVGStyleElementBase(aNodeInfo) +{ + AddMutationObserver(this); +} + +SVGStyleElement::~SVGStyleElement() +{ +} + +//---------------------------------------------------------------------- +// nsIDOMNode methods + + +NS_IMPL_ELEMENT_CLONE_WITH_INIT(SVGStyleElement) + + +//---------------------------------------------------------------------- +// nsIContent methods + +nsresult +SVGStyleElement::BindToTree(nsIDocument* aDocument, nsIContent* aParent, + nsIContent* aBindingParent, + bool aCompileEventHandlers) +{ + nsresult rv = SVGStyleElementBase::BindToTree(aDocument, aParent, + aBindingParent, + aCompileEventHandlers); + NS_ENSURE_SUCCESS(rv, rv); + + void (SVGStyleElement::*update)() = &SVGStyleElement::UpdateStyleSheetInternal; + nsContentUtils::AddScriptRunner(NewRunnableMethod(this, update)); + + return rv; +} + +void +SVGStyleElement::UnbindFromTree(bool aDeep, bool aNullParent) +{ + nsCOMPtr<nsIDocument> oldDoc = GetUncomposedDoc(); + ShadowRoot* oldShadow = GetContainingShadow(); + SVGStyleElementBase::UnbindFromTree(aDeep, aNullParent); + UpdateStyleSheetInternal(oldDoc, oldShadow); +} + +nsresult +SVGStyleElement::SetAttr(int32_t aNameSpaceID, nsIAtom* aName, + nsIAtom* aPrefix, const nsAString& aValue, + bool aNotify) +{ + nsresult rv = SVGStyleElementBase::SetAttr(aNameSpaceID, aName, aPrefix, + aValue, aNotify); + if (NS_SUCCEEDED(rv) && aNameSpaceID == kNameSpaceID_None) { + if (aName == nsGkAtoms::title || + aName == nsGkAtoms::media || + aName == nsGkAtoms::type) { + UpdateStyleSheetInternal(nullptr, nullptr, true); + } else if (aName == nsGkAtoms::scoped) { + UpdateStyleSheetScopedness(true); + } + } + + return rv; +} + +nsresult +SVGStyleElement::UnsetAttr(int32_t aNameSpaceID, nsIAtom* aAttribute, + bool aNotify) +{ + nsresult rv = SVGStyleElementBase::UnsetAttr(aNameSpaceID, aAttribute, + aNotify); + if (NS_SUCCEEDED(rv) && aNameSpaceID == kNameSpaceID_None) { + if (aAttribute == nsGkAtoms::title || + aAttribute == nsGkAtoms::media || + aAttribute == nsGkAtoms::type) { + UpdateStyleSheetInternal(nullptr, nullptr, true); + } else if (aAttribute == nsGkAtoms::scoped) { + UpdateStyleSheetScopedness(false); + } + } + + return rv; +} + +bool +SVGStyleElement::ParseAttribute(int32_t aNamespaceID, + nsIAtom* aAttribute, + const nsAString& aValue, + nsAttrValue& aResult) +{ + if (aNamespaceID == kNameSpaceID_None && + aAttribute == nsGkAtoms::crossorigin) { + ParseCORSValue(aValue, aResult); + return true; + } + + return SVGStyleElementBase::ParseAttribute(aNamespaceID, aAttribute, aValue, + aResult); +} + +//---------------------------------------------------------------------- +// nsIMutationObserver methods + +void +SVGStyleElement::CharacterDataChanged(nsIDocument* aDocument, + nsIContent* aContent, + CharacterDataChangeInfo* aInfo) +{ + ContentChanged(aContent); +} + +void +SVGStyleElement::ContentAppended(nsIDocument* aDocument, + nsIContent* aContainer, + nsIContent* aFirstNewContent, + int32_t aNewIndexInContainer) +{ + ContentChanged(aContainer); +} + +void +SVGStyleElement::ContentInserted(nsIDocument* aDocument, + nsIContent* aContainer, + nsIContent* aChild, + int32_t aIndexInContainer) +{ + ContentChanged(aChild); +} + +void +SVGStyleElement::ContentRemoved(nsIDocument* aDocument, + nsIContent* aContainer, + nsIContent* aChild, + int32_t aIndexInContainer, + nsIContent* aPreviousSibling) +{ + ContentChanged(aChild); +} + +void +SVGStyleElement::ContentChanged(nsIContent* aContent) +{ + if (nsContentUtils::IsInSameAnonymousTree(this, aContent)) { + UpdateStyleSheetInternal(nullptr, nullptr); + } +} + +//---------------------------------------------------------------------- + +void +SVGStyleElement::GetXmlspace(nsAString & aXmlspace) +{ + GetAttr(kNameSpaceID_XML, nsGkAtoms::space, aXmlspace); +} + +void +SVGStyleElement::SetXmlspace(const nsAString & aXmlspace, ErrorResult& rv) +{ + rv = SetAttr(kNameSpaceID_XML, nsGkAtoms::space, aXmlspace, true); +} + +void +SVGStyleElement::GetMedia(nsAString & aMedia) +{ + GetAttr(kNameSpaceID_None, nsGkAtoms::media, aMedia); +} + +void +SVGStyleElement::SetMedia(const nsAString& aMedia, ErrorResult& rv) +{ + rv = SetAttr(kNameSpaceID_None, nsGkAtoms::media, aMedia, true); +} + +bool +SVGStyleElement::Scoped() const +{ + return GetBoolAttr(nsGkAtoms::scoped); +} + +void +SVGStyleElement::SetScoped(bool aScoped, ErrorResult& rv) +{ + rv = SetBoolAttr(nsGkAtoms::scoped, aScoped); +} + +void +SVGStyleElement::GetType(nsAString & aType) +{ + GetAttr(kNameSpaceID_None, nsGkAtoms::type, aType); +} + +void +SVGStyleElement::SetType(const nsAString& aType, ErrorResult& rv) +{ + rv = SetAttr(kNameSpaceID_None, nsGkAtoms::type, aType, true); +} + +void +SVGStyleElement::GetTitle(nsAString & aTitle) +{ + GetAttr(kNameSpaceID_None, nsGkAtoms::title, aTitle); +} + +void +SVGStyleElement::SetTitle(const nsAString& aTitle, ErrorResult& rv) +{ + rv = SetAttr(kNameSpaceID_None, nsGkAtoms::title, aTitle, true); +} + +//---------------------------------------------------------------------- +// nsStyleLinkElement methods + +already_AddRefed<nsIURI> +SVGStyleElement::GetStyleSheetURL(bool* aIsInline) +{ + *aIsInline = true; + return nullptr; +} + +void +SVGStyleElement::GetStyleSheetInfo(nsAString& aTitle, + nsAString& aType, + nsAString& aMedia, + bool* aIsScoped, + bool* aIsAlternate) +{ + *aIsAlternate = false; + + nsAutoString title; + GetAttr(kNameSpaceID_None, nsGkAtoms::title, title); + title.CompressWhitespace(); + aTitle.Assign(title); + + GetAttr(kNameSpaceID_None, nsGkAtoms::media, aMedia); + // The SVG spec is formulated in terms of the CSS2 spec, + // which specifies that media queries are case insensitive. + nsContentUtils::ASCIIToLower(aMedia); + + GetAttr(kNameSpaceID_None, nsGkAtoms::type, aType); + if (aType.IsEmpty()) { + aType.AssignLiteral("text/css"); + } + + *aIsScoped = HasAttr(kNameSpaceID_None, nsGkAtoms::scoped); + + return; +} + +CORSMode +SVGStyleElement::GetCORSMode() const +{ + return AttrValueToCORSMode(GetParsedAttr(nsGkAtoms::crossorigin)); +} + +} // namespace dom +} // namespace mozilla + diff --git a/dom/svg/SVGStyleElement.h b/dom/svg/SVGStyleElement.h new file mode 100644 index 0000000000..9b126e7e8f --- /dev/null +++ b/dom/svg/SVGStyleElement.h @@ -0,0 +1,112 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef mozilla_dom_SVGStyleElement_h +#define mozilla_dom_SVGStyleElement_h + +#include "mozilla/Attributes.h" +#include "nsSVGElement.h" +#include "nsStyleLinkElement.h" +#include "nsStubMutationObserver.h" + +nsresult NS_NewSVGStyleElement(nsIContent **aResult, + already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo); + +typedef nsSVGElement SVGStyleElementBase; + +namespace mozilla { +namespace dom { + +class SVGStyleElement final : public SVGStyleElementBase, + public nsStyleLinkElement, + public nsStubMutationObserver +{ +protected: + friend nsresult (::NS_NewSVGStyleElement(nsIContent **aResult, + already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo)); + explicit SVGStyleElement(already_AddRefed<mozilla::dom::NodeInfo>& aNodeInfo); + ~SVGStyleElement(); + + virtual JSObject* WrapNode(JSContext *aCx, JS::Handle<JSObject*> aGivenProto) override; + +public: + NS_DECL_ISUPPORTS_INHERITED + + NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(SVGStyleElement, + SVGStyleElementBase) + + // nsIContent + virtual nsresult BindToTree(nsIDocument* aDocument, nsIContent* aParent, + nsIContent* aBindingParent, + bool aCompileEventHandlers) override; + virtual void UnbindFromTree(bool aDeep = true, + bool aNullParent = true) override; + nsresult SetAttr(int32_t aNameSpaceID, nsIAtom* aName, + const nsAString& aValue, bool aNotify) + { + return SetAttr(aNameSpaceID, aName, nullptr, aValue, aNotify); + } + virtual nsresult SetAttr(int32_t aNameSpaceID, nsIAtom* aName, + nsIAtom* aPrefix, const nsAString& aValue, + bool aNotify) override; + virtual nsresult UnsetAttr(int32_t aNameSpaceID, nsIAtom* aAttribute, + bool aNotify) override; + virtual bool ParseAttribute(int32_t aNamespaceID, + nsIAtom* aAttribute, + const nsAString& aValue, + nsAttrValue& aResult) override; + + virtual nsresult Clone(mozilla::dom::NodeInfo *aNodeInfo, nsINode **aResult) const override; + + // nsIMutationObserver + NS_DECL_NSIMUTATIONOBSERVER_CHARACTERDATACHANGED + NS_DECL_NSIMUTATIONOBSERVER_CONTENTAPPENDED + NS_DECL_NSIMUTATIONOBSERVER_CONTENTINSERTED + NS_DECL_NSIMUTATIONOBSERVER_CONTENTREMOVED + + // WebIDL + void GetXmlspace(nsAString & aXmlspace); + void SetXmlspace(const nsAString & aXmlspace, ErrorResult& rv); + void GetMedia(nsAString & aMedia); + void SetMedia(const nsAString& aMedia, ErrorResult& rv); + bool Scoped() const; + void SetScoped(bool aScoped, ErrorResult& rv); + void GetType(nsAString & aType); + void SetType(const nsAString& aType, ErrorResult& rv); + void GetTitle(nsAString & aTitle); + void SetTitle(const nsAString& aTitle, ErrorResult& rv); + +protected: + // Dummy init method to make the NS_IMPL_NS_NEW_SVG_ELEMENT and + // NS_IMPL_ELEMENT_CLONE_WITH_INIT usable with this class. This should be + // completely optimized away. + inline nsresult Init() + { + return NS_OK; + } + + // nsStyleLinkElement overrides + already_AddRefed<nsIURI> GetStyleSheetURL(bool* aIsInline) override; + + void GetStyleSheetInfo(nsAString& aTitle, + nsAString& aType, + nsAString& aMedia, + bool* aIsScoped, + bool* aIsAlternate) override; + virtual CORSMode GetCORSMode() const override; + + /** + * Common method to call from the various mutation observer methods. + * aContent is a content node that's either the one that changed or its + * parent; we should only respond to the change if aContent is non-anonymous. + */ + void ContentChanged(nsIContent* aContent); +}; + +} // namespace dom +} // namespace mozilla + +#endif // mozilla_dom_SVGStyleElement_h diff --git a/dom/svg/SVGSwitchElement.cpp b/dom/svg/SVGSwitchElement.cpp new file mode 100644 index 0000000000..a0358dc155 --- /dev/null +++ b/dom/svg/SVGSwitchElement.cpp @@ -0,0 +1,196 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "mozilla/dom/SVGSwitchElement.h" + +#include "nsLayoutUtils.h" +#include "nsSVGUtils.h" +#include "mozilla/Preferences.h" +#include "mozilla/dom/SVGSwitchElementBinding.h" + +class nsIFrame; + +NS_IMPL_NS_NEW_NAMESPACED_SVG_ELEMENT(Switch) + +namespace mozilla { +namespace dom { + +JSObject* +SVGSwitchElement::WrapNode(JSContext *aCx, JS::Handle<JSObject*> aGivenProto) +{ + return SVGSwitchElementBinding::Wrap(aCx, this, aGivenProto); +} + +//---------------------------------------------------------------------- +// nsISupports methods + +NS_IMPL_CYCLE_COLLECTION_INHERITED(SVGSwitchElement, SVGSwitchElementBase, + mActiveChild) + +NS_IMPL_ADDREF_INHERITED(SVGSwitchElement,SVGSwitchElementBase) +NS_IMPL_RELEASE_INHERITED(SVGSwitchElement,SVGSwitchElementBase) + +NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(SVGSwitchElement) +NS_INTERFACE_MAP_END_INHERITING(SVGSwitchElementBase) + +//---------------------------------------------------------------------- +// Implementation + +SVGSwitchElement::SVGSwitchElement(already_AddRefed<mozilla::dom::NodeInfo>& aNodeInfo) + : SVGSwitchElementBase(aNodeInfo) +{ +} + +SVGSwitchElement::~SVGSwitchElement() +{ +} + +void +SVGSwitchElement::MaybeInvalidate() +{ + // We must not change mActiveChild until after + // InvalidateAndScheduleBoundsUpdate has been called, otherwise + // it will not correctly invalidate the old mActiveChild area. + + nsIContent *newActiveChild = FindActiveChild(); + + if (newActiveChild == mActiveChild) { + return; + } + + nsIFrame *frame = GetPrimaryFrame(); + if (frame) { + nsLayoutUtils::PostRestyleEvent( + this, nsRestyleHint(0), + nsChangeHint_InvalidateRenderingObservers); + nsSVGUtils::ScheduleReflowSVG(frame); + } + + mActiveChild = newActiveChild; +} + +//---------------------------------------------------------------------- +// nsIDOMNode methods + + +NS_IMPL_ELEMENT_CLONE_WITH_INIT(SVGSwitchElement) + +//---------------------------------------------------------------------- +// nsINode methods + +nsresult +SVGSwitchElement::InsertChildAt(nsIContent* aKid, + uint32_t aIndex, + bool aNotify) +{ + nsresult rv = SVGSwitchElementBase::InsertChildAt(aKid, aIndex, aNotify); + if (NS_SUCCEEDED(rv)) { + MaybeInvalidate(); + } + return rv; +} + +void +SVGSwitchElement::RemoveChildAt(uint32_t aIndex, bool aNotify) +{ + SVGSwitchElementBase::RemoveChildAt(aIndex, aNotify); + MaybeInvalidate(); +} + +//---------------------------------------------------------------------- +// nsIContent methods + +NS_IMETHODIMP_(bool) +SVGSwitchElement::IsAttributeMapped(const nsIAtom* name) const +{ + static const MappedAttributeEntry* const map[] = { + sFEFloodMap, + sFiltersMap, + sFontSpecificationMap, + sGradientStopMap, + sLightingEffectsMap, + sMarkersMap, + sTextContentElementsMap, + sViewportsMap + }; + + return FindAttributeDependence(name, map) || + SVGSwitchElementBase::IsAttributeMapped(name); +} + +//---------------------------------------------------------------------- +// Implementation Helpers: + +nsIContent * +SVGSwitchElement::FindActiveChild() const +{ + const nsAdoptingString& acceptLangs = + Preferences::GetLocalizedString("intl.accept_languages"); + + if (!acceptLangs.IsEmpty()) { + int32_t bestLanguagePreferenceRank = -1; + nsIContent *bestChild = nullptr; + nsIContent *defaultChild = nullptr; + for (nsIContent* child = nsINode::GetFirstChild(); + child; + child = child->GetNextSibling()) { + + if (!child->IsElement()) { + continue; + } + nsCOMPtr<SVGTests> tests(do_QueryInterface(child)); + if (tests) { + if (tests->PassesConditionalProcessingTests( + SVGTests::kIgnoreSystemLanguage)) { + int32_t languagePreferenceRank = + tests->GetBestLanguagePreferenceRank(acceptLangs); + switch (languagePreferenceRank) { + case 0: + // best possible match + return child; + case -1: + // no match + break; + case -2: + // no systemLanguage attribute. If there's nothing better + // we'll use the first such child. + if (!defaultChild) { + defaultChild = child; + } + break; + default: + if (bestLanguagePreferenceRank == -1 || + languagePreferenceRank < bestLanguagePreferenceRank) { + bestLanguagePreferenceRank = languagePreferenceRank; + bestChild = child; + } + break; + } + } + } else if (!bestChild) { + bestChild = child; + } + } + return bestChild ? bestChild : defaultChild; + } + + for (nsIContent* child = nsINode::GetFirstChild(); + child; + child = child->GetNextSibling()) { + if (!child->IsElement()) { + continue; + } + nsCOMPtr<SVGTests> tests(do_QueryInterface(child)); + if (!tests || tests->PassesConditionalProcessingTests(&acceptLangs)) { + return child; + } + } + return nullptr; +} + +} // namespace dom +} // namespace mozilla + diff --git a/dom/svg/SVGSwitchElement.h b/dom/svg/SVGSwitchElement.h new file mode 100644 index 0000000000..1fbf5fd92e --- /dev/null +++ b/dom/svg/SVGSwitchElement.h @@ -0,0 +1,63 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef mozilla_dom_SVGSwitchElement_h +#define mozilla_dom_SVGSwitchElement_h + +#include "mozilla/dom/SVGGraphicsElement.h" + +class nsSVGSwitchFrame; + +nsresult NS_NewSVGSwitchElement(nsIContent **aResult, + already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo); + +namespace mozilla { +namespace dom { + +typedef SVGGraphicsElement SVGSwitchElementBase; + +class SVGSwitchElement final : public SVGSwitchElementBase +{ + friend class ::nsSVGSwitchFrame; +protected: + friend nsresult (::NS_NewSVGSwitchElement(nsIContent **aResult, + already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo)); + explicit SVGSwitchElement(already_AddRefed<mozilla::dom::NodeInfo>& aNodeInfo); + ~SVGSwitchElement(); + virtual JSObject* WrapNode(JSContext *aCx, JS::Handle<JSObject*> aGivenProto) override; + +public: + nsIContent * GetActiveChild() const + { return mActiveChild; } + void MaybeInvalidate(); + + // interfaces: + + NS_DECL_ISUPPORTS_INHERITED + NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(SVGSwitchElement, + SVGSwitchElementBase) + // nsINode + virtual nsresult InsertChildAt(nsIContent* aKid, uint32_t aIndex, + bool aNotify) override; + virtual void RemoveChildAt(uint32_t aIndex, bool aNotify) override; + + // nsIContent + NS_IMETHOD_(bool) IsAttributeMapped(const nsIAtom* aAttribute) const override; + + virtual nsresult Clone(mozilla::dom::NodeInfo *aNodeInfo, nsINode **aResult) const override; +private: + void UpdateActiveChild() + { mActiveChild = FindActiveChild(); } + nsIContent* FindActiveChild() const; + + // only this child will be displayed + nsCOMPtr<nsIContent> mActiveChild; +}; + +} // namespace dom +} // namespace mozilla + +#endif // mozilla_dom_SVGSwitchElement_h diff --git a/dom/svg/SVGSymbolElement.cpp b/dom/svg/SVGSymbolElement.cpp new file mode 100644 index 0000000000..42de96efd1 --- /dev/null +++ b/dom/svg/SVGSymbolElement.cpp @@ -0,0 +1,109 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "mozilla/dom/SVGSymbolElement.h" +#include "mozilla/dom/SVGSymbolElementBinding.h" + +NS_IMPL_NS_NEW_NAMESPACED_SVG_ELEMENT(Symbol) + +namespace mozilla { +namespace dom { + +JSObject* +SVGSymbolElement::WrapNode(JSContext *aCx, JS::Handle<JSObject*> aGivenProto) +{ + return SVGSymbolElementBinding::Wrap(aCx, this, aGivenProto); +} + +//---------------------------------------------------------------------- +// nsISupports methods + +NS_IMPL_ISUPPORTS_INHERITED(SVGSymbolElement, SVGSymbolElementBase, + nsIDOMNode, nsIDOMElement, + nsIDOMSVGElement, mozilla::dom::SVGTests) + +//---------------------------------------------------------------------- +// Implementation + +SVGSymbolElement::SVGSymbolElement(already_AddRefed<mozilla::dom::NodeInfo>& aNodeInfo) + : SVGSymbolElementBase(aNodeInfo) +{ +} + +SVGSymbolElement::~SVGSymbolElement() +{ +} + +//---------------------------------------------------------------------- +// nsIDOMNode methods + +NS_IMPL_ELEMENT_CLONE_WITH_INIT(SVGSymbolElement) + +//---------------------------------------------------------------------- + +already_AddRefed<SVGAnimatedRect> +SVGSymbolElement::ViewBox() +{ + return mViewBox.ToSVGAnimatedRect(this); +} + +already_AddRefed<DOMSVGAnimatedPreserveAspectRatio> +SVGSymbolElement::PreserveAspectRatio() +{ + return mPreserveAspectRatio.ToDOMAnimatedPreserveAspectRatio(this); +} + +//---------------------------------------------------------------------- +// nsIContent methods + +NS_IMETHODIMP_(bool) +SVGSymbolElement::IsAttributeMapped(const nsIAtom* name) const +{ + static const MappedAttributeEntry* const map[] = { + sColorMap, + sFEFloodMap, + sFillStrokeMap, + sFiltersMap, + sFontSpecificationMap, + sGradientStopMap, + sGraphicsMap, + sLightingEffectsMap, + sMarkersMap, + sTextContentElementsMap, + sViewportsMap + }; + + return FindAttributeDependence(name, map) || + SVGSymbolElementBase::IsAttributeMapped(name); +} + +//---------------------------------------------------------------------- +// SVGTests methods + +bool +SVGSymbolElement::IsInChromeDoc() const +{ + return nsContentUtils::IsChromeDoc(OwnerDoc()); +} + + +//---------------------------------------------------------------------- +// nsSVGElement methods + +nsSVGViewBox * +SVGSymbolElement::GetViewBox() +{ + return &mViewBox; +} + +SVGAnimatedPreserveAspectRatio * +SVGSymbolElement::GetPreserveAspectRatio() +{ + return &mPreserveAspectRatio; +} + +} // namespace dom +} // namespace mozilla diff --git a/dom/svg/SVGSymbolElement.h b/dom/svg/SVGSymbolElement.h new file mode 100644 index 0000000000..427840f3f2 --- /dev/null +++ b/dom/svg/SVGSymbolElement.h @@ -0,0 +1,61 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef mozilla_dom_SVGSymbolElement_h +#define mozilla_dom_SVGSymbolElement_h + +#include "mozilla/dom/SVGTests.h" +#include "nsSVGElement.h" +#include "nsSVGViewBox.h" +#include "SVGAnimatedPreserveAspectRatio.h" + +nsresult NS_NewSVGSymbolElement(nsIContent **aResult, + already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo); + +namespace mozilla { +namespace dom { + +typedef nsSVGElement SVGSymbolElementBase; + +class SVGSymbolElement final : public SVGSymbolElementBase, + public SVGTests +{ +protected: + friend nsresult (::NS_NewSVGSymbolElement(nsIContent **aResult, + already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo)); + explicit SVGSymbolElement(already_AddRefed<mozilla::dom::NodeInfo>& aNodeInfo); + ~SVGSymbolElement(); + virtual JSObject* WrapNode(JSContext *cx, JS::Handle<JSObject*> aGivenProto) override; + +public: + // interfaces: + + NS_DECL_ISUPPORTS_INHERITED + + // nsIContent interface + NS_IMETHOD_(bool) IsAttributeMapped(const nsIAtom* name) const override; + + virtual nsresult Clone(mozilla::dom::NodeInfo *aNodeInfo, nsINode **aResult) const override; + + // WebIDL + already_AddRefed<SVGAnimatedRect> ViewBox(); + already_AddRefed<DOMSVGAnimatedPreserveAspectRatio> PreserveAspectRatio(); + + // SVGTests + bool IsInChromeDoc() const override; + +protected: + virtual nsSVGViewBox *GetViewBox() override; + virtual SVGAnimatedPreserveAspectRatio *GetPreserveAspectRatio() override; + + nsSVGViewBox mViewBox; + SVGAnimatedPreserveAspectRatio mPreserveAspectRatio; +}; + +} // namespace dom +} // namespace mozilla + +#endif // mozilla_dom_SVGSymbolElement_h diff --git a/dom/svg/SVGTSpanElement.cpp b/dom/svg/SVGTSpanElement.cpp new file mode 100644 index 0000000000..29a0fbe11f --- /dev/null +++ b/dom/svg/SVGTSpanElement.cpp @@ -0,0 +1,69 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "mozilla/dom/SVGTSpanElement.h" +#include "mozilla/dom/SVGTSpanElementBinding.h" + +NS_IMPL_NS_NEW_NAMESPACED_SVG_ELEMENT(TSpan) + +namespace mozilla { +namespace dom { + +JSObject* +SVGTSpanElement::WrapNode(JSContext *aCx, JS::Handle<JSObject*> aGivenProto) +{ + return SVGTSpanElementBinding::Wrap(aCx, this, aGivenProto); +} + + +//---------------------------------------------------------------------- +// Implementation + +SVGTSpanElement::SVGTSpanElement(already_AddRefed<mozilla::dom::NodeInfo>& aNodeInfo) + : SVGTSpanElementBase(aNodeInfo) +{ +} + +nsSVGElement::EnumAttributesInfo +SVGTSpanElement::GetEnumInfo() +{ + return EnumAttributesInfo(mEnumAttributes, sEnumInfo, + ArrayLength(sEnumInfo)); +} + +nsSVGElement::LengthAttributesInfo +SVGTSpanElement::GetLengthInfo() +{ + return LengthAttributesInfo(mLengthAttributes, sLengthInfo, + ArrayLength(sLengthInfo)); +} + +//---------------------------------------------------------------------- +// nsIDOMNode methods + + +NS_IMPL_ELEMENT_CLONE_WITH_INIT(SVGTSpanElement) + +//---------------------------------------------------------------------- +// nsIContent methods + +NS_IMETHODIMP_(bool) +SVGTSpanElement::IsAttributeMapped(const nsIAtom* name) const +{ + static const MappedAttributeEntry* const map[] = { + sColorMap, + sFillStrokeMap, + sFontSpecificationMap, + sGraphicsMap, + sTextContentElementsMap + }; + + return FindAttributeDependence(name, map) || + SVGTSpanElementBase::IsAttributeMapped(name); +} + +} // namespace dom +} // namespace mozilla diff --git a/dom/svg/SVGTSpanElement.h b/dom/svg/SVGTSpanElement.h new file mode 100644 index 0000000000..5f7670fd19 --- /dev/null +++ b/dom/svg/SVGTSpanElement.h @@ -0,0 +1,50 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef mozilla_dom_SVGTSpanElement_h +#define mozilla_dom_SVGTSpanElement_h + +#include "mozilla/dom/SVGTextPositioningElement.h" + +nsresult NS_NewSVGTSpanElement(nsIContent **aResult, + already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo); + +namespace mozilla { +namespace dom { + +typedef SVGTextPositioningElement SVGTSpanElementBase; + +class SVGTSpanElement final : public SVGTSpanElementBase +{ +protected: + friend nsresult (::NS_NewSVGTSpanElement(nsIContent **aResult, + already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo)); + explicit SVGTSpanElement(already_AddRefed<mozilla::dom::NodeInfo>& aNodeInfo); + virtual JSObject* WrapNode(JSContext *cx, JS::Handle<JSObject*> aGivenProto) override; + +public: + // nsIContent interface + NS_IMETHOD_(bool) IsAttributeMapped(const nsIAtom* aAttribute) const override; + + virtual nsresult Clone(mozilla::dom::NodeInfo *aNodeInfo, nsINode **aResult) const override; + +protected: + virtual EnumAttributesInfo GetEnumInfo() override; + virtual LengthAttributesInfo GetLengthInfo() override; + + nsSVGEnum mEnumAttributes[1]; + virtual nsSVGEnum* EnumAttributes() override + { return mEnumAttributes; } + + nsSVGLength2 mLengthAttributes[1]; + virtual nsSVGLength2* LengthAttributes() override + { return mLengthAttributes; } +}; + +} // namespace dom +} // namespace mozilla + +#endif // mozilla_dom_SVGTSpanElement_h diff --git a/dom/svg/SVGTagList.h b/dom/svg/SVGTagList.h new file mode 100644 index 0000000000..219cafc43f --- /dev/null +++ b/dom/svg/SVGTagList.h @@ -0,0 +1,93 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/****** + + This file contains the list of all SVG tags. + + It is designed to be used as inline input to SVGElementFactory.cpp + *only* through the magic of C preprocessing. + + All entries must be enclosed in the macro SVG_TAG or SVG_FROM_PARSER_TAG + which will have cruel and unusual things done to them. + + SVG_FROM_PARSER_TAG is used where the element creation method takes + a FromParser argument, and SVG_TAG where it does not. + + It is recommended (but not strictly necessary) to keep all entries + in alphabetical order. + + The first argument to SVG_TAG is both the enum identifier of the + property and the atom name. The second argument is the "creator" + method of the form NS_New$TAGNAMEElement, that will be used by + SVGElementFactory.cpp to create a content object for a tag of that + type. + + ******/ + +SVG_TAG(a, A) +SVG_TAG(animate, Animate) +SVG_TAG(animateMotion, AnimateMotion) +SVG_TAG(animateTransform, AnimateTransform) +SVG_TAG(circle, Circle) +SVG_TAG(clipPath, ClipPath) +SVG_TAG(defs, Defs) +SVG_TAG(desc, Desc) +SVG_TAG(ellipse, Ellipse) +SVG_TAG(feBlend, FEBlend) +SVG_TAG(feColorMatrix, FEColorMatrix) +SVG_TAG(feComponentTransfer, FEComponentTransfer) +SVG_TAG(feComposite, FEComposite) +SVG_TAG(feConvolveMatrix, FEConvolveMatrix) +SVG_TAG(feDiffuseLighting, FEDiffuseLighting) +SVG_TAG(feDisplacementMap, FEDisplacementMap) +SVG_TAG(feDistantLight, FEDistantLight) +SVG_TAG(feDropShadow, FEDropShadow) +SVG_TAG(feFlood, FEFlood) +SVG_TAG(feFuncA, FEFuncA) +SVG_TAG(feFuncB, FEFuncB) +SVG_TAG(feFuncG, FEFuncG) +SVG_TAG(feFuncR, FEFuncR) +SVG_TAG(feGaussianBlur, FEGaussianBlur) +SVG_TAG(feImage, FEImage) +SVG_TAG(feMerge, FEMerge) +SVG_TAG(feMergeNode, FEMergeNode) +SVG_TAG(feMorphology, FEMorphology) +SVG_TAG(feOffset, FEOffset) +SVG_TAG(fePointLight, FEPointLight) +SVG_TAG(feSpecularLighting, FESpecularLighting) +SVG_TAG(feSpotLight, FESpotLight) +SVG_TAG(feTile, FETile) +SVG_TAG(feTurbulence, FETurbulence) +SVG_TAG(filter, Filter) +SVG_TAG(foreignObject, ForeignObject) +SVG_TAG(g, G) +SVG_TAG(image, Image) +SVG_TAG(line, Line) +SVG_TAG(linearGradient, LinearGradient) +SVG_TAG(marker, Marker) +SVG_TAG(mask, Mask) +SVG_TAG(metadata, Metadata) +SVG_TAG(mpath, MPath) +SVG_TAG(path, Path) +SVG_TAG(pattern, Pattern) +SVG_TAG(polygon, Polygon) +SVG_TAG(polyline, Polyline) +SVG_TAG(radialGradient, RadialGradient) +SVG_TAG(rect, Rect) +SVG_FROM_PARSER_TAG(script, Script) +SVG_TAG(set, Set) +SVG_TAG(stop, Stop) +SVG_TAG(style, Style) +SVG_FROM_PARSER_TAG(svg, SVG) +SVG_TAG(svgSwitch, Switch) +SVG_TAG(symbol, Symbol) +SVG_TAG(text, Text) +SVG_TAG(textPath, TextPath) +SVG_TAG(title, Title) +SVG_TAG(tspan, TSpan) +SVG_TAG(use, Use) +SVG_TAG(view, View) diff --git a/dom/svg/SVGTests.cpp b/dom/svg/SVGTests.cpp new file mode 100644 index 0000000000..963ac27e46 --- /dev/null +++ b/dom/svg/SVGTests.cpp @@ -0,0 +1,235 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "mozilla/dom/SVGTests.h" +#include "DOMSVGStringList.h" +#include "nsSVGFeatures.h" +#include "mozilla/dom/SVGSwitchElement.h" +#include "nsCharSeparatedTokenizer.h" +#include "nsStyleUtil.h" +#include "mozilla/Preferences.h" + +namespace mozilla { +namespace dom { + +nsIAtom** SVGTests::sStringListNames[3] = +{ + &nsGkAtoms::requiredFeatures, + &nsGkAtoms::requiredExtensions, + &nsGkAtoms::systemLanguage, +}; + +SVGTests::SVGTests() +{ + mStringListAttributes[LANGUAGE].SetIsCommaSeparated(true); +} + +already_AddRefed<DOMSVGStringList> +SVGTests::RequiredFeatures() +{ + nsCOMPtr<nsIDOMSVGElement> elem = do_QueryInterface(this); + nsSVGElement* element = static_cast<nsSVGElement*>(elem.get()); + return DOMSVGStringList::GetDOMWrapper( + &mStringListAttributes[FEATURES], element, true, FEATURES); +} + +already_AddRefed<DOMSVGStringList> +SVGTests::RequiredExtensions() +{ + nsCOMPtr<nsIDOMSVGElement> elem = do_QueryInterface(this); + nsSVGElement* element = static_cast<nsSVGElement*>(elem.get()); + return DOMSVGStringList::GetDOMWrapper( + &mStringListAttributes[EXTENSIONS], element, true, EXTENSIONS); +} + +already_AddRefed<DOMSVGStringList> +SVGTests::SystemLanguage() +{ + nsCOMPtr<nsIDOMSVGElement> elem = do_QueryInterface(this); + nsSVGElement* element = static_cast<nsSVGElement*>(elem.get()); + return DOMSVGStringList::GetDOMWrapper( + &mStringListAttributes[LANGUAGE], element, true, LANGUAGE); +} + +bool +SVGTests::HasExtension(const nsAString& aExtension) +{ + return nsSVGFeatures::HasExtension(aExtension, IsInChromeDoc()); +} + +bool +SVGTests::IsConditionalProcessingAttribute(const nsIAtom* aAttribute) const +{ + for (uint32_t i = 0; i < ArrayLength(sStringListNames); i++) { + if (aAttribute == *sStringListNames[i]) { + return true; + } + } + return false; +} + +int32_t +SVGTests::GetBestLanguagePreferenceRank(const nsSubstring& aAcceptLangs) const +{ + const nsDefaultStringComparator defaultComparator; + + if (!mStringListAttributes[LANGUAGE].IsExplicitlySet()) { + return -2; + } + + int32_t lowestRank = -1; + + for (uint32_t i = 0; i < mStringListAttributes[LANGUAGE].Length(); i++) { + nsCharSeparatedTokenizer languageTokenizer(aAcceptLangs, ','); + int32_t index = 0; + while (languageTokenizer.hasMoreTokens()) { + const nsSubstring &languageToken = languageTokenizer.nextToken(); + bool exactMatch = (languageToken == mStringListAttributes[LANGUAGE][i]); + bool prefixOnlyMatch = + !exactMatch && + nsStyleUtil::DashMatchCompare(mStringListAttributes[LANGUAGE][i], + languageTokenizer.nextToken(), + defaultComparator); + if (index == 0 && exactMatch) { + // best possible match + return 0; + } + if ((exactMatch || prefixOnlyMatch) && + (lowestRank == -1 || 2 * index + prefixOnlyMatch < lowestRank)) { + lowestRank = 2 * index + prefixOnlyMatch; + } + ++index; + } + } + return lowestRank; +} + +const nsString * const SVGTests::kIgnoreSystemLanguage = (nsString *) 0x01; + +bool +SVGTests::PassesConditionalProcessingTests(const nsString *aAcceptLangs) const +{ + // Required Extensions + // + // The requiredExtensions attribute defines a list of required language + // extensions. Language extensions are capabilities within a user agent that + // go beyond the feature set defined in the SVG specification. + // Each extension is identified by a URI reference. + // For now, claim that mozilla's SVG implementation supports XHTML and MathML. + if (mStringListAttributes[EXTENSIONS].IsExplicitlySet()) { + if (mStringListAttributes[EXTENSIONS].IsEmpty()) { + return false; + } + for (uint32_t i = 0; i < mStringListAttributes[EXTENSIONS].Length(); i++) { + if (!nsSVGFeatures::HasExtension(mStringListAttributes[EXTENSIONS][i], IsInChromeDoc())) { + return false; + } + } + } + + if (aAcceptLangs == kIgnoreSystemLanguage) { + return true; + } + + // systemLanguage + // + // Evaluates to "true" if one of the languages indicated by user preferences + // exactly equals one of the languages given in the value of this parameter, + // or if one of the languages indicated by user preferences exactly equals a + // prefix of one of the languages given in the value of this parameter such + // that the first tag character following the prefix is "-". + if (mStringListAttributes[LANGUAGE].IsExplicitlySet()) { + if (mStringListAttributes[LANGUAGE].IsEmpty()) { + return false; + } + + // Get our language preferences + const nsAutoString acceptLangs(aAcceptLangs ? *aAcceptLangs : + Preferences::GetLocalizedString("intl.accept_languages")); + + if (acceptLangs.IsEmpty()) { + NS_WARNING("no default language specified for systemLanguage conditional test"); + return false; + } + + const nsDefaultStringComparator defaultComparator; + + for (uint32_t i = 0; i < mStringListAttributes[LANGUAGE].Length(); i++) { + nsCharSeparatedTokenizer languageTokenizer(acceptLangs, ','); + while (languageTokenizer.hasMoreTokens()) { + if (nsStyleUtil::DashMatchCompare(mStringListAttributes[LANGUAGE][i], + languageTokenizer.nextToken(), + defaultComparator)) { + return true; + } + } + } + return false; + } + + return true; +} + +bool +SVGTests::ParseConditionalProcessingAttribute(nsIAtom* aAttribute, + const nsAString& aValue, + nsAttrValue& aResult) +{ + for (uint32_t i = 0; i < ArrayLength(sStringListNames); i++) { + if (aAttribute == *sStringListNames[i]) { + nsresult rv = mStringListAttributes[i].SetValue(aValue); + if (NS_FAILED(rv)) { + mStringListAttributes[i].Clear(); + } + MaybeInvalidate(); + return true; + } + } + return false; +} + +void +SVGTests::UnsetAttr(const nsIAtom* aAttribute) +{ + for (uint32_t i = 0; i < ArrayLength(sStringListNames); i++) { + if (aAttribute == *sStringListNames[i]) { + mStringListAttributes[i].Clear(); + MaybeInvalidate(); + return; + } + } +} + +nsIAtom* +SVGTests::GetAttrName(uint8_t aAttrEnum) const +{ + return *sStringListNames[aAttrEnum]; +} + +void +SVGTests::GetAttrValue(uint8_t aAttrEnum, nsAttrValue& aValue) const +{ + MOZ_ASSERT(aAttrEnum < ArrayLength(sStringListNames), + "aAttrEnum out of range"); + aValue.SetTo(mStringListAttributes[aAttrEnum], nullptr); +} + +void +SVGTests::MaybeInvalidate() +{ + nsCOMPtr<nsIDOMSVGElement> elem = do_QueryInterface(this); + nsSVGElement* element = static_cast<nsSVGElement*>(elem.get()); + + nsIContent* parent = element->GetFlattenedTreeParent(); + + if (parent && + parent->NodeInfo()->Equals(nsGkAtoms::svgSwitch, kNameSpaceID_SVG)) { + static_cast<dom::SVGSwitchElement*>(parent)->MaybeInvalidate(); + } +} + +} // namespace dom +} // namespace mozilla diff --git a/dom/svg/SVGTests.h b/dom/svg/SVGTests.h new file mode 100644 index 0000000000..1e0de76e3f --- /dev/null +++ b/dom/svg/SVGTests.h @@ -0,0 +1,114 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef mozilla_dom_SVGTests_h +#define mozilla_dom_SVGTests_h + +#include "nsStringFwd.h" +#include "SVGStringList.h" +#include "nsCOMPtr.h" + +class nsAttrValue; +class nsIAtom; +class nsString; + +namespace mozilla { +class DOMSVGStringList; + +#define MOZILLA_DOMSVGTESTS_IID \ + { 0x92370da8, 0xda28, 0x4895, \ + {0x9b, 0x1b, 0xe0, 0x06, 0x0d, 0xb7, 0x3f, 0xc3 } } + +namespace dom { + +class SVGTests : public nsISupports +{ +public: + NS_DECLARE_STATIC_IID_ACCESSOR(MOZILLA_DOMSVGTESTS_IID) + + SVGTests(); + + friend class mozilla::DOMSVGStringList; + typedef mozilla::SVGStringList SVGStringList; + + /** + * Compare the language name(s) in a systemLanguage attribute to the + * user's language preferences, as defined in + * http://www.w3.org/TR/SVG11/struct.html#SystemLanguageAttribute + * We have a match if a language name in the users language preferences + * exactly equals one of the language names or exactly equals a prefix of + * one of the language names in the systemLanguage attribute. + * @returns 2 * the lowest index in the aAcceptLangs that matches + 1 + * if only the prefix matches, -2 if there's no systemLanguage attribute, + * or -1 if no indices match. + * XXX This algorithm is O(M*N). + */ + int32_t GetBestLanguagePreferenceRank(const nsSubstring& aAcceptLangs) const; + + /** + * Special value to pass to PassesConditionalProcessingTests to ignore systemLanguage + * attributes + */ + static const nsString * const kIgnoreSystemLanguage; + + /** + * Check whether the conditional processing attributes requiredFeatures, + * requiredExtensions and systemLanguage all "return true" if they apply to + * and are specified on the given element. Returns true if this element + * should be rendered, false if it should not. + * + * @param aAcceptLangs Optional parameter to pass in the value of the + * intl.accept_languages preference if the caller has it cached. + * Alternatively, pass in kIgnoreSystemLanguage to skip the systemLanguage + * check if the caller is giving that special treatment. + */ + bool PassesConditionalProcessingTests( + const nsString *aAcceptLangs = nullptr) const; + + /** + * Returns true if the attribute is one of the conditional processing + * attributes. + */ + bool IsConditionalProcessingAttribute(const nsIAtom* aAttribute) const; + + bool ParseConditionalProcessingAttribute( + nsIAtom* aAttribute, + const nsAString& aValue, + nsAttrValue& aResult); + + /** + * Unsets a conditional processing attribute. + */ + void UnsetAttr(const nsIAtom* aAttribute); + + nsIAtom* GetAttrName(uint8_t aAttrEnum) const; + void GetAttrValue(uint8_t aAttrEnum, nsAttrValue &aValue) const; + + void MaybeInvalidate(); + + // WebIDL + already_AddRefed<DOMSVGStringList> RequiredFeatures(); + already_AddRefed<DOMSVGStringList> RequiredExtensions(); + already_AddRefed<DOMSVGStringList> SystemLanguage(); + bool HasExtension(const nsAString& aExtension); + + virtual bool IsInChromeDoc() const = 0; + +protected: + virtual ~SVGTests() {} + +private: + enum { FEATURES, EXTENSIONS, LANGUAGE }; + SVGStringList mStringListAttributes[3]; + static nsIAtom** sStringListNames[3]; +}; + +NS_DEFINE_STATIC_IID_ACCESSOR(SVGTests, MOZILLA_DOMSVGTESTS_IID) + +} // namespace dom +} // namespace mozilla + +#endif // mozilla_dom_SVGTests_h diff --git a/dom/svg/SVGTextContentElement.cpp b/dom/svg/SVGTextContentElement.cpp new file mode 100644 index 0000000000..a3e813e7eb --- /dev/null +++ b/dom/svg/SVGTextContentElement.cpp @@ -0,0 +1,161 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "mozilla/dom/SVGTextContentElement.h" +#include "nsISVGPoint.h" +#include "SVGTextFrame.h" +#include "mozilla/dom/SVGIRect.h" + +namespace mozilla { +namespace dom { + +nsSVGEnumMapping SVGTextContentElement::sLengthAdjustMap[] = { + { &nsGkAtoms::spacing, SVG_LENGTHADJUST_SPACING }, + { &nsGkAtoms::spacingAndGlyphs, SVG_LENGTHADJUST_SPACINGANDGLYPHS }, + { nullptr, 0 } +}; + +nsSVGElement::EnumInfo SVGTextContentElement::sEnumInfo[1] = +{ + { &nsGkAtoms::lengthAdjust, sLengthAdjustMap, SVG_LENGTHADJUST_SPACING } +}; + +nsSVGElement::LengthInfo SVGTextContentElement::sLengthInfo[1] = +{ + { &nsGkAtoms::textLength, 0, nsIDOMSVGLength::SVG_LENGTHTYPE_NUMBER, SVGContentUtils::XY } +}; + +SVGTextFrame* +SVGTextContentElement::GetSVGTextFrame() +{ + nsIFrame* frame = GetPrimaryFrame(Flush_Layout); + while (frame) { + SVGTextFrame* textFrame = do_QueryFrame(frame); + if (textFrame) { + return textFrame; + } + frame = frame->GetParent(); + } + return nullptr; +} + +already_AddRefed<SVGAnimatedLength> +SVGTextContentElement::TextLength() +{ + return LengthAttributes()[TEXTLENGTH].ToDOMAnimatedLength(this); +} + +already_AddRefed<SVGAnimatedEnumeration> +SVGTextContentElement::LengthAdjust() +{ + return EnumAttributes()[LENGTHADJUST].ToDOMAnimatedEnum(this); +} + +//---------------------------------------------------------------------- + +int32_t +SVGTextContentElement::GetNumberOfChars() +{ + SVGTextFrame* textFrame = GetSVGTextFrame(); + return textFrame ? textFrame->GetNumberOfChars(this) : 0; +} + +float +SVGTextContentElement::GetComputedTextLength() +{ + SVGTextFrame* textFrame = GetSVGTextFrame(); + return textFrame ? textFrame->GetComputedTextLength(this) : 0.0f; +} + +void +SVGTextContentElement::SelectSubString(uint32_t charnum, uint32_t nchars, ErrorResult& rv) +{ + SVGTextFrame* textFrame = GetSVGTextFrame(); + if (!textFrame) + return; + + rv = textFrame->SelectSubString(this, charnum, nchars); +} + +float +SVGTextContentElement::GetSubStringLength(uint32_t charnum, uint32_t nchars, ErrorResult& rv) +{ + SVGTextFrame* textFrame = GetSVGTextFrame(); + if (!textFrame) + return 0.0f; + + float length = 0.0f; + rv = textFrame->GetSubStringLength(this, charnum, nchars, &length); + return length; +} + +already_AddRefed<nsISVGPoint> +SVGTextContentElement::GetStartPositionOfChar(uint32_t charnum, ErrorResult& rv) +{ + SVGTextFrame* textFrame = GetSVGTextFrame(); + if (!textFrame) { + rv.Throw(NS_ERROR_FAILURE); + return nullptr; + } + + nsCOMPtr<nsISVGPoint> point; + rv = textFrame->GetStartPositionOfChar(this, charnum, getter_AddRefs(point)); + return point.forget(); +} + +already_AddRefed<nsISVGPoint> +SVGTextContentElement::GetEndPositionOfChar(uint32_t charnum, ErrorResult& rv) +{ + SVGTextFrame* textFrame = GetSVGTextFrame(); + if (!textFrame) { + rv.Throw(NS_ERROR_FAILURE); + return nullptr; + } + + nsCOMPtr<nsISVGPoint> point; + rv = textFrame->GetEndPositionOfChar(this, charnum, getter_AddRefs(point)); + return point.forget(); +} + +already_AddRefed<SVGIRect> +SVGTextContentElement::GetExtentOfChar(uint32_t charnum, ErrorResult& rv) +{ + SVGTextFrame* textFrame = GetSVGTextFrame(); + + if (!textFrame) { + rv.Throw(NS_ERROR_FAILURE); + return nullptr; + } + + RefPtr<SVGIRect> rect; + rv = textFrame->GetExtentOfChar(this, charnum, getter_AddRefs(rect)); + return rect.forget(); +} + +float +SVGTextContentElement::GetRotationOfChar(uint32_t charnum, ErrorResult& rv) +{ + SVGTextFrame* textFrame = GetSVGTextFrame(); + + if (!textFrame) { + rv.Throw(NS_ERROR_FAILURE); + return 0.0f; + } + + float rotation; + rv = textFrame->GetRotationOfChar(this, charnum, &rotation); + return rotation; +} + +int32_t +SVGTextContentElement::GetCharNumAtPosition(nsISVGPoint& aPoint) +{ + SVGTextFrame* textFrame = GetSVGTextFrame(); + return textFrame ? textFrame->GetCharNumAtPosition(this, &aPoint) : -1; +} + +} // namespace dom +} // namespace mozilla diff --git a/dom/svg/SVGTextContentElement.h b/dom/svg/SVGTextContentElement.h new file mode 100644 index 0000000000..905468228f --- /dev/null +++ b/dom/svg/SVGTextContentElement.h @@ -0,0 +1,69 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef mozilla_dom_SVGTextContentElement_h +#define mozilla_dom_SVGTextContentElement_h + +#include "mozilla/dom/SVGGraphicsElement.h" +#include "mozilla/dom/SVGAnimatedEnumeration.h" +#include "nsSVGEnum.h" +#include "nsSVGLength2.h" + +static const unsigned short SVG_LENGTHADJUST_UNKNOWN = 0; +static const unsigned short SVG_LENGTHADJUST_SPACING = 1; +static const unsigned short SVG_LENGTHADJUST_SPACINGANDGLYPHS = 2; + +class SVGTextFrame; + +namespace mozilla { +class nsISVGPoint; + +namespace dom { + +class SVGIRect; + +typedef SVGGraphicsElement SVGTextContentElementBase; + +class SVGTextContentElement : public SVGTextContentElementBase +{ +public: + using FragmentOrElement::TextLength; + + // WebIDL + already_AddRefed<SVGAnimatedLength> TextLength(); + already_AddRefed<SVGAnimatedEnumeration> LengthAdjust(); + int32_t GetNumberOfChars(); + float GetComputedTextLength(); + void SelectSubString(uint32_t charnum, uint32_t nchars, ErrorResult& rv); + float GetSubStringLength(uint32_t charnum, uint32_t nchars, ErrorResult& rv); + already_AddRefed<nsISVGPoint> GetStartPositionOfChar(uint32_t charnum, ErrorResult& rv); + already_AddRefed<nsISVGPoint> GetEndPositionOfChar(uint32_t charnum, ErrorResult& rv); + already_AddRefed<SVGIRect> GetExtentOfChar(uint32_t charnum, ErrorResult& rv); + float GetRotationOfChar(uint32_t charnum, ErrorResult& rv); + int32_t GetCharNumAtPosition(nsISVGPoint& point); + +protected: + + explicit SVGTextContentElement(already_AddRefed<mozilla::dom::NodeInfo>& aNodeInfo) + : SVGTextContentElementBase(aNodeInfo) + {} + + SVGTextFrame* GetSVGTextFrame(); + + enum { LENGTHADJUST }; + virtual nsSVGEnum* EnumAttributes() = 0; + static nsSVGEnumMapping sLengthAdjustMap[]; + static EnumInfo sEnumInfo[1]; + + enum { TEXTLENGTH }; + virtual nsSVGLength2* LengthAttributes() = 0; + static LengthInfo sLengthInfo[1]; +}; + +} // namespace dom +} // namespace mozilla + +#endif // mozilla_dom_SVGTextContentElement_h diff --git a/dom/svg/SVGTextElement.cpp b/dom/svg/SVGTextElement.cpp new file mode 100644 index 0000000000..3d1d1c2308 --- /dev/null +++ b/dom/svg/SVGTextElement.cpp @@ -0,0 +1,66 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "mozilla/dom/SVGTextElement.h" +#include "mozilla/dom/SVGTextElementBinding.h" + +NS_IMPL_NS_NEW_NAMESPACED_SVG_ELEMENT(Text) + +namespace mozilla { +namespace dom { + +JSObject* +SVGTextElement::WrapNode(JSContext *aCx, JS::Handle<JSObject*> aGivenProto) +{ + return SVGTextElementBinding::Wrap(aCx, this, aGivenProto); +} + +//---------------------------------------------------------------------- +// Implementation + +SVGTextElement::SVGTextElement(already_AddRefed<mozilla::dom::NodeInfo>& aNodeInfo) + : SVGTextElementBase(aNodeInfo) +{ +} + +nsSVGElement::EnumAttributesInfo +SVGTextElement::GetEnumInfo() +{ + return EnumAttributesInfo(mEnumAttributes, sEnumInfo, + ArrayLength(sEnumInfo)); +} + +nsSVGElement::LengthAttributesInfo +SVGTextElement::GetLengthInfo() +{ + return LengthAttributesInfo(mLengthAttributes, sLengthInfo, + ArrayLength(sLengthInfo)); +} + +//---------------------------------------------------------------------- +// nsIDOMNode methods + + +NS_IMPL_ELEMENT_CLONE_WITH_INIT(SVGTextElement) + + +//---------------------------------------------------------------------- +// nsIContent methods + +NS_IMETHODIMP_(bool) +SVGTextElement::IsAttributeMapped(const nsIAtom* name) const +{ + static const MappedAttributeEntry* const map[] = { + sTextContentElementsMap, + sFontSpecificationMap + }; + + return FindAttributeDependence(name, map) || + SVGTextElementBase::IsAttributeMapped(name); +} + +} // namespace dom +} // namespace mozilla diff --git a/dom/svg/SVGTextElement.h b/dom/svg/SVGTextElement.h new file mode 100644 index 0000000000..76388cc911 --- /dev/null +++ b/dom/svg/SVGTextElement.h @@ -0,0 +1,51 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef mozilla_dom_SVGTextElement_h +#define mozilla_dom_SVGTextElement_h + +#include "mozilla/dom/SVGTextPositioningElement.h" + +nsresult NS_NewSVGTextElement(nsIContent **aResult, + already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo); + +namespace mozilla { +namespace dom { + +typedef SVGTextPositioningElement SVGTextElementBase; + +class SVGTextElement final : public SVGTextElementBase +{ +protected: + explicit SVGTextElement(already_AddRefed<mozilla::dom::NodeInfo>& aNodeInfo); + virtual JSObject* WrapNode(JSContext *cx, JS::Handle<JSObject*> aGivenProto) override; + + friend nsresult (::NS_NewSVGTextElement(nsIContent **aResult, + already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo)); + +public: + // nsIContent interface + NS_IMETHOD_(bool) IsAttributeMapped(const nsIAtom* aAttribute) const override; + + virtual nsresult Clone(mozilla::dom::NodeInfo *aNodeInfo, nsINode **aResult) const override; + +protected: + virtual EnumAttributesInfo GetEnumInfo() override; + virtual LengthAttributesInfo GetLengthInfo() override; + + nsSVGEnum mEnumAttributes[1]; + virtual nsSVGEnum* EnumAttributes() override + { return mEnumAttributes; } + + nsSVGLength2 mLengthAttributes[1]; + virtual nsSVGLength2* LengthAttributes() override + { return mLengthAttributes; } +}; + +} // namespace dom +} // namespace mozilla + +#endif // mozilla_dom_SVGTextElement_h diff --git a/dom/svg/SVGTextPathElement.cpp b/dom/svg/SVGTextPathElement.cpp new file mode 100644 index 0000000000..7acfbbd884 --- /dev/null +++ b/dom/svg/SVGTextPathElement.cpp @@ -0,0 +1,154 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "mozilla/dom/SVGTextPathElement.h" +#include "mozilla/dom/SVGTextPathElementBinding.h" +#include "nsSVGElement.h" +#include "nsGkAtoms.h" +#include "nsError.h" + +NS_IMPL_NS_NEW_NAMESPACED_SVG_ELEMENT(TextPath) + +namespace mozilla { +namespace dom { + +class SVGAnimatedLength; + +JSObject* +SVGTextPathElement::WrapNode(JSContext *aCx, JS::Handle<JSObject*> aGivenProto) +{ + return SVGTextPathElementBinding::Wrap(aCx, this, aGivenProto); +} + +nsSVGElement::LengthInfo SVGTextPathElement::sLengthInfo[2] = +{ + // from SVGTextContentElement: + { &nsGkAtoms::textLength, 0, nsIDOMSVGLength::SVG_LENGTHTYPE_NUMBER, SVGContentUtils::XY }, + // from SVGTextPathElement: + { &nsGkAtoms::startOffset, 0, nsIDOMSVGLength::SVG_LENGTHTYPE_NUMBER, SVGContentUtils::X } +}; + +nsSVGEnumMapping SVGTextPathElement::sMethodMap[] = { + {&nsGkAtoms::align, TEXTPATH_METHODTYPE_ALIGN}, + {&nsGkAtoms::stretch, TEXTPATH_METHODTYPE_STRETCH}, + {nullptr, 0} +}; + +nsSVGEnumMapping SVGTextPathElement::sSpacingMap[] = { + {&nsGkAtoms::_auto, TEXTPATH_SPACINGTYPE_AUTO}, + {&nsGkAtoms::exact, TEXTPATH_SPACINGTYPE_EXACT}, + {nullptr, 0} +}; + +nsSVGElement::EnumInfo SVGTextPathElement::sEnumInfo[3] = +{ + // from SVGTextContentElement: + { &nsGkAtoms::lengthAdjust, + sLengthAdjustMap, + SVG_LENGTHADJUST_SPACING + }, + // from SVGTextPathElement: + { &nsGkAtoms::method, + sMethodMap, + TEXTPATH_METHODTYPE_ALIGN + }, + { &nsGkAtoms::spacing, + sSpacingMap, + TEXTPATH_SPACINGTYPE_EXACT + } +}; + +nsSVGElement::StringInfo SVGTextPathElement::sStringInfo[2] = +{ + { &nsGkAtoms::href, kNameSpaceID_None, true }, + { &nsGkAtoms::href, kNameSpaceID_XLink, true } +}; + +//---------------------------------------------------------------------- +// Implementation + +SVGTextPathElement::SVGTextPathElement(already_AddRefed<mozilla::dom::NodeInfo>& aNodeInfo) + : SVGTextPathElementBase(aNodeInfo) +{ +} + +//---------------------------------------------------------------------- +// nsIDOMNode methods + +NS_IMPL_ELEMENT_CLONE_WITH_INIT(SVGTextPathElement) + +already_AddRefed<SVGAnimatedString> +SVGTextPathElement::Href() +{ + return mStringAttributes[HREF].IsExplicitlySet() + ? mStringAttributes[HREF].ToDOMAnimatedString(this) + : mStringAttributes[XLINK_HREF].ToDOMAnimatedString(this); +} + +//---------------------------------------------------------------------- + +already_AddRefed<SVGAnimatedLength> +SVGTextPathElement::StartOffset() +{ + return mLengthAttributes[STARTOFFSET].ToDOMAnimatedLength(this); +} + +already_AddRefed<SVGAnimatedEnumeration> +SVGTextPathElement::Method() +{ + return mEnumAttributes[METHOD].ToDOMAnimatedEnum(this); +} + +already_AddRefed<SVGAnimatedEnumeration> +SVGTextPathElement::Spacing() +{ + return mEnumAttributes[SPACING].ToDOMAnimatedEnum(this); +} + +//---------------------------------------------------------------------- +// nsIContent methods + +NS_IMETHODIMP_(bool) +SVGTextPathElement::IsAttributeMapped(const nsIAtom* name) const +{ + static const MappedAttributeEntry* const map[] = { + sColorMap, + sFillStrokeMap, + sFontSpecificationMap, + sGraphicsMap, + sTextContentElementsMap + }; + + return FindAttributeDependence(name, map) || + SVGTextPathElementBase::IsAttributeMapped(name); +} + +//---------------------------------------------------------------------- +// nsSVGElement overrides + +nsSVGElement::LengthAttributesInfo +SVGTextPathElement::GetLengthInfo() +{ + return LengthAttributesInfo(mLengthAttributes, sLengthInfo, + ArrayLength(sLengthInfo)); +} + +nsSVGElement::EnumAttributesInfo +SVGTextPathElement::GetEnumInfo() +{ + return EnumAttributesInfo(mEnumAttributes, sEnumInfo, + ArrayLength(sEnumInfo)); +} + +nsSVGElement::StringAttributesInfo +SVGTextPathElement::GetStringInfo() +{ + return StringAttributesInfo(mStringAttributes, sStringInfo, + ArrayLength(sStringInfo)); +} + +} // namespace dom +} // namespace mozilla diff --git a/dom/svg/SVGTextPathElement.h b/dom/svg/SVGTextPathElement.h new file mode 100644 index 0000000000..39cf72e80f --- /dev/null +++ b/dom/svg/SVGTextPathElement.h @@ -0,0 +1,85 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef mozilla_dom_SVGTextPathElement_h +#define mozilla_dom_SVGTextPathElement_h + +#include "nsSVGEnum.h" +#include "nsSVGLength2.h" +#include "nsSVGString.h" +#include "mozilla/dom/SVGTextContentElement.h" + +class nsIAtom; +class nsIContent; + +nsresult NS_NewSVGTextPathElement(nsIContent **aResult, + already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo); + +namespace mozilla { +namespace dom { + +// textPath Method Types +static const unsigned short TEXTPATH_METHODTYPE_UNKNOWN = 0; +static const unsigned short TEXTPATH_METHODTYPE_ALIGN = 1; +static const unsigned short TEXTPATH_METHODTYPE_STRETCH = 2; +// textPath Spacing Types +static const unsigned short TEXTPATH_SPACINGTYPE_UNKNOWN = 0; +static const unsigned short TEXTPATH_SPACINGTYPE_AUTO = 1; +static const unsigned short TEXTPATH_SPACINGTYPE_EXACT = 2; + +typedef SVGTextContentElement SVGTextPathElementBase; + +class SVGTextPathElement final : public SVGTextPathElementBase +{ +friend class ::SVGTextFrame; + +protected: + friend nsresult (::NS_NewSVGTextPathElement(nsIContent **aResult, + already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo)); + explicit SVGTextPathElement(already_AddRefed<mozilla::dom::NodeInfo>& aNodeInfo); + virtual JSObject* WrapNode(JSContext *cx, JS::Handle<JSObject*> aGivenProto) override; + +public: + // nsIContent interface + NS_IMETHOD_(bool) IsAttributeMapped(const nsIAtom* aAttribute) const override; + + virtual nsresult Clone(mozilla::dom::NodeInfo *aNodeInfo, nsINode **aResult) const override; + + // WebIDL + already_AddRefed<SVGAnimatedLength> StartOffset(); + already_AddRefed<SVGAnimatedEnumeration> Method(); + already_AddRefed<SVGAnimatedEnumeration> Spacing(); + already_AddRefed<SVGAnimatedString> Href(); + + protected: + + virtual LengthAttributesInfo GetLengthInfo() override; + virtual EnumAttributesInfo GetEnumInfo() override; + virtual StringAttributesInfo GetStringInfo() override; + + enum { /* TEXTLENGTH, */ STARTOFFSET = 1 }; + nsSVGLength2 mLengthAttributes[2]; + virtual nsSVGLength2* LengthAttributes() override + { return mLengthAttributes; } + static LengthInfo sLengthInfo[2]; + + enum { /* LENGTHADJUST, */ METHOD = 1, SPACING }; + nsSVGEnum mEnumAttributes[3]; + virtual nsSVGEnum* EnumAttributes() override + { return mEnumAttributes; } + static nsSVGEnumMapping sMethodMap[]; + static nsSVGEnumMapping sSpacingMap[]; + static EnumInfo sEnumInfo[3]; + + enum { HREF, XLINK_HREF }; + nsSVGString mStringAttributes[2]; + static StringInfo sStringInfo[2]; +}; + +} // namespace dom +} // namespace mozilla + +#endif // mozilla_dom_SVGTextPathElement_h diff --git a/dom/svg/SVGTextPositioningElement.cpp b/dom/svg/SVGTextPositioningElement.cpp new file mode 100644 index 0000000000..e4a3cbf4fc --- /dev/null +++ b/dom/svg/SVGTextPositioningElement.cpp @@ -0,0 +1,84 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "mozilla/ArrayUtils.h" + +#include "mozilla/dom/SVGTextPositioningElement.h" +#include "SVGAnimatedLengthList.h" +#include "DOMSVGAnimatedLengthList.h" +#include "DOMSVGAnimatedNumberList.h" +#include "SVGContentUtils.h" + +namespace mozilla { +namespace dom { + +nsSVGElement::LengthListInfo SVGTextPositioningElement::sLengthListInfo[4] = +{ + { &nsGkAtoms::x, SVGContentUtils::X, false }, + { &nsGkAtoms::y, SVGContentUtils::Y, false }, + { &nsGkAtoms::dx, SVGContentUtils::X, true }, + { &nsGkAtoms::dy, SVGContentUtils::Y, true } +}; + +nsSVGElement::LengthListAttributesInfo +SVGTextPositioningElement::GetLengthListInfo() +{ + return LengthListAttributesInfo(mLengthListAttributes, sLengthListInfo, + ArrayLength(sLengthListInfo)); +} + + +nsSVGElement::NumberListInfo SVGTextPositioningElement::sNumberListInfo[1] = +{ + { &nsGkAtoms::rotate } +}; + +nsSVGElement::NumberListAttributesInfo +SVGTextPositioningElement::GetNumberListInfo() +{ + return NumberListAttributesInfo(mNumberListAttributes, sNumberListInfo, + ArrayLength(sNumberListInfo)); +} + +//---------------------------------------------------------------------- + +already_AddRefed<DOMSVGAnimatedLengthList> +SVGTextPositioningElement::X() +{ + return DOMSVGAnimatedLengthList::GetDOMWrapper(&mLengthListAttributes[ATTR_X], + this, ATTR_X, SVGContentUtils::X); +} + +already_AddRefed<DOMSVGAnimatedLengthList> +SVGTextPositioningElement::Y() +{ + return DOMSVGAnimatedLengthList::GetDOMWrapper(&mLengthListAttributes[ATTR_Y], + this, ATTR_Y, SVGContentUtils::Y); +} + +already_AddRefed<DOMSVGAnimatedLengthList> +SVGTextPositioningElement::Dx() +{ + return DOMSVGAnimatedLengthList::GetDOMWrapper(&mLengthListAttributes[ATTR_DX], + this, ATTR_DX, SVGContentUtils::X); +} + +already_AddRefed<DOMSVGAnimatedLengthList> +SVGTextPositioningElement::Dy() +{ + return DOMSVGAnimatedLengthList::GetDOMWrapper(&mLengthListAttributes[ATTR_DY], + this, ATTR_DY, SVGContentUtils::Y); +} + +already_AddRefed<DOMSVGAnimatedNumberList> +SVGTextPositioningElement::Rotate() +{ + return DOMSVGAnimatedNumberList::GetDOMWrapper(&mNumberListAttributes[ROTATE], + this, ROTATE); +} + +} // namespace dom +} // namespace mozilla diff --git a/dom/svg/SVGTextPositioningElement.h b/dom/svg/SVGTextPositioningElement.h new file mode 100644 index 0000000000..b58bba84a6 --- /dev/null +++ b/dom/svg/SVGTextPositioningElement.h @@ -0,0 +1,53 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef mozilla_dom_SVGTextPositioningElement_h +#define mozilla_dom_SVGTextPositioningElement_h + +#include "mozilla/dom/SVGTextContentElement.h" +#include "SVGAnimatedLengthList.h" +#include "SVGAnimatedNumberList.h" + +namespace mozilla { +class SVGAnimatedLengthList; +class DOMSVGAnimatedLengthList; +class DOMSVGAnimatedNumberList; + +namespace dom { +typedef SVGTextContentElement SVGTextPositioningElementBase; + +class SVGTextPositioningElement : public SVGTextPositioningElementBase +{ +public: + // WebIDL + already_AddRefed<DOMSVGAnimatedLengthList> X(); + already_AddRefed<DOMSVGAnimatedLengthList> Y(); + already_AddRefed<DOMSVGAnimatedLengthList> Dx(); + already_AddRefed<DOMSVGAnimatedLengthList> Dy(); + already_AddRefed<DOMSVGAnimatedNumberList> Rotate(); + +protected: + + explicit SVGTextPositioningElement(already_AddRefed<mozilla::dom::NodeInfo>& aNodeInfo) + : SVGTextPositioningElementBase(aNodeInfo) + {} + + virtual LengthListAttributesInfo GetLengthListInfo() override; + virtual NumberListAttributesInfo GetNumberListInfo() override; + + enum { ATTR_X, ATTR_Y, ATTR_DX, ATTR_DY }; + SVGAnimatedLengthList mLengthListAttributes[4]; + static LengthListInfo sLengthListInfo[4]; + + enum { ROTATE }; + SVGAnimatedNumberList mNumberListAttributes[1]; + static NumberListInfo sNumberListInfo[1]; +}; + +} // namespace dom +} // namespace mozilla + +#endif // mozilla_dom_SVGTextPositioningElement_h diff --git a/dom/svg/SVGTitleElement.cpp b/dom/svg/SVGTitleElement.cpp new file mode 100644 index 0000000000..bd72177811 --- /dev/null +++ b/dom/svg/SVGTitleElement.cpp @@ -0,0 +1,128 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "mozilla/dom/SVGTitleElement.h" +#include "mozilla/dom/SVGTitleElementBinding.h" + +NS_IMPL_NS_NEW_NAMESPACED_SVG_ELEMENT(Title) + +namespace mozilla { +namespace dom { + +JSObject* +SVGTitleElement::WrapNode(JSContext *aCx, JS::Handle<JSObject*> aGivenProto) +{ + return SVGTitleElementBinding::Wrap(aCx, this, aGivenProto); +} + +//---------------------------------------------------------------------- +// nsISupports methods + +NS_IMPL_ISUPPORTS_INHERITED(SVGTitleElement, SVGTitleElementBase, + nsIDOMNode, nsIDOMElement, + nsIDOMSVGElement, + nsIMutationObserver) + +//---------------------------------------------------------------------- +// Implementation + +SVGTitleElement::SVGTitleElement(already_AddRefed<mozilla::dom::NodeInfo>& aNodeInfo) + : SVGTitleElementBase(aNodeInfo) +{ + AddMutationObserver(this); +} + +SVGTitleElement::~SVGTitleElement() +{ +} + +void +SVGTitleElement::CharacterDataChanged(nsIDocument *aDocument, + nsIContent *aContent, + CharacterDataChangeInfo *aInfo) +{ + SendTitleChangeEvent(false); +} + +void +SVGTitleElement::ContentAppended(nsIDocument *aDocument, + nsIContent *aContainer, + nsIContent *aFirstNewContent, + int32_t aNewIndexInContainer) +{ + SendTitleChangeEvent(false); +} + +void +SVGTitleElement::ContentInserted(nsIDocument *aDocument, + nsIContent *aContainer, + nsIContent *aChild, + int32_t aIndexInContainer) +{ + SendTitleChangeEvent(false); +} + +void +SVGTitleElement::ContentRemoved(nsIDocument *aDocument, + nsIContent *aContainer, + nsIContent *aChild, + int32_t aIndexInContainer, + nsIContent *aPreviousSibling) +{ + SendTitleChangeEvent(false); +} + +nsresult +SVGTitleElement::BindToTree(nsIDocument *aDocument, + nsIContent *aParent, + nsIContent *aBindingParent, + bool aCompileEventHandlers) +{ + // Let this fall through. + nsresult rv = SVGTitleElementBase::BindToTree(aDocument, aParent, + aBindingParent, + aCompileEventHandlers); + NS_ENSURE_SUCCESS(rv, rv); + + SendTitleChangeEvent(true); + + return NS_OK; +} + +void +SVGTitleElement::UnbindFromTree(bool aDeep, bool aNullParent) +{ + SendTitleChangeEvent(false); + + // Let this fall through. + SVGTitleElementBase::UnbindFromTree(aDeep, aNullParent); +} + +void +SVGTitleElement::DoneAddingChildren(bool aHaveNotified) +{ + if (!aHaveNotified) { + SendTitleChangeEvent(false); + } +} + +void +SVGTitleElement::SendTitleChangeEvent(bool aBound) +{ + nsIDocument* doc = GetUncomposedDoc(); + if (doc) { + doc->NotifyPossibleTitleChange(aBound); + } +} + +//---------------------------------------------------------------------- +// nsIDOMNode methods + +NS_IMPL_ELEMENT_CLONE_WITH_INIT(SVGTitleElement) + +} // namespace dom +} // namespace mozilla + diff --git a/dom/svg/SVGTitleElement.h b/dom/svg/SVGTitleElement.h new file mode 100644 index 0000000000..ca9151d601 --- /dev/null +++ b/dom/svg/SVGTitleElement.h @@ -0,0 +1,61 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef mozilla_dom_SVGTitleElement_h +#define mozilla_dom_SVGTitleElement_h + +#include "mozilla/Attributes.h" +#include "nsSVGElement.h" +#include "nsStubMutationObserver.h" + +typedef nsSVGElement SVGTitleElementBase; + +nsresult NS_NewSVGTitleElement(nsIContent **aResult, + already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo); +namespace mozilla { +namespace dom { + +class SVGTitleElement final : public SVGTitleElementBase, + public nsStubMutationObserver +{ +protected: + friend nsresult (::NS_NewSVGTitleElement(nsIContent **aResult, + already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo)); + explicit SVGTitleElement(already_AddRefed<mozilla::dom::NodeInfo>& aNodeInfo); + ~SVGTitleElement(); + + virtual JSObject* WrapNode(JSContext *aCx, JS::Handle<JSObject*> aGivenProto) override; + +public: + // interfaces: + + NS_DECL_ISUPPORTS_INHERITED + + // nsIMutationObserver + NS_DECL_NSIMUTATIONOBSERVER_CHARACTERDATACHANGED + NS_DECL_NSIMUTATIONOBSERVER_CONTENTAPPENDED + NS_DECL_NSIMUTATIONOBSERVER_CONTENTINSERTED + NS_DECL_NSIMUTATIONOBSERVER_CONTENTREMOVED + + virtual nsresult Clone(mozilla::dom::NodeInfo *aNodeInfo, nsINode **aResult) const override; + + virtual nsresult BindToTree(nsIDocument *aDocument, nsIContent *aParent, + nsIContent *aBindingParent, + bool aCompileEventHandlers) override; + + virtual void UnbindFromTree(bool aDeep = true, + bool aNullParent = true) override; + + virtual void DoneAddingChildren(bool aHaveNotified) override; +private: + void SendTitleChangeEvent(bool aBound); +}; + +} // namespace dom +} // namespace mozilla + +#endif // mozilla_dom_SVGTitleElement_h + diff --git a/dom/svg/SVGTransform.cpp b/dom/svg/SVGTransform.cpp new file mode 100644 index 0000000000..b73018d1c4 --- /dev/null +++ b/dom/svg/SVGTransform.cpp @@ -0,0 +1,371 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "SVGTransform.h" + +#include "mozilla/dom/SVGTransform.h" +#include "mozilla/dom/SVGMatrix.h" +#include "mozilla/dom/SVGTransformBinding.h" +#include "nsError.h" +#include "nsSVGAnimatedTransformList.h" +#include "nsSVGAttrTearoffTable.h" +#include "mozilla/DebugOnly.h" +#include "mozilla/FloatingPoint.h" + +namespace { + const double kRadPerDegree = 2.0 * M_PI / 360.0; +} // namespace + +namespace mozilla { +namespace dom { + +static nsSVGAttrTearoffTable<SVGTransform, SVGMatrix>& +SVGMatrixTearoffTable() +{ + static nsSVGAttrTearoffTable<SVGTransform, SVGMatrix> sSVGMatrixTearoffTable; + return sSVGMatrixTearoffTable; +} + +//---------------------------------------------------------------------- + +// We could use NS_IMPL_CYCLE_COLLECTION(, 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(SVGTransform) + +NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(SVGTransform) + // We may not belong to a list, so we must null check tmp->mList. + if (tmp->mList) { + tmp->mList->mItems[tmp->mListIndex] = nullptr; + } +NS_IMPL_CYCLE_COLLECTION_UNLINK(mList) +NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER +NS_IMPL_CYCLE_COLLECTION_UNLINK_END + +NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(SVGTransform) +NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mList) + SVGMatrix* matrix = + SVGMatrixTearoffTable().GetTearoff(tmp); + CycleCollectionNoteChild(cb, matrix, "matrix"); +NS_IMPL_CYCLE_COLLECTION_TRAVERSE_SCRIPT_OBJECTS +NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END + +NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(SVGTransform) +NS_IMPL_CYCLE_COLLECTION_TRACE_PRESERVED_WRAPPER +NS_IMPL_CYCLE_COLLECTION_TRACE_END + +NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(SVGTransform, AddRef) +NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(SVGTransform, Release) + +JSObject* +SVGTransform::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) +{ + return SVGTransformBinding::Wrap(aCx, this, aGivenProto); +} + +//---------------------------------------------------------------------- +// Helper class: AutoChangeTransformNotifier +// Stack-based helper class to pair calls to WillChangeTransformList +// and DidChangeTransformList. +class MOZ_RAII AutoChangeTransformNotifier +{ +public: + explicit AutoChangeTransformNotifier(SVGTransform* aTransform MOZ_GUARD_OBJECT_NOTIFIER_PARAM) + : mTransform(aTransform) + { + MOZ_GUARD_OBJECT_NOTIFIER_INIT; + MOZ_ASSERT(mTransform, "Expecting non-null transform"); + if (mTransform->HasOwner()) { + mEmptyOrOldValue = + mTransform->Element()->WillChangeTransformList(); + } + } + + ~AutoChangeTransformNotifier() + { + if (mTransform->HasOwner()) { + mTransform->Element()->DidChangeTransformList(mEmptyOrOldValue); + if (mTransform->mList->IsAnimating()) { + mTransform->Element()->AnimationNeedsResample(); + } + } + } + +private: + SVGTransform* const mTransform; + nsAttrValue mEmptyOrOldValue; + MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER +}; + +//---------------------------------------------------------------------- +// Ctors: + +SVGTransform::SVGTransform(DOMSVGTransformList *aList, + uint32_t aListIndex, + bool aIsAnimValItem) + : mList(aList) + , mListIndex(aListIndex) + , mIsAnimValItem(aIsAnimValItem) + , mTransform(nullptr) +{ + // These shifts are in sync with the members in the header. + MOZ_ASSERT(aList && aListIndex <= MaxListIndex(), "bad arg"); + + MOZ_ASSERT(IndexIsValid(), "Bad index for DOMSVGNumber!"); +} + +SVGTransform::SVGTransform() + : mList(nullptr) + , mListIndex(0) + , mIsAnimValItem(false) + , mTransform(new nsSVGTransform()) // Default ctor for objects not in a list + // initialises to matrix type with identity + // matrix +{ +} + +SVGTransform::SVGTransform(const gfxMatrix &aMatrix) + : mList(nullptr) + , mListIndex(0) + , mIsAnimValItem(false) + , mTransform(new nsSVGTransform(aMatrix)) +{ +} + +SVGTransform::SVGTransform(const nsSVGTransform &aTransform) + : mList(nullptr) + , mListIndex(0) + , mIsAnimValItem(false) + , mTransform(new nsSVGTransform(aTransform)) +{ +} + +SVGTransform::~SVGTransform() +{ + SVGMatrix* matrix = SVGMatrixTearoffTable().GetTearoff(this); + if (matrix) { + SVGMatrixTearoffTable().RemoveTearoff(this); + NS_RELEASE(matrix); + } + // 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] = nullptr; + } +} + +uint16_t +SVGTransform::Type() const +{ + return Transform().Type(); +} + +SVGMatrix* +SVGTransform::GetMatrix() +{ + SVGMatrix* wrapper = + SVGMatrixTearoffTable().GetTearoff(this); + if (!wrapper) { + NS_ADDREF(wrapper = new SVGMatrix(*this)); + SVGMatrixTearoffTable().AddTearoff(this, wrapper); + } + return wrapper; +} + +float +SVGTransform::Angle() const +{ + return Transform().Angle(); +} + +void +SVGTransform::SetMatrix(SVGMatrix& aMatrix, ErrorResult& rv) +{ + if (mIsAnimValItem) { + rv.Throw(NS_ERROR_DOM_NO_MODIFICATION_ALLOWED_ERR); + return; + } + SetMatrix(aMatrix.GetMatrix()); +} + +void +SVGTransform::SetTranslate(float tx, float ty, ErrorResult& rv) +{ + if (mIsAnimValItem) { + rv.Throw(NS_ERROR_DOM_NO_MODIFICATION_ALLOWED_ERR); + return; + } + + if (Transform().Type() == SVG_TRANSFORM_TRANSLATE && + Matrixgfx()._31 == tx && Matrixgfx()._32 == ty) { + return; + } + + AutoChangeTransformNotifier notifier(this); + Transform().SetTranslate(tx, ty); +} + +void +SVGTransform::SetScale(float sx, float sy, ErrorResult& rv) +{ + if (mIsAnimValItem) { + rv.Throw(NS_ERROR_DOM_NO_MODIFICATION_ALLOWED_ERR); + return; + } + + if (Transform().Type() == SVG_TRANSFORM_SCALE && + Matrixgfx()._11 == sx && Matrixgfx()._22 == sy) { + return; + } + AutoChangeTransformNotifier notifier(this); + Transform().SetScale(sx, sy); +} + +void +SVGTransform::SetRotate(float angle, float cx, float cy, ErrorResult& rv) +{ + if (mIsAnimValItem) { + rv.Throw(NS_ERROR_DOM_NO_MODIFICATION_ALLOWED_ERR); + return; + } + + if (Transform().Type() == SVG_TRANSFORM_ROTATE) { + float currentCx, currentCy; + Transform().GetRotationOrigin(currentCx, currentCy); + if (Transform().Angle() == angle && currentCx == cx && currentCy == cy) { + return; + } + } + + AutoChangeTransformNotifier notifier(this); + Transform().SetRotate(angle, cx, cy); +} + +void +SVGTransform::SetSkewX(float angle, ErrorResult& rv) +{ + if (mIsAnimValItem) { + rv.Throw(NS_ERROR_DOM_NO_MODIFICATION_ALLOWED_ERR); + return; + } + + if (Transform().Type() == SVG_TRANSFORM_SKEWX && + Transform().Angle() == angle) { + return; + } + + if (!IsFinite(tan(angle * kRadPerDegree))) { + rv.ThrowRangeError<MSG_INVALID_TRANSFORM_ANGLE_ERROR>(); + return; + } + + AutoChangeTransformNotifier notifier(this); + DebugOnly<nsresult> result = Transform().SetSkewX(angle); + MOZ_ASSERT(NS_SUCCEEDED(result), "SetSkewX unexpectedly failed"); +} + +void +SVGTransform::SetSkewY(float angle, ErrorResult& rv) +{ + if (mIsAnimValItem) { + rv.Throw(NS_ERROR_DOM_NO_MODIFICATION_ALLOWED_ERR); + return; + } + + if (Transform().Type() == SVG_TRANSFORM_SKEWY && + Transform().Angle() == angle) { + return; + } + + if (!IsFinite(tan(angle * kRadPerDegree))) { + rv.ThrowRangeError<MSG_INVALID_TRANSFORM_ANGLE_ERROR>(); + return; + } + + AutoChangeTransformNotifier notifier(this); + DebugOnly<nsresult> result = Transform().SetSkewY(angle); + MOZ_ASSERT(NS_SUCCEEDED(result), "SetSkewY unexpectedly failed"); +} + +//---------------------------------------------------------------------- +// List management methods: + +void +SVGTransform::InsertingIntoList(DOMSVGTransformList *aList, + uint32_t aListIndex, + bool aIsAnimValItem) +{ + MOZ_ASSERT(!HasOwner(), "Inserting item that is already in a list"); + + mList = aList; + mListIndex = aListIndex; + mIsAnimValItem = aIsAnimValItem; + mTransform = nullptr; + + MOZ_ASSERT(IndexIsValid(), "Bad index for DOMSVGLength!"); +} + +void +SVGTransform::RemovingFromList() +{ + MOZ_ASSERT(!mTransform, + "Item in list also has another non-list value associated with it"); + + mTransform = new nsSVGTransform(InternalItem()); + mList = nullptr; + mIsAnimValItem = false; +} + +nsSVGTransform& +SVGTransform::InternalItem() +{ + nsSVGAnimatedTransformList *alist = Element()->GetAnimatedTransformList(); + return mIsAnimValItem && alist->mAnimVal ? + (*alist->mAnimVal)[mListIndex] : + alist->mBaseVal[mListIndex]; +} + +const nsSVGTransform& +SVGTransform::InternalItem() const +{ + return const_cast<SVGTransform*>(this)->InternalItem(); +} + +#ifdef DEBUG +bool +SVGTransform::IndexIsValid() +{ + nsSVGAnimatedTransformList *alist = Element()->GetAnimatedTransformList(); + return (mIsAnimValItem && + mListIndex < alist->GetAnimValue().Length()) || + (!mIsAnimValItem && + mListIndex < alist->GetBaseValue().Length()); +} +#endif // DEBUG + + +//---------------------------------------------------------------------- +// Interface for SVGMatrix's use + +void +SVGTransform::SetMatrix(const gfxMatrix& aMatrix) +{ + MOZ_ASSERT(!mIsAnimValItem, + "Attempting to modify read-only transform"); + + if (Transform().Type() == SVG_TRANSFORM_MATRIX && + nsSVGTransform::MatricesEqual(Matrixgfx(), aMatrix)) { + return; + } + + AutoChangeTransformNotifier notifier(this); + Transform().SetMatrix(aMatrix); +} + +} // namespace dom +} // namespace mozilla diff --git a/dom/svg/SVGTransform.h b/dom/svg/SVGTransform.h new file mode 100644 index 0000000000..49c0b03ed1 --- /dev/null +++ b/dom/svg/SVGTransform.h @@ -0,0 +1,190 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef mozilla_dom_SVGTransform_h +#define mozilla_dom_SVGTransform_h + +#include "DOMSVGTransformList.h" +#include "nsAutoPtr.h" +#include "nsCycleCollectionParticipant.h" +#include "nsDebug.h" +#include "nsID.h" +#include "nsSVGTransform.h" +#include "nsWrapperCache.h" +#include "mozilla/Attributes.h" + +class nsSVGElement; + +class gfxMatrix; + +#define MOZ_SVG_LIST_INDEX_BIT_COUNT 31 // supports > 2 billion list items + +namespace mozilla { +namespace dom { + +class SVGMatrix; + +/** + * DOM wrapper for an SVG transform. See DOMSVGLength.h. + */ +class SVGTransform final : public nsWrapperCache +{ + friend class AutoChangeTransformNotifier; + +public: + NS_INLINE_DECL_CYCLE_COLLECTING_NATIVE_REFCOUNTING(SVGTransform) + NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_NATIVE_CLASS(SVGTransform) + + /** + * Generic ctor for SVGTransform objects that are created for an attribute. + */ + SVGTransform(DOMSVGTransformList *aList, + uint32_t aListIndex, + bool aIsAnimValItem); + + /** + * Ctors for creating the objects returned by: + * SVGSVGElement.createSVGTransform(), + * SVGSVGElement.createSVGTransformFromMatrix(in SVGMatrix matrix), + * SVGTransformList.createSVGTransformFromMatrix(in SVGMatrix matrix) + * which do not initially belong to an attribute. + */ + explicit SVGTransform(); + explicit SVGTransform(const gfxMatrix &aMatrix); + + /** + * Ctor for creating an unowned copy. Used with Clone(). + */ + explicit SVGTransform(const nsSVGTransform &aMatrix); + + /** + * Create an unowned copy of an owned transform. The caller is responsible for + * the first AddRef(). + */ + SVGTransform* Clone() { + NS_ASSERTION(mList, "unexpected caller"); + return new SVGTransform(InternalItem()); + } + + bool IsInList() const { + return !!mList; + } + + /** + * In future, if this class is used for non-list transforms, this will be + * different to IsInList(). + */ + bool 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 + * DOMSVGTransformList).) + */ + void InsertingIntoList(DOMSVGTransformList *aList, + uint32_t aListIndex, + bool aIsAnimValItem); + + static uint32_t MaxListIndex() { + return (1U << MOZ_SVG_LIST_INDEX_BIT_COUNT) - 1; + } + + /// This method is called to notify this object that its list index changed. + void UpdateListIndex(uint32_t 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 + * "lose" its value on being removed.) + */ + void RemovingFromList(); + + nsSVGTransform ToSVGTransform() const { + return Transform(); + } + + // WebIDL + DOMSVGTransformList* GetParentObject() const { return mList; } + virtual JSObject* WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override; + uint16_t Type() const; + dom::SVGMatrix* GetMatrix(); + float Angle() const; + void SetMatrix(dom::SVGMatrix& matrix, ErrorResult& rv); + void SetTranslate(float tx, float ty, ErrorResult& rv); + void SetScale(float sx, float sy, ErrorResult& rv); + void SetRotate(float angle, float cx, float cy, ErrorResult& rv); + void SetSkewX(float angle, ErrorResult& rv); + void SetSkewY(float angle, ErrorResult& rv); + +protected: + ~SVGTransform(); + + // Interface for SVGMatrix's use + friend class dom::SVGMatrix; + bool IsAnimVal() const { + return mIsAnimValItem; + } + const gfxMatrix& Matrixgfx() const { + return Transform().GetMatrix(); + } + void SetMatrix(const gfxMatrix& aMatrix); + +private: + nsSVGElement* Element() { + return mList->Element(); + } + + /** + * Get a reference to the internal nsSVGTransform list item that this DOM + * wrapper object currently wraps. + */ + nsSVGTransform& InternalItem(); + const nsSVGTransform& InternalItem() const; + +#ifdef DEBUG + bool IndexIsValid(); +#endif + + const nsSVGTransform& Transform() const { + return HasOwner() ? InternalItem() : *mTransform; + } + nsSVGTransform& Transform() { + return HasOwner() ? InternalItem() : *mTransform; + } + + RefPtr<DOMSVGTransformList> 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. + + uint32_t mListIndex:MOZ_SVG_LIST_INDEX_BIT_COUNT; + uint32_t mIsAnimValItem:1; + + // Usually this class acts as a wrapper for an nsSVGTransform object which is + // part of a list and is accessed by going via the owning Element. + // + // However, in some circumstances, objects of this class may not be associated + // with any particular list and thus, no internal nsSVGTransform object. In + // that case we allocate an nsSVGTransform object on the heap to store the data. + nsAutoPtr<nsSVGTransform> mTransform; +}; + +} // namespace dom +} // namespace mozilla + +#undef MOZ_SVG_LIST_INDEX_BIT_COUNT + +#endif // mozilla_dom_SVGTransform_h diff --git a/dom/svg/SVGTransformList.cpp b/dom/svg/SVGTransformList.cpp new file mode 100644 index 0000000000..c6c4eefafe --- /dev/null +++ b/dom/svg/SVGTransformList.cpp @@ -0,0 +1,80 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "SVGTransformList.h" +#include "SVGTransformListParser.h" +#include "nsString.h" +#include "nsError.h" + +namespace mozilla { + +gfxMatrix +SVGTransformList::GetConsolidationMatrix() 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: + gfxMatrix result; + + if (mItems.IsEmpty()) + return result; + + result = mItems[0].GetMatrix(); + + if (mItems.Length() == 1) + return result; + + for (uint32_t i = 1; i < mItems.Length(); ++i) { + result.PreMultiply(mItems[i].GetMatrix()); + } + + return result; +} + +nsresult +SVGTransformList::CopyFrom(const SVGTransformList& rhs) +{ + return CopyFrom(rhs.mItems); +} + +nsresult +SVGTransformList::CopyFrom(const nsTArray<nsSVGTransform>& aTransformArray) +{ + if (!mItems.Assign(aTransformArray, fallible)) { + return NS_ERROR_OUT_OF_MEMORY; + } + return NS_OK; +} + +void +SVGTransformList::GetValueAsString(nsAString& aValue) const +{ + aValue.Truncate(); + uint32_t last = mItems.Length() - 1; + for (uint32_t i = 0; i < mItems.Length(); ++i) { + nsAutoString length; + mItems[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(' '); + } + } +} + +nsresult +SVGTransformList::SetValueFromString(const nsAString& aValue) +{ + SVGTransformListParser parser(aValue); + if (!parser.Parse()) { + // there was a parse error. + return NS_ERROR_DOM_SYNTAX_ERR; + } + + return CopyFrom(parser.GetTransformList()); +} + +} // namespace mozilla diff --git a/dom/svg/SVGTransformList.h b/dom/svg/SVGTransformList.h new file mode 100644 index 0000000000..ed8202bce7 --- /dev/null +++ b/dom/svg/SVGTransformList.h @@ -0,0 +1,145 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef MOZILLA_SVGTRANSFORMLIST_H__ +#define MOZILLA_SVGTRANSFORMLIST_H__ + +#include "gfxMatrix.h" +#include "nsDebug.h" +#include "nsTArray.h" +#include "nsSVGTransform.h" + +namespace mozilla { + +namespace dom { +class SVGTransform; +} // namespace dom + +/** + * 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 DOMSVGTransformList. + */ +class SVGTransformList +{ + friend class nsSVGAnimatedTransformList; + friend class DOMSVGTransformList; + friend class dom::SVGTransform; + +public: + SVGTransformList() {} + ~SVGTransformList() {} + + // 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; + + bool IsEmpty() const { + return mItems.IsEmpty(); + } + + uint32_t Length() const { + return mItems.Length(); + } + + const nsSVGTransform& operator[](uint32_t aIndex) const { + return mItems[aIndex]; + } + + bool operator==(const SVGTransformList& rhs) const { + return mItems == rhs.mItems; + } + + bool SetCapacity(uint32_t size) { + return mItems.SetCapacity(size, fallible); + } + + void Compact() { + mItems.Compact(); + } + + gfxMatrix GetConsolidationMatrix() const; + + // 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 + // SVGAnimatedTransformList and having that class act as an intermediary so it + // can take care of keeping DOM wrappers in sync. + +protected: + + /** + * These may fail on OOM if the internal capacity needs to be increased, in + * which case the list will be left unmodified. + */ + nsresult CopyFrom(const SVGTransformList& rhs); + nsresult CopyFrom(const nsTArray<nsSVGTransform>& aTransformArray); + + nsSVGTransform& operator[](uint32_t aIndex) { + return mItems[aIndex]; + } + + /** + * This may fail (return false) on OOM if the internal capacity is being + * increased, in which case the list will be left unmodified. + */ + bool SetLength(uint32_t aNumberOfItems) { + return mItems.SetLength(aNumberOfItems, fallible); + } + +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() { + mItems.Clear(); + } + + bool InsertItem(uint32_t aIndex, const nsSVGTransform& aTransform) { + if (aIndex >= mItems.Length()) { + aIndex = mItems.Length(); + } + return !!mItems.InsertElementAt(aIndex, aTransform, fallible); + } + + void ReplaceItem(uint32_t aIndex, const nsSVGTransform& aTransform) { + MOZ_ASSERT(aIndex < mItems.Length(), + "DOM wrapper caller should have raised INDEX_SIZE_ERR"); + mItems[aIndex] = aTransform; + } + + void RemoveItem(uint32_t aIndex) { + MOZ_ASSERT(aIndex < mItems.Length(), + "DOM wrapper caller should have raised INDEX_SIZE_ERR"); + mItems.RemoveElementAt(aIndex); + } + + bool AppendItem(const nsSVGTransform& aTransform) { + return !!mItems.AppendElement(aTransform, fallible); + } + +protected: + /* + * See SVGLengthList for the rationale for using FallibleTArray<nsSVGTransform> + * instead of FallibleTArray<nsSVGTransform, 1>. + */ + FallibleTArray<nsSVGTransform> mItems; +}; + +} // namespace mozilla + +#endif // MOZILLA_SVGTRANSFORMLIST_H__ diff --git a/dom/svg/SVGTransformListParser.cpp b/dom/svg/SVGTransformListParser.cpp new file mode 100644 index 0000000000..635c96b111 --- /dev/null +++ b/dom/svg/SVGTransformListParser.cpp @@ -0,0 +1,273 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "mozilla/ArrayUtils.h" + +#include "SVGTransformListParser.h" +#include "SVGContentUtils.h" +#include "nsSVGTransform.h" +#include "nsGkAtoms.h" +#include "nsIAtom.h" + +using namespace mozilla; + +//---------------------------------------------------------------------- +// private methods + +bool +SVGTransformListParser::Parse() +{ + mTransforms.Clear(); + return ParseTransforms(); +} + +bool +SVGTransformListParser::ParseTransforms() +{ + if (!SkipWsp()) { + return true; + } + + if (!ParseTransform()) { + return false; + } + + while (SkipWsp()) { + // The SVG BNF allows multiple comma-wsp between transforms + while (*mIter == ',') { + ++mIter; + if (!SkipWsp()) { + return false; + } + } + + if (!ParseTransform()) { + return false; + } + } + return true; +} + +bool +SVGTransformListParser::ParseTransform() +{ + RangedPtr<const char16_t> start(mIter); + while (IsAlpha(*mIter)) { + ++mIter; + if (mIter == mEnd) { + return false; + } + } + + if (start == mIter) { + // Didn't read anything + return false; + } + + const nsAString& transform = Substring(start.get(), mIter.get()); + nsIAtom* keyAtom = NS_GetStaticAtom(transform); + + if (!keyAtom || !SkipWsp()) { + return false; + } + + if (keyAtom == nsGkAtoms::translate) { + return ParseTranslate(); + } + if (keyAtom == nsGkAtoms::scale) { + return ParseScale(); + } + if (keyAtom == nsGkAtoms::rotate) { + return ParseRotate(); + } + if (keyAtom == nsGkAtoms::skewX) { + return ParseSkewX(); + } + if (keyAtom == nsGkAtoms::skewY) { + return ParseSkewY(); + } + if (keyAtom == nsGkAtoms::matrix) { + return ParseMatrix(); + } + return false; +} + +bool +SVGTransformListParser::ParseArguments(float* aResult, + uint32_t aMaxCount, + uint32_t* aParsedCount) +{ + if (*mIter != '(') { + return false; + } + ++mIter; + + if (!SkipWsp()) { + return false; + } + + if (!SVGContentUtils::ParseNumber(mIter, mEnd, aResult[0])) { + return false; + } + *aParsedCount = 1; + + while (SkipWsp()) { + if (*mIter == ')') { + ++mIter; + return true; + } + if (*aParsedCount == aMaxCount) { + return false; + } + SkipCommaWsp(); + if (!SVGContentUtils::ParseNumber(mIter, mEnd, aResult[(*aParsedCount)++])) { + return false; + } + } + return false; +} + +bool +SVGTransformListParser::ParseTranslate() +{ + float t[2]; + uint32_t count; + + if (!ParseArguments(t, ArrayLength(t), &count)) { + return false; + } + + switch (count) { + case 1: + t[1] = 0.f; + MOZ_FALLTHROUGH; + case 2: + { + nsSVGTransform* transform = mTransforms.AppendElement(fallible); + if (!transform) { + return false; + } + transform->SetTranslate(t[0], t[1]); + return true; + } + } + + return false; +} + +bool +SVGTransformListParser::ParseScale() +{ + float s[2]; + uint32_t count; + + if (!ParseArguments(s, ArrayLength(s), &count)) { + return false; + } + + switch (count) { + case 1: + s[1] = s[0]; + MOZ_FALLTHROUGH; + case 2: + { + nsSVGTransform* transform = mTransforms.AppendElement(fallible); + if (!transform) { + return false; + } + transform->SetScale(s[0], s[1]); + return true; + } + } + + return false; +} + + +bool +SVGTransformListParser::ParseRotate() +{ + float r[3]; + uint32_t count; + + if (!ParseArguments(r, ArrayLength(r), &count)) { + return false; + } + + switch (count) { + case 1: + r[1] = r[2] = 0.f; + MOZ_FALLTHROUGH; + case 3: + { + nsSVGTransform* transform = mTransforms.AppendElement(fallible); + if (!transform) { + return false; + } + transform->SetRotate(r[0], r[1], r[2]); + return true; + } + } + + return false; +} + +bool +SVGTransformListParser::ParseSkewX() +{ + float skew; + uint32_t count; + + if (!ParseArguments(&skew, 1, &count) || count != 1) { + return false; + } + + nsSVGTransform* transform = mTransforms.AppendElement(fallible); + if (!transform) { + return false; + } + transform->SetSkewX(skew); + + return true; +} + +bool +SVGTransformListParser::ParseSkewY() +{ + float skew; + uint32_t count; + + if (!ParseArguments(&skew, 1, &count) || count != 1) { + return false; + } + + nsSVGTransform* transform = mTransforms.AppendElement(fallible); + if (!transform) { + return false; + } + transform->SetSkewY(skew); + + return true; +} + +bool +SVGTransformListParser::ParseMatrix() +{ + float m[6]; + uint32_t count; + + if (!ParseArguments(m, ArrayLength(m), &count) || count != 6) { + return false; + } + + nsSVGTransform* transform = mTransforms.AppendElement(fallible); + if (!transform) { + return false; + } + transform->SetMatrix(gfxMatrix(m[0], m[1], m[2], m[3], m[4], m[5])); + + return true; +} diff --git a/dom/svg/SVGTransformListParser.h b/dom/svg/SVGTransformListParser.h new file mode 100644 index 0000000000..7842979638 --- /dev/null +++ b/dom/svg/SVGTransformListParser.h @@ -0,0 +1,58 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef MOZILLA_SVGTRANSFORMLISTPARSER_H__ +#define MOZILLA_SVGTRANSFORMLISTPARSER_H__ + +#include "mozilla/Attributes.h" +#include "nsSVGDataParser.h" +#include "nsTArray.h" + +//////////////////////////////////////////////////////////////////////// +// SVGTransformListParser: A simple recursive descent parser that builds +// transform lists from transform attributes. The grammar for path data +// can be found in SVG 1.1, chapter 7. +// http://www.w3.org/TR/SVG11/coords.html#TransformAttribute + +namespace mozilla { + +class nsSVGTransform; + +class SVGTransformListParser : public nsSVGDataParser +{ +public: + explicit SVGTransformListParser(const nsAString& aValue) + : nsSVGDataParser(aValue) {} + + bool Parse(); + + const nsTArray<nsSVGTransform>& GetTransformList() const { + return mTransforms; + } + +private: + // helpers + bool ParseArguments(float *aResult, + uint32_t aMaxCount, + uint32_t *aParsedCount); + + bool ParseTransforms(); + + bool ParseTransform(); + + bool ParseTranslate(); + bool ParseScale(); + bool ParseRotate(); + bool ParseSkewX(); + bool ParseSkewY(); + bool ParseMatrix(); + + FallibleTArray<nsSVGTransform> mTransforms; +}; + +} // namespace mozilla + +#endif // MOZILLA_SVGTRANSFORMLISTPARSER_H__ diff --git a/dom/svg/SVGTransformListSMILType.cpp b/dom/svg/SVGTransformListSMILType.cpp new file mode 100644 index 0000000000..40777b026b --- /dev/null +++ b/dom/svg/SVGTransformListSMILType.cpp @@ -0,0 +1,369 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "SVGTransformListSMILType.h" +#include "SVGTransformList.h" +#include "nsSVGTransform.h" +#include "nsSMILValue.h" +#include "nsCRT.h" +#include <math.h> + +using namespace mozilla; + +typedef FallibleTArray<SVGTransformSMILData> TransformArray; + +//---------------------------------------------------------------------- +// nsISMILType implementation + +void +SVGTransformListSMILType::Init(nsSMILValue &aValue) const +{ + NS_PRECONDITION(aValue.IsNull(), "Unexpected value type"); + + TransformArray* transforms = new TransformArray(1); + aValue.mU.mPtr = transforms; + aValue.mType = this; +} + +void +SVGTransformListSMILType::Destroy(nsSMILValue& aValue) const +{ + NS_PRECONDITION(aValue.mType == this, "Unexpected SMIL value type"); + TransformArray* params = static_cast<TransformArray*>(aValue.mU.mPtr); + delete params; + aValue.mU.mPtr = nullptr; + aValue.mType = nsSMILNullType::Singleton(); +} + +nsresult +SVGTransformListSMILType::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 TransformArray* srcTransforms = + static_cast<const TransformArray*>(aSrc.mU.mPtr); + TransformArray* dstTransforms = static_cast<TransformArray*>(aDest.mU.mPtr); + if (!dstTransforms->Assign(*srcTransforms, fallible)) { + return NS_ERROR_OUT_OF_MEMORY; + } + + return NS_OK; +} + +bool +SVGTransformListSMILType::IsEqual(const nsSMILValue& aLeft, + const nsSMILValue& aRight) const +{ + NS_PRECONDITION(aLeft.mType == aRight.mType, "Incompatible SMIL types"); + NS_PRECONDITION(aLeft.mType == this, "Unexpected SMIL type"); + + const TransformArray& leftArr + (*static_cast<const TransformArray*>(aLeft.mU.mPtr)); + const TransformArray& rightArr + (*static_cast<const TransformArray*>(aRight.mU.mPtr)); + + // If array-lengths don't match, we're trivially non-equal. + if (leftArr.Length() != rightArr.Length()) { + return false; + } + + // Array-lengths match -- check each array-entry for equality. + uint32_t length = leftArr.Length(); // == rightArr->Length(), if we get here + for (uint32_t i = 0; i < length; ++i) { + if (leftArr[i] != rightArr[i]) { + return false; + } + } + + // Found no differences. + return true; +} + +nsresult +SVGTransformListSMILType::Add(nsSMILValue& aDest, + const nsSMILValue& aValueToAdd, + uint32_t aCount) const +{ + NS_PRECONDITION(aDest.mType == this, "Unexpected SMIL type"); + NS_PRECONDITION(aDest.mType == aValueToAdd.mType, "Incompatible SMIL types"); + + TransformArray& dstTransforms(*static_cast<TransformArray*>(aDest.mU.mPtr)); + const TransformArray& srcTransforms + (*static_cast<const TransformArray*>(aValueToAdd.mU.mPtr)); + + // We're doing a simple add here (as opposed to a sandwich add below). + // We only do this when we're accumulating a repeat result or calculating + // a by-animation value. + // + // In either case we should have 1 transform in the source array. + NS_ASSERTION(srcTransforms.Length() == 1, + "Invalid source transform list to add"); + + // And we should have 0 or 1 transforms in the dest array. + // (We can have 0 transforms in the case of by-animation when we are + // calculating the by-value as "0 + by". Zero being represented by an + // nsSMILValue with an empty transform array.) + NS_ASSERTION(dstTransforms.Length() < 2, + "Invalid dest transform list to add to"); + + // Get the individual transforms to add + const SVGTransformSMILData& srcTransform = srcTransforms[0]; + if (dstTransforms.IsEmpty()) { + SVGTransformSMILData* result = dstTransforms.AppendElement( + SVGTransformSMILData(srcTransform.mTransformType), fallible); + NS_ENSURE_TRUE(result,NS_ERROR_OUT_OF_MEMORY); + } + SVGTransformSMILData& dstTransform = dstTransforms[0]; + + // The types must be the same + NS_ASSERTION(srcTransform.mTransformType == dstTransform.mTransformType, + "Trying to perform simple add of different transform types"); + + // And it should be impossible that one of them is of matrix type + NS_ASSERTION( + srcTransform.mTransformType != SVG_TRANSFORM_MATRIX, + "Trying to perform simple add with matrix transform"); + + // Add the parameters + for (int i = 0; i <= 2; ++i) { + dstTransform.mParams[i] += srcTransform.mParams[i] * aCount; + } + + return NS_OK; +} + +nsresult +SVGTransformListSMILType::SandwichAdd(nsSMILValue& aDest, + const nsSMILValue& aValueToAdd) const +{ + NS_PRECONDITION(aDest.mType == this, "Unexpected SMIL type"); + NS_PRECONDITION(aDest.mType == aValueToAdd.mType, "Incompatible SMIL types"); + + // For <animateTransform> a sandwich add means a matrix post-multiplication + // which just means to put the additional transform on the end of the array + + TransformArray& dstTransforms(*static_cast<TransformArray*>(aDest.mU.mPtr)); + const TransformArray& srcTransforms + (*static_cast<const TransformArray*>(aValueToAdd.mU.mPtr)); + + // We should have 0 or 1 transforms in the src list. + NS_ASSERTION(srcTransforms.Length() < 2, + "Trying to do sandwich add of more than one value"); + + // The empty src transform list case only occurs in some limited circumstances + // where we create an empty 'from' value to interpolate from (e.g. + // by-animation) but then skip the interpolation step for some reason (e.g. + // because we have an indefinite duration which means we'll never get past the + // first value) and instead attempt to add that empty value to the underlying + // value. + // In any case, the expected result is that nothing is added. + if (srcTransforms.IsEmpty()) + return NS_OK; + + // Stick the src on the end of the array + const SVGTransformSMILData& srcTransform = srcTransforms[0]; + SVGTransformSMILData* result = + dstTransforms.AppendElement(srcTransform, fallible); + NS_ENSURE_TRUE(result,NS_ERROR_OUT_OF_MEMORY); + + return NS_OK; +} + +nsresult +SVGTransformListSMILType::ComputeDistance(const nsSMILValue& aFrom, + const nsSMILValue& aTo, + double& aDistance) const +{ + NS_PRECONDITION(aFrom.mType == aTo.mType, + "Can't compute difference between different SMIL types"); + NS_PRECONDITION(aFrom.mType == this, "Unexpected SMIL type"); + + const TransformArray* fromTransforms = + static_cast<const TransformArray*>(aFrom.mU.mPtr); + const TransformArray* toTransforms = + static_cast<const TransformArray*>(aTo.mU.mPtr); + + // ComputeDistance is only used for calculating distances between single + // values in a values array which necessarily have the same type + // + // So we should only have one transform in each array and they should be of + // the same type + NS_ASSERTION(fromTransforms->Length() == 1, + "Wrong number of elements in from value"); + NS_ASSERTION(toTransforms->Length() == 1, + "Wrong number of elements in to value"); + + const SVGTransformSMILData& fromTransform = (*fromTransforms)[0]; + const SVGTransformSMILData& toTransform = (*toTransforms)[0]; + NS_ASSERTION(fromTransform.mTransformType == toTransform.mTransformType, + "Incompatible transform types to calculate distance between"); + + switch (fromTransform.mTransformType) + { + // We adopt the SVGT1.2 notions of distance here + // See: http://www.w3.org/TR/SVGTiny12/animate.html#complexDistances + // (As discussed in bug #469040) + case SVG_TRANSFORM_TRANSLATE: + case SVG_TRANSFORM_SCALE: + { + const float& a_tx = fromTransform.mParams[0]; + const float& a_ty = fromTransform.mParams[1]; + const float& b_tx = toTransform.mParams[0]; + const float& b_ty = toTransform.mParams[1]; + aDistance = sqrt(pow(a_tx - b_tx, 2) + (pow(a_ty - b_ty, 2))); + } + break; + + case SVG_TRANSFORM_ROTATE: + case SVG_TRANSFORM_SKEWX: + case SVG_TRANSFORM_SKEWY: + { + const float& a = fromTransform.mParams[0]; + const float& b = toTransform.mParams[0]; + aDistance = fabs(a-b); + } + break; + + default: + NS_ERROR("Got bad transform types for calculating distances"); + aDistance = 1.0; + return NS_ERROR_FAILURE; + } + + return NS_OK; +} + +nsresult +SVGTransformListSMILType::Interpolate(const nsSMILValue& aStartVal, + const nsSMILValue& aEndVal, + double aUnitDistance, + nsSMILValue& aResult) const +{ + NS_PRECONDITION(aStartVal.mType == aEndVal.mType, + "Can't interpolate between different SMIL types"); + NS_PRECONDITION(aStartVal.mType == this, + "Unexpected type for interpolation"); + NS_PRECONDITION(aResult.mType == this, "Unexpected result type"); + + const TransformArray& startTransforms = + (*static_cast<const TransformArray*>(aStartVal.mU.mPtr)); + const TransformArray& endTransforms + (*static_cast<const TransformArray*>(aEndVal.mU.mPtr)); + + // We may have 0..n transforms in the start transform array (the base + // value) but we should only have 1 transform in the end transform array + NS_ASSERTION(endTransforms.Length() == 1, + "Invalid end-point for interpolating between transform values"); + + // The end point should never be a matrix transform + const SVGTransformSMILData& endTransform = endTransforms[0]; + NS_ASSERTION( + endTransform.mTransformType != SVG_TRANSFORM_MATRIX, + "End point for interpolation should not be a matrix transform"); + + // If we have 0 or more than 1 transform in the start transform array then we + // just interpolate from 0, 0, 0 + // Likewise, even if there's only 1 transform in the start transform array + // then if the type of the start transform doesn't match the end then we + // can't interpolate and should just use 0, 0, 0 + static float identityParams[3] = { 0.f }; + const float* startParams = nullptr; + if (startTransforms.Length() == 1) { + const SVGTransformSMILData& startTransform = startTransforms[0]; + if (startTransform.mTransformType == endTransform.mTransformType) { + startParams = startTransform.mParams; + } + } + if (!startParams) { + startParams = identityParams; + } + + const float* endParams = endTransform.mParams; + + // Interpolate between the params + float newParams[3]; + for (int i = 0; i <= 2; ++i) { + const float& a = startParams[i]; + const float& b = endParams[i]; + newParams[i] = static_cast<float>(a + (b - a) * aUnitDistance); + } + + // Make the result + SVGTransformSMILData resultTransform(endTransform.mTransformType, newParams); + + // Clear the way for it in the result array + TransformArray& dstTransforms = + (*static_cast<TransformArray*>(aResult.mU.mPtr)); + dstTransforms.Clear(); + + // Assign the result + SVGTransformSMILData* transform = + dstTransforms.AppendElement(resultTransform, fallible); + NS_ENSURE_TRUE(transform,NS_ERROR_OUT_OF_MEMORY); + + return NS_OK; +} + +//---------------------------------------------------------------------- +// Transform array accessors + +// static +nsresult +SVGTransformListSMILType::AppendTransform( + const SVGTransformSMILData& aTransform, + nsSMILValue& aValue) +{ + NS_PRECONDITION(aValue.mType == Singleton(), "Unexpected SMIL value type"); + + TransformArray& transforms = *static_cast<TransformArray*>(aValue.mU.mPtr); + return transforms.AppendElement(aTransform, fallible) ? + NS_OK : NS_ERROR_OUT_OF_MEMORY; +} + +// static +bool +SVGTransformListSMILType::AppendTransforms(const SVGTransformList& aList, + nsSMILValue& aValue) +{ + NS_PRECONDITION(aValue.mType == Singleton(), "Unexpected SMIL value type"); + + TransformArray& transforms = *static_cast<TransformArray*>(aValue.mU.mPtr); + + if (!transforms.SetCapacity(transforms.Length() + aList.Length(), fallible)) + return false; + + for (uint32_t i = 0; i < aList.Length(); ++i) { + // No need to check the return value below since we have already allocated + // the necessary space + MOZ_ALWAYS_TRUE(transforms.AppendElement(SVGTransformSMILData(aList[i]), + fallible)); + } + return true; +} + +// static +bool +SVGTransformListSMILType::GetTransforms(const nsSMILValue& aValue, + FallibleTArray<nsSVGTransform>& aTransforms) +{ + NS_PRECONDITION(aValue.mType == Singleton(), "Unexpected SMIL value type"); + + const TransformArray& smilTransforms = + *static_cast<const TransformArray*>(aValue.mU.mPtr); + + aTransforms.Clear(); + if (!aTransforms.SetCapacity(smilTransforms.Length(), fallible)) + return false; + + for (uint32_t i = 0; i < smilTransforms.Length(); ++i) { + // No need to check the return value below since we have already allocated + // the necessary space + aTransforms.AppendElement(smilTransforms[i].ToSVGTransform(), fallible); + } + return true; +} diff --git a/dom/svg/SVGTransformListSMILType.h b/dom/svg/SVGTransformListSMILType.h new file mode 100644 index 0000000000..526f9ba50c --- /dev/null +++ b/dom/svg/SVGTransformListSMILType.h @@ -0,0 +1,131 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef SVGTRANSFORMLISTSMILTYPE_H_ +#define SVGTRANSFORMLISTSMILTYPE_H_ + +#include "mozilla/Attributes.h" +#include "nsISMILType.h" +#include "nsTArray.h" + +class nsSMILValue; + +namespace mozilla { + +class nsSVGTransform; +class SVGTransformList; +class SVGTransformSMILData; + +//////////////////////////////////////////////////////////////////////// +// SVGTransformListSMILType +// +// Operations for animating an nsSVGTransformList. +// +// This class is confused somewhat by the fact that: +// (i) An <animateTransform> element animates an SVGTransformList +// (ii) BUT <animateTransform> only allows the user to specify animation values +// for an nsSVGTransform +// +// This may be rectified in a future edition of SVG but for now it means that +// the underlying value of an animation may be something of the form: +// +// rotate(90) scale(2) skewX(50) +// +// BUT the animation values can only ever be SINGLE transform operations such as +// +// rotate(90) +// +// (actually the syntax here is: +// <animateTransform type="rotate" from="0" to="90"... +// OR maybe +// <animateTransform type="rotate" values="0; 90; 30; 50"... +// OR even (with a rotation centre) +// <animateTransform type="rotate" values="0 50 20; 30 50 20; 70 0 0"...) +// +// This has many implications for the number of elements we expect in the +// transform array supplied for each operation. +// +// For example, Add() only ever operates on the values specified on an +// <animateTransform> element and so these values can only ever contain 0 or +// 1 TRANSFORM elements as the syntax doesn't allow more. (A "value" here is +// a single element in the values array such as "0 50 20" above.) +// +// Likewise ComputeDistance() only ever operates within the values specified on +// an <animateTransform> element so similar conditions hold. +// +// However, SandwichAdd() combines with a base value which may contain 0..n +// transforms either because the base value of the attribute specifies a series +// of transforms, e.g. +// +// <circle transform="translate(30) rotate(50)"... > +// <animateTransform.../> +// </circle> +// +// or because several animations target the same attribute and are additive and +// so are simply appended on to the transformation array, e.g. +// +// <circle transform="translate(30)"... > +// <animateTransform type="rotate" additive="sum".../> +// <animateTransform type="scale" additive="sum".../> +// <animateTransform type="skewX" additive="sum".../> +// </circle> +// +// Similar conditions hold for Interpolate() which in cases such as to-animation +// may have use a start-value the base value of the target attribute (which as +// we have seen above can contain 0..n elements) whilst the end-value comes from +// the <animateTransform> and so can only hold 1 transform. +// +class SVGTransformListSMILType : public nsISMILType +{ +public: + // Singleton for nsSMILValue objects to hold onto. + static SVGTransformListSMILType* + Singleton() + { + static SVGTransformListSMILType sSingleton; + return &sSingleton; + } + +protected: + // nsISMILType Methods + // ------------------- + virtual void Init(nsSMILValue& aValue) const override; + virtual void Destroy(nsSMILValue& aValue) const override; + virtual nsresult Assign(nsSMILValue& aDest, const nsSMILValue& aSrc) const override; + virtual bool IsEqual(const nsSMILValue& aLeft, + const nsSMILValue& aRight) const override; + virtual nsresult Add(nsSMILValue& aDest, + const nsSMILValue& aValueToAdd, + uint32_t aCount) const override; + virtual nsresult SandwichAdd(nsSMILValue& aDest, + const nsSMILValue& aValueToAdd) const override; + virtual nsresult ComputeDistance(const nsSMILValue& aFrom, + const nsSMILValue& aTo, + double& aDistance) const override; + virtual nsresult Interpolate(const nsSMILValue& aStartVal, + const nsSMILValue& aEndVal, + double aUnitDistance, + nsSMILValue& aResult) const override; + +public: + // Transform array accessors + // ------------------------- + static nsresult AppendTransform(const SVGTransformSMILData& aTransform, + nsSMILValue& aValue); + static bool AppendTransforms(const SVGTransformList& aList, + nsSMILValue& aValue); + static bool GetTransforms(const nsSMILValue& aValue, + FallibleTArray<nsSVGTransform>& aTransforms); + + +private: + // Private constructor: prevent instances beyond my singleton. + constexpr SVGTransformListSMILType() {} +}; + +} // end namespace mozilla + +#endif // SVGLISTTRANSFORMSMILTYPE_H_ diff --git a/dom/svg/SVGTransformableElement.cpp b/dom/svg/SVGTransformableElement.cpp new file mode 100644 index 0000000000..a9028fc496 --- /dev/null +++ b/dom/svg/SVGTransformableElement.cpp @@ -0,0 +1,260 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "gfx2DGlue.h" +#include "mozilla/dom/SVGAnimatedTransformList.h" +#include "mozilla/dom/SVGGraphicsElementBinding.h" +#include "mozilla/dom/SVGTransformableElement.h" +#include "mozilla/dom/SVGMatrix.h" +#include "mozilla/dom/SVGSVGElement.h" +#include "nsContentUtils.h" +#include "nsIDOMMutationEvent.h" +#include "nsIFrame.h" +#include "nsISVGChildFrame.h" +#include "mozilla/dom/SVGRect.h" +#include "nsSVGUtils.h" +#include "SVGContentUtils.h" + +using namespace mozilla::gfx; + +namespace mozilla { +namespace dom { + +already_AddRefed<SVGAnimatedTransformList> +SVGTransformableElement::Transform() +{ + // We're creating a DOM wrapper, so we must tell GetAnimatedTransformList + // to allocate the SVGAnimatedTransformList if it hasn't already done so: + return SVGAnimatedTransformList::GetDOMWrapper( + GetAnimatedTransformList(DO_ALLOCATE), this); + +} + +//---------------------------------------------------------------------- +// nsIContent methods + +NS_IMETHODIMP_(bool) +SVGTransformableElement::IsAttributeMapped(const nsIAtom* name) const +{ + static const MappedAttributeEntry* const map[] = { + sColorMap, + sFillStrokeMap, + sGraphicsMap + }; + + return FindAttributeDependence(name, map) || + nsSVGElement::IsAttributeMapped(name); +} + +nsChangeHint +SVGTransformableElement::GetAttributeChangeHint(const nsIAtom* aAttribute, + int32_t aModType) const +{ + nsChangeHint retval = + nsSVGElement::GetAttributeChangeHint(aAttribute, aModType); + if (aAttribute == nsGkAtoms::transform || + aAttribute == nsGkAtoms::mozAnimateMotionDummyAttr) { + nsIFrame* frame = + const_cast<SVGTransformableElement*>(this)->GetPrimaryFrame(); + retval |= nsChangeHint_InvalidateRenderingObservers; + if (!frame || (frame->GetStateBits() & NS_FRAME_IS_NONDISPLAY)) { + return retval; + } + + bool isAdditionOrRemoval = false; + if (aModType == nsIDOMMutationEvent::ADDITION || + aModType == nsIDOMMutationEvent::REMOVAL) { + isAdditionOrRemoval = true; + } else { + MOZ_ASSERT(aModType == nsIDOMMutationEvent::MODIFICATION, + "Unknown modification type."); + if (!mTransforms || + !mTransforms->HasTransform() || + !mTransforms->HadTransformBeforeLastBaseValChange()) { + // New or old value is empty; this is effectively addition or removal. + isAdditionOrRemoval = true; + } + } + + if (isAdditionOrRemoval) { + // Reconstruct the frame tree to handle stacking context changes: + retval |= nsChangeHint_ReconstructFrame; + } else { + // We just assume the old and new transforms are different. + retval |= nsChangeHint_UpdatePostTransformOverflow | + nsChangeHint_UpdateTransformLayer; + } + } + return retval; +} + +bool +SVGTransformableElement::IsEventAttributeName(nsIAtom* aName) +{ + return nsContentUtils::IsEventAttributeName(aName, EventNameType_SVGGraphic); +} + +//---------------------------------------------------------------------- +// nsSVGElement overrides + +gfxMatrix +SVGTransformableElement::PrependLocalTransformsTo( + const gfxMatrix &aMatrix, + SVGTransformTypes aWhich) const +{ + return SVGContentUtils::PrependLocalTransformsTo( + aMatrix, aWhich, mAnimateMotionTransform, mTransforms); +} + +const gfx::Matrix* +SVGTransformableElement::GetAnimateMotionTransform() const +{ + return mAnimateMotionTransform.get(); +} + +void +SVGTransformableElement::SetAnimateMotionTransform(const gfx::Matrix* aMatrix) +{ + if ((!aMatrix && !mAnimateMotionTransform) || + (aMatrix && mAnimateMotionTransform && *aMatrix == *mAnimateMotionTransform)) { + return; + } + bool transformSet = mTransforms && mTransforms->IsExplicitlySet(); + bool prevSet = mAnimateMotionTransform || transformSet; + mAnimateMotionTransform = aMatrix ? new gfx::Matrix(*aMatrix) : nullptr; + bool nowSet = mAnimateMotionTransform || transformSet; + int32_t modType; + if (prevSet && !nowSet) { + modType = nsIDOMMutationEvent::REMOVAL; + } else if(!prevSet && nowSet) { + modType = nsIDOMMutationEvent::ADDITION; + } else { + modType = nsIDOMMutationEvent::MODIFICATION; + } + DidAnimateTransformList(modType); + nsIFrame* frame = GetPrimaryFrame(); + if (frame) { + // If the result of this transform and any other transforms on this frame + // is the identity matrix, then DoApplyRenderingChangeToTree won't handle + // our nsChangeHint_UpdateTransformLayer hint since aFrame->IsTransformed() + // will return false. That's fine, but we still need to schedule a repaint, + // and that won't otherwise happen. Since it's cheap to call SchedulePaint, + // we don't bother to check IsTransformed(). + frame->SchedulePaint(); + } +} + +nsSVGAnimatedTransformList* +SVGTransformableElement::GetAnimatedTransformList(uint32_t aFlags) +{ + if (!mTransforms && (aFlags & DO_ALLOCATE)) { + mTransforms = new nsSVGAnimatedTransformList(); + } + return mTransforms; +} + +nsSVGElement* +SVGTransformableElement::GetNearestViewportElement() +{ + return SVGContentUtils::GetNearestViewportElement(this); +} + +nsSVGElement* +SVGTransformableElement::GetFarthestViewportElement() +{ + return SVGContentUtils::GetOuterSVGElement(this); +} + +already_AddRefed<SVGIRect> +SVGTransformableElement::GetBBox(const SVGBoundingBoxOptions& aOptions, + ErrorResult& rv) +{ + nsIFrame* frame = GetPrimaryFrame(Flush_Layout); + + if (!frame || (frame->GetStateBits() & NS_FRAME_IS_NONDISPLAY)) { + rv.Throw(NS_ERROR_FAILURE); + return nullptr; + } + nsISVGChildFrame* svgframe = do_QueryFrame(frame); + if (!svgframe) { + rv.Throw(NS_ERROR_NOT_IMPLEMENTED); // XXX: outer svg + return nullptr; + } + + if (!NS_SVGNewGetBBoxEnabled()) { + return NS_NewSVGRect(this, ToRect(nsSVGUtils::GetBBox(frame))); + } else { + uint32_t flags = 0; + if (aOptions.mFill) { + flags |= nsSVGUtils::eBBoxIncludeFill; + } + if (aOptions.mStroke) { + flags |= nsSVGUtils::eBBoxIncludeStroke; + } + if (aOptions.mMarkers) { + flags |= nsSVGUtils::eBBoxIncludeMarkers; + } + if (aOptions.mClipped) { + flags |= nsSVGUtils::eBBoxIncludeClipped; + } + if (flags == 0) { + return NS_NewSVGRect(this,0,0,0,0); + } + if (flags == nsSVGUtils::eBBoxIncludeMarkers || + flags == nsSVGUtils::eBBoxIncludeClipped) { + flags |= nsSVGUtils::eBBoxIncludeFill; + } + return NS_NewSVGRect(this, ToRect(nsSVGUtils::GetBBox(frame, flags))); + } +} + +already_AddRefed<SVGMatrix> +SVGTransformableElement::GetCTM() +{ + nsIDocument* currentDoc = GetComposedDoc(); + if (currentDoc) { + // Flush all pending notifications so that our frames are up to date + currentDoc->FlushPendingNotifications(Flush_Layout); + } + gfx::Matrix m = SVGContentUtils::GetCTM(this, false); + RefPtr<SVGMatrix> mat = m.IsSingular() ? nullptr : new SVGMatrix(ThebesMatrix(m)); + return mat.forget(); +} + +already_AddRefed<SVGMatrix> +SVGTransformableElement::GetScreenCTM() +{ + nsIDocument* currentDoc = GetComposedDoc(); + if (currentDoc) { + // Flush all pending notifications so that our frames are up to date + currentDoc->FlushPendingNotifications(Flush_Layout); + } + gfx::Matrix m = SVGContentUtils::GetCTM(this, true); + RefPtr<SVGMatrix> mat = m.IsSingular() ? nullptr : new SVGMatrix(ThebesMatrix(m)); + return mat.forget(); +} + +already_AddRefed<SVGMatrix> +SVGTransformableElement::GetTransformToElement(SVGGraphicsElement& aElement, + ErrorResult& rv) +{ + // the easiest way to do this (if likely to increase rounding error): + RefPtr<SVGMatrix> ourScreenCTM = GetScreenCTM(); + RefPtr<SVGMatrix> targetScreenCTM = aElement.GetScreenCTM(); + if (!ourScreenCTM || !targetScreenCTM) { + rv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR); + return nullptr; + } + RefPtr<SVGMatrix> tmp = targetScreenCTM->Inverse(rv); + if (rv.Failed()) return nullptr; + + RefPtr<SVGMatrix> mat = tmp->Multiply(*ourScreenCTM); + return mat.forget(); +} + +} // namespace dom +} // namespace mozilla + diff --git a/dom/svg/SVGTransformableElement.h b/dom/svg/SVGTransformableElement.h new file mode 100644 index 0000000000..e356373f13 --- /dev/null +++ b/dom/svg/SVGTransformableElement.h @@ -0,0 +1,81 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef SVGTransformableElement_h +#define SVGTransformableElement_h + +#include "mozilla/Attributes.h" +#include "nsAutoPtr.h" +#include "nsSVGAnimatedTransformList.h" +#include "nsSVGElement.h" +#include "gfxMatrix.h" +#include "mozilla/gfx/Matrix.h" + +namespace mozilla { +namespace dom { + +class SVGAnimatedTransformList; +class SVGGraphicsElement; +class SVGMatrix; +class SVGIRect; +struct SVGBoundingBoxOptions; + +class SVGTransformableElement : public nsSVGElement +{ +public: + explicit SVGTransformableElement(already_AddRefed<mozilla::dom::NodeInfo>& aNodeInfo) + : nsSVGElement(aNodeInfo) {} + virtual ~SVGTransformableElement() {} + + virtual nsresult Clone(mozilla::dom::NodeInfo *aNodeInfo, nsINode **aResult) const override = 0; + + // WebIDL + already_AddRefed<SVGAnimatedTransformList> Transform(); + nsSVGElement* GetNearestViewportElement(); + nsSVGElement* GetFarthestViewportElement(); + already_AddRefed<SVGIRect> GetBBox(const SVGBoundingBoxOptions& aOptions, + ErrorResult& rv); + already_AddRefed<SVGMatrix> GetCTM(); + already_AddRefed<SVGMatrix> GetScreenCTM(); + already_AddRefed<SVGMatrix> GetTransformToElement(SVGGraphicsElement& aElement, + ErrorResult& rv); + + // nsIContent interface + NS_IMETHOD_(bool) IsAttributeMapped(const nsIAtom* aAttribute) const override; + + nsChangeHint GetAttributeChangeHint(const nsIAtom* aAttribute, + int32_t aModType) const override; + + + // nsSVGElement overrides + virtual bool IsEventAttributeName(nsIAtom* aName) override; + + + virtual gfxMatrix PrependLocalTransformsTo( + const gfxMatrix &aMatrix, + SVGTransformTypes aWhich = eAllTransforms) const override; + virtual const gfx::Matrix* GetAnimateMotionTransform() const override; + virtual void SetAnimateMotionTransform(const gfx::Matrix* aMatrix) override; + + virtual nsSVGAnimatedTransformList* + GetAnimatedTransformList(uint32_t aFlags = 0) override; + virtual nsIAtom* GetTransformListAttrName() const override { + return nsGkAtoms::transform; + } + + virtual bool IsTransformable() override { return true; } + +protected: + nsAutoPtr<nsSVGAnimatedTransformList> mTransforms; + + // XXX maybe move this to property table, to save space on un-animated elems? + nsAutoPtr<gfx::Matrix> mAnimateMotionTransform; +}; + +} // namespace dom +} // namespace mozilla + +#endif // SVGTransformableElement_h diff --git a/dom/svg/SVGUseElement.cpp b/dom/svg/SVGUseElement.cpp new file mode 100644 index 0000000000..4911e2cac8 --- /dev/null +++ b/dom/svg/SVGUseElement.cpp @@ -0,0 +1,519 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "mozilla/ArrayUtils.h" + +#include "mozilla/dom/SVGUseElement.h" +#include "mozilla/dom/SVGUseElementBinding.h" +#include "nsGkAtoms.h" +#include "mozilla/dom/SVGSVGElement.h" +#include "nsIDocument.h" +#include "nsIPresShell.h" +#include "mozilla/dom/Element.h" +#include "nsContentUtils.h" +#include "nsIURI.h" + +NS_IMPL_NS_NEW_NAMESPACED_SVG_ELEMENT(Use) + +namespace mozilla { +namespace dom { + +JSObject* +SVGUseElement::WrapNode(JSContext *aCx, JS::Handle<JSObject*> aGivenProto) +{ + return SVGUseElementBinding::Wrap(aCx, this, aGivenProto); +} + +//////////////////////////////////////////////////////////////////////// +// implementation + +nsSVGElement::LengthInfo SVGUseElement::sLengthInfo[4] = +{ + { &nsGkAtoms::x, 0, nsIDOMSVGLength::SVG_LENGTHTYPE_NUMBER, SVGContentUtils::X }, + { &nsGkAtoms::y, 0, nsIDOMSVGLength::SVG_LENGTHTYPE_NUMBER, SVGContentUtils::Y }, + { &nsGkAtoms::width, 0, nsIDOMSVGLength::SVG_LENGTHTYPE_NUMBER, SVGContentUtils::X }, + { &nsGkAtoms::height, 0, nsIDOMSVGLength::SVG_LENGTHTYPE_NUMBER, SVGContentUtils::Y }, +}; + +nsSVGElement::StringInfo SVGUseElement::sStringInfo[2] = +{ + { &nsGkAtoms::href, kNameSpaceID_None, true }, + { &nsGkAtoms::href, kNameSpaceID_XLink, true } +}; + +//---------------------------------------------------------------------- +// nsISupports methods + +NS_IMPL_CYCLE_COLLECTION_CLASS(SVGUseElement) + +NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(SVGUseElement, + SVGUseElementBase) + nsAutoScriptBlocker scriptBlocker; + NS_IMPL_CYCLE_COLLECTION_UNLINK(mOriginal) + tmp->DestroyAnonymousContent(); + tmp->UnlinkSource(); +NS_IMPL_CYCLE_COLLECTION_UNLINK_END +NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(SVGUseElement, + SVGUseElementBase) + NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mOriginal) + NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mClone) + tmp->mSource.Traverse(&cb); +NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END + +NS_IMPL_ADDREF_INHERITED(SVGUseElement,SVGUseElementBase) +NS_IMPL_RELEASE_INHERITED(SVGUseElement,SVGUseElementBase) + +NS_INTERFACE_TABLE_HEAD_CYCLE_COLLECTION_INHERITED(SVGUseElement) + NS_INTERFACE_TABLE_INHERITED(SVGUseElement, nsIMutationObserver) +NS_INTERFACE_TABLE_TAIL_INHERITING(SVGUseElementBase) + +//---------------------------------------------------------------------- +// Implementation + +SVGUseElement::SVGUseElement(already_AddRefed<mozilla::dom::NodeInfo>& aNodeInfo) + : SVGUseElementBase(aNodeInfo), mSource(this) +{ +} + +SVGUseElement::~SVGUseElement() +{ + UnlinkSource(); +} + +//---------------------------------------------------------------------- +// nsIDOMNode methods + +nsresult +SVGUseElement::Clone(mozilla::dom::NodeInfo *aNodeInfo, nsINode **aResult) const +{ + *aResult = nullptr; + already_AddRefed<mozilla::dom::NodeInfo> ni = RefPtr<mozilla::dom::NodeInfo>(aNodeInfo).forget(); + SVGUseElement *it = new SVGUseElement(ni); + + nsCOMPtr<nsINode> kungFuDeathGrip(it); + nsresult rv1 = it->Init(); + nsresult rv2 = const_cast<SVGUseElement*>(this)->CopyInnerTo(it); + + // SVGUseElement specific portion - record who we cloned from + it->mOriginal = const_cast<SVGUseElement*>(this); + + if (NS_SUCCEEDED(rv1) && NS_SUCCEEDED(rv2)) { + kungFuDeathGrip.swap(*aResult); + } + + return NS_FAILED(rv1) ? rv1 : rv2; +} + +already_AddRefed<SVGAnimatedString> +SVGUseElement::Href() +{ + return mStringAttributes[HREF].IsExplicitlySet() + ? mStringAttributes[HREF].ToDOMAnimatedString(this) + : mStringAttributes[XLINK_HREF].ToDOMAnimatedString(this); +} + +//---------------------------------------------------------------------- + +already_AddRefed<SVGAnimatedLength> +SVGUseElement::X() +{ + return mLengthAttributes[ATTR_X].ToDOMAnimatedLength(this); +} + +already_AddRefed<SVGAnimatedLength> +SVGUseElement::Y() +{ + return mLengthAttributes[ATTR_Y].ToDOMAnimatedLength(this); +} + +already_AddRefed<SVGAnimatedLength> +SVGUseElement::Width() +{ + return mLengthAttributes[ATTR_WIDTH].ToDOMAnimatedLength(this); +} + +already_AddRefed<SVGAnimatedLength> +SVGUseElement::Height() +{ + return mLengthAttributes[ATTR_HEIGHT].ToDOMAnimatedLength(this); +} + +//---------------------------------------------------------------------- +// nsIMutationObserver methods + +void +SVGUseElement::CharacterDataChanged(nsIDocument *aDocument, + nsIContent *aContent, + CharacterDataChangeInfo* aInfo) +{ + if (nsContentUtils::IsInSameAnonymousTree(this, aContent)) { + TriggerReclone(); + } +} + +void +SVGUseElement::AttributeChanged(nsIDocument* aDocument, + Element* aElement, + int32_t aNameSpaceID, + nsIAtom* aAttribute, + int32_t aModType, + const nsAttrValue* aOldValue) +{ + if (nsContentUtils::IsInSameAnonymousTree(this, aElement)) { + TriggerReclone(); + } +} + +void +SVGUseElement::ContentAppended(nsIDocument *aDocument, + nsIContent *aContainer, + nsIContent *aFirstNewContent, + int32_t aNewIndexInContainer) +{ + if (nsContentUtils::IsInSameAnonymousTree(this, aContainer)) { + TriggerReclone(); + } +} + +void +SVGUseElement::ContentInserted(nsIDocument *aDocument, + nsIContent *aContainer, + nsIContent *aChild, + int32_t aIndexInContainer) +{ + if (nsContentUtils::IsInSameAnonymousTree(this, aChild)) { + TriggerReclone(); + } +} + +void +SVGUseElement::ContentRemoved(nsIDocument *aDocument, + nsIContent *aContainer, + nsIContent *aChild, + int32_t aIndexInContainer, + nsIContent *aPreviousSibling) +{ + if (nsContentUtils::IsInSameAnonymousTree(this, aChild)) { + TriggerReclone(); + } +} + +void +SVGUseElement::NodeWillBeDestroyed(const nsINode *aNode) +{ + nsCOMPtr<nsIMutationObserver> kungFuDeathGrip(this); + UnlinkSource(); +} + +//---------------------------------------------------------------------- + +nsIContent* +SVGUseElement::CreateAnonymousContent() +{ + mClone = nullptr; + + if (mSource.get()) { + mSource.get()->RemoveMutationObserver(this); + } + + LookupHref(); + nsIContent* targetContent = mSource.get(); + if (!targetContent) + return nullptr; + + // make sure target is valid type for <use> + // QIable nsSVGGraphicsElement would eliminate enumerating all elements + if (!targetContent->IsAnyOfSVGElements(nsGkAtoms::svg, + nsGkAtoms::symbol, + nsGkAtoms::g, + nsGkAtoms::path, + nsGkAtoms::text, + nsGkAtoms::rect, + nsGkAtoms::circle, + nsGkAtoms::ellipse, + nsGkAtoms::line, + nsGkAtoms::polyline, + nsGkAtoms::polygon, + nsGkAtoms::image, + nsGkAtoms::use)) + return nullptr; + + // circular loop detection + + // check 1 - check if we're a document descendent of the target + if (nsContentUtils::ContentIsDescendantOf(this, targetContent)) + return nullptr; + + // check 2 - check if we're a clone, and if we already exist in the hierarchy + if (GetParent() && mOriginal) { + for (nsCOMPtr<nsIContent> content = GetParent(); + content; + content = content->GetParent()) { + if (content->IsSVGElement(nsGkAtoms::use) && + static_cast<SVGUseElement*>(content.get())->mOriginal == mOriginal) { + return nullptr; + } + } + } + + nsCOMPtr<nsINode> newnode; + nsCOMArray<nsINode> unused; + nsNodeInfoManager* nodeInfoManager = + targetContent->OwnerDoc() == OwnerDoc() ? + nullptr : OwnerDoc()->NodeInfoManager(); + nsNodeUtils::Clone(targetContent, true, nodeInfoManager, unused, + getter_AddRefs(newnode)); + + nsCOMPtr<nsIContent> newcontent = do_QueryInterface(newnode); + + if (!newcontent) + return nullptr; + + if (newcontent->IsSVGElement(nsGkAtoms::symbol)) { + nsIDocument *document = GetComposedDoc(); + if (!document) + return nullptr; + + nsNodeInfoManager *nodeInfoManager = document->NodeInfoManager(); + if (!nodeInfoManager) + return nullptr; + + RefPtr<mozilla::dom::NodeInfo> nodeInfo; + nodeInfo = nodeInfoManager->GetNodeInfo(nsGkAtoms::svg, nullptr, + kNameSpaceID_SVG, + nsIDOMNode::ELEMENT_NODE); + + nsCOMPtr<nsIContent> svgNode; + NS_NewSVGSVGElement(getter_AddRefs(svgNode), nodeInfo.forget(), + NOT_FROM_PARSER); + + if (!svgNode) + return nullptr; + + // copy attributes + BorrowedAttrInfo info; + uint32_t i; + for (i = 0; (info = newcontent->GetAttrInfoAt(i)); i++) { + nsAutoString value; + int32_t nsID = info.mName->NamespaceID(); + nsIAtom* lname = info.mName->LocalName(); + + info.mValue->ToString(value); + + svgNode->SetAttr(nsID, lname, info.mName->GetPrefix(), value, false); + } + + // move the children over + uint32_t num = newcontent->GetChildCount(); + for (i = 0; i < num; i++) { + nsCOMPtr<nsIContent> child = newcontent->GetFirstChild(); + newcontent->RemoveChildAt(0, false); + svgNode->InsertChildAt(child, i, true); + } + + newcontent = svgNode; + } + + if (newcontent->IsAnyOfSVGElements(nsGkAtoms::svg, nsGkAtoms::symbol)) { + nsSVGElement *newElement = static_cast<nsSVGElement*>(newcontent.get()); + + if (mLengthAttributes[ATTR_WIDTH].IsExplicitlySet()) + newElement->SetLength(nsGkAtoms::width, mLengthAttributes[ATTR_WIDTH]); + if (mLengthAttributes[ATTR_HEIGHT].IsExplicitlySet()) + newElement->SetLength(nsGkAtoms::height, mLengthAttributes[ATTR_HEIGHT]); + } + + // Set up its base URI correctly + nsCOMPtr<nsIURI> baseURI = targetContent->GetBaseURI(); + if (!baseURI) + return nullptr; + newcontent->SetExplicitBaseURI(baseURI); + + targetContent->AddMutationObserver(this); + mClone = newcontent; + +#ifdef DEBUG + // Our anonymous clone can get restyled by various things + // (e.g. SMIL). Reconstructing its frame is OK, though, because + // it's going to be our _only_ child in the frame tree, so can't get + // mis-ordered with anything. + mClone->SetProperty(nsGkAtoms::restylableAnonymousNode, + reinterpret_cast<void*>(true)); +#endif // DEBUG + + return mClone; +} + +nsIURI* +SVGUseElement::GetSourceDocURI() +{ + nsIContent* targetContent = mSource.get(); + if (!targetContent) + return nullptr; + + return targetContent->OwnerDoc()->GetDocumentURI(); +} + +void +SVGUseElement::DestroyAnonymousContent() +{ + nsContentUtils::DestroyAnonymousContent(&mClone); +} + +bool +SVGUseElement::OurWidthAndHeightAreUsed() const +{ + return mClone && mClone->IsAnyOfSVGElements(nsGkAtoms::svg, nsGkAtoms::symbol); +} + +//---------------------------------------------------------------------- +// implementation helpers + +void +SVGUseElement::SyncWidthOrHeight(nsIAtom* aName) +{ + NS_ASSERTION(aName == nsGkAtoms::width || aName == nsGkAtoms::height, + "The clue is in the function name"); + NS_ASSERTION(OurWidthAndHeightAreUsed(), "Don't call this"); + + if (OurWidthAndHeightAreUsed()) { + nsSVGElement *target = static_cast<nsSVGElement*>(mClone.get()); + uint32_t index = *sLengthInfo[ATTR_WIDTH].mName == aName ? ATTR_WIDTH : ATTR_HEIGHT; + + if (mLengthAttributes[index].IsExplicitlySet()) { + target->SetLength(aName, mLengthAttributes[index]); + return; + } + if (mClone->IsSVGElement(nsGkAtoms::svg)) { + // Our width/height attribute is now no longer explicitly set, so we + // need to revert the clone's width/height to the width/height of the + // content that's being cloned. + TriggerReclone(); + return; + } + // Our width/height attribute is now no longer explicitly set, so we + // need to set the value to 100% + nsSVGLength2 length; + length.Init(SVGContentUtils::XY, 0xff, + 100, nsIDOMSVGLength::SVG_LENGTHTYPE_PERCENTAGE); + target->SetLength(aName, length); + return; + } +} + +void +SVGUseElement::LookupHref() +{ + nsAutoString href; + if (mStringAttributes[HREF].IsExplicitlySet()) { + mStringAttributes[HREF].GetAnimValue(href, this); + } else { + mStringAttributes[XLINK_HREF].GetAnimValue(href, this); + } + + if (href.IsEmpty()) { + return; + } + + nsCOMPtr<nsIURI> targetURI; + nsCOMPtr<nsIURI> baseURI = mOriginal ? mOriginal->GetBaseURI() : GetBaseURI(); + nsContentUtils::NewURIWithDocumentCharset(getter_AddRefs(targetURI), href, + GetComposedDoc(), baseURI); + + mSource.Reset(this, targetURI); +} + +void +SVGUseElement::TriggerReclone() +{ + nsIDocument *doc = GetComposedDoc(); + if (!doc) + return; + nsIPresShell *presShell = doc->GetShell(); + if (!presShell) + return; + presShell->PostRecreateFramesFor(this); +} + +void +SVGUseElement::UnlinkSource() +{ + if (mSource.get()) { + mSource.get()->RemoveMutationObserver(this); + } + mSource.Unlink(); +} + +//---------------------------------------------------------------------- +// nsSVGElement methods + +/* virtual */ gfxMatrix +SVGUseElement::PrependLocalTransformsTo( + const gfxMatrix &aMatrix, SVGTransformTypes aWhich) const +{ + // 'transform' attribute: + gfxMatrix fromUserSpace = + SVGUseElementBase::PrependLocalTransformsTo(aMatrix, aWhich); + if (aWhich == eUserSpaceToParent) { + return fromUserSpace; + } + // our 'x' and 'y' attributes: + float x, y; + const_cast<SVGUseElement*>(this)->GetAnimatedLengthValues(&x, &y, nullptr); + gfxMatrix toUserSpace = gfxMatrix::Translation(x, y); + if (aWhich == eChildToUserSpace) { + return toUserSpace * aMatrix; + } + MOZ_ASSERT(aWhich == eAllTransforms, "Unknown TransformTypes"); + return toUserSpace * fromUserSpace; +} + +/* virtual */ bool +SVGUseElement::HasValidDimensions() const +{ + return (!mLengthAttributes[ATTR_WIDTH].IsExplicitlySet() || + mLengthAttributes[ATTR_WIDTH].GetAnimValInSpecifiedUnits() > 0) && + (!mLengthAttributes[ATTR_HEIGHT].IsExplicitlySet() || + mLengthAttributes[ATTR_HEIGHT].GetAnimValInSpecifiedUnits() > 0); +} + +nsSVGElement::LengthAttributesInfo +SVGUseElement::GetLengthInfo() +{ + return LengthAttributesInfo(mLengthAttributes, sLengthInfo, + ArrayLength(sLengthInfo)); +} + +nsSVGElement::StringAttributesInfo +SVGUseElement::GetStringInfo() +{ + return StringAttributesInfo(mStringAttributes, sStringInfo, + ArrayLength(sStringInfo)); +} + +//---------------------------------------------------------------------- +// nsIContent methods + +NS_IMETHODIMP_(bool) +SVGUseElement::IsAttributeMapped(const nsIAtom* name) const +{ + static const MappedAttributeEntry* const map[] = { + sFEFloodMap, + sFiltersMap, + sFontSpecificationMap, + sGradientStopMap, + sLightingEffectsMap, + sMarkersMap, + sTextContentElementsMap, + sViewportsMap + }; + + return FindAttributeDependence(name, map) || + SVGUseElementBase::IsAttributeMapped(name); +} + +} // namespace dom +} // namespace mozilla diff --git a/dom/svg/SVGUseElement.h b/dom/svg/SVGUseElement.h new file mode 100644 index 0000000000..7630e9af66 --- /dev/null +++ b/dom/svg/SVGUseElement.h @@ -0,0 +1,127 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef mozilla_dom_SVGUseElement_h +#define mozilla_dom_SVGUseElement_h + +#include "mozilla/dom/FromParser.h" +#include "nsReferencedElement.h" +#include "nsStubMutationObserver.h" +#include "mozilla/dom/SVGGraphicsElement.h" +#include "nsSVGLength2.h" +#include "nsSVGString.h" +#include "nsTArray.h" + +class nsIContent; +class nsSVGUseFrame; + +nsresult +NS_NewSVGSVGElement(nsIContent **aResult, + already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo, + mozilla::dom::FromParser aFromParser); +nsresult NS_NewSVGUseElement(nsIContent **aResult, + already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo); + +namespace mozilla { +namespace dom { + +typedef SVGGraphicsElement SVGUseElementBase; + +class SVGUseElement final : public SVGUseElementBase, + public nsStubMutationObserver +{ + friend class ::nsSVGUseFrame; +protected: + friend nsresult (::NS_NewSVGUseElement(nsIContent **aResult, + already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo)); + explicit SVGUseElement(already_AddRefed<mozilla::dom::NodeInfo>& aNodeInfo); + virtual ~SVGUseElement(); + virtual JSObject* WrapNode(JSContext *cx, JS::Handle<JSObject*> aGivenProto) override; + +public: + // interfaces: + + NS_DECL_ISUPPORTS_INHERITED + NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(SVGUseElement, SVGUseElementBase) + + NS_DECL_NSIMUTATIONOBSERVER_CHARACTERDATACHANGED + NS_DECL_NSIMUTATIONOBSERVER_ATTRIBUTECHANGED + NS_DECL_NSIMUTATIONOBSERVER_CONTENTAPPENDED + NS_DECL_NSIMUTATIONOBSERVER_CONTENTINSERTED + NS_DECL_NSIMUTATIONOBSERVER_CONTENTREMOVED + NS_DECL_NSIMUTATIONOBSERVER_NODEWILLBEDESTROYED + + // for nsSVGUseFrame's nsIAnonymousContentCreator implementation. + nsIContent* CreateAnonymousContent(); + nsIContent* GetAnonymousContent() const { return mClone; } + void DestroyAnonymousContent(); + + // nsSVGElement specializations: + virtual gfxMatrix PrependLocalTransformsTo( + const gfxMatrix &aMatrix, + SVGTransformTypes aWhich = eAllTransforms) const override; + virtual bool HasValidDimensions() const override; + + // nsIContent interface + virtual nsresult Clone(mozilla::dom::NodeInfo *aNodeInfo, nsINode **aResult) const override; + NS_IMETHOD_(bool) IsAttributeMapped(const nsIAtom* aAttribute) const override; + + // WebIDL + already_AddRefed<SVGAnimatedString> Href(); + already_AddRefed<SVGAnimatedLength> X(); + already_AddRefed<SVGAnimatedLength> Y(); + already_AddRefed<SVGAnimatedLength> Width(); + already_AddRefed<SVGAnimatedLength> Height(); + + nsIURI* GetSourceDocURI(); + +protected: + class SourceReference : public nsReferencedElement { + public: + explicit SourceReference(SVGUseElement* aContainer) : mContainer(aContainer) {} + protected: + virtual void ElementChanged(Element* aFrom, Element* aTo) override { + nsReferencedElement::ElementChanged(aFrom, aTo); + if (aFrom) { + aFrom->RemoveMutationObserver(mContainer); + } + mContainer->TriggerReclone(); + } + private: + SVGUseElement* mContainer; + }; + + virtual LengthAttributesInfo GetLengthInfo() override; + virtual StringAttributesInfo GetStringInfo() override; + + /** + * Returns true if our width and height should be used, or false if they + * should be ignored. As per the spec, this depends on the type of the + * element that we're referencing. + */ + bool OurWidthAndHeightAreUsed() const; + void SyncWidthOrHeight(nsIAtom *aName); + void LookupHref(); + void TriggerReclone(); + void UnlinkSource(); + + enum { ATTR_X, ATTR_Y, ATTR_WIDTH, ATTR_HEIGHT }; + nsSVGLength2 mLengthAttributes[4]; + static LengthInfo sLengthInfo[4]; + + enum { HREF, XLINK_HREF }; + nsSVGString mStringAttributes[2]; + static StringInfo sStringInfo[2]; + + nsCOMPtr<nsIContent> mOriginal; // if we've been cloned, our "real" copy + nsCOMPtr<nsIContent> mClone; // cloned tree + SourceReference mSource; // observed element +}; + +} // namespace dom +} // namespace mozilla + +#endif // mozilla_dom_SVGUseElement_h diff --git a/dom/svg/SVGViewBoxSMILType.cpp b/dom/svg/SVGViewBoxSMILType.cpp new file mode 100644 index 0000000000..d11ca1a82b --- /dev/null +++ b/dom/svg/SVGViewBoxSMILType.cpp @@ -0,0 +1,138 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "SVGViewBoxSMILType.h" +#include "nsSMILValue.h" +#include "nsSVGViewBox.h" +#include "nsDebug.h" +#include <math.h> + +namespace mozilla { + +/*static*/ SVGViewBoxSMILType SVGViewBoxSMILType::sSingleton; + +void +SVGViewBoxSMILType::Init(nsSMILValue& aValue) const +{ + MOZ_ASSERT(aValue.IsNull(), "Unexpected value type"); + + aValue.mU.mPtr = new nsSVGViewBoxRect(); + aValue.mType = this; +} + +void +SVGViewBoxSMILType::Destroy(nsSMILValue& aValue) const +{ + NS_PRECONDITION(aValue.mType == this, "Unexpected SMIL value"); + delete static_cast<nsSVGViewBoxRect*>(aValue.mU.mPtr); + aValue.mU.mPtr = nullptr; + aValue.mType = nsSMILNullType::Singleton(); +} + +nsresult +SVGViewBoxSMILType::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 nsSVGViewBoxRect* src = static_cast<const nsSVGViewBoxRect*>(aSrc.mU.mPtr); + nsSVGViewBoxRect* dst = static_cast<nsSVGViewBoxRect*>(aDest.mU.mPtr); + *dst = *src; + return NS_OK; +} + +bool +SVGViewBoxSMILType::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"); + + const nsSVGViewBoxRect* leftBox = + static_cast<const nsSVGViewBoxRect*>(aLeft.mU.mPtr); + const nsSVGViewBoxRect* rightBox = + static_cast<nsSVGViewBoxRect*>(aRight.mU.mPtr); + return *leftBox == *rightBox; +} + +nsresult +SVGViewBoxSMILType::Add(nsSMILValue& aDest, const nsSMILValue& aValueToAdd, + uint32_t aCount) const +{ + NS_PRECONDITION(aValueToAdd.mType == aDest.mType, + "Trying to add invalid types"); + NS_PRECONDITION(aValueToAdd.mType == this, "Unexpected source type"); + + // See https://bugzilla.mozilla.org/show_bug.cgi?id=541884#c3 and the two + // comments that follow that one for arguments for and against allowing + // viewBox to be additive. + + return NS_ERROR_FAILURE; +} + +nsresult +SVGViewBoxSMILType::ComputeDistance(const nsSMILValue& aFrom, + const nsSMILValue& aTo, + double& aDistance) const +{ + NS_PRECONDITION(aFrom.mType == aTo.mType,"Trying to compare different types"); + NS_PRECONDITION(aFrom.mType == this, "Unexpected source type"); + + const nsSVGViewBoxRect* from = static_cast<const nsSVGViewBoxRect*>(aFrom.mU.mPtr); + const nsSVGViewBoxRect* to = static_cast<const nsSVGViewBoxRect*>(aTo.mU.mPtr); + + if (from->none || to->none) { + return NS_ERROR_FAILURE; + } + + // We use the distances between the edges rather than the difference between + // the x, y, width and height for the "distance". This is necessary in + // order for the "distance" result that we calculate to be the same for a + // given change in the left side as it is for an equal change in the opposite + // side. See https://bugzilla.mozilla.org/show_bug.cgi?id=541884#c12 + + float dLeft = to->x - from->x; + float dTop = to->y - from->y; + float dRight = ( to->x + to->width ) - ( from->x + from->width ); + float dBottom = ( to->y + to->height ) - ( from->y + from->height ); + + aDistance = sqrt(dLeft*dLeft + dTop*dTop + dRight*dRight + dBottom*dBottom); + + return NS_OK; +} + +nsresult +SVGViewBoxSMILType::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 nsSVGViewBoxRect* start = static_cast<const nsSVGViewBoxRect*>(aStartVal.mU.mPtr); + const nsSVGViewBoxRect* end = static_cast<const nsSVGViewBoxRect*>(aEndVal.mU.mPtr); + + if (start->none || end->none) { + return NS_ERROR_FAILURE; + } + + nsSVGViewBoxRect* current = static_cast<nsSVGViewBoxRect*>(aResult.mU.mPtr); + + float x = (start->x + (end->x - start->x) * aUnitDistance); + float y = (start->y + (end->y - start->y) * aUnitDistance); + float width = (start->width + (end->width - start->width) * aUnitDistance); + float height = (start->height + (end->height - start->height) * aUnitDistance); + + *current = nsSVGViewBoxRect(x, y, width, height); + + return NS_OK; +} + +} // namespace mozilla diff --git a/dom/svg/SVGViewBoxSMILType.h b/dom/svg/SVGViewBoxSMILType.h new file mode 100644 index 0000000000..f05f3928d2 --- /dev/null +++ b/dom/svg/SVGViewBoxSMILType.h @@ -0,0 +1,48 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef MOZILLA_SVGVIEWBOXSMILTYPE_H_ +#define MOZILLA_SVGVIEWBOXSMILTYPE_H_ + +#include "mozilla/Attributes.h" +#include "nsISMILType.h" + +class nsSMILValue; + +namespace mozilla { + +class SVGViewBoxSMILType : public nsISMILType +{ +public: + // Singleton for nsSMILValue objects to hold onto. + static SVGViewBoxSMILType sSingleton; + +protected: + // nsISMILType Methods + // ------------------- + virtual void Init(nsSMILValue& aValue) const override; + virtual void Destroy(nsSMILValue&) const override; + virtual nsresult Assign(nsSMILValue& aDest, const nsSMILValue& aSrc) const override; + virtual bool IsEqual(const nsSMILValue& aLeft, + const nsSMILValue& aRight) const override; + virtual nsresult Add(nsSMILValue& aDest, const nsSMILValue& aValueToAdd, + uint32_t aCount) const override; + virtual nsresult ComputeDistance(const nsSMILValue& aFrom, + const nsSMILValue& aTo, + double& aDistance) const override; + virtual nsresult Interpolate(const nsSMILValue& aStartVal, + const nsSMILValue& aEndVal, + double aUnitDistance, + nsSMILValue& aResult) const override; + +private: + // Private constructor: prevent instances beyond my singleton. + constexpr SVGViewBoxSMILType() {} +}; + +} // namespace mozilla + +#endif // MOZILLA_SVGVIEWBOXSMILTYPE_H_ diff --git a/dom/svg/SVGViewElement.cpp b/dom/svg/SVGViewElement.cpp new file mode 100644 index 0000000000..a9c5c16212 --- /dev/null +++ b/dom/svg/SVGViewElement.cpp @@ -0,0 +1,119 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "mozilla/dom/SVGViewElement.h" +#include "mozilla/dom/SVGViewElementBinding.h" +#include "DOMSVGStringList.h" + +NS_IMPL_NS_NEW_NAMESPACED_SVG_ELEMENT(View) + +namespace mozilla { +namespace dom { + +JSObject* +SVGViewElement::WrapNode(JSContext *aCx, JS::Handle<JSObject*> aGivenProto) +{ + return SVGViewElementBinding::Wrap(aCx, this, aGivenProto); +} + +nsSVGElement::StringListInfo SVGViewElement::sStringListInfo[1] = +{ + { &nsGkAtoms::viewTarget } +}; + +nsSVGEnumMapping SVGViewElement::sZoomAndPanMap[] = { + {&nsGkAtoms::disable, SVG_ZOOMANDPAN_DISABLE}, + {&nsGkAtoms::magnify, SVG_ZOOMANDPAN_MAGNIFY}, + {nullptr, 0} +}; + +nsSVGElement::EnumInfo SVGViewElement::sEnumInfo[1] = +{ + { &nsGkAtoms::zoomAndPan, + sZoomAndPanMap, + SVG_ZOOMANDPAN_MAGNIFY + } +}; + +//---------------------------------------------------------------------- +// Implementation + +SVGViewElement::SVGViewElement(already_AddRefed<mozilla::dom::NodeInfo>& aNodeInfo) + : SVGViewElementBase(aNodeInfo) +{ +} + +//---------------------------------------------------------------------- +// nsIDOMNode methods + +NS_IMPL_ELEMENT_CLONE_WITH_INIT(SVGViewElement) + +void +SVGViewElement::SetZoomAndPan(uint16_t aZoomAndPan, ErrorResult& rv) +{ + if (aZoomAndPan == SVG_ZOOMANDPAN_DISABLE || + aZoomAndPan == SVG_ZOOMANDPAN_MAGNIFY) { + mEnumAttributes[ZOOMANDPAN].SetBaseValue(aZoomAndPan, this); + return; + } + + rv.ThrowRangeError<MSG_INVALID_ZOOMANDPAN_VALUE_ERROR>(); +} + +//---------------------------------------------------------------------- + +already_AddRefed<SVGAnimatedRect> +SVGViewElement::ViewBox() +{ + return mViewBox.ToSVGAnimatedRect(this); +} + +already_AddRefed<DOMSVGAnimatedPreserveAspectRatio> +SVGViewElement::PreserveAspectRatio() +{ + return mPreserveAspectRatio.ToDOMAnimatedPreserveAspectRatio(this); +} + +//---------------------------------------------------------------------- + +already_AddRefed<DOMSVGStringList> +SVGViewElement::ViewTarget() +{ + return DOMSVGStringList::GetDOMWrapper( + &mStringListAttributes[VIEW_TARGET], this, false, VIEW_TARGET); +} + +//---------------------------------------------------------------------- +// nsSVGElement methods + +nsSVGElement::EnumAttributesInfo +SVGViewElement::GetEnumInfo() +{ + return EnumAttributesInfo(mEnumAttributes, sEnumInfo, + ArrayLength(sEnumInfo)); +} + +nsSVGViewBox * +SVGViewElement::GetViewBox() +{ + return &mViewBox; +} + +SVGAnimatedPreserveAspectRatio * +SVGViewElement::GetPreserveAspectRatio() +{ + return &mPreserveAspectRatio; +} + +nsSVGElement::StringListAttributesInfo +SVGViewElement::GetStringListInfo() +{ + return StringListAttributesInfo(mStringListAttributes, sStringListInfo, + ArrayLength(sStringListInfo)); +} + +} // namespace dom +} // namespace mozilla diff --git a/dom/svg/SVGViewElement.h b/dom/svg/SVGViewElement.h new file mode 100644 index 0000000000..9b2db262ee --- /dev/null +++ b/dom/svg/SVGViewElement.h @@ -0,0 +1,81 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef mozilla_dom_SVGViewElement_h +#define mozilla_dom_SVGViewElement_h + +#include "nsSVGElement.h" +#include "nsSVGEnum.h" +#include "nsSVGViewBox.h" +#include "SVGAnimatedPreserveAspectRatio.h" +#include "SVGStringList.h" + +static const unsigned short SVG_ZOOMANDPAN_UNKNOWN = 0; +static const unsigned short SVG_ZOOMANDPAN_DISABLE = 1; +static const unsigned short SVG_ZOOMANDPAN_MAGNIFY = 2; + +typedef nsSVGElement SVGViewElementBase; + +class nsSVGOuterSVGFrame; + +nsresult NS_NewSVGViewElement(nsIContent **aResult, + already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo); + +namespace mozilla { +class SVGFragmentIdentifier; + +namespace dom { +class SVGSVGElement; + +class SVGViewElement : public SVGViewElementBase +{ +protected: + friend class mozilla::SVGFragmentIdentifier; + friend class SVGSVGElement; + friend class ::nsSVGOuterSVGFrame; + explicit SVGViewElement(already_AddRefed<mozilla::dom::NodeInfo>& aNodeInfo); + friend nsresult (::NS_NewSVGViewElement(nsIContent **aResult, + already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo)); + virtual JSObject* WrapNode(JSContext *cx, JS::Handle<JSObject*> aGivenProto) override; + +public: + virtual nsresult Clone(mozilla::dom::NodeInfo *aNodeInfo, nsINode **aResult) const override; + + // WebIDL + uint16_t ZoomAndPan() { return mEnumAttributes[ZOOMANDPAN].GetAnimValue(); } + void SetZoomAndPan(uint16_t aZoomAndPan, ErrorResult& rv); + already_AddRefed<SVGAnimatedRect> ViewBox(); + already_AddRefed<DOMSVGAnimatedPreserveAspectRatio> PreserveAspectRatio(); + already_AddRefed<DOMSVGStringList> ViewTarget(); + +private: + + // nsSVGElement overrides + + virtual EnumAttributesInfo GetEnumInfo() override; + + enum { ZOOMANDPAN }; + nsSVGEnum mEnumAttributes[1]; + static nsSVGEnumMapping sZoomAndPanMap[]; + static EnumInfo sEnumInfo[1]; + + virtual nsSVGViewBox *GetViewBox() override; + virtual SVGAnimatedPreserveAspectRatio *GetPreserveAspectRatio() override; + + nsSVGViewBox mViewBox; + SVGAnimatedPreserveAspectRatio mPreserveAspectRatio; + + virtual StringListAttributesInfo GetStringListInfo() override; + + enum { VIEW_TARGET }; + SVGStringList mStringListAttributes[1]; + static StringListInfo sStringListInfo[1]; +}; + +} // namespace dom +} // namespace mozilla + +#endif // mozilla_dom_SVGViewElement_h diff --git a/dom/svg/SVGZoomEvent.cpp b/dom/svg/SVGZoomEvent.cpp new file mode 100644 index 0000000000..4856f2c389 --- /dev/null +++ b/dom/svg/SVGZoomEvent.cpp @@ -0,0 +1,103 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "DOMSVGPoint.h" +#include "mozilla/ContentEvents.h" +#include "mozilla/dom/Element.h" +#include "mozilla/dom/SVGSVGElement.h" +#include "mozilla/dom/SVGZoomEvent.h" +#include "nsIDocument.h" +#include "nsIPresShell.h" +#include "prtime.h" + +namespace mozilla { +namespace dom { + +//---------------------------------------------------------------------- +// Implementation + +NS_IMPL_CYCLE_COLLECTION_INHERITED(SVGZoomEvent, UIEvent, mPreviousTranslate, mNewTranslate) + +NS_IMPL_ADDREF_INHERITED(SVGZoomEvent, UIEvent) +NS_IMPL_RELEASE_INHERITED(SVGZoomEvent, UIEvent) + +NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(SVGZoomEvent) +NS_INTERFACE_MAP_END_INHERITING(UIEvent) + +SVGZoomEvent::SVGZoomEvent(EventTarget* aOwner, + nsPresContext* aPresContext, + InternalSVGZoomEvent* aEvent) + : UIEvent(aOwner, aPresContext, + aEvent ? aEvent : new InternalSVGZoomEvent(false, eSVGZoom)) + , mPreviousScale(0) + , mNewScale(0) +{ + if (aEvent) { + mEventIsInternal = false; + } + else { + mEventIsInternal = true; + mEvent->mTime = PR_Now(); + } + + // We must store the "Previous" and "New" values before this event is + // dispatched. Reading the values from the root 'svg' element after we've + // been dispatched is not an option since event handler code may change + // currentScale and currentTranslate in response to this event. + nsIPresShell *presShell; + if (mPresContext && (presShell = mPresContext->GetPresShell())) { + nsIDocument *doc = presShell->GetDocument(); + if (doc) { + Element *rootElement = doc->GetRootElement(); + if (rootElement) { + // If the root element isn't an SVG 'svg' element + // (e.g. if this event was created by calling createEvent on a + // non-SVGDocument), then the "New" and "Previous" + // properties will be left null which is probably what we want. + if (rootElement->IsSVGElement(nsGkAtoms::svg)) { + SVGSVGElement *SVGSVGElem = + static_cast<SVGSVGElement*>(rootElement); + + mNewScale = SVGSVGElem->GetCurrentScale(); + mPreviousScale = SVGSVGElem->GetPreviousScale(); + + const SVGPoint& translate = SVGSVGElem->GetCurrentTranslate(); + mNewTranslate = + new DOMSVGPoint(translate.GetX(), translate.GetY()); + mNewTranslate->SetReadonly(true); + + const SVGPoint& prevTranslate = SVGSVGElem->GetPreviousTranslate(); + mPreviousTranslate = + new DOMSVGPoint(prevTranslate.GetX(), prevTranslate.GetY()); + mPreviousTranslate->SetReadonly(true); + } + } + } + } +} + +SVGZoomEvent::~SVGZoomEvent() +{ +} + +} // namespace dom +} // namespace mozilla + + +//////////////////////////////////////////////////////////////////////// +// Exported creation functions: + +using namespace mozilla; +using namespace mozilla::dom; + +already_AddRefed<SVGZoomEvent> +NS_NewDOMSVGZoomEvent(EventTarget* aOwner, + nsPresContext* aPresContext, + mozilla::InternalSVGZoomEvent* aEvent) +{ + RefPtr<SVGZoomEvent> it = new SVGZoomEvent(aOwner, aPresContext, aEvent); + return it.forget(); +} diff --git a/dom/svg/SVGZoomEvent.h b/dom/svg/SVGZoomEvent.h new file mode 100644 index 0000000000..c0937309b0 --- /dev/null +++ b/dom/svg/SVGZoomEvent.h @@ -0,0 +1,78 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef mozilla_dom_SVGZoomEvent_h +#define mozilla_dom_SVGZoomEvent_h + +#include "DOMSVGPoint.h" +#include "mozilla/dom/UIEvent.h" +#include "mozilla/dom/SVGZoomEventBinding.h" +#include "mozilla/EventForwards.h" + +class nsPresContext; + +namespace mozilla { + +class nsISVGPoint; + +namespace dom { + +class SVGZoomEvent final : public UIEvent +{ +public: + + NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(SVGZoomEvent, UIEvent) + NS_DECL_ISUPPORTS_INHERITED + + SVGZoomEvent(EventTarget* aOwner, nsPresContext* aPresContext, + InternalSVGZoomEvent* aEvent); + + // Forward to base class + NS_FORWARD_TO_UIEVENT + + virtual JSObject* WrapObjectInternal(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override + { + return SVGZoomEventBinding::Wrap(aCx, this, aGivenProto); + } + + float PreviousScale() const + { + return mPreviousScale; + } + + nsISVGPoint* GetPreviousTranslate() const + { + return mPreviousTranslate; + } + + float NewScale() const + { + return mNewScale; + } + + nsISVGPoint* GetNewTranslate() const + { + return mNewTranslate; + } + +private: + ~SVGZoomEvent(); + + float mPreviousScale; + float mNewScale; + RefPtr<DOMSVGPoint> mPreviousTranslate; + RefPtr<DOMSVGPoint> mNewTranslate; +}; + +} // namespace dom +} // namespace mozilla + +already_AddRefed<mozilla::dom::SVGZoomEvent> +NS_NewDOMSVGZoomEvent(mozilla::dom::EventTarget* aOwner, + nsPresContext* aPresContext, + mozilla::InternalSVGZoomEvent* aEvent); + +#endif // mozilla_dom_SVGZoomEvent_h diff --git a/dom/svg/crashtests/1035248-1.svg b/dom/svg/crashtests/1035248-1.svg new file mode 100644 index 0000000000..125f83ba49 --- /dev/null +++ b/dom/svg/crashtests/1035248-1.svg @@ -0,0 +1,18 @@ +<svg xmlns="http://www.w3.org/2000/svg"> +<script> + +function boom() +{ + var outer = document.createElementNS("http://www.w3.org/1999/xhtml", "div"); + var inner = document.createElementNS("http://www.w3.org/1999/xhtml", "div"); + inner.setAttributeNS(null, "style", "display: table-row-group;"); + outer.appendChild(inner); + + document.removeChild(document.documentElement); + document.appendChild(outer); +} + +window.addEventListener("load", boom, false); + +</script> +</svg> diff --git a/dom/svg/crashtests/1035248-2.svg b/dom/svg/crashtests/1035248-2.svg new file mode 100644 index 0000000000..019cfda304 --- /dev/null +++ b/dom/svg/crashtests/1035248-2.svg @@ -0,0 +1,16 @@ +<svg xmlns="http://www.w3.org/2000/svg"> + +<script> + +window.addEventListener("load", function() { + var div = document.createElementNS('http://www.w3.org/1999/xhtml', 'div'); + var tr = document.createElementNS('http://www.w3.org/1999/xhtml', 'tr'); + tr.style.display = "table-row"; + document.removeChild(document.documentElement); + div.appendChild(tr); + document.appendChild(div); +}, false); + +</script> + +</svg> diff --git a/dom/svg/crashtests/1244898-1.xhtml b/dom/svg/crashtests/1244898-1.xhtml new file mode 100644 index 0000000000..9b89c2c8b8 --- /dev/null +++ b/dom/svg/crashtests/1244898-1.xhtml @@ -0,0 +1,14 @@ +<html xmlns="http://www.w3.org/1999/xhtml" class="reftest-wait"> + +<script> + +window.addEventListener("load", function() { + location.hash = "#a"; + document.documentElement.removeAttribute("class"); +}, false); + +</script> + +<animate xmlns="http://www.w3.org/2000/svg" id="a"/> + +</html> diff --git a/dom/svg/crashtests/1250725.html b/dom/svg/crashtests/1250725.html new file mode 100644 index 0000000000..68052f344f --- /dev/null +++ b/dom/svg/crashtests/1250725.html @@ -0,0 +1,16 @@ +<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"> + + <script> + function boom() + { + document.getElementById("u").setAttribute("width", "30"); + document.getElementById("p").remove(); + } + window.addEventListener("load", boom, false); + </script> + + <symbol id="p" viewBox="0 0 100 20"/> + + <use id="u" xlink:href="#p"/> + +</svg> diff --git a/dom/svg/crashtests/1267272-1.svg b/dom/svg/crashtests/1267272-1.svg new file mode 100644 index 0000000000..67be18f553 --- /dev/null +++ b/dom/svg/crashtests/1267272-1.svg @@ -0,0 +1,7 @@ +<?xml version="1.0" encoding="Windows-1252"?> +<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"> + <filter id="f"> + <feImage xlink:href="invalid-image.svg"/> + </filter> + <rect filter='url(#f)' width="1" height="1"/> +</svg> diff --git a/dom/svg/crashtests/1282985-1.svg b/dom/svg/crashtests/1282985-1.svg new file mode 100644 index 0000000000..e0d838917b --- /dev/null +++ b/dom/svg/crashtests/1282985-1.svg @@ -0,0 +1,24 @@ +<svg xmlns="http://www.w3.org/2000/svg"> +<script> +<![CDATA[ + +function boom() { + var g = document.createElementNS("http://www.w3.org/2000/svg", "g"); + g.setAttribute("id", "g"); + var iframe = document.createElementNS("http://www.w3.org/1999/xhtml", "iframe"); + g.appendChild(iframe); + document.documentElement.appendChild(g); + var use = document.createElementNS("http://www.w3.org/2000/svg", "use"); + use.setAttributeNS("http://www.w3.org/1999/xlink", "xlink:href", "#g"); + document.documentElement.appendChild(use); + setTimeout(function() { + setTimeout(function() { + g.appendChild(document.createElementNS("http://www.w3.org/1999/xhtml", "video")); + }, 3); + }, 3); +} +window.addEventListener("load", boom, false); + +]]> +</script> +</svg> diff --git a/dom/svg/crashtests/1329849-1.svg b/dom/svg/crashtests/1329849-1.svg new file mode 100644 index 0000000000..350e549efd --- /dev/null +++ b/dom/svg/crashtests/1329849-1.svg @@ -0,0 +1,13 @@ +<svg height='600' + xmlns="http://www.w3.org/2000/svg" + xmlns:xlink="http://www.w3.org/1999/xlink"> + +<filter id='f1'> +<feSpecularLighting kernelUnitLength='-82'> +<feSpotLight/> +</feSpecularLighting> +</filter> +<filter id='f2' xlink:href='#f1'></filter> +<polyline filter='url(#f2)' points='10,59 293,88 18,289'/> + +</svg>
\ No newline at end of file diff --git a/dom/svg/crashtests/1329849-2.svg b/dom/svg/crashtests/1329849-2.svg new file mode 100644 index 0000000000..ec340e2316 --- /dev/null +++ b/dom/svg/crashtests/1329849-2.svg @@ -0,0 +1,13 @@ +<svg height='600' + xmlns="http://www.w3.org/2000/svg" + xmlns:xlink="http://www.w3.org/1999/xlink"> + +<filter id='f1'> +<feSpecularLighting kernelUnitLength='-80 10'> +<feSpotLight/> +</feSpecularLighting> +</filter> +<filter id='f2' xlink:href='#f1'></filter> +<polyline filter='url(#f2)' points='10,59 293,88 18,289'/> + +</svg>
\ No newline at end of file diff --git a/dom/svg/crashtests/1329849-3.svg b/dom/svg/crashtests/1329849-3.svg new file mode 100644 index 0000000000..a263e083fe --- /dev/null +++ b/dom/svg/crashtests/1329849-3.svg @@ -0,0 +1,13 @@ +<svg height='600' + xmlns="http://www.w3.org/2000/svg" + xmlns:xlink="http://www.w3.org/1999/xlink"> + +<filter id='f1'> +<feSpecularLighting kernelUnitLength='80 -10'> +<feSpotLight/> +</feSpecularLighting> +</filter> +<filter id='f2' xlink:href='#f1'></filter> +<polyline filter='url(#f2)' points='10,59 293,88 18,289'/> + +</svg>
\ No newline at end of file diff --git a/dom/svg/crashtests/1329849-4.svg b/dom/svg/crashtests/1329849-4.svg new file mode 100644 index 0000000000..b8bc061c5f --- /dev/null +++ b/dom/svg/crashtests/1329849-4.svg @@ -0,0 +1,13 @@ +<svg height='600' + xmlns="http://www.w3.org/2000/svg" + xmlns:xlink="http://www.w3.org/1999/xlink"> + +<filter id='f1'> +<feDiffuseLighting kernelUnitLength='-80'> +<feSpotLight/> +</feDiffuseLighting> +</filter> +<filter id='f2' xlink:href='#f1'></filter> +<polyline filter='url(#f2)' points='10,59 293,88 18,289'/> + +</svg>
\ No newline at end of file diff --git a/dom/svg/crashtests/1329849-5.svg b/dom/svg/crashtests/1329849-5.svg new file mode 100644 index 0000000000..16390ccb04 --- /dev/null +++ b/dom/svg/crashtests/1329849-5.svg @@ -0,0 +1,13 @@ +<svg height='600' + xmlns="http://www.w3.org/2000/svg" + xmlns:xlink="http://www.w3.org/1999/xlink"> + +<filter id='f1'> +<feDiffuseLighting kernelUnitLength='-80 10'> +<feSpotLight/> +</feDiffuseLighting> +</filter> +<filter id='f2' xlink:href='#f1'></filter> +<polyline filter='url(#f2)' points='10,59 293,88 18,289'/> + +</svg>
\ No newline at end of file diff --git a/dom/svg/crashtests/1329849-6.svg b/dom/svg/crashtests/1329849-6.svg new file mode 100644 index 0000000000..0a928393c7 --- /dev/null +++ b/dom/svg/crashtests/1329849-6.svg @@ -0,0 +1,13 @@ +<svg height='600' + xmlns="http://www.w3.org/2000/svg" + xmlns:xlink="http://www.w3.org/1999/xlink"> + +<filter id='f1'> +<feDiffuseLighting kernelUnitLength='80 -10'> +<feSpotLight/> +</feDiffuseLighting> +</filter> +<filter id='f2' xlink:href='#f1'></filter> +<polyline filter='url(#f2)' points='10,59 293,88 18,289'/> + +</svg>
\ No newline at end of file diff --git a/dom/svg/crashtests/1343147.svg b/dom/svg/crashtests/1343147.svg new file mode 100644 index 0000000000..8ecc6aa9a7 --- /dev/null +++ b/dom/svg/crashtests/1343147.svg @@ -0,0 +1,13 @@ +<svg xmlns="http://www.w3.org/2000/svg"> +<style> +<![CDATA[ + svg { + background-image: linear-gradient(lime, lime); + background-clip: text; + } + text { transform: skewy(30grad); } + g { perspective: 0; } +]]> +</style> + <g><text>hello</text></g> +</svg>
\ No newline at end of file diff --git a/dom/svg/crashtests/307322-1.svg b/dom/svg/crashtests/307322-1.svg new file mode 100644 index 0000000000..c9ad682665 --- /dev/null +++ b/dom/svg/crashtests/307322-1.svg @@ -0,0 +1,8 @@ +<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"> + + <symbol id="foo"> + <use xlink:href="#foo"/> + </symbol> + + <use xlink:href="#foo"/> +</svg>
\ No newline at end of file diff --git a/dom/svg/crashtests/327705-1.svg b/dom/svg/crashtests/327705-1.svg new file mode 100644 index 0000000000..f033d345d4 --- /dev/null +++ b/dom/svg/crashtests/327705-1.svg @@ -0,0 +1,19 @@ +<?xml version="1.0" encoding="UTF-8"?> +
<svg xmlns="http://www.w3.org/2000/svg">
+ +<script>
+ +function init() +{
+ var a = document.getElementById("a"); + var b = document.getElementById("b"); + a.appendChild(b); + b.getBBox(); +};
+ +window.addEventListener("load", init, false); +
</script> + +<rect id="a" width="20" height="10"/>
<rect id="b" width="10" height="20"/>
+ +</svg>
\ No newline at end of file diff --git a/dom/svg/crashtests/336994-1.html b/dom/svg/crashtests/336994-1.html new file mode 100644 index 0000000000..4e251cdde8 --- /dev/null +++ b/dom/svg/crashtests/336994-1.html @@ -0,0 +1,12 @@ +<html class="reftest-wait"> +<head> +<title>Testcase bug 336994 - Crash when window gets destroyed on SVGZoom event</title> +<script> +setTimeout('document.documentElement.className = ""', 500); +</script> +</head> +<body> +This page should not crash Mozilla, you should see no iframe<br> +<iframe src="data:image/svg+xml;charset=utf-8,%3C%3Fxml%20version%3D%221.0%22%20standalone%3D%22no%22%3F%3E%0A%3C%21DOCTYPE%20svg%20PUBLIC%20%22-//W3C//DTD%20SVG%201.1//EN%22%20%22http%3A//www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd%22%3E%0A%3Csvg%20width%3D%22100%25%22%20height%3D%22100%25%22%20version%3D%221.1%22%20xmlns%3D%22http%3A//www.w3.org/2000/svg%22%3E%0A%3Cscript%20xmlns%3D%22http%3A//www.w3.org/1999/xhtml%22%3E%0Awindow.addEventListener%28%27SVGZoom%27%2C%20doe%2C%20true%29%3B%0Afunction%20doe%28e%29%20%7B%0Avar%20x%3D%20parent.document.getElementsByTagName%28%27iframe%27%29%5B0%5D%3B%0Ax.parentNode.removeChild%28x%29%3B%0A%7D%0AsetTimeout%28doe2%2C%201000%29%3B%0A%0Afunction%20doe2%28%29%20%7B%0Adocument.documentElement.currentScale%20%3D%202%3B%0A%7D%0A%3C/script%3E%0A%3C/svg%3E"></iframe> +</body> +</html>
\ No newline at end of file diff --git a/dom/svg/crashtests/344888-1.svg b/dom/svg/crashtests/344888-1.svg new file mode 100644 index 0000000000..afbdb4a4f9 --- /dev/null +++ b/dom/svg/crashtests/344888-1.svg @@ -0,0 +1,4 @@ +<svg xmlns="http://www.w3.org/2000/svg"> + +<rect x="0 0 30 40" /> +
</svg>
\ No newline at end of file diff --git a/dom/svg/crashtests/345445-1.svg b/dom/svg/crashtests/345445-1.svg new file mode 100644 index 0000000000..b322b5ac05 --- /dev/null +++ b/dom/svg/crashtests/345445-1.svg @@ -0,0 +1,23 @@ +<svg xmlns="http://www.w3.org/2000/svg" onload="setTimeout(boom, 30);" class="reftest-wait"> + +<script> + +function boom() +{ + var doomed = document.getElementById("doomed"); + doomed.parentNode.removeChild(doomed); + document.documentElement.removeAttribute("class"); +} + +</script> + +<g stroke="url(#grad)"> + + <g id="doomed"> + + <linearGradient id="grad" /> + <rect fill="url(#grad)" /> + </g> +</g> + +</svg>
\ No newline at end of file diff --git a/dom/svg/crashtests/360836-1.svg b/dom/svg/crashtests/360836-1.svg new file mode 100644 index 0000000000..27dae964d0 --- /dev/null +++ b/dom/svg/crashtests/360836-1.svg @@ -0,0 +1,14 @@ +<?xml version="1.0"?> +<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.0//EN" + "http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd"> +<svg xmlns="http://www.w3.org/2000/svg"> + <style type="text/css"> + circle:hover {fill-opacity:0.9;} + </style> + <g style="fill-opacity:0.7;"> + <circle cx="6.5cm" cy="2cm" r="100" style="fill:red; stroke:black; stroke-width:0.1cm" transform="translate(0,50)" /> + <circle cx="6.5cm" cy="2cm" r="100" style="fill:blue; stroke:black; stroke-width:0.1cm" transform="translate(70,150)" /> + <circle cx="6.5cm" cy="2cm" r="100" style="fill:green; stroke:black; stroke-width:0.1cm" transform="translate(-70,150)"/> + </g> + +</svg> diff --git a/dom/svg/crashtests/367357-1.xhtml b/dom/svg/crashtests/367357-1.xhtml new file mode 100644 index 0000000000..207e2daee3 --- /dev/null +++ b/dom/svg/crashtests/367357-1.xhtml @@ -0,0 +1,20 @@ +<html xmlns="http://www.w3.org/1999/xhtml"> +<head> + +<script> +function boom() +{ + document.getElementById("path").pathSegList.pathSegTypeAsLetter; +} +</script> + +</head> + +<body onload="boom()"> + + <svg xmlns="http://www.w3.org/2000/svg" height="400px" width="400px"> + <path id="path" style="stroke: blue" d="M 200.50000,387.89713 L 201.19970,12.500000" /> + </svg> + +</body> +</html> diff --git a/dom/svg/crashtests/369051-1.svg b/dom/svg/crashtests/369051-1.svg new file mode 100644 index 0000000000..82cd3ef2a4 --- /dev/null +++ b/dom/svg/crashtests/369051-1.svg @@ -0,0 +1,9 @@ +<svg xmlns="http://www.w3.org/2000/svg"> + +<text x="240" y="240"> + <svg xmlns="http://www.w3.org/2000/svg"> + <circle xmlns="http://www.w3.org/2000/svg" cx="5cm" cy="5cm" r="50" style="fill: green;"/> + </svg> +</text> + +</svg> diff --git a/dom/svg/crashtests/369249-1.svg b/dom/svg/crashtests/369249-1.svg new file mode 100644 index 0000000000..64d55869b1 --- /dev/null +++ b/dom/svg/crashtests/369249-1.svg @@ -0,0 +1,20 @@ +<svg width='100%' height='100%'
+ xmlns='http://www.w3.org/2000/svg'
+ xmlns:html="http://www.w3.org/1999/xhtml"
+ onload='boom()'>
+
+<html:script>
+
+function boom()
+{
+ try {
+ document.getElementById("path").pathSegList.getItem(-10000000);
+ } catch(e) {
+ }
+}
+
+</html:script>
+
+<path id='path' d='M300,25 C320,250 375,150 400,150 S400,340 330,350'/>
+
+</svg>
diff --git a/dom/svg/crashtests/369291-1.svg b/dom/svg/crashtests/369291-1.svg new file mode 100644 index 0000000000..222d0c2acc --- /dev/null +++ b/dom/svg/crashtests/369291-1.svg @@ -0,0 +1,22 @@ +<svg xmlns="http://www.w3.org/2000/svg"
+ xmlns:html="http://www.w3.org/1999/xhtml"
+ onload="setTimeout(boom, 30);"
+ class="reftest-wait">
+
+<html:script>
+
+function boom()
+{
+ try {
+ document.getElementById("defs").getBBox();
+ } catch(e) {
+ // getBBox is expected to throw, because defs is not displayed.
+ document.documentElement.removeAttribute("class");
+ }
+}
+
+</html:script>
+
+<defs id="defs" />
+
+</svg>
diff --git a/dom/svg/crashtests/369291-2.svg b/dom/svg/crashtests/369291-2.svg new file mode 100644 index 0000000000..097e3dde2d --- /dev/null +++ b/dom/svg/crashtests/369291-2.svg @@ -0,0 +1,19 @@ +<svg xmlns="http://www.w3.org/2000/svg"
+ xmlns:html="http://www.w3.org/1999/xhtml"
+ onload="setTimeout(boom, 30);"
+ class="reftest-wait">
+
+<html:script>
+
+function boom()
+{
+ document.getElementById("rect").getBBox();
+
+ document.documentElement.removeAttribute("class");
+}
+
+</html:script>
+
+<rect id="rect" />
+
+</svg>
diff --git a/dom/svg/crashtests/369568-1.svg b/dom/svg/crashtests/369568-1.svg new file mode 100644 index 0000000000..1d8fd1511d --- /dev/null +++ b/dom/svg/crashtests/369568-1.svg @@ -0,0 +1,20 @@ +<svg xmlns="http://www.w3.org/2000/svg" onload="boom()">
+
+<script>
+function boom()
+{
+ document.getElementById("Gaussian_Blur").href;
+}
+</script>
+
+<defs>
+<filter id="Gaussian_Blur">
+<feGaussianBlur in="SourceGraphic" stdDeviation="3"/>
+</filter>
+</defs>
+
+<ellipse cx="200" cy="150" rx="70" ry="40"
+style="fill:#ff0000;stroke:#000000;
+stroke-width:2;filter:url(#Gaussian_Blur)"/>
+
+</svg>
diff --git a/dom/svg/crashtests/372046-1.svg b/dom/svg/crashtests/372046-1.svg new file mode 100644 index 0000000000..e18660c24c --- /dev/null +++ b/dom/svg/crashtests/372046-1.svg @@ -0,0 +1,17 @@ +<svg xmlns="http://www.w3.org/2000/svg" onload="setTimeout(boom, 30);" class="reftest-wait">
+
+<script>
+function boom()
+{
+ try {
+ document.getElementById("path_1").pathSegList.insertItemBefore({}, 0);
+ } catch(e) {
+ }
+
+ document.documentElement.removeAttribute("class");
+}
+</script>
+
+<path id='path_1' d='M300,25 C500,100 575,300 330,350'/>
+
+</svg> diff --git a/dom/svg/crashtests/372046-2.svg b/dom/svg/crashtests/372046-2.svg new file mode 100644 index 0000000000..e18660c24c --- /dev/null +++ b/dom/svg/crashtests/372046-2.svg @@ -0,0 +1,17 @@ +<svg xmlns="http://www.w3.org/2000/svg" onload="setTimeout(boom, 30);" class="reftest-wait">
+
+<script>
+function boom()
+{
+ try {
+ document.getElementById("path_1").pathSegList.insertItemBefore({}, 0);
+ } catch(e) {
+ }
+
+ document.documentElement.removeAttribute("class");
+}
+</script>
+
+<path id='path_1' d='M300,25 C500,100 575,300 330,350'/>
+
+</svg> diff --git a/dom/svg/crashtests/374882-1.svg b/dom/svg/crashtests/374882-1.svg new file mode 100644 index 0000000000..7a5a2cca9a --- /dev/null +++ b/dom/svg/crashtests/374882-1.svg @@ -0,0 +1,14 @@ +<svg xmlns="http://www.w3.org/2000/svg"> + +<defs> + + <filter id="over"> + <feOffset in="SourceGraphic" dx="100" result="red" /> + <feComposite in2="SourceGraphic" in="red" operator="over" /> + </filter> + +</defs> + +<path d="M 0 0 L 0 50 L 50 0 z" x="300" filter="url(#over)"/> + +</svg> diff --git a/dom/svg/crashtests/380101-1.svg b/dom/svg/crashtests/380101-1.svg new file mode 100644 index 0000000000..cb3aa3e263 --- /dev/null +++ b/dom/svg/crashtests/380101-1.svg @@ -0,0 +1,30 @@ +<svg xmlns:xlink="http://www.w3.org/1999/xlink"
+ xmlns:html="http://www.w3.org/1999/xhtml"
+ xmlns="http://www.w3.org/2000/svg"
+ onload="setTimeout(boom, 30);"
+ class="reftest-wait">
+
+<html:style>
+ .zind { z-index: 0; }
+ .gencon:after { content: counter(chicken); }
+</html:style>
+
+<script>
+function boom()
+{
+ document.getElementById("define").setAttribute("class", "gencon");
+ document.getElementById("use").setAttribute("class", "zind");
+
+ document.documentElement.removeAttribute("class");
+}
+</script>
+
+<defs>
+ <g id="define">
+ <rect x="0" y="0" width="75" height="75" fill="green"/>
+ </g>
+</defs>
+
+<use xlink:href="#define" x="0" y="20" id="use" />
+
+</svg>
diff --git a/dom/svg/crashtests/381777-1.svg b/dom/svg/crashtests/381777-1.svg new file mode 100644 index 0000000000..bc2df73c79 --- /dev/null +++ b/dom/svg/crashtests/381777-1.svg @@ -0,0 +1,96 @@ +<svg xmlns:xlink="http://www.w3.org/1999/xlink" xmlns="http://www.w3.org/2000/svg">
+ <defs>
+
+ <filter id="BlendNormal" >
+ <feOffset in="SourceGraphic" dx="75" dy="0" result="temp"/>
+ <feBlend in2="SourceGraphic" in="temp" mode="normal"/>
+ </filter>
+
+ <filter id="BlendMultiply" >
+ <feOffset in="SourceGraphic" dx="75" dy="0" result="temp"/>
+ <feBlend in2="SourceGraphic" in="temp" mode="multiply"/>
+ </filter>
+
+ <filter id="BlendScreen" >
+ <feOffset in="SourceGraphic" dx="75" dy="0" result="temp"/>
+ <feBlend in2="SourceGraphic" in="temp" mode="screen"/>
+ </filter>
+
+ <filter id="BlendDarken" >
+ <feOffset in="SourceGraphic" dx="75" dy="0" result="temp"/>
+ <feBlend in2="SourceGraphic" in="temp" mode="darken"/>
+ </filter>
+
+ <filter id="BlendLighten" >
+ <feOffset in="SourceGraphic" dx="75" dy="0" result="temp"/>
+ <feBlend in2="SourceGraphic" in="temp" mode="lighten"/>
+ </filter>
+
+ <filter id="noblend" >
+ <feOffset in="SourceGraphic" dx="75" dy="0" result="temp"/>
+ <feMerge>
+ <feMergeNode in="SourceGraphic"/>
+ <feMergeNode in="temp"/>
+ </feMerge>
+ </filter>
+
+ <radialGradient id="grad">
+ <stop offset="0" stop-color="yellow"/>
+ <stop offset="1" stop-color="lightgreen"/>
+ </radialGradient>
+
+ <g id="swatch">
+ <rect x="0" y="0" width="75" height="75" fill="lightblue"/>
+ <rect x="75" y="0" width="75" height="75" fill="url(#grad)"/>
+ </g>
+
+ <g id="swatchHalf">
+ <rect x="0" y="0" width="75" height="75" fill="lightblue" opacity="0.5"/>
+ <rect x="75" y="0" width="75" height="75" fill="url(#grad)" opacity="0.5"/>
+ </g>
+
+ <g id="mask">
+ <rect x="0" y="0" width="75" height="75" fill="white"/>
+ <rect x="150" y="0" width="75" height="75" fill="white"/>
+ </g>
+
+ </defs>
+
+
+ <use xlink:href="#swatch" x="0" y="25" filter="url(#BlendNormal)"/>
+ <use xlink:href="#mask" x="0" y ="25"/>
+ <use xlink:href="#swatchHalf" x="150" y="25" filter="url(#BlendNormal)"/>
+ <use xlink:href="#mask" x="150" y ="25"/>
+ <text x="10" y="50">normal</text>
+
+ <use xlink:href="#swatch" x="0" y="125" filter="url(#BlendMultiply)"/>
+ <use xlink:href="#mask" x="0" y ="125"/>
+ <use xlink:href="#swatchHalf" x="150" y="125" filter="url(#BlendMultiply)"/>
+ <use xlink:href="#mask" x="150" y ="125"/>
+ <text x="10" y="150">multiply</text>
+
+ <use xlink:href="#swatch" x="0" y="225" filter="url(#BlendScreen)"/>
+ <use xlink:href="#mask" x="0" y ="225"/>
+ <use xlink:href="#swatchHalf" x="150" y="225" filter="url(#BlendScreen)"/>
+ <use xlink:href="#mask" x="150" y ="225"/>
+ <text x="10" y="250">screen</text>
+
+ <use xlink:href="#swatch" x="0" y="325" filter="url(#BlendDarken)"/>
+ <use xlink:href="#mask" x="0" y ="325"/>
+ <use xlink:href="#swatchHalf" x="150" y="325" filter="url(#BlendDarken)"/>
+ <use xlink:href="#mask" x="150" y ="325"/>
+ <text x="10" y="350">darken</text>
+
+ <use xlink:href="#swatch" x="0" y="425" filter="url(#BlendLighten)"/>
+ <use xlink:href="#mask" x="0" y ="425"/>
+ <use xlink:href="#swatchHalf" x="150" y="425" filter="url(#BlendLighten)"/>
+ <use xlink:href="#mask" x="150" y ="425"/>
+ <text x="10" y="450">lighten</text>
+
+ <use xlink:href="#swatch" x="0" y="525" filter="url(#noblend)"/>
+ <use xlink:href="#mask" x="0" y ="525"/>
+ <use xlink:href="#swatchHalf" x="150" y="525" filter="url(#noblend)"/>
+ <use xlink:href="#mask" x="150" y ="525"/>
+ <text x="10" y="550">no blend</text>
+
+</svg>
diff --git a/dom/svg/crashtests/383685-1.svg b/dom/svg/crashtests/383685-1.svg new file mode 100644 index 0000000000..3888ef29e6 --- /dev/null +++ b/dom/svg/crashtests/383685-1.svg @@ -0,0 +1,16 @@ +<svg xmlns="http://www.w3.org/2000/svg" onload="setTimeout(boom, 100);"> + +<script> + +function boom() +{ + var gradient = document.getElementById("gradient"); + gradient.spreadMethod.baseVal = undefined; + uneval({p: gradient.attributes}); +} + +</script> + +<linearGradient id="gradient" /> + +</svg> diff --git a/dom/svg/crashtests/385096.html b/dom/svg/crashtests/385096.html new file mode 100644 index 0000000000..88b4e3e630 --- /dev/null +++ b/dom/svg/crashtests/385096.html @@ -0,0 +1,14 @@ +<html>
+<head>
+<script>
+function boom()
+{
+ var SVG_NS = "http://www.w3.org/2000/svg";
+ var feFuncG = document.createElementNS(SVG_NS, "feFuncG");
+ feFuncG.offset.baseVal = 1;
+}
+</script>
+</head>
+<body onload="boom();">
+</body>
+</html>
diff --git a/dom/svg/crashtests/385554-1.html b/dom/svg/crashtests/385554-1.html new file mode 100644 index 0000000000..1b2dce65b0 --- /dev/null +++ b/dom/svg/crashtests/385554-1.html @@ -0,0 +1,6 @@ +<script> +try { + document.createElementNS("http://www.w3.org/2000/svg", "svg").getScreenCTM(); +} catch(e) { +} +</script> diff --git a/dom/svg/crashtests/385554-2.xul b/dom/svg/crashtests/385554-2.xul new file mode 100644 index 0000000000..af40729ebe --- /dev/null +++ b/dom/svg/crashtests/385554-2.xul @@ -0,0 +1,5 @@ +<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> +<svg xmlns="http://www.w3.org/2000/svg"> +<foreignObject/> +</svg> +</window> diff --git a/dom/svg/crashtests/388712-1.svg b/dom/svg/crashtests/388712-1.svg new file mode 100644 index 0000000000..29d5be1d4d --- /dev/null +++ b/dom/svg/crashtests/388712-1.svg @@ -0,0 +1,12 @@ +<svg xmlns="http://www.w3.org/2000/svg" xmlns:html="http://www.w3.org/1999/xhtml" xmlns:xlink="http://www.w3.org/1999/xlink">
+
+ <defs>
+ <g id="s">
+ <rect x="0" y="0" width="75" height="75" fill="lightblue" />
+ <html:form><html:label/></html:form>
+ </g>
+ </defs>
+
+ <use xlink:href="#s" x="150" y="25" />
+
+</svg>
diff --git a/dom/svg/crashtests/395616-1.html b/dom/svg/crashtests/395616-1.html new file mode 100644 index 0000000000..43ca7961b8 --- /dev/null +++ b/dom/svg/crashtests/395616-1.html @@ -0,0 +1,13 @@ +<html> +<head> +<script> +function boom() +{ + var marker = document.createElementNS("http://www.w3.org/2000/svg", "marker"); + marker.orientType.baseVal = ""; +} +</script> +</head> +<body onload="boom();"> +</body> +</html> diff --git a/dom/svg/crashtests/396618-1.html b/dom/svg/crashtests/396618-1.html new file mode 100644 index 0000000000..72c63a0f0b --- /dev/null +++ b/dom/svg/crashtests/396618-1.html @@ -0,0 +1,14 @@ +<html> +<head> +<script> +function boom() +{ + var svgText = document.createElementNS("http://www.w3.org/2000/svg", "text"); + svgText.setAttribute("dx", "foo"); + svgText.removeAttribute("dx"); +} +</script> +</head> +<body onload="boom();"> +</body> +</html> diff --git a/dom/svg/crashtests/397017-1.html b/dom/svg/crashtests/397017-1.html new file mode 100644 index 0000000000..2a794e19e0 --- /dev/null +++ b/dom/svg/crashtests/397017-1.html @@ -0,0 +1,11 @@ +<html> +<head> +<script> +var f = document.createElementNS("http://www.w3.org/2000/svg", "feFuncA"); +f.setAttribute("tableValues", "3 7 5"); +f.removeAttribute("tableValues"); +</script> +</head> +<body> +</body> +</html> diff --git a/dom/svg/crashtests/397551-1.svg b/dom/svg/crashtests/397551-1.svg new file mode 100644 index 0000000000..7d2fc3dfb3 --- /dev/null +++ b/dom/svg/crashtests/397551-1.svg @@ -0,0 +1,7 @@ +<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"> + + <g id="rec"><use xlink:href="#rec" /></g> + + <use xlink:href="#rec" /> + +</svg> diff --git a/dom/svg/crashtests/397704-1.svg b/dom/svg/crashtests/397704-1.svg new file mode 100644 index 0000000000..75f44fd121 --- /dev/null +++ b/dom/svg/crashtests/397704-1.svg @@ -0,0 +1 @@ +<svg xmlns="http://www.w3.org/2000/svg"><marker orient="0" /></svg> diff --git a/dom/svg/crashtests/398926-both-different.svg b/dom/svg/crashtests/398926-both-different.svg new file mode 100644 index 0000000000..e4cfc7e5e6 --- /dev/null +++ b/dom/svg/crashtests/398926-both-different.svg @@ -0,0 +1,8 @@ +<svg xmlns="http://www.w3.org/2000/svg"> + +<pattern id="pat1" /> +<pattern id="pat2" /> + +<rect x="10" y="10" width="100" height="100" stroke="url(#pat1)" fill="url(#pat2)" /> + +</svg> diff --git a/dom/svg/crashtests/398926-both-same.svg b/dom/svg/crashtests/398926-both-same.svg new file mode 100644 index 0000000000..97454241ec --- /dev/null +++ b/dom/svg/crashtests/398926-both-same.svg @@ -0,0 +1,7 @@ +<svg xmlns="http://www.w3.org/2000/svg"> + +<pattern id="pat" /> + +<rect x="10" y="10" width="100" height="100" stroke="url(#pat)" fill="url(#pat)" /> + +</svg> diff --git a/dom/svg/crashtests/398926-fill.svg b/dom/svg/crashtests/398926-fill.svg new file mode 100644 index 0000000000..d259f1cdbe --- /dev/null +++ b/dom/svg/crashtests/398926-fill.svg @@ -0,0 +1,7 @@ +<svg xmlns="http://www.w3.org/2000/svg"> + +<pattern id="pat" /> + +<rect x="10" y="10" width="100" height="100" fill="url(#pat)" /> + +</svg> diff --git a/dom/svg/crashtests/398926-stroke.svg b/dom/svg/crashtests/398926-stroke.svg new file mode 100644 index 0000000000..60e7725c45 --- /dev/null +++ b/dom/svg/crashtests/398926-stroke.svg @@ -0,0 +1,7 @@ +<svg xmlns="http://www.w3.org/2000/svg"> + +<pattern id="pat" /> + +<rect x="10" y="10" width="100" height="100" stroke="url(#pat)" /> + +</svg> diff --git a/dom/svg/crashtests/405639-1.svg b/dom/svg/crashtests/405639-1.svg new file mode 100644 index 0000000000..242eff9aca --- /dev/null +++ b/dom/svg/crashtests/405639-1.svg @@ -0,0 +1,19 @@ +<svg xmlns="http://www.w3.org/2000/svg"
+ xmlns:xlink="http://www.w3.org/1999/xlink"
+ onload="boom();">
+
+<script type="text/javascript">
+
+function boom() {
+ var greensquare = document.getElementById("greensquare");
+ var use = document.getElementById("use");
+ greensquare.appendChild(use);
+}
+
+</script>
+
+<rect id="greensquare" x="10" y="10" width="10" height="10" fill="green" />
+
+<use xlink:href="#greensquare" id="use" />
+
+</svg>
diff --git a/dom/svg/crashtests/406361-1.html b/dom/svg/crashtests/406361-1.html new file mode 100644 index 0000000000..813ed7d825 --- /dev/null +++ b/dom/svg/crashtests/406361-1.html @@ -0,0 +1,13 @@ +<html> +<head> +<script> +function boom() +{ + var use = document.createElementNS("http://www.w3.org/2000/svg", "use"); + use.patternTransform = ""; +} +</script> +</head> +<body onload="boom();"> +</body> +</html>
\ No newline at end of file diff --git a/dom/svg/crashtests/409811-1.html b/dom/svg/crashtests/409811-1.html new file mode 100644 index 0000000000..28a064e4d1 --- /dev/null +++ b/dom/svg/crashtests/409811-1.html @@ -0,0 +1,15 @@ +<html> +<head> +<script type="text/javascript"> + +function boom() +{ + var marker = document.createElementNS("http://www.w3.org/2000/svg", "marker"); + marker.orientType.baseVal = ""; +} + +</script> +</head> + +<body onload="boom();"></body> +</html> diff --git a/dom/svg/crashtests/410659-1.svg b/dom/svg/crashtests/410659-1.svg new file mode 100644 index 0000000000..55bc90c58d --- /dev/null +++ b/dom/svg/crashtests/410659-1.svg @@ -0,0 +1,19 @@ +<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"> + + <filter id="filter1"> + <feImage x="0" + y="0" + width="5%" + height="20%" + result="raster1" + xlink:href="../../../../../testing/crashtest/images/tree.gif" /> + + </filter> + + <rect x="0" + y="0" + width="100%" + height="100%" + filter="url(#filter1)" /> + +</svg> diff --git a/dom/svg/crashtests/410659-2.svg b/dom/svg/crashtests/410659-2.svg new file mode 100644 index 0000000000..d54b8900f4 --- /dev/null +++ b/dom/svg/crashtests/410659-2.svg @@ -0,0 +1,19 @@ +<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"> + + <filter id="filter1"> + <feImage x="0" + y="0" + width="5%" + height="20%" + result="raster1" /> + + + </filter> + + <rect x="0" + y="0" + width="100%" + height="100%" + filter="url(#filter1)" /> + +</svg> diff --git a/dom/svg/crashtests/410659-3.svg b/dom/svg/crashtests/410659-3.svg new file mode 100644 index 0000000000..df971a0ec7 --- /dev/null +++ b/dom/svg/crashtests/410659-3.svg @@ -0,0 +1,19 @@ +<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"> + + <filter id="filter1"> + <feImage x="0" + y="0" + width="5%" + height="20%" + result="raster1" + xlink:href="../../../../../testing/crashtest/images/tree.gif" /> + <feTile /> + </filter> + + <rect x="0" + y="0" + width="100%" + height="100%" + filter="url(#filter1)" /> + +</svg> diff --git a/dom/svg/crashtests/412104-1.svg b/dom/svg/crashtests/412104-1.svg new file mode 100644 index 0000000000..154664a5af --- /dev/null +++ b/dom/svg/crashtests/412104-1.svg @@ -0,0 +1,9 @@ +<svg xmlns="http://www.w3.org/2000/svg" + xmlns:xbl="http://www.mozilla.org/xbl" + onload="document.documentElement.style.MozBinding = 'url(#tsw)';"> + +<xbl:bindings><xbl:binding id="tsw"><xbl:content> +<text stroke-width="50%"><xbl:children/></text> +</xbl:content></xbl:binding></xbl:bindings> + +</svg> diff --git a/dom/svg/crashtests/413174-1.svg b/dom/svg/crashtests/413174-1.svg new file mode 100644 index 0000000000..1b1bdf0b93 --- /dev/null +++ b/dom/svg/crashtests/413174-1.svg @@ -0,0 +1,25 @@ +<svg xmlns="http://www.w3.org/2000/svg" + xmlns:xlink="http://www.w3.org/1999/xlink" + onload="boom();"> + + <script> + +function boom() +{ + var g = document.getElementById("g"); + var use = document.getElementById("use"); + + g.appendChild(use); + document.documentElement.appendChild(use); + document.documentElement.appendChild(g); +} + + </script> + + <g id="g" style="counter-reset: c;"> + <rect width="1" height="1"/> + </g> + + <use id="use" xlink:href="#g"/> + +</svg> diff --git a/dom/svg/crashtests/414188-1.svg b/dom/svg/crashtests/414188-1.svg new file mode 100644 index 0000000000..9894975a6e --- /dev/null +++ b/dom/svg/crashtests/414188-1.svg @@ -0,0 +1,10 @@ +<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"> + +<filter id="filter1"> + <feImage width="0"/> + <feTile/> +</filter> + +<rect width="100%" height="100%" filter="url(#filter1)"/> + +</svg> diff --git a/dom/svg/crashtests/427325-1.svg b/dom/svg/crashtests/427325-1.svg new file mode 100644 index 0000000000..1711fe8cf2 --- /dev/null +++ b/dom/svg/crashtests/427325-1.svg @@ -0,0 +1,11 @@ +<svg xmlns="http://www.w3.org/2000/svg"> + + <filter id="f"> + <feGaussianBlur stdDeviation="0.1"/> + </filter> + + <g filter="url(#f)"> + <rect width="100" height="100" fill="lime"/> + </g> + +</svg> diff --git a/dom/svg/crashtests/428228-1.svg b/dom/svg/crashtests/428228-1.svg new file mode 100644 index 0000000000..fb4fd8bb45 --- /dev/null +++ b/dom/svg/crashtests/428228-1.svg @@ -0,0 +1,17 @@ +<html xmlns="http://www.w3.org/1999/xhtml" xmlns:svg="http://www.w3.org/2000/svg">
+<head>
+<script type="text/javascript">
+
+// Test that removing a direct child element of an SVG <svg> doesnt' crash:
+
+function boom()
+{
+ document.getElementById("s").removeChild(document.getElementById("r"));
+}
+
+</script>
+</head>
+
+<body onload="boom();">ۀ<svg:svg id="s"><svg:rect id="r"/></svg:svg></body>
+
+</html>
diff --git a/dom/svg/crashtests/428841-1.svg b/dom/svg/crashtests/428841-1.svg new file mode 100644 index 0000000000..77234aba9d --- /dev/null +++ b/dom/svg/crashtests/428841-1.svg @@ -0,0 +1,13 @@ +<svg xmlns="http://www.w3.org/2000/svg" onload="boom();">
+
+<script type="text/javascript">
+
+function boom()
+{
+ var path = document.createElementNS("http://www.w3.org/2000/svg", "path");
+ path.getPathSegAtLength(1);
+}
+
+</script>
+
+</svg>
diff --git a/dom/svg/crashtests/435209-1.svg b/dom/svg/crashtests/435209-1.svg new file mode 100644 index 0000000000..d674d50ed6 --- /dev/null +++ b/dom/svg/crashtests/435209-1.svg @@ -0,0 +1,11 @@ +<svg xmlns="http://www.w3.org/2000/svg"> +<path/> + +<script xmlns="http://www.w3.org/1999/xhtml"><![CDATA[ +var x=document.getElementsByTagName('path')[0]; +var y = x.pathSegList; +var z = x.createSVGPathSegMovetoRel(0,0); +y.appendItem(z,0); +y.replaceItem(z, 0); +]]></script> +</svg>
\ No newline at end of file diff --git a/dom/svg/crashtests/436418-mpathRoot-1.svg b/dom/svg/crashtests/436418-mpathRoot-1.svg new file mode 100644 index 0000000000..c1fcef780f --- /dev/null +++ b/dom/svg/crashtests/436418-mpathRoot-1.svg @@ -0,0 +1,4 @@ +<svg:mpath xmlns:svg="http://www.w3.org/2000/svg" + xmlns:xlink="http://www.w3.org/1999/xlink" + xlink:href="#foo"> +</svg:mpath> diff --git a/dom/svg/crashtests/448244-1.svg b/dom/svg/crashtests/448244-1.svg new file mode 100644 index 0000000000..f36c73e8ad --- /dev/null +++ b/dom/svg/crashtests/448244-1.svg @@ -0,0 +1,9 @@ +<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"> + +<filter id="filter1"> + <feTile/> +</filter> + +<rect width="100%" height="100%" filter="url(#filter1)"/> + +</svg> diff --git a/dom/svg/crashtests/466576-1.xhtml b/dom/svg/crashtests/466576-1.xhtml new file mode 100644 index 0000000000..a27d42f574 --- /dev/null +++ b/dom/svg/crashtests/466576-1.xhtml @@ -0,0 +1,22 @@ +<html xmlns="http://www.w3.org/1999/xhtml"> +<head> +<script type="text/javascript"> + +function boom() +{ + try { + document.getElementById("y").gradientTransform.baseVal.appendItem(function(){}); + } catch(e) { } + + document.documentElement.innerHTML; +} + +</script> +</head> + +<body onload="boom();"> + +<linearGradient id="y" xmlns="http://www.w3.org/2000/svg" /> + +</body> +</html> diff --git a/dom/svg/crashtests/499879-1.svg b/dom/svg/crashtests/499879-1.svg new file mode 100644 index 0000000000..b063d29dd6 --- /dev/null +++ b/dom/svg/crashtests/499879-1.svg @@ -0,0 +1 @@ +<svg xmlns="http://www.w3.org/2000/svg" onload="document.getElementById('b').tableValues.baseVal.initialize({});"><feFuncB id="b"/></svg> diff --git a/dom/svg/crashtests/535691-1.svg b/dom/svg/crashtests/535691-1.svg new file mode 100644 index 0000000000..8e5e4647ab --- /dev/null +++ b/dom/svg/crashtests/535691-1.svg @@ -0,0 +1 @@ +<svg xmlns="http://www.w3.org/2000/svg"><g transform="10 20"><animateTransform attributeName="transform"/></g></svg> diff --git a/dom/svg/crashtests/539167-1.svg b/dom/svg/crashtests/539167-1.svg new file mode 100644 index 0000000000..960ee57778 --- /dev/null +++ b/dom/svg/crashtests/539167-1.svg @@ -0,0 +1,6 @@ +<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"> + <circle id="circle" transform=""> + <animateTransform attributeName="transform" from="0" to="0.6"/> + </circle> + <use xlink:href="#circle"/> +</svg> diff --git a/dom/svg/crashtests/573316-1.svg b/dom/svg/crashtests/573316-1.svg new file mode 100644 index 0000000000..5410a983fd --- /dev/null +++ b/dom/svg/crashtests/573316-1.svg @@ -0,0 +1,3 @@ +<svg xmlns="http://www.w3.org/2000/svg"> +<text dx="㾫"/> +</svg> diff --git a/dom/svg/crashtests/579356-1.svg b/dom/svg/crashtests/579356-1.svg new file mode 100644 index 0000000000..eb9a8b049d --- /dev/null +++ b/dom/svg/crashtests/579356-1.svg @@ -0,0 +1,9 @@ +<svg xmlns="http://www.w3.org/2000/svg">
+ <animateMotion dur="1" begin="0" path="
+ M 0 0
+ C 0,0 0,0
+ 501.208526, 390.4
+ 501.208543, 390.4
+ 0,0 0,0
+ "/>
+</svg>
diff --git a/dom/svg/crashtests/579356-2.svg b/dom/svg/crashtests/579356-2.svg new file mode 100644 index 0000000000..870601c221 --- /dev/null +++ b/dom/svg/crashtests/579356-2.svg @@ -0,0 +1,13 @@ +<?xml version="1.0" standalone="no"?> +<svg xmlns:xlink="http://www.w3.org/1999/xlink" + xmlns="http://www.w3.org/2000/svg"> + <path d="M 0 0 + C 0,0 0,0 + 501.208526, 390.4 + 501.208543, 390.4 + 0,0 0,0 0,0 0,0 0,0" stroke="black" id="p"/> + <script> + var path = document.getElementById("p"); + var segNum = path.getPathSegAtLength(1200); + </script> +</svg> diff --git a/dom/svg/crashtests/595608-1.svg b/dom/svg/crashtests/595608-1.svg new file mode 100644 index 0000000000..f36e4381fe --- /dev/null +++ b/dom/svg/crashtests/595608-1.svg @@ -0,0 +1 @@ +<svg xmlns="http://www.w3.org/2000/svg"><rect><set to="k" type="s" attributeName="transform"/></rect></svg> diff --git a/dom/svg/crashtests/601251-1.html b/dom/svg/crashtests/601251-1.html new file mode 100644 index 0000000000..eb22dff408 --- /dev/null +++ b/dom/svg/crashtests/601251-1.html @@ -0,0 +1,8 @@ +<!DOCTYPE html> +<script> + +var cm = document.createElementNS("http://www.w3.org/2000/svg", "feConvolveMatrix"); +cm.kernelMatrix.baseVal.appendItem({}); +cm.setAttribute("kernelMatrix", "0 0 0 0 0 0 0 0 0"); + +</script> diff --git a/dom/svg/crashtests/601406-1.svg b/dom/svg/crashtests/601406-1.svg new file mode 100644 index 0000000000..3f5914a7cb --- /dev/null +++ b/dom/svg/crashtests/601406-1.svg @@ -0,0 +1,12 @@ +<svg xmlns="http://www.w3.org/2000/svg"> +<script> +<![CDATA[ +function boom() { document.getElementById("a").getBBox(); } +window.addEventListener("load", boom, false); +]]> +</script> + +<a id="a"/> + +</svg> + diff --git a/dom/svg/crashtests/603145-1.svg b/dom/svg/crashtests/603145-1.svg new file mode 100644 index 0000000000..1e95e3dc77 --- /dev/null +++ b/dom/svg/crashtests/603145-1.svg @@ -0,0 +1,11 @@ +<svg xmlns="http://www.w3.org/2000/svg"> +<script> +<![CDATA[ + +var q = document.createElementNS("http://www.w3.org/2000/svg", "text").rotate.baseVal.appendItem({}); +q.__proto__ = document.createTextNode("x"); +q.constructor; + +]]> +</script> +</svg> diff --git a/dom/svg/crashtests/613899-1.svg b/dom/svg/crashtests/613899-1.svg new file mode 100644 index 0000000000..581537703e --- /dev/null +++ b/dom/svg/crashtests/613899-1.svg @@ -0,0 +1,9 @@ +<svg xmlns='http://www.w3.org/2000/svg' + xmlns:xlink='http://www.w3.org/1999/xlink' + width='16' height='16'> + <image id='i' xlink:href='a.png' width='16' height='16'/> + <filter id="f" x="0%" y="0%" width="100%" height="100%"> + <feImage xlink:href="#image1" /> + </filter> + <rect filter="url(#f)" x="0" y="0" width="16" height="16"/> +</svg> diff --git a/dom/svg/crashtests/613899-2.svg b/dom/svg/crashtests/613899-2.svg new file mode 100644 index 0000000000..f88be69394 --- /dev/null +++ b/dom/svg/crashtests/613899-2.svg @@ -0,0 +1,7 @@ +<svg xmlns="http://www.w3.org/2000/svg" + xmlns:xlink="http://www.w3.org/1999/xlink"> + <set/> + <filter id="disp"> + <feImage xlink:href="#THIS_SVG_FILE_AS_AN_IMAGE"/> + </filter> +</svg> diff --git a/dom/svg/crashtests/719779-1.svg b/dom/svg/crashtests/719779-1.svg new file mode 100644 index 0000000000..3e9ac742f3 --- /dev/null +++ b/dom/svg/crashtests/719779-1.svg @@ -0,0 +1,19 @@ +<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="80" y="80" width="300" height="180" viewBox="0 0 200 120"> + + <defs> + <filter id="MyFilter" filterUnits="userSpaceOnUse" > + <feGaussianBlur /> + <feOffset /> + <feSpecularLighting > + <fePointLight /> + </feSpecularLighting> + <feComposite /> + <feMerge> + <feMergeNode /> + <feMergeNode /> + </feMerge> + </filter> + </defs> + <g transform="matrix(100,-2562303,3000,100,300,300) " filter="url(#MyFilter)"/> + +</svg> diff --git a/dom/svg/crashtests/723441-1.html b/dom/svg/crashtests/723441-1.html new file mode 100644 index 0000000000..959e9fa953 --- /dev/null +++ b/dom/svg/crashtests/723441-1.html @@ -0,0 +1,22 @@ +<!DOCTYPE html> +<html class="reftest-wait"> + <body> + <img id="x"> + <script> + var xhr = new XMLHttpRequest(); + xhr.open("GET", "723441-resource.svg"); + xhr.overrideMimeType( 'text/plain; charset=x-user-defined' ); + xhr.onreadystatechange = function() { + if (xhr.readyState == 4) { + var svg = xhr.responseText.replace(/#/g, '%23'); + var img = document.getElementById("x"); + img.onload = function() { + document.documentElement.className = ""; + } + img.src = "data:image/svg+xml;," + svg; + } + } + xhr.send(); + </script> + </body> +</html> diff --git a/dom/svg/crashtests/723441-resource.svg b/dom/svg/crashtests/723441-resource.svg new file mode 100644 index 0000000000..69830d856a --- /dev/null +++ b/dom/svg/crashtests/723441-resource.svg @@ -0,0 +1,4922 @@ +<?xml version="1.0" encoding="UTF-8"?> +<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="612pt" height="792pt" viewBox="0 0 612 792" version="1.1"> +<defs> +<g> +<symbol overflow="visible" id="glyph0-0"> +<path style="stroke:none;" d="M 4.375 -10.671875 L 4.375 -2.0625 C 4.375 -0.609375 4.1875 -0.40625 2.75 -0.328125 L 2.75 0 L 7.78125 0 L 7.78125 -0.328125 C 6.375 -0.390625 6.125 -0.625 6.125 -1.875 L 6.125 -10.671875 L 7.0625 -10.671875 C 9.015625 -10.671875 9.40625 -10.359375 9.796875 -8.46875 L 10.203125 -8.46875 L 10.109375 -11.40625 L 0.390625 -11.40625 L 0.296875 -8.46875 L 0.703125 -8.46875 C 1.125 -10.34375 1.515625 -10.671875 3.4375 -10.671875 Z M 4.375 -10.671875 "/> +</symbol> +<symbol overflow="visible" id="glyph0-1"> +<path style="stroke:none;" d="M 2.703125 -5.90625 C 3.421875 -6.703125 3.9375 -6.984375 4.609375 -6.984375 C 5.46875 -6.984375 5.90625 -6.375 5.90625 -5.171875 L 5.90625 -1.75 C 5.90625 -0.578125 5.734375 -0.359375 4.734375 -0.265625 L 4.734375 0 L 8.390625 0 L 8.390625 -0.265625 C 7.453125 -0.4375 7.359375 -0.5625 7.359375 -1.75 L 7.359375 -5.1875 C 7.359375 -6.984375 6.625 -7.921875 5.234375 -7.921875 C 4.21875 -7.921875 3.5 -7.5 2.703125 -6.46875 L 2.703125 -11.703125 L 2.625 -11.765625 C 2.03125 -11.546875 1.59375 -11.421875 0.640625 -11.140625 L 0.171875 -11 L 0.171875 -10.734375 C 0.234375 -10.75 0.296875 -10.75 0.375 -10.75 C 1.125 -10.75 1.25 -10.609375 1.25 -9.859375 L 1.25 -1.75 C 1.25 -0.546875 1.15625 -0.390625 0.15625 -0.265625 L 0.15625 0 L 3.875 0 L 3.875 -0.265625 C 2.875 -0.359375 2.703125 -0.5625 2.703125 -1.75 Z M 2.703125 -5.90625 "/> +</symbol> +<symbol overflow="visible" id="glyph0-2"> +<path style="stroke:none;" d="M 7.03125 -2.828125 C 6.203125 -1.515625 5.453125 -1.015625 4.359375 -1.015625 C 3.375 -1.015625 2.640625 -1.515625 2.140625 -2.5 C 1.828125 -3.15625 1.703125 -3.71875 1.671875 -4.765625 L 6.96875 -4.765625 C 6.828125 -5.890625 6.65625 -6.390625 6.234375 -6.9375 C 5.71875 -7.5625 4.921875 -7.921875 4.03125 -7.921875 C 3.171875 -7.921875 2.359375 -7.609375 1.703125 -7.03125 C 0.890625 -6.3125 0.4375 -5.09375 0.4375 -3.6875 C 0.4375 -1.3125 1.671875 0.171875 3.65625 0.171875 C 5.28125 0.171875 6.578125 -0.84375 7.296875 -2.703125 Z M 1.703125 -5.328125 C 1.890625 -6.65625 2.484375 -7.296875 3.53125 -7.296875 C 4.578125 -7.296875 5 -6.8125 5.21875 -5.328125 Z M 1.703125 -5.328125 "/> +</symbol> +<symbol overflow="visible" id="glyph0-3"> +<path style="stroke:none;" d="M 11.609375 -9.859375 L 11.609375 -2.0625 C 11.609375 -0.640625 11.40625 -0.40625 10.03125 -0.328125 L 10.03125 0 L 14.859375 0 L 14.859375 -0.328125 C 13.609375 -0.40625 13.359375 -0.65625 13.359375 -1.875 L 13.359375 -9.515625 C 13.359375 -10.75 13.578125 -10.96875 14.859375 -11.078125 L 14.859375 -11.40625 L 11.4375 -11.40625 L 7.625 -2.703125 L 3.65625 -11.40625 L 0.234375 -11.40625 L 0.234375 -11.078125 C 1.65625 -10.984375 1.875 -10.78125 1.875 -9.515625 L 1.875 -2.53125 C 1.875 -0.75 1.640625 -0.4375 0.203125 -0.328125 L 0.203125 0 L 4.25 0 L 4.25 -0.328125 C 2.921875 -0.390625 2.640625 -0.796875 2.640625 -2.53125 L 2.640625 -9.46875 L 6.953125 0 L 7.203125 0 Z M 11.609375 -9.859375 "/> +</symbol> +<symbol overflow="visible" id="glyph0-4"> +<path style="stroke:none;" d="M 8.25 -0.859375 L 8.15625 -0.859375 C 7.375 -0.859375 7.1875 -1.046875 7.1875 -1.84375 L 7.1875 -7.75 L 4.453125 -7.75 L 4.453125 -7.453125 C 5.53125 -7.40625 5.734375 -7.234375 5.734375 -6.375 L 5.734375 -2.328125 C 5.734375 -1.84375 5.640625 -1.59375 5.40625 -1.40625 C 4.9375 -1.03125 4.40625 -0.828125 3.890625 -0.828125 C 3.21875 -0.828125 2.671875 -1.40625 2.671875 -2.140625 L 2.671875 -7.75 L 0.15625 -7.75 L 0.15625 -7.5 C 0.984375 -7.453125 1.21875 -7.203125 1.21875 -6.40625 L 1.21875 -2.0625 C 1.21875 -0.703125 2.046875 0.171875 3.3125 0.171875 C 3.9375 0.171875 4.609375 -0.109375 5.078125 -0.5625 L 5.8125 -1.3125 L 5.8125 0.125 L 5.890625 0.15625 C 6.75 -0.1875 7.375 -0.375 8.25 -0.625 Z M 8.25 -0.859375 "/> +</symbol> +<symbol overflow="visible" id="glyph0-5"> +<path style="stroke:none;" d="M 0.328125 -10.734375 L 0.4375 -10.734375 C 0.625 -10.75 0.828125 -10.765625 0.96875 -10.765625 C 1.515625 -10.765625 1.6875 -10.515625 1.6875 -9.71875 L 1.6875 -1.5 C 1.6875 -0.5625 1.453125 -0.34375 0.359375 -0.265625 L 0.359375 0 L 4.421875 0 L 4.421875 -0.265625 C 3.34375 -0.328125 3.140625 -0.5 3.140625 -1.453125 L 3.140625 -11.71875 L 3.0625 -11.765625 C 2.171875 -11.46875 1.515625 -11.296875 0.328125 -11 Z M 0.328125 -10.734375 "/> +</symbol> +<symbol overflow="visible" id="glyph0-6"> +<path style="stroke:none;" d="M 4.390625 -7.75 L 2.65625 -7.75 L 2.65625 -9.75 C 2.65625 -9.921875 2.640625 -9.96875 2.53125 -9.96875 C 2.40625 -9.8125 2.3125 -9.65625 2.1875 -9.484375 C 1.53125 -8.546875 0.796875 -7.71875 0.515625 -7.640625 C 0.328125 -7.53125 0.21875 -7.40625 0.21875 -7.3125 C 0.21875 -7.265625 0.234375 -7.234375 0.296875 -7.203125 L 1.203125 -7.203125 L 1.203125 -2.015625 C 1.203125 -0.5625 1.71875 0.171875 2.734375 0.171875 C 3.578125 0.171875 4.234375 -0.234375 4.796875 -1.140625 L 4.578125 -1.328125 C 4.21875 -0.890625 3.921875 -0.71875 3.546875 -0.71875 C 2.90625 -0.71875 2.65625 -1.1875 2.65625 -2.265625 L 2.65625 -7.203125 L 4.390625 -7.203125 Z M 4.390625 -7.75 "/> +</symbol> +<symbol overflow="visible" id="glyph0-7"> +<path style="stroke:none;" d="M 3.015625 -7.921875 L 0.34375 -6.96875 L 0.34375 -6.71875 L 0.484375 -6.734375 C 0.6875 -6.765625 0.90625 -6.78125 1.0625 -6.78125 C 1.484375 -6.78125 1.640625 -6.515625 1.640625 -5.75 L 1.640625 -1.75 C 1.640625 -0.515625 1.46875 -0.328125 0.28125 -0.265625 L 0.28125 0 L 4.359375 0 L 4.359375 -0.265625 C 3.21875 -0.34375 3.078125 -0.515625 3.078125 -1.75 L 3.078125 -7.875 Z M 2.203125 -11.765625 C 1.734375 -11.765625 1.34375 -11.359375 1.34375 -10.875 C 1.34375 -10.40625 1.71875 -10 2.203125 -10 C 2.703125 -10 3.09375 -10.390625 3.09375 -10.875 C 3.09375 -11.359375 2.703125 -11.765625 2.203125 -11.765625 Z M 2.203125 -11.765625 "/> +</symbol> +<symbol overflow="visible" id="glyph0-8"> +<path style="stroke:none;" d="M 0.671875 -4.421875 L 0.671875 -3.34375 L 4.90625 -3.34375 L 4.90625 -4.421875 Z M 0.671875 -4.421875 "/> +</symbol> +<symbol overflow="visible" id="glyph0-9"> +<path style="stroke:none;" d="M 3.484375 -5.015625 C 3.921875 -4.96875 4.21875 -4.953125 4.671875 -4.953125 C 6.015625 -4.953125 6.9375 -5.125 7.671875 -5.546875 C 8.71875 -6.09375 9.328125 -7.140625 9.328125 -8.28125 C 9.328125 -9 9.09375 -9.65625 8.625 -10.15625 C 7.9375 -10.921875 6.4375 -11.40625 4.828125 -11.40625 L 0.28125 -11.40625 L 0.28125 -11.078125 C 1.546875 -10.9375 1.71875 -10.765625 1.71875 -9.515625 L 1.71875 -2.0625 C 1.71875 -0.625 1.578125 -0.453125 0.28125 -0.328125 L 0.28125 0 L 5.09375 0 L 5.09375 -0.328125 C 3.734375 -0.375 3.484375 -0.625 3.484375 -1.875 Z M 3.484375 -10.171875 C 3.484375 -10.640625 3.59375 -10.765625 4.0625 -10.765625 C 6.390625 -10.765625 7.453125 -9.953125 7.453125 -8.171875 C 7.453125 -6.515625 6.4375 -5.640625 4.453125 -5.640625 C 4.109375 -5.640625 3.875 -5.671875 3.484375 -5.703125 Z M 3.484375 -10.171875 "/> +</symbol> +<symbol overflow="visible" id="glyph0-10"> +<path style="stroke:none;" d="M 0.125 -6.71875 C 0.359375 -6.765625 0.515625 -6.78125 0.71875 -6.78125 C 1.15625 -6.78125 1.3125 -6.515625 1.3125 -5.75 L 1.3125 -1.453125 C 1.3125 -0.578125 1.1875 -0.46875 0.09375 -0.265625 L 0.09375 0 L 4.21875 0 L 4.21875 -0.265625 C 3.046875 -0.3125 2.75 -0.5625 2.75 -1.546875 L 2.75 -5.421875 C 2.75 -5.96875 3.5 -6.828125 3.953125 -6.828125 C 4.0625 -6.828125 4.21875 -6.75 4.40625 -6.578125 C 4.6875 -6.34375 4.875 -6.234375 5.09375 -6.234375 C 5.515625 -6.234375 5.765625 -6.53125 5.765625 -7.015625 C 5.765625 -7.578125 5.40625 -7.921875 4.828125 -7.921875 C 4.09375 -7.921875 3.59375 -7.53125 2.75 -6.296875 L 2.75 -7.890625 L 2.671875 -7.921875 C 1.75 -7.546875 1.140625 -7.3125 0.125 -6.984375 Z M 0.125 -6.71875 "/> +</symbol> +<symbol overflow="visible" id="glyph0-11"> +<path style="stroke:none;" d="M 0.28125 -6.859375 C 0.375 -6.90625 0.546875 -6.921875 0.734375 -6.921875 C 1.21875 -6.921875 1.375 -6.65625 1.375 -5.8125 L 1.375 -1.546875 C 1.375 -0.5625 1.1875 -0.328125 0.3125 -0.265625 L 0.3125 0 L 3.953125 0 L 3.953125 -0.265625 C 3.078125 -0.328125 2.828125 -0.53125 2.828125 -1.15625 L 2.828125 -5.984375 C 3.65625 -6.765625 4.03125 -6.96875 4.59375 -6.96875 C 5.4375 -6.96875 5.859375 -6.4375 5.859375 -5.296875 L 5.859375 -1.703125 C 5.859375 -0.625 5.625 -0.328125 4.765625 -0.265625 L 4.765625 0 L 8.34375 0 L 8.34375 -0.265625 C 7.5 -0.34375 7.296875 -0.546875 7.296875 -1.390625 L 7.296875 -5.34375 C 7.296875 -6.953125 6.546875 -7.921875 5.265625 -7.921875 C 4.484375 -7.921875 3.9375 -7.625 2.765625 -6.53125 L 2.765625 -7.890625 L 2.65625 -7.921875 C 1.8125 -7.609375 1.21875 -7.421875 0.28125 -7.140625 Z M 0.28125 -6.859375 "/> +</symbol> +<symbol overflow="visible" id="glyph0-12"> +<path style="stroke:none;" d="M 6.859375 -2.6875 C 6.03125 -1.484375 5.40625 -1.0625 4.421875 -1.0625 C 2.859375 -1.0625 1.75 -2.4375 1.75 -4.421875 C 1.75 -6.203125 2.703125 -7.421875 4.09375 -7.421875 C 4.71875 -7.421875 4.9375 -7.234375 5.109375 -6.59375 L 5.21875 -6.21875 C 5.359375 -5.734375 5.671875 -5.421875 6.03125 -5.421875 C 6.46875 -5.421875 6.859375 -5.75 6.859375 -6.140625 C 6.859375 -7.109375 5.640625 -7.921875 4.203125 -7.921875 C 3.359375 -7.921875 2.484375 -7.578125 1.78125 -6.953125 C 0.90625 -6.203125 0.4375 -5.03125 0.4375 -3.671875 C 0.4375 -1.421875 1.796875 0.171875 3.703125 0.171875 C 4.484375 0.171875 5.171875 -0.109375 5.78125 -0.640625 C 6.25 -1.046875 6.578125 -1.515625 7.09375 -2.53125 Z M 6.859375 -2.6875 "/> +</symbol> +<symbol overflow="visible" id="glyph0-13"> +<path style="stroke:none;" d="M 0.15625 -6.765625 C 0.3125 -6.78125 0.4375 -6.78125 0.578125 -6.78125 C 1.171875 -6.78125 1.296875 -6.609375 1.296875 -5.796875 L 1.296875 2.25 C 1.296875 3.15625 1.109375 3.34375 0.09375 3.4375 L 0.09375 3.734375 L 4.25 3.734375 L 4.25 3.421875 C 2.96875 3.40625 2.734375 3.21875 2.734375 2.140625 L 2.734375 -0.5625 C 3.34375 0 3.75 0.171875 4.484375 0.171875 C 6.515625 0.171875 8.09375 -1.75 8.09375 -4.25 C 8.09375 -6.390625 6.890625 -7.921875 5.21875 -7.921875 C 4.25 -7.921875 3.5 -7.484375 2.734375 -6.5625 L 2.734375 -7.890625 L 2.640625 -7.921875 C 1.703125 -7.5625 1.109375 -7.328125 0.15625 -7.046875 Z M 2.734375 -5.75 C 2.734375 -6.265625 3.703125 -6.890625 4.5 -6.890625 C 5.765625 -6.890625 6.609375 -5.578125 6.609375 -3.578125 C 6.609375 -1.671875 5.765625 -0.375 4.53125 -0.375 C 3.71875 -0.375 2.734375 -1 2.734375 -1.515625 Z M 2.734375 -5.75 "/> +</symbol> +<symbol overflow="visible" id="glyph0-14"> +<path style="stroke:none;" d="M 7.609375 -1.140625 C 7.3125 -0.890625 7.109375 -0.8125 6.859375 -0.8125 C 6.453125 -0.8125 6.34375 -1.046875 6.34375 -1.8125 L 6.34375 -5.171875 C 6.34375 -6.0625 6.25 -6.5625 5.984375 -6.96875 C 5.609375 -7.59375 4.875 -7.921875 3.859375 -7.921875 C 2.234375 -7.921875 0.96875 -7.078125 0.96875 -5.984375 C 0.96875 -5.59375 1.3125 -5.25 1.703125 -5.25 C 2.125 -5.25 2.484375 -5.59375 2.484375 -5.96875 C 2.484375 -6.046875 2.46875 -6.125 2.4375 -6.25 C 2.40625 -6.40625 2.390625 -6.546875 2.390625 -6.65625 C 2.390625 -7.125 2.9375 -7.5 3.640625 -7.5 C 4.484375 -7.5 4.9375 -7.015625 4.9375 -6.078125 L 4.9375 -5.03125 C 2.296875 -3.953125 2 -3.828125 1.25 -3.171875 C 0.875 -2.828125 0.640625 -2.234375 0.640625 -1.671875 C 0.640625 -0.578125 1.390625 0.171875 2.4375 0.171875 C 3.203125 0.171875 3.90625 -0.1875 4.953125 -1.078125 C 5.046875 -0.1875 5.359375 0.171875 6.0625 0.171875 C 6.640625 0.171875 7.015625 -0.03125 7.609375 -0.6875 Z M 4.9375 -2.125 C 4.9375 -1.578125 4.859375 -1.421875 4.5 -1.21875 C 4.078125 -0.984375 3.59375 -0.828125 3.234375 -0.828125 C 2.640625 -0.828125 2.15625 -1.40625 2.15625 -2.15625 L 2.15625 -2.21875 C 2.15625 -3.234375 2.859375 -3.859375 4.9375 -4.609375 Z M 4.9375 -2.125 "/> +</symbol> +<symbol overflow="visible" id="glyph0-15"> +<path style="stroke:none;" d="M 6.21875 -11.640625 C 2.921875 -11.640625 0.578125 -9.15625 0.578125 -5.703125 C 0.578125 -4.078125 1.140625 -2.515625 2.0625 -1.515625 C 3.0625 -0.4375 4.59375 0.234375 6.109375 0.234375 C 9.5 0.234375 11.84375 -2.171875 11.84375 -5.625 C 11.84375 -7.328125 11.34375 -8.796875 10.40625 -9.828125 C 9.3125 -11.015625 7.875 -11.640625 6.21875 -11.640625 Z M 6.21875 -11.015625 C 7.015625 -11.015625 7.796875 -10.703125 8.421875 -10.15625 C 9.34375 -9.3125 9.890625 -7.703125 9.890625 -5.640625 C 9.890625 -4.625 9.65625 -3.4375 9.3125 -2.546875 C 9.15625 -2.125 8.875 -1.6875 8.46875 -1.296875 C 7.875 -0.6875 7.09375 -0.375 6.1875 -0.375 C 5.390625 -0.375 4.609375 -0.6875 4.015625 -1.21875 C 3.109375 -2.015625 2.546875 -3.75 2.546875 -5.671875 C 2.546875 -7.421875 3.03125 -9.09375 3.75 -9.90625 C 4.421875 -10.640625 5.265625 -11.015625 6.21875 -11.015625 Z M 6.21875 -11.015625 "/> +</symbol> +<symbol overflow="visible" id="glyph0-16"> +<path style="stroke:none;" d="M 7.703125 -11.640625 L 7.328125 -11.640625 C 7.265625 -11.25 7.078125 -11.046875 6.78125 -11.046875 C 6.609375 -11.046875 6.3125 -11.125 6.03125 -11.265625 C 5.390625 -11.5 4.75 -11.640625 4.203125 -11.640625 C 3.46875 -11.640625 2.703125 -11.34375 2.140625 -10.84375 C 1.53125 -10.3125 1.21875 -9.59375 1.21875 -8.703125 C 1.21875 -7.3125 1.984375 -6.359375 3.90625 -5.34375 C 5.140625 -4.6875 6.046875 -3.984375 6.46875 -3.328125 C 6.625 -3.09375 6.71875 -2.734375 6.71875 -2.328125 C 6.71875 -1.171875 5.859375 -0.375 4.59375 -0.375 C 3.0625 -0.375 1.984375 -1.328125 1.125 -3.421875 L 0.71875 -3.421875 L 1.234375 0.21875 L 1.625 0.21875 C 1.640625 -0.109375 1.84375 -0.34375 2.09375 -0.34375 C 2.296875 -0.34375 2.578125 -0.28125 2.90625 -0.15625 C 3.5625 0.109375 4.25 0.234375 4.9375 0.234375 C 6.921875 0.234375 8.453125 -1.125 8.453125 -2.890625 C 8.453125 -4.3125 7.5 -5.421875 5.234375 -6.640625 C 3.421875 -7.640625 2.703125 -8.40625 2.703125 -9.328125 C 2.703125 -10.28125 3.421875 -10.9375 4.5 -10.9375 C 5.265625 -10.9375 5.984375 -10.609375 6.59375 -9.984375 C 7.125 -9.4375 7.375 -8.984375 7.640625 -7.96875 L 8.078125 -7.96875 Z M 7.703125 -11.640625 "/> +</symbol> +<symbol overflow="visible" id="glyph0-17"> +<path style="stroke:none;" d="M 10.671875 -7.75 L 10.515625 -11.640625 L 10.15625 -11.640625 C 10.0625 -11.28125 9.78125 -11.078125 9.4375 -11.078125 C 9.28125 -11.078125 9.015625 -11.125 8.765625 -11.234375 C 7.921875 -11.5 7.0625 -11.640625 6.25 -11.640625 C 4.84375 -11.640625 3.40625 -11.109375 2.34375 -10.15625 C 1.140625 -9.09375 0.484375 -7.484375 0.484375 -5.59375 C 0.484375 -4 1 -2.515625 1.875 -1.53125 C 2.90625 -0.40625 4.5 0.234375 6.203125 0.234375 C 8.140625 0.234375 9.84375 -0.546875 10.90625 -1.953125 L 10.59375 -2.25 C 9.3125 -1.03125 8.171875 -0.515625 6.75 -0.515625 C 5.671875 -0.515625 4.703125 -0.859375 3.953125 -1.515625 C 3.015625 -2.359375 2.484375 -3.90625 2.484375 -5.8125 C 2.484375 -8.9375 4.078125 -10.953125 6.578125 -10.953125 C 7.5625 -10.953125 8.453125 -10.59375 9.140625 -9.90625 C 9.6875 -9.34375 9.953125 -8.875 10.28125 -7.75 Z M 10.671875 -7.75 "/> +</symbol> +<symbol overflow="visible" id="glyph0-18"> +<path style="stroke:none;" d="M 4.3125 -7.921875 C 2.0625 -7.921875 0.5 -6.265625 0.5 -3.890625 C 0.5 -1.5625 2.09375 0.171875 4.265625 0.171875 C 6.4375 0.171875 8.09375 -1.65625 8.09375 -4.03125 C 8.09375 -6.28125 6.515625 -7.921875 4.3125 -7.921875 Z M 4.078125 -7.4375 C 5.53125 -7.4375 6.546875 -5.78125 6.546875 -3.421875 C 6.546875 -1.484375 5.765625 -0.3125 4.484375 -0.3125 C 3.8125 -0.3125 3.171875 -0.71875 2.8125 -1.40625 C 2.328125 -2.3125 2.046875 -3.515625 2.046875 -4.734375 C 2.046875 -6.375 2.859375 -7.4375 4.078125 -7.4375 Z M 4.078125 -7.4375 "/> +</symbol> +<symbol overflow="visible" id="glyph0-19"> +<path style="stroke:none;" d="M 5.421875 -5.40625 L 5.359375 -7.75 L 5.171875 -7.75 L 5.125 -7.71875 C 4.96875 -7.59375 4.953125 -7.578125 4.890625 -7.578125 C 4.78125 -7.578125 4.609375 -7.609375 4.421875 -7.703125 C 4.046875 -7.828125 3.671875 -7.90625 3.21875 -7.90625 C 1.859375 -7.90625 0.875 -7.03125 0.875 -5.78125 C 0.875 -4.828125 1.421875 -4.125 2.890625 -3.3125 L 3.890625 -2.734375 C 4.5 -2.390625 4.78125 -1.984375 4.78125 -1.453125 C 4.78125 -0.6875 4.234375 -0.203125 3.359375 -0.203125 C 2.765625 -0.203125 2.234375 -0.4375 1.90625 -0.8125 C 1.546875 -1.234375 1.390625 -1.640625 1.171875 -2.625 L 0.890625 -2.625 L 0.890625 0.0625 L 1.125 0.0625 C 1.234375 -0.109375 1.3125 -0.140625 1.515625 -0.140625 C 1.671875 -0.140625 1.90625 -0.109375 2.3125 0 C 2.796875 0.109375 3.25 0.171875 3.5625 0.171875 C 4.890625 0.171875 5.984375 -0.828125 5.984375 -2.03125 C 5.984375 -2.890625 5.578125 -3.46875 4.546875 -4.078125 L 2.6875 -5.1875 C 2.203125 -5.453125 1.953125 -5.890625 1.953125 -6.359375 C 1.953125 -7.046875 2.484375 -7.53125 3.265625 -7.53125 C 4.25 -7.53125 4.765625 -6.9375 5.171875 -5.40625 Z M 5.421875 -5.40625 "/> +</symbol> +<symbol overflow="visible" id="glyph0-20"> +<path style="stroke:none;" d="M 5.328125 -7.75 L 3.203125 -7.75 L 3.203125 -9.75 C 3.203125 -10.75 3.53125 -11.28125 4.1875 -11.28125 C 4.546875 -11.28125 4.78125 -11.109375 5.09375 -10.609375 C 5.375 -10.15625 5.578125 -9.984375 5.875 -9.984375 C 6.265625 -9.984375 6.59375 -10.296875 6.59375 -10.6875 C 6.59375 -11.3125 5.84375 -11.765625 4.796875 -11.765625 C 3.734375 -11.765625 2.828125 -11.296875 2.375 -10.5 C 1.921875 -9.75 1.796875 -9.125 1.78125 -7.75 L 0.359375 -7.75 L 0.359375 -7.203125 L 1.78125 -7.203125 L 1.78125 -1.796875 C 1.78125 -0.53125 1.578125 -0.328125 0.34375 -0.265625 L 0.34375 0 L 4.828125 0 L 4.828125 -0.265625 C 3.40625 -0.3125 3.21875 -0.5 3.21875 -1.796875 L 3.21875 -7.203125 L 5.328125 -7.203125 Z M 5.328125 -7.75 "/> +</symbol> +<symbol overflow="visible" id="glyph0-21"> +<path style="stroke:none;" d="M 12.203125 -6.09375 L 7.8125 -6.09375 L 7.8125 -5.78125 C 8.5625 -5.734375 8.765625 -5.6875 8.96875 -5.546875 C 9.234375 -5.390625 9.328125 -5 9.328125 -4.25 L 9.328125 -1.46875 C 9.328125 -0.90625 8.28125 -0.453125 7.046875 -0.453125 C 4.234375 -0.453125 2.515625 -2.40625 2.515625 -5.609375 C 2.515625 -7.234375 3 -8.796875 3.75 -9.640625 C 4.515625 -10.484375 5.578125 -10.953125 6.75 -10.953125 C 7.71875 -10.953125 8.5625 -10.625 9.234375 -10 C 9.75 -9.515625 10.015625 -9.0625 10.453125 -8 L 10.84375 -8 L 10.703125 -11.640625 L 10.328125 -11.640625 C 10.234375 -11.3125 9.921875 -11.078125 9.5625 -11.078125 C 9.390625 -11.078125 9.109375 -11.125 8.796875 -11.25 C 8.03125 -11.5 7.234375 -11.640625 6.453125 -11.640625 C 3.015625 -11.640625 0.546875 -9.109375 0.546875 -5.59375 C 0.546875 -3.90625 1.015625 -2.625 1.984375 -1.59375 C 3.140625 -0.40625 4.78125 0.234375 6.6875 0.234375 C 8.15625 0.234375 10.3125 -0.359375 11 -0.96875 L 11 -4.453125 C 11 -5.46875 11.203125 -5.703125 12.203125 -5.78125 Z M 12.203125 -6.09375 "/> +</symbol> +<symbol overflow="visible" id="glyph0-22"> +<path style="stroke:none;" d="M 7.203125 -2.328125 L 6.890625 -2.390625 C 6.71875 -1.53125 6.609375 -1.25 6.390625 -0.96875 C 6.140625 -0.671875 5.5625 -0.515625 4.6875 -0.515625 L 2.3125 -0.515625 L 6.9375 -7.484375 L 6.9375 -7.75 L 0.96875 -7.75 L 0.90625 -5.71875 L 1.21875 -5.71875 C 1.375 -6.96875 1.640625 -7.234375 2.671875 -7.234375 L 5.046875 -7.234375 L 0.46875 -0.265625 L 0.46875 0 L 6.953125 0 Z M 7.203125 -2.328125 "/> +</symbol> +<symbol overflow="visible" id="glyph0-23"> +<path style="stroke:none;" d="M 16.046875 -11.40625 L 12.640625 -11.40625 L 12.640625 -11.078125 C 13.515625 -10.984375 13.828125 -10.796875 13.828125 -10.328125 C 13.828125 -9.96875 13.71875 -9.515625 13.546875 -9.046875 L 11.40625 -3.203125 L 9.125 -9.078125 C 9.09375 -9.15625 8.9375 -9.546875 8.921875 -9.59375 C 8.765625 -9.953125 8.65625 -10.28125 8.65625 -10.46875 C 8.65625 -10.875 9.0625 -11.046875 9.984375 -11.078125 L 9.984375 -11.40625 L 5.390625 -11.40625 L 5.390625 -11.078125 C 6.375 -11.046875 6.546875 -10.90625 7.125 -9.515625 L 7.703125 -8.109375 L 5.859375 -3.25 L 3.375 -9.734375 C 3.25 -10.03125 3.1875 -10.328125 3.1875 -10.515625 C 3.1875 -10.90625 3.421875 -11.015625 4.3125 -11.078125 L 4.3125 -11.40625 L 0.09375 -11.40625 L 0.09375 -11.078125 C 0.96875 -10.984375 1.21875 -10.671875 1.859375 -9.0625 L 5.1875 0.1875 L 5.4375 0.1875 L 8.09375 -7.09375 L 10.84375 0.1875 L 11.109375 0.1875 C 12.5625 -4.3125 12.734375 -4.78125 14.625 -9.84375 C 14.953125 -10.75 15.140625 -10.90625 16.046875 -11.078125 Z M 16.046875 -11.40625 "/> +</symbol> +<symbol overflow="visible" id="glyph0-24"> +<path style="stroke:none;" d="M 2.640625 -11.71875 L 2.546875 -11.765625 C 1.828125 -11.5 1.359375 -11.359375 0.546875 -11.140625 L 0.046875 -11 L 0.046875 -10.734375 C 0.15625 -10.75 0.21875 -10.75 0.34375 -10.75 C 1.03125 -10.75 1.1875 -10.59375 1.1875 -9.859375 L 1.1875 -0.9375 C 1.1875 -0.390625 2.65625 0.171875 4.03125 0.171875 C 6.296875 0.171875 8.0625 -1.71875 8.0625 -4.1875 C 8.0625 -6.296875 6.75 -7.921875 5.03125 -7.921875 C 3.984375 -7.921875 2.984375 -7.296875 2.640625 -6.453125 Z M 2.640625 -5.546875 C 2.640625 -6.21875 3.4375 -6.828125 4.34375 -6.828125 C 5.6875 -6.828125 6.546875 -5.5 6.546875 -3.390625 C 6.546875 -1.484375 5.71875 -0.375 4.3125 -0.375 C 3.40625 -0.375 2.640625 -0.78125 2.640625 -1.203125 Z M 2.640625 -5.546875 "/> +</symbol> +<symbol overflow="visible" id="glyph0-25"> +<path style="stroke:none;" d="M 0.296875 -11.40625 L 0.296875 -11.078125 C 1.734375 -10.984375 1.953125 -10.796875 1.953125 -9.515625 L 1.953125 -1.875 C 1.953125 -0.609375 1.71875 -0.375 0.296875 -0.328125 L 0.296875 0 L 6.046875 0 C 7.390625 0 8.609375 -0.359375 9.25 -0.953125 C 9.859375 -1.5 10.203125 -2.265625 10.203125 -3.09375 C 10.203125 -3.859375 9.90625 -4.546875 9.359375 -5.046875 C 8.84375 -5.515625 8.390625 -5.71875 7.265625 -5.984375 C 8.15625 -6.21875 8.515625 -6.390625 8.9375 -6.75 C 9.359375 -7.125 9.625 -7.765625 9.625 -8.46875 C 9.625 -10.40625 8.09375 -11.40625 5.109375 -11.40625 Z M 3.703125 -5.609375 C 5.375 -5.609375 6.171875 -5.53125 6.78125 -5.265625 C 7.765625 -4.859375 8.234375 -4.171875 8.234375 -3.078125 C 8.234375 -2.15625 7.875 -1.484375 7.1875 -1.078125 C 6.625 -0.78125 5.921875 -0.640625 4.78125 -0.640625 C 3.9375 -0.640625 3.703125 -0.796875 3.703125 -1.34375 Z M 3.703125 -6.296875 L 3.703125 -10.25 C 3.703125 -10.609375 3.828125 -10.765625 4.078125 -10.765625 L 4.84375 -10.765625 C 6.8125 -10.765625 7.875 -9.9375 7.875 -8.40625 C 7.875 -7.0625 6.953125 -6.296875 5.34375 -6.296875 Z M 3.703125 -6.296875 "/> +</symbol> +<symbol overflow="visible" id="glyph0-26"> +<path style="stroke:none;" d="M 9.828125 -7.75 L 9.828125 -7.484375 C 10.421875 -7.375 10.59375 -7.25 10.59375 -6.953125 C 10.59375 -6.703125 10.484375 -6.28125 10.296875 -5.8125 L 8.75 -2 L 7.296875 -5.859375 C 7.015625 -6.640625 7.015625 -6.640625 7.015625 -6.875 C 7.015625 -7.25 7.203125 -7.375 8 -7.484375 L 8 -7.75 L 4.515625 -7.75 L 4.515625 -7.484375 C 5.140625 -7.421875 5.359375 -7.234375 5.703125 -6.296875 C 5.8125 -5.96875 5.9375 -5.640625 6.046875 -5.34375 L 4.484375 -1.90625 L 2.765625 -6.40625 C 2.703125 -6.578125 2.671875 -6.75 2.671875 -6.921875 C 2.671875 -7.28125 2.875 -7.421875 3.46875 -7.484375 L 3.46875 -7.75 L 0.359375 -7.75 L 0.359375 -7.484375 C 0.75 -7.453125 0.890625 -7.28125 1.28125 -6.40625 L 3.59375 -0.515625 C 3.8125 0 3.9375 0.234375 4.046875 0.234375 C 4.125 0.234375 4.265625 0.015625 4.484375 -0.4375 L 6.40625 -4.5625 L 7.96875 -0.5 C 8.21875 0.140625 8.28125 0.234375 8.390625 0.234375 C 8.5 0.234375 8.59375 0.09375 8.875 -0.609375 L 11.25 -6.5625 C 11.546875 -7.28125 11.609375 -7.375 11.953125 -7.484375 L 11.953125 -7.75 Z M 9.828125 -7.75 "/> +</symbol> +<symbol overflow="visible" id="glyph1-0"> +<path style="stroke:none;" d="M 2.5 -4.296875 L 2.5 -6.609375 C 2.5 -7.46875 2.625 -7.609375 3.546875 -7.6875 L 3.546875 -7.90625 L 0.234375 -7.90625 L 0.234375 -7.6875 C 1.140625 -7.609375 1.28125 -7.46875 1.28125 -6.609375 L 1.28125 -1.4375 C 1.28125 -0.4375 1.15625 -0.28125 0.234375 -0.234375 L 0.234375 0 L 3.546875 0 L 3.546875 -0.234375 C 2.65625 -0.296875 2.5 -0.453125 2.5 -1.296875 L 2.5 -3.765625 L 6.125 -3.765625 L 6.125 -1.4375 C 6.125 -0.4375 6 -0.28125 5.0625 -0.234375 L 5.0625 0 L 8.390625 0 L 8.390625 -0.234375 C 7.5 -0.296875 7.34375 -0.453125 7.34375 -1.296875 L 7.34375 -6.609375 C 7.34375 -7.46875 7.46875 -7.609375 8.390625 -7.6875 L 8.390625 -7.90625 L 5.0625 -7.90625 L 5.0625 -7.6875 C 5.984375 -7.609375 6.125 -7.46875 6.125 -6.609375 L 6.125 -4.296875 Z M 2.5 -4.296875 "/> +</symbol> +<symbol overflow="visible" id="glyph1-1"> +<path style="stroke:none;" d="M 4.875 -1.953125 C 4.296875 -1.046875 3.796875 -0.703125 3.03125 -0.703125 C 2.34375 -0.703125 1.828125 -1.046875 1.484375 -1.734375 C 1.265625 -2.1875 1.1875 -2.578125 1.15625 -3.3125 L 4.84375 -3.3125 C 4.75 -4.09375 4.625 -4.4375 4.328125 -4.8125 C 3.96875 -5.25 3.421875 -5.5 2.796875 -5.5 C 2.203125 -5.5 1.640625 -5.28125 1.1875 -4.875 C 0.625 -4.390625 0.296875 -3.53125 0.296875 -2.5625 C 0.296875 -0.90625 1.15625 0.125 2.53125 0.125 C 3.671875 0.125 4.5625 -0.578125 5.0625 -1.875 Z M 1.1875 -3.6875 C 1.3125 -4.625 1.71875 -5.0625 2.453125 -5.0625 C 3.171875 -5.0625 3.46875 -4.734375 3.625 -3.6875 Z M 1.1875 -3.6875 "/> +</symbol> +<symbol overflow="visible" id="glyph1-2"> +<path style="stroke:none;" d="M 0.234375 -7.453125 L 0.296875 -7.453125 C 0.4375 -7.453125 0.578125 -7.46875 0.671875 -7.46875 C 1.046875 -7.46875 1.171875 -7.296875 1.171875 -6.734375 L 1.171875 -1.046875 C 1.171875 -0.390625 1 -0.234375 0.25 -0.171875 L 0.25 0 L 3.078125 0 L 3.078125 -0.171875 C 2.3125 -0.234375 2.171875 -0.34375 2.171875 -1 L 2.171875 -8.140625 L 2.125 -8.15625 C 1.5 -7.953125 1.046875 -7.84375 0.234375 -7.640625 Z M 0.234375 -7.453125 "/> +</symbol> +<symbol overflow="visible" id="glyph1-3"> +<path style="stroke:none;" d="M 0.1875 -4.75 C 0.265625 -4.796875 0.375 -4.8125 0.515625 -4.8125 C 0.84375 -4.8125 0.953125 -4.625 0.953125 -4.046875 L 0.953125 -1.078125 C 0.953125 -0.390625 0.828125 -0.234375 0.21875 -0.171875 L 0.21875 0 L 2.75 0 L 2.75 -0.171875 C 2.140625 -0.234375 1.953125 -0.375 1.953125 -0.796875 L 1.953125 -4.15625 C 2.53125 -4.703125 2.796875 -4.84375 3.1875 -4.84375 C 3.78125 -4.84375 4.0625 -4.46875 4.0625 -3.6875 L 4.0625 -1.1875 C 4.0625 -0.4375 3.90625 -0.234375 3.3125 -0.171875 L 3.3125 0 L 5.796875 0 L 5.796875 -0.171875 C 5.21875 -0.234375 5.0625 -0.375 5.0625 -0.96875 L 5.0625 -3.703125 C 5.0625 -4.828125 4.546875 -5.5 3.65625 -5.5 C 3.109375 -5.5 2.734375 -5.296875 1.921875 -4.53125 L 1.921875 -5.46875 L 1.84375 -5.5 C 1.25 -5.28125 0.84375 -5.15625 0.1875 -4.953125 Z M 0.1875 -4.75 "/> +</symbol> +<symbol overflow="visible" id="glyph1-4"> +<path style="stroke:none;" d="M 3.328125 -6.609375 C 3.328125 -7.484375 3.453125 -7.609375 4.421875 -7.6875 L 4.421875 -7.90625 L 0.984375 -7.90625 L 0.984375 -7.6875 C 1.96875 -7.609375 2.109375 -7.484375 2.109375 -6.609375 L 2.109375 -1.078125 C 2.109375 -0.53125 1.953125 -0.28125 1.65625 -0.28125 C 1.484375 -0.28125 1.40625 -0.375 1.328125 -0.6875 C 1.203125 -1.09375 1.015625 -1.296875 0.703125 -1.296875 C 0.375 -1.296875 0.125 -1.015625 0.125 -0.6875 C 0.125 -0.1875 0.609375 0.171875 1.296875 0.171875 C 2.59375 0.171875 3.328125 -0.6875 3.328125 -2.1875 Z M 3.328125 -6.609375 "/> +</symbol> +<symbol overflow="visible" id="glyph1-5"> +<path style="stroke:none;" d="M 1.5 -1.1875 C 1.140625 -1.1875 0.84375 -0.890625 0.84375 -0.515625 C 0.84375 -0.171875 1.140625 0.125 1.484375 0.125 C 1.859375 0.125 2.15625 -0.171875 2.15625 -0.515625 C 2.15625 -0.890625 1.859375 -1.1875 1.5 -1.1875 Z M 1.5 -1.1875 "/> +</symbol> +<symbol overflow="visible" id="glyph1-6"> +<path style="stroke:none;" d="M 11.140625 -7.90625 L 8.78125 -7.90625 L 8.78125 -7.6875 C 9.390625 -7.625 9.59375 -7.5 9.59375 -7.171875 C 9.59375 -6.921875 9.53125 -6.609375 9.40625 -6.28125 L 7.90625 -2.21875 L 6.328125 -6.296875 C 6.3125 -6.359375 6.203125 -6.625 6.1875 -6.65625 C 6.078125 -6.90625 6.015625 -7.140625 6.015625 -7.265625 C 6.015625 -7.546875 6.28125 -7.671875 6.9375 -7.6875 L 6.9375 -7.90625 L 3.734375 -7.90625 L 3.734375 -7.6875 C 4.421875 -7.671875 4.546875 -7.5625 4.953125 -6.609375 L 5.34375 -5.625 L 4.0625 -2.265625 L 2.34375 -6.75 C 2.265625 -6.96875 2.21875 -7.171875 2.21875 -7.296875 C 2.21875 -7.5625 2.375 -7.65625 2.984375 -7.6875 L 2.984375 -7.90625 L 0.0625 -7.90625 L 0.0625 -7.6875 C 0.671875 -7.625 0.84375 -7.40625 1.296875 -6.28125 L 3.59375 0.125 L 3.78125 0.125 L 5.625 -4.921875 L 7.53125 0.125 L 7.703125 0.125 C 8.71875 -2.984375 8.84375 -3.328125 10.140625 -6.84375 C 10.375 -7.453125 10.5 -7.5625 11.140625 -7.6875 Z M 11.140625 -7.90625 "/> +</symbol> +<symbol overflow="visible" id="glyph1-7"> +<path style="stroke:none;" d="M 5.28125 -0.78125 C 5.078125 -0.625 4.9375 -0.5625 4.75 -0.5625 C 4.484375 -0.5625 4.40625 -0.734375 4.40625 -1.25 L 4.40625 -3.578125 C 4.40625 -4.203125 4.34375 -4.546875 4.15625 -4.84375 C 3.890625 -5.265625 3.375 -5.5 2.671875 -5.5 C 1.546875 -5.5 0.671875 -4.90625 0.671875 -4.15625 C 0.671875 -3.890625 0.90625 -3.640625 1.1875 -3.640625 C 1.46875 -3.640625 1.71875 -3.890625 1.71875 -4.140625 C 1.71875 -4.203125 1.703125 -4.25 1.703125 -4.34375 C 1.671875 -4.453125 1.65625 -4.546875 1.65625 -4.625 C 1.65625 -4.953125 2.046875 -5.21875 2.515625 -5.21875 C 3.109375 -5.21875 3.4375 -4.859375 3.4375 -4.21875 L 3.4375 -3.484375 C 1.59375 -2.75 1.390625 -2.65625 0.875 -2.203125 C 0.609375 -1.953125 0.4375 -1.546875 0.4375 -1.15625 C 0.4375 -0.40625 0.96875 0.125 1.703125 0.125 C 2.21875 0.125 2.71875 -0.125 3.4375 -0.75 C 3.5 -0.125 3.71875 0.125 4.203125 0.125 C 4.609375 0.125 4.859375 -0.03125 5.28125 -0.484375 Z M 3.4375 -1.46875 C 3.4375 -1.09375 3.375 -0.984375 3.125 -0.84375 C 2.828125 -0.6875 2.5 -0.578125 2.25 -0.578125 C 1.828125 -0.578125 1.5 -0.984375 1.5 -1.5 L 1.5 -1.546875 C 1.5 -2.25 1.984375 -2.671875 3.4375 -3.203125 Z M 3.4375 -1.46875 "/> +</symbol> +<symbol overflow="visible" id="glyph1-8"> +<path style="stroke:none;" d="M 5.625 -4.640625 L 5.625 -5.109375 L 4.703125 -5.109375 C 4.453125 -5.109375 4.28125 -5.140625 4.046875 -5.21875 L 3.78125 -5.3125 C 3.453125 -5.4375 3.125 -5.5 2.828125 -5.5 C 1.703125 -5.5 0.828125 -4.640625 0.828125 -3.546875 C 0.828125 -2.796875 1.140625 -2.34375 1.9375 -1.953125 C 1.703125 -1.734375 1.5 -1.53125 1.421875 -1.46875 C 1.03125 -1.125 0.875 -0.890625 0.875 -0.640625 C 0.875 -0.390625 1.015625 -0.25 1.5 -0.015625 C 0.65625 0.609375 0.328125 1 0.328125 1.453125 C 0.328125 2.078125 1.265625 2.609375 2.40625 2.609375 C 3.296875 2.609375 4.234375 2.296875 4.859375 1.796875 C 5.3125 1.421875 5.515625 1.046875 5.515625 0.578125 C 5.515625 -0.15625 4.953125 -0.65625 4.0625 -0.6875 L 2.515625 -0.765625 C 1.890625 -0.78125 1.59375 -0.890625 1.59375 -1.09375 C 1.59375 -1.328125 1.984375 -1.75 2.3125 -1.84375 C 2.421875 -1.828125 2.5 -1.8125 2.53125 -1.8125 C 2.765625 -1.796875 2.921875 -1.78125 2.984375 -1.78125 C 3.4375 -1.78125 3.90625 -1.953125 4.28125 -2.28125 C 4.671875 -2.625 4.859375 -3.03125 4.859375 -3.640625 C 4.859375 -3.984375 4.796875 -4.25 4.625 -4.640625 Z M 1.75 0.03125 C 2.15625 0.109375 3.109375 0.171875 3.6875 0.171875 C 4.78125 0.171875 5.171875 0.328125 5.171875 0.765625 C 5.171875 1.453125 4.265625 1.921875 2.921875 1.921875 C 1.859375 1.921875 1.171875 1.578125 1.171875 1.046875 C 1.171875 0.78125 1.25 0.625 1.75 0.03125 Z M 1.8125 -4.046875 C 1.8125 -4.75 2.15625 -5.15625 2.703125 -5.15625 C 3.078125 -5.15625 3.375 -4.953125 3.578125 -4.609375 C 3.796875 -4.1875 3.9375 -3.640625 3.9375 -3.171875 C 3.9375 -2.5 3.578125 -2.078125 3.03125 -2.078125 C 2.3125 -2.078125 1.8125 -2.859375 1.8125 -4 Z M 1.8125 -4.046875 "/> +</symbol> +<symbol overflow="visible" id="glyph1-9"> +<path style="stroke:none;" d="M 0.984375 1.6875 C 1.796875 1.296875 2.328125 0.5625 2.328125 -0.15625 C 2.328125 -0.75 1.90625 -1.21875 1.375 -1.21875 C 0.953125 -1.21875 0.671875 -0.9375 0.671875 -0.53125 C 0.671875 -0.15625 0.9375 0.078125 1.375 0.078125 C 1.453125 0.078125 1.53125 0.0625 1.609375 0.046875 C 1.6875 0.03125 1.6875 0.03125 1.703125 0.03125 C 1.796875 0.03125 1.859375 0.09375 1.859375 0.1875 C 1.859375 0.578125 1.53125 1.015625 0.890625 1.453125 Z M 0.984375 1.6875 "/> +</symbol> +<symbol overflow="visible" id="glyph1-10"> +<path style="stroke:none;" d="M 7.40625 -5.375 L 7.296875 -8.078125 L 7.046875 -8.078125 C 6.984375 -7.828125 6.796875 -7.6875 6.546875 -7.6875 C 6.4375 -7.6875 6.265625 -7.71875 6.078125 -7.796875 C 5.5 -7.984375 4.90625 -8.078125 4.34375 -8.078125 C 3.359375 -8.078125 2.359375 -7.703125 1.625 -7.046875 C 0.78125 -6.3125 0.328125 -5.203125 0.328125 -3.890625 C 0.328125 -2.765625 0.6875 -1.75 1.296875 -1.0625 C 2.015625 -0.28125 3.125 0.171875 4.296875 0.171875 C 5.65625 0.171875 6.84375 -0.375 7.5625 -1.34375 L 7.34375 -1.5625 C 6.46875 -0.71875 5.671875 -0.359375 4.6875 -0.359375 C 3.9375 -0.359375 3.265625 -0.59375 2.75 -1.046875 C 2.09375 -1.640625 1.71875 -2.71875 1.71875 -4.046875 C 1.71875 -6.203125 2.828125 -7.609375 4.5625 -7.609375 C 5.25 -7.609375 5.875 -7.34375 6.34375 -6.875 C 6.734375 -6.484375 6.90625 -6.15625 7.140625 -5.375 Z M 7.40625 -5.375 "/> +</symbol> +<symbol overflow="visible" id="glyph1-11"> +<path style="stroke:none;" d="M 1.875 -4.09375 C 2.375 -4.65625 2.734375 -4.859375 3.203125 -4.859375 C 3.796875 -4.859375 4.09375 -4.421875 4.09375 -3.578125 L 4.09375 -1.21875 C 4.09375 -0.40625 3.984375 -0.25 3.28125 -0.171875 L 3.28125 0 L 5.828125 0 L 5.828125 -0.171875 C 5.171875 -0.296875 5.109375 -0.390625 5.109375 -1.21875 L 5.109375 -3.59375 C 5.109375 -4.859375 4.609375 -5.5 3.640625 -5.5 C 2.921875 -5.5 2.421875 -5.21875 1.875 -4.5 L 1.875 -8.125 L 1.8125 -8.15625 C 1.40625 -8.015625 1.109375 -7.921875 0.4375 -7.734375 L 0.125 -7.640625 L 0.125 -7.453125 C 0.171875 -7.453125 0.203125 -7.453125 0.265625 -7.453125 C 0.78125 -7.453125 0.875 -7.359375 0.875 -6.84375 L 0.875 -1.21875 C 0.875 -0.375 0.796875 -0.28125 0.109375 -0.171875 L 0.109375 0 L 2.6875 0 L 2.6875 -0.171875 C 2 -0.25 1.875 -0.390625 1.875 -1.21875 Z M 1.875 -4.09375 "/> +</symbol> +<symbol overflow="visible" id="glyph1-12"> +<path style="stroke:none;" d="M 0.078125 -4.65625 C 0.25 -4.703125 0.359375 -4.703125 0.5 -4.703125 C 0.796875 -4.703125 0.90625 -4.515625 0.90625 -4 L 0.90625 -1 C 0.90625 -0.40625 0.828125 -0.328125 0.0625 -0.171875 L 0.0625 0 L 2.921875 0 L 2.921875 -0.171875 C 2.109375 -0.21875 1.90625 -0.390625 1.90625 -1.078125 L 1.90625 -3.765625 C 1.90625 -4.140625 2.421875 -4.75 2.75 -4.75 C 2.828125 -4.75 2.921875 -4.6875 3.0625 -4.5625 C 3.25 -4.40625 3.375 -4.328125 3.53125 -4.328125 C 3.828125 -4.328125 4 -4.53125 4 -4.859375 C 4 -5.265625 3.75 -5.5 3.34375 -5.5 C 2.84375 -5.5 2.5 -5.21875 1.90625 -4.375 L 1.90625 -5.46875 L 1.859375 -5.5 C 1.21875 -5.234375 0.78125 -5.078125 0.078125 -4.859375 Z M 0.078125 -4.65625 "/> +</symbol> +<symbol overflow="visible" id="glyph1-13"> +<path style="stroke:none;" d="M 2.09375 -5.5 L 0.234375 -4.84375 L 0.234375 -4.65625 L 0.328125 -4.671875 C 0.484375 -4.703125 0.640625 -4.703125 0.734375 -4.703125 C 1.03125 -4.703125 1.140625 -4.515625 1.140625 -4 L 1.140625 -1.21875 C 1.140625 -0.359375 1.015625 -0.234375 0.1875 -0.171875 L 0.1875 0 L 3.03125 0 L 3.03125 -0.171875 C 2.234375 -0.234375 2.140625 -0.359375 2.140625 -1.21875 L 2.140625 -5.46875 Z M 1.53125 -8.15625 C 1.203125 -8.15625 0.9375 -7.890625 0.9375 -7.546875 C 0.9375 -7.21875 1.1875 -6.9375 1.53125 -6.9375 C 1.875 -6.9375 2.15625 -7.203125 2.15625 -7.546875 C 2.15625 -7.890625 1.875 -8.15625 1.53125 -8.15625 Z M 1.53125 -8.15625 "/> +</symbol> +<symbol overflow="visible" id="glyph1-14"> +<path style="stroke:none;" d="M 3.765625 -3.75 L 3.71875 -5.375 L 3.578125 -5.375 L 3.5625 -5.359375 C 3.453125 -5.265625 3.4375 -5.265625 3.390625 -5.265625 C 3.328125 -5.265625 3.203125 -5.28125 3.078125 -5.34375 C 2.8125 -5.4375 2.546875 -5.484375 2.234375 -5.484375 C 1.296875 -5.484375 0.609375 -4.875 0.609375 -4.015625 C 0.609375 -3.34375 0.984375 -2.875 2.015625 -2.296875 L 2.703125 -1.90625 C 3.125 -1.65625 3.328125 -1.375 3.328125 -1 C 3.328125 -0.484375 2.9375 -0.140625 2.328125 -0.140625 C 1.921875 -0.140625 1.546875 -0.296875 1.328125 -0.5625 C 1.078125 -0.859375 0.96875 -1.140625 0.8125 -1.8125 L 0.625 -1.8125 L 0.625 0.046875 L 0.78125 0.046875 C 0.859375 -0.078125 0.90625 -0.09375 1.046875 -0.09375 C 1.15625 -0.09375 1.328125 -0.078125 1.609375 0 C 1.9375 0.078125 2.265625 0.125 2.46875 0.125 C 3.390625 0.125 4.15625 -0.578125 4.15625 -1.40625 C 4.15625 -2.015625 3.875 -2.40625 3.15625 -2.828125 L 1.859375 -3.59375 C 1.53125 -3.796875 1.34375 -4.09375 1.34375 -4.40625 C 1.34375 -4.890625 1.71875 -5.21875 2.265625 -5.21875 C 2.953125 -5.21875 3.3125 -4.8125 3.578125 -3.75 Z M 3.765625 -3.75 "/> +</symbol> +<symbol overflow="visible" id="glyph1-15"> +<path style="stroke:none;" d="M 8.46875 -4.234375 L 5.421875 -4.234375 L 5.421875 -4.015625 C 5.9375 -3.984375 6.078125 -3.9375 6.234375 -3.84375 C 6.40625 -3.734375 6.484375 -3.46875 6.484375 -2.953125 L 6.484375 -1.015625 C 6.484375 -0.640625 5.75 -0.3125 4.890625 -0.3125 C 2.9375 -0.3125 1.75 -1.671875 1.75 -3.890625 C 1.75 -5.015625 2.078125 -6.109375 2.609375 -6.6875 C 3.125 -7.28125 3.875 -7.609375 4.6875 -7.609375 C 5.359375 -7.609375 5.9375 -7.375 6.40625 -6.9375 C 6.765625 -6.609375 6.953125 -6.28125 7.25 -5.5625 L 7.53125 -5.5625 L 7.4375 -8.078125 L 7.171875 -8.078125 C 7.09375 -7.859375 6.890625 -7.6875 6.640625 -7.6875 C 6.515625 -7.6875 6.328125 -7.71875 6.109375 -7.8125 C 5.5625 -7.984375 5.015625 -8.078125 4.484375 -8.078125 C 2.09375 -8.078125 0.375 -6.328125 0.375 -3.890625 C 0.375 -2.71875 0.703125 -1.8125 1.375 -1.109375 C 2.171875 -0.28125 3.328125 0.171875 4.640625 0.171875 C 5.671875 0.171875 7.15625 -0.25 7.640625 -0.671875 L 7.640625 -3.09375 C 7.640625 -3.796875 7.78125 -3.953125 8.46875 -4.015625 Z M 8.46875 -4.234375 "/> +</symbol> +<symbol overflow="visible" id="glyph1-16"> +<path style="stroke:none;" d="M 8.4375 -0.234375 C 7.90625 -0.265625 7.78125 -0.375 7.359375 -1.265625 L 4.390625 -8.0625 L 4.140625 -8.0625 L 1.65625 -2.1875 C 0.890625 -0.4375 0.75 -0.25 0.171875 -0.234375 L 0.171875 0 L 2.546875 0 L 2.546875 -0.234375 C 1.96875 -0.234375 1.734375 -0.375 1.734375 -0.71875 C 1.734375 -0.859375 1.765625 -1.03125 1.828125 -1.1875 L 2.375 -2.578125 L 5.515625 -2.578125 L 6 -1.4375 C 6.140625 -1.109375 6.234375 -0.796875 6.234375 -0.640625 C 6.234375 -0.328125 6.03125 -0.234375 5.390625 -0.234375 L 5.390625 0 L 8.4375 0 Z M 2.578125 -3.078125 L 3.953125 -6.359375 L 5.34375 -3.078125 Z M 2.578125 -3.078125 "/> +</symbol> +<symbol overflow="visible" id="glyph1-17"> +<path style="stroke:none;" d="M 3.328125 0 L 5.71875 0 L 5.71875 -0.171875 C 5.359375 -0.171875 5.109375 -0.375 4.75 -0.890625 L 3.21875 -3.234375 L 4.203125 -4.671875 C 4.4375 -5 4.796875 -5.1875 5.171875 -5.203125 L 5.171875 -5.375 L 3.28125 -5.375 L 3.28125 -5.203125 C 3.640625 -5.171875 3.765625 -5.109375 3.765625 -4.9375 C 3.765625 -4.796875 3.625 -4.53125 3.328125 -4.15625 C 3.265625 -4.09375 3.125 -3.875 2.96875 -3.640625 L 2.796875 -3.875 C 2.46875 -4.375 2.25 -4.78125 2.25 -4.9375 C 2.25 -5.109375 2.40625 -5.1875 2.765625 -5.203125 L 2.765625 -5.375 L 0.28125 -5.375 L 0.28125 -5.203125 L 0.390625 -5.203125 C 0.75 -5.203125 0.9375 -5.046875 1.3125 -4.484375 L 2.4375 -2.765625 L 1.078125 -0.78125 C 0.71875 -0.296875 0.59375 -0.21875 0.203125 -0.171875 L 0.203125 0 L 1.9375 0 L 1.9375 -0.171875 C 1.609375 -0.171875 1.453125 -0.234375 1.453125 -0.390625 C 1.453125 -0.46875 1.546875 -0.640625 1.703125 -0.890625 L 2.640625 -2.359375 L 3.734375 -0.6875 C 3.78125 -0.609375 3.796875 -0.53125 3.796875 -0.46875 C 3.796875 -0.25 3.71875 -0.203125 3.328125 -0.171875 Z M 3.328125 0 "/> +</symbol> +<symbol overflow="visible" id="glyph1-18"> +<path style="stroke:none;" d="M 4.109375 0.125 L 5.875 -0.5 L 5.875 -0.6875 C 5.65625 -0.6875 5.625 -0.6875 5.59375 -0.6875 C 5.15625 -0.6875 5.0625 -0.8125 5.0625 -1.359375 L 5.0625 -8.140625 L 5.015625 -8.15625 C 4.4375 -7.953125 4.015625 -7.84375 3.25 -7.640625 L 3.25 -7.453125 C 3.34375 -7.453125 3.421875 -7.453125 3.515625 -7.453125 C 3.953125 -7.453125 4.0625 -7.34375 4.0625 -6.84375 L 4.0625 -4.984375 C 3.609375 -5.359375 3.28125 -5.5 2.8125 -5.5 C 1.4375 -5.5 0.328125 -4.140625 0.328125 -2.453125 C 0.328125 -0.921875 1.21875 0.125 2.53125 0.125 C 3.203125 0.125 3.65625 -0.125 4.0625 -0.6875 L 4.0625 0.078125 Z M 4.0625 -1.21875 C 4.0625 -1.140625 3.984375 -0.984375 3.859375 -0.859375 C 3.640625 -0.625 3.34375 -0.5 3 -0.5 C 2.015625 -0.5 1.34375 -1.453125 1.34375 -2.921875 C 1.34375 -4.28125 1.9375 -5.15625 2.84375 -5.15625 C 3.484375 -5.15625 4.0625 -4.609375 4.0625 -3.96875 Z M 4.0625 -1.21875 "/> +</symbol> +<symbol overflow="visible" id="glyph1-19"> +<path style="stroke:none;" d="M 8.0625 -6.84375 L 8.0625 -1.4375 C 8.0625 -0.4375 7.90625 -0.28125 6.96875 -0.234375 L 6.96875 0 L 10.3125 0 L 10.3125 -0.234375 C 9.4375 -0.28125 9.28125 -0.453125 9.28125 -1.296875 L 9.28125 -6.609375 C 9.28125 -7.453125 9.4375 -7.609375 10.3125 -7.6875 L 10.3125 -7.90625 L 7.9375 -7.90625 L 5.296875 -1.875 L 2.53125 -7.90625 L 0.171875 -7.90625 L 0.171875 -7.6875 C 1.140625 -7.625 1.296875 -7.484375 1.296875 -6.609375 L 1.296875 -1.75 C 1.296875 -0.53125 1.140625 -0.296875 0.140625 -0.234375 L 0.140625 0 L 2.953125 0 L 2.953125 -0.234375 C 2.03125 -0.28125 1.828125 -0.546875 1.828125 -1.75 L 1.828125 -6.578125 L 4.828125 0 L 5 0 Z M 8.0625 -6.84375 "/> +</symbol> +<symbol overflow="visible" id="glyph1-20"> +<path style="stroke:none;" d="M 2.984375 -5.5 C 1.4375 -5.5 0.34375 -4.34375 0.34375 -2.703125 C 0.34375 -1.09375 1.453125 0.125 2.96875 0.125 C 4.46875 0.125 5.625 -1.140625 5.625 -2.796875 C 5.625 -4.359375 4.515625 -5.5 2.984375 -5.5 Z M 2.828125 -5.15625 C 3.84375 -5.15625 4.546875 -4.015625 4.546875 -2.375 C 4.546875 -1.03125 4 -0.21875 3.109375 -0.21875 C 2.640625 -0.21875 2.203125 -0.5 1.953125 -0.984375 C 1.609375 -1.609375 1.421875 -2.4375 1.421875 -3.28125 C 1.421875 -4.421875 1.984375 -5.15625 2.828125 -5.15625 Z M 2.828125 -5.15625 "/> +</symbol> +<symbol overflow="visible" id="glyph1-21"> +<path style="stroke:none;" d="M 4.75 -1.859375 C 4.1875 -1.03125 3.75 -0.734375 3.078125 -0.734375 C 1.984375 -0.734375 1.21875 -1.703125 1.21875 -3.078125 C 1.21875 -4.296875 1.875 -5.15625 2.84375 -5.15625 C 3.28125 -5.15625 3.4375 -5.015625 3.546875 -4.578125 L 3.625 -4.3125 C 3.71875 -3.984375 3.9375 -3.765625 4.1875 -3.765625 C 4.5 -3.765625 4.75 -4 4.75 -4.265625 C 4.75 -4.9375 3.921875 -5.5 2.921875 -5.5 C 2.328125 -5.5 1.71875 -5.265625 1.234375 -4.828125 C 0.640625 -4.296875 0.296875 -3.484375 0.296875 -2.546875 C 0.296875 -0.984375 1.25 0.125 2.5625 0.125 C 3.109375 0.125 3.578125 -0.078125 4.015625 -0.4375 C 4.34375 -0.734375 4.5625 -1.046875 4.921875 -1.75 Z M 4.75 -1.859375 "/> +</symbol> +<symbol overflow="visible" id="glyph1-22"> +<path style="stroke:none;" d="M 5.71875 -0.59375 L 5.671875 -0.59375 C 5.109375 -0.59375 4.984375 -0.734375 4.984375 -1.28125 L 4.984375 -5.375 L 3.09375 -5.375 L 3.09375 -5.171875 C 3.84375 -5.140625 3.984375 -5.015625 3.984375 -4.421875 L 3.984375 -1.609375 C 3.984375 -1.28125 3.921875 -1.109375 3.75 -0.984375 C 3.4375 -0.71875 3.0625 -0.578125 2.703125 -0.578125 C 2.234375 -0.578125 1.859375 -0.984375 1.859375 -1.484375 L 1.859375 -5.375 L 0.109375 -5.375 L 0.109375 -5.21875 C 0.6875 -5.171875 0.84375 -5 0.84375 -4.453125 L 0.84375 -1.4375 C 0.84375 -0.484375 1.421875 0.125 2.296875 0.125 C 2.734375 0.125 3.203125 -0.078125 3.53125 -0.390625 L 4.046875 -0.90625 L 4.046875 0.078125 L 4.09375 0.109375 C 4.6875 -0.125 5.109375 -0.265625 5.71875 -0.4375 Z M 5.71875 -0.59375 "/> +</symbol> +<symbol overflow="visible" id="glyph1-23"> +<path style="stroke:none;" d="M 0.078125 -7.453125 C 0.234375 -7.453125 0.34375 -7.46875 0.46875 -7.46875 C 0.859375 -7.46875 0.984375 -7.3125 0.984375 -6.734375 L 0.984375 -0.984375 C 0.984375 -0.359375 0.9375 -0.328125 0.078125 -0.171875 L 0.078125 0 L 2.875 0 L 2.875 -0.171875 L 2.640625 -0.1875 C 2.15625 -0.21875 1.984375 -0.375 1.984375 -0.796875 L 1.984375 -3 L 3.65625 -0.765625 L 3.6875 -0.71875 C 3.71875 -0.6875 3.734375 -0.640625 3.78125 -0.609375 C 3.875 -0.484375 3.90625 -0.421875 3.90625 -0.359375 C 3.90625 -0.25 3.796875 -0.171875 3.65625 -0.171875 L 3.4375 -0.171875 L 3.4375 0 L 6.03125 0 L 6.03125 -0.171875 C 5.515625 -0.21875 5.140625 -0.4375 4.640625 -1.046875 L 2.8125 -3.375 L 3.15625 -3.6875 C 4 -4.484375 4.75 -5.03125 5.09375 -5.125 C 5.265625 -5.171875 5.4375 -5.203125 5.640625 -5.203125 L 5.734375 -5.203125 L 5.734375 -5.375 L 3.296875 -5.375 L 3.296875 -5.21875 C 3.765625 -5.203125 3.890625 -5.15625 3.890625 -4.984375 C 3.890625 -4.890625 3.796875 -4.71875 3.625 -4.578125 L 1.984375 -3.125 L 1.984375 -8.140625 L 1.9375 -8.15625 C 1.484375 -8.015625 1.140625 -7.921875 0.4375 -7.734375 L 0.078125 -7.640625 Z M 0.078125 -7.453125 "/> +</symbol> +<symbol overflow="visible" id="glyph1-24"> +<path style="stroke:none;" d="M 5.34375 -8.078125 L 5.09375 -8.078125 C 5.046875 -7.8125 4.90625 -7.671875 4.703125 -7.671875 C 4.59375 -7.671875 4.390625 -7.71875 4.1875 -7.8125 C 3.734375 -7.984375 3.296875 -8.078125 2.921875 -8.078125 C 2.40625 -8.078125 1.875 -7.875 1.484375 -7.53125 C 1.0625 -7.15625 0.84375 -6.65625 0.84375 -6.03125 C 0.84375 -5.078125 1.375 -4.40625 2.71875 -3.703125 C 3.578125 -3.25 4.203125 -2.765625 4.5 -2.3125 C 4.609375 -2.15625 4.65625 -1.90625 4.65625 -1.609375 C 4.65625 -0.8125 4.0625 -0.265625 3.1875 -0.265625 C 2.125 -0.265625 1.375 -0.921875 0.78125 -2.375 L 0.5 -2.375 L 0.859375 0.15625 L 1.125 0.15625 C 1.140625 -0.078125 1.28125 -0.234375 1.453125 -0.234375 C 1.59375 -0.234375 1.796875 -0.1875 2.015625 -0.109375 C 2.46875 0.078125 2.953125 0.171875 3.4375 0.171875 C 4.8125 0.171875 5.875 -0.78125 5.875 -2.015625 C 5.875 -2.984375 5.21875 -3.765625 3.640625 -4.609375 C 2.375 -5.3125 1.875 -5.828125 1.875 -6.484375 C 1.875 -7.140625 2.375 -7.59375 3.125 -7.59375 C 3.65625 -7.59375 4.15625 -7.359375 4.578125 -6.9375 C 4.953125 -6.546875 5.109375 -6.234375 5.3125 -5.53125 L 5.609375 -5.53125 Z M 5.34375 -8.078125 "/> +</symbol> +<symbol overflow="visible" id="glyph1-25"> +<path style="stroke:none;" d="M 0.234375 -4.75 C 0.375 -4.796875 0.484375 -4.8125 0.609375 -4.8125 C 0.921875 -4.8125 1.03125 -4.609375 1.03125 -4.046875 L 1.03125 -1.015625 C 1.03125 -0.375 0.859375 -0.1875 0.1875 -0.171875 L 0.1875 0 L 2.84375 0 L 2.84375 -0.171875 C 2.21875 -0.203125 2.03125 -0.328125 2.03125 -0.796875 L 2.03125 -4.171875 C 2.03125 -4.203125 2.125 -4.3125 2.21875 -4.40625 C 2.515625 -4.671875 3.03125 -4.875 3.4375 -4.875 C 3.96875 -4.875 4.234375 -4.453125 4.234375 -3.625 L 4.234375 -1.03125 C 4.234375 -0.359375 4.09375 -0.234375 3.421875 -0.171875 L 3.421875 0 L 6.09375 0 L 6.09375 -0.171875 C 5.421875 -0.1875 5.234375 -0.390625 5.234375 -1.140625 L 5.234375 -4.140625 C 5.59375 -4.65625 5.984375 -4.875 6.53125 -4.875 C 7.21875 -4.875 7.4375 -4.546875 7.4375 -3.5625 L 7.4375 -1.046875 C 7.4375 -0.359375 7.34375 -0.265625 6.640625 -0.171875 L 6.640625 0 L 9.265625 0 L 9.265625 -0.171875 L 8.953125 -0.203125 C 8.59375 -0.234375 8.4375 -0.4375 8.4375 -0.90625 L 8.4375 -3.375 C 8.4375 -4.78125 7.96875 -5.5 7.046875 -5.5 C 6.359375 -5.5 5.75 -5.1875 5.109375 -4.5 C 4.890625 -5.171875 4.484375 -5.5 3.84375 -5.5 C 3.3125 -5.5 2.96875 -5.328125 1.984375 -4.578125 L 1.984375 -5.46875 L 1.90625 -5.5 C 1.296875 -5.265625 0.890625 -5.140625 0.234375 -4.953125 Z M 0.234375 -4.75 "/> +</symbol> +<symbol overflow="visible" id="glyph1-26"> +<path style="stroke:none;" d="M 3.03125 -7.40625 L 3.03125 -1.4375 C 3.03125 -0.421875 2.90625 -0.28125 1.90625 -0.234375 L 1.90625 0 L 5.40625 0 L 5.40625 -0.234375 C 4.421875 -0.28125 4.25 -0.4375 4.25 -1.296875 L 4.25 -7.40625 L 4.90625 -7.40625 C 6.265625 -7.40625 6.53125 -7.203125 6.796875 -5.875 L 7.09375 -5.875 L 7.015625 -7.90625 L 0.28125 -7.90625 L 0.203125 -5.875 L 0.484375 -5.875 C 0.78125 -7.1875 1.046875 -7.40625 2.390625 -7.40625 Z M 3.03125 -7.40625 "/> +</symbol> +<symbol overflow="visible" id="glyph1-27"> +<path style="stroke:none;" d="M 4.9375 -7.6875 C 5.078125 -7.671875 5.203125 -7.671875 5.25 -7.671875 C 5.609375 -7.65625 5.75 -7.546875 5.75 -7.3125 C 5.75 -7.046875 5.46875 -6.6875 4.8125 -6.078125 L 2.703125 -4.15625 L 2.703125 -6.609375 C 2.703125 -7.484375 2.828125 -7.609375 3.796875 -7.6875 L 3.796875 -7.90625 L 0.40625 -7.90625 L 0.40625 -7.6875 C 1.34375 -7.609375 1.484375 -7.453125 1.484375 -6.609375 L 1.484375 -1.4375 C 1.484375 -0.4375 1.34375 -0.28125 0.40625 -0.234375 L 0.40625 0 L 3.78125 0 L 3.78125 -0.234375 C 2.84375 -0.28125 2.703125 -0.4375 2.703125 -1.296875 L 2.703125 -3.53125 L 3.015625 -3.796875 L 4.28125 -2.53125 C 5.1875 -1.640625 5.828125 -0.78125 5.828125 -0.5 C 5.828125 -0.328125 5.671875 -0.25 5.328125 -0.234375 C 5.265625 -0.234375 5.140625 -0.234375 5 -0.234375 L 5 0 L 8.640625 0 L 8.640625 -0.234375 C 8.015625 -0.234375 7.859375 -0.359375 6.765625 -1.515625 L 3.984375 -4.5 L 6.25 -6.75 C 7.0625 -7.53125 7.25 -7.625 8.0625 -7.6875 L 8.0625 -7.90625 L 4.9375 -7.90625 Z M 4.9375 -7.6875 "/> +</symbol> +<symbol overflow="visible" id="glyph1-28"> +<path style="stroke:none;" d="M 2.421875 -3.484375 C 2.71875 -3.453125 2.921875 -3.4375 3.234375 -3.4375 C 4.171875 -3.4375 4.8125 -3.5625 5.328125 -3.84375 C 6.046875 -4.234375 6.484375 -4.953125 6.484375 -5.75 C 6.484375 -6.25 6.3125 -6.703125 5.984375 -7.046875 C 5.515625 -7.578125 4.46875 -7.90625 3.34375 -7.90625 L 0.1875 -7.90625 L 0.1875 -7.6875 C 1.078125 -7.59375 1.1875 -7.46875 1.1875 -6.609375 L 1.1875 -1.4375 C 1.1875 -0.4375 1.09375 -0.3125 0.1875 -0.234375 L 0.1875 0 L 3.53125 0 L 3.53125 -0.234375 C 2.59375 -0.265625 2.421875 -0.4375 2.421875 -1.296875 Z M 2.421875 -7.0625 C 2.421875 -7.390625 2.5 -7.46875 2.828125 -7.46875 C 4.4375 -7.46875 5.171875 -6.90625 5.171875 -5.671875 C 5.171875 -4.515625 4.46875 -3.921875 3.09375 -3.921875 C 2.859375 -3.921875 2.6875 -3.9375 2.421875 -3.953125 Z M 2.421875 -7.0625 "/> +</symbol> +<symbol overflow="visible" id="glyph1-29"> +<path style="stroke:none;" d="M 5.671875 -5.375 L 4.0625 -5.375 L 4.0625 -5.203125 C 4.453125 -5.203125 4.640625 -5.09375 4.640625 -4.90625 C 4.640625 -4.859375 4.625 -4.78125 4.59375 -4.703125 L 3.4375 -1.40625 L 2.0625 -4.421875 C 1.984375 -4.59375 1.9375 -4.75 1.9375 -4.875 C 1.9375 -5.09375 2.109375 -5.171875 2.625 -5.203125 L 2.625 -5.375 L 0.171875 -5.375 L 0.171875 -5.21875 C 0.484375 -5.15625 0.6875 -5.03125 0.78125 -4.828125 L 2.140625 -1.890625 L 2.171875 -1.796875 L 2.359375 -1.4375 C 2.6875 -0.84375 2.875 -0.40625 2.875 -0.234375 C 2.875 -0.046875 2.609375 0.703125 2.40625 1.0625 C 2.234375 1.375 1.96875 1.609375 1.8125 1.609375 C 1.734375 1.609375 1.625 1.578125 1.5 1.515625 C 1.28125 1.4375 1.078125 1.390625 0.875 1.390625 C 0.59375 1.390625 0.359375 1.625 0.359375 1.90625 C 0.359375 2.3125 0.734375 2.609375 1.25 2.609375 C 2.046875 2.609375 2.625 1.9375 3.265625 0.21875 L 5.109375 -4.65625 C 5.265625 -5.046875 5.390625 -5.15625 5.671875 -5.203125 Z M 5.671875 -5.375 "/> +</symbol> +<symbol overflow="visible" id="glyph1-30"> +<path style="stroke:none;" d="M 8.328125 -7.90625 L 5.875 -7.90625 L 5.875 -7.6875 C 6.53125 -7.65625 6.75 -7.515625 6.75 -7.1875 C 6.75 -7 6.671875 -6.671875 6.53125 -6.3125 L 4.765625 -1.921875 L 2.96875 -5.96875 C 2.5625 -6.84375 2.46875 -7.109375 2.46875 -7.296875 C 2.46875 -7.515625 2.640625 -7.640625 3.03125 -7.65625 C 3.078125 -7.65625 3.203125 -7.671875 3.375 -7.6875 L 3.375 -7.90625 L 0.1875 -7.90625 L 0.1875 -7.6875 C 0.78125 -7.65625 0.9375 -7.5 1.453125 -6.4375 L 4.40625 0.125 L 4.578125 0.125 L 7.234375 -6.578125 C 7.625 -7.515625 7.75 -7.65625 8.328125 -7.6875 Z M 8.328125 -7.90625 "/> +</symbol> +<symbol overflow="visible" id="glyph1-31"> +<path style="stroke:none;" d="M 3.046875 -5.375 L 1.84375 -5.375 L 1.84375 -6.765625 C 1.84375 -6.890625 1.828125 -6.921875 1.75 -6.921875 C 1.671875 -6.8125 1.609375 -6.703125 1.515625 -6.59375 C 1.0625 -5.921875 0.546875 -5.359375 0.359375 -5.3125 C 0.234375 -5.21875 0.15625 -5.140625 0.15625 -5.078125 C 0.15625 -5.046875 0.171875 -5.015625 0.203125 -5 L 0.84375 -5 L 0.84375 -1.40625 C 0.84375 -0.390625 1.1875 0.125 1.90625 0.125 C 2.484375 0.125 2.9375 -0.171875 3.328125 -0.78125 L 3.171875 -0.921875 C 2.921875 -0.625 2.71875 -0.5 2.46875 -0.5 C 2.015625 -0.5 1.84375 -0.828125 1.84375 -1.578125 L 1.84375 -5 L 3.046875 -5 Z M 3.046875 -5.375 "/> +</symbol> +<symbol overflow="visible" id="glyph1-32"> +<path style="stroke:none;" d="M 3.6875 -5.375 L 2.21875 -5.375 L 2.21875 -6.765625 C 2.21875 -7.453125 2.453125 -7.828125 2.90625 -7.828125 C 3.15625 -7.828125 3.328125 -7.703125 3.53125 -7.359375 C 3.734375 -7.046875 3.875 -6.9375 4.078125 -6.9375 C 4.34375 -6.9375 4.578125 -7.140625 4.578125 -7.421875 C 4.578125 -7.859375 4.046875 -8.15625 3.328125 -8.15625 C 2.59375 -8.15625 1.953125 -7.84375 1.65625 -7.296875 C 1.34375 -6.765625 1.25 -6.328125 1.234375 -5.375 L 0.25 -5.375 L 0.25 -5 L 1.234375 -5 L 1.234375 -1.25 C 1.234375 -0.375 1.09375 -0.234375 0.234375 -0.171875 L 0.234375 0 L 3.34375 0 L 3.34375 -0.171875 C 2.359375 -0.21875 2.234375 -0.34375 2.234375 -1.25 L 2.234375 -5 L 3.6875 -5 Z M 3.6875 -5.375 "/> +</symbol> +<symbol overflow="visible" id="glyph1-33"> +<path style="stroke:none;" d="M 7.875 -0.234375 C 7.421875 -0.265625 7.1875 -0.375 6.84375 -0.78125 L 4.375 -3.8125 C 5.171875 -3.96875 5.53125 -4.109375 5.921875 -4.4375 C 6.3125 -4.75 6.53125 -5.25 6.53125 -5.8125 C 6.53125 -6.328125 6.390625 -6.765625 6.078125 -7.109375 C 5.625 -7.609375 4.625 -7.90625 3.5 -7.90625 L 0.203125 -7.90625 L 0.203125 -7.6875 C 1.09375 -7.59375 1.21875 -7.453125 1.21875 -6.609375 L 1.21875 -1.4375 C 1.21875 -0.4375 1.09375 -0.296875 0.203125 -0.234375 L 0.203125 0 L 3.515625 0 L 3.515625 -0.234375 C 2.59375 -0.28125 2.4375 -0.4375 2.4375 -1.296875 L 2.4375 -3.65625 L 3.109375 -3.6875 L 5.953125 0 L 7.875 0 Z M 2.4375 -7.046875 C 2.4375 -7.375 2.5625 -7.46875 3.046875 -7.46875 C 4.546875 -7.46875 5.234375 -6.953125 5.234375 -5.828125 C 5.234375 -5.234375 4.984375 -4.734375 4.546875 -4.5 C 4 -4.203125 3.578125 -4.125 2.4375 -4.09375 Z M 2.4375 -7.046875 "/> +</symbol> +<symbol overflow="visible" id="glyph1-34"> +<path style="stroke:none;" d="M 7.296875 -6.15625 C 7.296875 -7.375 7.453125 -7.5625 8.421875 -7.6875 L 8.421875 -7.90625 L 5.65625 -7.90625 L 5.65625 -7.6875 C 6.59375 -7.609375 6.78125 -7.34375 6.78125 -6.15625 L 6.78125 -2.921875 C 6.78125 -2.109375 6.6875 -1.65625 6.5 -1.296875 C 6.1875 -0.734375 5.421875 -0.359375 4.515625 -0.359375 C 3.671875 -0.359375 3.078125 -0.640625 2.765625 -1.203125 C 2.546875 -1.59375 2.46875 -2.03125 2.46875 -2.78125 L 2.46875 -6.609375 C 2.46875 -7.46875 2.59375 -7.609375 3.546875 -7.6875 L 3.546875 -7.90625 L 0.171875 -7.90625 L 0.171875 -7.6875 C 1.109375 -7.609375 1.25 -7.484375 1.25 -6.609375 L 1.25 -2.875 C 1.25 -0.8125 2.21875 0.171875 4.25 0.171875 C 5.546875 0.171875 6.4375 -0.25 6.90625 -1.0625 C 7.203125 -1.5625 7.296875 -2.109375 7.296875 -3.03125 Z M 7.296875 -6.15625 "/> +</symbol> +<symbol overflow="visible" id="glyph1-35"> +<path style="stroke:none;" d="M 5.703125 -5.375 L 4.046875 -5.375 L 4.046875 -5.203125 C 4.421875 -5.15625 4.609375 -5.046875 4.609375 -4.8125 C 4.609375 -4.703125 4.578125 -4.578125 4.53125 -4.453125 L 3.34375 -1.359375 L 2.125 -4.421875 C 2.0625 -4.59375 2.015625 -4.75 2.015625 -4.859375 C 2.015625 -5.078125 2.15625 -5.15625 2.5625 -5.203125 L 2.5625 -5.375 L 0.234375 -5.375 L 0.234375 -5.203125 C 0.6875 -5.171875 0.765625 -5.0625 1.3125 -3.828125 L 2.75 -0.390625 C 2.765625 -0.328125 2.8125 -0.234375 2.84375 -0.140625 C 2.921875 0.078125 2.984375 0.171875 3.0625 0.171875 C 3.125 0.171875 3.21875 0.015625 3.390625 -0.4375 L 4.921875 -4.265625 C 5.265625 -5.078125 5.34375 -5.15625 5.703125 -5.203125 Z M 5.703125 -5.375 "/> +</symbol> +<symbol overflow="visible" id="glyph1-36"> +<path style="stroke:none;" d="M 1.375 -1.296875 C 1.375 -0.40625 1.21875 -0.265625 0.21875 -0.234375 L 0.21875 0 L 3.765625 0 L 3.765625 -0.234375 C 2.78125 -0.265625 2.59375 -0.4375 2.59375 -1.296875 L 2.59375 -6.609375 C 2.59375 -7.484375 2.765625 -7.640625 3.765625 -7.6875 L 3.765625 -7.90625 L 0.21875 -7.90625 L 0.21875 -7.6875 C 1.234375 -7.625 1.375 -7.5 1.375 -6.609375 Z M 1.375 -1.296875 "/> +</symbol> +<symbol overflow="visible" id="glyph1-37"> +<path style="stroke:none;" d="M 1.828125 -8.140625 L 1.765625 -8.15625 C 1.265625 -7.984375 0.9375 -7.890625 0.375 -7.734375 L 0.03125 -7.640625 L 0.03125 -7.453125 C 0.109375 -7.453125 0.15625 -7.453125 0.234375 -7.453125 C 0.71875 -7.453125 0.828125 -7.34375 0.828125 -6.84375 L 0.828125 -0.640625 C 0.828125 -0.28125 1.84375 0.125 2.796875 0.125 C 4.375 0.125 5.59375 -1.1875 5.59375 -2.90625 C 5.59375 -4.375 4.6875 -5.5 3.484375 -5.5 C 2.765625 -5.5 2.0625 -5.0625 1.828125 -4.484375 Z M 1.828125 -3.84375 C 1.828125 -4.3125 2.390625 -4.75 3.015625 -4.75 C 3.9375 -4.75 4.546875 -3.8125 4.546875 -2.359375 C 4.546875 -1.03125 3.96875 -0.265625 2.984375 -0.265625 C 2.359375 -0.265625 1.828125 -0.53125 1.828125 -0.84375 Z M 1.828125 -3.84375 "/> +</symbol> +<symbol overflow="visible" id="glyph1-38"> +<path style="stroke:none;" d="M 0.46875 -3.078125 L 0.46875 -2.3125 L 3.40625 -2.3125 L 3.40625 -3.078125 Z M 0.46875 -3.078125 "/> +</symbol> +<symbol overflow="visible" id="glyph1-39"> +<path style="stroke:none;" d="M 0.109375 -4.703125 C 0.21875 -4.703125 0.296875 -4.703125 0.40625 -4.703125 C 0.8125 -4.703125 0.890625 -4.59375 0.890625 -4.03125 L 0.890625 1.5625 C 0.890625 2.1875 0.765625 2.3125 0.0625 2.390625 L 0.0625 2.59375 L 2.953125 2.59375 L 2.953125 2.375 C 2.0625 2.359375 1.90625 2.234375 1.90625 1.484375 L 1.90625 -0.390625 C 2.3125 0 2.609375 0.125 3.109375 0.125 C 4.515625 0.125 5.625 -1.21875 5.625 -2.953125 C 5.625 -4.4375 4.78125 -5.5 3.625 -5.5 C 2.953125 -5.5 2.421875 -5.203125 1.90625 -4.546875 L 1.90625 -5.46875 L 1.828125 -5.5 C 1.1875 -5.25 0.765625 -5.09375 0.109375 -4.890625 Z M 1.90625 -4 C 1.90625 -4.34375 2.5625 -4.78125 3.125 -4.78125 C 4 -4.78125 4.59375 -3.875 4.59375 -2.484375 C 4.59375 -1.15625 4 -0.265625 3.140625 -0.265625 C 2.578125 -0.265625 1.90625 -0.6875 1.90625 -1.046875 Z M 1.90625 -4 "/> +</symbol> +<symbol overflow="visible" id="glyph1-40"> +<path style="stroke:none;" d="M 6.828125 -5.375 L 6.828125 -5.203125 C 7.234375 -5.109375 7.34375 -5.03125 7.34375 -4.828125 C 7.34375 -4.65625 7.28125 -4.359375 7.140625 -4.046875 L 6.078125 -1.390625 L 5.0625 -4.0625 C 4.859375 -4.609375 4.859375 -4.609375 4.859375 -4.765625 C 4.859375 -5.03125 5 -5.109375 5.5625 -5.203125 L 5.5625 -5.375 L 3.125 -5.375 L 3.125 -5.203125 C 3.578125 -5.15625 3.71875 -5.015625 3.953125 -4.375 C 4.046875 -4.140625 4.125 -3.921875 4.203125 -3.703125 L 3.109375 -1.328125 L 1.921875 -4.453125 C 1.875 -4.5625 1.859375 -4.6875 1.859375 -4.8125 C 1.859375 -5.0625 2 -5.15625 2.40625 -5.203125 L 2.40625 -5.375 L 0.25 -5.375 L 0.25 -5.203125 C 0.53125 -5.171875 0.625 -5.0625 0.890625 -4.453125 L 2.5 -0.359375 C 2.640625 0 2.734375 0.171875 2.8125 0.171875 C 2.875 0.171875 2.96875 0.015625 3.109375 -0.296875 L 4.453125 -3.171875 L 5.53125 -0.34375 C 5.703125 0.09375 5.75 0.171875 5.828125 0.171875 C 5.90625 0.171875 5.96875 0.0625 6.15625 -0.421875 L 7.8125 -4.546875 C 8.015625 -5.0625 8.0625 -5.109375 8.296875 -5.203125 L 8.296875 -5.375 Z M 6.828125 -5.375 "/> +</symbol> +<symbol overflow="visible" id="glyph1-41"> +<path style="stroke:none;" d="M 8.21875 -0.875 C 7.296875 -0.453125 6.6875 -0.296875 5.875 -0.296875 C 3.84375 -0.296875 2.390625 -1.78125 2.390625 -3.859375 C 2.390625 -4.96875 2.828125 -6.09375 3.5 -6.796875 C 4.09375 -7.390625 4.890625 -7.703125 5.765625 -7.703125 C 7.640625 -7.703125 9.1875 -6.234375 9.1875 -4.453125 C 9.1875 -3.265625 8.4375 -2.109375 7.65625 -2.109375 C 7.34375 -2.109375 7.1875 -2.28125 7.1875 -2.65625 C 7.1875 -2.734375 7.203125 -2.796875 7.203125 -2.875 L 7.984375 -5.90625 L 7.15625 -5.90625 L 7.046875 -5.453125 C 6.78125 -5.9375 6.59375 -6.078125 6.1875 -6.078125 C 5.671875 -6.078125 5.25 -5.875 4.84375 -5.46875 C 4.203125 -4.84375 3.84375 -3.9375 3.84375 -3.0625 C 3.84375 -2.3125 4.296875 -1.71875 4.890625 -1.71875 C 5.421875 -1.71875 5.96875 -2.046875 6.390625 -2.578125 C 6.453125 -2.0625 6.890625 -1.703125 7.46875 -1.703125 C 8.65625 -1.703125 9.671875 -3.03125 9.671875 -4.578125 C 9.671875 -6.546875 7.953125 -8.078125 5.75 -8.078125 C 3.328125 -8.078125 1.390625 -6.234375 1.390625 -3.890625 C 1.390625 -1.609375 3.296875 0.171875 5.765625 0.171875 C 6.609375 0.171875 7.203125 0.015625 8.359375 -0.515625 Z M 6.34375 -5.546875 C 6.6875 -5.515625 6.859375 -5.234375 6.84375 -4.765625 C 6.8125 -4.203125 6.609375 -3.484375 6.296875 -2.921875 C 6.078125 -2.5 5.71875 -2.234375 5.40625 -2.234375 C 4.953125 -2.234375 4.6875 -2.625 4.6875 -3.25 C 4.6875 -3.890625 4.90625 -4.5 5.3125 -4.953125 C 5.640625 -5.328125 6.0625 -5.5625 6.34375 -5.546875 Z M 6.34375 -5.546875 "/> +</symbol> +<symbol overflow="visible" id="glyph1-42"> +<path style="stroke:none;" d="M 5.671875 -1.640625 L 5.515625 -1.703125 C 5.078125 -1.015625 4.921875 -0.90625 4.390625 -0.90625 L 1.53125 -0.90625 L 3.53125 -3.015625 C 4.609375 -4.125 5.0625 -5.03125 5.0625 -5.96875 C 5.0625 -7.15625 4.09375 -8.078125 2.859375 -8.078125 C 2.203125 -8.078125 1.578125 -7.8125 1.140625 -7.34375 C 0.75 -6.9375 0.578125 -6.546875 0.375 -5.703125 L 0.625 -5.640625 C 1.09375 -6.8125 1.53125 -7.203125 2.359375 -7.203125 C 3.359375 -7.203125 4.046875 -6.515625 4.046875 -5.515625 C 4.046875 -4.578125 3.484375 -3.46875 2.484375 -2.40625 L 0.359375 -0.140625 L 0.359375 0 L 5.015625 0 Z M 5.671875 -1.640625 "/> +</symbol> +<symbol overflow="visible" id="glyph1-43"> +<path style="stroke:none;" d="M 3.03125 -8.078125 C 2.375 -8.078125 1.875 -7.875 1.4375 -7.453125 C 0.734375 -6.796875 0.28125 -5.421875 0.28125 -4.015625 C 0.28125 -2.71875 0.6875 -1.3125 1.25 -0.640625 C 1.6875 -0.125 2.296875 0.171875 2.984375 0.171875 C 3.59375 0.171875 4.109375 -0.03125 4.546875 -0.453125 C 5.234375 -1.109375 5.6875 -2.5 5.6875 -3.9375 C 5.6875 -6.390625 4.609375 -8.078125 3.03125 -8.078125 Z M 3 -7.765625 C 4 -7.765625 4.546875 -6.421875 4.546875 -3.921875 C 4.546875 -1.421875 4.015625 -0.140625 2.984375 -0.140625 C 1.953125 -0.140625 1.4375 -1.421875 1.4375 -3.90625 C 1.4375 -6.4375 1.96875 -7.765625 3 -7.765625 Z M 3 -7.765625 "/> +</symbol> +<symbol overflow="visible" id="glyph1-44"> +<path style="stroke:none;" d="M 0.703125 0.265625 C 2.046875 0.109375 2.703125 -0.125 3.515625 -0.703125 C 4.75 -1.609375 5.484375 -3.09375 5.484375 -4.703125 C 5.484375 -6.6875 4.390625 -8.078125 2.84375 -8.078125 C 1.421875 -8.078125 0.359375 -6.875 0.359375 -5.265625 C 0.359375 -3.796875 1.21875 -2.828125 2.515625 -2.828125 C 3.171875 -2.828125 3.671875 -3.03125 4.296875 -3.515625 C 3.8125 -1.5625 2.484375 -0.28125 0.671875 0.03125 Z M 4.328125 -4.25 C 4.328125 -4 4.28125 -3.890625 4.140625 -3.796875 C 3.8125 -3.5 3.375 -3.34375 2.9375 -3.34375 C 2.03125 -3.34375 1.453125 -4.25 1.453125 -5.671875 C 1.453125 -6.34375 1.65625 -7.0625 1.90625 -7.375 C 2.109375 -7.609375 2.40625 -7.75 2.75 -7.75 C 3.796875 -7.75 4.328125 -6.71875 4.328125 -4.703125 Z M 4.328125 -4.25 "/> +</symbol> +<symbol overflow="visible" id="glyph1-45"> +<path style="stroke:none;" d="M 3.484375 -8.078125 L 1.328125 -7 L 1.328125 -6.828125 C 1.46875 -6.890625 1.609375 -6.9375 1.65625 -6.953125 C 1.859375 -7.046875 2.0625 -7.09375 2.1875 -7.09375 C 2.4375 -7.09375 2.546875 -6.90625 2.546875 -6.53125 L 2.546875 -1.109375 C 2.546875 -0.71875 2.453125 -0.4375 2.265625 -0.328125 C 2.078125 -0.234375 1.90625 -0.1875 1.40625 -0.171875 L 1.40625 0 L 4.703125 0 L 4.703125 -0.171875 C 3.765625 -0.1875 3.578125 -0.3125 3.578125 -0.890625 L 3.578125 -8.0625 Z M 3.484375 -8.078125 "/> +</symbol> +<symbol overflow="visible" id="glyph1-46"> +<path style="stroke:none;" d="M 5.328125 -8.171875 C 3.96875 -8.0625 3.28125 -7.828125 2.40625 -7.21875 C 1.109375 -6.296875 0.40625 -4.9375 0.40625 -3.328125 C 0.40625 -2.296875 0.734375 -1.25 1.25 -0.640625 C 1.703125 -0.125 2.34375 0.171875 3.078125 0.171875 C 4.5625 0.171875 5.59375 -0.96875 5.59375 -2.625 C 5.59375 -4.140625 4.71875 -5.109375 3.34375 -5.109375 C 2.828125 -5.109375 2.5625 -5.03125 1.8125 -4.578125 C 2.140625 -6.390625 3.484375 -7.671875 5.359375 -7.984375 Z M 2.890625 -4.5625 C 3.921875 -4.5625 4.515625 -3.703125 4.515625 -2.21875 C 4.515625 -0.890625 4.046875 -0.171875 3.21875 -0.171875 C 2.15625 -0.171875 1.515625 -1.296875 1.515625 -3.140625 C 1.515625 -3.75 1.609375 -4.09375 1.859375 -4.265625 C 2.109375 -4.453125 2.46875 -4.5625 2.890625 -4.5625 Z M 2.890625 -4.5625 "/> +</symbol> +<symbol overflow="visible" id="glyph2-0"> +<path style="stroke:none;" d="M 3.375 -6.734375 C 3.375 -7.59375 3.375 -7.859375 3.671875 -8.15625 C 3.890625 -8.359375 4.15625 -8.65625 5 -8.703125 C 5.0625 -8.71875 5.109375 -8.765625 5.109375 -8.828125 C 5.109375 -8.96875 5.015625 -8.96875 4.859375 -8.96875 C 3.671875 -8.96875 2.609375 -8.359375 2.578125 -7.5 L 2.578125 -5.3125 C 2.578125 -4.1875 2.578125 -4 2.265625 -3.65625 C 2.109375 -3.484375 1.78125 -3.171875 1.03125 -3.125 C 0.9375 -3.125 0.859375 -3.109375 0.859375 -2.984375 C 0.859375 -2.875 0.9375 -2.875 1.046875 -2.859375 C 1.5625 -2.828125 2.578125 -2.5625 2.578125 -1.375 L 2.578125 0.984375 C 2.578125 1.6875 2.578125 2.09375 3.203125 2.53125 C 3.71875 2.890625 4.5 2.984375 4.859375 2.984375 C 5.015625 2.984375 5.109375 2.984375 5.109375 2.859375 C 5.109375 2.734375 5.03125 2.734375 4.90625 2.71875 C 3.96875 2.671875 3.53125 2.125 3.421875 1.703125 C 3.375 1.5625 3.375 1.546875 3.375 1.125 L 3.375 -0.671875 C 3.375 -1.03125 3.375 -1.640625 3.359375 -1.75 C 3.203125 -2.546875 2.4375 -2.859375 1.96875 -2.984375 C 3.375 -3.390625 3.375 -4.25 3.375 -4.578125 Z M 3.375 -6.734375 "/> +</symbol> +<symbol overflow="visible" id="glyph2-1"> +<path style="stroke:none;" d="M 3.375 -6.96875 C 3.375 -7.65625 3.375 -8.0625 2.765625 -8.515625 C 2.25 -8.859375 1.484375 -8.96875 1.09375 -8.96875 C 0.984375 -8.96875 0.859375 -8.96875 0.859375 -8.828125 C 0.859375 -8.71875 0.9375 -8.71875 1.046875 -8.703125 C 2 -8.640625 2.4375 -8.109375 2.546875 -7.671875 C 2.578125 -7.546875 2.578125 -7.515625 2.578125 -7.09375 L 2.578125 -5.3125 C 2.578125 -4.953125 2.578125 -4.34375 2.609375 -4.21875 C 2.765625 -3.4375 3.53125 -3.125 4 -2.984375 C 2.578125 -2.578125 2.578125 -1.734375 2.578125 -1.40625 L 2.578125 0.75 C 2.578125 1.609375 2.578125 1.875 2.296875 2.171875 C 2.078125 2.390625 1.8125 2.671875 0.96875 2.71875 C 0.90625 2.734375 0.859375 2.78125 0.859375 2.859375 C 0.859375 2.984375 0.984375 2.984375 1.09375 2.984375 C 2.296875 2.984375 3.359375 2.375 3.375 1.515625 L 3.375 -0.671875 C 3.375 -1.796875 3.375 -1.984375 3.6875 -2.3125 C 3.859375 -2.484375 4.1875 -2.8125 4.9375 -2.859375 C 5.015625 -2.859375 5.109375 -2.875 5.109375 -2.984375 C 5.109375 -3.109375 5.03125 -3.109375 4.90625 -3.125 C 4.40625 -3.15625 3.375 -3.40625 3.375 -4.609375 Z M 3.375 -6.96875 "/> +</symbol> +<symbol overflow="visible" id="glyph3-0"> +<path style="stroke:none;" d="M 6.875 -0.25 L 6.828125 -0.25 C 6.4375 -0.25 6.28125 -0.453125 5.78125 -1.640625 L 3.5625 -6.875 L 3.28125 -6.875 L 1.0625 -1.421875 C 0.6875 -0.484375 0.5625 -0.34375 0.09375 -0.25 L 0.09375 0 L 2.109375 0 L 2.109375 -0.25 C 1.53125 -0.296875 1.296875 -0.40625 1.296875 -0.65625 C 1.296875 -0.78125 1.375 -1.03125 1.578125 -1.578125 L 1.734375 -1.96875 L 3.984375 -1.96875 C 4.3125 -1.1875 4.4375 -0.828125 4.4375 -0.609375 C 4.4375 -0.40625 4.3125 -0.296875 3.953125 -0.28125 C 3.90625 -0.28125 3.78125 -0.265625 3.640625 -0.25 L 3.640625 0 L 6.875 0 Z M 1.890625 -2.359375 L 2.828125 -4.78125 L 3.828125 -2.359375 Z M 1.890625 -2.359375 "/> +</symbol> +<symbol overflow="visible" id="glyph3-1"> +<path style="stroke:none;" d="M 2.109375 -6.734375 L 0.171875 -6.734375 L 0.171875 -6.5 C 0.625 -6.40625 0.71875 -6.3125 0.71875 -5.921875 L 0.71875 0.125 L 0.84375 0.125 L 1.625 -0.421875 C 2.078125 -0.015625 2.4375 0.140625 2.9375 0.140625 C 4.265625 0.140625 5.1875 -0.890625 5.1875 -2.375 C 5.1875 -3.765625 4.421875 -4.71875 3.3125 -4.71875 C 2.84375 -4.71875 2.46875 -4.546875 2.109375 -4.15625 Z M 2.109375 -3.59375 C 2.265625 -4.015625 2.46875 -4.171875 2.796875 -4.171875 C 3.421875 -4.171875 3.734375 -3.515625 3.734375 -2.203125 C 3.734375 -0.828125 3.421875 -0.171875 2.796875 -0.171875 C 2.375 -0.171875 2.109375 -0.484375 2.109375 -0.96875 Z M 2.109375 -3.59375 "/> +</symbol> +<symbol overflow="visible" id="glyph3-2"> +<path style="stroke:none;" d="M 3.390625 -3.25 L 3.390625 -4.6875 L 3.171875 -4.6875 C 3.109375 -4.546875 3.046875 -4.5 2.9375 -4.5 C 2.875 -4.5 2.765625 -4.515625 2.609375 -4.5625 C 2.28125 -4.671875 2.046875 -4.71875 1.828125 -4.71875 C 0.921875 -4.71875 0.265625 -4.09375 0.265625 -3.265625 C 0.265625 -2.609375 0.671875 -2.140625 1.6875 -1.71875 C 2.359375 -1.421875 2.640625 -1.171875 2.640625 -0.84375 C 2.640625 -0.453125 2.34375 -0.203125 1.890625 -0.203125 C 1.203125 -0.203125 0.734375 -0.640625 0.53125 -1.515625 L 0.25 -1.515625 L 0.25 0.125 L 0.5 0.125 C 0.609375 -0.078125 0.671875 -0.15625 0.75 -0.15625 C 0.8125 -0.15625 0.890625 -0.125 0.984375 -0.09375 C 1.25 0.03125 1.78125 0.140625 2.0625 0.140625 C 2.96875 0.140625 3.59375 -0.484375 3.59375 -1.375 C 3.59375 -2.078125 3.203125 -2.515625 2.21875 -2.9375 C 1.546875 -3.21875 1.265625 -3.46875 1.265625 -3.8125 C 1.265625 -4.140625 1.546875 -4.390625 1.921875 -4.390625 C 2.1875 -4.390625 2.453125 -4.28125 2.671875 -4.0625 C 2.875 -3.875 2.984375 -3.6875 3.140625 -3.25 Z M 3.390625 -3.25 "/> +</symbol> +<symbol overflow="visible" id="glyph3-3"> +<path style="stroke:none;" d="M 3.046875 -4.59375 L 2.109375 -4.59375 L 2.109375 -6.28125 L 1.859375 -6.28125 C 1.25 -5.421875 0.84375 -4.96875 0.203125 -4.421875 L 0.203125 -4.15625 L 0.71875 -4.15625 L 0.71875 -0.921875 C 0.71875 -0.28125 1.140625 0.125 1.828125 0.125 C 2.5 0.125 2.90625 -0.171875 3.3125 -1 L 3.0625 -1.109375 C 2.859375 -0.734375 2.703125 -0.59375 2.5 -0.59375 C 2.21875 -0.59375 2.109375 -0.75 2.109375 -1.15625 L 2.109375 -4.15625 L 3.046875 -4.15625 Z M 3.046875 -4.59375 "/> +</symbol> +<symbol overflow="visible" id="glyph3-4"> +<path style="stroke:none;" d="M 2.171875 -4.59375 L 0.296875 -4.59375 L 0.296875 -4.359375 C 0.71875 -4.296875 0.828125 -4.171875 0.828125 -3.765625 L 0.828125 -0.84375 C 0.828125 -0.421875 0.734375 -0.3125 0.296875 -0.234375 L 0.296875 0 L 2.9375 0 L 2.9375 -0.234375 C 2.328125 -0.28125 2.21875 -0.40625 2.21875 -1.03125 L 2.21875 -2.90625 C 2.21875 -3.421875 2.5 -3.859375 2.828125 -3.859375 C 2.90625 -3.859375 2.984375 -3.78125 3.09375 -3.625 C 3.296875 -3.359375 3.4375 -3.265625 3.703125 -3.265625 C 4.0625 -3.265625 4.328125 -3.546875 4.328125 -3.9375 C 4.328125 -4.390625 3.984375 -4.71875 3.515625 -4.71875 C 3.015625 -4.71875 2.640625 -4.453125 2.171875 -3.78125 Z M 2.171875 -4.59375 "/> +</symbol> +<symbol overflow="visible" id="glyph3-5"> +<path style="stroke:none;" d="M 4.71875 -0.640625 L 4.609375 -0.53125 C 4.578125 -0.515625 4.5625 -0.5 4.5 -0.5 C 4.359375 -0.5 4.296875 -0.59375 4.296875 -0.75 L 4.296875 -3.34375 C 4.296875 -4.1875 3.53125 -4.71875 2.328125 -4.71875 C 1.203125 -4.71875 0.4375 -4.203125 0.4375 -3.453125 C 0.4375 -3.046875 0.671875 -2.796875 1.09375 -2.796875 C 1.484375 -2.796875 1.765625 -3.046875 1.765625 -3.375 C 1.765625 -3.515625 1.703125 -3.65625 1.578125 -3.8125 C 1.5 -3.90625 1.46875 -3.96875 1.46875 -4.03125 C 1.46875 -4.234375 1.75 -4.390625 2.09375 -4.390625 C 2.671875 -4.390625 2.9375 -4.125 2.9375 -3.515625 L 2.9375 -2.796875 C 1.75 -2.4375 1.25 -2.234375 0.890625 -1.984375 C 0.453125 -1.6875 0.25 -1.34375 0.25 -0.921875 C 0.25 -0.3125 0.71875 0.140625 1.359375 0.140625 C 1.921875 0.140625 2.375 -0.0625 2.9375 -0.5625 C 3.046875 -0.046875 3.265625 0.140625 3.75 0.140625 C 4.171875 0.140625 4.484375 -0.015625 4.859375 -0.421875 Z M 2.921875 -1 C 2.65625 -0.6875 2.453125 -0.5625 2.21875 -0.5625 C 1.90625 -0.5625 1.703125 -0.84375 1.703125 -1.234375 C 1.703125 -1.8125 2.125 -2.234375 2.921875 -2.4375 Z M 2.921875 -1 "/> +</symbol> +<symbol overflow="visible" id="glyph3-6"> +<path style="stroke:none;" d="M 4.109375 -1.09375 C 3.734375 -0.671875 3.484375 -0.53125 3.078125 -0.53125 C 2.203125 -0.53125 1.65625 -1.390625 1.65625 -2.75 C 1.65625 -3.78125 1.96875 -4.40625 2.5 -4.40625 C 2.65625 -4.40625 2.796875 -4.328125 2.859375 -4.21875 C 2.90625 -4.125 2.90625 -4.125 2.90625 -3.703125 C 2.921875 -3.21875 3.09375 -2.984375 3.46875 -2.984375 C 3.890625 -2.984375 4.140625 -3.234375 4.140625 -3.625 C 4.140625 -4.234375 3.484375 -4.71875 2.609375 -4.71875 C 1.25 -4.71875 0.25 -3.65625 0.25 -2.21875 C 0.25 -0.84375 1.140625 0.140625 2.375 0.140625 C 3.15625 0.140625 3.703125 -0.171875 4.28125 -0.90625 Z M 4.109375 -1.09375 "/> +</symbol> +<symbol overflow="visible" id="glyph4-0"> +<path style="stroke:none;" d="M 9.296875 -6.59375 L 7.3125 -6.59375 L 7.3125 -6.40625 C 7.828125 -6.359375 8 -6.25 8 -5.984375 C 8 -5.765625 7.9375 -5.515625 7.84375 -5.234375 L 6.59375 -1.859375 L 5.28125 -5.25 C 5.265625 -5.296875 5.171875 -5.515625 5.15625 -5.546875 C 5.078125 -5.765625 5.015625 -5.953125 5.015625 -6.0625 C 5.015625 -6.296875 5.25 -6.40625 5.78125 -6.40625 L 5.78125 -6.59375 L 3.125 -6.59375 L 3.125 -6.40625 C 3.6875 -6.40625 3.78125 -6.3125 4.125 -5.515625 L 4.453125 -4.6875 L 3.390625 -1.890625 L 1.953125 -5.625 C 1.890625 -5.8125 1.84375 -5.984375 1.84375 -6.09375 C 1.84375 -6.3125 1.984375 -6.375 2.5 -6.40625 L 2.5 -6.59375 L 0.046875 -6.59375 L 0.046875 -6.40625 C 0.5625 -6.359375 0.703125 -6.1875 1.078125 -5.25 L 3 0.109375 L 3.15625 0.109375 L 4.6875 -4.109375 L 6.28125 0.109375 L 6.4375 0.109375 C 7.28125 -2.5 7.375 -2.765625 8.46875 -5.703125 C 8.65625 -6.21875 8.765625 -6.3125 9.296875 -6.40625 Z M 9.296875 -6.59375 "/> +</symbol> +<symbol overflow="visible" id="glyph4-1"> +<path style="stroke:none;" d="M 4.0625 -1.640625 C 3.59375 -0.875 3.15625 -0.59375 2.515625 -0.59375 C 1.953125 -0.59375 1.53125 -0.875 1.234375 -1.453125 C 1.0625 -1.828125 0.984375 -2.15625 0.96875 -2.765625 L 4.03125 -2.765625 C 3.953125 -3.40625 3.859375 -3.703125 3.609375 -4.015625 C 3.3125 -4.375 2.84375 -4.578125 2.328125 -4.578125 C 1.828125 -4.578125 1.359375 -4.40625 0.984375 -4.0625 C 0.515625 -3.65625 0.25 -2.953125 0.25 -2.140625 C 0.25 -0.75 0.96875 0.09375 2.109375 0.09375 C 3.0625 0.09375 3.8125 -0.484375 4.234375 -1.5625 Z M 0.984375 -3.078125 C 1.09375 -3.859375 1.4375 -4.234375 2.046875 -4.234375 C 2.65625 -4.234375 2.890625 -3.953125 3.015625 -3.078125 Z M 0.984375 -3.078125 "/> +</symbol> +<symbol overflow="visible" id="glyph4-2"> +<path style="stroke:none;" d="M 1.53125 -6.78125 L 1.46875 -6.8125 C 1.0625 -6.65625 0.78125 -6.578125 0.3125 -6.453125 L 0.03125 -6.375 L 0.03125 -6.203125 C 0.09375 -6.21875 0.125 -6.21875 0.203125 -6.21875 C 0.59375 -6.21875 0.6875 -6.125 0.6875 -5.71875 L 0.6875 -0.53125 C 0.6875 -0.234375 1.53125 0.09375 2.328125 0.09375 C 3.65625 0.09375 4.671875 -1 4.671875 -2.421875 C 4.671875 -3.65625 3.90625 -4.578125 2.90625 -4.578125 C 2.296875 -4.578125 1.71875 -4.234375 1.53125 -3.734375 Z M 1.53125 -3.203125 C 1.53125 -3.59375 2 -3.953125 2.515625 -3.953125 C 3.296875 -3.953125 3.78125 -3.1875 3.78125 -1.96875 C 3.78125 -0.859375 3.3125 -0.21875 2.5 -0.21875 C 1.96875 -0.21875 1.53125 -0.453125 1.53125 -0.703125 Z M 1.53125 -3.203125 "/> +</symbol> +<symbol overflow="visible" id="glyph4-3"> +<path style="stroke:none;" d="M 0.0625 -3.890625 C 0.203125 -3.921875 0.296875 -3.921875 0.421875 -3.921875 C 0.671875 -3.921875 0.75 -3.765625 0.75 -3.328125 L 0.75 -0.84375 C 0.75 -0.34375 0.6875 -0.265625 0.046875 -0.15625 L 0.046875 0 L 2.4375 0 L 2.4375 -0.15625 C 1.765625 -0.171875 1.59375 -0.328125 1.59375 -0.890625 L 1.59375 -3.140625 C 1.59375 -3.453125 2.03125 -3.953125 2.296875 -3.953125 C 2.359375 -3.953125 2.4375 -3.90625 2.546875 -3.8125 C 2.71875 -3.671875 2.828125 -3.609375 2.953125 -3.609375 C 3.1875 -3.609375 3.34375 -3.78125 3.34375 -4.0625 C 3.34375 -4.390625 3.125 -4.578125 2.796875 -4.578125 C 2.375 -4.578125 2.078125 -4.359375 1.59375 -3.65625 L 1.59375 -4.5625 L 1.546875 -4.578125 C 1.015625 -4.359375 0.65625 -4.234375 0.0625 -4.046875 Z M 0.0625 -3.890625 "/> +</symbol> +<symbol overflow="visible" id="glyph4-4"> +<path style="stroke:none;" d="M 2.5 -4.578125 C 1.203125 -4.578125 0.296875 -3.625 0.296875 -2.25 C 0.296875 -0.90625 1.21875 0.09375 2.46875 0.09375 C 3.734375 0.09375 4.6875 -0.953125 4.6875 -2.328125 C 4.6875 -3.640625 3.765625 -4.578125 2.5 -4.578125 Z M 2.359375 -4.3125 C 3.203125 -4.3125 3.78125 -3.34375 3.78125 -1.984375 C 3.78125 -0.859375 3.34375 -0.171875 2.59375 -0.171875 C 2.203125 -0.171875 1.828125 -0.421875 1.625 -0.8125 C 1.34375 -1.328125 1.1875 -2.03125 1.1875 -2.734375 C 1.1875 -3.6875 1.65625 -4.3125 2.359375 -4.3125 Z M 2.359375 -4.3125 "/> +</symbol> +<symbol overflow="visible" id="glyph4-5"> +<path style="stroke:none;" d="M 5.6875 -4.484375 L 5.6875 -4.34375 C 6.03125 -4.265625 6.125 -4.203125 6.125 -4.03125 C 6.125 -3.875 6.078125 -3.640625 5.96875 -3.375 L 5.0625 -1.15625 L 4.234375 -3.390625 C 4.0625 -3.84375 4.0625 -3.84375 4.0625 -3.984375 C 4.0625 -4.203125 4.171875 -4.265625 4.640625 -4.34375 L 4.640625 -4.484375 L 2.609375 -4.484375 L 2.609375 -4.34375 C 2.984375 -4.296875 3.09375 -4.1875 3.296875 -3.65625 C 3.375 -3.453125 3.4375 -3.265625 3.5 -3.09375 L 2.59375 -1.109375 L 1.609375 -3.703125 C 1.5625 -3.8125 1.546875 -3.90625 1.546875 -4 C 1.546875 -4.21875 1.671875 -4.296875 2 -4.34375 L 2 -4.484375 L 0.203125 -4.484375 L 0.203125 -4.34375 C 0.4375 -4.3125 0.515625 -4.21875 0.734375 -3.703125 L 2.078125 -0.296875 C 2.203125 0 2.28125 0.140625 2.34375 0.140625 C 2.390625 0.140625 2.46875 0.015625 2.59375 -0.25 L 3.703125 -2.640625 L 4.609375 -0.296875 C 4.75 0.078125 4.796875 0.140625 4.859375 0.140625 C 4.921875 0.140625 4.96875 0.046875 5.140625 -0.34375 L 6.515625 -3.796875 C 6.6875 -4.21875 6.71875 -4.265625 6.921875 -4.34375 L 6.921875 -4.484375 Z M 5.6875 -4.484375 "/> +</symbol> +<symbol overflow="visible" id="glyph4-6"> +<path style="stroke:none;" d="M 3.140625 -3.125 L 3.09375 -4.484375 L 2.984375 -4.484375 L 2.96875 -4.46875 C 2.875 -4.390625 2.875 -4.390625 2.828125 -4.390625 C 2.765625 -4.390625 2.671875 -4.40625 2.5625 -4.453125 C 2.34375 -4.53125 2.125 -4.578125 1.859375 -4.578125 C 1.078125 -4.578125 0.515625 -4.0625 0.515625 -3.34375 C 0.515625 -2.796875 0.828125 -2.390625 1.671875 -1.90625 L 2.25 -1.578125 C 2.609375 -1.390625 2.765625 -1.140625 2.765625 -0.84375 C 2.765625 -0.40625 2.453125 -0.125 1.9375 -0.125 C 1.609375 -0.125 1.296875 -0.25 1.109375 -0.46875 C 0.890625 -0.71875 0.8125 -0.953125 0.671875 -1.515625 L 0.515625 -1.515625 L 0.515625 0.046875 L 0.640625 0.046875 C 0.71875 -0.0625 0.75 -0.078125 0.875 -0.078125 C 0.96875 -0.078125 1.109375 -0.0625 1.328125 0 C 1.609375 0.0625 1.890625 0.09375 2.0625 0.09375 C 2.828125 0.09375 3.46875 -0.484375 3.46875 -1.171875 C 3.46875 -1.671875 3.234375 -2 2.625 -2.359375 L 1.5625 -3 C 1.28125 -3.15625 1.125 -3.40625 1.125 -3.671875 C 1.125 -4.078125 1.4375 -4.359375 1.890625 -4.359375 C 2.46875 -4.359375 2.765625 -4.015625 2.984375 -3.125 Z M 3.140625 -3.125 "/> +</symbol> +<symbol overflow="visible" id="glyph4-7"> +<path style="stroke:none;" d="M 1.75 -4.578125 L 0.203125 -4.03125 L 0.203125 -3.890625 L 0.28125 -3.890625 C 0.40625 -3.921875 0.53125 -3.921875 0.625 -3.921875 C 0.859375 -3.921875 0.953125 -3.765625 0.953125 -3.328125 L 0.953125 -1.015625 C 0.953125 -0.296875 0.84375 -0.1875 0.15625 -0.15625 L 0.15625 0 L 2.515625 0 L 2.515625 -0.15625 C 1.859375 -0.203125 1.78125 -0.296875 1.78125 -1.015625 L 1.78125 -4.5625 Z M 1.28125 -6.8125 C 1 -6.8125 0.78125 -6.578125 0.78125 -6.296875 C 0.78125 -6.015625 1 -5.796875 1.28125 -5.796875 C 1.5625 -5.796875 1.796875 -6.015625 1.796875 -6.296875 C 1.796875 -6.578125 1.5625 -6.8125 1.28125 -6.8125 Z M 1.28125 -6.8125 "/> +</symbol> +<symbol overflow="visible" id="glyph4-8"> +<path style="stroke:none;" d="M 4.6875 -3.875 L 4.6875 -4.25 L 3.921875 -4.25 C 3.71875 -4.25 3.5625 -4.28125 3.375 -4.359375 L 3.15625 -4.4375 C 2.875 -4.53125 2.609375 -4.578125 2.359375 -4.578125 C 1.421875 -4.578125 0.6875 -3.875 0.6875 -2.953125 C 0.6875 -2.328125 0.953125 -1.953125 1.609375 -1.625 C 1.421875 -1.453125 1.25 -1.28125 1.1875 -1.21875 C 0.859375 -0.9375 0.734375 -0.734375 0.734375 -0.53125 C 0.734375 -0.328125 0.84375 -0.203125 1.25 -0.015625 C 0.546875 0.515625 0.28125 0.84375 0.28125 1.203125 C 0.28125 1.734375 1.0625 2.171875 2 2.171875 C 2.75 2.171875 3.53125 1.90625 4.046875 1.5 C 4.421875 1.1875 4.59375 0.875 4.59375 0.484375 C 4.59375 -0.125 4.125 -0.546875 3.390625 -0.578125 L 2.109375 -0.640625 C 1.578125 -0.65625 1.328125 -0.75 1.328125 -0.90625 C 1.328125 -1.109375 1.65625 -1.453125 1.921875 -1.53125 C 2.015625 -1.53125 2.078125 -1.515625 2.109375 -1.515625 C 2.296875 -1.5 2.4375 -1.484375 2.5 -1.484375 C 2.859375 -1.484375 3.265625 -1.640625 3.5625 -1.90625 C 3.890625 -2.1875 4.046875 -2.53125 4.046875 -3.03125 C 4.046875 -3.3125 4 -3.546875 3.859375 -3.875 Z M 1.46875 0.015625 C 1.796875 0.09375 2.59375 0.15625 3.078125 0.15625 C 3.984375 0.15625 4.3125 0.28125 4.3125 0.640625 C 4.3125 1.21875 3.5625 1.609375 2.4375 1.609375 C 1.5625 1.609375 0.984375 1.3125 0.984375 0.875 C 0.984375 0.640625 1.046875 0.515625 1.46875 0.015625 Z M 1.515625 -3.375 C 1.515625 -3.953125 1.796875 -4.3125 2.25 -4.3125 C 2.5625 -4.3125 2.828125 -4.140625 2.984375 -3.84375 C 3.15625 -3.484375 3.28125 -3.03125 3.28125 -2.640625 C 3.28125 -2.078125 2.984375 -1.734375 2.53125 -1.734375 C 1.9375 -1.734375 1.515625 -2.375 1.515625 -3.34375 Z M 1.515625 -3.375 "/> +</symbol> +<symbol overflow="visible" id="glyph4-9"> +<path style="stroke:none;" d="M 0.15625 -3.96875 C 0.21875 -4 0.3125 -4 0.421875 -4 C 0.703125 -4 0.796875 -3.859375 0.796875 -3.375 L 0.796875 -0.890625 C 0.796875 -0.328125 0.6875 -0.1875 0.171875 -0.15625 L 0.171875 0 L 2.296875 0 L 2.296875 -0.15625 C 1.78125 -0.1875 1.640625 -0.3125 1.640625 -0.671875 L 1.640625 -3.46875 C 2.109375 -3.921875 2.328125 -4.03125 2.65625 -4.03125 C 3.15625 -4.03125 3.390625 -3.734375 3.390625 -3.078125 L 3.390625 -0.984375 C 3.390625 -0.359375 3.265625 -0.1875 2.765625 -0.15625 L 2.765625 0 L 4.828125 0 L 4.828125 -0.15625 C 4.34375 -0.203125 4.234375 -0.3125 4.234375 -0.8125 L 4.234375 -3.09375 C 4.234375 -4.03125 3.78125 -4.578125 3.046875 -4.578125 C 2.59375 -4.578125 2.28125 -4.421875 1.609375 -3.78125 L 1.609375 -4.5625 L 1.53125 -4.578125 C 1.046875 -4.40625 0.703125 -4.296875 0.15625 -4.140625 Z M 0.15625 -3.96875 "/> +</symbol> +<symbol overflow="visible" id="glyph4-10"> +<path style="stroke:none;" d="M 4.40625 -0.65625 C 4.234375 -0.515625 4.109375 -0.46875 3.96875 -0.46875 C 3.734375 -0.46875 3.671875 -0.609375 3.671875 -1.046875 L 3.671875 -2.984375 C 3.671875 -3.515625 3.625 -3.796875 3.46875 -4.03125 C 3.25 -4.390625 2.828125 -4.578125 2.234375 -4.578125 C 1.296875 -4.578125 0.5625 -4.09375 0.5625 -3.46875 C 0.5625 -3.234375 0.75 -3.046875 0.984375 -3.046875 C 1.21875 -3.046875 1.4375 -3.234375 1.4375 -3.453125 C 1.4375 -3.5 1.421875 -3.546875 1.421875 -3.625 C 1.390625 -3.703125 1.390625 -3.78125 1.390625 -3.859375 C 1.390625 -4.125 1.703125 -4.34375 2.109375 -4.34375 C 2.59375 -4.34375 2.859375 -4.0625 2.859375 -3.515625 L 2.859375 -2.90625 C 1.328125 -2.296875 1.15625 -2.21875 0.734375 -1.828125 C 0.515625 -1.640625 0.375 -1.296875 0.375 -0.96875 C 0.375 -0.34375 0.8125 0.09375 1.421875 0.09375 C 1.859375 0.09375 2.265625 -0.109375 2.875 -0.625 C 2.921875 -0.109375 3.09375 0.09375 3.515625 0.09375 C 3.84375 0.09375 4.0625 -0.015625 4.40625 -0.40625 Z M 2.859375 -1.21875 C 2.859375 -0.921875 2.8125 -0.828125 2.609375 -0.703125 C 2.359375 -0.5625 2.078125 -0.484375 1.875 -0.484375 C 1.53125 -0.484375 1.25 -0.8125 1.25 -1.25 L 1.25 -1.28125 C 1.25 -1.875 1.65625 -2.234375 2.859375 -2.671875 Z M 2.859375 -1.21875 "/> +</symbol> +<symbol overflow="visible" id="glyph4-11"> +<path style="stroke:none;" d="M 2.546875 -4.484375 L 1.53125 -4.484375 L 1.53125 -5.640625 C 1.53125 -5.734375 1.53125 -5.765625 1.46875 -5.765625 C 1.390625 -5.6875 1.328125 -5.59375 1.265625 -5.5 C 0.890625 -4.9375 0.453125 -4.46875 0.296875 -4.421875 C 0.1875 -4.359375 0.125 -4.28125 0.125 -4.234375 C 0.125 -4.203125 0.140625 -4.1875 0.171875 -4.171875 L 0.703125 -4.171875 L 0.703125 -1.171875 C 0.703125 -0.328125 1 0.09375 1.578125 0.09375 C 2.078125 0.09375 2.453125 -0.140625 2.78125 -0.65625 L 2.65625 -0.765625 C 2.4375 -0.515625 2.265625 -0.421875 2.046875 -0.421875 C 1.6875 -0.421875 1.53125 -0.6875 1.53125 -1.3125 L 1.53125 -4.171875 L 2.546875 -4.171875 Z M 2.546875 -4.484375 "/> +</symbol> +<symbol overflow="visible" id="glyph4-12"> +<path style="stroke:none;" d="M 3.421875 0.09375 L 4.890625 -0.421875 L 4.890625 -0.578125 C 4.71875 -0.5625 4.6875 -0.5625 4.671875 -0.5625 C 4.3125 -0.5625 4.234375 -0.671875 4.234375 -1.140625 L 4.234375 -6.78125 L 4.171875 -6.8125 C 3.703125 -6.640625 3.34375 -6.546875 2.71875 -6.375 L 2.71875 -6.203125 C 2.796875 -6.21875 2.84375 -6.21875 2.9375 -6.21875 C 3.296875 -6.21875 3.390625 -6.125 3.390625 -5.71875 L 3.390625 -4.15625 C 3.015625 -4.46875 2.734375 -4.578125 2.34375 -4.578125 C 1.203125 -4.578125 0.265625 -3.453125 0.265625 -2.046875 C 0.265625 -0.765625 1.015625 0.09375 2.109375 0.09375 C 2.671875 0.09375 3.046875 -0.09375 3.390625 -0.5625 L 3.390625 0.0625 Z M 3.390625 -1.015625 C 3.390625 -0.953125 3.3125 -0.828125 3.21875 -0.71875 C 3.046875 -0.515625 2.796875 -0.421875 2.5 -0.421875 C 1.671875 -0.421875 1.125 -1.21875 1.125 -2.4375 C 1.125 -3.5625 1.609375 -4.3125 2.375 -4.3125 C 2.90625 -4.3125 3.390625 -3.84375 3.390625 -3.3125 Z M 3.390625 -1.015625 "/> +</symbol> +<symbol overflow="visible" id="glyph4-13"> +<path style="stroke:none;" d="M 0.09375 -3.921875 C 0.171875 -3.921875 0.25 -3.921875 0.34375 -3.921875 C 0.671875 -3.921875 0.75 -3.828125 0.75 -3.359375 L 0.75 1.3125 C 0.75 1.828125 0.640625 1.9375 0.046875 2 L 0.046875 2.15625 L 2.46875 2.15625 L 2.46875 1.984375 C 1.71875 1.96875 1.578125 1.859375 1.578125 1.234375 L 1.578125 -0.328125 C 1.9375 0 2.171875 0.09375 2.59375 0.09375 C 3.765625 0.09375 4.6875 -1.015625 4.6875 -2.46875 C 4.6875 -3.703125 3.984375 -4.578125 3.015625 -4.578125 C 2.46875 -4.578125 2.03125 -4.34375 1.578125 -3.796875 L 1.578125 -4.5625 L 1.53125 -4.578125 C 0.984375 -4.375 0.640625 -4.25 0.09375 -4.078125 Z M 1.578125 -3.328125 C 1.578125 -3.625 2.140625 -3.984375 2.609375 -3.984375 C 3.34375 -3.984375 3.828125 -3.234375 3.828125 -2.078125 C 3.828125 -0.96875 3.34375 -0.21875 2.625 -0.21875 C 2.15625 -0.21875 1.578125 -0.578125 1.578125 -0.875 Z M 1.578125 -3.328125 "/> +</symbol> +<symbol overflow="visible" id="glyph4-14"> +<path style="stroke:none;" d="M 0.1875 -6.203125 L 0.25 -6.203125 C 0.359375 -6.21875 0.484375 -6.234375 0.5625 -6.234375 C 0.875 -6.234375 0.984375 -6.09375 0.984375 -5.625 L 0.984375 -0.875 C 0.984375 -0.328125 0.84375 -0.203125 0.203125 -0.15625 L 0.203125 0 L 2.5625 0 L 2.5625 -0.15625 C 1.9375 -0.1875 1.8125 -0.296875 1.8125 -0.84375 L 1.8125 -6.78125 L 1.78125 -6.8125 C 1.25 -6.640625 0.875 -6.546875 0.1875 -6.375 Z M 0.1875 -6.203125 "/> +</symbol> +<symbol overflow="visible" id="glyph4-15"> +<path style="stroke:none;" d="M 3.96875 -1.5625 C 3.484375 -0.859375 3.125 -0.625 2.5625 -0.625 C 1.65625 -0.625 1.015625 -1.421875 1.015625 -2.5625 C 1.015625 -3.59375 1.5625 -4.296875 2.375 -4.296875 C 2.734375 -4.296875 2.859375 -4.1875 2.953125 -3.8125 L 3.015625 -3.59375 C 3.09375 -3.3125 3.28125 -3.140625 3.484375 -3.140625 C 3.75 -3.140625 3.96875 -3.328125 3.96875 -3.5625 C 3.96875 -4.109375 3.265625 -4.578125 2.4375 -4.578125 C 1.9375 -4.578125 1.4375 -4.390625 1.03125 -4.03125 C 0.53125 -3.59375 0.25 -2.90625 0.25 -2.125 C 0.25 -0.828125 1.03125 0.09375 2.140625 0.09375 C 2.59375 0.09375 2.984375 -0.0625 3.34375 -0.375 C 3.625 -0.609375 3.8125 -0.875 4.109375 -1.46875 Z M 3.96875 -1.5625 "/> +</symbol> +<symbol overflow="visible" id="glyph4-16"> +<path style="stroke:none;" d="M 1.5625 -3.421875 C 1.984375 -3.875 2.28125 -4.046875 2.671875 -4.046875 C 3.171875 -4.046875 3.421875 -3.6875 3.421875 -2.984375 L 3.421875 -1.015625 C 3.421875 -0.34375 3.3125 -0.203125 2.734375 -0.15625 L 2.734375 0 L 4.859375 0 L 4.859375 -0.15625 C 4.3125 -0.25 4.25 -0.328125 4.25 -1.015625 L 4.25 -3 C 4.25 -4.046875 3.84375 -4.578125 3.03125 -4.578125 C 2.4375 -4.578125 2.03125 -4.34375 1.5625 -3.75 L 1.5625 -6.78125 L 1.515625 -6.8125 C 1.171875 -6.6875 0.921875 -6.609375 0.375 -6.453125 L 0.09375 -6.375 L 0.09375 -6.203125 C 0.140625 -6.21875 0.171875 -6.21875 0.21875 -6.21875 C 0.640625 -6.21875 0.734375 -6.140625 0.734375 -5.71875 L 0.734375 -1.015625 C 0.734375 -0.3125 0.671875 -0.234375 0.09375 -0.15625 L 0.09375 0 L 2.25 0 L 2.25 -0.15625 C 1.671875 -0.203125 1.5625 -0.328125 1.5625 -1.015625 Z M 1.5625 -3.421875 "/> +</symbol> +<symbol overflow="visible" id="glyph4-17"> +<path style="stroke:none;" d="M 4.78125 -0.5 L 4.71875 -0.5 C 4.265625 -0.5 4.15625 -0.609375 4.15625 -1.0625 L 4.15625 -4.484375 L 2.578125 -4.484375 L 2.578125 -4.3125 C 3.203125 -4.28125 3.3125 -4.1875 3.3125 -3.6875 L 3.3125 -1.34375 C 3.3125 -1.0625 3.265625 -0.921875 3.125 -0.8125 C 2.859375 -0.59375 2.546875 -0.484375 2.25 -0.484375 C 1.859375 -0.484375 1.546875 -0.8125 1.546875 -1.234375 L 1.546875 -4.484375 L 0.09375 -4.484375 L 0.09375 -4.34375 C 0.5625 -4.3125 0.703125 -4.171875 0.703125 -3.703125 L 0.703125 -1.203125 C 0.703125 -0.40625 1.1875 0.09375 1.90625 0.09375 C 2.28125 0.09375 2.671875 -0.0625 2.9375 -0.328125 L 3.375 -0.75 L 3.375 0.0625 L 3.40625 0.09375 C 3.90625 -0.109375 4.265625 -0.21875 4.78125 -0.359375 Z M 4.78125 -0.5 "/> +</symbol> +<symbol overflow="visible" id="glyph4-18"> +<path style="stroke:none;" d="M 4.75 -4.484375 L 3.375 -4.484375 L 3.375 -4.34375 C 3.6875 -4.3125 3.84375 -4.203125 3.84375 -4.015625 C 3.84375 -3.921875 3.8125 -3.8125 3.78125 -3.71875 L 2.796875 -1.140625 L 1.78125 -3.6875 C 1.71875 -3.828125 1.6875 -3.96875 1.6875 -4.0625 C 1.6875 -4.234375 1.796875 -4.3125 2.140625 -4.34375 L 2.140625 -4.484375 L 0.1875 -4.484375 L 0.1875 -4.34375 C 0.5625 -4.3125 0.640625 -4.21875 1.09375 -3.1875 L 2.296875 -0.328125 C 2.3125 -0.265625 2.34375 -0.203125 2.375 -0.125 C 2.4375 0.0625 2.5 0.140625 2.546875 0.140625 C 2.609375 0.140625 2.6875 0.015625 2.828125 -0.359375 L 4.109375 -3.5625 C 4.390625 -4.234375 4.453125 -4.3125 4.75 -4.34375 Z M 4.75 -4.484375 "/> +</symbol> +<symbol overflow="visible" id="glyph4-19"> +<path style="stroke:none;" d="M 3.59375 -4.234375 C 3.125 -4.5 2.828125 -4.59375 2.4375 -4.59375 C 1.203125 -4.59375 0.234375 -3.484375 0.234375 -2.046875 C 0.234375 -0.78125 0.890625 0.09375 1.828125 0.09375 C 2.40625 0.09375 2.953125 -0.15625 3.40625 -0.640625 L 3.40625 1.234375 C 3.40625 1.796875 3.21875 1.9375 2.515625 2 L 2.515625 2.15625 L 4.859375 2.15625 L 4.859375 2.03125 C 4.3125 1.90625 4.234375 1.828125 4.234375 1.40625 L 4.234375 -4.5625 L 4.125 -4.5625 Z M 3.40625 -1.265625 C 3.40625 -1.03125 3.359375 -0.890625 3.265625 -0.8125 C 3.046875 -0.625 2.71875 -0.515625 2.40625 -0.515625 C 2.09375 -0.515625 1.828125 -0.609375 1.625 -0.8125 C 1.3125 -1.09375 1.09375 -1.75 1.09375 -2.40625 C 1.09375 -3.578125 1.625 -4.3125 2.46875 -4.3125 C 3.09375 -4.3125 3.40625 -3.984375 3.40625 -3.3125 Z M 3.40625 -1.265625 "/> +</symbol> +<symbol overflow="visible" id="glyph4-20"> +<path style="stroke:none;" d="M 4.734375 -4.484375 L 3.390625 -4.484375 L 3.390625 -4.34375 C 3.703125 -4.34375 3.875 -4.25 3.875 -4.09375 C 3.875 -4.046875 3.859375 -3.984375 3.828125 -3.921875 L 2.859375 -1.171875 L 1.71875 -3.6875 C 1.65625 -3.828125 1.609375 -3.953125 1.609375 -4.0625 C 1.609375 -4.25 1.765625 -4.3125 2.1875 -4.34375 L 2.1875 -4.484375 L 0.140625 -4.484375 L 0.140625 -4.34375 C 0.40625 -4.3125 0.5625 -4.203125 0.640625 -4.03125 L 1.78125 -1.578125 L 1.8125 -1.5 L 1.96875 -1.203125 C 2.25 -0.703125 2.40625 -0.34375 2.40625 -0.1875 C 2.40625 -0.046875 2.171875 0.59375 2 0.890625 C 1.859375 1.140625 1.640625 1.328125 1.5 1.328125 C 1.453125 1.328125 1.359375 1.3125 1.25 1.265625 C 1.0625 1.203125 0.890625 1.15625 0.734375 1.15625 C 0.5 1.15625 0.296875 1.359375 0.296875 1.59375 C 0.296875 1.921875 0.625 2.171875 1.03125 2.171875 C 1.703125 2.171875 2.1875 1.609375 2.71875 0.171875 L 4.25 -3.890625 C 4.390625 -4.203125 4.5 -4.3125 4.734375 -4.34375 Z M 4.734375 -4.484375 "/> +</symbol> +<symbol overflow="visible" id="glyph4-21"> +<path style="stroke:none;" d="M 1.25 -1 C 0.953125 -1 0.703125 -0.734375 0.703125 -0.421875 C 0.703125 -0.140625 0.953125 0.109375 1.234375 0.109375 C 1.546875 0.109375 1.796875 -0.140625 1.796875 -0.421875 C 1.796875 -0.734375 1.546875 -1 1.25 -1 Z M 1.25 -1 "/> +</symbol> +<symbol overflow="visible" id="glyph4-22"> +<path style="stroke:none;" d="M 7.03125 -0.1875 C 6.59375 -0.21875 6.484375 -0.3125 6.140625 -1.0625 L 3.65625 -6.71875 L 3.453125 -6.71875 L 1.390625 -1.828125 C 0.75 -0.375 0.625 -0.203125 0.15625 -0.1875 L 0.15625 0 L 2.125 0 L 2.125 -0.1875 C 1.640625 -0.1875 1.453125 -0.3125 1.453125 -0.59375 C 1.453125 -0.71875 1.46875 -0.859375 1.53125 -0.984375 L 1.984375 -2.15625 L 4.59375 -2.15625 L 5 -1.203125 C 5.125 -0.921875 5.1875 -0.671875 5.1875 -0.53125 C 5.1875 -0.28125 5.03125 -0.203125 4.5 -0.1875 L 4.5 0 L 7.03125 0 Z M 2.15625 -2.5625 L 3.296875 -5.296875 L 4.453125 -2.5625 Z M 2.15625 -2.5625 "/> +</symbol> +<symbol overflow="visible" id="glyph4-23"> +<path style="stroke:none;" d="M 0.1875 -3.96875 C 0.3125 -4 0.40625 -4 0.515625 -4 C 0.765625 -4 0.859375 -3.84375 0.859375 -3.375 L 0.859375 -0.84375 C 0.859375 -0.3125 0.71875 -0.15625 0.15625 -0.15625 L 0.15625 0 L 2.375 0 L 2.375 -0.15625 C 1.84375 -0.171875 1.6875 -0.28125 1.6875 -0.671875 L 1.6875 -3.484375 C 1.6875 -3.5 1.78125 -3.59375 1.84375 -3.671875 C 2.09375 -3.890625 2.515625 -4.0625 2.875 -4.0625 C 3.3125 -4.0625 3.53125 -3.71875 3.53125 -3.015625 L 3.53125 -0.859375 C 3.53125 -0.296875 3.421875 -0.1875 2.84375 -0.15625 L 2.84375 0 L 5.078125 0 L 5.078125 -0.15625 C 4.515625 -0.15625 4.359375 -0.328125 4.359375 -0.953125 L 4.359375 -3.453125 C 4.671875 -3.890625 5 -4.0625 5.453125 -4.0625 C 6.015625 -4.0625 6.203125 -3.796875 6.203125 -2.96875 L 6.203125 -0.875 C 6.203125 -0.296875 6.125 -0.21875 5.546875 -0.15625 L 5.546875 0 L 7.71875 0 L 7.71875 -0.15625 L 7.46875 -0.171875 C 7.171875 -0.1875 7.03125 -0.375 7.03125 -0.75 L 7.03125 -2.8125 C 7.03125 -3.984375 6.65625 -4.578125 5.875 -4.578125 C 5.296875 -4.578125 4.796875 -4.328125 4.25 -3.75 C 4.078125 -4.3125 3.734375 -4.578125 3.203125 -4.578125 C 2.765625 -4.578125 2.484375 -4.453125 1.65625 -3.8125 L 1.65625 -4.5625 L 1.578125 -4.578125 C 1.078125 -4.390625 0.734375 -4.28125 0.1875 -4.140625 Z M 0.1875 -3.96875 "/> +</symbol> +<symbol overflow="visible" id="glyph4-24"> +<path style="stroke:none;" d="M 3.078125 -4.484375 L 1.859375 -4.484375 L 1.859375 -5.640625 C 1.859375 -6.21875 2.046875 -6.53125 2.421875 -6.53125 C 2.625 -6.53125 2.765625 -6.4375 2.953125 -6.140625 C 3.109375 -5.875 3.234375 -5.78125 3.40625 -5.78125 C 3.625 -5.78125 3.8125 -5.96875 3.8125 -6.1875 C 3.8125 -6.546875 3.375 -6.8125 2.78125 -6.8125 C 2.15625 -6.8125 1.640625 -6.546875 1.375 -6.078125 C 1.109375 -5.640625 1.03125 -5.28125 1.03125 -4.484375 L 0.203125 -4.484375 L 0.203125 -4.171875 L 1.03125 -4.171875 L 1.03125 -1.03125 C 1.03125 -0.3125 0.921875 -0.1875 0.203125 -0.15625 L 0.203125 0 L 2.796875 0 L 2.796875 -0.15625 C 1.96875 -0.171875 1.859375 -0.296875 1.859375 -1.03125 L 1.859375 -4.171875 L 3.078125 -4.171875 Z M 3.078125 -4.484375 "/> +</symbol> +<symbol overflow="visible" id="glyph4-25"> +<path style="stroke:none;" d="M 0.828125 1.40625 C 1.5 1.078125 1.9375 0.46875 1.9375 -0.125 C 1.9375 -0.625 1.59375 -1.015625 1.140625 -1.015625 C 0.796875 -1.015625 0.5625 -0.78125 0.5625 -0.453125 C 0.5625 -0.125 0.78125 0.0625 1.140625 0.0625 C 1.203125 0.0625 1.28125 0.046875 1.328125 0.046875 C 1.40625 0.015625 1.40625 0.015625 1.421875 0.015625 C 1.5 0.015625 1.5625 0.078125 1.5625 0.15625 C 1.5625 0.484375 1.28125 0.84375 0.734375 1.21875 Z M 0.828125 1.40625 "/> +</symbol> +<symbol overflow="visible" id="glyph4-26"> +<path style="stroke:none;" d="M 0.390625 -2.5625 L 0.390625 -1.9375 L 2.84375 -1.9375 L 2.84375 -2.5625 Z M 0.390625 -2.5625 "/> +</symbol> +<symbol overflow="visible" id="glyph4-27"> +<path style="stroke:none;" d="M 6.09375 0.109375 L 6.09375 -5.140625 C 6.09375 -5.734375 6.203125 -6.09375 6.390625 -6.234375 C 6.53125 -6.328125 6.671875 -6.375 7.046875 -6.40625 L 7.046875 -6.59375 L 4.703125 -6.59375 L 4.703125 -6.40625 C 5.078125 -6.375 5.21875 -6.34375 5.375 -6.25 C 5.578125 -6.109375 5.65625 -5.75 5.65625 -5.140625 L 5.65625 -1.78125 L 1.828125 -6.59375 L 0.125 -6.59375 L 0.125 -6.40625 C 0.546875 -6.40625 0.6875 -6.328125 1.09375 -5.859375 L 1.09375 -1.46875 C 1.09375 -0.4375 0.953125 -0.25 0.125 -0.1875 L 0.125 0 L 2.46875 0 L 2.46875 -0.1875 C 1.6875 -0.234375 1.53125 -0.453125 1.53125 -1.46875 L 1.53125 -5.375 L 5.9375 0.109375 Z M 6.09375 0.109375 "/> +</symbol> +<symbol overflow="visible" id="glyph4-28"> +<path style="stroke:none;" d="M 2.765625 0 L 4.78125 0 L 4.78125 -0.15625 C 4.46875 -0.15625 4.265625 -0.3125 3.953125 -0.75 L 2.6875 -2.703125 L 3.515625 -3.890625 C 3.703125 -4.171875 4 -4.328125 4.3125 -4.34375 L 4.3125 -4.484375 L 2.734375 -4.484375 L 2.734375 -4.34375 C 3.046875 -4.3125 3.140625 -4.25 3.140625 -4.109375 C 3.140625 -4 3.015625 -3.78125 2.765625 -3.46875 C 2.71875 -3.40625 2.609375 -3.234375 2.46875 -3.03125 L 2.328125 -3.234375 C 2.046875 -3.65625 1.875 -3.984375 1.875 -4.109375 C 1.875 -4.25 2 -4.328125 2.296875 -4.34375 L 2.296875 -4.484375 L 0.234375 -4.484375 L 0.234375 -4.34375 L 0.328125 -4.34375 C 0.625 -4.34375 0.78125 -4.203125 1.09375 -3.734375 L 2.03125 -2.296875 L 0.890625 -0.65625 C 0.59375 -0.25 0.5 -0.171875 0.171875 -0.15625 L 0.171875 0 L 1.609375 0 L 1.609375 -0.15625 C 1.328125 -0.15625 1.21875 -0.203125 1.21875 -0.328125 C 1.21875 -0.390625 1.28125 -0.53125 1.421875 -0.734375 L 2.203125 -1.96875 L 3.109375 -0.5625 C 3.15625 -0.515625 3.171875 -0.453125 3.171875 -0.390625 C 3.171875 -0.203125 3.09375 -0.171875 2.765625 -0.15625 Z M 2.765625 0 "/> +</symbol> +<symbol overflow="visible" id="glyph4-29"> +<path style="stroke:none;" d="M 0.0625 -6.203125 C 0.203125 -6.21875 0.296875 -6.234375 0.390625 -6.234375 C 0.71875 -6.234375 0.8125 -6.09375 0.8125 -5.625 L 0.8125 -0.8125 C 0.8125 -0.296875 0.78125 -0.265625 0.0625 -0.15625 L 0.0625 0 L 2.40625 0 L 2.40625 -0.15625 L 2.203125 -0.15625 C 1.796875 -0.171875 1.65625 -0.3125 1.65625 -0.671875 L 1.65625 -2.5 L 3.046875 -0.640625 L 3.078125 -0.59375 C 3.09375 -0.5625 3.125 -0.53125 3.15625 -0.515625 C 3.234375 -0.40625 3.265625 -0.34375 3.265625 -0.296875 C 3.265625 -0.203125 3.171875 -0.15625 3.046875 -0.15625 L 2.859375 -0.15625 L 2.859375 0 L 5.03125 0 L 5.03125 -0.15625 C 4.59375 -0.171875 4.28125 -0.375 3.875 -0.875 L 2.34375 -2.8125 L 2.625 -3.078125 C 3.34375 -3.734375 3.953125 -4.203125 4.25 -4.28125 C 4.390625 -4.3125 4.53125 -4.34375 4.703125 -4.34375 L 4.78125 -4.34375 L 4.78125 -4.484375 L 2.75 -4.484375 L 2.75 -4.34375 C 3.140625 -4.34375 3.25 -4.296875 3.25 -4.15625 C 3.25 -4.078125 3.15625 -3.9375 3.015625 -3.8125 L 1.65625 -2.609375 L 1.65625 -6.78125 L 1.609375 -6.8125 C 1.234375 -6.6875 0.953125 -6.609375 0.375 -6.453125 L 0.0625 -6.375 Z M 0.0625 -6.203125 "/> +</symbol> +<symbol overflow="visible" id="glyph4-30"> +<path style="stroke:none;" d="M 1.140625 -1.09375 C 1.140625 -0.34375 1.015625 -0.21875 0.171875 -0.1875 L 0.171875 0 L 3.140625 0 L 3.140625 -0.1875 C 2.328125 -0.21875 2.15625 -0.359375 2.15625 -1.09375 L 2.15625 -5.515625 C 2.15625 -6.234375 2.296875 -6.375 3.140625 -6.40625 L 3.140625 -6.59375 L 0.171875 -6.59375 L 0.171875 -6.40625 C 1.03125 -6.359375 1.140625 -6.25 1.140625 -5.515625 Z M 1.140625 -1.09375 "/> +</symbol> +<symbol overflow="visible" id="glyph4-31"> +<path style="stroke:none;" d="M 5.953125 -1.6875 L 5.671875 -1.6875 C 5.171875 -0.609375 4.75 -0.375 3.3125 -0.375 L 3.046875 -0.375 C 2.5625 -0.375 2.140625 -0.421875 2.078125 -0.484375 C 2.03125 -0.515625 2 -0.625 2 -0.796875 L 2 -3.265625 L 3.53125 -3.265625 C 4.359375 -3.265625 4.5 -3.125 4.640625 -2.296875 L 4.859375 -2.296875 L 4.859375 -4.609375 L 4.640625 -4.609375 C 4.5625 -4.203125 4.53125 -4.0625 4.421875 -3.921875 C 4.28125 -3.75 4 -3.671875 3.53125 -3.671875 L 2 -3.671875 L 2 -5.875 C 2 -6.15625 2.0625 -6.21875 2.328125 -6.21875 L 3.671875 -6.21875 C 4.8125 -6.21875 5.03125 -6.078125 5.1875 -5.171875 L 5.4375 -5.171875 L 5.40625 -6.59375 L 0.125 -6.59375 L 0.125 -6.40625 C 0.859375 -6.34375 0.984375 -6.203125 0.984375 -5.515625 L 0.984375 -1.09375 C 0.984375 -0.390625 0.84375 -0.234375 0.125 -0.1875 L 0.125 0 L 5.5 0 Z M 5.953125 -1.6875 "/> +</symbol> +<symbol overflow="visible" id="glyph4-32"> +<path style="stroke:none;" d="M 2.890625 -3.703125 C 3.875 -4.234375 4.234375 -4.640625 4.234375 -5.328125 C 4.234375 -6.140625 3.515625 -6.734375 2.515625 -6.734375 C 1.421875 -6.734375 0.625 -6.078125 0.625 -5.15625 C 0.625 -4.515625 0.8125 -4.234375 1.859375 -3.3125 C 0.78125 -2.5 0.5625 -2.1875 0.5625 -1.5 C 0.5625 -0.53125 1.34375 0.140625 2.46875 0.140625 C 3.671875 0.140625 4.4375 -0.515625 4.4375 -1.546875 C 4.4375 -2.3125 4.09375 -2.796875 2.890625 -3.703125 Z M 2.71875 -2.671875 C 3.4375 -2.15625 3.671875 -1.796875 3.671875 -1.234375 C 3.671875 -0.59375 3.234375 -0.140625 2.578125 -0.140625 C 1.828125 -0.140625 1.3125 -0.71875 1.3125 -1.578125 C 1.3125 -2.21875 1.53125 -2.640625 2.109375 -3.109375 Z M 2.609375 -3.875 C 1.71875 -4.453125 1.359375 -4.921875 1.359375 -5.46875 C 1.359375 -6.046875 1.796875 -6.453125 2.4375 -6.453125 C 3.109375 -6.453125 3.53125 -6.015625 3.53125 -5.328125 C 3.53125 -4.765625 3.265625 -4.3125 2.6875 -3.9375 C 2.640625 -3.90625 2.640625 -3.90625 2.609375 -3.875 Z M 2.609375 -3.875 "/> +</symbol> +<symbol overflow="visible" id="glyph4-33"> +<path style="stroke:none;" d="M 7.0625 -3.53125 L 4.53125 -3.53125 L 4.53125 -3.34375 C 4.953125 -3.3125 5.078125 -3.296875 5.1875 -3.203125 C 5.34375 -3.125 5.40625 -2.890625 5.40625 -2.46875 L 5.40625 -0.84375 C 5.40625 -0.53125 4.796875 -0.265625 4.078125 -0.265625 C 2.453125 -0.265625 1.453125 -1.390625 1.453125 -3.25 C 1.453125 -4.1875 1.734375 -5.09375 2.171875 -5.578125 C 2.609375 -6.078125 3.234375 -6.34375 3.90625 -6.34375 C 4.46875 -6.34375 4.953125 -6.15625 5.34375 -5.796875 C 5.640625 -5.515625 5.796875 -5.25 6.046875 -4.640625 L 6.28125 -4.640625 L 6.203125 -6.734375 L 5.984375 -6.734375 C 5.921875 -6.546875 5.734375 -6.40625 5.53125 -6.40625 C 5.4375 -6.40625 5.28125 -6.4375 5.09375 -6.515625 C 4.640625 -6.65625 4.1875 -6.734375 3.734375 -6.734375 C 1.75 -6.734375 0.3125 -5.28125 0.3125 -3.234375 C 0.3125 -2.265625 0.59375 -1.515625 1.140625 -0.921875 C 1.8125 -0.234375 2.765625 0.140625 3.875 0.140625 C 4.71875 0.140625 5.96875 -0.203125 6.375 -0.5625 L 6.375 -2.578125 C 6.375 -3.171875 6.484375 -3.296875 7.0625 -3.34375 Z M 7.0625 -3.53125 "/> +</symbol> +<symbol overflow="visible" id="glyph4-34"> +<path style="stroke:none;" d="M 6.1875 -4.484375 L 6.09375 -6.734375 L 5.875 -6.734375 C 5.828125 -6.53125 5.65625 -6.40625 5.46875 -6.40625 C 5.375 -6.40625 5.21875 -6.4375 5.078125 -6.5 C 4.578125 -6.65625 4.09375 -6.734375 3.625 -6.734375 C 2.796875 -6.734375 1.96875 -6.4375 1.359375 -5.875 C 0.65625 -5.265625 0.28125 -4.34375 0.28125 -3.234375 C 0.28125 -2.3125 0.578125 -1.453125 1.09375 -0.890625 C 1.6875 -0.234375 2.609375 0.140625 3.59375 0.140625 C 4.71875 0.140625 5.703125 -0.3125 6.3125 -1.125 L 6.125 -1.3125 C 5.390625 -0.59375 4.734375 -0.296875 3.90625 -0.296875 C 3.28125 -0.296875 2.71875 -0.5 2.296875 -0.875 C 1.75 -1.359375 1.4375 -2.265625 1.4375 -3.375 C 1.4375 -5.171875 2.359375 -6.34375 3.8125 -6.34375 C 4.375 -6.34375 4.890625 -6.125 5.296875 -5.734375 C 5.609375 -5.40625 5.765625 -5.140625 5.953125 -4.484375 Z M 6.1875 -4.484375 "/> +</symbol> +<symbol overflow="visible" id="glyph4-35"> +<path style="stroke:none;" d="M 3.59375 -6.734375 C 1.6875 -6.734375 0.34375 -5.296875 0.34375 -3.296875 C 0.34375 -2.359375 0.65625 -1.453125 1.203125 -0.875 C 1.78125 -0.25 2.65625 0.140625 3.53125 0.140625 C 5.5 0.140625 6.859375 -1.25 6.859375 -3.265625 C 6.859375 -4.25 6.5625 -5.09375 6.015625 -5.6875 C 5.390625 -6.375 4.5625 -6.734375 3.59375 -6.734375 Z M 3.59375 -6.375 C 4.0625 -6.375 4.515625 -6.203125 4.875 -5.875 C 5.40625 -5.390625 5.71875 -4.453125 5.71875 -3.265625 C 5.71875 -2.6875 5.59375 -2 5.390625 -1.46875 C 5.296875 -1.21875 5.140625 -0.984375 4.90625 -0.75 C 4.5625 -0.40625 4.109375 -0.21875 3.578125 -0.21875 C 3.125 -0.21875 2.671875 -0.40625 2.328125 -0.703125 C 1.796875 -1.171875 1.46875 -2.171875 1.46875 -3.28125 C 1.46875 -4.296875 1.75 -5.265625 2.171875 -5.734375 C 2.5625 -6.15625 3.046875 -6.375 3.59375 -6.375 Z M 3.59375 -6.375 "/> +</symbol> +<symbol overflow="visible" id="glyph4-36"> +<path style="stroke:none;" d="M 2.015625 -2.90625 C 2.265625 -2.875 2.4375 -2.875 2.703125 -2.875 C 3.484375 -2.875 4.015625 -2.96875 4.453125 -3.203125 C 5.046875 -3.53125 5.40625 -4.140625 5.40625 -4.796875 C 5.40625 -5.21875 5.265625 -5.59375 5 -5.875 C 4.59375 -6.3125 3.734375 -6.59375 2.796875 -6.59375 L 0.15625 -6.59375 L 0.15625 -6.40625 C 0.890625 -6.328125 1 -6.234375 1 -5.515625 L 1 -1.203125 C 1 -0.359375 0.921875 -0.265625 0.15625 -0.1875 L 0.15625 0 L 2.953125 0 L 2.953125 -0.1875 C 2.15625 -0.21875 2.015625 -0.359375 2.015625 -1.09375 Z M 2.015625 -5.890625 C 2.015625 -6.15625 2.078125 -6.234375 2.359375 -6.234375 C 3.703125 -6.234375 4.3125 -5.765625 4.3125 -4.734375 C 4.3125 -3.765625 3.734375 -3.265625 2.578125 -3.265625 C 2.375 -3.265625 2.25 -3.28125 2.015625 -3.296875 Z M 2.015625 -5.890625 "/> +</symbol> +<symbol overflow="visible" id="glyph4-37"> +<path style="stroke:none;" d="M 4.453125 -6.734375 L 4.25 -6.734375 C 4.203125 -6.515625 4.09375 -6.40625 3.921875 -6.40625 C 3.828125 -6.40625 3.65625 -6.4375 3.484375 -6.515625 C 3.125 -6.65625 2.75 -6.734375 2.4375 -6.734375 C 2 -6.734375 1.5625 -6.5625 1.234375 -6.28125 C 0.890625 -5.96875 0.703125 -5.546875 0.703125 -5.03125 C 0.703125 -4.234375 1.140625 -3.671875 2.265625 -3.09375 C 2.984375 -2.71875 3.5 -2.296875 3.75 -1.921875 C 3.84375 -1.796875 3.890625 -1.578125 3.890625 -1.34375 C 3.890625 -0.671875 3.390625 -0.21875 2.65625 -0.21875 C 1.78125 -0.21875 1.140625 -0.765625 0.640625 -1.984375 L 0.421875 -1.984375 L 0.71875 0.125 L 0.9375 0.125 C 0.953125 -0.0625 1.0625 -0.203125 1.21875 -0.203125 C 1.328125 -0.203125 1.5 -0.15625 1.6875 -0.09375 C 2.0625 0.0625 2.46875 0.140625 2.859375 0.140625 C 4 0.140625 4.890625 -0.640625 4.890625 -1.671875 C 4.890625 -2.5 4.34375 -3.140625 3.03125 -3.84375 C 1.984375 -4.421875 1.5625 -4.859375 1.5625 -5.40625 C 1.5625 -5.953125 1.984375 -6.328125 2.609375 -6.328125 C 3.046875 -6.328125 3.46875 -6.140625 3.8125 -5.78125 C 4.125 -5.46875 4.265625 -5.203125 4.421875 -4.609375 L 4.671875 -4.609375 Z M 4.453125 -6.734375 "/> +</symbol> +<symbol overflow="visible" id="glyph4-38"> +<path style="stroke:none;" d="M 4.171875 -1.34375 L 3.984375 -1.390625 C 3.890625 -0.890625 3.828125 -0.734375 3.703125 -0.5625 C 3.5625 -0.390625 3.21875 -0.296875 2.71875 -0.296875 L 1.328125 -0.296875 L 4.015625 -4.34375 L 4.015625 -4.484375 L 0.5625 -4.484375 L 0.53125 -3.3125 L 0.703125 -3.3125 C 0.796875 -4.03125 0.953125 -4.1875 1.546875 -4.1875 L 2.921875 -4.1875 L 0.265625 -0.15625 L 0.265625 0 L 4.03125 0 Z M 4.171875 -1.34375 "/> +</symbol> +<symbol overflow="visible" id="glyph4-39"> +<path style="stroke:none;" d="M 1.0625 -4.3125 C 1.71875 -4.640625 2.171875 -5.25 2.171875 -5.859375 C 2.171875 -6.34375 1.828125 -6.734375 1.375 -6.734375 C 1.03125 -6.734375 0.78125 -6.515625 0.78125 -6.171875 C 0.78125 -5.859375 1.015625 -5.65625 1.375 -5.65625 C 1.4375 -5.65625 1.5 -5.671875 1.5625 -5.6875 C 1.640625 -5.703125 1.640625 -5.703125 1.640625 -5.703125 C 1.71875 -5.703125 1.78125 -5.640625 1.78125 -5.5625 C 1.78125 -5.234375 1.5 -4.875 0.96875 -4.5 Z M 1.0625 -4.3125 "/> +</symbol> +<symbol overflow="visible" id="glyph4-40"> +<path style="stroke:none;" d="M 0.171875 -6.59375 L 0.171875 -6.40625 C 1 -6.359375 1.125 -6.25 1.125 -5.515625 L 1.125 -1.09375 C 1.125 -0.34375 1 -0.21875 0.171875 -0.1875 L 0.171875 0 L 3.5 0 C 4.28125 0 4.984375 -0.203125 5.359375 -0.546875 C 5.71875 -0.875 5.90625 -1.3125 5.90625 -1.796875 C 5.90625 -2.234375 5.734375 -2.625 5.421875 -2.921875 C 5.125 -3.1875 4.859375 -3.3125 4.203125 -3.46875 C 4.71875 -3.59375 4.9375 -3.703125 5.171875 -3.90625 C 5.421875 -4.125 5.578125 -4.5 5.578125 -4.90625 C 5.578125 -6.015625 4.6875 -6.59375 2.953125 -6.59375 Z M 2.140625 -3.25 C 3.109375 -3.25 3.5625 -3.203125 3.921875 -3.046875 C 4.5 -2.8125 4.765625 -2.40625 4.765625 -1.78125 C 4.765625 -1.25 4.5625 -0.859375 4.15625 -0.625 C 3.84375 -0.453125 3.421875 -0.375 2.765625 -0.375 C 2.28125 -0.375 2.140625 -0.453125 2.140625 -0.78125 Z M 2.140625 -3.65625 L 2.140625 -5.9375 C 2.140625 -6.140625 2.21875 -6.234375 2.359375 -6.234375 L 2.796875 -6.234375 C 3.953125 -6.234375 4.5625 -5.75 4.5625 -4.859375 C 4.5625 -4.09375 4.03125 -3.65625 3.09375 -3.65625 Z M 2.140625 -3.65625 "/> +</symbol> +<symbol overflow="visible" id="glyph4-41"> +<path style="stroke:none;" d="M 4.109375 -6.40625 C 4.234375 -6.40625 4.34375 -6.40625 4.375 -6.40625 C 4.671875 -6.375 4.796875 -6.296875 4.796875 -6.09375 C 4.796875 -5.875 4.5625 -5.578125 4.015625 -5.078125 L 2.25 -3.46875 L 2.25 -5.515625 C 2.25 -6.234375 2.359375 -6.34375 3.171875 -6.40625 L 3.171875 -6.59375 L 0.34375 -6.59375 L 0.34375 -6.40625 C 1.109375 -6.34375 1.234375 -6.21875 1.234375 -5.515625 L 1.234375 -1.203125 C 1.234375 -0.375 1.125 -0.234375 0.34375 -0.1875 L 0.34375 0 L 3.15625 0 L 3.15625 -0.1875 C 2.375 -0.234375 2.25 -0.359375 2.25 -1.09375 L 2.25 -2.953125 L 2.515625 -3.15625 L 3.5625 -2.109375 C 4.328125 -1.359375 4.859375 -0.65625 4.859375 -0.421875 C 4.859375 -0.28125 4.71875 -0.203125 4.453125 -0.203125 C 4.390625 -0.203125 4.28125 -0.203125 4.171875 -0.1875 L 4.171875 0 L 7.203125 0 L 7.203125 -0.1875 C 6.6875 -0.203125 6.546875 -0.296875 5.640625 -1.265625 L 3.3125 -3.765625 L 5.21875 -5.625 C 5.890625 -6.28125 6.046875 -6.359375 6.734375 -6.40625 L 6.734375 -6.59375 L 4.109375 -6.59375 Z M 4.109375 -6.40625 "/> +</symbol> +<symbol overflow="visible" id="glyph4-42"> +<path style="stroke:none;" d="M 2.53125 -6.1875 L 2.53125 -1.203125 C 2.53125 -0.34375 2.421875 -0.234375 1.59375 -0.1875 L 1.59375 0 L 4.5 0 L 4.5 -0.1875 C 3.6875 -0.234375 3.546875 -0.359375 3.546875 -1.09375 L 3.546875 -6.1875 L 4.09375 -6.1875 C 5.21875 -6.1875 5.4375 -6 5.671875 -4.90625 L 5.90625 -4.90625 L 5.859375 -6.59375 L 0.234375 -6.59375 L 0.171875 -4.90625 L 0.40625 -4.90625 C 0.640625 -5.984375 0.875 -6.1875 2 -6.1875 Z M 2.53125 -6.1875 "/> +</symbol> +<symbol overflow="visible" id="glyph4-43"> +<path style="stroke:none;" d="M 0.3125 -4.15625 L 0.984375 -4.15625 L 0.984375 -0.9375 C 0.984375 -0.296875 0.875 -0.171875 0.3125 -0.15625 L 0.3125 0 L 2.515625 0 L 2.515625 -0.15625 C 1.953125 -0.1875 1.828125 -0.328125 1.828125 -0.875 L 1.828125 -4.15625 L 2.6875 -4.15625 C 3.640625 -4.15625 3.6875 -4.125 3.6875 -3.59375 L 3.6875 -0.96875 C 3.6875 -0.296875 3.625 -0.203125 3 -0.15625 L 3 0 L 5.1875 0 L 5.1875 -0.15625 C 4.640625 -0.203125 4.53125 -0.328125 4.53125 -0.96875 L 4.53125 -3.578125 C 4.53125 -3.796875 4.53125 -4.03125 4.546875 -4.5625 L 4.5 -4.578125 C 4.0625 -4.515625 3.78125 -4.484375 3.375 -4.484375 L 1.8125 -4.484375 L 1.8125 -4.921875 C 1.8125 -5.359375 1.84375 -5.65625 1.890625 -5.828125 C 2.03125 -6.265625 2.421875 -6.5625 2.875 -6.5625 C 3.15625 -6.5625 3.34375 -6.4375 3.578125 -6.09375 C 3.765625 -5.828125 3.890625 -5.71875 4.0625 -5.71875 C 4.28125 -5.71875 4.421875 -5.890625 4.421875 -6.109375 C 4.421875 -6.53125 3.921875 -6.8125 3.15625 -6.8125 C 2.40625 -6.8125 1.828125 -6.546875 1.46875 -6.0625 C 1.171875 -5.65625 1.0625 -5.28125 1 -4.484375 L 0.3125 -4.484375 Z M 0.3125 -4.15625 "/> +</symbol> +<symbol overflow="visible" id="glyph5-0"> +<path style="stroke:none;" d="M 0.5625 -4.03125 L 0.78125 -4.03125 C 0.78125 -4.03125 0.8125 -4.03125 0.8125 -4.03125 C 0.953125 -4.09375 1.203125 -3.90625 1.203125 -3.765625 C 1.203125 -3.671875 0.84375 -2.25 0.5 -0.9375 C 0.234375 0.0625 -0.015625 1 -0.078125 1.296875 C -0.1875 1.75 -0.3125 1.875 -0.75 1.890625 L -0.75 2.046875 L 1.296875 2.046875 L 1.296875 1.890625 C 0.828125 1.890625 0.65625 1.796875 0.65625 1.59375 C 0.65625 1.453125 0.828125 0.671875 1.03125 -0.0625 C 1.28125 0.0625 1.453125 0.109375 1.6875 0.109375 C 3.140625 0.109375 4.671875 -1.5625 4.671875 -3.15625 C 4.671875 -3.921875 4.234375 -4.390625 3.53125 -4.390625 C 2.875 -4.390625 2.40625 -4.078125 1.828125 -3.265625 L 2.109375 -4.265625 L 2.140625 -4.359375 C 2.140625 -4.359375 2.140625 -4.359375 2.125 -4.390625 L 2.109375 -4.390625 C 2.109375 -4.40625 2.09375 -4.40625 2.09375 -4.40625 L 2.078125 -4.390625 L 0.53125 -4.171875 Z M 3.15625 -3.96875 C 3.59375 -3.953125 3.78125 -3.6875 3.78125 -3.109375 C 3.78125 -2.453125 3.484375 -1.59375 3.0625 -0.984375 C 2.640625 -0.390625 2.15625 -0.078125 1.640625 -0.078125 C 1.359375 -0.078125 1.171875 -0.234375 1.171875 -0.453125 C 1.171875 -0.78125 1.53125 -2.09375 1.8125 -2.828125 C 2.078125 -3.515625 2.65625 -4 3.15625 -3.96875 Z M 3.15625 -3.96875 "/> +</symbol> +<symbol overflow="visible" id="glyph5-1"> +<path style="stroke:none;" d="M 1.203125 0 C 1.71875 -1.6875 1.890625 -2.125 2.359375 -2.875 C 2.6875 -3.4375 2.953125 -3.75 3.125 -3.75 C 3.1875 -3.75 3.234375 -3.703125 3.296875 -3.609375 C 3.375 -3.4375 3.453125 -3.390625 3.65625 -3.390625 C 3.9375 -3.390625 4.109375 -3.5625 4.109375 -3.875 C 4.109375 -4.1875 3.921875 -4.390625 3.640625 -4.390625 C 3.40625 -4.390625 3.140625 -4.25 2.875 -3.984375 C 2.46875 -3.5625 2.078125 -2.96875 1.90625 -2.578125 L 1.78125 -2.21875 L 2.296875 -4.375 L 2.265625 -4.390625 C 1.546875 -4.265625 1.453125 -4.25 0.734375 -4.125 L 0.734375 -3.953125 C 0.96875 -4 1 -4 1.0625 -4 C 1.28125 -4 1.421875 -3.90625 1.421875 -3.734375 C 1.421875 -3.59375 1.421875 -3.59375 1.25 -2.90625 L 0.453125 0 Z M 1.203125 0 "/> +</symbol> +<symbol overflow="visible" id="glyph5-2"> +<path style="stroke:none;" d="M 2.21875 -1.140625 C 1.96875 -0.8125 1.90625 -0.734375 1.796875 -0.625 C 1.640625 -0.453125 1.484375 -0.359375 1.390625 -0.359375 C 1.3125 -0.359375 1.234375 -0.4375 1.234375 -0.515625 C 1.234375 -0.609375 1.265625 -0.75 1.328125 -0.953125 C 1.328125 -0.984375 1.359375 -1.046875 1.390625 -1.125 L 1.390625 -1.171875 L 2.265625 -4.375 L 2.25 -4.390625 C 1.234375 -4.203125 1.03125 -4.171875 0.640625 -4.140625 L 0.640625 -3.984375 C 1.171875 -3.984375 1.28125 -3.953125 1.28125 -3.75 C 1.28125 -3.671875 1.25 -3.515625 1.1875 -3.3125 L 0.703125 -1.546875 C 0.546875 -0.96875 0.484375 -0.65625 0.484375 -0.453125 C 0.484375 -0.09375 0.640625 0.109375 0.953125 0.109375 C 1.40625 0.109375 1.78125 -0.1875 2.34375 -1.03125 Z M 2.140625 -6.515625 C 1.859375 -6.515625 1.671875 -6.296875 1.671875 -5.984375 C 1.671875 -5.671875 1.859375 -5.46875 2.140625 -5.46875 C 2.40625 -5.46875 2.625 -5.6875 2.625 -5.96875 C 2.625 -6.265625 2.40625 -6.515625 2.140625 -6.515625 Z M 2.140625 -6.515625 "/> +</symbol> +<symbol overflow="visible" id="glyph5-3"> +<path style="stroke:none;" d="M 4.578125 -1.171875 L 4.375 -0.90625 C 4.09375 -0.53125 3.90625 -0.375 3.765625 -0.375 C 3.6875 -0.375 3.609375 -0.453125 3.609375 -0.53125 C 3.609375 -0.609375 3.609375 -0.609375 3.75 -1.171875 L 4.3125 -3.21875 C 4.359375 -3.421875 4.40625 -3.65625 4.40625 -3.78125 C 4.40625 -4.140625 4.140625 -4.390625 3.75 -4.390625 C 3.109375 -4.390625 2.484375 -3.796875 1.453125 -2.203125 L 2.125 -4.375 L 2.09375 -4.390625 C 1.5625 -4.28125 1.34375 -4.25 0.484375 -4.09375 L 0.484375 -3.921875 C 0.984375 -3.921875 1.109375 -3.859375 1.109375 -3.65625 C 1.109375 -3.59375 1.109375 -3.53125 1.09375 -3.484375 L 0.140625 0 L 0.890625 0 C 1.359375 -1.578125 1.453125 -1.796875 1.890625 -2.46875 C 2.484375 -3.390625 2.984375 -3.890625 3.359375 -3.890625 C 3.515625 -3.890625 3.59375 -3.78125 3.59375 -3.59375 C 3.59375 -3.484375 3.53125 -3.15625 3.453125 -2.84375 L 3.015625 -1.203125 C 2.890625 -0.6875 2.859375 -0.546875 2.859375 -0.453125 C 2.859375 -0.0625 3 0.09375 3.328125 0.09375 C 3.78125 0.09375 4.03125 -0.125 4.71875 -1.03125 Z M 4.578125 -1.171875 "/> +</symbol> +<symbol overflow="visible" id="glyph5-4"> +<path style="stroke:none;" d="M 3.484375 -1.0625 C 2.9375 -0.46875 2.546875 -0.25 2.0625 -0.25 C 1.5 -0.25 1.15625 -0.671875 1.15625 -1.390625 C 1.15625 -2.234375 1.5 -3.125 2.0625 -3.703125 C 2.359375 -4 2.75 -4.1875 3.140625 -4.1875 C 3.375 -4.1875 3.515625 -4.109375 3.515625 -3.984375 C 3.515625 -3.9375 3.5 -3.890625 3.453125 -3.796875 C 3.390625 -3.671875 3.375 -3.59375 3.375 -3.515625 C 3.375 -3.265625 3.515625 -3.125 3.765625 -3.125 C 4.03125 -3.125 4.234375 -3.328125 4.234375 -3.59375 C 4.234375 -4.046875 3.78125 -4.390625 3.1875 -4.390625 C 1.6875 -4.390625 0.296875 -2.9375 0.296875 -1.390625 C 0.296875 -0.4375 0.84375 0.109375 1.765625 0.109375 C 2.5 0.109375 3.046875 -0.203125 3.65625 -0.953125 Z M 3.484375 -1.0625 "/> +</symbol> +<symbol overflow="visible" id="glyph5-5"> +<path style="stroke:none;" d="M 4.625 -1.09375 C 4.46875 -0.953125 4.421875 -0.890625 4.34375 -0.8125 C 4.046875 -0.515625 3.921875 -0.40625 3.828125 -0.40625 C 3.75 -0.40625 3.6875 -0.46875 3.6875 -0.53125 C 3.6875 -0.734375 4.109375 -2.4375 4.578125 -4.15625 C 4.609375 -4.25 4.609375 -4.28125 4.640625 -4.359375 L 4.5625 -4.390625 L 3.953125 -4.328125 L 3.921875 -4.296875 L 3.8125 -3.8125 C 3.734375 -4.1875 3.453125 -4.390625 3.015625 -4.390625 C 1.703125 -4.390625 0.171875 -2.578125 0.171875 -1 C 0.171875 -0.296875 0.546875 0.109375 1.1875 0.109375 C 1.890625 0.109375 2.3125 -0.21875 3.1875 -1.453125 C 2.984375 -0.671875 2.953125 -0.546875 2.953125 -0.3125 C 2.953125 -0.015625 3.078125 0.09375 3.359375 0.09375 C 3.765625 0.09375 4 -0.09375 4.75 -1 Z M 3.078125 -4.171875 C 3.421875 -4.15625 3.640625 -3.921875 3.640625 -3.5625 C 3.640625 -2.734375 3.140625 -1.5625 2.453125 -0.8125 C 2.21875 -0.546875 1.875 -0.375 1.578125 -0.375 C 1.234375 -0.375 1 -0.671875 1 -1.125 C 1 -1.671875 1.390625 -2.6875 1.8125 -3.3125 C 2.21875 -3.890625 2.6875 -4.203125 3.078125 -4.171875 Z M 3.078125 -4.171875 "/> +</symbol> +<symbol overflow="visible" id="glyph5-6"> +<path style="stroke:none;" d="M 2.265625 -1.21875 C 2.1875 -1.109375 2.09375 -1 2 -0.890625 C 1.6875 -0.46875 1.5 -0.3125 1.3125 -0.3125 C 1.21875 -0.3125 1.171875 -0.390625 1.171875 -0.5 C 1.171875 -0.5625 1.203125 -0.6875 1.25 -0.890625 C 1.25 -0.921875 1.265625 -0.96875 1.28125 -0.984375 L 2.78125 -6.765625 L 2.734375 -6.8125 C 2.140625 -6.671875 1.765625 -6.609375 1.171875 -6.546875 L 1.171875 -6.375 C 1.65625 -6.375 1.859375 -6.3125 1.859375 -6.15625 C 1.859375 -6.125 1.84375 -6.0625 1.8125 -5.96875 L 0.453125 -0.703125 C 0.421875 -0.59375 0.40625 -0.5 0.40625 -0.453125 C 0.40625 -0.09375 0.5625 0.109375 0.890625 0.109375 C 1.40625 0.109375 1.75 -0.171875 2.40625 -1.140625 Z M 2.265625 -1.21875 "/> +</symbol> +<symbol overflow="visible" id="glyph5-7"> +<path style="stroke:none;" d="M 0.359375 -1.453125 L 0.15625 0.125 L 0.3125 0.125 C 0.40625 -0.03125 0.453125 -0.078125 0.5625 -0.078125 C 0.671875 -0.078125 0.84375 -0.046875 1.046875 0.015625 C 1.265625 0.078125 1.4375 0.109375 1.59375 0.109375 C 2.4375 0.109375 3.03125 -0.421875 3.03125 -1.1875 C 3.03125 -1.5625 2.828125 -2 2.359375 -2.578125 C 1.96875 -3.046875 1.8125 -3.359375 1.8125 -3.625 C 1.8125 -3.953125 2.03125 -4.171875 2.375 -4.171875 C 2.890625 -4.171875 3.1875 -3.796875 3.296875 -3.015625 L 3.453125 -3.015625 L 3.65625 -4.40625 L 3.515625 -4.40625 C 3.421875 -4.265625 3.34375 -4.234375 3.203125 -4.234375 C 3.140625 -4.234375 3.046875 -4.25 2.84375 -4.296875 C 2.609375 -4.375 2.453125 -4.390625 2.296875 -4.390625 C 1.5625 -4.390625 1.09375 -3.96875 1.09375 -3.296875 C 1.09375 -2.984375 1.296875 -2.546875 1.71875 -2.015625 C 2.109375 -1.5 2.265625 -1.15625 2.265625 -0.875 C 2.265625 -0.421875 1.96875 -0.09375 1.53125 -0.09375 C 0.984375 -0.09375 0.671875 -0.515625 0.515625 -1.453125 Z M 0.359375 -1.453125 "/> +</symbol> +<symbol overflow="visible" id="glyph5-8"> +<path style="stroke:none;" d="M 3.5625 -1.09375 C 2.84375 -0.5 2.546875 -0.34375 2.109375 -0.34375 C 1.546875 -0.34375 1.171875 -0.703125 1.171875 -1.234375 C 1.171875 -1.390625 1.203125 -1.53125 1.28125 -1.859375 L 1.5625 -1.890625 C 3.046875 -2.109375 4.109375 -2.859375 4.109375 -3.71875 C 4.109375 -4.140625 3.8125 -4.390625 3.3125 -4.390625 C 1.875 -4.390625 0.3125 -2.75 0.3125 -1.25 C 0.3125 -0.453125 0.84375 0.109375 1.625 0.109375 C 2.328125 0.109375 3.09375 -0.296875 3.6875 -0.96875 Z M 1.515625 -2.515625 C 1.859375 -3.40625 2.59375 -4.171875 3.09375 -4.171875 C 3.3125 -4.171875 3.453125 -4 3.453125 -3.78125 C 3.453125 -3.46875 3.265625 -3.109375 2.953125 -2.8125 C 2.578125 -2.46875 2.203125 -2.28125 1.34375 -2.078125 Z M 1.515625 -2.515625 "/> +</symbol> +<symbol overflow="visible" id="glyph5-9"> +<path style="stroke:none;" d="M 4 -1.109375 C 3.921875 -1.015625 3.875 -0.953125 3.78125 -0.84375 C 3.5625 -0.53125 3.4375 -0.4375 3.328125 -0.4375 C 3.1875 -0.4375 3.09375 -0.5625 3.015625 -0.84375 C 3 -0.921875 2.984375 -0.984375 2.984375 -1.015625 C 2.734375 -2.03125 2.625 -2.484375 2.625 -2.625 C 3.0625 -3.40625 3.421875 -3.84375 3.59375 -3.84375 C 3.65625 -3.84375 3.734375 -3.8125 3.84375 -3.765625 C 3.953125 -3.6875 4.03125 -3.671875 4.109375 -3.671875 C 4.3125 -3.671875 4.453125 -3.8125 4.453125 -4.03125 C 4.453125 -4.234375 4.28125 -4.390625 4.046875 -4.390625 C 3.609375 -4.390625 3.234375 -4.03125 2.546875 -2.96875 L 2.4375 -3.515625 C 2.296875 -4.203125 2.1875 -4.390625 1.90625 -4.390625 C 1.6875 -4.390625 1.359375 -4.3125 0.75 -4.109375 L 0.640625 -4.0625 L 0.671875 -3.921875 C 1.0625 -4 1.140625 -4.03125 1.234375 -4.03125 C 1.484375 -4.03125 1.546875 -3.9375 1.6875 -3.34375 L 1.96875 -2.109375 L 1.15625 -0.953125 C 0.953125 -0.640625 0.75 -0.46875 0.640625 -0.46875 C 0.59375 -0.46875 0.484375 -0.5 0.390625 -0.5625 C 0.265625 -0.625 0.15625 -0.65625 0.0625 -0.65625 C -0.125 -0.65625 -0.265625 -0.515625 -0.265625 -0.3125 C -0.265625 -0.046875 -0.0625 0.109375 0.234375 0.109375 C 0.53125 0.109375 0.65625 0.015625 1.15625 -0.59375 C 1.421875 -0.90625 1.640625 -1.171875 2.046875 -1.75 L 2.359375 -0.5625 C 2.484375 -0.046875 2.609375 0.109375 2.9375 0.109375 C 3.3125 0.109375 3.5625 -0.125 4.140625 -1.03125 Z M 4 -1.109375 "/> +</symbol> +<symbol overflow="visible" id="glyph5-10"> +<path style="stroke:none;" d="M 4.59375 -1.171875 C 4.078125 -0.515625 3.921875 -0.375 3.765625 -0.375 C 3.703125 -0.375 3.65625 -0.4375 3.65625 -0.546875 C 3.65625 -0.609375 3.65625 -0.609375 3.84375 -1.328125 L 4.640625 -4.3125 L 3.890625 -4.3125 C 3.328125 -2.765625 3.265625 -2.609375 2.796875 -1.875 C 2.203125 -0.921875 1.71875 -0.421875 1.390625 -0.421875 C 1.28125 -0.421875 1.1875 -0.515625 1.1875 -0.671875 C 1.1875 -0.703125 1.1875 -0.734375 1.203125 -0.75 L 2.109375 -4.375 L 2.078125 -4.390625 C 1.5 -4.265625 1.140625 -4.203125 0.578125 -4.125 L 0.578125 -3.984375 C 0.953125 -3.984375 0.984375 -3.984375 1.0625 -3.921875 C 1.125 -3.90625 1.171875 -3.8125 1.171875 -3.75 C 1.171875 -3.671875 1.125 -3.421875 1.03125 -3.078125 L 0.671875 -1.671875 C 0.484375 -0.953125 0.421875 -0.625 0.421875 -0.4375 C 0.421875 -0.078125 0.59375 0.109375 0.953125 0.109375 C 1.65625 0.109375 2.1875 -0.421875 3.328125 -2.359375 C 3.046875 -1.28125 2.875 -0.59375 2.875 -0.390625 C 2.875 -0.09375 3.0625 0.09375 3.359375 0.09375 C 3.828125 0.09375 4.0625 -0.109375 4.734375 -1.078125 Z M 4.59375 -1.171875 "/> +</symbol> +<symbol overflow="visible" id="glyph5-11"> +<path style="stroke:none;" d="M 0.203125 -4.015625 C 0.3125 -4.03125 0.40625 -4.03125 0.515625 -4.03125 C 0.890625 -4.03125 1 -3.859375 1.140625 -2.9375 C 1.234375 -2.25 1.359375 -0.75 1.359375 -0.171875 C 1.359375 0.109375 1.375 0.171875 1.453125 0.171875 C 1.6875 0.171875 2.625 -0.875 3.640625 -2.296875 C 3.984375 -2.796875 4.25 -3.4375 4.25 -3.8125 C 4.25 -4.125 3.984375 -4.390625 3.6875 -4.390625 C 3.46875 -4.390625 3.3125 -4.265625 3.3125 -4.0625 C 3.3125 -3.890625 3.375 -3.78125 3.5625 -3.625 C 3.6875 -3.5 3.734375 -3.421875 3.734375 -3.3125 C 3.734375 -2.859375 3.078125 -1.75 2.375 -1.015625 L 2.0625 -0.703125 C 2 -2.109375 1.9375 -2.625 1.796875 -3.359375 C 1.609375 -4.375 1.609375 -4.390625 1.515625 -4.390625 C 1.46875 -4.390625 1.390625 -4.390625 1.3125 -4.359375 C 1.140625 -4.3125 0.59375 -4.203125 0.203125 -4.140625 Z M 0.203125 -4.015625 "/> +</symbol> +<symbol overflow="visible" id="glyph6-0"> +<path style="stroke:none;" d="M 4.546875 -9.875 L 0.9375 -8.3125 L 0.9375 -7.9375 C 1.0625 -7.96875 1.171875 -8.015625 1.21875 -8.046875 C 1.609375 -8.1875 1.96875 -8.296875 2.15625 -8.296875 C 2.515625 -8.296875 2.671875 -7.96875 2.671875 -7.296875 L 2.671875 -1.78125 C 2.671875 -0.609375 2.375 -0.359375 0.96875 -0.34375 L 0.96875 0 L 6.34375 0 L 6.34375 -0.34375 C 5.03125 -0.375 4.796875 -0.59375 4.796875 -1.6875 L 4.796875 -9.875 Z M 4.546875 -9.875 "/> +</symbol> +<symbol overflow="visible" id="glyph6-1"> +<path style="stroke:none;" d="M 1.625 -1.375 C 1.625 -0.65625 1.359375 -0.453125 0.28125 -0.359375 L 0.28125 0 L 5.3125 0 L 5.3125 -0.359375 C 4.21875 -0.4375 3.9375 -0.625 3.9375 -1.375 L 3.9375 -8.3125 C 3.9375 -9.0625 4.25 -9.296875 5.3125 -9.34375 L 5.3125 -9.703125 L 0.28125 -9.703125 L 0.28125 -9.34375 C 1.3125 -9.265625 1.625 -9.046875 1.625 -8.3125 Z M 1.625 -1.375 "/> +</symbol> +<symbol overflow="visible" id="glyph6-2"> +<path style="stroke:none;" d="M 3.046875 -6.609375 L 0.296875 -6.609375 L 0.296875 -6.265625 C 0.9375 -6.171875 1.0625 -6.03125 1.0625 -5.421875 L 1.0625 -1.203125 C 1.0625 -0.609375 0.953125 -0.46875 0.296875 -0.34375 L 0.296875 0 L 3.765625 0 L 3.765625 -0.34375 C 3.234375 -0.421875 3.0625 -0.625 3.0625 -1.15625 L 3.0625 -4.984375 C 3.0625 -5.046875 3.15625 -5.1875 3.296875 -5.328125 C 3.609375 -5.65625 3.953125 -5.828125 4.28125 -5.828125 C 4.796875 -5.828125 5.03125 -5.4375 5.03125 -4.640625 L 5.03125 -1.15625 C 5.03125 -0.625 4.84375 -0.40625 4.359375 -0.34375 L 4.359375 0 L 7.734375 0 L 7.734375 -0.34375 C 7.171875 -0.390625 7.03125 -0.5625 7.03125 -1.15625 L 7.03125 -4.765625 C 7.03125 -6 6.265625 -6.78125 5.09375 -6.78125 C 4.234375 -6.78125 3.578125 -6.390625 3.046875 -5.53125 Z M 3.046875 -6.609375 "/> +</symbol> +<symbol overflow="visible" id="glyph6-3"> +<path style="stroke:none;" d="M 4.375 -6.609375 L 3.03125 -6.609375 L 3.03125 -9.03125 L 2.671875 -9.03125 C 1.796875 -7.796875 1.21875 -7.15625 0.28125 -6.375 L 0.28125 -5.984375 L 1.03125 -5.984375 L 1.03125 -1.328125 C 1.03125 -0.40625 1.65625 0.171875 2.640625 0.171875 C 3.59375 0.171875 4.171875 -0.265625 4.765625 -1.4375 L 4.40625 -1.59375 C 4.109375 -1.046875 3.890625 -0.84375 3.578125 -0.84375 C 3.1875 -0.84375 3.03125 -1.09375 3.03125 -1.65625 L 3.03125 -5.984375 L 4.375 -5.984375 Z M 4.375 -6.609375 "/> +</symbol> +<symbol overflow="visible" id="glyph6-4"> +<path style="stroke:none;" d="M 3.125 -6.609375 L 0.421875 -6.609375 L 0.421875 -6.265625 C 1.03125 -6.1875 1.1875 -6 1.1875 -5.421875 L 1.1875 -1.203125 C 1.1875 -0.609375 1.046875 -0.453125 0.421875 -0.34375 L 0.421875 0 L 4.234375 0 L 4.234375 -0.34375 C 3.359375 -0.40625 3.1875 -0.59375 3.1875 -1.484375 L 3.1875 -4.1875 C 3.1875 -4.9375 3.578125 -5.546875 4.0625 -5.546875 C 4.171875 -5.546875 4.296875 -5.453125 4.453125 -5.21875 C 4.734375 -4.828125 4.953125 -4.703125 5.328125 -4.703125 C 5.859375 -4.703125 6.21875 -5.109375 6.21875 -5.671875 C 6.21875 -6.3125 5.734375 -6.78125 5.0625 -6.78125 C 4.34375 -6.78125 3.796875 -6.390625 3.125 -5.4375 Z M 3.125 -6.609375 "/> +</symbol> +<symbol overflow="visible" id="glyph6-5"> +<path style="stroke:none;" d="M 3.59375 -6.78125 C 1.78125 -6.78125 0.359375 -5.28125 0.359375 -3.328125 C 0.359375 -1.28125 1.71875 0.203125 3.59375 0.203125 C 5.453125 0.203125 6.828125 -1.296875 6.828125 -3.28125 C 6.828125 -5.296875 5.453125 -6.78125 3.59375 -6.78125 Z M 3.609375 -6.34375 C 4.453125 -6.34375 4.71875 -5.546875 4.71875 -3.203125 C 4.71875 -0.984375 4.4375 -0.25 3.59375 -0.25 C 2.75 -0.25 2.46875 -0.96875 2.46875 -3.0625 C 2.46875 -5.59375 2.71875 -6.34375 3.609375 -6.34375 Z M 3.609375 -6.34375 "/> +</symbol> +<symbol overflow="visible" id="glyph6-6"> +<path style="stroke:none;" d="M 4.859375 0.1875 C 5.515625 0 5.875 -0.078125 6.765625 -0.171875 L 7.65625 -0.28125 L 7.65625 -0.609375 C 7 -0.65625 6.8125 -0.84375 6.8125 -1.453125 L 6.8125 -9.703125 L 3.734375 -9.703125 L 3.734375 -9.359375 C 4.6875 -9.28125 4.8125 -9.1875 4.8125 -8.515625 L 4.8125 -5.890625 C 4.203125 -6.5625 3.765625 -6.78125 3.109375 -6.78125 C 1.53125 -6.78125 0.359375 -5.25 0.359375 -3.171875 C 0.359375 -1.21875 1.453125 0.203125 2.953125 0.203125 C 3.71875 0.203125 4.1875 -0.03125 4.859375 -0.75 Z M 4.8125 -1.609375 C 4.8125 -1.515625 4.671875 -1.28125 4.5 -1.09375 C 4.21875 -0.765625 3.921875 -0.609375 3.59375 -0.609375 C 2.84375 -0.609375 2.484375 -1.46875 2.484375 -3.28125 C 2.484375 -5.140625 2.875 -5.984375 3.703125 -5.984375 C 4.171875 -5.984375 4.625 -5.640625 4.8125 -5.09375 Z M 4.8125 -1.609375 "/> +</symbol> +<symbol overflow="visible" id="glyph6-7"> +<path style="stroke:none;" d="M 4.921875 0.1875 C 5.53125 -0.03125 5.875 -0.09375 6.8125 -0.1875 L 7.703125 -0.28125 L 7.703125 -0.609375 C 7.078125 -0.640625 6.90625 -0.828125 6.90625 -1.453125 L 6.90625 -6.609375 L 4.015625 -6.609375 L 4.015625 -6.265625 C 4.734375 -6.203125 4.90625 -6.03125 4.90625 -5.421875 L 4.90625 -1.359375 C 4.4375 -0.890625 4.125 -0.734375 3.734375 -0.734375 C 3.140625 -0.734375 2.921875 -1.015625 2.921875 -1.75 L 2.921875 -6.609375 L 0.234375 -6.609375 L 0.234375 -6.265625 C 0.8125 -6.15625 0.9375 -6.03125 0.9375 -5.421875 L 0.9375 -1.8125 C 0.9375 -0.546875 1.65625 0.203125 2.84375 0.203125 C 3.578125 0.203125 4.09375 -0.03125 4.921875 -0.75 Z M 4.921875 0.1875 "/> +</symbol> +<symbol overflow="visible" id="glyph6-8"> +<path style="stroke:none;" d="M 5.90625 -1.5625 C 5.375 -0.96875 5 -0.765625 4.421875 -0.765625 C 3.171875 -0.765625 2.375 -2.015625 2.375 -3.953125 C 2.375 -5.4375 2.84375 -6.34375 3.578125 -6.34375 C 3.8125 -6.34375 4.03125 -6.21875 4.109375 -6.0625 C 4.1875 -5.9375 4.1875 -5.9375 4.1875 -5.328125 C 4.203125 -4.640625 4.453125 -4.296875 4.984375 -4.296875 C 5.59375 -4.296875 5.96875 -4.640625 5.96875 -5.203125 C 5.96875 -6.09375 5 -6.78125 3.75 -6.78125 C 1.796875 -6.78125 0.359375 -5.265625 0.359375 -3.203125 C 0.359375 -1.21875 1.65625 0.203125 3.421875 0.203125 C 4.53125 0.203125 5.328125 -0.25 6.171875 -1.3125 Z M 5.90625 -1.5625 "/> +</symbol> +<symbol overflow="visible" id="glyph6-9"> +<path style="stroke:none;" d="M 2.984375 -6.609375 L 0.234375 -6.609375 L 0.234375 -6.265625 C 0.859375 -6.140625 0.984375 -6.015625 0.984375 -5.421875 L 0.984375 -1.203125 C 0.984375 -0.609375 0.890625 -0.5 0.234375 -0.34375 L 0.234375 0 L 3.65625 0 L 3.65625 -0.34375 C 3.15625 -0.421875 2.984375 -0.609375 2.984375 -1.15625 Z M 1.984375 -9.90625 C 1.359375 -9.90625 0.859375 -9.40625 0.859375 -8.8125 C 0.859375 -8.15625 1.328125 -7.6875 1.96875 -7.6875 C 2.59375 -7.6875 3.078125 -8.15625 3.078125 -8.796875 C 3.078125 -9.40625 2.59375 -9.90625 1.984375 -9.90625 Z M 1.984375 -9.90625 "/> +</symbol> +<symbol overflow="visible" id="glyph7-0"> +<path style="stroke:none;" d="M 10.171875 -7.21875 L 8 -7.21875 L 8 -7.015625 C 8.5625 -6.953125 8.75 -6.84375 8.75 -6.546875 C 8.75 -6.3125 8.6875 -6.03125 8.578125 -5.71875 L 7.21875 -2.03125 L 5.78125 -5.75 C 5.765625 -5.796875 5.65625 -6.046875 5.65625 -6.078125 C 5.546875 -6.296875 5.484375 -6.515625 5.484375 -6.625 C 5.484375 -6.890625 5.734375 -7 6.328125 -7.015625 L 6.328125 -7.21875 L 3.40625 -7.21875 L 3.40625 -7.015625 C 4.03125 -7 4.140625 -6.90625 4.515625 -6.03125 L 4.875 -5.140625 L 3.703125 -2.0625 L 2.140625 -6.15625 C 2.0625 -6.359375 2.015625 -6.546875 2.015625 -6.65625 C 2.015625 -6.90625 2.171875 -6.984375 2.71875 -7.015625 L 2.71875 -7.21875 L 0.046875 -7.21875 L 0.046875 -7.015625 C 0.609375 -6.953125 0.78125 -6.765625 1.171875 -5.734375 L 3.28125 0.125 L 3.453125 0.125 L 5.125 -4.5 L 6.875 0.125 L 7.03125 0.125 C 7.96875 -2.71875 8.078125 -3.03125 9.265625 -6.234375 C 9.46875 -6.8125 9.59375 -6.90625 10.171875 -7.015625 Z M 10.171875 -7.21875 "/> +</symbol> +<symbol overflow="visible" id="glyph7-1"> +<path style="stroke:none;" d="M 4.453125 -1.78125 C 3.921875 -0.953125 3.453125 -0.640625 2.765625 -0.640625 C 2.140625 -0.640625 1.671875 -0.953125 1.359375 -1.578125 C 1.15625 -2 1.078125 -2.359375 1.0625 -3.015625 L 4.421875 -3.015625 C 4.328125 -3.734375 4.21875 -4.046875 3.953125 -4.390625 C 3.625 -4.78125 3.125 -5.015625 2.546875 -5.015625 C 2 -5.015625 1.5 -4.828125 1.078125 -4.453125 C 0.5625 -4 0.265625 -3.234375 0.265625 -2.328125 C 0.265625 -0.828125 1.0625 0.109375 2.3125 0.109375 C 3.34375 0.109375 4.171875 -0.53125 4.625 -1.71875 Z M 1.078125 -3.375 C 1.203125 -4.21875 1.578125 -4.625 2.234375 -4.625 C 2.90625 -4.625 3.15625 -4.3125 3.296875 -3.375 Z M 1.078125 -3.375 "/> +</symbol> +<symbol overflow="visible" id="glyph7-2"> +<path style="stroke:none;" d="M 1.671875 -7.421875 L 1.609375 -7.453125 C 1.15625 -7.28125 0.859375 -7.203125 0.34375 -7.0625 L 0.03125 -6.96875 L 0.03125 -6.796875 C 0.09375 -6.8125 0.140625 -6.8125 0.21875 -6.8125 C 0.65625 -6.8125 0.75 -6.703125 0.75 -6.25 L 0.75 -0.59375 C 0.75 -0.25 1.671875 0.109375 2.546875 0.109375 C 3.984375 0.109375 5.109375 -1.09375 5.109375 -2.65625 C 5.109375 -3.984375 4.28125 -5.015625 3.1875 -5.015625 C 2.515625 -5.015625 1.890625 -4.625 1.671875 -4.09375 Z M 1.671875 -3.515625 C 1.671875 -3.9375 2.1875 -4.328125 2.75 -4.328125 C 3.59375 -4.328125 4.140625 -3.484375 4.140625 -2.15625 C 4.140625 -0.9375 3.625 -0.234375 2.71875 -0.234375 C 2.15625 -0.234375 1.671875 -0.484375 1.671875 -0.765625 Z M 1.671875 -3.515625 "/> +</symbol> +<symbol overflow="visible" id="glyph7-3"> +<path style="stroke:none;" d="M 0.078125 -4.25 C 0.234375 -4.28125 0.328125 -4.296875 0.453125 -4.296875 C 0.734375 -4.296875 0.828125 -4.125 0.828125 -3.640625 L 0.828125 -0.921875 C 0.828125 -0.375 0.75 -0.296875 0.046875 -0.15625 L 0.046875 0 L 2.671875 0 L 2.671875 -0.15625 C 1.9375 -0.203125 1.75 -0.359375 1.75 -0.984375 L 1.75 -3.4375 C 1.75 -3.78125 2.21875 -4.328125 2.515625 -4.328125 C 2.578125 -4.328125 2.671875 -4.28125 2.796875 -4.171875 C 2.96875 -4.015625 3.09375 -3.953125 3.234375 -3.953125 C 3.484375 -3.953125 3.65625 -4.140625 3.65625 -4.4375 C 3.65625 -4.796875 3.421875 -5.015625 3.046875 -5.015625 C 2.59375 -5.015625 2.28125 -4.765625 1.75 -3.984375 L 1.75 -5 L 1.6875 -5.015625 C 1.109375 -4.78125 0.71875 -4.640625 0.078125 -4.421875 Z M 0.078125 -4.25 "/> +</symbol> +<symbol overflow="visible" id="glyph7-4"> +<path style="stroke:none;" d="M 2.71875 -5.015625 C 1.3125 -5.015625 0.3125 -3.96875 0.3125 -2.46875 C 0.3125 -1 1.328125 0.109375 2.703125 0.109375 C 4.078125 0.109375 5.125 -1.046875 5.125 -2.546875 C 5.125 -3.984375 4.125 -5.015625 2.71875 -5.015625 Z M 2.578125 -4.71875 C 3.5 -4.71875 4.140625 -3.671875 4.140625 -2.171875 C 4.140625 -0.9375 3.65625 -0.203125 2.828125 -0.203125 C 2.40625 -0.203125 2 -0.453125 1.78125 -0.890625 C 1.46875 -1.46875 1.296875 -2.21875 1.296875 -3 C 1.296875 -4.03125 1.8125 -4.71875 2.578125 -4.71875 Z M 2.578125 -4.71875 "/> +</symbol> +<symbol overflow="visible" id="glyph7-5"> +<path style="stroke:none;" d="M 6.234375 -4.90625 L 6.234375 -4.75 C 6.59375 -4.671875 6.703125 -4.59375 6.703125 -4.40625 C 6.703125 -4.25 6.640625 -3.984375 6.515625 -3.6875 L 5.546875 -1.265625 L 4.625 -3.703125 C 4.4375 -4.203125 4.4375 -4.203125 4.4375 -4.359375 C 4.4375 -4.59375 4.5625 -4.671875 5.078125 -4.75 L 5.078125 -4.90625 L 2.859375 -4.90625 L 2.859375 -4.75 C 3.265625 -4.703125 3.390625 -4.578125 3.609375 -3.984375 C 3.6875 -3.78125 3.765625 -3.578125 3.828125 -3.375 L 2.828125 -1.203125 L 1.75 -4.0625 C 1.71875 -4.171875 1.6875 -4.28125 1.6875 -4.390625 C 1.6875 -4.609375 1.828125 -4.703125 2.1875 -4.75 L 2.1875 -4.90625 L 0.234375 -4.90625 L 0.234375 -4.75 C 0.484375 -4.71875 0.5625 -4.609375 0.8125 -4.0625 L 2.28125 -0.328125 C 2.40625 0 2.5 0.15625 2.5625 0.15625 C 2.625 0.15625 2.703125 0.015625 2.828125 -0.265625 L 4.0625 -2.890625 L 5.046875 -0.3125 C 5.203125 0.09375 5.25 0.15625 5.3125 0.15625 C 5.390625 0.15625 5.4375 0.046875 5.609375 -0.375 L 7.125 -4.15625 C 7.3125 -4.609375 7.34375 -4.671875 7.5625 -4.75 L 7.5625 -4.90625 Z M 6.234375 -4.90625 "/> +</symbol> +<symbol overflow="visible" id="glyph7-6"> +<path style="stroke:none;" d="M 3.4375 -3.421875 L 3.390625 -4.90625 L 3.265625 -4.90625 L 3.25 -4.890625 C 3.15625 -4.8125 3.140625 -4.796875 3.09375 -4.796875 C 3.03125 -4.796875 2.921875 -4.828125 2.796875 -4.875 C 2.5625 -4.96875 2.328125 -5 2.046875 -5 C 1.171875 -5 0.5625 -4.453125 0.5625 -3.671875 C 0.5625 -3.046875 0.90625 -2.625 1.828125 -2.09375 L 2.46875 -1.734375 C 2.84375 -1.515625 3.03125 -1.25 3.03125 -0.921875 C 3.03125 -0.4375 2.6875 -0.125 2.125 -0.125 C 1.75 -0.125 1.421875 -0.265625 1.203125 -0.515625 C 0.984375 -0.78125 0.890625 -1.03125 0.734375 -1.65625 L 0.5625 -1.65625 L 0.5625 0.046875 L 0.703125 0.046875 C 0.78125 -0.0625 0.828125 -0.09375 0.953125 -0.09375 C 1.0625 -0.09375 1.203125 -0.0625 1.46875 0 C 1.765625 0.0625 2.0625 0.109375 2.25 0.109375 C 3.09375 0.109375 3.796875 -0.53125 3.796875 -1.28125 C 3.796875 -1.828125 3.53125 -2.1875 2.875 -2.578125 L 1.703125 -3.28125 C 1.390625 -3.453125 1.234375 -3.734375 1.234375 -4.03125 C 1.234375 -4.453125 1.578125 -4.765625 2.078125 -4.765625 C 2.6875 -4.765625 3.015625 -4.390625 3.265625 -3.421875 Z M 3.4375 -3.421875 "/> +</symbol> +<symbol overflow="visible" id="glyph7-7"> +<path style="stroke:none;" d="M 1.71875 -3.734375 C 2.171875 -4.25 2.5 -4.421875 2.921875 -4.421875 C 3.46875 -4.421875 3.734375 -4.03125 3.734375 -3.265625 L 3.734375 -1.109375 C 3.734375 -0.375 3.625 -0.234375 3 -0.15625 L 3 0 L 5.3125 0 L 5.3125 -0.15625 C 4.71875 -0.265625 4.65625 -0.359375 4.65625 -1.109375 L 4.65625 -3.28125 C 4.65625 -4.421875 4.203125 -5.015625 3.3125 -5.015625 C 2.671875 -5.015625 2.21875 -4.75 1.71875 -4.09375 L 1.71875 -7.421875 L 1.65625 -7.453125 C 1.28125 -7.3125 1.015625 -7.234375 0.40625 -7.0625 L 0.109375 -6.96875 L 0.109375 -6.796875 C 0.15625 -6.8125 0.1875 -6.8125 0.234375 -6.8125 C 0.703125 -6.8125 0.796875 -6.71875 0.796875 -6.25 L 0.796875 -1.109375 C 0.796875 -0.34375 0.734375 -0.25 0.09375 -0.15625 L 0.09375 0 L 2.453125 0 L 2.453125 -0.15625 C 1.828125 -0.234375 1.71875 -0.359375 1.71875 -1.109375 Z M 1.71875 -3.734375 "/> +</symbol> +<symbol overflow="visible" id="glyph7-8"> +<path style="stroke:none;" d="M 4.828125 -0.71875 C 4.640625 -0.5625 4.5 -0.515625 4.34375 -0.515625 C 4.09375 -0.515625 4.015625 -0.671875 4.015625 -1.140625 L 4.015625 -3.265625 C 4.015625 -3.84375 3.953125 -4.15625 3.796875 -4.421875 C 3.5625 -4.8125 3.09375 -5.015625 2.4375 -5.015625 C 1.421875 -5.015625 0.609375 -4.484375 0.609375 -3.796875 C 0.609375 -3.546875 0.828125 -3.328125 1.078125 -3.328125 C 1.34375 -3.328125 1.578125 -3.546875 1.578125 -3.78125 C 1.578125 -3.828125 1.5625 -3.875 1.546875 -3.953125 C 1.53125 -4.0625 1.515625 -4.140625 1.515625 -4.21875 C 1.515625 -4.515625 1.859375 -4.75 2.296875 -4.75 C 2.828125 -4.75 3.125 -4.4375 3.125 -3.84375 L 3.125 -3.1875 C 1.453125 -2.515625 1.265625 -2.421875 0.796875 -2 C 0.5625 -1.78125 0.40625 -1.421875 0.40625 -1.0625 C 0.40625 -0.375 0.890625 0.109375 1.546875 0.109375 C 2.03125 0.109375 2.46875 -0.125 3.140625 -0.6875 C 3.203125 -0.125 3.390625 0.109375 3.84375 0.109375 C 4.203125 0.109375 4.4375 -0.015625 4.828125 -0.4375 Z M 3.125 -1.34375 C 3.125 -1 3.078125 -0.90625 2.84375 -0.78125 C 2.578125 -0.625 2.28125 -0.53125 2.046875 -0.53125 C 1.671875 -0.53125 1.359375 -0.890625 1.359375 -1.359375 L 1.359375 -1.40625 C 1.359375 -2.046875 1.8125 -2.4375 3.125 -2.921875 Z M 3.125 -1.34375 "/> +</symbol> +<symbol overflow="visible" id="glyph7-9"> +<path style="stroke:none;" d="M 5.203125 -4.90625 L 3.6875 -4.90625 L 3.6875 -4.75 C 4.03125 -4.71875 4.203125 -4.609375 4.203125 -4.390625 C 4.203125 -4.28125 4.171875 -4.171875 4.140625 -4.0625 L 3.046875 -1.25 L 1.9375 -4.03125 C 1.875 -4.1875 1.84375 -4.34375 1.84375 -4.4375 C 1.84375 -4.640625 1.96875 -4.71875 2.34375 -4.75 L 2.34375 -4.90625 L 0.203125 -4.90625 L 0.203125 -4.75 C 0.625 -4.71875 0.703125 -4.609375 1.203125 -3.484375 L 2.515625 -0.359375 C 2.53125 -0.296875 2.5625 -0.21875 2.59375 -0.125 C 2.65625 0.0625 2.71875 0.15625 2.796875 0.15625 C 2.859375 0.15625 2.9375 0.015625 3.09375 -0.390625 L 4.5 -3.890625 C 4.8125 -4.640625 4.875 -4.71875 5.203125 -4.75 Z M 5.203125 -4.90625 "/> +</symbol> +<symbol overflow="visible" id="glyph7-10"> +<path style="stroke:none;" d="M 0.203125 -6.796875 L 0.265625 -6.796875 C 0.390625 -6.8125 0.53125 -6.8125 0.609375 -6.8125 C 0.953125 -6.8125 1.0625 -6.65625 1.0625 -6.15625 L 1.0625 -0.953125 C 1.0625 -0.359375 0.921875 -0.21875 0.234375 -0.15625 L 0.234375 0 L 2.796875 0 L 2.796875 -0.15625 C 2.109375 -0.203125 1.984375 -0.3125 1.984375 -0.921875 L 1.984375 -7.421875 L 1.9375 -7.453125 C 1.375 -7.265625 0.953125 -7.15625 0.203125 -6.96875 Z M 0.203125 -6.796875 "/> +</symbol> +<symbol overflow="visible" id="glyph7-11"> +<path style="stroke:none;" d="M 3.75 0.109375 L 5.359375 -0.453125 L 5.359375 -0.625 C 5.15625 -0.625 5.140625 -0.625 5.109375 -0.625 C 4.71875 -0.625 4.625 -0.734375 4.625 -1.25 L 4.625 -7.421875 L 4.5625 -7.453125 C 4.046875 -7.265625 3.671875 -7.15625 2.96875 -6.96875 L 2.96875 -6.796875 C 3.046875 -6.8125 3.125 -6.8125 3.203125 -6.8125 C 3.609375 -6.8125 3.703125 -6.703125 3.703125 -6.25 L 3.703125 -4.546875 C 3.296875 -4.890625 3 -5.015625 2.5625 -5.015625 C 1.3125 -5.015625 0.296875 -3.78125 0.296875 -2.234375 C 0.296875 -0.84375 1.109375 0.109375 2.3125 0.109375 C 2.921875 0.109375 3.34375 -0.109375 3.703125 -0.625 L 3.703125 0.078125 Z M 3.703125 -1.109375 C 3.703125 -1.03125 3.625 -0.90625 3.515625 -0.78125 C 3.328125 -0.5625 3.046875 -0.453125 2.734375 -0.453125 C 1.828125 -0.453125 1.234375 -1.328125 1.234375 -2.671875 C 1.234375 -3.90625 1.765625 -4.71875 2.59375 -4.71875 C 3.171875 -4.71875 3.703125 -4.203125 3.703125 -3.625 Z M 3.703125 -1.109375 "/> +</symbol> +<symbol overflow="visible" id="glyph7-12"> +<path style="stroke:none;" d="M 2.78125 -4.90625 L 1.671875 -4.90625 L 1.671875 -6.171875 C 1.671875 -6.28125 1.671875 -6.3125 1.609375 -6.3125 C 1.53125 -6.21875 1.46875 -6.125 1.390625 -6.015625 C 0.96875 -5.40625 0.5 -4.890625 0.328125 -4.84375 C 0.203125 -4.765625 0.140625 -4.6875 0.140625 -4.640625 C 0.140625 -4.609375 0.15625 -4.578125 0.1875 -4.5625 L 0.765625 -4.5625 L 0.765625 -1.28125 C 0.765625 -0.359375 1.09375 0.109375 1.734375 0.109375 C 2.265625 0.109375 2.6875 -0.15625 3.046875 -0.71875 L 2.90625 -0.84375 C 2.671875 -0.5625 2.484375 -0.453125 2.25 -0.453125 C 1.84375 -0.453125 1.671875 -0.75 1.671875 -1.4375 L 1.671875 -4.5625 L 2.78125 -4.5625 Z M 2.78125 -4.90625 "/> +</symbol> +<symbol overflow="visible" id="glyph7-13"> +<path style="stroke:none;" d="M 0.203125 -4.34375 C 0.34375 -4.375 0.4375 -4.390625 0.5625 -4.390625 C 0.84375 -4.390625 0.9375 -4.203125 0.9375 -3.6875 L 0.9375 -0.921875 C 0.9375 -0.34375 0.78125 -0.171875 0.171875 -0.15625 L 0.171875 0 L 2.59375 0 L 2.59375 -0.15625 C 2.015625 -0.1875 1.859375 -0.3125 1.859375 -0.734375 L 1.859375 -3.8125 C 1.859375 -3.828125 1.9375 -3.9375 2.015625 -4.015625 C 2.296875 -4.265625 2.765625 -4.453125 3.140625 -4.453125 C 3.625 -4.453125 3.859375 -4.0625 3.859375 -3.296875 L 3.859375 -0.9375 C 3.859375 -0.328125 3.734375 -0.203125 3.125 -0.15625 L 3.125 0 L 5.5625 0 L 5.5625 -0.15625 C 4.9375 -0.171875 4.78125 -0.359375 4.78125 -1.03125 L 4.78125 -3.78125 C 5.109375 -4.25 5.46875 -4.453125 5.96875 -4.453125 C 6.59375 -4.453125 6.78125 -4.15625 6.78125 -3.25 L 6.78125 -0.953125 C 6.78125 -0.328125 6.703125 -0.234375 6.0625 -0.15625 L 6.0625 0 L 8.453125 0 L 8.453125 -0.15625 L 8.171875 -0.1875 C 7.84375 -0.203125 7.703125 -0.40625 7.703125 -0.828125 L 7.703125 -3.078125 C 7.703125 -4.359375 7.28125 -5.015625 6.4375 -5.015625 C 5.796875 -5.015625 5.25 -4.734375 4.65625 -4.09375 C 4.453125 -4.71875 4.09375 -5.015625 3.5 -5.015625 C 3.015625 -5.015625 2.71875 -4.859375 1.8125 -4.171875 L 1.8125 -5 L 1.734375 -5.015625 C 1.171875 -4.8125 0.8125 -4.6875 0.203125 -4.53125 Z M 0.203125 -4.34375 "/> +</symbol> +<symbol overflow="visible" id="glyph7-14"> +<path style="stroke:none;" d="M 5.21875 -0.546875 L 5.171875 -0.546875 C 4.671875 -0.546875 4.546875 -0.671875 4.546875 -1.171875 L 4.546875 -4.90625 L 2.828125 -4.90625 L 2.828125 -4.71875 C 3.5 -4.6875 3.625 -4.578125 3.625 -4.03125 L 3.625 -1.46875 C 3.625 -1.171875 3.578125 -1.015625 3.421875 -0.890625 C 3.125 -0.65625 2.796875 -0.53125 2.46875 -0.53125 C 2.046875 -0.53125 1.6875 -0.890625 1.6875 -1.359375 L 1.6875 -4.90625 L 0.09375 -4.90625 L 0.09375 -4.75 C 0.625 -4.71875 0.78125 -4.5625 0.78125 -4.0625 L 0.78125 -1.3125 C 0.78125 -0.453125 1.296875 0.109375 2.09375 0.109375 C 2.5 0.109375 2.921875 -0.0625 3.21875 -0.359375 L 3.6875 -0.828125 L 3.6875 0.078125 L 3.734375 0.09375 C 4.28125 -0.125 4.671875 -0.234375 5.21875 -0.390625 Z M 5.21875 -0.546875 "/> +</symbol> +<symbol overflow="visible" id="glyph7-15"> +<path style="stroke:none;" d="M 1.90625 -5.015625 L 0.21875 -4.421875 L 0.21875 -4.25 L 0.3125 -4.265625 C 0.4375 -4.28125 0.578125 -4.296875 0.671875 -4.296875 C 0.9375 -4.296875 1.03125 -4.125 1.03125 -3.640625 L 1.03125 -1.109375 C 1.03125 -0.328125 0.921875 -0.203125 0.171875 -0.15625 L 0.171875 0 L 2.765625 0 L 2.765625 -0.15625 C 2.046875 -0.21875 1.953125 -0.328125 1.953125 -1.109375 L 1.953125 -4.984375 Z M 1.390625 -7.453125 C 1.09375 -7.453125 0.84375 -7.203125 0.84375 -6.890625 C 0.84375 -6.59375 1.09375 -6.34375 1.390625 -6.34375 C 1.71875 -6.34375 1.96875 -6.578125 1.96875 -6.890625 C 1.96875 -7.203125 1.71875 -7.453125 1.390625 -7.453125 Z M 1.390625 -7.453125 "/> +</symbol> +<symbol overflow="visible" id="glyph7-16"> +<path style="stroke:none;" d="M 0.421875 -2.796875 L 0.421875 -2.109375 L 3.109375 -2.109375 L 3.109375 -2.796875 Z M 0.421875 -2.796875 "/> +</symbol> +<symbol overflow="visible" id="glyph7-17"> +<path style="stroke:none;" d="M 0.09375 -4.28125 C 0.203125 -4.296875 0.265625 -4.296875 0.375 -4.296875 C 0.734375 -4.296875 0.8125 -4.1875 0.8125 -3.671875 L 0.8125 1.421875 C 0.8125 2 0.703125 2.109375 0.046875 2.1875 L 0.046875 2.359375 L 2.6875 2.359375 L 2.6875 2.171875 C 1.875 2.15625 1.734375 2.046875 1.734375 1.359375 L 1.734375 -0.359375 C 2.109375 0 2.375 0.109375 2.828125 0.109375 C 4.125 0.109375 5.125 -1.109375 5.125 -2.6875 C 5.125 -4.046875 4.359375 -5.015625 3.296875 -5.015625 C 2.6875 -5.015625 2.21875 -4.75 1.734375 -4.15625 L 1.734375 -5 L 1.671875 -5.015625 C 1.078125 -4.78125 0.703125 -4.640625 0.09375 -4.453125 Z M 1.734375 -3.640625 C 1.734375 -3.96875 2.34375 -4.359375 2.84375 -4.359375 C 3.65625 -4.359375 4.1875 -3.53125 4.1875 -2.265625 C 4.1875 -1.0625 3.65625 -0.234375 2.875 -0.234375 C 2.359375 -0.234375 1.734375 -0.625 1.734375 -0.953125 Z M 1.734375 -3.640625 "/> +</symbol> +<symbol overflow="visible" id="glyph7-18"> +<path style="stroke:none;" d="M 0.171875 -4.34375 C 0.234375 -4.375 0.34375 -4.390625 0.46875 -4.390625 C 0.78125 -4.390625 0.875 -4.21875 0.875 -3.6875 L 0.875 -0.984375 C 0.875 -0.359375 0.75 -0.203125 0.203125 -0.15625 L 0.203125 0 L 2.515625 0 L 2.515625 -0.15625 C 1.953125 -0.203125 1.78125 -0.34375 1.78125 -0.734375 L 1.78125 -3.796875 C 2.3125 -4.28125 2.546875 -4.421875 2.90625 -4.421875 C 3.453125 -4.421875 3.703125 -4.078125 3.703125 -3.359375 L 3.703125 -1.078125 C 3.703125 -0.390625 3.5625 -0.203125 3.015625 -0.15625 L 3.015625 0 L 5.296875 0 L 5.296875 -0.15625 C 4.75 -0.21875 4.625 -0.34375 4.625 -0.890625 L 4.625 -3.375 C 4.625 -4.40625 4.140625 -5.015625 3.34375 -5.015625 C 2.828125 -5.015625 2.5 -4.828125 1.75 -4.140625 L 1.75 -5 L 1.671875 -5.015625 C 1.140625 -4.828125 0.78125 -4.703125 0.171875 -4.53125 Z M 0.171875 -4.34375 "/> +</symbol> +<symbol overflow="visible" id="glyph7-19"> +<path style="stroke:none;" d="M 4.34375 -1.703125 C 3.8125 -0.9375 3.421875 -0.671875 2.796875 -0.671875 C 1.8125 -0.671875 1.109375 -1.546875 1.109375 -2.796875 C 1.109375 -3.921875 1.71875 -4.703125 2.59375 -4.703125 C 2.984375 -4.703125 3.125 -4.578125 3.234375 -4.171875 L 3.296875 -3.9375 C 3.390625 -3.625 3.59375 -3.4375 3.8125 -3.4375 C 4.09375 -3.4375 4.34375 -3.640625 4.34375 -3.890625 C 4.34375 -4.5 3.578125 -5.015625 2.65625 -5.015625 C 2.125 -5.015625 1.578125 -4.796875 1.125 -4.40625 C 0.578125 -3.921875 0.265625 -3.1875 0.265625 -2.328125 C 0.265625 -0.90625 1.140625 0.109375 2.34375 0.109375 C 2.828125 0.109375 3.265625 -0.0625 3.671875 -0.40625 C 3.953125 -0.671875 4.171875 -0.953125 4.5 -1.609375 Z M 4.34375 -1.703125 "/> +</symbol> +<symbol overflow="visible" id="glyph7-20"> +<path style="stroke:none;" d="M 5.125 -4.234375 L 5.125 -4.65625 L 4.28125 -4.65625 C 4.0625 -4.65625 3.90625 -4.6875 3.6875 -4.765625 L 3.453125 -4.859375 C 3.15625 -4.96875 2.859375 -5.015625 2.578125 -5.015625 C 1.5625 -5.015625 0.75 -4.234375 0.75 -3.234375 C 0.75 -2.546875 1.046875 -2.140625 1.765625 -1.78125 C 1.5625 -1.578125 1.359375 -1.390625 1.296875 -1.34375 C 0.9375 -1.03125 0.796875 -0.8125 0.796875 -0.59375 C 0.796875 -0.359375 0.921875 -0.234375 1.375 -0.015625 C 0.59375 0.5625 0.3125 0.921875 0.3125 1.3125 C 0.3125 1.890625 1.15625 2.375 2.1875 2.375 C 3.015625 2.375 3.859375 2.09375 4.421875 1.640625 C 4.84375 1.296875 5.03125 0.953125 5.03125 0.53125 C 5.03125 -0.140625 4.515625 -0.59375 3.703125 -0.625 L 2.296875 -0.703125 C 1.71875 -0.71875 1.453125 -0.8125 1.453125 -1 C 1.453125 -1.203125 1.8125 -1.59375 2.109375 -1.671875 C 2.203125 -1.671875 2.28125 -1.65625 2.3125 -1.65625 C 2.515625 -1.640625 2.65625 -1.625 2.71875 -1.625 C 3.125 -1.625 3.5625 -1.78125 3.90625 -2.078125 C 4.265625 -2.390625 4.421875 -2.765625 4.421875 -3.3125 C 4.421875 -3.625 4.375 -3.875 4.21875 -4.234375 Z M 1.609375 0.015625 C 1.96875 0.09375 2.828125 0.15625 3.375 0.15625 C 4.359375 0.15625 4.71875 0.3125 4.71875 0.703125 C 4.71875 1.328125 3.890625 1.75 2.65625 1.75 C 1.703125 1.75 1.0625 1.4375 1.0625 0.953125 C 1.0625 0.703125 1.140625 0.5625 1.609375 0.015625 Z M 1.65625 -3.6875 C 1.65625 -4.328125 1.96875 -4.71875 2.46875 -4.71875 C 2.796875 -4.71875 3.09375 -4.53125 3.265625 -4.203125 C 3.453125 -3.8125 3.59375 -3.3125 3.59375 -2.890625 C 3.59375 -2.28125 3.265625 -1.890625 2.765625 -1.890625 C 2.109375 -1.890625 1.65625 -2.609375 1.65625 -3.65625 Z M 1.65625 -3.6875 "/> +</symbol> +<symbol overflow="visible" id="glyph7-21"> +<path style="stroke:none;" d="M 3.265625 1.421875 L 2.328125 1.421875 C 1.96875 1.421875 1.78125 1.25 1.78125 0.859375 L 1.78125 -6.46875 C 1.78125 -6.8125 1.9375 -6.953125 2.28125 -6.953125 L 3.265625 -6.953125 L 3.265625 -7.21875 L 0.953125 -7.21875 L 0.953125 1.703125 L 3.265625 1.703125 Z M 3.265625 1.421875 "/> +</symbol> +<symbol overflow="visible" id="glyph7-22"> +<path style="stroke:none;" d="M 1.671875 -3.59375 C 2.3125 -3.59375 2.5625 -3.578125 2.828125 -3.484375 C 3.5 -3.234375 3.921875 -2.625 3.921875 -1.859375 C 3.921875 -0.953125 3.296875 -0.234375 2.5 -0.234375 C 2.203125 -0.234375 1.984375 -0.3125 1.578125 -0.578125 C 1.25 -0.78125 1.0625 -0.84375 0.890625 -0.84375 C 0.625 -0.84375 0.46875 -0.703125 0.46875 -0.46875 C 0.46875 -0.09375 0.9375 0.15625 1.703125 0.15625 C 2.546875 0.15625 3.40625 -0.125 3.921875 -0.578125 C 4.421875 -1.03125 4.71875 -1.65625 4.71875 -2.390625 C 4.71875 -2.9375 4.53125 -3.453125 4.21875 -3.796875 C 4 -4.03125 3.796875 -4.171875 3.3125 -4.375 C 4.0625 -4.890625 4.34375 -5.296875 4.34375 -5.875 C 4.34375 -6.765625 3.640625 -7.375 2.640625 -7.375 C 2.09375 -7.375 1.609375 -7.1875 1.21875 -6.84375 C 0.890625 -6.546875 0.734375 -6.265625 0.484375 -5.609375 L 0.65625 -5.5625 C 1.09375 -6.359375 1.59375 -6.71875 2.28125 -6.71875 C 2.984375 -6.71875 3.484375 -6.234375 3.484375 -5.546875 C 3.484375 -5.15625 3.3125 -4.765625 3.046875 -4.5 C 2.71875 -4.171875 2.40625 -4 1.671875 -3.734375 Z M 1.671875 -3.59375 "/> +</symbol> +<symbol overflow="visible" id="glyph7-23"> +<path style="stroke:none;" d="M 0.640625 0.234375 C 1.859375 0.09375 2.46875 -0.109375 3.203125 -0.640625 C 4.34375 -1.46875 5 -2.828125 5 -4.296875 C 5 -6.09375 4 -7.375 2.59375 -7.375 C 1.296875 -7.375 0.328125 -6.265625 0.328125 -4.796875 C 0.328125 -3.46875 1.109375 -2.578125 2.296875 -2.578125 C 2.890625 -2.578125 3.34375 -2.765625 3.921875 -3.203125 C 3.484375 -1.421875 2.265625 -0.265625 0.609375 0.015625 Z M 3.953125 -3.875 C 3.953125 -3.65625 3.90625 -3.5625 3.78125 -3.453125 C 3.484375 -3.203125 3.078125 -3.046875 2.6875 -3.046875 C 1.859375 -3.046875 1.328125 -3.875 1.328125 -5.171875 C 1.328125 -5.796875 1.5 -6.453125 1.734375 -6.734375 C 1.921875 -6.953125 2.1875 -7.0625 2.515625 -7.0625 C 3.453125 -7.0625 3.953125 -6.125 3.953125 -4.296875 Z M 3.953125 -3.875 "/> +</symbol> +<symbol overflow="visible" id="glyph7-24"> +<path style="stroke:none;" d="M 0.375 1.421875 L 0.375 1.703125 L 2.671875 1.703125 L 2.671875 -7.21875 L 0.375 -7.21875 L 0.375 -6.953125 L 1.3125 -6.953125 C 1.671875 -6.953125 1.84375 -6.765625 1.84375 -6.375 L 1.84375 0.953125 C 1.84375 1.28125 1.6875 1.421875 1.359375 1.421875 Z M 0.375 1.421875 "/> +</symbol> +<symbol overflow="visible" id="glyph7-25"> +<path style="stroke:none;" d="M 1.359375 -1.09375 C 1.03125 -1.09375 0.765625 -0.8125 0.765625 -0.46875 C 0.765625 -0.15625 1.03125 0.125 1.359375 0.125 C 1.6875 0.125 1.96875 -0.15625 1.96875 -0.46875 C 1.96875 -0.8125 1.6875 -1.09375 1.359375 -1.09375 Z M 1.359375 -1.09375 "/> +</symbol> +<symbol overflow="visible" id="glyph7-26"> +<path style="stroke:none;" d="M 4.875 -7.375 L 4.640625 -7.375 C 4.609375 -7.125 4.484375 -7 4.296875 -7 C 4.1875 -7 4 -7.046875 3.8125 -7.125 C 3.40625 -7.28125 3.015625 -7.375 2.65625 -7.375 C 2.1875 -7.375 1.71875 -7.1875 1.359375 -6.875 C 0.96875 -6.53125 0.78125 -6.078125 0.78125 -5.5 C 0.78125 -4.640625 1.25 -4.03125 2.46875 -3.375 C 3.265625 -2.96875 3.828125 -2.515625 4.09375 -2.109375 C 4.203125 -1.96875 4.25 -1.734375 4.25 -1.46875 C 4.25 -0.734375 3.703125 -0.234375 2.90625 -0.234375 C 1.9375 -0.234375 1.25 -0.84375 0.703125 -2.171875 L 0.453125 -2.171875 L 0.78125 0.140625 L 1.03125 0.140625 C 1.03125 -0.0625 1.171875 -0.21875 1.328125 -0.21875 C 1.453125 -0.21875 1.640625 -0.171875 1.84375 -0.09375 C 2.25 0.0625 2.6875 0.15625 3.125 0.15625 C 4.390625 0.15625 5.359375 -0.703125 5.359375 -1.828125 C 5.359375 -2.71875 4.75 -3.4375 3.3125 -4.203125 C 2.171875 -4.84375 1.71875 -5.328125 1.71875 -5.90625 C 1.71875 -6.515625 2.171875 -6.921875 2.84375 -6.921875 C 3.34375 -6.921875 3.796875 -6.71875 4.171875 -6.328125 C 4.515625 -5.984375 4.671875 -5.6875 4.84375 -5.046875 L 5.109375 -5.046875 Z M 4.875 -7.375 "/> +</symbol> +<symbol overflow="visible" id="glyph7-27"> +<path style="stroke:none;" d="M 5.1875 -4.90625 L 3.703125 -4.90625 L 3.703125 -4.75 C 4.0625 -4.75 4.234375 -4.640625 4.234375 -4.46875 C 4.234375 -4.421875 4.21875 -4.359375 4.1875 -4.28125 L 3.125 -1.28125 L 1.875 -4.03125 C 1.8125 -4.1875 1.765625 -4.328125 1.765625 -4.453125 C 1.765625 -4.640625 1.9375 -4.71875 2.40625 -4.75 L 2.40625 -4.90625 L 0.15625 -4.90625 L 0.15625 -4.75 C 0.4375 -4.71875 0.625 -4.59375 0.703125 -4.40625 L 1.953125 -1.71875 L 1.984375 -1.640625 L 2.15625 -1.3125 C 2.453125 -0.765625 2.625 -0.375 2.625 -0.203125 C 2.625 -0.046875 2.375 0.640625 2.1875 0.96875 C 2.046875 1.25 1.796875 1.46875 1.640625 1.46875 C 1.578125 1.46875 1.484375 1.4375 1.375 1.390625 C 1.171875 1.3125 0.984375 1.265625 0.796875 1.265625 C 0.546875 1.265625 0.328125 1.484375 0.328125 1.75 C 0.328125 2.109375 0.671875 2.375 1.140625 2.375 C 1.859375 2.375 2.390625 1.765625 2.984375 0.203125 L 4.65625 -4.25 C 4.796875 -4.609375 4.921875 -4.71875 5.1875 -4.75 Z M 5.1875 -4.90625 "/> +</symbol> +<symbol overflow="visible" id="glyph7-28"> +<path style="stroke:none;" d="M 3.9375 -7.375 C 1.859375 -7.375 0.375 -5.796875 0.375 -3.609375 C 0.375 -2.578125 0.71875 -1.59375 1.3125 -0.953125 C 1.9375 -0.265625 2.90625 0.15625 3.875 0.15625 C 6.015625 0.15625 7.5 -1.375 7.5 -3.5625 C 7.5 -4.640625 7.1875 -5.578125 6.59375 -6.234375 C 5.90625 -6.984375 4.984375 -7.375 3.9375 -7.375 Z M 3.9375 -6.984375 C 4.4375 -6.984375 4.9375 -6.78125 5.328125 -6.4375 C 5.921875 -5.90625 6.265625 -4.875 6.265625 -3.578125 C 6.265625 -2.9375 6.125 -2.1875 5.90625 -1.609375 C 5.796875 -1.34375 5.609375 -1.0625 5.359375 -0.8125 C 4.984375 -0.4375 4.5 -0.234375 3.921875 -0.234375 C 3.40625 -0.234375 2.921875 -0.4375 2.546875 -0.78125 C 1.96875 -1.28125 1.609375 -2.375 1.609375 -3.59375 C 1.609375 -4.703125 1.921875 -5.765625 2.375 -6.265625 C 2.796875 -6.734375 3.34375 -6.984375 3.9375 -6.984375 Z M 3.9375 -6.984375 "/> +</symbol> +<symbol overflow="visible" id="glyph7-29"> +<path style="stroke:none;" d="M 0.90625 1.53125 C 1.640625 1.171875 2.125 0.515625 2.125 -0.140625 C 2.125 -0.6875 1.75 -1.109375 1.25 -1.109375 C 0.875 -1.109375 0.609375 -0.859375 0.609375 -0.484375 C 0.609375 -0.140625 0.859375 0.0625 1.25 0.0625 C 1.3125 0.0625 1.390625 0.046875 1.46875 0.046875 C 1.53125 0.015625 1.53125 0.015625 1.546875 0.015625 C 1.640625 0.015625 1.703125 0.09375 1.703125 0.171875 C 1.703125 0.53125 1.390625 0.921875 0.8125 1.328125 Z M 0.90625 1.53125 "/> +</symbol> +<symbol overflow="visible" id="glyph7-30"> +<path style="stroke:none;" d="M 3.171875 -7.375 L 1.203125 -6.375 L 1.203125 -6.234375 C 1.34375 -6.28125 1.46875 -6.328125 1.5 -6.34375 C 1.703125 -6.421875 1.890625 -6.46875 2 -6.46875 C 2.21875 -6.46875 2.328125 -6.296875 2.328125 -5.953125 L 2.328125 -1.015625 C 2.328125 -0.65625 2.234375 -0.40625 2.0625 -0.3125 C 1.890625 -0.203125 1.75 -0.171875 1.28125 -0.15625 L 1.28125 0 L 4.296875 0 L 4.296875 -0.15625 C 3.4375 -0.171875 3.265625 -0.28125 3.265625 -0.8125 L 3.265625 -7.34375 Z M 3.171875 -7.375 "/> +</symbol> +<symbol overflow="visible" id="glyph7-31"> +<path style="stroke:none;" d="M 5.1875 -1.5 L 5.03125 -1.546875 C 4.640625 -0.921875 4.5 -0.828125 4 -0.828125 L 1.390625 -0.828125 L 3.234375 -2.75 C 4.203125 -3.765625 4.625 -4.59375 4.625 -5.4375 C 4.625 -6.53125 3.734375 -7.375 2.609375 -7.375 C 2 -7.375 1.4375 -7.125 1.03125 -6.703125 C 0.6875 -6.328125 0.53125 -5.984375 0.34375 -5.203125 L 0.5625 -5.140625 C 1 -6.21875 1.390625 -6.5625 2.15625 -6.5625 C 3.0625 -6.5625 3.6875 -5.9375 3.6875 -5.03125 C 3.6875 -4.171875 3.1875 -3.15625 2.265625 -2.1875 L 0.328125 -0.125 L 0.328125 0 L 4.578125 0 Z M 5.1875 -1.5 "/> +</symbol> +<symbol overflow="visible" id="glyph7-32"> +<path style="stroke:none;" d="M 5.140625 -2.515625 L 4.03125 -2.515625 L 4.03125 -7.375 L 3.5625 -7.375 L 0.125 -2.515625 L 0.125 -1.828125 L 3.203125 -1.828125 L 3.203125 0 L 4.03125 0 L 4.03125 -1.828125 L 5.140625 -1.828125 Z M 3.1875 -2.515625 L 0.5625 -2.515625 L 3.1875 -6.265625 Z M 3.1875 -2.515625 "/> +</symbol> +<symbol overflow="visible" id="glyph7-33"> +<path style="stroke:none;" d="M 0.078125 -6.796875 C 0.21875 -6.8125 0.3125 -6.8125 0.421875 -6.8125 C 0.78125 -6.8125 0.890625 -6.671875 0.890625 -6.15625 L 0.890625 -0.890625 C 0.890625 -0.328125 0.859375 -0.296875 0.078125 -0.15625 L 0.078125 0 L 2.625 0 L 2.625 -0.15625 L 2.40625 -0.171875 C 1.96875 -0.203125 1.8125 -0.34375 1.8125 -0.734375 L 1.8125 -2.734375 L 3.34375 -0.703125 L 3.375 -0.65625 C 3.390625 -0.625 3.40625 -0.59375 3.453125 -0.5625 C 3.53125 -0.453125 3.5625 -0.375 3.5625 -0.328125 C 3.5625 -0.234375 3.46875 -0.15625 3.34375 -0.15625 L 3.125 -0.15625 L 3.125 0 L 5.5 0 L 5.5 -0.15625 C 5.03125 -0.203125 4.6875 -0.40625 4.234375 -0.953125 L 2.5625 -3.078125 L 2.875 -3.375 C 3.65625 -4.09375 4.328125 -4.59375 4.640625 -4.671875 C 4.8125 -4.71875 4.96875 -4.75 5.140625 -4.75 L 5.234375 -4.75 L 5.234375 -4.90625 L 3.015625 -4.90625 L 3.015625 -4.75 C 3.4375 -4.75 3.5625 -4.703125 3.5625 -4.546875 C 3.5625 -4.453125 3.453125 -4.3125 3.296875 -4.171875 L 1.8125 -2.84375 L 1.8125 -7.421875 L 1.765625 -7.453125 C 1.359375 -7.3125 1.03125 -7.234375 0.40625 -7.0625 L 0.078125 -6.96875 Z M 0.078125 -6.796875 "/> +</symbol> +<symbol overflow="visible" id="glyph7-34"> +<path style="stroke:none;" d="M 1.25 -1.1875 C 1.25 -0.375 1.109375 -0.234375 0.203125 -0.203125 L 0.203125 0 L 3.4375 0 L 3.4375 -0.203125 C 2.546875 -0.234375 2.359375 -0.390625 2.359375 -1.1875 L 2.359375 -6.03125 C 2.359375 -6.828125 2.515625 -6.96875 3.4375 -7.015625 L 3.4375 -7.21875 L 0.203125 -7.21875 L 0.203125 -7.015625 C 1.125 -6.953125 1.25 -6.84375 1.25 -6.03125 Z M 1.25 -1.1875 "/> +</symbol> +<symbol overflow="visible" id="glyph7-35"> +<path style="stroke:none;" d="M 6.515625 -1.84375 L 6.203125 -1.84375 C 5.65625 -0.671875 5.1875 -0.40625 3.625 -0.40625 L 3.34375 -0.40625 C 2.796875 -0.40625 2.34375 -0.453125 2.265625 -0.53125 C 2.21875 -0.5625 2.1875 -0.6875 2.1875 -0.875 L 2.1875 -3.5625 L 3.875 -3.5625 C 4.765625 -3.5625 4.921875 -3.421875 5.078125 -2.515625 L 5.328125 -2.515625 L 5.328125 -5.046875 L 5.078125 -5.046875 C 5 -4.609375 4.953125 -4.4375 4.828125 -4.28125 C 4.6875 -4.09375 4.390625 -4.015625 3.875 -4.015625 L 2.1875 -4.015625 L 2.1875 -6.4375 C 2.1875 -6.734375 2.25 -6.8125 2.546875 -6.8125 L 4.03125 -6.8125 C 5.25 -6.8125 5.5 -6.640625 5.6875 -5.65625 L 5.953125 -5.65625 L 5.921875 -7.21875 L 0.125 -7.21875 L 0.125 -7.015625 C 0.9375 -6.953125 1.078125 -6.796875 1.078125 -6.03125 L 1.078125 -1.1875 C 1.078125 -0.421875 0.921875 -0.265625 0.125 -0.203125 L 0.125 0 L 6.015625 0 Z M 6.515625 -1.84375 "/> +</symbol> +<symbol overflow="visible" id="glyph7-36"> +<path style="stroke:none;" d="M 3.15625 -4.046875 C 4.25 -4.625 4.625 -5.078125 4.625 -5.828125 C 4.625 -6.71875 3.84375 -7.375 2.75 -7.375 C 1.5625 -7.375 0.671875 -6.640625 0.671875 -5.65625 C 0.671875 -4.9375 0.890625 -4.625 2.03125 -3.625 C 0.84375 -2.71875 0.609375 -2.390625 0.609375 -1.640625 C 0.609375 -0.59375 1.46875 0.15625 2.703125 0.15625 C 4.015625 0.15625 4.859375 -0.5625 4.859375 -1.6875 C 4.859375 -2.53125 4.484375 -3.0625 3.15625 -4.046875 Z M 2.96875 -2.921875 C 3.765625 -2.359375 4.03125 -1.96875 4.03125 -1.359375 C 4.03125 -0.640625 3.53125 -0.15625 2.828125 -0.15625 C 2 -0.15625 1.4375 -0.78125 1.4375 -1.734375 C 1.4375 -2.4375 1.671875 -2.890625 2.3125 -3.40625 Z M 2.84375 -4.25 C 1.875 -4.875 1.484375 -5.375 1.484375 -5.984375 C 1.484375 -6.625 1.96875 -7.0625 2.65625 -7.0625 C 3.40625 -7.0625 3.875 -6.59375 3.875 -5.828125 C 3.875 -5.21875 3.5625 -4.71875 2.9375 -4.3125 C 2.890625 -4.28125 2.890625 -4.28125 2.84375 -4.25 Z M 2.84375 -4.25 "/> +</symbol> +<symbol overflow="visible" id="glyph7-37"> +<path style="stroke:none;" d="M 2.765625 -7.375 C 2.171875 -7.375 1.71875 -7.1875 1.3125 -6.8125 C 0.671875 -6.1875 0.265625 -4.9375 0.265625 -3.671875 C 0.265625 -2.46875 0.625 -1.203125 1.140625 -0.59375 C 1.53125 -0.109375 2.09375 0.15625 2.71875 0.15625 C 3.28125 0.15625 3.75 -0.03125 4.140625 -0.421875 C 4.78125 -1.015625 5.1875 -2.28125 5.1875 -3.59375 C 5.1875 -5.828125 4.203125 -7.375 2.765625 -7.375 Z M 2.734375 -7.09375 C 3.65625 -7.09375 4.140625 -5.859375 4.140625 -3.578125 C 4.140625 -1.296875 3.671875 -0.125 2.71875 -0.125 C 1.78125 -0.125 1.3125 -1.296875 1.3125 -3.5625 C 1.3125 -5.875 1.796875 -7.09375 2.734375 -7.09375 Z M 2.734375 -7.09375 "/> +</symbol> +<symbol overflow="visible" id="glyph7-38"> +<path style="stroke:none;" d="M 5.21875 -2.515625 L 5.21875 -5.046875 L 4.96875 -5.046875 C 4.84375 -4.171875 4.640625 -4.015625 3.78125 -4.015625 L 2.1875 -4.015625 L 2.1875 -6.4375 C 2.1875 -6.734375 2.25 -6.8125 2.546875 -6.8125 L 4.03125 -6.8125 C 5.25 -6.8125 5.5 -6.640625 5.6875 -5.65625 L 5.953125 -5.65625 L 5.921875 -7.21875 L 0.125 -7.21875 L 0.125 -7.015625 C 0.9375 -6.953125 1.078125 -6.796875 1.078125 -6.03125 L 1.078125 -1.3125 C 1.078125 -0.40625 0.953125 -0.265625 0.125 -0.203125 L 0.125 0 L 3.1875 0 L 3.1875 -0.203125 C 2.34375 -0.25 2.1875 -0.40625 2.1875 -1.1875 L 2.1875 -3.5625 L 3.78125 -3.5625 C 4.65625 -3.5625 4.84375 -3.40625 4.96875 -2.515625 Z M 5.21875 -2.515625 "/> +</symbol> +<symbol overflow="visible" id="glyph7-39"> +<path style="stroke:none;" d="M 3.375 -4.90625 L 2.03125 -4.90625 L 2.03125 -6.171875 C 2.03125 -6.8125 2.234375 -7.140625 2.65625 -7.140625 C 2.875 -7.140625 3.03125 -7.03125 3.234375 -6.71875 C 3.40625 -6.4375 3.53125 -6.328125 3.71875 -6.328125 C 3.96875 -6.328125 4.171875 -6.515625 4.171875 -6.765625 C 4.171875 -7.171875 3.703125 -7.453125 3.046875 -7.453125 C 2.359375 -7.453125 1.78125 -7.15625 1.5 -6.65625 C 1.21875 -6.171875 1.140625 -5.78125 1.125 -4.90625 L 0.234375 -4.90625 L 0.234375 -4.5625 L 1.125 -4.5625 L 1.125 -1.140625 C 1.125 -0.34375 1 -0.203125 0.21875 -0.15625 L 0.21875 0 L 3.046875 0 L 3.046875 -0.15625 C 2.15625 -0.203125 2.046875 -0.3125 2.046875 -1.140625 L 2.046875 -4.5625 L 3.375 -4.5625 Z M 3.375 -4.90625 "/> +</symbol> +<symbol overflow="visible" id="glyph7-40"> +<path style="stroke:none;" d="M 3.03125 0 L 5.21875 0 L 5.21875 -0.15625 C 4.890625 -0.15625 4.671875 -0.34375 4.328125 -0.8125 L 2.9375 -2.953125 L 3.84375 -4.265625 C 4.046875 -4.5625 4.375 -4.734375 4.71875 -4.75 L 4.71875 -4.90625 L 3 -4.90625 L 3 -4.75 C 3.328125 -4.71875 3.4375 -4.65625 3.4375 -4.5 C 3.4375 -4.375 3.296875 -4.140625 3.03125 -3.796875 C 2.984375 -3.734375 2.84375 -3.53125 2.703125 -3.3125 L 2.546875 -3.53125 C 2.25 -3.984375 2.046875 -4.359375 2.046875 -4.5 C 2.046875 -4.65625 2.1875 -4.734375 2.515625 -4.75 L 2.515625 -4.90625 L 0.265625 -4.90625 L 0.265625 -4.75 L 0.359375 -4.75 C 0.6875 -4.75 0.859375 -4.609375 1.203125 -4.09375 L 2.21875 -2.515625 L 0.984375 -0.71875 C 0.65625 -0.265625 0.546875 -0.203125 0.1875 -0.15625 L 0.1875 0 L 1.765625 0 L 1.765625 -0.15625 C 1.46875 -0.15625 1.328125 -0.21875 1.328125 -0.359375 C 1.328125 -0.421875 1.40625 -0.59375 1.546875 -0.8125 L 2.40625 -2.15625 L 3.40625 -0.625 C 3.453125 -0.5625 3.46875 -0.484375 3.46875 -0.421875 C 3.46875 -0.234375 3.390625 -0.1875 3.03125 -0.15625 Z M 3.03125 0 "/> +</symbol> +<symbol overflow="visible" id="glyph7-41"> +<path style="stroke:none;" d="M 1.96875 -6.359375 L 4.109375 -6.359375 C 4.28125 -6.359375 4.328125 -6.375 4.359375 -6.453125 L 4.78125 -7.421875 L 4.671875 -7.5 C 4.515625 -7.28125 4.40625 -7.21875 4.171875 -7.21875 L 1.890625 -7.21875 L 0.703125 -4.640625 C 0.703125 -4.609375 0.703125 -4.609375 0.703125 -4.578125 C 0.703125 -4.53125 0.734375 -4.5 0.828125 -4.5 C 1.171875 -4.5 1.609375 -4.421875 2.0625 -4.28125 C 3.3125 -3.875 3.890625 -3.203125 3.890625 -2.109375 C 3.890625 -1.0625 3.234375 -0.25 2.375 -0.25 C 2.15625 -0.25 1.96875 -0.328125 1.640625 -0.5625 C 1.296875 -0.8125 1.046875 -0.921875 0.8125 -0.921875 C 0.5 -0.921875 0.34375 -0.796875 0.34375 -0.53125 C 0.34375 -0.109375 0.859375 0.15625 1.671875 0.15625 C 2.59375 0.15625 3.375 -0.140625 3.921875 -0.703125 C 4.421875 -1.1875 4.65625 -1.8125 4.65625 -2.640625 C 4.65625 -3.421875 4.453125 -3.921875 3.90625 -4.46875 C 3.421875 -4.953125 2.796875 -5.203125 1.515625 -5.4375 Z M 1.96875 -6.359375 "/> +</symbol> +<symbol overflow="visible" id="glyph7-42"> +<path style="stroke:none;" d="M 3.21875 -7.375 C 2.40625 -6.84375 2.078125 -6.5625 1.671875 -6.0625 C 0.90625 -5.109375 0.53125 -4.03125 0.53125 -2.75 C 0.53125 -1.359375 0.921875 -0.296875 1.890625 0.8125 C 2.328125 1.34375 2.625 1.578125 3.1875 1.9375 L 3.3125 1.75 C 2.4375 1.0625 2.140625 0.6875 1.84375 -0.125 C 1.578125 -0.859375 1.46875 -1.6875 1.46875 -2.78125 C 1.46875 -3.921875 1.609375 -4.828125 1.890625 -5.5 C 2.203125 -6.171875 2.53125 -6.5625 3.3125 -7.203125 Z M 3.21875 -7.375 "/> +</symbol> +<symbol overflow="visible" id="glyph7-43"> +<path style="stroke:none;" d="M 0.421875 1.9375 C 1.21875 1.40625 1.546875 1.125 1.953125 0.625 C 2.71875 -0.328125 3.109375 -1.421875 3.109375 -2.6875 C 3.109375 -4.09375 2.703125 -5.140625 1.75 -6.265625 C 1.296875 -6.78125 1.015625 -7.03125 0.453125 -7.375 L 0.3125 -7.203125 C 1.1875 -6.515625 1.484375 -6.125 1.78125 -5.3125 C 2.046875 -4.578125 2.171875 -3.75 2.171875 -2.65625 C 2.171875 -1.53125 2.03125 -0.625 1.734375 0.046875 C 1.421875 0.734375 1.09375 1.125 0.3125 1.75 Z M 0.421875 1.9375 "/> +</symbol> +<symbol overflow="visible" id="glyph7-44"> +<path style="stroke:none;" d="M 6.671875 0.125 L 6.671875 -5.609375 C 6.671875 -6.265625 6.78125 -6.671875 6.984375 -6.8125 C 7.140625 -6.921875 7.296875 -6.96875 7.703125 -7.015625 L 7.703125 -7.21875 L 5.140625 -7.21875 L 5.140625 -7.015625 C 5.5625 -6.984375 5.71875 -6.9375 5.875 -6.84375 C 6.09375 -6.6875 6.1875 -6.296875 6.1875 -5.609375 L 6.1875 -1.9375 L 2 -7.21875 L 0.125 -7.21875 L 0.125 -7.015625 C 0.59375 -7.015625 0.75 -6.921875 1.1875 -6.40625 L 1.1875 -1.609375 C 1.1875 -0.484375 1.03125 -0.265625 0.125 -0.203125 L 0.125 0 L 2.6875 0 L 2.6875 -0.203125 C 1.859375 -0.25 1.671875 -0.5 1.671875 -1.609375 L 1.671875 -5.875 L 6.484375 0.125 Z M 6.671875 0.125 "/> +</symbol> +<symbol overflow="visible" id="glyph7-45"> +<path style="stroke:none;" d="M 7.734375 -3.859375 L 4.953125 -3.859375 L 4.953125 -3.671875 C 5.421875 -3.625 5.546875 -3.59375 5.6875 -3.515625 C 5.84375 -3.40625 5.90625 -3.15625 5.90625 -2.6875 L 5.90625 -0.921875 C 5.90625 -0.578125 5.25 -0.28125 4.453125 -0.28125 C 2.6875 -0.28125 1.59375 -1.53125 1.59375 -3.5625 C 1.59375 -4.578125 1.890625 -5.578125 2.375 -6.109375 C 2.859375 -6.640625 3.53125 -6.9375 4.28125 -6.9375 C 4.890625 -6.9375 5.421875 -6.734375 5.84375 -6.34375 C 6.171875 -6.03125 6.34375 -5.734375 6.625 -5.078125 L 6.875 -5.078125 L 6.78125 -7.375 L 6.546875 -7.375 C 6.484375 -7.171875 6.28125 -7.015625 6.046875 -7.015625 C 5.9375 -7.015625 5.765625 -7.046875 5.578125 -7.125 C 5.078125 -7.28125 4.578125 -7.375 4.09375 -7.375 C 1.90625 -7.375 0.34375 -5.765625 0.34375 -3.546875 C 0.34375 -2.46875 0.640625 -1.65625 1.25 -1.015625 C 1.984375 -0.265625 3.03125 0.15625 4.234375 0.15625 C 5.171875 0.15625 6.53125 -0.234375 6.96875 -0.609375 L 6.96875 -2.828125 C 6.96875 -3.46875 7.09375 -3.609375 7.734375 -3.671875 Z M 7.734375 -3.859375 "/> +</symbol> +<symbol overflow="visible" id="glyph7-46"> +<path style="stroke:none;" d="M 6.765625 -4.90625 L 6.65625 -7.375 L 6.4375 -7.375 C 6.375 -7.140625 6.1875 -7.015625 5.984375 -7.015625 C 5.875 -7.015625 5.71875 -7.046875 5.546875 -7.109375 C 5.015625 -7.28125 4.46875 -7.375 3.953125 -7.375 C 3.0625 -7.375 2.15625 -7.03125 1.484375 -6.4375 C 0.71875 -5.765625 0.3125 -4.75 0.3125 -3.546875 C 0.3125 -2.53125 0.625 -1.59375 1.1875 -0.96875 C 1.84375 -0.265625 2.84375 0.15625 3.921875 0.15625 C 5.15625 0.15625 6.234375 -0.34375 6.90625 -1.234375 L 6.703125 -1.421875 C 5.90625 -0.65625 5.1875 -0.328125 4.28125 -0.328125 C 3.59375 -0.328125 2.984375 -0.546875 2.515625 -0.953125 C 1.90625 -1.5 1.578125 -2.46875 1.578125 -3.6875 C 1.578125 -5.65625 2.578125 -6.9375 4.171875 -6.9375 C 4.78125 -6.9375 5.359375 -6.703125 5.796875 -6.265625 C 6.140625 -5.921875 6.296875 -5.609375 6.515625 -4.90625 Z M 6.765625 -4.90625 "/> +</symbol> +<symbol overflow="visible" id="glyph7-47"> +<path style="stroke:none;" d="M 2.203125 -3.171875 C 2.484375 -3.15625 2.671875 -3.140625 2.953125 -3.140625 C 3.8125 -3.140625 4.390625 -3.25 4.859375 -3.515625 C 5.515625 -3.859375 5.90625 -4.53125 5.90625 -5.25 C 5.90625 -5.703125 5.765625 -6.125 5.46875 -6.4375 C 5.03125 -6.921875 4.078125 -7.21875 3.046875 -7.21875 L 0.171875 -7.21875 L 0.171875 -7.015625 C 0.984375 -6.921875 1.09375 -6.8125 1.09375 -6.03125 L 1.09375 -1.3125 C 1.09375 -0.390625 1 -0.28125 0.171875 -0.203125 L 0.171875 0 L 3.234375 0 L 3.234375 -0.203125 C 2.359375 -0.234375 2.203125 -0.390625 2.203125 -1.1875 Z M 2.203125 -6.453125 C 2.203125 -6.734375 2.28125 -6.8125 2.578125 -6.8125 C 4.046875 -6.8125 4.71875 -6.296875 4.71875 -5.1875 C 4.71875 -4.125 4.078125 -3.578125 2.828125 -3.578125 C 2.609375 -3.578125 2.453125 -3.59375 2.203125 -3.609375 Z M 2.203125 -6.453125 "/> +</symbol> +<symbol overflow="visible" id="glyph7-48"> +<path style="stroke:none;" d="M 0.1875 -7.21875 L 0.1875 -7.015625 C 1.09375 -6.953125 1.234375 -6.84375 1.234375 -6.03125 L 1.234375 -1.1875 C 1.234375 -0.375 1.09375 -0.234375 0.1875 -0.203125 L 0.1875 0 L 3.828125 0 C 4.671875 0 5.453125 -0.234375 5.859375 -0.59375 C 6.25 -0.953125 6.46875 -1.4375 6.46875 -1.96875 C 6.46875 -2.4375 6.265625 -2.875 5.9375 -3.203125 C 5.609375 -3.484375 5.3125 -3.625 4.609375 -3.796875 C 5.171875 -3.9375 5.40625 -4.046875 5.65625 -4.28125 C 5.9375 -4.515625 6.09375 -4.921875 6.09375 -5.359375 C 6.09375 -6.59375 5.125 -7.21875 3.234375 -7.21875 Z M 2.34375 -3.5625 C 3.40625 -3.5625 3.90625 -3.5 4.296875 -3.34375 C 4.921875 -3.078125 5.21875 -2.640625 5.21875 -1.953125 C 5.21875 -1.359375 4.984375 -0.9375 4.546875 -0.6875 C 4.203125 -0.484375 3.75 -0.40625 3.03125 -0.40625 C 2.5 -0.40625 2.34375 -0.5 2.34375 -0.84375 Z M 2.34375 -3.984375 L 2.34375 -6.484375 C 2.34375 -6.71875 2.421875 -6.8125 2.578125 -6.8125 L 3.0625 -6.8125 C 4.3125 -6.8125 4.984375 -6.296875 4.984375 -5.328125 C 4.984375 -4.46875 4.40625 -3.984375 3.375 -3.984375 Z M 2.34375 -3.984375 "/> +</symbol> +<symbol overflow="visible" id="glyph7-49"> +<path style="stroke:none;" d="M 4.5 -7.015625 C 4.640625 -7 4.75 -7 4.78125 -7 C 5.109375 -6.984375 5.25 -6.890625 5.25 -6.671875 C 5.25 -6.4375 5 -6.109375 4.390625 -5.546875 L 2.46875 -3.796875 L 2.46875 -6.03125 C 2.46875 -6.828125 2.578125 -6.953125 3.46875 -7.015625 L 3.46875 -7.21875 L 0.375 -7.21875 L 0.375 -7.015625 C 1.21875 -6.953125 1.359375 -6.8125 1.359375 -6.03125 L 1.359375 -1.3125 C 1.359375 -0.40625 1.234375 -0.265625 0.375 -0.203125 L 0.375 0 L 3.453125 0 L 3.453125 -0.203125 C 2.59375 -0.265625 2.46875 -0.390625 2.46875 -1.1875 L 2.46875 -3.234375 L 2.75 -3.453125 L 3.90625 -2.3125 C 4.734375 -1.5 5.328125 -0.71875 5.328125 -0.453125 C 5.328125 -0.3125 5.171875 -0.234375 4.859375 -0.21875 C 4.8125 -0.21875 4.6875 -0.21875 4.5625 -0.203125 L 4.5625 0 L 7.890625 0 L 7.890625 -0.203125 C 7.3125 -0.21875 7.171875 -0.328125 6.171875 -1.390625 L 3.625 -4.109375 L 5.703125 -6.15625 C 6.453125 -6.875 6.625 -6.953125 7.359375 -7.015625 L 7.359375 -7.21875 L 4.5 -7.21875 Z M 4.5 -7.015625 "/> +</symbol> +<symbol overflow="visible" id="glyph7-50"> +<path style="stroke:none;" d="M 4.5625 -1.46875 L 4.359375 -1.515625 C 4.25 -0.96875 4.1875 -0.796875 4.046875 -0.609375 C 3.890625 -0.421875 3.515625 -0.328125 2.96875 -0.328125 L 1.46875 -0.328125 L 4.390625 -4.75 L 4.390625 -4.90625 L 0.609375 -4.90625 L 0.578125 -3.625 L 0.78125 -3.625 C 0.875 -4.421875 1.03125 -4.578125 1.6875 -4.578125 L 3.203125 -4.578125 L 0.296875 -0.15625 L 0.296875 0 L 4.40625 0 Z M 4.5625 -1.46875 "/> +</symbol> +<symbol overflow="visible" id="glyph7-51"> +<path style="stroke:none;" d="M 1.15625 -4.71875 C 1.890625 -5.078125 2.375 -5.75 2.375 -6.40625 C 2.375 -6.953125 2 -7.375 1.5 -7.375 C 1.125 -7.375 0.859375 -7.125 0.859375 -6.75 C 0.859375 -6.40625 1.109375 -6.1875 1.5 -6.1875 C 1.578125 -6.1875 1.640625 -6.203125 1.71875 -6.21875 C 1.78125 -6.234375 1.78125 -6.234375 1.796875 -6.234375 C 1.890625 -6.234375 1.953125 -6.171875 1.953125 -6.078125 C 1.953125 -5.71875 1.640625 -5.328125 1.0625 -4.921875 Z M 1.15625 -4.71875 "/> +</symbol> +<symbol overflow="visible" id="glyph7-52"> +<path style="stroke:none;" d="M 0.34375 -4.546875 L 1.078125 -4.546875 L 1.078125 -1.03125 C 1.078125 -0.328125 0.953125 -0.1875 0.34375 -0.15625 L 0.34375 0 L 2.75 0 L 2.75 -0.15625 C 2.140625 -0.203125 2 -0.359375 2 -0.953125 L 2 -4.546875 L 2.9375 -4.546875 C 3.984375 -4.546875 4.03125 -4.515625 4.03125 -3.9375 L 4.03125 -1.0625 C 4.03125 -0.3125 3.953125 -0.21875 3.28125 -0.15625 L 3.28125 0 L 5.6875 0 L 5.6875 -0.15625 C 5.078125 -0.21875 4.953125 -0.359375 4.953125 -1.0625 L 4.953125 -3.921875 C 4.953125 -4.15625 4.953125 -4.40625 4.96875 -4.984375 L 4.921875 -5.015625 C 4.453125 -4.9375 4.140625 -4.90625 3.703125 -4.90625 L 1.984375 -4.90625 L 1.984375 -5.375 C 1.984375 -5.859375 2.015625 -6.1875 2.078125 -6.375 C 2.21875 -6.84375 2.65625 -7.171875 3.140625 -7.171875 C 3.453125 -7.171875 3.65625 -7.03125 3.921875 -6.65625 C 4.125 -6.375 4.265625 -6.265625 4.453125 -6.265625 C 4.671875 -6.265625 4.84375 -6.453125 4.84375 -6.6875 C 4.84375 -7.140625 4.296875 -7.453125 3.453125 -7.453125 C 2.625 -7.453125 2 -7.171875 1.609375 -6.625 C 1.28125 -6.1875 1.15625 -5.78125 1.09375 -4.90625 L 0.34375 -4.90625 Z M 0.34375 -4.546875 "/> +</symbol> +<symbol overflow="visible" id="glyph7-53"> +<path style="stroke:none;" d="M 2.765625 -6.765625 L 2.765625 -1.3125 C 2.765625 -0.375 2.65625 -0.265625 1.75 -0.203125 L 1.75 0 L 4.921875 0 L 4.921875 -0.203125 C 4.03125 -0.25 3.875 -0.390625 3.875 -1.1875 L 3.875 -6.765625 L 4.46875 -6.765625 C 5.71875 -6.765625 5.953125 -6.5625 6.203125 -5.359375 L 6.46875 -5.359375 L 6.40625 -7.21875 L 0.25 -7.21875 L 0.1875 -5.359375 L 0.453125 -5.359375 C 0.703125 -6.546875 0.953125 -6.765625 2.1875 -6.765625 Z M 2.765625 -6.765625 "/> +</symbol> +<symbol overflow="visible" id="glyph8-0"> +<path style="stroke:none;" d="M 1.8125 -2.671875 L 3.171875 -2.671875 C 4.4375 -2.671875 5.234375 -3.65625 5.234375 -4.671875 C 5.234375 -5.6875 4.421875 -6.65625 3.171875 -6.65625 L 0.734375 -6.65625 C 0.5625 -6.65625 0.28125 -6.65625 0.28125 -6.34375 C 0.28125 -6 0.5625 -6 0.734375 -6 L 1.0625 -6 L 1.0625 -0.671875 L 0.734375 -0.671875 C 0.5625 -0.671875 0.28125 -0.671875 0.28125 -0.328125 C 0.28125 0 0.5625 0 0.734375 0 L 2.140625 0 C 2.296875 0 2.578125 0 2.578125 -0.328125 C 2.578125 -0.671875 2.296875 -0.671875 2.140625 -0.671875 L 1.8125 -0.671875 Z M 1.8125 -6 L 2.96875 -6 C 4.015625 -6 4.484375 -5.25 4.484375 -4.671875 C 4.484375 -4.09375 4.015625 -3.34375 2.96875 -3.34375 L 1.8125 -3.34375 Z M 1.8125 -6 "/> +</symbol> +<symbol overflow="visible" id="glyph8-1"> +<path style="stroke:none;" d="M 5.09375 -2.359375 C 5.09375 -3.71875 4.078125 -4.796875 2.859375 -4.796875 C 1.640625 -4.796875 0.625 -3.71875 0.625 -2.359375 C 0.625 -0.96875 1.65625 0.0625 2.859375 0.0625 C 4.0625 0.0625 5.09375 -0.984375 5.09375 -2.359375 Z M 2.859375 -0.59375 C 2.046875 -0.59375 1.375 -1.421875 1.375 -2.4375 C 1.375 -3.421875 2.078125 -4.140625 2.859375 -4.140625 C 3.640625 -4.140625 4.34375 -3.421875 4.34375 -2.4375 C 4.34375 -1.421875 3.671875 -0.59375 2.859375 -0.59375 Z M 2.859375 -0.59375 "/> +</symbol> +<symbol overflow="visible" id="glyph8-2"> +<path style="stroke:none;" d="M 3.25 -2.78125 C 3 -2.828125 2.78125 -2.859375 2.515625 -2.90625 C 2.1875 -2.953125 1.453125 -3.09375 1.453125 -3.515625 C 1.453125 -3.796875 1.796875 -4.140625 2.828125 -4.140625 C 3.734375 -4.140625 3.890625 -3.8125 3.921875 -3.515625 C 3.9375 -3.34375 3.953125 -3.15625 4.296875 -3.15625 C 4.671875 -3.15625 4.671875 -3.375 4.671875 -3.59375 L 4.671875 -4.359375 C 4.671875 -4.53125 4.671875 -4.796875 4.359375 -4.796875 C 4.09375 -4.796875 4.046875 -4.640625 4.03125 -4.5625 C 3.546875 -4.796875 3.0625 -4.796875 2.859375 -4.796875 C 1.03125 -4.796875 0.78125 -3.90625 0.78125 -3.515625 C 0.78125 -2.515625 1.9375 -2.328125 2.9375 -2.171875 C 3.46875 -2.078125 4.34375 -1.9375 4.34375 -1.359375 C 4.34375 -0.953125 3.9375 -0.59375 2.9375 -0.59375 C 2.4375 -0.59375 1.828125 -0.71875 1.546875 -1.578125 C 1.5 -1.765625 1.453125 -1.890625 1.171875 -1.890625 C 0.78125 -1.890625 0.78125 -1.65625 0.78125 -1.4375 L 0.78125 -0.375 C 0.78125 -0.203125 0.78125 0.0625 1.109375 0.0625 C 1.203125 0.0625 1.390625 0.046875 1.515625 -0.34375 C 2.046875 0.046875 2.625 0.0625 2.9375 0.0625 C 4.65625 0.0625 5 -0.84375 5 -1.359375 C 5 -2.5 3.59375 -2.71875 3.25 -2.78125 Z M 3.25 -2.78125 "/> +</symbol> +<symbol overflow="visible" id="glyph8-3"> +<path style="stroke:none;" d="M 2.421875 -4.03125 L 4.203125 -4.03125 C 4.375 -4.03125 4.640625 -4.03125 4.640625 -4.359375 C 4.640625 -4.703125 4.390625 -4.703125 4.203125 -4.703125 L 2.421875 -4.703125 L 2.421875 -5.59375 C 2.421875 -5.796875 2.421875 -6.046875 2.046875 -6.046875 C 1.671875 -6.046875 1.671875 -5.8125 1.671875 -5.59375 L 1.671875 -4.703125 L 0.71875 -4.703125 C 0.546875 -4.703125 0.265625 -4.703125 0.265625 -4.359375 C 0.265625 -4.03125 0.546875 -4.03125 0.703125 -4.03125 L 1.671875 -4.03125 L 1.671875 -1.375 C 1.671875 -0.328125 2.40625 0.0625 3.203125 0.0625 C 4.015625 0.0625 4.890625 -0.40625 4.890625 -1.375 C 4.890625 -1.578125 4.890625 -1.796875 4.515625 -1.796875 C 4.15625 -1.796875 4.140625 -1.578125 4.140625 -1.390625 C 4.140625 -0.703125 3.515625 -0.59375 3.265625 -0.59375 C 2.421875 -0.59375 2.421875 -1.171875 2.421875 -1.4375 Z M 2.421875 -4.03125 "/> +</symbol> +<symbol overflow="visible" id="glyph8-4"> +<path style="stroke:none;" d="M 3.15625 -4.03125 C 3.03125 -3.625 2.9375 -3.3125 2.875 -3 L 2.859375 -3 C 2.75 -3.515625 1.78125 -6.34375 1.765625 -6.40625 C 1.65625 -6.65625 1.390625 -6.65625 1.234375 -6.65625 L 0.625 -6.65625 C 0.453125 -6.65625 0.1875 -6.65625 0.1875 -6.34375 C 0.1875 -6 0.421875 -6 0.765625 -6 L 0.765625 -0.671875 C 0.421875 -0.671875 0.1875 -0.671875 0.1875 -0.328125 C 0.1875 0 0.453125 0 0.625 0 L 1.515625 0 C 1.671875 0 1.953125 0 1.953125 -0.328125 C 1.953125 -0.671875 1.71875 -0.671875 1.375 -0.671875 L 1.375 -5.875 L 1.390625 -5.875 C 1.5 -5.359375 2.203125 -3.265625 2.25 -3.125 C 2.328125 -2.875 2.46875 -2.46875 2.53125 -2.390625 C 2.59375 -2.296875 2.71875 -2.21875 2.859375 -2.21875 C 3 -2.21875 3.15625 -2.3125 3.234375 -2.46875 C 3.265625 -2.53125 4.203125 -5.296875 4.328125 -5.875 L 4.34375 -5.875 L 4.34375 -0.671875 C 3.984375 -0.671875 3.765625 -0.671875 3.765625 -0.328125 C 3.765625 0 4.03125 0 4.203125 0 L 5.09375 0 C 5.25 0 5.53125 0 5.53125 -0.328125 C 5.53125 -0.671875 5.296875 -0.671875 4.953125 -0.671875 L 4.953125 -6 C 5.296875 -6 5.53125 -6 5.53125 -6.34375 C 5.53125 -6.65625 5.25 -6.65625 5.09375 -6.65625 L 4.484375 -6.65625 C 4.03125 -6.65625 3.984375 -6.515625 3.890625 -6.234375 Z M 3.15625 -4.03125 "/> +</symbol> +<symbol overflow="visible" id="glyph8-5"> +<path style="stroke:none;" d="M 4.625 -2.078125 C 4.859375 -2.078125 5.0625 -2.078125 5.0625 -2.484375 C 5.0625 -3.734375 4.359375 -4.796875 2.9375 -4.796875 C 1.640625 -4.796875 0.59375 -3.703125 0.59375 -2.359375 C 0.59375 -1.03125 1.703125 0.0625 3.109375 0.0625 C 4.546875 0.0625 5.0625 -0.921875 5.0625 -1.1875 C 5.0625 -1.5 4.75 -1.5 4.671875 -1.5 C 4.484375 -1.5 4.390625 -1.46875 4.3125 -1.25 C 4.078125 -0.703125 3.484375 -0.59375 3.1875 -0.59375 C 2.359375 -0.59375 1.546875 -1.140625 1.375 -2.078125 Z M 1.390625 -2.734375 C 1.53125 -3.53125 2.1875 -4.140625 2.9375 -4.140625 C 3.515625 -4.140625 4.1875 -3.859375 4.28125 -2.734375 Z M 1.390625 -2.734375 "/> +</symbol> +<symbol overflow="visible" id="glyph8-6"> +<path style="stroke:none;" d="M 3.984375 -0.34375 C 4.234375 -0.015625 4.75 0 5.171875 0 C 5.46875 0 5.71875 0 5.71875 -0.34375 C 5.71875 -0.671875 5.4375 -0.671875 5.28125 -0.671875 C 4.828125 -0.671875 4.71875 -0.71875 4.625 -0.75 L 4.625 -3.109375 C 4.625 -3.875 4.03125 -4.796875 2.46875 -4.796875 C 2 -4.796875 0.890625 -4.796875 0.890625 -4 C 0.890625 -3.671875 1.109375 -3.5 1.375 -3.5 C 1.53125 -3.5 1.84375 -3.59375 1.859375 -4 C 1.859375 -4.09375 1.859375 -4.09375 2.078125 -4.125 C 2.234375 -4.140625 2.375 -4.140625 2.46875 -4.140625 C 3.296875 -4.140625 3.875 -3.796875 3.875 -3.015625 C 1.9375 -2.984375 0.59375 -2.4375 0.59375 -1.390625 C 0.59375 -0.640625 1.28125 0.0625 2.40625 0.0625 C 2.796875 0.0625 3.484375 -0.015625 3.984375 -0.34375 Z M 3.875 -2.375 L 3.875 -1.46875 C 3.875 -1.203125 3.875 -0.984375 3.453125 -0.78125 C 3.046875 -0.59375 2.5625 -0.59375 2.46875 -0.59375 C 1.796875 -0.59375 1.359375 -0.96875 1.359375 -1.390625 C 1.359375 -1.9375 2.296875 -2.328125 3.875 -2.375 Z M 3.875 -2.375 "/> +</symbol> +<symbol overflow="visible" id="glyph8-7"> +<path style="stroke:none;" d="M 2.546875 -1.921875 C 1.953125 -1.921875 1.5 -2.4375 1.5 -3.015625 C 1.5 -3.625 1.96875 -4.109375 2.546875 -4.109375 C 3.125 -4.109375 3.59375 -3.59375 3.59375 -3.015625 C 3.59375 -2.40625 3.109375 -1.921875 2.546875 -1.921875 Z M 1.5625 -1.53125 C 1.59375 -1.515625 2 -1.265625 2.546875 -1.265625 C 3.546875 -1.265625 4.34375 -2.046875 4.34375 -3.015625 C 4.34375 -3.34375 4.25 -3.671875 4.0625 -3.96875 C 4.28125 -4.09375 4.546875 -4.140625 4.6875 -4.15625 C 4.75 -3.859375 5 -3.78125 5.109375 -3.78125 C 5.296875 -3.78125 5.546875 -3.921875 5.546875 -4.234375 C 5.546875 -4.484375 5.34375 -4.828125 4.75 -4.828125 C 4.640625 -4.828125 4.09375 -4.8125 3.59375 -4.4375 C 3.421875 -4.5625 3.046875 -4.765625 2.546875 -4.765625 C 1.515625 -4.765625 0.734375 -3.953125 0.734375 -3.015625 C 0.734375 -2.546875 0.921875 -2.1875 1.09375 -1.984375 C 0.96875 -1.8125 0.875 -1.578125 0.875 -1.25 C 0.875 -0.859375 1.03125 -0.59375 1.125 -0.453125 C 0.3125 0.03125 0.3125 0.78125 0.3125 0.890625 C 0.3125 1.828125 1.46875 2.5 2.859375 2.5 C 4.25 2.5 5.40625 1.828125 5.40625 0.890625 C 5.40625 0.484375 5.203125 -0.046875 4.640625 -0.34375 C 4.5 -0.421875 4.046875 -0.671875 3.0625 -0.671875 L 2.296875 -0.671875 C 2.21875 -0.671875 2.078125 -0.671875 1.984375 -0.6875 C 1.828125 -0.6875 1.75 -0.6875 1.625 -0.84375 C 1.5 -1 1.5 -1.203125 1.5 -1.234375 C 1.5 -1.28125 1.515625 -1.421875 1.5625 -1.53125 Z M 2.859375 1.84375 C 1.765625 1.84375 0.953125 1.375 0.953125 0.890625 C 0.953125 0.703125 1.046875 0.34375 1.390625 0.125 C 1.671875 -0.046875 1.765625 -0.046875 2.5625 -0.046875 C 3.53125 -0.046875 4.765625 -0.046875 4.765625 0.890625 C 4.765625 1.375 3.953125 1.84375 2.859375 1.84375 Z M 2.859375 1.84375 "/> +</symbol> +<symbol overflow="visible" id="glyph9-0"> +<path style="stroke:none;" d="M 3.90625 -1.1875 C 3.125 -0.546875 2.78125 -0.375 2.3125 -0.375 C 1.6875 -0.375 1.28125 -0.765625 1.28125 -1.359375 C 1.28125 -1.515625 1.3125 -1.671875 1.390625 -2.03125 L 1.703125 -2.078125 C 3.34375 -2.296875 4.5 -3.125 4.5 -4.0625 C 4.5 -4.53125 4.171875 -4.8125 3.625 -4.8125 C 2.046875 -4.8125 0.34375 -3.015625 0.34375 -1.375 C 0.34375 -0.484375 0.921875 0.125 1.78125 0.125 C 2.546875 0.125 3.390625 -0.328125 4.03125 -1.0625 Z M 1.65625 -2.75 C 2.03125 -3.71875 2.828125 -4.5625 3.390625 -4.5625 C 3.625 -4.5625 3.78125 -4.390625 3.78125 -4.140625 C 3.78125 -3.796875 3.5625 -3.40625 3.234375 -3.078125 C 2.828125 -2.6875 2.40625 -2.5 1.46875 -2.265625 Z M 1.65625 -2.75 "/> +</symbol> +<symbol overflow="visible" id="glyph9-1"> +<path style="stroke:none;" d="M 4.390625 -1.203125 C 4.296875 -1.109375 4.25 -1.046875 4.140625 -0.921875 C 3.890625 -0.59375 3.765625 -0.484375 3.640625 -0.484375 C 3.484375 -0.484375 3.375 -0.625 3.296875 -0.921875 C 3.28125 -1.015625 3.265625 -1.078125 3.265625 -1.109375 C 2.984375 -2.21875 2.875 -2.71875 2.875 -2.875 C 3.34375 -3.71875 3.734375 -4.203125 3.9375 -4.203125 C 4 -4.203125 4.09375 -4.171875 4.203125 -4.109375 C 4.328125 -4.03125 4.40625 -4.015625 4.5 -4.015625 C 4.71875 -4.015625 4.875 -4.171875 4.875 -4.40625 C 4.875 -4.640625 4.6875 -4.8125 4.421875 -4.8125 C 3.953125 -4.8125 3.53125 -4.421875 2.78125 -3.25 L 2.65625 -3.84375 C 2.515625 -4.59375 2.390625 -4.8125 2.09375 -4.8125 C 1.859375 -4.8125 1.484375 -4.71875 0.8125 -4.5 L 0.703125 -4.453125 L 0.734375 -4.28125 C 1.15625 -4.390625 1.25 -4.40625 1.359375 -4.40625 C 1.625 -4.40625 1.6875 -4.3125 1.84375 -3.65625 L 2.15625 -2.3125 L 1.265625 -1.03125 C 1.046875 -0.703125 0.828125 -0.515625 0.703125 -0.515625 C 0.640625 -0.515625 0.53125 -0.546875 0.421875 -0.609375 C 0.28125 -0.6875 0.15625 -0.71875 0.078125 -0.71875 C -0.125 -0.71875 -0.296875 -0.5625 -0.296875 -0.34375 C -0.296875 -0.046875 -0.078125 0.125 0.25 0.125 C 0.59375 0.125 0.71875 0.015625 1.265625 -0.640625 C 1.5625 -1 1.78125 -1.28125 2.25 -1.921875 L 2.578125 -0.609375 C 2.71875 -0.046875 2.859375 0.125 3.203125 0.125 C 3.625 0.125 3.90625 -0.140625 4.53125 -1.125 Z M 4.390625 -1.203125 "/> +</symbol> +<symbol overflow="visible" id="glyph9-2"> +<path style="stroke:none;" d="M 3.8125 -1.15625 C 3.21875 -0.515625 2.796875 -0.265625 2.25 -0.265625 C 1.640625 -0.265625 1.265625 -0.734375 1.265625 -1.515625 C 1.265625 -2.4375 1.640625 -3.40625 2.25 -4.0625 C 2.578125 -4.390625 3.015625 -4.578125 3.4375 -4.578125 C 3.6875 -4.578125 3.84375 -4.5 3.84375 -4.359375 C 3.84375 -4.3125 3.828125 -4.25 3.78125 -4.15625 C 3.703125 -4.015625 3.6875 -3.9375 3.6875 -3.84375 C 3.6875 -3.578125 3.84375 -3.421875 4.109375 -3.421875 C 4.40625 -3.421875 4.640625 -3.640625 4.640625 -3.921875 C 4.640625 -4.421875 4.140625 -4.8125 3.484375 -4.8125 C 1.859375 -4.8125 0.328125 -3.21875 0.328125 -1.515625 C 0.328125 -0.484375 0.921875 0.125 1.9375 0.125 C 2.734375 0.125 3.34375 -0.21875 3.984375 -1.046875 Z M 3.8125 -1.15625 "/> +</symbol> +<symbol overflow="visible" id="glyph9-3"> +<path style="stroke:none;" d="M 2.484375 -1.34375 C 2.390625 -1.21875 2.296875 -1.09375 2.1875 -0.96875 C 1.84375 -0.515625 1.640625 -0.34375 1.4375 -0.34375 C 1.328125 -0.34375 1.28125 -0.421875 1.28125 -0.546875 C 1.28125 -0.625 1.3125 -0.75 1.359375 -0.96875 C 1.375 -1 1.390625 -1.0625 1.390625 -1.078125 L 3.046875 -7.390625 L 2.984375 -7.453125 C 2.34375 -7.3125 1.9375 -7.234375 1.28125 -7.15625 L 1.28125 -6.984375 C 1.8125 -6.984375 2.03125 -6.90625 2.03125 -6.734375 C 2.03125 -6.703125 2.015625 -6.625 1.984375 -6.53125 L 0.484375 -0.78125 C 0.453125 -0.65625 0.4375 -0.546875 0.4375 -0.484375 C 0.4375 -0.09375 0.625 0.125 0.96875 0.125 C 1.53125 0.125 1.921875 -0.203125 2.625 -1.25 Z M 2.484375 -1.34375 "/> +</symbol> +<symbol overflow="visible" id="glyph9-4"> +<path style="stroke:none;" d="M 5.03125 -1.28125 C 4.453125 -0.5625 4.296875 -0.421875 4.125 -0.421875 C 4.046875 -0.421875 4 -0.484375 4 -0.59375 C 4 -0.671875 4 -0.671875 4.203125 -1.453125 L 5.078125 -4.71875 L 4.265625 -4.71875 C 3.640625 -3.015625 3.5625 -2.84375 3.0625 -2.046875 C 2.40625 -1.015625 1.875 -0.453125 1.53125 -0.453125 C 1.390625 -0.453125 1.296875 -0.5625 1.296875 -0.734375 C 1.296875 -0.78125 1.296875 -0.796875 1.3125 -0.828125 L 2.296875 -4.78125 L 2.265625 -4.8125 C 1.640625 -4.671875 1.25 -4.59375 0.625 -4.515625 L 0.625 -4.359375 C 1.046875 -4.359375 1.0625 -4.359375 1.171875 -4.296875 C 1.234375 -4.28125 1.28125 -4.171875 1.28125 -4.09375 C 1.28125 -4.015625 1.234375 -3.734375 1.140625 -3.375 L 0.734375 -1.828125 C 0.53125 -1.046875 0.453125 -0.671875 0.453125 -0.484375 C 0.453125 -0.09375 0.65625 0.125 1.03125 0.125 C 1.8125 0.125 2.390625 -0.46875 3.640625 -2.578125 C 3.328125 -1.40625 3.15625 -0.65625 3.15625 -0.421875 C 3.15625 -0.09375 3.34375 0.09375 3.671875 0.09375 C 4.1875 0.09375 4.4375 -0.125 5.1875 -1.171875 Z M 5.03125 -1.28125 "/> +</symbol> +<symbol overflow="visible" id="glyph9-5"> +<path style="stroke:none;" d="M 0.390625 -1.59375 L 0.171875 0.140625 L 0.34375 0.140625 C 0.4375 -0.03125 0.5 -0.09375 0.609375 -0.09375 C 0.734375 -0.09375 0.921875 -0.046875 1.140625 0.015625 C 1.390625 0.09375 1.578125 0.125 1.75 0.125 C 2.65625 0.125 3.3125 -0.46875 3.3125 -1.296875 C 3.3125 -1.71875 3.09375 -2.1875 2.578125 -2.828125 C 2.15625 -3.34375 1.984375 -3.671875 1.984375 -3.96875 C 1.984375 -4.328125 2.21875 -4.5625 2.59375 -4.5625 C 3.15625 -4.5625 3.484375 -4.15625 3.59375 -3.296875 L 3.78125 -3.296875 L 3.984375 -4.828125 L 3.84375 -4.828125 C 3.75 -4.671875 3.671875 -4.625 3.515625 -4.625 C 3.4375 -4.625 3.328125 -4.640625 3.125 -4.703125 C 2.84375 -4.78125 2.6875 -4.8125 2.515625 -4.8125 C 1.71875 -4.8125 1.1875 -4.34375 1.1875 -3.609375 C 1.1875 -3.265625 1.421875 -2.796875 1.875 -2.203125 C 2.296875 -1.640625 2.484375 -1.265625 2.484375 -0.953125 C 2.484375 -0.453125 2.15625 -0.109375 1.671875 -0.109375 C 1.078125 -0.109375 0.734375 -0.5625 0.5625 -1.59375 Z M 0.390625 -1.59375 "/> +</symbol> +<symbol overflow="visible" id="glyph9-6"> +<path style="stroke:none;" d="M 2.421875 -1.25 C 2.15625 -0.890625 2.078125 -0.8125 1.96875 -0.6875 C 1.78125 -0.5 1.625 -0.390625 1.53125 -0.390625 C 1.4375 -0.390625 1.359375 -0.484375 1.359375 -0.5625 C 1.359375 -0.671875 1.390625 -0.828125 1.46875 -1.03125 C 1.46875 -1.0625 1.5 -1.140625 1.515625 -1.234375 L 1.515625 -1.25 L 1.53125 -1.28125 L 2.484375 -4.78125 L 2.453125 -4.8125 C 1.359375 -4.609375 1.140625 -4.5625 0.703125 -4.53125 L 0.703125 -4.359375 C 1.28125 -4.359375 1.390625 -4.3125 1.390625 -4.09375 C 1.390625 -4.015625 1.359375 -3.84375 1.296875 -3.625 L 0.78125 -1.6875 C 0.59375 -1.0625 0.53125 -0.71875 0.53125 -0.5 C 0.53125 -0.09375 0.703125 0.125 1.03125 0.125 C 1.53125 0.125 1.9375 -0.203125 2.5625 -1.125 Z M 2.328125 -7.125 C 2.046875 -7.125 1.828125 -6.875 1.828125 -6.546875 C 1.828125 -6.203125 2.03125 -5.984375 2.34375 -5.984375 C 2.625 -5.984375 2.875 -6.234375 2.875 -6.53125 C 2.875 -6.84375 2.625 -7.125 2.328125 -7.125 Z M 2.328125 -7.125 "/> +</symbol> +<symbol overflow="visible" id="glyph9-7"> +<path style="stroke:none;" d="M 0.234375 -4.390625 C 0.34375 -4.40625 0.4375 -4.40625 0.5625 -4.40625 C 0.984375 -4.40625 1.09375 -4.21875 1.25 -3.21875 C 1.359375 -2.46875 1.484375 -0.8125 1.484375 -0.1875 C 1.484375 0.125 1.5 0.203125 1.578125 0.203125 C 1.859375 0.203125 2.875 -0.953125 3.984375 -2.515625 C 4.359375 -3.046875 4.640625 -3.765625 4.640625 -4.171875 C 4.640625 -4.515625 4.359375 -4.8125 4.03125 -4.8125 C 3.796875 -4.8125 3.625 -4.671875 3.625 -4.4375 C 3.625 -4.265625 3.703125 -4.140625 3.890625 -3.953125 C 4.03125 -3.828125 4.09375 -3.734375 4.09375 -3.625 C 4.09375 -3.125 3.375 -1.921875 2.59375 -1.109375 L 2.25 -0.765625 C 2.1875 -2.296875 2.109375 -2.875 1.96875 -3.671875 C 1.75 -4.78125 1.75 -4.8125 1.65625 -4.8125 C 1.609375 -4.8125 1.53125 -4.796875 1.4375 -4.765625 C 1.25 -4.71875 0.640625 -4.609375 0.234375 -4.53125 Z M 0.234375 -4.390625 "/> +</symbol> +<symbol overflow="visible" id="glyph9-8"> +<path style="stroke:none;" d="M 0.15625 -4.359375 C 0.3125 -4.390625 0.375 -4.40625 0.5 -4.40625 C 1.125 -4.40625 1.28125 -4.140625 1.78125 -2.25 C 1.96875 -1.546875 2.234375 -0.265625 2.234375 -0.09375 C 2.234375 0.09375 2.171875 0.265625 2 0.453125 C 1.671875 0.90625 1.453125 1.1875 1.328125 1.3125 C 1.09375 1.5625 0.96875 1.640625 0.828125 1.640625 C 0.765625 1.640625 0.6875 1.609375 0.5625 1.53125 C 0.40625 1.390625 0.28125 1.34375 0.15625 1.34375 C -0.078125 1.34375 -0.265625 1.53125 -0.265625 1.765625 C -0.265625 2.046875 -0.015625 2.25 0.296875 2.25 C 1 2.25 2.421875 0.609375 3.59375 -1.546875 C 4.34375 -2.890625 4.640625 -3.671875 4.640625 -4.203125 C 4.640625 -4.53125 4.375 -4.8125 4.046875 -4.8125 C 3.796875 -4.8125 3.625 -4.640625 3.625 -4.40625 C 3.625 -4.25 3.703125 -4.125 3.921875 -3.984375 C 4.140625 -3.859375 4.203125 -3.765625 4.203125 -3.609375 C 4.203125 -3.171875 3.8125 -2.328125 2.875 -0.78125 L 2.65625 -2.046875 C 2.5 -3.015625 1.890625 -4.8125 1.71875 -4.8125 L 1.671875 -4.8125 C 1.671875 -4.796875 1.625 -4.796875 1.578125 -4.796875 C 1.484375 -4.78125 1.09375 -4.71875 0.515625 -4.609375 C 0.453125 -4.609375 0.3125 -4.5625 0.15625 -4.546875 Z M 0.15625 -4.359375 "/> +</symbol> +<symbol overflow="visible" id="glyph9-9"> +<path style="stroke:none;" d="M 5.0625 -1.203125 C 4.890625 -1.03125 4.828125 -0.984375 4.75 -0.890625 C 4.421875 -0.5625 4.28125 -0.453125 4.1875 -0.453125 C 4.09375 -0.453125 4.03125 -0.515625 4.03125 -0.59375 C 4.03125 -0.8125 4.5 -2.671875 5 -4.546875 C 5.03125 -4.65625 5.046875 -4.671875 5.078125 -4.78125 L 5 -4.8125 L 4.328125 -4.734375 L 4.296875 -4.703125 L 4.171875 -4.171875 C 4.09375 -4.578125 3.78125 -4.8125 3.296875 -4.8125 C 1.859375 -4.8125 0.1875 -2.8125 0.1875 -1.09375 C 0.1875 -0.328125 0.59375 0.125 1.296875 0.125 C 2.0625 0.125 2.53125 -0.234375 3.484375 -1.59375 C 3.265625 -0.734375 3.234375 -0.59375 3.234375 -0.34375 C 3.234375 -0.015625 3.375 0.109375 3.671875 0.109375 C 4.109375 0.109375 4.390625 -0.09375 5.1875 -1.09375 Z M 3.375 -4.5625 C 3.734375 -4.546875 3.984375 -4.28125 3.984375 -3.90625 C 3.984375 -2.984375 3.4375 -1.703125 2.6875 -0.890625 C 2.421875 -0.59375 2.046875 -0.421875 1.734375 -0.421875 C 1.359375 -0.421875 1.09375 -0.734375 1.09375 -1.234375 C 1.09375 -1.828125 1.515625 -2.9375 1.984375 -3.625 C 2.421875 -4.25 2.9375 -4.609375 3.375 -4.5625 Z M 3.375 -4.5625 "/> +</symbol> +<symbol overflow="visible" id="glyph10-0"> +<path style="stroke:none;" d="M 2.3125 -5.390625 L 0.890625 -4.65625 L 0.890625 -4.546875 C 0.984375 -4.59375 1.0625 -4.625 1.09375 -4.640625 C 1.25 -4.6875 1.375 -4.71875 1.453125 -4.71875 C 1.625 -4.71875 1.703125 -4.609375 1.703125 -4.34375 L 1.703125 -0.734375 C 1.703125 -0.484375 1.640625 -0.296875 1.5 -0.21875 C 1.390625 -0.15625 1.28125 -0.125 0.9375 -0.125 L 0.9375 0 L 3.140625 0 L 3.140625 -0.125 C 2.515625 -0.125 2.375 -0.203125 2.375 -0.59375 L 2.375 -5.375 Z M 2.3125 -5.390625 "/> +</symbol> +<symbol overflow="visible" id="glyph11-0"> +<path style="stroke:none;" d="M 7.359375 -5.4375 C 7.5 -5.5 7.5625 -5.546875 7.5625 -5.671875 C 7.5625 -5.796875 7.46875 -5.890625 7.34375 -5.890625 C 7.3125 -5.890625 7.296875 -5.890625 7.15625 -5.8125 L 1.109375 -2.96875 C 1 -2.90625 0.90625 -2.859375 0.90625 -2.71875 C 0.90625 -2.59375 1 -2.546875 1.109375 -2.484375 L 7.15625 0.359375 C 7.296875 0.4375 7.3125 0.4375 7.34375 0.4375 C 7.46875 0.4375 7.5625 0.34375 7.5625 0.21875 C 7.5625 0.09375 7.5 0.046875 7.359375 -0.015625 L 1.640625 -2.71875 Z M 7.359375 -5.4375 "/> +</symbol> +<symbol overflow="visible" id="glyph11-1"> +<path style="stroke:none;" d="M 7.359375 -2.484375 C 7.484375 -2.546875 7.5625 -2.59375 7.5625 -2.71875 C 7.5625 -2.859375 7.484375 -2.90625 7.359375 -2.96875 L 1.3125 -5.8125 C 1.171875 -5.890625 1.15625 -5.890625 1.125 -5.890625 C 1 -5.890625 0.90625 -5.796875 0.90625 -5.671875 C 0.90625 -5.578125 0.953125 -5.5 1.109375 -5.4375 L 6.84375 -2.71875 L 1.109375 -0.015625 C 0.953125 0.046875 0.90625 0.125 0.90625 0.21875 C 0.90625 0.34375 1 0.4375 1.125 0.4375 C 1.15625 0.4375 1.171875 0.4375 1.3125 0.359375 Z M 7.359375 -2.484375 "/> +</symbol> +<symbol overflow="visible" id="glyph12-0"> +<path style="stroke:none;" d="M 1.734375 -4.046875 L 0.671875 -3.5 L 0.671875 -3.421875 C 0.734375 -3.453125 0.796875 -3.46875 0.828125 -3.484375 C 0.9375 -3.53125 1.03125 -3.546875 1.09375 -3.546875 C 1.21875 -3.546875 1.28125 -3.453125 1.28125 -3.265625 L 1.28125 -0.5625 C 1.28125 -0.359375 1.234375 -0.21875 1.125 -0.171875 C 1.046875 -0.109375 0.953125 -0.09375 0.703125 -0.09375 L 0.703125 0 L 2.359375 0 L 2.359375 -0.09375 C 1.890625 -0.09375 1.796875 -0.15625 1.796875 -0.4375 L 1.796875 -4.03125 Z M 1.734375 -4.046875 "/> +</symbol> +<symbol overflow="visible" id="glyph13-0"> +<path style="stroke:none;" d="M 1.03125 -0.984375 C 1.03125 -0.3125 0.921875 -0.203125 0.15625 -0.171875 L 0.15625 0 L 2.828125 0 L 2.828125 -0.171875 C 2.09375 -0.203125 1.953125 -0.328125 1.953125 -0.984375 L 1.953125 -4.953125 C 1.953125 -5.609375 2.078125 -5.734375 2.828125 -5.765625 L 2.828125 -5.9375 L 0.15625 -5.9375 L 0.15625 -5.765625 C 0.921875 -5.71875 1.03125 -5.625 1.03125 -4.953125 Z M 1.03125 -0.984375 "/> +</symbol> +<symbol overflow="visible" id="glyph13-1"> +<path style="stroke:none;" d="M 0.140625 -3.5625 C 0.203125 -3.59375 0.28125 -3.609375 0.390625 -3.609375 C 0.640625 -3.609375 0.71875 -3.46875 0.71875 -3.03125 L 0.71875 -0.8125 C 0.71875 -0.296875 0.625 -0.171875 0.15625 -0.140625 L 0.15625 0 L 2.0625 0 L 2.0625 -0.140625 C 1.609375 -0.171875 1.46875 -0.28125 1.46875 -0.59375 L 1.46875 -3.125 C 1.90625 -3.53125 2.09375 -3.625 2.390625 -3.625 C 2.828125 -3.625 3.046875 -3.359375 3.046875 -2.765625 L 3.046875 -0.890625 C 3.046875 -0.328125 2.9375 -0.171875 2.484375 -0.140625 L 2.484375 0 L 4.34375 0 L 4.34375 -0.140625 C 3.90625 -0.171875 3.796875 -0.28125 3.796875 -0.71875 L 3.796875 -2.78125 C 3.796875 -3.625 3.40625 -4.125 2.75 -4.125 C 2.328125 -4.125 2.046875 -3.96875 1.4375 -3.40625 L 1.4375 -4.109375 L 1.375 -4.125 C 0.9375 -3.96875 0.640625 -3.859375 0.140625 -3.71875 Z M 0.140625 -3.5625 "/> +</symbol> +<symbol overflow="visible" id="glyph13-2"> +<path style="stroke:none;" d="M 2.28125 -4.03125 L 1.375 -4.03125 L 1.375 -5.078125 C 1.375 -5.171875 1.375 -5.1875 1.3125 -5.1875 C 1.25 -5.109375 1.203125 -5.03125 1.140625 -4.9375 C 0.796875 -4.453125 0.40625 -4.015625 0.265625 -3.984375 C 0.171875 -3.921875 0.109375 -3.859375 0.109375 -3.8125 C 0.109375 -3.78125 0.125 -3.765625 0.15625 -3.75 L 0.625 -3.75 L 0.625 -1.046875 C 0.625 -0.296875 0.890625 0.09375 1.421875 0.09375 C 1.859375 0.09375 2.203125 -0.125 2.5 -0.59375 L 2.390625 -0.6875 C 2.203125 -0.46875 2.046875 -0.375 1.84375 -0.375 C 1.515625 -0.375 1.375 -0.625 1.375 -1.1875 L 1.375 -3.75 L 2.28125 -3.75 Z M 2.28125 -4.03125 "/> +</symbol> +<symbol overflow="visible" id="glyph13-3"> +<path style="stroke:none;" d="M 1.40625 -3.078125 C 1.78125 -3.484375 2.046875 -3.640625 2.40625 -3.640625 C 2.859375 -3.640625 3.078125 -3.3125 3.078125 -2.6875 L 3.078125 -0.921875 C 3.078125 -0.3125 2.984375 -0.1875 2.46875 -0.140625 L 2.46875 0 L 4.375 0 L 4.375 -0.140625 C 3.890625 -0.21875 3.828125 -0.296875 3.828125 -0.921875 L 3.828125 -2.703125 C 3.828125 -3.640625 3.453125 -4.125 2.71875 -4.125 C 2.203125 -4.125 1.828125 -3.90625 1.40625 -3.375 L 1.40625 -6.09375 L 1.359375 -6.125 C 1.0625 -6.015625 0.828125 -5.953125 0.328125 -5.796875 L 0.09375 -5.734375 L 0.09375 -5.59375 C 0.125 -5.59375 0.15625 -5.59375 0.203125 -5.59375 C 0.578125 -5.59375 0.65625 -5.53125 0.65625 -5.140625 L 0.65625 -0.921875 C 0.65625 -0.28125 0.59375 -0.203125 0.078125 -0.140625 L 0.078125 0 L 2.015625 0 L 2.015625 -0.140625 C 1.5 -0.1875 1.40625 -0.296875 1.40625 -0.921875 Z M 1.40625 -3.078125 "/> +</symbol> +<symbol overflow="visible" id="glyph13-4"> +<path style="stroke:none;" d="M 1.5625 -4.125 L 0.171875 -3.625 L 0.171875 -3.5 L 0.25 -3.5 C 0.359375 -3.53125 0.46875 -3.53125 0.5625 -3.53125 C 0.765625 -3.53125 0.859375 -3.390625 0.859375 -3 L 0.859375 -0.921875 C 0.859375 -0.265625 0.765625 -0.171875 0.140625 -0.140625 L 0.140625 0 L 2.265625 0 L 2.265625 -0.140625 C 1.671875 -0.171875 1.609375 -0.265625 1.609375 -0.921875 L 1.609375 -4.09375 Z M 1.140625 -6.125 C 0.90625 -6.125 0.703125 -5.921875 0.703125 -5.671875 C 0.703125 -5.421875 0.890625 -5.203125 1.140625 -5.203125 C 1.40625 -5.203125 1.609375 -5.40625 1.609375 -5.671875 C 1.609375 -5.921875 1.40625 -6.125 1.140625 -6.125 Z M 1.140625 -6.125 "/> +</symbol> +<symbol overflow="visible" id="glyph13-5"> +<path style="stroke:none;" d="M 2.828125 -2.8125 L 2.796875 -4.03125 L 2.6875 -4.03125 L 2.671875 -4.015625 C 2.59375 -3.953125 2.578125 -3.953125 2.546875 -3.953125 C 2.5 -3.953125 2.40625 -3.96875 2.3125 -4.015625 C 2.109375 -4.078125 1.90625 -4.109375 1.671875 -4.109375 C 0.96875 -4.109375 0.453125 -3.65625 0.453125 -3.015625 C 0.453125 -2.515625 0.75 -2.15625 1.5 -1.71875 L 2.03125 -1.421875 C 2.34375 -1.25 2.5 -1.03125 2.5 -0.75 C 2.5 -0.359375 2.203125 -0.109375 1.75 -0.109375 C 1.4375 -0.109375 1.171875 -0.21875 1 -0.421875 C 0.8125 -0.640625 0.71875 -0.859375 0.609375 -1.359375 L 0.46875 -1.359375 L 0.46875 0.03125 L 0.578125 0.03125 C 0.640625 -0.046875 0.6875 -0.078125 0.796875 -0.078125 C 0.875 -0.078125 1 -0.046875 1.203125 0 C 1.453125 0.046875 1.6875 0.09375 1.859375 0.09375 C 2.546875 0.09375 3.125 -0.4375 3.125 -1.0625 C 3.125 -1.5 2.90625 -1.796875 2.375 -2.125 L 1.40625 -2.703125 C 1.140625 -2.84375 1.015625 -3.0625 1.015625 -3.3125 C 1.015625 -3.671875 1.296875 -3.921875 1.703125 -3.921875 C 2.21875 -3.921875 2.484375 -3.609375 2.6875 -2.8125 Z M 2.828125 -2.8125 "/> +</symbol> +<symbol overflow="visible" id="glyph13-6"> +<path style="stroke:none;" d="M 0.078125 -3.53125 C 0.15625 -3.53125 0.21875 -3.53125 0.3125 -3.53125 C 0.609375 -3.53125 0.671875 -3.4375 0.671875 -3.015625 L 0.671875 1.171875 C 0.671875 1.640625 0.578125 1.734375 0.046875 1.796875 L 0.046875 1.953125 L 2.21875 1.953125 L 2.21875 1.78125 C 1.546875 1.78125 1.421875 1.671875 1.421875 1.109375 L 1.421875 -0.296875 C 1.734375 0 1.953125 0.09375 2.328125 0.09375 C 3.390625 0.09375 4.21875 -0.921875 4.21875 -2.21875 C 4.21875 -3.328125 3.59375 -4.125 2.71875 -4.125 C 2.21875 -4.125 1.828125 -3.90625 1.421875 -3.421875 L 1.421875 -4.109375 L 1.375 -4.125 C 0.890625 -3.9375 0.578125 -3.828125 0.078125 -3.671875 Z M 1.421875 -3 C 1.421875 -3.265625 1.921875 -3.59375 2.34375 -3.59375 C 3 -3.59375 3.4375 -2.90625 3.4375 -1.859375 C 3.4375 -0.875 3 -0.203125 2.359375 -0.203125 C 1.9375 -0.203125 1.421875 -0.515625 1.421875 -0.796875 Z M 1.421875 -3 "/> +</symbol> +<symbol overflow="visible" id="glyph13-7"> +<path style="stroke:none;" d="M 3.96875 -0.59375 C 3.8125 -0.46875 3.703125 -0.421875 3.5625 -0.421875 C 3.359375 -0.421875 3.296875 -0.546875 3.296875 -0.9375 L 3.296875 -2.6875 C 3.296875 -3.15625 3.25 -3.421875 3.125 -3.625 C 2.921875 -3.953125 2.53125 -4.125 2.015625 -4.125 C 1.171875 -4.125 0.5 -3.6875 0.5 -3.125 C 0.5 -2.921875 0.6875 -2.734375 0.890625 -2.734375 C 1.109375 -2.734375 1.296875 -2.921875 1.296875 -3.109375 C 1.296875 -3.140625 1.28125 -3.1875 1.28125 -3.25 C 1.25 -3.34375 1.25 -3.40625 1.25 -3.46875 C 1.25 -3.71875 1.53125 -3.90625 1.890625 -3.90625 C 2.328125 -3.90625 2.578125 -3.65625 2.578125 -3.171875 L 2.578125 -2.625 C 1.1875 -2.0625 1.046875 -1.984375 0.65625 -1.65625 C 0.453125 -1.46875 0.328125 -1.171875 0.328125 -0.875 C 0.328125 -0.3125 0.71875 0.09375 1.28125 0.09375 C 1.671875 0.09375 2.03125 -0.09375 2.578125 -0.5625 C 2.625 -0.09375 2.796875 0.09375 3.15625 0.09375 C 3.46875 0.09375 3.65625 -0.015625 3.96875 -0.359375 Z M 2.578125 -1.109375 C 2.578125 -0.828125 2.53125 -0.75 2.34375 -0.640625 C 2.125 -0.515625 1.875 -0.4375 1.6875 -0.4375 C 1.375 -0.4375 1.125 -0.734375 1.125 -1.125 L 1.125 -1.15625 C 1.125 -1.6875 1.484375 -2.015625 2.578125 -2.40625 Z M 2.578125 -1.109375 "/> +</symbol> +<symbol overflow="visible" id="glyph13-8"> +<path style="stroke:none;" d="M 3.65625 -1.46875 C 3.234375 -0.796875 2.84375 -0.53125 2.265625 -0.53125 C 1.765625 -0.53125 1.375 -0.796875 1.109375 -1.296875 C 0.953125 -1.640625 0.890625 -1.9375 0.875 -2.484375 L 3.625 -2.484375 C 3.5625 -3.0625 3.46875 -3.328125 3.25 -3.609375 C 2.984375 -3.9375 2.5625 -4.125 2.09375 -4.125 C 1.65625 -4.125 1.234375 -3.96875 0.890625 -3.65625 C 0.46875 -3.296875 0.21875 -2.65625 0.21875 -1.921875 C 0.21875 -0.6875 0.875 0.09375 1.90625 0.09375 C 2.75 0.09375 3.421875 -0.4375 3.796875 -1.40625 Z M 0.890625 -2.765625 C 0.984375 -3.46875 1.296875 -3.796875 1.84375 -3.796875 C 2.390625 -3.796875 2.59375 -3.546875 2.71875 -2.765625 Z M 0.890625 -2.765625 "/> +</symbol> +<symbol overflow="visible" id="glyph13-9"> +<path style="stroke:none;" d="M 0.0625 -3.5 C 0.1875 -3.53125 0.265625 -3.53125 0.375 -3.53125 C 0.59375 -3.53125 0.6875 -3.390625 0.6875 -3 L 0.6875 -0.75 C 0.6875 -0.3125 0.625 -0.234375 0.046875 -0.140625 L 0.046875 0 L 2.203125 0 L 2.203125 -0.140625 C 1.59375 -0.15625 1.4375 -0.296875 1.4375 -0.8125 L 1.4375 -2.828125 C 1.4375 -3.109375 1.828125 -3.5625 2.0625 -3.5625 C 2.109375 -3.5625 2.203125 -3.515625 2.296875 -3.421875 C 2.4375 -3.296875 2.53125 -3.25 2.65625 -3.25 C 2.875 -3.25 3 -3.40625 3 -3.65625 C 3 -3.953125 2.8125 -4.125 2.515625 -4.125 C 2.140625 -4.125 1.875 -3.921875 1.4375 -3.28125 L 1.4375 -4.109375 L 1.390625 -4.125 C 0.921875 -3.921875 0.59375 -3.8125 0.0625 -3.640625 Z M 0.0625 -3.5 "/> +</symbol> +<symbol overflow="visible" id="glyph13-10"> +<path style="stroke:none;" d="M 0.75 1.265625 C 1.34375 0.96875 1.75 0.421875 1.75 -0.109375 C 1.75 -0.5625 1.4375 -0.921875 1.03125 -0.921875 C 0.71875 -0.921875 0.5 -0.703125 0.5 -0.40625 C 0.5 -0.109375 0.703125 0.046875 1.03125 0.046875 C 1.078125 0.046875 1.140625 0.046875 1.203125 0.03125 C 1.265625 0.015625 1.265625 0.015625 1.28125 0.015625 C 1.34375 0.015625 1.40625 0.078125 1.40625 0.140625 C 1.40625 0.4375 1.140625 0.765625 0.65625 1.09375 Z M 0.75 1.265625 "/> +</symbol> +<symbol overflow="visible" id="glyph13-11"> +<path style="stroke:none;" d="M 5.125 -4.03125 L 5.125 -3.90625 C 5.421875 -3.84375 5.515625 -3.78125 5.515625 -3.625 C 5.515625 -3.484375 5.46875 -3.28125 5.359375 -3.03125 L 4.5625 -1.046875 L 3.796875 -3.046875 C 3.65625 -3.46875 3.65625 -3.46875 3.65625 -3.578125 C 3.65625 -3.78125 3.75 -3.84375 4.171875 -3.90625 L 4.171875 -4.03125 L 2.34375 -4.03125 L 2.34375 -3.90625 C 2.6875 -3.859375 2.796875 -3.765625 2.96875 -3.28125 C 3.03125 -3.109375 3.09375 -2.9375 3.140625 -2.78125 L 2.328125 -1 L 1.4375 -3.34375 C 1.40625 -3.421875 1.390625 -3.515625 1.390625 -3.609375 C 1.390625 -3.796875 1.5 -3.859375 1.796875 -3.90625 L 1.796875 -4.03125 L 0.1875 -4.03125 L 0.1875 -3.90625 C 0.390625 -3.890625 0.46875 -3.796875 0.65625 -3.34375 L 1.875 -0.265625 C 1.984375 0 2.046875 0.125 2.109375 0.125 C 2.15625 0.125 2.21875 0.015625 2.328125 -0.21875 L 3.34375 -2.375 L 4.15625 -0.265625 C 4.28125 0.078125 4.3125 0.125 4.375 0.125 C 4.4375 0.125 4.46875 0.046875 4.625 -0.3125 L 5.859375 -3.421875 C 6.015625 -3.796875 6.046875 -3.84375 6.21875 -3.90625 L 6.21875 -4.03125 Z M 5.125 -4.03125 "/> +</symbol> +<symbol overflow="visible" id="glyph13-12"> +<path style="stroke:none;" d="M 4.296875 -0.453125 L 4.25 -0.453125 C 3.84375 -0.453125 3.734375 -0.546875 3.734375 -0.953125 L 3.734375 -4.03125 L 2.328125 -4.03125 L 2.328125 -3.890625 C 2.875 -3.859375 2.984375 -3.765625 2.984375 -3.3125 L 2.984375 -1.203125 C 2.984375 -0.953125 2.9375 -0.828125 2.8125 -0.734375 C 2.578125 -0.53125 2.296875 -0.4375 2.03125 -0.4375 C 1.671875 -0.4375 1.390625 -0.734375 1.390625 -1.109375 L 1.390625 -4.03125 L 0.078125 -4.03125 L 0.078125 -3.90625 C 0.515625 -3.890625 0.640625 -3.75 0.640625 -3.34375 L 0.640625 -1.078125 C 0.640625 -0.375 1.0625 0.09375 1.71875 0.09375 C 2.046875 0.09375 2.40625 -0.046875 2.640625 -0.296875 L 3.03125 -0.6875 L 3.03125 0.0625 L 3.0625 0.078125 C 3.515625 -0.09375 3.84375 -0.203125 4.296875 -0.328125 Z M 4.296875 -0.453125 "/> +</symbol> +<symbol overflow="visible" id="glyph13-13"> +<path style="stroke:none;" d="M 1.390625 -6.0625 C 0.796875 -5.765625 0.390625 -5.21875 0.390625 -4.6875 C 0.390625 -4.21875 0.703125 -3.890625 1.109375 -3.890625 C 1.421875 -3.890625 1.625 -4.09375 1.625 -4.390625 C 1.625 -4.6875 1.421875 -4.859375 1.109375 -4.859375 C 1.046875 -4.859375 0.984375 -4.84375 0.9375 -4.828125 C 0.875 -4.8125 0.875 -4.8125 0.859375 -4.8125 C 0.796875 -4.8125 0.734375 -4.875 0.734375 -4.9375 C 0.734375 -5.234375 0.984375 -5.5625 1.46875 -5.890625 Z M 3.46875 -6.0625 C 2.875 -5.765625 2.46875 -5.21875 2.46875 -4.6875 C 2.46875 -4.21875 2.78125 -3.890625 3.1875 -3.890625 C 3.5 -3.890625 3.71875 -4.09375 3.71875 -4.390625 C 3.71875 -4.6875 3.5 -4.859375 3.1875 -4.859375 C 3.125 -4.859375 3.0625 -4.84375 3.015625 -4.828125 C 2.953125 -4.8125 2.953125 -4.8125 2.9375 -4.8125 C 2.875 -4.8125 2.8125 -4.875 2.8125 -4.9375 C 2.8125 -5.234375 3.0625 -5.5625 3.546875 -5.890625 Z M 3.46875 -6.0625 "/> +</symbol> +<symbol overflow="visible" id="glyph13-14"> +<path style="stroke:none;" d="M 3.5625 -1.40625 C 3.140625 -0.765625 2.8125 -0.5625 2.3125 -0.5625 C 1.484375 -0.5625 0.921875 -1.28125 0.921875 -2.3125 C 0.921875 -3.234375 1.40625 -3.859375 2.140625 -3.859375 C 2.453125 -3.859375 2.578125 -3.765625 2.65625 -3.4375 L 2.71875 -3.234375 C 2.796875 -2.984375 2.953125 -2.828125 3.140625 -2.828125 C 3.375 -2.828125 3.5625 -3 3.5625 -3.203125 C 3.5625 -3.703125 2.9375 -4.125 2.1875 -4.125 C 1.75 -4.125 1.296875 -3.953125 0.921875 -3.625 C 0.46875 -3.234375 0.21875 -2.625 0.21875 -1.90625 C 0.21875 -0.75 0.9375 0.09375 1.921875 0.09375 C 2.328125 0.09375 2.6875 -0.046875 3.015625 -0.328125 C 3.25 -0.546875 3.421875 -0.796875 3.6875 -1.3125 Z M 3.5625 -1.40625 "/> +</symbol> +<symbol overflow="visible" id="glyph13-15"> +<path style="stroke:none;" d="M 0.171875 -5.59375 L 0.21875 -5.59375 C 0.328125 -5.59375 0.4375 -5.609375 0.5 -5.609375 C 0.796875 -5.609375 0.875 -5.484375 0.875 -5.0625 L 0.875 -0.78125 C 0.875 -0.296875 0.75 -0.171875 0.1875 -0.140625 L 0.1875 0 L 2.3125 0 L 2.3125 -0.140625 C 1.734375 -0.171875 1.625 -0.265625 1.625 -0.75 L 1.625 -6.109375 L 1.59375 -6.125 C 1.125 -5.96875 0.796875 -5.890625 0.171875 -5.734375 Z M 0.171875 -5.59375 "/> +</symbol> +<symbol overflow="visible" id="glyph13-16"> +<path style="stroke:none;" d="M 0.515625 -3.890625 C 1.109375 -4.171875 1.515625 -4.734375 1.515625 -5.265625 C 1.515625 -5.71875 1.203125 -6.0625 0.796875 -6.0625 C 0.484375 -6.0625 0.265625 -5.859375 0.265625 -5.546875 C 0.265625 -5.265625 0.46875 -5.09375 0.796875 -5.09375 C 0.859375 -5.09375 0.921875 -5.109375 0.96875 -5.109375 C 1.03125 -5.125 1.03125 -5.125 1.046875 -5.125 C 1.109375 -5.125 1.171875 -5.078125 1.171875 -5 C 1.171875 -4.71875 0.921875 -4.390625 0.4375 -4.046875 Z M 2.59375 -3.890625 C 3.1875 -4.171875 3.59375 -4.734375 3.59375 -5.265625 C 3.59375 -5.71875 3.28125 -6.0625 2.875 -6.0625 C 2.5625 -6.0625 2.34375 -5.859375 2.34375 -5.546875 C 2.34375 -5.265625 2.546875 -5.09375 2.875 -5.09375 C 2.9375 -5.09375 3 -5.109375 3.046875 -5.109375 C 3.109375 -5.125 3.109375 -5.125 3.125 -5.125 C 3.1875 -5.125 3.25 -5.078125 3.25 -5 C 3.25 -4.71875 3 -4.390625 2.515625 -4.046875 Z M 2.59375 -3.890625 "/> +</symbol> +<symbol overflow="visible" id="glyph13-17"> +<path style="stroke:none;" d="M 3.078125 0.09375 L 4.40625 -0.375 L 4.40625 -0.515625 C 4.25 -0.515625 4.21875 -0.515625 4.203125 -0.515625 C 3.875 -0.515625 3.796875 -0.609375 3.796875 -1.015625 L 3.796875 -6.109375 L 3.765625 -6.125 C 3.328125 -5.96875 3.015625 -5.890625 2.4375 -5.734375 L 2.4375 -5.59375 C 2.515625 -5.59375 2.5625 -5.59375 2.640625 -5.59375 C 2.96875 -5.59375 3.046875 -5.5 3.046875 -5.140625 L 3.046875 -3.734375 C 2.703125 -4.03125 2.46875 -4.125 2.109375 -4.125 C 1.078125 -4.125 0.234375 -3.109375 0.234375 -1.84375 C 0.234375 -0.6875 0.921875 0.09375 1.90625 0.09375 C 2.40625 0.09375 2.75 -0.09375 3.046875 -0.515625 L 3.046875 0.0625 Z M 3.046875 -0.921875 C 3.046875 -0.859375 2.984375 -0.75 2.890625 -0.640625 C 2.734375 -0.46875 2.515625 -0.375 2.25 -0.375 C 1.5 -0.375 1.015625 -1.09375 1.015625 -2.203125 C 1.015625 -3.203125 1.453125 -3.875 2.140625 -3.875 C 2.609375 -3.875 3.046875 -3.453125 3.046875 -2.984375 Z M 3.046875 -0.921875 "/> +</symbol> +<symbol overflow="visible" id="glyph13-18"> +<path style="stroke:none;" d="M 2.25 -4.125 C 1.078125 -4.125 0.265625 -3.265625 0.265625 -2.03125 C 0.265625 -0.8125 1.09375 0.09375 2.21875 0.09375 C 3.359375 0.09375 4.21875 -0.859375 4.21875 -2.09375 C 4.21875 -3.28125 3.390625 -4.125 2.25 -4.125 Z M 2.125 -3.875 C 2.875 -3.875 3.40625 -3.015625 3.40625 -1.78125 C 3.40625 -0.765625 3 -0.15625 2.328125 -0.15625 C 1.984375 -0.15625 1.65625 -0.375 1.46875 -0.734375 C 1.203125 -1.203125 1.0625 -1.828125 1.0625 -2.46875 C 1.0625 -3.3125 1.484375 -3.875 2.125 -3.875 Z M 2.125 -3.875 "/> +</symbol> +<symbol overflow="visible" id="glyph13-19"> +<path style="stroke:none;" d="M 4.21875 -3.484375 L 4.21875 -3.828125 L 3.53125 -3.828125 C 3.34375 -3.828125 3.203125 -3.859375 3.03125 -3.921875 L 2.828125 -3.984375 C 2.59375 -4.078125 2.34375 -4.125 2.109375 -4.125 C 1.28125 -4.125 0.625 -3.484375 0.625 -2.65625 C 0.625 -2.09375 0.859375 -1.765625 1.453125 -1.46875 C 1.28125 -1.296875 1.125 -1.140625 1.0625 -1.109375 C 0.765625 -0.84375 0.65625 -0.65625 0.65625 -0.484375 C 0.65625 -0.296875 0.765625 -0.1875 1.125 -0.015625 C 0.5 0.453125 0.25 0.75 0.25 1.078125 C 0.25 1.5625 0.953125 1.953125 1.796875 1.953125 C 2.46875 1.953125 3.171875 1.71875 3.640625 1.34375 C 3.984375 1.0625 4.140625 0.78125 4.140625 0.4375 C 4.140625 -0.109375 3.71875 -0.5 3.046875 -0.515625 L 1.890625 -0.578125 C 1.421875 -0.59375 1.1875 -0.671875 1.1875 -0.8125 C 1.1875 -1 1.484375 -1.3125 1.734375 -1.375 C 1.8125 -1.375 1.875 -1.359375 1.90625 -1.359375 C 2.078125 -1.34375 2.1875 -1.34375 2.25 -1.34375 C 2.578125 -1.34375 2.9375 -1.46875 3.203125 -1.71875 C 3.5 -1.96875 3.640625 -2.28125 3.640625 -2.71875 C 3.640625 -2.984375 3.59375 -3.1875 3.46875 -3.484375 Z M 1.3125 0.015625 C 1.609375 0.078125 2.328125 0.140625 2.765625 0.140625 C 3.59375 0.140625 3.890625 0.25 3.890625 0.578125 C 3.890625 1.09375 3.203125 1.4375 2.1875 1.4375 C 1.40625 1.4375 0.875 1.1875 0.875 0.796875 C 0.875 0.578125 0.9375 0.46875 1.3125 0.015625 Z M 1.359375 -3.03125 C 1.359375 -3.5625 1.609375 -3.875 2.03125 -3.875 C 2.3125 -3.875 2.53125 -3.71875 2.6875 -3.453125 C 2.84375 -3.140625 2.953125 -2.71875 2.953125 -2.375 C 2.953125 -1.875 2.6875 -1.5625 2.28125 -1.5625 C 1.734375 -1.5625 1.359375 -2.140625 1.359375 -3 Z M 1.359375 -3.03125 "/> +</symbol> +<symbol overflow="visible" id="glyph13-20"> +<path style="stroke:none;" d="M 1.375 -6.109375 L 1.328125 -6.125 C 0.953125 -5.984375 0.703125 -5.921875 0.28125 -5.796875 L 0.03125 -5.734375 L 0.03125 -5.59375 C 0.078125 -5.59375 0.109375 -5.59375 0.171875 -5.59375 C 0.53125 -5.59375 0.625 -5.515625 0.625 -5.140625 L 0.625 -0.484375 C 0.625 -0.203125 1.375 0.09375 2.09375 0.09375 C 3.28125 0.09375 4.203125 -0.890625 4.203125 -2.171875 C 4.203125 -3.28125 3.515625 -4.125 2.625 -4.125 C 2.078125 -4.125 1.546875 -3.796875 1.375 -3.359375 Z M 1.375 -2.890625 C 1.375 -3.234375 1.796875 -3.5625 2.265625 -3.5625 C 2.953125 -3.5625 3.40625 -2.859375 3.40625 -1.765625 C 3.40625 -0.765625 2.984375 -0.203125 2.25 -0.203125 C 1.78125 -0.203125 1.375 -0.40625 1.375 -0.625 Z M 1.375 -2.890625 "/> +</symbol> +<symbol overflow="visible" id="glyph13-21"> +<path style="stroke:none;" d="M 4.265625 -4.03125 L 3.046875 -4.03125 L 3.046875 -3.90625 C 3.34375 -3.90625 3.484375 -3.828125 3.484375 -3.671875 C 3.484375 -3.640625 3.46875 -3.59375 3.4375 -3.53125 L 2.578125 -1.046875 L 1.546875 -3.3125 C 1.484375 -3.4375 1.453125 -3.5625 1.453125 -3.65625 C 1.453125 -3.828125 1.59375 -3.890625 1.96875 -3.90625 L 1.96875 -4.03125 L 0.125 -4.03125 L 0.125 -3.90625 C 0.359375 -3.875 0.515625 -3.78125 0.578125 -3.625 L 1.609375 -1.421875 L 1.625 -1.34375 L 1.765625 -1.078125 C 2.015625 -0.625 2.15625 -0.3125 2.15625 -0.171875 C 2.15625 -0.03125 1.953125 0.53125 1.796875 0.796875 C 1.671875 1.03125 1.484375 1.203125 1.359375 1.203125 C 1.296875 1.203125 1.21875 1.1875 1.125 1.140625 C 0.953125 1.078125 0.8125 1.046875 0.65625 1.046875 C 0.453125 1.046875 0.265625 1.21875 0.265625 1.4375 C 0.265625 1.734375 0.5625 1.953125 0.9375 1.953125 C 1.53125 1.953125 1.96875 1.453125 2.453125 0.15625 L 3.828125 -3.5 C 3.953125 -3.78125 4.046875 -3.875 4.265625 -3.90625 Z M 4.265625 -4.03125 "/> +</symbol> +<symbol overflow="visible" id="glyph13-22"> +<path style="stroke:none;" d="M 1.125 -0.890625 C 0.859375 -0.890625 0.625 -0.65625 0.625 -0.390625 C 0.625 -0.125 0.859375 0.09375 1.109375 0.09375 C 1.390625 0.09375 1.625 -0.125 1.625 -0.390625 C 1.625 -0.65625 1.390625 -0.890625 1.125 -0.890625 Z M 1.125 -0.890625 "/> +</symbol> +</g> +</defs> +<g id="surface1"> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph0-0" x="78.351778" y="126.69531"/> + <use xlink:href="#glyph0-1" x="88.870032" y="126.69531"/> + <use xlink:href="#glyph0-2" x="97.477442" y="126.69531"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph0-3" x="109.424526" y="126.69531"/> + <use xlink:href="#glyph0-4" x="124.7285" y="126.69531"/> + <use xlink:href="#glyph0-5" x="133.335909" y="126.69531"/> + <use xlink:href="#glyph0-6" x="138.121629" y="126.69531"/> + <use xlink:href="#glyph0-7" x="142.907349" y="126.69531"/> + <use xlink:href="#glyph0-8" x="147.693068" y="126.69531"/> + <use xlink:href="#glyph0-9" x="153.425603" y="126.69531"/> + <use xlink:href="#glyph0-10" x="162.997042" y="126.69531"/> + <use xlink:href="#glyph0-7" x="168.729577" y="126.69531"/> + <use xlink:href="#glyph0-11" x="173.515296" y="126.69531"/> + <use xlink:href="#glyph0-12" x="182.122706" y="126.69531"/> + <use xlink:href="#glyph0-7" x="189.766085" y="126.69531"/> + <use xlink:href="#glyph0-13" x="194.551805" y="126.69531"/> + <use xlink:href="#glyph0-14" x="203.159214" y="126.69531"/> + <use xlink:href="#glyph0-5" x="210.802594" y="126.69531"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph0-15" x="219.892018" y="126.69531"/> + <use xlink:href="#glyph0-16" x="232.321117" y="126.69531"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph0-17" x="246.196261" y="126.69531"/> + <use xlink:href="#glyph0-18" x="257.678545" y="126.69531"/> + <use xlink:href="#glyph0-11" x="266.285955" y="126.69531"/> + <use xlink:href="#glyph0-19" x="274.893364" y="126.69531"/> + <use xlink:href="#glyph0-6" x="281.589929" y="126.69531"/> + <use xlink:href="#glyph0-10" x="286.375648" y="126.69531"/> + <use xlink:href="#glyph0-4" x="292.108183" y="126.69531"/> + <use xlink:href="#glyph0-12" x="300.715592" y="126.69531"/> + <use xlink:href="#glyph0-6" x="308.358972" y="126.69531"/> + <use xlink:href="#glyph0-7" x="313.144692" y="126.69531"/> + <use xlink:href="#glyph0-18" x="317.930411" y="126.69531"/> + <use xlink:href="#glyph0-11" x="326.537821" y="126.69531"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph0-18" x="339.448935" y="126.69531"/> + <use xlink:href="#glyph0-20" x="348.056344" y="126.69531"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph0-6" x="358.092584" y="126.69531"/> + <use xlink:href="#glyph0-1" x="362.878303" y="126.69531"/> + <use xlink:href="#glyph0-2" x="371.485713" y="126.69531"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph0-21" x="383.432797" y="126.69531"/> + <use xlink:href="#glyph0-14" x="395.861896" y="126.69531"/> + <use xlink:href="#glyph0-22" x="403.505275" y="126.69531"/> + <use xlink:href="#glyph0-2" x="411.148655" y="126.69531"/> + <use xlink:href="#glyph0-5" x="418.792035" y="126.69531"/> + <use xlink:href="#glyph0-5" x="423.577754" y="126.69531"/> + <use xlink:href="#glyph0-2" x="428.363474" y="126.69531"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph0-23" x="440.310558" y="126.69531"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph0-2" x="455.201376" y="126.69531"/> + <use xlink:href="#glyph0-24" x="462.844756" y="126.69531"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph0-25" x="475.738655" y="126.69531"/> + <use xlink:href="#glyph0-10" x="487.220939" y="126.69531"/> + <use xlink:href="#glyph0-18" x="492.953474" y="126.69531"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph0-26" x="501.147728" y="126.69531"/> + <use xlink:href="#glyph0-19" x="513.576827" y="126.69531"/> + <use xlink:href="#glyph0-2" x="520.273391" y="126.69531"/> + <use xlink:href="#glyph0-10" x="527.916771" y="126.69531"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph1-0" x="77.981674" y="157.003794"/> + <use xlink:href="#glyph1-1" x="86.61199" y="157.003794"/> + <use xlink:href="#glyph1-2" x="91.919276" y="157.003794"/> + <use xlink:href="#glyph1-1" x="95.242306" y="157.003794"/> + <use xlink:href="#glyph1-3" x="100.549592" y="157.003794"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph1-4" x="109.502648" y="157.003794"/> + <use xlink:href="#glyph1-5" x="114.152499" y="157.003794"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph1-6" x="120.129172" y="157.003794"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph1-7" x="130.468817" y="157.003794"/> + <use xlink:href="#glyph1-3" x="135.776102" y="157.003794"/> + <use xlink:href="#glyph1-8" x="141.752775" y="157.003794"/> + <use xlink:href="#glyph1-9" x="147.729448" y="157.003794"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph1-10" x="153.694168" y="157.003794"/> + <use xlink:href="#glyph1-11" x="161.66705" y="157.003794"/> + <use xlink:href="#glyph1-12" x="167.643723" y="157.003794"/> + <use xlink:href="#glyph1-13" x="171.624187" y="157.003794"/> + <use xlink:href="#glyph1-14" x="174.947217" y="157.003794"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph1-15" x="182.585405" y="157.003794"/> + <use xlink:href="#glyph1-12" x="191.215721" y="157.003794"/> + <use xlink:href="#glyph1-13" x="195.196185" y="157.003794"/> + <use xlink:href="#glyph1-1" x="198.519215" y="157.003794"/> + <use xlink:href="#glyph1-12" x="203.826501" y="157.003794"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph1-9" x="207.340785" y="157.003794"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph1-16" x="213.305504" y="157.003794"/> + <use xlink:href="#glyph1-2" x="221.93582" y="157.003794"/> + <use xlink:href="#glyph1-1" x="225.25885" y="157.003794"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph1-17" x="230.398789" y="157.003794"/> + <use xlink:href="#glyph1-7" x="236.375462" y="157.003794"/> + <use xlink:href="#glyph1-3" x="241.682748" y="157.003794"/> + <use xlink:href="#glyph1-18" x="247.659421" y="157.003794"/> + <use xlink:href="#glyph1-1" x="253.636094" y="157.003794"/> + <use xlink:href="#glyph1-12" x="258.943379" y="157.003794"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph1-19" x="265.900227" y="157.003794"/> + <use xlink:href="#glyph1-20" x="276.526751" y="157.003794"/> + <use xlink:href="#glyph1-14" x="282.503424" y="157.003794"/> + <use xlink:href="#glyph1-11" x="287.153276" y="157.003794"/> + <use xlink:href="#glyph1-21" x="293.129949" y="157.003794"/> + <use xlink:href="#glyph1-11" x="298.437235" y="157.003794"/> + <use xlink:href="#glyph1-22" x="304.413908" y="157.003794"/> + <use xlink:href="#glyph1-23" x="310.390581" y="157.003794"/> + <use xlink:href="#glyph1-9" x="316.367254" y="157.003794"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph1-24" x="322.343927" y="157.003794"/> + <use xlink:href="#glyph1-7" x="328.989987" y="157.003794"/> + <use xlink:href="#glyph1-25" x="334.297273" y="157.003794"/> + <use xlink:href="#glyph1-22" x="343.596976" y="157.003794"/> + <use xlink:href="#glyph1-1" x="349.573649" y="157.003794"/> + <use xlink:href="#glyph1-2" x="354.880934" y="157.003794"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph1-26" x="361.192301" y="157.003794"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph1-5" x="367.623201" y="157.003794"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph1-27" x="373.587921" y="157.003794"/> + <use xlink:href="#glyph1-13" x="382.218237" y="157.003794"/> + <use xlink:href="#glyph1-3" x="385.541267" y="157.003794"/> + <use xlink:href="#glyph1-8" x="391.51794" y="157.003794"/> + <use xlink:href="#glyph1-9" x="397.494613" y="157.003794"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph1-28" x="403.471286" y="157.003794"/> + <use xlink:href="#glyph1-13" x="410.117346" y="157.003794"/> + <use xlink:href="#glyph1-7" x="413.440377" y="157.003794"/> + <use xlink:href="#glyph1-2" x="418.747662" y="157.003794"/> + <use xlink:href="#glyph1-13" x="422.070692" y="157.003794"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph1-10" x="428.382059" y="157.003794"/> + <use xlink:href="#glyph1-11" x="436.354941" y="157.003794"/> + <use xlink:href="#glyph1-20" x="442.331614" y="157.003794"/> + <use xlink:href="#glyph1-22" x="448.308287" y="157.003794"/> + <use xlink:href="#glyph1-18" x="454.28496" y="157.003794"/> + <use xlink:href="#glyph1-11" x="460.261633" y="157.003794"/> + <use xlink:href="#glyph1-22" x="466.238306" y="157.003794"/> + <use xlink:href="#glyph1-12" x="472.214979" y="157.003794"/> + <use xlink:href="#glyph1-29" x="476.195443" y="157.003794"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph1-9" x="481.407102" y="157.003794"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph1-0" x="487.371822" y="157.003794"/> + <use xlink:href="#glyph1-1" x="496.002137" y="157.003794"/> + <use xlink:href="#glyph1-12" x="501.309423" y="157.003794"/> + <use xlink:href="#glyph1-25" x="505.289887" y="157.003794"/> + <use xlink:href="#glyph1-7" x="514.58959" y="157.003794"/> + <use xlink:href="#glyph1-3" x="519.896876" y="157.003794"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph1-30" x="528.861886" y="157.003794"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph1-1" x="536.177333" y="157.003794"/> + <use xlink:href="#glyph1-3" x="541.484619" y="157.003794"/> + <use xlink:href="#glyph1-31" x="547.461292" y="157.003794"/> + <use xlink:href="#glyph1-1" x="550.784322" y="157.003794"/> + <use xlink:href="#glyph1-12" x="556.091608" y="157.003794"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph1-19" x="100.367941" y="170.9577"/> + <use xlink:href="#glyph1-13" x="110.994465" y="170.9577"/> + <use xlink:href="#glyph1-21" x="114.317496" y="170.9577"/> + <use xlink:href="#glyph1-12" x="119.624781" y="170.9577"/> + <use xlink:href="#glyph1-20" x="123.605245" y="170.9577"/> + <use xlink:href="#glyph1-14" x="129.581918" y="170.9577"/> + <use xlink:href="#glyph1-20" x="134.23177" y="170.9577"/> + <use xlink:href="#glyph1-32" x="140.208443" y="170.9577"/> + <use xlink:href="#glyph1-31" x="144.188907" y="170.9577"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph1-33" x="150.500274" y="170.9577"/> + <use xlink:href="#glyph1-1" x="158.473156" y="170.9577"/> + <use xlink:href="#glyph1-14" x="163.780441" y="170.9577"/> + <use xlink:href="#glyph1-1" x="168.430293" y="170.9577"/> + <use xlink:href="#glyph1-7" x="173.737579" y="170.9577"/> + <use xlink:href="#glyph1-12" x="179.044864" y="170.9577"/> + <use xlink:href="#glyph1-21" x="183.025328" y="170.9577"/> + <use xlink:href="#glyph1-11" x="188.332614" y="170.9577"/> + <use xlink:href="#glyph1-9" x="194.309287" y="170.9577"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph1-34" x="200.28596" y="170.9577"/> + <use xlink:href="#glyph1-3" x="208.916276" y="170.9577"/> + <use xlink:href="#glyph1-13" x="214.892949" y="170.9577"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph1-35" x="217.929099" y="170.9577"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph1-1" x="223.726472" y="170.9577"/> + <use xlink:href="#glyph1-12" x="229.033757" y="170.9577"/> + <use xlink:href="#glyph1-14" x="233.014221" y="170.9577"/> + <use xlink:href="#glyph1-13" x="237.664073" y="170.9577"/> + <use xlink:href="#glyph1-31" x="240.987103" y="170.9577"/> + <use xlink:href="#glyph1-29" x="244.310133" y="170.9577"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph1-20" x="253.263189" y="170.9577"/> + <use xlink:href="#glyph1-32" x="259.239862" y="170.9577"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph1-36" x="266.208663" y="170.9577"/> + <use xlink:href="#glyph1-2" x="270.189127" y="170.9577"/> + <use xlink:href="#glyph1-2" x="273.512158" y="170.9577"/> + <use xlink:href="#glyph1-13" x="276.835188" y="170.9577"/> + <use xlink:href="#glyph1-3" x="280.158218" y="170.9577"/> + <use xlink:href="#glyph1-20" x="286.134891" y="170.9577"/> + <use xlink:href="#glyph1-13" x="292.111564" y="170.9577"/> + <use xlink:href="#glyph1-14" x="295.434594" y="170.9577"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph1-7" x="303.072782" y="170.9577"/> + <use xlink:href="#glyph1-31" x="308.380068" y="170.9577"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph1-34" x="314.691435" y="170.9577"/> + <use xlink:href="#glyph1-12" x="323.32175" y="170.9577"/> + <use xlink:href="#glyph1-37" x="327.302215" y="170.9577"/> + <use xlink:href="#glyph1-7" x="333.278888" y="170.9577"/> + <use xlink:href="#glyph1-3" x="338.586173" y="170.9577"/> + <use xlink:href="#glyph1-7" x="344.562846" y="170.9577"/> + <use xlink:href="#glyph1-38" x="349.870132" y="170.9577"/> + <use xlink:href="#glyph1-10" x="353.850596" y="170.9577"/> + <use xlink:href="#glyph1-11" x="361.823478" y="170.9577"/> + <use xlink:href="#glyph1-7" x="367.800151" y="170.9577"/> + <use xlink:href="#glyph1-25" x="373.107436" y="170.9577"/> + <use xlink:href="#glyph1-39" x="382.40714" y="170.9577"/> + <use xlink:href="#glyph1-7" x="388.383813" y="170.9577"/> + <use xlink:href="#glyph1-13" x="393.691098" y="170.9577"/> + <use xlink:href="#glyph1-8" x="397.014128" y="170.9577"/> + <use xlink:href="#glyph1-3" x="402.990801" y="170.9577"/> + <use xlink:href="#glyph1-9" x="408.967474" y="170.9577"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph1-34" x="414.944147" y="170.9577"/> + <use xlink:href="#glyph1-3" x="423.574463" y="170.9577"/> + <use xlink:href="#glyph1-13" x="429.551136" y="170.9577"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph1-35" x="432.587286" y="170.9577"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph1-1" x="438.384659" y="170.9577"/> + <use xlink:href="#glyph1-12" x="443.691945" y="170.9577"/> + <use xlink:href="#glyph1-14" x="447.672409" y="170.9577"/> + <use xlink:href="#glyph1-13" x="452.32226" y="170.9577"/> + <use xlink:href="#glyph1-31" x="455.645291" y="170.9577"/> + <use xlink:href="#glyph1-29" x="458.968321" y="170.9577"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph1-20" x="467.921377" y="170.9577"/> + <use xlink:href="#glyph1-32" x="473.89805" y="170.9577"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph1-6" x="480.866851" y="170.9577"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph1-7" x="491.206495" y="170.9577"/> + <use xlink:href="#glyph1-14" x="496.513781" y="170.9577"/> + <use xlink:href="#glyph1-11" x="501.163632" y="170.9577"/> + <use xlink:href="#glyph1-13" x="507.140305" y="170.9577"/> + <use xlink:href="#glyph1-3" x="510.463335" y="170.9577"/> + <use xlink:href="#glyph1-8" x="516.440008" y="170.9577"/> + <use xlink:href="#glyph1-31" x="522.416681" y="170.9577"/> + <use xlink:href="#glyph1-20" x="525.739712" y="170.9577"/> + <use xlink:href="#glyph1-3" x="531.716385" y="170.9577"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph2-0" x="89.634936" y="184.901604"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph1-11" x="95.606608" y="184.901604"/> + <use xlink:href="#glyph1-1" x="101.583281" y="184.901604"/> + <use xlink:href="#glyph1-2" x="106.890567" y="184.901604"/> + <use xlink:href="#glyph1-1" x="110.213597" y="184.901604"/> + <use xlink:href="#glyph1-3" x="115.520882" y="184.901604"/> + <use xlink:href="#glyph1-40" x="121.497555" y="184.901604"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph1-9" x="129.362857" y="184.901604"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph1-39" x="135.327577" y="184.901604"/> + <use xlink:href="#glyph1-13" x="141.30425" y="184.901604"/> + <use xlink:href="#glyph1-7" x="144.62728" y="184.901604"/> + <use xlink:href="#glyph1-2" x="149.934566" y="184.901604"/> + <use xlink:href="#glyph1-13" x="153.257596" y="184.901604"/> + <use xlink:href="#glyph1-21" x="156.580626" y="184.901604"/> + <use xlink:href="#glyph1-9" x="161.887912" y="184.901604"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph1-11" x="167.864585" y="184.901604"/> + <use xlink:href="#glyph1-1" x="173.841258" y="184.901604"/> + <use xlink:href="#glyph1-12" x="179.148543" y="184.901604"/> + <use xlink:href="#glyph1-25" x="183.129007" y="184.901604"/> + <use xlink:href="#glyph1-7" x="192.428711" y="184.901604"/> + <use xlink:href="#glyph1-3" x="197.735996" y="184.901604"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph1-35" x="203.246489" y="184.901604"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph2-1" x="209.228413" y="184.901604"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph1-41" x="215.210088" y="184.901604"/> + <use xlink:href="#glyph1-25" x="226.219119" y="184.901604"/> + <use xlink:href="#glyph1-13" x="235.518822" y="184.901604"/> + <use xlink:href="#glyph1-21" x="238.841853" y="184.901604"/> + <use xlink:href="#glyph1-12" x="244.149138" y="184.901604"/> + <use xlink:href="#glyph1-20" x="248.129602" y="184.901604"/> + <use xlink:href="#glyph1-14" x="254.106275" y="184.901604"/> + <use xlink:href="#glyph1-20" x="258.756127" y="184.901604"/> + <use xlink:href="#glyph1-32" x="264.7328" y="184.901604"/> + <use xlink:href="#glyph1-31" x="268.713264" y="184.901604"/> + <use xlink:href="#glyph1-5" x="272.036294" y="184.901604"/> + <use xlink:href="#glyph1-21" x="275.024631" y="184.901604"/> + <use xlink:href="#glyph1-20" x="280.331917" y="184.901604"/> + <use xlink:href="#glyph1-25" x="286.30859" y="184.901604"/> + <use xlink:href="#glyph1-9" x="295.608293" y="184.901604"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph2-0" x="301.594268" y="184.901604"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph1-8" x="307.575943" y="184.901604"/> + <use xlink:href="#glyph1-12" x="313.552616" y="184.901604"/> + <use xlink:href="#glyph1-13" x="317.53308" y="184.901604"/> + <use xlink:href="#glyph1-1" x="320.85611" y="184.901604"/> + <use xlink:href="#glyph1-12" x="326.163396" y="184.901604"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph1-9" x="329.665726" y="184.901604"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph1-23" x="335.630446" y="184.901604"/> + <use xlink:href="#glyph1-13" x="341.607119" y="184.901604"/> + <use xlink:href="#glyph1-3" x="344.930149" y="184.901604"/> + <use xlink:href="#glyph1-8" x="350.906822" y="184.901604"/> + <use xlink:href="#glyph1-14" x="356.883495" y="184.901604"/> + <use xlink:href="#glyph1-31" x="361.533347" y="184.901604"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph2-1" x="364.871981" y="184.901604"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph1-41" x="370.853656" y="184.901604"/> + <use xlink:href="#glyph1-22" x="381.862687" y="184.901604"/> + <use xlink:href="#glyph1-13" x="387.83936" y="184.901604"/> + <use xlink:href="#glyph1-22" x="391.16239" y="184.901604"/> + <use xlink:href="#glyph1-21" x="397.139063" y="184.901604"/> + <use xlink:href="#glyph1-5" x="402.446349" y="184.901604"/> + <use xlink:href="#glyph1-1" x="405.434686" y="184.901604"/> + <use xlink:href="#glyph1-18" x="410.741971" y="184.901604"/> + <use xlink:href="#glyph1-22" x="416.718644" y="184.901604"/> + <use xlink:href="#glyph1-9" x="422.695317" y="184.901604"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph1-7" x="428.67199" y="184.901604"/> + <use xlink:href="#glyph1-3" x="433.979276" y="184.901604"/> + <use xlink:href="#glyph1-25" x="439.955949" y="184.901604"/> + <use xlink:href="#glyph1-41" x="449.255652" y="184.901604"/> + <use xlink:href="#glyph1-21" x="460.264684" y="184.901604"/> + <use xlink:href="#glyph1-14" x="465.571969" y="184.901604"/> + <use xlink:href="#glyph1-5" x="470.221821" y="184.901604"/> + <use xlink:href="#glyph1-40" x="473.210157" y="184.901604"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph1-7" x="481.732893" y="184.901604"/> + <use xlink:href="#glyph1-14" x="487.040179" y="184.901604"/> + <use xlink:href="#glyph1-11" x="491.69003" y="184.901604"/> + <use xlink:href="#glyph1-13" x="497.666703" y="184.901604"/> + <use xlink:href="#glyph1-3" x="500.989734" y="184.901604"/> + <use xlink:href="#glyph1-8" x="506.966407" y="184.901604"/> + <use xlink:href="#glyph1-31" x="512.94308" y="184.901604"/> + <use xlink:href="#glyph1-20" x="516.26611" y="184.901604"/> + <use xlink:href="#glyph1-3" x="522.242783" y="184.901604"/> + <use xlink:href="#glyph1-5" x="528.219456" y="184.901604"/> + <use xlink:href="#glyph1-1" x="531.207792" y="184.901604"/> + <use xlink:href="#glyph1-18" x="536.515078" y="184.901604"/> + <use xlink:href="#glyph1-22" x="542.491751" y="184.901604"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph1-19" x="218.36097" y="212.799413"/> + <use xlink:href="#glyph1-24" x="228.987494" y="212.799413"/> + <use xlink:href="#glyph1-33" x="235.633555" y="212.799413"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph1-26" x="246.58282" y="212.799413"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph1-1" x="253.061533" y="212.799413"/> + <use xlink:href="#glyph1-21" x="258.368819" y="212.799413"/> + <use xlink:href="#glyph1-11" x="263.676104" y="212.799413"/> + <use xlink:href="#glyph1-3" x="269.652777" y="212.799413"/> + <use xlink:href="#glyph1-13" x="275.62945" y="212.799413"/> + <use xlink:href="#glyph1-21" x="278.95248" y="212.799413"/> + <use xlink:href="#glyph1-7" x="284.259766" y="212.799413"/> + <use xlink:href="#glyph1-2" x="289.567052" y="212.799413"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph1-33" x="295.866465" y="212.799413"/> + <use xlink:href="#glyph1-1" x="303.839347" y="212.799413"/> + <use xlink:href="#glyph1-39" x="309.146632" y="212.799413"/> + <use xlink:href="#glyph1-20" x="315.123305" y="212.799413"/> + <use xlink:href="#glyph1-12" x="321.099978" y="212.799413"/> + <use xlink:href="#glyph1-31" x="325.080443" y="212.799413"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph1-19" x="331.391809" y="212.799413"/> + <use xlink:href="#glyph1-24" x="342.018334" y="212.799413"/> + <use xlink:href="#glyph1-33" x="348.664394" y="212.799413"/> + <use xlink:href="#glyph1-38" x="356.637276" y="212.799413"/> + <use xlink:href="#glyph1-26" x="360.61774" y="212.799413"/> + <use xlink:href="#glyph1-33" x="367.921235" y="212.799413"/> + <use xlink:href="#glyph1-38" x="375.894117" y="212.799413"/> + <use xlink:href="#glyph1-42" x="379.874581" y="212.799413"/> + <use xlink:href="#glyph1-43" x="385.851254" y="212.799413"/> + <use xlink:href="#glyph1-43" x="391.827927" y="212.799413"/> + <use xlink:href="#glyph1-44" x="397.8046" y="212.799413"/> + <use xlink:href="#glyph1-38" x="403.781273" y="212.799413"/> + <use xlink:href="#glyph1-45" x="407.761737" y="212.799413"/> + <use xlink:href="#glyph1-46" x="413.73841" y="212.799413"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph3-0" x="287.460312" y="253.160711"/> + <use xlink:href="#glyph3-1" x="294.653446" y="253.160711"/> + <use xlink:href="#glyph3-2" x="300.192756" y="253.160711"/> + <use xlink:href="#glyph3-3" x="304.068281" y="253.160711"/> + <use xlink:href="#glyph3-4" x="307.38589" y="253.160711"/> + <use xlink:href="#glyph3-5" x="311.809368" y="253.160711"/> + <use xlink:href="#glyph3-6" x="316.790762" y="253.160711"/> + <use xlink:href="#glyph3-3" x="321.21424" y="253.160711"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph4-0" x="114.221819" y="272.086008"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph4-1" x="122.829668" y="272.086008"/> + <use xlink:href="#glyph4-2" x="127.253147" y="272.086008"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph4-2" x="135.30308" y="272.086008"/> + <use xlink:href="#glyph4-3" x="140.284474" y="272.086008"/> + <use xlink:href="#glyph4-4" x="143.602083" y="272.086008"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph4-5" x="148.34437" y="272.086008"/> + <use xlink:href="#glyph4-6" x="155.537504" y="272.086008"/> + <use xlink:href="#glyph4-1" x="159.413029" y="272.086008"/> + <use xlink:href="#glyph4-3" x="163.836507" y="272.086008"/> + <use xlink:href="#glyph4-6" x="167.154116" y="272.086008"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph4-4" x="174.098179" y="272.086008"/> + <use xlink:href="#glyph4-3" x="179.079574" y="272.086008"/> + <use xlink:href="#glyph4-7" x="182.397183" y="272.086008"/> + <use xlink:href="#glyph4-8" x="185.166838" y="272.086008"/> + <use xlink:href="#glyph4-7" x="190.148232" y="272.086008"/> + <use xlink:href="#glyph4-9" x="192.917888" y="272.086008"/> + <use xlink:href="#glyph4-10" x="197.899282" y="272.086008"/> + <use xlink:href="#glyph4-11" x="202.32276" y="272.086008"/> + <use xlink:href="#glyph4-1" x="205.092415" y="272.086008"/> + <use xlink:href="#glyph4-12" x="209.515894" y="272.086008"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph4-10" x="217.585753" y="272.086008"/> + <use xlink:href="#glyph4-6" x="222.009231" y="272.086008"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph4-10" x="228.963257" y="272.086008"/> + <use xlink:href="#glyph4-13" x="233.386736" y="272.086008"/> + <use xlink:href="#glyph4-13" x="238.36813" y="272.086008"/> + <use xlink:href="#glyph4-14" x="243.349524" y="272.086008"/> + <use xlink:href="#glyph4-7" x="246.11918" y="272.086008"/> + <use xlink:href="#glyph4-15" x="248.888835" y="272.086008"/> + <use xlink:href="#glyph4-10" x="253.312313" y="272.086008"/> + <use xlink:href="#glyph4-11" x="257.735791" y="272.086008"/> + <use xlink:href="#glyph4-7" x="260.505447" y="272.086008"/> + <use xlink:href="#glyph4-4" x="263.275102" y="272.086008"/> + <use xlink:href="#glyph4-9" x="268.256496" y="272.086008"/> + <use xlink:href="#glyph4-6" x="273.237891" y="272.086008"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph4-11" x="280.191917" y="272.086008"/> + <use xlink:href="#glyph4-16" x="282.961573" y="272.086008"/> + <use xlink:href="#glyph4-10" x="287.942967" y="272.086008"/> + <use xlink:href="#glyph4-11" x="292.366445" y="272.086008"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph4-13" x="298.214602" y="272.086008"/> + <use xlink:href="#glyph4-1" x="303.195997" y="272.086008"/> + <use xlink:href="#glyph4-4" x="307.619475" y="272.086008"/> + <use xlink:href="#glyph4-13" x="312.600869" y="272.086008"/> + <use xlink:href="#glyph4-14" x="317.582264" y="272.086008"/> + <use xlink:href="#glyph4-1" x="320.351919" y="272.086008"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph4-17" x="327.853899" y="272.086008"/> + <use xlink:href="#glyph4-6" x="332.835293" y="272.086008"/> + <use xlink:href="#glyph4-1" x="336.710818" y="272.086008"/> + <use xlink:href="#glyph4-12" x="341.134297" y="272.086008"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph4-11" x="349.194193" y="272.086008"/> + <use xlink:href="#glyph4-4" x="351.963848" y="272.086008"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph4-18" x="360.033707" y="272.086008"/> + <use xlink:href="#glyph4-7" x="365.015101" y="272.086008"/> + <use xlink:href="#glyph4-1" x="367.784757" y="272.086008"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph4-5" x="371.969128" y="272.086008"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph4-6" x="382.2308" y="272.086008"/> + <use xlink:href="#glyph4-11" x="386.106325" y="272.086008"/> + <use xlink:href="#glyph4-10" x="388.87598" y="272.086008"/> + <use xlink:href="#glyph4-11" x="393.299459" y="272.086008"/> + <use xlink:href="#glyph4-7" x="396.069114" y="272.086008"/> + <use xlink:href="#glyph4-15" x="398.838769" y="272.086008"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph4-5" x="406.340749" y="272.086008"/> + <use xlink:href="#glyph4-1" x="413.533883" y="272.086008"/> + <use xlink:href="#glyph4-2" x="417.957361" y="272.086008"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph4-6" x="426.017257" y="272.086008"/> + <use xlink:href="#glyph4-7" x="429.892782" y="272.086008"/> + <use xlink:href="#glyph4-11" x="432.662437" y="272.086008"/> + <use xlink:href="#glyph4-1" x="435.432093" y="272.086008"/> + <use xlink:href="#glyph4-6" x="439.855571" y="272.086008"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph4-6" x="446.809597" y="272.086008"/> + <use xlink:href="#glyph4-1" x="450.685122" y="272.086008"/> + <use xlink:href="#glyph4-19" x="455.1086" y="272.086008"/> + <use xlink:href="#glyph4-17" x="460.089995" y="272.086008"/> + <use xlink:href="#glyph4-1" x="465.071389" y="272.086008"/> + <use xlink:href="#glyph4-9" x="469.494867" y="272.086008"/> + <use xlink:href="#glyph4-11" x="474.476262" y="272.086008"/> + <use xlink:href="#glyph4-7" x="477.245917" y="272.086008"/> + <use xlink:href="#glyph4-10" x="480.015572" y="272.086008"/> + <use xlink:href="#glyph4-14" x="484.439051" y="272.086008"/> + <use xlink:href="#glyph4-14" x="487.208706" y="272.086008"/> + <use xlink:href="#glyph4-20" x="489.978361" y="272.086008"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph4-21" x="494.322137" y="272.086008"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph4-22" x="501.664712" y="272.086008"/> + <use xlink:href="#glyph4-6" x="508.857846" y="272.086008"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph4-5" x="99.277636" y="284.049357"/> + <use xlink:href="#glyph4-1" x="106.470769" y="284.049357"/> + <use xlink:href="#glyph4-2" x="110.894247" y="284.049357"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph4-6" x="118.90433" y="284.049357"/> + <use xlink:href="#glyph4-7" x="122.779854" y="284.049357"/> + <use xlink:href="#glyph4-11" x="125.54951" y="284.049357"/> + <use xlink:href="#glyph4-1" x="128.319165" y="284.049357"/> + <use xlink:href="#glyph4-6" x="132.742643" y="284.049357"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph4-1" x="139.656819" y="284.049357"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph4-18" x="143.84119" y="284.049357"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph4-4" x="148.623329" y="284.049357"/> + <use xlink:href="#glyph4-14" x="153.604723" y="284.049357"/> + <use xlink:href="#glyph4-18" x="156.374378" y="284.049357"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph4-1" x="161.206331" y="284.049357"/> + <use xlink:href="#glyph4-12" x="165.629809" y="284.049357"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph4-7" x="173.639891" y="284.049357"/> + <use xlink:href="#glyph4-9" x="176.409546" y="284.049357"/> + <use xlink:href="#glyph4-11" x="181.390941" y="284.049357"/> + <use xlink:href="#glyph4-4" x="184.160596" y="284.049357"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph4-12" x="192.170678" y="284.049357"/> + <use xlink:href="#glyph4-20" x="197.152073" y="284.049357"/> + <use xlink:href="#glyph4-9" x="202.133467" y="284.049357"/> + <use xlink:href="#glyph4-10" x="207.114862" y="284.049357"/> + <use xlink:href="#glyph4-23" x="211.53834" y="284.049357"/> + <use xlink:href="#glyph4-7" x="219.289389" y="284.049357"/> + <use xlink:href="#glyph4-15" x="222.059045" y="284.049357"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph4-5" x="229.521174" y="284.049357"/> + <use xlink:href="#glyph4-1" x="236.714307" y="284.049357"/> + <use xlink:href="#glyph4-2" x="241.137785" y="284.049357"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph4-10" x="249.15783" y="284.049357"/> + <use xlink:href="#glyph4-13" x="253.581309" y="284.049357"/> + <use xlink:href="#glyph4-13" x="258.562703" y="284.049357"/> + <use xlink:href="#glyph4-14" x="263.544097" y="284.049357"/> + <use xlink:href="#glyph4-7" x="266.313753" y="284.049357"/> + <use xlink:href="#glyph4-15" x="269.083408" y="284.049357"/> + <use xlink:href="#glyph4-10" x="273.506886" y="284.049357"/> + <use xlink:href="#glyph4-11" x="277.930364" y="284.049357"/> + <use xlink:href="#glyph4-7" x="280.70002" y="284.049357"/> + <use xlink:href="#glyph4-4" x="283.469675" y="284.049357"/> + <use xlink:href="#glyph4-9" x="288.451069" y="284.049357"/> + <use xlink:href="#glyph4-6" x="293.432464" y="284.049357"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph4-15" x="300.346639" y="284.049357"/> + <use xlink:href="#glyph4-4" x="304.770117" y="284.049357"/> + <use xlink:href="#glyph4-23" x="309.751512" y="284.049357"/> + <use xlink:href="#glyph4-13" x="317.502561" y="284.049357"/> + <use xlink:href="#glyph4-4" x="322.483956" y="284.049357"/> + <use xlink:href="#glyph4-6" x="327.46535" y="284.049357"/> + <use xlink:href="#glyph4-7" x="331.340875" y="284.049357"/> + <use xlink:href="#glyph4-9" x="334.11053" y="284.049357"/> + <use xlink:href="#glyph4-8" x="339.091925" y="284.049357"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph4-15" x="347.11197" y="284.049357"/> + <use xlink:href="#glyph4-4" x="351.535448" y="284.049357"/> + <use xlink:href="#glyph4-9" x="356.516842" y="284.049357"/> + <use xlink:href="#glyph4-11" x="361.498237" y="284.049357"/> + <use xlink:href="#glyph4-1" x="364.267892" y="284.049357"/> + <use xlink:href="#glyph4-9" x="368.69137" y="284.049357"/> + <use xlink:href="#glyph4-11" x="373.672765" y="284.049357"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph4-24" x="379.481071" y="284.049357"/> + <use xlink:href="#glyph4-3" x="382.798679" y="284.049357"/> + <use xlink:href="#glyph4-4" x="386.116288" y="284.049357"/> + <use xlink:href="#glyph4-23" x="391.097682" y="284.049357"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph4-18" x="401.887383" y="284.049357"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph4-10" x="406.62967" y="284.049357"/> + <use xlink:href="#glyph4-3" x="411.053148" y="284.049357"/> + <use xlink:href="#glyph4-7" x="414.370757" y="284.049357"/> + <use xlink:href="#glyph4-4" x="417.140412" y="284.049357"/> + <use xlink:href="#glyph4-17" x="422.121807" y="284.049357"/> + <use xlink:href="#glyph4-6" x="427.103201" y="284.049357"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph4-5" x="434.007414" y="284.049357"/> + <use xlink:href="#glyph4-1" x="441.200547" y="284.049357"/> + <use xlink:href="#glyph4-2" x="445.624025" y="284.049357"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph4-6" x="453.634108" y="284.049357"/> + <use xlink:href="#glyph4-7" x="457.509632" y="284.049357"/> + <use xlink:href="#glyph4-11" x="460.279288" y="284.049357"/> + <use xlink:href="#glyph4-1" x="463.048943" y="284.049357"/> + <use xlink:href="#glyph4-6" x="467.472421" y="284.049357"/> + <use xlink:href="#glyph4-25" x="471.347946" y="284.049357"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph4-2" x="477.016773" y="284.049357"/> + <use xlink:href="#glyph4-3" x="481.998167" y="284.049357"/> + <use xlink:href="#glyph4-4" x="485.315776" y="284.049357"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph4-5" x="490.058063" y="284.049357"/> + <use xlink:href="#glyph4-6" x="497.251197" y="284.049357"/> + <use xlink:href="#glyph4-1" x="501.126722" y="284.049357"/> + <use xlink:href="#glyph4-3" x="505.5502" y="284.049357"/> + <use xlink:href="#glyph4-6" x="508.867809" y="284.049357"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph4-16" x="99.277636" y="296.002703"/> + <use xlink:href="#glyph4-10" x="104.25903" y="296.002703"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph4-18" x="108.483252" y="296.002703"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph4-1" x="113.315205" y="296.002703"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph4-2" x="120.408711" y="296.002703"/> + <use xlink:href="#glyph4-1" x="125.390105" y="296.002703"/> + <use xlink:href="#glyph4-15" x="129.813583" y="296.002703"/> + <use xlink:href="#glyph4-4" x="134.237061" y="296.002703"/> + <use xlink:href="#glyph4-23" x="139.218456" y="296.002703"/> + <use xlink:href="#glyph4-1" x="146.969506" y="296.002703"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph4-23" x="154.072974" y="296.002703"/> + <use xlink:href="#glyph4-17" x="161.824024" y="296.002703"/> + <use xlink:href="#glyph4-14" x="166.805418" y="296.002703"/> + <use xlink:href="#glyph4-11" x="169.575073" y="296.002703"/> + <use xlink:href="#glyph4-7" x="172.344729" y="296.002703"/> + <use xlink:href="#glyph4-26" x="175.114384" y="296.002703"/> + <use xlink:href="#glyph4-13" x="178.431993" y="296.002703"/> + <use xlink:href="#glyph4-3" x="183.413387" y="296.002703"/> + <use xlink:href="#glyph4-7" x="186.730996" y="296.002703"/> + <use xlink:href="#glyph4-9" x="189.500651" y="296.002703"/> + <use xlink:href="#glyph4-15" x="194.482045" y="296.002703"/> + <use xlink:href="#glyph4-7" x="198.905524" y="296.002703"/> + <use xlink:href="#glyph4-13" x="201.675179" y="296.002703"/> + <use xlink:href="#glyph4-10" x="206.656573" y="296.002703"/> + <use xlink:href="#glyph4-14" x="211.080052" y="296.002703"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph4-4" x="216.53966" y="296.002703"/> + <use xlink:href="#glyph4-13" x="221.521054" y="296.002703"/> + <use xlink:href="#glyph4-1" x="226.502449" y="296.002703"/> + <use xlink:href="#glyph4-3" x="230.925927" y="296.002703"/> + <use xlink:href="#glyph4-10" x="234.243535" y="296.002703"/> + <use xlink:href="#glyph4-11" x="238.667014" y="296.002703"/> + <use xlink:href="#glyph4-7" x="241.436669" y="296.002703"/> + <use xlink:href="#glyph4-9" x="244.206324" y="296.002703"/> + <use xlink:href="#glyph4-8" x="249.187719" y="296.002703"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph4-1" x="256.849103" y="296.002703"/> + <use xlink:href="#glyph4-9" x="261.272581" y="296.002703"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph4-18" x="265.865427" y="296.002703"/> + <use xlink:href="#glyph4-7" x="270.846822" y="296.002703"/> + <use xlink:href="#glyph4-3" x="273.616477" y="296.002703"/> + <use xlink:href="#glyph4-4" x="276.934085" y="296.002703"/> + <use xlink:href="#glyph4-9" x="281.91548" y="296.002703"/> + <use xlink:href="#glyph4-23" x="286.896874" y="296.002703"/> + <use xlink:href="#glyph4-1" x="294.647924" y="296.002703"/> + <use xlink:href="#glyph4-9" x="299.071402" y="296.002703"/> + <use xlink:href="#glyph4-11" x="304.052797" y="296.002703"/> + <use xlink:href="#glyph4-6" x="306.822452" y="296.002703"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph4-5" x="313.368004" y="296.002703"/> + <use xlink:href="#glyph4-7" x="320.561138" y="296.002703"/> + <use xlink:href="#glyph4-11" x="323.330793" y="296.002703"/> + <use xlink:href="#glyph4-16" x="326.100448" y="296.002703"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph4-3" x="333.761833" y="296.002703"/> + <use xlink:href="#glyph4-1" x="337.079441" y="296.002703"/> + <use xlink:href="#glyph4-6" x="341.50292" y="296.002703"/> + <use xlink:href="#glyph4-4" x="345.378445" y="296.002703"/> + <use xlink:href="#glyph4-17" x="350.359839" y="296.002703"/> + <use xlink:href="#glyph4-3" x="355.341233" y="296.002703"/> + <use xlink:href="#glyph4-15" x="358.658842" y="296.002703"/> + <use xlink:href="#glyph4-1" x="363.08232" y="296.002703"/> + <use xlink:href="#glyph4-6" x="367.505798" y="296.002703"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph4-6" x="374.061313" y="296.002703"/> + <use xlink:href="#glyph4-16" x="377.936838" y="296.002703"/> + <use xlink:href="#glyph4-10" x="382.918233" y="296.002703"/> + <use xlink:href="#glyph4-3" x="387.341711" y="296.002703"/> + <use xlink:href="#glyph4-1" x="390.65932" y="296.002703"/> + <use xlink:href="#glyph4-12" x="395.082798" y="296.002703"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph4-10" x="402.754145" y="296.002703"/> + <use xlink:href="#glyph4-23" x="407.177623" y="296.002703"/> + <use xlink:href="#glyph4-4" x="414.928673" y="296.002703"/> + <use xlink:href="#glyph4-9" x="419.910068" y="296.002703"/> + <use xlink:href="#glyph4-8" x="424.891462" y="296.002703"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph4-23" x="432.552847" y="296.002703"/> + <use xlink:href="#glyph4-17" x="440.303896" y="296.002703"/> + <use xlink:href="#glyph4-11" x="445.285291" y="296.002703"/> + <use xlink:href="#glyph4-17" x="448.054946" y="296.002703"/> + <use xlink:href="#glyph4-10" x="453.03634" y="296.002703"/> + <use xlink:href="#glyph4-14" x="457.459819" y="296.002703"/> + <use xlink:href="#glyph4-14" x="460.229474" y="296.002703"/> + <use xlink:href="#glyph4-20" x="462.999129" y="296.002703"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph4-12" x="470.660514" y="296.002703"/> + <use xlink:href="#glyph4-7" x="475.641908" y="296.002703"/> + <use xlink:href="#glyph4-6" x="478.411563" y="296.002703"/> + <use xlink:href="#glyph4-11" x="482.287088" y="296.002703"/> + <use xlink:href="#glyph4-3" x="485.056744" y="296.002703"/> + <use xlink:href="#glyph4-17" x="488.374352" y="296.002703"/> + <use xlink:href="#glyph4-6" x="493.355747" y="296.002703"/> + <use xlink:href="#glyph4-11" x="497.231271" y="296.002703"/> + <use xlink:href="#glyph4-7" x="500.000927" y="296.002703"/> + <use xlink:href="#glyph4-9" x="502.770582" y="296.002703"/> + <use xlink:href="#glyph4-8" x="507.751976" y="296.002703"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph4-5" x="99.277636" y="307.956049"/> + <use xlink:href="#glyph4-1" x="106.470769" y="307.956049"/> + <use xlink:href="#glyph4-2" x="110.894247" y="307.956049"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph4-6" x="118.874441" y="307.956049"/> + <use xlink:href="#glyph4-7" x="122.749966" y="307.956049"/> + <use xlink:href="#glyph4-11" x="125.519621" y="307.956049"/> + <use xlink:href="#glyph4-1" x="128.289277" y="307.956049"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph5-0" x="135.717836" y="307.956049"/> + <use xlink:href="#glyph5-1" x="140.69923" y="307.956049"/> + <use xlink:href="#glyph5-2" x="144.574755" y="307.956049"/> + <use xlink:href="#glyph5-3" x="147.344411" y="307.956049"/> + <use xlink:href="#glyph5-4" x="152.325805" y="307.956049"/> + <use xlink:href="#glyph5-2" x="156.749283" y="307.956049"/> + <use xlink:href="#glyph5-0" x="159.518938" y="307.956049"/> + <use xlink:href="#glyph5-5" x="164.500333" y="307.956049"/> + <use xlink:href="#glyph5-6" x="169.481727" y="307.956049"/> + <use xlink:href="#glyph5-7" x="172.251383" y="307.956049"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph4-21" x="176.129148" y="307.956049"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph4-27" x="183.242579" y="307.956049"/> + <use xlink:href="#glyph4-1" x="190.435713" y="307.956049"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph4-18" x="194.620084" y="307.956049"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph4-1" x="199.452037" y="307.956049"/> + <use xlink:href="#glyph4-3" x="203.875515" y="307.956049"/> + <use xlink:href="#glyph4-11" x="207.193123" y="307.956049"/> + <use xlink:href="#glyph4-16" x="209.962779" y="307.956049"/> + <use xlink:href="#glyph4-1" x="214.944173" y="307.956049"/> + <use xlink:href="#glyph4-14" x="219.367651" y="307.956049"/> + <use xlink:href="#glyph4-1" x="222.137307" y="307.956049"/> + <use xlink:href="#glyph4-6" x="226.560785" y="307.956049"/> + <use xlink:href="#glyph4-6" x="230.43631" y="307.956049"/> + <use xlink:href="#glyph4-25" x="234.311835" y="307.956049"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph4-9" x="239.920885" y="307.956049"/> + <use xlink:href="#glyph4-4" x="244.902279" y="307.956049"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph4-1" x="252.892436" y="307.956049"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph4-28" x="257.176435" y="307.956049"/> + <use xlink:href="#glyph4-7" x="262.157829" y="307.956049"/> + <use xlink:href="#glyph4-6" x="264.927485" y="307.956049"/> + <use xlink:href="#glyph4-11" x="268.803009" y="307.956049"/> + <use xlink:href="#glyph4-7" x="271.572665" y="307.956049"/> + <use xlink:href="#glyph4-9" x="274.34232" y="307.956049"/> + <use xlink:href="#glyph4-8" x="279.323714" y="307.956049"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph4-2" x="287.293945" y="307.956049"/> + <use xlink:href="#glyph4-3" x="292.27534" y="307.956049"/> + <use xlink:href="#glyph4-4" x="295.592949" y="307.956049"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph4-5" x="300.335236" y="307.956049"/> + <use xlink:href="#glyph4-6" x="307.528369" y="307.956049"/> + <use xlink:href="#glyph4-1" x="311.403894" y="307.956049"/> + <use xlink:href="#glyph4-3" x="315.827373" y="307.956049"/> + <use xlink:href="#glyph4-6" x="319.144981" y="307.956049"/> + <use xlink:href="#glyph4-25" x="323.020506" y="307.956049"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph4-7" x="328.639519" y="307.956049"/> + <use xlink:href="#glyph4-9" x="331.409174" y="307.956049"/> + <use xlink:href="#glyph4-15" x="336.390569" y="307.956049"/> + <use xlink:href="#glyph4-14" x="340.814047" y="307.956049"/> + <use xlink:href="#glyph4-17" x="343.583702" y="307.956049"/> + <use xlink:href="#glyph4-12" x="348.565097" y="307.956049"/> + <use xlink:href="#glyph4-7" x="353.546491" y="307.956049"/> + <use xlink:href="#glyph4-9" x="356.316146" y="307.956049"/> + <use xlink:href="#glyph4-8" x="361.297541" y="307.956049"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph4-9" x="369.277734" y="307.956049"/> + <use xlink:href="#glyph4-1" x="374.259129" y="307.956049"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph4-5" x="378.4435" y="307.956049"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph4-10" x="388.635433" y="307.956049"/> + <use xlink:href="#glyph4-3" x="393.058911" y="307.956049"/> + <use xlink:href="#glyph4-15" x="396.37652" y="307.956049"/> + <use xlink:href="#glyph4-16" x="400.799998" y="307.956049"/> + <use xlink:href="#glyph4-7" x="405.781393" y="307.956049"/> + <use xlink:href="#glyph4-11" x="408.551048" y="307.956049"/> + <use xlink:href="#glyph4-1" x="411.320703" y="307.956049"/> + <use xlink:href="#glyph4-15" x="415.744181" y="307.956049"/> + <use xlink:href="#glyph4-11" x="420.16766" y="307.956049"/> + <use xlink:href="#glyph4-17" x="422.937315" y="307.956049"/> + <use xlink:href="#glyph4-3" x="427.918709" y="307.956049"/> + <use xlink:href="#glyph4-1" x="431.236318" y="307.956049"/> + <use xlink:href="#glyph4-6" x="435.659796" y="307.956049"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph4-14" x="442.534121" y="307.956049"/> + <use xlink:href="#glyph4-7" x="445.303776" y="307.956049"/> + <use xlink:href="#glyph4-29" x="448.073431" y="307.956049"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph4-1" x="452.96516" y="307.956049"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph4-30" x="460.387438" y="307.956049"/> + <use xlink:href="#glyph4-31" x="463.705047" y="307.956049"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph4-32" x="472.79111" y="307.956049"/> + <use xlink:href="#glyph4-25" x="477.772504" y="307.956049"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph4-33" x="483.40148" y="307.956049"/> + <use xlink:href="#glyph4-4" x="490.594614" y="307.956049"/> + <use xlink:href="#glyph4-4" x="495.576008" y="307.956049"/> + <use xlink:href="#glyph4-8" x="500.557402" y="307.956049"/> + <use xlink:href="#glyph4-14" x="505.538797" y="307.956049"/> + <use xlink:href="#glyph4-1" x="508.308452" y="307.956049"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph4-34" x="99.277636" y="319.909395"/> + <use xlink:href="#glyph4-16" x="105.922816" y="319.909395"/> + <use xlink:href="#glyph4-3" x="110.90421" y="319.909395"/> + <use xlink:href="#glyph4-4" x="114.221819" y="319.909395"/> + <use xlink:href="#glyph4-23" x="119.203213" y="319.909395"/> + <use xlink:href="#glyph4-1" x="126.954263" y="319.909395"/> + <use xlink:href="#glyph4-25" x="131.377741" y="319.909395"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph4-10" x="136.100103" y="319.909395"/> + <use xlink:href="#glyph4-9" x="140.523581" y="319.909395"/> + <use xlink:href="#glyph4-12" x="145.504976" y="319.909395"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph4-35" x="152.668221" y="319.909395"/> + <use xlink:href="#glyph4-36" x="159.861354" y="319.909395"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph4-25" x="164.304758" y="319.909395"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph4-16" x="168.967343" y="319.909395"/> + <use xlink:href="#glyph4-10" x="173.948738" y="319.909395"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph4-18" x="178.182923" y="319.909395"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph4-1" x="183.014875" y="319.909395"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph4-10" x="189.610242" y="319.909395"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph4-23" x="196.215571" y="319.909395"/> + <use xlink:href="#glyph4-17" x="203.96662" y="319.909395"/> + <use xlink:href="#glyph4-14" x="208.948015" y="319.909395"/> + <use xlink:href="#glyph4-11" x="211.71767" y="319.909395"/> + <use xlink:href="#glyph4-7" x="214.487325" y="319.909395"/> + <use xlink:href="#glyph4-26" x="217.256981" y="319.909395"/> + <use xlink:href="#glyph4-13" x="220.574589" y="319.909395"/> + <use xlink:href="#glyph4-3" x="225.555984" y="319.909395"/> + <use xlink:href="#glyph4-7" x="228.873592" y="319.909395"/> + <use xlink:href="#glyph4-9" x="231.643248" y="319.909395"/> + <use xlink:href="#glyph4-15" x="236.624642" y="319.909395"/> + <use xlink:href="#glyph4-7" x="241.04812" y="319.909395"/> + <use xlink:href="#glyph4-13" x="243.817776" y="319.909395"/> + <use xlink:href="#glyph4-10" x="248.79917" y="319.909395"/> + <use xlink:href="#glyph4-14" x="253.222648" y="319.909395"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph4-4" x="258.174154" y="319.909395"/> + <use xlink:href="#glyph4-13" x="263.155549" y="319.909395"/> + <use xlink:href="#glyph4-1" x="268.136943" y="319.909395"/> + <use xlink:href="#glyph4-3" x="272.560421" y="319.909395"/> + <use xlink:href="#glyph4-10" x="275.87803" y="319.909395"/> + <use xlink:href="#glyph4-11" x="280.301508" y="319.909395"/> + <use xlink:href="#glyph4-7" x="283.071163" y="319.909395"/> + <use xlink:href="#glyph4-9" x="285.840819" y="319.909395"/> + <use xlink:href="#glyph4-8" x="290.822213" y="319.909395"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph4-6" x="297.985458" y="319.909395"/> + <use xlink:href="#glyph4-20" x="301.860983" y="319.909395"/> + <use xlink:href="#glyph4-6" x="306.842377" y="319.909395"/> + <use xlink:href="#glyph4-11" x="310.717902" y="319.909395"/> + <use xlink:href="#glyph4-1" x="313.487558" y="319.909395"/> + <use xlink:href="#glyph4-23" x="317.911036" y="319.909395"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph4-15" x="327.843936" y="319.909395"/> + <use xlink:href="#glyph4-4" x="332.267414" y="319.909395"/> + <use xlink:href="#glyph4-9" x="337.248809" y="319.909395"/> + <use xlink:href="#glyph4-6" x="342.230203" y="319.909395"/> + <use xlink:href="#glyph4-11" x="346.105728" y="319.909395"/> + <use xlink:href="#glyph4-3" x="348.875383" y="319.909395"/> + <use xlink:href="#glyph4-17" x="352.192992" y="319.909395"/> + <use xlink:href="#glyph4-15" x="357.174386" y="319.909395"/> + <use xlink:href="#glyph4-11" x="361.597865" y="319.909395"/> + <use xlink:href="#glyph4-7" x="364.36752" y="319.909395"/> + <use xlink:href="#glyph4-4" x="367.137175" y="319.909395"/> + <use xlink:href="#glyph4-9" x="372.11857" y="319.909395"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph4-11" x="379.271852" y="319.909395"/> + <use xlink:href="#glyph4-16" x="382.041507" y="319.909395"/> + <use xlink:href="#glyph4-10" x="387.022902" y="319.909395"/> + <use xlink:href="#glyph4-11" x="391.44638" y="319.909395"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph4-8" x="396.397886" y="319.909395"/> + <use xlink:href="#glyph4-7" x="401.37928" y="319.909395"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph4-18" x="403.909829" y="319.909395"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph4-1" x="408.741781" y="319.909395"/> + <use xlink:href="#glyph4-6" x="413.16526" y="319.909395"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph4-10" x="419.212672" y="319.909395"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph4-2" x="425.818001" y="319.909395"/> + <use xlink:href="#glyph4-3" x="430.799396" y="319.909395"/> + <use xlink:href="#glyph4-4" x="434.117004" y="319.909395"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph4-5" x="438.859292" y="319.909395"/> + <use xlink:href="#glyph4-6" x="446.052425" y="319.909395"/> + <use xlink:href="#glyph4-1" x="449.92795" y="319.909395"/> + <use xlink:href="#glyph4-3" x="454.351428" y="319.909395"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph4-26" x="457.469781" y="319.909395"/> + <use xlink:href="#glyph4-2" x="460.78739" y="319.909395"/> + <use xlink:href="#glyph4-10" x="465.768784" y="319.909395"/> + <use xlink:href="#glyph4-6" x="470.192263" y="319.909395"/> + <use xlink:href="#glyph4-1" x="474.067787" y="319.909395"/> + <use xlink:href="#glyph4-12" x="478.491266" y="319.909395"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph4-35" x="485.644548" y="319.909395"/> + <use xlink:href="#glyph4-37" x="492.837682" y="319.909395"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph4-11" x="500.558843" y="319.909395"/> + <use xlink:href="#glyph4-16" x="503.328498" y="319.909395"/> + <use xlink:href="#glyph4-1" x="508.309893" y="319.909395"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph5-8" x="99.277636" y="331.862741"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph5-9" x="103.501858" y="331.862741"/> + <use xlink:href="#glyph5-4" x="107.925336" y="331.862741"/> + <use xlink:href="#glyph5-6" x="112.348815" y="331.862741"/> + <use xlink:href="#glyph5-10" x="115.11847" y="331.862741"/> + <use xlink:href="#glyph5-7" x="120.099864" y="331.862741"/> + <use xlink:href="#glyph5-2" x="123.975389" y="331.862741"/> + <use xlink:href="#glyph5-11" x="126.745044" y="331.862741"/> + <use xlink:href="#glyph5-8" x="131.168523" y="331.862741"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph4-15" x="138.078497" y="331.862741"/> + <use xlink:href="#glyph4-4" x="142.501975" y="331.862741"/> + <use xlink:href="#glyph4-9" x="147.483369" y="331.862741"/> + <use xlink:href="#glyph4-11" x="152.464764" y="331.862741"/> + <use xlink:href="#glyph4-3" x="155.234419" y="331.862741"/> + <use xlink:href="#glyph4-4" x="158.552028" y="331.862741"/> + <use xlink:href="#glyph4-14" x="163.533422" y="331.862741"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph4-11" x="168.783812" y="331.862741"/> + <use xlink:href="#glyph4-4" x="171.553467" y="331.862741"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph4-23" x="179.025559" y="331.862741"/> + <use xlink:href="#glyph4-10" x="186.776608" y="331.862741"/> + <use xlink:href="#glyph4-9" x="191.200087" y="331.862741"/> + <use xlink:href="#glyph4-10" x="196.181481" y="331.862741"/> + <use xlink:href="#glyph4-8" x="200.604959" y="331.862741"/> + <use xlink:href="#glyph4-1" x="205.586354" y="331.862741"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph4-11" x="212.500529" y="331.862741"/> + <use xlink:href="#glyph4-16" x="215.270184" y="331.862741"/> + <use xlink:href="#glyph4-1" x="220.251579" y="331.862741"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph4-13" x="227.165754" y="331.862741"/> + <use xlink:href="#glyph4-3" x="232.147149" y="331.862741"/> + <use xlink:href="#glyph4-4" x="235.464757" y="331.862741"/> + <use xlink:href="#glyph4-11" x="240.446152" y="331.862741"/> + <use xlink:href="#glyph4-1" x="243.215807" y="331.862741"/> + <use xlink:href="#glyph4-15" x="247.639285" y="331.862741"/> + <use xlink:href="#glyph4-11" x="252.062763" y="331.862741"/> + <use xlink:href="#glyph4-7" x="254.832419" y="331.862741"/> + <use xlink:href="#glyph4-4" x="257.602074" y="331.862741"/> + <use xlink:href="#glyph4-9" x="262.583468" y="331.862741"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph4-4" x="270.05556" y="331.862741"/> + <use xlink:href="#glyph4-24" x="275.036954" y="331.862741"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph4-10" x="280.84526" y="331.862741"/> + <use xlink:href="#glyph4-14" x="285.268739" y="331.862741"/> + <use xlink:href="#glyph4-14" x="288.038394" y="331.862741"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph4-6" x="293.298746" y="331.862741"/> + <use xlink:href="#glyph4-20" x="297.174271" y="331.862741"/> + <use xlink:href="#glyph4-6" x="302.155666" y="331.862741"/> + <use xlink:href="#glyph4-11" x="306.03119" y="331.862741"/> + <use xlink:href="#glyph4-1" x="308.800846" y="331.862741"/> + <use xlink:href="#glyph4-23" x="313.224324" y="331.862741"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph4-3" x="323.466071" y="331.862741"/> + <use xlink:href="#glyph4-1" x="326.783679" y="331.862741"/> + <use xlink:href="#glyph4-6" x="331.207158" y="331.862741"/> + <use xlink:href="#glyph4-4" x="335.082683" y="331.862741"/> + <use xlink:href="#glyph4-17" x="340.064077" y="331.862741"/> + <use xlink:href="#glyph4-3" x="345.045471" y="331.862741"/> + <use xlink:href="#glyph4-15" x="348.36308" y="331.862741"/> + <use xlink:href="#glyph4-1" x="352.786558" y="331.862741"/> + <use xlink:href="#glyph4-6" x="357.210036" y="331.862741"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph4-10" x="363.576258" y="331.862741"/> + <use xlink:href="#glyph4-23" x="367.999737" y="331.862741"/> + <use xlink:href="#glyph4-4" x="375.750786" y="331.862741"/> + <use xlink:href="#glyph4-9" x="380.732181" y="331.862741"/> + <use xlink:href="#glyph4-8" x="385.713575" y="331.862741"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph4-5" x="393.185667" y="331.862741"/> + <use xlink:href="#glyph4-1" x="400.3788" y="331.862741"/> + <use xlink:href="#glyph4-2" x="404.802279" y="331.862741"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph4-6" x="412.27437" y="331.862741"/> + <use xlink:href="#glyph4-7" x="416.149895" y="331.862741"/> + <use xlink:href="#glyph4-11" x="418.91955" y="331.862741"/> + <use xlink:href="#glyph4-1" x="421.689206" y="331.862741"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph4-13" x="428.603381" y="331.862741"/> + <use xlink:href="#glyph4-3" x="433.584775" y="331.862741"/> + <use xlink:href="#glyph4-7" x="436.902384" y="331.862741"/> + <use xlink:href="#glyph4-9" x="439.672039" y="331.862741"/> + <use xlink:href="#glyph4-15" x="444.653434" y="331.862741"/> + <use xlink:href="#glyph4-7" x="449.076912" y="331.862741"/> + <use xlink:href="#glyph4-13" x="451.846567" y="331.862741"/> + <use xlink:href="#glyph4-10" x="456.827962" y="331.862741"/> + <use xlink:href="#glyph4-14" x="461.25144" y="331.862741"/> + <use xlink:href="#glyph4-6" x="464.021095" y="331.862741"/> + <use xlink:href="#glyph4-21" x="467.89662" y="331.862741"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph4-30" x="114.221819" y="343.82609"/> + <use xlink:href="#glyph4-9" x="117.539427" y="343.82609"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph4-11" x="126.565714" y="343.82609"/> + <use xlink:href="#glyph4-16" x="129.335369" y="343.82609"/> + <use xlink:href="#glyph4-7" x="134.316764" y="343.82609"/> + <use xlink:href="#glyph4-6" x="137.086419" y="343.82609"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph4-13" x="145.006836" y="343.82609"/> + <use xlink:href="#glyph4-10" x="149.988231" y="343.82609"/> + <use xlink:href="#glyph4-13" x="154.411709" y="343.82609"/> + <use xlink:href="#glyph4-1" x="159.393103" y="343.82609"/> + <use xlink:href="#glyph4-3" x="163.816581" y="343.82609"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph4-25" x="166.745641" y="343.82609"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph4-5" x="173.66978" y="343.82609"/> + <use xlink:href="#glyph4-1" x="180.862913" y="343.82609"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph4-7" x="189.331284" y="343.82609"/> + <use xlink:href="#glyph4-9" x="192.100939" y="343.82609"/> + <use xlink:href="#glyph4-11" x="197.082333" y="343.82609"/> + <use xlink:href="#glyph4-3" x="199.851989" y="343.82609"/> + <use xlink:href="#glyph4-4" x="203.169597" y="343.82609"/> + <use xlink:href="#glyph4-12" x="208.150992" y="343.82609"/> + <use xlink:href="#glyph4-17" x="213.132386" y="343.82609"/> + <use xlink:href="#glyph4-15" x="218.11378" y="343.82609"/> + <use xlink:href="#glyph4-1" x="222.537259" y="343.82609"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph4-33" x="231.015592" y="343.82609"/> + <use xlink:href="#glyph4-10" x="238.208725" y="343.82609"/> + <use xlink:href="#glyph4-38" x="242.632204" y="343.82609"/> + <use xlink:href="#glyph4-1" x="247.055682" y="343.82609"/> + <use xlink:href="#glyph4-14" x="251.47916" y="343.82609"/> + <use xlink:href="#glyph4-14" x="254.248815" y="343.82609"/> + <use xlink:href="#glyph4-1" x="257.018471" y="343.82609"/> + <use xlink:href="#glyph4-25" x="261.441949" y="343.82609"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph4-10" x="268.37605" y="343.82609"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph4-6" x="276.84442" y="343.82609"/> + <use xlink:href="#glyph4-1" x="280.719945" y="343.82609"/> + <use xlink:href="#glyph4-15" x="285.143423" y="343.82609"/> + <use xlink:href="#glyph4-17" x="289.566902" y="343.82609"/> + <use xlink:href="#glyph4-3" x="294.548296" y="343.82609"/> + <use xlink:href="#glyph4-1" x="297.865905" y="343.82609"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph4-5" x="306.344238" y="343.82609"/> + <use xlink:href="#glyph4-1" x="313.537372" y="343.82609"/> + <use xlink:href="#glyph4-2" x="317.96085" y="343.82609"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph4-2" x="326.987136" y="343.82609"/> + <use xlink:href="#glyph4-3" x="331.968531" y="343.82609"/> + <use xlink:href="#glyph4-4" x="335.286139" y="343.82609"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph4-5" x="340.028427" y="343.82609"/> + <use xlink:href="#glyph4-6" x="347.22156" y="343.82609"/> + <use xlink:href="#glyph4-1" x="351.097085" y="343.82609"/> + <use xlink:href="#glyph4-3" x="355.520564" y="343.82609"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph4-15" x="362.883064" y="343.82609"/> + <use xlink:href="#glyph4-4" x="367.306543" y="343.82609"/> + <use xlink:href="#glyph4-9" x="372.287937" y="343.82609"/> + <use xlink:href="#glyph4-6" x="377.269331" y="343.82609"/> + <use xlink:href="#glyph4-11" x="381.144856" y="343.82609"/> + <use xlink:href="#glyph4-3" x="383.914512" y="343.82609"/> + <use xlink:href="#glyph4-17" x="387.23212" y="343.82609"/> + <use xlink:href="#glyph4-15" x="392.213515" y="343.82609"/> + <use xlink:href="#glyph4-11" x="396.636993" y="343.82609"/> + <use xlink:href="#glyph4-1" x="399.406648" y="343.82609"/> + <use xlink:href="#glyph4-12" x="403.830126" y="343.82609"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph4-10" x="412.856413" y="343.82609"/> + <use xlink:href="#glyph4-6" x="417.279891" y="343.82609"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph4-10" x="425.210271" y="343.82609"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph4-23" x="433.678642" y="343.82609"/> + <use xlink:href="#glyph4-17" x="441.429691" y="343.82609"/> + <use xlink:href="#glyph4-14" x="446.411086" y="343.82609"/> + <use xlink:href="#glyph4-11" x="449.180741" y="343.82609"/> + <use xlink:href="#glyph4-7" x="451.950396" y="343.82609"/> + <use xlink:href="#glyph4-26" x="454.720052" y="343.82609"/> + <use xlink:href="#glyph4-13" x="458.03766" y="343.82609"/> + <use xlink:href="#glyph4-3" x="463.019055" y="343.82609"/> + <use xlink:href="#glyph4-7" x="466.336663" y="343.82609"/> + <use xlink:href="#glyph4-9" x="469.106319" y="343.82609"/> + <use xlink:href="#glyph4-15" x="474.087713" y="343.82609"/> + <use xlink:href="#glyph4-7" x="478.511191" y="343.82609"/> + <use xlink:href="#glyph4-13" x="481.280847" y="343.82609"/> + <use xlink:href="#glyph4-10" x="486.262241" y="343.82609"/> + <use xlink:href="#glyph4-14" x="490.685719" y="343.82609"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph4-35" x="497.51023" y="343.82609"/> + <use xlink:href="#glyph4-37" x="504.703363" y="343.82609"/> + <use xlink:href="#glyph4-21" x="510.242674" y="343.82609"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph4-33" x="99.277636" y="355.779436"/> + <use xlink:href="#glyph4-10" x="106.470769" y="355.779436"/> + <use xlink:href="#glyph4-38" x="110.894247" y="355.779436"/> + <use xlink:href="#glyph4-1" x="115.317726" y="355.779436"/> + <use xlink:href="#glyph4-14" x="119.741204" y="355.779436"/> + <use xlink:href="#glyph4-14" x="122.510859" y="355.779436"/> + <use xlink:href="#glyph4-1" x="125.280514" y="355.779436"/> + <use xlink:href="#glyph4-39" x="129.703993" y="355.779436"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph4-6" x="132.473648" y="355.779436"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph4-40" x="138.780093" y="355.779436"/> + <use xlink:href="#glyph4-3" x="145.425273" y="355.779436"/> + <use xlink:href="#glyph4-4" x="148.742882" y="355.779436"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph4-5" x="153.485169" y="355.779436"/> + <use xlink:href="#glyph4-6" x="160.678303" y="355.779436"/> + <use xlink:href="#glyph4-1" x="164.553828" y="355.779436"/> + <use xlink:href="#glyph4-3" x="168.977306" y="355.779436"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph4-41" x="174.735798" y="355.779436"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph4-1" x="181.689825" y="355.779436"/> + <use xlink:href="#glyph4-3" x="186.113303" y="355.779436"/> + <use xlink:href="#glyph4-9" x="189.430911" y="355.779436"/> + <use xlink:href="#glyph4-1" x="194.412306" y="355.779436"/> + <use xlink:href="#glyph4-14" x="198.835784" y="355.779436"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph4-7" x="204.03636" y="355.779436"/> + <use xlink:href="#glyph4-6" x="206.806015" y="355.779436"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph4-10" x="213.132386" y="355.779436"/> + <use xlink:href="#glyph4-9" x="217.555864" y="355.779436"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph4-4" x="224.978142" y="355.779436"/> + <use xlink:href="#glyph4-13" x="229.959536" y="355.779436"/> + <use xlink:href="#glyph4-1" x="234.940931" y="355.779436"/> + <use xlink:href="#glyph4-3" x="239.364409" y="355.779436"/> + <use xlink:href="#glyph4-10" x="242.682018" y="355.779436"/> + <use xlink:href="#glyph4-11" x="247.105496" y="355.779436"/> + <use xlink:href="#glyph4-7" x="249.875151" y="355.779436"/> + <use xlink:href="#glyph4-9" x="252.644806" y="355.779436"/> + <use xlink:href="#glyph4-8" x="257.626201" y="355.779436"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph4-6" x="265.058441" y="355.779436"/> + <use xlink:href="#glyph4-20" x="268.933966" y="355.779436"/> + <use xlink:href="#glyph4-6" x="273.91536" y="355.779436"/> + <use xlink:href="#glyph4-11" x="277.790885" y="355.779436"/> + <use xlink:href="#glyph4-1" x="280.560541" y="355.779436"/> + <use xlink:href="#glyph4-23" x="284.984019" y="355.779436"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph4-11" x="295.175952" y="355.779436"/> + <use xlink:href="#glyph4-16" x="297.945607" y="355.779436"/> + <use xlink:href="#glyph4-10" x="302.927001" y="355.779436"/> + <use xlink:href="#glyph4-11" x="307.35048" y="355.779436"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph4-1" x="312.570981" y="355.779436"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph4-28" x="316.85498" y="355.779436"/> + <use xlink:href="#glyph4-15" x="321.836375" y="355.779436"/> + <use xlink:href="#glyph4-14" x="326.259853" y="355.779436"/> + <use xlink:href="#glyph4-17" x="329.029508" y="355.779436"/> + <use xlink:href="#glyph4-6" x="334.010903" y="355.779436"/> + <use xlink:href="#glyph4-7" x="337.886427" y="355.779436"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph4-18" x="340.407013" y="355.779436"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph4-1" x="345.238965" y="355.779436"/> + <use xlink:href="#glyph4-14" x="349.662444" y="355.779436"/> + <use xlink:href="#glyph4-20" x="352.432099" y="355.779436"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph4-23" x="359.844414" y="355.779436"/> + <use xlink:href="#glyph4-10" x="367.595464" y="355.779436"/> + <use xlink:href="#glyph4-9" x="372.018942" y="355.779436"/> + <use xlink:href="#glyph4-10" x="377.000336" y="355.779436"/> + <use xlink:href="#glyph4-8" x="381.423814" y="355.779436"/> + <use xlink:href="#glyph4-1" x="386.405209" y="355.779436"/> + <use xlink:href="#glyph4-6" x="390.828687" y="355.779436"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph4-3" x="397.155058" y="355.779436"/> + <use xlink:href="#glyph4-1" x="400.472667" y="355.779436"/> + <use xlink:href="#glyph4-6" x="404.896145" y="355.779436"/> + <use xlink:href="#glyph4-4" x="408.77167" y="355.779436"/> + <use xlink:href="#glyph4-17" x="413.753064" y="355.779436"/> + <use xlink:href="#glyph4-3" x="418.734458" y="355.779436"/> + <use xlink:href="#glyph4-15" x="422.052067" y="355.779436"/> + <use xlink:href="#glyph4-1" x="426.475545" y="355.779436"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph4-13" x="433.339907" y="355.779436"/> + <use xlink:href="#glyph4-3" x="438.321301" y="355.779436"/> + <use xlink:href="#glyph4-4" x="441.63891" y="355.779436"/> + <use xlink:href="#glyph4-11" x="446.620304" y="355.779436"/> + <use xlink:href="#glyph4-1" x="449.38996" y="355.779436"/> + <use xlink:href="#glyph4-15" x="453.813438" y="355.779436"/> + <use xlink:href="#glyph4-11" x="458.236916" y="355.779436"/> + <use xlink:href="#glyph4-7" x="461.006571" y="355.779436"/> + <use xlink:href="#glyph4-4" x="463.776227" y="355.779436"/> + <use xlink:href="#glyph4-9" x="468.757621" y="355.779436"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph4-10" x="476.189861" y="355.779436"/> + <use xlink:href="#glyph4-9" x="480.61334" y="355.779436"/> + <use xlink:href="#glyph4-12" x="485.594734" y="355.779436"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph4-6" x="493.017012" y="355.779436"/> + <use xlink:href="#glyph4-16" x="496.892537" y="355.779436"/> + <use xlink:href="#glyph4-10" x="501.873931" y="355.779436"/> + <use xlink:href="#glyph4-3" x="506.297409" y="355.779436"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph4-26" x="509.425725" y="355.779436"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph4-7" x="99.277636" y="367.732782"/> + <use xlink:href="#glyph4-9" x="102.047291" y="367.732782"/> + <use xlink:href="#glyph4-8" x="107.028685" y="367.732782"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph4-10" x="114.988954" y="367.732782"/> + <use xlink:href="#glyph4-15" x="119.412432" y="367.732782"/> + <use xlink:href="#glyph4-3" x="123.83591" y="367.732782"/> + <use xlink:href="#glyph4-4" x="127.153519" y="367.732782"/> + <use xlink:href="#glyph4-6" x="132.134913" y="367.732782"/> + <use xlink:href="#glyph4-6" x="136.010438" y="367.732782"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph4-5" x="142.874799" y="367.732782"/> + <use xlink:href="#glyph4-1" x="150.067933" y="367.732782"/> + <use xlink:href="#glyph4-2" x="154.491411" y="367.732782"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph4-6" x="162.461642" y="367.732782"/> + <use xlink:href="#glyph4-7" x="166.337167" y="367.732782"/> + <use xlink:href="#glyph4-11" x="169.106822" y="367.732782"/> + <use xlink:href="#glyph4-1" x="171.876478" y="367.732782"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph4-13" x="179.288792" y="367.732782"/> + <use xlink:href="#glyph4-3" x="184.270187" y="367.732782"/> + <use xlink:href="#glyph4-7" x="187.587796" y="367.732782"/> + <use xlink:href="#glyph4-9" x="190.357451" y="367.732782"/> + <use xlink:href="#glyph4-15" x="195.338845" y="367.732782"/> + <use xlink:href="#glyph4-7" x="199.762323" y="367.732782"/> + <use xlink:href="#glyph4-13" x="202.531979" y="367.732782"/> + <use xlink:href="#glyph4-10" x="207.513373" y="367.732782"/> + <use xlink:href="#glyph4-14" x="211.936851" y="367.732782"/> + <use xlink:href="#glyph4-6" x="214.706507" y="367.732782"/> + <use xlink:href="#glyph4-21" x="218.582031" y="367.732782"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph4-42" x="225.655612" y="367.732782"/> + <use xlink:href="#glyph4-16" x="231.742875" y="367.732782"/> + <use xlink:href="#glyph4-7" x="236.72427" y="367.732782"/> + <use xlink:href="#glyph4-6" x="239.493925" y="367.732782"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph4-15" x="246.358287" y="367.732782"/> + <use xlink:href="#glyph4-4" x="250.781765" y="367.732782"/> + <use xlink:href="#glyph4-9" x="255.763159" y="367.732782"/> + <use xlink:href="#glyph4-6" x="260.744554" y="367.732782"/> + <use xlink:href="#glyph4-11" x="264.620079" y="367.732782"/> + <use xlink:href="#glyph4-3" x="267.389734" y="367.732782"/> + <use xlink:href="#glyph4-17" x="270.707342" y="367.732782"/> + <use xlink:href="#glyph4-15" x="275.688737" y="367.732782"/> + <use xlink:href="#glyph4-11" x="280.112215" y="367.732782"/> + <use xlink:href="#glyph4-7" x="282.88187" y="367.732782"/> + <use xlink:href="#glyph4-4" x="285.651526" y="367.732782"/> + <use xlink:href="#glyph4-9" x="290.63292" y="367.732782"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph4-1" x="298.613114" y="367.732782"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph4-28" x="302.897113" y="367.732782"/> + <use xlink:href="#glyph4-13" x="307.878507" y="367.732782"/> + <use xlink:href="#glyph4-4" x="312.859902" y="367.732782"/> + <use xlink:href="#glyph4-6" x="317.841296" y="367.732782"/> + <use xlink:href="#glyph4-1" x="321.716821" y="367.732782"/> + <use xlink:href="#glyph4-6" x="326.140299" y="367.732782"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph4-7" x="332.994698" y="367.732782"/> + <use xlink:href="#glyph4-9" x="335.764353" y="367.732782"/> + <use xlink:href="#glyph4-11" x="340.745748" y="367.732782"/> + <use xlink:href="#glyph4-3" x="343.515403" y="367.732782"/> + <use xlink:href="#glyph4-7" x="346.833012" y="367.732782"/> + <use xlink:href="#glyph4-15" x="349.602667" y="367.732782"/> + <use xlink:href="#glyph4-10" x="354.026145" y="367.732782"/> + <use xlink:href="#glyph4-11" x="358.449623" y="367.732782"/> + <use xlink:href="#glyph4-1" x="361.219279" y="367.732782"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph4-12" x="368.631594" y="367.732782"/> + <use xlink:href="#glyph4-1" x="373.612988" y="367.732782"/> + <use xlink:href="#glyph4-6" x="378.036466" y="367.732782"/> + <use xlink:href="#glyph4-7" x="381.911991" y="367.732782"/> + <use xlink:href="#glyph4-8" x="384.681646" y="367.732782"/> + <use xlink:href="#glyph4-9" x="389.663041" y="367.732782"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph4-7" x="397.633272" y="367.732782"/> + <use xlink:href="#glyph4-6" x="400.402927" y="367.732782"/> + <use xlink:href="#glyph4-6" x="404.278452" y="367.732782"/> + <use xlink:href="#glyph4-17" x="408.153977" y="367.732782"/> + <use xlink:href="#glyph4-1" x="413.135371" y="367.732782"/> + <use xlink:href="#glyph4-6" x="417.558849" y="367.732782"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph4-11" x="424.423211" y="367.732782"/> + <use xlink:href="#glyph4-16" x="427.192866" y="367.732782"/> + <use xlink:href="#glyph4-10" x="432.174261" y="367.732782"/> + <use xlink:href="#glyph4-11" x="436.597739" y="367.732782"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph4-9" x="442.356231" y="367.732782"/> + <use xlink:href="#glyph4-4" x="447.337625" y="367.732782"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph4-13" x="455.307856" y="367.732782"/> + <use xlink:href="#glyph4-3" x="460.289251" y="367.732782"/> + <use xlink:href="#glyph4-1" x="463.606859" y="367.732782"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph4-18" x="467.791231" y="367.732782"/> + <use xlink:href="#glyph4-7" x="472.772625" y="367.732782"/> + <use xlink:href="#glyph4-4" x="475.54228" y="367.732782"/> + <use xlink:href="#glyph4-17" x="480.523675" y="367.732782"/> + <use xlink:href="#glyph4-6" x="485.505069" y="367.732782"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph4-5" x="492.359468" y="367.732782"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph4-4" x="499.462936" y="367.732782"/> + <use xlink:href="#glyph4-3" x="504.444331" y="367.732782"/> + <use xlink:href="#glyph4-29" x="507.761939" y="367.732782"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph4-16" x="99.277636" y="379.686128"/> + <use xlink:href="#glyph4-10" x="104.25903" y="379.686128"/> + <use xlink:href="#glyph4-6" x="108.682508" y="379.686128"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph4-7" x="115.138395" y="379.686128"/> + <use xlink:href="#glyph4-12" x="117.908051" y="379.686128"/> + <use xlink:href="#glyph4-1" x="122.889445" y="379.686128"/> + <use xlink:href="#glyph4-9" x="127.312923" y="379.686128"/> + <use xlink:href="#glyph4-11" x="132.294318" y="379.686128"/> + <use xlink:href="#glyph4-7" x="135.063973" y="379.686128"/> + <use xlink:href="#glyph4-43" x="137.833628" y="379.686128"/> + <use xlink:href="#glyph4-1" x="143.372939" y="379.686128"/> + <use xlink:href="#glyph4-12" x="147.796417" y="379.686128"/> + <use xlink:href="#glyph4-25" x="152.777811" y="379.686128"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph4-6" x="157.888722" y="379.686128"/> + <use xlink:href="#glyph4-17" x="161.764247" y="379.686128"/> + <use xlink:href="#glyph4-15" x="166.745641" y="379.686128"/> + <use xlink:href="#glyph4-16" x="171.16912" y="379.686128"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph4-10" x="178.740839" y="379.686128"/> + <use xlink:href="#glyph4-6" x="183.164317" y="379.686128"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph4-14" x="189.630167" y="379.686128"/> + <use xlink:href="#glyph4-1" x="192.399823" y="379.686128"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph4-8" x="196.683822" y="379.686128"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph4-10" x="201.615402" y="379.686128"/> + <use xlink:href="#glyph4-15" x="206.03888" y="379.686128"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph4-20" x="210.312917" y="379.686128"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph4-13" x="217.874673" y="379.686128"/> + <use xlink:href="#glyph4-3" x="222.856068" y="379.686128"/> + <use xlink:href="#glyph4-4" x="226.173677" y="379.686128"/> + <use xlink:href="#glyph4-11" x="231.155071" y="379.686128"/> + <use xlink:href="#glyph4-1" x="233.924726" y="379.686128"/> + <use xlink:href="#glyph4-15" x="238.348204" y="379.686128"/> + <use xlink:href="#glyph4-11" x="242.771683" y="379.686128"/> + <use xlink:href="#glyph4-7" x="245.541338" y="379.686128"/> + <use xlink:href="#glyph4-4" x="248.310993" y="379.686128"/> + <use xlink:href="#glyph4-9" x="253.292388" y="379.686128"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph4-4" x="260.864107" y="379.686128"/> + <use xlink:href="#glyph4-24" x="265.845502" y="379.686128"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph4-15" x="271.753435" y="379.686128"/> + <use xlink:href="#glyph4-3" x="276.176914" y="379.686128"/> + <use xlink:href="#glyph4-4" x="279.494522" y="379.686128"/> + <use xlink:href="#glyph4-6" x="284.475917" y="379.686128"/> + <use xlink:href="#glyph4-6" x="288.351441" y="379.686128"/> + <use xlink:href="#glyph4-26" x="292.226966" y="379.686128"/> + <use xlink:href="#glyph4-4" x="295.544575" y="379.686128"/> + <use xlink:href="#glyph4-3" x="300.525969" y="379.686128"/> + <use xlink:href="#glyph4-7" x="303.843578" y="379.686128"/> + <use xlink:href="#glyph4-8" x="306.613233" y="379.686128"/> + <use xlink:href="#glyph4-7" x="311.594628" y="379.686128"/> + <use xlink:href="#glyph4-9" x="314.364283" y="379.686128"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph4-6" x="321.936002" y="379.686128"/> + <use xlink:href="#glyph4-15" x="325.811527" y="379.686128"/> + <use xlink:href="#glyph4-3" x="330.235006" y="379.686128"/> + <use xlink:href="#glyph4-7" x="333.552614" y="379.686128"/> + <use xlink:href="#glyph4-13" x="336.32227" y="379.686128"/> + <use xlink:href="#glyph4-11" x="341.303664" y="379.686128"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph4-6" x="346.663644" y="379.686128"/> + <use xlink:href="#glyph4-4" x="350.539169" y="379.686128"/> + <use xlink:href="#glyph4-17" x="355.520564" y="379.686128"/> + <use xlink:href="#glyph4-3" x="360.501958" y="379.686128"/> + <use xlink:href="#glyph4-15" x="363.819567" y="379.686128"/> + <use xlink:href="#glyph4-1" x="368.243045" y="379.686128"/> + <use xlink:href="#glyph4-25" x="372.666523" y="379.686128"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph4-10" x="377.767471" y="379.686128"/> + <use xlink:href="#glyph4-9" x="382.190949" y="379.686128"/> + <use xlink:href="#glyph4-12" x="387.172344" y="379.686128"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph4-15" x="394.744063" y="379.686128"/> + <use xlink:href="#glyph4-3" x="399.167541" y="379.686128"/> + <use xlink:href="#glyph4-4" x="402.48515" y="379.686128"/> + <use xlink:href="#glyph4-6" x="407.466544" y="379.686128"/> + <use xlink:href="#glyph4-6" x="411.342069" y="379.686128"/> + <use xlink:href="#glyph4-26" x="415.217594" y="379.686128"/> + <use xlink:href="#glyph4-13" x="418.535203" y="379.686128"/> + <use xlink:href="#glyph4-3" x="423.516597" y="379.686128"/> + <use xlink:href="#glyph4-7" x="426.834206" y="379.686128"/> + <use xlink:href="#glyph4-9" x="429.603861" y="379.686128"/> + <use xlink:href="#glyph4-15" x="434.585255" y="379.686128"/> + <use xlink:href="#glyph4-7" x="439.008734" y="379.686128"/> + <use xlink:href="#glyph4-13" x="441.778389" y="379.686128"/> + <use xlink:href="#glyph4-10" x="446.759783" y="379.686128"/> + <use xlink:href="#glyph4-14" x="451.183262" y="379.686128"/> + <use xlink:href="#glyph4-25" x="453.952917" y="379.686128"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph4-15" x="459.063828" y="379.686128"/> + <use xlink:href="#glyph4-3" x="463.487306" y="379.686128"/> + <use xlink:href="#glyph4-4" x="466.804914" y="379.686128"/> + <use xlink:href="#glyph4-6" x="471.786309" y="379.686128"/> + <use xlink:href="#glyph4-6" x="475.661834" y="379.686128"/> + <use xlink:href="#glyph4-26" x="479.537359" y="379.686128"/> + <use xlink:href="#glyph4-13" x="482.854967" y="379.686128"/> + <use xlink:href="#glyph4-3" x="487.836362" y="379.686128"/> + <use xlink:href="#glyph4-4" x="491.15397" y="379.686128"/> + <use xlink:href="#glyph4-15" x="496.135365" y="379.686128"/> + <use xlink:href="#glyph4-1" x="500.558843" y="379.686128"/> + <use xlink:href="#glyph4-6" x="504.982321" y="379.686128"/> + <use xlink:href="#glyph4-6" x="508.857846" y="379.686128"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph4-12" x="99.277636" y="391.639474"/> + <use xlink:href="#glyph4-7" x="104.25903" y="391.639474"/> + <use xlink:href="#glyph4-6" x="107.028685" y="391.639474"/> + <use xlink:href="#glyph4-13" x="110.90421" y="391.639474"/> + <use xlink:href="#glyph4-14" x="115.885605" y="391.639474"/> + <use xlink:href="#glyph4-10" x="118.65526" y="391.639474"/> + <use xlink:href="#glyph4-20" x="123.078738" y="391.639474"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph4-10" x="130.540867" y="391.639474"/> + <use xlink:href="#glyph4-9" x="134.964345" y="391.639474"/> + <use xlink:href="#glyph4-12" x="139.945739" y="391.639474"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph4-1" x="147.417831" y="391.639474"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph4-18" x="151.602202" y="391.639474"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph4-1" x="156.434155" y="391.639474"/> + <use xlink:href="#glyph4-9" x="160.857633" y="391.639474"/> + <use xlink:href="#glyph4-11" x="165.839028" y="391.639474"/> + <use xlink:href="#glyph4-6" x="168.608683" y="391.639474"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph4-13" x="174.964942" y="391.639474"/> + <use xlink:href="#glyph4-3" x="179.946337" y="391.639474"/> + <use xlink:href="#glyph4-4" x="183.263945" y="391.639474"/> + <use xlink:href="#glyph4-11" x="188.24534" y="391.639474"/> + <use xlink:href="#glyph4-1" x="191.014995" y="391.639474"/> + <use xlink:href="#glyph4-15" x="195.438473" y="391.639474"/> + <use xlink:href="#glyph4-11" x="199.861951" y="391.639474"/> + <use xlink:href="#glyph4-7" x="202.631607" y="391.639474"/> + <use xlink:href="#glyph4-4" x="205.401262" y="391.639474"/> + <use xlink:href="#glyph4-9" x="210.382656" y="391.639474"/> + <use xlink:href="#glyph4-21" x="215.364051" y="391.639474"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph4-0" x="220.943212" y="391.639474"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph4-1" x="229.561025" y="391.639474"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph4-1" x="236.465237" y="391.639474"/> + <use xlink:href="#glyph4-14" x="240.888716" y="391.639474"/> + <use xlink:href="#glyph4-10" x="243.658371" y="391.639474"/> + <use xlink:href="#glyph4-2" x="248.081849" y="391.639474"/> + <use xlink:href="#glyph4-4" x="253.063244" y="391.639474"/> + <use xlink:href="#glyph4-3" x="258.044638" y="391.639474"/> + <use xlink:href="#glyph4-10" x="261.362247" y="391.639474"/> + <use xlink:href="#glyph4-11" x="265.785725" y="391.639474"/> + <use xlink:href="#glyph4-1" x="268.55538" y="391.639474"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph4-4" x="275.469556" y="391.639474"/> + <use xlink:href="#glyph4-9" x="280.45095" y="391.639474"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph4-11" x="287.923042" y="391.639474"/> + <use xlink:href="#glyph4-16" x="290.692697" y="391.639474"/> + <use xlink:href="#glyph4-1" x="295.674091" y="391.639474"/> + <use xlink:href="#glyph4-6" x="300.097569" y="391.639474"/> + <use xlink:href="#glyph4-1" x="303.973094" y="391.639474"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph4-7" x="310.88727" y="391.639474"/> + <use xlink:href="#glyph4-6" x="313.656925" y="391.639474"/> + <use xlink:href="#glyph4-6" x="317.53245" y="391.639474"/> + <use xlink:href="#glyph4-17" x="321.407975" y="391.639474"/> + <use xlink:href="#glyph4-1" x="326.389369" y="391.639474"/> + <use xlink:href="#glyph4-6" x="330.812847" y="391.639474"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph4-10" x="337.179069" y="391.639474"/> + <use xlink:href="#glyph4-9" x="341.602548" y="391.639474"/> + <use xlink:href="#glyph4-12" x="346.583942" y="391.639474"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph4-13" x="354.056034" y="391.639474"/> + <use xlink:href="#glyph4-3" x="359.037428" y="391.639474"/> + <use xlink:href="#glyph4-4" x="362.355037" y="391.639474"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph4-18" x="367.196952" y="391.639474"/> + <use xlink:href="#glyph4-7" x="372.178346" y="391.639474"/> + <use xlink:href="#glyph4-12" x="374.948002" y="391.639474"/> + <use xlink:href="#glyph4-1" x="379.929396" y="391.639474"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph4-15" x="386.833609" y="391.639474"/> + <use xlink:href="#glyph4-4" x="391.257087" y="391.639474"/> + <use xlink:href="#glyph4-23" x="396.238481" y="391.639474"/> + <use xlink:href="#glyph4-13" x="403.989531" y="391.639474"/> + <use xlink:href="#glyph4-3" x="408.970925" y="391.639474"/> + <use xlink:href="#glyph4-1" x="412.288534" y="391.639474"/> + <use xlink:href="#glyph4-16" x="416.712012" y="391.639474"/> + <use xlink:href="#glyph4-1" x="421.693407" y="391.639474"/> + <use xlink:href="#glyph4-9" x="426.116885" y="391.639474"/> + <use xlink:href="#glyph4-6" x="431.098279" y="391.639474"/> + <use xlink:href="#glyph4-7" x="434.973804" y="391.639474"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph4-18" x="437.504353" y="391.639474"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph4-1" x="442.336305" y="391.639474"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph4-6" x="449.240518" y="391.639474"/> + <use xlink:href="#glyph4-4" x="453.116043" y="391.639474"/> + <use xlink:href="#glyph4-14" x="458.097437" y="391.639474"/> + <use xlink:href="#glyph4-17" x="460.867092" y="391.639474"/> + <use xlink:href="#glyph4-11" x="465.848487" y="391.639474"/> + <use xlink:href="#glyph4-7" x="468.618142" y="391.639474"/> + <use xlink:href="#glyph4-4" x="471.387797" y="391.639474"/> + <use xlink:href="#glyph4-9" x="476.369192" y="391.639474"/> + <use xlink:href="#glyph4-6" x="481.350586" y="391.639474"/> + <use xlink:href="#glyph4-21" x="485.226111" y="391.639474"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph4-35" x="114.221819" y="403.59282"/> + <use xlink:href="#glyph4-17" x="121.414952" y="403.59282"/> + <use xlink:href="#glyph4-3" x="126.396347" y="403.59282"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph4-13" x="132.852234" y="403.59282"/> + <use xlink:href="#glyph4-3" x="137.833628" y="403.59282"/> + <use xlink:href="#glyph4-4" x="141.151237" y="403.59282"/> + <use xlink:href="#glyph4-11" x="146.132631" y="403.59282"/> + <use xlink:href="#glyph4-4" x="148.902287" y="403.59282"/> + <use xlink:href="#glyph4-11" x="153.883681" y="403.59282"/> + <use xlink:href="#glyph4-20" x="156.653336" y="403.59282"/> + <use xlink:href="#glyph4-13" x="161.634731" y="403.59282"/> + <use xlink:href="#glyph4-1" x="166.616125" y="403.59282"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph4-7" x="174.177882" y="403.59282"/> + <use xlink:href="#glyph4-23" x="176.947537" y="403.59282"/> + <use xlink:href="#glyph4-13" x="184.698587" y="403.59282"/> + <use xlink:href="#glyph4-14" x="189.679981" y="403.59282"/> + <use xlink:href="#glyph4-1" x="192.449636" y="403.59282"/> + <use xlink:href="#glyph4-23" x="196.873115" y="403.59282"/> + <use xlink:href="#glyph4-1" x="204.624164" y="403.59282"/> + <use xlink:href="#glyph4-9" x="209.047643" y="403.59282"/> + <use xlink:href="#glyph4-11" x="214.029037" y="403.59282"/> + <use xlink:href="#glyph4-10" x="216.798692" y="403.59282"/> + <use xlink:href="#glyph4-11" x="221.222171" y="403.59282"/> + <use xlink:href="#glyph4-7" x="223.991826" y="403.59282"/> + <use xlink:href="#glyph4-4" x="226.761481" y="403.59282"/> + <use xlink:href="#glyph4-9" x="231.742875" y="403.59282"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph4-10" x="239.872511" y="403.59282"/> + <use xlink:href="#glyph4-9" x="244.295989" y="403.59282"/> + <use xlink:href="#glyph4-12" x="249.277384" y="403.59282"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph4-1" x="257.407019" y="403.59282"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph4-18" x="261.591391" y="403.59282"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph4-10" x="266.323715" y="403.59282"/> + <use xlink:href="#glyph4-14" x="270.747194" y="403.59282"/> + <use xlink:href="#glyph4-17" x="273.516849" y="403.59282"/> + <use xlink:href="#glyph4-10" x="278.498243" y="403.59282"/> + <use xlink:href="#glyph4-11" x="282.921722" y="403.59282"/> + <use xlink:href="#glyph4-7" x="285.691377" y="403.59282"/> + <use xlink:href="#glyph4-4" x="288.461032" y="403.59282"/> + <use xlink:href="#glyph4-9" x="293.442427" y="403.59282"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph4-1" x="301.562099" y="403.59282"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph4-28" x="305.846099" y="403.59282"/> + <use xlink:href="#glyph4-13" x="310.827493" y="403.59282"/> + <use xlink:href="#glyph4-1" x="315.808887" y="403.59282"/> + <use xlink:href="#glyph4-3" x="320.232366" y="403.59282"/> + <use xlink:href="#glyph4-7" x="323.549974" y="403.59282"/> + <use xlink:href="#glyph4-1" x="326.31963" y="403.59282"/> + <use xlink:href="#glyph4-9" x="330.743108" y="403.59282"/> + <use xlink:href="#glyph4-15" x="335.724502" y="403.59282"/> + <use xlink:href="#glyph4-1" x="340.14798" y="403.59282"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph4-7" x="347.699774" y="403.59282"/> + <use xlink:href="#glyph4-9" x="350.46943" y="403.59282"/> + <use xlink:href="#glyph4-12" x="355.450824" y="403.59282"/> + <use xlink:href="#glyph4-7" x="360.432218" y="403.59282"/> + <use xlink:href="#glyph4-15" x="363.201874" y="403.59282"/> + <use xlink:href="#glyph4-10" x="367.625352" y="403.59282"/> + <use xlink:href="#glyph4-11" x="372.04883" y="403.59282"/> + <use xlink:href="#glyph4-1" x="374.818485" y="403.59282"/> + <use xlink:href="#glyph4-6" x="379.241964" y="403.59282"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph4-11" x="386.26573" y="403.59282"/> + <use xlink:href="#glyph4-16" x="389.035385" y="403.59282"/> + <use xlink:href="#glyph4-10" x="394.016779" y="403.59282"/> + <use xlink:href="#glyph4-11" x="398.440258" y="403.59282"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph4-7" x="404.358154" y="403.59282"/> + <use xlink:href="#glyph4-11" x="407.12781" y="403.59282"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph4-7" x="413.035743" y="403.59282"/> + <use xlink:href="#glyph4-6" x="415.805399" y="403.59282"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph4-3" x="422.829165" y="403.59282"/> + <use xlink:href="#glyph4-1" x="426.146773" y="403.59282"/> + <use xlink:href="#glyph4-10" x="430.570252" y="403.59282"/> + <use xlink:href="#glyph4-14" x="434.99373" y="403.59282"/> + <use xlink:href="#glyph4-7" x="437.763385" y="403.59282"/> + <use xlink:href="#glyph4-6" x="440.53304" y="403.59282"/> + <use xlink:href="#glyph4-11" x="444.408565" y="403.59282"/> + <use xlink:href="#glyph4-7" x="447.17822" y="403.59282"/> + <use xlink:href="#glyph4-15" x="449.947876" y="403.59282"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph4-11" x="457.519595" y="403.59282"/> + <use xlink:href="#glyph4-4" x="460.289251" y="403.59282"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph4-11" x="468.418886" y="403.59282"/> + <use xlink:href="#glyph4-17" x="471.188542" y="403.59282"/> + <use xlink:href="#glyph4-3" x="476.169936" y="403.59282"/> + <use xlink:href="#glyph4-9" x="479.487545" y="403.59282"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph4-10" x="487.607217" y="403.59282"/> + <use xlink:href="#glyph4-9" x="492.030696" y="403.59282"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph4-1" x="500.160331" y="403.59282"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph4-28" x="504.444331" y="403.59282"/> + <use xlink:href="#glyph4-26" x="509.425725" y="403.59282"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph4-7" x="99.277636" y="415.556169"/> + <use xlink:href="#glyph4-6" x="102.047291" y="415.556169"/> + <use xlink:href="#glyph4-11" x="105.922816" y="415.556169"/> + <use xlink:href="#glyph4-7" x="108.692471" y="415.556169"/> + <use xlink:href="#glyph4-9" x="111.462126" y="415.556169"/> + <use xlink:href="#glyph4-8" x="116.443521" y="415.556169"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph4-2" x="124.563194" y="415.556169"/> + <use xlink:href="#glyph4-3" x="129.544588" y="415.556169"/> + <use xlink:href="#glyph4-4" x="132.862197" y="415.556169"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph4-5" x="137.604484" y="415.556169"/> + <use xlink:href="#glyph4-6" x="144.797618" y="415.556169"/> + <use xlink:href="#glyph4-1" x="148.673142" y="415.556169"/> + <use xlink:href="#glyph4-3" x="153.096621" y="415.556169"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph4-7" x="159.542545" y="415.556169"/> + <use xlink:href="#glyph4-9" x="162.3122" y="415.556169"/> + <use xlink:href="#glyph4-11" x="167.293595" y="415.556169"/> + <use xlink:href="#glyph4-4" x="170.06325" y="415.556169"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph4-10" x="178.192886" y="415.556169"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph4-23" x="185.764605" y="415.556169"/> + <use xlink:href="#glyph4-17" x="193.515655" y="415.556169"/> + <use xlink:href="#glyph4-14" x="198.497049" y="415.556169"/> + <use xlink:href="#glyph4-11" x="201.266705" y="415.556169"/> + <use xlink:href="#glyph4-7" x="204.03636" y="415.556169"/> + <use xlink:href="#glyph4-26" x="206.806015" y="415.556169"/> + <use xlink:href="#glyph4-13" x="210.123624" y="415.556169"/> + <use xlink:href="#glyph4-3" x="215.105018" y="415.556169"/> + <use xlink:href="#glyph4-7" x="218.422627" y="415.556169"/> + <use xlink:href="#glyph4-9" x="221.192282" y="415.556169"/> + <use xlink:href="#glyph4-15" x="226.173677" y="415.556169"/> + <use xlink:href="#glyph4-7" x="230.597155" y="415.556169"/> + <use xlink:href="#glyph4-13" x="233.36681" y="415.556169"/> + <use xlink:href="#glyph4-10" x="238.348204" y="415.556169"/> + <use xlink:href="#glyph4-14" x="242.771683" y="415.556169"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph4-35" x="248.679616" y="415.556169"/> + <use xlink:href="#glyph4-37" x="255.87275" y="415.556169"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph4-11" x="264.560302" y="415.556169"/> + <use xlink:href="#glyph4-16" x="267.329957" y="415.556169"/> + <use xlink:href="#glyph4-10" x="272.311351" y="415.556169"/> + <use xlink:href="#glyph4-11" x="276.73483" y="415.556169"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph4-20" x="282.642763" y="415.556169"/> + <use xlink:href="#glyph4-7" x="287.624158" y="415.556169"/> + <use xlink:href="#glyph4-1" x="290.393813" y="415.556169"/> + <use xlink:href="#glyph4-14" x="294.817291" y="415.556169"/> + <use xlink:href="#glyph4-12" x="297.586947" y="415.556169"/> + <use xlink:href="#glyph4-6" x="302.568341" y="415.556169"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph4-6" x="309.592107" y="415.556169"/> + <use xlink:href="#glyph4-7" x="313.467632" y="415.556169"/> + <use xlink:href="#glyph4-8" x="316.237287" y="415.556169"/> + <use xlink:href="#glyph4-9" x="321.218682" y="415.556169"/> + <use xlink:href="#glyph4-7" x="326.200076" y="415.556169"/> + <use xlink:href="#glyph4-43" x="328.969731" y="415.556169"/> + <use xlink:href="#glyph4-15" x="334.509042" y="415.556169"/> + <use xlink:href="#glyph4-10" x="338.93252" y="415.556169"/> + <use xlink:href="#glyph4-9" x="343.355998" y="415.556169"/> + <use xlink:href="#glyph4-11" x="348.337393" y="415.556169"/> + <use xlink:href="#glyph4-14" x="351.107048" y="415.556169"/> + <use xlink:href="#glyph4-20" x="353.876703" y="415.556169"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph4-6" x="362.006339" y="415.556169"/> + <use xlink:href="#glyph4-11" x="365.881864" y="415.556169"/> + <use xlink:href="#glyph4-3" x="368.651519" y="415.556169"/> + <use xlink:href="#glyph4-4" x="371.969128" y="415.556169"/> + <use xlink:href="#glyph4-9" x="376.950522" y="415.556169"/> + <use xlink:href="#glyph4-8" x="381.931917" y="415.556169"/> + <use xlink:href="#glyph4-1" x="386.913311" y="415.556169"/> + <use xlink:href="#glyph4-3" x="391.336789" y="415.556169"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph4-6" x="397.792676" y="415.556169"/> + <use xlink:href="#glyph4-1" x="401.668201" y="415.556169"/> + <use xlink:href="#glyph4-15" x="406.091679" y="415.556169"/> + <use xlink:href="#glyph4-17" x="410.515158" y="415.556169"/> + <use xlink:href="#glyph4-3" x="415.496552" y="415.556169"/> + <use xlink:href="#glyph4-7" x="418.814161" y="415.556169"/> + <use xlink:href="#glyph4-11" x="421.583816" y="415.556169"/> + <use xlink:href="#glyph4-20" x="424.353471" y="415.556169"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph4-10" x="432.483107" y="415.556169"/> + <use xlink:href="#glyph4-9" x="436.906585" y="415.556169"/> + <use xlink:href="#glyph4-12" x="441.88798" y="415.556169"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph4-3" x="450.007653" y="415.556169"/> + <use xlink:href="#glyph4-4" x="453.325261" y="415.556169"/> + <use xlink:href="#glyph4-2" x="458.306656" y="415.556169"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph4-17" x="463.098757" y="415.556169"/> + <use xlink:href="#glyph4-6" x="468.080151" y="415.556169"/> + <use xlink:href="#glyph4-11" x="471.955676" y="415.556169"/> + <use xlink:href="#glyph4-9" x="474.725332" y="415.556169"/> + <use xlink:href="#glyph4-1" x="479.706726" y="415.556169"/> + <use xlink:href="#glyph4-6" x="484.130204" y="415.556169"/> + <use xlink:href="#glyph4-6" x="488.005729" y="415.556169"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph4-5" x="495.019532" y="415.556169"/> + <use xlink:href="#glyph4-7" x="502.212666" y="415.556169"/> + <use xlink:href="#glyph4-11" x="504.982321" y="415.556169"/> + <use xlink:href="#glyph4-16" x="507.751976" y="415.556169"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph4-10" x="99.277636" y="427.509515"/> + <use xlink:href="#glyph4-15" x="103.701114" y="427.509515"/> + <use xlink:href="#glyph4-15" x="108.124592" y="427.509515"/> + <use xlink:href="#glyph4-1" x="112.54807" y="427.509515"/> + <use xlink:href="#glyph4-13" x="116.971549" y="427.509515"/> + <use xlink:href="#glyph4-11" x="121.952943" y="427.509515"/> + <use xlink:href="#glyph4-10" x="124.722598" y="427.509515"/> + <use xlink:href="#glyph4-2" x="129.146076" y="427.509515"/> + <use xlink:href="#glyph4-14" x="134.127471" y="427.509515"/> + <use xlink:href="#glyph4-1" x="136.897126" y="427.509515"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph4-13" x="144.857394" y="427.509515"/> + <use xlink:href="#glyph4-1" x="149.838789" y="427.509515"/> + <use xlink:href="#glyph4-3" x="154.262267" y="427.509515"/> + <use xlink:href="#glyph4-24" x="157.579876" y="427.509515"/> + <use xlink:href="#glyph4-4" x="160.897484" y="427.509515"/> + <use xlink:href="#glyph4-3" x="165.878879" y="427.509515"/> + <use xlink:href="#glyph4-23" x="169.196487" y="427.509515"/> + <use xlink:href="#glyph4-10" x="176.947537" y="427.509515"/> + <use xlink:href="#glyph4-9" x="181.371015" y="427.509515"/> + <use xlink:href="#glyph4-15" x="186.35241" y="427.509515"/> + <use xlink:href="#glyph4-1" x="190.775888" y="427.509515"/> + <use xlink:href="#glyph4-21" x="195.199366" y="427.509515"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph4-35" x="203.956658" y="427.509515"/> + <use xlink:href="#glyph4-17" x="211.149791" y="427.509515"/> + <use xlink:href="#glyph4-3" x="216.131185" y="427.509515"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph4-6" x="222.995547" y="427.509515"/> + <use xlink:href="#glyph4-1" x="226.871072" y="427.509515"/> + <use xlink:href="#glyph4-15" x="231.29455" y="427.509515"/> + <use xlink:href="#glyph4-17" x="235.718028" y="427.509515"/> + <use xlink:href="#glyph4-3" x="240.699423" y="427.509515"/> + <use xlink:href="#glyph4-7" x="244.017031" y="427.509515"/> + <use xlink:href="#glyph4-11" x="246.786687" y="427.509515"/> + <use xlink:href="#glyph4-20" x="249.556342" y="427.509515"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph4-13" x="258.084489" y="427.509515"/> + <use xlink:href="#glyph4-4" x="263.065883" y="427.509515"/> + <use xlink:href="#glyph4-14" x="268.047278" y="427.509515"/> + <use xlink:href="#glyph4-7" x="270.816933" y="427.509515"/> + <use xlink:href="#glyph4-15" x="273.586588" y="427.509515"/> + <use xlink:href="#glyph4-7" x="278.010067" y="427.509515"/> + <use xlink:href="#glyph4-1" x="280.779722" y="427.509515"/> + <use xlink:href="#glyph4-6" x="285.2032" y="427.509515"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph4-13" x="292.635441" y="427.509515"/> + <use xlink:href="#glyph4-4" x="297.616835" y="427.509515"/> + <use xlink:href="#glyph4-6" x="302.598229" y="427.509515"/> + <use xlink:href="#glyph4-1" x="306.473754" y="427.509515"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph4-6" x="314.443985" y="427.509515"/> + <use xlink:href="#glyph4-4" x="318.31951" y="427.509515"/> + <use xlink:href="#glyph4-23" x="323.300905" y="427.509515"/> + <use xlink:href="#glyph4-1" x="331.051954" y="427.509515"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph4-7" x="339.022185" y="427.509515"/> + <use xlink:href="#glyph4-9" x="341.791841" y="427.509515"/> + <use xlink:href="#glyph4-15" x="346.773235" y="427.509515"/> + <use xlink:href="#glyph4-4" x="351.196713" y="427.509515"/> + <use xlink:href="#glyph4-23" x="356.178108" y="427.509515"/> + <use xlink:href="#glyph4-13" x="363.929157" y="427.509515"/> + <use xlink:href="#glyph4-10" x="368.910552" y="427.509515"/> + <use xlink:href="#glyph4-11" x="373.33403" y="427.509515"/> + <use xlink:href="#glyph4-7" x="376.103685" y="427.509515"/> + <use xlink:href="#glyph4-2" x="378.87334" y="427.509515"/> + <use xlink:href="#glyph4-7" x="383.854735" y="427.509515"/> + <use xlink:href="#glyph4-14" x="386.62439" y="427.509515"/> + <use xlink:href="#glyph4-7" x="389.394045" y="427.509515"/> + <use xlink:href="#glyph4-11" x="392.163701" y="427.509515"/> + <use xlink:href="#glyph4-20" x="394.933356" y="427.509515"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph4-25" x="399.277132" y="427.509515"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph4-11" x="405.573614" y="427.509515"/> + <use xlink:href="#glyph4-16" x="408.34327" y="427.509515"/> + <use xlink:href="#glyph4-1" x="413.324664" y="427.509515"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph4-15" x="421.294895" y="427.509515"/> + <use xlink:href="#glyph4-4" x="425.718373" y="427.509515"/> + <use xlink:href="#glyph4-6" x="430.699768" y="427.509515"/> + <use xlink:href="#glyph4-11" x="434.575293" y="427.509515"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph4-4" x="440.891701" y="427.509515"/> + <use xlink:href="#glyph4-24" x="445.873095" y="427.509515"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph4-5" x="452.747419" y="427.509515"/> + <use xlink:href="#glyph4-16" x="459.940553" y="427.509515"/> + <use xlink:href="#glyph4-7" x="464.921947" y="427.509515"/> + <use xlink:href="#glyph4-15" x="467.691603" y="427.509515"/> + <use xlink:href="#glyph4-16" x="472.115081" y="427.509515"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph4-3" x="480.643228" y="427.509515"/> + <use xlink:href="#glyph4-1" x="483.960837" y="427.509515"/> + <use xlink:href="#glyph4-19" x="488.384315" y="427.509515"/> + <use xlink:href="#glyph4-17" x="493.365709" y="427.509515"/> + <use xlink:href="#glyph4-7" x="498.347104" y="427.509515"/> + <use xlink:href="#glyph4-3" x="501.116759" y="427.509515"/> + <use xlink:href="#glyph4-1" x="504.434368" y="427.509515"/> + <use xlink:href="#glyph4-6" x="508.857846" y="427.509515"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph4-24" x="99.277636" y="439.462861"/> + <use xlink:href="#glyph4-17" x="102.595244" y="439.462861"/> + <use xlink:href="#glyph4-3" x="107.576639" y="439.462861"/> + <use xlink:href="#glyph4-11" x="110.894247" y="439.462861"/> + <use xlink:href="#glyph4-16" x="113.663903" y="439.462861"/> + <use xlink:href="#glyph4-1" x="118.645297" y="439.462861"/> + <use xlink:href="#glyph4-3" x="123.068775" y="439.462861"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph4-7" x="128.867118" y="439.462861"/> + <use xlink:href="#glyph4-9" x="131.636774" y="439.462861"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph4-18" x="136.229619" y="439.462861"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph4-1" x="141.061572" y="439.462861"/> + <use xlink:href="#glyph4-6" x="145.48505" y="439.462861"/> + <use xlink:href="#glyph4-11" x="149.360575" y="439.462861"/> + <use xlink:href="#glyph4-7" x="152.13023" y="439.462861"/> + <use xlink:href="#glyph4-8" x="154.899885" y="439.462861"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph4-10" x="159.831466" y="439.462861"/> + <use xlink:href="#glyph4-11" x="164.254944" y="439.462861"/> + <use xlink:href="#glyph4-7" x="167.024599" y="439.462861"/> + <use xlink:href="#glyph4-4" x="169.794255" y="439.462861"/> + <use xlink:href="#glyph4-9" x="174.775649" y="439.462861"/> + <use xlink:href="#glyph4-21" x="179.757044" y="439.462861"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph6-0" x="72" y="474.572689"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph6-1" x="93.516023" y="474.572689"/> + <use xlink:href="#glyph6-2" x="99.095845" y="474.572689"/> + <use xlink:href="#glyph6-3" x="107.071117" y="474.572689"/> + <use xlink:href="#glyph6-4" x="111.847674" y="474.572689"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph6-5" x="117.972569" y="474.572689"/> + <use xlink:href="#glyph6-6" x="125.144576" y="474.572689"/> + <use xlink:href="#glyph6-7" x="133.119849" y="474.572689"/> + <use xlink:href="#glyph6-8" x="141.095121" y="474.572689"/> + <use xlink:href="#glyph6-3" x="147.463864" y="474.572689"/> + <use xlink:href="#glyph6-9" x="152.240421" y="474.572689"/> + <use xlink:href="#glyph6-5" x="156.228057" y="474.572689"/> + <use xlink:href="#glyph6-2" x="163.400065" y="474.572689"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph7-0" x="72" y="499.419644"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph7-1" x="81.439792" y="499.419644"/> + <use xlink:href="#glyph7-2" x="86.285189" y="499.419644"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph7-2" x="96.357938" y="499.419644"/> + <use xlink:href="#glyph7-3" x="101.814466" y="499.419644"/> + <use xlink:href="#glyph7-4" x="105.448513" y="499.419644"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph7-5" x="110.643127" y="499.419644"/> + <use xlink:href="#glyph7-6" x="118.522353" y="499.419644"/> + <use xlink:href="#glyph7-1" x="122.767531" y="499.419644"/> + <use xlink:href="#glyph7-3" x="127.612927" y="499.419644"/> + <use xlink:href="#glyph7-6" x="131.246975" y="499.419644"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph7-7" x="140.119288" y="499.419644"/> + <use xlink:href="#glyph7-8" x="145.575815" y="499.419644"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph7-9" x="150.213864" y="499.419644"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph7-1" x="155.506695" y="499.419644"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph7-1" x="164.979227" y="499.419644"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph7-9" x="169.56271" y="499.419644"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph7-4" x="174.800976" y="499.419644"/> + <use xlink:href="#glyph7-10" x="180.257504" y="499.419644"/> + <use xlink:href="#glyph7-9" x="183.291333" y="499.419644"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph7-1" x="188.584164" y="499.419644"/> + <use xlink:href="#glyph7-11" x="193.429561" y="499.419644"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph7-12" x="203.513223" y="499.419644"/> + <use xlink:href="#glyph7-4" x="206.547053" y="499.419644"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph7-2" x="216.630715" y="499.419644"/> + <use xlink:href="#glyph7-1" x="222.087243" y="499.419644"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph7-8" x="231.570687" y="499.419644"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph7-13" x="241.054132" y="499.419644"/> + <use xlink:href="#glyph7-14" x="249.544489" y="499.419644"/> + <use xlink:href="#glyph7-10" x="255.001016" y="499.419644"/> + <use xlink:href="#glyph7-12" x="258.034845" y="499.419644"/> + <use xlink:href="#glyph7-15" x="261.068674" y="499.419644"/> + <use xlink:href="#glyph7-16" x="264.102504" y="499.419644"/> + <use xlink:href="#glyph7-17" x="267.736551" y="499.419644"/> + <use xlink:href="#glyph7-3" x="273.193078" y="499.419644"/> + <use xlink:href="#glyph7-15" x="276.827126" y="499.419644"/> + <use xlink:href="#glyph7-18" x="279.860955" y="499.419644"/> + <use xlink:href="#glyph7-19" x="285.317482" y="499.419644"/> + <use xlink:href="#glyph7-15" x="290.162879" y="499.419644"/> + <use xlink:href="#glyph7-17" x="293.196708" y="499.419644"/> + <use xlink:href="#glyph7-8" x="298.653235" y="499.419644"/> + <use xlink:href="#glyph7-10" x="303.498631" y="499.419644"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph7-4" x="311.170509" y="499.419644"/> + <use xlink:href="#glyph7-17" x="316.627036" y="499.419644"/> + <use xlink:href="#glyph7-1" x="322.083564" y="499.419644"/> + <use xlink:href="#glyph7-3" x="326.92896" y="499.419644"/> + <use xlink:href="#glyph7-8" x="330.563007" y="499.419644"/> + <use xlink:href="#glyph7-12" x="335.408404" y="499.419644"/> + <use xlink:href="#glyph7-15" x="338.442233" y="499.419644"/> + <use xlink:href="#glyph7-18" x="341.476062" y="499.419644"/> + <use xlink:href="#glyph7-20" x="346.93259" y="499.419644"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph7-1" x="357.016252" y="499.419644"/> + <use xlink:href="#glyph7-18" x="361.861649" y="499.419644"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph7-9" x="366.892567" y="499.419644"/> + <use xlink:href="#glyph7-15" x="372.349094" y="499.419644"/> + <use xlink:href="#glyph7-3" x="375.382923" y="499.419644"/> + <use xlink:href="#glyph7-4" x="379.016971" y="499.419644"/> + <use xlink:href="#glyph7-18" x="384.473498" y="499.419644"/> + <use xlink:href="#glyph7-13" x="389.930025" y="499.419644"/> + <use xlink:href="#glyph7-1" x="398.420382" y="499.419644"/> + <use xlink:href="#glyph7-18" x="403.265778" y="499.419644"/> + <use xlink:href="#glyph7-12" x="408.722306" y="499.419644"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph7-5" x="416.38327" y="499.419644"/> + <use xlink:href="#glyph7-7" x="424.262496" y="499.419644"/> + <use xlink:href="#glyph7-1" x="429.719023" y="499.419644"/> + <use xlink:href="#glyph7-3" x="434.56442" y="499.419644"/> + <use xlink:href="#glyph7-1" x="438.198467" y="499.419644"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph7-8" x="447.681911" y="499.419644"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph7-17" x="457.165356" y="499.419644"/> + <use xlink:href="#glyph7-3" x="462.621884" y="499.419644"/> + <use xlink:href="#glyph7-15" x="466.255931" y="499.419644"/> + <use xlink:href="#glyph7-18" x="469.28976" y="499.419644"/> + <use xlink:href="#glyph7-19" x="474.746287" y="499.419644"/> + <use xlink:href="#glyph7-15" x="479.591684" y="499.419644"/> + <use xlink:href="#glyph7-17" x="482.625513" y="499.419644"/> + <use xlink:href="#glyph7-8" x="488.08204" y="499.419644"/> + <use xlink:href="#glyph7-10" x="492.927437" y="499.419644"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph7-15" x="500.588401" y="499.419644"/> + <use xlink:href="#glyph7-6" x="503.62223" y="499.419644"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph7-8" x="512.505457" y="499.419644"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph7-5" x="521.988902" y="499.419644"/> + <use xlink:href="#glyph7-1" x="529.868127" y="499.419644"/> + <use xlink:href="#glyph7-2" x="534.713524" y="499.419644"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph7-6" x="72" y="512.963435"/> + <use xlink:href="#glyph7-15" x="76.245178" y="512.963435"/> + <use xlink:href="#glyph7-12" x="79.279008" y="512.963435"/> + <use xlink:href="#glyph7-1" x="82.312837" y="512.963435"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph7-21" x="90.00654" y="512.963435"/> + <use xlink:href="#glyph7-22" x="93.640588" y="512.963435"/> + <use xlink:href="#glyph7-23" x="99.097115" y="512.963435"/> + <use xlink:href="#glyph7-24" x="104.553642" y="512.963435"/> + <use xlink:href="#glyph7-25" x="108.18769" y="512.963435"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph7-26" x="114.670044" y="512.963435"/> + <use xlink:href="#glyph7-15" x="120.737703" y="512.963435"/> + <use xlink:href="#glyph7-13" x="123.771532" y="512.963435"/> + <use xlink:href="#glyph7-15" x="132.261889" y="512.963435"/> + <use xlink:href="#glyph7-10" x="135.295718" y="512.963435"/> + <use xlink:href="#glyph7-8" x="138.329547" y="512.963435"/> + <use xlink:href="#glyph7-3" x="143.174943" y="512.963435"/> + <use xlink:href="#glyph7-10" x="146.808991" y="512.963435"/> + <use xlink:href="#glyph7-27" x="149.84282" y="512.963435"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph7-12" x="158.147655" y="512.963435"/> + <use xlink:href="#glyph7-4" x="161.181484" y="512.963435"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph7-8" x="169.486319" y="512.963435"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph7-13" x="177.180022" y="512.963435"/> + <use xlink:href="#glyph7-14" x="185.670379" y="512.963435"/> + <use xlink:href="#glyph7-10" x="191.126906" y="512.963435"/> + <use xlink:href="#glyph7-12" x="194.160735" y="512.963435"/> + <use xlink:href="#glyph7-15" x="197.194565" y="512.963435"/> + <use xlink:href="#glyph7-16" x="200.228394" y="512.963435"/> + <use xlink:href="#glyph7-17" x="203.862441" y="512.963435"/> + <use xlink:href="#glyph7-3" x="209.318969" y="512.963435"/> + <use xlink:href="#glyph7-15" x="212.953016" y="512.963435"/> + <use xlink:href="#glyph7-18" x="215.986845" y="512.963435"/> + <use xlink:href="#glyph7-19" x="221.443372" y="512.963435"/> + <use xlink:href="#glyph7-15" x="226.288769" y="512.963435"/> + <use xlink:href="#glyph7-17" x="229.322598" y="512.963435"/> + <use xlink:href="#glyph7-8" x="234.779125" y="512.963435"/> + <use xlink:href="#glyph7-10" x="239.624522" y="512.963435"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph7-28" x="245.517571" y="512.963435"/> + <use xlink:href="#glyph7-26" x="253.396797" y="512.963435"/> + <use xlink:href="#glyph7-29" x="259.464455" y="512.963435"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph7-3" x="265.041026" y="512.963435"/> + <use xlink:href="#glyph7-1" x="268.675074" y="512.963435"/> + <use xlink:href="#glyph7-19" x="273.52047" y="512.963435"/> + <use xlink:href="#glyph7-1" x="278.365866" y="512.963435"/> + <use xlink:href="#glyph7-18" x="283.211263" y="512.963435"/> + <use xlink:href="#glyph7-12" x="288.66779" y="512.963435"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph7-17" x="294.549927" y="512.963435"/> + <use xlink:href="#glyph7-3" x="300.006454" y="512.963435"/> + <use xlink:href="#glyph7-4" x="303.640501" y="512.963435"/> + <use xlink:href="#glyph7-17" x="309.097029" y="512.963435"/> + <use xlink:href="#glyph7-4" x="314.553556" y="512.963435"/> + <use xlink:href="#glyph7-6" x="320.010083" y="512.963435"/> + <use xlink:href="#glyph7-8" x="324.255262" y="512.963435"/> + <use xlink:href="#glyph7-10" x="329.100658" y="512.963435"/> + <use xlink:href="#glyph7-6" x="332.134487" y="512.963435"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph7-21" x="339.227973" y="512.963435"/> + <use xlink:href="#glyph7-30" x="342.86202" y="512.963435"/> + <use xlink:href="#glyph7-30" x="348.318548" y="512.963435"/> + <use xlink:href="#glyph7-29" x="353.775075" y="512.963435"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph7-30" x="358.00934" y="512.963435"/> + <use xlink:href="#glyph7-31" x="363.465868" y="512.963435"/> + <use xlink:href="#glyph7-29" x="368.922395" y="512.963435"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph7-31" x="373.145747" y="512.963435"/> + <use xlink:href="#glyph7-31" x="378.602275" y="512.963435"/> + <use xlink:href="#glyph7-29" x="384.058802" y="512.963435"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph7-22" x="388.293067" y="512.963435"/> + <use xlink:href="#glyph7-23" x="393.749595" y="512.963435"/> + <use xlink:href="#glyph7-29" x="399.206122" y="512.963435"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph7-32" x="403.429474" y="512.963435"/> + <use xlink:href="#glyph7-31" x="408.886002" y="512.963435"/> + <use xlink:href="#glyph7-24" x="414.342529" y="512.963435"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph7-8" x="420.835797" y="512.963435"/> + <use xlink:href="#glyph7-18" x="425.681193" y="512.963435"/> + <use xlink:href="#glyph7-11" x="431.13772" y="512.963435"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph7-2" x="439.442555" y="512.963435"/> + <use xlink:href="#glyph7-3" x="444.899083" y="512.963435"/> + <use xlink:href="#glyph7-4" x="448.53313" y="512.963435"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph7-5" x="453.727744" y="512.963435"/> + <use xlink:href="#glyph7-6" x="461.606969" y="512.963435"/> + <use xlink:href="#glyph7-1" x="465.852148" y="512.963435"/> + <use xlink:href="#glyph7-3" x="470.697544" y="512.963435"/> + <use xlink:href="#glyph7-6" x="474.331591" y="512.963435"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph7-10" x="481.414164" y="512.963435"/> + <use xlink:href="#glyph7-15" x="484.447993" y="512.963435"/> + <use xlink:href="#glyph7-33" x="487.481822" y="512.963435"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph7-1" x="492.840132" y="512.963435"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph7-34" x="500.522923" y="512.963435"/> + <use xlink:href="#glyph7-35" x="504.15697" y="512.963435"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph7-36" x="513.684067" y="512.963435"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph7-21" x="521.988902" y="512.963435"/> + <use xlink:href="#glyph7-22" x="525.622949" y="512.963435"/> + <use xlink:href="#glyph7-37" x="531.079476" y="512.963435"/> + <use xlink:href="#glyph7-24" x="536.536004" y="512.963435"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph7-8" x="72" y="526.517229"/> + <use xlink:href="#glyph7-18" x="76.845396" y="526.517229"/> + <use xlink:href="#glyph7-11" x="82.301924" y="526.517229"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph7-38" x="89.821018" y="526.517229"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph7-15" x="95.89959" y="526.517229"/> + <use xlink:href="#glyph7-3" x="98.933419" y="526.517229"/> + <use xlink:href="#glyph7-1" x="102.567466" y="526.517229"/> + <use xlink:href="#glyph7-39" x="107.412863" y="526.517229"/> + <use xlink:href="#glyph7-4" x="111.04691" y="526.517229"/> + <use xlink:href="#glyph7-40" x="116.503437" y="526.517229"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph7-22" x="124.022532" y="526.517229"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph7-21" x="131.563453" y="526.517229"/> + <use xlink:href="#glyph7-30" x="135.1975" y="526.517229"/> + <use xlink:href="#glyph7-41" x="140.654028" y="526.517229"/> + <use xlink:href="#glyph7-24" x="146.110555" y="526.517229"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph7-8" x="151.80717" y="526.517229"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph7-11" x="156.663479" y="526.517229"/> + <use xlink:href="#glyph7-9" x="162.120007" y="526.517229"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph7-4" x="167.369186" y="526.517229"/> + <use xlink:href="#glyph7-19" x="172.825713" y="526.517229"/> + <use xlink:href="#glyph7-8" x="177.67111" y="526.517229"/> + <use xlink:href="#glyph7-12" x="182.516506" y="526.517229"/> + <use xlink:href="#glyph7-1" x="185.550335" y="526.517229"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph7-8" x="192.458299" y="526.517229"/> + <use xlink:href="#glyph7-18" x="197.303695" y="526.517229"/> + <use xlink:href="#glyph7-11" x="202.760223" y="526.517229"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph7-6" x="210.29023" y="526.517229"/> + <use xlink:href="#glyph7-14" x="214.535409" y="526.517229"/> + <use xlink:href="#glyph7-17" x="219.991936" y="526.517229"/> + <use xlink:href="#glyph7-17" x="225.448464" y="526.517229"/> + <use xlink:href="#glyph7-4" x="230.904991" y="526.517229"/> + <use xlink:href="#glyph7-3" x="236.361518" y="526.517229"/> + <use xlink:href="#glyph7-12" x="239.995566" y="526.517229"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph7-8" x="245.102875" y="526.517229"/> + <use xlink:href="#glyph7-2" x="249.948272" y="526.517229"/> + <use xlink:href="#glyph7-6" x="255.404799" y="526.517229"/> + <use xlink:href="#glyph7-12" x="259.649977" y="526.517229"/> + <use xlink:href="#glyph7-3" x="262.683807" y="526.517229"/> + <use xlink:href="#glyph7-8" x="266.317854" y="526.517229"/> + <use xlink:href="#glyph7-19" x="271.16325" y="526.517229"/> + <use xlink:href="#glyph7-12" x="276.008646" y="526.517229"/> + <use xlink:href="#glyph7-15" x="279.042476" y="526.517229"/> + <use xlink:href="#glyph7-4" x="282.076305" y="526.517229"/> + <use xlink:href="#glyph7-18" x="287.532832" y="526.517229"/> + <use xlink:href="#glyph7-6" x="292.98936" y="526.517229"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph7-39" x="299.308018" y="526.517229"/> + <use xlink:href="#glyph7-4" x="302.942066" y="526.517229"/> + <use xlink:href="#glyph7-3" x="308.398593" y="526.517229"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph7-19" x="314.106121" y="526.517229"/> + <use xlink:href="#glyph7-3" x="318.951517" y="526.517229"/> + <use xlink:href="#glyph7-4" x="322.585564" y="526.517229"/> + <use xlink:href="#glyph7-6" x="328.042092" y="526.517229"/> + <use xlink:href="#glyph7-6" x="332.28727" y="526.517229"/> + <use xlink:href="#glyph7-16" x="336.532448" y="526.517229"/> + <use xlink:href="#glyph7-17" x="340.166496" y="526.517229"/> + <use xlink:href="#glyph7-3" x="345.623023" y="526.517229"/> + <use xlink:href="#glyph7-15" x="349.25707" y="526.517229"/> + <use xlink:href="#glyph7-18" x="352.290899" y="526.517229"/> + <use xlink:href="#glyph7-19" x="357.747427" y="526.517229"/> + <use xlink:href="#glyph7-15" x="362.592823" y="526.517229"/> + <use xlink:href="#glyph7-17" x="365.626652" y="526.517229"/> + <use xlink:href="#glyph7-8" x="371.08318" y="526.517229"/> + <use xlink:href="#glyph7-10" x="375.928576" y="526.517229"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph7-19" x="381.024973" y="526.517229"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph7-4" x="385.881282" y="526.517229"/> + <use xlink:href="#glyph7-13" x="391.33781" y="526.517229"/> + <use xlink:href="#glyph7-13" x="399.828166" y="526.517229"/> + <use xlink:href="#glyph7-14" x="408.318523" y="526.517229"/> + <use xlink:href="#glyph7-18" x="413.77505" y="526.517229"/> + <use xlink:href="#glyph7-15" x="419.231578" y="526.517229"/> + <use xlink:href="#glyph7-19" x="422.265407" y="526.517229"/> + <use xlink:href="#glyph7-8" x="427.110803" y="526.517229"/> + <use xlink:href="#glyph7-12" x="431.9562" y="526.517229"/> + <use xlink:href="#glyph7-15" x="434.990029" y="526.517229"/> + <use xlink:href="#glyph7-4" x="438.023858" y="526.517229"/> + <use xlink:href="#glyph7-18" x="443.480385" y="526.517229"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph7-42" x="450.99948" y="526.517229"/> + <use xlink:href="#glyph7-1" x="454.633527" y="526.517229"/> + <use xlink:href="#glyph7-25" x="459.478924" y="526.517229"/> + <use xlink:href="#glyph7-20" x="462.207187" y="526.517229"/> + <use xlink:href="#glyph7-25" x="467.663715" y="526.517229"/> + <use xlink:href="#glyph7-29" x="470.391979" y="526.517229"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph8-0" x="475.18286" y="526.517229"/> + <use xlink:href="#glyph8-1" x="480.912213" y="526.517229"/> + <use xlink:href="#glyph8-2" x="486.641567" y="526.517229"/> + <use xlink:href="#glyph8-3" x="492.370921" y="526.517229"/> + <use xlink:href="#glyph8-4" x="498.100275" y="526.517229"/> + <use xlink:href="#glyph8-5" x="503.829628" y="526.517229"/> + <use xlink:href="#glyph8-2" x="509.558982" y="526.517229"/> + <use xlink:href="#glyph8-2" x="515.288336" y="526.517229"/> + <use xlink:href="#glyph8-6" x="521.01769" y="526.517229"/> + <use xlink:href="#glyph8-7" x="526.747044" y="526.517229"/> + <use xlink:href="#glyph8-5" x="532.476397" y="526.517229"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph7-43" x="538.190497" y="526.517229"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph7-8" x="72" y="540.06102"/> + <use xlink:href="#glyph7-18" x="76.845396" y="540.06102"/> + <use xlink:href="#glyph7-11" x="82.301924" y="540.06102"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph7-17" x="90.759541" y="540.06102"/> + <use xlink:href="#glyph7-3" x="96.216069" y="540.06102"/> + <use xlink:href="#glyph7-4" x="99.850116" y="540.06102"/> + <use xlink:href="#glyph7-12" x="105.306643" y="540.06102"/> + <use xlink:href="#glyph7-1" x="108.340472" y="540.06102"/> + <use xlink:href="#glyph7-19" x="113.185869" y="540.06102"/> + <use xlink:href="#glyph7-12" x="118.031265" y="540.06102"/> + <use xlink:href="#glyph7-15" x="121.065094" y="540.06102"/> + <use xlink:href="#glyph7-4" x="124.098924" y="540.06102"/> + <use xlink:href="#glyph7-18" x="129.555451" y="540.06102"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph7-42" x="138.023982" y="540.06102"/> + <use xlink:href="#glyph7-39" x="141.658029" y="540.06102"/> + <use xlink:href="#glyph7-4" x="145.292076" y="540.06102"/> + <use xlink:href="#glyph7-3" x="150.748603" y="540.06102"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph7-39" x="157.394654" y="540.06102"/> + <use xlink:href="#glyph7-3" x="161.028701" y="540.06102"/> + <use xlink:href="#glyph7-8" x="164.662748" y="540.06102"/> + <use xlink:href="#glyph7-13" x="169.508145" y="540.06102"/> + <use xlink:href="#glyph7-1" x="177.998501" y="540.06102"/> + <use xlink:href="#glyph7-6" x="182.843898" y="540.06102"/> + <use xlink:href="#glyph7-43" x="187.089076" y="540.06102"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph7-12" x="193.735126" y="540.06102"/> + <use xlink:href="#glyph7-4" x="196.768956" y="540.06102"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph7-5" x="205.226573" y="540.06102"/> + <use xlink:href="#glyph7-1" x="213.105799" y="540.06102"/> + <use xlink:href="#glyph7-2" x="217.951195" y="540.06102"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph7-17" x="226.419725" y="540.06102"/> + <use xlink:href="#glyph7-3" x="231.876253" y="540.06102"/> + <use xlink:href="#glyph7-4" x="235.5103" y="540.06102"/> + <use xlink:href="#glyph7-20" x="240.966827" y="540.06102"/> + <use xlink:href="#glyph7-3" x="246.423355" y="540.06102"/> + <use xlink:href="#glyph7-8" x="250.057402" y="540.06102"/> + <use xlink:href="#glyph7-13" x="254.902798" y="540.06102"/> + <use xlink:href="#glyph7-13" x="263.393155" y="540.06102"/> + <use xlink:href="#glyph7-1" x="271.883512" y="540.06102"/> + <use xlink:href="#glyph7-3" x="276.728908" y="540.06102"/> + <use xlink:href="#glyph7-6" x="280.362955" y="540.06102"/> + <use xlink:href="#glyph7-25" x="284.608134" y="540.06102"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph7-44" x="291.55975" y="540.06102"/> + <use xlink:href="#glyph7-1" x="299.438975" y="540.06102"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph7-9" x="304.022458" y="540.06102"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph7-1" x="309.31529" y="540.06102"/> + <use xlink:href="#glyph7-3" x="314.160686" y="540.06102"/> + <use xlink:href="#glyph7-12" x="317.794733" y="540.06102"/> + <use xlink:href="#glyph7-7" x="320.828562" y="540.06102"/> + <use xlink:href="#glyph7-1" x="326.28509" y="540.06102"/> + <use xlink:href="#glyph7-10" x="331.130486" y="540.06102"/> + <use xlink:href="#glyph7-1" x="334.164315" y="540.06102"/> + <use xlink:href="#glyph7-6" x="339.009712" y="540.06102"/> + <use xlink:href="#glyph7-6" x="343.25489" y="540.06102"/> + <use xlink:href="#glyph7-29" x="347.500068" y="540.06102"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph7-18" x="353.305814" y="540.06102"/> + <use xlink:href="#glyph7-4" x="358.762341" y="540.06102"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph7-1" x="367.219958" y="540.06102"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph7-40" x="371.912572" y="540.06102"/> + <use xlink:href="#glyph7-15" x="377.369099" y="540.06102"/> + <use xlink:href="#glyph7-6" x="380.402929" y="540.06102"/> + <use xlink:href="#glyph7-12" x="384.648107" y="540.06102"/> + <use xlink:href="#glyph7-15" x="387.681936" y="540.06102"/> + <use xlink:href="#glyph7-18" x="390.715765" y="540.06102"/> + <use xlink:href="#glyph7-20" x="396.172293" y="540.06102"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph7-2" x="404.62991" y="540.06102"/> + <use xlink:href="#glyph7-3" x="410.086438" y="540.06102"/> + <use xlink:href="#glyph7-4" x="413.720485" y="540.06102"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph7-5" x="418.915099" y="540.06102"/> + <use xlink:href="#glyph7-6" x="426.794325" y="540.06102"/> + <use xlink:href="#glyph7-1" x="431.039503" y="540.06102"/> + <use xlink:href="#glyph7-3" x="435.884899" y="540.06102"/> + <use xlink:href="#glyph7-6" x="439.518946" y="540.06102"/> + <use xlink:href="#glyph7-29" x="443.764125" y="540.06102"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph7-15" x="449.558957" y="540.06102"/> + <use xlink:href="#glyph7-18" x="452.592786" y="540.06102"/> + <use xlink:href="#glyph7-19" x="458.049314" y="540.06102"/> + <use xlink:href="#glyph7-10" x="462.89471" y="540.06102"/> + <use xlink:href="#glyph7-14" x="465.928539" y="540.06102"/> + <use xlink:href="#glyph7-11" x="471.385067" y="540.06102"/> + <use xlink:href="#glyph7-15" x="476.841594" y="540.06102"/> + <use xlink:href="#glyph7-18" x="479.875423" y="540.06102"/> + <use xlink:href="#glyph7-20" x="485.331951" y="540.06102"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph7-18" x="493.800481" y="540.06102"/> + <use xlink:href="#glyph7-1" x="499.257008" y="540.06102"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph7-5" x="503.840491" y="540.06102"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph7-8" x="514.720807" y="540.06102"/> + <use xlink:href="#glyph7-3" x="519.566203" y="540.06102"/> + <use xlink:href="#glyph7-19" x="523.200251" y="540.06102"/> + <use xlink:href="#glyph7-7" x="528.045647" y="540.06102"/> + <use xlink:href="#glyph7-15" x="533.502174" y="540.06102"/> + <use xlink:href="#glyph7-16" x="536.536004" y="540.06102"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph7-12" x="72" y="553.614814"/> + <use xlink:href="#glyph7-1" x="75.033829" y="553.614814"/> + <use xlink:href="#glyph7-19" x="79.879226" y="553.614814"/> + <use xlink:href="#glyph7-12" x="84.724622" y="553.614814"/> + <use xlink:href="#glyph7-14" x="87.758451" y="553.614814"/> + <use xlink:href="#glyph7-3" x="93.214979" y="553.614814"/> + <use xlink:href="#glyph7-1" x="96.849026" y="553.614814"/> + <use xlink:href="#glyph7-6" x="101.694422" y="553.614814"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph7-10" x="108.58056" y="553.614814"/> + <use xlink:href="#glyph7-15" x="111.614389" y="553.614814"/> + <use xlink:href="#glyph7-33" x="114.648218" y="553.614814"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph7-1" x="120.006528" y="553.614814"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph7-34" x="127.492884" y="553.614814"/> + <use xlink:href="#glyph7-35" x="131.126931" y="553.614814"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph7-36" x="140.44668" y="553.614814"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph7-21" x="148.544166" y="553.614814"/> + <use xlink:href="#glyph7-31" x="152.178214" y="553.614814"/> + <use xlink:href="#glyph7-22" x="157.634741" y="553.614814"/> + <use xlink:href="#glyph7-24" x="163.091268" y="553.614814"/> + <use xlink:href="#glyph7-29" x="166.725316" y="553.614814"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph7-45" x="172.127278" y="553.614814"/> + <use xlink:href="#glyph7-4" x="180.006503" y="553.614814"/> + <use xlink:href="#glyph7-4" x="185.463031" y="553.614814"/> + <use xlink:href="#glyph7-20" x="190.919558" y="553.614814"/> + <use xlink:href="#glyph7-10" x="196.376086" y="553.614814"/> + <use xlink:href="#glyph7-1" x="199.409915" y="553.614814"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph7-46" x="206.89627" y="553.614814"/> + <use xlink:href="#glyph7-7" x="214.175278" y="553.614814"/> + <use xlink:href="#glyph7-3" x="219.631805" y="553.614814"/> + <use xlink:href="#glyph7-4" x="223.265853" y="553.614814"/> + <use xlink:href="#glyph7-13" x="228.72238" y="553.614814"/> + <use xlink:href="#glyph7-1" x="237.212737" y="553.614814"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph7-21" x="244.710005" y="553.614814"/> + <use xlink:href="#glyph7-22" x="248.344053" y="553.614814"/> + <use xlink:href="#glyph7-22" x="253.80058" y="553.614814"/> + <use xlink:href="#glyph7-24" x="259.257107" y="553.614814"/> + <use xlink:href="#glyph7-29" x="262.891155" y="553.614814"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph7-8" x="268.282204" y="553.614814"/> + <use xlink:href="#glyph7-18" x="273.1276" y="553.614814"/> + <use xlink:href="#glyph7-11" x="278.584127" y="553.614814"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph7-28" x="286.692527" y="553.614814"/> + <use xlink:href="#glyph7-47" x="294.571753" y="553.614814"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph7-21" x="303.291283" y="553.614814"/> + <use xlink:href="#glyph7-31" x="306.925331" y="553.614814"/> + <use xlink:href="#glyph7-37" x="312.381858" y="553.614814"/> + <use xlink:href="#glyph7-24" x="317.838385" y="553.614814"/> + <use xlink:href="#glyph7-29" x="321.472433" y="553.614814"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph7-7" x="326.863482" y="553.614814"/> + <use xlink:href="#glyph7-8" x="332.320009" y="553.614814"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph7-9" x="336.958057" y="553.614814"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph7-1" x="342.250889" y="553.614814"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph7-8" x="349.726332" y="553.614814"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph7-13" x="357.2236" y="553.614814"/> + <use xlink:href="#glyph7-14" x="365.713957" y="553.614814"/> + <use xlink:href="#glyph7-10" x="371.170484" y="553.614814"/> + <use xlink:href="#glyph7-12" x="374.204314" y="553.614814"/> + <use xlink:href="#glyph7-15" x="377.238143" y="553.614814"/> + <use xlink:href="#glyph7-16" x="380.271972" y="553.614814"/> + <use xlink:href="#glyph7-17" x="383.906019" y="553.614814"/> + <use xlink:href="#glyph7-3" x="389.362547" y="553.614814"/> + <use xlink:href="#glyph7-15" x="392.996594" y="553.614814"/> + <use xlink:href="#glyph7-18" x="396.030423" y="553.614814"/> + <use xlink:href="#glyph7-19" x="401.486951" y="553.614814"/> + <use xlink:href="#glyph7-15" x="406.332347" y="553.614814"/> + <use xlink:href="#glyph7-17" x="409.366176" y="553.614814"/> + <use xlink:href="#glyph7-8" x="414.822703" y="553.614814"/> + <use xlink:href="#glyph7-10" x="419.6681" y="553.614814"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph7-28" x="425.353801" y="553.614814"/> + <use xlink:href="#glyph7-26" x="433.233027" y="553.614814"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph7-19" x="441.952558" y="553.614814"/> + <use xlink:href="#glyph7-4" x="446.797954" y="553.614814"/> + <use xlink:href="#glyph7-18" x="452.254481" y="553.614814"/> + <use xlink:href="#glyph7-6" x="457.711009" y="553.614814"/> + <use xlink:href="#glyph7-12" x="461.956187" y="553.614814"/> + <use xlink:href="#glyph7-3" x="464.990016" y="553.614814"/> + <use xlink:href="#glyph7-14" x="468.624064" y="553.614814"/> + <use xlink:href="#glyph7-19" x="474.080591" y="553.614814"/> + <use xlink:href="#glyph7-12" x="478.925987" y="553.614814"/> + <use xlink:href="#glyph7-15" x="481.959817" y="553.614814"/> + <use xlink:href="#glyph7-4" x="484.993646" y="553.614814"/> + <use xlink:href="#glyph7-18" x="490.450173" y="553.614814"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph7-12" x="498.54766" y="553.614814"/> + <use xlink:href="#glyph7-7" x="501.581489" y="553.614814"/> + <use xlink:href="#glyph7-8" x="507.038017" y="553.614814"/> + <use xlink:href="#glyph7-12" x="511.883413" y="553.614814"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph7-20" x="517.569114" y="553.614814"/> + <use xlink:href="#glyph7-15" x="523.025642" y="553.614814"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph7-9" x="525.797558" y="553.614814"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph7-1" x="531.090389" y="553.614814"/> + <use xlink:href="#glyph7-6" x="535.935786" y="553.614814"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph7-8" x="72" y="567.168608"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph7-2" x="80.435791" y="567.168608"/> + <use xlink:href="#glyph7-3" x="85.892319" y="567.168608"/> + <use xlink:href="#glyph7-4" x="89.526366" y="567.168608"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph7-5" x="94.72098" y="567.168608"/> + <use xlink:href="#glyph7-6" x="102.600206" y="567.168608"/> + <use xlink:href="#glyph7-1" x="106.845384" y="567.168608"/> + <use xlink:href="#glyph7-3" x="111.69078" y="567.168608"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph7-16" x="115.106566" y="567.168608"/> + <use xlink:href="#glyph7-2" x="118.740614" y="567.168608"/> + <use xlink:href="#glyph7-8" x="124.197141" y="567.168608"/> + <use xlink:href="#glyph7-6" x="129.042537" y="567.168608"/> + <use xlink:href="#glyph7-1" x="133.287716" y="567.168608"/> + <use xlink:href="#glyph7-11" x="138.133112" y="567.168608"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph7-28" x="147.180035" y="567.168608"/> + <use xlink:href="#glyph7-26" x="155.05926" y="567.168608"/> + <use xlink:href="#glyph7-29" x="161.126919" y="567.168608"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph7-12" x="167.45649" y="567.168608"/> + <use xlink:href="#glyph7-27" x="170.49032" y="567.168608"/> + <use xlink:href="#glyph7-17" x="175.946847" y="567.168608"/> + <use xlink:href="#glyph7-15" x="181.403374" y="567.168608"/> + <use xlink:href="#glyph7-19" x="184.437204" y="567.168608"/> + <use xlink:href="#glyph7-8" x="189.2826" y="567.168608"/> + <use xlink:href="#glyph7-10" x="194.127996" y="567.168608"/> + <use xlink:href="#glyph7-10" x="197.161826" y="567.168608"/> + <use xlink:href="#glyph7-27" x="200.195655" y="567.168608"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph7-19" x="209.25349" y="567.168608"/> + <use xlink:href="#glyph7-8" x="214.098887" y="567.168608"/> + <use xlink:href="#glyph7-10" x="218.944283" y="567.168608"/> + <use xlink:href="#glyph7-10" x="221.978112" y="567.168608"/> + <use xlink:href="#glyph7-1" x="225.011941" y="567.168608"/> + <use xlink:href="#glyph7-11" x="229.857338" y="567.168608"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph7-48" x="238.90426" y="567.168608"/> + <use xlink:href="#glyph7-3" x="246.183268" y="567.168608"/> + <use xlink:href="#glyph7-4" x="249.817315" y="567.168608"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph7-5" x="255.011929" y="567.168608"/> + <use xlink:href="#glyph7-6" x="262.891155" y="567.168608"/> + <use xlink:href="#glyph7-1" x="267.136333" y="567.168608"/> + <use xlink:href="#glyph7-3" x="271.981729" y="567.168608"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph7-49" x="279.206171" y="567.168608"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph7-1" x="286.823484" y="567.168608"/> + <use xlink:href="#glyph7-3" x="291.66888" y="567.168608"/> + <use xlink:href="#glyph7-18" x="295.302927" y="567.168608"/> + <use xlink:href="#glyph7-1" x="300.759455" y="567.168608"/> + <use xlink:href="#glyph7-10" x="305.604851" y="567.168608"/> + <use xlink:href="#glyph7-29" x="308.63868" y="567.168608"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph7-12" x="315.1756" y="567.168608"/> + <use xlink:href="#glyph7-7" x="318.209429" y="567.168608"/> + <use xlink:href="#glyph7-1" x="323.665957" y="567.168608"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph9-0" x="332.012783" y="567.168608"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph9-1" x="336.650831" y="567.168608"/> + <use xlink:href="#glyph9-2" x="341.496228" y="567.168608"/> + <use xlink:href="#glyph9-3" x="346.341624" y="567.168608"/> + <use xlink:href="#glyph9-4" x="349.375453" y="567.168608"/> + <use xlink:href="#glyph9-5" x="354.831981" y="567.168608"/> + <use xlink:href="#glyph9-6" x="359.077159" y="567.168608"/> + <use xlink:href="#glyph9-7" x="362.110988" y="567.168608"/> + <use xlink:href="#glyph9-0" x="366.956385" y="567.168608"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph7-19" x="375.374921" y="567.168608"/> + <use xlink:href="#glyph7-4" x="380.220318" y="567.168608"/> + <use xlink:href="#glyph7-18" x="385.676845" y="567.168608"/> + <use xlink:href="#glyph7-12" x="391.133372" y="567.168608"/> + <use xlink:href="#glyph7-3" x="394.167202" y="567.168608"/> + <use xlink:href="#glyph7-4" x="397.801249" y="567.168608"/> + <use xlink:href="#glyph7-10" x="403.257776" y="567.168608"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph7-12" x="409.882" y="567.168608"/> + <use xlink:href="#glyph7-4" x="412.91583" y="567.168608"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph7-13" x="421.973665" y="567.168608"/> + <use xlink:href="#glyph7-8" x="430.464022" y="567.168608"/> + <use xlink:href="#glyph7-18" x="435.309418" y="567.168608"/> + <use xlink:href="#glyph7-8" x="440.765946" y="567.168608"/> + <use xlink:href="#glyph7-20" x="445.611342" y="567.168608"/> + <use xlink:href="#glyph7-1" x="451.067869" y="567.168608"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph7-12" x="459.514574" y="567.168608"/> + <use xlink:href="#glyph7-7" x="462.548403" y="567.168608"/> + <use xlink:href="#glyph7-1" x="468.00493" y="567.168608"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph7-17" x="476.451635" y="567.168608"/> + <use xlink:href="#glyph7-3" x="481.908162" y="567.168608"/> + <use xlink:href="#glyph7-4" x="485.542209" y="567.168608"/> + <use xlink:href="#glyph7-12" x="490.998737" y="567.168608"/> + <use xlink:href="#glyph7-1" x="494.032566" y="567.168608"/> + <use xlink:href="#glyph7-19" x="498.877962" y="567.168608"/> + <use xlink:href="#glyph7-12" x="503.723359" y="567.168608"/> + <use xlink:href="#glyph7-15" x="506.757188" y="567.168608"/> + <use xlink:href="#glyph7-4" x="509.791017" y="567.168608"/> + <use xlink:href="#glyph7-18" x="515.247545" y="567.168608"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph7-8" x="524.294467" y="567.168608"/> + <use xlink:href="#glyph7-18" x="529.139863" y="567.168608"/> + <use xlink:href="#glyph7-11" x="534.596391" y="567.168608"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph7-39" x="72" y="580.7124"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph7-8" x="75.53583" y="580.7124"/> + <use xlink:href="#glyph7-15" x="80.381226" y="580.7124"/> + <use xlink:href="#glyph7-3" x="83.415055" y="580.7124"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph7-16" x="86.830841" y="580.7124"/> + <use xlink:href="#glyph7-6" x="90.464889" y="580.7124"/> + <use xlink:href="#glyph7-7" x="94.710067" y="580.7124"/> + <use xlink:href="#glyph7-8" x="100.166594" y="580.7124"/> + <use xlink:href="#glyph7-3" x="105.011991" y="580.7124"/> + <use xlink:href="#glyph7-15" x="108.646038" y="580.7124"/> + <use xlink:href="#glyph7-18" x="111.679867" y="580.7124"/> + <use xlink:href="#glyph7-20" x="117.136395" y="580.7124"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph7-4" x="125.310273" y="580.7124"/> + <use xlink:href="#glyph7-39" x="130.7668" y="580.7124"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph7-8" x="137.129111" y="580.7124"/> + <use xlink:href="#glyph7-10" x="141.974507" y="580.7124"/> + <use xlink:href="#glyph7-10" x="145.008337" y="580.7124"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph7-6" x="150.77043" y="580.7124"/> + <use xlink:href="#glyph7-27" x="155.015608" y="580.7124"/> + <use xlink:href="#glyph7-6" x="160.472135" y="580.7124"/> + <use xlink:href="#glyph7-12" x="164.717314" y="580.7124"/> + <use xlink:href="#glyph7-1" x="167.751143" y="580.7124"/> + <use xlink:href="#glyph7-13" x="172.596539" y="580.7124"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph7-3" x="183.815159" y="580.7124"/> + <use xlink:href="#glyph7-1" x="187.449207" y="580.7124"/> + <use xlink:href="#glyph7-6" x="192.294603" y="580.7124"/> + <use xlink:href="#glyph7-4" x="196.539781" y="580.7124"/> + <use xlink:href="#glyph7-14" x="201.996309" y="580.7124"/> + <use xlink:href="#glyph7-3" x="207.452836" y="580.7124"/> + <use xlink:href="#glyph7-19" x="211.086883" y="580.7124"/> + <use xlink:href="#glyph7-1" x="215.93228" y="580.7124"/> + <use xlink:href="#glyph7-6" x="220.777676" y="580.7124"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph7-8" x="227.740205" y="580.7124"/> + <use xlink:href="#glyph7-13" x="232.585601" y="580.7124"/> + <use xlink:href="#glyph7-4" x="241.075958" y="580.7124"/> + <use xlink:href="#glyph7-18" x="246.532485" y="580.7124"/> + <use xlink:href="#glyph7-20" x="251.989013" y="580.7124"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph7-2" x="260.173804" y="580.7124"/> + <use xlink:href="#glyph7-3" x="265.630331" y="580.7124"/> + <use xlink:href="#glyph7-4" x="269.264379" y="580.7124"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph7-5" x="274.458993" y="580.7124"/> + <use xlink:href="#glyph7-6" x="282.338218" y="580.7124"/> + <use xlink:href="#glyph7-1" x="286.583397" y="580.7124"/> + <use xlink:href="#glyph7-3" x="291.428793" y="580.7124"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph7-17" x="297.780191" y="580.7124"/> + <use xlink:href="#glyph7-3" x="303.236718" y="580.7124"/> + <use xlink:href="#glyph7-15" x="306.870765" y="580.7124"/> + <use xlink:href="#glyph7-18" x="309.904595" y="580.7124"/> + <use xlink:href="#glyph7-19" x="315.361122" y="580.7124"/> + <use xlink:href="#glyph7-15" x="320.206518" y="580.7124"/> + <use xlink:href="#glyph7-17" x="323.240348" y="580.7124"/> + <use xlink:href="#glyph7-8" x="328.696875" y="580.7124"/> + <use xlink:href="#glyph7-10" x="333.542271" y="580.7124"/> + <use xlink:href="#glyph7-6" x="336.576101" y="580.7124"/> + <use xlink:href="#glyph7-25" x="340.821279" y="580.7124"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph7-34" x="88.93474" y="594.266194"/> + <use xlink:href="#glyph7-18" x="92.568788" y="594.266194"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph7-12" x="101.855797" y="594.266194"/> + <use xlink:href="#glyph7-7" x="104.889627" y="594.266194"/> + <use xlink:href="#glyph7-15" x="110.346154" y="594.266194"/> + <use xlink:href="#glyph7-6" x="113.379983" y="594.266194"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph7-17" x="121.466557" y="594.266194"/> + <use xlink:href="#glyph7-8" x="126.923084" y="594.266194"/> + <use xlink:href="#glyph7-17" x="131.76848" y="594.266194"/> + <use xlink:href="#glyph7-1" x="137.225008" y="594.266194"/> + <use xlink:href="#glyph7-3" x="142.070404" y="594.266194"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph7-29" x="145.278842" y="594.266194"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph7-5" x="152.110415" y="594.266194"/> + <use xlink:href="#glyph7-1" x="159.98964" y="594.266194"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph7-17" x="168.665519" y="594.266194"/> + <use xlink:href="#glyph7-3" x="174.122046" y="594.266194"/> + <use xlink:href="#glyph7-1" x="177.756093" y="594.266194"/> + <use xlink:href="#glyph7-6" x="182.60149" y="594.266194"/> + <use xlink:href="#glyph7-1" x="186.846668" y="594.266194"/> + <use xlink:href="#glyph7-18" x="191.692064" y="594.266194"/> + <use xlink:href="#glyph7-12" x="197.148592" y="594.266194"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph7-8" x="204.023816" y="594.266194"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph7-13" x="212.699695" y="594.266194"/> + <use xlink:href="#glyph7-14" x="221.190052" y="594.266194"/> + <use xlink:href="#glyph7-10" x="226.646579" y="594.266194"/> + <use xlink:href="#glyph7-12" x="229.680408" y="594.266194"/> + <use xlink:href="#glyph7-15" x="232.714237" y="594.266194"/> + <use xlink:href="#glyph7-16" x="235.748067" y="594.266194"/> + <use xlink:href="#glyph7-17" x="239.382114" y="594.266194"/> + <use xlink:href="#glyph7-3" x="244.838641" y="594.266194"/> + <use xlink:href="#glyph7-15" x="248.472689" y="594.266194"/> + <use xlink:href="#glyph7-18" x="251.506518" y="594.266194"/> + <use xlink:href="#glyph7-19" x="256.963045" y="594.266194"/> + <use xlink:href="#glyph7-15" x="261.808441" y="594.266194"/> + <use xlink:href="#glyph7-17" x="264.842271" y="594.266194"/> + <use xlink:href="#glyph7-8" x="270.298798" y="594.266194"/> + <use xlink:href="#glyph7-10" x="275.144194" y="594.266194"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph7-28" x="282.019419" y="594.266194"/> + <use xlink:href="#glyph7-26" x="289.898645" y="594.266194"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph7-19" x="299.796785" y="594.266194"/> + <use xlink:href="#glyph7-4" x="304.642182" y="594.266194"/> + <use xlink:href="#glyph7-18" x="310.098709" y="594.266194"/> + <use xlink:href="#glyph7-6" x="315.555236" y="594.266194"/> + <use xlink:href="#glyph7-12" x="319.800415" y="594.266194"/> + <use xlink:href="#glyph7-3" x="322.834244" y="594.266194"/> + <use xlink:href="#glyph7-14" x="326.468291" y="594.266194"/> + <use xlink:href="#glyph7-19" x="331.924819" y="594.266194"/> + <use xlink:href="#glyph7-12" x="336.770215" y="594.266194"/> + <use xlink:href="#glyph7-15" x="339.804044" y="594.266194"/> + <use xlink:href="#glyph7-4" x="342.837873" y="594.266194"/> + <use xlink:href="#glyph7-18" x="348.294401" y="594.266194"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph7-4" x="357.592323" y="594.266194"/> + <use xlink:href="#glyph7-39" x="363.048851" y="594.266194"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph7-8" x="370.51338" y="594.266194"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph7-6" x="379.200172" y="594.266194"/> + <use xlink:href="#glyph7-1" x="383.44535" y="594.266194"/> + <use xlink:href="#glyph7-19" x="388.290747" y="594.266194"/> + <use xlink:href="#glyph7-14" x="393.136143" y="594.266194"/> + <use xlink:href="#glyph7-3" x="398.59267" y="594.266194"/> + <use xlink:href="#glyph7-1" x="402.226718" y="594.266194"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph7-5" x="410.913509" y="594.266194"/> + <use xlink:href="#glyph7-1" x="418.792735" y="594.266194"/> + <use xlink:href="#glyph7-2" x="423.638131" y="594.266194"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph7-2" x="432.925141" y="594.266194"/> + <use xlink:href="#glyph7-3" x="438.381668" y="594.266194"/> + <use xlink:href="#glyph7-4" x="442.015715" y="594.266194"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph7-5" x="447.210329" y="594.266194"/> + <use xlink:href="#glyph7-6" x="455.089555" y="594.266194"/> + <use xlink:href="#glyph7-1" x="459.334733" y="594.266194"/> + <use xlink:href="#glyph7-3" x="464.18013" y="594.266194"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph7-29" x="467.377655" y="594.266194"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph7-19" x="474.209227" y="594.266194"/> + <use xlink:href="#glyph7-8" x="479.054623" y="594.266194"/> + <use xlink:href="#glyph7-10" x="483.90002" y="594.266194"/> + <use xlink:href="#glyph7-10" x="486.933849" y="594.266194"/> + <use xlink:href="#glyph7-1" x="489.967678" y="594.266194"/> + <use xlink:href="#glyph7-11" x="494.813075" y="594.266194"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph7-45" x="504.110997" y="594.266194"/> + <use xlink:href="#glyph7-8" x="511.990223" y="594.266194"/> + <use xlink:href="#glyph7-50" x="516.835619" y="594.266194"/> + <use xlink:href="#glyph7-1" x="521.681015" y="594.266194"/> + <use xlink:href="#glyph7-10" x="526.526412" y="594.266194"/> + <use xlink:href="#glyph7-10" x="529.560241" y="594.266194"/> + <use xlink:href="#glyph7-1" x="532.59407" y="594.266194"/> + <use xlink:href="#glyph7-25" x="537.439467" y="594.266194"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph7-45" x="72" y="607.809985"/> + <use xlink:href="#glyph7-8" x="79.879226" y="607.809985"/> + <use xlink:href="#glyph7-50" x="84.724622" y="607.809985"/> + <use xlink:href="#glyph7-1" x="89.570018" y="607.809985"/> + <use xlink:href="#glyph7-10" x="94.415415" y="607.809985"/> + <use xlink:href="#glyph7-10" x="97.449244" y="607.809985"/> + <use xlink:href="#glyph7-1" x="100.483073" y="607.809985"/> + <use xlink:href="#glyph7-51" x="105.328469" y="607.809985"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph7-6" x="108.373212" y="607.809985"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph7-48" x="115.783176" y="607.809985"/> + <use xlink:href="#glyph7-3" x="123.062183" y="607.809985"/> + <use xlink:href="#glyph7-4" x="126.696231" y="607.809985"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph7-5" x="131.890845" y="607.809985"/> + <use xlink:href="#glyph7-6" x="139.77007" y="607.809985"/> + <use xlink:href="#glyph7-1" x="144.015249" y="607.809985"/> + <use xlink:href="#glyph7-3" x="148.860645" y="607.809985"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph7-49" x="155.659478" y="607.809985"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph7-1" x="163.27679" y="607.809985"/> + <use xlink:href="#glyph7-3" x="168.122187" y="607.809985"/> + <use xlink:href="#glyph7-18" x="171.756234" y="607.809985"/> + <use xlink:href="#glyph7-1" x="177.212761" y="607.809985"/> + <use xlink:href="#glyph7-10" x="182.058158" y="607.809985"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph9-0" x="188.21253" y="607.809985"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph9-1" x="192.850579" y="607.809985"/> + <use xlink:href="#glyph9-2" x="197.695975" y="607.809985"/> + <use xlink:href="#glyph9-3" x="202.541371" y="607.809985"/> + <use xlink:href="#glyph9-4" x="205.575201" y="607.809985"/> + <use xlink:href="#glyph9-5" x="211.031728" y="607.809985"/> + <use xlink:href="#glyph9-6" x="215.276906" y="607.809985"/> + <use xlink:href="#glyph9-7" x="218.310736" y="607.809985"/> + <use xlink:href="#glyph9-0" x="223.156132" y="607.809985"/> + <use xlink:href="#glyph9-3" x="228.001528" y="607.809985"/> + <use xlink:href="#glyph9-8" x="231.035357" y="607.809985"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph7-17" x="239.026754" y="607.809985"/> + <use xlink:href="#glyph7-3" x="244.483282" y="607.809985"/> + <use xlink:href="#glyph7-4" x="248.117329" y="607.809985"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph7-9" x="253.421074" y="607.809985"/> + <use xlink:href="#glyph7-15" x="258.877601" y="607.809985"/> + <use xlink:href="#glyph7-11" x="261.91143" y="607.809985"/> + <use xlink:href="#glyph7-1" x="267.367958" y="607.809985"/> + <use xlink:href="#glyph7-6" x="272.213354" y="607.809985"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph7-19" x="279.623318" y="607.809985"/> + <use xlink:href="#glyph7-3" x="284.468715" y="607.809985"/> + <use xlink:href="#glyph7-4" x="288.102762" y="607.809985"/> + <use xlink:href="#glyph7-6" x="293.559289" y="607.809985"/> + <use xlink:href="#glyph7-6" x="297.804468" y="607.809985"/> + <use xlink:href="#glyph7-16" x="302.049646" y="607.809985"/> + <use xlink:href="#glyph7-17" x="305.683693" y="607.809985"/> + <use xlink:href="#glyph7-3" x="311.140221" y="607.809985"/> + <use xlink:href="#glyph7-15" x="314.774268" y="607.809985"/> + <use xlink:href="#glyph7-18" x="317.808097" y="607.809985"/> + <use xlink:href="#glyph7-19" x="323.264624" y="607.809985"/> + <use xlink:href="#glyph7-15" x="328.110021" y="607.809985"/> + <use xlink:href="#glyph7-17" x="331.14385" y="607.809985"/> + <use xlink:href="#glyph7-8" x="336.600377" y="607.809985"/> + <use xlink:href="#glyph7-10" x="341.445774" y="607.809985"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph7-17" x="347.655302" y="607.809985"/> + <use xlink:href="#glyph7-3" x="353.111829" y="607.809985"/> + <use xlink:href="#glyph7-4" x="356.745877" y="607.809985"/> + <use xlink:href="#glyph7-12" x="362.202404" y="607.809985"/> + <use xlink:href="#glyph7-1" x="365.236233" y="607.809985"/> + <use xlink:href="#glyph7-19" x="370.081629" y="607.809985"/> + <use xlink:href="#glyph7-12" x="374.927026" y="607.809985"/> + <use xlink:href="#glyph7-15" x="377.960855" y="607.809985"/> + <use xlink:href="#glyph7-4" x="380.994684" y="607.809985"/> + <use xlink:href="#glyph7-18" x="386.451212" y="607.809985"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph7-8" x="395.083438" y="607.809985"/> + <use xlink:href="#glyph7-18" x="399.928834" y="607.809985"/> + <use xlink:href="#glyph7-11" x="405.385362" y="607.809985"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph7-39" x="414.017588" y="607.809985"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph7-8" x="417.553418" y="607.809985"/> + <use xlink:href="#glyph7-15" x="422.398814" y="607.809985"/> + <use xlink:href="#glyph7-3" x="425.432643" y="607.809985"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph7-6" x="432.231477" y="607.809985"/> + <use xlink:href="#glyph7-7" x="436.476655" y="607.809985"/> + <use xlink:href="#glyph7-8" x="441.933182" y="607.809985"/> + <use xlink:href="#glyph7-3" x="446.778579" y="607.809985"/> + <use xlink:href="#glyph7-15" x="450.412626" y="607.809985"/> + <use xlink:href="#glyph7-18" x="453.446455" y="607.809985"/> + <use xlink:href="#glyph7-20" x="458.902983" y="607.809985"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph7-4" x="467.535209" y="607.809985"/> + <use xlink:href="#glyph7-39" x="472.991736" y="607.809985"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph9-9" x="479.714128" y="607.809985"/> + <use xlink:href="#glyph9-3" x="485.170655" y="607.809985"/> + <use xlink:href="#glyph9-3" x="488.204485" y="607.809985"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph7-6" x="494.408241" y="607.809985"/> + <use xlink:href="#glyph7-27" x="498.65342" y="607.809985"/> + <use xlink:href="#glyph7-6" x="504.109947" y="607.809985"/> + <use xlink:href="#glyph7-12" x="508.355125" y="607.809985"/> + <use xlink:href="#glyph7-1" x="511.388954" y="607.809985"/> + <use xlink:href="#glyph7-13" x="516.234351" y="607.809985"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph7-3" x="527.900406" y="607.809985"/> + <use xlink:href="#glyph7-1" x="531.534454" y="607.809985"/> + <use xlink:href="#glyph7-16" x="536.37985" y="607.809985"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph7-6" x="72" y="621.363779"/> + <use xlink:href="#glyph7-4" x="76.245178" y="621.363779"/> + <use xlink:href="#glyph7-14" x="81.701706" y="621.363779"/> + <use xlink:href="#glyph7-3" x="87.158233" y="621.363779"/> + <use xlink:href="#glyph7-19" x="90.79228" y="621.363779"/> + <use xlink:href="#glyph7-1" x="95.637677" y="621.363779"/> + <use xlink:href="#glyph7-6" x="100.483073" y="621.363779"/> + <use xlink:href="#glyph7-25" x="104.728251" y="621.363779"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph7-34" x="110.828649" y="621.363779"/> + <use xlink:href="#glyph7-18" x="114.462696" y="621.363779"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph7-12" x="122.647487" y="621.363779"/> + <use xlink:href="#glyph7-7" x="125.681317" y="621.363779"/> + <use xlink:href="#glyph7-15" x="131.137844" y="621.363779"/> + <use xlink:href="#glyph7-6" x="134.171673" y="621.363779"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph7-17" x="141.145115" y="621.363779"/> + <use xlink:href="#glyph7-8" x="146.601643" y="621.363779"/> + <use xlink:href="#glyph7-17" x="151.447039" y="621.363779"/> + <use xlink:href="#glyph7-1" x="156.903566" y="621.363779"/> + <use xlink:href="#glyph7-3" x="161.748963" y="621.363779"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph7-29" x="164.968314" y="621.363779"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph7-5" x="170.403015" y="621.363779"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph7-1" x="178.293154" y="621.363779"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph7-39" x="185.866814" y="621.363779"/> + <use xlink:href="#glyph7-4" x="189.500861" y="621.363779"/> + <use xlink:href="#glyph7-19" x="194.957388" y="621.363779"/> + <use xlink:href="#glyph7-14" x="199.802785" y="621.363779"/> + <use xlink:href="#glyph7-6" x="205.259312" y="621.363779"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph7-4" x="212.221841" y="621.363779"/> + <use xlink:href="#glyph7-18" x="217.678369" y="621.363779"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph7-3" x="225.86316" y="621.363779"/> + <use xlink:href="#glyph7-1" x="229.497207" y="621.363779"/> + <use xlink:href="#glyph7-6" x="234.342603" y="621.363779"/> + <use xlink:href="#glyph7-4" x="238.587782" y="621.363779"/> + <use xlink:href="#glyph7-14" x="244.044309" y="621.363779"/> + <use xlink:href="#glyph7-3" x="249.500836" y="621.363779"/> + <use xlink:href="#glyph7-19" x="253.134884" y="621.363779"/> + <use xlink:href="#glyph7-1" x="257.98028" y="621.363779"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph7-17" x="265.55394" y="621.363779"/> + <use xlink:href="#glyph7-3" x="271.010467" y="621.363779"/> + <use xlink:href="#glyph7-4" x="274.644515" y="621.363779"/> + <use xlink:href="#glyph7-12" x="280.101042" y="621.363779"/> + <use xlink:href="#glyph7-1" x="283.134871" y="621.363779"/> + <use xlink:href="#glyph7-19" x="287.980268" y="621.363779"/> + <use xlink:href="#glyph7-12" x="292.825664" y="621.363779"/> + <use xlink:href="#glyph7-15" x="295.859493" y="621.363779"/> + <use xlink:href="#glyph7-4" x="298.893322" y="621.363779"/> + <use xlink:href="#glyph7-18" x="304.34985" y="621.363779"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph7-4" x="312.534641" y="621.363779"/> + <use xlink:href="#glyph7-18" x="317.991168" y="621.363779"/> + <use xlink:href="#glyph7-10" x="323.447696" y="621.363779"/> + <use xlink:href="#glyph7-27" x="326.481525" y="621.363779"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph7-25" x="331.239617" y="621.363779"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph7-48" x="88.93474" y="634.90757"/> + <use xlink:href="#glyph7-3" x="96.213748" y="634.90757"/> + <use xlink:href="#glyph7-4" x="99.847795" y="634.90757"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph7-5" x="105.042409" y="634.90757"/> + <use xlink:href="#glyph7-6" x="112.921635" y="634.90757"/> + <use xlink:href="#glyph7-1" x="117.166813" y="634.90757"/> + <use xlink:href="#glyph7-3" x="122.012209" y="634.90757"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph7-49" x="128.494564" y="634.90757"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph7-1" x="136.111876" y="634.90757"/> + <use xlink:href="#glyph7-3" x="140.957273" y="634.90757"/> + <use xlink:href="#glyph7-18" x="144.59132" y="634.90757"/> + <use xlink:href="#glyph7-1" x="150.047847" y="634.90757"/> + <use xlink:href="#glyph7-10" x="154.893244" y="634.90757"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph7-3" x="160.77538" y="634.90757"/> + <use xlink:href="#glyph7-14" x="164.409427" y="634.90757"/> + <use xlink:href="#glyph7-18" x="169.865955" y="634.90757"/> + <use xlink:href="#glyph7-6" x="175.322482" y="634.90757"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph7-15" x="182.426881" y="634.90757"/> + <use xlink:href="#glyph7-18" x="185.46071" y="634.90757"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph7-8" x="193.787371" y="634.90757"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph7-6" x="201.491988" y="634.90757"/> + <use xlink:href="#glyph7-1" x="205.737166" y="634.90757"/> + <use xlink:href="#glyph7-17" x="210.582562" y="634.90757"/> + <use xlink:href="#glyph7-8" x="216.03909" y="634.90757"/> + <use xlink:href="#glyph7-3" x="220.884486" y="634.90757"/> + <use xlink:href="#glyph7-8" x="224.518533" y="634.90757"/> + <use xlink:href="#glyph7-12" x="229.36393" y="634.90757"/> + <use xlink:href="#glyph7-1" x="232.397759" y="634.90757"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph7-28" x="240.102375" y="634.90757"/> + <use xlink:href="#glyph7-26" x="247.981601" y="634.90757"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph7-17" x="256.90848" y="634.90757"/> + <use xlink:href="#glyph7-3" x="262.365007" y="634.90757"/> + <use xlink:href="#glyph7-4" x="265.999055" y="634.90757"/> + <use xlink:href="#glyph7-19" x="271.455582" y="634.90757"/> + <use xlink:href="#glyph7-1" x="276.300978" y="634.90757"/> + <use xlink:href="#glyph7-6" x="281.146375" y="634.90757"/> + <use xlink:href="#glyph7-6" x="285.391553" y="634.90757"/> + <use xlink:href="#glyph7-29" x="289.636731" y="634.90757"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph7-11" x="295.267868" y="634.90757"/> + <use xlink:href="#glyph7-15" x="300.724395" y="634.90757"/> + <use xlink:href="#glyph7-3" x="303.758224" y="634.90757"/> + <use xlink:href="#glyph7-1" x="307.392271" y="634.90757"/> + <use xlink:href="#glyph7-19" x="312.237668" y="634.90757"/> + <use xlink:href="#glyph7-12" x="317.083064" y="634.90757"/> + <use xlink:href="#glyph7-10" x="320.116893" y="634.90757"/> + <use xlink:href="#glyph7-27" x="323.150723" y="634.90757"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph7-15" x="331.46647" y="634.90757"/> + <use xlink:href="#glyph7-18" x="334.5003" y="634.90757"/> + <use xlink:href="#glyph7-12" x="339.956827" y="634.90757"/> + <use xlink:href="#glyph7-1" x="342.990656" y="634.90757"/> + <use xlink:href="#glyph7-3" x="347.836052" y="634.90757"/> + <use xlink:href="#glyph7-8" x="351.4701" y="634.90757"/> + <use xlink:href="#glyph7-19" x="356.315496" y="634.90757"/> + <use xlink:href="#glyph7-12" x="361.160892" y="634.90757"/> + <use xlink:href="#glyph7-6" x="364.194722" y="634.90757"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph7-5" x="371.29912" y="634.90757"/> + <use xlink:href="#glyph7-15" x="379.178346" y="634.90757"/> + <use xlink:href="#glyph7-12" x="382.212175" y="634.90757"/> + <use xlink:href="#glyph7-7" x="385.246004" y="634.90757"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph7-12" x="393.561752" y="634.90757"/> + <use xlink:href="#glyph7-7" x="396.595581" y="634.90757"/> + <use xlink:href="#glyph7-1" x="402.052109" y="634.90757"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph7-14" x="409.767638" y="634.90757"/> + <use xlink:href="#glyph7-18" x="415.224166" y="634.90757"/> + <use xlink:href="#glyph7-11" x="420.680693" y="634.90757"/> + <use xlink:href="#glyph7-1" x="426.137221" y="634.90757"/> + <use xlink:href="#glyph7-3" x="430.982617" y="634.90757"/> + <use xlink:href="#glyph7-10" x="434.616664" y="634.90757"/> + <use xlink:href="#glyph7-27" x="437.650493" y="634.90757"/> + <use xlink:href="#glyph7-15" x="443.107021" y="634.90757"/> + <use xlink:href="#glyph7-18" x="446.14085" y="634.90757"/> + <use xlink:href="#glyph7-20" x="451.597378" y="634.90757"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph7-28" x="459.913125" y="634.90757"/> + <use xlink:href="#glyph7-26" x="467.792351" y="634.90757"/> + <use xlink:href="#glyph7-29" x="473.860009" y="634.90757"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph7-8" x="479.447493" y="634.90757"/> + <use xlink:href="#glyph7-18" x="484.29289" y="634.90757"/> + <use xlink:href="#glyph7-11" x="489.749417" y="634.90757"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph7-1" x="498.065165" y="634.90757"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph7-40" x="502.757778" y="634.90757"/> + <use xlink:href="#glyph7-17" x="508.214306" y="634.90757"/> + <use xlink:href="#glyph7-4" x="513.670833" y="634.90757"/> + <use xlink:href="#glyph7-6" x="519.127361" y="634.90757"/> + <use xlink:href="#glyph7-1" x="523.372539" y="634.90757"/> + <use xlink:href="#glyph7-6" x="528.217935" y="634.90757"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph7-8" x="535.322334" y="634.90757"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph7-6" x="72" y="648.461364"/> + <use xlink:href="#glyph7-1" x="76.245178" y="648.461364"/> + <use xlink:href="#glyph7-12" x="81.090575" y="648.461364"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph7-4" x="86.830841" y="648.461364"/> + <use xlink:href="#glyph7-39" x="92.287369" y="648.461364"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph7-6" x="98.638767" y="648.461364"/> + <use xlink:href="#glyph7-27" x="102.883945" y="648.461364"/> + <use xlink:href="#glyph7-6" x="108.340472" y="648.461364"/> + <use xlink:href="#glyph7-12" x="112.585651" y="648.461364"/> + <use xlink:href="#glyph7-1" x="115.61948" y="648.461364"/> + <use xlink:href="#glyph7-13" x="120.464876" y="648.461364"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph7-19" x="131.672584" y="648.461364"/> + <use xlink:href="#glyph7-8" x="136.51798" y="648.461364"/> + <use xlink:href="#glyph7-10" x="141.363376" y="648.461364"/> + <use xlink:href="#glyph7-10" x="144.397206" y="648.461364"/> + <use xlink:href="#glyph7-6" x="147.431035" y="648.461364"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph7-39" x="154.382651" y="648.461364"/> + <use xlink:href="#glyph7-4" x="158.016698" y="648.461364"/> + <use xlink:href="#glyph7-3" x="163.473225" y="648.461364"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph7-2" x="169.824623" y="648.461364"/> + <use xlink:href="#glyph7-3" x="175.281151" y="648.461364"/> + <use xlink:href="#glyph7-4" x="178.915198" y="648.461364"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph7-5" x="184.109812" y="648.461364"/> + <use xlink:href="#glyph7-6" x="191.989038" y="648.461364"/> + <use xlink:href="#glyph7-1" x="196.234216" y="648.461364"/> + <use xlink:href="#glyph7-3" x="201.079612" y="648.461364"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph7-17" x="207.420097" y="648.461364"/> + <use xlink:href="#glyph7-3" x="212.876624" y="648.461364"/> + <use xlink:href="#glyph7-15" x="216.510672" y="648.461364"/> + <use xlink:href="#glyph7-18" x="219.544501" y="648.461364"/> + <use xlink:href="#glyph7-19" x="225.001028" y="648.461364"/> + <use xlink:href="#glyph7-15" x="229.846425" y="648.461364"/> + <use xlink:href="#glyph7-17" x="232.880254" y="648.461364"/> + <use xlink:href="#glyph7-8" x="238.336781" y="648.461364"/> + <use xlink:href="#glyph7-10" x="243.182178" y="648.461364"/> + <use xlink:href="#glyph7-6" x="246.216007" y="648.461364"/> + <use xlink:href="#glyph7-25" x="250.461185" y="648.461364"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph7-0" x="256.561583" y="648.461364"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph7-1" x="266.001375" y="648.461364"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph7-11" x="273.553209" y="648.461364"/> + <use xlink:href="#glyph7-3" x="279.009737" y="648.461364"/> + <use xlink:href="#glyph7-8" x="282.643784" y="648.461364"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph7-5" x="287.336397" y="648.461364"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph7-12" x="297.92206" y="648.461364"/> + <use xlink:href="#glyph7-7" x="300.95589" y="648.461364"/> + <use xlink:href="#glyph7-1" x="306.412417" y="648.461364"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph7-15" x="313.975164" y="648.461364"/> + <use xlink:href="#glyph7-6" x="317.008993" y="648.461364"/> + <use xlink:href="#glyph7-4" x="321.254172" y="648.461364"/> + <use xlink:href="#glyph7-10" x="326.710699" y="648.461364"/> + <use xlink:href="#glyph7-8" x="329.744528" y="648.461364"/> + <use xlink:href="#glyph7-12" x="334.589925" y="648.461364"/> + <use xlink:href="#glyph7-15" x="337.623754" y="648.461364"/> + <use xlink:href="#glyph7-4" x="340.657583" y="648.461364"/> + <use xlink:href="#glyph7-18" x="346.11411" y="648.461364"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph7-2" x="354.277075" y="648.461364"/> + <use xlink:href="#glyph7-4" x="359.733603" y="648.461364"/> + <use xlink:href="#glyph7-14" x="365.19013" y="648.461364"/> + <use xlink:href="#glyph7-18" x="370.646658" y="648.461364"/> + <use xlink:href="#glyph7-11" x="376.103185" y="648.461364"/> + <use xlink:href="#glyph7-8" x="381.559712" y="648.461364"/> + <use xlink:href="#glyph7-3" x="386.405109" y="648.461364"/> + <use xlink:href="#glyph7-27" x="390.039156" y="648.461364"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph7-8" x="398.213034" y="648.461364"/> + <use xlink:href="#glyph7-19" x="403.05843" y="648.461364"/> + <use xlink:href="#glyph7-3" x="407.903827" y="648.461364"/> + <use xlink:href="#glyph7-4" x="411.537874" y="648.461364"/> + <use xlink:href="#glyph7-6" x="416.994401" y="648.461364"/> + <use xlink:href="#glyph7-6" x="421.23958" y="648.461364"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph7-12" x="428.202109" y="648.461364"/> + <use xlink:href="#glyph7-7" x="431.235938" y="648.461364"/> + <use xlink:href="#glyph7-1" x="436.692465" y="648.461364"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph7-1" x="444.255212" y="648.461364"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph7-40" x="448.947826" y="648.461364"/> + <use xlink:href="#glyph7-15" x="454.404353" y="648.461364"/> + <use xlink:href="#glyph7-6" x="457.438182" y="648.461364"/> + <use xlink:href="#glyph7-12" x="461.683361" y="648.461364"/> + <use xlink:href="#glyph7-15" x="464.71719" y="648.461364"/> + <use xlink:href="#glyph7-18" x="467.751019" y="648.461364"/> + <use xlink:href="#glyph7-20" x="473.207547" y="648.461364"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph7-2" x="481.359599" y="648.461364"/> + <use xlink:href="#glyph7-3" x="486.816126" y="648.461364"/> + <use xlink:href="#glyph7-4" x="490.450173" y="648.461364"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph7-5" x="495.644787" y="648.461364"/> + <use xlink:href="#glyph7-6" x="503.524013" y="648.461364"/> + <use xlink:href="#glyph7-1" x="507.769191" y="648.461364"/> + <use xlink:href="#glyph7-3" x="512.614588" y="648.461364"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph7-17" x="518.955072" y="648.461364"/> + <use xlink:href="#glyph7-3" x="524.4116" y="648.461364"/> + <use xlink:href="#glyph7-15" x="528.045647" y="648.461364"/> + <use xlink:href="#glyph7-18" x="531.079476" y="648.461364"/> + <use xlink:href="#glyph7-16" x="536.536004" y="648.461364"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph7-19" x="72" y="662.005155"/> + <use xlink:href="#glyph7-15" x="76.845396" y="662.005155"/> + <use xlink:href="#glyph7-17" x="79.879226" y="662.005155"/> + <use xlink:href="#glyph7-8" x="85.335753" y="662.005155"/> + <use xlink:href="#glyph7-10" x="90.181149" y="662.005155"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph10-0" x="93.205936" y="658.054049"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph7-11" x="100.527986" y="662.005155"/> + <use xlink:href="#glyph7-1" x="105.984513" y="662.005155"/> + <use xlink:href="#glyph7-52" x="110.829909" y="662.005155"/> + <use xlink:href="#glyph7-18" x="116.897568" y="662.005155"/> + <use xlink:href="#glyph7-1" x="122.354095" y="662.005155"/> + <use xlink:href="#glyph7-11" x="127.199492" y="662.005155"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph7-2" x="135.493413" y="662.005155"/> + <use xlink:href="#glyph7-27" x="140.949941" y="662.005155"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph7-12" x="149.254775" y="662.005155"/> + <use xlink:href="#glyph7-7" x="152.288605" y="662.005155"/> + <use xlink:href="#glyph7-1" x="157.745132" y="662.005155"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph7-6" x="165.427922" y="662.005155"/> + <use xlink:href="#glyph7-8" x="169.673101" y="662.005155"/> + <use xlink:href="#glyph7-13" x="174.518497" y="662.005155"/> + <use xlink:href="#glyph7-1" x="183.008854" y="662.005155"/> + <use xlink:href="#glyph7-16" x="187.85425" y="662.005155"/> + <use xlink:href="#glyph7-4" x="191.488297" y="662.005155"/> + <use xlink:href="#glyph7-3" x="196.944825" y="662.005155"/> + <use xlink:href="#glyph7-15" x="200.578872" y="662.005155"/> + <use xlink:href="#glyph7-20" x="203.612701" y="662.005155"/> + <use xlink:href="#glyph7-15" x="209.069229" y="662.005155"/> + <use xlink:href="#glyph7-18" x="212.103058" y="662.005155"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph7-17" x="220.407893" y="662.005155"/> + <use xlink:href="#glyph7-4" x="225.86442" y="662.005155"/> + <use xlink:href="#glyph7-10" x="231.320947" y="662.005155"/> + <use xlink:href="#glyph7-15" x="234.354777" y="662.005155"/> + <use xlink:href="#glyph7-19" x="237.388606" y="662.005155"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph7-27" x="242.081219" y="662.005155"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph7-42" x="250.375141" y="662.005155"/> + <use xlink:href="#glyph7-26" x="254.009188" y="662.005155"/> + <use xlink:href="#glyph7-28" x="260.076847" y="662.005155"/> + <use xlink:href="#glyph7-47" x="267.956072" y="662.005155"/> + <use xlink:href="#glyph7-43" x="274.023731" y="662.005155"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph7-21" x="280.495172" y="662.005155"/> + <use xlink:href="#glyph7-22" x="284.12922" y="662.005155"/> + <use xlink:href="#glyph7-41" x="289.585747" y="662.005155"/> + <use xlink:href="#glyph7-24" x="295.042274" y="662.005155"/> + <use xlink:href="#glyph7-29" x="298.676322" y="662.005155"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph7-18" x="304.274719" y="662.005155"/> + <use xlink:href="#glyph7-8" x="309.731246" y="662.005155"/> + <use xlink:href="#glyph7-13" x="314.576642" y="662.005155"/> + <use xlink:href="#glyph7-1" x="323.066999" y="662.005155"/> + <use xlink:href="#glyph7-10" x="327.912395" y="662.005155"/> + <use xlink:href="#glyph7-27" x="330.946225" y="662.005155"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph7-29" x="335.704317" y="662.005155"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph7-12" x="341.291801" y="662.005155"/> + <use xlink:href="#glyph7-7" x="344.32563" y="662.005155"/> + <use xlink:href="#glyph7-1" x="349.782157" y="662.005155"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph7-12" x="357.475861" y="662.005155"/> + <use xlink:href="#glyph7-3" x="360.50969" y="662.005155"/> + <use xlink:href="#glyph7-15" x="364.143737" y="662.005155"/> + <use xlink:href="#glyph7-17" x="367.177567" y="662.005155"/> + <use xlink:href="#glyph7-10" x="372.634094" y="662.005155"/> + <use xlink:href="#glyph7-1" x="375.667923" y="662.005155"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph7-4" x="383.350714" y="662.005155"/> + <use xlink:href="#glyph7-39" x="388.807241" y="662.005155"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph11-0" x="395.190468" y="662.005155"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph7-17" x="403.672842" y="662.005155"/> + <use xlink:href="#glyph7-3" x="409.12937" y="662.005155"/> + <use xlink:href="#glyph7-4" x="412.763417" y="662.005155"/> + <use xlink:href="#glyph7-12" x="418.219944" y="662.005155"/> + <use xlink:href="#glyph7-4" x="421.253774" y="662.005155"/> + <use xlink:href="#glyph7-19" x="426.710301" y="662.005155"/> + <use xlink:href="#glyph7-4" x="431.555697" y="662.005155"/> + <use xlink:href="#glyph7-10" x="437.012225" y="662.005155"/> + <use xlink:href="#glyph7-29" x="440.046054" y="662.005155"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph7-11" x="445.633538" y="662.005155"/> + <use xlink:href="#glyph7-4" x="451.090066" y="662.005155"/> + <use xlink:href="#glyph7-13" x="456.546593" y="662.005155"/> + <use xlink:href="#glyph7-8" x="465.03695" y="662.005155"/> + <use xlink:href="#glyph7-15" x="469.882346" y="662.005155"/> + <use xlink:href="#glyph7-18" x="472.916175" y="662.005155"/> + <use xlink:href="#glyph7-16" x="478.372703" y="662.005155"/> + <use xlink:href="#glyph7-18" x="482.00675" y="662.005155"/> + <use xlink:href="#glyph7-8" x="487.463277" y="662.005155"/> + <use xlink:href="#glyph7-13" x="492.308673" y="662.005155"/> + <use xlink:href="#glyph7-1" x="500.79903" y="662.005155"/> + <use xlink:href="#glyph7-29" x="505.644426" y="662.005155"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph7-17" x="511.253737" y="662.005155"/> + <use xlink:href="#glyph7-4" x="516.710264" y="662.005155"/> + <use xlink:href="#glyph7-3" x="522.166791" y="662.005155"/> + <use xlink:href="#glyph7-12" x="525.800839" y="662.005155"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph11-1" x="528.787865" y="662.005155"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph7-29" x="537.270239" y="662.005155"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph7-14" x="72" y="675.558949"/> + <use xlink:href="#glyph7-6" x="77.456527" y="675.558949"/> + <use xlink:href="#glyph7-15" x="81.701706" y="675.558949"/> + <use xlink:href="#glyph7-18" x="84.735535" y="675.558949"/> + <use xlink:href="#glyph7-20" x="90.192062" y="675.558949"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph7-6" x="98.551462" y="675.558949"/> + <use xlink:href="#glyph7-8" x="102.796641" y="675.558949"/> + <use xlink:href="#glyph7-18" x="107.642037" y="675.558949"/> + <use xlink:href="#glyph7-11" x="113.098564" y="675.558949"/> + <use xlink:href="#glyph7-2" x="118.555092" y="675.558949"/> + <use xlink:href="#glyph7-4" x="124.011619" y="675.558949"/> + <use xlink:href="#glyph7-40" x="129.468147" y="675.558949"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph7-1" x="134.771891" y="675.558949"/> + <use xlink:href="#glyph7-11" x="139.617288" y="675.558949"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph7-28" x="147.976688" y="675.558949"/> + <use xlink:href="#glyph7-26" x="155.855913" y="675.558949"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph7-17" x="164.826444" y="675.558949"/> + <use xlink:href="#glyph7-3" x="170.282972" y="675.558949"/> + <use xlink:href="#glyph7-4" x="173.917019" y="675.558949"/> + <use xlink:href="#glyph7-19" x="179.373546" y="675.558949"/> + <use xlink:href="#glyph7-1" x="184.218943" y="675.558949"/> + <use xlink:href="#glyph7-6" x="189.064339" y="675.558949"/> + <use xlink:href="#glyph7-6" x="193.309517" y="675.558949"/> + <use xlink:href="#glyph7-1" x="197.554695" y="675.558949"/> + <use xlink:href="#glyph7-6" x="202.400092" y="675.558949"/> + <use xlink:href="#glyph7-25" x="206.64527" y="675.558949"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph7-53" x="213.302234" y="675.558949"/> + <use xlink:href="#glyph7-7" x="219.97011" y="675.558949"/> + <use xlink:href="#glyph7-1" x="225.426637" y="675.558949"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph7-17" x="233.185819" y="675.558949"/> + <use xlink:href="#glyph7-3" x="238.642347" y="675.558949"/> + <use xlink:href="#glyph7-4" x="242.276394" y="675.558949"/> + <use xlink:href="#glyph7-19" x="247.732921" y="675.558949"/> + <use xlink:href="#glyph7-1" x="252.578318" y="675.558949"/> + <use xlink:href="#glyph7-6" x="257.423714" y="675.558949"/> + <use xlink:href="#glyph7-6" x="261.668892" y="675.558949"/> + <use xlink:href="#glyph7-1" x="265.914071" y="675.558949"/> + <use xlink:href="#glyph7-6" x="270.759467" y="675.558949"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph7-8" x="277.907518" y="675.558949"/> + <use xlink:href="#glyph7-3" x="282.752914" y="675.558949"/> + <use xlink:href="#glyph7-1" x="286.386962" y="675.558949"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph7-6" x="294.146144" y="675.558949"/> + <use xlink:href="#glyph7-8" x="298.391322" y="675.558949"/> + <use xlink:href="#glyph7-18" x="303.236718" y="675.558949"/> + <use xlink:href="#glyph7-11" x="308.693246" y="675.558949"/> + <use xlink:href="#glyph7-2" x="314.149773" y="675.558949"/> + <use xlink:href="#glyph7-4" x="319.6063" y="675.558949"/> + <use xlink:href="#glyph7-40" x="325.062828" y="675.558949"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph7-1" x="330.366572" y="675.558949"/> + <use xlink:href="#glyph7-11" x="335.211969" y="675.558949"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph7-6" x="343.571369" y="675.558949"/> + <use xlink:href="#glyph7-4" x="347.816547" y="675.558949"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph7-12" x="356.175947" y="675.558949"/> + <use xlink:href="#glyph7-7" x="359.209776" y="675.558949"/> + <use xlink:href="#glyph7-8" x="364.666304" y="675.558949"/> + <use xlink:href="#glyph7-12" x="369.5117" y="675.558949"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph7-12" x="375.459315" y="675.558949"/> + <use xlink:href="#glyph7-7" x="378.493144" y="675.558949"/> + <use xlink:href="#glyph7-1" x="383.949671" y="675.558949"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph7-27" x="388.642285" y="675.558949"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph7-19" x="396.990772" y="675.558949"/> + <use xlink:href="#glyph7-8" x="401.836168" y="675.558949"/> + <use xlink:href="#glyph7-18" x="406.681565" y="675.558949"/> + <use xlink:href="#glyph7-18" x="412.138092" y="675.558949"/> + <use xlink:href="#glyph7-4" x="417.594619" y="675.558949"/> + <use xlink:href="#glyph7-12" x="423.051147" y="675.558949"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph7-15" x="428.998762" y="675.558949"/> + <use xlink:href="#glyph7-18" x="432.032591" y="675.558949"/> + <use xlink:href="#glyph7-12" x="437.489118" y="675.558949"/> + <use xlink:href="#glyph7-1" x="440.522948" y="675.558949"/> + <use xlink:href="#glyph7-3" x="445.368344" y="675.558949"/> + <use xlink:href="#glyph7-8" x="449.002391" y="675.558949"/> + <use xlink:href="#glyph7-19" x="453.847787" y="675.558949"/> + <use xlink:href="#glyph7-12" x="458.693184" y="675.558949"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph7-5" x="464.640799" y="675.558949"/> + <use xlink:href="#glyph7-15" x="472.520024" y="675.558949"/> + <use xlink:href="#glyph7-12" x="475.553853" y="675.558949"/> + <use xlink:href="#glyph7-7" x="478.587683" y="675.558949"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph7-12" x="486.947083" y="675.558949"/> + <use xlink:href="#glyph7-7" x="489.980912" y="675.558949"/> + <use xlink:href="#glyph7-1" x="495.437439" y="675.558949"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph7-14" x="503.196621" y="675.558949"/> + <use xlink:href="#glyph7-18" x="508.653149" y="675.558949"/> + <use xlink:href="#glyph7-11" x="514.109676" y="675.558949"/> + <use xlink:href="#glyph7-1" x="519.566203" y="675.558949"/> + <use xlink:href="#glyph7-3" x="524.4116" y="675.558949"/> + <use xlink:href="#glyph7-10" x="528.045647" y="675.558949"/> + <use xlink:href="#glyph7-27" x="531.079476" y="675.558949"/> + <use xlink:href="#glyph7-16" x="536.536004" y="675.558949"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph7-15" x="72" y="689.10274"/> + <use xlink:href="#glyph7-18" x="75.033829" y="689.10274"/> + <use xlink:href="#glyph7-20" x="80.490357" y="689.10274"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph7-6" x="88.784278" y="689.10274"/> + <use xlink:href="#glyph7-27" x="93.029457" y="689.10274"/> + <use xlink:href="#glyph7-6" x="98.485984" y="689.10274"/> + <use xlink:href="#glyph7-12" x="102.731162" y="689.10274"/> + <use xlink:href="#glyph7-1" x="105.764992" y="689.10274"/> + <use xlink:href="#glyph7-13" x="110.610388" y="689.10274"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph7-8" x="121.938139" y="689.10274"/> + <use xlink:href="#glyph7-18" x="126.783535" y="689.10274"/> + <use xlink:href="#glyph7-11" x="132.240062" y="689.10274"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph7-13" x="140.533984" y="689.10274"/> + <use xlink:href="#glyph7-14" x="149.024341" y="689.10274"/> + <use xlink:href="#glyph7-6" x="154.480868" y="689.10274"/> + <use xlink:href="#glyph7-12" x="158.726046" y="689.10274"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph7-14" x="164.59727" y="689.10274"/> + <use xlink:href="#glyph7-6" x="170.053797" y="689.10274"/> + <use xlink:href="#glyph7-1" x="174.298976" y="689.10274"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph7-6" x="181.981766" y="689.10274"/> + <use xlink:href="#glyph7-27" x="186.226945" y="689.10274"/> + <use xlink:href="#glyph7-6" x="191.683472" y="689.10274"/> + <use xlink:href="#glyph7-12" x="195.92865" y="689.10274"/> + <use xlink:href="#glyph7-1" x="198.96248" y="689.10274"/> + <use xlink:href="#glyph7-13" x="203.807876" y="689.10274"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph7-19" x="215.135627" y="689.10274"/> + <use xlink:href="#glyph7-8" x="219.981023" y="689.10274"/> + <use xlink:href="#glyph7-10" x="224.826419" y="689.10274"/> + <use xlink:href="#glyph7-10" x="227.860249" y="689.10274"/> + <use xlink:href="#glyph7-6" x="230.894078" y="689.10274"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph7-17" x="237.97665" y="689.10274"/> + <use xlink:href="#glyph7-3" x="243.433178" y="689.10274"/> + <use xlink:href="#glyph7-4" x="247.067225" y="689.10274"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph7-9" x="252.37097" y="689.10274"/> + <use xlink:href="#glyph7-15" x="257.827497" y="689.10274"/> + <use xlink:href="#glyph7-11" x="260.861326" y="689.10274"/> + <use xlink:href="#glyph7-1" x="266.317854" y="689.10274"/> + <use xlink:href="#glyph7-11" x="271.16325" y="689.10274"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph7-2" x="279.446259" y="689.10274"/> + <use xlink:href="#glyph7-27" x="284.902786" y="689.10274"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph7-48" x="293.196708" y="689.10274"/> + <use xlink:href="#glyph7-3" x="300.475715" y="689.10274"/> + <use xlink:href="#glyph7-4" x="304.109763" y="689.10274"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph7-5" x="309.304377" y="689.10274"/> + <use xlink:href="#glyph7-6" x="317.183602" y="689.10274"/> + <use xlink:href="#glyph7-1" x="321.428781" y="689.10274"/> + <use xlink:href="#glyph7-3" x="326.274177" y="689.10274"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph7-49" x="332.734705" y="689.10274"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph7-1" x="340.352018" y="689.10274"/> + <use xlink:href="#glyph7-3" x="345.197414" y="689.10274"/> + <use xlink:href="#glyph7-18" x="348.831461" y="689.10274"/> + <use xlink:href="#glyph7-1" x="354.287989" y="689.10274"/> + <use xlink:href="#glyph7-10" x="359.133385" y="689.10274"/> + <use xlink:href="#glyph7-25" x="362.167214" y="689.10274"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph7-47" x="368.605916" y="689.10274"/> + <use xlink:href="#glyph7-3" x="374.673575" y="689.10274"/> + <use xlink:href="#glyph7-15" x="378.307622" y="689.10274"/> + <use xlink:href="#glyph7-18" x="381.341451" y="689.10274"/> + <use xlink:href="#glyph7-19" x="386.797979" y="689.10274"/> + <use xlink:href="#glyph7-15" x="391.643375" y="689.10274"/> + <use xlink:href="#glyph7-17" x="394.677204" y="689.10274"/> + <use xlink:href="#glyph7-8" x="400.133732" y="689.10274"/> + <use xlink:href="#glyph7-10" x="404.979128" y="689.10274"/> + <use xlink:href="#glyph7-6" x="408.012957" y="689.10274"/> + <use xlink:href="#glyph7-51" x="412.258136" y="689.10274"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph7-17" x="418.729577" y="689.10274"/> + <use xlink:href="#glyph7-3" x="424.186105" y="689.10274"/> + <use xlink:href="#glyph7-4" x="427.820152" y="689.10274"/> + <use xlink:href="#glyph7-19" x="433.276679" y="689.10274"/> + <use xlink:href="#glyph7-1" x="438.122075" y="689.10274"/> + <use xlink:href="#glyph7-6" x="442.967472" y="689.10274"/> + <use xlink:href="#glyph7-6" x="447.21265" y="689.10274"/> + <use xlink:href="#glyph7-1" x="451.457828" y="689.10274"/> + <use xlink:href="#glyph7-6" x="456.303225" y="689.10274"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph7-19" x="463.385797" y="689.10274"/> + <use xlink:href="#glyph7-8" x="468.231194" y="689.10274"/> + <use xlink:href="#glyph7-18" x="473.07659" y="689.10274"/> +</g> +<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> + <use xlink:href="#glyph7-19" x="481.370512" y="689.10274"/> + <use xlink:href="#glyph7-4" x="486.215908" y="689.10274"/> + <use xlink:href="#glyph7-13" x="491.672435" y="689.10274"/> + <use xlink:href="#glyph7-13" x="500.162792" y="689.10274"/> + <use xlink:href="#glyph7-14" x="508.653149" y="689.10274"/> + <use xlink:href="#glyph7-18" x="514.109676" y="689.10274"/> + <use xlink:href="#glyph7-15" x="519.566203" y="689.10274"/> + <use xlink:href="#glyph7-19" x="522.600033" y="689.10274"/> + <use xlink:href="#glyph7-8" x="527.445429" y="689.10274"/> + <use xlink:href="#glyph7-12" x="532.290825" y="689.10274"/> + <use xlink:href="#glyph7-1" x="535.324655" y="689.10274"/> +</g> +<path style="fill:none;stroke-width:0.99972;stroke-linecap:butt;stroke-linejoin:miter;stroke:rgb(13.729858%,12.159729%,12.548828%);stroke-opacity:1;stroke-miterlimit:10;" d="M 0.49986 -626.324629 L 187.447515 -626.324629 " transform="matrix(1.00028,0,0,-1.00028,72,72)"/> +<g style="fill:rgb(13.729858%,12.159729%,12.548828%);fill-opacity:1;"> + <use xlink:href="#glyph12-0" x="84.653542" y="705.507332"/> +</g> +<g style="fill:rgb(13.729858%,12.159729%,12.548828%);fill-opacity:1;"> + <use xlink:href="#glyph13-0" x="88.144519" y="709.318399"/> + <use xlink:href="#glyph13-1" x="91.129035" y="709.318399"/> +</g> +<g style="fill:rgb(13.729858%,12.159729%,12.548828%);fill-opacity:1;"> + <use xlink:href="#glyph13-2" x="97.841954" y="709.318399"/> + <use xlink:href="#glyph13-3" x="100.333531" y="709.318399"/> + <use xlink:href="#glyph13-4" x="104.814786" y="709.318399"/> + <use xlink:href="#glyph13-5" x="107.306363" y="709.318399"/> +</g> +<g style="fill:rgb(13.729858%,12.159729%,12.548828%);fill-opacity:1;"> + <use xlink:href="#glyph13-6" x="113.033406" y="709.318399"/> + <use xlink:href="#glyph13-7" x="117.514661" y="709.318399"/> + <use xlink:href="#glyph13-6" x="121.494014" y="709.318399"/> + <use xlink:href="#glyph13-8" x="125.975269" y="709.318399"/> + <use xlink:href="#glyph13-9" x="129.954623" y="709.318399"/> +</g> +<g style="fill:rgb(13.729858%,12.159729%,12.548828%);fill-opacity:1;"> + <use xlink:href="#glyph13-10" x="132.5896" y="709.318399"/> +</g> +<g style="fill:rgb(13.729858%,12.159729%,12.548828%);fill-opacity:1;"> + <use xlink:href="#glyph13-11" x="137.061892" y="709.318399"/> + <use xlink:href="#glyph13-8" x="143.532824" y="709.318399"/> +</g> +<g style="fill:rgb(13.729858%,12.159729%,12.548828%);fill-opacity:1;"> + <use xlink:href="#glyph13-12" x="149.752805" y="709.318399"/> + <use xlink:href="#glyph13-5" x="154.234059" y="709.318399"/> + <use xlink:href="#glyph13-8" x="157.720475" y="709.318399"/> +</g> +<g style="fill:rgb(13.729858%,12.159729%,12.548828%);fill-opacity:1;"> + <use xlink:href="#glyph13-13" x="163.940456" y="709.318399"/> + <use xlink:href="#glyph13-6" x="167.91981" y="709.318399"/> + <use xlink:href="#glyph13-9" x="172.401064" y="709.318399"/> + <use xlink:href="#glyph13-4" x="175.38558" y="709.318399"/> + <use xlink:href="#glyph13-1" x="177.877157" y="709.318399"/> + <use xlink:href="#glyph13-14" x="182.358412" y="709.318399"/> + <use xlink:href="#glyph13-4" x="186.337766" y="709.318399"/> + <use xlink:href="#glyph13-6" x="188.829343" y="709.318399"/> + <use xlink:href="#glyph13-7" x="193.310597" y="709.318399"/> + <use xlink:href="#glyph13-15" x="197.289951" y="709.318399"/> + <use xlink:href="#glyph13-16" x="199.781529" y="709.318399"/> +</g> +<g style="fill:rgb(13.729858%,12.159729%,12.548828%);fill-opacity:1;"> + <use xlink:href="#glyph13-7" x="206.00151" y="709.318399"/> + <use xlink:href="#glyph13-1" x="209.980864" y="709.318399"/> + <use xlink:href="#glyph13-17" x="214.462118" y="709.318399"/> +</g> +<g style="fill:rgb(13.729858%,12.159729%,12.548828%);fill-opacity:1;"> + <use xlink:href="#glyph13-13" x="221.184" y="709.318399"/> + <use xlink:href="#glyph13-18" x="225.163354" y="709.318399"/> + <use xlink:href="#glyph13-9" x="229.644608" y="709.318399"/> + <use xlink:href="#glyph13-4" x="232.629124" y="709.318399"/> + <use xlink:href="#glyph13-19" x="235.120701" y="709.318399"/> + <use xlink:href="#glyph13-4" x="239.601955" y="709.318399"/> + <use xlink:href="#glyph13-1" x="242.093533" y="709.318399"/> + <use xlink:href="#glyph13-16" x="246.574787" y="709.318399"/> +</g> +<g style="fill:rgb(13.729858%,12.159729%,12.548828%);fill-opacity:1;"> + <use xlink:href="#glyph13-4" x="252.794768" y="709.318399"/> + <use xlink:href="#glyph13-1" x="255.286346" y="709.318399"/> + <use xlink:href="#glyph13-2" x="259.7676" y="709.318399"/> + <use xlink:href="#glyph13-8" x="262.259178" y="709.318399"/> + <use xlink:href="#glyph13-9" x="266.238532" y="709.318399"/> + <use xlink:href="#glyph13-14" x="269.223047" y="709.318399"/> + <use xlink:href="#glyph13-3" x="273.202401" y="709.318399"/> + <use xlink:href="#glyph13-7" x="277.683655" y="709.318399"/> + <use xlink:href="#glyph13-1" x="281.663009" y="709.318399"/> + <use xlink:href="#glyph13-19" x="286.144264" y="709.318399"/> + <use xlink:href="#glyph13-8" x="290.625518" y="709.318399"/> + <use xlink:href="#glyph13-7" x="294.604872" y="709.318399"/> + <use xlink:href="#glyph13-20" x="298.584226" y="709.318399"/> + <use xlink:href="#glyph13-15" x="303.06548" y="709.318399"/> + <use xlink:href="#glyph13-21" x="305.557058" y="709.318399"/> +</g> +<g style="fill:rgb(13.729858%,12.159729%,12.548828%);fill-opacity:1;"> + <use xlink:href="#glyph13-22" x="309.464712" y="709.318399"/> +</g> +<g style="fill:rgb(13.729858%,12.159729%,12.548828%);fill-opacity:1;"> + <use xlink:href="#glyph7-30" x="303.274739" y="749.889756"/> +</g> +</g> +</svg> diff --git a/dom/svg/crashtests/751515-1.svg b/dom/svg/crashtests/751515-1.svg new file mode 100644 index 0000000000..7485434c91 --- /dev/null +++ b/dom/svg/crashtests/751515-1.svg @@ -0,0 +1,9 @@ +<?xml version="1.0"?> + +<svg xmlns="http://www.w3.org/2000/svg"> +<script> + +document.documentElement.createSVGAngle().convertToSpecifiedUnits(2); + +</script> +</svg> diff --git a/dom/svg/crashtests/761507-1.svg b/dom/svg/crashtests/761507-1.svg new file mode 100644 index 0000000000..172d71ed95 --- /dev/null +++ b/dom/svg/crashtests/761507-1.svg @@ -0,0 +1,18 @@ +<svg xmlns="http://www.w3.org/2000/svg"> +<script> +<![CDATA[ + +function boom() +{ + var r = document.createElementNS("http://www.w3.org/2000/svg", "rect"); + var f = r.requiredFeatures; + f.appendItem(1); + document.implementation.createDocument('', '', null).adoptNode(r); + r.getAttribute("requiredFeatures"); +} + +window.addEventListener("load", boom, false); + +]]> +</script> +</svg> diff --git a/dom/svg/crashtests/831561.html b/dom/svg/crashtests/831561.html new file mode 100644 index 0000000000..d86a8cb5bd --- /dev/null +++ b/dom/svg/crashtests/831561.html @@ -0,0 +1,10 @@ +<!DOCTYPE html> +<html> + <body> + <script> + var clipPath = document.createElementNS("http://www.w3.org/2000/svg", + "clipPath"); + clipPath["hasExtension"].apply(clipPath,[]); + </script> + </body> +</html> diff --git a/dom/svg/crashtests/837450-1.svg b/dom/svg/crashtests/837450-1.svg new file mode 100644 index 0000000000..7c57363ce7 --- /dev/null +++ b/dom/svg/crashtests/837450-1.svg @@ -0,0 +1,10 @@ +<svg xmlns="http://www.w3.org/2000/svg"> + +<script> + +var root = document.documentElement; +root.getTransformToElement(root); + +</script> + +</svg> diff --git a/dom/svg/crashtests/842463-1.html b/dom/svg/crashtests/842463-1.html new file mode 100644 index 0000000000..f20db0d6ca --- /dev/null +++ b/dom/svg/crashtests/842463-1.html @@ -0,0 +1,16 @@ +<!DOCTYPE html> +<html> +<head> +<script> + +function boom() +{ + var f = document.createElementNS("http://www.w3.org/2000/svg", "feImage"); + f.setAttributeNS("http://www.w3.org/1999/xlink", "xlink:href", "#s"); +} + +</script> +</head> +<body onload="boom();"></body> +</html> + diff --git a/dom/svg/crashtests/847138-1.svg b/dom/svg/crashtests/847138-1.svg new file mode 100644 index 0000000000..8f10f04e9a --- /dev/null +++ b/dom/svg/crashtests/847138-1.svg @@ -0,0 +1,8 @@ +<svg xmlns="http://www.w3.org/2000/svg"> +<script> + +var globalE = document.createEvent('SVGZoomEvents'); +globalE.previousTranslate.expando = null; + +</script> +</svg> diff --git a/dom/svg/crashtests/864509.svg b/dom/svg/crashtests/864509.svg new file mode 100644 index 0000000000..e88dd1fcd8 --- /dev/null +++ b/dom/svg/crashtests/864509.svg @@ -0,0 +1,7 @@ +<svg xmlns="http://www.w3.org/2000/svg"> + +<script> +document.documentElement.requiredFeatures.foo = null; +</script> + +</svg> diff --git a/dom/svg/crashtests/880544-1.svg b/dom/svg/crashtests/880544-1.svg new file mode 100644 index 0000000000..9052d23961 --- /dev/null +++ b/dom/svg/crashtests/880544-1.svg @@ -0,0 +1,15 @@ +<svg xmlns="http://www.w3.org/2000/svg">
+ <script>//<![CDATA[
+
+function add_watch()
+{
+ document.getElementById("p").transform.baseVal.watch("0", function(){});
+}
+
+window.addEventListener("load", add_watch, false);
+
+ //]]></script>
+
+ <path id="p" transform="scale(1)" />
+
+</svg>
diff --git a/dom/svg/crashtests/880544-2.svg b/dom/svg/crashtests/880544-2.svg new file mode 100644 index 0000000000..7570c7cbfe --- /dev/null +++ b/dom/svg/crashtests/880544-2.svg @@ -0,0 +1,15 @@ +<svg xmlns="http://www.w3.org/2000/svg">
+ <script>//<![CDATA[
+
+function add_watch()
+{
+ document.getElementById("e").x.baseVal.watch("0", function(){});
+}
+
+window.addEventListener("load", add_watch, false);
+
+ //]]></script>
+
+ <text id="e" x="10">foo</text>
+
+</svg>
diff --git a/dom/svg/crashtests/880544-3.svg b/dom/svg/crashtests/880544-3.svg new file mode 100644 index 0000000000..5791b8ec62 --- /dev/null +++ b/dom/svg/crashtests/880544-3.svg @@ -0,0 +1,15 @@ +<svg xmlns="http://www.w3.org/2000/svg">
+ <script>//<![CDATA[
+
+function add_watch()
+{
+ document.getElementById("e").rotate.baseVal.watch("0", function(){});
+}
+
+window.addEventListener("load", add_watch, false);
+
+ //]]></script>
+
+ <text id="e" rotate="10">foo</text>
+
+</svg>
diff --git a/dom/svg/crashtests/880544-4.svg b/dom/svg/crashtests/880544-4.svg new file mode 100644 index 0000000000..7bdb80f478 --- /dev/null +++ b/dom/svg/crashtests/880544-4.svg @@ -0,0 +1,15 @@ +<svg xmlns="http://www.w3.org/2000/svg">
+ <script>//<![CDATA[
+
+function add_watch()
+{
+ document.getElementById("e").pathSegList.watch("0", function(){});
+}
+
+window.addEventListener("load", add_watch, false);
+
+ //]]></script>
+
+ <path id="e" d="M0,0"/>
+
+</svg>
diff --git a/dom/svg/crashtests/880544-5.svg b/dom/svg/crashtests/880544-5.svg new file mode 100644 index 0000000000..ef7f468f8a --- /dev/null +++ b/dom/svg/crashtests/880544-5.svg @@ -0,0 +1,15 @@ +<svg xmlns="http://www.w3.org/2000/svg">
+ <script>//<![CDATA[
+
+function add_watch()
+{
+ document.getElementById("e").points.watch("0", function(){});
+}
+
+window.addEventListener("load", add_watch, false);
+
+ //]]></script>
+
+ <polygon id="e" points="0,0"/>
+
+</svg>
diff --git a/dom/svg/crashtests/898915-1.svg b/dom/svg/crashtests/898915-1.svg new file mode 100644 index 0000000000..6e9b59c36f --- /dev/null +++ b/dom/svg/crashtests/898915-1.svg @@ -0,0 +1,5 @@ +<svg xmlns="http://www.w3.org/2000/svg"> + <text> + <animate attributeName="y" by="" /> + </text> +</svg> diff --git a/dom/svg/crashtests/crashtests.list b/dom/svg/crashtests/crashtests.list new file mode 100644 index 0000000000..147838bbe7 --- /dev/null +++ b/dom/svg/crashtests/crashtests.list @@ -0,0 +1,90 @@ +asserts(0-6) load 307322-1.svg # bug 563481 +load 327705-1.svg +load 336994-1.html +load 344888-1.svg +load 345445-1.svg +load 360836-1.svg +load 367357-1.xhtml +load 369051-1.svg +load 369249-1.svg +load 369291-1.svg +load 369291-2.svg +load 369568-1.svg +load 372046-1.svg +load 372046-2.svg +load 374882-1.svg +load 380101-1.svg +load 381777-1.svg +load 383685-1.svg +load 385096.html +load 385554-1.html +load 385554-2.xul +load 388712-1.svg +load 395616-1.html +load 396618-1.html +load 397017-1.html +load 397551-1.svg +load 397704-1.svg +load 398926-both-different.svg +load 398926-both-same.svg +load 398926-fill.svg +load 398926-stroke.svg +load 405639-1.svg +load 406361-1.html +load 409811-1.html +load 410659-1.svg +load 410659-2.svg +load 410659-3.svg +asserts(3-4) load 412104-1.svg # bug 903785 +load 413174-1.svg +load 414188-1.svg +load 427325-1.svg +load 428228-1.svg +load 428841-1.svg +load 435209-1.svg +load 436418-mpathRoot-1.svg +load 448244-1.svg +load 466576-1.xhtml +load 499879-1.svg +load 535691-1.svg +load 539167-1.svg +load 573316-1.svg +load 579356-1.svg +load 579356-2.svg +load 595608-1.svg +load 601251-1.html +load 601406-1.svg +load 603145-1.svg +load 613899-1.svg +load 613899-2.svg +load 719779-1.svg +load 723441-1.html +load 751515-1.svg +load 761507-1.svg +load 831561.html +load 837450-1.svg +load 842463-1.html +load 847138-1.svg +load 864509.svg +load 880544-1.svg +load 880544-2.svg +load 880544-3.svg +load 880544-4.svg +load 880544-5.svg +load 898915-1.svg +load 1035248-1.svg +load 1035248-2.svg +load 1244898-1.xhtml +load 1250725.html +load 1267272-1.svg +load 1282985-1.svg +load 1329849-1.svg +load 1329849-2.svg +load 1329849-3.svg +load 1329849-4.svg +load 1329849-5.svg +load 1329849-6.svg +load 1343147.svg +# Disabled for now due to it taking a very long time to run - bug 1259356 +#load long-clipPath-reference-chain.svg +load zero-size-image.svg diff --git a/dom/svg/crashtests/invalid-image.svg b/dom/svg/crashtests/invalid-image.svg new file mode 100644 index 0000000000..62d8fe9f6d --- /dev/null +++ b/dom/svg/crashtests/invalid-image.svg @@ -0,0 +1 @@ +X diff --git a/dom/svg/crashtests/long-clipPath-reference-chain.svg b/dom/svg/crashtests/long-clipPath-reference-chain.svg new file mode 100644 index 0000000000..31a587c740 --- /dev/null +++ b/dom/svg/crashtests/long-clipPath-reference-chain.svg @@ -0,0 +1,53 @@ +<svg xmlns="http://www.w3.org/2000/svg"> + <title>Test very long clipPath chain - MAY CRASH</title> + <script><![CDATA[ + +// This script creates a very long chain of clipPath elements to test whether +// that causes a stack overflow that crashes the UA. The first clipPath clips +// to a 50x100 rect, the last to a 25x100 rect. If a UA was to apply the +// entire clipPath chain (unlikely) a rect 25x100 would be rendered. +// +// At the time of writing, Firefox would treat the entire chain of clipPaths as +// invalid due to its excessive length, and refuse to render the referencing +// element at all. One alternative would be to ignore the clipPath reference +// and render the referencing element without any clipping. Another +// alternative would be to break the chain and clip the referencing element, +// but only using the first X clipPaths in the chain (in which case a 50x100 +// rect would be rendered). + +var chainLength = 100000; + +var SVG_NS = "http://www.w3.org/2000/svg"; +var template = document.createElementNS(SVG_NS, "clipPath"); +var templatesRect = document.createElementNS(SVG_NS, "rect"); +templatesRect.setAttribute("width", "100"); +templatesRect.setAttribute("height", "100"); +template.appendChild(templatesRect); + +function createClipPath(index) { + var cp = template.cloneNode(true); + cp.id = "c" + index; + cp.setAttribute("clip-path", "url(#c" + (index + 1) + ")"); + return cp; +} + +var de = document.documentElement; + +for (var i = chainLength; i > 0; --i) { + var cp = createClipPath(i); + + if (i == chainLength) { + cp.firstChild.setAttribute("width", "25"); + } + else if (i == 1) { + cp.firstChild.setAttribute("width", "50"); + } + + de.appendChild(cp); +} + + ]]></script> + <rect width="100%" height="100%" fill="lime"/> + <!-- We don't expect the following element to render at all --> + <rect width="500" height="500" clip-path="url(#c1)"/> +</svg> diff --git a/dom/svg/crashtests/zero-size-image.svg b/dom/svg/crashtests/zero-size-image.svg new file mode 100644 index 0000000000..20d40b2be8 --- /dev/null +++ b/dom/svg/crashtests/zero-size-image.svg @@ -0,0 +1,6 @@ +<svg xmlns="http://www.w3.org/2000/svg" + xmlns:xlink="http://www.w3.org/1999/xlink"> + <!-- Test that we don't fail assertions with zero-size <image> elements. --> + <image xlink:href="data:image/svg+xml,%3Csvg%3E%3C%2Fsvg%3E"/> + <image xlink:href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAAAAAA6fptVAAAAAXNSR0IArs4c6QAAAApJREFUCNdjYAAAAAIAAeIhvDMAAAAASUVORK5CYII%3D"/> +</svg> diff --git a/dom/svg/moz.build b/dom/svg/moz.build new file mode 100644 index 0000000000..298a7293d8 --- /dev/null +++ b/dom/svg/moz.build @@ -0,0 +1,270 @@ +# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*- +# vim: set filetype=python: +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + +MOCHITEST_MANIFESTS += ['test/mochitest.ini'] + +EXPORTS += [ + 'nsSVGClass.h', + 'nsSVGElement.h', + 'nsSVGFeatures.h', + 'SVGAttrValueWrapper.h', + 'SVGContentUtils.h', + 'SVGPreserveAspectRatio.h', + 'SVGStringList.h', +] + +EXPORTS.mozilla.dom += [ + 'nsSVGAnimatedTransformList.h', + 'SVGAElement.h', + 'SVGAngle.h', + 'SVGAnimatedAngle.h', + 'SVGAnimatedBoolean.h', + 'SVGAnimatedEnumeration.h', + 'SVGAnimatedInteger.h', + 'SVGAnimatedLength.h', + 'SVGAnimatedNumber.h', + 'SVGAnimatedRect.h', + 'SVGAnimatedString.h', + 'SVGAnimatedTransformList.h', + 'SVGAnimateElement.h', + 'SVGAnimateMotionElement.h', + 'SVGAnimateTransformElement.h', + 'SVGAnimationElement.h', + 'SVGCircleElement.h', + 'SVGClipPathElement.h', + 'SVGComponentTransferFunctionElement.h', + 'SVGDefsElement.h', + 'SVGDescElement.h', + 'SVGDocument.h', + 'SVGEllipseElement.h', + 'SVGFEBlendElement.h', + 'SVGFEColorMatrixElement.h', + 'SVGFEComponentTransferElement.h', + 'SVGFECompositeElement.h', + 'SVGFEConvolveMatrixElement.h', + 'SVGFEDiffuseLightingElement.h', + 'SVGFEDisplacementMapElement.h', + 'SVGFEDistantLightElement.h', + 'SVGFEDropShadowElement.h', + 'SVGFEFloodElement.h', + 'SVGFEGaussianBlurElement.h', + 'SVGFEImageElement.h', + 'SVGFEMergeElement.h', + 'SVGFEMergeNodeElement.h', + 'SVGFEMorphologyElement.h', + 'SVGFEOffsetElement.h', + 'SVGFEPointLightElement.h', + 'SVGFESpecularLightingElement.h', + 'SVGFESpotLightElement.h', + 'SVGFETileElement.h', + 'SVGFETurbulenceElement.h', + 'SVGFilterElement.h', + 'SVGForeignObjectElement.h', + 'SVGGElement.h', + 'SVGGradientElement.h', + 'SVGGraphicsElement.h', + 'SVGImageElement.h', + 'SVGIRect.h', + 'SVGLineElement.h', + 'SVGMarkerElement.h', + 'SVGMaskElement.h', + 'SVGMatrix.h', + 'SVGMetadataElement.h', + 'SVGMPathElement.h', + 'SVGPathElement.h', + 'SVGPatternElement.h', + 'SVGPolygonElement.h', + 'SVGPolylineElement.h', + 'SVGRect.h', + 'SVGRectElement.h', + 'SVGScriptElement.h', + 'SVGSetElement.h', + 'SVGStopElement.h', + 'SVGStyleElement.h', + 'SVGSVGElement.h', + 'SVGSwitchElement.h', + 'SVGSymbolElement.h', + 'SVGTests.h', + 'SVGTextContentElement.h', + 'SVGTextElement.h', + 'SVGTextPathElement.h', + 'SVGTextPositioningElement.h', + 'SVGTitleElement.h', + 'SVGTransform.h', + 'SVGTransformableElement.h', + 'SVGTSpanElement.h', + 'SVGUseElement.h', + 'SVGViewElement.h', + 'SVGZoomEvent.h', +] + +UNIFIED_SOURCES += [ + 'DOMSVGAnimatedLengthList.cpp', + 'DOMSVGAnimatedNumberList.cpp', + 'DOMSVGLength.cpp', + 'DOMSVGLengthList.cpp', + 'DOMSVGNumber.cpp', + 'DOMSVGNumberList.cpp', + 'DOMSVGPathSeg.cpp', + 'DOMSVGPathSegList.cpp', + 'DOMSVGPoint.cpp', + 'DOMSVGPointList.cpp', + 'DOMSVGStringList.cpp', + 'DOMSVGTransformList.cpp', + 'nsISVGPoint.cpp', + 'nsSVGAngle.cpp', + 'nsSVGAnimatedTransformList.cpp', + 'nsSVGBoolean.cpp', + 'nsSVGClass.cpp', + 'nsSVGDataParser.cpp', + 'nsSVGElement.cpp', + 'nsSVGEnum.cpp', + 'nsSVGFeatures.cpp', + 'nsSVGFilters.cpp', + 'nsSVGInteger.cpp', + 'nsSVGIntegerPair.cpp', + 'nsSVGLength2.cpp', + 'nsSVGNumber2.cpp', + 'nsSVGNumberPair.cpp', + 'nsSVGPathDataParser.cpp', + 'nsSVGPathGeometryElement.cpp', + 'nsSVGPolyElement.cpp', + 'nsSVGString.cpp', + 'nsSVGTransform.cpp', + 'nsSVGViewBox.cpp', + 'SVGAElement.cpp', + 'SVGAngle.cpp', + 'SVGAnimatedAngle.cpp', + 'SVGAnimatedBoolean.cpp', + 'SVGAnimatedEnumeration.cpp', + 'SVGAnimatedInteger.cpp', + 'SVGAnimatedLength.cpp', + 'SVGAnimatedLengthList.cpp', + 'SVGAnimatedNumber.cpp', + 'SVGAnimatedNumberList.cpp', + 'SVGAnimatedPathSegList.cpp', + 'SVGAnimatedPointList.cpp', + 'SVGAnimatedPreserveAspectRatio.cpp', + 'SVGAnimatedRect.cpp', + 'SVGAnimatedString.cpp', + 'SVGAnimatedTransformList.cpp', + 'SVGAnimateElement.cpp', + 'SVGAnimateMotionElement.cpp', + 'SVGAnimateTransformElement.cpp', + 'SVGAnimationElement.cpp', + 'SVGAttrValueWrapper.cpp', + 'SVGCircleElement.cpp', + 'SVGClipPathElement.cpp', + 'SVGContentUtils.cpp', + 'SVGDefsElement.cpp', + 'SVGDescElement.cpp', + 'SVGDocument.cpp', + 'SVGElementFactory.cpp', + 'SVGEllipseElement.cpp', + 'SVGFEBlendElement.cpp', + 'SVGFEColorMatrixElement.cpp', + 'SVGFEComponentTransferElement.cpp', + 'SVGFECompositeElement.cpp', + 'SVGFEConvolveMatrixElement.cpp', + 'SVGFEDiffuseLightingElement.cpp', + 'SVGFEDisplacementMapElement.cpp', + 'SVGFEDistantLightElement.cpp', + 'SVGFEDropShadowElement.cpp', + 'SVGFEFloodElement.cpp', + 'SVGFEGaussianBlurElement.cpp', + 'SVGFEImageElement.cpp', + 'SVGFEMergeElement.cpp', + 'SVGFEMergeNodeElement.cpp', + 'SVGFEMorphologyElement.cpp', + 'SVGFEOffsetElement.cpp', + 'SVGFEPointLightElement.cpp', + 'SVGFESpecularLightingElement.cpp', + 'SVGFESpotLightElement.cpp', + 'SVGFETileElement.cpp', + 'SVGFETurbulenceElement.cpp', + 'SVGFilterElement.cpp', + 'SVGForeignObjectElement.cpp', + 'SVGFragmentIdentifier.cpp', + 'SVGGElement.cpp', + 'SVGGradientElement.cpp', + 'SVGGraphicsElement.cpp', + 'SVGImageElement.cpp', + 'SVGIntegerPairSMILType.cpp', + 'SVGLength.cpp', + 'SVGLengthList.cpp', + 'SVGLengthListSMILType.cpp', + 'SVGLineElement.cpp', + 'SVGMarkerElement.cpp', + 'SVGMaskElement.cpp', + 'SVGMatrix.cpp', + 'SVGMetadataElement.cpp', + 'SVGMotionSMILAnimationFunction.cpp', + 'SVGMotionSMILAttr.cpp', + 'SVGMotionSMILPathUtils.cpp', + 'SVGMotionSMILType.cpp', + 'SVGMPathElement.cpp', + 'SVGNumberList.cpp', + 'SVGNumberListSMILType.cpp', + 'SVGNumberPairSMILType.cpp', + 'SVGOrientSMILType.cpp', + 'SVGPathData.cpp', + 'SVGPathElement.cpp', + 'SVGPathSegListSMILType.cpp', + 'SVGPathSegUtils.cpp', + 'SVGPatternElement.cpp', + 'SVGPointList.cpp', + 'SVGPointListSMILType.cpp', + 'SVGPolygonElement.cpp', + 'SVGPolylineElement.cpp', + 'SVGPreserveAspectRatio.cpp', + 'SVGRect.cpp', + 'SVGRectElement.cpp', + 'SVGScriptElement.cpp', + 'SVGSetElement.cpp', + 'SVGStopElement.cpp', + 'SVGStringList.cpp', + 'SVGStyleElement.cpp', + 'SVGSVGElement.cpp', + 'SVGSwitchElement.cpp', + 'SVGSymbolElement.cpp', + 'SVGTests.cpp', + 'SVGTextContentElement.cpp', + 'SVGTextElement.cpp', + 'SVGTextPathElement.cpp', + 'SVGTextPositioningElement.cpp', + 'SVGTitleElement.cpp', + 'SVGTransform.cpp', + 'SVGTransformableElement.cpp', + 'SVGTransformList.cpp', + 'SVGTransformListParser.cpp', + 'SVGTransformListSMILType.cpp', + 'SVGTSpanElement.cpp', + 'SVGUseElement.cpp', + 'SVGViewBoxSMILType.cpp', + 'SVGViewElement.cpp', + 'SVGZoomEvent.cpp', +] + +include('/ipc/chromium/chromium-config.mozbuild') + +FINAL_LIBRARY = 'xul' +LOCAL_INCLUDES += [ + '/dom', + '/dom/base', + '/dom/html', + '/dom/smil', + '/dom/svg', + '/dom/xbl', + '/dom/xml', + '/layout/base', + '/layout/generic', + '/layout/style', + '/layout/svg', + '/layout/xul', +] + +if CONFIG['GNU_CXX']: + CXXFLAGS += ['-Wno-error=shadow'] diff --git a/dom/svg/nsISVGPoint.cpp b/dom/svg/nsISVGPoint.cpp new file mode 100644 index 0000000000..1c19710041 --- /dev/null +++ b/dom/svg/nsISVGPoint.cpp @@ -0,0 +1,88 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "nsISVGPoint.h" +#include "DOMSVGPointList.h" +#include "SVGPoint.h" +#include "nsSVGElement.h" +#include "nsError.h" +#include "mozilla/dom/SVGPointBinding.h" + +// See the architecture comment in DOMSVGPointList.h. + +using namespace mozilla; + +// We could use NS_IMPL_CYCLE_COLLECTION(, 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(nsISVGPoint) + +NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsISVGPoint) + // We may not belong to a list, so we must null check tmp->mList. + if (tmp->mList) { + tmp->mList->mItems[tmp->mListIndex] = nullptr; + } +NS_IMPL_CYCLE_COLLECTION_UNLINK(mList) +NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER +NS_IMPL_CYCLE_COLLECTION_UNLINK_END + +NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsISVGPoint) +NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mList) +NS_IMPL_CYCLE_COLLECTION_TRAVERSE_SCRIPT_OBJECTS +NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END + +NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(nsISVGPoint) +NS_IMPL_CYCLE_COLLECTION_TRACE_PRESERVED_WRAPPER +NS_IMPL_CYCLE_COLLECTION_TRACE_END + +NS_IMPL_CYCLE_COLLECTING_ADDREF(nsISVGPoint) +NS_IMPL_CYCLE_COLLECTING_RELEASE(nsISVGPoint) + +NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsISVGPoint) + NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY + NS_INTERFACE_MAP_ENTRY(nsISVGPoint) + NS_INTERFACE_MAP_ENTRY(nsISupports) +NS_INTERFACE_MAP_END + +void +nsISVGPoint::InsertingIntoList(DOMSVGPointList *aList, + uint32_t aListIndex, + bool aIsAnimValItem) +{ + MOZ_ASSERT(!HasOwner(), "Inserting item that already has an owner"); + + mList = aList; + mListIndex = aListIndex; + mIsReadonly = false; + mIsAnimValItem = aIsAnimValItem; + + MOZ_ASSERT(IndexIsValid(), "Bad index for DOMSVGPoint!"); +} + +void +nsISVGPoint::RemovingFromList() +{ + mPt = InternalItem(); + mList = nullptr; + MOZ_ASSERT(!mIsReadonly, "mIsReadonly set for list"); + mIsAnimValItem = false; +} + +SVGPoint& +nsISVGPoint::InternalItem() +{ + return mList->InternalList().mItems[mListIndex]; +} + +#ifdef DEBUG +bool +nsISVGPoint::IndexIsValid() +{ + return mListIndex < mList->InternalList().Length(); +} +#endif + diff --git a/dom/svg/nsISVGPoint.h b/dom/svg/nsISVGPoint.h new file mode 100644 index 0000000000..4c075e8d37 --- /dev/null +++ b/dom/svg/nsISVGPoint.h @@ -0,0 +1,187 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#pragma once + +#include "nsCycleCollectionParticipant.h" +#include "nsWrapperCache.h" +#include "mozilla/dom/SVGPointBinding.h" +#include "DOMSVGPointList.h" + +// {d6b6c440-af8d-40ee-856b-02a317cab275} +#define MOZILLA_NSISVGPOINT_IID \ + { 0xd6b6c440, 0xaf8d, 0x40ee, \ + { 0x85, 0x6b, 0x02, 0xa3, 0x17, 0xca, 0xb2, 0x75 } } + +#define MOZ_SVG_LIST_INDEX_BIT_COUNT 29 + +namespace mozilla { + +namespace dom { +class SVGMatrix; +} // namespace dom + +/** + * Class nsISVGPoint + * + * This class creates the DOM objects that wrap internal SVGPoint objects. + * An nsISVGPoint can be either a DOMSVGPoint or a DOMSVGTranslatePoint + */ +class nsISVGPoint : public nsISupports, + public nsWrapperCache +{ +public: + NS_DECLARE_STATIC_IID_ACCESSOR(MOZILLA_NSISVGPOINT_IID) + NS_DECL_CYCLE_COLLECTING_ISUPPORTS + NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(nsISVGPoint) + + /** + * Generic ctor for DOMSVGPoint objects that are created for an attribute. + */ + explicit nsISVGPoint() + : mList(nullptr) + , mListIndex(0) + , mIsReadonly(false) + , mIsAnimValItem(false) + , mIsTranslatePoint(false) + { + } + + explicit nsISVGPoint(SVGPoint* aPt, bool aIsTranslatePoint) + : mList(nullptr) + , mListIndex(0) + , mIsReadonly(false) + , mIsAnimValItem(false) + , mIsTranslatePoint(aIsTranslatePoint) + { + mPt.mX = aPt->GetX(); + mPt.mY = aPt->GetY(); + } + +protected: + virtual ~nsISVGPoint() + { + // 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] = nullptr; + } + } + +public: + /** + * Creates an unowned copy of this object's point as a DOMSVGPoint. + */ + virtual DOMSVGPoint* Copy() = 0; + + SVGPoint ToSVGPoint() const { + return HasOwner() ? const_cast<nsISVGPoint*>(this)->InternalItem() : mPt; + } + + bool IsInList() const { + return !!mList; + } + + /** + * In future, if this class is used for non-list points, this will be + * different to IsInList(). "Owner" here means that the instance has an + * internal counterpart from which it gets its values. (A better name may + * be HasWrappee().) + */ + bool HasOwner() const { + return !!mList; + } + + bool IsTranslatePoint() const { + return mIsTranslatePoint; + } + + /** + * 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 DOMSVGPointList).) + */ + void InsertingIntoList(DOMSVGPointList *aList, + uint32_t aListIndex, + bool aIsAnimValItem); + + static uint32_t MaxListIndex() { + return (1U << MOZ_SVG_LIST_INDEX_BIT_COUNT) - 1; + } + + /// This method is called to notify this object that its list index changed. + void UpdateListIndex(uint32_t 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 + * "lose" its value on being removed.) + */ + void RemovingFromList(); + + bool IsReadonly() const { + return mIsReadonly; + } + void SetReadonly(bool aReadonly) { + mIsReadonly = aReadonly; + } + + // WebIDL + virtual float X() = 0; + virtual void SetX(float aX, ErrorResult& rv) = 0; + virtual float Y() = 0; + virtual void SetY(float aY, ErrorResult& rv) = 0; + virtual already_AddRefed<nsISVGPoint> MatrixTransform(dom::SVGMatrix& matrix) = 0; + virtual JSObject* WrapObject(JSContext *cx, JS::Handle<JSObject*> aGivenProto) override + { return dom::SVGPointBinding::Wrap(cx, this, aGivenProto); } + + virtual nsISupports* GetParentObject() = 0; + +protected: +#ifdef DEBUG + bool IndexIsValid(); +#endif + + RefPtr<DOMSVGPointList> 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. + + uint32_t mListIndex:MOZ_SVG_LIST_INDEX_BIT_COUNT; + uint32_t mIsReadonly:1; // These flags are uint32_t because MSVC won't + uint32_t mIsAnimValItem:1; // pack otherwise. + uint32_t mIsTranslatePoint:1; + + /** + * Get a reference to the internal SVGPoint list item that this DOM wrapper + * object currently wraps. + * + * To simplify 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. + */ + SVGPoint& InternalItem(); + + // The following member is only used when we're not in a list: + SVGPoint mPt; +}; + +NS_DEFINE_STATIC_IID_ACCESSOR(nsISVGPoint, MOZILLA_NSISVGPOINT_IID) + +} // namespace mozilla + +#undef MOZ_SVG_LIST_INDEX_BIT_COUNT + + diff --git a/dom/svg/nsSVGAngle.cpp b/dom/svg/nsSVGAngle.cpp new file mode 100644 index 0000000000..848ecb04ed --- /dev/null +++ b/dom/svg/nsSVGAngle.cpp @@ -0,0 +1,431 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "nsSVGAngle.h" + +#include "mozilla/ArrayUtils.h" +#include "mozilla/dom/SVGMarkerElement.h" +#include "mozilla/Move.h" +#include "nsContentUtils.h" // NS_ENSURE_FINITE +#include "nsSMILValue.h" +#include "nsSVGAttrTearoffTable.h" +#include "nsTextFormatter.h" +#include "SVGAngle.h" +#include "SVGAnimatedAngle.h" +#include "SVGOrientSMILType.h" + +using namespace mozilla; +using namespace mozilla::dom; + +static nsIAtom** const unitMap[] = +{ + nullptr, /* SVG_ANGLETYPE_UNKNOWN */ + nullptr, /* SVG_ANGLETYPE_UNSPECIFIED */ + &nsGkAtoms::deg, + &nsGkAtoms::rad, + &nsGkAtoms::grad +}; + +static nsSVGAttrTearoffTable<nsSVGAngle, SVGAnimatedAngle> + sSVGAnimatedAngleTearoffTable; +static nsSVGAttrTearoffTable<nsSVGAngle, SVGAngle> + sBaseSVGAngleTearoffTable; +static nsSVGAttrTearoffTable<nsSVGAngle, SVGAngle> + sAnimSVGAngleTearoffTable; + +/* Helper functions */ + +static bool +IsValidUnitType(uint16_t unit) +{ + if (unit > SVG_ANGLETYPE_UNKNOWN && + unit <= SVG_ANGLETYPE_GRAD) + return true; + + return false; +} + +static void +GetUnitString(nsAString& unit, uint16_t unitType) +{ + if (IsValidUnitType(unitType)) { + if (unitMap[unitType]) { + (*unitMap[unitType])->ToString(unit); + } + return; + } + + NS_NOTREACHED("Unknown unit type"); + return; +} + +static uint16_t +GetUnitTypeForString(const nsAString& unitStr) +{ + if (unitStr.IsEmpty()) + return SVG_ANGLETYPE_UNSPECIFIED; + + nsIAtom *unitAtom = NS_GetStaticAtom(unitStr); + + if (unitAtom) { + for (uint32_t i = 0 ; i < ArrayLength(unitMap) ; i++) { + if (unitMap[i] && *unitMap[i] == unitAtom) { + return i; + } + } + } + + return SVG_ANGLETYPE_UNKNOWN; +} + +static void +GetValueString(nsAString &aValueAsString, float aValue, uint16_t aUnitType) +{ + char16_t buf[24]; + nsTextFormatter::snprintf(buf, sizeof(buf)/sizeof(char16_t), + u"%g", + (double)aValue); + aValueAsString.Assign(buf); + + nsAutoString unitString; + GetUnitString(unitString, aUnitType); + aValueAsString.Append(unitString); +} + +static bool +GetValueFromString(const nsAString& aString, + float& aValue, + uint16_t* aUnitType) +{ + RangedPtr<const char16_t> iter = + SVGContentUtils::GetStartRangedPtr(aString); + const RangedPtr<const char16_t> end = + SVGContentUtils::GetEndRangedPtr(aString); + + if (!SVGContentUtils::ParseNumber(iter, end, aValue)) { + return false; + } + + const nsAString& units = Substring(iter.get(), end.get()); + *aUnitType = GetUnitTypeForString(units); + return IsValidUnitType(*aUnitType); +} + +/* static */ float +nsSVGAngle::GetDegreesPerUnit(uint8_t aUnit) +{ + switch (aUnit) { + case SVG_ANGLETYPE_UNSPECIFIED: + case SVG_ANGLETYPE_DEG: + return 1; + case SVG_ANGLETYPE_RAD: + return static_cast<float>(180.0 / M_PI); + case SVG_ANGLETYPE_GRAD: + return 90.0f / 100.0f; + default: + NS_NOTREACHED("Unknown unit type"); + return 0; + } +} + +void +nsSVGAngle::SetBaseValueInSpecifiedUnits(float aValue, + nsSVGElement *aSVGElement) +{ + if (mBaseVal == aValue) { + return; + } + + nsAttrValue emptyOrOldValue = aSVGElement->WillChangeAngle(mAttrEnum); + mBaseVal = aValue; + if (!mIsAnimated) { + mAnimVal = mBaseVal; + } + else { + aSVGElement->AnimationNeedsResample(); + } + aSVGElement->DidChangeAngle(mAttrEnum, emptyOrOldValue); +} + +nsresult +nsSVGAngle::ConvertToSpecifiedUnits(uint16_t unitType, + nsSVGElement *aSVGElement) +{ + if (!IsValidUnitType(unitType)) + return NS_ERROR_DOM_NOT_SUPPORTED_ERR; + + if (mBaseValUnit == uint8_t(unitType)) + return NS_OK; + + nsAttrValue emptyOrOldValue; + if (aSVGElement) { + emptyOrOldValue = aSVGElement->WillChangeAngle(mAttrEnum); + } + + float valueInUserUnits = mBaseVal * GetDegreesPerUnit(mBaseValUnit); + mBaseValUnit = uint8_t(unitType); + // Setting aDoSetAttr to false here will ensure we don't call + // Will/DidChangeAngle a second time (and dispatch duplicate notifications). + SetBaseValue(valueInUserUnits, aSVGElement, false); + + if (aSVGElement) { + aSVGElement->DidChangeAngle(mAttrEnum, emptyOrOldValue); + } + + return NS_OK; +} + +nsresult +nsSVGAngle::NewValueSpecifiedUnits(uint16_t unitType, + float valueInSpecifiedUnits, + nsSVGElement *aSVGElement) +{ + NS_ENSURE_FINITE(valueInSpecifiedUnits, NS_ERROR_ILLEGAL_VALUE); + + if (!IsValidUnitType(unitType)) + return NS_ERROR_DOM_NOT_SUPPORTED_ERR; + + if (mBaseVal == valueInSpecifiedUnits && mBaseValUnit == uint8_t(unitType)) + return NS_OK; + + nsAttrValue emptyOrOldValue; + if (aSVGElement) { + emptyOrOldValue = aSVGElement->WillChangeAngle(mAttrEnum); + } + mBaseVal = valueInSpecifiedUnits; + mBaseValUnit = uint8_t(unitType); + if (!mIsAnimated) { + mAnimVal = mBaseVal; + mAnimValUnit = mBaseValUnit; + } + else { + aSVGElement->AnimationNeedsResample(); + } + if (aSVGElement) { + aSVGElement->DidChangeAngle(mAttrEnum, emptyOrOldValue); + } + return NS_OK; +} + +already_AddRefed<SVGAngle> +nsSVGAngle::ToDOMBaseVal(nsSVGElement *aSVGElement) +{ + RefPtr<SVGAngle> domBaseVal = + sBaseSVGAngleTearoffTable.GetTearoff(this); + if (!domBaseVal) { + domBaseVal = new SVGAngle(this, aSVGElement, SVGAngle::BaseValue); + sBaseSVGAngleTearoffTable.AddTearoff(this, domBaseVal); + } + + return domBaseVal.forget(); +} + +already_AddRefed<SVGAngle> +nsSVGAngle::ToDOMAnimVal(nsSVGElement *aSVGElement) +{ + RefPtr<SVGAngle> domAnimVal = + sAnimSVGAngleTearoffTable.GetTearoff(this); + if (!domAnimVal) { + domAnimVal = new SVGAngle(this, aSVGElement, SVGAngle::AnimValue); + sAnimSVGAngleTearoffTable.AddTearoff(this, domAnimVal); + } + + return domAnimVal.forget(); +} + +SVGAngle::~SVGAngle() +{ + if (mType == BaseValue) { + sBaseSVGAngleTearoffTable.RemoveTearoff(mVal); + } else if (mType == AnimValue) { + sAnimSVGAngleTearoffTable.RemoveTearoff(mVal); + } else { + delete mVal; + } +} + +/* Implementation */ + +nsresult +nsSVGAngle::SetBaseValueString(const nsAString &aValueAsString, + nsSVGElement *aSVGElement, + bool aDoSetAttr) +{ + float value; + uint16_t unitType; + + if (!GetValueFromString(aValueAsString, value, &unitType)) { + return NS_ERROR_DOM_SYNTAX_ERR; + } + if (mBaseVal == value && mBaseValUnit == uint8_t(unitType)) { + return NS_OK; + } + + nsAttrValue emptyOrOldValue; + if (aDoSetAttr) { + emptyOrOldValue = aSVGElement->WillChangeAngle(mAttrEnum); + } + mBaseVal = value; + mBaseValUnit = uint8_t(unitType); + if (!mIsAnimated) { + mAnimVal = mBaseVal; + mAnimValUnit = mBaseValUnit; + } + else { + aSVGElement->AnimationNeedsResample(); + } + + if (aDoSetAttr) { + aSVGElement->DidChangeAngle(mAttrEnum, emptyOrOldValue); + } + return NS_OK; +} + +void +nsSVGAngle::GetBaseValueString(nsAString & aValueAsString) const +{ + GetValueString(aValueAsString, mBaseVal, mBaseValUnit); +} + +void +nsSVGAngle::GetAnimValueString(nsAString & aValueAsString) const +{ + GetValueString(aValueAsString, mAnimVal, mAnimValUnit); +} + +void +nsSVGAngle::SetBaseValue(float aValue, nsSVGElement *aSVGElement, + bool aDoSetAttr) +{ + if (mBaseVal == aValue * GetDegreesPerUnit(mBaseValUnit)) { + return; + } + nsAttrValue emptyOrOldValue; + if (aSVGElement && aDoSetAttr) { + emptyOrOldValue = aSVGElement->WillChangeAngle(mAttrEnum); + } + + mBaseVal = aValue / GetDegreesPerUnit(mBaseValUnit); + if (!mIsAnimated) { + mAnimVal = mBaseVal; + } + else { + aSVGElement->AnimationNeedsResample(); + } + if (aSVGElement && aDoSetAttr) { + aSVGElement->DidChangeAngle(mAttrEnum, emptyOrOldValue); + } +} + +void +nsSVGAngle::SetAnimValue(float aValue, uint8_t aUnit, nsSVGElement *aSVGElement) +{ + if (mIsAnimated && mAnimVal == aValue && mAnimValUnit == aUnit) { + return; + } + mAnimVal = aValue; + mAnimValUnit = aUnit; + mIsAnimated = true; + aSVGElement->DidAnimateAngle(mAttrEnum); +} + +already_AddRefed<SVGAnimatedAngle> +nsSVGAngle::ToDOMAnimatedAngle(nsSVGElement *aSVGElement) +{ + RefPtr<SVGAnimatedAngle> domAnimatedAngle = + sSVGAnimatedAngleTearoffTable.GetTearoff(this); + if (!domAnimatedAngle) { + domAnimatedAngle = new SVGAnimatedAngle(this, aSVGElement); + sSVGAnimatedAngleTearoffTable.AddTearoff(this, domAnimatedAngle); + } + + return domAnimatedAngle.forget(); +} + +SVGAnimatedAngle::~SVGAnimatedAngle() +{ + sSVGAnimatedAngleTearoffTable.RemoveTearoff(mVal); +} + +nsISMILAttr* +nsSVGAngle::ToSMILAttr(nsSVGElement *aSVGElement) +{ + if (aSVGElement->NodeInfo()->Equals(nsGkAtoms::marker, kNameSpaceID_SVG)) { + SVGMarkerElement *marker = static_cast<SVGMarkerElement*>(aSVGElement); + return new SMILOrient(marker->GetOrientType(), this, aSVGElement); + } + // SMILOrient would not be useful for general angle attributes (also, + // "orient" is the only animatable <angle>-valued attribute in SVG 1.1). + NS_NOTREACHED("Trying to animate unknown angle attribute."); + return nullptr; +} + +nsresult +nsSVGAngle::SMILOrient::ValueFromString(const nsAString& aStr, + const SVGAnimationElement* /*aSrcElement*/, + nsSMILValue& aValue, + bool& aPreventCachingOfSandwich) const +{ + nsSMILValue val(&SVGOrientSMILType::sSingleton); + if (aStr.EqualsLiteral("auto")) { + val.mU.mOrient.mOrientType = SVG_MARKER_ORIENT_AUTO; + } else if (aStr.EqualsLiteral("auto-start-reverse")) { + val.mU.mOrient.mOrientType = SVG_MARKER_ORIENT_AUTO_START_REVERSE; + } else { + float value; + uint16_t unitType; + if (!GetValueFromString(aStr, value, &unitType)) { + return NS_ERROR_DOM_SYNTAX_ERR; + } + val.mU.mOrient.mAngle = value; + val.mU.mOrient.mUnit = unitType; + val.mU.mOrient.mOrientType = SVG_MARKER_ORIENT_ANGLE; + } + aValue = Move(val); + aPreventCachingOfSandwich = false; + + return NS_OK; +} + +nsSMILValue +nsSVGAngle::SMILOrient::GetBaseValue() const +{ + nsSMILValue val(&SVGOrientSMILType::sSingleton); + val.mU.mOrient.mAngle = mAngle->GetBaseValInSpecifiedUnits(); + val.mU.mOrient.mUnit = mAngle->GetBaseValueUnit(); + val.mU.mOrient.mOrientType = mOrientType->GetBaseValue(); + return val; +} + +void +nsSVGAngle::SMILOrient::ClearAnimValue() +{ + if (mAngle->mIsAnimated) { + mOrientType->SetAnimValue(mOrientType->GetBaseValue()); + mAngle->mIsAnimated = false; + mAngle->mAnimVal = mAngle->mBaseVal; + mAngle->mAnimValUnit = mAngle->mBaseValUnit; + mSVGElement->DidAnimateAngle(mAngle->mAttrEnum); + } +} + +nsresult +nsSVGAngle::SMILOrient::SetAnimValue(const nsSMILValue& aValue) +{ + NS_ASSERTION(aValue.mType == &SVGOrientSMILType::sSingleton, + "Unexpected type to assign animated value"); + + if (aValue.mType == &SVGOrientSMILType::sSingleton) { + mOrientType->SetAnimValue(aValue.mU.mOrient.mOrientType); + if (aValue.mU.mOrient.mOrientType == SVG_MARKER_ORIENT_AUTO || + aValue.mU.mOrient.mOrientType == SVG_MARKER_ORIENT_AUTO_START_REVERSE) { + mAngle->SetAnimValue(0.0f, SVG_ANGLETYPE_UNSPECIFIED, mSVGElement); + } else { + mAngle->SetAnimValue(aValue.mU.mOrient.mAngle, aValue.mU.mOrient.mUnit, mSVGElement); + } + } + return NS_OK; +} diff --git a/dom/svg/nsSVGAngle.h b/dom/svg/nsSVGAngle.h new file mode 100644 index 0000000000..29b7d066a9 --- /dev/null +++ b/dom/svg/nsSVGAngle.h @@ -0,0 +1,129 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef __NS_SVGANGLE_H__ +#define __NS_SVGANGLE_H__ + +#include "nsCOMPtr.h" +#include "nsError.h" +#include "nsISMILAttr.h" +#include "mozilla/Attributes.h" + +class nsISupports; +class nsSMILValue; +class nsSVGElement; + +namespace mozilla { + +// Angle Unit Types +static const unsigned short SVG_ANGLETYPE_UNKNOWN = 0; +static const unsigned short SVG_ANGLETYPE_UNSPECIFIED = 1; +static const unsigned short SVG_ANGLETYPE_DEG = 2; +static const unsigned short SVG_ANGLETYPE_RAD = 3; +static const unsigned short SVG_ANGLETYPE_GRAD = 4; + +namespace dom { +class nsSVGOrientType; +class SVGAngle; +class SVGAnimatedAngle; +class SVGAnimationElement; +} // namespace dom +} // namespace mozilla + +class nsSVGAngle +{ + friend class mozilla::dom::SVGAngle; + friend class mozilla::dom::SVGAnimatedAngle; + +public: + void Init(uint8_t aAttrEnum = 0xff, + float aValue = 0, + uint8_t aUnitType = mozilla::SVG_ANGLETYPE_UNSPECIFIED) { + mAnimVal = mBaseVal = aValue; + mAnimValUnit = mBaseValUnit = aUnitType; + mAttrEnum = aAttrEnum; + mIsAnimated = false; + } + + nsresult SetBaseValueString(const nsAString& aValue, + nsSVGElement *aSVGElement, + bool aDoSetAttr); + void GetBaseValueString(nsAString& aValue) const; + void GetAnimValueString(nsAString& aValue) const; + + float GetBaseValue() const + { return mBaseVal * GetDegreesPerUnit(mBaseValUnit); } + float GetAnimValue() const + { return mAnimVal * GetDegreesPerUnit(mAnimValUnit); } + + void SetBaseValue(float aValue, nsSVGElement *aSVGElement, bool aDoSetAttr); + void SetAnimValue(float aValue, uint8_t aUnit, nsSVGElement *aSVGElement); + + uint8_t GetBaseValueUnit() const { return mBaseValUnit; } + uint8_t GetAnimValueUnit() const { return mAnimValUnit; } + float GetBaseValInSpecifiedUnits() const { return mBaseVal; } + float GetAnimValInSpecifiedUnits() const { return mAnimVal; } + + static nsresult ToDOMSVGAngle(nsISupports **aResult); + already_AddRefed<mozilla::dom::SVGAnimatedAngle> + ToDOMAnimatedAngle(nsSVGElement* aSVGElement); + // Returns a new nsISMILAttr object that the caller must delete + nsISMILAttr* ToSMILAttr(nsSVGElement* aSVGElement); + + static float GetDegreesPerUnit(uint8_t aUnit); + +private: + + float mAnimVal; + float mBaseVal; + uint8_t mAnimValUnit; + uint8_t mBaseValUnit; + uint8_t mAttrEnum; // element specified tracking for attribute + bool mIsAnimated; + + void SetBaseValueInSpecifiedUnits(float aValue, nsSVGElement *aSVGElement); + nsresult NewValueSpecifiedUnits(uint16_t aUnitType, float aValue, + nsSVGElement *aSVGElement); + nsresult ConvertToSpecifiedUnits(uint16_t aUnitType, nsSVGElement *aSVGElement); + already_AddRefed<mozilla::dom::SVGAngle> ToDOMBaseVal(nsSVGElement* aSVGElement); + already_AddRefed<mozilla::dom::SVGAngle> ToDOMAnimVal(nsSVGElement* aSVGElement); + +public: + // We do not currently implemente a SMILAngle struct because in SVG 1.1 the + // only *animatable* attribute that takes an <angle> is 'orient', on the + // 'marker' element, and 'orient' must be special cased since it can also + // take the value 'auto', making it a more complex type. + + struct SMILOrient final : public nsISMILAttr + { + public: + SMILOrient(mozilla::dom::nsSVGOrientType* aOrientType, + nsSVGAngle* aAngle, + nsSVGElement* aSVGElement) + : mOrientType(aOrientType) + , mAngle(aAngle) + , mSVGElement(aSVGElement) + {} + + // 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. + mozilla::dom::nsSVGOrientType* mOrientType; + nsSVGAngle* mAngle; + nsSVGElement* mSVGElement; + + // nsISMILAttr methods + virtual nsresult ValueFromString(const nsAString& aStr, + const mozilla::dom::SVGAnimationElement* aSrcElement, + nsSMILValue& aValue, + bool& aPreventCachingOfSandwich) const override; + virtual nsSMILValue GetBaseValue() const override; + virtual void ClearAnimValue() override; + virtual nsresult SetAnimValue(const nsSMILValue& aValue) override; + }; +}; + +#endif //__NS_SVGANGLE_H__ diff --git a/dom/svg/nsSVGAnimatedTransformList.cpp b/dom/svg/nsSVGAnimatedTransformList.cpp new file mode 100644 index 0000000000..b5933faef7 --- /dev/null +++ b/dom/svg/nsSVGAnimatedTransformList.cpp @@ -0,0 +1,323 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "nsSVGAnimatedTransformList.h" + +#include "mozilla/dom/SVGAnimatedTransformList.h" +#include "mozilla/dom/SVGAnimationElement.h" +#include "mozilla/Move.h" +#include "nsCharSeparatedTokenizer.h" +#include "nsSVGTransform.h" +#include "nsSMILValue.h" +#include "SVGContentUtils.h" +#include "SVGTransformListSMILType.h" +#include "nsIDOMMutationEvent.h" + +namespace mozilla { + +using namespace dom; + +nsresult +nsSVGAnimatedTransformList::SetBaseValueString(const nsAString& aValue) +{ + SVGTransformList newBaseValue; + nsresult rv = newBaseValue.SetValueFromString(aValue); + if (NS_FAILED(rv)) { + return rv; + } + + return SetBaseValue(newBaseValue); +} + +nsresult +nsSVGAnimatedTransformList::SetBaseValue(const SVGTransformList& aValue) +{ + SVGAnimatedTransformList *domWrapper = + SVGAnimatedTransformList::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->InternalBaseValListWillChangeLengthTo(aValue.Length()); + } + + // (This bool will be copied to our member-var, if attr-change succeeds.) + bool hadTransform = HasTransform(); + + // We don't need to call DidChange* here - we're only called by + // nsSVGElement::ParseAttribute under Element::SetAttr, + // which takes care of notifying. + + nsresult rv = mBaseVal.CopyFrom(aValue); + if (NS_FAILED(rv) && domWrapper) { + // Attempting to increase mBaseVal's length failed - reduce domWrapper + // back to the same length: + domWrapper->InternalBaseValListWillChangeLengthTo(mBaseVal.Length()); + } else { + mIsAttrSet = true; + mHadTransformBeforeLastBaseValChange = hadTransform; + } + return rv; +} + +void +nsSVGAnimatedTransformList::ClearBaseValue() +{ + mHadTransformBeforeLastBaseValChange = HasTransform(); + + SVGAnimatedTransformList *domWrapper = + SVGAnimatedTransformList::GetDOMWrapperIfExists(this); + if (domWrapper) { + // We must send this notification *before* changing mBaseVal! (See above.) + domWrapper->InternalBaseValListWillChangeLengthTo(0); + } + mBaseVal.Clear(); + mIsAttrSet = false; + // Caller notifies +} + +nsresult +nsSVGAnimatedTransformList::SetAnimValue(const SVGTransformList& aValue, + nsSVGElement *aElement) +{ + bool prevSet = HasTransform() || aElement->GetAnimateMotionTransform(); + SVGAnimatedTransformList *domWrapper = + SVGAnimatedTransformList::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 SVGAnimatedTransformList::InternalBaseValListWillChangeLengthTo). + // + // 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->InternalAnimValListWillChangeLengthTo(aValue.Length()); + } + if (!mAnimVal) { + mAnimVal = new SVGTransformList(); + } + nsresult rv = mAnimVal->CopyFrom(aValue); + 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); + return rv; + } + int32_t modType; + if(prevSet) { + modType = nsIDOMMutationEvent::MODIFICATION; + } else { + modType = nsIDOMMutationEvent::ADDITION; + } + aElement->DidAnimateTransformList(modType); + return NS_OK; +} + +void +nsSVGAnimatedTransformList::ClearAnimValue(nsSVGElement *aElement) +{ + SVGAnimatedTransformList *domWrapper = + SVGAnimatedTransformList::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->InternalAnimValListWillChangeLengthTo(mBaseVal.Length()); + } + mAnimVal = nullptr; + int32_t modType; + if (HasTransform() || aElement->GetAnimateMotionTransform()) { + modType = nsIDOMMutationEvent::MODIFICATION; + } else { + modType = nsIDOMMutationEvent::REMOVAL; + } + aElement->DidAnimateTransformList(modType); +} + +bool +nsSVGAnimatedTransformList::IsExplicitlySet() const +{ + // Like other methods of this name, we need to know when a transform value has + // been explicitly set. + // + // There are three ways an animated list can become set: + // 1) Markup -- we set mIsAttrSet to true on any successful call to + // SetBaseValueString and clear it on ClearBaseValue (as called by + // nsSVGElement::UnsetAttr or a failed nsSVGElement::ParseAttribute) + // 2) DOM call -- simply fetching the baseVal doesn't mean the transform value + // has been set. It is set if that baseVal has one or more transforms in + // the list. + // 3) Animation -- which will cause the mAnimVal member to be allocated + return mIsAttrSet || !mBaseVal.IsEmpty() || mAnimVal; +} + +nsISMILAttr* +nsSVGAnimatedTransformList::ToSMILAttr(nsSVGElement* aSVGElement) +{ + return new SMILAnimatedTransformList(this, aSVGElement); +} + +nsresult +nsSVGAnimatedTransformList::SMILAnimatedTransformList::ValueFromString( + const nsAString& aStr, + const dom::SVGAnimationElement* aSrcElement, + nsSMILValue& aValue, + bool& aPreventCachingOfSandwich) const +{ + NS_ENSURE_TRUE(aSrcElement, NS_ERROR_FAILURE); + MOZ_ASSERT(aValue.IsNull(), + "aValue should have been cleared before calling ValueFromString"); + + const nsAttrValue* typeAttr = aSrcElement->GetAnimAttr(nsGkAtoms::type); + const nsIAtom* transformType = nsGkAtoms::translate; // default val + if (typeAttr) { + if (typeAttr->Type() != nsAttrValue::eAtom) { + // Recognized values of |type| are parsed as an atom -- so if we have + // something other than an atom, then we know already our |type| is + // invalid. + return NS_ERROR_FAILURE; + } + transformType = typeAttr->GetAtomValue(); + } + + ParseValue(aStr, transformType, aValue); + aPreventCachingOfSandwich = false; + return aValue.IsNull() ? NS_ERROR_FAILURE : NS_OK; +} + +void +nsSVGAnimatedTransformList::SMILAnimatedTransformList::ParseValue( + const nsAString& aSpec, + const nsIAtom* aTransformType, + nsSMILValue& aResult) +{ + MOZ_ASSERT(aResult.IsNull(), "Unexpected type for SMIL value"); + + static_assert(SVGTransformSMILData::NUM_SIMPLE_PARAMS == 3, + "nsSVGSMILTransform constructor should be expecting array " + "with 3 params"); + + float params[3] = { 0.f }; + int32_t numParsed = ParseParameterList(aSpec, params, 3); + uint16_t transformType; + + if (aTransformType == nsGkAtoms::translate) { + // tx [ty=0] + if (numParsed != 1 && numParsed != 2) + return; + transformType = SVG_TRANSFORM_TRANSLATE; + } else if (aTransformType == nsGkAtoms::scale) { + // sx [sy=sx] + if (numParsed != 1 && numParsed != 2) + return; + if (numParsed == 1) { + params[1] = params[0]; + } + transformType = SVG_TRANSFORM_SCALE; + } else if (aTransformType == nsGkAtoms::rotate) { + // r [cx=0 cy=0] + if (numParsed != 1 && numParsed != 3) + return; + transformType = SVG_TRANSFORM_ROTATE; + } else if (aTransformType == nsGkAtoms::skewX) { + // x-angle + if (numParsed != 1) + return; + transformType = SVG_TRANSFORM_SKEWX; + } else if (aTransformType == nsGkAtoms::skewY) { + // y-angle + if (numParsed != 1) + return; + transformType = SVG_TRANSFORM_SKEWY; + } else { + return; + } + + nsSMILValue val(SVGTransformListSMILType::Singleton()); + SVGTransformSMILData transform(transformType, params); + if (NS_FAILED(SVGTransformListSMILType::AppendTransform(transform, val))) { + return; // OOM + } + + // Success! Populate our outparam with parsed value. + aResult = Move(val); +} + +int32_t +nsSVGAnimatedTransformList::SMILAnimatedTransformList::ParseParameterList( + const nsAString& aSpec, + float* aVars, + int32_t aNVars) +{ + nsCharSeparatedTokenizerTemplate<IsSVGWhitespace> + tokenizer(aSpec, ',', nsCharSeparatedTokenizer::SEPARATOR_OPTIONAL); + + int numArgsFound = 0; + + while (tokenizer.hasMoreTokens()) { + float f; + if (!SVGContentUtils::ParseNumber(tokenizer.nextToken(), f)) { + return -1; + } + if (numArgsFound < aNVars) { + aVars[numArgsFound] = f; + } + numArgsFound++; + } + return numArgsFound; +} + +nsSMILValue +nsSVGAnimatedTransformList::SMILAnimatedTransformList::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(SVGTransformListSMILType::Singleton()); + if (!SVGTransformListSMILType::AppendTransforms(mVal->mBaseVal, val)) { + val = nsSMILValue(); + } + + return val; +} + +nsresult +nsSVGAnimatedTransformList::SMILAnimatedTransformList::SetAnimValue( + const nsSMILValue& aNewAnimValue) +{ + MOZ_ASSERT(aNewAnimValue.mType == SVGTransformListSMILType::Singleton(), + "Unexpected type to assign animated value"); + SVGTransformList animVal; + if (!SVGTransformListSMILType::GetTransforms(aNewAnimValue, + animVal.mItems)) { + return NS_ERROR_FAILURE; + } + + return mVal->SetAnimValue(animVal, mElement); +} + +void +nsSVGAnimatedTransformList::SMILAnimatedTransformList::ClearAnimValue() +{ + if (mVal->mAnimVal) { + mVal->ClearAnimValue(mElement); + } +} + +} // namespace mozilla diff --git a/dom/svg/nsSVGAnimatedTransformList.h b/dom/svg/nsSVGAnimatedTransformList.h new file mode 100644 index 0000000000..8b7b299ed7 --- /dev/null +++ b/dom/svg/nsSVGAnimatedTransformList.h @@ -0,0 +1,163 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef MOZILLA_SVGANIMATEDTRANSFORMLIST_H__ +#define MOZILLA_SVGANIMATEDTRANSFORMLIST_H__ + +#include "mozilla/Attributes.h" +#include "nsAutoPtr.h" +#include "nsISMILAttr.h" +#include "SVGTransformList.h" + +class nsIAtom; +class nsSMILValue; +class nsSVGElement; + +namespace mozilla { + +namespace dom { +class SVGAnimationElement; +class SVGTransform; +} // namespace dom + +/** + * Class nsSVGAnimatedTransformList + * + * 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 + * SVGAnimatedTransformList for the heavier DOM class that wraps instances of + * this class and implements the SVG specification's SVGAnimatedTransformList + * DOM interface. + * + * Except where noted otherwise, this class' methods take care of keeping the + * appropriate DOM wrappers in sync (see the comment in + * SVGAnimatedTransformList::InternalBaseValListWillChangeTo) so that their + * consumers don't need to concern themselves with that. + */ +class nsSVGAnimatedTransformList +{ + // friends so that they can get write access to mBaseVal + friend class dom::SVGTransform; + friend class DOMSVGTransformList; + +public: + nsSVGAnimatedTransformList() + : mIsAttrSet(false), + mHadTransformBeforeLastBaseValChange(false) { } + + /** + * Because it's so important that mBaseVal and its DOMSVGTransformList wrapper + * (if any) be kept in sync (see the comment in + * SVGAnimatedTransformList::InternalBaseValListWillChangeTo), this method + * returns a const reference. Only our friend classes may get mutable + * references to mBaseVal. + */ + const SVGTransformList& GetBaseValue() const { + return mBaseVal; + } + + nsresult SetBaseValue(const SVGTransformList& aValue); + + nsresult SetBaseValueString(const nsAString& aValue); + + void ClearBaseValue(); + + const SVGTransformList& GetAnimValue() const { + return mAnimVal ? *mAnimVal : mBaseVal; + } + + nsresult SetAnimValue(const SVGTransformList& aNewAnimValue, + nsSVGElement *aElement); + + void ClearAnimValue(nsSVGElement *aElement); + + /** + * Returns true if the corresponding transform attribute is set (or animated) + * to a valid value. Unlike HasTransform it will return true for an empty + * transform. + */ + bool IsExplicitlySet() const; + + /** + * Returns true if the corresponding transform attribute is set (or animated) + * to a valid value, such that we have at least one transform in our list. + * Returns false otherwise (e.g. if the transform attribute is missing or empty + * or invalid). + */ + bool HasTransform() const + { return (mAnimVal && !mAnimVal->IsEmpty()) || !mBaseVal.IsEmpty(); } + + bool IsAnimating() const { + return !!mAnimVal; + } + + /** + * Returns true iff "HasTransform" returned true just before our most recent + * SetBaseValue/SetBaseValueString/ClearBaseValue change. + * + * (This is used as part of an optimization in + * SVGTransformableElement::GetAttributeChangeHint. That function reports an + * inexpensive nsChangeHint when a transform has just modified -- but this + * accessor lets it detect cases where the "modification" is actually adding + * a transform where we previously had none. These cases require a more + * thorough nsChangeHint.) + */ + bool HadTransformBeforeLastBaseValChange() const { + return mHadTransformBeforeLastBaseValChange; + } + + /// Callers own the returned nsISMILAttr + nsISMILAttr* ToSMILAttr(nsSVGElement* aSVGElement); + +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="">). + + SVGTransformList mBaseVal; + nsAutoPtr<SVGTransformList> mAnimVal; + bool mIsAttrSet; + // (See documentation for accessor, HadTransformBeforeLastBaseValChange.) + bool mHadTransformBeforeLastBaseValChange; + + struct SMILAnimatedTransformList : public nsISMILAttr + { + public: + SMILAnimatedTransformList(nsSVGAnimatedTransformList* aVal, + nsSVGElement* aSVGElement) + : mVal(aVal) + , mElement(aSVGElement) + {} + + // nsISMILAttr methods + virtual nsresult ValueFromString(const nsAString& aStr, + const dom::SVGAnimationElement* aSrcElement, + nsSMILValue& aValue, + bool& aPreventCachingOfSandwich) const override; + virtual nsSMILValue GetBaseValue() const override; + virtual void ClearAnimValue() override; + virtual nsresult SetAnimValue(const nsSMILValue& aValue) override; + + protected: + static void ParseValue(const nsAString& aSpec, + const nsIAtom* aTransformType, + nsSMILValue& aResult); + static int32_t ParseParameterList(const nsAString& aSpec, float* aVars, + int32_t aNVars); + + // 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. + nsSVGAnimatedTransformList* mVal; + nsSVGElement* mElement; + }; +}; + +} // namespace mozilla + +#endif // MOZILLA_SVGANIMATEDTRANSFORMLIST_H__ diff --git a/dom/svg/nsSVGAttrTearoffTable.h b/dom/svg/nsSVGAttrTearoffTable.h new file mode 100644 index 0000000000..ff907caa2c --- /dev/null +++ b/dom/svg/nsSVGAttrTearoffTable.h @@ -0,0 +1,102 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef NS_SVGATTRTEAROFFTABLE_H_ +#define NS_SVGATTRTEAROFFTABLE_H_ + +#include "nsDataHashtable.h" +#include "nsDebug.h" +#include "nsHashKeys.h" + +/** + * Global hashmap to associate internal SVG data types (e.g. nsSVGLength2) with + * DOM tear-off objects (e.g. nsIDOMSVGLength). This allows us to always return + * the same object for subsequent requests for DOM objects. + * + * We don't keep an owning reference to the tear-off objects so they are + * responsible for removing themselves from this table when they die. + */ +template<class SimpleType, class TearoffType> +class nsSVGAttrTearoffTable +{ +public: +#ifdef DEBUG + ~nsSVGAttrTearoffTable() + { + MOZ_ASSERT(!mTable, "Tear-off objects remain in hashtable at shutdown."); + } +#endif + + TearoffType* GetTearoff(SimpleType* aSimple); + + void AddTearoff(SimpleType* aSimple, TearoffType* aTearoff); + + void RemoveTearoff(SimpleType* aSimple); + +private: + typedef nsPtrHashKey<SimpleType> SimpleTypePtrKey; + typedef nsDataHashtable<SimpleTypePtrKey, TearoffType* > TearoffTable; + + TearoffTable* mTable; +}; + +template<class SimpleType, class TearoffType> +TearoffType* +nsSVGAttrTearoffTable<SimpleType, TearoffType>::GetTearoff(SimpleType* aSimple) +{ + if (!mTable) + return nullptr; + + TearoffType *tearoff = nullptr; + +#ifdef DEBUG + bool found = +#endif + mTable->Get(aSimple, &tearoff); + MOZ_ASSERT(!found || tearoff, + "null pointer stored in attribute tear-off map"); + + return tearoff; +} + +template<class SimpleType, class TearoffType> +void +nsSVGAttrTearoffTable<SimpleType, TearoffType>::AddTearoff(SimpleType* aSimple, + TearoffType* aTearoff) +{ + if (!mTable) { + mTable = new TearoffTable; + } + + // We shouldn't be adding a tear-off if there already is one. If that happens, + // something is wrong. + if (mTable->Get(aSimple, nullptr)) { + MOZ_ASSERT(false, "There is already a tear-off for this object."); + return; + } + + mTable->Put(aSimple, aTearoff); +} + +template<class SimpleType, class TearoffType> +void +nsSVGAttrTearoffTable<SimpleType, TearoffType>::RemoveTearoff( + SimpleType* aSimple) +{ + if (!mTable) { + // Perhaps something happened in between creating the SimpleType object and + // registering it + return; + } + + mTable->Remove(aSimple); + if (mTable->Count() == 0) { + delete mTable; + mTable = nullptr; + } +} + +#endif // NS_SVGATTRTEAROFFTABLE_H_ diff --git a/dom/svg/nsSVGBoolean.cpp b/dom/svg/nsSVGBoolean.cpp new file mode 100644 index 0000000000..10b800475a --- /dev/null +++ b/dom/svg/nsSVGBoolean.cpp @@ -0,0 +1,184 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "nsError.h" +#include "nsSVGAttrTearoffTable.h" +#include "nsSVGBoolean.h" +#include "nsSMILValue.h" +#include "SMILBoolType.h" +#include "SVGAnimatedBoolean.h" + +using namespace mozilla; +using namespace mozilla::dom; + +/* Implementation */ + +static inline +nsSVGAttrTearoffTable<nsSVGBoolean, SVGAnimatedBoolean>& +SVGAnimatedBooleanTearoffTable() +{ + static nsSVGAttrTearoffTable<nsSVGBoolean, SVGAnimatedBoolean> + sSVGAnimatedBooleanTearoffTable; + return sSVGAnimatedBooleanTearoffTable; +} + +static bool +GetValueFromString(const nsAString& aValueAsString, + bool& aValue) +{ + if (aValueAsString.EqualsLiteral("true")) { + aValue = true; + return true; + } + if (aValueAsString.EqualsLiteral("false")) { + aValue = false; + return true; + } + return false; +} + +static nsresult +GetValueFromAtom(const nsIAtom* aValueAsAtom, bool *aValue) +{ + if (aValueAsAtom == nsGkAtoms::_true) { + *aValue = true; + return NS_OK; + } + if (aValueAsAtom == nsGkAtoms::_false) { + *aValue = false; + return NS_OK; + } + return NS_ERROR_DOM_SYNTAX_ERR; +} + +nsresult +nsSVGBoolean::SetBaseValueAtom(const nsIAtom* aValue, nsSVGElement *aSVGElement) +{ + bool val = false; + + nsresult rv = GetValueFromAtom(aValue, &val); + if (NS_FAILED(rv)) { + return rv; + } + + mBaseVal = val; + if (!mIsAnimated) { + mAnimVal = mBaseVal; + } + else { + aSVGElement->AnimationNeedsResample(); + } + + // We don't need to call DidChange* here - we're only called by + // nsSVGElement::ParseAttribute under Element::SetAttr, + // which takes care of notifying. + return NS_OK; +} + +nsIAtom* +nsSVGBoolean::GetBaseValueAtom() const +{ + return mBaseVal ? nsGkAtoms::_true : nsGkAtoms::_false; +} + +void +nsSVGBoolean::SetBaseValue(bool aValue, nsSVGElement *aSVGElement) +{ + if (aValue == mBaseVal) { + return; + } + + mBaseVal = aValue; + if (!mIsAnimated) { + mAnimVal = mBaseVal; + } else { + aSVGElement->AnimationNeedsResample(); + } + aSVGElement->DidChangeBoolean(mAttrEnum); +} + +void +nsSVGBoolean::SetAnimValue(bool aValue, nsSVGElement *aSVGElement) +{ + if (mIsAnimated && mAnimVal == aValue) { + return; + } + mAnimVal = aValue; + mIsAnimated = true; + aSVGElement->DidAnimateBoolean(mAttrEnum); +} + +already_AddRefed<SVGAnimatedBoolean> +nsSVGBoolean::ToDOMAnimatedBoolean(nsSVGElement* aSVGElement) +{ + RefPtr<SVGAnimatedBoolean> domAnimatedBoolean = + SVGAnimatedBooleanTearoffTable().GetTearoff(this); + if (!domAnimatedBoolean) { + domAnimatedBoolean = new SVGAnimatedBoolean(this, aSVGElement); + SVGAnimatedBooleanTearoffTable().AddTearoff(this, domAnimatedBoolean); + } + + return domAnimatedBoolean.forget(); +} + +SVGAnimatedBoolean::~SVGAnimatedBoolean() +{ + SVGAnimatedBooleanTearoffTable().RemoveTearoff(mVal); +} + +nsISMILAttr* +nsSVGBoolean::ToSMILAttr(nsSVGElement *aSVGElement) +{ + return new SMILBool(this, aSVGElement); +} + +nsresult +nsSVGBoolean::SMILBool::ValueFromString(const nsAString& aStr, + const SVGAnimationElement* /*aSrcElement*/, + nsSMILValue& aValue, + bool& aPreventCachingOfSandwich) const +{ + bool value; + if (!GetValueFromString(aStr, value)) { + return NS_ERROR_DOM_SYNTAX_ERR; + } + + nsSMILValue val(SMILBoolType::Singleton()); + val.mU.mBool = value; + aValue = val; + aPreventCachingOfSandwich = false; + + return NS_OK; +} + +nsSMILValue +nsSVGBoolean::SMILBool::GetBaseValue() const +{ + nsSMILValue val(SMILBoolType::Singleton()); + val.mU.mBool = mVal->mBaseVal; + return val; +} + +void +nsSVGBoolean::SMILBool::ClearAnimValue() +{ + if (mVal->mIsAnimated) { + mVal->mIsAnimated = false; + mVal->mAnimVal = mVal->mBaseVal; + mSVGElement->DidAnimateBoolean(mVal->mAttrEnum); + } +} + +nsresult +nsSVGBoolean::SMILBool::SetAnimValue(const nsSMILValue& aValue) +{ + NS_ASSERTION(aValue.mType == SMILBoolType::Singleton(), + "Unexpected type to assign animated value"); + if (aValue.mType == SMILBoolType::Singleton()) { + mVal->SetAnimValue(uint16_t(aValue.mU.mBool), mSVGElement); + } + return NS_OK; +} diff --git a/dom/svg/nsSVGBoolean.h b/dom/svg/nsSVGBoolean.h new file mode 100644 index 0000000000..95bfced326 --- /dev/null +++ b/dom/svg/nsSVGBoolean.h @@ -0,0 +1,82 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef __NS_SVGBOOLEAN_H__ +#define __NS_SVGBOOLEAN_H__ + +#include "nsCOMPtr.h" +#include "nsError.h" +#include "nsISMILAttr.h" +#include "mozilla/Attributes.h" + +class nsIAtom; +class nsSMILValue; +class nsSVGElement; + +namespace mozilla { +namespace dom { +class SVGAnimationElement; +class SVGAnimatedBoolean; +} // namespace dom +} // namespace mozilla + +class nsSVGBoolean +{ + +public: + void Init(uint8_t aAttrEnum = 0xff, bool aValue = false) { + mAnimVal = mBaseVal = aValue; + mAttrEnum = aAttrEnum; + mIsAnimated = false; + } + + nsresult SetBaseValueAtom(const nsIAtom* aValue, nsSVGElement *aSVGElement); + nsIAtom* GetBaseValueAtom() const; + + void SetBaseValue(bool aValue, nsSVGElement *aSVGElement); + bool GetBaseValue() const + { return mBaseVal; } + + void SetAnimValue(bool aValue, nsSVGElement *aSVGElement); + bool GetAnimValue() const + { return mAnimVal; } + + already_AddRefed<mozilla::dom::SVGAnimatedBoolean> + ToDOMAnimatedBoolean(nsSVGElement* aSVGElement); + // Returns a new nsISMILAttr object that the caller must delete + nsISMILAttr* ToSMILAttr(nsSVGElement* aSVGElement); + +private: + + bool mAnimVal; + bool mBaseVal; + bool mIsAnimated; + uint8_t mAttrEnum; // element specified tracking for attribute + +public: + struct SMILBool : public nsISMILAttr + { + public: + SMILBool(nsSVGBoolean* aVal, nsSVGElement* aSVGElement) + : mVal(aVal), mSVGElement(aSVGElement) {} + + // 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. + nsSVGBoolean* mVal; + nsSVGElement* mSVGElement; + + // nsISMILAttr methods + virtual nsresult ValueFromString(const nsAString& aStr, + const mozilla::dom::SVGAnimationElement* aSrcElement, + nsSMILValue& aValue, + bool& aPreventCachingOfSandwich) const override; + virtual nsSMILValue GetBaseValue() const override; + virtual void ClearAnimValue() override; + virtual nsresult SetAnimValue(const nsSMILValue& aValue) override; + }; +}; +#endif //__NS_SVGBOOLEAN_H__ diff --git a/dom/svg/nsSVGClass.cpp b/dom/svg/nsSVGClass.cpp new file mode 100644 index 0000000000..6b64c3be7f --- /dev/null +++ b/dom/svg/nsSVGClass.cpp @@ -0,0 +1,167 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "nsSVGClass.h" + +#include "mozilla/dom/SVGAnimatedString.h" +#include "mozilla/Move.h" +#include "nsSVGElement.h" +#include "nsSMILValue.h" +#include "SMILStringType.h" + +using namespace mozilla; +using namespace mozilla::dom; + +struct DOMAnimatedString final : public SVGAnimatedString +{ + NS_DECL_CYCLE_COLLECTING_ISUPPORTS + NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(DOMAnimatedString) + + DOMAnimatedString(nsSVGClass* aVal, nsSVGElement* aSVGElement) + : SVGAnimatedString(aSVGElement) + , mVal(aVal) + {} + + nsSVGClass* mVal; // kept alive because it belongs to content + + void GetBaseVal(nsAString& aResult) override + { + mVal->GetBaseValue(aResult, mSVGElement); + } + + void SetBaseVal(const nsAString& aValue) override + { + mVal->SetBaseValue(aValue, mSVGElement, true); + } + + void GetAnimVal(nsAString& aResult) override; + +private: + ~DOMAnimatedString() {} +}; + +NS_SVG_VAL_IMPL_CYCLE_COLLECTION_WRAPPERCACHED(DOMAnimatedString, mSVGElement) + +NS_IMPL_CYCLE_COLLECTING_ADDREF(DOMAnimatedString) +NS_IMPL_CYCLE_COLLECTING_RELEASE(DOMAnimatedString) + + +NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(DOMAnimatedString) + NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY + NS_INTERFACE_MAP_ENTRY(nsISupports) +NS_INTERFACE_MAP_END + +already_AddRefed<SVGAnimatedString> +nsSVGClass::ToDOMAnimatedString(nsSVGElement* aSVGElement) +{ + RefPtr<DOMAnimatedString> result = new DOMAnimatedString(this, aSVGElement); + return result.forget(); +} + +/* Implementation */ + +void +nsSVGClass::SetBaseValue(const nsAString& aValue, + nsSVGElement *aSVGElement, + bool aDoSetAttr) +{ + NS_ASSERTION(aSVGElement, "Null element passed to SetBaseValue"); + + aSVGElement->SetFlags(NODE_MAY_HAVE_CLASS); + if (aDoSetAttr) { + aSVGElement->SetAttr(kNameSpaceID_None, nsGkAtoms::_class, aValue, true); + } + if (mAnimVal) { + aSVGElement->AnimationNeedsResample(); + } +} + +void +nsSVGClass::GetBaseValue(nsAString& aValue, const nsSVGElement *aSVGElement) const +{ + aSVGElement->GetAttr(kNameSpaceID_None, nsGkAtoms::_class, aValue); +} + +void +nsSVGClass::GetAnimValue(nsAString& aResult, const nsSVGElement *aSVGElement) const +{ + if (mAnimVal) { + aResult = *mAnimVal; + return; + } + + aSVGElement->GetAttr(kNameSpaceID_None, nsGkAtoms::_class, aResult); +} + +void +nsSVGClass::SetAnimValue(const nsAString& aValue, nsSVGElement *aSVGElement) +{ + if (mAnimVal && mAnimVal->Equals(aValue)) { + return; + } + if (!mAnimVal) { + mAnimVal = new nsString(); + } + *mAnimVal = aValue; + aSVGElement->SetFlags(NODE_MAY_HAVE_CLASS); + aSVGElement->DidAnimateClass(); +} + +void +DOMAnimatedString::GetAnimVal(nsAString& aResult) +{ + mSVGElement->FlushAnimations(); + mVal->GetAnimValue(aResult, mSVGElement); +} + +nsISMILAttr* +nsSVGClass::ToSMILAttr(nsSVGElement *aSVGElement) +{ + return new SMILString(this, aSVGElement); +} + +nsresult +nsSVGClass::SMILString::ValueFromString(const nsAString& aStr, + const dom::SVGAnimationElement* /*aSrcElement*/, + nsSMILValue& aValue, + bool& aPreventCachingOfSandwich) const +{ + nsSMILValue val(SMILStringType::Singleton()); + + *static_cast<nsAString*>(val.mU.mPtr) = aStr; + aValue = Move(val); + aPreventCachingOfSandwich = false; + return NS_OK; +} + +nsSMILValue +nsSVGClass::SMILString::GetBaseValue() const +{ + nsSMILValue val(SMILStringType::Singleton()); + mSVGElement->GetAttr(kNameSpaceID_None, nsGkAtoms::_class, + *static_cast<nsAString*>(val.mU.mPtr)); + return val; +} + +void +nsSVGClass::SMILString::ClearAnimValue() +{ + if (mVal->mAnimVal) { + mVal->mAnimVal = nullptr; + mSVGElement->DidAnimateClass(); + } +} + +nsresult +nsSVGClass::SMILString::SetAnimValue(const nsSMILValue& aValue) +{ + NS_ASSERTION(aValue.mType == SMILStringType::Singleton(), + "Unexpected type to assign animated value"); + if (aValue.mType == SMILStringType::Singleton()) { + mVal->SetAnimValue(*static_cast<nsAString*>(aValue.mU.mPtr), mSVGElement); + } + return NS_OK; +} diff --git a/dom/svg/nsSVGClass.h b/dom/svg/nsSVGClass.h new file mode 100644 index 0000000000..8f38ba39f8 --- /dev/null +++ b/dom/svg/nsSVGClass.h @@ -0,0 +1,76 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef __NS_SVGCLASS_H__ +#define __NS_SVGCLASS_H__ + +#include "nsAutoPtr.h" +#include "nsCycleCollectionParticipant.h" +#include "nsError.h" +#include "nsISMILAttr.h" +#include "nsString.h" +#include "mozilla/Attributes.h" + +class nsSVGElement; + +namespace mozilla { +namespace dom { +class SVGAnimatedString; +} // namespace dom +} // namespace mozilla + +class nsSVGClass +{ + +public: + void Init() { + mAnimVal = nullptr; + } + + void SetBaseValue(const nsAString& aValue, + nsSVGElement *aSVGElement, + bool aDoSetAttr); + void GetBaseValue(nsAString& aValue, const nsSVGElement *aSVGElement) const; + + void SetAnimValue(const nsAString& aValue, nsSVGElement *aSVGElement); + void GetAnimValue(nsAString& aValue, const nsSVGElement *aSVGElement) const; + bool IsAnimated() const + { return !!mAnimVal; } + + already_AddRefed<mozilla::dom::SVGAnimatedString> + ToDOMAnimatedString(nsSVGElement* aSVGElement); + + // Returns a new nsISMILAttr object that the caller must delete + nsISMILAttr* ToSMILAttr(nsSVGElement *aSVGElement); + +private: + + nsAutoPtr<nsString> mAnimVal; + +public: + struct SMILString : public nsISMILAttr + { + public: + SMILString(nsSVGClass *aVal, nsSVGElement *aSVGElement) + : mVal(aVal), mSVGElement(aSVGElement) {} + + // 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. + nsSVGClass* mVal; + nsSVGElement* mSVGElement; + + // nsISMILAttr methods + virtual nsresult ValueFromString(const nsAString& aStr, + const mozilla::dom::SVGAnimationElement *aSrcElement, + nsSMILValue& aValue, + bool& aPreventCachingOfSandwich) const override; + virtual nsSMILValue GetBaseValue() const override; + virtual void ClearAnimValue() override; + virtual nsresult SetAnimValue(const nsSMILValue& aValue) override; + }; +}; +#endif //__NS_SVGCLASS_H__ diff --git a/dom/svg/nsSVGDataParser.cpp b/dom/svg/nsSVGDataParser.cpp new file mode 100644 index 0000000000..ce81ce4be2 --- /dev/null +++ b/dom/svg/nsSVGDataParser.cpp @@ -0,0 +1,40 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "nsSVGDataParser.h" +#include "SVGContentUtils.h" + +nsSVGDataParser::nsSVGDataParser(const nsAString& aValue) + : mIter(SVGContentUtils::GetStartRangedPtr(aValue)), + mEnd(SVGContentUtils::GetEndRangedPtr(aValue)) +{ +} + +bool +nsSVGDataParser::SkipCommaWsp() +{ + if (!SkipWsp()) { + // end of string + return false; + } + if (*mIter != ',') { + return true; + } + ++mIter; + return SkipWsp(); +} + +bool +nsSVGDataParser::SkipWsp() +{ + while (mIter != mEnd) { + if (!IsSVGWhitespace(*mIter)) { + return true; + } + ++mIter; + } + return false; +} diff --git a/dom/svg/nsSVGDataParser.h b/dom/svg/nsSVGDataParser.h new file mode 100644 index 0000000000..1d28cae8df --- /dev/null +++ b/dom/svg/nsSVGDataParser.h @@ -0,0 +1,39 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef __NS_SVGDATAPARSER_H__ +#define __NS_SVGDATAPARSER_H__ + +#include "mozilla/RangedPtr.h" +#include "nsString.h" + +//////////////////////////////////////////////////////////////////////// +// nsSVGDataParser: a simple base class for parsing values +// for path and transform values. +// +class nsSVGDataParser +{ +public: + explicit nsSVGDataParser(const nsAString& aValue); + +protected: + static bool IsAlpha(char16_t aCh) { + // Exclude non-ascii characters before calling isalpha + return (aCh & 0x7f) == aCh && isalpha(aCh); + } + + // Returns true if there are more characters to read, false otherwise. + bool SkipCommaWsp(); + + // Returns true if there are more characters to read, false otherwise. + bool SkipWsp(); + + mozilla::RangedPtr<const char16_t> mIter; + const mozilla::RangedPtr<const char16_t> mEnd; +}; + + +#endif // __NS_SVGDATAPARSER_H__ diff --git a/dom/svg/nsSVGElement.cpp b/dom/svg/nsSVGElement.cpp new file mode 100644 index 0000000000..ce849acf0f --- /dev/null +++ b/dom/svg/nsSVGElement.cpp @@ -0,0 +1,2756 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "mozilla/ArrayUtils.h" +#include "mozilla/DebugOnly.h" +#include "mozilla/Unused.h" + +#include "nsSVGElement.h" + +#include "mozilla/dom/SVGSVGElement.h" +#include "mozilla/dom/SVGTests.h" +#include "nsContentUtils.h" +#include "nsICSSDeclaration.h" +#include "nsIDocument.h" +#include "nsIDOMMutationEvent.h" +#include "nsSVGPathGeometryElement.h" +#include "mozilla/InternalMutationEvent.h" +#include "nsError.h" +#include "nsIPresShell.h" +#include "nsGkAtoms.h" +#include "mozilla/css/StyleRule.h" +#include "nsRuleWalker.h" +#include "mozilla/css/Declaration.h" +#include "nsCSSProps.h" +#include "nsCSSParser.h" +#include "mozilla/EventListenerManager.h" +#include "nsLayoutUtils.h" +#include "nsSVGAnimatedTransformList.h" +#include "nsSVGLength2.h" +#include "nsSVGNumber2.h" +#include "nsSVGNumberPair.h" +#include "nsSVGInteger.h" +#include "nsSVGIntegerPair.h" +#include "nsSVGAngle.h" +#include "nsSVGBoolean.h" +#include "nsSVGEnum.h" +#include "nsSVGViewBox.h" +#include "nsSVGString.h" +#include "mozilla/dom/SVGAnimatedEnumeration.h" +#include "SVGAnimatedNumberList.h" +#include "SVGAnimatedLengthList.h" +#include "SVGAnimatedPointList.h" +#include "SVGAnimatedPathSegList.h" +#include "SVGContentUtils.h" +#include "nsIFrame.h" +#include "nsQueryObject.h" +#include <stdarg.h> +#include "nsSMILMappedAttribute.h" +#include "SVGMotionSMILAttr.h" +#include "nsAttrValueOrString.h" +#include "nsSMILAnimationController.h" +#include "mozilla/dom/SVGElementBinding.h" +#include "mozilla/Unused.h" +#include "mozilla/RestyleManagerHandle.h" +#include "mozilla/RestyleManagerHandleInlines.h" + +using namespace mozilla; +using namespace mozilla::dom; + +// This is needed to ensure correct handling of calls to the +// vararg-list methods in this file: +// nsSVGElement::GetAnimated{Length,Number,Integer}Values +// See bug 547964 for details: +static_assert(sizeof(void*) == sizeof(nullptr), + "nullptr should be the correct size"); + +nsresult +NS_NewSVGElement(Element **aResult, already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo) +{ + RefPtr<nsSVGElement> it = new nsSVGElement(aNodeInfo); + nsresult rv = it->Init(); + + if (NS_FAILED(rv)) { + return rv; + } + + it.forget(aResult); + return rv; +} + +NS_IMPL_ELEMENT_CLONE_WITH_INIT(nsSVGElement) + +nsSVGEnumMapping nsSVGElement::sSVGUnitTypesMap[] = { + {&nsGkAtoms::userSpaceOnUse, SVG_UNIT_TYPE_USERSPACEONUSE}, + {&nsGkAtoms::objectBoundingBox, SVG_UNIT_TYPE_OBJECTBOUNDINGBOX}, + {nullptr, 0} +}; + +nsSVGElement::nsSVGElement(already_AddRefed<mozilla::dom::NodeInfo>& aNodeInfo) + : nsSVGElementBase(aNodeInfo) +{ +} + +JSObject* +nsSVGElement::WrapNode(JSContext *aCx, JS::Handle<JSObject*> aGivenProto) +{ + return SVGElementBinding::Wrap(aCx, this, aGivenProto); +} + +//---------------------------------------------------------------------- + +NS_IMETHODIMP +nsSVGElement::GetSVGClassName(nsISupports** aClassName) +{ + *aClassName = ClassName().take(); + return NS_OK; +} + +NS_IMETHODIMP +nsSVGElement::GetStyle(nsIDOMCSSStyleDeclaration** aStyle) +{ + NS_ADDREF(*aStyle = Style()); + return NS_OK; +} + +//---------------------------------------------------------------------- +// nsSVGElement methods + +void +nsSVGElement::DidAnimateClass() +{ + nsAutoString src; + mClassAttribute.GetAnimValue(src, this); + if (!mClassAnimAttr) { + mClassAnimAttr = new nsAttrValue(); + } + mClassAnimAttr->ParseAtomArray(src); + + nsIPresShell* shell = OwnerDoc()->GetShell(); + if (shell) { + shell->RestyleForAnimation(this, eRestyle_Self); + } +} + +nsresult +nsSVGElement::Init() +{ + // Set up length attributes - can't do this in the constructor + // because we can't do a virtual call at that point + + LengthAttributesInfo lengthInfo = GetLengthInfo(); + + uint32_t i; + for (i = 0; i < lengthInfo.mLengthCount; i++) { + lengthInfo.Reset(i); + } + + NumberAttributesInfo numberInfo = GetNumberInfo(); + + for (i = 0; i < numberInfo.mNumberCount; i++) { + numberInfo.Reset(i); + } + + NumberPairAttributesInfo numberPairInfo = GetNumberPairInfo(); + + for (i = 0; i < numberPairInfo.mNumberPairCount; i++) { + numberPairInfo.Reset(i); + } + + IntegerAttributesInfo integerInfo = GetIntegerInfo(); + + for (i = 0; i < integerInfo.mIntegerCount; i++) { + integerInfo.Reset(i); + } + + IntegerPairAttributesInfo integerPairInfo = GetIntegerPairInfo(); + + for (i = 0; i < integerPairInfo.mIntegerPairCount; i++) { + integerPairInfo.Reset(i); + } + + AngleAttributesInfo angleInfo = GetAngleInfo(); + + for (i = 0; i < angleInfo.mAngleCount; i++) { + angleInfo.Reset(i); + } + + BooleanAttributesInfo booleanInfo = GetBooleanInfo(); + + for (i = 0; i < booleanInfo.mBooleanCount; i++) { + booleanInfo.Reset(i); + } + + EnumAttributesInfo enumInfo = GetEnumInfo(); + + for (i = 0; i < enumInfo.mEnumCount; i++) { + enumInfo.Reset(i); + } + + nsSVGViewBox *viewBox = GetViewBox(); + + if (viewBox) { + viewBox->Init(); + } + + SVGAnimatedPreserveAspectRatio *preserveAspectRatio = + GetPreserveAspectRatio(); + + if (preserveAspectRatio) { + preserveAspectRatio->Init(); + } + + LengthListAttributesInfo lengthListInfo = GetLengthListInfo(); + + for (i = 0; i < lengthListInfo.mLengthListCount; i++) { + lengthListInfo.Reset(i); + } + + NumberListAttributesInfo numberListInfo = GetNumberListInfo(); + + for (i = 0; i < numberListInfo.mNumberListCount; i++) { + numberListInfo.Reset(i); + } + + // No need to reset SVGPointList since the default value is always the same + // (an empty list). + + // No need to reset SVGPathData since the default value is always the same + // (an empty list). + + StringAttributesInfo stringInfo = GetStringInfo(); + + for (i = 0; i < stringInfo.mStringCount; i++) { + stringInfo.Reset(i); + } + + return NS_OK; +} + +//---------------------------------------------------------------------- +// nsISupports methods + +NS_IMPL_ISUPPORTS_INHERITED(nsSVGElement, nsSVGElementBase, + nsIDOMNode, nsIDOMElement, + nsIDOMSVGElement) + +//---------------------------------------------------------------------- +// Implementation + +//---------------------------------------------------------------------- +// nsIContent methods + +nsresult +nsSVGElement::BindToTree(nsIDocument* aDocument, nsIContent* aParent, + nsIContent* aBindingParent, + bool aCompileEventHandlers) +{ + nsresult rv = nsSVGElementBase::BindToTree(aDocument, aParent, + aBindingParent, + aCompileEventHandlers); + NS_ENSURE_SUCCESS(rv, rv); + + if (!MayHaveStyle()) { + return NS_OK; + } + const nsAttrValue* oldVal = mAttrsAndChildren.GetAttr(nsGkAtoms::style); + + if (oldVal && oldVal->Type() == nsAttrValue::eCSSDeclaration) { + // we need to force a reparse because the baseURI of the document + // may have changed, and in particular because we may be clones of + // XBL anonymous content now being bound to the document we should + // render in and due to the hacky way in which we implement the + // interaction of XBL and SVG resources. Once we have a sane + // ownerDocument on XBL anonymous content, this can all go away. + nsAttrValue attrValue; + nsAutoString stringValue; + oldVal->ToString(stringValue); + // Force in data doc, since we already have a style rule + ParseStyleAttribute(stringValue, attrValue, true); + // Don't bother going through SetInlineStyleDeclaration; we don't + // want to fire off mutation events or document notifications anyway + rv = mAttrsAndChildren.SetAndSwapAttr(nsGkAtoms::style, attrValue); + NS_ENSURE_SUCCESS(rv, rv); + } + + return NS_OK; +} + +nsresult +nsSVGElement::AfterSetAttr(int32_t aNamespaceID, nsIAtom* aName, + const nsAttrValue* aValue, bool aNotify) +{ + // We don't currently use nsMappedAttributes within SVG. If this changes, we + // need to be very careful because some nsAttrValues used by SVG point to + // member data of SVG elements and if an nsAttrValue outlives the SVG element + // whose data it points to (by virtue of being stored in + // mAttrsAndChildren->mMappedAttributes, meaning it's shared between + // elements), the pointer will dangle. See bug 724680. + MOZ_ASSERT(!mAttrsAndChildren.HasMappedAttrs(), + "Unexpected use of nsMappedAttributes within SVG"); + + // If this is an svg presentation attribute we need to map it into + // the content stylerule. + // XXX For some reason incremental mapping doesn't work, so for now + // just delete the style rule and lazily reconstruct it in + // GetContentStyleRule() + if (aNamespaceID == kNameSpaceID_None && IsAttributeMapped(aName)) { + mContentStyleRule = nullptr; + } + + if (IsEventAttributeName(aName) && aValue) { + MOZ_ASSERT(aValue->Type() == nsAttrValue::eString, + "Expected string value for script body"); + nsresult rv = SetEventHandler(GetEventNameForAttr(aName), + aValue->GetStringValue()); + NS_ENSURE_SUCCESS(rv, rv); + } + + return nsSVGElementBase::AfterSetAttr(aNamespaceID, aName, aValue, aNotify); +} + +bool +nsSVGElement::ParseAttribute(int32_t aNamespaceID, + nsIAtom* aAttribute, + const nsAString& aValue, + nsAttrValue& aResult) +{ + nsresult rv = NS_OK; + bool foundMatch = false; + bool didSetResult = false; + + if (aNamespaceID == kNameSpaceID_None) { + // Check for nsSVGLength2 attribute + LengthAttributesInfo lengthInfo = GetLengthInfo(); + + uint32_t i; + for (i = 0; i < lengthInfo.mLengthCount; i++) { + if (aAttribute == *lengthInfo.mLengthInfo[i].mName) { + rv = lengthInfo.mLengths[i].SetBaseValueString(aValue, this, false); + if (NS_FAILED(rv)) { + lengthInfo.Reset(i); + } else { + aResult.SetTo(lengthInfo.mLengths[i], &aValue); + didSetResult = true; + } + foundMatch = true; + break; + } + } + + 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)) { + lengthListInfo.Reset(i); + } else { + aResult.SetTo(lengthListInfo.mLengthLists[i].GetBaseValue(), + &aValue); + didSetResult = true; + } + foundMatch = true; + break; + } + } + } + + if (!foundMatch) { + // Check for SVGAnimatedNumberList attribute + NumberListAttributesInfo numberListInfo = GetNumberListInfo(); + for (i = 0; i < numberListInfo.mNumberListCount; i++) { + if (aAttribute == *numberListInfo.mNumberListInfo[i].mName) { + rv = numberListInfo.mNumberLists[i].SetBaseValueString(aValue); + if (NS_FAILED(rv)) { + numberListInfo.Reset(i); + } else { + aResult.SetTo(numberListInfo.mNumberLists[i].GetBaseValue(), + &aValue); + didSetResult = true; + } + foundMatch = true; + break; + } + } + } + + if (!foundMatch) { + // Check for SVGAnimatedPointList attribute + if (GetPointListAttrName() == aAttribute) { + SVGAnimatedPointList* pointList = GetAnimatedPointList(); + if (pointList) { + pointList->SetBaseValueString(aValue); + // The spec says we parse everything up to the failure, so we DON'T + // need to check the result of SetBaseValueString or call + // pointList->ClearBaseValue() if it fails + aResult.SetTo(pointList->GetBaseValue(), &aValue); + didSetResult = true; + foundMatch = true; + } + } + } + + if (!foundMatch) { + // Check for SVGAnimatedPathSegList attribute + if (GetPathDataAttrName() == aAttribute) { + SVGAnimatedPathSegList* segList = GetAnimPathSegList(); + if (segList) { + segList->SetBaseValueString(aValue); + // The spec says we parse everything up to the failure, so we DON'T + // need to check the result of SetBaseValueString or call + // segList->ClearBaseValue() if it fails + aResult.SetTo(segList->GetBaseValue(), &aValue); + didSetResult = true; + foundMatch = true; + } + } + } + + if (!foundMatch) { + // Check for nsSVGNumber2 attribute + NumberAttributesInfo numberInfo = GetNumberInfo(); + for (i = 0; i < numberInfo.mNumberCount; i++) { + if (aAttribute == *numberInfo.mNumberInfo[i].mName) { + rv = numberInfo.mNumbers[i].SetBaseValueString(aValue, this); + if (NS_FAILED(rv)) { + numberInfo.Reset(i); + } else { + aResult.SetTo(numberInfo.mNumbers[i].GetBaseValue(), &aValue); + didSetResult = true; + } + foundMatch = true; + break; + } + } + } + + if (!foundMatch) { + // Check for nsSVGNumberPair attribute + NumberPairAttributesInfo numberPairInfo = GetNumberPairInfo(); + for (i = 0; i < numberPairInfo.mNumberPairCount; i++) { + if (aAttribute == *numberPairInfo.mNumberPairInfo[i].mName) { + rv = numberPairInfo.mNumberPairs[i].SetBaseValueString(aValue, this); + if (NS_FAILED(rv)) { + numberPairInfo.Reset(i); + } else { + aResult.SetTo(numberPairInfo.mNumberPairs[i], &aValue); + didSetResult = true; + } + foundMatch = true; + break; + } + } + } + + if (!foundMatch) { + // Check for nsSVGInteger attribute + IntegerAttributesInfo integerInfo = GetIntegerInfo(); + for (i = 0; i < integerInfo.mIntegerCount; i++) { + if (aAttribute == *integerInfo.mIntegerInfo[i].mName) { + rv = integerInfo.mIntegers[i].SetBaseValueString(aValue, this); + if (NS_FAILED(rv)) { + integerInfo.Reset(i); + } else { + aResult.SetTo(integerInfo.mIntegers[i].GetBaseValue(), &aValue); + didSetResult = true; + } + foundMatch = true; + break; + } + } + } + + if (!foundMatch) { + // Check for nsSVGIntegerPair attribute + IntegerPairAttributesInfo integerPairInfo = GetIntegerPairInfo(); + for (i = 0; i < integerPairInfo.mIntegerPairCount; i++) { + if (aAttribute == *integerPairInfo.mIntegerPairInfo[i].mName) { + rv = + integerPairInfo.mIntegerPairs[i].SetBaseValueString(aValue, this); + if (NS_FAILED(rv)) { + integerPairInfo.Reset(i); + } else { + aResult.SetTo(integerPairInfo.mIntegerPairs[i], &aValue); + didSetResult = true; + } + foundMatch = true; + break; + } + } + } + + if (!foundMatch) { + // Check for nsSVGAngle attribute + AngleAttributesInfo angleInfo = GetAngleInfo(); + for (i = 0; i < angleInfo.mAngleCount; i++) { + if (aAttribute == *angleInfo.mAngleInfo[i].mName) { + rv = angleInfo.mAngles[i].SetBaseValueString(aValue, this, false); + if (NS_FAILED(rv)) { + angleInfo.Reset(i); + } else { + aResult.SetTo(angleInfo.mAngles[i], &aValue); + didSetResult = true; + } + foundMatch = true; + break; + } + } + } + + if (!foundMatch) { + // Check for nsSVGBoolean attribute + BooleanAttributesInfo booleanInfo = GetBooleanInfo(); + for (i = 0; i < booleanInfo.mBooleanCount; i++) { + if (aAttribute == *booleanInfo.mBooleanInfo[i].mName) { + nsIAtom *valAtom = NS_GetStaticAtom(aValue); + rv = valAtom ? booleanInfo.mBooleans[i].SetBaseValueAtom(valAtom, this) : + NS_ERROR_DOM_SYNTAX_ERR; + if (NS_FAILED(rv)) { + booleanInfo.Reset(i); + } else { + aResult.SetTo(valAtom); + didSetResult = true; + } + foundMatch = true; + break; + } + } + } + + if (!foundMatch) { + // Check for nsSVGEnum attribute + EnumAttributesInfo enumInfo = GetEnumInfo(); + for (i = 0; i < enumInfo.mEnumCount; i++) { + if (aAttribute == *enumInfo.mEnumInfo[i].mName) { + nsCOMPtr<nsIAtom> valAtom = NS_Atomize(aValue); + rv = enumInfo.mEnums[i].SetBaseValueAtom(valAtom, this); + if (NS_FAILED(rv)) { + enumInfo.Reset(i); + } else { + aResult.SetTo(valAtom); + didSetResult = true; + } + foundMatch = true; + break; + } + } + } + + if (!foundMatch) { + // Check for conditional processing attributes + nsCOMPtr<SVGTests> tests = do_QueryObject(this); + if (tests && tests->ParseConditionalProcessingAttribute( + aAttribute, aValue, aResult)) { + foundMatch = true; + } + } + + if (!foundMatch) { + // Check for StringList attribute + StringListAttributesInfo stringListInfo = GetStringListInfo(); + for (i = 0; i < stringListInfo.mStringListCount; i++) { + if (aAttribute == *stringListInfo.mStringListInfo[i].mName) { + rv = stringListInfo.mStringLists[i].SetValue(aValue); + if (NS_FAILED(rv)) { + stringListInfo.Reset(i); + } else { + aResult.SetTo(stringListInfo.mStringLists[i], &aValue); + didSetResult = true; + } + foundMatch = true; + break; + } + } + } + + if (!foundMatch) { + // Check for nsSVGViewBox attribute + if (aAttribute == nsGkAtoms::viewBox) { + nsSVGViewBox* viewBox = GetViewBox(); + if (viewBox) { + rv = viewBox->SetBaseValueString(aValue, this, false); + if (NS_FAILED(rv)) { + viewBox->Init(); + } else { + aResult.SetTo(*viewBox, &aValue); + didSetResult = true; + } + foundMatch = true; + } + // Check for SVGAnimatedPreserveAspectRatio attribute + } else if (aAttribute == nsGkAtoms::preserveAspectRatio) { + SVGAnimatedPreserveAspectRatio *preserveAspectRatio = + GetPreserveAspectRatio(); + if (preserveAspectRatio) { + rv = preserveAspectRatio->SetBaseValueString(aValue, this, false); + if (NS_FAILED(rv)) { + preserveAspectRatio->Init(); + } else { + aResult.SetTo(*preserveAspectRatio, &aValue); + didSetResult = true; + } + foundMatch = true; + } + // Check for SVGAnimatedTransformList attribute + } else if (GetTransformListAttrName() == aAttribute) { + // The transform attribute is being set, so we must ensure that the + // nsSVGAnimatedTransformList is/has been allocated: + nsSVGAnimatedTransformList *transformList = + GetAnimatedTransformList(DO_ALLOCATE); + rv = transformList->SetBaseValueString(aValue); + if (NS_FAILED(rv)) { + transformList->ClearBaseValue(); + } else { + aResult.SetTo(transformList->GetBaseValue(), &aValue); + didSetResult = true; + } + foundMatch = true; + } else if (aAttribute == nsGkAtoms::tabindex) { + didSetResult = aResult.ParseIntValue(aValue); + foundMatch = true; + } + } + + if (aAttribute == nsGkAtoms::_class) { + mClassAttribute.SetBaseValue(aValue, this, false); + aResult.ParseAtomArray(aValue); + return true; + } + } + + if (!foundMatch) { + // Check for nsSVGString attribute + StringAttributesInfo stringInfo = GetStringInfo(); + for (uint32_t i = 0; i < stringInfo.mStringCount; i++) { + if (aNamespaceID == stringInfo.mStringInfo[i].mNamespaceID && + aAttribute == *stringInfo.mStringInfo[i].mName) { + stringInfo.mStrings[i].SetBaseValue(aValue, this, false); + foundMatch = true; + break; + } + } + } + + if (foundMatch) { + if (NS_FAILED(rv)) { + ReportAttributeParseFailure(OwnerDoc(), aAttribute, aValue); + return false; + } + if (!didSetResult) { + aResult.SetTo(aValue); + } + return true; + } + + return nsSVGElementBase::ParseAttribute(aNamespaceID, aAttribute, aValue, + aResult); +} + +void +nsSVGElement::UnsetAttrInternal(int32_t aNamespaceID, nsIAtom* aName, + bool aNotify) +{ + // XXXbz there's a bunch of redundancy here with AfterSetAttr. + // Maybe consolidate? + + if (aNamespaceID == kNameSpaceID_None) { + // If this is an svg presentation attribute, remove rule to force an update + if (IsAttributeMapped(aName)) + mContentStyleRule = nullptr; + + if (IsEventAttributeName(aName)) { + EventListenerManager* manager = GetExistingListenerManager(); + if (manager) { + nsIAtom* eventName = GetEventNameForAttr(aName); + manager->RemoveEventHandler(eventName, EmptyString()); + } + return; + } + + // Check if this is a length attribute going away + LengthAttributesInfo lenInfo = GetLengthInfo(); + + for (uint32_t i = 0; i < lenInfo.mLengthCount; i++) { + if (aName == *lenInfo.mLengthInfo[i].mName) { + MaybeSerializeAttrBeforeRemoval(aName, aNotify); + lenInfo.Reset(i); + return; + } + } + + // Check if this is a length list attribute going away + LengthListAttributesInfo lengthListInfo = GetLengthListInfo(); + + for (uint32_t i = 0; i < lengthListInfo.mLengthListCount; i++) { + if (aName == *lengthListInfo.mLengthListInfo[i].mName) { + MaybeSerializeAttrBeforeRemoval(aName, aNotify); + lengthListInfo.Reset(i); + return; + } + } + + // Check if this is a number list attribute going away + NumberListAttributesInfo numberListInfo = GetNumberListInfo(); + + for (uint32_t i = 0; i < numberListInfo.mNumberListCount; i++) { + if (aName == *numberListInfo.mNumberListInfo[i].mName) { + MaybeSerializeAttrBeforeRemoval(aName, aNotify); + numberListInfo.Reset(i); + return; + } + } + + // Check if this is a point list attribute going away + if (GetPointListAttrName() == aName) { + SVGAnimatedPointList *pointList = GetAnimatedPointList(); + if (pointList) { + MaybeSerializeAttrBeforeRemoval(aName, aNotify); + pointList->ClearBaseValue(); + return; + } + } + + // Check if this is a path segment list attribute going away + if (GetPathDataAttrName() == aName) { + SVGAnimatedPathSegList *segList = GetAnimPathSegList(); + if (segList) { + MaybeSerializeAttrBeforeRemoval(aName, aNotify); + segList->ClearBaseValue(); + return; + } + } + + // Check if this is a number attribute going away + NumberAttributesInfo numInfo = GetNumberInfo(); + + for (uint32_t i = 0; i < numInfo.mNumberCount; i++) { + if (aName == *numInfo.mNumberInfo[i].mName) { + numInfo.Reset(i); + return; + } + } + + // Check if this is a number pair attribute going away + NumberPairAttributesInfo numPairInfo = GetNumberPairInfo(); + + for (uint32_t i = 0; i < numPairInfo.mNumberPairCount; i++) { + if (aName == *numPairInfo.mNumberPairInfo[i].mName) { + MaybeSerializeAttrBeforeRemoval(aName, aNotify); + numPairInfo.Reset(i); + return; + } + } + + // Check if this is an integer attribute going away + IntegerAttributesInfo intInfo = GetIntegerInfo(); + + for (uint32_t i = 0; i < intInfo.mIntegerCount; i++) { + if (aName == *intInfo.mIntegerInfo[i].mName) { + intInfo.Reset(i); + return; + } + } + + // Check if this is an integer pair attribute going away + IntegerPairAttributesInfo intPairInfo = GetIntegerPairInfo(); + + for (uint32_t i = 0; i < intPairInfo.mIntegerPairCount; i++) { + if (aName == *intPairInfo.mIntegerPairInfo[i].mName) { + MaybeSerializeAttrBeforeRemoval(aName, aNotify); + intPairInfo.Reset(i); + return; + } + } + + // Check if this is an angle attribute going away + AngleAttributesInfo angleInfo = GetAngleInfo(); + + for (uint32_t i = 0; i < angleInfo.mAngleCount; i++) { + if (aName == *angleInfo.mAngleInfo[i].mName) { + MaybeSerializeAttrBeforeRemoval(aName, aNotify); + angleInfo.Reset(i); + return; + } + } + + // Check if this is a boolean attribute going away + BooleanAttributesInfo boolInfo = GetBooleanInfo(); + + for (uint32_t i = 0; i < boolInfo.mBooleanCount; i++) { + if (aName == *boolInfo.mBooleanInfo[i].mName) { + boolInfo.Reset(i); + return; + } + } + + // Check if this is an enum attribute going away + EnumAttributesInfo enumInfo = GetEnumInfo(); + + for (uint32_t i = 0; i < enumInfo.mEnumCount; i++) { + if (aName == *enumInfo.mEnumInfo[i].mName) { + enumInfo.Reset(i); + return; + } + } + + // Check if this is a nsViewBox attribute going away + if (aName == nsGkAtoms::viewBox) { + nsSVGViewBox* viewBox = GetViewBox(); + if (viewBox) { + MaybeSerializeAttrBeforeRemoval(aName, aNotify); + viewBox->Init(); + return; + } + } + + // Check if this is a preserveAspectRatio attribute going away + if (aName == nsGkAtoms::preserveAspectRatio) { + SVGAnimatedPreserveAspectRatio *preserveAspectRatio = + GetPreserveAspectRatio(); + if (preserveAspectRatio) { + MaybeSerializeAttrBeforeRemoval(aName, aNotify); + preserveAspectRatio->Init(); + return; + } + } + + // Check if this is a transform list attribute going away + if (GetTransformListAttrName() == aName) { + nsSVGAnimatedTransformList *transformList = GetAnimatedTransformList(); + if (transformList) { + MaybeSerializeAttrBeforeRemoval(aName, aNotify); + transformList->ClearBaseValue(); + return; + } + } + + // Check for conditional processing attributes + nsCOMPtr<SVGTests> tests = do_QueryObject(this); + if (tests && tests->IsConditionalProcessingAttribute(aName)) { + MaybeSerializeAttrBeforeRemoval(aName, aNotify); + tests->UnsetAttr(aName); + return; + } + + // Check if this is a string list attribute going away + StringListAttributesInfo stringListInfo = GetStringListInfo(); + + for (uint32_t i = 0; i < stringListInfo.mStringListCount; i++) { + if (aName == *stringListInfo.mStringListInfo[i].mName) { + MaybeSerializeAttrBeforeRemoval(aName, aNotify); + stringListInfo.Reset(i); + return; + } + } + + if (aName == nsGkAtoms::_class) { + mClassAttribute.Init(); + return; + } + } + + // Check if this is a string attribute going away + StringAttributesInfo stringInfo = GetStringInfo(); + + for (uint32_t i = 0; i < stringInfo.mStringCount; i++) { + if (aNamespaceID == stringInfo.mStringInfo[i].mNamespaceID && + aName == *stringInfo.mStringInfo[i].mName) { + stringInfo.Reset(i); + return; + } + } +} + +nsresult +nsSVGElement::UnsetAttr(int32_t aNamespaceID, nsIAtom* aName, + bool aNotify) +{ + UnsetAttrInternal(aNamespaceID, aName, aNotify); + return nsSVGElementBase::UnsetAttr(aNamespaceID, aName, aNotify); +} + +nsChangeHint +nsSVGElement::GetAttributeChangeHint(const nsIAtom* aAttribute, + int32_t aModType) const +{ + nsChangeHint retval = + nsSVGElementBase::GetAttributeChangeHint(aAttribute, aModType); + + nsCOMPtr<SVGTests> tests = do_QueryObject(const_cast<nsSVGElement*>(this)); + if (tests && tests->IsConditionalProcessingAttribute(aAttribute)) { + // It would be nice to only reconstruct the frame if the value returned by + // SVGTests::PassesConditionalProcessingTests has changed, but we don't + // know that + retval |= nsChangeHint_ReconstructFrame; + } + return retval; +} + +bool +nsSVGElement::IsNodeOfType(uint32_t aFlags) const +{ + return !(aFlags & ~eCONTENT); +} + +NS_IMETHODIMP +nsSVGElement::WalkContentStyleRules(nsRuleWalker* aRuleWalker) +{ +#ifdef DEBUG +// printf("nsSVGElement(%p)::WalkContentStyleRules()\n", this); +#endif + if (!mContentStyleRule) + UpdateContentStyleRule(); + + if (mContentStyleRule) { + css::Declaration* declaration = mContentStyleRule->GetDeclaration(); + declaration->SetImmutable(); + aRuleWalker->Forward(declaration); + } + + return NS_OK; +} + +void +nsSVGElement::WalkAnimatedContentStyleRules(nsRuleWalker* aRuleWalker) +{ + // Update & walk the animated content style rule, to include style from + // animated mapped attributes. But first, get nsPresContext to check + // whether this is a "no-animation restyle". (This should match the check + // in nsHTMLCSSStyleSheet::RulesMatching(), where we determine whether to + // apply the SMILOverrideStyle.) + RestyleManagerHandle restyleManager = + aRuleWalker->PresContext()->RestyleManager(); + MOZ_ASSERT(restyleManager->IsGecko(), + "stylo: Servo-backed style system should not be calling " + "WalkAnimatedContentStyleRules"); + if (!restyleManager->AsGecko()->SkipAnimationRules()) { + // update/walk the animated content style rule. + css::StyleRule* animContentStyleRule = GetAnimatedContentStyleRule(); + if (!animContentStyleRule) { + UpdateAnimatedContentStyleRule(); + animContentStyleRule = GetAnimatedContentStyleRule(); + } + if (animContentStyleRule) { + css::Declaration* declaration = animContentStyleRule->GetDeclaration(); + declaration->SetImmutable(); + aRuleWalker->Forward(declaration); + } + } +} + +NS_IMETHODIMP_(bool) +nsSVGElement::IsAttributeMapped(const nsIAtom* name) const +{ + if (name == nsGkAtoms::lang) { + return true; + } + return nsSVGElementBase::IsAttributeMapped(name); +} + +// PresentationAttributes-FillStroke +/* static */ const Element::MappedAttributeEntry +nsSVGElement::sFillStrokeMap[] = { + { &nsGkAtoms::fill }, + { &nsGkAtoms::fill_opacity }, + { &nsGkAtoms::fill_rule }, + { &nsGkAtoms::paint_order }, + { &nsGkAtoms::stroke }, + { &nsGkAtoms::stroke_dasharray }, + { &nsGkAtoms::stroke_dashoffset }, + { &nsGkAtoms::stroke_linecap }, + { &nsGkAtoms::stroke_linejoin }, + { &nsGkAtoms::stroke_miterlimit }, + { &nsGkAtoms::stroke_opacity }, + { &nsGkAtoms::stroke_width }, + { &nsGkAtoms::vector_effect }, + { nullptr } +}; + +// PresentationAttributes-Graphics +/* static */ const Element::MappedAttributeEntry +nsSVGElement::sGraphicsMap[] = { + { &nsGkAtoms::clip_path }, + { &nsGkAtoms::clip_rule }, + { &nsGkAtoms::colorInterpolation }, + { &nsGkAtoms::cursor }, + { &nsGkAtoms::display }, + { &nsGkAtoms::filter }, + { &nsGkAtoms::image_rendering }, + { &nsGkAtoms::mask }, + { &nsGkAtoms::opacity }, + { &nsGkAtoms::pointer_events }, + { &nsGkAtoms::shape_rendering }, + { &nsGkAtoms::text_rendering }, + { &nsGkAtoms::visibility }, + { nullptr } +}; + +// PresentationAttributes-TextContentElements +/* static */ const Element::MappedAttributeEntry +nsSVGElement::sTextContentElementsMap[] = { + // Properties that we don't support are commented out. + // { &nsGkAtoms::alignment_baseline }, + // { &nsGkAtoms::baseline_shift }, + { &nsGkAtoms::direction }, + { &nsGkAtoms::dominant_baseline }, + { &nsGkAtoms::letter_spacing }, + { &nsGkAtoms::text_anchor }, + { &nsGkAtoms::text_decoration }, + { &nsGkAtoms::unicode_bidi }, + { &nsGkAtoms::word_spacing }, + { &nsGkAtoms::writing_mode }, + { nullptr } +}; + +// PresentationAttributes-FontSpecification +/* static */ const Element::MappedAttributeEntry +nsSVGElement::sFontSpecificationMap[] = { + { &nsGkAtoms::font_family }, + { &nsGkAtoms::font_size }, + { &nsGkAtoms::font_size_adjust }, + { &nsGkAtoms::font_stretch }, + { &nsGkAtoms::font_style }, + { &nsGkAtoms::font_variant }, + { &nsGkAtoms::fontWeight }, + { nullptr } +}; + +// PresentationAttributes-GradientStop +/* static */ const Element::MappedAttributeEntry +nsSVGElement::sGradientStopMap[] = { + { &nsGkAtoms::stop_color }, + { &nsGkAtoms::stop_opacity }, + { nullptr } +}; + +// PresentationAttributes-Viewports +/* static */ const Element::MappedAttributeEntry +nsSVGElement::sViewportsMap[] = { + { &nsGkAtoms::overflow }, + { &nsGkAtoms::clip }, + { nullptr } +}; + +// PresentationAttributes-Makers +/* static */ const Element::MappedAttributeEntry +nsSVGElement::sMarkersMap[] = { + { &nsGkAtoms::marker_end }, + { &nsGkAtoms::marker_mid }, + { &nsGkAtoms::marker_start }, + { nullptr } +}; + +// PresentationAttributes-Color +/* static */ const Element::MappedAttributeEntry +nsSVGElement::sColorMap[] = { + { &nsGkAtoms::color }, + { nullptr } +}; + +// PresentationAttributes-Filters +/* static */ const Element::MappedAttributeEntry +nsSVGElement::sFiltersMap[] = { + { &nsGkAtoms::colorInterpolationFilters }, + { nullptr } +}; + +// PresentationAttributes-feFlood +/* static */ const Element::MappedAttributeEntry +nsSVGElement::sFEFloodMap[] = { + { &nsGkAtoms::flood_color }, + { &nsGkAtoms::flood_opacity }, + { nullptr } +}; + +// PresentationAttributes-LightingEffects +/* static */ const Element::MappedAttributeEntry +nsSVGElement::sLightingEffectsMap[] = { + { &nsGkAtoms::lighting_color }, + { nullptr } +}; + +// PresentationAttributes-mask +/* static */ const Element::MappedAttributeEntry +nsSVGElement::sMaskMap[] = { + { &nsGkAtoms::mask_type }, + { nullptr } +}; + +//---------------------------------------------------------------------- +// nsIDOMElement methods + +// forwarded to Element implementations + + +//---------------------------------------------------------------------- +// nsIDOMSVGElement methods + +NS_IMETHODIMP +nsSVGElement::GetOwnerSVGElement(nsIDOMSVGElement * *aOwnerSVGElement) +{ + NS_IF_ADDREF(*aOwnerSVGElement = GetOwnerSVGElement()); + return NS_OK; +} + +SVGSVGElement* +nsSVGElement::GetOwnerSVGElement() +{ + return GetCtx(); // this may return nullptr +} + +NS_IMETHODIMP +nsSVGElement::GetViewportElement(nsIDOMSVGElement * *aViewportElement) +{ + nsSVGElement* elem = GetViewportElement(); + NS_ADDREF(*aViewportElement = elem); + return NS_OK; +} + +nsSVGElement* +nsSVGElement::GetViewportElement() +{ + return SVGContentUtils::GetNearestViewportElement(this); +} + +already_AddRefed<SVGAnimatedString> +nsSVGElement::ClassName() +{ + return mClassAttribute.ToDOMAnimatedString(this); +} + +bool +nsSVGElement::IsFocusableInternal(int32_t* aTabIndex, bool) +{ + int32_t index = TabIndex(); + + if (index == -1) { + return false; + } + + *aTabIndex = index; + return true; +} + +//------------------------------------------------------------------------ +// Helper class: MappedAttrParser, for parsing values of mapped attributes + +namespace { + +class MOZ_STACK_CLASS MappedAttrParser { +public: + MappedAttrParser(css::Loader* aLoader, + nsIURI* aDocURI, + already_AddRefed<nsIURI> aBaseURI, + nsSVGElement* aElement); + ~MappedAttrParser(); + + // Parses a mapped attribute value. + void ParseMappedAttrValue(nsIAtom* aMappedAttrName, + const nsAString& aMappedAttrValue); + + // If we've parsed any values for mapped attributes, this method returns + // a new already_AddRefed css::StyleRule that incorporates the parsed + // values. Otherwise, this method returns null. + already_AddRefed<css::StyleRule> CreateStyleRule(); + +private: + // MEMBER DATA + // ----------- + nsCSSParser mParser; + + // Arguments for nsCSSParser::ParseProperty + nsIURI* mDocURI; + nsCOMPtr<nsIURI> mBaseURI; + + // Declaration for storing parsed values (lazily initialized) + css::Declaration* mDecl; + + // For reporting use counters + nsSVGElement* mElement; +}; + +MappedAttrParser::MappedAttrParser(css::Loader* aLoader, + nsIURI* aDocURI, + already_AddRefed<nsIURI> aBaseURI, + nsSVGElement* aElement) + : mParser(aLoader), mDocURI(aDocURI), mBaseURI(aBaseURI), + mDecl(nullptr), mElement(aElement) +{ +} + +MappedAttrParser::~MappedAttrParser() +{ + MOZ_ASSERT(!mDecl, + "If mDecl was initialized, it should have been converted " + "into a style rule (and had its pointer cleared)"); +} + +void +MappedAttrParser::ParseMappedAttrValue(nsIAtom* aMappedAttrName, + const nsAString& aMappedAttrValue) +{ + if (!mDecl) { + mDecl = new css::Declaration(); + mDecl->InitializeEmpty(); + } + + // Get the nsCSSPropertyID ID for our mapped attribute. + nsCSSPropertyID propertyID = + nsCSSProps::LookupProperty(nsDependentAtomString(aMappedAttrName), + CSSEnabledState::eForAllContent); + if (propertyID != eCSSProperty_UNKNOWN) { + bool changed = false; // outparam for ParseProperty. + mParser.ParseProperty(propertyID, aMappedAttrValue, mDocURI, mBaseURI, + mElement->NodePrincipal(), mDecl, &changed, false, true); + if (changed) { + // The normal reporting of use counters by the nsCSSParser won't happen + // since it doesn't have a sheet. + if (nsCSSProps::IsShorthand(propertyID)) { + CSSPROPS_FOR_SHORTHAND_SUBPROPERTIES(subprop, propertyID, + CSSEnabledState::eForAllContent) { + UseCounter useCounter = nsCSSProps::UseCounterFor(*subprop); + if (useCounter != eUseCounter_UNKNOWN) { + mElement->OwnerDoc()->SetDocumentAndPageUseCounter(useCounter); + } + } + } else { + UseCounter useCounter = nsCSSProps::UseCounterFor(propertyID); + if (useCounter != eUseCounter_UNKNOWN) { + mElement->OwnerDoc()->SetDocumentAndPageUseCounter(useCounter); + } + } + } + return; + } + MOZ_ASSERT(aMappedAttrName == nsGkAtoms::lang, + "Only 'lang' should be unrecognized!"); + // nsCSSParser doesn't know about 'lang', so we need to handle it specially. + if (aMappedAttrName == nsGkAtoms::lang) { + propertyID = eCSSProperty__x_lang; + nsCSSExpandedDataBlock block; + mDecl->ExpandTo(&block); + nsCSSValue cssValue(PromiseFlatString(aMappedAttrValue), eCSSUnit_Ident); + block.AddLonghandProperty(propertyID, cssValue); + mDecl->ValueAppended(propertyID); + mDecl->CompressFrom(&block); + } +} + +already_AddRefed<css::StyleRule> +MappedAttrParser::CreateStyleRule() +{ + if (!mDecl) { + return nullptr; // No mapped attributes were parsed + } + + RefPtr<css::StyleRule> rule = new css::StyleRule(nullptr, mDecl, 0, 0); + mDecl = nullptr; // We no longer own the declaration -- drop our pointer to it + return rule.forget(); +} + +} // namespace + +//---------------------------------------------------------------------- +// Implementation Helpers: + +void +nsSVGElement::UpdateContentStyleRule() +{ + NS_ASSERTION(!mContentStyleRule, "we already have a content style rule"); + + uint32_t attrCount = mAttrsAndChildren.AttrCount(); + if (!attrCount) { + // nothing to do + return; + } + + nsIDocument* doc = OwnerDoc(); + MappedAttrParser mappedAttrParser(doc->CSSLoader(), doc->GetDocumentURI(), + GetBaseURI(), this); + + for (uint32_t i = 0; i < attrCount; ++i) { + const nsAttrName* attrName = mAttrsAndChildren.AttrNameAt(i); + if (!attrName->IsAtom() || !IsAttributeMapped(attrName->Atom())) + continue; + + if (attrName->NamespaceID() != kNameSpaceID_None && + !attrName->Equals(nsGkAtoms::lang, kNameSpaceID_XML)) { + continue; + } + + if (attrName->Equals(nsGkAtoms::lang, kNameSpaceID_None) && + HasAttr(kNameSpaceID_XML, nsGkAtoms::lang)) { + continue; // xml:lang has precedence + } + + if (IsSVGElement(nsGkAtoms::svg)) { + // Special case: we don't want <svg> 'width'/'height' mapped into style + // if the attribute value isn't a valid <length> according to SVG (which + // only supports a subset of the CSS <length> values). We don't enforce + // this by checking the attribute value in SVGSVGElement:: + // IsAttributeMapped since we don't want that method to depend on the + // value of the attribute that is being checked. Rather we just prevent + // the actual mapping here, as necessary. + if (attrName->Atom() == nsGkAtoms::width && + !GetAnimatedLength(nsGkAtoms::width)->HasBaseVal()) { + continue; + } + if (attrName->Atom() == nsGkAtoms::height && + !GetAnimatedLength(nsGkAtoms::height)->HasBaseVal()) { + continue; + } + } + + nsAutoString value; + mAttrsAndChildren.AttrAt(i)->ToString(value); + mappedAttrParser.ParseMappedAttrValue(attrName->Atom(), value); + } + mContentStyleRule = mappedAttrParser.CreateStyleRule(); +} + +static void +ParseMappedAttrAnimValueCallback(void* aObject, + nsIAtom* aPropertyName, + void* aPropertyValue, + void* aData) +{ + MOZ_ASSERT(aPropertyName != SMIL_MAPPED_ATTR_STYLERULE_ATOM, + "animated content style rule should have been removed " + "from properties table already (we're rebuilding it now)"); + + MappedAttrParser* mappedAttrParser = static_cast<MappedAttrParser*>(aData); + MOZ_ASSERT(mappedAttrParser, "parser should be non-null"); + + nsStringBuffer* animValBuf = static_cast<nsStringBuffer*>(aPropertyValue); + MOZ_ASSERT(animValBuf, "animated value should be non-null"); + + nsString animValStr; + nsContentUtils::PopulateStringFromStringBuffer(animValBuf, animValStr); + + mappedAttrParser->ParseMappedAttrValue(aPropertyName, animValStr); +} + +// Callback for freeing animated content style rule, in property table. +static void +ReleaseStyleRule(void* aObject, /* unused */ + nsIAtom* aPropertyName, + void* aPropertyValue, + void* aData /* unused */) +{ + MOZ_ASSERT(aPropertyName == SMIL_MAPPED_ATTR_STYLERULE_ATOM, + "unexpected property name, for animated content style rule"); + css::StyleRule* styleRule = static_cast<css::StyleRule*>(aPropertyValue); + MOZ_ASSERT(styleRule, "unexpected null style rule"); + styleRule->Release(); +} + +void +nsSVGElement::UpdateAnimatedContentStyleRule() +{ + MOZ_ASSERT(!GetAnimatedContentStyleRule(), + "Animated content style rule already set"); + + nsIDocument* doc = OwnerDoc(); + if (!doc) { + NS_ERROR("SVG element without owner document"); + return; + } + + MappedAttrParser mappedAttrParser(doc->CSSLoader(), doc->GetDocumentURI(), + GetBaseURI(), this); + doc->PropertyTable(SMIL_MAPPED_ATTR_ANIMVAL)-> + Enumerate(this, ParseMappedAttrAnimValueCallback, &mappedAttrParser); + + RefPtr<css::StyleRule> + animContentStyleRule(mappedAttrParser.CreateStyleRule()); + + if (animContentStyleRule) { +#ifdef DEBUG + nsresult rv = +#endif + SetProperty(SMIL_MAPPED_ATTR_ANIMVAL, + SMIL_MAPPED_ATTR_STYLERULE_ATOM, + animContentStyleRule.get(), + ReleaseStyleRule); + Unused << animContentStyleRule.forget(); + MOZ_ASSERT(rv == NS_OK, + "SetProperty failed (or overwrote something)"); + } +} + +css::StyleRule* +nsSVGElement::GetAnimatedContentStyleRule() +{ + return + static_cast<css::StyleRule*>(GetProperty(SMIL_MAPPED_ATTR_ANIMVAL, + SMIL_MAPPED_ATTR_STYLERULE_ATOM, + nullptr)); +} + +/** + * Helper methods for the type-specific WillChangeXXX methods. + * + * This method sends out appropriate pre-change notifications so that selector + * restyles (e.g. due to changes that cause |elem[attr="val"]| to start/stop + * matching) work, and it returns an nsAttrValue that _may_ contain the + * attribute's pre-change value. + * + * The nsAttrValue returned by this method depends on whether there are + * mutation event listeners listening for changes to this element's attributes. + * If not, then the object returned is empty. If there are, then the + * nsAttrValue returned contains a serialized copy of the attribute's value + * prior to the change, and this object should be passed to the corresponding + * DidChangeXXX method call (assuming a WillChangeXXX call is required for the + * SVG type - see comment below). This is necessary so that the 'prevValue' + * property of the mutation event that is dispatched will correctly contain the + * old value. + * + * The reason we need to serialize the old value if there are mutation + * event listeners is because the underlying nsAttrValue for the attribute + * points directly to a parsed representation of the attribute (e.g. an + * SVGAnimatedLengthList*) that is a member of the SVG element. That object + * will have changed by the time DidChangeXXX has been called, so without the + * serialization of the old attribute value that we provide, DidChangeXXX + * would have no way to get the old value to pass to SetAttrAndNotify. + * + * We only return the old value when there are mutation event listeners because + * it's not needed otherwise, and because it's expensive to serialize the old + * value. This is especially true for list type attributes, which may be built + * up via the SVG DOM resulting in a large number of Will/DidModifyXXX calls + * before the script finally finishes setting the attribute. + * + * Note that unlike using SetParsedAttr, using Will/DidChangeXXX does NOT check + * and filter out redundant changes. Before calling WillChangeXXX, the caller + * should check whether the new and old values are actually the same, and skip + * calling Will/DidChangeXXX if they are. + * + * Also note that not all SVG types use this scheme. For types that can be + * represented by an nsAttrValue without pointing back to an SVG object (e.g. + * enums, booleans, integers) we can simply use SetParsedAttr which will do all + * of the above for us. For such types there is no matching WillChangeXXX + * method, only DidChangeXXX which calls SetParsedAttr. + */ +nsAttrValue +nsSVGElement::WillChangeValue(nsIAtom* aName) +{ + // We need an empty attr value: + // a) to pass to BeforeSetAttr when GetParsedAttr returns nullptr + // b) to store the old value in the case we have mutation listeners + // We can use the same value for both purposes since (a) happens before (b). + // Also, we should be careful to always return this value to benefit from + // return value optimization. + nsAttrValue emptyOrOldAttrValue; + const nsAttrValue* attrValue = GetParsedAttr(aName); + + // This is not strictly correct--the attribute value parameter for + // BeforeSetAttr should reflect the value that *will* be set but that implies + // allocating, e.g. an extra nsSVGLength2, and isn't necessary at the moment + // since no SVG elements overload BeforeSetAttr. For now we just pass the + // current value. + nsAttrValueOrString attrStringOrValue(attrValue ? *attrValue + : emptyOrOldAttrValue); + DebugOnly<nsresult> rv = + BeforeSetAttr(kNameSpaceID_None, aName, &attrStringOrValue, + kNotifyDocumentObservers); + // SVG elements aren't expected to overload BeforeSetAttr in such a way that + // it may fail. So long as this is the case we don't need to check and pass on + // the return value which simplifies the calling code significantly. + MOZ_ASSERT(NS_SUCCEEDED(rv), "Unexpected failure from BeforeSetAttr"); + + // We only need to set the old value if we have listeners since otherwise it + // isn't used. + if (attrValue && + nsContentUtils::HasMutationListeners(this, + NS_EVENT_BITS_MUTATION_ATTRMODIFIED, + this)) { + emptyOrOldAttrValue.SetToSerialized(*attrValue); + } + + uint8_t modType = attrValue + ? static_cast<uint8_t>(nsIDOMMutationEvent::MODIFICATION) + : static_cast<uint8_t>(nsIDOMMutationEvent::ADDITION); + nsNodeUtils::AttributeWillChange(this, kNameSpaceID_None, aName, modType, + nullptr); + + return emptyOrOldAttrValue; +} + +/** + * Helper methods for the type-specific DidChangeXXX methods. + * + * aEmptyOrOldValue will normally be the object returned from the corresponding + * WillChangeXXX call. This is because: + * a) WillChangeXXX will ensure the object is set when we have mutation + * listeners, and + * b) WillChangeXXX will ensure the object represents a serialized version of + * the old attribute value so that the value doesn't change when the + * underlying SVG type is updated. + * + * aNewValue is replaced with the old value. + */ +void +nsSVGElement::DidChangeValue(nsIAtom* aName, + const nsAttrValue& aEmptyOrOldValue, + nsAttrValue& aNewValue) +{ + bool hasListeners = + nsContentUtils::HasMutationListeners(this, + NS_EVENT_BITS_MUTATION_ATTRMODIFIED, + this); + uint8_t modType = HasAttr(kNameSpaceID_None, aName) + ? static_cast<uint8_t>(nsIDOMMutationEvent::MODIFICATION) + : static_cast<uint8_t>(nsIDOMMutationEvent::ADDITION); + SetAttrAndNotify(kNameSpaceID_None, aName, nullptr, aEmptyOrOldValue, + aNewValue, modType, hasListeners, kNotifyDocumentObservers, + kCallAfterSetAttr); +} + +void +nsSVGElement::MaybeSerializeAttrBeforeRemoval(nsIAtom* aName, bool aNotify) +{ + if (!aNotify || + !nsContentUtils::HasMutationListeners(this, + NS_EVENT_BITS_MUTATION_ATTRMODIFIED, + this)) { + return; + } + + const nsAttrValue* attrValue = mAttrsAndChildren.GetAttr(aName); + if (!attrValue) + return; + + nsAutoString serializedValue; + attrValue->ToString(serializedValue); + nsAttrValue oldAttrValue(serializedValue); + mAttrsAndChildren.SetAndSwapAttr(aName, oldAttrValue); +} + +/* static */ +nsIAtom* nsSVGElement::GetEventNameForAttr(nsIAtom* aAttr) +{ + if (aAttr == nsGkAtoms::onload) + return nsGkAtoms::onSVGLoad; + if (aAttr == nsGkAtoms::onunload) + return nsGkAtoms::onSVGUnload; + if (aAttr == nsGkAtoms::onresize) + return nsGkAtoms::onSVGResize; + if (aAttr == nsGkAtoms::onscroll) + return nsGkAtoms::onSVGScroll; + if (aAttr == nsGkAtoms::onzoom) + return nsGkAtoms::onSVGZoom; + if (aAttr == nsGkAtoms::onbegin) + return nsGkAtoms::onbeginEvent; + if (aAttr == nsGkAtoms::onrepeat) + return nsGkAtoms::onrepeatEvent; + if (aAttr == nsGkAtoms::onend) + return nsGkAtoms::onendEvent; + + return aAttr; +} + +SVGSVGElement * +nsSVGElement::GetCtx() const +{ + nsIContent* ancestor = GetFlattenedTreeParent(); + + while (ancestor && ancestor->IsSVGElement()) { + if (ancestor->IsSVGElement(nsGkAtoms::foreignObject)) { + return nullptr; + } + if (ancestor->IsSVGElement(nsGkAtoms::svg)) { + return static_cast<SVGSVGElement*>(ancestor); + } + ancestor = ancestor->GetFlattenedTreeParent(); + } + + // we don't have an ancestor <svg> element... + return nullptr; +} + +/* virtual */ gfxMatrix +nsSVGElement::PrependLocalTransformsTo( + const gfxMatrix &aMatrix, SVGTransformTypes aWhich) const +{ + return aMatrix; +} + +nsSVGElement::LengthAttributesInfo +nsSVGElement::GetLengthInfo() +{ + return LengthAttributesInfo(nullptr, nullptr, 0); +} + +void nsSVGElement::LengthAttributesInfo::Reset(uint8_t aAttrEnum) +{ + mLengths[aAttrEnum].Init(mLengthInfo[aAttrEnum].mCtxType, + aAttrEnum, + mLengthInfo[aAttrEnum].mDefaultValue, + mLengthInfo[aAttrEnum].mDefaultUnitType); +} + +void +nsSVGElement::SetLength(nsIAtom* aName, const nsSVGLength2 &aLength) +{ + LengthAttributesInfo lengthInfo = GetLengthInfo(); + + for (uint32_t i = 0; i < lengthInfo.mLengthCount; i++) { + if (aName == *lengthInfo.mLengthInfo[i].mName) { + lengthInfo.mLengths[i] = aLength; + DidAnimateLength(i); + return; + } + } + MOZ_ASSERT(false, "no length found to set"); +} + +nsAttrValue +nsSVGElement::WillChangeLength(uint8_t aAttrEnum) +{ + return WillChangeValue(*GetLengthInfo().mLengthInfo[aAttrEnum].mName); +} + +void +nsSVGElement::DidChangeLength(uint8_t aAttrEnum, + const nsAttrValue& aEmptyOrOldValue) +{ + LengthAttributesInfo info = GetLengthInfo(); + + NS_ASSERTION(info.mLengthCount > 0, + "DidChangeLength on element with no length attribs"); + NS_ASSERTION(aAttrEnum < info.mLengthCount, "aAttrEnum out of range"); + + nsAttrValue newValue; + newValue.SetTo(info.mLengths[aAttrEnum], nullptr); + + DidChangeValue(*info.mLengthInfo[aAttrEnum].mName, aEmptyOrOldValue, + newValue); +} + +void +nsSVGElement::DidAnimateLength(uint8_t aAttrEnum) +{ + ClearAnyCachedPath(); + + nsIFrame* frame = GetPrimaryFrame(); + + if (frame) { + LengthAttributesInfo info = GetLengthInfo(); + frame->AttributeChanged(kNameSpaceID_None, + *info.mLengthInfo[aAttrEnum].mName, + nsIDOMMutationEvent::MODIFICATION); + } +} + +nsSVGLength2* +nsSVGElement::GetAnimatedLength(const nsIAtom *aAttrName) +{ + LengthAttributesInfo lengthInfo = GetLengthInfo(); + + for (uint32_t i = 0; i < lengthInfo.mLengthCount; i++) { + if (aAttrName == *lengthInfo.mLengthInfo[i].mName) { + return &lengthInfo.mLengths[i]; + } + } + MOZ_ASSERT(false, "no matching length found"); + return nullptr; +} + +void +nsSVGElement::GetAnimatedLengthValues(float *aFirst, ...) +{ + LengthAttributesInfo info = GetLengthInfo(); + + NS_ASSERTION(info.mLengthCount > 0, + "GetAnimatedLengthValues on element with no length attribs"); + + SVGSVGElement *ctx = nullptr; + + float *f = aFirst; + uint32_t i = 0; + + va_list args; + va_start(args, aFirst); + + while (f && i < info.mLengthCount) { + uint8_t type = info.mLengths[i].GetSpecifiedUnitType(); + if (!ctx) { + if (type != nsIDOMSVGLength::SVG_LENGTHTYPE_NUMBER && + type != nsIDOMSVGLength::SVG_LENGTHTYPE_PX) + ctx = GetCtx(); + } + if (type == nsIDOMSVGLength::SVG_LENGTHTYPE_EMS || + type == nsIDOMSVGLength::SVG_LENGTHTYPE_EXS) + *f = info.mLengths[i++].GetAnimValue(this); + else + *f = info.mLengths[i++].GetAnimValue(ctx); + f = va_arg(args, float*); + } + + va_end(args); +} + +nsSVGElement::LengthListAttributesInfo +nsSVGElement::GetLengthListInfo() +{ + return LengthListAttributesInfo(nullptr, nullptr, 0); +} + +void +nsSVGElement::LengthListAttributesInfo::Reset(uint8_t aAttrEnum) +{ + mLengthLists[aAttrEnum].ClearBaseValue(aAttrEnum); + // caller notifies +} + +nsAttrValue +nsSVGElement::WillChangeLengthList(uint8_t aAttrEnum) +{ + return WillChangeValue(*GetLengthListInfo().mLengthListInfo[aAttrEnum].mName); +} + +void +nsSVGElement::DidChangeLengthList(uint8_t aAttrEnum, + const nsAttrValue& aEmptyOrOldValue) +{ + 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"); + + nsAttrValue newValue; + newValue.SetTo(info.mLengthLists[aAttrEnum].GetBaseValue(), nullptr); + + DidChangeValue(*info.mLengthListInfo[aAttrEnum].mName, aEmptyOrOldValue, + newValue); +} + +void +nsSVGElement::DidAnimateLengthList(uint8_t 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; + uint32_t 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(uint8_t aAttrEnum) +{ + LengthListAttributesInfo info = GetLengthListInfo(); + if (aAttrEnum < info.mLengthListCount) { + return &(info.mLengthLists[aAttrEnum]); + } + NS_NOTREACHED("Bad attrEnum"); + return nullptr; +} + + +nsSVGElement::NumberListAttributesInfo +nsSVGElement::GetNumberListInfo() +{ + return NumberListAttributesInfo(nullptr, nullptr, 0); +} + +void +nsSVGElement::NumberListAttributesInfo::Reset(uint8_t aAttrEnum) +{ + MOZ_ASSERT(aAttrEnum < mNumberListCount, "Bad attr enum"); + mNumberLists[aAttrEnum].ClearBaseValue(aAttrEnum); + // caller notifies +} + +nsAttrValue +nsSVGElement::WillChangeNumberList(uint8_t aAttrEnum) +{ + return WillChangeValue(*GetNumberListInfo().mNumberListInfo[aAttrEnum].mName); +} + +void +nsSVGElement::DidChangeNumberList(uint8_t aAttrEnum, + const nsAttrValue& aEmptyOrOldValue) +{ + NumberListAttributesInfo info = GetNumberListInfo(); + + MOZ_ASSERT(info.mNumberListCount > 0, + "DidChangeNumberList on element with no number list attribs"); + MOZ_ASSERT(aAttrEnum < info.mNumberListCount, + "aAttrEnum out of range"); + + nsAttrValue newValue; + newValue.SetTo(info.mNumberLists[aAttrEnum].GetBaseValue(), nullptr); + + DidChangeValue(*info.mNumberListInfo[aAttrEnum].mName, aEmptyOrOldValue, + newValue); +} + +void +nsSVGElement::DidAnimateNumberList(uint8_t aAttrEnum) +{ + nsIFrame* frame = GetPrimaryFrame(); + + if (frame) { + NumberListAttributesInfo info = GetNumberListInfo(); + MOZ_ASSERT(aAttrEnum < info.mNumberListCount, "aAttrEnum out of range"); + + frame->AttributeChanged(kNameSpaceID_None, + *info.mNumberListInfo[aAttrEnum].mName, + nsIDOMMutationEvent::MODIFICATION); + } +} + +SVGAnimatedNumberList* +nsSVGElement::GetAnimatedNumberList(uint8_t aAttrEnum) +{ + NumberListAttributesInfo info = GetNumberListInfo(); + if (aAttrEnum < info.mNumberListCount) { + return &(info.mNumberLists[aAttrEnum]); + } + MOZ_ASSERT(false, "Bad attrEnum"); + return nullptr; +} + +SVGAnimatedNumberList* +nsSVGElement::GetAnimatedNumberList(nsIAtom *aAttrName) +{ + NumberListAttributesInfo info = GetNumberListInfo(); + for (uint32_t i = 0; i < info.mNumberListCount; i++) { + if (aAttrName == *info.mNumberListInfo[i].mName) { + return &info.mNumberLists[i]; + } + } + MOZ_ASSERT(false, "Bad caller"); + return nullptr; +} + +nsAttrValue +nsSVGElement::WillChangePointList() +{ + MOZ_ASSERT(GetPointListAttrName(), + "Changing non-existent point list?"); + return WillChangeValue(GetPointListAttrName()); +} + +void +nsSVGElement::DidChangePointList(const nsAttrValue& aEmptyOrOldValue) +{ + MOZ_ASSERT(GetPointListAttrName(), + "Changing non-existent point list?"); + + nsAttrValue newValue; + newValue.SetTo(GetAnimatedPointList()->GetBaseValue(), nullptr); + + DidChangeValue(GetPointListAttrName(), aEmptyOrOldValue, newValue); +} + +void +nsSVGElement::DidAnimatePointList() +{ + MOZ_ASSERT(GetPointListAttrName(), + "Animating non-existent path data?"); + + ClearAnyCachedPath(); + + nsIFrame* frame = GetPrimaryFrame(); + + if (frame) { + frame->AttributeChanged(kNameSpaceID_None, + GetPointListAttrName(), + nsIDOMMutationEvent::MODIFICATION); + } +} + +nsAttrValue +nsSVGElement::WillChangePathSegList() +{ + MOZ_ASSERT(GetPathDataAttrName(), + "Changing non-existent path seg list?"); + return WillChangeValue(GetPathDataAttrName()); +} + +void +nsSVGElement::DidChangePathSegList(const nsAttrValue& aEmptyOrOldValue) +{ + MOZ_ASSERT(GetPathDataAttrName(), + "Changing non-existent path seg list?"); + + nsAttrValue newValue; + newValue.SetTo(GetAnimPathSegList()->GetBaseValue(), nullptr); + + DidChangeValue(GetPathDataAttrName(), aEmptyOrOldValue, newValue); +} + +void +nsSVGElement::DidAnimatePathSegList() +{ + MOZ_ASSERT(GetPathDataAttrName(), + "Animating non-existent path data?"); + + ClearAnyCachedPath(); + + nsIFrame* frame = GetPrimaryFrame(); + + if (frame) { + frame->AttributeChanged(kNameSpaceID_None, + GetPathDataAttrName(), + nsIDOMMutationEvent::MODIFICATION); + } +} + +nsSVGElement::NumberAttributesInfo +nsSVGElement::GetNumberInfo() +{ + return NumberAttributesInfo(nullptr, nullptr, 0); +} + +void nsSVGElement::NumberAttributesInfo::Reset(uint8_t aAttrEnum) +{ + mNumbers[aAttrEnum].Init(aAttrEnum, + mNumberInfo[aAttrEnum].mDefaultValue); +} + +void +nsSVGElement::DidChangeNumber(uint8_t aAttrEnum) +{ + NumberAttributesInfo info = GetNumberInfo(); + + NS_ASSERTION(info.mNumberCount > 0, + "DidChangeNumber on element with no number attribs"); + NS_ASSERTION(aAttrEnum < info.mNumberCount, "aAttrEnum out of range"); + + nsAttrValue attrValue; + attrValue.SetTo(info.mNumbers[aAttrEnum].GetBaseValue(), nullptr); + + SetParsedAttr(kNameSpaceID_None, *info.mNumberInfo[aAttrEnum].mName, nullptr, + attrValue, true); +} + +void +nsSVGElement::DidAnimateNumber(uint8_t aAttrEnum) +{ + nsIFrame* frame = GetPrimaryFrame(); + + if (frame) { + NumberAttributesInfo info = GetNumberInfo(); + frame->AttributeChanged(kNameSpaceID_None, + *info.mNumberInfo[aAttrEnum].mName, + nsIDOMMutationEvent::MODIFICATION); + } +} + +void +nsSVGElement::GetAnimatedNumberValues(float *aFirst, ...) +{ + NumberAttributesInfo info = GetNumberInfo(); + + NS_ASSERTION(info.mNumberCount > 0, + "GetAnimatedNumberValues on element with no number attribs"); + + float *f = aFirst; + uint32_t i = 0; + + va_list args; + va_start(args, aFirst); + + while (f && i < info.mNumberCount) { + *f = info.mNumbers[i++].GetAnimValue(); + f = va_arg(args, float*); + } + va_end(args); +} + +nsSVGElement::NumberPairAttributesInfo +nsSVGElement::GetNumberPairInfo() +{ + return NumberPairAttributesInfo(nullptr, nullptr, 0); +} + +void nsSVGElement::NumberPairAttributesInfo::Reset(uint8_t aAttrEnum) +{ + mNumberPairs[aAttrEnum].Init(aAttrEnum, + mNumberPairInfo[aAttrEnum].mDefaultValue1, + mNumberPairInfo[aAttrEnum].mDefaultValue2); +} + +nsAttrValue +nsSVGElement::WillChangeNumberPair(uint8_t aAttrEnum) +{ + return WillChangeValue(*GetNumberPairInfo().mNumberPairInfo[aAttrEnum].mName); +} + +void +nsSVGElement::DidChangeNumberPair(uint8_t aAttrEnum, + const nsAttrValue& aEmptyOrOldValue) +{ + NumberPairAttributesInfo info = GetNumberPairInfo(); + + NS_ASSERTION(info.mNumberPairCount > 0, + "DidChangePairNumber on element with no number pair attribs"); + NS_ASSERTION(aAttrEnum < info.mNumberPairCount, "aAttrEnum out of range"); + + nsAttrValue newValue; + newValue.SetTo(info.mNumberPairs[aAttrEnum], nullptr); + + DidChangeValue(*info.mNumberPairInfo[aAttrEnum].mName, aEmptyOrOldValue, + newValue); +} + +void +nsSVGElement::DidAnimateNumberPair(uint8_t aAttrEnum) +{ + nsIFrame* frame = GetPrimaryFrame(); + + if (frame) { + NumberPairAttributesInfo info = GetNumberPairInfo(); + frame->AttributeChanged(kNameSpaceID_None, + *info.mNumberPairInfo[aAttrEnum].mName, + nsIDOMMutationEvent::MODIFICATION); + } +} + +nsSVGElement::IntegerAttributesInfo +nsSVGElement::GetIntegerInfo() +{ + return IntegerAttributesInfo(nullptr, nullptr, 0); +} + +void nsSVGElement::IntegerAttributesInfo::Reset(uint8_t aAttrEnum) +{ + mIntegers[aAttrEnum].Init(aAttrEnum, + mIntegerInfo[aAttrEnum].mDefaultValue); +} + +void +nsSVGElement::DidChangeInteger(uint8_t aAttrEnum) +{ + IntegerAttributesInfo info = GetIntegerInfo(); + + NS_ASSERTION(info.mIntegerCount > 0, + "DidChangeInteger on element with no integer attribs"); + NS_ASSERTION(aAttrEnum < info.mIntegerCount, "aAttrEnum out of range"); + + nsAttrValue attrValue; + attrValue.SetTo(info.mIntegers[aAttrEnum].GetBaseValue(), nullptr); + + SetParsedAttr(kNameSpaceID_None, *info.mIntegerInfo[aAttrEnum].mName, nullptr, + attrValue, true); +} + +void +nsSVGElement::DidAnimateInteger(uint8_t aAttrEnum) +{ + nsIFrame* frame = GetPrimaryFrame(); + + if (frame) { + IntegerAttributesInfo info = GetIntegerInfo(); + frame->AttributeChanged(kNameSpaceID_None, + *info.mIntegerInfo[aAttrEnum].mName, + nsIDOMMutationEvent::MODIFICATION); + } +} + +void +nsSVGElement::GetAnimatedIntegerValues(int32_t *aFirst, ...) +{ + IntegerAttributesInfo info = GetIntegerInfo(); + + NS_ASSERTION(info.mIntegerCount > 0, + "GetAnimatedIntegerValues on element with no integer attribs"); + + int32_t *n = aFirst; + uint32_t i = 0; + + va_list args; + va_start(args, aFirst); + + while (n && i < info.mIntegerCount) { + *n = info.mIntegers[i++].GetAnimValue(); + n = va_arg(args, int32_t*); + } + va_end(args); +} + +nsSVGElement::IntegerPairAttributesInfo +nsSVGElement::GetIntegerPairInfo() +{ + return IntegerPairAttributesInfo(nullptr, nullptr, 0); +} + +void nsSVGElement::IntegerPairAttributesInfo::Reset(uint8_t aAttrEnum) +{ + mIntegerPairs[aAttrEnum].Init(aAttrEnum, + mIntegerPairInfo[aAttrEnum].mDefaultValue1, + mIntegerPairInfo[aAttrEnum].mDefaultValue2); +} + +nsAttrValue +nsSVGElement::WillChangeIntegerPair(uint8_t aAttrEnum) +{ + return WillChangeValue( + *GetIntegerPairInfo().mIntegerPairInfo[aAttrEnum].mName); +} + +void +nsSVGElement::DidChangeIntegerPair(uint8_t aAttrEnum, + const nsAttrValue& aEmptyOrOldValue) +{ + IntegerPairAttributesInfo info = GetIntegerPairInfo(); + + NS_ASSERTION(info.mIntegerPairCount > 0, + "DidChangeIntegerPair on element with no integer pair attribs"); + NS_ASSERTION(aAttrEnum < info.mIntegerPairCount, "aAttrEnum out of range"); + + nsAttrValue newValue; + newValue.SetTo(info.mIntegerPairs[aAttrEnum], nullptr); + + DidChangeValue(*info.mIntegerPairInfo[aAttrEnum].mName, aEmptyOrOldValue, + newValue); +} + +void +nsSVGElement::DidAnimateIntegerPair(uint8_t aAttrEnum) +{ + nsIFrame* frame = GetPrimaryFrame(); + + if (frame) { + IntegerPairAttributesInfo info = GetIntegerPairInfo(); + frame->AttributeChanged(kNameSpaceID_None, + *info.mIntegerPairInfo[aAttrEnum].mName, + nsIDOMMutationEvent::MODIFICATION); + } +} + +nsSVGElement::AngleAttributesInfo +nsSVGElement::GetAngleInfo() +{ + return AngleAttributesInfo(nullptr, nullptr, 0); +} + +void nsSVGElement::AngleAttributesInfo::Reset(uint8_t aAttrEnum) +{ + mAngles[aAttrEnum].Init(aAttrEnum, + mAngleInfo[aAttrEnum].mDefaultValue, + mAngleInfo[aAttrEnum].mDefaultUnitType); +} + +nsAttrValue +nsSVGElement::WillChangeAngle(uint8_t aAttrEnum) +{ + return WillChangeValue(*GetAngleInfo().mAngleInfo[aAttrEnum].mName); +} + +void +nsSVGElement::DidChangeAngle(uint8_t aAttrEnum, + const nsAttrValue& aEmptyOrOldValue) +{ + AngleAttributesInfo info = GetAngleInfo(); + + NS_ASSERTION(info.mAngleCount > 0, + "DidChangeAngle on element with no angle attribs"); + NS_ASSERTION(aAttrEnum < info.mAngleCount, "aAttrEnum out of range"); + + nsAttrValue newValue; + newValue.SetTo(info.mAngles[aAttrEnum], nullptr); + + DidChangeValue(*info.mAngleInfo[aAttrEnum].mName, aEmptyOrOldValue, newValue); +} + +void +nsSVGElement::DidAnimateAngle(uint8_t aAttrEnum) +{ + nsIFrame* frame = GetPrimaryFrame(); + + if (frame) { + AngleAttributesInfo info = GetAngleInfo(); + frame->AttributeChanged(kNameSpaceID_None, + *info.mAngleInfo[aAttrEnum].mName, + nsIDOMMutationEvent::MODIFICATION); + } +} + +nsSVGElement::BooleanAttributesInfo +nsSVGElement::GetBooleanInfo() +{ + return BooleanAttributesInfo(nullptr, nullptr, 0); +} + +void nsSVGElement::BooleanAttributesInfo::Reset(uint8_t aAttrEnum) +{ + mBooleans[aAttrEnum].Init(aAttrEnum, + mBooleanInfo[aAttrEnum].mDefaultValue); +} + +void +nsSVGElement::DidChangeBoolean(uint8_t aAttrEnum) +{ + BooleanAttributesInfo info = GetBooleanInfo(); + + NS_ASSERTION(info.mBooleanCount > 0, + "DidChangeBoolean on element with no boolean attribs"); + NS_ASSERTION(aAttrEnum < info.mBooleanCount, "aAttrEnum out of range"); + + nsAttrValue attrValue(info.mBooleans[aAttrEnum].GetBaseValueAtom()); + SetParsedAttr(kNameSpaceID_None, *info.mBooleanInfo[aAttrEnum].mName, nullptr, + attrValue, true); +} + +void +nsSVGElement::DidAnimateBoolean(uint8_t aAttrEnum) +{ + nsIFrame* frame = GetPrimaryFrame(); + + if (frame) { + BooleanAttributesInfo info = GetBooleanInfo(); + frame->AttributeChanged(kNameSpaceID_None, + *info.mBooleanInfo[aAttrEnum].mName, + nsIDOMMutationEvent::MODIFICATION); + } +} + +nsSVGElement::EnumAttributesInfo +nsSVGElement::GetEnumInfo() +{ + return EnumAttributesInfo(nullptr, nullptr, 0); +} + +void nsSVGElement::EnumAttributesInfo::Reset(uint8_t aAttrEnum) +{ + mEnums[aAttrEnum].Init(aAttrEnum, + mEnumInfo[aAttrEnum].mDefaultValue); +} + +void +nsSVGElement::DidChangeEnum(uint8_t aAttrEnum) +{ + EnumAttributesInfo info = GetEnumInfo(); + + NS_ASSERTION(info.mEnumCount > 0, + "DidChangeEnum on element with no enum attribs"); + NS_ASSERTION(aAttrEnum < info.mEnumCount, "aAttrEnum out of range"); + + nsAttrValue attrValue(info.mEnums[aAttrEnum].GetBaseValueAtom(this)); + SetParsedAttr(kNameSpaceID_None, *info.mEnumInfo[aAttrEnum].mName, nullptr, + attrValue, true); +} + +void +nsSVGElement::DidAnimateEnum(uint8_t aAttrEnum) +{ + nsIFrame* frame = GetPrimaryFrame(); + + if (frame) { + EnumAttributesInfo info = GetEnumInfo(); + frame->AttributeChanged(kNameSpaceID_None, + *info.mEnumInfo[aAttrEnum].mName, + nsIDOMMutationEvent::MODIFICATION); + } +} + +nsSVGViewBox * +nsSVGElement::GetViewBox() +{ + return nullptr; +} + +nsAttrValue +nsSVGElement::WillChangeViewBox() +{ + return WillChangeValue(nsGkAtoms::viewBox); +} + +void +nsSVGElement::DidChangeViewBox(const nsAttrValue& aEmptyOrOldValue) +{ + nsSVGViewBox *viewBox = GetViewBox(); + + NS_ASSERTION(viewBox, "DidChangeViewBox on element with no viewBox attrib"); + + nsAttrValue newValue; + newValue.SetTo(*viewBox, nullptr); + + DidChangeValue(nsGkAtoms::viewBox, aEmptyOrOldValue, newValue); +} + +void +nsSVGElement::DidAnimateViewBox() +{ + nsIFrame* frame = GetPrimaryFrame(); + + if (frame) { + frame->AttributeChanged(kNameSpaceID_None, + nsGkAtoms::viewBox, + nsIDOMMutationEvent::MODIFICATION); + } +} + +SVGAnimatedPreserveAspectRatio * +nsSVGElement::GetPreserveAspectRatio() +{ + return nullptr; +} + +nsAttrValue +nsSVGElement::WillChangePreserveAspectRatio() +{ + return WillChangeValue(nsGkAtoms::preserveAspectRatio); +} + +void +nsSVGElement::DidChangePreserveAspectRatio(const nsAttrValue& aEmptyOrOldValue) +{ + SVGAnimatedPreserveAspectRatio *preserveAspectRatio = + GetPreserveAspectRatio(); + + NS_ASSERTION(preserveAspectRatio, + "DidChangePreserveAspectRatio on element with no " + "preserveAspectRatio attrib"); + + nsAttrValue newValue; + newValue.SetTo(*preserveAspectRatio, nullptr); + + DidChangeValue(nsGkAtoms::preserveAspectRatio, aEmptyOrOldValue, newValue); +} + +void +nsSVGElement::DidAnimatePreserveAspectRatio() +{ + nsIFrame* frame = GetPrimaryFrame(); + + if (frame) { + frame->AttributeChanged(kNameSpaceID_None, + nsGkAtoms::preserveAspectRatio, + nsIDOMMutationEvent::MODIFICATION); + } +} + +nsAttrValue +nsSVGElement::WillChangeTransformList() +{ + return WillChangeValue(GetTransformListAttrName()); +} + +void +nsSVGElement::DidChangeTransformList(const nsAttrValue& aEmptyOrOldValue) +{ + MOZ_ASSERT(GetTransformListAttrName(), + "Changing non-existent transform list?"); + + // The transform attribute is being set, so we must ensure that the + // SVGAnimatedTransformList is/has been allocated: + nsAttrValue newValue; + newValue.SetTo(GetAnimatedTransformList(DO_ALLOCATE)->GetBaseValue(), nullptr); + + DidChangeValue(GetTransformListAttrName(), aEmptyOrOldValue, newValue); +} + +void +nsSVGElement::DidAnimateTransformList(int32_t aModType) +{ + MOZ_ASSERT(GetTransformListAttrName(), + "Animating non-existent transform data?"); + + nsIFrame* frame = GetPrimaryFrame(); + + if (frame) { + nsIAtom *transformAttr = GetTransformListAttrName(); + frame->AttributeChanged(kNameSpaceID_None, + transformAttr, + aModType); + // When script changes the 'transform' attribute, Element::SetAttrAndNotify + // will call nsNodeUtills::AttributeChanged, under which + // SVGTransformableElement::GetAttributeChangeHint will be called and an + // appropriate change event posted to update our frame's overflow rects. + // The SetAttrAndNotify doesn't happen for transform changes caused by + // 'animateTransform' though (and sending out the mutation events that + // nsNodeUtills::AttributeChanged dispatches would be inappropriate + // anyway), so we need to post the change event ourself. + nsChangeHint changeHint = GetAttributeChangeHint(transformAttr, aModType); + if (changeHint) { + nsLayoutUtils::PostRestyleEvent(this, nsRestyleHint(0), changeHint); + } + } +} + +nsSVGElement::StringAttributesInfo +nsSVGElement::GetStringInfo() +{ + return StringAttributesInfo(nullptr, nullptr, 0); +} + +void nsSVGElement::StringAttributesInfo::Reset(uint8_t aAttrEnum) +{ + mStrings[aAttrEnum].Init(aAttrEnum); +} + +void nsSVGElement::GetStringBaseValue(uint8_t aAttrEnum, nsAString& aResult) const +{ + nsSVGElement::StringAttributesInfo info = const_cast<nsSVGElement*>(this)->GetStringInfo(); + + NS_ASSERTION(info.mStringCount > 0, + "GetBaseValue on element with no string attribs"); + + NS_ASSERTION(aAttrEnum < info.mStringCount, "aAttrEnum out of range"); + + GetAttr(info.mStringInfo[aAttrEnum].mNamespaceID, + *info.mStringInfo[aAttrEnum].mName, aResult); +} + +void nsSVGElement::SetStringBaseValue(uint8_t aAttrEnum, const nsAString& aValue) +{ + nsSVGElement::StringAttributesInfo info = GetStringInfo(); + + NS_ASSERTION(info.mStringCount > 0, + "SetBaseValue on element with no string attribs"); + + NS_ASSERTION(aAttrEnum < info.mStringCount, "aAttrEnum out of range"); + + SetAttr(info.mStringInfo[aAttrEnum].mNamespaceID, + *info.mStringInfo[aAttrEnum].mName, aValue, true); +} + +void +nsSVGElement::DidAnimateString(uint8_t aAttrEnum) +{ + nsIFrame* frame = GetPrimaryFrame(); + + if (frame) { + StringAttributesInfo info = GetStringInfo(); + frame->AttributeChanged(info.mStringInfo[aAttrEnum].mNamespaceID, + *info.mStringInfo[aAttrEnum].mName, + nsIDOMMutationEvent::MODIFICATION); + } +} + +nsSVGElement::StringListAttributesInfo +nsSVGElement::GetStringListInfo() +{ + return StringListAttributesInfo(nullptr, nullptr, 0); +} + +nsAttrValue +nsSVGElement::WillChangeStringList(bool aIsConditionalProcessingAttribute, + uint8_t aAttrEnum) +{ + nsIAtom* name; + if (aIsConditionalProcessingAttribute) { + nsCOMPtr<SVGTests> tests(do_QueryInterface(static_cast<nsIDOMSVGElement*>(this))); + name = tests->GetAttrName(aAttrEnum); + } else { + name = *GetStringListInfo().mStringListInfo[aAttrEnum].mName; + } + return WillChangeValue(name); +} + +void +nsSVGElement::DidChangeStringList(bool aIsConditionalProcessingAttribute, + uint8_t aAttrEnum, + const nsAttrValue& aEmptyOrOldValue) +{ + nsIAtom* name; + nsAttrValue newValue; + nsCOMPtr<SVGTests> tests; + + if (aIsConditionalProcessingAttribute) { + tests = do_QueryObject(this); + name = tests->GetAttrName(aAttrEnum); + tests->GetAttrValue(aAttrEnum, newValue); + } else { + StringListAttributesInfo info = GetStringListInfo(); + + NS_ASSERTION(info.mStringListCount > 0, + "DidChangeStringList on element with no string list attribs"); + NS_ASSERTION(aAttrEnum < info.mStringListCount, "aAttrEnum out of range"); + + name = *info.mStringListInfo[aAttrEnum].mName; + newValue.SetTo(info.mStringLists[aAttrEnum], nullptr); + } + + DidChangeValue(name, aEmptyOrOldValue, newValue); + + if (aIsConditionalProcessingAttribute) { + tests->MaybeInvalidate(); + } +} + +void +nsSVGElement::StringListAttributesInfo::Reset(uint8_t aAttrEnum) +{ + mStringLists[aAttrEnum].Clear(); + // caller notifies +} + +nsresult +nsSVGElement::ReportAttributeParseFailure(nsIDocument* aDocument, + nsIAtom* aAttribute, + const nsAString& aValue) +{ + const nsAFlatString& attributeValue = PromiseFlatString(aValue); + const char16_t *strings[] = { aAttribute->GetUTF16String(), + attributeValue.get() }; + return SVGContentUtils::ReportToConsole(aDocument, + "AttributeParseWarning", + strings, ArrayLength(strings)); +} + +void +nsSVGElement::RecompileScriptEventListeners() +{ + int32_t i, count = mAttrsAndChildren.AttrCount(); + for (i = 0; i < count; ++i) { + const nsAttrName *name = mAttrsAndChildren.AttrNameAt(i); + + // Eventlistenener-attributes are always in the null namespace + if (!name->IsAtom()) { + continue; + } + + nsIAtom *attr = name->Atom(); + if (!IsEventAttributeName(attr)) { + continue; + } + + nsAutoString value; + GetAttr(kNameSpaceID_None, attr, value); + SetEventHandler(GetEventNameForAttr(attr), value, true); + } +} + +nsISMILAttr* +nsSVGElement::GetAnimatedAttr(int32_t aNamespaceID, nsIAtom* aName) +{ + if (aNamespaceID == kNameSpaceID_None) { + // We check mapped-into-style attributes first so that animations + // targeting width/height on outer-<svg> don't appear to be ignored + // because we returned a nsISMILAttr for the corresponding + // SVGAnimatedLength. + + // Mapped attributes: + if (IsAttributeMapped(aName)) { + nsCSSPropertyID prop = + nsCSSProps::LookupProperty(nsDependentAtomString(aName), + CSSEnabledState::eForAllContent); + // Check IsPropertyAnimatable to avoid attributes that... + // - map to explicitly unanimatable properties (e.g. 'direction') + // - map to unsupported attributes (e.g. 'glyph-orientation-horizontal') + if (nsSMILCSSProperty::IsPropertyAnimatable(prop)) { + return new nsSMILMappedAttribute(prop, this); + } + } + + // Transforms: + if (GetTransformListAttrName() == aName) { + // The transform attribute is being animated, so we must ensure that the + // SVGAnimatedTransformList is/has been allocated: + return GetAnimatedTransformList(DO_ALLOCATE)->ToSMILAttr(this); + } + + // Motion (fake 'attribute' for animateMotion) + if (aName == nsGkAtoms::mozAnimateMotionDummyAttr) { + return new SVGMotionSMILAttr(this); + } + + // Lengths: + LengthAttributesInfo info = GetLengthInfo(); + for (uint32_t i = 0; i < info.mLengthCount; i++) { + if (aName == *info.mLengthInfo[i].mName) { + return info.mLengths[i].ToSMILAttr(this); + } + } + + // Numbers: + { + NumberAttributesInfo info = GetNumberInfo(); + for (uint32_t i = 0; i < info.mNumberCount; i++) { + if (aName == *info.mNumberInfo[i].mName) { + return info.mNumbers[i].ToSMILAttr(this); + } + } + } + + // Number Pairs: + { + NumberPairAttributesInfo info = GetNumberPairInfo(); + for (uint32_t i = 0; i < info.mNumberPairCount; i++) { + if (aName == *info.mNumberPairInfo[i].mName) { + return info.mNumberPairs[i].ToSMILAttr(this); + } + } + } + + // Integers: + { + IntegerAttributesInfo info = GetIntegerInfo(); + for (uint32_t i = 0; i < info.mIntegerCount; i++) { + if (aName == *info.mIntegerInfo[i].mName) { + return info.mIntegers[i].ToSMILAttr(this); + } + } + } + + // Integer Pairs: + { + IntegerPairAttributesInfo info = GetIntegerPairInfo(); + for (uint32_t i = 0; i < info.mIntegerPairCount; i++) { + if (aName == *info.mIntegerPairInfo[i].mName) { + return info.mIntegerPairs[i].ToSMILAttr(this); + } + } + } + + // Enumerations: + { + EnumAttributesInfo info = GetEnumInfo(); + for (uint32_t i = 0; i < info.mEnumCount; i++) { + if (aName == *info.mEnumInfo[i].mName) { + return info.mEnums[i].ToSMILAttr(this); + } + } + } + + // Booleans: + { + BooleanAttributesInfo info = GetBooleanInfo(); + for (uint32_t i = 0; i < info.mBooleanCount; i++) { + if (aName == *info.mBooleanInfo[i].mName) { + return info.mBooleans[i].ToSMILAttr(this); + } + } + } + + // Angles: + { + AngleAttributesInfo info = GetAngleInfo(); + for (uint32_t i = 0; i < info.mAngleCount; i++) { + if (aName == *info.mAngleInfo[i].mName) { + return info.mAngles[i].ToSMILAttr(this); + } + } + } + + // viewBox: + if (aName == nsGkAtoms::viewBox) { + nsSVGViewBox *viewBox = GetViewBox(); + return viewBox ? viewBox->ToSMILAttr(this) : nullptr; + } + + // preserveAspectRatio: + if (aName == nsGkAtoms::preserveAspectRatio) { + SVGAnimatedPreserveAspectRatio *preserveAspectRatio = + GetPreserveAspectRatio(); + return preserveAspectRatio ? + preserveAspectRatio->ToSMILAttr(this) : nullptr; + } + + // NumberLists: + { + NumberListAttributesInfo info = GetNumberListInfo(); + for (uint32_t i = 0; i < info.mNumberListCount; i++) { + if (aName == *info.mNumberListInfo[i].mName) { + MOZ_ASSERT(i <= UCHAR_MAX, "Too many attributes"); + return info.mNumberLists[i].ToSMILAttr(this, uint8_t(i)); + } + } + } + + // LengthLists: + { + LengthListAttributesInfo info = GetLengthListInfo(); + for (uint32_t i = 0; i < info.mLengthListCount; i++) { + if (aName == *info.mLengthListInfo[i].mName) { + MOZ_ASSERT(i <= UCHAR_MAX, "Too many attributes"); + return info.mLengthLists[i].ToSMILAttr(this, + uint8_t(i), + info.mLengthListInfo[i].mAxis, + info.mLengthListInfo[i].mCouldZeroPadList); + } + } + } + + // PointLists: + { + if (GetPointListAttrName() == aName) { + SVGAnimatedPointList *pointList = GetAnimatedPointList(); + if (pointList) { + return pointList->ToSMILAttr(this); + } + } + } + + // PathSegLists: + { + if (GetPathDataAttrName() == aName) { + SVGAnimatedPathSegList *segList = GetAnimPathSegList(); + if (segList) { + return segList->ToSMILAttr(this); + } + } + } + + if (aName == nsGkAtoms::_class) { + return mClassAttribute.ToSMILAttr(this); + } + } + + // Strings + { + StringAttributesInfo info = GetStringInfo(); + for (uint32_t i = 0; i < info.mStringCount; i++) { + if (aNamespaceID == info.mStringInfo[i].mNamespaceID && + aName == *info.mStringInfo[i].mName) { + return info.mStrings[i].ToSMILAttr(this); + } + } + } + + return nullptr; +} + +void +nsSVGElement::AnimationNeedsResample() +{ + nsIDocument* doc = GetComposedDoc(); + if (doc && doc->HasAnimationController()) { + doc->GetAnimationController()->SetResampleNeeded(); + } +} + +void +nsSVGElement::FlushAnimations() +{ + nsIDocument* doc = GetComposedDoc(); + if (doc && doc->HasAnimationController()) { + doc->GetAnimationController()->FlushResampleRequests(); + } +} diff --git a/dom/svg/nsSVGElement.h b/dom/svg/nsSVGElement.h new file mode 100644 index 0000000000..42bc057469 --- /dev/null +++ b/dom/svg/nsSVGElement.h @@ -0,0 +1,724 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef __NS_SVGELEMENT_H__ +#define __NS_SVGELEMENT_H__ + +/* + nsSVGElement is the base class for all SVG content elements. + It implements all the common DOM interfaces and handles attributes. +*/ + +#include "mozilla/Attributes.h" +#include "mozilla/css/StyleRule.h" +#include "nsAutoPtr.h" +#include "nsChangeHint.h" +#include "nsCOMPtr.h" +#include "nsCycleCollectionParticipant.h" +#include "nsError.h" +#include "mozilla/dom/DOMRect.h" +#include "mozilla/dom/ElementInlines.h" +#include "nsISupportsImpl.h" +#include "nsStyledElement.h" +#include "nsSVGClass.h" +#include "nsIDOMSVGElement.h" +#include "SVGContentUtils.h" + +class nsSVGAngle; +class nsSVGBoolean; +class nsSVGEnum; +class nsSVGInteger; +class nsSVGIntegerPair; +class nsSVGLength2; +class nsSVGNumber2; +class nsSVGNumberPair; +class nsSVGString; +class nsSVGViewBox; + +namespace mozilla { +namespace dom { +class SVGSVGElement; + +static const unsigned short SVG_UNIT_TYPE_UNKNOWN = 0; +static const unsigned short SVG_UNIT_TYPE_USERSPACEONUSE = 1; +static const unsigned short SVG_UNIT_TYPE_OBJECTBOUNDINGBOX = 2; + +} // namespace dom + +class SVGAnimatedNumberList; +class SVGNumberList; +class SVGAnimatedLengthList; +class SVGUserUnitList; +class SVGAnimatedPointList; +class SVGAnimatedPathSegList; +class SVGAnimatedPreserveAspectRatio; +class nsSVGAnimatedTransformList; +class SVGStringList; +class DOMSVGStringList; + +namespace gfx { +class Matrix; +} // namespace gfx + +} // namespace mozilla + +class gfxMatrix; +struct nsSVGEnumMapping; + +typedef nsStyledElement nsSVGElementBase; + +class nsSVGElement : public nsSVGElementBase // nsIContent + , public nsIDOMSVGElement +{ +protected: + explicit nsSVGElement(already_AddRefed<mozilla::dom::NodeInfo>& aNodeInfo); + friend nsresult NS_NewSVGElement(mozilla::dom::Element **aResult, + already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo); + nsresult Init(); + virtual ~nsSVGElement(){} + +public: + + virtual nsresult Clone(mozilla::dom::NodeInfo *aNodeInfo, nsINode **aResult) const MOZ_MUST_OVERRIDE override; + + typedef mozilla::SVGNumberList SVGNumberList; + typedef mozilla::SVGAnimatedNumberList SVGAnimatedNumberList; + typedef mozilla::SVGUserUnitList SVGUserUnitList; + typedef mozilla::SVGAnimatedLengthList SVGAnimatedLengthList; + typedef mozilla::SVGAnimatedPointList SVGAnimatedPointList; + typedef mozilla::SVGAnimatedPathSegList SVGAnimatedPathSegList; + typedef mozilla::SVGAnimatedPreserveAspectRatio SVGAnimatedPreserveAspectRatio; + typedef mozilla::nsSVGAnimatedTransformList nsSVGAnimatedTransformList; + typedef mozilla::SVGStringList SVGStringList; + + // nsISupports + NS_DECL_ISUPPORTS_INHERITED + + void DidAnimateClass(); + + // nsIContent interface methods + + virtual nsresult BindToTree(nsIDocument* aDocument, nsIContent* aParent, + nsIContent* aBindingParent, + bool aCompileEventHandlers) override; + + virtual nsresult UnsetAttr(int32_t aNameSpaceID, nsIAtom* aAttribute, + bool aNotify) override; + + virtual nsChangeHint GetAttributeChangeHint(const nsIAtom* aAttribute, + int32_t aModType) const override; + + virtual bool IsNodeOfType(uint32_t aFlags) const override; + + NS_IMETHOD WalkContentStyleRules(nsRuleWalker* aRuleWalker) override; + void WalkAnimatedContentStyleRules(nsRuleWalker* aRuleWalker); + + NS_IMETHOD_(bool) IsAttributeMapped(const nsIAtom* aAttribute) const override; + + static const MappedAttributeEntry sFillStrokeMap[]; + static const MappedAttributeEntry sGraphicsMap[]; + static const MappedAttributeEntry sTextContentElementsMap[]; + static const MappedAttributeEntry sFontSpecificationMap[]; + static const MappedAttributeEntry sGradientStopMap[]; + static const MappedAttributeEntry sViewportsMap[]; + static const MappedAttributeEntry sMarkersMap[]; + static const MappedAttributeEntry sColorMap[]; + static const MappedAttributeEntry sFiltersMap[]; + static const MappedAttributeEntry sFEFloodMap[]; + static const MappedAttributeEntry sLightingEffectsMap[]; + static const MappedAttributeEntry sMaskMap[]; + + NS_FORWARD_NSIDOMNODE_TO_NSINODE + NS_FORWARD_NSIDOMELEMENT_TO_GENERIC + NS_DECL_NSIDOMSVGELEMENT + + // Gets the element that establishes the rectangular viewport against which + // we should resolve percentage lengths (our "coordinate context"). Returns + // nullptr for outer <svg> or SVG without an <svg> parent (invalid SVG). + mozilla::dom::SVGSVGElement* GetCtx() const; + + /** + * Returns aMatrix pre-multiplied by (explicit or implicit) transforms that + * are introduced by attributes on this element. + * + * If aWhich is eAllTransforms, then all the transforms from the coordinate + * space established by this element for its children to the coordinate + * space established by this element's parent element for this element, are + * included. + * + * If aWhich is eUserSpaceToParent, then only the transforms from this + * element's userspace to the coordinate space established by its parent is + * included. This includes any transforms introduced by the 'transform' + * attribute, transform animations and animateMotion, but not any offsets + * due to e.g. 'x'/'y' attributes, or any transform due to a 'viewBox' + * attribute. (SVG userspace is defined to be the coordinate space in which + * coordinates on an element apply.) + * + * If aWhich is eChildToUserSpace, then only the transforms from the + * coordinate space established by this element for its childre to this + * elements userspace are included. This includes any offsets due to e.g. + * 'x'/'y' attributes, and any transform due to a 'viewBox' attribute, but + * does not include any transforms due to the 'transform' attribute. + */ + virtual gfxMatrix PrependLocalTransformsTo( + const gfxMatrix &aMatrix, SVGTransformTypes aWhich = eAllTransforms) const; + + // Setter for to set the current <animateMotion> transformation + // Only visible for nsSVGGraphicElement, so it's a no-op here, and that + // subclass has the useful implementation. + virtual void SetAnimateMotionTransform(const mozilla::gfx::Matrix* aMatrix) {/*no-op*/} + virtual const mozilla::gfx::Matrix* GetAnimateMotionTransform() const { return nullptr; } + + bool IsStringAnimatable(uint8_t aAttrEnum) { + return GetStringInfo().mStringInfo[aAttrEnum].mIsAnimatable; + } + bool NumberAttrAllowsPercentage(uint8_t aAttrEnum) { + return GetNumberInfo().mNumberInfo[aAttrEnum].mPercentagesAllowed; + } + virtual bool HasValidDimensions() const { + return true; + } + void SetLength(nsIAtom* aName, const nsSVGLength2 &aLength); + + nsAttrValue WillChangeLength(uint8_t aAttrEnum); + nsAttrValue WillChangeNumberPair(uint8_t aAttrEnum); + nsAttrValue WillChangeIntegerPair(uint8_t aAttrEnum); + nsAttrValue WillChangeAngle(uint8_t aAttrEnum); + nsAttrValue WillChangeViewBox(); + nsAttrValue WillChangePreserveAspectRatio(); + nsAttrValue WillChangeNumberList(uint8_t aAttrEnum); + nsAttrValue WillChangeLengthList(uint8_t aAttrEnum); + nsAttrValue WillChangePointList(); + nsAttrValue WillChangePathSegList(); + nsAttrValue WillChangeTransformList(); + nsAttrValue WillChangeStringList(bool aIsConditionalProcessingAttribute, + uint8_t aAttrEnum); + + void DidChangeLength(uint8_t aAttrEnum, const nsAttrValue& aEmptyOrOldValue); + void DidChangeNumber(uint8_t aAttrEnum); + void DidChangeNumberPair(uint8_t aAttrEnum, + const nsAttrValue& aEmptyOrOldValue); + void DidChangeInteger(uint8_t aAttrEnum); + void DidChangeIntegerPair(uint8_t aAttrEnum, + const nsAttrValue& aEmptyOrOldValue); + void DidChangeAngle(uint8_t aAttrEnum, const nsAttrValue& aEmptyOrOldValue); + void DidChangeBoolean(uint8_t aAttrEnum); + void DidChangeEnum(uint8_t aAttrEnum); + void DidChangeViewBox(const nsAttrValue& aEmptyOrOldValue); + void DidChangePreserveAspectRatio(const nsAttrValue& aEmptyOrOldValue); + void DidChangeNumberList(uint8_t aAttrEnum, + const nsAttrValue& aEmptyOrOldValue); + void DidChangeLengthList(uint8_t aAttrEnum, + const nsAttrValue& aEmptyOrOldValue); + void DidChangePointList(const nsAttrValue& aEmptyOrOldValue); + void DidChangePathSegList(const nsAttrValue& aEmptyOrOldValue); + void DidChangeTransformList(const nsAttrValue& aEmptyOrOldValue); + void DidChangeString(uint8_t aAttrEnum) {} + void DidChangeStringList(bool aIsConditionalProcessingAttribute, + uint8_t aAttrEnum, + const nsAttrValue& aEmptyOrOldValue); + + void DidAnimateLength(uint8_t aAttrEnum); + void DidAnimateNumber(uint8_t aAttrEnum); + void DidAnimateNumberPair(uint8_t aAttrEnum); + void DidAnimateInteger(uint8_t aAttrEnum); + void DidAnimateIntegerPair(uint8_t aAttrEnum); + void DidAnimateAngle(uint8_t aAttrEnum); + void DidAnimateBoolean(uint8_t aAttrEnum); + void DidAnimateEnum(uint8_t aAttrEnum); + void DidAnimateViewBox(); + void DidAnimatePreserveAspectRatio(); + void DidAnimateNumberList(uint8_t aAttrEnum); + void DidAnimateLengthList(uint8_t aAttrEnum); + void DidAnimatePointList(); + void DidAnimatePathSegList(); + void DidAnimateTransformList(int32_t aModType); + void DidAnimateString(uint8_t aAttrEnum); + + enum { + /** + * Flag to indicate to GetAnimatedXxx() methods that the object being + * requested should be allocated if it hasn't already been allocated, and + * that the method should not return null. Only applicable to methods that + * need to allocate the object that they return. + */ + DO_ALLOCATE = 0x1 + }; + + nsSVGLength2* GetAnimatedLength(const nsIAtom *aAttrName); + void GetAnimatedLengthValues(float *aFirst, ...); + void GetAnimatedNumberValues(float *aFirst, ...); + void GetAnimatedIntegerValues(int32_t *aFirst, ...); + SVGAnimatedNumberList* GetAnimatedNumberList(uint8_t aAttrEnum); + SVGAnimatedNumberList* GetAnimatedNumberList(nsIAtom *aAttrName); + void GetAnimatedLengthListValues(SVGUserUnitList *aFirst, ...); + SVGAnimatedLengthList* GetAnimatedLengthList(uint8_t aAttrEnum); + virtual SVGAnimatedPointList* GetAnimatedPointList() { + return nullptr; + } + virtual SVGAnimatedPathSegList* GetAnimPathSegList() { + // DOM interface 'SVGAnimatedPathData' (*inherited* by nsSVGPathElement) + // has a member called 'animatedPathSegList' member, so we have a shorter + // name so we don't get hidden by the GetAnimatedPathSegList declared by + // NS_DECL_NSIDOMSVGANIMATEDPATHDATA. + return nullptr; + } + /** + * Get the nsSVGAnimatedTransformList for this element. + * + * Despite the fact that animated transform lists are used for a variety of + * attributes, no SVG element uses more than one. + * + * It's relatively uncommon for elements to have their transform attribute + * set, so to save memory the nsSVGAnimatedTransformList is not allocated until + * the attribute is set/animated or its DOM wrapper is created. Callers that + * require the nsSVGAnimatedTransformList to be allocated and for this method + * to return non-null must pass the DO_ALLOCATE flag. + */ + virtual nsSVGAnimatedTransformList* GetAnimatedTransformList( + uint32_t aFlags = 0) { + return nullptr; + } + + virtual nsISMILAttr* GetAnimatedAttr(int32_t aNamespaceID, nsIAtom* aName) override; + void AnimationNeedsResample(); + void FlushAnimations(); + + virtual void RecompileScriptEventListeners() override; + + void GetStringBaseValue(uint8_t aAttrEnum, nsAString& aResult) const; + void SetStringBaseValue(uint8_t aAttrEnum, const nsAString& aValue); + + virtual nsIAtom* GetPointListAttrName() const { + return nullptr; + } + virtual nsIAtom* GetPathDataAttrName() const { + return nullptr; + } + virtual nsIAtom* GetTransformListAttrName() const { + return nullptr; + } + const nsAttrValue* GetAnimatedClassName() const + { + if (!mClassAttribute.IsAnimated()) { + return nullptr; + } + return mClassAnimAttr; + } + + virtual void ClearAnyCachedPath() {} + virtual nsIDOMNode* AsDOMNode() final override { return this; } + virtual bool IsTransformable() { return false; } + + // WebIDL + mozilla::dom::SVGSVGElement* GetOwnerSVGElement(); + nsSVGElement* GetViewportElement(); + already_AddRefed<mozilla::dom::SVGAnimatedString> ClassName(); + virtual bool IsFocusableInternal(int32_t* aTabIndex, bool aWithMouse) override; + +protected: + virtual JSObject* WrapNode(JSContext *cx, JS::Handle<JSObject*> aGivenProto) override; + +#ifdef DEBUG + // We define BeforeSetAttr here and mark it final to ensure it is NOT used + // by SVG elements. + // This is because we're not currently passing the correct value for aValue to + // BeforeSetAttr since it would involve allocating extra SVG value types. + // See the comment in nsSVGElement::WillChangeValue. + virtual nsresult BeforeSetAttr(int32_t aNamespaceID, nsIAtom* aName, + nsAttrValueOrString* aValue, + bool aNotify) override final + { + return nsSVGElementBase::BeforeSetAttr(aNamespaceID, aName, aValue, aNotify); + } +#endif // DEBUG + virtual nsresult AfterSetAttr(int32_t aNamespaceID, nsIAtom* aName, + const nsAttrValue* aValue, bool aNotify) override; + virtual bool ParseAttribute(int32_t aNamespaceID, nsIAtom* aAttribute, + const nsAString& aValue, nsAttrValue& aResult) override; + static nsresult ReportAttributeParseFailure(nsIDocument* aDocument, + nsIAtom* aAttribute, + const nsAString& aValue); + + void UpdateContentStyleRule(); + void UpdateAnimatedContentStyleRule(); + mozilla::css::StyleRule* GetAnimatedContentStyleRule(); + + nsAttrValue WillChangeValue(nsIAtom* aName); + // aNewValue is set to the old value. This value may be invalid if + // !StoresOwnData. + void DidChangeValue(nsIAtom* aName, const nsAttrValue& aEmptyOrOldValue, + nsAttrValue& aNewValue); + void MaybeSerializeAttrBeforeRemoval(nsIAtom* aName, bool aNotify); + + static nsIAtom* GetEventNameForAttr(nsIAtom* aAttr); + + struct LengthInfo { + nsIAtom** mName; + float mDefaultValue; + uint8_t mDefaultUnitType; + uint8_t mCtxType; + }; + + struct LengthAttributesInfo { + nsSVGLength2* mLengths; + LengthInfo* mLengthInfo; + uint32_t mLengthCount; + + LengthAttributesInfo(nsSVGLength2 *aLengths, + LengthInfo *aLengthInfo, + uint32_t aLengthCount) : + mLengths(aLengths), mLengthInfo(aLengthInfo), mLengthCount(aLengthCount) + {} + + void Reset(uint8_t aAttrEnum); + }; + + struct NumberInfo { + nsIAtom** mName; + float mDefaultValue; + bool mPercentagesAllowed; + }; + + struct NumberAttributesInfo { + nsSVGNumber2* mNumbers; + NumberInfo* mNumberInfo; + uint32_t mNumberCount; + + NumberAttributesInfo(nsSVGNumber2 *aNumbers, + NumberInfo *aNumberInfo, + uint32_t aNumberCount) : + mNumbers(aNumbers), mNumberInfo(aNumberInfo), mNumberCount(aNumberCount) + {} + + void Reset(uint8_t aAttrEnum); + }; + + struct NumberPairInfo { + nsIAtom** mName; + float mDefaultValue1; + float mDefaultValue2; + }; + + struct NumberPairAttributesInfo { + nsSVGNumberPair* mNumberPairs; + NumberPairInfo* mNumberPairInfo; + uint32_t mNumberPairCount; + + NumberPairAttributesInfo(nsSVGNumberPair *aNumberPairs, + NumberPairInfo *aNumberPairInfo, + uint32_t aNumberPairCount) : + mNumberPairs(aNumberPairs), mNumberPairInfo(aNumberPairInfo), + mNumberPairCount(aNumberPairCount) + {} + + void Reset(uint8_t aAttrEnum); + }; + + struct IntegerInfo { + nsIAtom** mName; + int32_t mDefaultValue; + }; + + struct IntegerAttributesInfo { + nsSVGInteger* mIntegers; + IntegerInfo* mIntegerInfo; + uint32_t mIntegerCount; + + IntegerAttributesInfo(nsSVGInteger *aIntegers, + IntegerInfo *aIntegerInfo, + uint32_t aIntegerCount) : + mIntegers(aIntegers), mIntegerInfo(aIntegerInfo), mIntegerCount(aIntegerCount) + {} + + void Reset(uint8_t aAttrEnum); + }; + + struct IntegerPairInfo { + nsIAtom** mName; + int32_t mDefaultValue1; + int32_t mDefaultValue2; + }; + + struct IntegerPairAttributesInfo { + nsSVGIntegerPair* mIntegerPairs; + IntegerPairInfo* mIntegerPairInfo; + uint32_t mIntegerPairCount; + + IntegerPairAttributesInfo(nsSVGIntegerPair *aIntegerPairs, + IntegerPairInfo *aIntegerPairInfo, + uint32_t aIntegerPairCount) : + mIntegerPairs(aIntegerPairs), mIntegerPairInfo(aIntegerPairInfo), + mIntegerPairCount(aIntegerPairCount) + {} + + void Reset(uint8_t aAttrEnum); + }; + + struct AngleInfo { + nsIAtom** mName; + float mDefaultValue; + uint8_t mDefaultUnitType; + }; + + struct AngleAttributesInfo { + nsSVGAngle* mAngles; + AngleInfo* mAngleInfo; + uint32_t mAngleCount; + + AngleAttributesInfo(nsSVGAngle *aAngles, + AngleInfo *aAngleInfo, + uint32_t aAngleCount) : + mAngles(aAngles), mAngleInfo(aAngleInfo), mAngleCount(aAngleCount) + {} + + void Reset(uint8_t aAttrEnum); + }; + + struct BooleanInfo { + nsIAtom** mName; + bool mDefaultValue; + }; + + struct BooleanAttributesInfo { + nsSVGBoolean* mBooleans; + BooleanInfo* mBooleanInfo; + uint32_t mBooleanCount; + + BooleanAttributesInfo(nsSVGBoolean *aBooleans, + BooleanInfo *aBooleanInfo, + uint32_t aBooleanCount) : + mBooleans(aBooleans), mBooleanInfo(aBooleanInfo), mBooleanCount(aBooleanCount) + {} + + void Reset(uint8_t aAttrEnum); + }; + + friend class nsSVGEnum; + + struct EnumInfo { + nsIAtom** mName; + nsSVGEnumMapping* mMapping; + uint16_t mDefaultValue; + }; + + struct EnumAttributesInfo { + nsSVGEnum* mEnums; + EnumInfo* mEnumInfo; + uint32_t mEnumCount; + + EnumAttributesInfo(nsSVGEnum *aEnums, + EnumInfo *aEnumInfo, + uint32_t aEnumCount) : + mEnums(aEnums), mEnumInfo(aEnumInfo), mEnumCount(aEnumCount) + {} + + void Reset(uint8_t aAttrEnum); + }; + + struct NumberListInfo { + nsIAtom** mName; + }; + + struct NumberListAttributesInfo { + SVGAnimatedNumberList* mNumberLists; + NumberListInfo* mNumberListInfo; + uint32_t mNumberListCount; + + NumberListAttributesInfo(SVGAnimatedNumberList *aNumberLists, + NumberListInfo *aNumberListInfo, + uint32_t aNumberListCount) + : mNumberLists(aNumberLists) + , mNumberListInfo(aNumberListInfo) + , mNumberListCount(aNumberListCount) + {} + + void Reset(uint8_t aAttrEnum); + }; + + struct LengthListInfo { + nsIAtom** mName; + uint8_t 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. + */ + bool mCouldZeroPadList; + }; + + struct LengthListAttributesInfo { + SVGAnimatedLengthList* mLengthLists; + LengthListInfo* mLengthListInfo; + uint32_t mLengthListCount; + + LengthListAttributesInfo(SVGAnimatedLengthList *aLengthLists, + LengthListInfo *aLengthListInfo, + uint32_t aLengthListCount) + : mLengthLists(aLengthLists) + , mLengthListInfo(aLengthListInfo) + , mLengthListCount(aLengthListCount) + {} + + void Reset(uint8_t aAttrEnum); + }; + + struct StringInfo { + nsIAtom** mName; + int32_t mNamespaceID; + bool mIsAnimatable; + }; + + struct StringAttributesInfo { + nsSVGString* mStrings; + StringInfo* mStringInfo; + uint32_t mStringCount; + + StringAttributesInfo(nsSVGString *aStrings, + StringInfo *aStringInfo, + uint32_t aStringCount) : + mStrings(aStrings), mStringInfo(aStringInfo), mStringCount(aStringCount) + {} + + void Reset(uint8_t aAttrEnum); + }; + + friend class mozilla::DOMSVGStringList; + + struct StringListInfo { + nsIAtom** mName; + }; + + struct StringListAttributesInfo { + SVGStringList* mStringLists; + StringListInfo* mStringListInfo; + uint32_t mStringListCount; + + StringListAttributesInfo(SVGStringList *aStringLists, + StringListInfo *aStringListInfo, + uint32_t aStringListCount) : + mStringLists(aStringLists), mStringListInfo(aStringListInfo), + mStringListCount(aStringListCount) + {} + + void Reset(uint8_t aAttrEnum); + }; + + virtual LengthAttributesInfo GetLengthInfo(); + virtual NumberAttributesInfo GetNumberInfo(); + virtual NumberPairAttributesInfo GetNumberPairInfo(); + virtual IntegerAttributesInfo GetIntegerInfo(); + virtual IntegerPairAttributesInfo GetIntegerPairInfo(); + virtual AngleAttributesInfo GetAngleInfo(); + virtual BooleanAttributesInfo GetBooleanInfo(); + virtual EnumAttributesInfo GetEnumInfo(); + // We assume all viewboxes and preserveAspectRatios are alike + // so we don't need to wrap the class + virtual nsSVGViewBox *GetViewBox(); + virtual SVGAnimatedPreserveAspectRatio *GetPreserveAspectRatio(); + virtual NumberListAttributesInfo GetNumberListInfo(); + virtual LengthListAttributesInfo GetLengthListInfo(); + virtual StringAttributesInfo GetStringInfo(); + virtual StringListAttributesInfo GetStringListInfo(); + + static nsSVGEnumMapping sSVGUnitTypesMap[]; + +private: + void UnsetAttrInternal(int32_t aNameSpaceID, nsIAtom* aAttribute, + bool aNotify); + + nsSVGClass mClassAttribute; + nsAutoPtr<nsAttrValue> mClassAnimAttr; + RefPtr<mozilla::css::StyleRule> mContentStyleRule; +}; + +/** + * A macro to implement the NS_NewSVGXXXElement() functions. + */ +#define NS_IMPL_NS_NEW_SVG_ELEMENT(_elementName) \ +nsresult \ +NS_NewSVG##_elementName##Element(nsIContent **aResult, \ + already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo) \ +{ \ + RefPtr<nsSVG##_elementName##Element> it = \ + new nsSVG##_elementName##Element(aNodeInfo); \ + \ + nsresult rv = it->Init(); \ + \ + if (NS_FAILED(rv)) { \ + return rv; \ + } \ + \ + it.forget(aResult); \ + \ + return rv; \ +} + +#define NS_IMPL_NS_NEW_NAMESPACED_SVG_ELEMENT(_elementName) \ +nsresult \ +NS_NewSVG##_elementName##Element(nsIContent **aResult, \ + already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo) \ +{ \ + RefPtr<mozilla::dom::SVG##_elementName##Element> it = \ + new mozilla::dom::SVG##_elementName##Element(aNodeInfo); \ + \ + nsresult rv = it->Init(); \ + \ + if (NS_FAILED(rv)) { \ + return rv; \ + } \ + \ + it.forget(aResult); \ + \ + return rv; \ +} + +#define NS_IMPL_NS_NEW_NAMESPACED_SVG_ELEMENT_CHECK_PARSER(_elementName) \ +nsresult \ +NS_NewSVG##_elementName##Element(nsIContent **aResult, \ + already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo, \ + mozilla::dom::FromParser aFromParser) \ +{ \ + RefPtr<mozilla::dom::SVG##_elementName##Element> it = \ + new mozilla::dom::SVG##_elementName##Element(aNodeInfo, aFromParser); \ + \ + nsresult rv = it->Init(); \ + \ + if (NS_FAILED(rv)) { \ + return rv; \ + } \ + \ + it.forget(aResult); \ + \ + return rv; \ +} + +// No unlinking, we'd need to null out the value pointer (the object it +// points to is held by the element) and null-check it everywhere. +#define NS_SVG_VAL_IMPL_CYCLE_COLLECTION(_val, _element) \ +NS_IMPL_CYCLE_COLLECTION_CLASS(_val) \ +NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(_val) \ + NS_IMPL_CYCLE_COLLECTION_TRAVERSE(_element) \ +NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END \ +NS_IMPL_CYCLE_COLLECTION_UNLINK_0(_val) + +#define NS_SVG_VAL_IMPL_CYCLE_COLLECTION_WRAPPERCACHED(_val, _element) \ +NS_IMPL_CYCLE_COLLECTION_CLASS(_val) \ +NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(_val) \ +NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER \ +NS_IMPL_CYCLE_COLLECTION_UNLINK_END \ +NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(_val) \ + NS_IMPL_CYCLE_COLLECTION_TRAVERSE(_element) \ + NS_IMPL_CYCLE_COLLECTION_TRAVERSE_SCRIPT_OBJECTS \ +NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END \ +NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(_val) \ +NS_IMPL_CYCLE_COLLECTION_TRACE_PRESERVED_WRAPPER \ +NS_IMPL_CYCLE_COLLECTION_TRACE_END + +#endif // __NS_SVGELEMENT_H__ diff --git a/dom/svg/nsSVGEnum.cpp b/dom/svg/nsSVGEnum.cpp new file mode 100644 index 0000000000..cb6c7c450e --- /dev/null +++ b/dom/svg/nsSVGEnum.cpp @@ -0,0 +1,194 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "nsError.h" +#include "nsSVGAttrTearoffTable.h" +#include "nsSVGEnum.h" +#include "nsIAtom.h" +#include "nsSVGElement.h" +#include "nsSMILValue.h" +#include "SMILEnumType.h" + +using namespace mozilla; +using namespace mozilla::dom; + +static nsSVGAttrTearoffTable<nsSVGEnum, nsSVGEnum::DOMAnimatedEnum> + sSVGAnimatedEnumTearoffTable; + +nsSVGEnumMapping * +nsSVGEnum::GetMapping(nsSVGElement *aSVGElement) +{ + nsSVGElement::EnumAttributesInfo info = aSVGElement->GetEnumInfo(); + + NS_ASSERTION(info.mEnumCount > 0 && mAttrEnum < info.mEnumCount, + "mapping request for a non-attrib enum"); + + return info.mEnumInfo[mAttrEnum].mMapping; +} + +nsresult +nsSVGEnum::SetBaseValueAtom(const nsIAtom* aValue, nsSVGElement *aSVGElement) +{ + nsSVGEnumMapping *mapping = GetMapping(aSVGElement); + + while (mapping && mapping->mKey) { + if (aValue == *(mapping->mKey)) { + mIsBaseSet = true; + if (mBaseVal != mapping->mVal) { + mBaseVal = mapping->mVal; + if (!mIsAnimated) { + mAnimVal = mBaseVal; + } + else { + aSVGElement->AnimationNeedsResample(); + } + // We don't need to call DidChange* here - we're only called by + // nsSVGElement::ParseAttribute under Element::SetAttr, + // which takes care of notifying. + } + return NS_OK; + } + mapping++; + } + + // only a warning since authors may mistype attribute values + NS_WARNING("unknown enumeration key"); + return NS_ERROR_DOM_SYNTAX_ERR; +} + +nsIAtom* +nsSVGEnum::GetBaseValueAtom(nsSVGElement *aSVGElement) +{ + nsSVGEnumMapping *mapping = GetMapping(aSVGElement); + + while (mapping && mapping->mKey) { + if (mBaseVal == mapping->mVal) { + return *mapping->mKey; + } + mapping++; + } + NS_ERROR("unknown enumeration value"); + return nsGkAtoms::_empty; +} + +nsresult +nsSVGEnum::SetBaseValue(uint16_t aValue, + nsSVGElement *aSVGElement) +{ + nsSVGEnumMapping *mapping = GetMapping(aSVGElement); + + while (mapping && mapping->mKey) { + if (mapping->mVal == aValue) { + mIsBaseSet = true; + if (mBaseVal != uint8_t(aValue)) { + mBaseVal = uint8_t(aValue); + if (!mIsAnimated) { + mAnimVal = mBaseVal; + } + else { + aSVGElement->AnimationNeedsResample(); + } + aSVGElement->DidChangeEnum(mAttrEnum); + } + return NS_OK; + } + mapping++; + } + return NS_ERROR_DOM_SYNTAX_ERR; +} + +void +nsSVGEnum::SetAnimValue(uint16_t aValue, nsSVGElement *aSVGElement) +{ + if (mIsAnimated && aValue == mAnimVal) { + return; + } + mAnimVal = aValue; + mIsAnimated = true; + aSVGElement->DidAnimateEnum(mAttrEnum); +} + +already_AddRefed<SVGAnimatedEnumeration> +nsSVGEnum::ToDOMAnimatedEnum(nsSVGElement* aSVGElement) +{ + RefPtr<DOMAnimatedEnum> domAnimatedEnum = + sSVGAnimatedEnumTearoffTable.GetTearoff(this); + if (!domAnimatedEnum) { + domAnimatedEnum = new DOMAnimatedEnum(this, aSVGElement); + sSVGAnimatedEnumTearoffTable.AddTearoff(this, domAnimatedEnum); + } + + return domAnimatedEnum.forget(); +} + +nsSVGEnum::DOMAnimatedEnum::~DOMAnimatedEnum() +{ + sSVGAnimatedEnumTearoffTable.RemoveTearoff(mVal); +} + +nsISMILAttr* +nsSVGEnum::ToSMILAttr(nsSVGElement *aSVGElement) +{ + return new SMILEnum(this, aSVGElement); +} + +nsresult +nsSVGEnum::SMILEnum::ValueFromString(const nsAString& aStr, + const dom::SVGAnimationElement* /*aSrcElement*/, + nsSMILValue& aValue, + bool& aPreventCachingOfSandwich) const +{ + nsIAtom *valAtom = NS_GetStaticAtom(aStr); + if (valAtom) { + nsSVGEnumMapping *mapping = mVal->GetMapping(mSVGElement); + + while (mapping && mapping->mKey) { + if (valAtom == *(mapping->mKey)) { + nsSMILValue val(SMILEnumType::Singleton()); + val.mU.mUint = mapping->mVal; + aValue = val; + aPreventCachingOfSandwich = false; + return NS_OK; + } + mapping++; + } + } + + // only a warning since authors may mistype attribute values + NS_WARNING("unknown enumeration key"); + return NS_ERROR_FAILURE; +} + +nsSMILValue +nsSVGEnum::SMILEnum::GetBaseValue() const +{ + nsSMILValue val(SMILEnumType::Singleton()); + val.mU.mUint = mVal->mBaseVal; + return val; +} + +void +nsSVGEnum::SMILEnum::ClearAnimValue() +{ + if (mVal->mIsAnimated) { + mVal->mIsAnimated = false; + mVal->mAnimVal = mVal->mBaseVal; + mSVGElement->DidAnimateEnum(mVal->mAttrEnum); + } +} + +nsresult +nsSVGEnum::SMILEnum::SetAnimValue(const nsSMILValue& aValue) +{ + NS_ASSERTION(aValue.mType == SMILEnumType::Singleton(), + "Unexpected type to assign animated value"); + if (aValue.mType == SMILEnumType::Singleton()) { + MOZ_ASSERT(aValue.mU.mUint <= USHRT_MAX, + "Very large enumerated value - too big for uint16_t"); + mVal->SetAnimValue(uint16_t(aValue.mU.mUint), mSVGElement); + } + return NS_OK; +} diff --git a/dom/svg/nsSVGEnum.h b/dom/svg/nsSVGEnum.h new file mode 100644 index 0000000000..7802853847 --- /dev/null +++ b/dom/svg/nsSVGEnum.h @@ -0,0 +1,125 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef __NS_SVGENUM_H__ +#define __NS_SVGENUM_H__ + +#include "nsCycleCollectionParticipant.h" +#include "nsError.h" +#include "nsISMILAttr.h" +#include "nsSVGElement.h" +#include "mozilla/Attributes.h" +#include "mozilla/dom/SVGAnimatedEnumeration.h" + +class nsIAtom; +class nsSMILValue; + +namespace mozilla { +namespace dom { +class SVGAnimationElement; +} // namespace dom +} // namespace mozilla + +typedef uint8_t nsSVGEnumValue; + +struct nsSVGEnumMapping { + nsIAtom **mKey; + nsSVGEnumValue mVal; +}; + +class nsSVGEnum +{ +public: + void Init(uint8_t aAttrEnum, uint16_t aValue) { + mAnimVal = mBaseVal = uint8_t(aValue); + mAttrEnum = aAttrEnum; + mIsAnimated = false; + mIsBaseSet = false; + } + + nsresult SetBaseValueAtom(const nsIAtom* aValue, nsSVGElement *aSVGElement); + nsIAtom* GetBaseValueAtom(nsSVGElement *aSVGElement); + nsresult SetBaseValue(uint16_t aValue, + nsSVGElement *aSVGElement); + uint16_t GetBaseValue() const + { return mBaseVal; } + + void SetAnimValue(uint16_t aValue, nsSVGElement *aSVGElement); + uint16_t GetAnimValue() const + { return mAnimVal; } + bool IsExplicitlySet() const + { return mIsAnimated || mIsBaseSet; } + + already_AddRefed<mozilla::dom::SVGAnimatedEnumeration> + ToDOMAnimatedEnum(nsSVGElement* aSVGElement); + + // Returns a new nsISMILAttr object that the caller must delete + nsISMILAttr* ToSMILAttr(nsSVGElement* aSVGElement); + +private: + nsSVGEnumValue mAnimVal; + nsSVGEnumValue mBaseVal; + uint8_t mAttrEnum; // element specified tracking for attribute + bool mIsAnimated; + bool mIsBaseSet; + + nsSVGEnumMapping *GetMapping(nsSVGElement *aSVGElement); + +public: + struct DOMAnimatedEnum final : public mozilla::dom::SVGAnimatedEnumeration + { + DOMAnimatedEnum(nsSVGEnum* aVal, nsSVGElement *aSVGElement) + : mozilla::dom::SVGAnimatedEnumeration(aSVGElement) + , mVal(aVal) + {} + virtual ~DOMAnimatedEnum(); + + nsSVGEnum *mVal; // kept alive because it belongs to content + + using mozilla::dom::SVGAnimatedEnumeration::SetBaseVal; + virtual uint16_t BaseVal() override + { + return mVal->GetBaseValue(); + } + virtual void SetBaseVal(uint16_t aBaseVal, + mozilla::ErrorResult& aRv) override + { + aRv = mVal->SetBaseValue(aBaseVal, mSVGElement); + } + virtual uint16_t AnimVal() override + { + // Script may have modified animation parameters or timeline -- DOM + // getters need to flush any resample requests to reflect these + // modifications. + mSVGElement->FlushAnimations(); + return mVal->GetAnimValue(); + } + }; + + struct SMILEnum : public nsISMILAttr + { + public: + SMILEnum(nsSVGEnum* aVal, nsSVGElement* aSVGElement) + : mVal(aVal), mSVGElement(aSVGElement) {} + + // 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. + nsSVGEnum* mVal; + nsSVGElement* mSVGElement; + + // nsISMILAttr methods + virtual nsresult ValueFromString(const nsAString& aStr, + const mozilla::dom::SVGAnimationElement* aSrcElement, + nsSMILValue& aValue, + bool& aPreventCachingOfSandwich) const override; + virtual nsSMILValue GetBaseValue() const override; + virtual void ClearAnimValue() override; + virtual nsresult SetAnimValue(const nsSMILValue& aValue) override; + }; +}; + +#endif //__NS_SVGENUM_H__ diff --git a/dom/svg/nsSVGFeatures.cpp b/dom/svg/nsSVGFeatures.cpp new file mode 100644 index 0000000000..c7d3bf22ac --- /dev/null +++ b/dom/svg/nsSVGFeatures.cpp @@ -0,0 +1,35 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/** + * This file contains code to help implement the Conditional Processing + * section of the SVG specification (i.e. the <switch> element and the + * requiredFeatures, requiredExtensions and systemLanguage attributes). + * + * http://www.w3.org/TR/SVG11/struct.html#ConditionalProcessing + */ + +#include "nsSVGFeatures.h" +#include "nsIContent.h" +#include "nsIDocument.h" +#include "nsNameSpaceManager.h" +#include "mozilla/Preferences.h" + +using namespace mozilla; + +/*static*/ bool +nsSVGFeatures::HasExtension(const nsAString& aExtension, const bool aIsInChrome) +{ +#define SVG_SUPPORTED_EXTENSION(str) if (aExtension.EqualsLiteral(str)) return true; + SVG_SUPPORTED_EXTENSION("http://www.w3.org/1999/xhtml") + nsNameSpaceManager* nameSpaceManager = nsNameSpaceManager::GetInstance(); + if (aIsInChrome || !nameSpaceManager->mMathMLDisabled) { + SVG_SUPPORTED_EXTENSION("http://www.w3.org/1998/Math/MathML") + } +#undef SVG_SUPPORTED_EXTENSION + + return false; +} diff --git a/dom/svg/nsSVGFeatures.h b/dom/svg/nsSVGFeatures.h new file mode 100644 index 0000000000..9c0421deb0 --- /dev/null +++ b/dom/svg/nsSVGFeatures.h @@ -0,0 +1,25 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef __NS_SVGFEATURES_H__ +#define __NS_SVGFEATURES_H__ + +#include "nsString.h" + +class nsSVGFeatures +{ +public: + /** + * Check whether we support the given extension string. + * + * @param aExtension the URI of an extension. Known extensions are + * "http://www.w3.org/1999/xhtml" and "http://www.w3.org/1998/Math/MathML" + */ + static bool + HasExtension(const nsAString& aExtension, const bool aIsInChrome); +}; + +#endif // __NS_SVGFEATURES_H__ diff --git a/dom/svg/nsSVGFilters.cpp b/dom/svg/nsSVGFilters.cpp new file mode 100644 index 0000000000..c677156c9e --- /dev/null +++ b/dom/svg/nsSVGFilters.cpp @@ -0,0 +1,567 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "mozilla/ArrayUtils.h" + +#include "nsSVGElement.h" +#include "nsGkAtoms.h" +#include "nsSVGNumber2.h" +#include "nsSVGNumberPair.h" +#include "nsSVGInteger.h" +#include "nsSVGIntegerPair.h" +#include "nsSVGBoolean.h" +#include "nsCOMPtr.h" +#include "nsSVGFilterInstance.h" +#include "nsSVGEnum.h" +#include "SVGNumberList.h" +#include "SVGAnimatedNumberList.h" +#include "DOMSVGAnimatedNumberList.h" +#include "nsSVGFilters.h" +#include "nsLayoutUtils.h" +#include "nsSVGUtils.h" +#include "nsStyleContext.h" +#include "nsIFrame.h" +#include "imgIContainer.h" +#include "mozilla/dom/SVGFilterElement.h" +#include "nsSVGString.h" +#include "SVGContentUtils.h" +#include <algorithm> +#include "mozilla/dom/SVGAnimatedLength.h" +#include "mozilla/dom/SVGComponentTransferFunctionElement.h" +#include "mozilla/dom/SVGFEDistantLightElement.h" +#include "mozilla/dom/SVGFEFuncAElementBinding.h" +#include "mozilla/dom/SVGFEFuncBElementBinding.h" +#include "mozilla/dom/SVGFEFuncGElementBinding.h" +#include "mozilla/dom/SVGFEFuncRElementBinding.h" +#include "mozilla/dom/SVGFEPointLightElement.h" +#include "mozilla/dom/SVGFESpotLightElement.h" + +#if defined(XP_WIN) +// Prevent Windows redefining LoadImage +#undef LoadImage +#endif + +using namespace mozilla; +using namespace mozilla::dom; +using namespace mozilla::gfx; + +//--------------------Filter Element Base Class----------------------- + +nsSVGElement::LengthInfo nsSVGFE::sLengthInfo[4] = +{ + { &nsGkAtoms::x, 0, nsIDOMSVGLength::SVG_LENGTHTYPE_PERCENTAGE, SVGContentUtils::X }, + { &nsGkAtoms::y, 0, nsIDOMSVGLength::SVG_LENGTHTYPE_PERCENTAGE, SVGContentUtils::Y }, + { &nsGkAtoms::width, 100, nsIDOMSVGLength::SVG_LENGTHTYPE_PERCENTAGE, SVGContentUtils::X }, + { &nsGkAtoms::height, 100, nsIDOMSVGLength::SVG_LENGTHTYPE_PERCENTAGE, SVGContentUtils::Y } +}; + +//---------------------------------------------------------------------- +// nsISupports methods + +NS_IMPL_ADDREF_INHERITED(nsSVGFE,nsSVGFEBase) +NS_IMPL_RELEASE_INHERITED(nsSVGFE,nsSVGFEBase) + +NS_INTERFACE_MAP_BEGIN(nsSVGFE) + // nsISupports is an ambiguous base of nsSVGFE so we have to work + // around that + if ( aIID.Equals(NS_GET_IID(nsSVGFE)) ) + foundInterface = static_cast<nsISupports*>(static_cast<void*>(this)); + else +NS_INTERFACE_MAP_END_INHERITING(nsSVGFEBase) + +//---------------------------------------------------------------------- +// Implementation + +void +nsSVGFE::GetSourceImageNames(nsTArray<nsSVGStringInfo>& aSources) +{ +} + +bool +nsSVGFE::OutputIsTainted(const nsTArray<bool>& aInputsAreTainted, + nsIPrincipal* aReferencePrincipal) +{ + // This is the default implementation for OutputIsTainted. + // Our output is tainted if we have at least one tainted input. + for (uint32_t i = 0; i < aInputsAreTainted.Length(); i++) { + if (aInputsAreTainted[i]) { + return true; + } + } + return false; +} + +bool +nsSVGFE::AttributeAffectsRendering(int32_t aNameSpaceID, + nsIAtom* aAttribute) const +{ + return aNameSpaceID == kNameSpaceID_None && + (aAttribute == nsGkAtoms::x || + aAttribute == nsGkAtoms::y || + aAttribute == nsGkAtoms::width || + aAttribute == nsGkAtoms::height || + aAttribute == nsGkAtoms::result); +} + +already_AddRefed<SVGAnimatedLength> +nsSVGFE::X() +{ + return mLengthAttributes[ATTR_X].ToDOMAnimatedLength(this); +} + +already_AddRefed<SVGAnimatedLength> +nsSVGFE::Y() +{ + return mLengthAttributes[ATTR_Y].ToDOMAnimatedLength(this); +} + +already_AddRefed<SVGAnimatedLength> +nsSVGFE::Width() +{ + return mLengthAttributes[ATTR_WIDTH].ToDOMAnimatedLength(this); +} + +already_AddRefed<SVGAnimatedLength> +nsSVGFE::Height() +{ + return mLengthAttributes[ATTR_HEIGHT].ToDOMAnimatedLength(this); +} + +already_AddRefed<SVGAnimatedString> +nsSVGFE::Result() +{ + return GetResultImageName().ToDOMAnimatedString(this); +} + +//---------------------------------------------------------------------- +// nsIContent methods + +NS_IMETHODIMP_(bool) +nsSVGFE::IsAttributeMapped(const nsIAtom* name) const +{ + static const MappedAttributeEntry* const map[] = { + sFiltersMap + }; + + return FindAttributeDependence(name, map) || + nsSVGFEBase::IsAttributeMapped(name); +} + +//---------------------------------------------------------------------- +// nsSVGElement methods + + +bool +nsSVGFE::StyleIsSetToSRGB() +{ + nsIFrame* frame = GetPrimaryFrame(); + if (!frame) return false; + + nsStyleContext* style = frame->StyleContext(); + return style->StyleSVG()->mColorInterpolationFilters == + NS_STYLE_COLOR_INTERPOLATION_SRGB; +} + +/* virtual */ bool +nsSVGFE::HasValidDimensions() const +{ + return (!mLengthAttributes[ATTR_WIDTH].IsExplicitlySet() || + mLengthAttributes[ATTR_WIDTH].GetAnimValInSpecifiedUnits() > 0) && + (!mLengthAttributes[ATTR_HEIGHT].IsExplicitlySet() || + mLengthAttributes[ATTR_HEIGHT].GetAnimValInSpecifiedUnits() > 0); +} + +Size +nsSVGFE::GetKernelUnitLength(nsSVGFilterInstance* aInstance, + nsSVGNumberPair *aKernelUnitLength) +{ + if (!aKernelUnitLength->IsExplicitlySet()) { + return Size(1, 1); + } + + float kernelX = aInstance->GetPrimitiveNumber(SVGContentUtils::X, + aKernelUnitLength, + nsSVGNumberPair::eFirst); + float kernelY = aInstance->GetPrimitiveNumber(SVGContentUtils::Y, + aKernelUnitLength, + nsSVGNumberPair::eSecond); + return Size(kernelX, kernelY); +} + +nsSVGElement::LengthAttributesInfo +nsSVGFE::GetLengthInfo() +{ + return LengthAttributesInfo(mLengthAttributes, sLengthInfo, + ArrayLength(sLengthInfo)); +} + +namespace mozilla { +namespace dom { + +nsSVGElement::NumberListInfo SVGComponentTransferFunctionElement::sNumberListInfo[1] = +{ + { &nsGkAtoms::tableValues } +}; + +nsSVGElement::NumberInfo SVGComponentTransferFunctionElement::sNumberInfo[5] = +{ + { &nsGkAtoms::slope, 1, false }, + { &nsGkAtoms::intercept, 0, false }, + { &nsGkAtoms::amplitude, 1, false }, + { &nsGkAtoms::exponent, 1, false }, + { &nsGkAtoms::offset, 0, false } +}; + +nsSVGEnumMapping SVGComponentTransferFunctionElement::sTypeMap[] = { + {&nsGkAtoms::identity, + SVG_FECOMPONENTTRANSFER_TYPE_IDENTITY}, + {&nsGkAtoms::table, + SVG_FECOMPONENTTRANSFER_TYPE_TABLE}, + {&nsGkAtoms::discrete, + SVG_FECOMPONENTTRANSFER_TYPE_DISCRETE}, + {&nsGkAtoms::linear, + SVG_FECOMPONENTTRANSFER_TYPE_LINEAR}, + {&nsGkAtoms::gamma, + SVG_FECOMPONENTTRANSFER_TYPE_GAMMA}, + {nullptr, 0} +}; + +nsSVGElement::EnumInfo SVGComponentTransferFunctionElement::sEnumInfo[1] = +{ + { &nsGkAtoms::type, + sTypeMap, + SVG_FECOMPONENTTRANSFER_TYPE_IDENTITY + } +}; + +//---------------------------------------------------------------------- +// nsISupports methods + +NS_IMPL_ADDREF_INHERITED(SVGComponentTransferFunctionElement,SVGComponentTransferFunctionElementBase) +NS_IMPL_RELEASE_INHERITED(SVGComponentTransferFunctionElement,SVGComponentTransferFunctionElementBase) + +NS_INTERFACE_MAP_BEGIN(SVGComponentTransferFunctionElement) + // nsISupports is an ambiguous base of nsSVGFE so we have to work + // around that + if ( aIID.Equals(NS_GET_IID(SVGComponentTransferFunctionElement)) ) + foundInterface = static_cast<nsISupports*>(static_cast<void*>(this)); + else +NS_INTERFACE_MAP_END_INHERITING(SVGComponentTransferFunctionElementBase) + + +//---------------------------------------------------------------------- +// nsFEUnstyledElement methods + +bool +SVGComponentTransferFunctionElement::AttributeAffectsRendering(int32_t aNameSpaceID, + nsIAtom* aAttribute) const +{ + return aNameSpaceID == kNameSpaceID_None && + (aAttribute == nsGkAtoms::tableValues || + aAttribute == nsGkAtoms::slope || + aAttribute == nsGkAtoms::intercept || + aAttribute == nsGkAtoms::amplitude || + aAttribute == nsGkAtoms::exponent || + aAttribute == nsGkAtoms::offset || + aAttribute == nsGkAtoms::type); +} + +//---------------------------------------------------------------------- + +already_AddRefed<SVGAnimatedEnumeration> +SVGComponentTransferFunctionElement::Type() +{ + return mEnumAttributes[TYPE].ToDOMAnimatedEnum(this); +} + +already_AddRefed<DOMSVGAnimatedNumberList> +SVGComponentTransferFunctionElement::TableValues() +{ + return DOMSVGAnimatedNumberList::GetDOMWrapper( + &mNumberListAttributes[TABLEVALUES], this, TABLEVALUES); +} + +already_AddRefed<SVGAnimatedNumber> +SVGComponentTransferFunctionElement::Slope() +{ + return mNumberAttributes[SLOPE].ToDOMAnimatedNumber(this); +} + +already_AddRefed<SVGAnimatedNumber> +SVGComponentTransferFunctionElement::Intercept() +{ + return mNumberAttributes[INTERCEPT].ToDOMAnimatedNumber(this); +} + +already_AddRefed<SVGAnimatedNumber> +SVGComponentTransferFunctionElement::Amplitude() +{ + return mNumberAttributes[AMPLITUDE].ToDOMAnimatedNumber(this); +} + +already_AddRefed<SVGAnimatedNumber> +SVGComponentTransferFunctionElement::Exponent() +{ + return mNumberAttributes[EXPONENT].ToDOMAnimatedNumber(this); +} + +already_AddRefed<SVGAnimatedNumber> +SVGComponentTransferFunctionElement::Offset() +{ + return mNumberAttributes[OFFSET].ToDOMAnimatedNumber(this); +} + +AttributeMap +SVGComponentTransferFunctionElement::ComputeAttributes() +{ + uint32_t type = mEnumAttributes[TYPE].GetAnimValue(); + + float slope, intercept, amplitude, exponent, offset; + GetAnimatedNumberValues(&slope, &intercept, &litude, + &exponent, &offset, nullptr); + + const SVGNumberList &tableValues = + mNumberListAttributes[TABLEVALUES].GetAnimValue(); + + AttributeMap map; + map.Set(eComponentTransferFunctionType, type); + map.Set(eComponentTransferFunctionSlope, slope); + map.Set(eComponentTransferFunctionIntercept, intercept); + map.Set(eComponentTransferFunctionAmplitude, amplitude); + map.Set(eComponentTransferFunctionExponent, exponent); + map.Set(eComponentTransferFunctionOffset, offset); + if (tableValues.Length()) { + map.Set(eComponentTransferFunctionTableValues, &tableValues[0], tableValues.Length()); + } else { + map.Set(eComponentTransferFunctionTableValues, nullptr, 0); + } + return map; +} + +//---------------------------------------------------------------------- +// nsSVGElement methods + +nsSVGElement::NumberListAttributesInfo +SVGComponentTransferFunctionElement::GetNumberListInfo() +{ + return NumberListAttributesInfo(mNumberListAttributes, sNumberListInfo, + ArrayLength(sNumberListInfo)); +} + +nsSVGElement::EnumAttributesInfo +SVGComponentTransferFunctionElement::GetEnumInfo() +{ + return EnumAttributesInfo(mEnumAttributes, sEnumInfo, + ArrayLength(sEnumInfo)); +} + +nsSVGElement::NumberAttributesInfo +SVGComponentTransferFunctionElement::GetNumberInfo() +{ + return NumberAttributesInfo(mNumberAttributes, sNumberInfo, + ArrayLength(sNumberInfo)); +} + +/* virtual */ JSObject* +SVGFEFuncRElement::WrapNode(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) +{ + return SVGFEFuncRElementBinding::Wrap(aCx, this, aGivenProto); +} + +} // namespace dom +} // namespace mozilla + +NS_IMPL_NS_NEW_NAMESPACED_SVG_ELEMENT(FEFuncR) + +namespace mozilla { +namespace dom { + +NS_IMPL_ELEMENT_CLONE_WITH_INIT(SVGFEFuncRElement) + +/* virtual */ JSObject* +SVGFEFuncGElement::WrapNode(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) +{ + return SVGFEFuncGElementBinding::Wrap(aCx, this, aGivenProto); +} + +} // namespace dom +} // namespace mozilla + +NS_IMPL_NS_NEW_NAMESPACED_SVG_ELEMENT(FEFuncG) + +namespace mozilla { +namespace dom { + +NS_IMPL_ELEMENT_CLONE_WITH_INIT(SVGFEFuncGElement) + +/* virtual */ JSObject* +SVGFEFuncBElement::WrapNode(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) +{ + return SVGFEFuncBElementBinding::Wrap(aCx, this, aGivenProto); +} + +} // namespace dom +} // namespace mozilla + +NS_IMPL_NS_NEW_NAMESPACED_SVG_ELEMENT(FEFuncB) + +namespace mozilla { +namespace dom { + +NS_IMPL_ELEMENT_CLONE_WITH_INIT(SVGFEFuncBElement) + +/* virtual */ JSObject* +SVGFEFuncAElement::WrapNode(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) +{ + return SVGFEFuncAElementBinding::Wrap(aCx, this, aGivenProto); +} + +} // namespace dom +} // namespace mozilla + +NS_IMPL_NS_NEW_NAMESPACED_SVG_ELEMENT(FEFuncA) + +namespace mozilla { +namespace dom { + +NS_IMPL_ELEMENT_CLONE_WITH_INIT(SVGFEFuncAElement) + +} // namespace dom +} // namespace mozilla + +//-------------------------------------------------------------------- +// +nsSVGElement::NumberInfo nsSVGFELightingElement::sNumberInfo[4] = +{ + { &nsGkAtoms::surfaceScale, 1, false }, + { &nsGkAtoms::diffuseConstant, 1, false }, + { &nsGkAtoms::specularConstant, 1, false }, + { &nsGkAtoms::specularExponent, 1, false } +}; + +nsSVGElement::NumberPairInfo nsSVGFELightingElement::sNumberPairInfo[1] = +{ + { &nsGkAtoms::kernelUnitLength, 0, 0 } +}; + +nsSVGElement::StringInfo nsSVGFELightingElement::sStringInfo[2] = +{ + { &nsGkAtoms::result, kNameSpaceID_None, true }, + { &nsGkAtoms::in, kNameSpaceID_None, true } +}; + +//---------------------------------------------------------------------- +// nsISupports methods + +NS_IMPL_ADDREF_INHERITED(nsSVGFELightingElement,nsSVGFELightingElementBase) +NS_IMPL_RELEASE_INHERITED(nsSVGFELightingElement,nsSVGFELightingElementBase) + +NS_INTERFACE_MAP_BEGIN(nsSVGFELightingElement) +NS_INTERFACE_MAP_END_INHERITING(nsSVGFELightingElementBase) + +//---------------------------------------------------------------------- +// Implementation + +NS_IMETHODIMP_(bool) +nsSVGFELightingElement::IsAttributeMapped(const nsIAtom* name) const +{ + static const MappedAttributeEntry* const map[] = { + sLightingEffectsMap + }; + + return FindAttributeDependence(name, map) || + nsSVGFELightingElementBase::IsAttributeMapped(name); +} + +void +nsSVGFELightingElement::GetSourceImageNames(nsTArray<nsSVGStringInfo>& aSources) +{ + aSources.AppendElement(nsSVGStringInfo(&mStringAttributes[IN1], this)); +} + +AttributeMap +nsSVGFELightingElement::ComputeLightAttributes(nsSVGFilterInstance* aInstance) +{ + // find specified light + for (nsCOMPtr<nsIContent> child = nsINode::GetFirstChild(); + child; + child = child->GetNextSibling()) { + if (child->IsAnyOfSVGElements(nsGkAtoms::feDistantLight, + nsGkAtoms::fePointLight, + nsGkAtoms::feSpotLight)) { + return static_cast<SVGFELightElement*>(child.get())->ComputeLightAttributes(aInstance); + } + } + + AttributeMap map; + map.Set(eLightType, (uint32_t)eLightTypeNone); + return map; +} + +FilterPrimitiveDescription +nsSVGFELightingElement::AddLightingAttributes(FilterPrimitiveDescription aDescription, + nsSVGFilterInstance* aInstance) +{ + nsIFrame* frame = GetPrimaryFrame(); + if (!frame) { + return FilterPrimitiveDescription(PrimitiveType::Empty); + } + + nsStyleContext* style = frame->StyleContext(); + Color color(Color::FromABGR(style->StyleSVGReset()->mLightingColor)); + color.a = 1.f; + float surfaceScale = mNumberAttributes[SURFACE_SCALE].GetAnimValue(); + Size kernelUnitLength = + GetKernelUnitLength(aInstance, &mNumberPairAttributes[KERNEL_UNIT_LENGTH]); + + if (kernelUnitLength.width <= 0 || kernelUnitLength.height <= 0) { + // According to spec, A negative or zero value is an error. See link below for details. + // https://www.w3.org/TR/SVG/filters.html#feSpecularLightingKernelUnitLengthAttribute + return FilterPrimitiveDescription(PrimitiveType::Empty); + } + + FilterPrimitiveDescription& descr = aDescription; + descr.Attributes().Set(eLightingLight, ComputeLightAttributes(aInstance)); + descr.Attributes().Set(eLightingSurfaceScale, surfaceScale); + descr.Attributes().Set(eLightingKernelUnitLength, kernelUnitLength); + descr.Attributes().Set(eLightingColor, color); + return descr; +} + +bool +nsSVGFELightingElement::AttributeAffectsRendering(int32_t aNameSpaceID, + nsIAtom* aAttribute) const +{ + return nsSVGFELightingElementBase::AttributeAffectsRendering(aNameSpaceID, aAttribute) || + (aNameSpaceID == kNameSpaceID_None && + (aAttribute == nsGkAtoms::in || + aAttribute == nsGkAtoms::surfaceScale || + aAttribute == nsGkAtoms::kernelUnitLength)); +} + +//---------------------------------------------------------------------- +// nsSVGElement methods + +nsSVGElement::NumberAttributesInfo +nsSVGFELightingElement::GetNumberInfo() +{ + return NumberAttributesInfo(mNumberAttributes, sNumberInfo, + ArrayLength(sNumberInfo)); +} + +nsSVGElement::NumberPairAttributesInfo +nsSVGFELightingElement::GetNumberPairInfo() +{ + return NumberPairAttributesInfo(mNumberPairAttributes, sNumberPairInfo, + ArrayLength(sNumberPairInfo)); +} + +nsSVGElement::StringAttributesInfo +nsSVGFELightingElement::GetStringInfo() +{ + return StringAttributesInfo(mStringAttributes, sStringInfo, + ArrayLength(sStringInfo)); +} diff --git a/dom/svg/nsSVGFilters.h b/dom/svg/nsSVGFilters.h new file mode 100644 index 0000000000..4d845b244a --- /dev/null +++ b/dom/svg/nsSVGFilters.h @@ -0,0 +1,242 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef __NS_SVGFILTERSELEMENT_H__ +#define __NS_SVGFILTERSELEMENT_H__ + +#include "mozilla/Attributes.h" +#include "nsImageLoadingContent.h" +#include "nsSVGLength2.h" +#include "nsSVGString.h" +#include "nsSVGElement.h" +#include "nsSVGNumber2.h" +#include "nsSVGNumberPair.h" +#include "FilterSupport.h" + +class nsSVGFilterInstance; +class nsSVGNumberPair; + +struct nsSVGStringInfo { + nsSVGStringInfo(const nsSVGString* aString, + nsSVGElement *aElement) : + mString(aString), mElement(aElement) {} + + const nsSVGString* mString; + nsSVGElement* mElement; +}; + +typedef nsSVGElement nsSVGFEBase; + +#define NS_SVG_FE_CID \ +{ 0x60483958, 0xd229, 0x4a77, \ + { 0x96, 0xb2, 0x62, 0x3e, 0x69, 0x95, 0x1e, 0x0e } } + +/** + * Base class for filter primitive elements + * Children of those elements e.g. feMergeNode + * derive from SVGFEUnstyledElement instead + */ +class nsSVGFE : public nsSVGFEBase +{ + friend class nsSVGFilterInstance; + +protected: + typedef mozilla::gfx::SourceSurface SourceSurface; + typedef mozilla::gfx::Size Size; + typedef mozilla::gfx::IntRect IntRect; + typedef mozilla::gfx::ColorSpace ColorSpace; + typedef mozilla::gfx::FilterPrimitiveDescription FilterPrimitiveDescription; + + explicit nsSVGFE(already_AddRefed<mozilla::dom::NodeInfo>& aNodeInfo) + : nsSVGFEBase(aNodeInfo) {} + virtual ~nsSVGFE() {} + +public: + typedef mozilla::gfx::AttributeMap AttributeMap; + + ColorSpace + GetInputColorSpace(int32_t aInputIndex, ColorSpace aUnchangedInputColorSpace) { + return OperatesOnSRGB(aInputIndex, aUnchangedInputColorSpace == ColorSpace::SRGB) ? + ColorSpace::SRGB : ColorSpace::LinearRGB; + } + + // This is only called for filter primitives without inputs. For primitives + // with inputs, the output color model is the same as of the first input. + ColorSpace + GetOutputColorSpace() { + return ProducesSRGB() ? ColorSpace::SRGB : ColorSpace::LinearRGB; + } + + // See http://www.w3.org/TR/SVG/filters.html#FilterPrimitiveSubRegion + virtual bool SubregionIsUnionOfRegions() { return true; } + + NS_DECLARE_STATIC_IID_ACCESSOR(NS_SVG_FE_CID) + + // interfaces: + NS_DECL_ISUPPORTS_INHERITED + + // nsIContent interface + NS_IMETHOD_(bool) IsAttributeMapped(const nsIAtom* aAttribute) const override; + + // nsSVGElement interface + virtual nsresult Clone(mozilla::dom::NodeInfo *aNodeInfo, nsINode **aResult) const override = 0; + + virtual bool HasValidDimensions() const override; + + bool IsNodeOfType(uint32_t aFlags) const override + { return !(aFlags & ~(eCONTENT | eFILTER)); } + + virtual nsSVGString& GetResultImageName() = 0; + // Return a list of all image names used as sources. Default is to + // return no sources. + virtual void GetSourceImageNames(nsTArray<nsSVGStringInfo>& aSources); + + virtual FilterPrimitiveDescription + GetPrimitiveDescription(nsSVGFilterInstance* aInstance, + const IntRect& aFilterSubregion, + const nsTArray<bool>& aInputsAreTainted, + nsTArray<RefPtr<SourceSurface>>& aInputImages) = 0; + + // returns true if changes to the attribute should cause us to + // repaint the filter + virtual bool AttributeAffectsRendering( + int32_t aNameSpaceID, nsIAtom* aAttribute) const; + + // Return whether this filter primitive has tainted output. A filter's + // output is tainted if it depends on things that the web page is not + // allowed to read from, e.g. the source graphic or cross-origin images. + // aReferencePrincipal is the node principal of the filtered frame's element. + virtual bool OutputIsTainted(const nsTArray<bool>& aInputsAreTainted, + nsIPrincipal* aReferencePrincipal); + + static nsIntRect GetMaxRect() { + // Try to avoid overflow errors dealing with this rect. It will + // be intersected with some other reasonable-sized rect eventually. + return nsIntRect(INT32_MIN/2, INT32_MIN/2, INT32_MAX, INT32_MAX); + } + + operator nsISupports*() { return static_cast<nsIContent*>(this); } + + // WebIDL + already_AddRefed<mozilla::dom::SVGAnimatedLength> X(); + already_AddRefed<mozilla::dom::SVGAnimatedLength> Y(); + already_AddRefed<mozilla::dom::SVGAnimatedLength> Width(); + already_AddRefed<mozilla::dom::SVGAnimatedLength> Height(); + already_AddRefed<mozilla::dom::SVGAnimatedString> Result(); + +protected: + virtual bool OperatesOnSRGB(int32_t aInputIndex, bool aInputIsAlreadySRGB) { + return StyleIsSetToSRGB(); + } + + // Only called for filter primitives without inputs. + virtual bool ProducesSRGB() { return StyleIsSetToSRGB(); } + + bool StyleIsSetToSRGB(); + + // nsSVGElement specializations: + virtual LengthAttributesInfo GetLengthInfo() override; + + Size GetKernelUnitLength(nsSVGFilterInstance* aInstance, + nsSVGNumberPair *aKernelUnitLength); + + enum { ATTR_X, ATTR_Y, ATTR_WIDTH, ATTR_HEIGHT }; + nsSVGLength2 mLengthAttributes[4]; + static LengthInfo sLengthInfo[4]; +}; + +NS_DEFINE_STATIC_IID_ACCESSOR(nsSVGFE, NS_SVG_FE_CID) + +typedef nsSVGElement SVGFEUnstyledElementBase; + +class SVGFEUnstyledElement : public SVGFEUnstyledElementBase +{ +protected: + explicit SVGFEUnstyledElement(already_AddRefed<mozilla::dom::NodeInfo>& aNodeInfo) + : SVGFEUnstyledElementBase(aNodeInfo) {} + +public: + virtual nsresult Clone(mozilla::dom::NodeInfo *aNodeInfo, nsINode **aResult) const override = 0; + + // returns true if changes to the attribute should cause us to + // repaint the filter + virtual bool AttributeAffectsRendering( + int32_t aNameSpaceID, nsIAtom* aAttribute) const = 0; +}; + +//------------------------------------------------------------ + +typedef nsSVGFE nsSVGFELightingElementBase; + +class nsSVGFELightingElement : public nsSVGFELightingElementBase +{ +protected: + explicit nsSVGFELightingElement(already_AddRefed<mozilla::dom::NodeInfo>& aNodeInfo) + : nsSVGFELightingElementBase(aNodeInfo) {} + + virtual ~nsSVGFELightingElement() {} + +public: + // interfaces: + NS_DECL_ISUPPORTS_INHERITED + + virtual bool AttributeAffectsRendering( + int32_t aNameSpaceID, nsIAtom* aAttribute) const override; + virtual nsSVGString& GetResultImageName() override { return mStringAttributes[RESULT]; } + virtual void GetSourceImageNames(nsTArray<nsSVGStringInfo>& aSources) override; + NS_FORWARD_NSIDOMSVGELEMENT(nsSVGFELightingElementBase::) + + NS_IMETHOD_(bool) IsAttributeMapped(const nsIAtom* aAttribute) const override; + +protected: + virtual bool OperatesOnSRGB(int32_t aInputIndex, + bool aInputIsAlreadySRGB) override { return true; } + + virtual NumberAttributesInfo GetNumberInfo() override; + virtual NumberPairAttributesInfo GetNumberPairInfo() override; + virtual StringAttributesInfo GetStringInfo() override; + + AttributeMap ComputeLightAttributes(nsSVGFilterInstance* aInstance); + + FilterPrimitiveDescription + AddLightingAttributes(FilterPrimitiveDescription aDescription, + nsSVGFilterInstance* aInstance); + + enum { SURFACE_SCALE, DIFFUSE_CONSTANT, SPECULAR_CONSTANT, SPECULAR_EXPONENT }; + nsSVGNumber2 mNumberAttributes[4]; + static NumberInfo sNumberInfo[4]; + + enum { KERNEL_UNIT_LENGTH }; + nsSVGNumberPair mNumberPairAttributes[1]; + static NumberPairInfo sNumberPairInfo[1]; + + enum { RESULT, IN1 }; + nsSVGString mStringAttributes[2]; + static StringInfo sStringInfo[2]; +}; + +namespace mozilla { +namespace dom { + +typedef SVGFEUnstyledElement SVGFELightElementBase; + +class SVGFELightElement : public SVGFELightElementBase +{ +protected: + explicit SVGFELightElement(already_AddRefed<mozilla::dom::NodeInfo>& aNodeInfo) + : SVGFELightElementBase(aNodeInfo) {} + +public: + typedef gfx::AttributeMap AttributeMap; + + virtual AttributeMap + ComputeLightAttributes(nsSVGFilterInstance* aInstance) = 0; +}; + +} // namespace dom +} // namespace mozilla + +#endif diff --git a/dom/svg/nsSVGInteger.cpp b/dom/svg/nsSVGInteger.cpp new file mode 100644 index 0000000000..c0eb9c85e5 --- /dev/null +++ b/dom/svg/nsSVGInteger.cpp @@ -0,0 +1,153 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "nsError.h" +#include "nsSVGAttrTearoffTable.h" +#include "nsSVGInteger.h" +#include "nsSMILValue.h" +#include "SMILIntegerType.h" +#include "SVGContentUtils.h" + +using namespace mozilla; +using namespace mozilla::dom; + +/* Implementation */ + +static nsSVGAttrTearoffTable<nsSVGInteger, nsSVGInteger::DOMAnimatedInteger> + sSVGAnimatedIntegerTearoffTable; + +nsresult +nsSVGInteger::SetBaseValueString(const nsAString &aValueAsString, + nsSVGElement *aSVGElement) +{ + int32_t value; + + if (!SVGContentUtils::ParseInteger(aValueAsString, value)) { + return NS_ERROR_DOM_SYNTAX_ERR; + } + + mIsBaseSet = true; + mBaseVal = value; + if (!mIsAnimated) { + mAnimVal = mBaseVal; + } + else { + aSVGElement->AnimationNeedsResample(); + } + return NS_OK; +} + +void +nsSVGInteger::GetBaseValueString(nsAString & aValueAsString) +{ + aValueAsString.Truncate(); + aValueAsString.AppendInt(mBaseVal); +} + +void +nsSVGInteger::SetBaseValue(int aValue, nsSVGElement *aSVGElement) +{ + // We can't just rely on SetParsedAttrValue (as called by DidChangeInteger) + // detecting redundant changes since it will compare false if the existing + // attribute value has an associated serialized version (a string value) even + // if the integers match due to the way integers are stored in nsAttrValue. + if (aValue == mBaseVal && mIsBaseSet) { + return; + } + + mBaseVal = aValue; + mIsBaseSet = true; + if (!mIsAnimated) { + mAnimVal = mBaseVal; + } + else { + aSVGElement->AnimationNeedsResample(); + } + aSVGElement->DidChangeInteger(mAttrEnum); +} + +void +nsSVGInteger::SetAnimValue(int aValue, nsSVGElement *aSVGElement) +{ + if (mIsAnimated && aValue == mAnimVal) { + return; + } + mAnimVal = aValue; + mIsAnimated = true; + aSVGElement->DidAnimateInteger(mAttrEnum); +} + +already_AddRefed<SVGAnimatedInteger> +nsSVGInteger::ToDOMAnimatedInteger(nsSVGElement *aSVGElement) +{ + RefPtr<DOMAnimatedInteger> domAnimatedInteger = + sSVGAnimatedIntegerTearoffTable.GetTearoff(this); + if (!domAnimatedInteger) { + domAnimatedInteger = new DOMAnimatedInteger(this, aSVGElement); + sSVGAnimatedIntegerTearoffTable.AddTearoff(this, domAnimatedInteger); + } + + return domAnimatedInteger.forget(); +} + +nsSVGInteger::DOMAnimatedInteger::~DOMAnimatedInteger() +{ + sSVGAnimatedIntegerTearoffTable.RemoveTearoff(mVal); +} + +nsISMILAttr* +nsSVGInteger::ToSMILAttr(nsSVGElement *aSVGElement) +{ + return new SMILInteger(this, aSVGElement); +} + +nsresult +nsSVGInteger::SMILInteger::ValueFromString(const nsAString& aStr, + const dom::SVGAnimationElement* /*aSrcElement*/, + nsSMILValue& aValue, + bool& aPreventCachingOfSandwich) const +{ + int32_t val; + + if (!SVGContentUtils::ParseInteger(aStr, val)) { + return NS_ERROR_DOM_SYNTAX_ERR; + } + + nsSMILValue smilVal(SMILIntegerType::Singleton()); + smilVal.mU.mInt = val; + aValue = smilVal; + aPreventCachingOfSandwich = false; + return NS_OK; +} + +nsSMILValue +nsSVGInteger::SMILInteger::GetBaseValue() const +{ + nsSMILValue val(SMILIntegerType::Singleton()); + val.mU.mInt = mVal->mBaseVal; + return val; +} + +void +nsSVGInteger::SMILInteger::ClearAnimValue() +{ + if (mVal->mIsAnimated) { + mVal->mIsAnimated = false; + mVal->mAnimVal = mVal->mBaseVal; + mSVGElement->DidAnimateInteger(mVal->mAttrEnum); + } +} + +nsresult +nsSVGInteger::SMILInteger::SetAnimValue(const nsSMILValue& aValue) +{ + NS_ASSERTION(aValue.mType == SMILIntegerType::Singleton(), + "Unexpected type to assign animated value"); + if (aValue.mType == SMILIntegerType::Singleton()) { + mVal->SetAnimValue(int(aValue.mU.mInt), mSVGElement); + } + return NS_OK; +} diff --git a/dom/svg/nsSVGInteger.h b/dom/svg/nsSVGInteger.h new file mode 100644 index 0000000000..cc237e4639 --- /dev/null +++ b/dom/svg/nsSVGInteger.h @@ -0,0 +1,121 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef __NS_SVGINTEGER_H__ +#define __NS_SVGINTEGER_H__ + +#include "nsCycleCollectionParticipant.h" +#include "nsError.h" +#include "SVGAnimatedInteger.h" +#include "nsISMILAttr.h" +#include "nsSVGElement.h" +#include "mozilla/Attributes.h" + +class nsSMILValue; + +namespace mozilla { +namespace dom { +class SVGAnimationElement; +} // namespace dom +} // namespace mozilla + +class nsSVGInteger +{ + +public: + void Init(uint8_t aAttrEnum = 0xff, int32_t aValue = 0) { + mAnimVal = mBaseVal = aValue; + mAttrEnum = aAttrEnum; + mIsAnimated = false; + mIsBaseSet = false; + } + + nsresult SetBaseValueString(const nsAString& aValue, + nsSVGElement *aSVGElement); + void GetBaseValueString(nsAString& aValue); + + void SetBaseValue(int32_t aValue, nsSVGElement *aSVGElement); + int32_t GetBaseValue() const + { return mBaseVal; } + + void SetAnimValue(int aValue, nsSVGElement *aSVGElement); + int GetAnimValue() const + { return mAnimVal; } + + // Returns true if the animated value of this integer has been explicitly + // set (either by animation, or by taking on the base value which has been + // explicitly set by markup or a DOM call), false otherwise. + // If this returns false, the animated value is still valid, that is, + // useable, and represents the default base value of the attribute. + bool IsExplicitlySet() const + { return mIsAnimated || mIsBaseSet; } + + already_AddRefed<mozilla::dom::SVGAnimatedInteger> + ToDOMAnimatedInteger(nsSVGElement* aSVGElement); + // Returns a new nsISMILAttr object that the caller must delete + nsISMILAttr* ToSMILAttr(nsSVGElement* aSVGElement); + +private: + + int32_t mAnimVal; + int32_t mBaseVal; + uint8_t mAttrEnum; // element specified tracking for attribute + bool mIsAnimated; + bool mIsBaseSet; + +public: + struct DOMAnimatedInteger final : public mozilla::dom::SVGAnimatedInteger + { + DOMAnimatedInteger(nsSVGInteger* aVal, nsSVGElement* aSVGElement) + : mozilla::dom::SVGAnimatedInteger(aSVGElement) + , mVal(aVal) + {} + virtual ~DOMAnimatedInteger(); + + nsSVGInteger* mVal; // kept alive because it belongs to content + + virtual int32_t BaseVal() override + { + return mVal->GetBaseValue(); + } + virtual void SetBaseVal(int32_t aValue) override + { + mVal->SetBaseValue(aValue, mSVGElement); + } + + // Script may have modified animation parameters or timeline -- DOM getters + // need to flush any resample requests to reflect these modifications. + virtual int32_t AnimVal() override + { + mSVGElement->FlushAnimations(); + return mVal->GetAnimValue(); + } + }; + + struct SMILInteger : public nsISMILAttr + { + public: + SMILInteger(nsSVGInteger* aVal, nsSVGElement* aSVGElement) + : mVal(aVal), mSVGElement(aSVGElement) {} + + // 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. + nsSVGInteger* mVal; + nsSVGElement* mSVGElement; + + // nsISMILAttr methods + virtual nsresult ValueFromString(const nsAString& aStr, + const mozilla::dom::SVGAnimationElement* aSrcElement, + nsSMILValue& aValue, + bool& aPreventCachingOfSandwich) const override; + virtual nsSMILValue GetBaseValue() const override; + virtual void ClearAnimValue() override; + virtual nsresult SetAnimValue(const nsSMILValue& aValue) override; + }; +}; + +#endif //__NS_SVGINTEGER_H__ diff --git a/dom/svg/nsSVGIntegerPair.cpp b/dom/svg/nsSVGIntegerPair.cpp new file mode 100644 index 0000000000..5a3998c2ba --- /dev/null +++ b/dom/svg/nsSVGIntegerPair.cpp @@ -0,0 +1,237 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "nsSVGIntegerPair.h" +#include "nsSVGAttrTearoffTable.h" +#include "nsCharSeparatedTokenizer.h" +#include "nsError.h" +#include "nsMathUtils.h" +#include "nsSMILValue.h" +#include "SVGContentUtils.h" +#include "SVGIntegerPairSMILType.h" + +using namespace mozilla; +using namespace mozilla::dom; + +static nsSVGAttrTearoffTable<nsSVGIntegerPair, nsSVGIntegerPair::DOMAnimatedInteger> + sSVGFirstAnimatedIntegerTearoffTable; +static nsSVGAttrTearoffTable<nsSVGIntegerPair, nsSVGIntegerPair::DOMAnimatedInteger> + sSVGSecondAnimatedIntegerTearoffTable; + +/* Implementation */ + +static nsresult +ParseIntegerOptionalInteger(const nsAString& aValue, + int32_t aValues[2]) +{ + nsCharSeparatedTokenizerTemplate<IsSVGWhitespace> + tokenizer(aValue, ',', + nsCharSeparatedTokenizer::SEPARATOR_OPTIONAL); + if (tokenizer.whitespaceBeforeFirstToken()) { + return NS_ERROR_DOM_SYNTAX_ERR; + } + + uint32_t i; + for (i = 0; i < 2 && tokenizer.hasMoreTokens(); ++i) { + if (!SVGContentUtils::ParseInteger(tokenizer.nextToken(), aValues[i])) { + return NS_ERROR_DOM_SYNTAX_ERR; + } + } + if (i == 1) { + aValues[1] = aValues[0]; + } + + if (i == 0 || // Too few values. + tokenizer.hasMoreTokens() || // Too many values. + tokenizer.whitespaceAfterCurrentToken() || // Trailing whitespace. + tokenizer.separatorAfterCurrentToken()) { // Trailing comma. + return NS_ERROR_DOM_SYNTAX_ERR; + } + + return NS_OK; +} + +nsresult +nsSVGIntegerPair::SetBaseValueString(const nsAString &aValueAsString, + nsSVGElement *aSVGElement) +{ + int32_t val[2]; + + nsresult rv = ParseIntegerOptionalInteger(aValueAsString, val); + + if (NS_FAILED(rv)) { + return rv; + } + + mBaseVal[0] = val[0]; + mBaseVal[1] = val[1]; + mIsBaseSet = true; + if (!mIsAnimated) { + mAnimVal[0] = mBaseVal[0]; + mAnimVal[1] = mBaseVal[1]; + } + else { + aSVGElement->AnimationNeedsResample(); + } + + // We don't need to call DidChange* here - we're only called by + // nsSVGElement::ParseAttribute under Element::SetAttr, + // which takes care of notifying. + return NS_OK; +} + +void +nsSVGIntegerPair::GetBaseValueString(nsAString &aValueAsString) const +{ + aValueAsString.Truncate(); + aValueAsString.AppendInt(mBaseVal[0]); + if (mBaseVal[0] != mBaseVal[1]) { + aValueAsString.AppendLiteral(", "); + aValueAsString.AppendInt(mBaseVal[1]); + } +} + +void +nsSVGIntegerPair::SetBaseValue(int32_t aValue, PairIndex aPairIndex, + nsSVGElement *aSVGElement) +{ + uint32_t index = (aPairIndex == eFirst ? 0 : 1); + if (mIsBaseSet && mBaseVal[index] == aValue) { + return; + } + + nsAttrValue emptyOrOldValue = aSVGElement->WillChangeIntegerPair(mAttrEnum); + mBaseVal[index] = aValue; + mIsBaseSet = true; + if (!mIsAnimated) { + mAnimVal[index] = aValue; + } + else { + aSVGElement->AnimationNeedsResample(); + } + aSVGElement->DidChangeIntegerPair(mAttrEnum, emptyOrOldValue); +} + +void +nsSVGIntegerPair::SetBaseValues(int32_t aValue1, int32_t aValue2, + nsSVGElement *aSVGElement) +{ + if (mIsBaseSet && mBaseVal[0] == aValue1 && mBaseVal[1] == aValue2) { + return; + } + + nsAttrValue emptyOrOldValue = aSVGElement->WillChangeIntegerPair(mAttrEnum); + mBaseVal[0] = aValue1; + mBaseVal[1] = aValue2; + mIsBaseSet = true; + if (!mIsAnimated) { + mAnimVal[0] = aValue1; + mAnimVal[1] = aValue2; + } + else { + aSVGElement->AnimationNeedsResample(); + } + aSVGElement->DidChangeIntegerPair(mAttrEnum, emptyOrOldValue); +} + +void +nsSVGIntegerPair::SetAnimValue(const int32_t aValue[2], nsSVGElement *aSVGElement) +{ + if (mIsAnimated && mAnimVal[0] == aValue[0] && mAnimVal[1] == aValue[1]) { + return; + } + mAnimVal[0] = aValue[0]; + mAnimVal[1] = aValue[1]; + mIsAnimated = true; + aSVGElement->DidAnimateIntegerPair(mAttrEnum); +} + +already_AddRefed<SVGAnimatedInteger> +nsSVGIntegerPair::ToDOMAnimatedInteger(PairIndex aIndex, + nsSVGElement* aSVGElement) +{ + RefPtr<DOMAnimatedInteger> domAnimatedInteger = + aIndex == eFirst ? sSVGFirstAnimatedIntegerTearoffTable.GetTearoff(this) : + sSVGSecondAnimatedIntegerTearoffTable.GetTearoff(this); + if (!domAnimatedInteger) { + domAnimatedInteger = new DOMAnimatedInteger(this, aIndex, aSVGElement); + if (aIndex == eFirst) { + sSVGFirstAnimatedIntegerTearoffTable.AddTearoff(this, domAnimatedInteger); + } else { + sSVGSecondAnimatedIntegerTearoffTable.AddTearoff(this, domAnimatedInteger); + } + } + + return domAnimatedInteger.forget(); +} + +nsSVGIntegerPair::DOMAnimatedInteger::~DOMAnimatedInteger() +{ + if (mIndex == eFirst) { + sSVGFirstAnimatedIntegerTearoffTable.RemoveTearoff(mVal); + } else { + sSVGSecondAnimatedIntegerTearoffTable.RemoveTearoff(mVal); + } +} + +nsISMILAttr* +nsSVGIntegerPair::ToSMILAttr(nsSVGElement *aSVGElement) +{ + return new SMILIntegerPair(this, aSVGElement); +} + +nsresult +nsSVGIntegerPair::SMILIntegerPair::ValueFromString(const nsAString& aStr, + const dom::SVGAnimationElement* /*aSrcElement*/, + nsSMILValue& aValue, + bool& aPreventCachingOfSandwich) const +{ + int32_t values[2]; + + nsresult rv = ParseIntegerOptionalInteger(aStr, values); + if (NS_FAILED(rv)) { + return rv; + } + + nsSMILValue val(SVGIntegerPairSMILType::Singleton()); + val.mU.mIntPair[0] = values[0]; + val.mU.mIntPair[1] = values[1]; + aValue = val; + aPreventCachingOfSandwich = false; + + return NS_OK; +} + +nsSMILValue +nsSVGIntegerPair::SMILIntegerPair::GetBaseValue() const +{ + nsSMILValue val(SVGIntegerPairSMILType::Singleton()); + val.mU.mIntPair[0] = mVal->mBaseVal[0]; + val.mU.mIntPair[1] = mVal->mBaseVal[1]; + return val; +} + +void +nsSVGIntegerPair::SMILIntegerPair::ClearAnimValue() +{ + if (mVal->mIsAnimated) { + mVal->mIsAnimated = false; + mVal->mAnimVal[0] = mVal->mBaseVal[0]; + mVal->mAnimVal[1] = mVal->mBaseVal[1]; + mSVGElement->DidAnimateIntegerPair(mVal->mAttrEnum); + } +} + +nsresult +nsSVGIntegerPair::SMILIntegerPair::SetAnimValue(const nsSMILValue& aValue) +{ + NS_ASSERTION(aValue.mType == SVGIntegerPairSMILType::Singleton(), + "Unexpected type to assign animated value"); + if (aValue.mType == SVGIntegerPairSMILType::Singleton()) { + mVal->SetAnimValue(aValue.mU.mIntPair, mSVGElement); + } + return NS_OK; +} diff --git a/dom/svg/nsSVGIntegerPair.h b/dom/svg/nsSVGIntegerPair.h new file mode 100644 index 0000000000..b3eec328a9 --- /dev/null +++ b/dom/svg/nsSVGIntegerPair.h @@ -0,0 +1,131 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef __NS_SVGINTEGERPAIR_H__ +#define __NS_SVGINTEGERPAIR_H__ + +#include "nsCycleCollectionParticipant.h" +#include "nsError.h" +#include "nsISMILAttr.h" +#include "nsSVGElement.h" +#include "mozilla/Attributes.h" +#include "mozilla/dom/SVGAnimatedInteger.h" + +class nsSMILValue; + +namespace mozilla { +namespace dom { +class SVGAnimationElement; +} // namespace dom +} // namespace mozilla + +class nsSVGIntegerPair +{ + +public: + enum PairIndex { + eFirst, + eSecond + }; + + void Init(uint8_t aAttrEnum = 0xff, int32_t aValue1 = 0, int32_t aValue2 = 0) { + mAnimVal[0] = mBaseVal[0] = aValue1; + mAnimVal[1] = mBaseVal[1] = aValue2; + mAttrEnum = aAttrEnum; + mIsAnimated = false; + mIsBaseSet = false; + } + + nsresult SetBaseValueString(const nsAString& aValue, + nsSVGElement *aSVGElement); + void GetBaseValueString(nsAString& aValue) const; + + void SetBaseValue(int32_t aValue, PairIndex aIndex, nsSVGElement *aSVGElement); + void SetBaseValues(int32_t aValue1, int32_t aValue2, nsSVGElement *aSVGElement); + int32_t GetBaseValue(PairIndex aIndex) const + { return mBaseVal[aIndex == eFirst ? 0 : 1]; } + void SetAnimValue(const int32_t aValue[2], nsSVGElement *aSVGElement); + int32_t GetAnimValue(PairIndex aIndex) const + { return mAnimVal[aIndex == eFirst ? 0 : 1]; } + + // Returns true if the animated value of this integer has been explicitly + // set (either by animation, or by taking on the base value which has been + // explicitly set by markup or a DOM call), false otherwise. + // If this returns false, the animated value is still valid, that is, + // useable, and represents the default base value of the attribute. + bool IsExplicitlySet() const + { return mIsAnimated || mIsBaseSet; } + + already_AddRefed<mozilla::dom::SVGAnimatedInteger> + ToDOMAnimatedInteger(PairIndex aIndex, + nsSVGElement* aSVGElement); + // Returns a new nsISMILAttr object that the caller must delete + nsISMILAttr* ToSMILAttr(nsSVGElement* aSVGElement); + +private: + + int32_t mAnimVal[2]; + int32_t mBaseVal[2]; + uint8_t mAttrEnum; // element specified tracking for attribute + bool mIsAnimated; + bool mIsBaseSet; + +public: + struct DOMAnimatedInteger final : public mozilla::dom::SVGAnimatedInteger + { + DOMAnimatedInteger(nsSVGIntegerPair* aVal, PairIndex aIndex, + nsSVGElement* aSVGElement) + : mozilla::dom::SVGAnimatedInteger(aSVGElement) + , mVal(aVal) + , mIndex(aIndex) + {} + virtual ~DOMAnimatedInteger(); + + nsSVGIntegerPair* mVal; // kept alive because it belongs to content + PairIndex mIndex; // are we the first or second integer + + virtual int32_t BaseVal() override + { + return mVal->GetBaseValue(mIndex); + } + virtual void SetBaseVal(int32_t aValue) override + { + mVal->SetBaseValue(aValue, mIndex, mSVGElement); + } + + // Script may have modified animation parameters or timeline -- DOM getters + // need to flush any resample requests to reflect these modifications. + virtual int32_t AnimVal() override + { + mSVGElement->FlushAnimations(); + return mVal->GetAnimValue(mIndex); + } + }; + + struct SMILIntegerPair : public nsISMILAttr + { + public: + SMILIntegerPair(nsSVGIntegerPair* aVal, nsSVGElement* aSVGElement) + : mVal(aVal), mSVGElement(aSVGElement) {} + + // 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. + nsSVGIntegerPair* mVal; + nsSVGElement* mSVGElement; + + // nsISMILAttr methods + virtual nsresult ValueFromString(const nsAString& aStr, + const mozilla::dom::SVGAnimationElement* aSrcElement, + nsSMILValue& aValue, + bool& aPreventCachingOfSandwich) const override; + virtual nsSMILValue GetBaseValue() const override; + virtual void ClearAnimValue() override; + virtual nsresult SetAnimValue(const nsSMILValue& aValue) override; + }; +}; + +#endif //__NS_SVGINTEGERPAIR_H__ diff --git a/dom/svg/nsSVGLength2.cpp b/dom/svg/nsSVGLength2.cpp new file mode 100644 index 0000000000..1deec1006b --- /dev/null +++ b/dom/svg/nsSVGLength2.cpp @@ -0,0 +1,529 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "mozilla/ArrayUtils.h" + +#include "nsSVGLength2.h" +#include "mozilla/dom/SVGAnimatedLength.h" +#include "mozilla/dom/SVGSVGElement.h" +#include "nsContentUtils.h" // NS_ENSURE_FINITE +#include "nsIFrame.h" +#include "nsSMILFloatType.h" +#include "nsSMILValue.h" +#include "nsSVGAttrTearoffTable.h" +#include "nsSVGIntegrationUtils.h" +#include "nsTextFormatter.h" +#include "DOMSVGLength.h" +#include "LayoutLogging.h" + +using namespace mozilla; +using namespace mozilla::dom; + +static nsIAtom** const unitMap[] = +{ + nullptr, /* SVG_LENGTHTYPE_UNKNOWN */ + nullptr, /* SVG_LENGTHTYPE_NUMBER */ + &nsGkAtoms::percentage, + &nsGkAtoms::em, + &nsGkAtoms::ex, + &nsGkAtoms::px, + &nsGkAtoms::cm, + &nsGkAtoms::mm, + &nsGkAtoms::in, + &nsGkAtoms::pt, + &nsGkAtoms::pc +}; + +static nsSVGAttrTearoffTable<nsSVGLength2, SVGAnimatedLength> + sSVGAnimatedLengthTearoffTable; + +/* Helper functions */ + +static bool +IsValidUnitType(uint16_t unit) +{ + if (unit > nsIDOMSVGLength::SVG_LENGTHTYPE_UNKNOWN && + unit <= nsIDOMSVGLength::SVG_LENGTHTYPE_PC) + return true; + + return false; +} + +static void +GetUnitString(nsAString& unit, uint16_t unitType) +{ + if (IsValidUnitType(unitType)) { + if (unitMap[unitType]) { + (*unitMap[unitType])->ToString(unit); + } + return; + } + + NS_NOTREACHED("Unknown unit type"); + return; +} + +static uint16_t +GetUnitTypeForString(const nsAString& unitStr) +{ + if (unitStr.IsEmpty()) + return nsIDOMSVGLength::SVG_LENGTHTYPE_NUMBER; + + nsIAtom *unitAtom = NS_GetStaticAtom(unitStr); + if (unitAtom) { + for (uint32_t i = 0 ; i < ArrayLength(unitMap) ; i++) { + if (unitMap[i] && *unitMap[i] == unitAtom) { + return i; + } + } + } + + return nsIDOMSVGLength::SVG_LENGTHTYPE_UNKNOWN; +} + +static void +GetValueString(nsAString &aValueAsString, float aValue, uint16_t aUnitType) +{ + char16_t buf[24]; + nsTextFormatter::snprintf(buf, sizeof(buf)/sizeof(char16_t), + u"%g", + (double)aValue); + aValueAsString.Assign(buf); + + nsAutoString unitString; + GetUnitString(unitString, aUnitType); + aValueAsString.Append(unitString); +} + +static bool +GetValueFromString(const nsAString& aString, + float& aValue, + uint16_t* aUnitType) +{ + RangedPtr<const char16_t> iter = + SVGContentUtils::GetStartRangedPtr(aString); + const RangedPtr<const char16_t> end = + SVGContentUtils::GetEndRangedPtr(aString); + + if (!SVGContentUtils::ParseNumber(iter, end, aValue)) { + return false; + } + const nsAString& units = Substring(iter.get(), end.get()); + *aUnitType = GetUnitTypeForString(units); + return IsValidUnitType(*aUnitType); +} + +static float GetMMPerPixel() { return MM_PER_INCH_FLOAT / 96; } + +static float +FixAxisLength(float aLength) +{ + if (aLength == 0.0f) { + LAYOUT_WARNING("zero axis length"); + return 1e-20f; + } + return aLength; +} + +SVGElementMetrics::SVGElementMetrics(nsSVGElement* aSVGElement, + SVGSVGElement* aCtx) + : mSVGElement(aSVGElement) + , mCtx(aCtx) +{ +} + +float +SVGElementMetrics::GetEmLength() const +{ + return SVGContentUtils::GetFontSize(mSVGElement); +} + +float +SVGElementMetrics::GetExLength() const +{ + return SVGContentUtils::GetFontXHeight(mSVGElement); +} + +float +SVGElementMetrics::GetAxisLength(uint8_t aCtxType) const +{ + if (!EnsureCtx()) { + return 1; + } + + return FixAxisLength(mCtx->GetLength(aCtxType)); +} + +bool +SVGElementMetrics::EnsureCtx() const +{ + if (!mCtx && mSVGElement) { + mCtx = mSVGElement->GetCtx(); + if (!mCtx && mSVGElement->IsSVGElement(nsGkAtoms::svg)) { + // mSVGElement must be the outer svg element + mCtx = static_cast<SVGSVGElement*>(mSVGElement); + } + } + return mCtx != nullptr; +} + +NonSVGFrameUserSpaceMetrics::NonSVGFrameUserSpaceMetrics(nsIFrame* aFrame) + : mFrame(aFrame) +{ +} + +float +NonSVGFrameUserSpaceMetrics::GetEmLength() const +{ + return SVGContentUtils::GetFontSize(mFrame); +} + +float +NonSVGFrameUserSpaceMetrics::GetExLength() const +{ + return SVGContentUtils::GetFontXHeight(mFrame); +} + +gfx::Size +NonSVGFrameUserSpaceMetrics::GetSize() const +{ + return nsSVGIntegrationUtils::GetSVGCoordContextForNonSVGFrame(mFrame); +} + +float +UserSpaceMetricsWithSize::GetAxisLength(uint8_t aCtxType) const +{ + gfx::Size size = GetSize(); + float length; + switch (aCtxType) { + case SVGContentUtils::X: + length = size.width; + break; + case SVGContentUtils::Y: + length = size.height; + break; + case SVGContentUtils::XY: + length = SVGContentUtils::ComputeNormalizedHypotenuse(size.width, size.height); + break; + default: + NS_NOTREACHED("Unknown axis type"); + length = 1; + break; + } + return FixAxisLength(length); +} + +float +nsSVGLength2::GetUnitScaleFactor(nsSVGElement *aSVGElement, + uint8_t aUnitType) const +{ + return GetUnitScaleFactor(SVGElementMetrics(aSVGElement), aUnitType); +} + +float +nsSVGLength2::GetUnitScaleFactor(SVGSVGElement *aCtx, uint8_t aUnitType) const +{ + return GetUnitScaleFactor(SVGElementMetrics(aCtx, aCtx), aUnitType); +} + +float +nsSVGLength2::GetUnitScaleFactor(nsIFrame *aFrame, uint8_t aUnitType) const +{ + nsIContent* content = aFrame->GetContent(); + if (content->IsSVGElement()) { + return GetUnitScaleFactor(SVGElementMetrics(static_cast<nsSVGElement*>(content)), aUnitType); + } + return GetUnitScaleFactor(NonSVGFrameUserSpaceMetrics(aFrame), aUnitType); +} + +float +nsSVGLength2::GetUnitScaleFactor(const UserSpaceMetrics& aMetrics, uint8_t aUnitType) const +{ + switch (aUnitType) { + case nsIDOMSVGLength::SVG_LENGTHTYPE_NUMBER: + case nsIDOMSVGLength::SVG_LENGTHTYPE_PX: + return 1; + case nsIDOMSVGLength::SVG_LENGTHTYPE_MM: + return GetMMPerPixel(); + case nsIDOMSVGLength::SVG_LENGTHTYPE_CM: + return GetMMPerPixel() / 10.0f; + case nsIDOMSVGLength::SVG_LENGTHTYPE_IN: + return GetMMPerPixel() / MM_PER_INCH_FLOAT; + case nsIDOMSVGLength::SVG_LENGTHTYPE_PT: + return GetMMPerPixel() * POINTS_PER_INCH_FLOAT / MM_PER_INCH_FLOAT; + case nsIDOMSVGLength::SVG_LENGTHTYPE_PC: + return GetMMPerPixel() * POINTS_PER_INCH_FLOAT / MM_PER_INCH_FLOAT / 12.0f; + case nsIDOMSVGLength::SVG_LENGTHTYPE_PERCENTAGE: + return 100.0f / aMetrics.GetAxisLength(mCtxType); + case nsIDOMSVGLength::SVG_LENGTHTYPE_EMS: + return 1 / aMetrics.GetEmLength(); + case nsIDOMSVGLength::SVG_LENGTHTYPE_EXS: + return 1 / aMetrics.GetExLength(); + default: + NS_NOTREACHED("Unknown unit type"); + return 0; + } +} + +void +nsSVGLength2::SetBaseValueInSpecifiedUnits(float aValue, + nsSVGElement *aSVGElement, + bool aDoSetAttr) +{ + if (mIsBaseSet && mBaseVal == aValue) { + return; + } + + nsAttrValue emptyOrOldValue; + if (aDoSetAttr) { + emptyOrOldValue = aSVGElement->WillChangeLength(mAttrEnum); + } + mBaseVal = aValue; + mIsBaseSet = true; + if (!mIsAnimated) { + mAnimVal = mBaseVal; + } + else { + aSVGElement->AnimationNeedsResample(); + } + if (aDoSetAttr) { + aSVGElement->DidChangeLength(mAttrEnum, emptyOrOldValue); + } +} + +nsresult +nsSVGLength2::ConvertToSpecifiedUnits(uint16_t unitType, + nsSVGElement *aSVGElement) +{ + if (!IsValidUnitType(unitType)) + return NS_ERROR_DOM_NOT_SUPPORTED_ERR; + + if (mIsBaseSet && mSpecifiedUnitType == uint8_t(unitType)) + return NS_OK; + + // Even though we're not changing the visual effect this length will have + // on the document, we still need to send out notifications in case we have + // mutation listeners, since the actual string value of the attribute will + // change. + nsAttrValue emptyOrOldValue = aSVGElement->WillChangeLength(mAttrEnum); + + float valueInUserUnits = + mBaseVal / GetUnitScaleFactor(aSVGElement, mSpecifiedUnitType); + mSpecifiedUnitType = uint8_t(unitType); + // Setting aDoSetAttr to false here will ensure we don't call + // Will/DidChangeAngle a second time (and dispatch duplicate notifications). + SetBaseValue(valueInUserUnits, aSVGElement, false); + + aSVGElement->DidChangeLength(mAttrEnum, emptyOrOldValue); + + return NS_OK; +} + +nsresult +nsSVGLength2::NewValueSpecifiedUnits(uint16_t unitType, + float valueInSpecifiedUnits, + nsSVGElement *aSVGElement) +{ + NS_ENSURE_FINITE(valueInSpecifiedUnits, NS_ERROR_ILLEGAL_VALUE); + + if (!IsValidUnitType(unitType)) + return NS_ERROR_DOM_NOT_SUPPORTED_ERR; + + if (mIsBaseSet && mBaseVal == valueInSpecifiedUnits && + mSpecifiedUnitType == uint8_t(unitType)) { + return NS_OK; + } + + nsAttrValue emptyOrOldValue = aSVGElement->WillChangeLength(mAttrEnum); + mBaseVal = valueInSpecifiedUnits; + mIsBaseSet = true; + mSpecifiedUnitType = uint8_t(unitType); + if (!mIsAnimated) { + mAnimVal = mBaseVal; + } + else { + aSVGElement->AnimationNeedsResample(); + } + aSVGElement->DidChangeLength(mAttrEnum, emptyOrOldValue); + return NS_OK; +} + +nsresult +nsSVGLength2::ToDOMBaseVal(DOMSVGLength **aResult, nsSVGElement *aSVGElement) +{ + RefPtr<DOMSVGLength> domBaseVal = + DOMSVGLength::GetTearOff(this, aSVGElement, false); + + domBaseVal.forget(aResult); + return NS_OK; +} + +nsresult +nsSVGLength2::ToDOMAnimVal(DOMSVGLength **aResult, nsSVGElement *aSVGElement) +{ + RefPtr<DOMSVGLength> domAnimVal = + DOMSVGLength::GetTearOff(this, aSVGElement, true); + + domAnimVal.forget(aResult); + return NS_OK; +} + +/* Implementation */ + +nsresult +nsSVGLength2::SetBaseValueString(const nsAString &aValueAsString, + nsSVGElement *aSVGElement, + bool aDoSetAttr) +{ + float value; + uint16_t unitType; + + if (!GetValueFromString(aValueAsString, value, &unitType)) { + return NS_ERROR_DOM_SYNTAX_ERR; + } + + if (mIsBaseSet && mBaseVal == float(value) && + mSpecifiedUnitType == uint8_t(unitType)) { + return NS_OK; + } + + nsAttrValue emptyOrOldValue; + if (aDoSetAttr) { + emptyOrOldValue = aSVGElement->WillChangeLength(mAttrEnum); + } + mBaseVal = value; + mIsBaseSet = true; + mSpecifiedUnitType = uint8_t(unitType); + if (!mIsAnimated) { + mAnimVal = mBaseVal; + } + else { + aSVGElement->AnimationNeedsResample(); + } + + if (aDoSetAttr) { + aSVGElement->DidChangeLength(mAttrEnum, emptyOrOldValue); + } + return NS_OK; +} + +void +nsSVGLength2::GetBaseValueString(nsAString & aValueAsString) const +{ + GetValueString(aValueAsString, mBaseVal, mSpecifiedUnitType); +} + +void +nsSVGLength2::GetAnimValueString(nsAString & aValueAsString) const +{ + GetValueString(aValueAsString, mAnimVal, mSpecifiedUnitType); +} + +void +nsSVGLength2::SetBaseValue(float aValue, nsSVGElement *aSVGElement, + bool aDoSetAttr) +{ + SetBaseValueInSpecifiedUnits(aValue * GetUnitScaleFactor(aSVGElement, + mSpecifiedUnitType), + aSVGElement, aDoSetAttr); +} + +void +nsSVGLength2::SetAnimValueInSpecifiedUnits(float aValue, + nsSVGElement* aSVGElement) +{ + if (mAnimVal == aValue && mIsAnimated) { + return; + } + mAnimVal = aValue; + mIsAnimated = true; + aSVGElement->DidAnimateLength(mAttrEnum); +} + +void +nsSVGLength2::SetAnimValue(float aValue, nsSVGElement *aSVGElement) +{ + SetAnimValueInSpecifiedUnits(aValue * GetUnitScaleFactor(aSVGElement, + mSpecifiedUnitType), + aSVGElement); +} + +already_AddRefed<SVGAnimatedLength> +nsSVGLength2::ToDOMAnimatedLength(nsSVGElement* aSVGElement) +{ + RefPtr<SVGAnimatedLength> svgAnimatedLength = + sSVGAnimatedLengthTearoffTable.GetTearoff(this); + if (!svgAnimatedLength) { + svgAnimatedLength = new SVGAnimatedLength(this, aSVGElement); + sSVGAnimatedLengthTearoffTable.AddTearoff(this, svgAnimatedLength); + } + + return svgAnimatedLength.forget(); +} + +SVGAnimatedLength::~SVGAnimatedLength() +{ + sSVGAnimatedLengthTearoffTable.RemoveTearoff(mVal); +} + +nsISMILAttr* +nsSVGLength2::ToSMILAttr(nsSVGElement *aSVGElement) +{ + return new SMILLength(this, aSVGElement); +} + +nsresult +nsSVGLength2::SMILLength::ValueFromString(const nsAString& aStr, + const SVGAnimationElement* /*aSrcElement*/, + nsSMILValue& aValue, + bool& aPreventCachingOfSandwich) const +{ + float value; + uint16_t unitType; + + if (!GetValueFromString(aStr, value, &unitType)) { + return NS_ERROR_DOM_SYNTAX_ERR; + } + + nsSMILValue val(nsSMILFloatType::Singleton()); + val.mU.mDouble = value / mVal->GetUnitScaleFactor(mSVGElement, unitType); + aValue = val; + aPreventCachingOfSandwich = + (unitType == nsIDOMSVGLength::SVG_LENGTHTYPE_PERCENTAGE || + unitType == nsIDOMSVGLength::SVG_LENGTHTYPE_EMS || + unitType == nsIDOMSVGLength::SVG_LENGTHTYPE_EXS); + + return NS_OK; +} + +nsSMILValue +nsSVGLength2::SMILLength::GetBaseValue() const +{ + nsSMILValue val(nsSMILFloatType::Singleton()); + val.mU.mDouble = mVal->GetBaseValue(mSVGElement); + return val; +} + +void +nsSVGLength2::SMILLength::ClearAnimValue() +{ + if (mVal->mIsAnimated) { + mVal->mIsAnimated = false; + mVal->mAnimVal = mVal->mBaseVal; + mSVGElement->DidAnimateLength(mVal->mAttrEnum); + } +} + +nsresult +nsSVGLength2::SMILLength::SetAnimValue(const nsSMILValue& aValue) +{ + NS_ASSERTION(aValue.mType == nsSMILFloatType::Singleton(), + "Unexpected type to assign animated value"); + if (aValue.mType == nsSMILFloatType::Singleton()) { + mVal->SetAnimValue(float(aValue.mU.mDouble), mSVGElement); + } + return NS_OK; +} diff --git a/dom/svg/nsSVGLength2.h b/dom/svg/nsSVGLength2.h new file mode 100644 index 0000000000..d67bbdaad2 --- /dev/null +++ b/dom/svg/nsSVGLength2.h @@ -0,0 +1,209 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef __NS_SVGLENGTH2_H__ +#define __NS_SVGLENGTH2_H__ + +#include "mozilla/Attributes.h" +#include "nsCoord.h" +#include "nsCycleCollectionParticipant.h" +#include "nsError.h" +#include "nsIDOMSVGLength.h" +#include "nsISMILAttr.h" +#include "nsMathUtils.h" +#include "nsSVGElement.h" +#include "SVGContentUtils.h" +#include "mozilla/gfx/Rect.h" + +class nsIFrame; +class nsSMILValue; + +namespace mozilla { +class DOMSVGLength; +namespace dom { +class SVGAnimatedLength; +class SVGAnimationElement; +class SVGSVGElement; +} // namespace dom +} // namespace mozilla + +namespace mozilla { +namespace dom { + +class UserSpaceMetrics +{ +public: + virtual ~UserSpaceMetrics() {} + + virtual float GetEmLength() const = 0; + virtual float GetExLength() const = 0; + virtual float GetAxisLength(uint8_t aCtxType) const = 0; +}; + +class UserSpaceMetricsWithSize : public UserSpaceMetrics +{ +public: + virtual gfx::Size GetSize() const = 0; + virtual float GetAxisLength(uint8_t aCtxType) const override; +}; + +class SVGElementMetrics : public UserSpaceMetrics +{ +public: + explicit SVGElementMetrics(nsSVGElement* aSVGElement, + mozilla::dom::SVGSVGElement* aCtx = nullptr); + + virtual float GetEmLength() const override; + virtual float GetExLength() const override; + virtual float GetAxisLength(uint8_t aCtxType) const override; + +private: + bool EnsureCtx() const; + + nsSVGElement* mSVGElement; + mutable mozilla::dom::SVGSVGElement* mCtx; +}; + +class NonSVGFrameUserSpaceMetrics : public UserSpaceMetricsWithSize +{ +public: + explicit NonSVGFrameUserSpaceMetrics(nsIFrame* aFrame); + + virtual float GetEmLength() const override; + virtual float GetExLength() const override; + virtual gfx::Size GetSize() const override; + +private: + nsIFrame* mFrame; +}; + +} // namespace dom +} // namespace mozilla + +class nsSVGLength2 +{ + friend class mozilla::dom::SVGAnimatedLength; + friend class mozilla::DOMSVGLength; + typedef mozilla::dom::UserSpaceMetrics UserSpaceMetrics; +public: + void Init(uint8_t aCtxType = SVGContentUtils::XY, + uint8_t aAttrEnum = 0xff, + float aValue = 0, + uint8_t aUnitType = nsIDOMSVGLength::SVG_LENGTHTYPE_NUMBER) { + mAnimVal = mBaseVal = aValue; + mSpecifiedUnitType = aUnitType; + mAttrEnum = aAttrEnum; + mCtxType = aCtxType; + mIsAnimated = false; + mIsBaseSet = false; + } + + nsSVGLength2& operator=(const nsSVGLength2& aLength) { + mBaseVal = aLength.mBaseVal; + mAnimVal = aLength.mAnimVal; + mSpecifiedUnitType = aLength.mSpecifiedUnitType; + mIsAnimated = aLength.mIsAnimated; + mIsBaseSet = aLength.mIsBaseSet; + return *this; + } + + nsresult SetBaseValueString(const nsAString& aValue, + nsSVGElement *aSVGElement, + bool aDoSetAttr); + void GetBaseValueString(nsAString& aValue) const; + void GetAnimValueString(nsAString& aValue) const; + + float GetBaseValue(nsSVGElement* aSVGElement) const + { return mBaseVal / GetUnitScaleFactor(aSVGElement, mSpecifiedUnitType); } + + float GetAnimValue(nsSVGElement* aSVGElement) const + { return mAnimVal / GetUnitScaleFactor(aSVGElement, mSpecifiedUnitType); } + float GetAnimValue(nsIFrame* aFrame) const + { return mAnimVal / GetUnitScaleFactor(aFrame, mSpecifiedUnitType); } + float GetAnimValue(mozilla::dom::SVGSVGElement* aCtx) const + { return mAnimVal / GetUnitScaleFactor(aCtx, mSpecifiedUnitType); } + float GetAnimValue(const UserSpaceMetrics& aMetrics) const + { return mAnimVal / GetUnitScaleFactor(aMetrics, mSpecifiedUnitType); } + + uint8_t GetCtxType() const { return mCtxType; } + uint8_t GetSpecifiedUnitType() const { return mSpecifiedUnitType; } + bool IsPercentage() const + { return mSpecifiedUnitType == nsIDOMSVGLength::SVG_LENGTHTYPE_PERCENTAGE; } + float GetAnimValInSpecifiedUnits() const { return mAnimVal; } + float GetBaseValInSpecifiedUnits() const { return mBaseVal; } + + float GetBaseValue(mozilla::dom::SVGSVGElement* aCtx) const + { return mBaseVal / GetUnitScaleFactor(aCtx, mSpecifiedUnitType); } + + bool HasBaseVal() const { + return mIsBaseSet; + } + // Returns true if the animated value of this length has been explicitly + // set (either by animation, or by taking on the base value which has been + // explicitly set by markup or a DOM call), false otherwise. + // If this returns false, the animated value is still valid, that is, + // useable, and represents the default base value of the attribute. + bool IsExplicitlySet() const + { return mIsAnimated || mIsBaseSet; } + + already_AddRefed<mozilla::dom::SVGAnimatedLength> + ToDOMAnimatedLength(nsSVGElement* aSVGElement); + + // Returns a new nsISMILAttr object that the caller must delete + nsISMILAttr* ToSMILAttr(nsSVGElement* aSVGElement); + +private: + + float mAnimVal; + float mBaseVal; + uint8_t mSpecifiedUnitType; + uint8_t mAttrEnum; // element specified tracking for attribute + uint8_t mCtxType; // X, Y or Unspecified + bool mIsAnimated:1; + bool mIsBaseSet:1; + + float GetUnitScaleFactor(nsIFrame *aFrame, uint8_t aUnitType) const; + float GetUnitScaleFactor(const UserSpaceMetrics& aMetrics, uint8_t aUnitType) const; + float GetUnitScaleFactor(nsSVGElement *aSVGElement, uint8_t aUnitType) const; + float GetUnitScaleFactor(mozilla::dom::SVGSVGElement *aCtx, uint8_t aUnitType) const; + + // SetBaseValue and SetAnimValue set the value in user units + void SetBaseValue(float aValue, nsSVGElement *aSVGElement, bool aDoSetAttr); + void SetBaseValueInSpecifiedUnits(float aValue, nsSVGElement *aSVGElement, + bool aDoSetAttr); + void SetAnimValue(float aValue, nsSVGElement *aSVGElement); + void SetAnimValueInSpecifiedUnits(float aValue, nsSVGElement *aSVGElement); + nsresult NewValueSpecifiedUnits(uint16_t aUnitType, float aValue, + nsSVGElement *aSVGElement); + nsresult ConvertToSpecifiedUnits(uint16_t aUnitType, nsSVGElement *aSVGElement); + nsresult ToDOMBaseVal(mozilla::DOMSVGLength **aResult, nsSVGElement* aSVGElement); + nsresult ToDOMAnimVal(mozilla::DOMSVGLength **aResult, nsSVGElement* aSVGElement); + +public: + struct SMILLength : public nsISMILAttr + { + public: + SMILLength(nsSVGLength2* aVal, nsSVGElement *aSVGElement) + : mVal(aVal), mSVGElement(aSVGElement) {} + + // 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. + nsSVGLength2* mVal; + nsSVGElement* mSVGElement; + + // nsISMILAttr methods + virtual nsresult ValueFromString(const nsAString& aStr, + const mozilla::dom::SVGAnimationElement* aSrcElement, + nsSMILValue &aValue, + bool& aPreventCachingOfSandwich) const override; + virtual nsSMILValue GetBaseValue() const override; + virtual void ClearAnimValue() override; + virtual nsresult SetAnimValue(const nsSMILValue& aValue) override; + }; +}; + +#endif // __NS_SVGLENGTH2_H__ diff --git a/dom/svg/nsSVGNumber2.cpp b/dom/svg/nsSVGNumber2.cpp new file mode 100644 index 0000000000..ece721ee0c --- /dev/null +++ b/dom/svg/nsSVGNumber2.cpp @@ -0,0 +1,184 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "nsSVGNumber2.h" +#include "mozilla/Attributes.h" +#include "nsContentUtils.h" // NS_ENSURE_FINITE +#include "nsSMILFloatType.h" +#include "nsSMILValue.h" +#include "nsSVGAttrTearoffTable.h" +#include "SVGContentUtils.h" + +using namespace mozilla; +using namespace mozilla::dom; + +/* Implementation */ + +static nsSVGAttrTearoffTable<nsSVGNumber2, nsSVGNumber2::DOMAnimatedNumber> + sSVGAnimatedNumberTearoffTable; + +static bool +GetValueFromString(const nsAString& aString, + bool aPercentagesAllowed, + float& aValue) +{ + RangedPtr<const char16_t> iter = + SVGContentUtils::GetStartRangedPtr(aString); + const RangedPtr<const char16_t> end = + SVGContentUtils::GetEndRangedPtr(aString); + + if (!SVGContentUtils::ParseNumber(iter, end, aValue)) { + return false; + } + + if (aPercentagesAllowed) { + const nsAString& units = Substring(iter.get(), end.get()); + if (units.EqualsLiteral("%")) { + aValue /= 100; + return true; + } + } + + return iter == end; +} + +nsresult +nsSVGNumber2::SetBaseValueString(const nsAString &aValueAsString, + nsSVGElement *aSVGElement) +{ + float val; + + if (!GetValueFromString(aValueAsString, + aSVGElement->NumberAttrAllowsPercentage(mAttrEnum), + val)) { + return NS_ERROR_DOM_SYNTAX_ERR; + } + + mBaseVal = val; + mIsBaseSet = true; + if (!mIsAnimated) { + mAnimVal = mBaseVal; + } + else { + aSVGElement->AnimationNeedsResample(); + } + + // We don't need to call DidChange* here - we're only called by + // nsSVGElement::ParseAttribute under Element::SetAttr, + // which takes care of notifying. + return NS_OK; +} + +void +nsSVGNumber2::GetBaseValueString(nsAString & aValueAsString) +{ + aValueAsString.Truncate(); + aValueAsString.AppendFloat(mBaseVal); +} + +void +nsSVGNumber2::SetBaseValue(float aValue, nsSVGElement *aSVGElement) +{ + if (mIsBaseSet && aValue == mBaseVal) { + return; + } + + mBaseVal = aValue; + mIsBaseSet = true; + if (!mIsAnimated) { + mAnimVal = mBaseVal; + } + else { + aSVGElement->AnimationNeedsResample(); + } + aSVGElement->DidChangeNumber(mAttrEnum); +} + +void +nsSVGNumber2::SetAnimValue(float aValue, nsSVGElement *aSVGElement) +{ + if (mIsAnimated && aValue == mAnimVal) { + return; + } + mAnimVal = aValue; + mIsAnimated = true; + aSVGElement->DidAnimateNumber(mAttrEnum); +} + +already_AddRefed<SVGAnimatedNumber> +nsSVGNumber2::ToDOMAnimatedNumber(nsSVGElement* aSVGElement) +{ + RefPtr<DOMAnimatedNumber> domAnimatedNumber = + sSVGAnimatedNumberTearoffTable.GetTearoff(this); + if (!domAnimatedNumber) { + domAnimatedNumber = new DOMAnimatedNumber(this, aSVGElement); + sSVGAnimatedNumberTearoffTable.AddTearoff(this, domAnimatedNumber); + } + + return domAnimatedNumber.forget(); +} + +nsSVGNumber2::DOMAnimatedNumber::~DOMAnimatedNumber() +{ + sSVGAnimatedNumberTearoffTable.RemoveTearoff(mVal); +} + +nsISMILAttr* +nsSVGNumber2::ToSMILAttr(nsSVGElement *aSVGElement) +{ + return new SMILNumber(this, aSVGElement); +} + +nsresult +nsSVGNumber2::SMILNumber::ValueFromString(const nsAString& aStr, + const mozilla::dom::SVGAnimationElement* /*aSrcElement*/, + nsSMILValue& aValue, + bool& aPreventCachingOfSandwich) const +{ + float value; + + if (!GetValueFromString(aStr, + mSVGElement->NumberAttrAllowsPercentage(mVal->mAttrEnum), + value)) { + return NS_ERROR_DOM_SYNTAX_ERR; + } + + nsSMILValue val(nsSMILFloatType::Singleton()); + val.mU.mDouble = value; + aValue = val; + aPreventCachingOfSandwich = false; + + return NS_OK; +} + +nsSMILValue +nsSVGNumber2::SMILNumber::GetBaseValue() const +{ + nsSMILValue val(nsSMILFloatType::Singleton()); + val.mU.mDouble = mVal->mBaseVal; + return val; +} + +void +nsSVGNumber2::SMILNumber::ClearAnimValue() +{ + if (mVal->mIsAnimated) { + mVal->mIsAnimated = false; + mVal->mAnimVal = mVal->mBaseVal; + mSVGElement->DidAnimateNumber(mVal->mAttrEnum); + } +} + +nsresult +nsSVGNumber2::SMILNumber::SetAnimValue(const nsSMILValue& aValue) +{ + NS_ASSERTION(aValue.mType == nsSMILFloatType::Singleton(), + "Unexpected type to assign animated value"); + if (aValue.mType == nsSMILFloatType::Singleton()) { + mVal->SetAnimValue(float(aValue.mU.mDouble), mSVGElement); + } + return NS_OK; +} diff --git a/dom/svg/nsSVGNumber2.h b/dom/svg/nsSVGNumber2.h new file mode 100644 index 0000000000..7a39a56997 --- /dev/null +++ b/dom/svg/nsSVGNumber2.h @@ -0,0 +1,123 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef __NS_SVGNUMBER2_H__ +#define __NS_SVGNUMBER2_H__ + +#include "nsCycleCollectionParticipant.h" +#include "nsError.h" +#include "nsISMILAttr.h" +#include "nsMathUtils.h" +#include "nsSVGElement.h" +#include "mozilla/Attributes.h" +#include "mozilla/FloatingPoint.h" +#include "mozilla/dom/SVGAnimatedNumber.h" + +class nsSMILValue; + +namespace mozilla { +namespace dom { +class SVGAnimationElement; +} // namespace dom +} // namespace mozilla + +class nsSVGNumber2 +{ + +public: + void Init(uint8_t aAttrEnum = 0xff, float aValue = 0) { + mAnimVal = mBaseVal = aValue; + mAttrEnum = aAttrEnum; + mIsAnimated = false; + mIsBaseSet = false; + } + + nsresult SetBaseValueString(const nsAString& aValue, + nsSVGElement *aSVGElement); + void GetBaseValueString(nsAString& aValue); + + void SetBaseValue(float aValue, nsSVGElement *aSVGElement); + float GetBaseValue() const + { return mBaseVal; } + void SetAnimValue(float aValue, nsSVGElement *aSVGElement); + float GetAnimValue() const + { return mAnimVal; } + + // Returns true if the animated value of this number has been explicitly + // set (either by animation, or by taking on the base value which has been + // explicitly set by markup or a DOM call), false otherwise. + // If this returns false, the animated value is still valid, that is, + // useable, and represents the default base value of the attribute. + bool IsExplicitlySet() const + { return mIsAnimated || mIsBaseSet; } + + already_AddRefed<mozilla::dom::SVGAnimatedNumber> + ToDOMAnimatedNumber(nsSVGElement* aSVGElement); + // Returns a new nsISMILAttr object that the caller must delete + nsISMILAttr* ToSMILAttr(nsSVGElement* aSVGElement); + +private: + + float mAnimVal; + float mBaseVal; + uint8_t mAttrEnum; // element specified tracking for attribute + bool mIsAnimated; + bool mIsBaseSet; + +public: + struct DOMAnimatedNumber final : public mozilla::dom::SVGAnimatedNumber + { + DOMAnimatedNumber(nsSVGNumber2* aVal, nsSVGElement* aSVGElement) + : mozilla::dom::SVGAnimatedNumber(aSVGElement) + , mVal(aVal) + {} + virtual ~DOMAnimatedNumber(); + + nsSVGNumber2* mVal; // kept alive because it belongs to content + + virtual float BaseVal() override + { + return mVal->GetBaseValue(); + } + virtual void SetBaseVal(float aValue) override + { + MOZ_ASSERT(mozilla::IsFinite(aValue)); + mVal->SetBaseValue(aValue, mSVGElement); + } + + // Script may have modified animation parameters or timeline -- DOM getters + // need to flush any resample requests to reflect these modifications. + virtual float AnimVal() override + { + mSVGElement->FlushAnimations(); + return mVal->GetAnimValue(); + } + }; + + struct SMILNumber : public nsISMILAttr + { + public: + SMILNumber(nsSVGNumber2* aVal, nsSVGElement* aSVGElement) + : mVal(aVal), mSVGElement(aSVGElement) {} + + // 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. + nsSVGNumber2* mVal; + nsSVGElement* mSVGElement; + + // nsISMILAttr methods + virtual nsresult ValueFromString(const nsAString& aStr, + const mozilla::dom::SVGAnimationElement* aSrcElement, + nsSMILValue& aValue, + bool& aPreventCachingOfSandwich) const override; + virtual nsSMILValue GetBaseValue() const override; + virtual void ClearAnimValue() override; + virtual nsresult SetAnimValue(const nsSMILValue& aValue) override; + }; +}; + +#endif //__NS_SVGNUMBER2_H__ diff --git a/dom/svg/nsSVGNumberPair.cpp b/dom/svg/nsSVGNumberPair.cpp new file mode 100644 index 0000000000..fd1532546c --- /dev/null +++ b/dom/svg/nsSVGNumberPair.cpp @@ -0,0 +1,230 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "nsSVGNumberPair.h" +#include "nsSVGAttrTearoffTable.h" +#include "nsCharSeparatedTokenizer.h" +#include "nsSMILValue.h" +#include "SVGContentUtils.h" +#include "SVGNumberPairSMILType.h" + +using namespace mozilla; +using namespace mozilla::dom; + +static nsSVGAttrTearoffTable<nsSVGNumberPair, nsSVGNumberPair::DOMAnimatedNumber> + sSVGFirstAnimatedNumberTearoffTable; +static nsSVGAttrTearoffTable<nsSVGNumberPair, nsSVGNumberPair::DOMAnimatedNumber> + sSVGSecondAnimatedNumberTearoffTable; + +static nsresult +ParseNumberOptionalNumber(const nsAString& aValue, + float aValues[2]) +{ + nsCharSeparatedTokenizerTemplate<IsSVGWhitespace> + tokenizer(aValue, ',', + nsCharSeparatedTokenizer::SEPARATOR_OPTIONAL); + if (tokenizer.whitespaceBeforeFirstToken()) { + return NS_ERROR_DOM_SYNTAX_ERR; + } + + uint32_t i; + for (i = 0; i < 2 && tokenizer.hasMoreTokens(); ++i) { + if (!SVGContentUtils::ParseNumber(tokenizer.nextToken(), aValues[i])) { + return NS_ERROR_DOM_SYNTAX_ERR; + } + } + if (i == 1) { + aValues[1] = aValues[0]; + } + + if (i == 0 || // Too few values. + tokenizer.hasMoreTokens() || // Too many values. + tokenizer.whitespaceAfterCurrentToken() || // Trailing whitespace. + tokenizer.separatorAfterCurrentToken()) { // Trailing comma. + return NS_ERROR_DOM_SYNTAX_ERR; + } + + return NS_OK; +} + +nsresult +nsSVGNumberPair::SetBaseValueString(const nsAString &aValueAsString, + nsSVGElement *aSVGElement) +{ + float val[2]; + + nsresult rv = ParseNumberOptionalNumber(aValueAsString, val); + if (NS_FAILED(rv)) { + return rv; + } + + mBaseVal[0] = val[0]; + mBaseVal[1] = val[1]; + mIsBaseSet = true; + if (!mIsAnimated) { + mAnimVal[0] = mBaseVal[0]; + mAnimVal[1] = mBaseVal[1]; + } + else { + aSVGElement->AnimationNeedsResample(); + } + + // We don't need to call Will/DidChange* here - we're only called by + // nsSVGElement::ParseAttribute under Element::SetAttr, + // which takes care of notifying. + return NS_OK; +} + +void +nsSVGNumberPair::GetBaseValueString(nsAString &aValueAsString) const +{ + aValueAsString.Truncate(); + aValueAsString.AppendFloat(mBaseVal[0]); + if (mBaseVal[0] != mBaseVal[1]) { + aValueAsString.AppendLiteral(", "); + aValueAsString.AppendFloat(mBaseVal[1]); + } +} + +void +nsSVGNumberPair::SetBaseValue(float aValue, PairIndex aPairIndex, + nsSVGElement *aSVGElement) +{ + uint32_t index = (aPairIndex == eFirst ? 0 : 1); + if (mIsBaseSet && mBaseVal[index] == aValue) { + return; + } + nsAttrValue emptyOrOldValue = aSVGElement->WillChangeNumberPair(mAttrEnum); + mBaseVal[index] = aValue; + mIsBaseSet = true; + if (!mIsAnimated) { + mAnimVal[index] = aValue; + } + else { + aSVGElement->AnimationNeedsResample(); + } + aSVGElement->DidChangeNumberPair(mAttrEnum, emptyOrOldValue); +} + +void +nsSVGNumberPair::SetBaseValues(float aValue1, float aValue2, + nsSVGElement *aSVGElement) +{ + if (mIsBaseSet && mBaseVal[0] == aValue1 && mBaseVal[1] == aValue2) { + return; + } + nsAttrValue emptyOrOldValue = aSVGElement->WillChangeNumberPair(mAttrEnum); + mBaseVal[0] = aValue1; + mBaseVal[1] = aValue2; + mIsBaseSet = true; + if (!mIsAnimated) { + mAnimVal[0] = aValue1; + mAnimVal[1] = aValue2; + } + else { + aSVGElement->AnimationNeedsResample(); + } + aSVGElement->DidChangeNumberPair(mAttrEnum, emptyOrOldValue); +} + +void +nsSVGNumberPair::SetAnimValue(const float aValue[2], nsSVGElement *aSVGElement) +{ + if (mIsAnimated && mAnimVal[0] == aValue[0] && mAnimVal[1] == aValue[1]) { + return; + } + mAnimVal[0] = aValue[0]; + mAnimVal[1] = aValue[1]; + mIsAnimated = true; + aSVGElement->DidAnimateNumberPair(mAttrEnum); +} + +already_AddRefed<SVGAnimatedNumber> +nsSVGNumberPair::ToDOMAnimatedNumber(PairIndex aIndex, + nsSVGElement* aSVGElement) +{ + RefPtr<DOMAnimatedNumber> domAnimatedNumber = + aIndex == eFirst ? sSVGFirstAnimatedNumberTearoffTable.GetTearoff(this) : + sSVGSecondAnimatedNumberTearoffTable.GetTearoff(this); + if (!domAnimatedNumber) { + domAnimatedNumber = new DOMAnimatedNumber(this, aIndex, aSVGElement); + if (aIndex == eFirst) { + sSVGFirstAnimatedNumberTearoffTable.AddTearoff(this, domAnimatedNumber); + } else { + sSVGSecondAnimatedNumberTearoffTable.AddTearoff(this, domAnimatedNumber); + } + } + + return domAnimatedNumber.forget(); +} + +nsSVGNumberPair::DOMAnimatedNumber::~DOMAnimatedNumber() +{ + if (mIndex == eFirst) { + sSVGFirstAnimatedNumberTearoffTable.RemoveTearoff(mVal); + } else { + sSVGSecondAnimatedNumberTearoffTable.RemoveTearoff(mVal); + } +} + +nsISMILAttr* +nsSVGNumberPair::ToSMILAttr(nsSVGElement *aSVGElement) +{ + return new SMILNumberPair(this, aSVGElement); +} + +nsresult +nsSVGNumberPair::SMILNumberPair::ValueFromString(const nsAString& aStr, + const dom::SVGAnimationElement* /*aSrcElement*/, + nsSMILValue& aValue, + bool& aPreventCachingOfSandwich) const +{ + float values[2]; + + nsresult rv = ParseNumberOptionalNumber(aStr, values); + if (NS_FAILED(rv)) { + return rv; + } + + nsSMILValue val(&SVGNumberPairSMILType::sSingleton); + val.mU.mNumberPair[0] = values[0]; + val.mU.mNumberPair[1] = values[1]; + aValue = val; + aPreventCachingOfSandwich = false; + + return NS_OK; +} + +nsSMILValue +nsSVGNumberPair::SMILNumberPair::GetBaseValue() const +{ + nsSMILValue val(&SVGNumberPairSMILType::sSingleton); + val.mU.mNumberPair[0] = mVal->mBaseVal[0]; + val.mU.mNumberPair[1] = mVal->mBaseVal[1]; + return val; +} + +void +nsSVGNumberPair::SMILNumberPair::ClearAnimValue() +{ + if (mVal->mIsAnimated) { + mVal->mIsAnimated = false; + mVal->mAnimVal[0] = mVal->mBaseVal[0]; + mVal->mAnimVal[1] = mVal->mBaseVal[1]; + mSVGElement->DidAnimateNumberPair(mVal->mAttrEnum); + } +} + +nsresult +nsSVGNumberPair::SMILNumberPair::SetAnimValue(const nsSMILValue& aValue) +{ + NS_ASSERTION(aValue.mType == &SVGNumberPairSMILType::sSingleton, + "Unexpected type to assign animated value"); + if (aValue.mType == &SVGNumberPairSMILType::sSingleton) { + mVal->SetAnimValue(aValue.mU.mNumberPair, mSVGElement); + } + return NS_OK; +} diff --git a/dom/svg/nsSVGNumberPair.h b/dom/svg/nsSVGNumberPair.h new file mode 100644 index 0000000000..54e28b486d --- /dev/null +++ b/dom/svg/nsSVGNumberPair.h @@ -0,0 +1,134 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef __NS_SVGNUMBERPAIR_H__ +#define __NS_SVGNUMBERPAIR_H__ + +#include "nsCycleCollectionParticipant.h" +#include "nsError.h" +#include "nsISMILAttr.h" +#include "nsMathUtils.h" +#include "nsSVGElement.h" +#include "mozilla/Attributes.h" +#include "mozilla/dom/SVGAnimatedNumber.h" +#include "mozilla/FloatingPoint.h" + + +class nsSMILValue; + +namespace mozilla { +namespace dom { +class SVGAnimationElement; +} // namespace dom +} // namespace mozilla + +class nsSVGNumberPair +{ + +public: + enum PairIndex { + eFirst, + eSecond + }; + + void Init(uint8_t aAttrEnum = 0xff, float aValue1 = 0, float aValue2 = 0) { + mAnimVal[0] = mBaseVal[0] = aValue1; + mAnimVal[1] = mBaseVal[1] = aValue2; + mAttrEnum = aAttrEnum; + mIsAnimated = false; + mIsBaseSet = false; + } + + nsresult SetBaseValueString(const nsAString& aValue, + nsSVGElement *aSVGElement); + void GetBaseValueString(nsAString& aValue) const; + + void SetBaseValue(float aValue, PairIndex aIndex, nsSVGElement *aSVGElement); + void SetBaseValues(float aValue1, float aValue2, nsSVGElement *aSVGElement); + float GetBaseValue(PairIndex aIndex) const + { return mBaseVal[aIndex == eFirst ? 0 : 1]; } + void SetAnimValue(const float aValue[2], nsSVGElement *aSVGElement); + float GetAnimValue(PairIndex aIndex) const + { return mAnimVal[aIndex == eFirst ? 0 : 1]; } + + // Returns true if the animated value of this number has been explicitly + // set (either by animation, or by taking on the base value which has been + // explicitly set by markup or a DOM call), false otherwise. + // If this returns false, the animated value is still valid, that is, + // useable, and represents the default base value of the attribute. + bool IsExplicitlySet() const + { return mIsAnimated || mIsBaseSet; } + + already_AddRefed<mozilla::dom::SVGAnimatedNumber> + ToDOMAnimatedNumber(PairIndex aIndex, + nsSVGElement* aSVGElement); + // Returns a new nsISMILAttr object that the caller must delete + nsISMILAttr* ToSMILAttr(nsSVGElement* aSVGElement); + +private: + + float mAnimVal[2]; + float mBaseVal[2]; + uint8_t mAttrEnum; // element specified tracking for attribute + bool mIsAnimated; + bool mIsBaseSet; + +public: + struct DOMAnimatedNumber final : public mozilla::dom::SVGAnimatedNumber + { + DOMAnimatedNumber(nsSVGNumberPair* aVal, PairIndex aIndex, nsSVGElement *aSVGElement) + : mozilla::dom::SVGAnimatedNumber(aSVGElement) + , mVal(aVal) + , mIndex(aIndex) + {} + virtual ~DOMAnimatedNumber(); + + nsSVGNumberPair* mVal; // kept alive because it belongs to content + PairIndex mIndex; // are we the first or second number + + virtual float BaseVal() override + { + return mVal->GetBaseValue(mIndex); + } + virtual void SetBaseVal(float aValue) override + { + MOZ_ASSERT(mozilla::IsFinite(aValue)); + mVal->SetBaseValue(aValue, mIndex, mSVGElement); + } + + // Script may have modified animation parameters or timeline -- DOM getters + // need to flush any resample requests to reflect these modifications. + virtual float AnimVal() override + { + mSVGElement->FlushAnimations(); + return mVal->GetAnimValue(mIndex); + } + }; + + struct SMILNumberPair : public nsISMILAttr + { + public: + SMILNumberPair(nsSVGNumberPair* aVal, nsSVGElement* aSVGElement) + : mVal(aVal), mSVGElement(aSVGElement) {} + + // 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. + nsSVGNumberPair* mVal; + nsSVGElement* mSVGElement; + + // nsISMILAttr methods + virtual nsresult ValueFromString(const nsAString& aStr, + const mozilla::dom::SVGAnimationElement* aSrcElement, + nsSMILValue& aValue, + bool& aPreventCachingOfSandwich) const override; + virtual nsSMILValue GetBaseValue() const override; + virtual void ClearAnimValue() override; + virtual nsresult SetAnimValue(const nsSMILValue& aValue) override; + }; +}; + +#endif //__NS_SVGNUMBERPAIR_H__ diff --git a/dom/svg/nsSVGPathDataParser.cpp b/dom/svg/nsSVGPathDataParser.cpp new file mode 100644 index 0000000000..8ab5268201 --- /dev/null +++ b/dom/svg/nsSVGPathDataParser.cpp @@ -0,0 +1,516 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "nsSVGPathDataParser.h" + +#include "mozilla/gfx/Point.h" +#include "nsSVGDataParser.h" +#include "SVGContentUtils.h" +#include "SVGPathData.h" +#include "SVGPathSegUtils.h" + +using namespace mozilla; +using namespace mozilla::gfx; + +static inline char16_t ToUpper(char16_t aCh) +{ + return aCh >= 'a' && aCh <= 'z' ? aCh - 'a' + 'A' : aCh; +} + +bool +nsSVGPathDataParser::Parse() +{ + mPathSegList->Clear(); + return ParsePath(); +} + +//---------------------------------------------------------------------- + +bool +nsSVGPathDataParser::ParseCoordPair(float& aX, float& aY) +{ + return SVGContentUtils::ParseNumber(mIter, mEnd, aX) && + SkipCommaWsp() && + SVGContentUtils::ParseNumber(mIter, mEnd, aY); +} + +bool +nsSVGPathDataParser::ParseFlag(bool& aFlag) +{ + if (mIter == mEnd || (*mIter != '0' && *mIter != '1')) { + return false; + } + aFlag = (*mIter == '1'); + + ++mIter; + return true; +} + +//---------------------------------------------------------------------- + +bool +nsSVGPathDataParser::ParsePath() +{ + while (SkipWsp()) { + if (!ParseSubPath()) { + return false; + } + } + + return true; +} + +//---------------------------------------------------------------------- + +bool +nsSVGPathDataParser::ParseSubPath() +{ + return ParseMoveto() && ParseSubPathElements(); +} + +bool +nsSVGPathDataParser::ParseSubPathElements() +{ + while (SkipWsp() && !IsStartOfSubPath()) { + char16_t commandType = ToUpper(*mIter); + + // Upper case commands have absolute co-ordinates, + // lower case commands have relative co-ordinates. + bool absCoords = commandType == *mIter; + + ++mIter; + SkipWsp(); + + if (!ParseSubPathElement(commandType, absCoords)) { + return false; + } + } + return true; +} + +bool +nsSVGPathDataParser::ParseSubPathElement(char16_t aCommandType, + bool aAbsCoords) +{ + switch (aCommandType) { + case 'Z': + return ParseClosePath(); + case 'L': + return ParseLineto(aAbsCoords); + case 'H': + return ParseHorizontalLineto(aAbsCoords); + case 'V': + return ParseVerticalLineto(aAbsCoords); + case 'C': + return ParseCurveto(aAbsCoords); + case 'S': + return ParseSmoothCurveto(aAbsCoords); + case 'Q': + return ParseQuadBezierCurveto(aAbsCoords); + case 'T': + return ParseSmoothQuadBezierCurveto(aAbsCoords); + case 'A': + return ParseEllipticalArc(aAbsCoords); + } + return false; +} + +bool +nsSVGPathDataParser::IsStartOfSubPath() const +{ + return *mIter == 'm' || *mIter == 'M'; +} + +//---------------------------------------------------------------------- + +bool +nsSVGPathDataParser::ParseMoveto() +{ + if (!IsStartOfSubPath()) { + return false; + } + + bool absCoords = (*mIter == 'M'); + + ++mIter; + SkipWsp(); + + float x, y; + if (!ParseCoordPair(x, y)) { + return false; + } + + if (NS_FAILED(mPathSegList->AppendSeg( + absCoords ? PATHSEG_MOVETO_ABS : PATHSEG_MOVETO_REL, + x, y))) { + return false; + } + + if (!SkipWsp() || IsAlpha(*mIter)) { + // End of data, or start of a new command + return true; + } + + SkipCommaWsp(); + + // Per SVG 1.1 Section 8.3.2 + // If a moveto is followed by multiple pairs of coordinates, + // the subsequent pairs are treated as implicit lineto commands + return ParseLineto(absCoords); +} + +//---------------------------------------------------------------------- + +bool +nsSVGPathDataParser::ParseClosePath() +{ + return NS_SUCCEEDED(mPathSegList->AppendSeg(PATHSEG_CLOSEPATH)); +} + +//---------------------------------------------------------------------- + +bool +nsSVGPathDataParser::ParseLineto(bool aAbsCoords) +{ + while (true) { + float x, y; + if (!ParseCoordPair(x, y)) { + return false; + } + + if (NS_FAILED(mPathSegList->AppendSeg( + aAbsCoords ? PATHSEG_LINETO_ABS : PATHSEG_LINETO_REL, + x, y))) { + return false; + } + + if (!SkipWsp() || IsAlpha(*mIter)) { + // End of data, or start of a new command + return true; + } + SkipCommaWsp(); + } +} + +//---------------------------------------------------------------------- + +bool +nsSVGPathDataParser::ParseHorizontalLineto(bool aAbsCoords) +{ + while (true) { + float x; + if (!SVGContentUtils::ParseNumber(mIter, mEnd, x)) { + return false; + } + + if (NS_FAILED(mPathSegList->AppendSeg( + aAbsCoords ? PATHSEG_LINETO_HORIZONTAL_ABS : PATHSEG_LINETO_HORIZONTAL_REL, + x))) { + return false; + } + + if (!SkipWsp() || IsAlpha(*mIter)) { + // End of data, or start of a new command + return true; + } + SkipCommaWsp(); + } +} + +//---------------------------------------------------------------------- + +bool +nsSVGPathDataParser::ParseVerticalLineto(bool aAbsCoords) +{ + while (true) { + float y; + if (!SVGContentUtils::ParseNumber(mIter, mEnd, y)) { + return false; + } + + if (NS_FAILED(mPathSegList->AppendSeg( + aAbsCoords ? PATHSEG_LINETO_VERTICAL_ABS : PATHSEG_LINETO_VERTICAL_REL, + y))) { + return false; + } + + if (!SkipWsp() || IsAlpha(*mIter)) { + // End of data, or start of a new command + return true; + } + SkipCommaWsp(); + } +} + +//---------------------------------------------------------------------- + +bool +nsSVGPathDataParser::ParseCurveto(bool aAbsCoords) +{ + while (true) { + float x1, y1, x2, y2, x, y; + + if (!(ParseCoordPair(x1, y1) && + SkipCommaWsp() && + ParseCoordPair(x2, y2) && + SkipCommaWsp() && + ParseCoordPair(x, y))) { + return false; + } + + if (NS_FAILED(mPathSegList->AppendSeg( + aAbsCoords ? PATHSEG_CURVETO_CUBIC_ABS : PATHSEG_CURVETO_CUBIC_REL, + x1, y1, x2, y2, x, y))) { + return false; + } + + if (!SkipWsp() || IsAlpha(*mIter)) { + // End of data, or start of a new command + return true; + } + SkipCommaWsp(); + } +} + +//---------------------------------------------------------------------- + +bool +nsSVGPathDataParser::ParseSmoothCurveto(bool aAbsCoords) +{ + while (true) { + float x2, y2, x, y; + if (!(ParseCoordPair(x2, y2) && + SkipCommaWsp() && + ParseCoordPair(x, y))) { + return false; + } + + if (NS_FAILED(mPathSegList->AppendSeg( + aAbsCoords ? PATHSEG_CURVETO_CUBIC_SMOOTH_ABS : PATHSEG_CURVETO_CUBIC_SMOOTH_REL, + x2, y2, x, y))) { + return false; + } + + if (!SkipWsp() || IsAlpha(*mIter)) { + // End of data, or start of a new command + return true; + } + SkipCommaWsp(); + } +} + +//---------------------------------------------------------------------- + +bool +nsSVGPathDataParser::ParseQuadBezierCurveto(bool aAbsCoords) +{ + while (true) { + float x1, y1, x, y; + if (!(ParseCoordPair(x1, y1) && + SkipCommaWsp() && + ParseCoordPair(x, y))) { + return false; + } + + if (NS_FAILED(mPathSegList->AppendSeg( + aAbsCoords ? PATHSEG_CURVETO_QUADRATIC_ABS : PATHSEG_CURVETO_QUADRATIC_REL, + x1, y1, x, y))) { + return false; + } + + if (!SkipWsp() || IsAlpha(*mIter)) { + // Start of a new command + return true; + } + SkipCommaWsp(); + } +} + +//---------------------------------------------------------------------- + +bool +nsSVGPathDataParser::ParseSmoothQuadBezierCurveto(bool aAbsCoords) +{ + while (true) { + float x, y; + if (!ParseCoordPair(x, y)) { + return false; + } + + if (NS_FAILED(mPathSegList->AppendSeg( + aAbsCoords ? PATHSEG_CURVETO_QUADRATIC_SMOOTH_ABS : PATHSEG_CURVETO_QUADRATIC_SMOOTH_REL, + x, y))) { + return false; + } + + if (!SkipWsp() || IsAlpha(*mIter)) { + // End of data, or start of a new command + return true; + } + SkipCommaWsp(); + } +} + +//---------------------------------------------------------------------- + +bool +nsSVGPathDataParser::ParseEllipticalArc(bool aAbsCoords) +{ + while (true) { + float r1, r2, angle, x, y; + bool largeArcFlag, sweepFlag; + + if (!(SVGContentUtils::ParseNumber(mIter, mEnd, r1) && + SkipCommaWsp() && + SVGContentUtils::ParseNumber(mIter, mEnd, r2) && + SkipCommaWsp() && + SVGContentUtils::ParseNumber(mIter, mEnd, angle)&& + SkipCommaWsp() && + ParseFlag(largeArcFlag) && + SkipCommaWsp() && + ParseFlag(sweepFlag) && + SkipCommaWsp() && + ParseCoordPair(x, y))) { + return false; + } + + // We can only pass floats after 'type', and per the SVG spec for arc, + // non-zero args are treated at 'true'. + if (NS_FAILED(mPathSegList->AppendSeg( + aAbsCoords ? PATHSEG_ARC_ABS : PATHSEG_ARC_REL, + r1, r2, angle, + largeArcFlag ? 1.0f : 0.0f, + sweepFlag ? 1.0f : 0.0f, + x, y))) { + return false; + } + + if (!SkipWsp() || IsAlpha(*mIter)) { + // End of data, or start of a new command + return true; + } + SkipCommaWsp(); + } +} + +//----------------------------------------------------------------------- + + + + +static double +CalcVectorAngle(double ux, double uy, double vx, double vy) +{ + double ta = atan2(uy, ux); + double tb = atan2(vy, vx); + if (tb >= ta) + return tb-ta; + return 2 * M_PI - (ta-tb); +} + + +nsSVGArcConverter::nsSVGArcConverter(const Point& from, + const Point& to, + const Point& radii, + double angle, + bool largeArcFlag, + bool sweepFlag) +{ + const double radPerDeg = M_PI/180.0; + mSegIndex = 0; + + if (from == to) { + mNumSegs = 0; + return; + } + + // Convert to center parameterization as shown in + // http://www.w3.org/TR/SVG/implnote.html + mRx = fabs(radii.x); + mRy = fabs(radii.y); + + mSinPhi = sin(angle*radPerDeg); + mCosPhi = cos(angle*radPerDeg); + + double x1dash = mCosPhi * (from.x-to.x)/2.0 + mSinPhi * (from.y-to.y)/2.0; + double y1dash = -mSinPhi * (from.x-to.x)/2.0 + mCosPhi * (from.y-to.y)/2.0; + + double root; + double numerator = mRx*mRx*mRy*mRy - mRx*mRx*y1dash*y1dash - + mRy*mRy*x1dash*x1dash; + + if (numerator < 0.0) { + // If mRx , mRy and are such that there is no solution (basically, + // the ellipse is not big enough to reach from 'from' to 'to' + // then the ellipse is scaled up uniformly until there is + // exactly one solution (until the ellipse is just big enough). + + // -> find factor s, such that numerator' with mRx'=s*mRx and + // mRy'=s*mRy becomes 0 : + double s = sqrt(1.0 - numerator/(mRx*mRx*mRy*mRy)); + + mRx *= s; + mRy *= s; + root = 0.0; + + } + else { + root = (largeArcFlag == sweepFlag ? -1.0 : 1.0) * + sqrt( numerator/(mRx*mRx*y1dash*y1dash + mRy*mRy*x1dash*x1dash) ); + } + + double cxdash = root*mRx*y1dash/mRy; + double cydash = -root*mRy*x1dash/mRx; + + mC.x = mCosPhi * cxdash - mSinPhi * cydash + (from.x+to.x)/2.0; + mC.y = mSinPhi * cxdash + mCosPhi * cydash + (from.y+to.y)/2.0; + mTheta = CalcVectorAngle(1.0, 0.0, (x1dash-cxdash)/mRx, (y1dash-cydash)/mRy); + double dtheta = CalcVectorAngle((x1dash-cxdash)/mRx, (y1dash-cydash)/mRy, + (-x1dash-cxdash)/mRx, (-y1dash-cydash)/mRy); + if (!sweepFlag && dtheta>0) + dtheta -= 2.0*M_PI; + else if (sweepFlag && dtheta<0) + dtheta += 2.0*M_PI; + + // Convert into cubic bezier segments <= 90deg + mNumSegs = static_cast<int>(ceil(fabs(dtheta/(M_PI/2.0)))); + mDelta = dtheta/mNumSegs; + mT = 8.0/3.0 * sin(mDelta/4.0) * sin(mDelta/4.0) / sin(mDelta/2.0); + + mFrom = from; +} + +bool +nsSVGArcConverter::GetNextSegment(Point* cp1, Point* cp2, Point* to) +{ + if (mSegIndex == mNumSegs) { + return false; + } + + double cosTheta1 = cos(mTheta); + double sinTheta1 = sin(mTheta); + double theta2 = mTheta + mDelta; + double cosTheta2 = cos(theta2); + double sinTheta2 = sin(theta2); + + // a) calculate endpoint of the segment: + to->x = mCosPhi * mRx*cosTheta2 - mSinPhi * mRy*sinTheta2 + mC.x; + to->y = mSinPhi * mRx*cosTheta2 + mCosPhi * mRy*sinTheta2 + mC.y; + + // b) calculate gradients at start/end points of segment: + cp1->x = mFrom.x + mT * ( - mCosPhi * mRx*sinTheta1 - mSinPhi * mRy*cosTheta1); + cp1->y = mFrom.y + mT * ( - mSinPhi * mRx*sinTheta1 + mCosPhi * mRy*cosTheta1); + + cp2->x = to->x + mT * ( mCosPhi * mRx*sinTheta2 + mSinPhi * mRy*cosTheta2); + cp2->y = to->y + mT * ( mSinPhi * mRx*sinTheta2 - mCosPhi * mRy*cosTheta2); + + // do next segment + mTheta = theta2; + mFrom = *to; + ++mSegIndex; + + return true; +} diff --git a/dom/svg/nsSVGPathDataParser.h b/dom/svg/nsSVGPathDataParser.h new file mode 100644 index 0000000000..3a880218d3 --- /dev/null +++ b/dom/svg/nsSVGPathDataParser.h @@ -0,0 +1,83 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef __NS_SVGPATHDATAPARSER_H__ +#define __NS_SVGPATHDATAPARSER_H__ + +#include "mozilla/Attributes.h" +#include "mozilla/gfx/Point.h" +#include "nsSVGDataParser.h" + +namespace mozilla { +class SVGPathData; +} // namespace mozilla + +//////////////////////////////////////////////////////////////////////// +// nsSVGPathDataParser: a simple recursive descent parser that builds +// DOMSVGPathSegs from path data strings. The grammar for path data +// can be found in SVG CR 20001102, chapter 8. + +class nsSVGPathDataParser : public nsSVGDataParser +{ +public: + nsSVGPathDataParser(const nsAString& aValue, + mozilla::SVGPathData* aList) + : nsSVGDataParser(aValue), + mPathSegList(aList) + { + MOZ_ASSERT(aList, "null path data"); + } + + bool Parse(); + +private: + + bool ParseCoordPair(float& aX, float& aY); + bool ParseFlag(bool& aFlag); + + bool ParsePath(); + bool IsStartOfSubPath() const; + bool ParseSubPath(); + + bool ParseSubPathElements(); + bool ParseSubPathElement(char16_t aCommandType, + bool aAbsCoords); + + bool ParseMoveto(); + bool ParseClosePath(); + bool ParseLineto(bool aAbsCoords); + bool ParseHorizontalLineto(bool aAbsCoords); + bool ParseVerticalLineto(bool aAbsCoords); + bool ParseCurveto(bool aAbsCoords); + bool ParseSmoothCurveto(bool aAbsCoords); + bool ParseQuadBezierCurveto(bool aAbsCoords); + bool ParseSmoothQuadBezierCurveto(bool aAbsCoords); + bool ParseEllipticalArc(bool aAbsCoords); + + mozilla::SVGPathData * const mPathSegList; +}; + +class nsSVGArcConverter +{ + typedef mozilla::gfx::Point Point; + +public: + nsSVGArcConverter(const Point& from, + const Point& to, + const Point& radii, + double angle, + bool largeArcFlag, + bool sweepFlag); + bool GetNextSegment(Point* cp1, Point* cp2, Point* to); +protected: + int32_t mNumSegs, mSegIndex; + double mTheta, mDelta, mT; + double mSinPhi, mCosPhi; + double mRx, mRy; + Point mFrom, mC; +}; + +#endif // __NS_SVGPATHDATAPARSER_H__ diff --git a/dom/svg/nsSVGPathGeometryElement.cpp b/dom/svg/nsSVGPathGeometryElement.cpp new file mode 100644 index 0000000000..ea2e1c7c47 --- /dev/null +++ b/dom/svg/nsSVGPathGeometryElement.cpp @@ -0,0 +1,132 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "nsSVGPathGeometryElement.h" + +#include "gfxPlatform.h" +#include "mozilla/gfx/2D.h" +#include "nsComputedDOMStyle.h" +#include "nsSVGUtils.h" +#include "nsSVGLength2.h" +#include "SVGContentUtils.h" + +using namespace mozilla; +using namespace mozilla::gfx; + +//---------------------------------------------------------------------- +// Implementation + +nsSVGPathGeometryElement::nsSVGPathGeometryElement(already_AddRefed<mozilla::dom::NodeInfo>& aNodeInfo) + : nsSVGPathGeometryElementBase(aNodeInfo) +{ +} + +nsresult +nsSVGPathGeometryElement::AfterSetAttr(int32_t aNamespaceID, nsIAtom* aName, + const nsAttrValue* aValue, bool aNotify) +{ + if (mCachedPath && + aNamespaceID == kNameSpaceID_None && + AttributeDefinesGeometry(aName)) { + mCachedPath = nullptr; + } + return nsSVGPathGeometryElementBase::AfterSetAttr(aNamespaceID, aName, + aValue, aNotify); +} + +bool +nsSVGPathGeometryElement::AttributeDefinesGeometry(const nsIAtom *aName) +{ + // Check for nsSVGLength2 attribute + LengthAttributesInfo info = GetLengthInfo(); + for (uint32_t i = 0; i < info.mLengthCount; i++) { + if (aName == *info.mLengthInfo[i].mName) { + return true; + } + } + + return false; +} + +bool +nsSVGPathGeometryElement::GeometryDependsOnCoordCtx() +{ + // Check the nsSVGLength2 attribute + LengthAttributesInfo info = const_cast<nsSVGPathGeometryElement*>(this)->GetLengthInfo(); + for (uint32_t i = 0; i < info.mLengthCount; i++) { + if (info.mLengths[i].GetSpecifiedUnitType() == nsIDOMSVGLength::SVG_LENGTHTYPE_PERCENTAGE) { + return true; + } + } + return false; +} + +bool +nsSVGPathGeometryElement::IsMarkable() +{ + return false; +} + +void +nsSVGPathGeometryElement::GetMarkPoints(nsTArray<nsSVGMark> *aMarks) +{ +} + +already_AddRefed<Path> +nsSVGPathGeometryElement::GetOrBuildPath(const DrawTarget& aDrawTarget, + FillRule aFillRule) +{ + // We only cache the path if it matches the backend used for screen painting: + bool cacheable = aDrawTarget.GetBackendType() == + gfxPlatform::GetPlatform()->GetDefaultContentBackend(); + + // Checking for and returning mCachedPath before checking the pref means + // that the pref is only live on page reload (or app restart for SVG in + // chrome). The benefit is that we avoid causing a CPU memory cache miss by + // looking at the global variable that the pref's stored in. + if (cacheable && mCachedPath) { + if (aDrawTarget.GetBackendType() == mCachedPath->GetBackendType()) { + RefPtr<Path> path(mCachedPath); + return path.forget(); + } + } + RefPtr<PathBuilder> builder = aDrawTarget.CreatePathBuilder(aFillRule); + RefPtr<Path> path = BuildPath(builder); + if (cacheable && NS_SVGPathCachingEnabled()) { + mCachedPath = path; + } + return path.forget(); +} + +already_AddRefed<Path> +nsSVGPathGeometryElement::GetOrBuildPathForMeasuring() +{ + return nullptr; +} + +FillRule +nsSVGPathGeometryElement::GetFillRule() +{ + FillRule fillRule = FillRule::FILL_WINDING; // Equivalent to StyleFillRule::Nonzero + + RefPtr<nsStyleContext> styleContext = + nsComputedDOMStyle::GetStyleContextForElementNoFlush(this, nullptr, + nullptr); + + if (styleContext) { + MOZ_ASSERT(styleContext->StyleSVG()->mFillRule == StyleFillRule::Nonzero || + styleContext->StyleSVG()->mFillRule == StyleFillRule::Evenodd); + + if (styleContext->StyleSVG()->mFillRule == StyleFillRule::Evenodd) { + fillRule = FillRule::FILL_EVEN_ODD; + } + } else { + // ReportToConsole + NS_WARNING("Couldn't get style context for content in GetFillRule"); + } + + return fillRule; +} diff --git a/dom/svg/nsSVGPathGeometryElement.h b/dom/svg/nsSVGPathGeometryElement.h new file mode 100644 index 0000000000..1091fa0dcd --- /dev/null +++ b/dom/svg/nsSVGPathGeometryElement.h @@ -0,0 +1,198 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef __NS_SVGPATHGEOMETRYELEMENT_H__ +#define __NS_SVGPATHGEOMETRYELEMENT_H__ + +#include "mozilla/gfx/2D.h" +#include "SVGGraphicsElement.h" + +struct nsSVGMark { + enum Type { + eStart, + eMid, + eEnd, + + eTypeCount + }; + + float x, y, angle; + Type type; + nsSVGMark(float aX, float aY, float aAngle, Type aType) : + x(aX), y(aY), angle(aAngle), type(aType) {} +}; + +typedef mozilla::dom::SVGGraphicsElement nsSVGPathGeometryElementBase; + +class nsSVGPathGeometryElement : public nsSVGPathGeometryElementBase +{ +protected: + typedef mozilla::gfx::CapStyle CapStyle; + typedef mozilla::gfx::DrawTarget DrawTarget; + typedef mozilla::gfx::FillRule FillRule; + typedef mozilla::gfx::Float Float; + typedef mozilla::gfx::Matrix Matrix; + typedef mozilla::gfx::Path Path; + typedef mozilla::gfx::Point Point; + typedef mozilla::gfx::PathBuilder PathBuilder; + typedef mozilla::gfx::Rect Rect; + typedef mozilla::gfx::StrokeOptions StrokeOptions; + +public: + explicit nsSVGPathGeometryElement(already_AddRefed<mozilla::dom::NodeInfo>& aNodeInfo); + + virtual nsresult AfterSetAttr(int32_t aNamespaceID, nsIAtom* aName, + const nsAttrValue* aValue, bool aNotify) override; + + /** + * Causes this element to discard any Path object that GetOrBuildPath may + * have cached. + */ + virtual void ClearAnyCachedPath() override final { + mCachedPath = nullptr; + } + + virtual bool AttributeDefinesGeometry(const nsIAtom *aName); + + /** + * Returns true if this element's geometry depends on the width or height of its + * coordinate context (typically the viewport established by its nearest <svg> + * ancestor). In other words, returns true if one of the attributes for which + * AttributeDefinesGeometry returns true has a percentage value. + * + * This could be moved up to a more general class so it can be used for non-leaf + * elements, but that would require care and for now there's no need. + */ + bool GeometryDependsOnCoordCtx(); + + virtual bool IsMarkable(); + virtual void GetMarkPoints(nsTArray<nsSVGMark> *aMarks); + + /** + * A method that can be faster than using a Moz2D Path and calling GetBounds/ + * GetStrokedBounds on it. It also helps us avoid rounding error for simple + * shapes and simple transforms where the Moz2D Path backends can fail to + * produce the clean integer bounds that content authors expect in some cases. + * + * If |aToNonScalingStrokeSpace| is non-null then |aBounds|, which is computed + * in bounds space, has the property that it's the smallest (axis-aligned) + * rectangular bound containing the image of this shape as stroked in + * non-scaling-stroke space. (When all transforms involved are rectilinear + * the bounds of the image of |aBounds| in non-scaling-stroke space will be + * tight, but if there are non-rectilinear transforms involved then that may + * be impossible and this method will return false). + * + * If |aToNonScalingStrokeSpace| is non-null then |*aToNonScalingStrokeSpace| + * must be non-singular. + */ + virtual bool GetGeometryBounds(Rect* aBounds, const StrokeOptions& aStrokeOptions, + const Matrix& aToBoundsSpace, + const Matrix* aToNonScalingStrokeSpace = nullptr) { + return false; + } + + /** + * For use with GetAsSimplePath. + */ + class SimplePath + { + public: + SimplePath() + : mType(NONE) + {} + bool IsPath() const { + return mType != NONE; + } + void SetRect(Float x, Float y, Float width, Float height) { + mX = x; mY = y, mWidthOrX2 = width, mHeightOrY2 = height; + mType = RECT; + } + Rect AsRect() const { + MOZ_ASSERT(mType == RECT); + return Rect(mX, mY, mWidthOrX2, mHeightOrY2); + } + bool IsRect() const { + return mType == RECT; + } + void SetLine(Float x1, Float y1, Float x2, Float y2) { + mX = x1, mY = y1, mWidthOrX2 = x2, mHeightOrY2 = y2; + mType = LINE; + } + Point Point1() const { + MOZ_ASSERT(mType == LINE); + return Point(mX, mY); + } + Point Point2() const { + MOZ_ASSERT(mType == LINE); + return Point(mWidthOrX2, mHeightOrY2); + } + bool IsLine() const { + return mType == LINE; + } + void Reset() { + mType = NONE; + } + private: + enum Type { + NONE, RECT, LINE + }; + Float mX, mY, mWidthOrX2, mHeightOrY2; + Type mType; + }; + + /** + * For some platforms there is significant overhead to creating and painting + * a Moz2D Path object. For Rects and lines it is better to get the path data + * using this method and then use the optimized DrawTarget methods for + * filling/stroking rects and lines. + */ + virtual void GetAsSimplePath(SimplePath* aSimplePath) { + aSimplePath->Reset(); + } + + /** + * Returns a Path that can be used to paint, hit-test or calculate bounds for + * this element. May return nullptr if there is no [valid] path. The path + * that is created may be cached and returned on subsequent calls. + */ + virtual already_AddRefed<Path> GetOrBuildPath(const DrawTarget& aDrawTarget, + FillRule fillRule); + + /** + * The same as GetOrBuildPath, but bypasses the cache (neither returns any + * previously cached Path, nor caches the Path that in does return). + * this element. May return nullptr if there is no [valid] path. + */ + virtual already_AddRefed<Path> BuildPath(PathBuilder* aBuilder) = 0; + + /** + * Returns a Path that can be used to measure the length of this elements + * path, or to find the position at a given distance along it. + * + * This is currently equivalent to calling GetOrBuildPath, but it may not be + * in the future. The reason for this function to be separate from + * GetOrBuildPath is because SVGPathData::BuildPath inserts small lines into + * the path if zero length subpaths are encountered, in order to implement + * the SVG specifications requirements that zero length subpaths should + * render circles/squares if stroke-linecap is round/square, respectively. + * In principle these inserted lines could interfere with path measurement, + * so we keep callers that are looking to do measurement separate in case we + * run into problems with the inserted lines negatively affecting measuring + * for content. + */ + virtual already_AddRefed<Path> GetOrBuildPathForMeasuring(); + + /** + * Returns the current computed value of the CSS property 'fill-rule' for + * this element. + */ + FillRule GetFillRule(); + +protected: + mutable RefPtr<Path> mCachedPath; +}; + +#endif diff --git a/dom/svg/nsSVGPolyElement.cpp b/dom/svg/nsSVGPolyElement.cpp new file mode 100644 index 0000000000..441f8aa517 --- /dev/null +++ b/dom/svg/nsSVGPolyElement.cpp @@ -0,0 +1,159 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "nsSVGPolyElement.h" +#include "DOMSVGPointList.h" +#include "mozilla/gfx/2D.h" +#include "SVGContentUtils.h" + +using namespace mozilla; +using namespace mozilla::gfx; + +//---------------------------------------------------------------------- +// nsISupports methods + +NS_IMPL_ADDREF_INHERITED(nsSVGPolyElement,nsSVGPolyElementBase) +NS_IMPL_RELEASE_INHERITED(nsSVGPolyElement,nsSVGPolyElementBase) + +NS_INTERFACE_MAP_BEGIN(nsSVGPolyElement) +NS_INTERFACE_MAP_END_INHERITING(nsSVGPolyElementBase) + +//---------------------------------------------------------------------- +// Implementation + +nsSVGPolyElement::nsSVGPolyElement(already_AddRefed<mozilla::dom::NodeInfo>& aNodeInfo) + : nsSVGPolyElementBase(aNodeInfo) +{ +} + +nsSVGPolyElement::~nsSVGPolyElement() +{ +} + +already_AddRefed<DOMSVGPointList> +nsSVGPolyElement::Points() +{ + void *key = mPoints.GetBaseValKey(); + RefPtr<DOMSVGPointList> points = DOMSVGPointList::GetDOMWrapper(key, this, false); + return points.forget(); +} + +already_AddRefed<DOMSVGPointList> +nsSVGPolyElement::AnimatedPoints() +{ + void *key = mPoints.GetAnimValKey(); + RefPtr<DOMSVGPointList> points = DOMSVGPointList::GetDOMWrapper(key, this, true); + return points.forget(); +} + + +//---------------------------------------------------------------------- +// nsIContent methods + +NS_IMETHODIMP_(bool) +nsSVGPolyElement::IsAttributeMapped(const nsIAtom* name) const +{ + static const MappedAttributeEntry* const map[] = { + sMarkersMap + }; + + return FindAttributeDependence(name, map) || + nsSVGPolyElementBase::IsAttributeMapped(name); +} + +//---------------------------------------------------------------------- +// nsSVGElement methods + +/* virtual */ bool +nsSVGPolyElement::HasValidDimensions() const +{ + return !mPoints.GetAnimValue().IsEmpty(); +} + +//---------------------------------------------------------------------- +// nsSVGPathGeometryElement methods + +bool +nsSVGPolyElement::AttributeDefinesGeometry(const nsIAtom *aName) +{ + if (aName == nsGkAtoms::points) + return true; + + return false; +} + +void +nsSVGPolyElement::GetMarkPoints(nsTArray<nsSVGMark> *aMarks) +{ + const SVGPointList &points = mPoints.GetAnimValue(); + + if (!points.Length()) + return; + + float px = points[0].mX, py = points[0].mY, prevAngle = 0.0; + + aMarks->AppendElement(nsSVGMark(px, py, 0, nsSVGMark::eStart)); + + for (uint32_t i = 1; i < points.Length(); ++i) { + float x = points[i].mX; + float y = points[i].mY; + float angle = atan2(y-py, x-px); + + // Vertex marker. + if (i == 1) { + aMarks->ElementAt(0).angle = angle; + } else { + aMarks->ElementAt(aMarks->Length() - 1).angle = + SVGContentUtils::AngleBisect(prevAngle, angle); + } + + aMarks->AppendElement(nsSVGMark(x, y, 0, nsSVGMark::eMid)); + + prevAngle = angle; + px = x; + py = y; + } + + aMarks->LastElement().angle = prevAngle; + aMarks->LastElement().type = nsSVGMark::eEnd; +} + +bool +nsSVGPolyElement::GetGeometryBounds(Rect* aBounds, + const StrokeOptions& aStrokeOptions, + const Matrix& aToBoundsSpace, + const Matrix* aToNonScalingStrokeSpace) +{ + const SVGPointList &points = mPoints.GetAnimValue(); + + if (!points.Length()) { + // Rendering of the element is disabled + aBounds->SetEmpty(); + return true; + } + + if (aStrokeOptions.mLineWidth > 0 || aToNonScalingStrokeSpace) { + // We don't handle non-scaling-stroke or stroke-miterlimit etc. yet + return false; + } + + if (aToBoundsSpace.IsRectilinear()) { + // We can avoid transforming each point and just transform the result. + // Important for large point lists. + Rect bounds(points[0], Size()); + for (uint32_t i = 1; i < points.Length(); ++i) { + bounds.ExpandToEnclose(points[i]); + } + *aBounds = aToBoundsSpace.TransformBounds(bounds); + } else { + *aBounds = Rect(aToBoundsSpace.TransformPoint(points[0]), Size()); + for (uint32_t i = 1; i < points.Length(); ++i) { + aBounds->ExpandToEnclose(aToBoundsSpace.TransformPoint(points[i])); + } + } + return true; +} + diff --git a/dom/svg/nsSVGPolyElement.h b/dom/svg/nsSVGPolyElement.h new file mode 100644 index 0000000000..1dcde00784 --- /dev/null +++ b/dom/svg/nsSVGPolyElement.h @@ -0,0 +1,61 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef NS_SVGPOLYELEMENT_H_ +#define NS_SVGPOLYELEMENT_H_ + +#include "mozilla/Attributes.h" +#include "nsSVGPathGeometryElement.h" +#include "SVGAnimatedPointList.h" + +typedef nsSVGPathGeometryElement nsSVGPolyElementBase; + +namespace mozilla { +class DOMSVGPointList; +} // namespace mozilla + +class nsSVGPolyElement : public nsSVGPolyElementBase +{ +protected: + explicit nsSVGPolyElement(already_AddRefed<mozilla::dom::NodeInfo>& aNodeInfo); + + virtual ~nsSVGPolyElement(); + +public: + //interfaces + + NS_DECL_ISUPPORTS_INHERITED + + // nsIContent interface + NS_IMETHOD_(bool) IsAttributeMapped(const nsIAtom* name) const override; + + virtual SVGAnimatedPointList* GetAnimatedPointList() override { + return &mPoints; + } + virtual nsIAtom* GetPointListAttrName() const override { + return nsGkAtoms::points; + } + + // nsSVGElement methods: + virtual bool HasValidDimensions() const override; + + // nsSVGPathGeometryElement methods: + virtual bool AttributeDefinesGeometry(const nsIAtom *aName) override; + virtual bool IsMarkable() override { return true; } + virtual void GetMarkPoints(nsTArray<nsSVGMark> *aMarks) override; + virtual bool GetGeometryBounds(Rect* aBounds, const StrokeOptions& aStrokeOptions, + const Matrix& aToBoundsSpace, + const Matrix* aToNonScalingStrokeSpace = nullptr) override; + + // WebIDL + already_AddRefed<mozilla::DOMSVGPointList> Points(); + already_AddRefed<mozilla::DOMSVGPointList> AnimatedPoints(); + +protected: + SVGAnimatedPointList mPoints; +}; + +#endif //NS_SVGPOLYELEMENT_H_ diff --git a/dom/svg/nsSVGString.cpp b/dom/svg/nsSVGString.cpp new file mode 100644 index 0000000000..637a03dee2 --- /dev/null +++ b/dom/svg/nsSVGString.cpp @@ -0,0 +1,146 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "nsSVGString.h" + +#include "mozilla/Move.h" +#include "nsSVGAttrTearoffTable.h" +#include "nsSMILValue.h" +#include "SMILStringType.h" + +using namespace mozilla; +using namespace mozilla::dom; + +NS_SVG_VAL_IMPL_CYCLE_COLLECTION_WRAPPERCACHED(nsSVGString::DOMAnimatedString, mSVGElement) + +NS_IMPL_CYCLE_COLLECTING_ADDREF(nsSVGString::DOMAnimatedString) +NS_IMPL_CYCLE_COLLECTING_RELEASE(nsSVGString::DOMAnimatedString) + +NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsSVGString::DOMAnimatedString) + NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY + NS_INTERFACE_MAP_ENTRY(nsISupports) +NS_INTERFACE_MAP_END + +static inline +nsSVGAttrTearoffTable<nsSVGString, nsSVGString::DOMAnimatedString>& +SVGAnimatedStringTearoffTable() +{ + static nsSVGAttrTearoffTable<nsSVGString, nsSVGString::DOMAnimatedString> + sSVGAnimatedStringTearoffTable; + return sSVGAnimatedStringTearoffTable; +} + +/* Implementation */ + +void +nsSVGString::SetBaseValue(const nsAString& aValue, + nsSVGElement *aSVGElement, + bool aDoSetAttr) +{ + NS_ASSERTION(aSVGElement, "Null element passed to SetBaseValue"); + + mIsBaseSet = true; + if (aDoSetAttr) { + aSVGElement->SetStringBaseValue(mAttrEnum, aValue); + } + if (mAnimVal) { + aSVGElement->AnimationNeedsResample(); + } + + aSVGElement->DidChangeString(mAttrEnum); +} + +void +nsSVGString::GetAnimValue(nsAString& aResult, const nsSVGElement *aSVGElement) const +{ + if (mAnimVal) { + aResult = *mAnimVal; + return; + } + + aSVGElement->GetStringBaseValue(mAttrEnum, aResult); +} + +void +nsSVGString::SetAnimValue(const nsAString& aValue, nsSVGElement *aSVGElement) +{ + if (aSVGElement->IsStringAnimatable(mAttrEnum)) { + if (mAnimVal && mAnimVal->Equals(aValue)) { + return; + } + if (!mAnimVal) { + mAnimVal = new nsString(); + } + *mAnimVal = aValue; + aSVGElement->DidAnimateString(mAttrEnum); + } +} + +already_AddRefed<SVGAnimatedString> +nsSVGString::ToDOMAnimatedString(nsSVGElement* aSVGElement) +{ + RefPtr<DOMAnimatedString> domAnimatedString = + SVGAnimatedStringTearoffTable().GetTearoff(this); + if (!domAnimatedString) { + domAnimatedString = new DOMAnimatedString(this, aSVGElement); + SVGAnimatedStringTearoffTable().AddTearoff(this, domAnimatedString); + } + + return domAnimatedString.forget(); +} + +nsSVGString::DOMAnimatedString::~DOMAnimatedString() +{ + SVGAnimatedStringTearoffTable().RemoveTearoff(mVal); +} + +nsISMILAttr* +nsSVGString::ToSMILAttr(nsSVGElement *aSVGElement) +{ + return new SMILString(this, aSVGElement); +} + +nsresult +nsSVGString::SMILString::ValueFromString(const nsAString& aStr, + const dom::SVGAnimationElement* /*aSrcElement*/, + nsSMILValue& aValue, + bool& aPreventCachingOfSandwich) const +{ + nsSMILValue val(SMILStringType::Singleton()); + + *static_cast<nsAString*>(val.mU.mPtr) = aStr; + aValue = Move(val); + aPreventCachingOfSandwich = false; + return NS_OK; +} + +nsSMILValue +nsSVGString::SMILString::GetBaseValue() const +{ + nsSMILValue val(SMILStringType::Singleton()); + mSVGElement->GetStringBaseValue(mVal->mAttrEnum, *static_cast<nsAString*>(val.mU.mPtr)); + return val; +} + +void +nsSVGString::SMILString::ClearAnimValue() +{ + if (mVal->mAnimVal) { + mVal->mAnimVal = nullptr; + mSVGElement->DidAnimateString(mVal->mAttrEnum); + } +} + +nsresult +nsSVGString::SMILString::SetAnimValue(const nsSMILValue& aValue) +{ + NS_ASSERTION(aValue.mType == SMILStringType::Singleton(), + "Unexpected type to assign animated value"); + if (aValue.mType == SMILStringType::Singleton()) { + mVal->SetAnimValue(*static_cast<nsAString*>(aValue.mU.mPtr), mSVGElement); + } + return NS_OK; +} diff --git a/dom/svg/nsSVGString.h b/dom/svg/nsSVGString.h new file mode 100644 index 0000000000..a27743b662 --- /dev/null +++ b/dom/svg/nsSVGString.h @@ -0,0 +1,109 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef __NS_SVGSTRING_H__ +#define __NS_SVGSTRING_H__ + +#include "nsAutoPtr.h" +#include "nsError.h" +#include "nsSVGElement.h" +#include "mozilla/Attributes.h" +#include "mozilla/dom/SVGAnimatedString.h" + +class nsSVGString +{ + +public: + void Init(uint8_t aAttrEnum) { + mAnimVal = nullptr; + mAttrEnum = aAttrEnum; + mIsBaseSet = false; + } + + void SetBaseValue(const nsAString& aValue, + nsSVGElement *aSVGElement, + bool aDoSetAttr); + void GetBaseValue(nsAString& aValue, const nsSVGElement *aSVGElement) const + { aSVGElement->GetStringBaseValue(mAttrEnum, aValue); } + + void SetAnimValue(const nsAString& aValue, nsSVGElement *aSVGElement); + void GetAnimValue(nsAString& aValue, const nsSVGElement *aSVGElement) const; + + // Returns true if the animated value of this string has been explicitly + // set (either by animation, or by taking on the base value which has been + // explicitly set by markup or a DOM call), false otherwise. + // If this returns false, the animated value is still valid, that is, + // useable, and represents the default base value of the attribute. + bool IsExplicitlySet() const + { return !!mAnimVal || mIsBaseSet; } + + already_AddRefed<mozilla::dom::SVGAnimatedString> + ToDOMAnimatedString(nsSVGElement* aSVGElement); + + // Returns a new nsISMILAttr object that the caller must delete + nsISMILAttr* ToSMILAttr(nsSVGElement *aSVGElement); + +private: + + nsAutoPtr<nsString> mAnimVal; + uint8_t mAttrEnum; // element specified tracking for attribute + bool mIsBaseSet; + +public: + struct DOMAnimatedString final : public mozilla::dom::SVGAnimatedString + { + NS_DECL_CYCLE_COLLECTING_ISUPPORTS + NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(DOMAnimatedString) + + DOMAnimatedString(nsSVGString* aVal, nsSVGElement* aSVGElement) + : mozilla::dom::SVGAnimatedString(aSVGElement) + , mVal(aVal) + {} + + nsSVGString* mVal; // kept alive because it belongs to content + + void GetBaseVal(nsAString & aResult) override + { + mVal->GetBaseValue(aResult, mSVGElement); + } + + void SetBaseVal(const nsAString & aValue) override + { + mVal->SetBaseValue(aValue, mSVGElement, true); + } + + void GetAnimVal(nsAString & aResult) override + { + mSVGElement->FlushAnimations(); + mVal->GetAnimValue(aResult, mSVGElement); + } + + private: + virtual ~DOMAnimatedString(); + }; + struct SMILString : public nsISMILAttr + { + public: + SMILString(nsSVGString *aVal, nsSVGElement *aSVGElement) + : mVal(aVal), mSVGElement(aSVGElement) {} + + // 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. + nsSVGString* mVal; + nsSVGElement* mSVGElement; + + // nsISMILAttr methods + virtual nsresult ValueFromString(const nsAString& aStr, + const mozilla::dom::SVGAnimationElement *aSrcElement, + nsSMILValue& aValue, + bool& aPreventCachingOfSandwich) const override; + virtual nsSMILValue GetBaseValue() const override; + virtual void ClearAnimValue() override; + virtual nsresult SetAnimValue(const nsSMILValue& aValue) override; + }; +}; +#endif //__NS_SVGSTRING_H__ diff --git a/dom/svg/nsSVGTransform.cpp b/dom/svg/nsSVGTransform.cpp new file mode 100644 index 0000000000..3a07fa21f8 --- /dev/null +++ b/dom/svg/nsSVGTransform.cpp @@ -0,0 +1,244 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "nsError.h" +#include "nsSVGTransform.h" +#include "nsContentUtils.h" // for NS_ENSURE_FINITE +#include "nsTextFormatter.h" + +namespace { + const double kRadPerDegree = 2.0 * M_PI / 360.0; +} // namespace + +namespace mozilla { + +void +nsSVGTransform::GetValueAsString(nsAString& aValue) const +{ + char16_t buf[256]; + + switch (mType) { + case SVG_TRANSFORM_TRANSLATE: + // The spec say that if Y is not provided, it is assumed to be zero. + if (mMatrix._32 != 0) + nsTextFormatter::snprintf(buf, sizeof(buf)/sizeof(char16_t), + u"translate(%g, %g)", + mMatrix._31, mMatrix._32); + else + nsTextFormatter::snprintf(buf, sizeof(buf)/sizeof(char16_t), + u"translate(%g)", + mMatrix._31); + break; + case SVG_TRANSFORM_ROTATE: + if (mOriginX != 0.0f || mOriginY != 0.0f) + nsTextFormatter::snprintf(buf, sizeof(buf)/sizeof(char16_t), + u"rotate(%g, %g, %g)", + mAngle, mOriginX, mOriginY); + else + nsTextFormatter::snprintf(buf, sizeof(buf)/sizeof(char16_t), + u"rotate(%g)", mAngle); + break; + case SVG_TRANSFORM_SCALE: + if (mMatrix._11 != mMatrix._22) + nsTextFormatter::snprintf(buf, sizeof(buf)/sizeof(char16_t), + u"scale(%g, %g)", mMatrix._11, mMatrix._22); + else + nsTextFormatter::snprintf(buf, sizeof(buf)/sizeof(char16_t), + u"scale(%g)", mMatrix._11); + break; + case SVG_TRANSFORM_SKEWX: + nsTextFormatter::snprintf(buf, sizeof(buf)/sizeof(char16_t), + u"skewX(%g)", mAngle); + break; + case SVG_TRANSFORM_SKEWY: + nsTextFormatter::snprintf(buf, sizeof(buf)/sizeof(char16_t), + u"skewY(%g)", mAngle); + break; + case SVG_TRANSFORM_MATRIX: + nsTextFormatter::snprintf(buf, sizeof(buf)/sizeof(char16_t), + u"matrix(%g, %g, %g, %g, %g, %g)", + mMatrix._11, mMatrix._12, + mMatrix._21, mMatrix._22, + mMatrix._31, mMatrix._32); + break; + default: + buf[0] = '\0'; + NS_ERROR("unknown transformation type"); + break; + } + + aValue.Assign(buf); +} + +void +nsSVGTransform::SetMatrix(const gfxMatrix& aMatrix) +{ + mType = SVG_TRANSFORM_MATRIX; + mMatrix = aMatrix; + // We set the other members here too, since operator== requires it and + // the DOM requires it for mAngle. + mAngle = 0.f; + mOriginX = 0.f; + mOriginY = 0.f; +} + +void +nsSVGTransform::SetTranslate(float aTx, float aTy) +{ + mType = SVG_TRANSFORM_TRANSLATE; + mMatrix.Reset(); + mMatrix._31 = aTx; + mMatrix._32 = aTy; + mAngle = 0.f; + mOriginX = 0.f; + mOriginY = 0.f; +} + +void +nsSVGTransform::SetScale(float aSx, float aSy) +{ + mType = SVG_TRANSFORM_SCALE; + mMatrix.Reset(); + mMatrix._11 = aSx; + mMatrix._22 = aSy; + mAngle = 0.f; + mOriginX = 0.f; + mOriginY = 0.f; +} + +void +nsSVGTransform::SetRotate(float aAngle, float aCx, float aCy) +{ + mType = SVG_TRANSFORM_ROTATE; + mMatrix.Reset(); + mMatrix.Translate(aCx, aCy); + mMatrix.Rotate(aAngle*kRadPerDegree); + mMatrix.Translate(-aCx, -aCy); + mAngle = aAngle; + mOriginX = aCx; + mOriginY = aCy; +} + +nsresult +nsSVGTransform::SetSkewX(float aAngle) +{ + double ta = tan(aAngle*kRadPerDegree); + NS_ENSURE_FINITE(ta, NS_ERROR_RANGE_ERR); + + mType = SVG_TRANSFORM_SKEWX; + mMatrix.Reset(); + mMatrix._21 = ta; + mAngle = aAngle; + mOriginX = 0.f; + mOriginY = 0.f; + return NS_OK; +} + +nsresult +nsSVGTransform::SetSkewY(float aAngle) +{ + double ta = tan(aAngle*kRadPerDegree); + NS_ENSURE_FINITE(ta, NS_ERROR_RANGE_ERR); + + mType = SVG_TRANSFORM_SKEWY; + mMatrix.Reset(); + mMatrix._12 = ta; + mAngle = aAngle; + mOriginX = 0.f; + mOriginY = 0.f; + return NS_OK; +} + +SVGTransformSMILData::SVGTransformSMILData(const nsSVGTransform& aTransform) + : mTransformType(aTransform.Type()) +{ + MOZ_ASSERT(mTransformType >= SVG_TRANSFORM_MATRIX && + mTransformType <= SVG_TRANSFORM_SKEWY, + "Unexpected transform type"); + + for (uint32_t i = 0; i < NUM_STORED_PARAMS; ++i) { + mParams[i] = 0.f; + } + + switch (mTransformType) { + case SVG_TRANSFORM_MATRIX: { + const gfxMatrix& mx = aTransform.GetMatrix(); + mParams[0] = static_cast<float>(mx._11); + mParams[1] = static_cast<float>(mx._12); + mParams[2] = static_cast<float>(mx._21); + mParams[3] = static_cast<float>(mx._22); + mParams[4] = static_cast<float>(mx._31); + mParams[5] = static_cast<float>(mx._32); + break; + } + case SVG_TRANSFORM_TRANSLATE: { + const gfxMatrix& mx = aTransform.GetMatrix(); + mParams[0] = static_cast<float>(mx._31); + mParams[1] = static_cast<float>(mx._32); + break; + } + case SVG_TRANSFORM_SCALE: { + const gfxMatrix& mx = aTransform.GetMatrix(); + mParams[0] = static_cast<float>(mx._11); + mParams[1] = static_cast<float>(mx._22); + break; + } + case SVG_TRANSFORM_ROTATE: + mParams[0] = aTransform.Angle(); + aTransform.GetRotationOrigin(mParams[1], mParams[2]); + break; + + case SVG_TRANSFORM_SKEWX: + case SVG_TRANSFORM_SKEWY: + mParams[0] = aTransform.Angle(); + break; + + default: + NS_NOTREACHED("Unexpected transform type"); + break; + } +} + +nsSVGTransform +SVGTransformSMILData::ToSVGTransform() const +{ + nsSVGTransform result; + + switch (mTransformType) { + case SVG_TRANSFORM_MATRIX: + result.SetMatrix(gfxMatrix(mParams[0], mParams[1], + mParams[2], mParams[3], + mParams[4], mParams[5])); + break; + + case SVG_TRANSFORM_TRANSLATE: + result.SetTranslate(mParams[0], mParams[1]); + break; + + case SVG_TRANSFORM_SCALE: + result.SetScale(mParams[0], mParams[1]); + break; + + case SVG_TRANSFORM_ROTATE: + result.SetRotate(mParams[0], mParams[1], mParams[2]); + break; + + case SVG_TRANSFORM_SKEWX: + result.SetSkewX(mParams[0]); + break; + + case SVG_TRANSFORM_SKEWY: + result.SetSkewY(mParams[0]); + break; + + default: + NS_NOTREACHED("Unexpected transform type"); + break; + } + return result; +} + +} // namespace mozilla diff --git a/dom/svg/nsSVGTransform.h b/dom/svg/nsSVGTransform.h new file mode 100644 index 0000000000..be55d3e2e5 --- /dev/null +++ b/dom/svg/nsSVGTransform.h @@ -0,0 +1,178 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef MOZILLA_SVGTRANSFORM_H__ +#define MOZILLA_SVGTRANSFORM_H__ + +#include "gfxMatrix.h" +#include "nsDebug.h" + +namespace mozilla { + +// Transform Types +static const unsigned short SVG_TRANSFORM_UNKNOWN = 0; +static const unsigned short SVG_TRANSFORM_MATRIX = 1; +static const unsigned short SVG_TRANSFORM_TRANSLATE = 2; +static const unsigned short SVG_TRANSFORM_SCALE = 3; +static const unsigned short SVG_TRANSFORM_ROTATE = 4; +static const unsigned short SVG_TRANSFORM_SKEWX = 5; +static const unsigned short SVG_TRANSFORM_SKEWY = 6; + +/* + * The DOM wrapper class for this class is DOMSVGTransformMatrix. + */ +class nsSVGTransform +{ +public: + // Default ctor initialises to matrix type with identity matrix + nsSVGTransform() + : mMatrix() // Initialises to identity + , mAngle(0.f) + , mOriginX(0.f) + , mOriginY(0.f) + , mType(SVG_TRANSFORM_MATRIX) + { } + + explicit nsSVGTransform(const gfxMatrix& aMatrix) + : mMatrix(aMatrix) + , mAngle(0.f) + , mOriginX(0.f) + , mOriginY(0.f) + , mType(SVG_TRANSFORM_MATRIX) + { } + + bool operator==(const nsSVGTransform& rhs) const { + return mType == rhs.mType && + MatricesEqual(mMatrix, rhs.mMatrix) && + mAngle == rhs.mAngle && + mOriginX == rhs.mOriginX && + mOriginY == rhs.mOriginY; + } + + void GetValueAsString(nsAString& aValue) const; + + float Angle() const { + return mAngle; + } + void GetRotationOrigin(float& aOriginX, float& aOriginY) const { + aOriginX = mOriginX; + aOriginY = mOriginY; + } + uint16_t Type() const { + return mType; + } + + const gfxMatrix& GetMatrix() const { return mMatrix; } + void SetMatrix(const gfxMatrix& aMatrix); + void SetTranslate(float aTx, float aTy); + void SetScale(float aSx, float aSy); + void SetRotate(float aAngle, float aCx, float aCy); + nsresult SetSkewX(float aAngle); + nsresult SetSkewY(float aAngle); + + static bool MatricesEqual(const gfxMatrix& a, const gfxMatrix& b) + { + return a._11 == b._11 && + a._12 == b._12 && + a._21 == b._21 && + a._22 == b._22 && + a._31 == b._31 && + a._32 == b._32; + } + +protected: + gfxMatrix mMatrix; + float mAngle, mOriginX, mOriginY; + uint16_t mType; +}; + +/* + * A slightly more light-weight version of nsSVGTransform for SMIL animation. + * + * Storing the parameters in an array (rather than a matrix) also allows simpler + * (transform type-agnostic) interpolation and addition. + * + * The meaning of the mParams array depends on the transform type as follows: + * + * Type | mParams[0], mParams[1], mParams[2], ... + * --------------------+----------------------------------------- + * translate | tx, ty + * scale | sx, sy + * rotate | rotation-angle (in degrees), cx, cy + * skewX | skew-angle (in degrees) + * skewY | skew-angle (in degrees) + * matrix | a, b, c, d, e, f + * + * The matrix type is never generated by animation code (it is only produced + * when the user inserts one via the DOM) and often requires special handling + * when we do encounter it. Therefore many users of this class are only + * interested in the first three parameters and so we provide a special + * constructor for setting those parameters only. + */ +class SVGTransformSMILData +{ +public: + // Number of float-params required in constructor, if constructing one of the + // 'simple' transform types (all but matrix type) + static const uint32_t NUM_SIMPLE_PARAMS = 3; + + // Number of float-params required in constructor for matrix type. + // This is also the number of params we actually store, regardless of type. + static const uint32_t NUM_STORED_PARAMS = 6; + + explicit SVGTransformSMILData(uint16_t aType) + : mTransformType(aType) + { + MOZ_ASSERT(aType >= SVG_TRANSFORM_MATRIX && aType <= SVG_TRANSFORM_SKEWY, + "Unexpected transform type"); + for (uint32_t i = 0; i < NUM_STORED_PARAMS; ++i) { + mParams[i] = 0.f; + } + } + + SVGTransformSMILData(uint16_t aType, float (&aParams)[NUM_SIMPLE_PARAMS]) + : mTransformType(aType) + { + MOZ_ASSERT(aType >= SVG_TRANSFORM_TRANSLATE && aType <= SVG_TRANSFORM_SKEWY, + "Expected 'simple' transform type"); + for (uint32_t i = 0; i < NUM_SIMPLE_PARAMS; ++i) { + mParams[i] = aParams[i]; + } + for (uint32_t i = NUM_SIMPLE_PARAMS; i < NUM_STORED_PARAMS; ++i) { + mParams[i] = 0.f; + } + } + + // Conversion to/from a fully-fledged nsSVGTransform + explicit SVGTransformSMILData(const nsSVGTransform& aTransform); + nsSVGTransform ToSVGTransform() const; + + bool operator==(const SVGTransformSMILData& aOther) const + { + if (mTransformType != aOther.mTransformType) + return false; + + for (uint32_t i = 0; i < NUM_STORED_PARAMS; ++i) { + if (mParams[i] != aOther.mParams[i]) { + return false; + } + } + + return true; + } + + bool operator!=(const SVGTransformSMILData& aOther) const + { + return !(*this == aOther); + } + + uint16_t mTransformType; + float mParams[NUM_STORED_PARAMS]; +}; + +} // namespace mozilla + +#endif // MOZILLA_SVGTRANSFORM_H__ diff --git a/dom/svg/nsSVGViewBox.cpp b/dom/svg/nsSVGViewBox.cpp new file mode 100644 index 0000000000..f0e6b0092d --- /dev/null +++ b/dom/svg/nsSVGViewBox.cpp @@ -0,0 +1,360 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "nsSVGViewBox.h" + +#include "mozilla/Move.h" +#include "nsCharSeparatedTokenizer.h" +#include "nsSMILValue.h" +#include "nsTextFormatter.h" +#include "SVGContentUtils.h" +#include "SVGViewBoxSMILType.h" + +#define NUM_VIEWBOX_COMPONENTS 4 +using namespace mozilla; + +/* Implementation of nsSVGViewBoxRect methods */ + +bool +nsSVGViewBoxRect::operator==(const nsSVGViewBoxRect& aOther) const +{ + if (&aOther == this) + return true; + + return (none && aOther.none) || + (!none && !aOther.none && + x == aOther.x && + y == aOther.y && + width == aOther.width && + height == aOther.height); +} + +/* Cycle collection macros for nsSVGViewBox */ + +NS_SVG_VAL_IMPL_CYCLE_COLLECTION_WRAPPERCACHED(nsSVGViewBox::DOMBaseVal, mSVGElement) +NS_SVG_VAL_IMPL_CYCLE_COLLECTION_WRAPPERCACHED(nsSVGViewBox::DOMAnimVal, mSVGElement) + +NS_IMPL_CYCLE_COLLECTING_ADDREF(nsSVGViewBox::DOMBaseVal) +NS_IMPL_CYCLE_COLLECTING_RELEASE(nsSVGViewBox::DOMBaseVal) + +NS_IMPL_CYCLE_COLLECTING_ADDREF(nsSVGViewBox::DOMAnimVal) +NS_IMPL_CYCLE_COLLECTING_RELEASE(nsSVGViewBox::DOMAnimVal) + +NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsSVGViewBox::DOMBaseVal) + NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY + NS_INTERFACE_MAP_ENTRY(nsISupports) +NS_INTERFACE_MAP_END + +NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsSVGViewBox::DOMAnimVal) + NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY + NS_INTERFACE_MAP_ENTRY(nsISupports) +NS_INTERFACE_MAP_END + +static nsSVGAttrTearoffTable<nsSVGViewBox, nsSVGViewBox::DOMBaseVal> + sBaseSVGViewBoxTearoffTable; +static nsSVGAttrTearoffTable<nsSVGViewBox, nsSVGViewBox::DOMAnimVal> + sAnimSVGViewBoxTearoffTable; +nsSVGAttrTearoffTable<nsSVGViewBox, dom::SVGAnimatedRect> + nsSVGViewBox::sSVGAnimatedRectTearoffTable; + + +/* Implementation of nsSVGViewBox methods */ + +void +nsSVGViewBox::Init() +{ + mHasBaseVal = false; + // We shouldn't use mBaseVal for rendering (its usages should be guarded with + // "mHasBaseVal" checks), but just in case we do by accident, this will + // ensure that we treat it as "none" and ignore its numeric values: + mBaseVal.none = true; + + mAnimVal = nullptr; +} + +bool +nsSVGViewBox::HasRect() const +{ + // Check mAnimVal if we have one; otherwise, check mBaseVal if we have one; + // otherwise, just return false (we clearly do not have a rect). + const nsSVGViewBoxRect* rect = mAnimVal; + if (!rect) { + if (!mHasBaseVal) { + // no anim val, no base val --> no viewbox rect + return false; + } + rect = &mBaseVal; + } + + return !rect->none && rect->width >= 0 && rect->height >= 0; +} + +void +nsSVGViewBox::SetAnimValue(const nsSVGViewBoxRect& aRect, + nsSVGElement *aSVGElement) +{ + if (!mAnimVal) { + // it's okay if allocation fails - and no point in reporting that + mAnimVal = new nsSVGViewBoxRect(aRect); + } else { + if (aRect == *mAnimVal) { + return; + } + *mAnimVal = aRect; + } + aSVGElement->DidAnimateViewBox(); +} + +void +nsSVGViewBox::SetBaseValue(const nsSVGViewBoxRect& aRect, + nsSVGElement *aSVGElement) +{ + if (!mHasBaseVal || mBaseVal == aRect) { + // This method is used to set a single x, y, width + // or height value. It can't create a base value + // as the other components may be undefined. We record + // the new value though, so as not to lose data. + mBaseVal = aRect; + return; + } + + nsAttrValue emptyOrOldValue = aSVGElement->WillChangeViewBox(); + + mBaseVal = aRect; + mHasBaseVal = true; + + aSVGElement->DidChangeViewBox(emptyOrOldValue); + if (mAnimVal) { + aSVGElement->AnimationNeedsResample(); + } +} + +static nsresult +ToSVGViewBoxRect(const nsAString& aStr, nsSVGViewBoxRect *aViewBox) +{ + if (aStr.EqualsLiteral("none")) { + aViewBox->none = true; + return NS_OK; + } + + nsCharSeparatedTokenizerTemplate<IsSVGWhitespace> + tokenizer(aStr, ',', + nsCharSeparatedTokenizer::SEPARATOR_OPTIONAL); + float vals[NUM_VIEWBOX_COMPONENTS]; + uint32_t i; + for (i = 0; i < NUM_VIEWBOX_COMPONENTS && tokenizer.hasMoreTokens(); ++i) { + if (!SVGContentUtils::ParseNumber(tokenizer.nextToken(), vals[i])) { + return NS_ERROR_DOM_SYNTAX_ERR; + } + } + + if (i != NUM_VIEWBOX_COMPONENTS || // Too few values. + tokenizer.hasMoreTokens() || // Too many values. + tokenizer.separatorAfterCurrentToken()) { // Trailing comma. + return NS_ERROR_DOM_SYNTAX_ERR; + } + + aViewBox->x = vals[0]; + aViewBox->y = vals[1]; + aViewBox->width = vals[2]; + aViewBox->height = vals[3]; + aViewBox->none = false; + + return NS_OK; +} + +nsresult +nsSVGViewBox::SetBaseValueString(const nsAString& aValue, + nsSVGElement *aSVGElement, + bool aDoSetAttr) +{ + nsSVGViewBoxRect viewBox; + + nsresult rv = ToSVGViewBoxRect(aValue, &viewBox); + if (NS_FAILED(rv)) { + return rv; + } + // Comparison against mBaseVal is only valid if we currently have a base val. + if (mHasBaseVal && viewBox == mBaseVal) { + return NS_OK; + } + + nsAttrValue emptyOrOldValue; + if (aDoSetAttr) { + emptyOrOldValue = aSVGElement->WillChangeViewBox(); + } + mHasBaseVal = true; + mBaseVal = viewBox; + + if (aDoSetAttr) { + aSVGElement->DidChangeViewBox(emptyOrOldValue); + } + if (mAnimVal) { + aSVGElement->AnimationNeedsResample(); + } + return NS_OK; +} + +void +nsSVGViewBox::GetBaseValueString(nsAString& aValue) const +{ + if (mBaseVal.none) { + aValue.AssignLiteral("none"); + return; + } + char16_t buf[200]; + nsTextFormatter::snprintf(buf, sizeof(buf)/sizeof(char16_t), + u"%g %g %g %g", + (double)mBaseVal.x, (double)mBaseVal.y, + (double)mBaseVal.width, (double)mBaseVal.height); + aValue.Assign(buf); +} + + +already_AddRefed<dom::SVGAnimatedRect> +nsSVGViewBox::ToSVGAnimatedRect(nsSVGElement* aSVGElement) +{ + RefPtr<dom::SVGAnimatedRect> domAnimatedRect = + sSVGAnimatedRectTearoffTable.GetTearoff(this); + if (!domAnimatedRect) { + domAnimatedRect = new dom::SVGAnimatedRect(this, aSVGElement); + sSVGAnimatedRectTearoffTable.AddTearoff(this, domAnimatedRect); + } + + return domAnimatedRect.forget(); +} + +already_AddRefed<dom::SVGIRect> +nsSVGViewBox::ToDOMBaseVal(nsSVGElement *aSVGElement) +{ + if (!mHasBaseVal || mBaseVal.none) { + return nullptr; + } + + RefPtr<DOMBaseVal> domBaseVal = + sBaseSVGViewBoxTearoffTable.GetTearoff(this); + if (!domBaseVal) { + domBaseVal = new DOMBaseVal(this, aSVGElement); + sBaseSVGViewBoxTearoffTable.AddTearoff(this, domBaseVal); + } + + return domBaseVal.forget(); +} + +nsSVGViewBox::DOMBaseVal::~DOMBaseVal() +{ + sBaseSVGViewBoxTearoffTable.RemoveTearoff(mVal); +} + +already_AddRefed<dom::SVGIRect> +nsSVGViewBox::ToDOMAnimVal(nsSVGElement *aSVGElement) +{ + if ((mAnimVal && mAnimVal->none) || + (!mAnimVal && (!mHasBaseVal || mBaseVal.none))) { + return nullptr; + } + + RefPtr<DOMAnimVal> domAnimVal = + sAnimSVGViewBoxTearoffTable.GetTearoff(this); + if (!domAnimVal) { + domAnimVal = new DOMAnimVal(this, aSVGElement); + sAnimSVGViewBoxTearoffTable.AddTearoff(this, domAnimVal); + } + + return domAnimVal.forget(); +} + +nsSVGViewBox::DOMAnimVal::~DOMAnimVal() +{ + sAnimSVGViewBoxTearoffTable.RemoveTearoff(mVal); +} + +void +nsSVGViewBox::DOMBaseVal::SetX(float aX, ErrorResult& aRv) +{ + nsSVGViewBoxRect rect = mVal->GetBaseValue(); + rect.x = aX; + mVal->SetBaseValue(rect, mSVGElement); +} + +void +nsSVGViewBox::DOMBaseVal::SetY(float aY, ErrorResult& aRv) +{ + nsSVGViewBoxRect rect = mVal->GetBaseValue(); + rect.y = aY; + mVal->SetBaseValue(rect, mSVGElement); +} + +void +nsSVGViewBox::DOMBaseVal::SetWidth(float aWidth, ErrorResult& aRv) +{ + nsSVGViewBoxRect rect = mVal->GetBaseValue(); + rect.width = aWidth; + mVal->SetBaseValue(rect, mSVGElement); +} + +void +nsSVGViewBox::DOMBaseVal::SetHeight(float aHeight, ErrorResult& aRv) +{ + nsSVGViewBoxRect rect = mVal->GetBaseValue(); + rect.height = aHeight; + mVal->SetBaseValue(rect, mSVGElement); +} + +nsISMILAttr* +nsSVGViewBox::ToSMILAttr(nsSVGElement *aSVGElement) +{ + return new SMILViewBox(this, aSVGElement); +} + +nsresult +nsSVGViewBox::SMILViewBox + ::ValueFromString(const nsAString& aStr, + const dom::SVGAnimationElement* /*aSrcElement*/, + nsSMILValue& aValue, + bool& aPreventCachingOfSandwich) const +{ + nsSVGViewBoxRect viewBox; + nsresult res = ToSVGViewBoxRect(aStr, &viewBox); + if (NS_FAILED(res)) { + return res; + } + nsSMILValue val(&SVGViewBoxSMILType::sSingleton); + *static_cast<nsSVGViewBoxRect*>(val.mU.mPtr) = viewBox; + aValue = Move(val); + aPreventCachingOfSandwich = false; + + return NS_OK; +} + +nsSMILValue +nsSVGViewBox::SMILViewBox::GetBaseValue() const +{ + nsSMILValue val(&SVGViewBoxSMILType::sSingleton); + *static_cast<nsSVGViewBoxRect*>(val.mU.mPtr) = mVal->mBaseVal; + return val; +} + +void +nsSVGViewBox::SMILViewBox::ClearAnimValue() +{ + if (mVal->mAnimVal) { + mVal->mAnimVal = nullptr; + mSVGElement->DidAnimateViewBox(); + } +} + +nsresult +nsSVGViewBox::SMILViewBox::SetAnimValue(const nsSMILValue& aValue) +{ + NS_ASSERTION(aValue.mType == &SVGViewBoxSMILType::sSingleton, + "Unexpected type to assign animated value"); + if (aValue.mType == &SVGViewBoxSMILType::sSingleton) { + nsSVGViewBoxRect &vb = *static_cast<nsSVGViewBoxRect*>(aValue.mU.mPtr); + mVal->SetAnimValue(vb, mSVGElement); + } + return NS_OK; +} diff --git a/dom/svg/nsSVGViewBox.h b/dom/svg/nsSVGViewBox.h new file mode 100644 index 0000000000..9b046a9656 --- /dev/null +++ b/dom/svg/nsSVGViewBox.h @@ -0,0 +1,247 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef __NS_SVGVIEWBOX_H__ +#define __NS_SVGVIEWBOX_H__ + +#include "nsAutoPtr.h" +#include "nsCycleCollectionParticipant.h" +#include "nsError.h" +#include "mozilla/dom/SVGAnimatedRect.h" +#include "mozilla/dom/SVGIRect.h" +#include "nsISMILAttr.h" +#include "nsSVGElement.h" +#include "mozilla/Attributes.h" +#include "nsSVGAttrTearoffTable.h" + +class nsSMILValue; + +namespace mozilla { +namespace dom { +class SVGAnimationElement; +} // namespace dom +} // namespace mozilla + +struct nsSVGViewBoxRect +{ + float x, y; + float width, height; + bool none; + + nsSVGViewBoxRect() : none(true) {} + nsSVGViewBoxRect(float aX, float aY, float aWidth, float aHeight) : + x(aX), y(aY), width(aWidth), height(aHeight), none(false) {} + nsSVGViewBoxRect(const nsSVGViewBoxRect& rhs) : + x(rhs.x), y(rhs.y), width(rhs.width), height(rhs.height), none(rhs.none) {} + bool operator==(const nsSVGViewBoxRect& aOther) const; +}; + +class nsSVGViewBox +{ +public: + + void Init(); + + /** + * Returns true if the corresponding "viewBox" attribute defined a rectangle + * with finite values and nonnegative width/height. + * Returns false if the viewBox was set to an invalid + * string, or if any of the four rect values were too big to store in a + * float, or the width/height are negative. + */ + bool HasRect() const; + + /** + * Returns true if the corresponding "viewBox" attribute either defined a + * rectangle with finite values or the special "none" value. + */ + bool IsExplicitlySet() const + { + if (mAnimVal || mHasBaseVal) { + const nsSVGViewBoxRect& rect = GetAnimValue(); + return rect.none || (rect.width >= 0 && rect.height >= 0); + } + return false; + } + + const nsSVGViewBoxRect& GetBaseValue() const + { return mBaseVal; } + void SetBaseValue(const nsSVGViewBoxRect& aRect, + nsSVGElement *aSVGElement); + const nsSVGViewBoxRect& GetAnimValue() const + { return mAnimVal ? *mAnimVal : mBaseVal; } + void SetAnimValue(const nsSVGViewBoxRect& aRect, + nsSVGElement *aSVGElement); + + nsresult SetBaseValueString(const nsAString& aValue, + nsSVGElement *aSVGElement, + bool aDoSetAttr); + void GetBaseValueString(nsAString& aValue) const; + + already_AddRefed<mozilla::dom::SVGAnimatedRect> + ToSVGAnimatedRect(nsSVGElement *aSVGElement); + + already_AddRefed<mozilla::dom::SVGIRect> + ToDOMBaseVal(nsSVGElement* aSVGElement); + + already_AddRefed<mozilla::dom::SVGIRect> + ToDOMAnimVal(nsSVGElement* aSVGElement); + + // Returns a new nsISMILAttr object that the caller must delete + nsISMILAttr* ToSMILAttr(nsSVGElement* aSVGElement); + +private: + + nsSVGViewBoxRect mBaseVal; + nsAutoPtr<nsSVGViewBoxRect> mAnimVal; + bool mHasBaseVal; + +public: + struct DOMBaseVal final : public mozilla::dom::SVGIRect + { + NS_DECL_CYCLE_COLLECTING_ISUPPORTS + NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(DOMBaseVal) + + DOMBaseVal(nsSVGViewBox *aVal, nsSVGElement *aSVGElement) + : mozilla::dom::SVGIRect() + , mVal(aVal) + , mSVGElement(aSVGElement) + {} + + nsSVGViewBox* mVal; // kept alive because it belongs to content + RefPtr<nsSVGElement> mSVGElement; + + float X() const override final + { + return mVal->GetBaseValue().x; + } + + float Y() const override final + { + return mVal->GetBaseValue().y; + } + + float Width() const override final + { + return mVal->GetBaseValue().width; + } + + float Height() const override final + { + return mVal->GetBaseValue().height; + } + + void SetX(float aX, mozilla::ErrorResult& aRv) final override; + void SetY(float aY, mozilla::ErrorResult& aRv) final override; + void SetWidth(float aWidth, mozilla::ErrorResult& aRv) final override; + void SetHeight(float aHeight, mozilla::ErrorResult& aRv) final override; + + virtual nsIContent* GetParentObject() const override + { + return mSVGElement; + } + + private: + virtual ~DOMBaseVal(); + }; + + struct DOMAnimVal final : public mozilla::dom::SVGIRect + { + NS_DECL_CYCLE_COLLECTING_ISUPPORTS + NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(DOMAnimVal) + + DOMAnimVal(nsSVGViewBox *aVal, nsSVGElement *aSVGElement) + : mozilla::dom::SVGIRect() + , mVal(aVal) + , mSVGElement(aSVGElement) + {} + + nsSVGViewBox* mVal; // kept alive because it belongs to content + RefPtr<nsSVGElement> mSVGElement; + + // Script may have modified animation parameters or timeline -- DOM getters + // need to flush any resample requests to reflect these modifications. + float X() const override final + { + mSVGElement->FlushAnimations(); + return mVal->GetAnimValue().x; + } + + float Y() const override final + { + mSVGElement->FlushAnimations(); + return mVal->GetAnimValue().y; + } + + float Width() const override final + { + mSVGElement->FlushAnimations(); + return mVal->GetAnimValue().width; + } + + float Height() const override final + { + mSVGElement->FlushAnimations(); + return mVal->GetAnimValue().height; + } + + void SetX(float aX, mozilla::ErrorResult& aRv) final override + { + aRv.Throw(NS_ERROR_DOM_NO_MODIFICATION_ALLOWED_ERR); + } + + void SetY(float aY, mozilla::ErrorResult& aRv) final override + { + aRv.Throw(NS_ERROR_DOM_NO_MODIFICATION_ALLOWED_ERR); + } + + void SetWidth(float aWidth, mozilla::ErrorResult& aRv) final override + { + aRv.Throw(NS_ERROR_DOM_NO_MODIFICATION_ALLOWED_ERR); + } + + void SetHeight(float aHeight, mozilla::ErrorResult& aRv) final override + { + aRv.Throw(NS_ERROR_DOM_NO_MODIFICATION_ALLOWED_ERR); + } + + virtual nsIContent* GetParentObject() const override + { + return mSVGElement; + } + + private: + virtual ~DOMAnimVal(); + + }; + + struct SMILViewBox : public nsISMILAttr + { + public: + SMILViewBox(nsSVGViewBox* aVal, nsSVGElement* aSVGElement) + : mVal(aVal), mSVGElement(aSVGElement) {} + + // 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. + nsSVGViewBox* mVal; + nsSVGElement* mSVGElement; + + // nsISMILAttr methods + virtual nsresult ValueFromString(const nsAString& aStr, + const mozilla::dom::SVGAnimationElement* aSrcElement, + nsSMILValue& aValue, + bool& aPreventCachingOfSandwich) const override; + virtual nsSMILValue GetBaseValue() const override; + virtual void ClearAnimValue() override; + virtual nsresult SetAnimValue(const nsSMILValue& aValue) override; + }; + + static nsSVGAttrTearoffTable<nsSVGViewBox, mozilla::dom::SVGAnimatedRect> + sSVGAnimatedRectTearoffTable; +}; + +#endif // __NS_SVGVIEWBOX_H__ diff --git a/dom/svg/test/MutationEventChecker.js b/dom/svg/test/MutationEventChecker.js new file mode 100644 index 0000000000..1394b6ca16 --- /dev/null +++ b/dom/svg/test/MutationEventChecker.js @@ -0,0 +1,245 @@ +// Helper class to check DOM MutationEvents +// +// Usage: +// +// * Create a new event checker: +// var eventChecker = new MutationEventChecker; +// * Set the attribute to watch +// eventChecker.watchAttr(<DOM element>, "<attribute name>"); +// * Set the events to expect (0..n) +// eventChecker.expect("add", "modify"); +// OR +// eventChecker.expect("add modify"); +// OR +// eventChecker.expect(MutationEvent.ADDITION, MutationEvent.MODIFICATION); +// +// An empty string or empty set of arguments is also fine as a way of checking +// that all expected events have been received and indicating no events are +// expected from the following code, e.g. +// +// eventChecker.expect(""); +// // changes that are not expected to generate events +// eventChecker.expect("modify"); +// // change that is expected to generate an event +// ... +// +// * Either finish listening or set the next attribute to watch +// eventChecker.finish(); +// eventChecker.watchAttr(element, "nextAttribute"); +// +// In either case a check is performed that all expected events have been +// received. +// +// * Event checking can be temporarily disabled with ignoreEvents(). The next +// call to expect() will cause it to resume. + +function MutationEventChecker() +{ + this.expectedEvents = []; + + this.watchAttr = function(element, attr) + { + if (this.attr) { + this.finish(); + } + + this.expectedEvents = []; + this.element = element; + this.attr = attr; + this.oldValue = element.getAttribute(attr); + this.giveUp = false; + this.ignore = false; + + this.element.addEventListener('DOMAttrModified', this._listener, false); + } + + this.expect = function() + { + if (this.giveUp) { + return; + } + + ok(this.expectedEvents.length == 0, + "Expecting new events for " + this.attr + + " but the following previously expected events have still not been " + + "received: " + this._stillExpecting()); + if (this.expectedEvents.length != 0) { + this.giveUp = true; + return; + } + + this.ignore = false; + + if (arguments.length == 0 || + arguments.length == 1 && arguments[0] == "") { + return; + } + + // Turn arguments object into an array + var args = Array.prototype.slice.call(arguments); + // Check for whitespace separated keywords + if (args.length == 1 && typeof args[0] === 'string' && + args[0].indexOf(' ') > 0) { + args = args[0].split(' '); + } + // Convert strings to event Ids + this.expectedEvents = args.map(this._argToEventId); + } + + // Temporarily disable event checking + this.ignoreEvents = function() + { + // Check all events have been received + ok(this.giveUp || this.expectedEvents.length == 0, + "Going to ignore subsequent events on " + this.attr + + " attribute, but we're still expecting the following events: " + + this._stillExpecting()); + + this.ignore = true; + } + + this.finish = function() + { + // Check all events have been received + ok(this.giveUp || this.expectedEvents.length == 0, + "Finishing listening to " + this.attr + + " attribute, but we're still expecting the following events: " + + this._stillExpecting()); + + this.element.removeEventListener('DOMAttrModified', this._listener, false); + this.attr = ""; + } + + this._receiveEvent = function(e) + { + if (this.giveUp || this.ignore) { + this.oldValue = e.newValue; + return; + } + + // Make sure we're expecting something at all + if (this.expectedEvents.length == 0) { + ok(false, 'Unexpected ' + this._eventToName(e.attrChange) + + ' event when none expected on ' + this.attr + ' attribute.'); + return; + } + + var expectedEvent = this.expectedEvents.shift(); + + // Make sure we got the event we expected + if (e.attrChange != expectedEvent) { + ok(false, 'Unexpected ' + this._eventToName(e.attrChange) + + ' on ' + this.attr + ' attribute. Expected ' + + this._eventToName(expectedEvent) + ' (followed by: ' + + this._stillExpecting() + ")"); + // If we get events out of sequence, it doesn't make sense to do any + // further testing since we don't really know what to expect + this.giveUp = true; + return; + } + + // Common param checking + is(e.target, this.element, + 'Unexpected node for mutation event on ' + this.attr + ' attribute'); + is(e.attrName, this.attr, 'Unexpected attribute name for mutation event'); + + // Don't bother testing e.relatedNode since Attr nodes are on the way + // out anyway (but then, so are mutation events...) + + // Event-specific checking + if (e.attrChange == MutationEvent.MODIFICATION) { + ok(this.element.hasAttribute(this.attr), + 'Attribute not set after modification'); + is(e.prevValue, this.oldValue, + 'Unexpected old value for modification to ' + this.attr + + ' attribute'); + isnot(e.newValue, this.oldValue, + 'Unexpected new value for modification to ' + this.attr + + ' attribute'); + } else if (e.attrChange == MutationEvent.REMOVAL) { + ok(!this.element.hasAttribute(this.attr), 'Attribute set after removal'); + is(e.prevValue, this.oldValue, + 'Unexpected old value for removal of ' + this.attr + + ' attribute'); + // DOM 3 Events doesn't say what value newValue will be for a removal + // event but generally empty strings are used for other events when an + // attribute isn't relevant + ok(e.newValue === "", + 'Unexpected new value for removal of ' + this.attr + + ' attribute'); + } else if (e.attrChange == MutationEvent.ADDITION) { + ok(this.element.hasAttribute(this.attr), + 'Attribute not set after addition'); + // DOM 3 Events doesn't say what value prevValue will be for an addition + // event but generally empty strings are used for other events when an + // attribute isn't relevant + ok(e.prevValue === "", + 'Unexpected old value for addition of ' + this.attr + + ' attribute'); + ok(typeof(e.newValue) == 'string' && e.newValue !== "", + 'Unexpected new value for addition of ' + this.attr + + ' attribute'); + } else { + ok(false, 'Unexpected mutation event type: ' + e.attrChange); + this.giveUp = true; + } + this.oldValue = e.newValue; + } + this._listener = this._receiveEvent.bind(this); + + this._stillExpecting = function() + { + if (this.expectedEvents.length == 0) { + return "(nothing)"; + } + var eventNames = []; + for (var i=0; i < this.expectedEvents.length; i++) { + eventNames.push(this._eventToName(this.expectedEvents[i])); + } + return eventNames.join(", "); + } + + this._eventToName = function(evtId) + { + switch (evtId) + { + case MutationEvent.MODIFICATION: + return "modification"; + case MutationEvent.ADDITION: + return "addition"; + case MutationEvent.REMOVAL: + return "removal"; + } + } + + this._argToEventId = function(arg) + { + if (typeof arg === 'number') + return arg; + + if (typeof arg !== 'string') { + ok(false, "Unexpected event type: " + arg); + return 0; + } + + switch (arg.toLowerCase()) + { + case "mod": + case "modify": + case "modification": + return MutationEvent.MODIFICATION; + + case "add": + case "addition": + return MutationEvent.ADDITION; + + case "removal": + case "remove": + return MutationEvent.REMOVAL; + + default: + ok(false, "Unexpected event name: " + arg); + return 0; + } + } +} diff --git a/dom/svg/test/a_href_destination.svg b/dom/svg/test/a_href_destination.svg new file mode 100644 index 0000000000..43e4c812f4 --- /dev/null +++ b/dom/svg/test/a_href_destination.svg @@ -0,0 +1,3 @@ +<svg xmlns="http://www.w3.org/2000/svg"> + <rect width="100%" height="100%" fill="green"/> +</svg> diff --git a/dom/svg/test/a_href_helper_01.svg b/dom/svg/test/a_href_helper_01.svg new file mode 100644 index 0000000000..8f33cea404 --- /dev/null +++ b/dom/svg/test/a_href_helper_01.svg @@ -0,0 +1,5 @@ +<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"> + <a id="a" xlink:href="a_href_destination.svg"> + <rect width="100%" height="100%"/> + </a> +</svg> diff --git a/dom/svg/test/a_href_helper_02_03.svg b/dom/svg/test/a_href_helper_02_03.svg new file mode 100644 index 0000000000..af4b7e2736 --- /dev/null +++ b/dom/svg/test/a_href_helper_02_03.svg @@ -0,0 +1,5 @@ +<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"> + <a id="a" xlink:href="initial.svg"> + <rect width="100%" height="100%"/> + </a> +</svg> diff --git a/dom/svg/test/a_href_helper_04.svg b/dom/svg/test/a_href_helper_04.svg new file mode 100644 index 0000000000..50aca28898 --- /dev/null +++ b/dom/svg/test/a_href_helper_04.svg @@ -0,0 +1,6 @@ +<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"> + <a id="a" xlink:href="initial.svg"> + <set attributeName="xlink:href" to="a_href_destination.svg"/> + <rect width="100%" height="100%"/> + </a> +</svg> diff --git a/dom/svg/test/a_href_helper_05.svg b/dom/svg/test/a_href_helper_05.svg new file mode 100644 index 0000000000..8600960b8d --- /dev/null +++ b/dom/svg/test/a_href_helper_05.svg @@ -0,0 +1,5 @@ +<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"> + <a id="a" href="a_href_destination.svg" xlink:href="a_href_fake_destination.svg"> + <rect width="100%" height="100%"/> + </a> +</svg> diff --git a/dom/svg/test/a_href_helper_06.svg b/dom/svg/test/a_href_helper_06.svg new file mode 100644 index 0000000000..9414c39892 --- /dev/null +++ b/dom/svg/test/a_href_helper_06.svg @@ -0,0 +1,5 @@ +<svg xmlns="http://www.w3.org/2000/svg"> + <a id="a" href="initial.svg"> + <rect width="100%" height="100%"/> + </a> +</svg> diff --git a/dom/svg/test/a_href_helper_07.svg b/dom/svg/test/a_href_helper_07.svg new file mode 100644 index 0000000000..9fe1d23f7f --- /dev/null +++ b/dom/svg/test/a_href_helper_07.svg @@ -0,0 +1,6 @@ +<svg xmlns="http://www.w3.org/2000/svg"> + <a id="a" href="initial.svg"> + <set attributeName="href" to="a_href_destination.svg"/> + <rect width="100%" height="100%"/> + </a> +</svg> diff --git a/dom/svg/test/animated-svg-image-helper.html b/dom/svg/test/animated-svg-image-helper.html new file mode 100644 index 0000000000..94af2098bc --- /dev/null +++ b/dom/svg/test/animated-svg-image-helper.html @@ -0,0 +1,3 @@ +<html> + <img src="animated-svg-image-helper.svg"> +</html> diff --git a/dom/svg/test/animated-svg-image-helper.svg b/dom/svg/test/animated-svg-image-helper.svg new file mode 100644 index 0000000000..5f6f564e92 --- /dev/null +++ b/dom/svg/test/animated-svg-image-helper.svg @@ -0,0 +1,3 @@ +<svg xmlns="http://www.w3.org/2000/svg"> + <set attributeName="font-size" to="50"/> +</svg> diff --git a/dom/svg/test/bbox-helper.svg b/dom/svg/test/bbox-helper.svg new file mode 100644 index 0000000000..cf085b4fd7 --- /dev/null +++ b/dom/svg/test/bbox-helper.svg @@ -0,0 +1,38 @@ +<?xml version="1.0"?> +<svg xmlns="http://www.w3.org/2000/svg"> + <g transform="scale(0.5)"> + <foreignObject id="fO" x="10" y="10" width="100" height="100"/> + <image id="i" x="10" y="10" width="100" height="100"/> + </g> + <text id="b" x="20" y="20">b</text> + <text id="a" x="20" y="30">a</text> + <text id="y" x="20" y="40">y</text> + <text id="tspan"> + <tspan x="20" y="20">b</tspan> + </text> + <text id="text" x="20" y="60">text</text> + <!-- ‎ is the same as the HTML ‎ --> + <text id="lrmText" x="20" y="60">‎text</text> + <g id="v"> + <circle cx="100" cy="50" r="5"/> + <path d="M 100,100 L 100,200"/> + </g> + <g id="h"> + <circle cx="200" cy="50" r="5"/> + <path d="M 200,100 L 300,100"/> + </g> + <g id="e"> + <!-- empty container should not affect parent's bbox --> + <g/> + <!-- neither should a path, --> + <path/> + <!-- a polygon --> + <polygon/> + <!-- or an empty text element --> + <text x="185" y="25"/> + <circle cx="100" cy="100" r="5"/> + <g/> + <circle cx="100" cy="100" r="5"/> + <g/> + </g> +</svg> diff --git a/dom/svg/test/bounds-helper.svg b/dom/svg/test/bounds-helper.svg new file mode 100644 index 0000000000..aaa87ee686 --- /dev/null +++ b/dom/svg/test/bounds-helper.svg @@ -0,0 +1,75 @@ +<?xml version="1.0"?> +<svg xmlns="http://www.w3.org/2000/svg" width="750" + xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1"> + <style type="text/css"> +text { font: 20px monospace; } + </style> + +<g id="g"> + <svg id="svg1" x="10" y="10" width="25" height="30"/> + <svg id="svg2" width="1" height="1" overflow="visible"> + <rect width="2" height="2" fill="yellow"/> + </svg> + <svg id="svg3" width="1" height="1" overflow="hidden"> + <rect width="2" height="2" fill="yellow"/> + </svg> + <text id="text1" x="25" y="25">abc</text> + <text id="text1a" x="85" y="25" stroke="black" stroke-width="4">abc</text> + <rect id="rect1" x="50" y="50" width="50" height="50" fill="green"/> + <rect id="rect1a" x="50" y="50" width="50" height="50" fill="none" stroke-width="4" stroke="yellow"/> + <text id="text2" x="125" y="25">abc</text> + <text id="text2a" x="185" y="25" stroke="black" stroke-width="10">abc</text> + <g transform="rotate(45 175 75)"> + <rect id="rect2" x="150" y="50" width="50" height="50" fill="yellow"/> + <rect id="rect2a" x="150" y="50" width="50" height="50" fill="none" stroke-width="4" stroke="blue"/> + <text id="text3" x="150" y="50" text-anchor="middle">abc</text> + </g> + <g transform="scale(2)"> + <rect id="rect3" x="25" y="80" width="50" height="50" fill="green"/> + <rect id="rect3a" x="25" y="80" width="50" height="50" fill="none" stroke-width="4" stroke="blue"/> + <rect id="rect3b" vector-effect="non-scaling-stroke" x="100" y="100" width="25" height="25" fill="orange" stroke-width="4" stroke="yellow"/> + <image id="i" x="10" y="10" width="100" height="100"/> + </g> + <g transform="scale(2) rotate(45 175 75)"> + <rect id="rect4" x="150" y="50" width="50" height="50" fill="yellow"/> + <rect id="rect4a" x="150" y="50" width="50" height="50" fill="none" stroke-width="4" stroke="blue"/> + </g> + <text id="text4" x="185" y="25"/> + <g id="g2"> + <rect x="100" y="100" width="50" height="50" fill="pink"/> + <text x="200" y="200"/> + </g> + <circle id="nonScalingStrokedCircle1" cx="0" cy="0" r="10" + transform="translate(45 130) scale(3 -2)" + fill="none" stroke="gray" stroke-width="10" + vector-effect="non-scaling-stroke"/> + <ellipse id="nonScalingStrokedEllipse1" cx="20" cy="-10" rx="5" ry="5" + transform="matrix(0 3 -2 0 0 0)" + fill="none" stroke="steelblue" stroke-width="10" + vector-effect="non-scaling-stroke" /> + <line id="nonScalingStrokedLine1" x1="120" y1="5" x2="120" y2="10" + transform="scale(2 3)" + stroke-width="10" stroke-linecap="round" stroke="orange" + vector-effect="non-scaling-stroke" /> + <line id="nonScalingStrokedLine2" x1="130" y1="5" x2="140" y2="5" + transform="rotate(45 260 15) scale(2 3)" + stroke-width="10" stroke-linecap="square" stroke="crimson" + vector-effect="non-scaling-stroke" /> + <line id="nonScalingStrokedLine3" x1="140" y1="5" x2="150" y2="5" + transform="rotate(45 280 15) scale(2 3)" + stroke-width="10" stroke-linecap="butt" stroke="indigo" + vector-effect="non-scaling-stroke" /> + + <marker id="marker1" markerWidth="100" markerHeight="100" + refX="0" refY="50" markerUnits="userSpaceOnUse"> + <line x1="0" y1="50" x2="50" y2="100" stroke="aqua" stroke-width="20" + transform="rotate(-45 0 50)" /> + </marker> + <line id="shapeWithMarker1" x1="160" y1="130" x2="170" y2="130" + stroke="black" stroke-width="3" marker-end="url(#marker1)"/> + + <line id="rotatedLine1" x1="160" y1="150" x2="180" y2="170" + stroke="darkmagenta" stroke-width="10" + transform="rotate(-45 160 150)" /> +</g> +</svg> diff --git a/dom/svg/test/dataTypes-helper.svg b/dom/svg/test/dataTypes-helper.svg new file mode 100644 index 0000000000..e2a17e31c3 --- /dev/null +++ b/dom/svg/test/dataTypes-helper.svg @@ -0,0 +1,20 @@ +<?xml version="1.0"?> +<svg xmlns="http://www.w3.org/2000/svg" width="750"> + <defs> + <filter id="filter"> + <!-- <boolean> (preserveAlpha) --> + <!-- <enum> (edgeMode) --> + <!-- <number> (divisor) --> + <!-- <integer> (targetX) --> + <!-- <integer-optional-integer> (order) --> + <!-- <string> (result) --> + <feConvolveMatrix id="convolve"/> + <!-- <number-optional-number> (stdDeviation) --> + <feGaussianBlur id="blur"/> + </filter> + <!-- <angle> (orient) --> + <!-- <length> (markerWidth) --> + <!-- <preserveAspectRatio> (preserveAspectRatio) --> + <marker id="marker"/> + </defs> +</svg> diff --git a/dom/svg/test/fragments-helper.svg b/dom/svg/test/fragments-helper.svg new file mode 100644 index 0000000000..c7fa8fca7a --- /dev/null +++ b/dom/svg/test/fragments-helper.svg @@ -0,0 +1,4 @@ +<?xml version="1.0"?> +<svg xmlns="http://www.w3.org/2000/svg"> + <view id="view" viewBox="0 200 100 100" preserveAspectRatio="none"/> +</svg> diff --git a/dom/svg/test/getBBox-method-helper.svg b/dom/svg/test/getBBox-method-helper.svg new file mode 100644 index 0000000000..acf8c8365b --- /dev/null +++ b/dom/svg/test/getBBox-method-helper.svg @@ -0,0 +1,299 @@ +<?xml version="1.0" encoding="UTF-8"?> +<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" + viewBox="0 0 500 500" width="500px" height="500px"> + <defs> + <clipPath id="rect01" clip-rule="evenodd" clipPathUnits="objectBoundingBox"> + <rect x="0" y="0" width="0.5" height="1.0"/> + </clipPath> + <clipPath id="rect02" clip-rule="evenodd" clipPathUnits="objectBoundingBox"> + <rect x="0.5" y="0" width="0.5" height="1.0"/> + </clipPath> + <clipPath id="rect03" clip-rule="evenodd" clipPathUnits="objectBoundingBox"> + <rect x="0.5" y="0" width="0.5" height="1.0"/> + </clipPath> + <clipPath id="rect04" clip-rule="evenodd" clipPathUnits="objectBoundingBox"> + <rect x="0" y="0" width="0.5" height="1.0"/> + </clipPath> + <clipPath id="rect05" clip-rule="evenodd"> + <rect x="0" y="60" width="10px" height="23px"/> + </clipPath> + <clipPath id="rect06" clip-rule="evenodd"> + <rect x="10" y="60" width="10px" height="23px"/> + </clipPath> + <clipPath id="rect4" clip-rule="evenodd"> + <rect x="200" y="200" width="200" height="200"/> + </clipPath> + <clipPath id="rect-none" clip-rule="evenodd"> + </clipPath> + <clipPath id="rect5" clip-rule="evenodd"> + <rect x="0" y="0" width="100" height="100"/> + </clipPath> + <clipPath id="rect6" clip-rule="evenodd"> + <rect x="150" y="0" width="100" height="100"/> + </clipPath> + <clipPath id="rect7" clip-rule="evenodd"> + <rect x="0" y="100" width="100" height="100"/> + </clipPath> + <clipPath id="rect8" clip-rule="evenodd"> + <rect x="10" y="10" width="180" height="180"/> + </clipPath> + <clipPath id="rect9" clip-rule="evenodd"> + <rect x="100" y="100" width="200" height="200"/> + </clipPath> + + <clipPath id="circle1" clip-rule="evenodd"> + <circle cx="203" cy="203" r="150"/> + </clipPath> + <clipPath id="circle2" clip-rule="evenodd" clipPathUnits="objectBoundingBox"> + <circle cx="0.5" cy="0.5" r="0.25"/> + </clipPath> + <clipPath id="circle3" clip-rule="evenodd"> + <circle cx="100" cy="100" r="50"/> + <circle cx="300" cy="300" r="50"/> + </clipPath> + + <clipPath id="circle4" clip-rule="evenodd"> + <circle cx="50" cy="50" r="50"/> + </clipPath> + <clipPath id="circle5" clip-rule="evenodd"> + <circle cx="150" cy="50" r="50"/> + </clipPath> + <clipPath id="circle6" clip-rule="evenodd"> + <circle cx="50" cy="200" r="50"/> + </clipPath> + <clipPath id="circle7" clip-rule="evenodd" clipPathUnits="objectBoundingBox"> + <circle cx="0.5" cy="0.5" r="0.5"/> + </clipPath> + + <clipPath id="circle8" clip-rule="evenodd" clipPathUnits="userSpaceOnUse"> + <circle cx="110" cy="20" r="90"/> + </clipPath> + + <clipPath id="circle9" clip-rule="evenodd" clipPathUnits="userSpaceOnUse"> + <circle cx="290" cy="20" r="90"/> + </clipPath> + + <clipPath id="circle10" clip-rule="evenodd" clipPathUnits="userSpaceOnUse"> + <circle cx="110" cy="200" r="90"/> + </clipPath> + + <clipPath id="circle11" clip-rule="evenodd"> + <circle cx="0" cy="0" r="150"/> + </clipPath> + + <clipPath id="star" clip-rule="evenodd"> + <path d="M400,25 L619,703 43,283 757,283 181,703 z" /> + </clipPath> + + <marker id="m_atr" markerUnits="strokeWidth" markerWidth="3" markerHeight="3" viewBox="0 0 10 10" refX="5" refY="5"> + <polygon points="0,0 5,5 0,10 10,5" fill="red"/> + </marker> + + <switch> + <rect id="rect-10" x="20" y="20" width="180" height="180" fill="blue" stroke="cyan" stroke-width="8"/> + <rect id="rect-11" x="200" y="20" width="180" height="180" fill="lightgreen" stroke="none" /> + <rect id="rect-12" x="20" y="200" width="180" height="180" fill="darkcyan" stroke="none" /> + </switch> + + <clipPath id="clipCircle1"> + <circle id="c1" cx="100" cy="100" r="50"/> + </clipPath> + + <clipPath id="clipCircle2"> + <circle id="c2" cx="150" cy="150" r="50"/> + </clipPath> + + <clipPath id="clipPath1"> + <path id="p1" d="M10 10l100 0 0 100 -100 0ZM50 50l40 0 0 40 -40 0Z" clip-rule="evenodd"/> + </clipPath> + + <!-- "If a valid 'clip-path' reference is placed on one of the children of a 'clipPath' element, + then the given child element is clipped by the referenced clipping path before OR'ing the + silhouette of the child element with the silhouettes of the other child elements." --> + + <clipPath id="clipRects1"> + <rect x="50" y="30" width="25" height="100"/> + <rect x="25" y="50" width="10" height="10" clip-path="url(#clipTwoCircles)"/> + </clipPath> + + <!-- Test use in a clipPath --> + <clipPath id="clipTwoCircles"> + <use xlink:href="#c1"/> + <use xlink:href="#c2"/> + </clipPath> + + <clipPath id="clipInClip1"> + <use xlink:href="#c2" clip-path="url(#clipCircle1)"/> + <use xlink:href="#p1"/> + </clipPath> + + <clipPath id="clipOnClip1" clip-path="url(#clipCircle1)"> + <use xlink:href="#c2"/> + <use xlink:href="#p1"/> + </clipPath> + + </defs> + + <!-- text --> + <text id="text1" font-size="20px" font-familiy="monospace" fill="red" x="0" y="50" clip-path="url('#rect01')">99</text> + <text id="text2" font-size="20px" font-familiy="monospace" fill="blue" x="100" y="120" clip-path="url('#rect02')">99</text> + <text id="text3" font-size="20px" font-familiy="monospace" clip-path="url('#rect03')" x="0" y="120"> + <tspan x="0" y="50" fill="red">99</tspan> + </text> + <text id="text4" font-size="20px" font-familiy="monospace" clip-path="url('#rect04')" x="0" y="120"> + <tspan x="100" y="120" fill="blue">99</tspan> + </text> + <text id="text5" font-size="20px" font-familiy="monospace" fill="red" x="0" y="80" clip-path="url('#rect05')">99</text> + <text id="text6" font-size="20px" font-familiy="monospace" fill="blue" x="0" y="80" clip-path="url('#rect06')">99</text> + + <!-- image --> + <image id="image1" x="150" y="150" width="200" height="200" preserveApectRatio="none" clip="rect(200,300,300,200)" + xlink:href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAIAAACQkWg2AAAAKXRFWHRDcmVhdGlvbiBUaW1lAJCFIDMwIDQgMjAxNCAwOToyODo0MyArMDkwMH73aTcAAAAHdElNRQfeBB4AHhEfV06+AAAACXBIWXMAAB7BAAAewQHDaVRTAAAABGdBTUEAALGPC/xhBQAAABlJREFUeNpjZPj/n4EUwESS6lENoxqGlAYASU8CHux7qQ4AAAAASUVORK5CYII="/> + + <image id="image2" x="2" y="2" width="200" height="200" clip-path="url('#circle1')" preserveApectRatio="none" + xlink:href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAIAAACQkWg2AAAAKXRFWHRDcmVhdGlvbiBUaW1lAJCFIDMwIDQgMjAxNCAwOToyODo0MyArMDkwMH73aTcAAAAHdElNRQfeBB4AHhEfV06+AAAACXBIWXMAAB7BAAAewQHDaVRTAAAABGdBTUEAALGPC/xhBQAAABlJREFUeNpjZPj/n4EUwESS6lENoxqGlAYASU8CHux7qQ4AAAAASUVORK5CYII="/> + + <image id="image3" x="205" y="2" width="200" height="200" clip-path="url('#circle1')" preserveApectRatio="none" + xlink:href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAIAAACQkWg2AAAAKXRFWHRDcmVhdGlvbiBUaW1lAJCFIDMwIDQgMjAxNCAwOToyODo0MyArMDkwMH73aTcAAAAHdElNRQfeBB4AHhEfV06+AAAACXBIWXMAAB7BAAAewQHDaVRTAAAABGdBTUEAALGPC/xhBQAAABlJREFUeNpjZPj/n4EUwESS6lENoxqGlAYASU8CHux7qQ4AAAAASUVORK5CYII="/> + + <image id="image4" x="2" y="205" width="200" height="200" clip-path="url('#circle1')" preserveApectRatio="none" + xlink:href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAIAAACQkWg2AAAAKXRFWHRDcmVhdGlvbiBUaW1lAJCFIDMwIDQgMjAxNCAwOToyODo0MyArMDkwMH73aTcAAAAHdElNRQfeBB4AHhEfV06+AAAACXBIWXMAAB7BAAAewQHDaVRTAAAABGdBTUEAALGPC/xhBQAAABlJREFUeNpjZPj/n4EUwESS6lENoxqGlAYASU8CHux7qQ4AAAAASUVORK5CYII="/> + + <image id="image5" x="205" y="205" width="200" height="200" clip-path="url('#circle1')" preserveApectRatio="none" + xlink:href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAIAAACQkWg2AAAAKXRFWHRDcmVhdGlvbiBUaW1lAJCFIDMwIDQgMjAxNCAwOToyODo0MyArMDkwMH73aTcAAAAHdElNRQfeBB4AHhEfV06+AAAACXBIWXMAAB7BAAAewQHDaVRTAAAABGdBTUEAALGPC/xhBQAAABlJREFUeNpjZPj/n4EUwESS6lENoxqGlAYASU8CHux7qQ4AAAAASUVORK5CYII="/> + + <image id="image6" x="2" y="2" width="200" height="200" clip-path="url('#circle2')" preserveApectRatio="none" + xlink:href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAIAAACQkWg2AAAAKXRFWHRDcmVhdGlvbiBUaW1lAJCFIDMwIDQgMjAxNCAwOToyODo0MyArMDkwMH73aTcAAAAHdElNRQfeBB4AHhEfV06+AAAACXBIWXMAAB7BAAAewQHDaVRTAAAABGdBTUEAALGPC/xhBQAAABlJREFUeNpjZPj/n4EUwESS6lENoxqGlAYASU8CHux7qQ4AAAAASUVORK5CYII="/> + + <image id="image7" x="205" y="2" width="200" height="200" clip-path="url('#circle2')" preserveApectRatio="none" + xlink:href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAIAAACQkWg2AAAAKXRFWHRDcmVhdGlvbiBUaW1lAJCFIDMwIDQgMjAxNCAwOToyODo0MyArMDkwMH73aTcAAAAHdElNRQfeBB4AHhEfV06+AAAACXBIWXMAAB7BAAAewQHDaVRTAAAABGdBTUEAALGPC/xhBQAAABlJREFUeNpjZPj/n4EUwESS6lENoxqGlAYASU8CHux7qQ4AAAAASUVORK5CYII="/> + + <image id="image8" x="2" y="205" width="200" height="200" clip-path="url('#circle2')" preserveApectRatio="none" + xlink:href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAIAAACQkWg2AAAAKXRFWHRDcmVhdGlvbiBUaW1lAJCFIDMwIDQgMjAxNCAwOToyODo0MyArMDkwMH73aTcAAAAHdElNRQfeBB4AHhEfV06+AAAACXBIWXMAAB7BAAAewQHDaVRTAAAABGdBTUEAALGPC/xhBQAAABlJREFUeNpjZPj/n4EUwESS6lENoxqGlAYASU8CHux7qQ4AAAAASUVORK5CYII="/> + + <image id="image9" x="205" y="205" width="200" height="200" clip-path="url('#circle2')" preserveApectRatio="none" + xlink:href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAIAAACQkWg2AAAAKXRFWHRDcmVhdGlvbiBUaW1lAJCFIDMwIDQgMjAxNCAwOToyODo0MyArMDkwMH73aTcAAAAHdElNRQfeBB4AHhEfV06+AAAACXBIWXMAAB7BAAAewQHDaVRTAAAABGdBTUEAALGPC/xhBQAAABlJREFUeNpjZPj/n4EUwESS6lENoxqGlAYASU8CHux7qQ4AAAAASUVORK5CYII="/> + + <image id="image10" x="0" y="0" width="400" height="400" clip-path="url('#rect4')" + xlink:href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAIAAACQkWg2AAAAKXRFWHRDcmVhdGlvbiBUaW1lAJCFIDMwIDQgMjAxNCAwOToyODo0MyArMDkwMH73aTcAAAAHdElNRQfeBB4AHhEfV06+AAAACXBIWXMAAB7BAAAewQHDaVRTAAAABGdBTUEAALGPC/xhBQAAABlJREFUeNpjZPj/n4EUwESS6lENoxqGlAYASU8CHux7qQ4AAAAASUVORK5CYII="/> + + <image id="image11" x="0" y="0" width="400" height="400" clip-path="url('#rect-none')" + xlink:href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAIAAACQkWg2AAAAKXRFWHRDcmVhdGlvbiBUaW1lAJCFIDMwIDQgMjAxNCAwOToyODo0MyArMDkwMH73aTcAAAAHdElNRQfeBB4AHhEfV06+AAAACXBIWXMAAB7BAAAewQHDaVRTAAAABGdBTUEAALGPC/xhBQAAABlJREFUeNpjZPj/n4EUwESS6lENoxqGlAYASU8CHux7qQ4AAAAASUVORK5CYII="/> + + <image id="image12" x="25" y="43" width="768" height="768" clip-path="url('#star')" preserveApectRatio="none" + xlink:href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAIAAACQkWg2AAAAKXRFWHRDcmVhdGlvbiBUaW1lAJCFIDMwIDQgMjAxNCAwOToyODo0MyArMDkwMH73aTcAAAAHdElNRQfeBB4AHhEfV06+AAAACXBIWXMAAB7BAAAewQHDaVRTAAAABGdBTUEAALGPC/xhBQAAABlJREFUeNpjZPj/n4EUwESS6lENoxqGlAYASU8CHux7qQ4AAAAASUVORK5CYII="/> + + <image id="image13" x="0" y="0" width="400" height="400" clip-path="url('#circle3')" + xlink:href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAIAAACQkWg2AAAAKXRFWHRDcmVhdGlvbiBUaW1lAJCFIDMwIDQgMjAxNCAwOToyODo0MyArMDkwMH73aTcAAAAHdElNRQfeBB4AHhEfV06+AAAACXBIWXMAAB7BAAAewQHDaVRTAAAABGdBTUEAALGPC/xhBQAAABlJREFUeNpjZPj/n4EUwESS6lENoxqGlAYASU8CHux7qQ4AAAAASUVORK5CYII="/> + + <image id="image14" x="0" y="0" width="400" height="400" clip-path="url('#m_atr')" + xlink:href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAIAAACQkWg2AAAAKXRFWHRDcmVhdGlvbiBUaW1lAJCFIDMwIDQgMjAxNCAwOToyODo0MyArMDkwMH73aTcAAAAHdElNRQfeBB4AHhEfV06+AAAACXBIWXMAAB7BAAAewQHDaVRTAAAABGdBTUEAALGPC/xhBQAAABlJREFUeNpjZPj/n4EUwESS6lENoxqGlAYASU8CHux7qQ4AAAAASUVORK5CYII="/> + + <!-- path --> + <path id="path1" d="M10,50 L25,100 H110 V50 Q60,0 10,50" stroke="black" stroke-width="8" stroke-miterlimit="1" stroke-linejoin="miter" fill="lightcyan" marker-mid="url(#m_atr)"/> + <path id="path2" d="M160,50 L175,100 H260 V50 Q210,0 160,50" stroke="black" stroke-width="8" stroke-linejoin="bevel" fill="lightcyan" marker-mid="url(#m_atr)"/> + <path id="path3" d="M10,150 L25,200 H110 V150 Q60,100 10,150" stroke="black" stroke-width="8" stroke-linejoin="round" fill="lightcyan" marker-mid="url(#m_atr)"/> + + + <path id="path4" d="M10,50 L25,100 H110 V50 Q60,0 10,50" stroke="black" stroke-width="8" stroke-miterlimit="1" stroke-linejoin="miter" fill="lightcyan" + marker-mid="url(#m_atr)" clip-path="url(#circle4)"/> + + <path id="path5" d="M160,50 L175,100 H260 V50 Q210,0 160,50" stroke="black" stroke-width="8" stroke-linejoin="bevel" fill="lightcyan" + marker-mid="url(#m_atr)" clip-path="url(#circle5)"/> + + <path id="path6" d="M10,150 L25,200 H110 V150 Q60,100 10,150" stroke="black" stroke-width="8" stroke-linejoin="round" fill="lightcyan" + marker-mid="url(#m_atr)" clip-path="url(#circle6)"/> + + <path id="path7" d="M10,50 L25,100 H110 V50 Q60,0 10,50" + stroke="black" stroke-width="8" stroke-miterlimit="1" stroke-linejoin="miter" fill="lightcyan" + clip-path="url('#rect5')" marker-mid="url(#m_atr)"/> + + <path id="path8" d="M160,50 L175,100 H260 V50 Q210,0 160,50" + stroke="black" stroke-width="8" stroke-linejoin="bevel" fill="lightcyan" + clip-path="url('#rect6')" marker-mid="url(#m_atr)"/> + + <path id="path9" d="M10,150 L25,200 H110 V150 Q60,100 10,150" + stroke="black" stroke-width="8" stroke-linejoin="round" fill="lightcyan" + clip-path="url('#rect7')" marker-mid="url(#m_atr)"/> + + <path id="path10" d="M10,50 L25,100 H110 V50 Q60,0 10,50" + stroke="black" stroke-width="8" stroke-miterlimit="1" stroke-linejoin="miter" fill="lightcyan" + clip-path="url('#circle7')" marker-mid="url(#m_atr)"/> + + <path id="path11" d="M160,50 L175,100 H260 V50 Q210,0 160,50" + stroke="black" stroke-width="8" stroke-linejoin="bevel" fill="lightcyan" + clip-path="url('#circle7')" marker-mid="url(#m_atr)"/> + + <path id="path12" d="M10,150 L25,200 H110 V150 Q60,100 10,150" + stroke="black" stroke-width="8" stroke-linejoin="round" fill="lightcyan" + clip-path="url('#circle7')" marker-mid="url(#m_atr)"/> + + <!-- use --> + <use id="use1" xlink:href="#rect-10" x="50" y="50" clip-path="url('#circle8')"/> + <use id="use2" xlink:href="#rect-11" x="50" y="50" clip-path="url('#circle9')"/> + <use id="use3" xlink:href="#rect-12" x="50" y="50" clip-path="url('#circle10')"/> + + <use id="use4" xlink:href="#rect-10" x="2" y="2" width="200" height="200" clip-path="url('#circle11')"/> + <use id="use5" xlink:href="#rect-10" x="205" y="2" width="200" height="200" clip-path="url('#circle11')"/> + <use id="use6" xlink:href="#rect-10" x="2" y="205" width="200" height="200" clip-path="url('#circle11')"/> + <use id="use7" xlink:href="#rect-10" x="205" y="205" width="200" height="200" clip-path="url('#circle11')"/> + + <use id="use8" xlink:href="#rect-10" x="50" y="50" clip-path="url('#m_atr')"/> + + <!-- foreignObject --> + <foreignObject id="fo1" x="2" y="2" width="200" height="200" clip-path="url('#circle1')" clip="rect(2,102,102,2)"> + <div xmlns="http://www.w3.org/1999/xhtml" style="background-color:pink;width:100%;height:100%;"> +There are issues of dynamic loading required for tiling. Accroding to 'postpone' attribute of Resource Priorities, the dynamic loading is controlled by positional relation with bounding box of embedded contents and container's viewport. However, each bounding boxes of embedded contents should be whole earth basically when this method is used. (green part on Example) Tiling is impossible unless this situation is changed. + </div> + </foreignObject> + <foreignObject id="fo2" x="205" y="2" width="200" height="200" clip-path="url('#circle1')" > + <div xmlns="http://www.w3.org/1999/xhtml" style="background-color:pink;width:100%;height:100%;"> +There are issues of dynamic loading required for tiling. Accroding to 'postpone' attribute of Resource Priorities, the dynamic loading is controlled by positional relation with bounding box of embedded contents and container's viewport. However, each bounding boxes of embedded contents should be whole earth basically when this method is used. (green part on Example) Tiling is impossible unless this situation is changed. + </div> + </foreignObject> + <foreignObject id="fo3" x="2" y="205" width="200" height="200" clip-path="url('#circle1')" > + <div xmlns="http://www.w3.org/1999/xhtml" style="background-color:pink;width:100%;height:100%;"> +There are issues of dynamic loading required for tiling. Accroding to 'postpone' attribute of Resource Priorities, the dynamic loading is controlled by positional relation with bounding box of embedded contents and container's viewport. However, each bounding boxes of embedded contents should be whole earth basically when this method is used. (green part on Example) Tiling is impossible unless this situation is changed. + </div> + </foreignObject> + <foreignObject id="fo4" x="205" y="205" width="200" height="200" clip-path="url('#circle1')" clip="rect(2,102,102,2)"> + <div xmlns="http://www.w3.org/1999/xhtml" style="background-color:pink;width:100%;height:100%;"> +There are issues of dynamic loading required for tiling. Accroding to 'postpone' attribute of Resource Priorities, the dynamic loading is controlled by positional relation with bounding box of embedded contents and container's viewport. However, each bounding boxes of embedded contents should be whole earth basically when this method is used. (green part on Example) Tiling is impossible unless this situation is changed. + </div> + </foreignObject> + + <foreignObject id="fo5" x="250" y="250" width="200" height="200" clip-path="url('#rect8')"> + <div xmlns="http://www.w3.org/1999/xhtml" style="background-color:pink;width:100%;height:100%;"> +There are issues of dynamic loading required for tiling. Accroding to 'postpone' attribute of Resource Priorities, the dynamic loading is controlled by positional relation with bounding box of embedded contents and container's viewport. However, each bounding boxes of embedded contents should be whole earth basically when this method is used. (green part on Example) Tiling is impossible unless this situation is changed. + </div> + </foreignObject> + + <foreignObject id="fo6" x="0" y="0" width="200" height="200" clip-path="url('#rect9')"> + <div xmlns="http://www.w3.org/1999/xhtml" style="background-color:pink;width:100%;height:100%;"> +There are issues of dynamic loading required for tiling. Accroding to 'postpone' attribute of Resource Priorities, the dynamic loading is controlled by positional relation with bounding box of embedded contents and container's viewport. However, each bounding boxes of embedded contents should be whole earth basically when this method is used. (green part on Example) Tiling is impossible unless this situation is changed. + </div> + </foreignObject> + + <foreignObject id="fo7" x="0" y="0" width="200" height="200" clip-path="url('#rect8')"> + <div xmlns="http://www.w3.org/1999/xhtml" style="background-color:pink;width:100%;height:100%;"> +There are issues of dynamic loading required for tiling. Accroding to 'postpone' attribute of Resource Priorities, the dynamic loading is controlled by positional relation with bounding box of embedded contents and container's viewport. However, each bounding boxes of embedded contents should be whole earth basically when this method is used. (green part on Example) Tiling is impossible unless this situation is changed. + </div> + </foreignObject> + + <foreignObject id="fo8" x="0" y="0" width="200" height="200" clip-path="url('#m_atr')"> + <div xmlns="http://www.w3.org/1999/xhtml" style="background-color:pink;width:100%;height:100%;"> +There are issues of dynamic loading required for tiling. Accroding to 'postpone' attribute of Resource Priorities, the dynamic loading is controlled by positional relation with bounding box of embedded contents and container's viewport. However, each bounding boxes of embedded contents should be whole earth basically when this method is used. (green part on Example) Tiling is impossible unless this situation is changed. + </div> + </foreignObject> + + <!-- --> + <rect id="rect-1" width="200" height="200" fill="blue" clip-path="url(#clipInClip1)"/> + <rect id="rect-2" width="200" height="200" fill="blue" clip-path="url(#clipRects1)"/> + <rect id="rect-3" width="300" height="300" fill="blue" clip-path="url(#clipOnClip1)"/> + + <g clip-path="url(#clipCircle1)" id="g1"> + <use xlink:href="#c2" fill="red"/> + <use xlink:href="#p1" fill="red" fill-rule="evenodd"/> + </g> + +</svg> diff --git a/dom/svg/test/getCTM-helper.svg b/dom/svg/test/getCTM-helper.svg new file mode 100644 index 0000000000..835efc5067 --- /dev/null +++ b/dom/svg/test/getCTM-helper.svg @@ -0,0 +1,47 @@ +<?xml version="1.0"?> +<svg xmlns="http://www.w3.org/2000/svg" + xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="100" height="100" viewBox="-11 -22 100 100"> + <g transform="translate(3, 4)"> + <svg x="1" y="2" width="888" height="999"> + <g> + <svg x="30" y="40" width="100" height="100"> + <g id="buggy"/> + </svg> + + <defs> + <symbol id="sym" width="100" height="100"> + <rect id="symbolRect" width="0" height="0" + transform="translate(70, 80)"/> + </symbol> + </defs> + <svg id="inner" x="30" y="40" width="100" height="100"> + <g id="g1"/> + </svg> + <svg id="inner-2" viewBox="0 0 10 10" width="-10" height="10"> + <g id="g5"/> + </svg> + <foreignObject id="fO" x="30" y="40" width="100" height="100" transform="translate(1, 1)"> + <!-- current layout implementation ignores x="50" and y="60". + thus, I made getCTM and getScreenCTM do the same. --> + <svg id="outer" x="50" y="60" width="100" height="100"> + <g id="g2" transform="translate(600, 700)"/> + </svg> + </foreignObject> + <foreignObject x="30" y="40" width="100" height="100" transform="translate(1, 1)"> + <html xmlns="http://www.w3.org/1999/xhtml" style="width: 100%; height: 100%"> + <svg xmlns="http://www.w3.org/2000/svg" id="outer2" + width="100" height="100" viewBox="100 100 200 200"/> + </html> + </foreignObject> + <!-- something invalid --> + <foreignObject> + <g id="g3"/> + </foreignObject> + <image> + <g id="g4"/> + </image> + <use xlink:href="#sym" id="use"/> + </g> + </svg> + </g> +</svg> diff --git a/dom/svg/test/getSubStringLength-helper.svg b/dom/svg/test/getSubStringLength-helper.svg new file mode 100644 index 0000000000..6c80d9d46b --- /dev/null +++ b/dom/svg/test/getSubStringLength-helper.svg @@ -0,0 +1,7 @@ +<?xml version="1.0"?> +<svg xmlns="http://www.w3.org/2000/svg" width="750"> + <style type="text/css"> +.t { font: 20px monospace; } + </style> + <text id="text" x="5" class="t" y="25">abc</text> +</svg> diff --git a/dom/svg/test/matrixUtils.js b/dom/svg/test/matrixUtils.js new file mode 100644 index 0000000000..9e316ed07a --- /dev/null +++ b/dom/svg/test/matrixUtils.js @@ -0,0 +1,76 @@ +/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- + * vim: sw=2 ts=2 et lcs=trail\:.,tab\:>~ : + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/* + * Utilities for testing SVG matrices + */ + +function createMatrix(a, b, c, d, e, f) +{ + var svg = document.getElementsByTagName("svg")[0]; + var m = svg.createSVGMatrix(); + m.a = a; + m.b = b; + m.c = c; + m.d = d; + m.e = e; + m.f = f; + return m; +} + +// Lightweight dummy Matrix class for representing arrays that get passed in +function MatrixFromArray(a) +{ + this.a = a[0]; + this.b = a[1]; + this.c = a[2]; + this.d = a[3]; + this.e = a[4]; + this.f = a[5]; +} + +function cmpMatrix(a, b, msg) +{ + if (a.constructor === Array) + a = new MatrixFromArray(a); + if (b.constructor === Array) + b = new MatrixFromArray(b); + + ok(a.a == b.a && + a.b == b.b && + a.c == b.c && + a.d == b.d && + a.e == b.e && + a.f == b.f, + msg + " - got " + formatMatrix(a) + + ", expected " + formatMatrix(b)); +} + +function roughCmpMatrix(a, b, msg) +{ + if (a.constructor === Array) + a = new MatrixFromArray(a); + if (b.constructor === Array) + b = new MatrixFromArray(b); + + const tolerance = 1 / 65535; + ok(Math.abs(b.a - a.a) < tolerance && + Math.abs(b.b - a.b) < tolerance && + Math.abs(b.c - a.c) < tolerance && + Math.abs(b.d - a.d) < tolerance && + Math.abs(b.e - a.e) < tolerance && + Math.abs(b.f - a.f) < tolerance, + msg + " - got " + formatMatrix(a) + + ", expected " + formatMatrix(b)); +} + +function formatMatrix(m) +{ + if (m.constructor != Array) + return "(" + [m.a, m.b, m.c, m.d, m.e, m.f].join(', ') + ")"; + + return "(" + m.join(', ') + ")"; +} diff --git a/dom/svg/test/mochitest.ini b/dom/svg/test/mochitest.ini new file mode 100644 index 0000000000..c44776b2ff --- /dev/null +++ b/dom/svg/test/mochitest.ini @@ -0,0 +1,109 @@ +[DEFAULT] +support-files = + MutationEventChecker.js + a_href_destination.svg + a_href_helper_01.svg + a_href_helper_02_03.svg + a_href_helper_04.svg + a_href_helper_05.svg + a_href_helper_06.svg + a_href_helper_07.svg + animated-svg-image-helper.html + animated-svg-image-helper.svg + bbox-helper.svg + bounds-helper.svg + dataTypes-helper.svg + fragments-helper.svg + getBBox-method-helper.svg + getCTM-helper.svg + getSubStringLength-helper.svg + matrixUtils.js + object-delayed-intrinsic-size.sjs + pointer-events.js + scientific-helper.svg + selectSubString-helper.svg + switch-helper.svg + text-helper-scaled.svg + text-helper-selection.svg + text-helper.svg + viewport-helper.svg + zoom-helper.svg + +[test_a_href_01.xhtml] +[test_a_href_02.xhtml] +[test_animLengthObjectIdentity.xhtml] +[test_animLengthReadonly.xhtml] +[test_animLengthUnits.xhtml] +[test_bbox-with-invalid-viewBox.xhtml] +[test_bbox.xhtml] +[test_bbox-changes.xhtml] +[test_bounds.html] +[test_bug872812.html] +[test_getBBox-method.html] +[test_dataTypes.html] +[test_dataTypesModEvents.html] +[test_fragments.html] +[test_getCTM.html] +[test_getElementById.xhtml] +[test_getSubStringLength.xhtml] +[test_lang.xhtml] +skip-if = true # disabled-for-intermittent-failures--bug-701060 +[test_length.xhtml] +[test_lengthParsing.html] +[test_markerOrient.xhtml] +[test_nonAnimStrings.xhtml] +[test_non-scaling-stroke.html] +[test_object-delayed-intrinsic-size.html] +[test_onerror.xhtml] +[test_pathAnimInterpolation.xhtml] +[test_pathLength.html] +[test_pathSeg.xhtml] +[test_pointAtLength.xhtml] +[test_pointer-events-1a.xhtml] +[test_pointer-events-1b.xhtml] +[test_pointer-events-2.xhtml] +[test_pointer-events-3.xhtml] +skip-if = android_version == '18' # bug 1147994 +[test_pointer-events-4.xhtml] +[test_pointer-events-5.xhtml] +[test_pointer-events-6.xhtml] +[test_pointer-events-7.xhtml] +[test_scientific.html] +[test_selectSubString.xhtml] +[test_style_sheet.html] +[test_stroke-hit-testing.xhtml] +[test_stroke-linecap-hit-testing.xhtml] +[test_SVGLengthList-2.xhtml] +[test_SVGLengthList.xhtml] +[test_SVGMatrix.xhtml] +[test_SVG_namespace_ids.html] +[test_SVGNumberList.xhtml] +[test_SVGPathSegList.xhtml] +[test_SVGPointList.xhtml] +[test_SVGStringList.xhtml] +[test_SVGStyleElement.xhtml] +[test_SVGTransformListAddition.xhtml] +[test_SVGTransformList.xhtml] +[test_SVGUnitTypes.html] +[test_SVGxxxListIndexing.xhtml] +[test_SVGxxxList.xhtml] +[test_switch.xhtml] +[test_tabindex.html] +[test_tearoff_with_cc.html] +support-files = tearoff_with_cc_helper.html +[test_text_2.html] +[test_text_dirty.html] +[test_text.html] +[test_text_lengthAdjust.html] +[test_text_scaled.html] +[test_text_selection.html] +[test_text_update.html] +[test_transform.xhtml] +[test_transformParsing.html] +[test_use_with_hsts.html] +support-files = use-with-hsts-helper.html use-with-hsts-helper.html^headers^ +[test_valueAsString.xhtml] +[test_valueLeaks.xhtml] +[test_viewport.html] +[test_zoom.xhtml] + diff --git a/dom/svg/test/object-delayed-intrinsic-size.sjs b/dom/svg/test/object-delayed-intrinsic-size.sjs new file mode 100644 index 0000000000..0f98c4dc1e --- /dev/null +++ b/dom/svg/test/object-delayed-intrinsic-size.sjs @@ -0,0 +1,24 @@ + +var timer = null; + +function handleRequest(request, response) +{ + response.processAsync(); + + response.setStatusLine(null, 200, "OK"); + response.setHeader("Content-Type", "image/svg+xml", false); + + // We need some body output or else gecko will not do an initial reflow + // while waiting for the rest of the document to load: + response.bodyOutputStream.write("\n", 1); + + timer = Components.classes["@mozilla.org/timer;1"] + .createInstance(Components.interfaces.nsITimer); + timer.initWithCallback(function() + { + var body = "<svg xmlns='http://www.w3.org/2000/svg' width='70' height='0'></svg>"; + response.bodyOutputStream.write(body, body.length); + response.finish(); + }, 1000 /* milliseconds */, Components.interfaces.nsITimer.TYPE_ONE_SHOT); +} + diff --git a/dom/svg/test/pointer-events.js b/dom/svg/test/pointer-events.js new file mode 100644 index 0000000000..cf5d6cb709 --- /dev/null +++ b/dom/svg/test/pointer-events.js @@ -0,0 +1,328 @@ +SimpleTest.waitForExplicitFinish(); + +var pointer_events_values = [ + 'auto', + 'visiblePainted', + 'visibleFill', + 'visibleStroke', + 'visible', + 'painted', + 'fill', + 'stroke', + 'all', + 'none' +]; + +var paint_values = [ + 'blue', + 'transparent', + 'none' +]; + +var opacity_values = [ + '1', + '0.5', + '0' +]; + +var visibility_values = [ + 'visible', + 'hidden', + 'collapse' +]; + +/** + * List of attributes and various values for which we want to test permutations + * when hit testing a pointer event that is over an element's fill area, + * stroke area, or both (where they overlap). + * + * We're using an array of objects so that we have control over the order in + * which permutations are tested. + * + * TODO: test the effect of clipping, masking, filters, markers, etc. + */ +var hit_test_inputs = { + fill: [ + { name: 'pointer-events', values: pointer_events_values }, + { name: 'fill', values: paint_values }, + { name: 'fill-opacity', values: opacity_values }, + { name: 'opacity', values: opacity_values }, + { name: 'visibility', values: visibility_values } + ], + stroke: [ + { name: 'pointer-events', values: pointer_events_values }, + { name: 'stroke', values: paint_values }, + { name: 'stroke-opacity', values: opacity_values }, + { name: 'opacity', values: opacity_values }, + { name: 'visibility', values: visibility_values } + ], + both: [ + { name: 'pointer-events', values: pointer_events_values }, + { name: 'fill', values: paint_values }, + { name: 'fill-opacity', values: opacity_values }, + { name: 'stroke', values: paint_values }, + { name: 'stroke-opacity', values: opacity_values }, + { name: 'opacity', values: opacity_values }, + { name: 'visibility', values: visibility_values } + ] +} + +/** + * The following object contains a list of 'pointer-events' property values, + * each with an object detailing the conditions under which the fill and stroke + * of a graphical object will intercept pointer events for the given value. If + * the object contains a 'fill-intercepts-iff' property then the fill is + * expected to intercept pointer events for that value of 'pointer-events' if + * and only if the conditions listed in the 'fill-intercepts-iff' object are + * met. If there are no conditions in the 'fill-intercepts-iff' object then the + * fill should always intercept pointer events. However, if the + * 'fill-intercepts-iff' property is not set at all then it indicates that the + * fill should never intercept pointer events. The same rules apply for + * 'stroke-intercepts-iff'. + * + * If an attribute name in the conditions list is followed by the "!" + * character then the requirement for a hit is that its value is NOT any + * of the values listed in the given array. + */ +var hit_conditions = { + auto: { + 'fill-intercepts-iff': { + 'visibility': ['visible'], + 'fill!': ['none'] + }, + 'stroke-intercepts-iff': { + 'visibility': ['visible'], + 'stroke!': ['none'] + } + }, + visiblePainted: { + 'fill-intercepts-iff': { + 'visibility': ['visible'], + 'fill!': ['none'] + }, + 'stroke-intercepts-iff': { + 'visibility': ['visible'], + 'stroke!': ['none'] + } + }, + visibleFill: { + 'fill-intercepts-iff': { + visibility: ['visible'] + } + // stroke never intercepts pointer events + }, + visibleStroke: { + // fill never intercepts pointer events + 'stroke-intercepts-iff': { + visibility: ['visible'] + } + }, + visible: { + 'fill-intercepts-iff': { + visibility: ['visible'] + }, + 'stroke-intercepts-iff': { + visibility: ['visible'] + } + }, + painted: { + 'fill-intercepts-iff': { + 'fill!': ['none'] + }, + 'stroke-intercepts-iff': { + 'stroke!': ['none'] + } + }, + fill: { + 'fill-intercepts-iff': { + // fill always intercepts pointer events + } + // stroke never intercepts pointer events + }, + stroke: { + // fill never intercepts pointer events + 'stroke-intercepts-iff': { + // stroke always intercepts pointer events + } + }, + all: { + 'fill-intercepts-iff': { + // fill always intercepts pointer events + }, + 'stroke-intercepts-iff': { + // stroke always intercepts pointer events + } + }, + none: { + // neither fill nor stroke intercept pointer events + } +} + +// bit flags +var POINT_OVER_FILL = 0x1; +var POINT_OVER_STROKE = 0x2; + +/** + * Examine the element's attribute values and, based on the area(s) of the + * element that the pointer event is over (fill and/or stroke areas), return + * true if the element is expected to intercept the event, otherwise false. + */ +function hit_expected(element, over /* bit flags indicating which area(s) of the element the pointer is over */) +{ + function expect_hit(target){ + var intercepts_iff = + hit_conditions[element.getAttribute('pointer-events')][target + '-intercepts-iff']; + + if (!intercepts_iff) { + return false; // never intercepts events + } + + for (var attr in intercepts_iff) { + var vals = intercepts_iff[attr]; // must get this before we adjust 'attr' + var invert = false; + if (attr.substr(-1) == '!') { + invert = true; + attr = attr.substr(0, attr.length-1); + } + var match = vals.indexOf(element.getAttribute(attr)) > -1; + if (invert) { + match = !match; + } + if (!match) { + return false; + } + } + + return true; + } + + return (over & POINT_OVER_FILL) != 0 && expect_hit('fill') || + (over & POINT_OVER_STROKE) != 0 && expect_hit('stroke'); +} + +function for_all_permutations(inputs, callback) +{ + var current_permutation = arguments[2] || {}; + var index = arguments[3] || 0; + + if (index < inputs.length) { + var name = inputs[index].name; + var values = inputs[index].values; + for (var i = 0; i < values.length; ++i) { + current_permutation[name] = values[i]; + for_all_permutations(inputs, callback, current_permutation, index+1); + } + return; + } + + callback(current_permutation); +} + +function make_log_msg(over, tag, attributes) +{ + var target; + if (over == (POINT_OVER_FILL | POINT_OVER_STROKE)) { + target = 'fill and stroke'; + } else if (over == POINT_OVER_FILL) { + target = 'fill'; + } else if (over == POINT_OVER_STROKE) { + target = 'stroke'; + } else { + throw "unexpected bit combination in 'over'"; + } + var msg = 'Check if events are intercepted at a point over the '+target+' on <'+tag+'> for'; + for (var attr in attributes) { + msg += ' '+attr+'='+attributes[attr]; + } + return msg; +} + +var dx, dy; // offset of <svg> element from pointer coordinates origin + +function test_element(id, x, y, over /* bit flags indicating which area(s) of the element the pointer is over */) +{ + var element = document.getElementById(id); + var tag = element.tagName; + + function test_permutation(attributes) { + for (var attr in attributes) { + element.setAttribute(attr, attributes[attr]); + } + var hits = document.elementFromPoint(dx + x, dy + y) == element; + var msg = make_log_msg(over, tag, attributes); + + is(hits, hit_expected(element, over), msg); + } + + var inputs; + if (over == (POINT_OVER_FILL | POINT_OVER_STROKE)) { + inputs = hit_test_inputs['both']; + } else if (over == POINT_OVER_FILL) { + inputs = hit_test_inputs['fill']; + } else if (over == POINT_OVER_STROKE) { + inputs = hit_test_inputs['stroke']; + } else { + throw "unexpected bit combination in 'over'"; + } + + for_all_permutations(inputs, test_permutation); + + // To reduce the chance of bogus results in subsequent tests: + element.setAttribute('fill', 'none'); + element.setAttribute('stroke', 'none'); +} + +function run_tests(subtest) +{ + var div = document.getElementById("div"); + dx = div.offsetLeft; + dy = div.offsetTop; + + // Run the test with only a subset of pointer-events values, to avoid + // running over the mochitest time limit. The subtest argument indicates + // whether to use the first half of the pointer-events values (0) + // or the second half (1). + var partition = Math.floor(pointer_events_values.length / 2); + switch (subtest) { + case 0: + pointer_events_values.splice(partition); + break; + case 1: + pointer_events_values.splice(0, partition); + break; + case 2: + throw "unexpected subtest number"; + } + + test_element('rect', 30, 30, POINT_OVER_FILL); + test_element('rect', 5, 5, POINT_OVER_STROKE); + + // The SVG 1.1 spec essentially says that, for text, hit testing is done + // against the character cells of the text, and not the fill and stroke as + // you might expect for a normal graphics element like <path>. See the + // paragraph starting "For text elements..." in this section: + // + // http://www.w3.org/TR/SVG11/interact.html#PointerEventsProperty + // + // This requirement essentially means that for the purposes of hit testing + // the fill and stroke areas are the same area - the character cell. (At + // least until we support having any fill or stroke that lies outside the + // character cells intercept events like Opera does - see below.) Thus, for + // text, when a pointer event is over a character cell it is essentially over + // both the fill and stroke at the same time. That's the reason we pass both + // the POINT_OVER_FILL and POINT_OVER_STROKE bits in test_element's 'over' + // argument below. It's also the reason why we only test one point in the + // text rather than having separate tests for fill and stroke. + // + // For hit testing of text, Opera essentially treats fill and stroke like it + // would on any normal element, but it adds the character cells of glyhs to + // both the glyphs' fill AND stroke. I think this is what we should do too. + // It's compatible with the letter of the SVG 1.1 rules, and it allows any + // parts of a glyph that are outside the glyph's character cells to also + // intercept events in the normal way. When we make that change we'll be able + // to add separate fill and stroke tests for text below. + + test_element('text', 210, 30, POINT_OVER_FILL | POINT_OVER_STROKE); + + SimpleTest.finish(); +} diff --git a/dom/svg/test/scientific-helper.svg b/dom/svg/test/scientific-helper.svg new file mode 100644 index 0000000000..8ac2227e62 --- /dev/null +++ b/dom/svg/test/scientific-helper.svg @@ -0,0 +1,5 @@ +<?xml version="1.0"?> +<svg xmlns="http://www.w3.org/2000/svg" width="750"> + <rect id="rect" stroke-width="10"/> + <text id="text">x</text> +</svg> diff --git a/dom/svg/test/selectSubString-helper.svg b/dom/svg/test/selectSubString-helper.svg new file mode 100644 index 0000000000..6c80d9d46b --- /dev/null +++ b/dom/svg/test/selectSubString-helper.svg @@ -0,0 +1,7 @@ +<?xml version="1.0"?> +<svg xmlns="http://www.w3.org/2000/svg" width="750"> + <style type="text/css"> +.t { font: 20px monospace; } + </style> + <text id="text" x="5" class="t" y="25">abc</text> +</svg> diff --git a/dom/svg/test/switch-helper.svg b/dom/svg/test/switch-helper.svg new file mode 100644 index 0000000000..842296efa7 --- /dev/null +++ b/dom/svg/test/switch-helper.svg @@ -0,0 +1,12 @@ +<?xml version="1.0"?> +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/licenses/publicdomain/ +--> +<svg version="1.1" xmlns="http://www.w3.org/2000/svg"> + <switch id="s"> + <rect systemLanguage="fr" id="first" x="75" y="100" width="70" height="70" fill="yellow"/> + <rect id="second" x="75" y="100" width="50" height="50" fill="lime"/> + <rect id="third" x="75" y="100" width="80" height="80" fill="red"/> + </switch> +</svg> diff --git a/dom/svg/test/tearoff_with_cc_helper.html b/dom/svg/test/tearoff_with_cc_helper.html new file mode 100644 index 0000000000..6d63e939fe --- /dev/null +++ b/dom/svg/test/tearoff_with_cc_helper.html @@ -0,0 +1,36 @@ +<!DOCTYPE html> +<html> +<body onload="go()"> + <svg id="outerSvg" width="50%" height="50%" + style="border: 1px solid black"> + </svg> + <script type="application/javascript"> + /* I'm not sure what exactly was required to trigger bug 1288228's crash, + * but it involved tweaking a length's specified units and cycle-collecting + * and reloading (in some combination). So, we'll tweak the units and + * cycle-collect a few times, and message the outer page to reload us + * after we've made the first tweak. + */ + const maxTweaks = 5; + let remainingTweaks = maxTweaks; + + var savedBaseVal = document.getElementById("outerSvg").width.baseVal; + function go() { + window.parent.SpecialPowers.DOMWindowUtils.cycleCollect(); + tweak(); + } + + function tweak() { + console.log("tweaked"); + savedBaseVal.convertToSpecifiedUnits(SVGLength.SVG_LENGTHTYPE_PX); + savedBaseVal.convertToSpecifiedUnits(SVGLength.SVG_LENGTHTYPE_PERCENTAGE); + if (remainingTweaks == maxTweaks) { + window.parent.postMessage("ping", "*"); // only do this on first tweak + } + if (--remainingTweaks) { + setTimeout(tweak, 0); + } + } +</script> +</body> +</html> diff --git a/dom/svg/test/test_SVGLengthList-2.xhtml b/dom/svg/test/test_SVGLengthList-2.xhtml new file mode 100644 index 0000000000..9567a6cdb2 --- /dev/null +++ b/dom/svg/test/test_SVGLengthList-2.xhtml @@ -0,0 +1,65 @@ +<html xmlns="http://www.w3.org/1999/xhtml"> +<!-- +https://bugzilla.mozilla.org/show_bug.cgi?id=630760 +--> +<head> + <title>Tests specific to SVGLengthList</title> + <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" /> +</head> +<body> +<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=515116">Mozilla Bug 630760</a> +<p id="display"></p> +<div id="content" style="display:none;"> +<svg id="svg" xmlns="http://www.w3.org/2000/svg" width="100" height="100"> + <text id="text"> + <set attributeName="x" to="10 20 30 40" begin="0" dur="indefinite"/> + </text> +</svg> +</div> +<pre id="test"> +<script class="testbody" type="text/javascript"> +<![CDATA[ + +SimpleTest.waitForExplicitFinish(); + +function run_tests() +{ + var svg = document.getElementById('svg'); + svg.pauseAnimations(); + + // Check that the animVal list for 'x' on <text> gives the correct number of + // items when examined for the FIRST time DURING animation: + + var text = document.getElementById("text"); + var list = text.x.animVal; + + is(list.numberOfItems, 4, 'Checking numberOfItems'); + + // Check that items at an index larger than 255 (max value for PRUint8) are + // returning the correct values: + + var item; + list = text.x.baseVal; + for (var i = 0; i < 256; ++i) { + item = svg.createSVGLength(); + item.value = 1; + list.appendItem(item); + } + item = svg.createSVGLength(); + item.value = 2; + list.appendItem(item); + + is(list.getItem(0).value, 1, 'Check value of first item'); + is(list.getItem(256).value, 2, 'Check value of item at index > 255'); + + SimpleTest.finish(); +} + +window.addEventListener("load", run_tests, false); + +]]> +</script> +</pre> +</body> +</html> diff --git a/dom/svg/test/test_SVGLengthList.xhtml b/dom/svg/test/test_SVGLengthList.xhtml new file mode 100644 index 0000000000..3e0dab736a --- /dev/null +++ b/dom/svg/test/test_SVGLengthList.xhtml @@ -0,0 +1,159 @@ +<html xmlns="http://www.w3.org/1999/xhtml"> +<!-- +https://bugzilla.mozilla.org/show_bug.cgi?id=515116 +--> +<head> + <title>Tests specific to SVGLengthList</title> + <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <script type="text/javascript" src="MutationEventChecker.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" /> +</head> +<body> +<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=515116">Mozilla Bug 515116</a> +<p id="display"></p> +<div id="content" style="display:none;"> +<svg id="svg" xmlns="http://www.w3.org/2000/svg" width="100" height="100"> + <text id="text" x="10cm 20cm 30mc"/> + <rect id="rect" x="40" y="50"/> + <text id="text2" x="60"/> +</svg> +</div> +<pre id="test"> +<script class="testbody" type="text/javascript"> +<![CDATA[ + +SimpleTest.waitForExplicitFinish(); + +/* +This file runs a series of SVGLengthList specific tests. Generic SVGXxxList +tests can be found in test_SVGxxxList.xhtml. Anything that can be generalized +to other list types belongs there. +*/ + +function run_tests() +{ + document.getElementById('svg').pauseAnimations(); + + var text = document.getElementById("text"); + var lengths = text.x.baseVal; + + is(lengths.numberOfItems, 0, 'Checking numberOfItems'); + + // Test mutation events + // --- Initialization + eventChecker = new MutationEventChecker; + eventChecker.watchAttr(text, "x"); + eventChecker.expect("modify"); + text.textContent = "abc"; + text.setAttribute("x", "10 20 30"); + is(lengths.numberOfItems, 3, 'Checking numberOfItems'); + // -- Actual changes + eventChecker.expect("modify modify modify modify modify"); + lengths[0].value = 8; + lengths[0].valueInSpecifiedUnits = 9; + lengths[0].valueAsString = "10"; + lengths[0].convertToSpecifiedUnits(SVGLength.SVG_LENGTHTYPE_CM); + lengths[0].newValueSpecifiedUnits(SVGLength.SVG_LENGTHTYPE_MM, 11); + // -- Redundant changes + eventChecker.expect("modify"); + lengths[0].valueAsString = "10"; + eventChecker.expect(""); + lengths[0].value = 10; + lengths[0].valueInSpecifiedUnits = 10; + lengths[0].valueAsString = "10"; + lengths[0].convertToSpecifiedUnits(SVGLength.SVG_LENGTHTYPE_NUMBER); + lengths[0].newValueSpecifiedUnits(SVGLength.SVG_LENGTHTYPE_NUMBER, 10); + // -- Invalid attribute + eventChecker.expect("modify"); + text.setAttribute("x", ",20"); + is(lengths.numberOfItems, 0, 'Checking that parsing stops at invalid token'); + // -- Attribute removal + eventChecker.expect("remove"); + text.removeAttribute("x"); + // -- Non-existent attribute removal + eventChecker.expect(""); + text.removeAttribute("x"); + text.removeAttributeNS(null, "x"); + eventChecker.finish(); + + // Test that the addition of an owned SVGLength to an SVGLengthList creates a + // copy of the SVGLength, and an unowned SVGLength does not make a copy + var text2 = document.getElementById("text2"); + var rect = document.getElementById("rect"); + var subtests = [ + function initialize(aItem) { + text.removeAttribute("x"); + return lengths.initialize(aItem); + }, + function insertItemBefore(aItem) { + text.removeAttribute("x"); + return lengths.insertItemBefore(aItem, 0); + }, + function replaceItem(aItem) { + text.setAttribute("x", "10"); + return lengths.replaceItem(aItem, 0); + }, + function appendItem(aItem) { + text.removeAttribute("x"); + return lengths.appendItem(aItem); + } + ]; + subtests.forEach(function(aFunction) { + // -- Adding an unowned SVGLength + var name = aFunction.name; + var existingItem = document.getElementById("svg").createSVGLength(); + var newItem = aFunction(existingItem); + is(newItem, lengths.getItem(0), name + " return value is correct when passed an unowned object"); + is(newItem, existingItem, name + " did not make a copy when passed an unowned object"); + }); + subtests.forEach(function(aFunction) { + // -- Adding an SVGLength that is a baseVal + var name = aFunction.name; + var existingItem = rect.x.baseVal; + var newItem = aFunction(existingItem); + is(newItem, lengths.getItem(0), name + " return value is correct when passed a baseVal"); + isnot(newItem, existingItem, name + " made a copy when passed a baseVal"); + is(newItem.value, existingItem.value, name + " made a copy with the right values when passed a baseVal"); + is(rect.x.baseVal, existingItem, name + " left the original object alone when passed a baseVal"); + }); + subtests.forEach(function(aFunction) { + // -- Adding an SVGLength that is an animVal + var name = aFunction.name; + var existingItem = rect.x.animVal; + var newItem = aFunction(existingItem); + is(newItem, lengths.getItem(0), name + " return value is correct when passed an animVal"); + isnot(newItem, existingItem, name + " made a copy when passed an animVal"); + is(newItem.value, existingItem.value, name + " made a copy with the right values when passed an animVal"); + is(rect.x.animVal, existingItem, name + " left the original object alone when passed an animVal"); + }); + subtests.forEach(function(aFunction) { + // -- Adding an SVGLength that is in a baseVal list + var name = aFunction.name; + var existingItem = text2.x.baseVal.getItem(0); + var newItem = aFunction(existingItem); + is(newItem, lengths.getItem(0), name + " return value is correct when passed a baseVal list item"); + isnot(newItem, existingItem, name + " made a copy when passed a baseVal list item"); + is(newItem.value, existingItem.value, name + " made a copy with the right values when passed a baseVal list item"); + is(text2.x.baseVal.getItem(0), existingItem, name + " left the original object alone when passed a baseVal list item"); + }); + subtests.forEach(function(aFunction) { + // -- Adding an SVGLength that is in a animVal list + var name = aFunction.name; + var existingItem = text2.x.animVal.getItem(0); + var newItem = aFunction(existingItem); + is(newItem, lengths.getItem(0), name + " return value is correct when passed a animVal list item"); + isnot(newItem, existingItem, name + " made a copy when passed a animVal list item"); + is(newItem.value, existingItem.value, name + " made a copy with the right values when passed a animVal list item"); + is(text2.x.animVal.getItem(0), existingItem, name + " left the original object alone when passed a animVal list item"); + }); + + SimpleTest.finish(); +} + +window.addEventListener("load", run_tests, false); + +]]> +</script> +</pre> +</body> +</html> diff --git a/dom/svg/test/test_SVGMatrix.xhtml b/dom/svg/test/test_SVGMatrix.xhtml new file mode 100644 index 0000000000..e6fc057bc9 --- /dev/null +++ b/dom/svg/test/test_SVGMatrix.xhtml @@ -0,0 +1,194 @@ +<html xmlns="http://www.w3.org/1999/xhtml"> +<head> + <title>Test SVGMatrix behavior</title> + <script type="text/javascript" src="/MochiKit/packed.js"></script> + <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <script type="text/javascript" src="matrixUtils.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" /> +</head> +<body> +<p id="display"></p> +<div id="content"> + <svg xmlns="http://www.w3.org/2000/svg" width="100%" height="1" id="svg"> + <g id="g" transform="translate(10, 20)"/> + </svg> +</div> +<pre id="test"> +<script class="testbody" type="text/javascript"> +<![CDATA[ + +SimpleTest.waitForExplicitFinish(); + +function main() +{ + var tests = + [ testCreateMatrix, + testMultiply, + testInverse, + testTranslate, + testScale, + testScaleNonUniform, + testRotate, + testRotateFromVector, + testFlipX, + testFlipY, + testSkewX, + testSkewY + ]; + for (var i = 0; i < tests.length; i++) { + tests[i](); + } + SimpleTest.finish(); +} + +function testCreateMatrix() +{ + svg = $('svg'); + var m = svg.createSVGMatrix(); + + // Should be initialised to identity + cmpMatrix(m, [1, 0, 0, 1, 0, 0], + "createMatrix should produce identity matrix"); + + // Should return a new object each time; + ok(m != svg.createSVGMatrix(), + "Got identical objects when creating new matrix"); +} + +// SVGMatrix multiply(in SVGMatrix secondMatrix); +function testMultiply() +{ + // This is the example from SVG 1.1 section 7.5 + var m1 = createMatrix(1, 0, 0, 1, 50, 90); + var m2 = createMatrix(0.707, -0.707, 0.707, 0.707, 0, 0); + var m3 = createMatrix(1, 0, 0, 1, 130, 160); + var result = m1.multiply(m2).multiply(m3); + roughCmpMatrix(result, [0.707, -0.707, 0.707, 0.707, 255.03, 111.21], + "Unexpected result after multiplying matrices"); + + // Check orig matrices are unchanged + cmpMatrix(m1, [1, 0, 0, 1, 50, 90], "Matrix changed after multiplication"); + roughCmpMatrix(m2, [0.707, -0.707, 0.707, 0.707, 0, 0], + "Matrix changed after multiplication"); + cmpMatrix(m3, [1, 0, 0, 1, 130, 160], "Matrix changed after multiplication"); +} + +// SVGMatrix inverse() raises(SVGException); +function testInverse() +{ + // Test inversion + var m = createMatrix(2, 0, 0, 4, 110, -50); + roughCmpMatrix(m.inverse(), [0.5, 0, 0, 0.25, -55, 12.5], + "Unexpected result after inverting matrix"); + + // Test non-invertable + m = createMatrix(0, 0, 1, 0, 0, 0); + try { + m.inverse(); + ok(false, "Failed to throw exception when inverting singular matrix"); + } catch (e) { + is(e.name, "InvalidStateError", + "Got unexpected exception " + e + ", expected InvalidStateError"); + } +} + +// SVGMatrix translate(in float x, in float y); +function testTranslate() +{ + var m = createMatrix(2, 0, 0, 1, 120, 100); + roughCmpMatrix(m.translate(100, -50), [2, 0, 0, 1, 320, 50], + "Unexpected result after translate"); +} + +// SVGMatrix scale(in float scaleFactor); +function testScale() +{ + var m = createMatrix(2, 0, 0, 1, 120, 100); + roughCmpMatrix(m.scale(0.5), [1, 0, 0, 0.5, 120, 100], + "Unexpected result after scale"); +} + +// SVGMatrix scaleNonUniform(in float scaleFactorX, in float scaleFactorY); +function testScaleNonUniform() +{ + var m = createMatrix(2, 0, 0, 1, 120, 100); + roughCmpMatrix(m.scaleNonUniform(0.5, -3), [1, 0, 0, -3, 120, 100], + "Unexpected result after scaleNonUniform"); +} + +// SVGMatrix rotate(in float angle); +function testRotate() +{ + var m = createMatrix(2, 0, 0, 1, 120, 100); + roughCmpMatrix(m.rotate(45), + [2*Math.cos(Math.PI/4), Math.sin(Math.PI/4), + 2*-Math.sin(Math.PI/4), Math.cos(Math.PI/4), + 120, 100], + "Unexpected result after rotate"); +} + +// SVGMatrix rotateFromVector(in float x, in float y) raises(SVGException); +function testRotateFromVector() +{ + var m = createMatrix(2, 0, 0, 1, 120, 100); + // Make a 150 degree angle + var result = m.rotateFromVector(-2, 1.1547); + roughCmpMatrix(result, + [2*Math.cos(5*Math.PI/6), Math.sin(5*Math.PI/6), + 2*-Math.sin(5*Math.PI/6), Math.cos(5*Math.PI/6), + 120, 100], + "Unexpected result after rotateFromVector"); + + // Test bad input (1) + try { + m.rotateFromVector(1, 0); + ok(false, "Failed to throw exception with zero coord for rotateFromVector"); + } catch (e) { + is(e.name, "InvalidAccessError", + "Got unexpected exception " + e + ", expected TypeError"); + } + + // Test bad input (2) + try { + m.rotateFromVector(0, 1); + ok(false, "Failed to throw exception with zero coord for rotateFromVector"); + } catch (e) { } +} + +// SVGMatrix flipX(); +function testFlipX() +{ + var m = createMatrix(1, 2, 3, 4, 5, 6); + cmpMatrix(m.flipX(), [-1, -2, 3, 4, 5, 6], "Unexpected result after flipX"); +} + +// SVGMatrix flipY(); +function testFlipY() +{ + var m = createMatrix(1, 2, 3, 4, 5, 6); + cmpMatrix(m.flipY(), [1, 2, -3, -4, 5, 6], "Unexpected result after flipY"); +} + +// SVGMatrix skewX(in float angle); +function testSkewX() +{ + var m = createMatrix(2, 0, 0, 1, 120, 100); + roughCmpMatrix(m.skewX(30), [2, 0, 2*Math.tan(Math.PI/6), 1, 120, 100], + "Unexpected result after skewX"); +} + +// SVGMatrix skewY(in float angle); +function testSkewY() +{ + var m = createMatrix(2, 0, 0, 1, 120, 100); + roughCmpMatrix(m.skewY(30), [2, Math.tan(Math.PI/6), 0, 1, 120, 100], + "Unexpected result after skewY"); +} + +window.addEventListener("load", main, false); + +]]> +</script> +</pre> +</body> +</html> diff --git a/dom/svg/test/test_SVGNumberList.xhtml b/dom/svg/test/test_SVGNumberList.xhtml new file mode 100644 index 0000000000..9925d02cf6 --- /dev/null +++ b/dom/svg/test/test_SVGNumberList.xhtml @@ -0,0 +1,75 @@ +<html xmlns="http://www.w3.org/1999/xhtml"> +<!-- +https://bugzilla.mozilla.org/show_bug.cgi?id=629200 +--> +<head> + <title>Tests specific to SVGNumberList</title> + <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <script type="text/javascript" src="MutationEventChecker.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" /> +</head> +<body> +<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=629200">Mozilla Bug 629200</a> +<p id="display"></p> +<div id="content" style="display:none;"> +<svg id="svg" xmlns="http://www.w3.org/2000/svg" width="100" height="100"> + <text id="text" rotate="10 20 30">abc</text> +</svg> +</div> +<pre id="test"> +<script class="testbody" type="text/javascript"> +<![CDATA[ + +SimpleTest.waitForExplicitFinish(); + +/* +This file runs a series of SVGNumberList specific tests. Generic SVGXxxList +tests can be found in test_SVGxxxList.xhtml. Anything that can be generalized +to other list types belongs there. +*/ + +function run_tests() +{ + document.getElementById('svg').pauseAnimations(); + + var text = document.getElementById("text"); + var numbers = text.rotate.baseVal; + + is(numbers.numberOfItems, 3, 'Checking numberOfItems'); + + // Test mutation events + // --- Initialization + eventChecker = new MutationEventChecker; + eventChecker.watchAttr(text, "rotate"); + // -- Actual changes + eventChecker.expect("modify modify"); + numbers[0].value = 15; + text.setAttribute("rotate", "17 20 30"); + // -- Redundant changes + eventChecker.expect(""); + numbers[0].value = 17; + numbers[1].value = 20; + text.setAttribute("rotate", "17 20 30"); + // -- Invalid attribute + eventChecker.expect("modify"); + text.setAttribute("rotate", ",20"); + is(numbers.numberOfItems, 0, 'Checking that parsing stops at invalid token'); + // -- Attribute removal + eventChecker.expect("remove"); + text.removeAttribute("rotate"); + // -- Non-existent attribute removal + eventChecker.expect(""); + text.removeAttribute("rotate"); + text.removeAttributeNS(null, "rotate"); + eventChecker.finish(); + + SimpleTest.finish(); +} + +window.addEventListener("load", run_tests, false); + +]]> +</script> +</pre> +</body> +</html> diff --git a/dom/svg/test/test_SVGPathSegList.xhtml b/dom/svg/test/test_SVGPathSegList.xhtml new file mode 100644 index 0000000000..5b442676a1 --- /dev/null +++ b/dom/svg/test/test_SVGPathSegList.xhtml @@ -0,0 +1,128 @@ +<html xmlns="http://www.w3.org/1999/xhtml"> +<!-- +https://bugzilla.mozilla.org/show_bug.cgi?id=611138 +--> +<head> + <title>Generic tests for SVG animated length lists</title> + <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <script type="text/javascript" src="MutationEventChecker.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" /> +</head> +<body> +<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=611138">Mozilla Bug 611138</a> +<p id="display"></p> +<div id="content" style="display:none;"> +<svg id="svg" xmlns="http://www.w3.org/2000/svg" width="100" height="100"> + <path id="path"/> +</svg> +</div> +<pre id="test"> +<script class="testbody" type="text/javascript"> +<![CDATA[ + +SimpleTest.waitForExplicitFinish(); + +/* +This file runs a series of SVGPathSegList specific tests. Generic SVGXxxList +tests can be found in test_SVGxxxList.xhtml. Anything that can be generalized +to other list types belongs there. +*/ + +function run_tests() +{ + document.getElementById('svg').pauseAnimations(); + + var d; + var seg; + var path = document.getElementById("path"); + var list = path.pathSegList; + + // See https://bugzilla.mozilla.org/show_bug.cgi?id=611138 + // Here we are doing a replace with a segment (arc) that has more arguments + // than the total number of arguments in the entire path + 2 (the +2 + // refering to the encoding of the segment types for the two segments). + path.setAttribute('d', 'M0,0 L100,100'); + var arc = path.createSVGPathSegArcAbs(400, 0, 200, 200, 0, 1, 1); + list.replaceItem(arc, 1); + + is(list.numberOfItems, 2, 'The length of the list should be the same after a valid replaceItem() call'); + is(list.getItem(1), arc, 'The inserted object should now be the object at the index being replaced'); + + // Test whether and when we normalize the 'd' attribute: + + d = " \n M 10 , 10 \n L 20 10 \n "; + path.setAttribute('d', d); + is(path.getAttribute('d'), d, "Values passed to setAttribute for the 'd' attribute should not be normalised"); + list.getItem(1).y = 20; + isnot(path.getAttribute('d'), d, "The 'd' attribute should change when its underlying DOM list changes"); + + // Test that path manipulations still work even when the path is invalid due + // to it not starting with a moveto segment: + + path.setAttribute('d', 'M0,0 L1,1'); + is(list.numberOfItems, 2, 'setAttribute should result in two items') + + seg = list.getItem(1); + list.removeItem(0); + ok(list.numberOfItems == 1 && list.getItem(0) == seg, + 'If removeItem removes the initial moveto leaving an invalid path, the other items should still be left in the list') + + seg = path.createSVGPathSegLinetoAbs(1, 2); + list.appendItem(seg); + ok(list.numberOfItems == 2 && list.getItem(1) == seg, + 'appendItem should be able to append to an invalid path'); + + seg = path.createSVGPathSegLinetoAbs(1, 2); + list.replaceItem(seg, 1); + ok(list.numberOfItems == 2 && list.getItem(1) == seg, + 'replaceItem should be able to replace items in an invalid path'); + + seg = path.createSVGPathSegLinetoAbs(1, 2); + list.insertItemBefore(seg, 1); + ok(list.numberOfItems == 3 && list.getItem(1) == seg, + 'insertItemBefore should be able insert items into an invalid path'); + + seg = path.createSVGPathSegLinetoAbs(1, 2); + list.initialize(seg); + ok(list.numberOfItems == 1 && list.getItem(0) == seg, + 'initialize should be able initialize an invalid path with a non-moveto item'); + + // Test mutation events + + eventChecker = new MutationEventChecker; + d = 'M0,0 L12,34' + path.setAttribute('d', d); + eventChecker.watchAttr(path, "d"); + + // -- Actual changes + eventChecker.expect("modify modify modify"); + list[0].x = 10; + list[0].y = 5; + path.setAttribute("d", "M20,5 L12,34"); + + // -- Redundant changes + eventChecker.expect(""); + list[0].x = 20; + list[1].y = 34; + path.setAttribute("d", "M20,5 L12,34"); + + // -- Attribute removal + eventChecker.expect("remove"); + path.removeAttribute("d"); + + // -- Non-existent attribute removal + eventChecker.expect(""); + path.removeAttribute("d"); + path.removeAttributeNS(null, "d"); + eventChecker.finish(); + + SimpleTest.finish(); +} + +window.addEventListener("load", run_tests, false); + +]]> +</script> +</pre> +</body> +</html> diff --git a/dom/svg/test/test_SVGPointList.xhtml b/dom/svg/test/test_SVGPointList.xhtml new file mode 100644 index 0000000000..6fa1a71f40 --- /dev/null +++ b/dom/svg/test/test_SVGPointList.xhtml @@ -0,0 +1,130 @@ +<html xmlns="http://www.w3.org/1999/xhtml"> +<!-- +https://bugzilla.mozilla.org/show_bug.cgi?id=629200 +--> +<head> + <title>Tests specific to SVGPointList</title> + <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <script type="text/javascript" src="MutationEventChecker.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" /> +</head> +<body> +<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=629200">Mozilla Bug 629200</a> +<p id="display"></p> +<div id="content" style="display:none;"> +<svg id="svg" xmlns="http://www.w3.org/2000/svg" width="100" height="100"> + <polyline id="polyline" points="50,375 150,380"/> + <polyline id="polyline2" points="10,20"/> +</svg> +</div> +<pre id="test"> +<script class="testbody" type="text/javascript"> +<![CDATA[ + +SimpleTest.waitForExplicitFinish(); + +/* +This file runs a series of SVGPointList specific tests. Generic SVGXxxList +tests can be found in test_SVGxxxList.xhtml. Anything that can be generalized +to other list types belongs there. +*/ + +function run_tests() +{ + document.getElementById('svg').pauseAnimations(); + + var polyline = document.getElementById("polyline"); + var points = polyline.points; + + is(points.numberOfItems, 2, 'Checking numberOfItems'); + + // Test mutation events + // --- Initialization + eventChecker = new MutationEventChecker; + eventChecker.watchAttr(polyline, "points"); + // -- Actual changes + eventChecker.expect("modify modify"); + points[0].x = 40; + polyline.setAttribute("points", "30,375 150,380"); + // -- Redundant changes + eventChecker.expect(""); + points[0].x = 30; + points[1].y = 380; + polyline.setAttribute("points", "30,375 150,380"); + // -- Invalid attribute + eventChecker.expect("modify"); + polyline.setAttribute("points", ",30,375"); + is(points.numberOfItems, 0, 'Checking that parsing stops at invalid token'); + // -- Attribute removal + eventChecker.expect("remove"); + polyline.removeAttribute("points"); + // -- Non-existent attribute removal + eventChecker.expect(""); + polyline.removeAttribute("points"); + polyline.removeAttributeNS(null, "points"); + eventChecker.finish(); + + // Test that the addition of an owned SVGPoint to an SVGPointList creates a + // copy of the SVGPoint + var polyline2 = document.getElementById("polyline2"); + var subtests = [ + function initialize(aItem) { + polyline.removeAttribute("points"); + return points.initialize(aItem); + }, + function insertItemBefore(aItem) { + polyline.removeAttribute("points"); + return points.insertItemBefore(aItem, 0); + }, + function replaceItem(aItem) { + polyline.setAttribute("points", "10,20"); + return points.replaceItem(aItem, 0); + }, + function appendItem(aItem) { + polyline.removeAttribute("points"); + return points.appendItem(aItem); + } + ]; + subtests.forEach(function(aFunction) { + // -- Adding SVGSVGElement.currentTranslate, which is the only instance + // of an owned, single SVGPoint + var svg = document.getElementById("svg"); + var name = aFunction.name; + var existingItem = svg.currentTranslate; + var newItem = aFunction(existingItem); + is(newItem, points.getItem(0), name + " return value is correct when passed currentTranslate"); + isnot(newItem, existingItem, name + " made a copy when passed currentTranslate"); + is(newItem.value, existingItem.value, name + " made a copy with the right values when passed currentTranslate"); + todo(svg.currentTranslate == existingItem, name + " left the original object alone when passed currentTranslate"); + }); + subtests.forEach(function(aFunction) { + // -- Adding an SVGPoint that is in a baseVal list + var name = aFunction.name; + var existingItem = polyline2.points.getItem(0); + var newItem = aFunction(existingItem); + is(newItem, points.getItem(0), name + " return value is correct when passed a baseVal list item"); + isnot(newItem, existingItem, name + " made a copy when passed a baseVal list item"); + is(newItem.value, existingItem.value, name + " made a copy with the right values when passed a baseVal list item"); + is(polyline2.points.getItem(0), existingItem, name + " left the original object alone when passed a baseVal list item"); + }); + subtests.forEach(function(aFunction) { + // -- Adding an SVGPoint that is in a animVal list + var name = aFunction.name; + var existingItem = polyline2.animatedPoints.getItem(0); + var newItem = aFunction(existingItem); + is(newItem, points.getItem(0), name + " return value is correct when passed a animVal list item"); + isnot(newItem, existingItem, name + " made a copy when passed a animVal list item"); + is(newItem.value, existingItem.value, name + " made a copy with the right values when passed a animVal list item"); + is(polyline2.animatedPoints.getItem(0), existingItem, name + " left the original object alone when passed a animVal list item"); + }); + + SimpleTest.finish(); +} + +window.addEventListener("load", run_tests, false); + +]]> +</script> +</pre> +</body> +</html> diff --git a/dom/svg/test/test_SVGStringList.xhtml b/dom/svg/test/test_SVGStringList.xhtml new file mode 100644 index 0000000000..7fcc2a8577 --- /dev/null +++ b/dom/svg/test/test_SVGStringList.xhtml @@ -0,0 +1,123 @@ +<html xmlns="http://www.w3.org/1999/xhtml"> +<!-- +https://bugzilla.mozilla.org/show_bug.cgi?id=724993--> +<head> + <title>Tests specific to SVGStringList</title> + <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" /> +</head> +<body> +<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=724993">Mozilla Bug 724993</a> +<p id="display"></p> +<div id="content" style="display:none;"> +<svg id="svg" xmlns="http://www.w3.org/2000/svg" width="100" height="100"> + <g id="g" requiredFeatures="foo bar baz"/> +</svg> +</div> +<pre id="test"> +<script class="testbody" type="text/javascript"> +<![CDATA[ + +SimpleTest.waitForExplicitFinish(); + +/* +This file runs a series of SVGStringList specific tests. Generic SVGXxxList +tests can be found in test_SVGxxxList.xhtml. Anything that can be generalized +to other list types belongs there. +*/ + +function initializeThrowsFor(stringList, value) +{ + try { + stringList.initialize(value); + } catch (e) { + return true; + } + return false; +} + +function insertItemBeforeThrowsFor(stringList, value) +{ + try { + stringList.insertItemBefore(value, 0); + } catch (e) { + return true; + } + return false; +} + +function replaceItemThrowsFor(stringList, value) +{ + try { + stringList.replaceItem(value, 0); + } catch (e) { + return true; + } + return false; +} + +function appendItemThrowsFor(stringList, value) +{ + try { + stringList.appendItem(value); + } catch (e) { + return true; + } + return false; +} + +function run_tests() +{ + var g = document.getElementById("g"); + var strings = g.requiredFeatures; + + // sanity check: + is(strings.numberOfItems, 3, 'numberOfItems should be 3'); + + + ok(!initializeThrowsFor(strings, null), + "SVGStringList.initialize() should not throw when passed null"); + ok(initializeThrowsFor(strings, ""), + "SVGStringList.initialize() should throw when passed the empty string"); + is(strings.length, 0, 'length should be 0'); + + ok(!insertItemBeforeThrowsFor(strings, null), + "SVGStringList.insertItemBefore() should not throw when passed null"); + ok(insertItemBeforeThrowsFor(strings, ""), + "SVGStringList.insertItemBefore() should throw when passed the empty string"); + is(strings.length, 1, 'length should be 1'); + + ok(!replaceItemThrowsFor(strings, null), + "SVGStringList.replaceItem() should not throw when passed null"); + ok(replaceItemThrowsFor(strings, ""), + "SVGStringList.replaceItem() should throw when passed the empty string"); + is(strings.length, 1, 'length should be 1'); + + ok(!appendItemThrowsFor(strings, null), + "SVGStringList.appendItem() should not throw when passed null"); + ok(appendItemThrowsFor(strings, ""), + "SVGStringList.appendItem() should throw when passed the empty string"); + is(strings.length, 2, 'length should be 2'); + + + // more sanity checks: + ok(!initializeThrowsFor(strings, "valid-string"), + "SVGStringList.initialize() should not throw when passed a valid string"); + ok(!insertItemBeforeThrowsFor(strings, "valid-string"), + "SVGStringList.insertItemBefore() should not throw when passed a valid string"); + ok(!replaceItemThrowsFor(strings, "valid-string"), + "SVGStringList.replaceItem() should not throw when passed a valid string"); + ok(!appendItemThrowsFor(strings, "valid-string"), + "SVGStringList.appendItem() should not throw when passed a valid string"); + is(strings.length, 3, 'numberOfItems should be 3'); + + SimpleTest.finish(); +} + +window.addEventListener("load", run_tests, false); + +]]> +</script> +</pre> +</body> +</html> diff --git a/dom/svg/test/test_SVGStyleElement.xhtml b/dom/svg/test/test_SVGStyleElement.xhtml new file mode 100644 index 0000000000..19651d4973 --- /dev/null +++ b/dom/svg/test/test_SVGStyleElement.xhtml @@ -0,0 +1,33 @@ +<html xmlns="http://www.w3.org/1999/xhtml"> +<!-- +https://bugzilla.mozilla.org/show_bug.cgi?id=559024 +--> +<head> + <title>Test SVGStyleElement</title> + <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> +</head> +<body> +<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=559024">Mozilla Bug 559024</a> +<p id="display"></p> +<div id="content" style="display: none"> +<svg xmlns="http://www.w3.org/2000/svg"> +<style></style> +</svg> +</div> +<pre id="test"> +<script type="application/javascript"> +<![CDATA[ +/** Test for Bug 559024 **/ +var el = document.getElementsByTagNameNS("http://www.w3.org/2000/svg", "style")[0]; +el.media = "ssss"; +is(el.media, "ssss"); +is(el.getAttributeNS("", "media"), "ssss"); +el.title = "tttt"; +is(el.title, "tttt"); +is(el.getAttributeNS("", "title"), "tttt"); +]]> +</script> +</pre> +</body> +</html> diff --git a/dom/svg/test/test_SVGTransformList.xhtml b/dom/svg/test/test_SVGTransformList.xhtml new file mode 100644 index 0000000000..125fd7542f --- /dev/null +++ b/dom/svg/test/test_SVGTransformList.xhtml @@ -0,0 +1,455 @@ +<html xmlns="http://www.w3.org/1999/xhtml"> +<!-- +https://bugzilla.mozilla.org/show_bug.cgi?id=602759 +--> +<head> + <title>Tests specific to SVGTransformList</title> + <script type="text/javascript" src="/MochiKit/packed.js"></script> + <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <script type="text/javascript" src="matrixUtils.js"></script> + <script type="text/javascript" src="MutationEventChecker.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" /> +</head> +<body> +<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=602759"> + Mozilla Bug 602759</a> +<p id="display"></p> +<div id="content" style="display:none;"> +<svg id="svg" xmlns="http://www.w3.org/2000/svg" width="100" height="100" + onload="this.pauseAnimations();"> + <g id="g"/> +</svg> +</div> +<pre id="test"> +<script class="testbody" type="text/javascript"> +<![CDATA[ + +SimpleTest.waitForExplicitFinish(); + +/* +This file runs a series of SVGTransformList specific tests. Generic SVGXxxList +tests can be found in test_SVGxxxList.xhtml. Anything that can be generalized +to other list types belongs there. +*/ + +function main() +{ + var g = $('g'); + var tests = + [ testConsolidateMatrix, + testConsolidateMatrixOneElem, + testConsolidateMatrixZeroElem, + testCreateSVGTransformFromMatrix, + testReadOnly, + testOrphan, + testFailedSet, + testMutationEvents + ]; + for (var i = 0; i < tests.length; i++) { + tests[i](g); + } + SimpleTest.finish(); +} + +function testConsolidateMatrix(g) +{ + // This is the example from SVG 1.1 section 7.5 + g.setAttribute("transform", + "translate(50 90) rotate(-45) translate(130 160)"); + var list = g.transform.baseVal; + is(list.numberOfItems, 3, "Unexpected length of unconsolidated list"); + + // Sanity check -- take ref to first item in list and validate it + var first_item = list.getItem(0); + is(first_item.type, SVGTransform.SVG_TRANSFORM_TRANSLATE, + "Unexpected type of first item in list"); + cmpMatrix(first_item.matrix, [1, 0, 0, 1, 50, 90], + "Unexpected value for first item in list"); + + // Consolidate + var consolidated = list.consolidate(); + is(list.numberOfItems, 1, "Unexpected length of consolidated list"); + ok(consolidated === list.getItem(0), + "Consolidate return value should be first item in list, not a copy"); + is(consolidated.type, SVGTransform.SVG_TRANSFORM_MATRIX, + "Consolidated transform not of type matrix"); + const angle = -Math.PI/4; + roughCmpMatrix(consolidated.matrix, + [Math.cos(angle), Math.sin(angle), + -Math.sin(angle), Math.cos(angle), + 130 * Math.cos(angle) - 160 * Math.sin(angle) + 50, + 160 * Math.cos(angle) + 130 * Math.sin(angle) + 90], + "Unexpected result after consolidating matrices"); + + // Check ref to first item in list + // a) should not have changed + is(first_item.type, SVGTransform.SVG_TRANSFORM_TRANSLATE, + "Unexpected type of cached first item in list after consolidating"); + cmpMatrix(first_item.matrix, [1, 0, 0, 1, 50, 90], + "Unexpected value for cached first item in list after consolidating"); + // b) should still be useable + first_item.setScale(2, 3); + is(first_item.type, SVGTransform.SVG_TRANSFORM_SCALE, + "Cached first item in list not useable after consolidating"); + + // Check consolidated is live + // a) Changes to 'consolidated' affect list + consolidated.setSkewX(45); + is(list.getItem(0).type, SVGTransform.SVG_TRANSFORM_SKEWX, + "Changing return value from consolidate doesn't affect list"); + // b) Changes to list affect 'consolidated' + list.getItem(0).setRotate(90, 0, 0); + is(consolidated.type, SVGTransform.SVG_TRANSFORM_ROTATE, + "Changing list doesn't affect return value from consolidate"); +} + +function testConsolidateMatrixOneElem(g) +{ + // Check that even if we only have one item in the list it becomes a matrix + // transform (as per the spec) + g.setAttribute("transform", "translate(50 90)"); + var list = g.transform.baseVal; + is(list.numberOfItems, 1, "Unexpected length of unconsolidated list"); + var first_item = list.getItem(0); + is(first_item.type, SVGTransform.SVG_TRANSFORM_TRANSLATE, + "Unexpected type of first item in list"); + cmpMatrix(first_item.matrix, [1, 0, 0, 1, 50, 90], + "Unexpected value for first item in list"); + + // Consolidate + var consolidated = list.consolidate(); + is(list.numberOfItems, 1, "Unexpected length of consolidated list"); + ok(consolidated === list.getItem(0), + "Consolidate return value should be first item in list, not a copy"); + is(consolidated.type, SVGTransform.SVG_TRANSFORM_MATRIX, + "Consolidated transform not of type matrix"); + cmpMatrix(consolidated.matrix, [1, 0, 0, 1, 50, 90], + "Unexpected consolidated matrix value"); +} + +function testConsolidateMatrixZeroElem(g) +{ + // Check that zero items returns null + g.setAttribute("transform", ""); + var list = g.transform.baseVal; + is(list.numberOfItems, 0, "Unexpected length of unconsolidated list"); + var consolidated = list.consolidate(); + ok(consolidated === null, + "consolidate() should return null for a zero-length transform list"); +} + +function testCreateSVGTransformFromMatrix(g) +{ + var m = createMatrix(1, 2, 3, 4, 5, 6); + + // "Creates an SVGTransform object which is initialized to transform of type + // SVG_TRANSFORM_MATRIX and whose values are the given matrix. The values from + // the parameter matrix are copied, the matrix parameter is not adopted as + // SVGTransform::matrix." + var list = g.transform.baseVal; + list.clear(); + var t = list.createSVGTransformFromMatrix(m); + + // Check that list hasn't changed + is(list.numberOfItems, 0, + "Transform list changed after calling createSVGTransformFromMatrix"); + + // Check return value + is(t.type, SVGTransform.SVG_TRANSFORM_MATRIX, + "Returned transform not of type matrix"); + cmpMatrix(t.matrix, [1, 2, 3, 4, 5, 6], + "Unexpected returned matrix value"); + + // Check values are copied + ok(t.matrix != m, "Matrix should be copied not adopted"); + m.a = 2; + is(t.matrix.a, 1, + "Changing source matrix should not affect newly created transform"); + + // Try passing in bad values (null, "undefined" etc.) + var exception = null; + try { + t = list.createSVGTransformFromMatrix(null); + } catch(e) { exception = e; } + ok(exception, + "Failed to throw for null input to createSVGTransformFromMatrix"); + exception = null; + try { + t = list.createSVGTransformFromMatrix("undefined"); + } catch(e) { exception = e; } + ok(exception, + "Failed to throw for string input to createSVGTransformFromMatrix"); + exception = null; + try { + t = list.createSVGTransformFromMatrix(SVGMatrix(t)); + } catch(e) { exception = e; } + ok(exception, + "Failed to throw for bad input to createSVGTransformFromMatrix"); + exception = null; +} + +function testReadOnly(g) +{ + var SVG_NS = 'http://www.w3.org/2000/svg'; + + // Just some data to work with + g.setAttribute("transform", "translate(50 90)"); + + // baseVal / animVal are readonly attributes + // Create another (empty) transform list + var otherg = document.createElementNS(SVG_NS, 'g'); + g.parentNode.appendChild(otherg); + is(g.transform.baseVal.numberOfItems, 1, + "Unexpected number of items in transform list before attempting to set"); + is(otherg.transform.baseVal.numberOfItems, 0, + "Unexpected number of items in source transform list before attempting to" + + " set"); + // Attempt to set the base value and check nothing changes + g.transform.baseVal = otherg.transform.baseVal; + is(g.transform.baseVal.numberOfItems, 1, + "baseVal should be read-only but its value has changed"); + is(otherg.transform.baseVal.numberOfItems, 0, + "baseVal changed after attempting to use it set another value"); + + // Read-only SVGTransformList: + // Standard list methods are covered in test_SVGxxxList.xhtml so here we + // just add tests for SVGTransformList-specific methods + var roList = g.transform.animVal; + // consolidate() + var threw = false; + try { + roList.consolidate(); + } catch (e) { + is(e.name, "NoModificationAllowedError", + "Got unexpected exception " + e + + ", expected NoModificationAllowedError"); + is(e.code, DOMException.NO_MODIFICATION_ALLOWED_ERR, + "Got unexpected exception " + e + + ", expected NO_MODIFICATION_ALLOWED_ERR"); + threw = true; + } + ok(threw, + "Failed to throw exception when calling consolidate on read-only list"); + + // Read-only SVGTransform: + // read-only attributes are tested in test_transform.xhtml. Here we are + // concerned with methods that throw because this *object* is read-only + // (since it belongs to a read-only transform list) + var roTransform = roList.getItem(0); + // setMatrix + threw = false; + try { + var m = createMatrix(1, 2, 3, 4, 5, 6); + roTransform.setMatrix(m); + } catch (e) { + is(e.name, "NoModificationAllowedError", + "Got unexpected exception " + e + + ", expected NoModificationAllowedError"); + is(e.code, DOMException.NO_MODIFICATION_ALLOWED_ERR, + "Got unexpected exception " + e + + ", expected NO_MODIFICATION_ALLOWED_ERR"); + threw = true; + } + ok(threw, "Failed to throw exception when calling setMatrix on read-only" + + " transform"); + // setTranslate + threw = false; + try { + roTransform.setTranslate(2, 3); + } catch(e) { + threw = true; + } + ok(threw, "Failed to throw when calling setTranslate on read-only" + + " transform"); + // setScale + threw = false; + try { + roTransform.setScale(2, 3); + } catch(e) { + threw = true; + } + ok(threw, "Failed to throw when calling setScale on read-only transform"); + // setRotate + threw = false; + try { + roTransform.setRotate(1, 2, 3); + } catch(e) { + threw = true; + } + ok(threw, "Failed to throw when calling setRotate on read-only transform"); + // setSkewX + threw = false; + try { + roTransform.setSkewX(2); + } catch(e) { + threw = true; + } + ok(threw, "Failed to throw when calling setSkewX on read-only transform"); + // setSkewY + threw = false; + try { + roTransform.setSkewY(2); + } catch(e) { + threw = true; + } + ok(threw, "Failed to throw when calling setSkewY on read-only transform"); + + // Read-only SVGMatrix + var roMatrix = roTransform.matrix; + threw = false; + try { + roMatrix.a = 1; + } catch (e) { + is(e.name, "NoModificationAllowedError", + "Got unexpected exception " + e + + ", expected NoModificationAllowedError"); + is(e.code, DOMException.NO_MODIFICATION_ALLOWED_ERR, + "Got unexpected exception " + e + + ", expected NO_MODIFICATION_ALLOWED_ERR"); + threw = true; + } + ok(threw, "Failed to throw exception when modifying read-only matrix"); +} + +function testOrphan(g) +{ + // Although this isn't defined, if a read-only object becomes orphaned + // (detached from it's parent), then presumably it should become editable + // again. + + // As with the read-only test set a value to test with + g.setAttribute("transform", "translate(50 90)"); + + var roList = g.transform.animVal; + var roTransform = roList.getItem(0); + var roMatrix = roTransform.matrix; + + // Orphan transform list contents by re-setting transform attribute + g.setAttribute("transform", ""); + + // Transform should now be editable + var exception = null; + try { + roTransform.setTranslate(5, 3); + } catch(e) { + exception = e; + } + ok(exception===null, + "Unexpected exception " + exception + " modifying orphaned transform"); + uexception = null; + + // So should matrix + exception = null; + try { + roMatrix.a = 1; + } catch(e) { + exception = e; + } + ok(exception===null, + "Unexpected exception " + exception + " modifying orphaned matrix"); +} + +function testFailedSet(g) +{ + // Check that a parse failure results in the attribute being empty + + // Set initial value + g.setAttribute("transform", "translate(50 90)"); + var list = g.transform.baseVal; + is(list.numberOfItems, 1, "Unexpected initial length of list"); + + // Attempt to set bad value + g.setAttribute("transform", "translate(40 50) scale(a)"); + is(list.numberOfItems, 0, + "Transform list should be empty after setting bad value"); + is(g.transform.animVal.numberOfItems, 0, + "Animated transform list should also be empty after setting bad value"); +} + +function testMutationEvents(g) +{ + // Check mutation events + + // Set initial value + g.setAttribute("transform", "translate(50 90)"); + var list = g.transform.baseVal; + is(list.numberOfItems, 1, "Unexpected initial length of list"); + eventChecker = new MutationEventChecker; + eventChecker.watchAttr(g, "transform"); + + // consolidate + // + // Consolidate happens to generate two modification events in our + // implementation--it's not ideal but it's better than none + eventChecker.expect("modify modify modify"); + g.setAttribute("transform", "translate(10 10) translate(10 10)"); + list.consolidate(); + + // In the following, each of the operations is performed twice but only one + // mutation event is expected. This is to check that redundant mutation + // events are not sent. + + // transform.setMatrix + eventChecker.expect("modify"); + var mx = $('svg').createSVGMatrix(); + list[0].setMatrix(mx); + list[0].setMatrix(mx); + + // transform.setTranslate + eventChecker.expect("modify"); + list[0].setTranslate(10, 10); + list[0].setTranslate(10, 10); + + // transform.setScale + eventChecker.expect("modify"); + list[0].setScale(2, 2); + list[0].setScale(2, 2); + + // transform.setRotate + eventChecker.expect("modify"); + list[0].setRotate(45, 1, 2); + list[0].setRotate(45, 1, 2); + + // transform.setSkewX + eventChecker.expect("modify"); + list[0].setSkewX(45); + list[0].setSkewX(45); + + // transform.setSkewY + eventChecker.expect("modify"); + list[0].setSkewY(25); + list[0].setSkewY(25); + + // transform.matrix + eventChecker.expect("modify modify"); + list[0].matrix.a = 1; + list[0].matrix.a = 1; + list[0].matrix.e = 5; + list[0].matrix.e = 5; + + // setAttribute interaction + eventChecker.expect("modify"); + list[0].setMatrix(mx); + eventChecker.expect(""); + g.setAttribute("transform", "matrix(1, 0, 0, 1, 0, 0)"); + list[0].setMatrix(mx); + + // Attribute removal + eventChecker.expect("remove"); + g.removeAttribute("transform"); + + // Non-existent attribute removal + eventChecker.expect(""); + g.removeAttribute("transform"); + g.removeAttributeNS(null, "transform"); + + eventChecker.finish(); +} + +window.addEventListener("load", main, false); + +]]> +</script> +</pre> +</body> +</html> diff --git a/dom/svg/test/test_SVGTransformListAddition.xhtml b/dom/svg/test/test_SVGTransformListAddition.xhtml new file mode 100644 index 0000000000..4ba3d4773d --- /dev/null +++ b/dom/svg/test/test_SVGTransformListAddition.xhtml @@ -0,0 +1,192 @@ +<html xmlns="http://www.w3.org/1999/xhtml"> +<!-- +https://bugzilla.mozilla.org/show_bug.cgi?id=602759 +--> +<head> + <title>Tests specific to SVGLengthList addition</title> + <script type="text/javascript" src="/MochiKit/packed.js"></script> + <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" /> +</head> +<body> +<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=602759"> + Mozilla Bug 602759</a> +<p id="display"></p> +<div id="content"> +<svg id="svg" xmlns="http://www.w3.org/2000/svg" width="100" height="100" + onload="this.pauseAnimations();"> + <g id="g"/> +</svg> +</div> +<pre id="test"> +<script class="testbody" type="text/javascript"> +<![CDATA[ + +SimpleTest.waitForExplicitFinish(); + +/* +This file runs a series of tests specific to addition of SVGTransformList in +animation. +*/ + +function AdditionTestCase(desc, baseVal, animSpecs, expectedTransformList) +{ + this.desc = desc; + this.baseVal = baseVal; + this.animSpecs = animSpecs; + this.expectedTransformList = expectedTransformList; +} + +function Transform(type, angle) +{ + this.type = type; + this.angle = angle; +} + +function main(g) +{ + var cases = [ + new AdditionTestCase("Not additive", + "translate(150 50)", + {type: 'rotate', from: '0', to: '90'}, + [new Transform(SVGTransform.SVG_TRANSFORM_ROTATE, 90)] + ), + new AdditionTestCase("To animation", + "rotate(-90)", + {type: 'rotate', to: '90'}, + [new Transform(SVGTransform.SVG_TRANSFORM_ROTATE, 90)] + ), + new AdditionTestCase("By animation", + "rotate(-90)", + {type: 'rotate', by: '180'}, + [new Transform(SVGTransform.SVG_TRANSFORM_ROTATE, -90), + new Transform(SVGTransform.SVG_TRANSFORM_ROTATE, 180)] + ), + new AdditionTestCase("Normal additive: same type", + "rotate(45)", + {type: 'rotate', from: '0', to: '45', additive: 'sum'}, + [new Transform(SVGTransform.SVG_TRANSFORM_ROTATE, 45), + new Transform(SVGTransform.SVG_TRANSFORM_ROTATE, 45)] + ), + new AdditionTestCase("Normal additive: different type", + "translate(50)", + {type: 'rotate', from: '0', to: '90', additive: 'sum'}, + [new Transform(SVGTransform.SVG_TRANSFORM_TRANSLATE, 0), + new Transform(SVGTransform.SVG_TRANSFORM_ROTATE, 90)] + ), + new AdditionTestCase("Stacked additive: same type", + "rotate(-90)", + [{type: 'rotate', from: '0', to: '90', additive: 'sum'}, + {type: 'rotate', from: '0', to: '90', additive: 'sum'}], + [new Transform(SVGTransform.SVG_TRANSFORM_ROTATE, -90), + new Transform(SVGTransform.SVG_TRANSFORM_ROTATE, 90), + new Transform(SVGTransform.SVG_TRANSFORM_ROTATE, 90)] + ), + new AdditionTestCase("Stacked additive: different types #1", + "translate(50)", + [{type: 'rotate', from: '0', to: '45', additive: 'sum'}, + {type: 'rotate', from: '0', to: '45', additive: 'sum'}], + [new Transform(SVGTransform.SVG_TRANSFORM_TRANSLATE, 0), + new Transform(SVGTransform.SVG_TRANSFORM_ROTATE, 45), + new Transform(SVGTransform.SVG_TRANSFORM_ROTATE, 45)] + ), + new AdditionTestCase("Stacked additive: different types #2", + "skewX(20) translate(50)", + [{type: 'rotate', from: '0', to: '45', additive: 'sum'}, + {type: 'rotate', from: '0', to: '45', additive: 'sum'}], + [new Transform(SVGTransform.SVG_TRANSFORM_SKEWX, 20), + new Transform(SVGTransform.SVG_TRANSFORM_TRANSLATE, 0), + new Transform(SVGTransform.SVG_TRANSFORM_ROTATE, 45), + new Transform(SVGTransform.SVG_TRANSFORM_ROTATE, 45)] + ), + new AdditionTestCase("Stacked additive: different types #3", + "skewX(20) translate(50)", + [{type: 'rotate', from: '0', to: '45', additive: 'sum'}, + {type: 'translate', from: '0', to: '30', additive: 'sum'}, + {type: 'translate', from: '0', to: '-30', additive: 'sum'}, + {type: 'rotate', from: '0', to: '45', additive: 'sum'}], + [new Transform(SVGTransform.SVG_TRANSFORM_SKEWX, 20), + new Transform(SVGTransform.SVG_TRANSFORM_TRANSLATE, 0), + new Transform(SVGTransform.SVG_TRANSFORM_ROTATE, 45), + new Transform(SVGTransform.SVG_TRANSFORM_TRANSLATE, 0), + new Transform(SVGTransform.SVG_TRANSFORM_TRANSLATE, 0), + new Transform(SVGTransform.SVG_TRANSFORM_ROTATE, 45)] + ), + new AdditionTestCase("Base value with rotation around a centre", + "rotate(90 50 50)", + {type: 'translate', from: '0 0', to: '0 -50', additive: 'sum'}, + [new Transform(SVGTransform.SVG_TRANSFORM_ROTATE, 90), + new Transform(SVGTransform.SVG_TRANSFORM_TRANSLATE, 0)] + ), + ]; + + for (var i = 0; i < cases.length; i++) { + runAdditionTestCase(cases[i], $('g'), $('svg')); + } + + SimpleTest.finish(); +} + +function runAdditionTestCase(test, elem, svg) +{ + var anims = createAnims(test.animSpecs); + + elem.setAttribute('transform', test.baseVal); + elem.appendChild(anims); + + svg.setCurrentTime(1); + var expected = test.expectedTransformList; // Array of Transform objects + var actual = elem.transform.animVal; // SVGTransformList + is(actual.numberOfItems, expected.length, + "Unexpected number of transforms"); + + if (actual.numberOfItems == expected.length) { + for (var i = 0; i < actual.numberOfItems; i++) { + var transform = actual.getItem(i); + var testDesc = " for transform " + i + " in '" + test.desc + "' test"; + is(transform.type, expected[i].type, + "Unexpected transform type" + testDesc); + is(transform.angle, expected[i].angle, + "Unexpected transform angle" + testDesc); + } + } + // We assume the only children of elem are the animation elements we've just + // added. + while (elem.firstChild) { + elem.removeChild(elem.firstChild); + } +} + +function createAnims(specs) +{ + if (specs.constructor == Array) { + var frag = document.createDocumentFragment(); + for (var i = 0; i < specs.length; ++i) { + frag.appendChild(createAnim(specs[i])); + } + return frag; + } + + return createAnim(specs); +} + +function createAnim(attrs) +{ + var SVG_NS = 'http://www.w3.org/2000/svg'; + var anim = document.createElementNS(SVG_NS, 'animateTransform'); + anim.setAttribute('attributeName', 'transform'); + anim.setAttribute('dur', '1s'); + anim.setAttribute('fill', 'freeze'); + for (attr in attrs) { + anim.setAttribute(attr, attrs[attr]); + } + return anim; +} + +window.addEventListener("load", main, false); + +]]> +</script> +</pre> +</body> +</html> diff --git a/dom/svg/test/test_SVGUnitTypes.html b/dom/svg/test/test_SVGUnitTypes.html new file mode 100644 index 0000000000..aa6d1b2359 --- /dev/null +++ b/dom/svg/test/test_SVGUnitTypes.html @@ -0,0 +1,41 @@ +<!DOCTYPE html> +<html xmlns="http://www.w3.org/1999/xhtml"> +<!-- +https://bugzilla.mozilla.org/show_bug.cgi?id=366697 +--> +<head> +<title>Test for Bug 842201</title> + <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" /> +</head> +<body> +<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=842201">Mozilla Bug 842201</a> +<p id="display"></p> +<div id="content" style="display: none"></div> + +<pre id="test"> +<script class="testbody" type="application/javascript"> +SimpleTest.waitForExplicitFinish(); + +function runTest() +{ + is(SVGPatternElement.SVG_UNIT_TYPE_OBJECTBOUNDINGBOX, 2, + "Pattern should implement SVGUnitTypes values"); + is(SVGFilterElement.SVG_UNIT_TYPE_OBJECTBOUNDINGBOX, 2, + "Filter should implement SVGUnitTypes values"); + is(SVGMaskElement.SVG_UNIT_TYPE_OBJECTBOUNDINGBOX, 2, + "Mask should implement SVGUnitTypes values"); + is(SVGClipPathElement.SVG_UNIT_TYPE_OBJECTBOUNDINGBOX, 2, + "ClipPath should implement SVGUnitTypes values"); + is(SVGLinearGradientElement.SVG_UNIT_TYPE_OBJECTBOUNDINGBOX, 2, + "LinearGradient should implement SVGUnitTypes values"); + is(SVGRadialGradientElement.SVG_UNIT_TYPE_OBJECTBOUNDINGBOX, 2, + "RadialGradient should implement SVGUnitTypes values"); + SimpleTest.finish(); +} + +window.addEventListener("load", runTest, false); +</script> +</pre> +</body> +</html> diff --git a/dom/svg/test/test_SVG_namespace_ids.html b/dom/svg/test/test_SVG_namespace_ids.html new file mode 100644 index 0000000000..6072671825 --- /dev/null +++ b/dom/svg/test/test_SVG_namespace_ids.html @@ -0,0 +1,114 @@ +<!DOCTYPE html> +<html> +<!-- +https://bugzilla.mozilla.org/show_bug.cgi?id=589640 +--> +<head> + <title>Test for Bug 589640</title> + <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" /> + <script class="testbody" type="application/javascript"> + SimpleTest.waitForExplicitFinish(); + + function debug(message) { + document.getElementById('debug').appendChild(document.createTextNode(message + "\n")); + } + + function runTests() + { + var svg = document.getElementById('svg1'); + for (var el = 0; el < svg.children.length; el++) { + is(svg.children[el].id, svg.children[el].localName, svg.children[el].localName + " in the SVG namespace has a valid ID"); + debug(svg.children[el].localName + '.id = ' + svg.children[el].id); + } + + SimpleTest.finish(); + } + </script> +</head> +<body onload="runTests()"> +<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=589640">Mozilla Bug 589640</a> +<pre id="debug"></pre> +<!-- NOTE: This test relies on the ids being the same as the element names --> +<svg id="svg1"> + <a id="a" /> + <animate id="animate" /> + <animateColor id="animateColor" /> + <animateMotion id="animateMotion" /> + <animateTransform id="animateTransform" /> + <circle id="circle" /> + <clipPath id="clipPath" /> + <color-profile id="color-profile" /> + <cursor id="cursor" /> + <defs id="defs" /> + <desc id="desc" /> + <ellipse id="ellipse" /> + <feBlend id="feBlend" /> + <feColorMatrix id="feColorMatrix" /> + <feComponentTransfer id="feComponentTransfer" /> + <feComposite id="feComposite" /> + <feConvolveMatrix id="feConvolveMatrix" /> + <feDiffuseLighting id="feDiffuseLighting" /> + <feDisplacementMap id="feDisplacementMap" /> + <feDistantLight id="feDistantLight" /> + <feDropShadow id="feDropShadow" /> + <feFlood id="feFlood" /> + <feFuncA id="feFuncA" /> + <feFuncB id="feFuncB" /> + <feFuncG id="feFuncG" /> + <feFuncR id="feFuncR" /> + <feGaussianBlur id="feGaussianBlur" /> + <feImage id="feImage" /> + <feMerge id="feMerge" /> + <feMergeNode id="feMergeNode" /> + <feMorphology id="feMorphology" /> + <feOffset id="feOffset" /> + <fePointLight id="fePointLight" /> + <feSpecularLighting id="feSpecularLighting" /> + <feSpotLight id="feSpotLight" /> + <feTile id="feTile" /> + <feTurbulence id="feTurbulence" /> + <filter id="filter" /> + <font id="font" /> + <font-face id="font-face" /> + <font-face-format id="font-face-format" /> + <font-face-name id="font-face-name" /> + <font-face-src id="font-face-src" /> + <font-face-uri id="font-face-uri" /> + <foreignObject id="foreignObject" /> + <g id="g" /> + <glyph id="glyph" /> + <glyphRef id="glyphRef" /> + <hkern id="hkern" /> + <image id="image" /> + <line id="line" /> + <linearGradient id="linearGradient" /> + <marker id="marker" /> + <mask id="mask" /> + <metadata id="metadata" /> + <missing-glyph id="missing-glyph" /> + <mpath id="mpath" /> + <path id="path" /> + <pattern id="pattern" /> + <polygon id="polygon" /> + <polyline id="polyline" /> + <radialGradient id="radialGradient" /> + <rect id="rect" /> + <script id="script" /> + <set id="set" /> + <stop id="stop" /> + <style id="style" /> + <svg id="svg" /> + <switch id="switch" /> + <symbol id="symbol" /> + <text id="text" /> + <textPath id="textPath" /> + <title id="title" /> + <tref id="tref" /> + <tspan id="tspan" /> + <use id="use" /> + <view id="view" /> + <vkern id="vkern" /> +</svg> +</body> +</html> diff --git a/dom/svg/test/test_SVGxxxList.xhtml b/dom/svg/test/test_SVGxxxList.xhtml new file mode 100644 index 0000000000..7062427425 --- /dev/null +++ b/dom/svg/test/test_SVGxxxList.xhtml @@ -0,0 +1,1439 @@ +<html xmlns="http://www.w3.org/1999/xhtml"> +<!-- +https://bugzilla.mozilla.org/show_bug.cgi?id=515116 +--> +<head> + <title>Generic tests for SVG animated length lists</title> + <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <script type="text/javascript" src="matrixUtils.js"></script> + <script type="text/javascript" src="MutationEventChecker.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" /> +</head> +<body> +<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=515116">Mozilla Bug 515116</a> +<p id="display"></p> +<div id="content"> +<svg id="svg" xmlns="http://www.w3.org/2000/svg" width="100" height="100" + onload="this.pauseAnimations();"> + <defs> + <filter> + <feComponentTransfer> + <feFuncR id="feFuncR" type="table"/> + </feComponentTransfer> + </filter> + </defs> + <text id="text">text</text> + <path id="path"/> + <polyline id="polyline"/> + <g id="g"/> +</svg> +</div> +<pre id="test"> +<script class="testbody" type="text/javascript"> +<![CDATA[ + + +SimpleTest.waitForExplicitFinish(); + +/* +This file runs a series of type-agnostic tests to check the state of the mini DOM trees that represent various SVG 'list' attributes (including checking the "object identity" of the objects in those trees) in the face of various changes, both with and without the complication of SMIL animation being active. + +For additional high level information on the tests that are run, see the comment for 'create_animate_elements' below. + +To have the battery of generic tests run for a new list attribute, add an element with that attribute to the document, then add a JavaScript object literal to the following 'tests' array with the following properties: + + target_element_id + The ID of the element that has the attribute that is to be tested. + attr_name + The name of the attribute that is to be tested. + prop_name + The name of the DOM property that corresponds to the attribute that is to + be tested. For some list types the SVGAnimatedXxxList interface is + inherited by the element interface rather than the element having a + property of that type, and in others the list type is not animatable so + there is no SVGAnimatedXxxList interface for that list type. In these + cases this property should be set to null. + bv_name + The name of the DOM base value property for the attribute that is to be + tested. This is usually 'baseVal', but not always. In the case of + SVGStringList, which is not animatable, this is the name of the + SVGStringList property. + av_name + The name of the DOM anim value property for the attribute that is to be + tested. This is usually 'animVal' but not always. In the case of + SVGStringList, which is not animatable, this should be set to null. + el_type + The name of the SVGXxxElement interface on which the property corresponding + to the attribute being tested is defined. + prop_type + The name of the SVGAnimatedXxxList interface (e.g. SVGAnimatedLengthList), + if it exists, and if the element has a property is of this type (as + opposed to the element interface inheriting it). + list_type + The name of the SVGXxxList inteface implemented by the baseVal and + animVal objects. + item_type + The name of the SVGXxx interface implemented by the list items. + attr_val_3a: + attr_val_3b: + Two attribute values containing three different items. + attr_val_4 + An attribute value containing four items. + attr_val_5a: + attr_val_5b: + Two attribute values containing five different items. + attr_val_5b_firstItem_x3_constructor: + Function to construct a list-item that should match the first item in a + SVGXxxList after three repeats of a cumulative animation to attr_val_5b. + This function takes t.item_constructor as its only argument. + item_constructor: + Function to create a dummy list item. + item_is: + Function to compare two list items for equality, like "is()". If this + property is omitted, it is assumed that we can just compare + "item.value" (which is the case for most list types). +*/ +//helper method +function keys(obj) { + var rval = []; + for (var prop in obj) { + rval.push(prop); + } + return rval; +}; + +var tests = [ + { + // SVGLengthList test: + target_element_id: 'text', + attr_name: 'x', + prop_name: 'x', + bv_name: 'baseVal', + av_name: 'animVal', + el_type: 'SVGTextElement', + prop_type: 'SVGAnimatedLengthList', + list_type: 'SVGLengthList', + item_type: 'SVGLength', + attr_val_3a: '10 20ex, 30in', + attr_val_3b: '30in 10, 20ex', + attr_val_4 : '10 20ex, 30in ,40cm', + attr_val_5a: '10 20ex, 30in ,40cm , 50%', + attr_val_5b: '20 50%, 20ex ,30in , 40cm', + attr_val_5b_firstItem_x3_constructor: function(constructor) { + var expected = constructor(); + expected.value = 60; + return expected; + }, + item_constructor: function() { + // We need this function literal to avoid "Illegal operation on + // WrappedNative prototype object" NS_ERROR_XPC_BAD_OP_ON_WN_PROTO. + return document.getElementById('svg').createSVGLength(); + } + }, + { + // SVGNumberList test: + target_element_id: 'text', + attr_name: 'rotate', + prop_name: 'rotate', + bv_name: 'baseVal', + av_name: 'animVal', + el_type: 'SVGTextElement', + prop_type: 'SVGAnimatedNumberList', + list_type: 'SVGNumberList', + item_type: 'SVGNumber', + attr_val_3a: '0 20 40', + attr_val_3b: '60 40 20', + attr_val_4 : '40 20 10 80', + attr_val_5a: '90 30 60 20 70', + attr_val_5b: '30 20 70 30 90', + attr_val_5b_firstItem_x3_constructor: function(constructor) { + var expected = constructor(); + expected.value = 90; + return expected; + }, + item_constructor: function() { + // We need this function literal to avoid "Illegal operation on + // WrappedNative prototype object" NS_ERROR_XPC_BAD_OP_ON_WN_PROTO. + return document.getElementById('svg').createSVGNumber(); + } + }, + { + // SVGNumberList test: + target_element_id: 'feFuncR', + attr_name: 'tableValues', + prop_name: 'tableValues', + bv_name: 'baseVal', + av_name: 'animVal', + el_type: 'SVGFEComponentTransferElement', + prop_type: 'SVGAnimatedNumberList', + list_type: 'SVGNumberList', + item_type: 'SVGNumber', + attr_val_3a: '0 .5 .2', + attr_val_3b: '1 .7 .1', + attr_val_4 : '.5 .3 .8 .2', + attr_val_5a: '3 4 5 6 7', + attr_val_5b: '7 6 5 4 3', + attr_val_5b_firstItem_x3_constructor: function(constructor) { + var expected = constructor(); + expected.value = 21; + return expected; + }, + item_constructor: function() { + // We need this function literal to avoid "Illegal operation on + // WrappedNative prototype object" NS_ERROR_XPC_BAD_OP_ON_WN_PROTO. + return document.getElementById('svg').createSVGNumber(); + } + }, + { + // SVGPointList test: + target_element_id: 'polyline', + attr_name: 'points', + prop_name: null, // SVGAnimatedPoints is an inherited interface! + bv_name: 'points', + av_name: 'animatedPoints', + el_type: 'SVGPolylineElement', + prop_type: null, + list_type: 'SVGPointList', + item_type: 'SVGPoint', + attr_val_3a: ' 10,10 50,50 90,10 ', + attr_val_3b: ' 10,50 50,10 90,50 ', + attr_val_4 : ' 10,10 50,50 90,10 200,100 ', + attr_val_5a: ' 10,10 50,50 90,10 130,50 170,10 ', + attr_val_5b: ' 50,10 50,10 90,50 130,10 170,50 ', + attr_val_5b_firstItem_x3_constructor: function(constructor) { + var expected = constructor(); + expected.x = 150; + expected.y = 30; + return expected; + }, + item_constructor: function() { + // XXX return different values each time + return document.getElementById('svg').createSVGPoint(); + }, + item_is: function(itemA, itemB, message) { + ok(typeof(itemA.x) != 'undefined' && + typeof(itemB.x) != 'undefined', + 'expecting x property'); + ok(typeof(itemA.y) != 'undefined' && + typeof(itemB.y) != 'undefined', + 'expecting y property'); + + is(itemA.x, itemB.x, message); + is(itemA.y, itemB.y, message); + } + }, + { + // SVGPathSegList test: + target_element_id: 'path', + attr_name: 'd', + prop_name: null, // SVGAnimatedPathData is an inherited interface! + bv_name: 'pathSegList', + av_name: 'animatedPathSegList', + el_type: 'SVGPathElement', + prop_type: null, + list_type: 'SVGPathSegList', + item_type: 'SVGPathSeg', + attr_val_3a: 'M 10,10 L 50,50 L 90,10', + attr_val_3b: 'M 10,50 L 50,10 L 90,50', + attr_val_4 : 'M 10,10 L 50,50 L 90,10 M 200,100', + attr_val_5a: 'M 10,10 L 50,50 L 90,10 L 130,50 L 170,10', + attr_val_5b: 'M 50,10 L 50,10 L 90,50 L 130,10 L 170,50', + attr_val_5b_firstItem_x3_constructor: function(constructor) { + var expected = constructor(); + is(expected.pathSegTypeAsLetter, "M", + "test error -- expected constructor to generate a segment of type M"); + expected.x = 150; + expected.y = 30; + return expected; + }, + item_constructor: function() { + // XXX return different values each time + return document.getElementById('path').createSVGPathSegMovetoAbs(1, 1); + }, + item_is: function(itemA, itemB, message) { + ok(typeof(itemA.pathSegTypeAsLetter) != 'undefined' && + typeof(itemB.pathSegTypeAsLetter) != 'undefined', + 'expecting pathSegTypeAsLetter property'); + + // First: are we dealing with the same type of segment? + is(itemA.pathSegTypeAsLetter, itemB.pathSegTypeAsLetter, message); + if (itemA.pathSegTypeAsLetter != itemB.pathSegTypeAsLetter) + return; // The rest of this function is nonsense if types don't match. + + // Make sure property-counts match (so we can iterate across itemA's + // properties and not worry about itemB having extra properties that + // we might be skipping over). + is(keys(itemA).length, keys(itemB).length, + 'expecting same property-count when comparing path segs of same type.'); + + // Compare the properties, skipping the constant properties inherited + // from 'SVGPathSeg', and skipping the pathSegTypeAsLetter field since we + // already checked that above. + for (var prop in itemA) { + if (!SVGPathSeg.hasOwnProperty(prop) && + prop != 'pathSegTypeAsLetter') { + is(itemA[prop], itemB[prop], message); + } + } + } + }, + { + // SVGStringList test: + target_element_id: 'g', + attr_name: 'requiredFeatures', // requiredExtensions, systemLanguage, viewTarget + prop_name: null, // SVGStringList attributes are not animatable + bv_name: 'requiredFeatures', + av_name: null, + el_type: 'SVGGElement', + prop_type: null, + list_type: 'SVGStringList', + item_type: 'DOMString', + attr_val_3a: 'http://www.w3.org/TR/SVG11/feature#Shape http://www.w3.org/TR/SVG11/feature#Image ' + + 'http://www.w3.org/TR/SVG11/feature#Style', + attr_val_3b: 'http://www.w3.org/TR/SVG11/feature#CoreAttribute http://www.w3.org/TR/SVG11/feature#Structure ' + + 'http://www.w3.org/TR/SVG11/feature#Gradient', + attr_val_4 : 'http://www.w3.org/TR/SVG11/feature#Pattern http://www.w3.org/TR/SVG11/feature#Clip ' + + 'http://www.w3.org/TR/SVG11/feature#Mask http://www.w3.org/TR/SVG11/feature#Extensibility', + attr_val_5a: 'http://www.w3.org/TR/SVG11/feature#BasicStructure http://www.w3.org/TR/SVG11/feature#BasicText ' + + 'http://www.w3.org/TR/SVG11/feature#BasicPaintAttribute http://www.w3.org/TR/SVG11/feature#BasicGraphicsAttribute ' + + 'http://www.w3.org/TR/SVG11/feature#BasicClip', + attr_val_5b: 'http://www.w3.org/TR/SVG11/feature#DocumentEventsAttribute http://www.w3.org/TR/SVG11/feature#GraphicalEventsAttribute ' + + 'http://www.w3.org/TR/SVG11/feature#AnimationEventsAttribute http://www.w3.org/TR/SVG11/feature#Hyperlinking ' + + 'http://www.w3.org/TR/SVG11/feature#XlinkAttribute', + item_constructor: function() { + return 'http://www.w3.org/TR/SVG11/feature#XlinkAttribute'; + } + }, + { + // SVGTransformList test: + target_element_id: 'g', + attr_name: 'transform', // gradientTransform, patternTransform + prop_name: 'transform', + bv_name: 'baseVal', + av_name: 'animVal', + el_type: 'SVGGElement', + prop_type: 'SVGAnimatedTransformList', + list_type: 'SVGTransformList', + item_type: 'SVGTransform', + attr_val_3a: 'translate(20 10) rotate(90 10 10) skewX(45)', + attr_val_3b: 'translate(30 40) scale(2) matrix(1 2 3 4 5 6)', + attr_val_4 : 'scale(3 2) translate(19) skewY(2) rotate(-10)', + attr_val_5a: + 'translate(20) rotate(-10) skewY(3) matrix(1 2 3 4 5 6) scale(0.5)', + attr_val_5b: + 'skewX(45) rotate(45 -10 -10) skewX(-45) scale(2) matrix(6 5 4 3 2 1)', + // SVGTransformList animation addition is tested in + // test_SVGTransformListAddition.xhtml so we don't need: + // - attr_val_3b + // - attr_val_3b + // - attr_val_5b_firstItem_x3_constructor + // But we populate the first two anyway just in case they are later used for + // something other than testing animation. + // attr_val_5b_firstItem_x3_constructor is only used for animation + item_constructor: function() { + // XXX populate the matrix with different values each time + return document.getElementById('svg').createSVGTransform(); + }, + item_is: function(itemA, itemB, message) { + ok(typeof(itemA.type) != 'undefined' && + typeof(itemB.type) != 'undefined', + 'expecting type property'); + ok(typeof(itemA.matrix) != 'undefined' && + typeof(itemB.matrix) != 'undefined', + 'expecting matrix property'); + ok(typeof(itemA.angle) != 'undefined' && + typeof(itemB.angle) != 'undefined', + 'expecting matrix property'); + + is(itemA.type, itemB.type, message); + is(itemA.angle, itemB.angle, message); + cmpMatrix(itemA.matrix, itemB.matrix, message); + } + }, +]; + + +/* +This function returns a DocumentFragment with three 'animate' element children. The duration of the three animations is as follows: + + animation 1: | *-----------*-----------*-----------* + animation 2: | *--* + animation 3: | *--* + |___________________________________________> time (s) + | | | | | | | | | | | | | | | + 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 + +The first animation repeats once so that we can test state on a repeat animation. + +The second animation overrides the first animation for a short time, and has fewer list items than the first animation. This allows us to test object identity and other state on and after an overriding animation. Specifically, it allows us to check whether animVal list items are kept or discarded after the end of an overriding animation that has fewer items. + +The third animation has additive="sum", with fewer items than the lower priority animation 1, allowing us to test object identity and other state in that scenario. TODO: some type aware tests to check whether the composite fails or works? + +At t=0s and t=1s we test the effect of an attribute value changes in the absence and presence of SMIL animation respectively. + +At t=10s we programatically remove the fill="freeze" from animation 1. +*/ +function create_animate_elements(test) +{ + var SVG_NS = 'http://www.w3.org/2000/svg'; + var df = document.createDocumentFragment(); + + if (is_transform_attr(test.attr_name)) { + // animateTransform is "special". Although it targets an + // nsSVGAnimatedTransformList it only takes nsSVGTransform values as + // animation values. Therefore all the assumptions we're testing about the + // length of lists don't apply. We simply have to test it separately. + // This is done in test_SVGTransformListAddition.xhtml. + return df; // Return the empty document fragment + } + + var animate1 = document.createElementNS(SVG_NS, 'animate'); + var animate2 = document.createElementNS(SVG_NS, 'animate'); + var animate3 = document.createElementNS(SVG_NS, 'animate'); + + animate1.setAttribute('attributeName', test.attr_name); + animate1.setAttribute('from', test.attr_val_5a); + animate1.setAttribute('to', test.attr_val_5b); + animate1.setAttribute('begin', '1s'); + animate1.setAttribute('dur', '4s'); + animate1.setAttribute('repeatCount', '3'); + animate1.setAttribute('accumulate', 'sum'); + animate1.setAttribute('fill', 'freeze'); + df.appendChild(animate1); + + animate2.setAttribute('attributeName', test.attr_name); + animate2.setAttribute('from', test.attr_val_3a); + animate2.setAttribute('to', test.attr_val_3b); + animate2.setAttribute('begin', '2s'); + animate2.setAttribute('dur', '1s'); + df.appendChild(animate2); + + animate3.setAttribute('attributeName', test.attr_name); + animate3.setAttribute('from', test.attr_val_3a); + animate3.setAttribute('to', test.attr_val_3b); + animate3.setAttribute('begin', '7s'); + animate3.setAttribute('dur', '1s'); + animate3.setAttribute('additive', 'sum'); + df.appendChild(animate3); + + return df; +} + +function is_transform_attr(attr_name) { + return attr_name == 'transform' || + attr_name == 'gradientTransform' || + attr_name == 'patternTransform'; +} + +function get_array_of_list_items(list) +{ + array = []; + for (var i = 0; i < list.numberOfItems; ++i) { + array.push(list.getItem(i)); + } + return array; +} + + +/** + * This function tests the SVGXxxList API for the base val list. This means + * running tests for the following property and methods: + * + * numberOfItems + * clear() + * SVGLength initialize(in SVGLength newItem) + * SVGLength getItem(in unsigned long index) + * SVGLength insertItemBefore(in SVGLength newItem, in unsigned long index) + * SVGLength replaceItem(in SVGLength newItem, in unsigned long index) + * SVGLength removeItem(in unsigned long index) + * SVGLength appendItem(in SVGLength newItem) + * + * @param t A test from the 'tests' array. + */ +function run_baseVal_API_tests() +{ + var res, threw, items; + var eventChecker = new MutationEventChecker; + + for (var t of tests) { + + // Test .clear(): + + t.element.setAttribute(t.attr_name, t.attr_val_4); + + is(t.baseVal.numberOfItems, 4, + 'The '+t.list_type+' object should contain four list items.'); + + eventChecker.watchAttr(t.element, t.attr_name); + eventChecker.expect("modify"); + res = t.baseVal.clear(); + + is(t.baseVal.numberOfItems, 0, + 'The method '+t.list_type+'.clear() should clear the '+t.list_type+ + ' object.'); + is(res, undefined, + 'The method '+t.list_type+'.clear() should not return a value.'); + ok(t.element.hasAttribute(t.attr_name), + 'The method '+t.list_type+'.clear() should not remove the attribute.'); + ok(t.element.getAttribute(t.attr_name) === "", + 'Cleared '+t.attr_name+' ('+t.list_type+') but did not get an '+ + 'empty string back.'); + + eventChecker.expect(""); + t.baseVal.clear(); + eventChecker.ignoreEvents(); + + // Test empty strings + + t.element.setAttribute(t.attr_name, ""); + ok(t.element.getAttribute(t.attr_name) === "", + 'Set an empty attribute value for '+t.attr_name+' ('+t.list_type+ + ') but did not get an empty string back.'); + + // Test removed attributes + + t.element.removeAttribute(t.attr_name); + ok(t.element.getAttribute(t.attr_name) === null, + 'Removed attribute value for '+t.attr_name+' ('+t.list_type+ + ') but did not get null back.'); + ok(!t.element.hasAttribute(t.attr_name), + 'Removed attribute value for '+t.attr_name+' ('+t.list_type+ + ') but hasAttribute still returns true.'); + + // Test .initialize(): + + t.element.setAttribute(t.attr_name, t.attr_val_4); + + var item = t.item_constructor(); + // Our current implementation of 'initialize' for most list types performs + // a 'clear' followed by an 'insertItemBefore'. This results in two + // modification events being dispatched. SVGStringList however avoids the + // additional clear. + var expectedModEvents = + t.item_type == "DOMString" ? "modify" : "modify modify"; + eventChecker.expect(expectedModEvents); + var res = t.baseVal.initialize(item); + eventChecker.ignoreEvents(); + + + is(t.baseVal.numberOfItems, 1, + 'The '+t.list_type+' object should contain one list item.'); + ok(res === item, + 'The list item returned by '+t.list_type+'.initialize() should be the '+ + 'exact same object as the item that was passed to that method, since '+ + 'the item that was passed to that method did not already belong to a '+ + 'list.'); + ok(t.baseVal.getItem(0) === item, + 'The list item at index 0 should be the exact same object as the '+ + 'object that was passed to the '+t.list_type+'.initialize() method, '+ + 'since the item that was passed to that method did not already '+ + 'belong to a list.'); + + t.element.setAttribute(t.attr_name, t.attr_val_4); + + if (t.item_type != "DOMString") { + var old_items = get_array_of_list_items(t.baseVal); + item = t.baseVal.getItem(3); + res = t.baseVal.initialize(item); + + ok(res !== item && + t.baseVal.getItem(0) !== item && + t.baseVal.getItem(0) !== old_items[0] && + res === t.baseVal.getItem(0), + 'The method '+t.list_type+'.initialize() should clone the object that '+ + 'is passed in if that object is already in a list.'); + // [SVGWG issue] not what the spec currently says + + + item = t.baseVal.getItem(0); + res = t.baseVal.initialize(item); + + ok(res !== item && + t.baseVal.getItem(0) !== item, + 'The method '+t.list_type+'.initialize() should clone the object that '+ + 'is passed in, even if that object is the only item in that list.'); + // [SVGWG issue] not what the spec currently says + + eventChecker.expect(""); + threw = false; + try { + t.baseVal.initialize({}); + } catch(e) { + threw = true; + } + ok(threw, + 'The method '+t.list_type+'.initialize() should throw if passed an '+ + 'object of the wrong type.'); + eventChecker.ignoreEvents(); + } + + // Test .insertItemBefore(): + + t.element.setAttribute(t.attr_name, t.attr_val_4); + + old_items = get_array_of_list_items(t.baseVal); + item = t.item_constructor(); + eventChecker.expect("modify"); + res = t.baseVal.insertItemBefore(item, 2); + eventChecker.ignoreEvents(); + + is(t.baseVal.numberOfItems, 5, + 'The '+t.list_type+' object should contain five list items.'); + ok(res === item, + 'The list item returned by '+t.list_type+'.insertItemBefore() should '+ + 'be the exact same object as the item that was passed to that method, '+ + 'since the item that was passed to that method did not already belong '+ + 'to a list.'); + ok(t.baseVal.getItem(2) === item, + 'The list item at index 2 should be the exact same object as the '+ + 'object that was passed to the '+t.list_type+'.insertItemBefore() '+ + 'method, since the item that was passed to that method did not '+ + 'already belong to a list.'); + ok(t.baseVal.getItem(3) === old_items[2], + 'The list item that was at index 2 should be at index 3 after '+ + 'inserting a new item at index 2 using the '+t.list_type+ + '.insertItemBefore() method.'); + + item = t.item_constructor(); + t.baseVal.insertItemBefore(item, 100); + + ok(t.baseVal.getItem(5) === item, + 'When the index passed to the '+t.list_type+'.insertItemBefore() '+ + 'method is out of bounds, the supplied list item should be appended '+ + 'to the list.'); + + item = t.baseVal.getItem(4); + res = t.baseVal.insertItemBefore(item, 2); + + is(t.baseVal.numberOfItems, 7, + 'The '+t.list_type+' object should contain seven list items.'); + if (t.item_type != "DOMString") { + ok(res !== item && + t.baseVal.getItem(2) !== item && + t.baseVal.getItem(2) !== old_items[2] && + res === t.baseVal.getItem(2), + 'The method '+t.list_type+'.insertItemBefore() should clone the '+ + 'object that is passed in if that object is already in a list.'); + // [SVGWG issue] not what the spec currently says + } + + item = t.baseVal.getItem(2); + res = t.baseVal.insertItemBefore(item, 2); + + is(t.baseVal.numberOfItems, 8, + 'The '+t.list_type+' object should contain eight list items.'); + if (t.item_type != "DOMString") { + ok(res !== item && + t.baseVal.getItem(2) !== item, + 'The method '+t.list_type+'.insertItemBefore() should clone the '+ + 'object that is passed in, even if that object is the item in '+ + 'the list at the index specified.'); + // [SVGWG issue] not what the spec currently says + + eventChecker.expect(""); + threw = false; + try { + t.baseVal.insertItemBefore({}, 2); + } catch(e) { + threw = true; + } + ok(threw, + 'The method '+t.list_type+'.insertItemBefore() should throw if passed '+ + 'an object of the wrong type.'); + eventChecker.ignoreEvents(); + } + + // Test .replaceItem(): + + t.element.setAttribute(t.attr_name, t.attr_val_4); + + old_items = get_array_of_list_items(t.baseVal); + item = t.item_constructor(); + eventChecker.expect("modify"); + res = t.baseVal.replaceItem(item, 2); + eventChecker.ignoreEvents(); + + is(t.baseVal.numberOfItems, 4, + 'The '+t.list_type+' object should contain four list items.'); + if (t.item_type != "DOMString") { + ok(res === item, + 'The list item returned by '+t.list_type+'.replaceItem() should be '+ + 'the exact same object as the item that was passed to that method, '+ + 'since the item that was passed to that method did not already belong '+ + 'to a list.'); + } + ok(t.baseVal.getItem(2) === item, + 'The list item at index 2 should be the exact same object as the '+ + 'object that was passed to the '+t.list_type+'.replaceItem() method, '+ + 'since the item that was passed to that method did not already belong '+ + 'to a list.'); + ok(t.baseVal.getItem(3) === old_items[3], + 'The list item that was at index 3 should still be at index 3 after '+ + 'the item at index 2 was replaced using the '+t.list_type+ + '.replaceItem() method.'); + + item = t.item_constructor(); + + eventChecker.expect(""); + threw = false; + try { + t.baseVal.replaceItem(item, 100); + } catch(e) { + threw = true; + } + ok(threw, + 'The method '+t.list_type+'.replaceItem() should throw if passed '+ + 'an index that is out of bounds.'); + eventChecker.ignoreEvents(); + + old_items = get_array_of_list_items(t.baseVal); + item = t.baseVal.getItem(3); + res = t.baseVal.replaceItem(item, 1); + + is(t.baseVal.numberOfItems, 4, + 'The '+t.list_type+' object should contain four list items.'); + if (t.item_type != "DOMString") { + ok(res !== item && + t.baseVal.getItem(1) !== item && + t.baseVal.getItem(1) !== old_items[1] && + res === t.baseVal.getItem(1), + 'The method '+t.list_type+'.replaceItem() should clone the object '+ + 'that is passed in if that object is already in a list.'); + // [SVGWG issue] not what the spec currently says + } + + item = t.baseVal.getItem(1); + res = t.baseVal.replaceItem(item, 1); + + is(t.baseVal.numberOfItems, 4, + 'The '+t.list_type+' object should contain four list items.'); + if (t.item_type != "DOMString") { + ok(res !== item && + t.baseVal.getItem(1) !== item, + 'The method '+t.list_type+'.replaceItem() should clone the object '+ + 'that is passed in, even if the object that object and the object '+ + 'that is being replaced are the exact same objects.'); + // [SVGWG issue] not what the spec currently says + + threw = false; + try { + t.baseVal.replaceItem({}, 2); + } catch(e) { + threw = true; + } + ok(threw, + 'The method '+t.list_type+'.replaceItem() should throw if passed '+ + 'an object of the wrong type.'); + } + + // Test .removeItem(): + + t.element.setAttribute(t.attr_name, t.attr_val_4); + + old_items = get_array_of_list_items(t.baseVal); + item = t.baseVal.getItem(2); + eventChecker.expect("modify"); + res = t.baseVal.removeItem(2); + eventChecker.ignoreEvents(); + + is(t.baseVal.numberOfItems, 3, + 'The '+t.list_type+' object should contain three list items.'); + if (t.item_type != "DOMString") { + ok(res === item, + 'The list item returned by '+t.list_type+'.removeItem() should be the '+ + 'exact same object as the item that was at the specified index.'); + } + ok(t.baseVal.getItem(1) === old_items[1], + 'The list item that was at index 1 should still be at index 1 after '+ + 'the item at index 2 was removed using the '+t.list_type+ + '.replaceItem() method.'); + ok(t.baseVal.getItem(2) === old_items[3], + 'The list item that was at index 3 should still be at index 2 after '+ + 'the item at index 2 was removed using the '+t.list_type+ + '.replaceItem() method.'); + + eventChecker.expect(""); + threw = false; + try { + t.baseVal.removeItem(100); + } catch(e) { + threw = true; + } + ok(threw, + 'The method '+t.list_type+'.removeItem() should throw if passed '+ + 'an index that is out of bounds.'); + eventChecker.ignoreEvents(); + + // Test .appendItem(): + + t.element.setAttribute(t.attr_name, t.attr_val_4); + + old_items = get_array_of_list_items(t.baseVal); + item = t.item_constructor(); + eventChecker.expect("modify"); + res = t.baseVal.appendItem(item); + eventChecker.ignoreEvents(); + + is(t.baseVal.numberOfItems, 5, + 'The '+t.list_type+' object should contain five list items.'); + ok(res === item, + 'The list item returned by '+t.list_type+'.appendItem() should be the '+ + 'exact same object as the item that was passed to that method, since '+ + 'the item that was passed to that method did not already belong '+ + 'to a list.'); + ok(t.baseVal.getItem(4) === item, + 'The last list item should be the exact same object as the object '+ + 'that was passed to the '+t.list_type+'.appendItem() method, since '+ + 'the item that was passed to that method did not already belong to '+ + 'a list.'); + ok(t.baseVal.getItem(3) === old_items[3], + 'The list item that was at index 4 should still be at index 4 after '+ + 'appending a new item using the '+t.list_type+'.appendItem() '+ + 'method.'); + + item = t.baseVal.getItem(2); + res = t.baseVal.appendItem(item); + + is(t.baseVal.numberOfItems, 6, + 'The '+t.list_type+' object should contain six list items.'); + if (t.item_type != "DOMString") { + ok(res !== item && + t.baseVal.getItem(5) !== item && + res === t.baseVal.getItem(5), + 'The method '+t.list_type+'.appendItem() should clone the object '+ + 'that is passed in if that object is already in a list.'); + // [SVGWG issue] not what the spec currently says + } + + item = t.baseVal.getItem(5); + res = t.baseVal.appendItem(item); + + is(t.baseVal.numberOfItems, 7, + 'The '+t.list_type+' object should contain seven list items.'); + if (t.item_type != "DOMString") { + ok(res !== item && + t.baseVal.getItem(6) !== item, + 'The method '+t.list_type+'.appendItem() should clone the object '+ + 'that is passed in, if that object is already the last item in '+ + 'that list.'); + // [SVGWG issue] not what the spec currently says + + eventChecker.expect(""); + threw = false; + try { + t.baseVal.appendItem({}); + } catch(e) { + threw = true; + } + ok(threw, + 'The method '+t.list_type+'.appendItem() should throw if passed '+ + 'an object of the wrong type.'); + eventChecker.ignoreEvents(); + } + + // Test removal and addition events + + eventChecker.expect("remove add"); + t.element.removeAttribute(t.attr_name); + t.element.removeAttributeNS(null, t.attr_name); + res = t.baseVal.appendItem(item); + eventChecker.finish(); + } +} + + +/** + * This function tests the SVGXxxList API for the anim val list (see also the + * comment for test_baseVal_API). + */ +function run_animVal_API_tests() +{ + var threw, item; + + for (var t of tests) { + if (!t.animVal) + continue; // SVGStringList isn't animatable + + item = t.item_constructor(); + + t.element.setAttribute(t.attr_name, t.attr_val_4); + + is(t.animVal.numberOfItems, 4, + 'The '+t.list_type+' object should contain four list items.'); + + // Test .clear(): + + threw = false; + try { + t.animVal.clear(); + } catch(e) { + threw = true; + } + ok(threw, + 'The method '+t.list_type+'.clear() should throw when called on an '+ + 'anim val list, since anim val lists should be readonly.'); + + // Test .getItem(): + + var item = t.animVal.getItem(2); + ok(item != null && item === t.animVal.getItem(2), + 'The method '+t.list_type+'.getItem() should work when called on an '+ + 'anim val list, and always return the exact same object.'); + + // .initialize() + + threw = false; + try { + t.animVal.initialize(item); + } catch(e) { + threw = true; + } + ok(threw, + 'The method '+t.list_type+'.initialize() should throw when called on '+ + 'an anim val list, since anim val lists should be readonly.'); + + // Test .insertItemBefore(): + + threw = false; + try { + t.animVal.insertItemBefore(item, 2); + } catch(e) { + threw = true; + } + ok(threw, + 'The method '+t.list_type+'.insertItemBefore() should throw when '+ + 'called on an anim val list, since anim val lists should be readonly.'); + + // Test .replaceItem(): + + threw = false; + try { + t.animVal.replaceItem(item, 2); + } catch(e) { + threw = true; + } + ok(threw, + 'The method '+t.list_type+'.replaceItem() should throw when called '+ + 'on an anim val list, since anim val lists should be readonly.'); + + // Test .removeItem(): + + threw = false; + try { + t.animVal.removeItem(2); + } catch(e) { + threw = true; + } + ok(threw, + 'The method '+t.list_type+'.removeItem() should throw when called '+ + 'on an anim val list, since anim val lists should be readonly.'); + + // Test .appendItem(): + + threw = false; + try { + t.animVal.appendItem(item); + } catch(e) { + threw = true; + } + ok(threw, + 'The method '+t.list_type+'.appendItem() should throw when called '+ + 'on an anim val list, since anim val lists should be readonly.'); + } +} + + +/** + * This function runs some basic tests to check the effect of setAttribute() + * calls on object identity, without taking SMIL animation into consideration. + */ +function run_basic_setAttribute_tests() +{ + for (var t of tests) { + + // Since the t.prop, t.baseVal and t.animVal objects should never ever + // change, we leave testing of them to our caller so that it can check + // them after all the other mutations such as SMIL changes. + + t.element.setAttribute(t.attr_name, t.attr_val_4); + + ok(t.baseVal.numberOfItems == 4 && t.baseVal.getItem(3) != null, + 'The length of the '+t.list_type+' object for '+t.bv_path+' should '+ + 'have been set to 4 by the setAttribute() call.'); + + if (t.animVal) { + ok(t.baseVal.numberOfItems == t.animVal.numberOfItems, + 'When no animations are active, the '+t.list_type+' objects for '+ + t.bv_path+' and '+t.av_path+' should be the same length (4).'); + + ok(t.baseVal !== t.animVal, + 'The '+t.list_type+' objects for '+t.bv_path+' and '+t.av_path+ + ' should be different objects.'); + + ok(t.baseVal.getItem(0) !== t.animVal.getItem(0), + 'The '+t.item_type+' list items in the '+t.list_type+' objects for '+ + t.bv_path+' and '+t.av_path+' should be different objects.'); + } + + ok(t.baseVal.getItem(0) === t.baseVal.getItem(0), + 'The exact same '+t.item_type+' DOM object should be returned each '+ + 'time the item at a given index in the '+t.list_type+' for '+ + t.bv_path+' is accessed, given that the index was not made invalid '+ + 'by a change in list length between the successive accesses.'); + + if (t.animVal) { + ok(t.animVal.getItem(0) === t.animVal.getItem(0), + 'The exact same '+t.item_type+' DOM object should be returned each '+ + 'time the item at a given index in the '+t.list_type+' for '+ + t.av_path+' is accessed, given that the index was not made invalid '+ + 'by a change in list length between the successive accesses.'); + } + + // Test the effect of setting the attribute to new values: + + t.old_baseVal_items = get_array_of_list_items(t.baseVal); + if (t.animVal) { + t.old_animVal_items = get_array_of_list_items(t.animVal); + } + + t.element.setAttribute(t.attr_name, t.attr_val_3a); + t.element.setAttribute(t.attr_name, t.attr_val_5a); + + ok(t.baseVal.numberOfItems == 5 && t.baseVal.getItem(4) != null, + 'The length of the '+t.list_type+' object for '+t.bv_path+' should '+ + 'have been set to 5 by the setAttribute() call.'); + + if (t.animVal) { + ok(t.baseVal.numberOfItems == t.animVal.numberOfItems, + 'Since no animations are active, the length of the '+t.list_type+' '+ + 'objects for '+t.bv_path+' and '+t.av_path+' should be the same '+ + '(5).'); + } + + if (t.item_type != "DOMString") { + ok(t.baseVal.getItem(2) === t.old_baseVal_items[2], + 'After its attribute changes, list items in the '+t.list_type+' for '+ + t.bv_path+' that are at indexes that existed prior to the attribute '+ + 'change should be the exact same objects as the objects that were '+ + 'at those indexes prior to the attribute change.'); + + ok(t.baseVal.getItem(3) !== t.old_baseVal_items[3], + 'After its attribute changes, list items in the '+t.list_type+' for '+ + t.bv_path+' that are at indexes that did not exist prior to the '+ + 'attribute change should not be the same objects as any objects that '+ + 'were at those indexes at some earlier time.'); + } + + if (t.animVal) { + ok(t.animVal.getItem(2) === t.old_animVal_items[2], + 'After its attribute changes, list items in the '+t.list_type+' for '+ + t.av_path+' that are at indexes that existed prior to the attribute '+ + 'change should be the exact same objects as the objects that were '+ + 'at those indexes prior to the attribute change.'); + + ok(t.animVal.getItem(3) !== t.old_animVal_items[3], + 'After its attribute changes, list items in the '+t.list_type+' for '+ + t.av_path+' that are at indexes that did not exist prior to the '+ + 'attribute change should not be the same objects as any objects '+ + 'that were at those indexes at some earlier time.'); + } + } +} + +/** + * This function verifies that a list's animVal is kept in sync with its + * baseVal, when we add & remove items from the baseVal. + */ +function run_list_mutation_tests() +{ + for (var t of tests) { + if (t.animVal) { + // Test removeItem() + // ================= + // Save second item in baseVal list; then make it the first item, and + // check that animVal is updated accordingly. + t.element.setAttribute(t.attr_name, t.attr_val_4); + + var secondVal = t.baseVal.getItem(1); + var removedFirstVal = t.baseVal.removeItem(0); + t.item_is(t.animVal.getItem(0), secondVal, + 'animVal for '+t.attr_name+' needs update after first item ' + + 'removed'); + + // Repeat with last item + var secondToLastVal = t.baseVal.getItem(1); + var removedLastVal = t.baseVal.removeItem(2); + + var threw = false; + try { + t.animVal.getItem(2); + } catch(e) { + threw = true; + } + ok(threw, + 'The method '+t.attr_name+'.animVal.getItem() for previously-final ' + + 'index should throw after final item is removed from baseVal.'); + + t.item_is(t.animVal.getItem(1), secondToLastVal, + 'animVal for ' + t.attr_name +' needs update after last item ' + + 'removed'); + + // Test insertItemBefore() + // ======================= + // Reset base value, insert value @ start, check that animVal is updated. + t.element.setAttribute(t.attr_name, t.attr_val_3a); + t.baseVal.insertItemBefore(removedLastVal, 0); + t.item_is(t.animVal.getItem(0), removedLastVal, + 'animVal for '+t.attr_name+' needs update after insert at ' + + 'beginning'); + + // Repeat with insert at end + t.element.setAttribute(t.attr_name, t.attr_val_3a); + t.baseVal.insertItemBefore(removedFirstVal, t.baseVal.numberOfItems); + t.item_is(t.animVal.getItem(t.baseVal.numberOfItems - 1), + removedFirstVal, + 'animVal for '+t.attr_name+' needs update after insert at end'); + + // Test appendItem() + // ================= + var dummy = t.item_constructor(); + t.baseVal.appendItem(dummy); + t.item_is(t.animVal.getItem(t.baseVal.numberOfItems - 1), dummy, + 'animVal for '+t.attr_name+' needs update after appendItem'); + + // Test clear() + // ============ + t.baseVal.clear(); + threw = false; + try { + t.animVal.getItem(0); + } catch(e) { + threw = true; + } + ok(threw, + 'The method '+t.attr_name+'.animVal.getItem() should throw after ' + + 'we\'ve cleared baseVal.'); + + is(t.animVal.numberOfItems, 0, + 'animVal for '+t.attr_name+' should be empty after baseVal cleared'); + + // Test initialize() + // ================= + t.element.setAttribute(t.attr_name, t.attr_val_3a); + t.baseVal.initialize(dummy); + + is(t.animVal.numberOfItems, 1, + 'animVal for '+t.attr_name+' should have length 1 after initialize'); + t.item_is(t.animVal.getItem(0), dummy, + 'animVal for '+t.attr_name+' needs update after initialize'); + } + } +} + +/** + * In this function we run a series of tests at various points along the SMIL + * animation timeline, using SVGSVGElement.setCurrentTime() to move forward + * along the timeline. + * + * See the comment for create_animate_elements() for details of the animations + * and their timings. + */ +function run_animation_timeline_tests() +{ + var svg = document.getElementById('svg'); + + for (var t of tests) { + // Skip if there is no animVal for this test or if it is a transform list + // since these are handled specially + if (!t.animVal || is_transform_attr(t.attr_name)) + continue; + + svg.setCurrentTime(0); // reset timeline + + // Reset attributes before moving along the timeline and triggering SMIL: + t.element.setAttribute(t.attr_name, t.attr_val_4); + t.old_baseVal_items = get_array_of_list_items(t.baseVal); + t.old_animVal_items = get_array_of_list_items(t.animVal); + + + /******************** t = 1s ********************/ + + svg.setCurrentTime(1); // begin first animation + + ok(t.baseVal.numberOfItems == t.old_baseVal_items.length && + t.baseVal.getItem(3) === t.old_baseVal_items[3], + 'The start of an animation should never affect the '+t.list_type+ + ' for '+t.bv_path+', or its list items.'); + + ok(t.animVal.numberOfItems == 5 && t.animVal.getItem(4) != null, + 'The start of the animation should have changed the number of items '+ + 'in the '+t.list_type+' for '+t.bv_path+' to 5.'); + + // TODO + if (t.list_type != 'SVGPathSegList') { + ok(t.animVal.getItem(3) === t.old_animVal_items[3], + 'When affected by SMIL animation, list items in the '+t.list_type+ + ' for '+t.bv_path+' that are at indexes that existed prior to the '+ + 'start of the animation should be the exact same objects as the '+ + 'objects that were at those indexes prior to the start of the '+ + 'animation.'); + } + + t.old_animVal_items = get_array_of_list_items(t.animVal); + + t.element.setAttribute(t.attr_name, t.attr_val_3a); + + ok(t.baseVal.numberOfItems == 3 && + t.baseVal.getItem(2) === t.old_baseVal_items[2], + 'Setting the underlying attribute should change the items in the '+ + t.list_type+' for '+t.bv_path+', including when an animation is '+ + 'in progress.'); + + ok(t.animVal.numberOfItems == 5 && + t.animVal.getItem(4) === t.old_animVal_items[4], + 'Setting the underlying attribute should not change the '+t.list_type+ + ' for '+t.bv_path+' when an animation that does not depend on the '+ + 'base val is in progress.'); + + t.element.setAttribute(t.attr_name, t.attr_val_4); // reset + + t.old_baseVal_items = get_array_of_list_items(t.baseVal); + t.old_animVal_items = get_array_of_list_items(t.animVal); + + + /******************** t = 2s ********************/ + + svg.setCurrentTime(2); // begin override animation + + ok(t.baseVal.numberOfItems == t.old_baseVal_items.length && + t.baseVal.getItem(3) === t.old_baseVal_items[3], + 'The start of an override animation should never affect the '+ + t.list_type+' for '+t.bv_path+', or its list items.'); + + is(t.animVal.numberOfItems, 3, + 'The start of the override animation should have changed the number '+ + 'of items in the '+t.list_type+' for '+t.bv_path+' to 3.'); + + ok(t.animVal.getItem(2) === t.old_animVal_items[2], + 'When affected by an override SMIL animation, list items in the '+ + t.list_type+' for '+t.bv_path+' that are at indexes that existed '+ + 'prior to the start of the animation should be the exact same '+ + 'objects as the objects that were at those indexes prior to the '+ + 'start of that animation.'); + + t.old_animVal_items = get_array_of_list_items(t.animVal); + + + /******************** t = 3s ********************/ + + svg.setCurrentTime(3); // end of override animation + + ok(t.baseVal.numberOfItems == t.old_baseVal_items.length && + t.baseVal.getItem(3) === t.old_baseVal_items[3], + 'The end of an override animation should never affect the '+ + t.list_type+' for '+t.bv_path+', or its list items.'); + + is(t.animVal.numberOfItems, 5, + 'At the end of the override animation, the number of items in the '+ + t.list_type+' for '+t.bv_path+' should have reverted to 5.'); + + ok(t.animVal.getItem(2) === t.old_animVal_items[2], + 'At the end of the override animation, list items in the '+ + t.list_type+' for '+t.bv_path+' that are at indexes that existed '+ + 'prior to the end of the animation should be the exact same '+ + 'objects as the objects that were at those indexes prior to the '+ + 'end of that animation.'); + + t.old_animVal_items = get_array_of_list_items(t.animVal); + + + /******************** t = 5s ********************/ + + svg.setCurrentTime(5); // animation repeat point + + ok(t.baseVal.numberOfItems == t.old_baseVal_items.length && + t.baseVal.getItem(3) === t.old_baseVal_items[3], + 'When a SMIL animation repeats, it should never affect the '+ + t.list_type+' for '+t.bv_path+', or its list items.'); + + ok(t.animVal.numberOfItems == t.old_animVal_items.length && + t.animVal.getItem(4) === t.old_animVal_items[4], + 'When an animation repeats, the list items that are at a given '+ + 'index in the '+t.list_type+' for '+t.av_path+' should be the exact '+ + 'same objects as were at that index before the repeat occured.'); + + + /******************** t = 6s ********************/ + + svg.setCurrentTime(6); // inside animation repeat + + ok(t.baseVal.numberOfItems == t.old_baseVal_items.length && + t.baseVal.getItem(3) === t.old_baseVal_items[3], + 'When a SMIL animation repeats, it should never affect the '+ + t.list_type+' for '+t.bv_path+', or its list items.'); + + ok(t.animVal.numberOfItems == t.old_animVal_items.length && + t.animVal.getItem(4) === t.old_animVal_items[4], + 'When an animation repeats, the list items that are at a given '+ + 'index in the '+t.list_type+' for '+t.av_path+' should be the exact '+ + 'same objects as were at that index before the repeat occured.'); + + + /******************** t = 7s ********************/ + + svg.setCurrentTime(7); // start of additive="sum" animation + + ok(t.baseVal.numberOfItems == t.old_baseVal_items.length && + t.baseVal.getItem(3) === t.old_baseVal_items[3], + 'When a new SMIL animation starts and should blend with an '+ + 'underlying animation, it should never affect the '+ + t.list_type+' for '+t.bv_path+', or its list items.'); + + if (t.list_type == 'SVGLengthList') { + + // Length lists are a special case where it makes sense to allow shorter + // lists to be composed on top of longer lists (but not necessarily vice + // versa - see comment below). + + ok(t.animVal.numberOfItems == t.old_animVal_items.length && + t.animVal.getItem(3) === t.old_animVal_items[3], + 'When an animation with additive="sum" is added on top of an '+ + 'existing animation that has more list items, the length of the '+ + t.list_type+' for '+t.av_path+' should not change.'); + + } else { + +/* TODO + ok(false, + 'Decide what to do here - see ' + + 'https://bugzilla.mozilla.org/show_bug.cgi?id=573716 - we ' + + 'probably should be discarding any animation sandwich layers from ' + + 'a layer that fails to add, on up.'); +*/ + + // In other words, we wouldn't need the if-else check here. + + } + + // XXX what if the higher priority sandwich layer has *more* list items + // than the underlying animation? In that case we would need to + // distinguish between different SVGLengthList attributes, since although + // all SVGLengthList attributes allow a short list to be added to longer + // list, they do not all allow a longer list to be added to shorter list. + // Specifically that would not be good for 'x' and 'y' on <text> since + // lengths there are not naturally zero. See the comment in + // SVGLengthListSMILAttr::Add(). + + + /******************** t = 13s ********************/ + + svg.setCurrentTime(13); // all animations have finished, but one is frozen + + ok(t.baseVal.numberOfItems == t.old_baseVal_items.length && + t.baseVal.getItem(3) === t.old_baseVal_items[3], + 'When a SMIL animation ends, it should never affect the '+ + t.list_type+' for '+t.bv_path+', or its list items.'); + + is(t.animVal.numberOfItems, 5, + 'Even though all SMIL animation have finished, the number '+ + 'of items in the '+t.list_type+' for '+t.av_path+ + ' should still be more than the same as the number of items in '+ + t.bv_path+' since one of the animations is still frozen.'); + + var expected = t.attr_val_5b_firstItem_x3_constructor(t.item_constructor); + t.item_is(t.animVal.getItem(0), expected, + 'animation with accumulate="sum" and repeatCount="3" for attribute "'+ + t.attr_name+'" should end up at 3x the "to" value.'); + + // Unfreeze frozen animation (removing its effects) + var frozen_animate_element = + t.element.querySelector('animate[fill][attributeName="'+t.attr_name+'"]'); + frozen_animate_element.removeAttribute('fill'); + + ok(t.animVal.numberOfItems == t.baseVal.numberOfItems, + 'Once all SMIL animation have finished and been un-frozen, the number '+ + 'of items in the '+t.list_type+' for '+t.av_path+ + ' should be the same as the number of items in '+t.bv_path+'.'); + + ok(t.animVal.getItem(2) === t.old_animVal_items[2], + 'Even after an animation finishes and is un-frozen, the list items '+ + 'that are at a given index in the '+t.list_type+' for '+t.av_path+ + ' should be the exact same objects as were at that index before the '+ + 'end and unfreezing of the animation occured.'); + } +} + + +function run_tests() +{ + // Initialize each test object with some useful properties, and create their + // 'animate' elements. Note that 'prop' and 'animVal' may be null. + for (var t of tests) { + t.element = document.getElementById(t.target_element_id); + t.prop = t.prop_name ? t.element[t.prop_name] : null; + t.baseVal = ( t.prop || t.element )[t.bv_name]; + t.animVal = t.av_name ? ( t.prop || t.element )[t.av_name] : null; + t.bv_path = t.el_type + '.' + + (t.prop ? t.prop_name + '.' : '') + + t.bv_name; // e.g. 'SVGTextElement.x.baseVal' + if (t.animVal) { + t.av_path = t.el_type + '.' + + (t.prop ? t.prop_name + '.' : '') + + t.av_name; + } + t.prop_type = t.prop_type || null; + + // use fallback 'is' function, if none was provided. + if (!t.item_is) { + t.item_is = function(itemA, itemB, message) { + ok(typeof(itemA.value) != 'undefined' && + typeof(itemB.value) != 'undefined', + 'expecting value property'); + is(itemA.value, itemB.value, message); + }; + } + + if (t.animVal) { + t.element.appendChild(create_animate_elements(t)); + } + } + + // Run the major test groups: + + run_baseVal_API_tests(); + run_animVal_API_tests(); + run_basic_setAttribute_tests(); + run_list_mutation_tests(); + run_animation_timeline_tests(); + + // After all the other test manipulations, we check that the following + // objects have still not changed, since they never should: + + for (var t of tests) { + if (t.prop) { + ok(t.prop === t.element[t.prop_name], + 'The same '+t.prop_type+' object should ALWAYS be returned for '+ + t.el_type+'.'+t.prop_name+' each time it is accessed.'); + } + + ok(t.baseVal === ( t.prop || t.element )[t.bv_name], + 'The same '+t.list_type+' object should ALWAYS be returned for '+ + t.el_type+'.'+t.prop_name+'.'+t.bv_name+' each time it is accessed.'); + + if (t.animVal) { + ok(t.animVal === ( t.prop || t.element )[t.av_name], + 'The same '+t.list_type+' object should ALWAYS be returned for '+ + t.el_type+'.'+t.prop_name+'.'+t.av_name+' each time it is accessed.'); + } + } + + SimpleTest.finish(); +} + +window.addEventListener("load", run_tests, false); + +]]> +</script> +</pre> +</body> +</html> diff --git a/dom/svg/test/test_SVGxxxListIndexing.xhtml b/dom/svg/test/test_SVGxxxListIndexing.xhtml new file mode 100644 index 0000000000..31544b23dd --- /dev/null +++ b/dom/svg/test/test_SVGxxxListIndexing.xhtml @@ -0,0 +1,100 @@ +<html xmlns="http://www.w3.org/1999/xhtml"> +<!-- +https://bugzilla.mozilla.org/show_bug.cgi?id=631437 +--> +<head> + <title>Tests the array indexing and .length on SVGXXXList objects</title> + <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" /> +</head> +<body> +<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=631437">Mozilla Bug 631437</a> +<svg xmlns="http://www.w3.org/2000/svg" id="svg"> + <text id="text" x="10 20 30" rotate="40 50 60">abcde</text> + <path id="path" d="M0,0 L100,100"/> + <polygon id="poly" points="50,50 70,70 90,50"/> + <g id="g" transform="translate(20 30) rotate(50 60 70) scale(2)" + requiredFeatures="foo bar baz"/> +</svg> +<script type="text/javascript;version=1.8"><![CDATA[ +var text = document.getElementById("text"), + path = document.getElementById("path"), + poly = document.getElementById("poly"); + g = document.getElementById("g"); + +function CheckList(aListObject, aExpectedListLength, aListDescription) +{ + is(aListObject.numberOfItems, aExpectedListLength, aListDescription + ".numberOfItems"); + is(aListObject.length, aExpectedListLength, aListDescription + ".length"); + for (let i = 0; i < aListObject.length; i++) { + let item = aListObject.getItem(i); + ok(aListObject[i] === item, aListDescription + "[" + i + "]"); + } + is(typeof(aListObject[aListObject.length]), "undefined", aListDescription + "[outOfBounds]"); +} + +var tests = [ + { element: text, + attribute: "x", + listProperty: "x.baseVal", + type: "SVGLengthList", + subtests: [ { values: null, length: 3 }, + { values: "40", length: 1 }, + { values: "1em 2em 3em 4em 5em", length: 5 } ] }, + { element: text, + attribute: "rotate", + listProperty: "rotate.baseVal", + type: "SVGNumberList", + subtests: [ { values: null, length: 3 }, + { values: "10", length: 1 }, + { values: "1 2 3 4 5", length: 5 } ] }, + { element: path, + attribute: "d", + listProperty: "pathSegList", + type: "SVGPathSegList", + subtests: [ { values: null, length: 2 }, + { values: "M50,50", length: 1 }, + { values: "M0,0 h10 v20 h30 v40", length: 5 } ] }, + { element: poly, + attribute: "points", + listProperty: "animatedPoints", + type: "SVGPointList", + subtests: [ { values: null, length: 3 }, + { values: "100,100", length: 1 }, + { values: "0,0 10,10 20,0 30,10 40,0", length: 5 } ] }, + { element: g, + attribute: "transform", + listProperty: "transform.baseVal", + type: "SVGTransformList", + subtests: [ { values: null, length: 3 }, + { values: "skewX(45)", length: 1 }, + { values: "translate(1 2) rotate(3) scale(4) skewY(5) skewX(6)", + length: 5 } ] }, + { element: g, + attribute: "requiredFeatures", + listProperty: "requiredFeatures", + type: "SVGStringList", + subtests: [ { values: null, length: 3 }, + { values: "foo", length: 1 }, + { values: "foo bar baz qux", length: 4 } ] } +]; + +for (let test of tests) { + let list = test.element; + for (let property of test.listProperty.split(".")) { + list = list[property]; + } + + for (let subtest of test.subtests) { + if (subtest.values) { + test.element.setAttribute(test.attribute, subtest.values); + } + + CheckList(list, subtest.length, + test.type + ": " + test.element.localName + "." + + test.listProperty); + } +} +]]></script> +</body> +</html> diff --git a/dom/svg/test/test_a_href_01.xhtml b/dom/svg/test/test_a_href_01.xhtml new file mode 100644 index 0000000000..9086338894 --- /dev/null +++ b/dom/svg/test/test_a_href_01.xhtml @@ -0,0 +1,96 @@ +<html xmlns="http://www.w3.org/1999/xhtml"> +<!-- +https://bugzilla.mozilla.org/show_bug.cgi?id=620295 +https://bugzilla.mozilla.org/show_bug.cgi?id=1245751 +--> +<head> + <title>Test that activating SVG 'a' elements navigate to their xlink:href or href</title> + <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <script type="application/javascript" src="/tests/SimpleTest/EventUtils.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" /> +</head> +<body> +<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=620295">Mozilla Bug 620295</a> +<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1245751">Mozilla Bug 1245751</a> +<p id="display"></p> +<pre id="test"> +<script class="testbody" type="text/javascript"><![CDATA[ + +SimpleTest.waitForExplicitFinish(); + +var testCount = 7; +var didWindowLoad = false; +var frameLoadCount = 0; +var navigationCount = 0; + +function endsWith(s1, s2) { + s1 = String(s1); + return s1.length >= s2.length && s1.substring(s1.length - s2.length) == s2; +} + +function windowLoaded() { + didWindowLoad = true; + doNavigationIfReady(); +} + +function frameLoaded() { + frameLoadCount++; + doNavigationIfReady(); +} + +function doNavigationIfReady() { + if (didWindowLoad && frameLoadCount == testCount) { + doNavigation(); + } +} + +function doNavigation() { + // Test clicking on an unmodified <a>. + doNavigationTest(1, "a_href_helper_01.svg"); + // Test clicking on an <a> whose xlink:href is modified by assigning to href.baseVal. + doNavigationTest(2, "a_href_helper_02_03.svg", function(a) { a.href.baseVal = "a_href_destination.svg"; }); + // Test clicking on an <a> whose xlink:href is modified by a setAttributeNS call. + doNavigationTest(3, "a_href_helper_02_03.svg", function(a) { a.setAttributeNS("http://www.w3.org/1999/xlink", "xlink:href", "a_href_destination.svg"); }); + // Test clicking on an <a> whose xlink:href is modified by animation. + doNavigationTest(4, "a_href_helper_04.svg"); + // Test clicking on an unmodified <a> with both href and xlink:href. + doNavigationTest(5, "a_href_helper_05.svg"); + // Test clicking on an <a> whose href is modified by a setAttribute call. + doNavigationTest(6, "a_href_helper_06.svg", function(a) { a.setAttribute("href", "a_href_destination.svg"); }); + // Test clicking on an <a> whose href is modified by animation. + doNavigationTest(7, "a_href_helper_07.svg"); +} + +function doNavigationTest(testNumber, initialHref, f) { + var iframe = document.getElementById("iframe" + testNumber); + var a = iframe.contentDocument.getElementById("a"); + ok(endsWith(iframe.contentWindow.location, initialHref), "Initial href of test " + testNumber); + is("pointer", window.getComputedStyle(a).getPropertyValue("cursor"), "expected pointer cursor"); + iframe.onload = function() { + ok(endsWith(iframe.contentWindow.location, "a_href_destination.svg"), "Final href of test " + testNumber); + if (++navigationCount == testCount) { + SimpleTest.finish(); + } + }; + if (f) { + f(a); + } + sendMouseEvent({type:'click'}, a); +} + +window.onload = windowLoaded; + +]]></script> +</pre> +<div id="content" style="visibility: hidden"> +<!-- These must come after frameLoaded is defined --> +<iframe id="iframe1" src="a_href_helper_01.svg" onload="frameLoaded()"></iframe> +<iframe id="iframe2" src="a_href_helper_02_03.svg" onload="frameLoaded()"></iframe> +<iframe id="iframe3" src="a_href_helper_02_03.svg" onload="frameLoaded()"></iframe> +<iframe id="iframe4" src="a_href_helper_04.svg" onload="frameLoaded()"></iframe> +<iframe id="iframe5" src="a_href_helper_05.svg" onload="frameLoaded()"></iframe> +<iframe id="iframe6" src="a_href_helper_06.svg" onload="frameLoaded()"></iframe> +<iframe id="iframe7" src="a_href_helper_07.svg" onload="frameLoaded()"></iframe> +</div> +</body> +</html> diff --git a/dom/svg/test/test_a_href_02.xhtml b/dom/svg/test/test_a_href_02.xhtml new file mode 100644 index 0000000000..ee3fd0581f --- /dev/null +++ b/dom/svg/test/test_a_href_02.xhtml @@ -0,0 +1,37 @@ +<html xmlns="http://www.w3.org/1999/xhtml"> +<!-- +https://bugzilla.mozilla.org/show_bug.cgi?id=620295 +--> +<head> + <title>Test that the href property reflects xlink:href="" on 'a' elements</title> + <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" /> +</head> +<body> +<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=620295">Mozilla Bug 620295</a> +<p id="display"></p> +<div id="content"> +<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"> + <a id="a" xlink:href="a"/> +</svg> +</div> +<pre id="test"> +<script><![CDATA[ + +var a = document.getElementById("a"); + +// Initial attribute value should be reflected in the href property +is(a.href.baseVal, "a", "Initial property value"); + +// Updated attribute value should be reflected in the href property +a.setAttributeNS("http://www.w3.org/1999/xlink", "xlink:href", "b"); +is(a.href.baseVal, "b", "Updated property value"); + +// Modifying the href property should cause the attribute to be updated +a.href.baseVal = "c"; +is(a.getAttributeNS("http://www.w3.org/1999/xlink", "href"), "c", "Updated attribute value"); + +]]></script> +</pre> +</body> +</html> diff --git a/dom/svg/test/test_animLengthObjectIdentity.xhtml b/dom/svg/test/test_animLengthObjectIdentity.xhtml new file mode 100644 index 0000000000..af6ababd5a --- /dev/null +++ b/dom/svg/test/test_animLengthObjectIdentity.xhtml @@ -0,0 +1,86 @@ +<html xmlns="http://www.w3.org/1999/xhtml"> +<!-- +https://bugzilla.mozilla.org/show_bug.cgi?id=508496 +--> +<head> + <title>Test for object identity of SVG animated lengths</title> + <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" /> +</head> +<body> +<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=506856">Mozilla Bug 508496</a> +<p id="display"></p> +<div id="content" style="display: none"> +<svg id="svg" xmlns="http://www.w3.org/2000/svg" width="120px" height="120px" + onload="this.pauseAnimations()"> + <circle cx="-100" cy="-100" r="15" fill="blue" id="circle"> + <animate attributeName="cx" from="0" to="100" dur="4s" begin="1s; 10s" + fill="freeze" id="animate"/> + </circle> +</svg> +</div> +<pre id="test"> +<script class="testbody" type="text/javascript"> +<![CDATA[ +/** Test object identity of animated lengths **/ + +/* Global Variables */ +const svgns="http://www.w3.org/2000/svg"; +var svg = document.getElementById("svg"); +var circle = document.getElementById('circle'); + +SimpleTest.waitForExplicitFinish(); + +function main() { + ok(svg.animationsPaused(), "should be paused by <svg> load handler"); + is(svg.getCurrentTime(), 0, "should be paused at 0 in <svg> load handler"); + + var animLength = circle.cx; + ok(animLength === circle.cx, + "Got different SVGAnimatedLength objects at startup"); + + var baseVal = circle.cx.baseVal; + ok(baseVal === circle.cx.baseVal, + "Got different baseVal SVGLength objects at startup"); + + var animVal = circle.cx.animVal; + ok(animVal === circle.cx.animVal, + "Got different animVal SVGLength objects at startup"); + + var animate = document.getElementById('animate'); + if (animate && animate.targetElement) { + // Sample mid-way through the animation + svg.setCurrentTime(5); + + ok(animLength === circle.cx, + "Got different SVGAnimatedLength objects during animation"); + ok(baseVal === circle.cx.baseVal, + "Got different baseVal SVGLength objects during animation"); + ok(animVal === circle.cx.animVal, + "Got different animVal SVGLength objects during animation"); + } + + // Drop all references to the tear off objects + var oldValue = circle.cx.animVal.value; // Just a float, not an object ref + animLength = null; + baseVal = null; + animVal = null; + SpecialPowers.gc(); + + // The tearoff objects should no longer exist and we should create new ones. + // If somehow, the tearoff objects have died and yet not been removed from the + // hashmap we'll end up in all sorts of trouble when we try to access them. + // So in the following, we're not really interested in the value, just that we + // don't crash. + is(circle.cx.animVal.value, oldValue, + "Unexpected result accessing new(?) length object."); + + SimpleTest.finish(); +} + +window.addEventListener("load", main, false); +]]> +</script> +</pre> +</body> +</html> diff --git a/dom/svg/test/test_animLengthReadonly.xhtml b/dom/svg/test/test_animLengthReadonly.xhtml new file mode 100644 index 0000000000..67953f6761 --- /dev/null +++ b/dom/svg/test/test_animLengthReadonly.xhtml @@ -0,0 +1,221 @@ +<html xmlns="http://www.w3.org/1999/xhtml"> +<!-- +https://bugzilla.mozilla.org/show_bug.cgi?id=506856 +--> +<head> + <title>Test for read-only times of SVG animated lengths</title> + <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" /> +</head> +<body> +<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=506856">Mozilla Bug 506856</a> +<p id="display"></p> +<div id="content" style="display: none"> +<svg id="svg" xmlns="http://www.w3.org/2000/svg" + width="100px" height="100px" viewBox="0 0 100 100" + onload="this.pauseAnimations()"> + <!-- Note: Need a viewBox on the SVG element, or else our percent-length + basis will be '0' (based off of the viewport width, which is 0 in this + case since we're in a display:none iframe. We use viewport width because + the lack of a viewbox forces us into the not-mViewBox::IsValid() case of + SVGSVGElement::GetLength). + + And we don't want a percent-length basis of 0, because then when we call + convertToSpecifiedUnits to convert out of percent units, we divide by 0 + and get unexpected results. + --> + <circle cx="-100" cy="-100" r="15" fill="blue" id="circle"> + <animate attributeName="cx" from="0" to="100" dur="4s" begin="1s; 10s" + fill="freeze" id="animate"/> + <animate attributeName="cy" from="-100" to="-100" dur="4s" begin="1s; 10s" + fill="remove"/> + </circle> +</svg> +</div> +<pre id="test"> +<script class="testbody" type="text/javascript"> +<![CDATA[ +/** Test read-only times of animated lengths **/ + +/* Global Variables */ +const svgns="http://www.w3.org/2000/svg"; +var svg = document.getElementById("svg"); +var circle = document.getElementById('circle'); + +SimpleTest.waitForExplicitFinish(); + +function main() { + ok(svg.animationsPaused(), "should be paused by <svg> load handler"); + is(svg.getCurrentTime(), 0, "should be paused at 0 in <svg> load handler"); + + // Sanity check: check initial values + is(circle.cx.baseVal.value, -100); + is(circle.cx.animVal.value, -100); + is(circle.cy.baseVal.value, -100); + is(circle.cy.animVal.value, -100); + + // (1): Check before animation that animVal's are readonly + ok(checkReadOnly(circle.cx), + "(1) animVal cx is editable when not animated"); + ok(checkReadOnly(circle.cy), + "(1) animVal cy is editable when not animated"); + + // Skip to mid-way through the animation + svg.setCurrentTime(4); + + // (2): Check that whilst animations are active the animVal's are readonly + ok(checkReadOnly(circle.cx), + "(2) animVal cx is editable when animated"); + ok(checkReadOnly(circle.cy), + "(2) animVal cy is editable when animated"); + + // Skip to past end + svg.setCurrentTime(6); + + // (3): Check that frozen animations are readonly and have different values + ok(checkReadOnly(circle.cx), + "(3) animVal cx is editable when frozen"); + checkDiffValue(circle.cx); + + // (4): Check that finished animations are readonly and have same values + ok(checkReadOnly(circle.cy), + "(4) animVal cy is editable when inactive"); + checkSameValue(circle.cy); + + SimpleTest.finish(); +} + +function checkReadOnly(animLength) { + var exceptionCaught = false; + var oldAnimValue = animLength.animVal.value; + + // Try changing value + try { + animLength.animVal.value = (animLength.animVal.value == 77) ? 88 : 77; + } catch (e) { + if (e.name == "NoModificationAllowedError" && + e.code == DOMException.NO_MODIFICATION_ALLOWED_ERR) { + exceptionCaught = true; + } else { + ok(false, "Got unexpected exception " + e); + return false; + } + } + is(animLength.animVal.value, oldAnimValue, + "No exception thrown, but animVal was changed."); + if (animLength.animVal.value != oldAnimValue) return false; + + // valueInSpecifiedUnits + try { + exceptionCaught = false; + animLength.animVal.valueInSpecifiedUnits = -100; + } catch (e) { exceptionCaught = true; } + ok(exceptionCaught, "animVal.valueInSpecifiedUnits failed to throw."); + if (!exceptionCaught) return false; + + // valueAsString + try { + exceptionCaught = false; + animLength.animVal.valueAsString = "-100px"; + } catch (e) { exceptionCaught = true; } + ok(exceptionCaught, "animVal.valueAsString failed to throw."); + if (!exceptionCaught) return false; + + // newValueSpecifiedUnits + try { + exceptionCaught = false; + animLength.animVal.newValueSpecifiedUnits(SVGLength.SVG_LENGTHTYPE_PX,-100); + } catch (e) { exceptionCaught = true; } + ok(exceptionCaught, "animVal.newValueSpecifiedUnits failed to throw."); + if (!exceptionCaught) return false; + + // convertToSpecifiedUnits + try { + exceptionCaught = false; + animLength.animVal.convertToSpecifiedUnits(SVGLength.SVG_LENGTHTYPE_NUMBER); + } catch (e) { exceptionCaught = true; } + ok(exceptionCaught, "animVal.convertToSpecifiedUnits failed to throw."); + + return exceptionCaught; +} + +function checkSameValue(animLength) +{ + // value + animLength.baseVal.value = 1; + is(animLength.animVal.value, 1, + "un-animated animVal.value not changed after setting baseValue.value"); + + // valueInSpecifiedUnits + animLength.baseVal.valueInSpecifiedUnits = 2; + is(animLength.animVal.value, 2, + "un-animated animVal.value not changed after setting " + + "baseValue.valueInSpecifiedUnits"); + + // valueAsString + animLength.baseVal.valueAsString = "3"; + is(animLength.animVal.value, 3, + "un-animated animVal.value not changed after setting " + + "baseValue.valueAsString"); + + // newValueSpecifiedUnits + animLength.baseVal.newValueSpecifiedUnits(SVGLength.SVG_LENGTHTYPE_CM, 4); + is(animLength.animVal.valueInSpecifiedUnits, 4, + "un-animated animVal.value not changed after setting " + + "baseValue.newValueSpecifiedUnits"); + + // convertToSpecifiedUnits + animLength.baseVal.convertToSpecifiedUnits(SVGLength.SVG_LENGTHTYPE_MM); + is(animLength.animVal.valueInSpecifiedUnits, 40, + "un-animated animVal.value not changed after calling " + + "baseValue.convertToSpecifiedUnits"); +} + +function checkDiffValue(animLength) +{ + // We assume here that the animation is not additive and hence changing the + // baseValue will not be reflected in the animValue + var origValue = animLength.animVal.value; + + // value + animLength.baseVal.value = 1; + is(animLength.animVal.value, origValue, + "animated animVal.value changed after setting baseValue.value"); + + // valueInSpecifiedUnits + animLength.baseVal.valueInSpecifiedUnits = 2; + is(animLength.animVal.value, origValue, + "animated animVal.value changed after setting " + + "baseValue.valueInSpecifiedUnits"); + + // valueAsString + animLength.baseVal.valueAsString = "3"; + is(animLength.animVal.value, origValue, + "animated animVal.value changed after setting baseValue.valueAsString"); + + // newValueSpecifiedUnits + // (Note: we'd like to convert to MM here and CM in the next step for + // consistency with the other tests. However, internally that will cause the + // animVal to be converted to MM and back again which, given certain dpi + // values such as we get in Linux, this may lead to rounding errors so that + // 100 becomes 99.99999237060547. So instead we convert to something + // independent of dpi) + animLength.baseVal.newValueSpecifiedUnits( + SVGLength.SVG_LENGTHTYPE_PERCENTAGE, 40); + is(animLength.animVal.value, origValue, + "animated animVal.value changed after setting " + + "baseValue.newValueSpecifiedUnits"); + + // convertToSpecifiedUnits + animLength.baseVal.convertToSpecifiedUnits(SVGLength.SVG_LENGTHTYPE_PX); + is(animLength.animVal.value, origValue, + "animated animVal.value changed after calling " + + "baseValue.convertToSpecifiedUnits"); +} + +window.addEventListener("load", main, false); +]]> +</script> +</pre> +</body> +</html> diff --git a/dom/svg/test/test_animLengthUnits.xhtml b/dom/svg/test/test_animLengthUnits.xhtml new file mode 100644 index 0000000000..b04fa0f00c --- /dev/null +++ b/dom/svg/test/test_animLengthUnits.xhtml @@ -0,0 +1,126 @@ +<html xmlns="http://www.w3.org/1999/xhtml"> +<!-- +https://bugzilla.mozilla.org/show_bug.cgi?id=507067 +--> +<head> + <title>Test for units of SVG animated lengths</title> + <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" /> +</head> +<body> +<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=507067">Mozilla Bug 507067</a> +<p id="display"></p> +<div id="content"> +<svg id="svg" xmlns="http://www.w3.org/2000/svg" width="120px" height="120px" + onload="this.pauseAnimations()"> + <g font-size="10px"> + <circle cx="-100" cy="20" r="15" fill="blue" id="circle"> + <animate attributeName="cx" from="0em" to="10em" dur="8s" begin="1s" + fill="freeze" id="animate"/> + </circle> + </g> +</svg> +</div> +<pre id="test"> +<script class="testbody" type="text/javascript"> +<![CDATA[ +/** Test units of animated lengths **/ + +/* Global Variables */ +const svgns="http://www.w3.org/2000/svg"; +var svg = document.getElementById("svg"); +var circle = document.getElementById('circle'); +var animate = document.getElementById('animate'); + +SimpleTest.waitForExplicitFinish(); + +// Interop comments are based on: +// +// Opera -- 10 beta 2 +// WebKit -- July 09 trunk build +// Batik -- 1.7 +// Firefox -- July 09 trunk build +// + +function main() { + ok(svg.animationsPaused(), "should be paused by <svg> load handler"); + is(svg.getCurrentTime(), 0, "should be paused at 0 in <svg> load handler"); + + // Sanity check: check initial values + is(circle.cx.baseVal.valueInSpecifiedUnits, -100, + "Unexpected initial baseVal"); + is(circle.cx.baseVal.unitType, SVGLength.SVG_LENGTHTYPE_NUMBER, + "Unexpected initial baseVal units"); + is(circle.cx.animVal.valueInSpecifiedUnits, -100, + "Unexpected initial animVal"); + is(circle.cx.animVal.unitType, SVGLength.SVG_LENGTHTYPE_NUMBER, + "Unexpected initial animVal units"); + + // Sample mid-way through the animation + svg.setCurrentTime(5); + + // (1) Check the absolute value is right + // + // We're not too worried about the units. Based on our testing we get: + // Opera: Will use user units for the animVal + // Safari: Doesn't work + // Batik: Will use the units specified on the animation function provided they + // are the same + // FF: Will use the units of the baseVal for the animVal + // + is(circle.cx.baseVal.value, -100, + "(1) Unexpected value for baseVal during animation"); + is(circle.cx.animVal.value, 50, + "(1) Unexpected value for animVal during animation"); + + // Change font-size and check + circle.parentNode.setAttribute('font-size', '5px'); + + // Currently, changing the font-size on a parent doesn't force a resample (see + // bug 508206) so we have to give the animation a chance to run + window.requestAnimationFrame(checkAfterChangeFontSize); +} + +function checkAfterChangeFontSize() { + // (2) Check that changing the font-size of the parent element is reflected in + // the anim val + is(circle.cx.baseVal.value, -100, + "(2) Unexpected value for baseVal after changing font-size during " + + "animation"); + is(circle.cx.animVal.value, 25, + "(2) Unexpected value for animVal after changing font-size during " + + "animation"); + + // Do the same again, when the animation is frozen + svg.setCurrentTime(10); + circle.parentNode.setAttribute('font-size', '7px'); + + // Again, due to bug 508206 we need to give the animation a chance to resample + window.requestAnimationFrame(checkWhilstFrozen); +} + +function checkWhilstFrozen() { + // (3) Check that changing the font-size of the parent element is reflected in + // the anim val + is(circle.cx.baseVal.value, -100, + "(3) Unexpected value for baseVal after changing font-size whilst " + + "frozen"); + is(circle.cx.animVal.value, 70, + "(3) Unexpected value for animVal after changing font-size whilst " + + "frozen"); + + SimpleTest.finish(); +} + +var animate = document.getElementById('animate'); +if (animate && animate.targetElement) { + window.addEventListener("load", main, false); +} else { + ok(true); // Skip tests but don't report 'todo' either + SimpleTest.finish(); +} +]]> +</script> +</pre> +</body> +</html> diff --git a/dom/svg/test/test_bbox-changes.xhtml b/dom/svg/test/test_bbox-changes.xhtml new file mode 100644 index 0000000000..502f6a2c96 --- /dev/null +++ b/dom/svg/test/test_bbox-changes.xhtml @@ -0,0 +1,78 @@ +<!DOCTYPE html> +<html xmlns="http://www.w3.org/1999/xhtml"> +<!-- +https://bugzilla.mozilla.org/show_bug.cgi?id=1159053 +--> +<head> + <title>Test that the results of getBBox update for changes</title> + <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" /> +</head> +<body> + +<p id="display"> + <svg xmlns="http://www.w3.org/2000/svg"> + <rect id="rect1" x="10" y="10" width="10" height="10"/> + <rect id="rect2" x="30" y="10" width="10" height="10"/> + <g id="g"> + <circle id="circle1" cx="60" cy="20" r="5"/> + <circle id="circle2" cx="120" cy="20" r="5"/> + </g> + </svg> +</p> + +<div id="content" style="display: none"></div> + +<pre id="test"> +<script class="testbody" type="application/javascript">//<![CDATA[ + +SimpleTest.waitForExplicitFinish(); + +function init_and_run() { + SpecialPowers.pushPrefEnv({"set": [["svg.new-getBBox.enabled", true]]}, run); +} + +function checkBBox(id, options, x, y, width, height, msg) { + var bbox = document.getElementById(id).getBBox(options); + is(bbox.x, x, id + ".getBBox().x" + msg); + is(bbox.y, y, id + ".getBBox().y" + msg); + is(bbox.width, width, id + ".getBBox().width" + msg); + is(bbox.height, height, id + ".getBBox().height" + msg); +} + +function run() +{ + // First call getBBox on 'rect1' with stroke included: + $("rect1").setAttribute("stroke", "black"); + $("rect1").setAttribute("stroke-width", "10"); + checkBBox("rect1", { fill:true, stroke:true }, 5, 5, 20, 20, " with stroke"); + + // Now remove the stroke from 'rect1' and check again: + $("rect1").removeAttribute("stroke"); + $("rect1").removeAttribute("stroke-width"); + checkBBox("rect1", { fill:true }, 10, 10, 10, 10, " after stroke removed"); + + // First call getBBox on 'rect2' without a stroke included: + checkBBox("rect2", { fill:true }, 30, 10, 10, 10, " with stroke"); + + // Now add a stroke to 'rect2' and check again: + $("rect2").setAttribute("stroke", "black"); + $("rect2").setAttribute("stroke-width", "10"); + checkBBox("rect2", { fill:true, stroke:true }, 25, 5, 20, 20, " with stroke"); + + // Check the initial result for getBBox on the group: + checkBBox("g", { fill:true }, 55, 15, 70, 10, " before child moves"); + + // Now move one of the circle children and check again: + $("circle2").setAttribute("cx", "110"); + checkBBox("g", { fill:true }, 55, 15, 60, 10, " after child moves"); + + SimpleTest.finish(); +} + +window.addEventListener("load", init_and_run, false); + +//]]></script> +</pre> +</body> +</html> diff --git a/dom/svg/test/test_bbox-with-invalid-viewBox.xhtml b/dom/svg/test/test_bbox-with-invalid-viewBox.xhtml new file mode 100644 index 0000000000..84b4fe0e2b --- /dev/null +++ b/dom/svg/test/test_bbox-with-invalid-viewBox.xhtml @@ -0,0 +1,39 @@ +<!DOCTYPE html> +<html xmlns="http://www.w3.org/1999/xhtml"> +<!-- +https://bugzilla.mozilla.org/show_bug.cgi?id=433063 +--> +<head> + <title>Test for getBBox when the viewBox is invalid</title> + <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" /> +</head> +<body> + +<p id="display"></p> +<div id="content" style="display: none"></div> + +<svg id="svg" xmlns="http://www.w3.org/2000/svg" + style="width:200px; height:200px;" viewBox="0 0 200 0"> + <rect width="120" height="120"/> +</svg> + +<pre id="test"> +<script class="testbody" type="application/javascript"> +SimpleTest.waitForExplicitFinish(); + +function run() +{ + var bbox = $("svg").getBBox(); + is(bbox.x, 0, "Check bbox.x"); + is(bbox.y, 0, "Check bbox.y"); + is(bbox.width, 120, "Check bbox.width"); + is(bbox.height, 120, "Check bbox.height"); + SimpleTest.finish(); +} + +window.addEventListener("load", run, false); +</script> +</pre> +</body> +</html> diff --git a/dom/svg/test/test_bbox.xhtml b/dom/svg/test/test_bbox.xhtml new file mode 100644 index 0000000000..1e624349de --- /dev/null +++ b/dom/svg/test/test_bbox.xhtml @@ -0,0 +1,74 @@ +<!DOCTYPE html> +<html xmlns="http://www.w3.org/1999/xhtml"> +<!-- +https://bugzilla.mozilla.org/show_bug.cgi?id=449327 +--> +<head> + <title>Test for getBBox</title> + <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" /> +</head> +<body> + +<p id="display"></p> +<div id="content" style="display: none"></div> + +<iframe id="svg" src="bbox-helper.svg"></iframe> + +<pre id="test"> +<script class="testbody" type="application/javascript">//<![CDATA[ + +SimpleTest.waitForExplicitFinish(); + +function run() +{ + var doc = $("svg").contentDocument; + + function isFuzzy(a, b, error, name) + { + ok(!(Math.abs(a - b) > error), name, "got " + a + ", expected " + b + " (within " + error + ")"); + } + + function getBBox(id) { + return doc.getElementById(id).getBBox(); + } + function checkBBox(id, x, y, width, height, error) { + var bbox = getBBox(id); + isFuzzy(bbox.x, x, error, id + ".getBBox().x"); + isFuzzy(bbox.y, y, error, id + ".getBBox().y"); + isFuzzy(bbox.width, width, error, id + ".getBBox().width"); + isFuzzy(bbox.height, height, error, id + ".getBBox().height"); + } + function compareBBox(id1, id2) { + var bbox1 = getBBox(id1); + var bbox2 = getBBox(id2); + is(bbox1.x, bbox2.x, id1 + ".getBBox().x"); + is(bbox1.y, bbox2.y, id1 + ".getBBox().y"); + isFuzzy(bbox1.width, bbox2.width, 0.0002, id1 + ".getBBox().width"); + is(bbox1.height, bbox2.height, id1 + ".getBBox().height"); + } + function compareBBoxHeight(id1, id2) { + var bbox1 = getBBox(id1); + var bbox2 = getBBox(id2); + is(bbox1.height, bbox2.height, id1 + ".getBBox().height"); + } + + checkBBox("fO", 10, 10, 100, 100, 0.0); + checkBBox("i", 10, 10, 100, 100, 0.0); + compareBBoxHeight("a", "b"); + compareBBoxHeight("a", "y"); + compareBBox("b", "tspan"); + compareBBoxHeight("text", "lrmText"); + checkBBox("v", 95, 45, 10, 155, 0.001); + checkBBox("h", 195, 45, 105, 55, 0.001); + checkBBox("e", 95, 95, 10, 10, 0.001); + + SimpleTest.finish(); +} + +window.addEventListener("load", run, false); + +//]]></script> +</pre> +</body> +</html> diff --git a/dom/svg/test/test_bounds.html b/dom/svg/test/test_bounds.html new file mode 100644 index 0000000000..4b7046bc7e --- /dev/null +++ b/dom/svg/test/test_bounds.html @@ -0,0 +1,293 @@ +<!DOCTYPE html> +<html xmlns="http://www.w3.org/1999/xhtml"> +<!-- +https://bugzilla.mozilla.org/show_bug.cgi?id=463934 +--> +<head> + <title>Test for Bug 463934</title> + <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" /> +</head> +<body> +<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=463934">Mozilla Bug 463934</a> +<p id="display"></p> +<div id="content"> + <svg id="outer-1" xmlns="http://www.w3.org/2000/svg" width="30" height="30"></svg> + <svg id="outer-2" xmlns="http://www.w3.org/2000/svg" width="30" height="30" + style="padding: 10px;"> + </svg> + <svg id="outer-3" xmlns="http://www.w3.org/2000/svg" width="30" height="30" + style="border: 10px solid black;"> + </svg> + <svg id="outer-4" xmlns="http://www.w3.org/2000/svg" width="30" height="30" + style="border: 10px solid black; padding: 10px"> + </svg> +</div> + +<iframe id="svg" src="bounds-helper.svg"></iframe> + +<pre id="test"> +<script class="testbody" type="application/javascript"> +SimpleTest.waitForExplicitFinish(); + +function Rect(left, top, width, height) +{ + this.left = left; + this.top = top; + this.width = width; + this.height = height; +} + +Rect.prototype.roundOut = function() +{ + this.width = Math.ceil(this.left + this.width) - Math.floor(this.left); + this.height = Math.ceil(this.top + this.height) - Math.floor(this.top); + this.left = Math.floor(this.left); + this.top = Math.floor(this.top); +} + +function isWithAbsTolerance(a, b, tolerance, message) +{ + ok(tolerance >= Math.abs(a - b), message + " - got " + a + ", expected " + b + " ± " + tolerance); +} + +function runTest() +{ + var bounds; + + bounds = document.getElementById("outer-1").getBoundingClientRect(); + is(bounds.width, 30, "outer-svg 1 getBoundingClientRect().width"); + is(bounds.height, 30, "outer-svg 1 getBoundingClientRect().height"); + + bounds = document.getElementById("outer-2").getBoundingClientRect(); + is(bounds.width, 50, "outer-svg 2 getBoundingClientRect().width"); + is(bounds.height, 50, "outer-svg 2 getBoundingClientRect().height"); + + bounds = document.getElementById("outer-3").getBoundingClientRect(); + is(bounds.width, 50, "outer-svg 3 getBoundingClientRect().width"); + is(bounds.height, 50, "outer-svg 3 getBoundingClientRect().height"); + + bounds = document.getElementById("outer-4").getBoundingClientRect(); + is(bounds.width, 70, "outer-svg 4 getBoundingClientRect().width"); + is(bounds.height, 70, "outer-svg 4 getBoundingClientRect().height"); + + var doc = $("svg").contentWindow.document; + + var svg1Bounds = doc.getElementById("svg1").getBoundingClientRect(); + is(svg1Bounds.left, 10, "svg1.getBoundingClientRect().left"); + is(svg1Bounds.top, 10, "svg1.getBoundingClientRect().top"); + is(svg1Bounds.width, 25, "svg1.getBoundingClientRect().width"); + is(svg1Bounds.height, 30, "svg1.getBoundingClientRect().height"); + + var svg2Bounds = doc.getElementById("svg2").getBoundingClientRect(); + is(svg2Bounds.left, 0, "svg2.getBoundingClientRect().left"); + is(svg2Bounds.top, 0, "svg2.getBoundingClientRect().top"); + is(svg2Bounds.width, 2, "svg2.getBoundingClientRect().width"); + is(svg2Bounds.height, 2, "svg2.getBoundingClientRect().height"); + + var svg3Bounds = doc.getElementById("svg3").getBoundingClientRect(); + is(svg3Bounds.left, 0, "svg3.getBoundingClientRect().left"); + is(svg3Bounds.top, 0, "svg3.getBoundingClientRect().top"); + is(svg3Bounds.width, 1, "svg3.getBoundingClientRect().width"); + is(svg3Bounds.height, 1, "svg3.getBoundingClientRect().height"); + + var text1 = doc.getElementById("text1"); + + var text1Bounds = text1.getBoundingClientRect(); + var text2Bounds = doc.getElementById("text2").getBoundingClientRect(); + var text3Bounds = doc.getElementById("text3").getBoundingClientRect(); + + var sin45 = Math.sin(Math.PI / 4); + + isWithAbsTolerance(text1Bounds.left, 24, 1, "text1.getBoundingClientRect().left"); + + is(text2Bounds.left, text1Bounds.left + 100, "text2.getBoundingClientRect().left"); + is(text2Bounds.top, text1Bounds.top, "text2.getBoundingClientRect().top"); + is(text2Bounds.width, text1Bounds.width, "text2.getBoundingClientRect().width"); + is(text2Bounds.height, text1Bounds.height, "text2.getBoundingClientRect().height"); + + var r = (text1Bounds.width + text1Bounds.height) * sin45; + isWithAbsTolerance(text3Bounds.width, Math.ceil(r), 1, "text3.getBoundingClientRect().width"); + isWithAbsTolerance(text3Bounds.height, Math.ceil(r), 1, "text3.getBoundingClientRect().height"); + + var rect1Bounds = doc.getElementById("rect1").getBoundingClientRect(); + var rect2Bounds = doc.getElementById("rect2").getBoundingClientRect(); + var rect3Bounds = doc.getElementById("rect3").getBoundingClientRect(); + var rect4Bounds = doc.getElementById("rect4").getBoundingClientRect(); + + is(rect1Bounds.left, 50, "rect1.getBoundingClientRect().left"); + is(rect1Bounds.top, 50, "rect1.getBoundingClientRect().top"); + is(rect1Bounds.width, 50, "rect1.getBoundingClientRect().width"); + is(rect1Bounds.height, 50, "rect1.getBoundingClientRect().height"); + + rect = new Rect(175 - 50 * sin45, 75 - 50 * sin45, 50 * sin45 * 2, 50 * sin45 * 2); + isWithAbsTolerance(rect2Bounds.left, rect.left, 0.1, "rect2.getBoundingClientRect().left"); + isWithAbsTolerance(rect2Bounds.top, rect.top, 0.1, "rect2.getBoundingClientRect().top"); + isWithAbsTolerance(rect2Bounds.width, rect.width, 0.1, "rect2.getBoundingClientRect().width"); + isWithAbsTolerance(rect2Bounds.height, rect.height, 0.1, "rect2.getBoundingClientRect().height"); + + is(rect3Bounds.left, 50, "rect3.getBoundingClientRect().left"); + is(rect3Bounds.top, 160, "rect3.getBoundingClientRect().top"); + is(rect3Bounds.width, 100, "rect3.getBoundingClientRect().width"); + is(rect3Bounds.height, 100, "rect3.getBoundingClientRect().height"); + + rect = new Rect(350 - 100 * sin45, 150 - 100 * sin45, 100 * sin45 * 2, 100 * sin45 * 2); + isWithAbsTolerance(rect4Bounds.left, rect.left, 0.1, "rect4.getBoundingClientRect().left"); + isWithAbsTolerance(rect4Bounds.top, rect.top, 0.1, "rect4.getBoundingClientRect().top"); + isWithAbsTolerance(rect4Bounds.width, rect.width, 0.1, "rect4.getBoundingClientRect().width"); + isWithAbsTolerance(rect4Bounds.height, rect.height, 0.1, "rect4.getBoundingClientRect().height"); + + var rect1aBounds = doc.getElementById("rect1a").getBoundingClientRect(); + var rect2aBounds = doc.getElementById("rect2a").getBoundingClientRect(); + var rect3aBounds = doc.getElementById("rect3a").getBoundingClientRect(); + var rect3bBounds = doc.getElementById("rect3b").getBoundingClientRect(); + var rect4aBounds = doc.getElementById("rect4a").getBoundingClientRect(); + + is(rect1aBounds.left, 48, "rect1a.getBoundingClientRect().left"); + is(rect1aBounds.top, 48, "rect1a.getBoundingClientRect().top"); + is(rect1aBounds.width, 54, "rect1a.getBoundingClientRect().width"); + is(rect1aBounds.height, 54, "rect1a.getBoundingClientRect().height"); + + rect = new Rect(175 - 54 * sin45, 75 - 54 * sin45, 54 * sin45 * 2, 54 * sin45 * 2); + isWithAbsTolerance(rect2aBounds.left, rect.left, 0.1, "rect2a.getBoundingClientRect().left"); + isWithAbsTolerance(rect2aBounds.top, rect.top, 0.1, "rect2a.getBoundingClientRect().top"); + isWithAbsTolerance(rect2aBounds.width, rect.width, 0.1, "rect2a.getBoundingClientRect().width"); + isWithAbsTolerance(rect2aBounds.height, rect.height, 0.1, "rect2a.getBoundingClientRect().height"); + + is(rect3aBounds.left, 46, "rect3a.getBoundingClientRect().left"); + is(rect3aBounds.top, 156, "rect3a.getBoundingClientRect().top"); + is(rect3aBounds.width, 108, "rect3a.getBoundingClientRect().width"); + is(rect3aBounds.height, 108, "rect3a.getBoundingClientRect().height"); + + isWithAbsTolerance(rect3bBounds.left, 198, 0.1, "rect3b.getBoundingClientRect().left"); + isWithAbsTolerance(rect3bBounds.top, 198, 0.1, "rect3b.getBoundingClientRect().top"); + isWithAbsTolerance(rect3bBounds.width, 54, 0.1, "rect3b.getBoundingClientRect().width"); + isWithAbsTolerance(rect3bBounds.height, 54, 0.1, "rect3b.getBoundingClientRect().height"); + + rect = new Rect(350 - 108 * sin45, 150 - 108 * sin45, 108 * sin45 * 2, 108 * sin45 * 2); + isWithAbsTolerance(rect4aBounds.left, rect.left, 0.1, "rect4a.getBoundingClientRect().left"); + isWithAbsTolerance(rect4aBounds.top, rect.top, 0.1, "rect4a.getBoundingClientRect().top"); + isWithAbsTolerance(rect4aBounds.width, rect.width, 0.1, "rect4a.getBoundingClientRect().width"); + isWithAbsTolerance(rect4aBounds.height, rect.height, 0.1, "rect4a.getBoundingClientRect().height"); + + var text1a = doc.getElementById("text1a"); + + var text1aBounds = text1a.getBoundingClientRect(); + var text2aBounds = doc.getElementById("text2a").getBoundingClientRect(); + + isWithAbsTolerance(text1aBounds.left, 82, 1, "text1a.getBoundingClientRect().left"); + is(text1aBounds.width, text1Bounds.width + 4, "text1a.getBoundingClientRect().width"); + + is(text2aBounds.left, text1aBounds.left + 100 - 3, "text2a.getBoundingClientRect().left"); + is(text2aBounds.width, text1aBounds.width + 6, "text2a.getBoundingClientRect().width"); + + var i = doc.getElementById("i"); + var iBounds = i.getBoundingClientRect(); + is(iBounds.left, 20, "i.getBoundingClientRect().left"); + is(iBounds.top, 20, "i.getBoundingClientRect().top"); + is(iBounds.width, 200, "i.getBoundingClientRect().width"); + is(iBounds.height, 200, "i.getBoundingClientRect().height"); + + var text4Bounds = doc.getElementById("text4").getBoundingClientRect(); + is(text4Bounds.left, 0, "text4.getBoundingClientRect().left"); + is(text4Bounds.top, 0, "text4.getBoundingClientRect().top"); + is(text4Bounds.width, 0, "text4.getBoundingClientRect().width"); + is(text4Bounds.height, 0, "text4.getBoundingClientRect().height"); + + var gBounds = doc.getElementById("g2").getBoundingClientRect(); + is(gBounds.left, 100, "g2.getBoundingClientRect().left"); + is(gBounds.top, 100, "g2.getBoundingClientRect().top"); + is(gBounds.width, 50, "g2.getBoundingClientRect().width"); + is(gBounds.height, 50, "g2.getBoundingClientRect().height"); + + var nonScalingStrokedCircle1Bounds = + doc.getElementById("nonScalingStrokedCircle1").getBoundingClientRect(); + isWithAbsTolerance(nonScalingStrokedCircle1Bounds.left, 10, 0.15, + "nonScalingStrokedCircle1.getBoundingClientRect().left"); + isWithAbsTolerance(nonScalingStrokedCircle1Bounds.top, 105, 0.15, + "nonScalingStrokedCircle1.getBoundingClientRect().top"); + isWithAbsTolerance(nonScalingStrokedCircle1Bounds.width, 70, 0.15, + "nonScalingStrokedCircle1.getBoundingClientRect().width"); + isWithAbsTolerance(nonScalingStrokedCircle1Bounds.height, 50, 0.15, + "nonScalingStrokedCircle1.getBoundingClientRect().height"); + + var nonScalingStrokedEllipse1Bounds = + doc.getElementById("nonScalingStrokedEllipse1").getBoundingClientRect(); + isWithAbsTolerance(nonScalingStrokedEllipse1Bounds.left, 5, 0.15, + "nonScalingStrokedEllipse1.getBoundingClientRect().left"); + isWithAbsTolerance(nonScalingStrokedEllipse1Bounds.top, 40, 0.15, + "nonScalingStrokedEllipse1.getBoundingClientRect().top"); + isWithAbsTolerance(nonScalingStrokedEllipse1Bounds.width, 30, 0.15, + "nonScalingStrokedEllipse1.getBoundingClientRect().width"); + isWithAbsTolerance(nonScalingStrokedEllipse1Bounds.height, 40, 0.15, + "nonScalingStrokedEllipse1.getBoundingClientRect().height"); + + var nonScalingStrokedLine1Bounds = + doc.getElementById("nonScalingStrokedLine1").getBoundingClientRect(); + isWithAbsTolerance(nonScalingStrokedLine1Bounds.left, 235, 0.1, + "nonScalingStrokedLine1.getBoundingClientRect().left"); + isWithAbsTolerance(nonScalingStrokedLine1Bounds.top, 10, 0.1, + "nonScalingStrokedLine1.getBoundingClientRect().top"); + isWithAbsTolerance(nonScalingStrokedLine1Bounds.width, 10, 0.1, + "nonScalingStrokedLine1.getBoundingClientRect().width"); + isWithAbsTolerance(nonScalingStrokedLine1Bounds.height, 25, 0.1, + "nonScalingStrokedLine1.getBoundingClientRect().height"); + + var nonScalingStrokedLine2Bounds = + doc.getElementById("nonScalingStrokedLine2").getBoundingClientRect(); + var capDelta = 5/Math.SQRT2 + 5/Math.SQRT2; + rect = new Rect(260 - capDelta, 15 - capDelta, 20/Math.SQRT2 + 2 * capDelta, + 20/Math.SQRT2 + 2 * capDelta); + isWithAbsTolerance(nonScalingStrokedLine2Bounds.left, rect.left, 0.1, + "nonScalingStrokedLine2.getBoundingClientRect().left"); + isWithAbsTolerance(nonScalingStrokedLine2Bounds.top, rect.top, 0.1, + "nonScalingStrokedLine2.getBoundingClientRect().top"); + isWithAbsTolerance(nonScalingStrokedLine2Bounds.width, rect.width, 0.15, + "nonScalingStrokedLine2.getBoundingClientRect().width"); + isWithAbsTolerance(nonScalingStrokedLine2Bounds.height, rect.height, 0.15, + "nonScalingStrokedLine2.getBoundingClientRect().height"); + + var nonScalingStrokedLine3Bounds = + doc.getElementById("nonScalingStrokedLine3").getBoundingClientRect(); + var capDelta = 5/Math.SQRT2; + rect = new Rect(280 - capDelta, 15 - capDelta, 20/Math.SQRT2 + 2 * capDelta, + 20/Math.SQRT2 + 2 * capDelta); + isWithAbsTolerance(nonScalingStrokedLine3Bounds.left, rect.left, 0.1, + "nonScalingStrokedLine3.getBoundingClientRect().left"); + isWithAbsTolerance(nonScalingStrokedLine3Bounds.top, rect.top, 0.1, + "nonScalingStrokedLine3.getBoundingClientRect().top"); + isWithAbsTolerance(nonScalingStrokedLine3Bounds.width, rect.width, 0.1, + "nonScalingStrokedLine3.getBoundingClientRect().width"); + isWithAbsTolerance(nonScalingStrokedLine3Bounds.height, rect.height, 0.1, + "nonScalingStrokedLine3.getBoundingClientRect().height"); + + var shapeWithMarker1Bounds = + doc.getElementById("shapeWithMarker1").getBoundingClientRect(); + isWithAbsTolerance(shapeWithMarker1Bounds.left, 160, 0.1, + "shapeWithMarker1Bounds.left"); + isWithAbsTolerance(shapeWithMarker1Bounds.top, 120, 0.1, + "shapeWithMarker1Bounds.top"); + isWithAbsTolerance(shapeWithMarker1Bounds.width, 10 + Math.SQRT2 * 50, 0.1, + "shapeWithMarker1Bounds.width"); + isWithAbsTolerance(shapeWithMarker1Bounds.height, 20, 0.1, + "shapeWithMarker1Bounds.height"); + + var rotatedLine1Bounds = + doc.getElementById("rotatedLine1").getBoundingClientRect(); + isWithAbsTolerance(rotatedLine1Bounds.left, 160, 0.1, + "rotatedLine1Bounds.left"); + isWithAbsTolerance(rotatedLine1Bounds.top, 145, 0.1, + "rotatedLine1Bounds.top"); + isWithAbsTolerance(rotatedLine1Bounds.width, Math.SQRT2 * 20, 0.1, + "rotatedLine1Bounds.width"); + isWithAbsTolerance(rotatedLine1Bounds.height, 10, 0.1, + "rotatedLine1Bounds.height"); + + SimpleTest.finish(); +} + +window.addEventListener("load", runTest, false); +</script> +</pre> +</body> +</html> diff --git a/dom/svg/test/test_bug872812.html b/dom/svg/test/test_bug872812.html new file mode 100644 index 0000000000..de21d4131a --- /dev/null +++ b/dom/svg/test/test_bug872812.html @@ -0,0 +1,29 @@ +<!DOCTYPE html> +<html xmlns="http://www.w3.org/1999/xhtml"> +<!-- +https://bugzilla.mozilla.org/show_bug.cgi?id=872812 +--> +<head> + <title>Test for Bug 872812</title> + <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" /> +</head> +<body> +<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=872812">Mozilla Bug 872812</a> +<p id="display"></p> +<div id="content" style="display: none"></div> + +<iframe id="svg" src="viewport-helper.svg"></iframe> + +<pre id="test"> +<script class="testbody" type="application/javascript"> + +var svg = document.createElementNS("http://www.w3.org/2000/svg", 'svg'); +ok(svg, "SVG exists"); +var a = document.createEvent('CustomEvent').initCustomEvent('', '', '', svg.viewBox); +ok(true, "CustomEvent exists and we are not crashed!"); + +</script> +</pre> +</body> +</html> diff --git a/dom/svg/test/test_dataTypes.html b/dom/svg/test/test_dataTypes.html new file mode 100644 index 0000000000..4be3d9c809 --- /dev/null +++ b/dom/svg/test/test_dataTypes.html @@ -0,0 +1,382 @@ +<!DOCTYPE html> +<html xmlns="http://www.w3.org/1999/xhtml"> +<!-- +https://bugzilla.mozilla.org/show_bug.cgi?id=437448 +--> +<head> + <title>Test for Bug 437448</title> + <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" /> +</head> +<body> +<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=437448">Mozilla Bug 437448</a> +<p id="display"></p> +<div id="content" style="display: none"></div> + +<iframe id="svg" src="dataTypes-helper.svg"></iframe> + +<pre id="test"> +<script class="testbody" type="application/javascript"> +SimpleTest.waitForExplicitFinish(); + +function runTests() +{ + var doc = $("svg").contentWindow.document; + var filter = doc.getElementById("filter"); + var convolve = doc.getElementById("convolve"); + var blur = doc.getElementById("blur"); + var marker = doc.getElementById("marker"); + + // class attribute + filter.setAttribute("class", "foo"); + is(filter.getAttribute("class"), "foo", "class attribute"); + is(filter.className.baseVal, "foo", "className baseVal"); + is(filter.className.animVal, "foo", "className animVal"); + filter.className.baseVal = "bar"; + is(filter.getAttribute("class"), "bar", "class attribute"); + is(filter.className.baseVal, "bar", "className baseVal"); + is(filter.className.animVal, "bar", "className animVal"); + filter.removeAttribute("class"); + is(filter.hasAttribute("class"), false, "class attribute"); + ok(filter.getAttribute("class") === null, "removed class attribute"); + is(filter.className.baseVal, "", "className baseVal"); + is(filter.className.animVal, "", "className animVal"); + filter.setAttribute("class", ""); + ok(filter.getAttribute("class") === "", "empty class attribute"); + + // length attribute + + marker.setAttribute("markerWidth", "12.5"); + is(marker.markerWidth.baseVal.value, 12.5, "length baseVal"); + is(marker.markerWidth.animVal.value, 12.5, "length animVal"); + + var baseMarkerWidth = marker.markerWidth.baseVal; + var animMarkerWidth = marker.markerWidth.animVal; + marker.setAttribute("markerWidth", "15.5"); + is(baseMarkerWidth.value, 15.5, "length baseVal"); + is(animMarkerWidth.value, 15.5, "length animVal"); + + marker.markerWidth.baseVal.value = 7.5; + is(marker.markerWidth.animVal.value, 7.5, "length animVal"); + is(marker.getAttribute("markerWidth"), "7.5", "length attribute"); + + marker.setAttribute("markerWidth", ""); + ok(marker.getAttribute("markerWidth") === "", "empty length attribute"); + marker.removeAttribute("markerWidth"); + ok(marker.getAttribute("markerWidth") === null, "removed length attribute"); + + // number attribute + + convolve.setAttribute("divisor", "12.5"); + is(convolve.divisor.baseVal, 12.5, "number baseVal"); + is(convolve.divisor.animVal, 12.5, "number animVal"); + + convolve.divisor.baseVal = 7.5; + is(convolve.divisor.animVal, 7.5, "number animVal"); + is(convolve.getAttribute("divisor"), "7.5", "number attribute"); + + convolve.setAttribute("divisor", ""); + ok(convolve.getAttribute("divisor") === "", "empty number attribute"); + convolve.removeAttribute("divisor"); + ok(convolve.getAttribute("divisor") === null, "removed number attribute"); + + // number-optional-number attribute + + blur.setAttribute("stdDeviation", "20.5"); + is(blur.stdDeviationX.baseVal, 20.5, "number-optional-number first baseVal"); + is(blur.stdDeviationX.animVal, 20.5, "number-optional-number first animVal"); + is(blur.stdDeviationY.baseVal, 20.5, "number-optional-number second baseVal"); + is(blur.stdDeviationY.animVal, 20.5, "number-optional-number second animVal"); + + blur.stdDeviationX.baseVal = 8.5; + is(blur.stdDeviationX.animVal, 8.5, "number-optional-number first animVal"); + is(blur.stdDeviationY.animVal, 20.5, "number-optional-number second animVal"); + is(blur.getAttribute("stdDeviation"), "8.5, 20.5", "number-optional-number attribute"); + + blur.stdDeviationY.baseVal = 8.5; + is(blur.getAttribute("stdDeviation"), "8.5", "number-optional-number attribute"); + + blur.setStdDeviation(24.5, 0.5); + is(blur.stdDeviationX.baseVal, 24.5, "number-optional-number first baseVal"); + is(blur.stdDeviationX.animVal, 24.5, "number-optional-number first animVal"); + is(blur.stdDeviationY.baseVal, 0.5, "number-optional-number second baseVal"); + is(blur.stdDeviationY.animVal, 0.5, "number-optional-number second animVal"); + + blur.setAttribute("stdDeviation", ""); + ok(blur.getAttribute("stdDeviation") === "", + "empty number-optional-number attribute"); + blur.removeAttribute("stdDeviation"); + ok(blur.getAttribute("stdDeviation") === null, + "removed number-optional-number attribute"); + + // integer attribute + + convolve.setAttribute("targetX", "12"); + is(convolve.targetX.baseVal, 12, "integer baseVal"); + is(convolve.targetX.animVal, 12, "integer animVal"); + convolve.targetX.baseVal = 7; + is(convolve.targetX.animVal, 7, "integer animVal"); + is(convolve.getAttribute("targetX"), "7", "integer attribute"); + convolve.setAttribute("targetX", ""); + ok(convolve.getAttribute("targetX") === "", "empty integer attribute"); + convolve.removeAttribute("targetX"); + ok(convolve.getAttribute("targetX") === null, "removed integer attribute"); + + // integer-optional-integer attribute + + convolve.setAttribute("order", "5"); + is(convolve.orderX.baseVal, 5, "integer-optional-integer first baseVal"); + is(convolve.orderX.animVal, 5, "integer-optional-integer first animVal"); + is(convolve.orderY.baseVal, 5, "integer-optional-integer second baseVal"); + is(convolve.orderY.animVal, 5, "integer-optional-integer second animVal"); + + convolve.orderX.baseVal = 7; + is(convolve.orderX.animVal, 7, "integer-optional-integer first animVal"); + is(convolve.orderY.animVal, 5, "integer-optional-integer second animVal"); + is(convolve.getAttribute("order"), "7, 5", "integer-optional-integer attribute"); + + convolve.orderY.baseVal = 7; + is(convolve.getAttribute("order"), "7", "integer-optional-integer attribute"); + + convolve.setAttribute("order", "11, 13"); + is(convolve.orderX.baseVal, 11, "integer-optional-integer first baseVal"); + is(convolve.orderX.animVal, 11, "integer-optional-integer first animVal"); + is(convolve.orderY.baseVal, 13, "integer-optional-integer second baseVal"); + is(convolve.orderY.animVal, 13, "integer-optional-integer second animVal"); + + // 32 bit integer range + convolve.setAttribute("order", "-2147483648, 2147483647"); + is(convolve.orderX.baseVal, -2147483648, "integer-optional-integer first baseVal"); + is(convolve.orderX.animVal, -2147483648, "integer-optional-integer first animVal"); + is(convolve.orderY.baseVal, 2147483647, "integer-optional-integer second baseVal"); + is(convolve.orderY.animVal, 2147483647, "integer-optional-integer second animVal"); + + // too big, clamp + convolve.setAttribute("order", "-2147483649, 2147483648"); + is(convolve.orderX.baseVal, -2147483648, "integer-optional-integer first baseVal"); + is(convolve.orderX.animVal, -2147483648, "integer-optional-integer first animVal"); + is(convolve.orderY.baseVal, 2147483647, "integer-optional-integer second baseVal"); + is(convolve.orderY.animVal, 2147483647, "integer-optional-integer second animVal"); + + // invalid + convolve.setAttribute("order", "-00000000000invalid, 214748364720invalid"); + is(convolve.orderX.baseVal, 3, "integer-optional-integer first baseVal"); + is(convolve.orderX.animVal, 3, "integer-optional-integer first animVal"); + is(convolve.orderY.baseVal, 3, "integer-optional-integer second baseVal"); + is(convolve.orderY.animVal, 3, "integer-optional-integer second animVal"); + + convolve.setAttribute("order", ""); + ok(convolve.getAttribute("order") === "", + "empty integer-optional-integer attribute"); + convolve.removeAttribute("order"); + ok(convolve.getAttribute("order") === null, + "removed integer-optional-integer attribute"); + + // angle attribute + + marker.setAttribute("orient", "90deg"); + is(marker.orientAngle.baseVal.value, 90, "angle baseVal"); + is(marker.orientAngle.animVal.value, 90, "angle animVal"); + + var baseAngle = marker.orientAngle.baseVal; + var animAngle = marker.orientAngle.animVal; + marker.setAttribute("orient", "45deg"); + is(baseAngle.value, 45, "angle baseVal"); + is(animAngle.value, 45, "angle animVal"); + + marker.orientAngle.baseVal.value = 30; + is(marker.orientAngle.animVal.value, 30, "angle animVal"); + is(marker.getAttribute("orient"), "30deg", "angle attribute"); + + marker.setAttribute("orient", "auto"); + is(marker.getAttribute("orient"), "auto", "checking 'auto' string preserved"); + is(marker.orientType.baseVal, SVGMarkerElement.SVG_MARKER_ORIENT_AUTO, "type baseVal"); + is(marker.orientType.animVal, SVGMarkerElement.SVG_MARKER_ORIENT_AUTO, "type animVal"); + + marker.setAttribute("orient", "auto-start-reverse"); + is(marker.getAttribute("orient"), "auto-start-reverse", "checking 'auto-start-reverse' string preserved"); + is(marker.orientType.baseVal, SVGMarkerElement.SVG_MARKER_ORIENT_UNKNOWN, "type baseVal"); + is(marker.orientType.animVal, SVGMarkerElement.SVG_MARKER_ORIENT_UNKNOWN, "type animVal"); + + marker.setAttribute("orient", ""); + ok(marker.getAttribute("orient") === "", "empty angle attribute"); + marker.removeAttribute("orient"); + ok(marker.getAttribute("orient") === null, "removed angle attribute"); + + // boolean attribute + + convolve.setAttribute("preserveAlpha", "false"); + is(convolve.preserveAlpha.baseVal, false, "boolean baseVal"); + is(convolve.preserveAlpha.animVal, false, "boolean animVal"); + convolve.preserveAlpha.baseVal = true; + is(convolve.preserveAlpha.animVal, true, "boolean animVal"); + is(convolve.getAttribute("preserveAlpha"), "true", "boolean attribute"); + convolve.setAttribute("preserveAlpha", ""); + ok(convolve.getAttribute("preserveAlpha") === "", "empty boolean attribute"); + convolve.removeAttribute("preserveAlpha"); + ok(convolve.getAttribute("preserveAlpha") === null, + "removed boolean attribute"); + + // enum attribute + + is(1, SVGFEConvolveMatrixElement.SVG_EDGEMODE_DUPLICATE, "SVG_EDGEMODE_DUPLICATE value"); + is(2, SVGFEConvolveMatrixElement.SVG_EDGEMODE_WRAP, "SVG_EDGEMODE_WRAP value"); + + convolve.setAttribute("edgeMode", "wrap"); + is(convolve.edgeMode.baseVal, SVGFEConvolveMatrixElement.SVG_EDGEMODE_WRAP, "enum baseVal"); + is(convolve.edgeMode.animVal, SVGFEConvolveMatrixElement.SVG_EDGEMODE_WRAP, "enum animVal"); + convolve.edgeMode.baseVal = SVGFEConvolveMatrixElement.SVG_EDGEMODE_DUPLICATE; + is(convolve.edgeMode.animVal, SVGFEConvolveMatrixElement.SVG_EDGEMODE_DUPLICATE, "enum animVal"); + is(convolve.getAttribute("edgeMode"), "duplicate", "enum attribute"); + convolve.setAttribute("edgeMode", ""); + ok(convolve.getAttribute("edgeMode") === "", "empty enum attribute"); + convolve.removeAttribute("edgeMode"); + ok(convolve.getAttribute("edgeMode") === null, "removed enum attribute"); + + // string attribute + + convolve.setAttribute("result", "foo"); + is(convolve.result.baseVal, "foo", "string baseVal"); + is(convolve.result.animVal, "foo", "string animVal"); + convolve.result.baseVal = "bar"; + is(convolve.result.animVal, "bar", "string animVal"); + is(convolve.getAttribute("result"), "bar", "string attribute"); + convolve.setAttribute("result", ""); + ok(convolve.getAttribute("result") === "", "empty string attribute"); + convolve.removeAttribute("result"); + ok(convolve.getAttribute("result") === null, "removed string attribute"); + + // preserveAspectRatio attribute + + is(0, SVGPreserveAspectRatio.SVG_PRESERVEASPECTRATIO_UNKNOWN, "SVG_PRESERVEASPECTRATIO_UNKNOWN value"); + is(1, SVGPreserveAspectRatio.SVG_PRESERVEASPECTRATIO_NONE, "SVG_PRESERVEASPECTRATIO_NONE value"); + is(3, SVGPreserveAspectRatio.SVG_PRESERVEASPECTRATIO_XMIDYMIN, "SVG_PRESERVEASPECTRATIO_XMIDYMIN value"); + is(5, SVGPreserveAspectRatio.SVG_PRESERVEASPECTRATIO_XMINYMID, "SVG_PRESERVEASPECTRATIO_XMINYMID value"); + is(7, SVGPreserveAspectRatio.SVG_PRESERVEASPECTRATIO_XMAXYMID, "SVG_PRESERVEASPECTRATIO_XMAXYMID value"); + is(10, SVGPreserveAspectRatio.SVG_PRESERVEASPECTRATIO_XMAXYMAX , "SVG_PRESERVEASPECTRATIO_XMAXYMAX value"); + + is(0, SVGPreserveAspectRatio.SVG_MEETORSLICE_UNKNOWN, "SVG_MEETORSLICE_UNKNOWN value"); + is(1, SVGPreserveAspectRatio.SVG_MEETORSLICE_MEET, "SVG_MEETORSLICE_MEET value"); + is(2, SVGPreserveAspectRatio.SVG_MEETORSLICE_SLICE, "SVG_MEETORSLICE_SLICE value"); + + marker.setAttribute("preserveAspectRatio", "xMinYMid slice"); + is(marker.preserveAspectRatio.baseVal.align, + SVGPreserveAspectRatio.SVG_PRESERVEASPECTRATIO_XMINYMID, "preserveAspectRatio.align baseVal"); + is(marker.preserveAspectRatio.animVal.align, + SVGPreserveAspectRatio.SVG_PRESERVEASPECTRATIO_XMINYMID, "preserveAspectRatio.align animVal"); + is(marker.preserveAspectRatio.baseVal.meetOrSlice, + SVGPreserveAspectRatio.SVG_MEETORSLICE_SLICE, "preserveAspectRatio.meetOrSlice baseVal"); + is(marker.preserveAspectRatio.animVal.meetOrSlice, + SVGPreserveAspectRatio.SVG_MEETORSLICE_SLICE, "preserveAspectRatio.meetOrSlice animVal"); + marker.preserveAspectRatio.baseVal.align = + SVGPreserveAspectRatio.SVG_PRESERVEASPECTRATIO_XMIDYMIN; + is(marker.preserveAspectRatio.animVal.align, + SVGPreserveAspectRatio.SVG_PRESERVEASPECTRATIO_XMIDYMIN, "preserveAspectRatio animVal"); + is(marker.preserveAspectRatio.animVal.meetOrSlice, + SVGPreserveAspectRatio.SVG_MEETORSLICE_SLICE, "preserveAspectRatio.meetOrSlice animVal"); + marker.preserveAspectRatio.baseVal.meetOrSlice = SVGPreserveAspectRatio.SVG_MEETORSLICE_MEET; + is(marker.preserveAspectRatio.animVal.align, + SVGPreserveAspectRatio.SVG_PRESERVEASPECTRATIO_XMIDYMIN, "preserveAspectRatio animVal"); + is(marker.preserveAspectRatio.animVal.meetOrSlice, + SVGPreserveAspectRatio.SVG_MEETORSLICE_MEET, "preserveAspectRatio.meetOrSlice animVal"); + is(marker.getAttribute("preserveAspectRatio"), "xMidYMin meet", "preserveAspectRatio attribute"); + + var basePreserveAspectRatio = marker.preserveAspectRatio.baseVal; + var animPreserveAspectRatio = marker.preserveAspectRatio.animVal; + marker.setAttribute("preserveAspectRatio", "xMaxYMid slice"); + is(basePreserveAspectRatio.align, + SVGPreserveAspectRatio.SVG_PRESERVEASPECTRATIO_XMAXYMID, "preserveAspectRatio.align baseVal"); + is(animPreserveAspectRatio.align, + SVGPreserveAspectRatio.SVG_PRESERVEASPECTRATIO_XMAXYMID, "preserveAspectRatio.align animVal"); + is(basePreserveAspectRatio.meetOrSlice, + SVGPreserveAspectRatio.SVG_MEETORSLICE_SLICE, "preserveAspectRatio.meetOrSlice baseVal"); + is(animPreserveAspectRatio.meetOrSlice, + SVGPreserveAspectRatio.SVG_MEETORSLICE_SLICE, "preserveAspectRatio.meetOrSlice animVal"); + + marker.setAttribute("preserveAspectRatio", " none"); // invalid, space at beginning + is(basePreserveAspectRatio.align, SVGPreserveAspectRatio.SVG_PRESERVEASPECTRATIO_XMIDYMID, + "default preserveAspectRatio attribute"); + + marker.setAttribute("preserveAspectRatio", "none "); // invalid, space at end + is(basePreserveAspectRatio.align, SVGPreserveAspectRatio.SVG_PRESERVEASPECTRATIO_XMIDYMID, + "default preserveAspectRatio attribute"); + + marker.setAttribute("preserveAspectRatio", ""); + ok(marker.getAttribute("preserveAspectRatio") === "", + "empty preserveAspectRatio attribute"); + marker.removeAttribute("preserveAspectRatio"); + ok(marker.getAttribute("preserveAspectRatio") === null, + "removed preserveAspectRatio attribute"); + + // viewBox attribute + var baseViewBox = marker.viewBox.baseVal; + var animViewBox = marker.viewBox.animVal; + is(baseViewBox, null, "viewBox baseVal"); + is(animViewBox, null, "viewBox animVal"); + + marker.setAttribute("viewBox", "1 2 3 4"); + is(marker.viewBox.baseVal.x, 1, "viewBox.x baseVal"); + is(marker.viewBox.animVal.x, 1, "viewBox.x animVal"); + is(marker.viewBox.baseVal.y, 2, "viewbox.y baseVal"); + is(marker.viewBox.animVal.y, 2, "viewbox.y animVal"); + is(marker.viewBox.baseVal.width, 3, "viewbox.width baseVal"); + is(marker.viewBox.animVal.width, 3, "viewbox.width animVal"); + is(marker.viewBox.baseVal.height, 4, "viewbox.heigth baseVal"); + is(marker.viewBox.animVal.height, 4, "viewbox.heigth animVal"); + marker.viewBox.baseVal.x = 5; + is(marker.viewBox.animVal.x, 5, "viewBox.x animVal"); + marker.viewBox.baseVal.y = 6; + is(marker.viewBox.animVal.y, 6, "viewBox.y animVal"); + marker.viewBox.baseVal.width = 7; + is(marker.viewBox.animVal.width, 7, "viewBox.width animVal"); + marker.viewBox.baseVal.height = 8; + is(marker.viewBox.animVal.height, 8, "viewBox.height animVal"); + is(marker.getAttribute("viewBox"), "5 6 7 8", "viewBox attribute"); + var storedViewBox = marker.viewBox.baseVal; + marker.removeAttribute("viewBox"); + is(marker.hasAttribute("viewBox"), false, "viewBox hasAttribute"); + ok(marker.getAttribute("viewBox") === null, "removed viewBox attribute"); + is(marker.viewBox.baseVal, null, "viewBox baseVal"); + is(marker.viewBox.animVal, null, "viewBox animVal"); + + is(storedViewBox.width, 7, "Should not lose values"); + storedViewBox.width = 200; + is(storedViewBox.width, 200, "Should be able to change detached viewBox rect"); + is(marker.hasAttribute("viewBox"), false, "viewBox hasAttribute should still be false"); + ok(marker.getAttribute("viewBox") === null, "viewBox attribute should still be null"); + is(marker.viewBox.baseVal, null, "viewBox baseVal"); + is(marker.viewBox.animVal, null, "viewBox animVal"); + + marker.setAttribute("viewBox", "none"); + is(marker.hasAttribute("viewBox"), true, "viewBox hasAttribute"); + is(marker.viewBox.baseVal, null, "viewBox baseVal"); + is(marker.viewBox.animVal, null, "viewBox animVal"); + + marker.setAttribute("viewBox", ""); + is(marker.hasAttribute("viewBox"), true, "viewBox hasAttribute"); + ok(marker.getAttribute("viewBox") === "", "empty viewBox attribute"); + + marker.setAttribute("viewBox", "9 10 11 12"); + marker.removeAttribute("viewBox"); + marker.setAttribute("viewBox", "9 10 11 12"); + is(marker.viewBox.baseVal.x, 9, "viewBox.x baseVal after re-setting attribute to same rect value"); + is(marker.viewBox.baseVal.y, 10, "viewBox.y baseVal after re-setting attribute to same rect value"); + is(marker.viewBox.baseVal.width, 11, "viewBox.width baseVal after re-setting attribute to same rect value"); + is(marker.viewBox.baseVal.height, 12, "viewBox.height baseVal after re-setting attribute to same rect value"); + is(marker.viewBox.animVal.x, 9, "viewBox.x animVal after re-setting attribute to same rect value"); + is(marker.viewBox.animVal.y, 10, "viewBox.y animVal after re-setting attribute to same rect value"); + is(marker.viewBox.animVal.width, 11, "viewBox.width animVal after re-setting attribute to same rect value"); + is(marker.viewBox.animVal.height, 12, "viewBox.height animVal after re-setting attribute to same rect value"); + + SimpleTest.finish(); +} + +function runTestsWithPref() { + SpecialPowers.pushPrefEnv({ 'set': [['svg.marker-improvements.enabled', true]] }, runTests); +} + +window.addEventListener("load", runTestsWithPref, false); +</script> +</pre> +</body> +</html> diff --git a/dom/svg/test/test_dataTypesModEvents.html b/dom/svg/test/test_dataTypesModEvents.html new file mode 100644 index 0000000000..92270a63b5 --- /dev/null +++ b/dom/svg/test/test_dataTypesModEvents.html @@ -0,0 +1,258 @@ +<!DOCTYPE html> +<html xmlns="http://www.w3.org/1999/xhtml"> +<!-- +https://bugzilla.mozilla.org/show_bug.cgi?id=629200 +--> +<head> + <title>Test for Bug 629200</title> + <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <script type="application/javascript" src="MutationEventChecker.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" /> +</head> +<body> +<a target="_blank" + href="https://bugzilla.mozilla.org/show_bug.cgi?id=629200">Mozilla + Bug 629200</a> +<p id="display"></p> +<div id="content" style="display: none"></div> + +<iframe id="svg" src="dataTypes-helper.svg"></iframe> + +<pre id="test"> +<script class="testbody" type="application/javascript"> +SimpleTest.waitForExplicitFinish(); + +function runTests() +{ + var doc = $("svg").contentWindow.document; + var filter = doc.getElementById("filter"); + var convolve = doc.getElementById("convolve"); + var blur = doc.getElementById("blur"); + var marker = doc.getElementById("marker"); + var eventChecker = new MutationEventChecker; + + // class attribute + + eventChecker.watchAttr(filter, "class"); + eventChecker.expect("add modify remove add"); + filter.setAttribute("class", "foo"); + filter.className.baseVal = "bar"; + filter.removeAttribute("class"); + filter.removeAttributeNS(null, "class"); + filter.className.baseVal = "foo"; + + eventChecker.expect(""); + filter.className.baseVal = "foo"; + filter.setAttribute("class", "foo"); + + // length attribute + + eventChecker.watchAttr(marker, "markerWidth"); + eventChecker.expect("add modify modify modify modify modify remove add"); + marker.setAttribute("markerWidth", "12.5"); + marker.markerWidth.baseVal.value = 8; + marker.markerWidth.baseVal.valueInSpecifiedUnits = 9; + marker.markerWidth.baseVal.valueAsString = "10"; + marker.markerWidth.baseVal.convertToSpecifiedUnits( + SVGLength.SVG_LENGTHTYPE_CM); + marker.markerWidth.baseVal.newValueSpecifiedUnits( + SVGLength.SVG_LENGTHTYPE_MM, 11); + marker.removeAttribute("markerWidth"); + marker.removeAttributeNS(null, "markerWidth"); + marker.markerWidth.baseVal.value = 8; + + eventChecker.expect("remove add modify"); + marker.removeAttribute("markerWidth"); + console.log(marker.getAttribute("markerWidth")); + marker.markerWidth.baseVal.convertToSpecifiedUnits( + SVGLength.SVG_LENGTHTYPE_NUMBER); + console.log(marker.getAttribute("markerWidth")); + marker.markerWidth.baseVal.value = 8; + + eventChecker.expect(""); + marker.markerWidth.baseVal.value = 8; + marker.setAttribute("markerWidth", "8"); + marker.markerWidth.baseVal.value = 8; + marker.markerWidth.baseVal.valueAsString = "8"; + marker.markerWidth.baseVal.convertToSpecifiedUnits( + SVGLength.SVG_LENGTHTYPE_NUMBER); + marker.markerWidth.baseVal.newValueSpecifiedUnits( + SVGLength.SVG_LENGTHTYPE_NUMBER, 8); + + // number attribute + + eventChecker.watchAttr(convolve, "divisor"); + eventChecker.expect("add modify remove add"); + convolve.setAttribute("divisor", "12.5"); + convolve.divisor.baseVal = 6; + convolve.removeAttribute("divisor"); + convolve.removeAttributeNS(null, "divisor"); + convolve.divisor.baseVal = 8; + + eventChecker.expect(""); + convolve.divisor.baseVal = 8; + convolve.setAttribute("divisor", "8"); + + // number-optional-number attribute + + eventChecker.watchAttr(blur, "stdDeviation"); + eventChecker.expect("add modify remove add"); + blur.setAttribute("stdDeviation", "5, 6"); + blur.stdDeviationX.baseVal = 8; + blur.removeAttribute("stdDeviation"); + blur.removeAttributeNS(null, "stdDeviation"); + blur.setAttribute("stdDeviation", "2, 3"); + + eventChecker.expect(""); + blur.stdDeviationX.baseVal = 2; + blur.stdDeviationY.baseVal = 3; + blur.setAttribute("stdDeviation", "2, 3"); + + // integer attribute + + eventChecker.watchAttr(convolve, "targetX"); + eventChecker.expect("add modify remove add"); + convolve.setAttribute("targetX", "12"); + convolve.targetX.baseVal = 6; + convolve.removeAttribute("targetX"); + convolve.removeAttributeNS(null, "targetX"); + convolve.targetX.baseVal = 8; + + // Check redundant change when comparing typed value to attribute value + eventChecker.expect(""); + convolve.setAttribute("targetX", "8"); + // Check redundant change when comparing attribute value to typed value + eventChecker.expect("remove add"); + convolve.removeAttribute("targetX"); + convolve.setAttribute("targetX", "8"); + convolve.targetX.baseVal = 8; + + // integer-optional-integer attribute + + eventChecker.watchAttr(convolve, "order"); + eventChecker.expect("add modify remove add"); + convolve.setAttribute("order", "5, 7"); + convolve.orderX.baseVal = 9; + convolve.removeAttribute("order"); + convolve.removeAttributeNS(null, "order"); + convolve.setAttribute("order", "9, 5"); + + eventChecker.expect(""); + convolve.orderX.baseVal = 9; + convolve.setAttribute("order", "9, 5"); + convolve.orderY.baseVal = 5; + + // angle attribute + + eventChecker.watchAttr(marker, "orient"); + eventChecker.expect("add modify modify modify modify modify remove add"); + marker.setAttribute("orient", "90deg"); + marker.orientAngle.baseVal.value = 12; + marker.orientAngle.baseVal.valueInSpecifiedUnits = 23; + marker.orientAngle.baseVal.valueAsString = "34"; + marker.orientAngle.baseVal.newValueSpecifiedUnits( + SVGAngle.SVG_ANGLETYPE_GRAD, 34); + marker.orientAngle.baseVal.newValueSpecifiedUnits( + SVGAngle.SVG_ANGLETYPE_GRAD, 45); + marker.removeAttribute("orient"); + marker.removeAttributeNS(null, "orient"); + marker.orientAngle.baseVal.value = 40; + + eventChecker.expect(""); + marker.orientAngle.baseVal.value = 40; + marker.orientAngle.baseVal.valueInSpecifiedUnits = 40; + marker.orientAngle.baseVal.valueAsString = "40"; + marker.orientAngle.baseVal.convertToSpecifiedUnits( + SVGAngle.SVG_ANGLETYPE_UNSPECIFIED); + marker.orientAngle.baseVal.newValueSpecifiedUnits( + SVGAngle.SVG_ANGLETYPE_UNSPECIFIED, 40); + + // boolean attribute + + eventChecker.watchAttr(convolve, "preserveAlpha"); + eventChecker.expect("add modify remove add"); + convolve.setAttribute("preserveAlpha", "true"); + convolve.preserveAlpha.baseVal = false; + convolve.removeAttribute("preserveAlpha"); + convolve.removeAttributeNS(null, "preserveAlpha"); + convolve.preserveAlpha.baseVal = true; + + eventChecker.expect(""); + convolve.preserveAlpha.baseVal = true; + convolve.setAttribute("preserveAlpha", "true"); + + // enum attribute + + eventChecker.watchAttr(convolve, "edgeMode"); + eventChecker.expect("add modify remove add"); + convolve.setAttribute("edgeMode", "none"); + convolve.edgeMode.baseVal = SVGFEConvolveMatrixElement.SVG_EDGEMODE_WRAP; + convolve.removeAttribute("edgeMode"); + convolve.removeAttributeNS(null, "edgeMode"); + convolve.edgeMode.baseVal = SVGFEConvolveMatrixElement.SVG_EDGEMODE_NONE; + + eventChecker.expect(""); + convolve.edgeMode.baseVal = SVGFEConvolveMatrixElement.SVG_EDGEMODE_NONE; + convolve.setAttribute("edgeMode", "none"); + convolve.edgeMode.baseVal = SVGFEConvolveMatrixElement.SVG_EDGEMODE_NONE; + + // string attribute + + eventChecker.watchAttr(convolve, "result"); + eventChecker.expect("add modify remove add"); + convolve.setAttribute("result", "bar"); + convolve.result.baseVal = "foo"; + convolve.removeAttribute("result"); + convolve.removeAttributeNS(null, "result"); + convolve.result.baseVal = "bar"; + + eventChecker.expect(""); + convolve.result.baseVal = "bar"; + convolve.setAttribute("result", "bar"); + convolve.result.baseVal = "bar"; + + // preserveAspectRatio attribute + + eventChecker.watchAttr(marker, "preserveAspectRatio"); + eventChecker.expect("add modify remove add"); + marker.setAttribute("preserveAspectRatio", "xMaxYMid slice"); + marker.preserveAspectRatio.baseVal.align = + SVGPreserveAspectRatio.SVG_PRESERVEASPECTRATIO_XMAXYMAX; + marker.removeAttribute("preserveAspectRatio"); + marker.removeAttributeNS(null, "preserveAspectRatio"); + marker.preserveAspectRatio.baseVal.align = + SVGPreserveAspectRatio.SVG_PRESERVEASPECTRATIO_XMIDYMIN; + + eventChecker.expect(""); + marker.preserveAspectRatio.baseVal.meetOrSlice = + SVGPreserveAspectRatio.SVG_MEETORSLICE_MEET; + marker.setAttribute("preserveAspectRatio", "xMidYMin meet"); + + // viewBox attribute + + eventChecker.watchAttr(marker, "viewBox"); + eventChecker.expect("add modify remove add"); + marker.setAttribute("viewBox", "1 2 3 4"); + marker.viewBox.baseVal.height = 5; + marker.removeAttribute("viewBox"); + marker.removeAttributeNS(null, "viewBox"); + marker.setAttribute("viewBox", "none"); + marker.setAttribute("viewBox", "none"); + + eventChecker.ignoreEvents(); + marker.setAttribute("viewBox", "1 2 3 4"); + eventChecker.expect(""); + marker.viewBox.baseVal.height = 4; + marker.viewBox.baseVal.x = 1; + marker.setAttribute("viewBox", "1 2 3 4"); + + eventChecker.finish(); + + SimpleTest.finish(); +} + +window.addEventListener("load", runTests, false); +</script> +</pre> +</body> +</html> diff --git a/dom/svg/test/test_fragments.html b/dom/svg/test/test_fragments.html new file mode 100644 index 0000000000..41e5fd0382 --- /dev/null +++ b/dom/svg/test/test_fragments.html @@ -0,0 +1,116 @@ +<!DOCTYPE html> +<html xmlns="http://www.w3.org/1999/xhtml"> +<!-- +https://bugzilla.mozilla.org/show_bug.cgi?id=759124 +--> +<head> + <title>Test for Bug 759124</title> + <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" /> +</head> +<body> +<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=759124">Mozilla Bug 759124</a> +<p id="display"></p> +<div id="content" style="display: none"></div> + +<iframe id="svg" src="fragments-helper.svg"></iframe> + +<pre id="test"> +<script class="testbody" type="application/javascript"> +SimpleTest.waitForExplicitFinish(); + +function Test(svgFragmentIdentifier, valid, viewBoxString, + preserveAspectRatioString, zoomAndPanString) +{ + this.svgFragmentIdentifier = svgFragmentIdentifier; + this.valid = valid; +} + +function runTests() +{ + var svg = $("svg"); + var doc = svg.contentWindow.document; + var rootElement = doc.documentElement; + + var tests = [ + new Test("unknown", false), + new Test("svgView(viewBox(0,0,200,200))", true), + new Test("svgView(preserveAspectRatio(xMaxYMin slice))", true), + new Test("svgView(viewBox(1,2,3,4);preserveAspectRatio(xMinYMax))", true), + new Test("svgView(viewBox(none))", true), + new Test("svgView(zoomAndPan(disable))", true), + new Test("svgView(transform(translate(-10,-20) scale(2) rotate(45) translate(5,10)))", true), + // No duplicates allowed + new Test("svgView(zoomAndPan(disable);zoomAndPan(disable))", false), + new Test("svgView(viewBox(0,0,200,200);viewBox(0,0,200,200))", false), + new Test("svgView(preserveAspectRatio(xMaxYMin);preserveAspectRatio(xMaxYMin))", false), + new Test("svgView(transform(translate(0,200));transform(translate(0,200)))", false), + // No invalid values allowed + new Test("svgView(viewBox(bad)", false), + new Test("svgView(preserveAspectRatio(bad))", false), + new Test("svgView(zoomAndPan(bad))", false), + new Test("svgView(transform(bad))", false), + new Test("svgView", false), + new Test("svgView(", false), + new Test("svgView()", false), + // Be sure we verify that there's a closing paren for svgView() + // (and not too many closing parens) + new Test("svgView(zoomAndPan(disable)", false), + new Test("svgView(zoomAndPan(disable) ", false), + new Test("svgView(zoomAndPan(disable)]", false), + new Test("svgView(zoomAndPan(disable)))", false) + ]; + + var src = svg.getAttribute("src"); + + is(false, rootElement.hasAttribute("viewBox"), + "expecting to start without a viewBox set"); + is(false, rootElement.hasAttribute("preserveAspectRatio"), + "expecting to start without preserveAspectRatio set"); + is(false, rootElement.hasAttribute("zoomAndPan"), + "expecting to start without zoomAndPan set"); + + for (var j = 0; j < 2; j++) { + var initialViewBox = rootElement.getAttribute("viewBox"); + var initialPreserveAspectRatio = + rootElement.getAttribute("preserveAspectRatio"); + var initialZoomAndPan = rootElement.getAttribute("zoomAndPan"); + var initialTransform = rootElement.getAttribute("transform"); + + for (var i = 0; i < tests.length; i++) { + var test = tests[i]; + svg.setAttribute("src", src + "#" + test.svgFragmentIdentifier); + is(rootElement.useCurrentView, test.valid, + "Expected " + test.svgFragmentIdentifier + " to be " + + (test.valid ? "valid" : "invalid")); + + // check that assigning a viewSpec does not modify the underlying + // attribute values. + is(rootElement.getAttribute("viewBox"), + initialViewBox, "unexpected viewBox"); + + is(rootElement.getAttribute("preserveAspectRatio"), + initialPreserveAspectRatio, "unexpected preserveAspectRatio"); + + is(rootElement.getAttribute("zoomAndPan"), + initialZoomAndPan, "unexpected zoomAndPan"); + + is(rootElement.getAttribute("transform"), + initialTransform, "unexpected transform"); + } + + // repeat tests with underlying attributes set to values + rootElement.setAttribute("viewBox", "0 0 100 100"); + rootElement.setAttribute("preserveAspectRatio", "none"); + rootElement.setAttribute("zoomAndPan", "disable"); + rootElement.setAttribute("transform", "translate(10,10)"); + } + + SimpleTest.finish(); +} + +$(svg).addEventListener("load", runTests, false); +</script> +</pre> +</body> +</html> diff --git a/dom/svg/test/test_getBBox-method.html b/dom/svg/test/test_getBBox-method.html new file mode 100644 index 0000000000..0f200634cd --- /dev/null +++ b/dom/svg/test/test_getBBox-method.html @@ -0,0 +1,249 @@ +<!DOCTYPE HTML> +<!-- +https://bugzilla.mozilla.org/show_bug.cgi?id=999964 +--> +<head> + <meta charset="utf-8"/> + <title>Test case for Bug 999964</title> + <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> +</head> + +<body> +<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=999964">Mozilla Bug 999964</a> +<p id="display"></p> +<div id="content" style="display: none"></div> + +<iframe id="svg" src="getBBox-method-helper.svg"></iframe> + +<pre id="test"> +<script class="testbody" type="application/javascript"> + + /** Test case for Bug 999964 **/ + +SimpleTest.waitForExplicitFinish(); + +function run() +{ + var flag = SpecialPowers.getBoolPref("svg.new-getBBox.enabled"); + if (!flag) { + ok(!flag, "skip test for bug999964."); + SimpleTest.finish(); + return; + } + + var doc = $("svg").contentDocument; + + function isFuzzy(a, b, error, name) + { + ok(!(Math.abs(a - b) > error), name, "got " + a + ", expected " + b + " (within " + error + ")"); + } + + function getBBox(id, opt) { + return doc.getElementById(id).getBBox(opt); + } + + function checkBBox(id, opt, x, y, width, height, error) { + var bbox = getBBox(id, opt); + isFuzzy(bbox.x, x, error, id + ".getBBox().x"); + isFuzzy(bbox.y, y, error, id + ".getBBox().y"); + isFuzzy(bbox.width, width, error, id + ".getBBox().width"); + isFuzzy(bbox.height, height, error, id + ".getBBox().height"); + } + + function compareBBox1(id1, id2) { + var bbox1 = getBBox(id1); + var bbox2 = getBBox(id2); + is(bbox1.x, bbox2.x, id1 + ".getBBox().x"); + is(bbox1.y, bbox2.y, id1 + ".getBBox().y"); + isFuzzy(bbox1.width, bbox2.width, 0.0002, id1 + ".getBBox().width"); + isFuzzy(bbox1.height, bbox2.height, 0.0001, id1 + ".getBBox().height"); + } + + function compareBBox2(id1, id2) { + // without 'x' + var bbox1 = getBBox(id1); + var bbox2 = getBBox(id2); + is(bbox1.y, bbox2.y, id1 + ".getBBox().y"); + isFuzzy(bbox1.width, bbox2.width, 0.0002, id1 + ".getBBox().width"); + isFuzzy(bbox1.height, bbox2.height, 0.0001, id1 + ".getBBox().height"); + } + + var opt = { fill: true, stroke: true, markers: true, clipped: true }; + + // <text> + // fill + opt = { fill: true, stroke: false, markers: false, clipped: false }; + compareBBox1("text1","text3"); + compareBBox1("text2","text4"); + compareBBox1("text5","text6"); + // all + opt = { fill: true, stroke: true, markers: true, clipped: true }; + compareBBox2("text1","text3"); + compareBBox2("text2","text4"); + compareBBox2("text5","text6"); + // clipped + opt = { fill: false, stroke: false, markers: false, clipped: true }; + compareBBox2("text1","text3"); + compareBBox2("text2","text4"); + compareBBox2("text5","text6"); + + // <image> + opt = { fill: true, stroke: true, markers: true, clipped: true }; + checkBBox("image1", opt, 250, 250, 100, 100); + checkBBox("image2", opt, 53, 53, 149, 149); + checkBBox("image3", opt, 205, 53, 148, 149); + checkBBox("image4", opt, 53, 205, 149, 148); + checkBBox("image5", opt, 205, 205, 148, 148); + checkBBox("image6", opt, 52, 52, 100, 100); + checkBBox("image7", opt, 255, 52, 100, 100); + checkBBox("image8", opt, 52, 255, 100, 100); + checkBBox("image9", opt, 255, 255, 100, 100); + checkBBox("image10", opt, 200, 200, 200, 200); + checkBBox("image11", opt, 0, 0, 0, 0); + checkBBox("image12", opt, 43, 43, 714, 660); + checkBBox("image13", opt, 50, 50, 300, 300); + checkBBox("image14", opt, 0, 0, 0, 0); + + opt = { fill: true, stroke: false, markers: false, clipped: false }; + checkBBox("image1", opt, 150,150,200,200, 0); + checkBBox("image2", opt, 2,2,200,200, 0); + checkBBox("image3", opt, 205,2,200,200, 0); + checkBBox("image4", opt, 2,205,200,200, 0); + checkBBox("image5", opt, 205,205,200,200, 0); + checkBBox("image6", opt, 2,2,200,200, 0); + checkBBox("image7", opt, 205,2,200,200, 0); + checkBBox("image8", opt, 2,205,200,200, 0); + checkBBox("image9", opt, 205,205,200,200, 0); + checkBBox("image10", opt, 0,0,400,400, 0); + checkBBox("image11", opt, 0,0,400,400, 0); + checkBBox("image12", opt, 25,43,768,768, 0); + checkBBox("image13", opt, 0,0,400,400, 0); + + // <path> + opt = { fill: true, stroke: true, markers: true, clipped: true }; + checkBBox("path1", opt, 2,17,120,95, 0); + checkBBox("path2", opt, 156,21,116,91, 0); + checkBBox("path3", opt, 6,121,116,91, 0); + checkBBox("path4", opt, 2,17,98,83, 0); + checkBBox("path5", opt, 156,21,44,79, 0); + checkBBox("path6", opt, 6,150,94,62, 0); + checkBBox("path7", opt, 2,17,98,83, 0); + checkBBox("path8", opt, 156,21,94,79, 0); + checkBBox("path9", opt, 6,121,94,79, 0); + checkBBox("path10", opt, 10,25,100,75, 0); + checkBBox("path11", opt, 160,25,100,75, 0); + checkBBox("path12", opt, 10,125,100,75, 0); + + opt = { fill: true, stroke: false, markers: false, clipped: true }; + checkBBox("path1", opt, 10,25,100,75, 0); + checkBBox("path2", opt, 160,25,100,75, 0); + checkBBox("path3", opt, 10,125,100,75, 0); + checkBBox("path4", opt, 10,25,90,75, 0); + checkBBox("path5", opt, 160,25,40,75, 0); + checkBBox("path6", opt, 10,150,90,50, 0); + checkBBox("path7", opt, 10,25,90,75, 0); + checkBBox("path8", opt, 160,25,90,75, 0); + checkBBox("path9", opt, 10,125,90,75, 0); + checkBBox("path10", opt, 10,25,100,75, 0); + checkBBox("path11", opt, 160,25,100,75, 0); + checkBBox("path12", opt, 10,125,100,75, 0); + + opt = { fill: true, stroke: false, markers: false, clipped: false }; + checkBBox("path1", opt, 10,25,100,75, 0); + checkBBox("path2", opt, 160,25,100,75, 0); + checkBBox("path3", opt, 10,125,100,75, 0); + checkBBox("path4", opt, 10,25,100,75, 0); + checkBBox("path5", opt, 160,25,100,75, 0); + checkBBox("path6", opt, 10,125,100,75, 0); + checkBBox("path7", opt, 10,25,100,75, 0); + checkBBox("path8", opt, 160,25,100,75, 0); + checkBBox("path9", opt, 10,125,100,75, 0); + checkBBox("path10", opt, 10,25,100,75, 0); + checkBBox("path11", opt, 160,25,100,75, 0); + checkBBox("path12", opt, 10,125,100,75, 0); + + opt = { fill: false, stroke: true, markers: false, clipped: false }; + checkBBox("path1", opt, 2,17,116,91, 0); + checkBBox("path2", opt, 156,21,108,83, 0); + checkBBox("path3", opt, 6,121,108,83, 0); + checkBBox("path4", opt, 2,17,116,91, 0); + checkBBox("path5", opt, 156,21,108,83, 0); + checkBBox("path6", opt, 6,121,108,83, 0); + checkBBox("path7", opt, 2,17,116,91, 0); + checkBBox("path8", opt, 156,21,108,83, 0); + checkBBox("path9", opt, 6,121,108,83, 0); + checkBBox("path10", opt, 2,17,116,91, 0); + checkBBox("path11", opt, 156,21,108,83, 0); + checkBBox("path12", opt, 6,121,108,83, 0); + + opt = { fill: false, stroke: false, markers: true, clipped: false }; + checkBBox("path1", opt, 10,25,112,87, 0); + checkBBox("path2", opt, 160,25,112,87, 0); + checkBBox("path3", opt, 10,125,112,87, 0); + checkBBox("path4", opt, 10,25,112,87, 0); + checkBBox("path5", opt, 160,25,112,87, 0); + checkBBox("path6", opt, 10,125,112,87, 0); + checkBBox("path7", opt, 10,25,112,87, 0); + checkBBox("path8", opt, 160,25,112,87, 0); + checkBBox("path9", opt, 10,125,112,87, 0); + checkBBox("path10", opt, 10,25,112,87, 0); + checkBBox("path11", opt, 160,25,112,87, 0); + checkBBox("path12", opt, 10,125,112,87, 0); + + // <use> + opt = { fill: true, stroke: false, markers: false, clipped: false }; + checkBBox("use1", opt, 70,70,180,180, 0); + checkBBox("use2", opt, 250,70,180,180, 0); + checkBBox("use3", opt, 70,250,180,180, 0); + checkBBox("use4", opt, 22,22,180,180, 0); + checkBBox("use5", opt, 225,22,180,180, 0); + checkBBox("use6", opt, 22,225,180,180, 0); + checkBBox("use7", opt, 225,225,180,180, 0); + + opt = { fill: true, stroke: true, markers: true, clipped: true }; + checkBBox("use1", opt, 70,66,180,94, 0); + checkBBox("use2", opt, 250,70,180,90, 0); + checkBBox("use3", opt, 70,250,180,90, 0); + checkBBox("use4", opt, 18,18,134,134, 0); + checkBBox("use5", opt, 221,18,134,134, 0); + checkBBox("use6", opt, 18,221,134,134, 0); + checkBBox("use7", opt, 221,221,134,134, 0); + checkBBox("use8", opt, 0,0,0,0, 0); + + // <foreignObject> + opt = { fill: true, stroke: false, markers: false, clipped: false }; + checkBBox("fo1", opt, 2,2,200,200, 0); + checkBBox("fo2", opt, 205,2,200,200, 0); + checkBBox("fo3", opt, 2,205,200,200, 0); + checkBBox("fo4", opt, 205,205,200,200, 0); + checkBBox("fo5", opt, 250,250,200,200, 0); + checkBBox("fo6", opt, 0,0,200,200, 0); + checkBBox("fo7", opt, 0,0,200,200, 0); + + opt = { fill: true, stroke: true, markers: true, clipped: true }; + checkBBox("fo1", opt, 53,53,51,51, 0); + checkBBox("fo2", opt, 205,53,148,149, 0); + checkBBox("fo3", opt, 53,205,149,148, 0); + checkBBox("fo4", opt, 207,207,100,100, 0); + checkBBox("fo5", opt, 0,0,0,0, 0); + checkBBox("fo6", opt, 100,100,100,100, 0); + checkBBox("fo7", opt, 10,10,180,180, 0); + checkBBox("fo8", opt, 0,0,0,0, 0); + + // from http://www.w3.org/Graphics/SVG/Test/20110816/harness/htmlObjectApproved/masking-path-07-b.html + opt = { fill: true, stroke: true, markers: true, clipped: true }; + checkBBox("rect-1", opt, 10,10,140,140, 0); + checkBBox("rect-2", opt, 50,30,25,100, 0); + checkBBox("rect-3", opt, 50,50,100,100, 0); + checkBBox("g1", opt, 50,50,100,100, 0); + + SimpleTest.finish(); +} + +window.addEventListener("load", run, false); + +</script> +</pre> +</body> +</html> diff --git a/dom/svg/test/test_getCTM.html b/dom/svg/test/test_getCTM.html new file mode 100644 index 0000000000..ec267626f9 --- /dev/null +++ b/dom/svg/test/test_getCTM.html @@ -0,0 +1,110 @@ +<!DOCTYPE html> +<html xmlns="http://www.w3.org/1999/xhtml"> +<!-- +https://bugzilla.mozilla.org/show_bug.cgi?id=366697 +--> +<head> + <title>Test for Bug 366697</title> + <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" /> +</head> +<body> +<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=366697">Mozilla Bug 366697</a> +<p id="display"></p> +<div id="content" style="display: none"></div> + +<iframe id="svg" src="getCTM-helper.svg"></iframe> + +<pre id="test"> +<script class="testbody" type="application/javascript"> +SimpleTest.waitForExplicitFinish(); + +function runTest() +{ + var doc = $("svg").contentWindow.document; + + /* Minimal */ + var buggy = doc.getElementById("buggy"); + is(buggy.getCTM().e, 30, "buggy.getCTM().e"); + is(buggy.getCTM().f, 40, "buggy.getCTM().f"); + + var root = doc.documentElement; + var inner = doc.getElementById("inner"); + var g1 = doc.getElementById("g1"); + var outer = doc.getElementById("outer"); + var outer2 = doc.getElementById("outer2"); + var g2 = doc.getElementById("g2"); + var g3 = doc.getElementById("g3"); + var g4 = doc.getElementById("g4"); + var g5 = doc.getElementById("g5"); + var sym = doc.getElementById("sym"); + var symbolRect = doc.getElementById("symbolRect"); + var fO = doc.getElementById("fO"); + /* Tests the consistency with nearestViewportElement + (code is from test_viewport.html) */ + // root.nearestViewportElement == null + is((function(){try{return root.getCTM()}catch(e){return e}})(), null, "root.getCTM()"); + // inner.nearestViewportElement == root + is((function(){try{return inner.getCTM().e}catch(e){return e}})(), 1, "inner.getCTM().e"); + is((function(){try{return inner.getCTM().f}catch(e){return e}})(), 2, "inner.getCTM().f"); + // g1.nearestViewportElement == inner + is((function(){try{return g1.getCTM().e}catch(e){return e}})(), 30, "g1.getCTM().e"); + is((function(){try{return g1.getCTM().f}catch(e){return e}})(), 40, "g1.getCTM().f"); + // outer.nearestViewportElement == null + is((function(){try{return outer.getCTM()}catch(e){return e}})(), null, "outer.getCTM()"); + // g2.nearestViewportElement == outer + is((function(){try{return g2.getCTM().e}catch(e){return e}})(), 600, "g2.getCTM().e"); + is((function(){try{return g2.getCTM().f}catch(e){return e}})(), 700, "g2.getCTM().f"); + // g3.nearestViewportElement == null + is((function(){try{return g3.getCTM()}catch(e){return e}})(), null, "g3.getCTM()"); + // g4.nearestViewportElement == null + is((function(){try{return g4.getCTM().e}catch(e){return e}})(), 1, "g4.getCTM().e"); + is((function(){try{return g4.getCTM().f}catch(e){return e}})(), 2, "g4.getCTM().f"); + // symbolRect.nearestViewportElement == sym + is((function(){try{return symbolRect.getCTM().e}catch(e){return e}})(), 70, "symbolRect.getCTM().e"); + is((function(){try{return symbolRect.getCTM().f}catch(e){return e}})(), 80, "symbolRect.getCTM().f"); + // fO.nearestViewportElement == <svg> with no 'id' + is((function(){try{return fO.getCTM().e}catch(e){return e}})(), 2, "fO.getCTM().e"); + is((function(){try{return fO.getCTM().f}catch(e){return e}})(), 3, "fO.getCTM().f"); + // g5.nearestViewportElement == inner-2 + is((function(){try{return g5.getCTM()}catch(e){return e}})(), null, "g5.getCTM()"); + + /* Tests the consistency with farthestViewportElement + (code is from test_viewport.html) */ + // root.farthestViewportElement == null (but actually == root) + is((function(){try{return root.getScreenCTM().e}catch(e){return e}})(), 91.5, "root.getScreenCTM().e"); + is((function(){try{return root.getScreenCTM().f}catch(e){return e}})(), 33, "root.getScreenCTM().f"); + // inner.farthestViewportElement == root + is((function(){try{return inner.getScreenCTM().e}catch(e){return e}})(), 97.5, "inner.getScreenCTM().e"); + is((function(){try{return inner.getScreenCTM().f}catch(e){return e}})(), 42, "inner.getScreenCTM().f"); + // g1.farthestViewportElement == root + is((function(){try{return g1.getScreenCTM().e}catch(e){return e}})(), 142.5, "g1.getScreenCTM().e"); + is((function(){try{return g1.getScreenCTM().f}catch(e){return e}})(), 102, "g1.getScreenCTM().f"); + // outer.farthestViewportElement == null (but actually == root) + is((function(){try{return outer.getScreenCTM().e}catch(e){return e}})(), 144, "outer.getScreenCTM().e"); + is((function(){try{return outer.getScreenCTM().f}catch(e){return e}})(), 103.5, "outer.getScreenCTM().f"); + // outer.farthestViewportElement == null (but actually == root) + is((function(){try{return outer2.getScreenCTM().e}catch(e){return e}})(), -19, "outer2.getScreenCTM().e"); + is((function(){try{return outer2.getScreenCTM().f}catch(e){return e}})(), -8, "outer2.getScreenCTM().f"); + // g2.farthestViewportElement == outer (but actually == root) + is((function(){try{return g2.getScreenCTM().e}catch(e){return e}})(), 1044, "g2.getScreenCTM().e"); + is((function(){try{return g2.getScreenCTM().f}catch(e){return e}})(), 1153.5, "g2.getScreenCTM().f"); + // g3.farthestViewportElement == null (but actually == null) + is((function(){try{return g3.getScreenCTM()}catch(e){return e}})(), null, "g3.getScreenCTM()"); + // symbolRect.farthestViewportElement == root + is((function(){try{return symbolRect.getScreenCTM().e}catch(e){return e}})(), 202.5, "symbolRect.getScreenCTM().e"); + is((function(){try{return symbolRect.getScreenCTM().f}catch(e){return e}})(), 162, "symbolRect.getScreenCTM().f"); + // fO.farthestViewportElement == root + is((function(){try{return fO.getScreenCTM().e}catch(e){return e}})(), 99, "symbolRect.getScreenCTM().e"); + is((function(){try{return fO.getScreenCTM().f}catch(e){return e}})(), 43.5, "symbolRect.getScreenCTM().f"); + // g5.farthestViewportElement == root + is((function(){try{return g5.getScreenCTM()}catch(e){return e}})(), null, "g5.getScreenCTM()"); + + SimpleTest.finish(); +} + +window.addEventListener("load", runTest, false); +</script> +</pre> +</body> +</html> diff --git a/dom/svg/test/test_getElementById.xhtml b/dom/svg/test/test_getElementById.xhtml new file mode 100644 index 0000000000..863f520474 --- /dev/null +++ b/dom/svg/test/test_getElementById.xhtml @@ -0,0 +1,67 @@ +<html xmlns="http://www.w3.org/1999/xhtml"> +<head> + <title>Test getElementById behaviour</title> + <script type="text/javascript" src="/MochiKit/packed.js"></script> + <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <script type="text/javascript" src="matrixUtils.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" /> +</head> +<body> +<p id="display"></p> +<div id="content"> + <svg xmlns="http://www.w3.org/2000/svg" width="100%" height="1" id="svg"> + <!-- decoy element, same Id but not a <g> --> + <rect id="g"/> + <svg id="inner"> + <!-- the one we want to find --> + <g id="g"/> + <!-- check we don't get confused by CSS selectors --> + <g id="foo bar"/> + <g id="goo > car"/> + <g id="hoo~dar"/> + <g id="ioo+ear"/> + </svg> + <g id="g2"/> + </svg> +</div> +<pre id="test"> +<script class="testbody" type="text/javascript"> +<![CDATA[ + +SimpleTest.waitForExplicitFinish(); + +function main() +{ + var svgns = "http://www.w3.org/2000/svg"; + + var svg = document.getElementById("inner"); + + is(svg.getElementById("g").nodeName, "g", "expected to find g element child"); + is(svg.getElementById("foo bar").nodeName, "g", "expected to find foo bar element child"); + is(svg.getElementById("goo > car").nodeName, "g", "expected to find goo > car element child"); + is(svg.getElementById("hoo~dar").nodeName, "g", "expected to find hoo~dar element child"); + is(svg.getElementById("ioo+ear").nodeName, "g", "expected to find ioo+ear element child"); + + is(svg.getElementById("g2"), null, "did not expect to find an element with id g2"); + + // no element with Id = "g3" in the document at all + is(svg.getElementById("g3"), null, "did not expect to find an element with id g3"); + + svg = document.createElementNS(svgns, "svg"); + + var c = document.createElementNS(svgns, "circle"); + c.setAttribute("id", "c"); + svg.appendChild(c); + + is(svg.getElementById("c").nodeName, "circle", "expected to find circle element child"); + + SimpleTest.finish(); +} + +window.addEventListener("load", main, false); + +]]> +</script> +</pre> +</body> +</html> diff --git a/dom/svg/test/test_getSubStringLength.xhtml b/dom/svg/test/test_getSubStringLength.xhtml new file mode 100644 index 0000000000..d41bf51d40 --- /dev/null +++ b/dom/svg/test/test_getSubStringLength.xhtml @@ -0,0 +1,104 @@ +<!DOCTYPE html> +<html xmlns="http://www.w3.org/1999/xhtml"> +<!-- +https://bugzilla.mozilla.org/show_bug.cgi?id=420243 +--> +<head> + <title>Test for Bug 420243</title> + <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" /> +</head> +<body> +<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=420243">Mozilla Bug 420243</a> +<p id="display"></p> +<div id="content" style="display: none"></div> + +<iframe id="svg" src="getSubStringLength-helper.svg"></iframe> + +<pre id="test"> +<script class="testbody" type="application/javascript"> +SimpleTest.waitForExplicitFinish(); + +function runTests(text, charWidth) +{ + function chars(n) { return charWidth * n; } + + function expectThrow(charnum, nchars) + { + try + { + text.getSubStringLength(charnum, nchars); + ok(false, + "text.getSubStringLength(" + charnum + "," + nchars + ") " + + "should have thrown"); + } + catch (e) + { + is(e.name, "IndexSizeError", + "expected an index error for " + + "text.getSubStringLength(" + charnum + "," + nchars + ")"); + is(e.code, DOMException.INDEX_SIZE_ERR, + "expected an index error for " + + "text.getSubStringLength(" + charnum + "," + nchars + ")"); + } + } + + function expectValue(charnum, nchars, expected) + { + try + { + is(text.getSubStringLength(charnum, nchars), expected, + "text.getSubStringLength(" + charnum + "," + nchars + ") " + + "returned wrong value"); + } + catch (e) + { + ok(false, + "unexpected exception for " + + "text.getSubStringLength(" + charnum + "," + nchars + ")"); + } + } + + expectThrow(100, 2); + expectThrow(100, 0); + expectThrow(3, 0); + expectThrow(3, 1); + + expectValue(1, 3, chars(2)); + expectValue(0, 4, chars(3)); + expectValue(0, 0, chars(0)); + expectValue(1, 0, chars(0)); + expectValue(2, 0, chars(0)); + expectValue(0, 1, chars(1)); + expectValue(1, 1, chars(1)); + expectValue(2, 1, chars(1)); + expectValue(0, 2, chars(2)); + expectValue(1, 2, chars(2)); + expectValue(0, 3, chars(3)); + expectValue(1, 100, chars(2)); + expectValue(2, 100, chars(1)); +} + + +function run() +{ + try + { + var document = $("svg").contentWindow.document; + var text = document.getElementById("text"); + + runTests(text, text.getSubStringLength(0, 1)); + } + catch (e) + { + ok(false, "threw error: " + e); + } + + SimpleTest.finish(); +} + +window.addEventListener("load", run, false); +</script> +</pre> +</body> +</html> diff --git a/dom/svg/test/test_lang.xhtml b/dom/svg/test/test_lang.xhtml new file mode 100644 index 0000000000..4e4935e0c9 --- /dev/null +++ b/dom/svg/test/test_lang.xhtml @@ -0,0 +1,90 @@ +<!DOCTYPE html> +<html lang="en" xmlns="http://www.w3.org/1999/xhtml"> +<!-- +https://bugzilla.mozilla.org/show_bug.cgi?id=721920 +--> +<head> + <title>Test for Bug 721920</title> + <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/> + <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" /> + <style type="text/css"> + +svg text { word-spacing: 1em; } + + </style> +</head> +<body> +<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=721920">Mozilla Bug 721920</a> +<p id="display"> + <svg xmlns="http://www.w3.org/2000/svg" width="400" height="300"> + <g lang="zh-Hans"> + <text id="s0" y="40" style="font-size: 0">汉字</text> + <text id="s4" y="80" style="font-size: 4px">汉字</text> + <text id="s12" y="120" style="font-size: 12px">汉字</text> + <text id="s28" y="160" style="font-size: 28px">汉字</text> + </g> + </svg> +</p> +<div id="content" style="display: none"> + +</div> +<pre id="test"> +<script class="testbody" type="text/javascript"> +//<![CDATA[ + +/** Test for Bug 721920 **/ + +SimpleTest.waitForExplicitFinish(); + +var elts = [ + document.getElementById("s0"), + document.getElementById("s4"), + document.getElementById("s12"), + document.getElementById("s28") +]; + +function fs(idx) { + // The computed font size actually *doesn't* currently reflect the + // minimum font size preference, but things in em units do. Hence + // why we use word-spacing here. + // test_bug401046.html uses margin-bottom instead, but there's an + // SVG bug that prevents that working in SVG (bug 728723). + return getComputedStyle(elts[idx], "").wordSpacing; +} + +SpecialPowers.pushPrefEnv({'clear': [['font.minimum-size.zh-CN']]}, step1); + +function step1() { + is(fs(0), "0px", "at min font size 0, 0px should compute to 0px"); + is(fs(1), "4px", "at min font size 0, 4px should compute to 4px"); + is(fs(2), "12px", "at min font size 0, 12px should compute to 12px"); + is(fs(3), "28px", "at min font size 0, 28px should compute to 28px"); + + SpecialPowers.pushPrefEnv({'set': [['font.minimum-size.zh-CN', 7]]}, step2); +} + +function step2() { + is(fs(0), "0px", "at min font size 7, 0px should compute to 0px"); + is(fs(1), "7px", "at min font size 7, 4px should compute to 7px"); + is(fs(2), "12px", "at min font size 7, 12px should compute to 12px"); + is(fs(3), "28px", "at min font size 7, 28px should compute to 28px"); + + SpecialPowers.pushPrefEnv({'set': [['font.minimum-size.zh-CN', 18]]}, step3); +} + +function step3() { + is(fs(0), "0px", "at min font size 18, 0px should compute to 0px"); + is(fs(1), "18px", "at min font size 18, 4px should compute to 18px"); + is(fs(2), "18px", "at min font size 18, 12px should compute to 18px"); + is(fs(3), "28px", "at min font size 18, 28px should compute to 28px"); + + SpecialPowers.pushPrefEnv({'clear': [['font.minimum-size.zh-CN']]}, SimpleTest.finish); +} + +//]]> +</script> +</pre> +</body> +</html> + diff --git a/dom/svg/test/test_length.xhtml b/dom/svg/test/test_length.xhtml new file mode 100644 index 0000000000..f2cccca507 --- /dev/null +++ b/dom/svg/test/test_length.xhtml @@ -0,0 +1,50 @@ +<html xmlns="http://www.w3.org/1999/xhtml"> +<!-- +https://bugzilla.mozilla.org/show_bug.cgi?id=342513 +--> +<head> + <title>Test SVG Length conversions</title> + <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" /> +</head> +<body> +<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=342513">Mozilla Bug 342513</a> +<p id="display"></p> +<div id="content"> + + <div width="100%" height="1" id="div"> + </div> + <svg xmlns="http://www.w3.org/2000/svg" width="100%" height="1" id="svg"> + </svg> + +</div> +<pre id="test"> +<script class="testbody" type="text/javascript"> +<![CDATA[ + +SimpleTest.waitForExplicitFinish(); + +function run() +{ + var svgDoc = document.getElementById('svg'); + var divElem = document.getElementById('div'); + var expectedWidth = divElem.clientWidth; + // test for the pixel width of the svg + is(svgDoc.width.baseVal.value, expectedWidth, 'svgDoc.width.baseVal.value'); + + // set the SVG width to ~50% in pixels + svgDoc.width.baseVal.newValueSpecifiedUnits(svgDoc.width.baseVal.SVG_LENGTHTYPE_PX, Math.floor(expectedWidth/2)); + svgDoc.width.baseVal.convertToSpecifiedUnits(svgDoc.width.baseVal.SVG_LENGTHTYPE_PERCENTAGE); + // the valueInSpecifiedUnits should now be 50% + is(Math.round(svgDoc.width.baseVal.valueInSpecifiedUnits), 50, 'valueInSpecifiedUnits after convertToSpecifiedUnits'); + + SimpleTest.finish(); +} + +window.addEventListener("load", run, false); + +]]> +</script> +</pre> +</body> +</html> diff --git a/dom/svg/test/test_lengthParsing.html b/dom/svg/test/test_lengthParsing.html new file mode 100644 index 0000000000..71fd509547 --- /dev/null +++ b/dom/svg/test/test_lengthParsing.html @@ -0,0 +1,77 @@ +<!doctype html> +<html> +<!-- +https://bugzilla.mozilla.org/show_bug.cgi?id=946529 +--> +<head> + <meta charset="utf-8"> + <title>Test transform parsing</title> + <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" /> +</head> +<body> +<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=946529">Mozilla Bug 946529</a> +<p id="display"></p> +<div id="content" style="display: none"> + <svg width="100%" height="1" id="svg"> + <rect id="rect"/> + </svg> +</div> +<pre id="test"> +<script class="testbody" type="text/javascript"> +// Test cases +checkParseOk("", 0); +checkParseOk("-.1", -0.1); +checkParseOk("1e1", 10); +checkParseOk("1em", 1, "em"); +checkParseOk("1ex", 1, "ex"); +checkParseOk("1e1em", 10, "em"); +checkParseOk("1E+2", 100); +checkParseOk(".1e-2", 0.001); + +// Fail cases +checkParseFail("1e"); +checkParseFail("1 e"); +checkParseFail("1 em"); +checkParseFail("1ee"); + +function checkParseOk(spec, valueInUnits, units) { + var rect = document.getElementById("rect"); + + // Clear previous value + rect.removeAttribute("x"); + rect.setAttribute("x", spec); + + // Check number part + const tolerance = 1 / 65535; + var actual = rect.x.baseVal.valueInSpecifiedUnits; + ok(Math.abs(actual - valueInUnits) < tolerance, + spec + ' (value) - got ' + actual + ', expected ' + valueInUnits); + + // Check unit part + var unitMapping = { + "unknown": SVGLength.SVG_LENGTHTYPE_UNKNOWN, + "": SVGLength.SVG_LENGTHTYPE_NUMBER, + "%": SVGLength.SVG_LENGTHTYPE_PERCENTAGE, + "em": SVGLength.SVG_LENGTHTYPE_EMS, + "ex": SVGLength.SVG_LENGTHTYPE_EXS, + "px": SVGLength.SVG_LENGTHTYPE_PX, + "cm": SVGLength.SVG_LENGTHTYPE_CM, + "mm": SVGLength.SVG_LENGTHTYPE_MM, + "in": SVGLength.SVG_LENGTHTYPE_IN, + "pt": SVGLength.SVG_LENGTHTYPE_PT, + "pc": SVGLength.SVG_LENGTHTYPE_PC + }; + if (typeof units == "undefined") { + units = ""; + } + is(rect.x.baseVal.unitType, unitMapping[units], spec + " (unit)"); +} + +function checkParseFail(spec) { + checkParseOk(spec, 0); +} +</script> +</pre> +</body> +</html> diff --git a/dom/svg/test/test_markerOrient.xhtml b/dom/svg/test/test_markerOrient.xhtml new file mode 100644 index 0000000000..63e285dc8e --- /dev/null +++ b/dom/svg/test/test_markerOrient.xhtml @@ -0,0 +1,115 @@ +<html xmlns="http://www.w3.org/1999/xhtml"> +<!-- +https://bugzilla.mozilla.org/show_bug.cgi?id=892372 +--> +<head> + <title>Test for Bug 892372</title> + <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> + <script type="application/javascript"> + <![CDATA[ + + /** Test for Bug 892372 **/ + SimpleTest.waitForExplicitFinish(); + + function testAutoIsSet(marker) { + is(marker.orientType.baseVal, SVGMarkerElement.SVG_MARKER_ORIENT_AUTO, + "orientType baseVal for auto"); + is(marker.orientType.animVal, SVGMarkerElement.SVG_MARKER_ORIENT_AUTO, + "orientType animVal for auto"); + is(marker.orientAngle.baseVal.value, 0, "orientAngle baseVal for auto"); + is(marker.orientAngle.animVal.value, 0, "orientAngle animVal for auto"); + } + + function testAutoStartReverseIsSet(marker) { + is(marker.orientType.baseVal, SVGMarkerElement.SVG_MARKER_ORIENT_UNKNOWN, + "orientType baseVal for auto-start-reverse"); + is(marker.orientType.animVal, SVGMarkerElement.SVG_MARKER_ORIENT_UNKNOWN, + "orientType animVal for auto-start-reverse"); + is(marker.orientAngle.baseVal.value, 0, + "orientAngle baseVal for auto-start-reverse"); + is(marker.orientAngle.animVal.value, 0, + "orientAngle animVal for auto-start-reverse"); + } + + function testAngleIsSet(marker, angleVal) { + is(marker.orientType.baseVal, SVGMarkerElement.SVG_MARKER_ORIENT_ANGLE, + "orientType baseVal after numeric angle is set"); + is(marker.orientType.animVal, SVGMarkerElement.SVG_MARKER_ORIENT_ANGLE, + "orientType animVal after numeric angle is set"); + is(m.orientAngle.baseVal.value, angleVal, + "orientAngle baseVal after numeric angle is set"); + is(m.orientAngle.animVal.value, angleVal, + "orientAngle animVal after numeric angle is set"); + } + + function run() + { + var m = $("m"); + + // Testing two conditions: + // 1) If orient is set to a numeric angle and then set to auto or + // auto-start-reverse, orientAngle should return a value of 0 + // 2) If orient is set to something of type other than + // SVG_MARKER_ORIENT_ANGLE and then set to a numeric angle, + // orientType should return SVG_MARKER_ORIENT_ANGLE + + // default is orient="0" + testAngleIsSet(m, 0); + + m.setOrientToAuto(); + testAutoIsSet(m); + + // testing condition 2 for an angle set using setOrientToAngle + var a = $("svg").createSVGAngle(); + a.newValueSpecifiedUnits(SVGAngle.SVG_ANGLETYPE_DEG, 90); + m.setOrientToAngle(a); + testAngleIsSet(m, a.value); + + // testing condition 1 for orient set using setOrientToAuto + m.setOrientToAuto(); + testAutoIsSet(m); + + // testing condition 2 for an angle set using setAttribute + m.setAttribute("orient", "180"); + testAngleIsSet(m, 180); + + // testing condition 1 for orient set to "auto" using setAttribute + m.setAttribute("orient", "auto"); + testAutoIsSet(m); + + m.setAttribute("orient", "270"); + + // testing condition 1 for orient set to "auto-start-reverse" using + // setAttribute + m.setAttribute("orient", "auto-start-reverse"); + testAutoStartReverseIsSet(m); + + SimpleTest.finish(); + } + + function runTestsWithPref() { + SpecialPowers.pushPrefEnv({ 'set': [['svg.marker-improvements.enabled', true]] }, run); + } + + window.addEventListener("load", runTestsWithPref, false); + + ]]> +</script> +</head> +<body> +<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=892372">Mozilla Bug 892372</a> +<p id="display"></p> +<div id="content" style="display: none"> + + <svg xmlns="http://www.w3.org/2000/svg" id="svg"> + <defs> + <marker id="m" /> + </defs> + </svg> + +</div> +<pre id="test"> +</pre> +</body> +</html> diff --git a/dom/svg/test/test_non-scaling-stroke.html b/dom/svg/test/test_non-scaling-stroke.html new file mode 100644 index 0000000000..06099bd9bf --- /dev/null +++ b/dom/svg/test/test_non-scaling-stroke.html @@ -0,0 +1,53 @@ +<!DOCTYPE HTML> +<html> +<!-- +https://bugzilla.mozilla.org/show_bug.cgi?id=829085 +--> +<head> + <title>Test for Bug 829085 - non-scaling-stroke hit testing</title> + <meta charset="utf-8"></meta> + <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <script type="text/javascript" src="/tests/SimpleTest/EventUtils.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> +</head> +<body> + <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=829085">Mozilla Bug 829085 - non-scaling-stroke hit testing</a> +<p id="display"></p> +<div id="content"> + + <svg id="svg" xmlns="http://www.w3.org/2000/svg" width="300" height="250"> + <rect width="100%" height="100%" fill="none" stroke-width="4" stroke="blue"/> + <style> + line:hover { stroke: lime; } + </style> + <g transform="scale(1.3) translate(100, -80) rotate(45)"> + <line id="line" stroke="teal" stroke-width="120px" + x1="50" y1="130" x2="200" y2="130" + vector-effect="non-scaling-stroke"></line> + </g> + </svg> + +</div> +<pre id="test"> +<script type="application/javascript"> + + function startTest() { + SimpleTest.waitForFocus(function() { + disableNonTestMouseEvents(true); + var line = document.getElementById('line'); + // Send a click + synthesizeMouseExpectEvent($("svg"), 170, 100, { }, + $("line"), "click", + "Testing mouse event on non-scaling-stroke"); + disableNonTestMouseEvents(false); + SimpleTest.finish(); + }); + } + + SimpleTest.waitForExplicitFinish(); + addLoadEvent(startTest); + +</script> +</pre> +</body> +</html> diff --git a/dom/svg/test/test_nonAnimStrings.xhtml b/dom/svg/test/test_nonAnimStrings.xhtml new file mode 100644 index 0000000000..b742318376 --- /dev/null +++ b/dom/svg/test/test_nonAnimStrings.xhtml @@ -0,0 +1,78 @@ +<html xmlns="http://www.w3.org/1999/xhtml"> +<!-- +https://bugzilla.mozilla.org/show_bug.cgi?id=589436 +--> +<head> + <title>Test for non-animated strings</title> + <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" /> +</head> +<body> +<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=589436">Mozilla Bug 589436</a> +<p id="display"></p> +<div id="content" style="display: none"> +<svg id="svg" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="120px" height="120px" + onload="this.pauseAnimations()"> + <defs> + <path id="moveFarAway" d="M300,300 h0"/> + <path id="moveToUpperLeft" d="M100,100 h0"/> + </defs> + <script id="script"> + <animate attributeName="xlink:href" from="" to="animated" dur="0.5s" begin="1s" + fill="freeze" id="animate"/> + </script> + <rect class="test" x="0" y="0" width="50" height="50"> + <animateMotion begin="1" dur="1" fill="freeze"> + <mpath id="mpath" xlink:href="#moveFarAway"> + <animate attributeName="xlink:href" from="#moveFarAway" to="#moveToUpperLeft" dur="0.5s" begin="1s" + fill="freeze"/> + </mpath> + </animateMotion> + </rect> +</svg> +</div> +<pre id="test"> +<script class="testbody" type="text/javascript"> +<![CDATA[ +/** Test some strings are not animatable **/ + +/* Global Variables */ +var svg = document.getElementById("svg"); +var script = document.getElementById('script'); +var mpath = document.getElementById('mpath'); +var animate = document.getElementById('animate'); + +SimpleTest.waitForExplicitFinish(); + +function main() { + ok(svg.animationsPaused(), "should be paused by <svg> load handler"); + is(svg.getCurrentTime(), 0, "should be paused at 0 in <svg> load handler"); + + // Sanity check: check initial values + is(script.href.baseVal, "", "Unexpected initial script baseVal"); + is(script.href.animVal, "", "Unexpected initial script animVal"); + is(mpath.href.baseVal, "#moveFarAway", "Unexpected initial mpath baseVal"); + is(mpath.href.animVal, "#moveFarAway", "Unexpected initial mpath animVal"); + + // Move to the end of the animation - should make no difference + svg.setCurrentTime(2); + + is(script.href.baseVal, "", "Unexpected value for script baseVal after animation"); + is(script.href.animVal, "", "Unexpected value for script animVal after animation"); + is(mpath.href.baseVal, "#moveFarAway", "Unexpected value for mpath baseVal after animation"); + is(mpath.href.animVal, "#moveFarAway", "Unexpected value for mpath animVal after animation"); + + SimpleTest.finish(); +} + +if (animate && animate.targetElement) { + window.addEventListener("load", main, false); +} else { + ok(true); // Skip tests but don't report 'todo' either + SimpleTest.finish(); +} +]]> +</script> +</pre> +</body> +</html> diff --git a/dom/svg/test/test_object-delayed-intrinsic-size.html b/dom/svg/test/test_object-delayed-intrinsic-size.html new file mode 100644 index 0000000000..c89029daa7 --- /dev/null +++ b/dom/svg/test/test_object-delayed-intrinsic-size.html @@ -0,0 +1,40 @@ +<!DOCTYPE html> +<!-- +https://bugzilla.mozilla.org/show_bug.cgi?id=1063073 +--> +<html> + <head> + <title>Test that <object> embedding SVG and using its intrinsic + size will resize if the <object> gets a reflow before the + root-<svg> gets its nsSVGOuterSVGFrame + </title> + <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" /> + <script> + +// This test checks for a race condition. If it fails intermittently then it +// may actually be a full failure. + +SimpleTest.waitForExplicitFinish(); + +function runTest() +{ + var object = document.querySelector("object"); + var cs = document.defaultView.getComputedStyle(object, ""); + var width = cs.getPropertyValue("width"); + is(width, "70px", "Check that the <object> size updated"); + SimpleTest.finish(); +} + + </script> + </head> + <body onload="runTest();"> + <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1063073">Mozilla Bug 1063073</a> + <p id="display"></p> + <div id="content"> + <object style="border:1px solid black" type="image/svg+xml" + data="object-delayed-intrinsic-size.sjs"></object> + </div> + </body> +</html> + diff --git a/dom/svg/test/test_onerror.xhtml b/dom/svg/test/test_onerror.xhtml new file mode 100644 index 0000000000..59c67cd860 --- /dev/null +++ b/dom/svg/test/test_onerror.xhtml @@ -0,0 +1,36 @@ +<html xmlns="http://www.w3.org/1999/xhtml"> +<!-- +https://bugzilla.mozilla.org/show_bug.cgi?id=500261 +--> +<head> + <title>Test onerror behaviour</title> + <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" /> +</head> +<body> +<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=500261">Mozilla Bug 500261</a> +<p id="display"></p> +<script class="testbody" type="text/javascript"> +<![CDATA[ + +SimpleTest.waitForExplicitFinish(); + +function run() +{ + ok(true, 'onerror method called'); + SimpleTest.finish(); +} + +]]> +</script> +<div id="content"> + + <svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="100%" height="1" id="svg"> + <image width="1" height="1" xlink:href="http://localhost/serverGone.gif" onerror="run()"/> + </svg> + +</div> +<pre id="test"> +</pre> +</body> +</html> diff --git a/dom/svg/test/test_pathAnimInterpolation.xhtml b/dom/svg/test/test_pathAnimInterpolation.xhtml new file mode 100644 index 0000000000..7a97f53855 --- /dev/null +++ b/dom/svg/test/test_pathAnimInterpolation.xhtml @@ -0,0 +1,348 @@ +<html xmlns="http://www.w3.org/1999/xhtml"> +<!-- +https://bugzilla.mozilla.org/show_bug.cgi?id=619498 +--> +<head> + <title>Test interpolation between different path segment types</title> + <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" /> +</head> +<body> +<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=619498">Mozilla Bug 619498</a> +<svg xmlns="http://www.w3.org/2000/svg" id="svg" visibility="hidden" + onload="this.pauseAnimations()"/> +<script type="application/javascript;version=1.8"><![CDATA[ +SimpleTest.waitForExplicitFinish(); + +var gSVG = document.getElementById("svg"); + +// Array of all subtests to run. This is populated by addTest. +var gTests = []; + +// Array of all path segment types. +var gTypes = "zMmLlCcQqAaHhVvSsTt".split(""); + +// Property names on the SVGPathSeg objects for the given segment type, in the +// order that they would appear in a path data string. +var gArgumentNames = { + Z: [], + M: ['x', 'y'], + L: ['x', 'y'], + C: ['x1', 'y1', 'x2', 'y2', 'x', 'y'], + Q: ['x1', 'y1', 'x', 'y'], + A: ['r1', 'r2', 'angle', 'largeArcFlag', 'sweepFlag', 'x', 'y'], + H: ['x'], + V: ['y'], + S: ['x2', 'y2', 'x', 'y'], + T: ['x', 'y'] +}; + +// All of these prefixes leave the current point at 100,100. Some of them +// affect the implied control point if followed by a smooth quadratic or +// cubic segment, but no valid interpolations depend on those control points. +var gPrefixes = [ + [1, "M100,100"], + [2, "M50,50 M100,100"], + [2, "M50,50 m50,50"], + [2, "M50,50 L100,100"], + [2, "M50,50 l50,50"], + [3, "M50,50 H100 V100"], + [3, "M50,50 h50 V100"], + [3, "M50,50 H100 v50"], + [2, "M50,50 A10,10,10,0,0,100,100"], + [2, "M50,50 a10,10,10,0,0,50,50"], + [4, "M50,50 l50,50 z m50,50"], + + // These leave the quadratic implied control point at 125,125. + [2, "M50,50 Q75,75,100,100"], + [2, "M50,50 q25,25,50,50"], + [2, "M75,75 T100,100"], + [2, "M75,75 t25,25"], + [3, "M50,50 T62.5,62.5 t37.5,37.5"], + [3, "M50,50 T62.5,62.5 T100,100"], + [3, "M50,50 t12.5,12.5 t37.5,37.5"], + [3, "M50,50 t12.5,12.5 T100,100"], + [3, "M50,50 Q50,50,62.5,62.5 t37.5,37.5"], + [3, "M50,50 Q50,50,62.5,62.5 T100,100"], + [3, "M50,50 q0,0,12.5,12.5 t37.5,37.5"], + [3, "M50,50 q0,0,12.5,12.5 T100,100"], + + // These leave the cubic implied control point at 125,125. + [2, "M50,50 C10,10,75,75,100,100"], + [2, "M50,50 c10,10,25,25,50,50"], + [2, "M50,50 S75,75,100,100"], + [2, "M50,50 s25,25,50,50"], + [3, "M50,50 S10,10,75,75 S75,75,100,100"], + [3, "M50,50 S10,10,75,75 s0,0,25,25"], + [3, "M50,50 s10,10,25,25 S75,75,100,100"], + [3, "M50,50 s10,10,25,25 s0,0,25,25"], + [3, "M50,50 C10,10,20,20,75,75 S75,75,100,100"], + [3, "M50,50 C10,10,20,20,75,75 s0,0,25,25"], + [3, "M50,50 c10,10,20,20,25,25 S75,75,100,100"], + [3, "M50,50 c10,10,20,20,25,25 s0,0,25,25"] +]; + +// These are all of the suffixes whose result is not dependent on whether the +// preceding segment types are quadratic or cubic types. Each entry is: +// +// "<fromType><toType>": [fromArguments, +// toArguments, +// expectedArguments, +// expectedArgumentsAdditive] +// +// As an example: +// +// "Mm": [[10, 20], [30, 40], [-30, -20], [-120, -100]] +// +// This will testing interpolating between "M10,20" and "m30,40". All of the +// these tests assume that the current point is left at 100,100. So the above +// entry represents two kinds of tests, one where additive and one not: +// +// <path d="... M10,20"> +// <animate attributeName="d" from="... M10,20" to="... m30,40"/> +// </path> +// +// and +// +// <path d="... M10,20"> +// <animate attributeName="d" from="... M10,20" to="... m30,40" +// additive="sum"/> +// </path> +// +// where the "..." is some prefix that leaves the current point at 100,100. +// Each of the suffixes here in gSuffixes will be paired with each of the +// prefixes in gPrefixes, all of which leave the current point at 100,100. +// (Thus the above two tests for interpolating between "M" and "m" will be +// performed many times, with different preceding commands.) +// +// The expected result of the non-additive test is "m-30,-20". Since the +// animation is from an absolute moveto to a relative moveto, we first +// convert the "M10,20" into its relative form, which is "m-90,-80" due to the +// current point being 100,100. Half way through the animation between +// "m-90,-80" and "m30,40" is thus "m-30,-20". +// +// The expected result of the additive test is "m-120,-100". We take the +// halfway value of the animation, "m-30,-20" and add it on to the underlying +// value. Since the underlying value "M10,20" is an absolute moveto, we first +// convert it to relative, "m-90,-80", and then add the "m-30,-20" to it, +// giving us the result "m-120,-100". +var gSuffixes = { + // Same path segment type, no conversion required. + MM: [[10, 20], [30, 40], [20, 30], [30, 50]], + mm: [[10, 20], [30, 40], [20, 30], [30, 50]], + LL: [[10, 20], [30, 40], [20, 30], [30, 50]], + ll: [[10, 20], [30, 40], [20, 30], [30, 50]], + CC: [[10, 20, 30, 40, 50, 60], [70, 80, 90, 100, 110, 120], + [40, 50, 60, 70, 80, 90], [50, 70, 90, 110, 130, 150]], + cc: [[10, 20, 30, 40, 50, 60], [70, 80, 90, 100, 110, 120], + [40, 50, 60, 70, 80, 90], [50, 70, 90, 110, 130, 150]], + QQ: [[10, 20, 30, 40], [50, 60, 70, 80], [30, 40, 50, 60], [40, 60, 80, 100]], + qq: [[10, 20, 30, 40], [50, 60, 70, 80], [30, 40, 50, 60], [40, 60, 80, 100]], + AA: [[10, 20, 30, 0, 0, 40, 50], [60, 70, 80, 0, 0, 90, 100], + [35, 45, 55, 0, 0, 65, 75], [45, 65, 85, 0, 0, 105, 125]], + aa: [[10, 20, 30, 0, 0, 40, 50], [60, 70, 80, 0, 0, 90, 100], + [35, 45, 55, 0, 0, 65, 75], [45, 65, 85, 0, 0, 105, 125]], + HH: [[10], [20], [15], [25]], + hh: [[10], [20], [15], [25]], + VV: [[10], [20], [15], [25]], + vv: [[10], [20], [15], [25]], + SS: [[10, 20, 30, 40], [50, 60, 70, 80], [30, 40, 50, 60], [40, 60, 80, 100]], + ss: [[10, 20, 30, 40], [50, 60, 70, 80], [30, 40, 50, 60], [40, 60, 80, 100]], + TT: [[10, 20], [30, 40], [20, 30], [30, 50]], + tt: [[10, 20], [30, 40], [20, 30], [30, 50]], + + // Relative <-> absolute conversion. + Mm: [[10, 20], [30, 40], [-30, -20], [-120, -100]], + mM: [[10, 20], [30, 40], [70, 80], [180, 200]], + Ll: [[10, 20], [30, 40], [-30, -20], [-120, -100]], + lL: [[10, 20], [30, 40], [70, 80], [180, 200]], + Cc: [[10, 20, 30, 40, 50, 60], [70, 80, 90, 100, 110, 120], + [-10, 0, 10, 20, 30, 40], [-100, -80, -60, -40, -20, 0]], + cC: [[10, 20, 30, 40, 50, 60], [70, 80, 90, 100, 110, 120], + [90, 100, 110, 120, 130, 140], [200, 220, 240, 260, 280, 300]], + Qq: [[10, 20, 30, 40], [50, 60, 70, 80], + [-20, -10, 0, 10], [-110, -90, -70, -50]], + qQ: [[10, 20, 30, 40], [50, 60, 70, 80], + [80, 90, 100, 110], [190, 210, 230, 250]], + Aa: [[10, 20, 30, 0, 0, 40, 50], [60, 70, 80, 0, 0, 90, 100], + [35, 45, 55, 0, 0, 15, 25], [45, 65, 85, 0, 0, -45, -25]], + aA: [[10, 20, 30, 0, 0, 40, 50], [60, 70, 80, 0, 0, 90, 100], + [35, 45, 55, 0, 0, 115, 125], [45, 65, 85, 0, 0, 255, 275]], + Hh: [[10], [20], [-35], [-125]], + hH: [[10], [20], [65], [175]], + Vv: [[10], [20], [-35], [-125]], + vV: [[10], [20], [65], [175]], + Tt: [[10, 20], [30, 40], [-30,-20], [-120, -100]], + tT: [[10, 20], [30, 40], [70, 80], [180, 200]], + Ss: [[10, 20, 30, 40], [50, 60, 70, 80], + [-20, -10, 0, 10], [-110, -90, -70, -50]], + sS: [[10, 20, 30, 40], [50, 60, 70, 80], + [80, 90, 100, 110], [190, 210, 230, 250]] +}; + +// Returns an array of property names that exist on an SVGPathSeg object +// corresponding to the given segment type, in the order that they would +// be present in a path data string. +function argumentNames(aType) +{ + return gArgumentNames[aType.toUpperCase()]; +} + +// Creates and returns a new element and sets some attributes on it. +function newElement(aNamespaceURI, aLocalName, aAttributes) +{ + var e = document.createElementNS(aNamespaceURI, aLocalName); + if (aAttributes) { + for (let [name, value] of Object.entries(aAttributes)) { + e.setAttribute(name, value); + } + } + return e; +} + +// Creates and returns a new SVG element and sets some attributes on it. +function newSVGElement(aLocalName, aAttributes) +{ + return newElement("http://www.w3.org/2000/svg", aLocalName, aAttributes); +} + +// Creates a subtest and adds it to the document. +// +// * aPrefixLength/aPrefix the prefix to use +// * aFromType/aFromArguments the segment to interpolate from +// * aToType/aToArguments the segment to interpolate to +// * aExpectedType/aExpectedArguments the expected result of the interpolated +// segment half way through the animation +// duration +// * aAdditive whether the subtest is for an additive +// animation +function addTest(aPrefixLength, aPrefix, aFromType, aFromArguments, + aToType, aToArguments, aExpectedType, aExpectedArguments, + aAdditive) +{ + var fromPath = aPrefix + aFromType + aFromArguments, + toPath = aPrefix + aToType + aToArguments; + + var path = newSVGElement("path", { d: fromPath }); + var animate = + newSVGElement("animate", { attributeName: "d", + from: fromPath, + to: toPath, + dur: "8s", + additive: aAdditive ? "sum" : "replace" }); + path.appendChild(animate); + gSVG.appendChild(path); + + gTests.push({ element: path, + prefixLength: aPrefixLength, + from: fromPath, + to: toPath, + toType: aToType, + expectedType: aExpectedType, + expected: aExpectedArguments, + usesAddition: aAdditive }); +} + +// Generates an array of path segment arguments for the given type. aOffset +// is a number to add on to all non-Boolean segment arguments. +function generatePathSegmentArguments(aType, aOffset) +{ + var args = new Array(argumentNames(aType).length); + for (let i = 0; i < args.length; i++) { + args[i] = i * 10 + aOffset; + } + if (aType == "A" || aType == "a") { + args[3] = 0; + args[4] = 0; + } + return args; +} + +// Returns whether interpolating between the two given types is valid. +function isValidInterpolation(aFromType, aToType) +{ + return aFromType.toUpperCase() == aToType.toUpperCase(); +} + +// Runs the test. +function run() +{ + for (let additive of [false, true]) { + let indexOfExpectedArguments = additive ? 3 : 2; + + // Add subtests for each combination of prefix and suffix, and additive + // or not. + for (let [typePair, suffixEntry] of Object.entries(gSuffixes)) { + let fromType = typePair[0], + toType = typePair[1], + fromArguments = suffixEntry[0], + toArguments = suffixEntry[1], + expectedArguments = suffixEntry[indexOfExpectedArguments]; + + for (let prefixEntry of gPrefixes) { + let [prefixLength, prefix] = prefixEntry; + addTest(prefixLength, prefix, fromType, fromArguments, + toType, toArguments, toType, expectedArguments, additive); + } + } + + // Test that differences in arc flag parameters cause the + // interpolation/addition not to occur. + addTest(1, "M100,100", + "A", [10, 20, 30, 0, 0, 40, 50], + "a", [60, 70, 80, 0, 1, 90, 100], + "a", [60, 70, 80, 0, 1, 90, 100], additive); + addTest(1, "M100,100", + "A", [10, 20, 30, 0, 0, 40, 50], + "a", [60, 70, 80, 1, 0, 90, 100], + "a", [60, 70, 80, 1, 0, 90, 100], additive); + + // Test all pairs of segment types that cannot be interpolated between. + for (let fromType of gTypes) { + let fromArguments = generatePathSegmentArguments(fromType, 0); + for (let toType of gTypes) { + if (!isValidInterpolation(fromType, toType)) { + let toArguments = generatePathSegmentArguments(toType, 1000); + addTest(1, "M100,100", fromType, fromArguments, + toType, toArguments, toType, toArguments, additive); + } + } + } + } + + // Move the document time to half way through the animations. + gSVG.setCurrentTime(4); + + // Inspect the results of each subtest. + for (let test of gTests) { + let list = test.element.animatedPathSegList; + is(list.numberOfItems, test.prefixLength + 1, + "Length of animatedPathSegList for interpolation " + + (test.usesAddition ? "with addition " : "") + + " from " + test.from + " to " + test.to); + + let seg = list.getItem(list.numberOfItems - 1); + let propertyNames = argumentNames(test.expectedType); + + let actual = []; + for (let i = 0; i < test.expected.length; i++) { + actual.push(+seg[propertyNames[i]]); + } + + is(seg.pathSegTypeAsLetter + actual, test.expectedType + test.expected, + "Path segment for interpolation " + + (test.usesAddition ? "with addition " : "") + + " from " + test.from + " to " + test.to); + } + + // Clear all the tests. We have tons of them attached to the DOM and refresh driver tick will + // go through them all by calling animation controller. + gSVG.remove(); + + SimpleTest.finish(); +} + +window.addEventListener("load", run, false); +]]></script> +</body> +</html> diff --git a/dom/svg/test/test_pathLength.html b/dom/svg/test/test_pathLength.html new file mode 100644 index 0000000000..499343fa33 --- /dev/null +++ b/dom/svg/test/test_pathLength.html @@ -0,0 +1,52 @@ +<!doctype html> +<html> +<!-- +https://bugzilla.mozilla.org/show_bug.cgi?id=1024926 +--> +<head> + <meta charset="utf-8"> + <title>Test path length changes when manipulated</title> + <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" /> +</head> +<body> +<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1024926">Mozilla Bug 1024926</a> +<p id="display"></p> +<div id="content" style="display: none"> + <svg width="100%" height="1" id="svg"> + <path id="path_lines" d="M50,100l0,0l0,-50l100,0l86.3325,122.665z"></path> + <path id="path_straight_curve" d="M0,0 C100,0 150,0 200,0" /> + </svg> +</div> +<pre id="test"> +<script class="testbody" type="text/javascript"> + + SimpleTest.waitForExplicitFinish(); + + // Test a closed path with a series of lines. + var path = document.getElementById("path_lines"); + is(path.getTotalLength(), 500, "Unexpected path length"); + + // Test a path that's been shortened via the DOM. + for (var i = 0; i < 2; i++) { + path.pathSegList.removeItem(path.pathSegList.numberOfItems - 1); + } + is(path.getTotalLength(), 150, "Unexpected path length"); + + // Test a path that's been shortened to be empty, via the DOM. + while (path.pathSegList.numberOfItems > 0) { + path.pathSegList.removeItem(0); + } + is(path.getTotalLength(), 0, "Unexpected path length"); + + // Test a path with a curve command ("C") that is really a straight line. + path = document.getElementById("path_straight_curve"); + is(path.getTotalLength(), 200, "Unexpected path length, for straight line " + + "generated by 'C' command"); + + SimpleTest.finish(); + +</script> +</pre> +</body> +</html> diff --git a/dom/svg/test/test_pathSeg.xhtml b/dom/svg/test/test_pathSeg.xhtml new file mode 100644 index 0000000000..698d078f43 --- /dev/null +++ b/dom/svg/test/test_pathSeg.xhtml @@ -0,0 +1,142 @@ +<!DOCTYPE html> +<html xmlns="http://www.w3.org/1999/xhtml"> +<!-- +https://bugzilla.mozilla.org/show_bug.cgi?id=459953 +--> +<head> + <title>Test for Bug 459953</title> + <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" /> +</head> +<body> +<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=459953">Mozilla Bug 459953</a> +<p id="display"></p> + +<pre id="test"> +<script class="testbody" type="application/javascript"> +<![CDATA[ +SimpleTest.waitForExplicitFinish(); + +function runTest() +{ + var svgns="http://www.w3.org/2000/svg"; + + var path1=document.createElementNS(svgns, "path"); + + var sseg; + + var a=10,s=20,d=30,z=9; //Arbitrary numbers for arguments + + var whatever=true; //This is often so, but here it does not matter + + sseg=path1.createSVGPathSegMovetoAbs(a, s); + path1.pathSegList.appendItem(sseg); + sseg=path1.createSVGPathSegMovetoRel(a, s); + path1.pathSegList.appendItem(sseg); + sseg=path1.createSVGPathSegLinetoAbs(a, s); + path1.pathSegList.appendItem(sseg); + sseg=path1.createSVGPathSegLinetoRel(a, s); + path1.pathSegList.appendItem(sseg); + sseg=path1.createSVGPathSegLinetoVerticalAbs(a); + path1.pathSegList.appendItem(sseg); + sseg=path1.createSVGPathSegLinetoVerticalRel(a); + path1.pathSegList.appendItem(sseg); + sseg=path1.createSVGPathSegLinetoHorizontalAbs(a); + path1.pathSegList.appendItem(sseg); + sseg=path1.createSVGPathSegLinetoHorizontalRel(a); + path1.pathSegList.appendItem(sseg); + sseg=path1.createSVGPathSegCurvetoCubicAbs(a, s, d, z, a, s); + path1.pathSegList.appendItem(sseg); + sseg=path1.createSVGPathSegCurvetoCubicRel(a, s, d, z, a, s); + path1.pathSegList.appendItem(sseg); + sseg=path1.createSVGPathSegCurvetoCubicSmoothAbs(a, s, d, z); + path1.pathSegList.appendItem(sseg); + sseg=path1.createSVGPathSegCurvetoCubicSmoothRel(a, s, d, z); + path1.pathSegList.appendItem(sseg); + sseg=path1.createSVGPathSegCurvetoQuadraticAbs(a, s, d, z); + path1.pathSegList.appendItem(sseg); + sseg=path1.createSVGPathSegCurvetoQuadraticRel(a, s, d, z); + path1.pathSegList.appendItem(sseg); + sseg=path1.createSVGPathSegCurvetoQuadraticSmoothAbs(a, s); + path1.pathSegList.appendItem(sseg); + sseg=path1.createSVGPathSegCurvetoQuadraticSmoothRel(a, s); + path1.pathSegList.appendItem(sseg); + sseg=path1.createSVGPathSegArcAbs(a, s, d, z, a, whatever, whatever); + path1.pathSegList.appendItem(sseg); + sseg=path1.createSVGPathSegArcRel(a, s, d, z, a, whatever, whatever); + path1.pathSegList.appendItem(sseg); + sseg=path1.createSVGPathSegClosePath(); + path1.pathSegList.appendItem(sseg); + + for(var i=0;i<path1.pathSegList.numberOfItems;i++){ + var seg=path1.pathSegList.getItem(i); + switch(seg.pathSegType){ + case seg.PATHSEG_MOVETO_ABS: + is(seg.pathSegTypeAsLetter, "M", "wrong path segment letter"); + break; + case seg.PATHSEG_MOVETO_REL: + is(seg.pathSegTypeAsLetter, "m", "wrong path segment letter"); + break; + case seg.PATHSEG_CLOSEPATH: + is(seg.pathSegTypeAsLetter, "z", "wrong path segment letter"); + break; + case seg.PATHSEG_LINETO_ABS: + is(seg.pathSegTypeAsLetter, "L", "wrong path segment letter"); + break; + case seg.PATHSEG_LINETO_REL: + is(seg.pathSegTypeAsLetter, "l", "wrong path segment letter"); + break; + case seg.PATHSEG_LINETO_VERTICAL_ABS: + is(seg.pathSegTypeAsLetter, "V", "wrong path segment letter"); + break; + case seg.PATHSEG_LINETO_VERTICAL_REL: + is(seg.pathSegTypeAsLetter, "v", "wrong path segment letter"); + break; + case seg.PATHSEG_LINETO_HORIZONTAL_ABS: + is(seg.pathSegTypeAsLetter, "H", "wrong path segment letter"); + break; + case seg.PATHSEG_LINETO_HORIZONTAL_REL: + is(seg.pathSegTypeAsLetter, "h", "wrong path segment letter"); + break; + case seg.PATHSEG_CURVETO_CUBIC_ABS: + is(seg.pathSegTypeAsLetter, "C", "wrong path segment letter"); + break; + case seg.PATHSEG_CURVETO_CUBIC_REL: + is(seg.pathSegTypeAsLetter, "c", "wrong path segment letter"); + break; + case seg.PATHSEG_CURVETO_CUBIC_SMOOTH_ABS: + is(seg.pathSegTypeAsLetter, "S", "wrong path segment letter"); + break; + case seg.PATHSEG_CURVETO_CUBIC_SMOOTH_REL: + is(seg.pathSegTypeAsLetter, "s", "wrong path segment letter"); + break; + case seg.PATHSEG_CURVETO_QUADRATIC_ABS: + is(seg.pathSegTypeAsLetter, "Q", "wrong path segment letter"); + break; + case seg.PATHSEG_CURVETO_QUADRATIC_REL: + is(seg.pathSegTypeAsLetter, "q", "wrong path segment letter"); + break; + case seg.PATHSEG_CURVETO_QUADRATIC_SMOOTH_ABS: + is(seg.pathSegTypeAsLetter, "T", "wrong path segment letter"); + break; + case seg.PATHSEG_CURVETO_QUADRATIC_SMOOTH_REL: + is(seg.pathSegTypeAsLetter, "t", "wrong path segment letter"); + break; + case seg.PATHSEG_ARC_ABS: + is(seg.pathSegTypeAsLetter, "A", "wrong path segment letter"); + break; + case seg.PATHSEG_ARC_REL: + is(seg.pathSegTypeAsLetter, "a", "wrong path segment letter"); + break; + + } + } + SimpleTest.finish(); +} + +window.addEventListener("load", runTest, false); +]]> +</script> +</pre> +</body> +</html> diff --git a/dom/svg/test/test_pointAtLength.xhtml b/dom/svg/test/test_pointAtLength.xhtml new file mode 100644 index 0000000000..c02b02dc8b --- /dev/null +++ b/dom/svg/test/test_pointAtLength.xhtml @@ -0,0 +1,49 @@ +<html xmlns="http://www.w3.org/1999/xhtml"> +<!-- +https://bugzilla.mozilla.org/show_bug.cgi?id=643419 +--> +<head> + <title>Test getPointAtLength</title> + <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" /> +</head> +<body> +<script class="testbody" type="text/javascript"> +<![CDATA[ + +SimpleTest.waitForExplicitFinish(); + +function run() +{ + var p1 = document.getElementById("p1"); + var point = p1.getPointAtLength(200); + is(point.x, 200); + is(point.y, 50); + + // set the pathLength to twice its actual length + p1.setAttribute("pathLength", "800"); + var point = p1.getPointAtLength(200); + is(point.x, 100); + is(point.y, 50); + + SimpleTest.finish(); +} + +window.addEventListener("load", run, false); +]]> +</script> +<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=643419">Mozilla Bug 643419</a> +<p id="display"></p> +<div id="content"> + + <svg xmlns="http://www.w3.org/2000/svg" width="750"> + <defs> + <path id="p1" d="M 0 50 h 400"/> + </defs> + </svg> + +</div> +<pre id="test"> +</pre> +</body> +</html> diff --git a/dom/svg/test/test_pointer-events-1a.xhtml b/dom/svg/test/test_pointer-events-1a.xhtml new file mode 100644 index 0000000000..311efc4338 --- /dev/null +++ b/dom/svg/test/test_pointer-events-1a.xhtml @@ -0,0 +1,27 @@ +<html xmlns="http://www.w3.org/1999/xhtml"> +<!-- +https://bugzilla.mozilla.org/show_bug.cgi?id=619959 +--> +<head> + <title>Test 'pointer-events' handling</title> + <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" /> +</head> +<body onload="run_tests(0)"> +<script class="testbody" src="pointer-events.js"></script> +<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=619959">Mozilla Bug 619959</a> +<p id="display"></p> +<div id="content"> + + <div width="100%" height="1" id="div"></div> + + <svg xmlns="http://www.w3.org/2000/svg" id="svg"> + <rect id="rect" x="10" y="10" width="40" height="40" stroke-width="20"/> + <text id="text" x="190" y="50" font-size="40px" stroke-width="20">X</text> + </svg> + +</div> +<pre id="test"> +</pre> +</body> +</html> diff --git a/dom/svg/test/test_pointer-events-1b.xhtml b/dom/svg/test/test_pointer-events-1b.xhtml new file mode 100644 index 0000000000..4edb27ae2b --- /dev/null +++ b/dom/svg/test/test_pointer-events-1b.xhtml @@ -0,0 +1,27 @@ +<html xmlns="http://www.w3.org/1999/xhtml"> +<!-- +https://bugzilla.mozilla.org/show_bug.cgi?id=619959 +--> +<head> + <title>Test 'pointer-events' handling</title> + <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" /> +</head> +<body onload="run_tests(1)"> +<script class="testbody" src="pointer-events.js"></script> +<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=619959">Mozilla Bug 619959</a> +<p id="display"></p> +<div id="content"> + + <div width="100%" height="1" id="div"></div> + + <svg xmlns="http://www.w3.org/2000/svg" id="svg"> + <rect id="rect" x="10" y="10" width="40" height="40" stroke-width="20"/> + <text id="text" x="190" y="50" font-size="40px" stroke-width="20">X</text> + </svg> + +</div> +<pre id="test"> +</pre> +</body> +</html> diff --git a/dom/svg/test/test_pointer-events-2.xhtml b/dom/svg/test/test_pointer-events-2.xhtml new file mode 100644 index 0000000000..afe63b3f0c --- /dev/null +++ b/dom/svg/test/test_pointer-events-2.xhtml @@ -0,0 +1,66 @@ +<html xmlns="http://www.w3.org/1999/xhtml"> +<!-- +https://bugzilla.mozilla.org/show_bug.cgi?id=500174 +--> +<head> + <title>Test Pointer Events</title> + <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" /> +</head> +<body onload="run()"> +<script class="testbody" type="text/javascript"> +<![CDATA[ + +SimpleTest.waitForExplicitFinish(); + +function run() +{ + var svgDoc = document.getElementById('svg'); + var div = document.getElementById("div"); + // Get the coords of the origin of the SVG canvas: + var originX = div.offsetLeft; + var originY = div.offsetTop; + var circle = document.getElementById('circle'); + var path = document.getElementById('path'); + + var elementFromPoint = document.elementFromPoint(originX + 55, originY + 55); + is(elementFromPoint, circle, 'Over circle stroke with pointer-events="all"'); + + elementFromPoint = document.elementFromPoint(originX + 205, originY + 55); + is(elementFromPoint, circle, 'Over foreignObject, outside clip path'); + + elementFromPoint = document.elementFromPoint(originX + 225, originY + 75); + // XXX disabled. See See https://bugzilla.mozilla.org/show_bug.cgi?id=580983#c116 + //is(elementFromPoint, path, 'Over foreignObject, inside clip path'); + + SimpleTest.finish(); +} + +]]> +</script> +<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=500174">Mozilla Bug 500174</a> +<p id="display"></p> +<div id="content"> + + <div width="100%" height="1" id="div"> + </div> + <svg xmlns="http://www.w3.org/2000/svg" width="100%" height="100%" id="svg"> + <defs> + <clipPath id="clip"> + <rect x="20" y="20" width="30" height="30"/> + </clipPath> + </defs> + <rect id="bad" width="100%" height="100%" fill="blue"/> + <circle id="circle" cx="50%" cy="50%" r="500" stroke-width="500" fill="none" pointer-events="all"/> + <foreignObject id="fo" x="200" y="50" width="50" height="50" clip-path="url(#clip)"> + <svg> + <path id="path" d="M0,0 H50 V50 H0 Z" fill="green"/> + </svg> + </foreignObject> + </svg> + +</div> +<pre id="test"> +</pre> +</body> +</html> diff --git a/dom/svg/test/test_pointer-events-3.xhtml b/dom/svg/test/test_pointer-events-3.xhtml new file mode 100644 index 0000000000..7981002237 --- /dev/null +++ b/dom/svg/test/test_pointer-events-3.xhtml @@ -0,0 +1,56 @@ +<html xmlns="http://www.w3.org/1999/xhtml"> +<!-- +https://bugzilla.mozilla.org/show_bug.cgi?id=762679 +--> +<head> + <title>Test pointer events for small objects scaled up</title> + <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" /> +</head> +<body onload="run()"> +<script class="testbody" type="text/javascript"> +<![CDATA[ + +SimpleTest.waitForExplicitFinish(); + +function run() +{ + var svgDoc = document.getElementById("svg"); + var div = document.getElementById("div"); + // Get the coords of the origin of the SVG canvas: + var originX = div.offsetLeft; + var originY = div.offsetTop; + var circle = document.getElementById("circle"); + + var elementFromPoint = document.elementFromPoint(originX + 150, originY + 52); + is(elementFromPoint, circle, "Top of circle should hit"); + + var elementFromPoint = document.elementFromPoint(originX + 249, originY + 150); + is(elementFromPoint, circle, "Right of circle should hit"); + + var elementFromPoint = document.elementFromPoint(originX + 150, originY + 249); + is(elementFromPoint, circle, "Bottom of circle should hit"); + + var elementFromPoint = document.elementFromPoint(originX + 51, originY + 150); + is(elementFromPoint, circle, "Left of circle should hit"); + + SimpleTest.finish(); +} + +]]> +</script> +<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=762679">Mozilla Bug 762679</a> +<p id="display"></p> +<div id="content"> + + <div width="100%" height="1" id="div"> + <svg xmlns="http://www.w3.org/2000/svg" width="100%" height="400" id="svg"> + <circle id="circle" cx="1.5" cy="1.5" r="1" transform="scale(100, 100)"/> + </svg> + </div> + +</div> +<pre id="test"> +</pre> +</body> +</html> diff --git a/dom/svg/test/test_pointer-events-4.xhtml b/dom/svg/test/test_pointer-events-4.xhtml new file mode 100644 index 0000000000..bab3baae83 --- /dev/null +++ b/dom/svg/test/test_pointer-events-4.xhtml @@ -0,0 +1,110 @@ +<html xmlns="http://www.w3.org/1999/xhtml"> +<!-- +https://bugzilla.mozilla.org/show_bug.cgi?id=820506 +--> +<head> + <title>Test pointer events with clipPath</title> + <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" /> +</head> +<body onload="run()"> +<script class="testbody" type="text/javascript"> +<![CDATA[ + +SimpleTest.waitForExplicitFinish(); + +function run() +{ + var svgDoc = document.getElementById('svg'); + var div = document.getElementById("div"); + // Get the coords of the origin of the SVG canvas: + var originX = div.offsetLeft; + var originY = div.offsetTop; + var r1 = document.getElementById('r1'); + var r2 = document.getElementById('r2'); + var element; + + // Test r1 just outsite the clip area: + + element = document.elementFromPoint(originX + 19, originY + 19); + is(element, background, 'Should not hit top-left of r1'); + + element = document.elementFromPoint(originX + 101, originY + 19); + is(element, background, 'Should not hit top-right of r1'); + + element = document.elementFromPoint(originX + 101, originY + 101); + is(element, background, 'Should not hit bottom-right of r1'); + + element = document.elementFromPoint(originX + 19, originY + 101); + is(element, background, 'Should not hit bottom-left of r1'); + + // Test r1 just inside the clip area: + + element = document.elementFromPoint(originX + 21, originY + 21); + is(element, r1, 'Should hit top-left of r1'); + + element = document.elementFromPoint(originX + 99, originY + 21); + is(element, r1, 'Should hit top-right of r1'); + + element = document.elementFromPoint(originX + 99, originY + 99); + is(element, r1, 'Should hit bottom-right of r1'); + + element = document.elementFromPoint(originX + 21, originY + 99); + is(element, r1, 'Should hit bottom-left of r1'); + + // Test r2 just outsite the clip area: + + element = document.elementFromPoint(originX + 109, originY + 19); + is(element, background, 'Should not hit top-left of r2'); + + element = document.elementFromPoint(originX + 201, originY + 19); + is(element, background, 'Should not hit top-right of r2'); + + element = document.elementFromPoint(originX + 201, originY + 101); + is(element, background, 'Should not hit bottom-right of r2'); + + element = document.elementFromPoint(originX + 109, originY + 101); + is(element, background, 'Should not hit bottom-left of r2'); + + // Test r2 just inside the clip area: + + element = document.elementFromPoint(originX + 121, originY + 21); + is(element, r2, 'Should hit top-left of r2'); + + element = document.elementFromPoint(originX + 199, originY + 21); + is(element, r2, 'Should hit top-right of r2'); + + element = document.elementFromPoint(originX + 199, originY + 99); + is(element, r2, 'Should hit bottom-right of r2'); + + element = document.elementFromPoint(originX + 121, originY + 99); + is(element, r2, 'Should hit bottom-left of r2'); + + SimpleTest.finish(); +} + +]]> +</script> +<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=500174">Mozilla Bug 500174</a> +<p id="display"></p> +<div id="content"> + + <div width="100%" height="1" id="div"> + </div> + <svg xmlns="http://www.w3.org/2000/svg" width="100%" height="100%" id="svg"> + <clipPath id="cp1" clipPathUnits="userSpaceOnUse"> + <rect x="20" y="20" width="80" height="80"/> + </clipPath> + <clipPath id="cp2" clipPathUnits="objectBoundingBox"> + <rect x="0.1" y="0.1" width="0.8" height="0.8"/> + </clipPath> + <rect id="background" width="100%" height="100%" fill="blue"/> + <rect id="r1" x="10" y="10" width="100" height="100" clip-path="url(#cp1)"/> + <rect id="r2" x="110" y="10" width="100" height="100" clip-path="url(#cp2)"/> + </svg> + +</div> +<pre id="test"> +</pre> +</body> +</html> diff --git a/dom/svg/test/test_pointer-events-5.xhtml b/dom/svg/test/test_pointer-events-5.xhtml new file mode 100644 index 0000000000..afe63b3f0c --- /dev/null +++ b/dom/svg/test/test_pointer-events-5.xhtml @@ -0,0 +1,66 @@ +<html xmlns="http://www.w3.org/1999/xhtml"> +<!-- +https://bugzilla.mozilla.org/show_bug.cgi?id=500174 +--> +<head> + <title>Test Pointer Events</title> + <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" /> +</head> +<body onload="run()"> +<script class="testbody" type="text/javascript"> +<![CDATA[ + +SimpleTest.waitForExplicitFinish(); + +function run() +{ + var svgDoc = document.getElementById('svg'); + var div = document.getElementById("div"); + // Get the coords of the origin of the SVG canvas: + var originX = div.offsetLeft; + var originY = div.offsetTop; + var circle = document.getElementById('circle'); + var path = document.getElementById('path'); + + var elementFromPoint = document.elementFromPoint(originX + 55, originY + 55); + is(elementFromPoint, circle, 'Over circle stroke with pointer-events="all"'); + + elementFromPoint = document.elementFromPoint(originX + 205, originY + 55); + is(elementFromPoint, circle, 'Over foreignObject, outside clip path'); + + elementFromPoint = document.elementFromPoint(originX + 225, originY + 75); + // XXX disabled. See See https://bugzilla.mozilla.org/show_bug.cgi?id=580983#c116 + //is(elementFromPoint, path, 'Over foreignObject, inside clip path'); + + SimpleTest.finish(); +} + +]]> +</script> +<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=500174">Mozilla Bug 500174</a> +<p id="display"></p> +<div id="content"> + + <div width="100%" height="1" id="div"> + </div> + <svg xmlns="http://www.w3.org/2000/svg" width="100%" height="100%" id="svg"> + <defs> + <clipPath id="clip"> + <rect x="20" y="20" width="30" height="30"/> + </clipPath> + </defs> + <rect id="bad" width="100%" height="100%" fill="blue"/> + <circle id="circle" cx="50%" cy="50%" r="500" stroke-width="500" fill="none" pointer-events="all"/> + <foreignObject id="fo" x="200" y="50" width="50" height="50" clip-path="url(#clip)"> + <svg> + <path id="path" d="M0,0 H50 V50 H0 Z" fill="green"/> + </svg> + </foreignObject> + </svg> + +</div> +<pre id="test"> +</pre> +</body> +</html> diff --git a/dom/svg/test/test_pointer-events-6.xhtml b/dom/svg/test/test_pointer-events-6.xhtml new file mode 100644 index 0000000000..7ec590d09c --- /dev/null +++ b/dom/svg/test/test_pointer-events-6.xhtml @@ -0,0 +1,70 @@ +<html xmlns="http://www.w3.org/1999/xhtml"> +<!-- +https://bugzilla.mozilla.org/show_bug.cgi?id=500174 +--> +<head> + <title>Test pointer events for clip-path on non-SVG elements</title> + <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" /> +</head> +<body onload="run()"> +<script class="testbody" type="text/javascript"> +<![CDATA[ + +SimpleTest.waitForExplicitFinish(); + +function run() +{ + var div = document.getElementById("div"); + // Get the coords of the origin of the SVG canvas: + var originX = div.offsetLeft; + var originY = div.offsetTop; + var elementFromPoint; + + elementFromPoint = document.elementFromPoint(originX + 18, originY + 30); + isnot(elementFromPoint, div, 'Outside left edge of clipped area'); + + elementFromPoint = document.elementFromPoint(originX + 22, originY + 30); + is(elementFromPoint, div, 'Inside left edge of clipped area'); + + elementFromPoint = document.elementFromPoint(originX + 30, originY + 18); + isnot(elementFromPoint, div, 'Outside top edge of clipped area'); + + elementFromPoint = document.elementFromPoint(originX + 30, originY + 22); + is(elementFromPoint, div, 'Inside top edge of clipped area'); + + elementFromPoint = document.elementFromPoint(originX + 42, originY + 30); + isnot(elementFromPoint, div, 'Outside right edge of clipped area'); + + elementFromPoint = document.elementFromPoint(originX + 38, originY + 30); + is(elementFromPoint, div, 'Inside right edge of clipped area'); + + elementFromPoint = document.elementFromPoint(originX + 30, originY + 42); + isnot(elementFromPoint, div, 'Outside bottom edge of clipped area'); + + elementFromPoint = document.elementFromPoint(originX + 30, originY + 38); + is(elementFromPoint, div, 'Inside bottom edge of clipped area'); + + SimpleTest.finish(); +} + +]]> +</script> +<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=500174">Mozilla Bug 500174</a> +<p id="display"></p> +<div id="content"> + + <svg xmlns="http://www.w3.org/2000/svg" width="100%" height="100%" id="svg"> + <defs> + <clipPath id="clip"> + <rect x="20" y="20" width="20" height="20"/> + </clipPath> + </defs> + </svg> + <div id="div" style="width:100px; height:100px; clip-path:url(#clip)"></div> + +</div> +<pre id="test"> +</pre> +</body> +</html> diff --git a/dom/svg/test/test_pointer-events-7.xhtml b/dom/svg/test/test_pointer-events-7.xhtml new file mode 100644 index 0000000000..3ac95cb303 --- /dev/null +++ b/dom/svg/test/test_pointer-events-7.xhtml @@ -0,0 +1,66 @@ +<html xmlns="http://www.w3.org/1999/xhtml"> +<!-- +https://bugzilla.mozilla.org/show_bug.cgi?id=1119698 +--> +<head> + <title>Test pointer events with image</title> + <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" /> +</head> +<body onload="run()"> +<script class="testbody" type="text/javascript"> +<![CDATA[ + +SimpleTest.waitForExplicitFinish(); + +function run() +{ + var svgDoc = document.getElementById('svg'); + var div = document.getElementById("div"); + // Get the coords of the origin of the SVG canvas: + var originX = div.offsetLeft; + var originY = div.offsetTop; + var image4 = document.getElementById('image4'); + var image5 = document.getElementById('image5'); + var element; + + element = document.elementFromPoint(originX + 20, originY + 20); + is(element, background, 'Should not hit visibility:hidden image by default'); + + element = document.elementFromPoint(originX + 120, originY + 20); + is(element, background, 'Should not hit pointer-events:none image'); + + element = document.elementFromPoint(originX + 220, originY + 20); + is(element, background, 'Should not hit pointer-events:visible visibility:hidden image'); + + element = document.elementFromPoint(originX + 320, originY + 20); + is(element, image4, 'Should hit pointer-events:painted visibility:hidden image'); + + element = document.elementFromPoint(originX + 420, originY + 20); + is(element, image5, 'Should hit pointer-events:stroke visibility:hidden image'); + + SimpleTest.finish(); +} + +]]> +</script> +<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1119698">Mozilla Bug 1119698</a> +<p id="display"></p> +<div id="content"> + + <div width="100%" height="1" id="div"> + </div> + <svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="100%" height="100%" id="svg"> + <rect id="background" width="100%" height="100%" fill="blue"/> + <image x="10" y="10" width="100" height="100" visibility="hidden" xlink:href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAIAAACQd1PeAAAAAXNSR0IArs4c6QAAAAxJREFUCNdj+M/AAAADAQEAGN2NsAAAAABJRU5ErkJggg"/> + <image x="110" y="10" width="100" height="100" pointer-events="none" xlink:href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAIAAACQd1PeAAAAAXNSR0IArs4c6QAAAAxJREFUCNdj+M/AAAADAQEAGN2NsAAAAABJRU5ErkJggg"/> + <image x="210" y="10" width="100" height="100" pointer-events="visible" visibility="hidden" xlink:href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAIAAACQd1PeAAAAAXNSR0IArs4c6QAAAAxJREFUCNdj+M/AAAADAQEAGN2NsAAAAABJRU5ErkJggg"/> + <image id="image4" x="310" y="10" width="100" height="100" pointer-events="painted" visibility="hidden" xlink:href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAIAAACQd1PeAAAAAXNSR0IArs4c6QAAAAxJREFUCNdj+M/AAAADAQEAGN2NsAAAAABJRU5ErkJggg"/> + <image id="image5" x="410" y="10" width="100" height="100" pointer-events="stroke" visibility="hidden" xlink:href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAIAAACQd1PeAAAAAXNSR0IArs4c6QAAAAxJREFUCNdj+M/AAAADAQEAGN2NsAAAAABJRU5ErkJggg"/> + </svg> + +</div> +<pre id="test"> +</pre> +</body> +</html> diff --git a/dom/svg/test/test_scientific.html b/dom/svg/test/test_scientific.html new file mode 100644 index 0000000000..482738514c --- /dev/null +++ b/dom/svg/test/test_scientific.html @@ -0,0 +1,84 @@ +<!DOCTYPE html> +<html xmlns="http://www.w3.org/1999/xhtml"> +<!-- +https://bugzilla.mozilla.org/show_bug.cgi?id=302971 +--> +<head> + <title>Test for Bug 302971</title> + <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" /> +</head> +<body> +<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=302971">Mozilla Bug 302971</a> +<p id="display"></p> +<div id="content" style="display: none"></div> + +<iframe id="svg" src="scientific-helper.svg"></iframe> + +<pre id="test"> +<script class="testbody" type="application/javascript"> + SimpleTest.waitForExplicitFinish(); + + function runTests() + { + var doc = $("svg").contentWindow.document; + var rect = doc.getElementById("rect"); + var text = doc.getElementById("text"); + + // ordinary + + rect.setAttribute("stroke-width", "5"); + is(doc.defaultView.getComputedStyle(rect, '').getPropertyValue("stroke-width"), "5", "Ordinary"); + + // valid exponential notation + + rect.setAttribute("stroke-width", "4E1"); + is(doc.defaultView.getComputedStyle(rect, '').getPropertyValue("stroke-width"), "40", "Exponent"); + + rect.setAttribute("stroke-width", "6e1"); + is(doc.defaultView.getComputedStyle(rect, '').getPropertyValue("stroke-width"), "60", "Lower-case Exponent"); + + rect.setAttribute("stroke-width", "2E+1"); + is(doc.defaultView.getComputedStyle(rect, '').getPropertyValue("stroke-width"), "20", "Positive Exponent"); + + rect.setAttribute("stroke-width", "100E-1"); + is(doc.defaultView.getComputedStyle(rect, '').getPropertyValue("stroke-width"), "10", "Negative Exponent"); + + rect.setAttribute("stroke-width", "0.7E1"); + is(doc.defaultView.getComputedStyle(rect, '').getPropertyValue("stroke-width"), "7", "Floating Point with Exponent"); + + rect.setAttribute("stroke-width", "50.0E-1"); + is(doc.defaultView.getComputedStyle(rect, '').getPropertyValue("stroke-width"), "5", "Floating Point with Negative Exponent"); + + rect.setAttribute("stroke-width", "0.8E+1"); + is(doc.defaultView.getComputedStyle(rect, '').getPropertyValue("stroke-width"), "8", "Floating Point with Positive Exponent"); + + rect.setAttribute("stroke-width", "4E1px"); + is(doc.defaultView.getComputedStyle(rect, '').getPropertyValue("stroke-width"), "40px", "Units"); + + // check units that begin with the letter e + + var font_size = doc.defaultView.getComputedStyle(rect, '').getPropertyValue("font-size"); + + rect.setAttribute("stroke-width", "1em"); + is(doc.defaultView.getComputedStyle(rect, '').getPropertyValue("stroke-width"), font_size, "em Units"); + + // invalid exponential notation + + rect.setAttribute("stroke-width", "1E1.1"); + is(doc.defaultView.getComputedStyle(rect, '').getPropertyValue("stroke-width"), "1px", "Floating Point Exponent"); + + rect.setAttribute("stroke-width", "E1"); + is(doc.defaultView.getComputedStyle(rect, '').getPropertyValue("stroke-width"), "1px", "No Mantissa"); + + rect.setAttribute("stroke-width", "1 e"); + is(doc.defaultView.getComputedStyle(rect, '').getPropertyValue("stroke-width"), "1px", "Spaces"); + + SimpleTest.finish(); + } + + window.addEventListener("load", runTests, false); +</script> +</pre> +</body> +</html> diff --git a/dom/svg/test/test_selectSubString.xhtml b/dom/svg/test/test_selectSubString.xhtml new file mode 100644 index 0000000000..f626820f7c --- /dev/null +++ b/dom/svg/test/test_selectSubString.xhtml @@ -0,0 +1,83 @@ +<!DOCTYPE html> +<html xmlns="http://www.w3.org/1999/xhtml"> +<!-- +https://bugzilla.mozilla.org/show_bug.cgi?id=398825 +--> +<head> + <title>Test for Bug 398825</title> + <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" /> +</head> +<body> +<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=398825">Mozilla Bug 398825</a> +<p id="display"></p> +<div id="content" style="display: none"></div> + +<iframe id="svg" src="selectSubString-helper.svg"></iframe> + +<pre id="test"> +<script class="testbody" type="application/javascript"> +SimpleTest.waitForExplicitFinish(); + +function runTests() +{ + var document = $("svg").contentWindow.document; + var text = document.getElementById("text"); + + function expectThrow(charnum, nchars) + { + try + { + text.selectSubString(charnum, nchars); + ok(false, + "text.selectSubString(" + charnum + "," + nchars + ") " + + "should have thrown"); + } + catch (e) + { + is(e.name, "IndexSizeError", + "expected an index error for " + + "text.selectSubString(" + charnum + "," + nchars + ")"); + is(e.code, DOMException.INDEX_SIZE_ERR, + "expected an index error for " + + "text.selectSubString(" + charnum + "," + nchars + ")"); + } + } + + function expectNoThrow(charnum, nchars, expected) + { + try + { + text.selectSubString(charnum, nchars); + ok(true, + "text.selectSubString(" + charnum + "," + nchars + ") " + + "should not have thrown"); + } + catch (e) + { + ok(false, + "unexpected exception for " + + "text.selectSubString(" + charnum + "," + nchars + ")"); + } + } + + expectThrow(100, 2); + expectThrow(100, 0); + expectThrow(3, 0); + expectThrow(3, 100); + expectThrow(3, 100); + expectThrow(100, 100); + + expectNoThrow(1, 100); + expectNoThrow(2, 100); + expectNoThrow(1, 3); + expectNoThrow(0, 4); + + SimpleTest.finish(); +} + +window.addEventListener("load", runTests, false); +</script> +</pre> +</body> +</html> diff --git a/dom/svg/test/test_stroke-hit-testing.xhtml b/dom/svg/test/test_stroke-hit-testing.xhtml new file mode 100644 index 0000000000..04021905ec --- /dev/null +++ b/dom/svg/test/test_stroke-hit-testing.xhtml @@ -0,0 +1,67 @@ +<html xmlns="http://www.w3.org/1999/xhtml"> +<!-- +https://bugzilla.mozilla.org/show_bug.cgi?id=676001 +--> +<head> + <title>Test hit-testing of stroke</title> + <style> + +:hover { stroke: lime; } + + </style> + <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" /> +</head> +<body onload="run()"> +<script class="testbody" type="text/javascript"> +<![CDATA[ + +SimpleTest.waitForExplicitFinish(); + +function run() +{ + var div = document.getElementById('div'); + var line = document.getElementById('line'); + var circle = document.getElementById('circle'); + var offsetX = div.offsetLeft; + var offsetY = div.offsetTop; + var got; + + // line + got = document.elementFromPoint(offsetX + 116, offsetY + 103); + is(got, line, 'Should hit line (1)'); + + got = document.elementFromPoint(offsetX + 123, offsetY + 108); + is(got, line, 'Should hit line (2)'); + + // circle + got = document.elementFromPoint(offsetX + 188, offsetY + 158); + is(got, circle, 'Should hit circle (1)'); + + got = document.elementFromPoint(offsetX + 185, offsetY + 162); + is(got, circle, 'Should hit circle (2)'); + + SimpleTest.finish(); +} + +]]> +</script> +<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=676001">Mozilla Bug 676001</a> +<p id="display"></p> +<div id="content"> + + <div width="100%" height="1" id="div"></div> + <svg xmlns="http://www.w3.org/2000/svg" id="svg" width="500" height="300"> + <line id="line" x1="100" y1="100" x2="600" y2="180" + stroke="red" stroke-width="40"/> + <!-- the circle test points need to be within the mochitest test harness + viewport for test content in order for elementFromPoint to work --> + <circle id="circle" cx="100" cy="150" r="100" + fill="none" stroke="red" stroke-width="40"/> + </svg> + +</div> +<pre id="test"> +</pre> +</body> +</html> diff --git a/dom/svg/test/test_stroke-linecap-hit-testing.xhtml b/dom/svg/test/test_stroke-linecap-hit-testing.xhtml new file mode 100644 index 0000000000..da9bfb002e --- /dev/null +++ b/dom/svg/test/test_stroke-linecap-hit-testing.xhtml @@ -0,0 +1,47 @@ +<html xmlns="http://www.w3.org/1999/xhtml"> +<!-- +https://bugzilla.mozilla.org/show_bug.cgi?id=589648 +--> +<head> + <title>Test hit-testing of line caps</title> + <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" /> +</head> +<body onload="run()"> +<script class="testbody" type="text/javascript"> +<![CDATA[ + +SimpleTest.waitForExplicitFinish(); + +function run() +{ + var svg = document.getElementById('svg'); + var div = document.getElementById("div"); + var x = div.offsetLeft; + var y = div.offsetTop; + var got, expected; + + got = document.elementFromPoint(5 + x, 5 + y); + expected = document.getElementById('zero-length-square-caps'); + is(got, expected, 'Check hit on zero length subpath\'s square caps'); + + SimpleTest.finish(); +} + +]]> +</script> +<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=500174">Mozilla Bug 500174</a> +<p id="display"></p> +<div id="content"> + + <div width="100%" height="1" id="div"></div> + <svg xmlns="http://www.w3.org/2000/svg" id="svg" width="400" height="300"> + <path id="zero-length-square-caps" stroke="blue" stroke-width="50" + stroke-linecap="square" d="M25,25 L25,25"/> + </svg> + +</div> +<pre id="test"> +</pre> +</body> +</html> diff --git a/dom/svg/test/test_style_sheet.html b/dom/svg/test/test_style_sheet.html new file mode 100644 index 0000000000..955dda1ac8 --- /dev/null +++ b/dom/svg/test/test_style_sheet.html @@ -0,0 +1,27 @@ +<!DOCTYPE html> +<title>Test for Bug 1239128</title> +<script src="/tests/SimpleTest/SimpleTest.js"></script> +<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"> +<body> +<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1239128">Mozilla Bug 1239128</a> +<p id="display"</p> + +<svg> + <style>svg { fill: blue; }</style> +</svg> + +<pre id="test"> +<script class="testbody" type="application/javascript"> +var style = document.querySelector("style"); + +var exceptionThrown = false; +try { + is(style.sheet.cssRules[0].cssText, "svg { fill: blue; }", + "Should get the fill: blue rule back"); +} catch (e) { + exceptionThrown = true; +} + +ok(!exceptionThrown, "Should be able to access data: <style> stylesheet"); +</script> +</pre> diff --git a/dom/svg/test/test_switch.xhtml b/dom/svg/test/test_switch.xhtml new file mode 100644 index 0000000000..6cb55d8d2d --- /dev/null +++ b/dom/svg/test/test_switch.xhtml @@ -0,0 +1,104 @@ +<html xmlns="http://www.w3.org/1999/xhtml"> +<!-- +https://bugzilla.mozilla.org/show_bug.cgi?id=484176 +--> +<head> + <title>Test SVG Switch</title> + <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" /> +</head> +<body> +<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=484176">Mozilla Bug 484176</a> +<p id="display"></p> +<div id="content"></div> + +<iframe id="svg" src="switch-helper.svg"></iframe> + +<pre id="test"> +<script class="testbody" type="text/javascript"> + <![CDATA[ + +SimpleTest.waitForExplicitFinish(); + +var test = 1; + +function checkBounds(element, x, y, w, h) +{ + var bbox = element.getBBox(); + var name = element.nodeName; + + is(bbox.x, x, test + " " + name + ".getBBox().x"); + is(bbox.y, y, test + " " + name + ".getBBox().y"); + is(bbox.width, w, test + " " + name + ".getBBox().width"); + is(bbox.height, h, test + " " + name + ".getBBox().height"); + ++test; +} + +function checkWidth(element, w) +{ + var bbox = element.getBBox(); + var name = element.nodeName; + + is(bbox.width, w, test + " " + name + ".getBBox().width"); + ++test; +} + +function run() +{ + // Set accept_languages to something we know + SpecialPowers.pushPrefEnv({"set": [["intl.accept_languages", "en-gb,en,it"]]}, run1); +} + +function run1() +{ + try { + var doc = $("svg").contentDocument; + var s = doc.getElementById("s"); + var first = doc.getElementById("first"); + var second = doc.getElementById("second"); + var third = doc.getElementById("third"); + + first.setAttribute("systemLanguage", "fr"); + + /* test for an exact match */ + second.setAttribute("systemLanguage", "en-gb"); + checkWidth(s, 50); + + /* test for a close match i.e. the same language prefix */ + second.setAttribute("systemLanguage", "en-us"); + checkWidth(s, 50); + + /* test that we pick the best match */ + second.setAttribute("systemLanguage", "it"); + checkWidth(s, 50); + + /* test that we use the default if nothing matches */ + second.setAttribute("systemLanguage", "fr"); + checkWidth(s, 80); + + /* test we still ignore non-matches */ + second.removeAttribute("systemLanguage"); + third.setAttribute("systemLanguage", "fr"); + checkWidth(s, 50); + + /* check what happens if nothing matches */ + second.setAttribute("systemLanguage", "fr"); + checkWidth(s, 0); + + /* test that we pick the best match */ + first.setAttribute("systemLanguage", "en"); + second.setAttribute("systemLanguage", "en-gb"); + checkWidth(s, 50); + + } finally { + SimpleTest.finish(); + } +} + +window.addEventListener("load", run, false); + +]]> +</script> +</pre> +</body> +</html> diff --git a/dom/svg/test/test_tabindex.html b/dom/svg/test/test_tabindex.html new file mode 100644 index 0000000000..1cdba4bce3 --- /dev/null +++ b/dom/svg/test/test_tabindex.html @@ -0,0 +1,68 @@ +<html xmlns="http://www.w3.org/1999/xhtml"> +<head> + <title>Test for SVG tabIndex - Bug 778654</title> + <link rel="stylesheet" type="text/css" + href="chrome://mochikit/content/tests/SimpleTest/test.css" /> + + <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <script type="application/javascript" src="/tests/SimpleTest/EventUtils.js"></script> +</head> +<body> +<svg xmlns="http://www.w3.org/2000/svg" overflow="visible"> + <foreignObject id="f" x="0" y="0" width="200" height="60" tabindex="0"> + <body xmlns="http://www.w3.org/1999/xhtml"> + <p>Here is a paragraph that requires word wrap</p> + </body> + </foreignObject> + <rect id="r" x="0" y="70" width="100" height="100" fill="yellow" tabindex="1"/> + <text id="t" x="0" y="200" tabindex="2"> + This is SVG text + </text> +</svg> +<pre id="test"> +<script class="testbody" type="text/javascript"> +SimpleTest.waitForExplicitFinish(); + +function main() +{ + var f = document.getElementById('f'); + var r = document.getElementById('r'); + var t = document.getElementById('t'); + + try { + // Step 1: Checking by assigning tabIndex + is(f.tabIndex, 0, "foreignObject initial tabIndex"); + f.tabIndex = 1; + is(f.tabIndex, 1, "foreignObject tabIndex is set to 1"); + + is(r.tabIndex, 1, "rect initial tabIndex"); + r.tabIndex = 2; + is(r.tabIndex, 2, "rect tabIndex is set to 2"); + + is(t.tabIndex, 2, "text initial tabIndex"); + t.tabIndex = 3; + is(t.tabIndex, 3, "text is set to 3"); + + // Step 2: Checking by triggering TAB event + is(document.activeElement.tabIndex, -1, "In the beginning, the active element tabindex is -1"); + + synthesizeKey("VK_TAB", {}); + is(document.activeElement.tabIndex, 1, "The active element tabindex is 1"); + + synthesizeKey("VK_TAB", {}); + is(document.activeElement.tabIndex, 2, "The active element tabindex is 2"); + + synthesizeKey("VK_TAB", {}); + is(document.activeElement.tabIndex, 3, "The active element tabindex is 3"); + } catch(e) { + ok(false, "Got unexpected exception" + e); + } + + SimpleTest.finish(); +} + +window.addEventListener("load", main, false); +</script> +</pre> +</body> +</html> diff --git a/dom/svg/test/test_tearoff_with_cc.html b/dom/svg/test/test_tearoff_with_cc.html new file mode 100644 index 0000000000..f4c566b314 --- /dev/null +++ b/dom/svg/test/test_tearoff_with_cc.html @@ -0,0 +1,48 @@ +<!DOCTYPE HTML> +<html> +<!-- +https://bugzilla.mozilla.org/show_bug.cgi?id=1288228 +--> +<head> + <meta charset="utf-8"> + <title>Test for Bug 1288228</title> + <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> + <script type="application/javascript"> + /** Test for Bug 1288228 **/ + /* Note: the crash in bug 1288228 doesn't happen reliably (and only happens + * after several reloads). So, we reload the iframe 10 times, and then call + * it good if we haven't crashed. + */ + const maxReloads = 10; + let remainingReloads = maxReloads; + + /* The helper-file in the iframe will notify us after it's performed its + * potentially-crash-triggering tweak. At that point, we reload the iframe + * and wait for it to notify again (or we simply finish, if we've completed + * all of the reloads we planned to do). + */ + window.addEventListener("message", reloadIframe, false); + + function reloadIframe() { + if (--remainingReloads == 0) { + ok(true, "Didn't crash!"); + SimpleTest.finish(); + } else { + var frame = document.getElementById("testIframe"); + frame.setAttribute("src", ""); + frame.setAttribute("src", "tearoff_with_cc_helper.html"); + } + } + SimpleTest.waitForExplicitFinish(); + </script> +</head> +<body onload="reloadIframe()"> +<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1288228"> + Mozilla Bug 1288228 +</a> +<p id="display"> + <iframe id="testIframe"></iframe> +</p> +</body> +</html> diff --git a/dom/svg/test/test_text.html b/dom/svg/test/test_text.html new file mode 100644 index 0000000000..ca03f2af35 --- /dev/null +++ b/dom/svg/test/test_text.html @@ -0,0 +1,188 @@ +<!DOCTYPE html> +<html xmlns="http://www.w3.org/1999/xhtml"> +<!-- +https://bugzilla.mozilla.org/show_bug.cgi?id=392233 +--> +<head> + <title>Test for Bug 392233</title> + <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" /> +</head> +<body> +<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=392233">Mozilla Bug 392233</a> +<p id="display"></p> +<div id="content" style="display: none"></div> + +<iframe id="svg" src="text-helper.svg"></iframe> + +<pre id="test"> +<script class="testbody" type="application/javascript"> +SimpleTest.waitForExplicitFinish(); + +function runTest() +{ + var doc = $("svg").contentWindow.document; + var text1 = doc.getElementById("text1"); + var text2 = doc.getElementById("text2"); + var text3 = doc.getElementById("text3"); + var text4 = doc.getElementById("text4"); + + var charWidth = text1.getSubStringLength(0, 1); + + function isPoint(pt1, x, y, str) + { + is(pt1.x, x, str + " x"); + is(pt1.y, y, str + " y"); + } + + function ymost(r) + { + return r.y + r.height; + } + + function xmost(r) + { + return r.x + r.width; + } + + var p = text1.getStartPositionOfChar(0); + + // Simple horizontal string + + is(text1.getNumberOfChars(), 3, "text1 length"); + ok(text1.getComputedTextLength() > 0, "text1 measured length"); + is(text1.getComputedTextLength(), text1.getSubStringLength(0, 3), "text1 substring length"); + isPoint(text1.getStartPositionOfChar(0), 5, 25, "text1 char 0 start offset"); + isPoint(text1.getStartPositionOfChar(1), 5 + charWidth, 25, "text1 char 1 start offset"); + isPoint(text1.getStartPositionOfChar(2), 5 + 2*charWidth, 25, "text1 char 2 start offset"); + isPoint(text1.getEndPositionOfChar(0), 5 + charWidth, 25, "text1 char 0 end offset"); + isPoint(text1.getEndPositionOfChar(1), 5 + 2*charWidth, 25, "text1 char 1 end offset"); + isPoint(text1.getEndPositionOfChar(2), 5 + 3*charWidth, 25, "text1 char 2 end offset"); + is(text1.getExtentOfChar(0).x, 5, "text1 char 0 extent x"); + is(text1.getExtentOfChar(0).width, text1.getSubStringLength(0, 1), "text1 char 0 extent width"); + ok(text1.getExtentOfChar(0).y < 25, "text1 char 0 extent y"); + ok(ymost(text1.getExtentOfChar(0)) > 25, "text1 char 0 extent height"); + is(text1.getExtentOfChar(1).x, 5 + charWidth, "text1 char 1 extent x"); + is(text1.getExtentOfChar(1).width, text1.getSubStringLength(0, 1), "text1 char 1 extent width"); + is(text1.getExtentOfChar(1).y, text1.getExtentOfChar(0).y, "text1 char 0/1 extent y"); + is(text1.getExtentOfChar(1).height, text1.getExtentOfChar(0).height, "text1 char 0/1 extent height"); + is(text1.getExtentOfChar(2).x, 5 + 2*charWidth, "text1 char 2 extent x"); + is(text1.getExtentOfChar(2).width, text1.getSubStringLength(0, 1), "text1 char 2 extent width"); + is(text1.getExtentOfChar(2).y, text1.getExtentOfChar(0).y, "text1 char 0/2 extent y"); + is(text1.getExtentOfChar(2).height, text1.getExtentOfChar(0).height, "text1 char 0/2 extent height"); + is(text1.getRotationOfChar(0), 0, "text1 char 0 rotation"); + is(text1.getRotationOfChar(1), 0, "text1 char 0 rotation"); + is(text1.getRotationOfChar(2), 0, "text1 char 0 rotation"); + p.x = 5 + 0.1; + p.y = 25; + is(text1.getCharNumAtPosition(p), 0, "text1 finding char 0 left edge"); + p.x = 5 + charWidth - 0.1; + is(text1.getCharNumAtPosition(p), 0, "text1 finding char 0 on right"); + p.x = 5 - 0.1; + is(text1.getCharNumAtPosition(p), -1, "text1 finding no char on left"); + p.x = 5 + 0.1; + p.y = text1.getExtentOfChar(0).y - 0.1; + is(text1.getCharNumAtPosition(p), -1, "text1 finding no char on top"); + p.y = text1.getExtentOfChar(0).y + 0.1; + is(text1.getCharNumAtPosition(p), 0, "text1 finding char 0 top edge"); + p.x = 5 + 3*charWidth - 0.1; + is(text1.getCharNumAtPosition(p), 2, "text1 finding char 2 top edge"); + p.x = 5 + 3*charWidth + 0.1; + is(text1.getCharNumAtPosition(p), -1, "text1 finding no char on right"); + + // Simple rotated-90 string ... width might change because of hinting + + charWidth = text2.getSubStringLength(0, 1); + + is(text2.getNumberOfChars(), 3, "text2 length"); + ok(text2.getComputedTextLength() > 0, "text2 measured length"); + is(text2.getComputedTextLength(), text2.getSubStringLength(0, 3), "text2 substring length"); + isPoint(text2.getStartPositionOfChar(0), 100, 125, "text2 char 0 start offset"); + isPoint(text2.getStartPositionOfChar(1), 100, 125 + charWidth, "text2 char 1 start offset"); + isPoint(text2.getStartPositionOfChar(2), 100, 125 + 2*charWidth, "text2 char 2 start offset"); + isPoint(text2.getEndPositionOfChar(0), 100, 125 + charWidth, "text2 char 0 end offset"); + isPoint(text2.getEndPositionOfChar(1), 100, 125 + 2*charWidth, "text2 char 1 end offset"); + isPoint(text2.getEndPositionOfChar(2), 100, 125 + 3*charWidth, "text2 char 2 end offset"); + is(text2.getExtentOfChar(0).y, 125, "text2 char 0 extent y"); + isfuzzy(text2.getExtentOfChar(0).height, charWidth, 0.000001, "text2 char 0 extent height"); + ok(text2.getExtentOfChar(0).width < 100, "text2 char 0 extent x"); + ok(xmost(text2.getExtentOfChar(0)) > 100, "text2 char 0 extent width"); + is(text2.getExtentOfChar(1).y, 125 + charWidth, "text2 char 1 extent x"); + isfuzzy(text2.getExtentOfChar(1).height, text2.getSubStringLength(0, 1), 0.000001, "text2 char 1 extent width"); + is(text2.getExtentOfChar(1).x, text2.getExtentOfChar(0).x, "text2 char 0/1 extent y"); + is(text2.getExtentOfChar(1).width, text2.getExtentOfChar(0).width, "text2 char 0/1 extent height"); + is(text2.getExtentOfChar(2).y, 125 + 2*charWidth, "text2 char 2 extent x"); + isfuzzy(text2.getExtentOfChar(2).height, text2.getSubStringLength(0, 1), 0.000001, "text2 char 2 extent width"); + is(text2.getExtentOfChar(2).x, text2.getExtentOfChar(0).x, "text2 char 0/2 extent y"); + is(text2.getExtentOfChar(2).width, text2.getExtentOfChar(0).width, "text2 char 0/2 extent height"); + is(text2.getRotationOfChar(0), 90, "text2 char 0 rotation"); + is(text2.getRotationOfChar(1), 90, "text2 char 0 rotation"); + is(text2.getRotationOfChar(2), 90, "text2 char 0 rotation"); + p.y = 125 + 0.1; + p.x = 100; + is(text2.getCharNumAtPosition(p), 0, "text2 finding char 0 top edge"); + p.y = 125 + charWidth - 0.1; + is(text2.getCharNumAtPosition(p), 0, "text2 finding char 0 on bottom"); + p.y = 125 - 0.1; + is(text2.getCharNumAtPosition(p), -1, "text2 finding no char on top"); + p.y = 125 + 0.1; + p.x = text2.getExtentOfChar(0).x - 0.1; + is(text2.getCharNumAtPosition(p), -1, "text2 finding no char on left"); + p.x = text2.getExtentOfChar(0).x + 0.1; + is(text2.getCharNumAtPosition(p), 0, "text2 finding char 0 left edge"); + p.y = 125 + 3*charWidth - 0.1; + is(text2.getCharNumAtPosition(p), 2, "text2 finding char 2 bottom edge"); + p.y = 1225 + 3*charWidth + 0.1; + is(text2.getCharNumAtPosition(p), -1, "text2 finding no char on bottom"); + + // Text along a thin rectangle path + + charWidth = text3.getSubStringLength(0, 1); + + is(text3.getNumberOfChars(), 26, "text3 length"); + ok(text3.getComputedTextLength() > 0, "text3 measured length"); + is(text3.getComputedTextLength(), text3.getSubStringLength(0, 26), "text3 substring length"); + + // character 12 should be on the bottom side + is(text3.getStartPositionOfChar(12).y, 253, "text3 char 12 start offset"); + is(text3.getEndPositionOfChar(12).y, 253, "text3 char 12 end offset"); + ok(text3.getExtentOfChar(12).y < 253, "text3 char 12 extent y"); + ok(ymost(text3.getExtentOfChar(12)) > 253, "text3 char 12 extent height"); + isfuzzy(text3.getRotationOfChar(12), 180, 0.001, "text3 char 12 rotation"); + p.x = text3.getExtentOfChar(12).x + 0.1; + p.y = ymost(text3.getExtentOfChar(12)) - 0.1; + is(text3.getCharNumAtPosition(p), 12, "text3 finding char 12"); + // This next test is tricky. The glyph for character 3 may overlap from the above + // but character 12 wins because it's the last to render + p.y = text3.getExtentOfChar(12).y + 0.1; + is(text3.getCharNumAtPosition(p), 12, "text3 finding last rendered char"); + + // character 25 should be beyond the end of the path + // Not sure what should happen here. Currently we throw, which seems wrong + // is(text3.getStartPositionOfChar(25).x, 0, "text3 char 25 start offset"); + + // Display:none string + + is(text4.getNumberOfChars(), 0, "text4 length"); + is(text4.getComputedTextLength(), 0, "text4 measured length"); + is(text4.getSubStringLength(0, 3), 0, "text4 substring length"); + p = text1.getStartPositionOfChar(0); + is(text4.getCharNumAtPosition(p), -1, "text4 shouldn't find rendered char"); +} + +function runTests() { + runTest(); + + var doc = $("svg").contentWindow.document; + doc.getElementById("g").setAttribute("transform", "scale(2) rotate(90 200 200)"); + + runTest(); + + SimpleTest.finish(); +} + +window.addEventListener("load", runTests, false); +</script> +</pre> +</body> +</html> diff --git a/dom/svg/test/test_text_2.html b/dom/svg/test/test_text_2.html new file mode 100644 index 0000000000..aa64bb8e2a --- /dev/null +++ b/dom/svg/test/test_text_2.html @@ -0,0 +1,60 @@ +<!DOCTYPE html> +<html xmlns="http://www.w3.org/1999/xhtml"> +<!-- +https://bugzilla.mozilla.org/show_bug.cgi?id=655877 +--> +<head> + <meta charset=UTF-8> + <title>Test for Bug 655877</title> + <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" /> +</head> +<body> +<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=655877">Mozilla Bug 655877</a> +<p id="display"></p> +<div id="content"> + <svg width="400" height="200"> + <text x="100" y="100" style="font: 16px sans-serif"> + abc אבג 123 דהו defg + </text> + </svg> +</div> + +<pre id="test"> +<script class="testbody" type="application/javascript"> +SimpleTest.waitForExplicitFinish(); + +function close(x, y, message) { + ok(Math.abs(x - y) < 1e-4, message); +} + +function runTest() { + var text = document.querySelector("text"); + + // there are only 20 addressable characters + is(text.getNumberOfChars(), 20, "getNumberOfChars"); + + var sum, total = text.getComputedTextLength(); + + close(text.getSubStringLength(0, 20), total, "getSubStringLength all"); + + // add the advance of each glyph + sum = 0; + for (var i = 0; i < 20; i++) { + sum += text.getSubStringLength(i, 1); + } + close(total, sum, "sum getSubStringLength 1"); + + // split the text up into three chunks and add them together + close(total, text.getSubStringLength(0, 6) + + text.getSubStringLength(6, 8) + + text.getSubStringLength(14, 6), "sum getSubStringLength 2"); + + SimpleTest.finish(); +} + +window.addEventListener("load", runTest, false); +</script> +</pre> +</body> +</html> diff --git a/dom/svg/test/test_text_dirty.html b/dom/svg/test/test_text_dirty.html new file mode 100644 index 0000000000..a7f5d1b586 --- /dev/null +++ b/dom/svg/test/test_text_dirty.html @@ -0,0 +1,47 @@ +<!DOCTYPE html> +<html> +<!-- +https://bugzilla.mozilla.org/show_bug.cgi?id=886230 +--> +<head> + <title>Test for Bug 886230</title> + <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/> + <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" /> + <style type="text/css"> + </style> +</head> +<body> +<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=886230">Mozilla Bug 886230</a> +<p id="display"> + <svg> + <mask id="m"><text id="t">x</text></mask> + <rect width="600" height="400" mask="url(#m)"/> + </svg> +</p> +<div id="content" style="display: none"> +</div> +<pre id="test"> +<script class="testbody" type="text/javascript"> +function runTest() { + var svgText = document.getElementById("t"); + + // Dirty the frames. + document.getElementById("display").style.width = "700px"; + svgText.removeChild(svgText.firstChild); + + // Paint without flushing layout. If the test fails, we'll trigger + // an assertion. + SpecialPowers.snapshotWindowWithOptions(window, undefined, undefined, { DRAWWINDOW_DO_NOT_FLUSH: true }); + + ok(true); + SimpleTest.finish(); +} + +window.addEventListener("load", runTest, false); + +SimpleTest.waitForExplicitFinish(); +</script> +</pre> +</body> +</html> diff --git a/dom/svg/test/test_text_lengthAdjust.html b/dom/svg/test/test_text_lengthAdjust.html new file mode 100644 index 0000000000..9d1e007d00 --- /dev/null +++ b/dom/svg/test/test_text_lengthAdjust.html @@ -0,0 +1,106 @@ +<!DOCTYPE html> +<html> +<!-- +https://bugzilla.mozilla.org/show_bug.cgi?id=569722 +--> +<head> + <meta charset=UTF-8> + <title>Test for Bug 569722</title> + <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" /> +</head> +<body> +<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=569722">Mozilla Bug 569722</a> +<p id="display"></p> +<div id="content"> + <svg width="400" height="200"> + <text x="0" y="100" style="font: 16px sans-serif">aaa</text> + </svg> +</div> + +<pre id="test"> +<script class="testbody" type="application/javascript"> +SimpleTest.waitForExplicitFinish(); + +function close(x, y, message) { + ok(Math.abs(x - y) < 1e-4, message); +} + +function runTest() { + var text = document.querySelector("text"); + + // get the original length + var length = text.getComputedTextLength(); + + // get the original glyph positions + var startPositions = [], + endPositions = [], + extents = []; + for (var i = 0; i < 3; i++) { + startPositions.push(text.getStartPositionOfChar(i)); + endPositions.push(text.getEndPositionOfChar(i)); + extents.push(text.getExtentOfChar(i)); + } + + // widths should all be the same + is(extents[0].width, extents[1].width); + is(extents[0].width, extents[2].width); + + var checkCharNumAtPosition = function(x, y, i) { + var p = document.querySelector("svg").createSVGPoint(); + p.x = x; + p.y = y; + is(text.getCharNumAtPosition(p), i, "getCharNumAtPosition(" + i + ")"); + }; + + var checkPositions = function(start, end, width) { + for (var i = 0; i < 3; i++) { + // check their positions + close(text.getStartPositionOfChar(i).x, start[i], "start position of glyph " + i); + close(text.getEndPositionOfChar(i).x, end[i], "end position of glyph " + i); + close(text.getExtentOfChar(i).x, start[i], "left edge of extent of glyph " + i); + close(text.getExtentOfChar(i).width, width, "width of glyph " + i); + checkCharNumAtPosition((start[i] + end[i]) / 2, 100, i); + } + } + + var w = extents[0].width; + + var doLengthAdjustSpacingTest = function() { + // getComputedTextLength should return the sum of the advances, and since + // we are just changing the positions of the glyphs, it should be the same + // as without a textLength="" attribute + close(text.getComputedTextLength(), length, "getComputedTextLength when lengthAdjust=\"spacing\""); + + // expected start and end positions of the glyphs + var start = [0, 50 - w / 2, 100 - w]; + var end = [w, 50 + w / 2, 100]; + checkPositions(start, end, w); + }; + + // switch to adjust glyph positions, using the default value of lengthAdjust="" + text.setAttribute("textLength", "100"); + doLengthAdjustSpacingTest(); + // then with an explicit lengthAdjust="spacing" + text.setAttribute("lengthAdjust", "spacing"); + doLengthAdjustSpacingTest(); + + // now test with lengthAdjust="spacingAndGlyphs" + text.setAttribute("lengthAdjust", "spacingAndGlyphs"); + + // now that each glyph is stretched, the total advance should be the textLength + close(text.getComputedTextLength(), 100, "getComputedTextLength when lengthAdjust=\"spacingAndGlyphs\""); + + // expected start and end positions of the glyphs + var start = [0, 33.3333, 66.6666]; + var end = [33.3333, 66.6666, 100]; + checkPositions(start, end, 33.3333); + + SimpleTest.finish(); +} + +window.addEventListener("load", runTest, false); +</script> +</pre> +</body> +</html> diff --git a/dom/svg/test/test_text_scaled.html b/dom/svg/test/test_text_scaled.html new file mode 100644 index 0000000000..deb3ae60b5 --- /dev/null +++ b/dom/svg/test/test_text_scaled.html @@ -0,0 +1,142 @@ +<!DOCTYPE html> +<html xmlns="http://www.w3.org/1999/xhtml"> +<!-- +https://bugzilla.mozilla.org/show_bug.cgi?id=655877 +--> +<head> + <title>Test for Bug 655877</title> + <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" /> +</head> +<body> +<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=655877">Mozilla Bug 655877</a> +<p id="display"></p> +<div id="content" style="display: none"></div> + +<iframe id="svg" src="text-helper-scaled.svg"></iframe> + +<pre id="test"> +<script class="testbody" type="application/javascript"> +SimpleTest.waitForExplicitFinish(); + +function runTest() +{ + var doc = $("svg").contentWindow.document; + var text1 = doc.getElementById("text1"); + var text2 = doc.getElementById("text2"); + + var charWidth = text1.getSubStringLength(0, 1); + + var epsilon = 0.001; + + function isClose(a, b, str) + { + ok(Math.abs(a - b) < epsilon, str + " - " + b + " should be close to " + a); + } + + function isPoint(pt1, x, y, str) + { + is(pt1.x, x, str + " x"); + is(pt1.y, y, str + " y"); + } + + function isPointCloseX(pt1, x, y, str) + { + isClose(pt1.x, x, str + " x"); + is(pt1.y, y, str + " y"); + } + + function ymost(r) + { + return r.y + r.height; + } + + function xmost(r) + { + return r.x + r.width; + } + + var p = text1.getStartPositionOfChar(0); + + // Simple horizontal string + + is(text1.getNumberOfChars(), 3, "text1 length"); + ok(text1.getComputedTextLength() > 0, "text1 measured length"); + is(text1.getComputedTextLength(), text1.getSubStringLength(0, 3), "text1 substring length"); + isPointCloseX(text1.getStartPositionOfChar(0), 10, 400, "text1 char 0 start offset"); + isPointCloseX(text1.getStartPositionOfChar(1), 10 + charWidth, 400, "text1 char 1 start offset"); + isPointCloseX(text1.getStartPositionOfChar(2), 10 + 2*charWidth, 400, "text1 char 2 start offset"); + isPointCloseX(text1.getEndPositionOfChar(0), 10 + charWidth, 400, "text1 char 0 end offset"); + isPointCloseX(text1.getEndPositionOfChar(1), 10 + 2*charWidth, 400, "text1 char 1 end offset"); + isPointCloseX(text1.getEndPositionOfChar(2), 10 + 3*charWidth, 400, "text1 char 2 end offset"); + is(text1.getExtentOfChar(0).x, 10, "text1 char 0 extent x"); + is(text1.getExtentOfChar(0).width, text1.getSubStringLength(0, 1), "text1 char 0 extent width"); + ok(text1.getExtentOfChar(0).y < 400, "text1 char 0 extent y"); + ok(ymost(text1.getExtentOfChar(0)) > 400, "text1 char 0 extent height"); + isClose(text1.getExtentOfChar(1).x, 10 + charWidth, "text1 char 1 extent x"); + is(text1.getExtentOfChar(1).width, text1.getSubStringLength(0, 1), "text1 char 1 extent width"); + is(text1.getExtentOfChar(1).y, text1.getExtentOfChar(0).y, "text1 char 0/1 extent y"); + is(text1.getExtentOfChar(1).height, text1.getExtentOfChar(0).height, "text1 char 0/1 extent height"); + is(text1.getExtentOfChar(2).x, 10 + 2*charWidth, "text1 char 2 extent x"); + is(text1.getExtentOfChar(2).width, text1.getSubStringLength(0, 1), "text1 char 2 extent width"); + is(text1.getExtentOfChar(2).y, text1.getExtentOfChar(0).y, "text1 char 0/2 extent y"); + is(text1.getExtentOfChar(2).height, text1.getExtentOfChar(0).height, "text1 char 0/2 extent height"); + is(text1.getRotationOfChar(0), 0, "text1 char 0 rotation"); + is(text1.getRotationOfChar(1), 0, "text1 char 0 rotation"); + is(text1.getRotationOfChar(2), 0, "text1 char 0 rotation"); + p.x = 10 + 0.1; + p.y = 400; + is(text1.getCharNumAtPosition(p), 0, "text1 finding char 0 left edge"); + p.x = 10 + charWidth - 0.1; + is(text1.getCharNumAtPosition(p), 0, "text1 finding char 0 on right"); + p.x = 10 - 0.1; + is(text1.getCharNumAtPosition(p), -1, "text1 finding no char on left"); + p.x = 10 + 0.1; + p.y = text1.getExtentOfChar(0).y - 0.1; + is(text1.getCharNumAtPosition(p), -1, "text1 finding no char on top"); + p.y = text1.getExtentOfChar(0).y + 0.1; + is(text1.getCharNumAtPosition(p), 0, "text1 finding char 0 top edge"); + p.x = 10 + 3*charWidth - 0.1; + is(text1.getCharNumAtPosition(p), 2, "text1 finding char 2 top edge"); + p.x = 10 + 3*charWidth + 0.1; + is(text1.getCharNumAtPosition(p), -1, "text1 finding no char on right"); + + // Text along a thin rectangle path + + charWidth = text2.getSubStringLength(0, 1); + + is(text2.getNumberOfChars(), 26, "text2 length"); + ok(text2.getComputedTextLength() > 0, "text2 measured length"); + is(text2.getComputedTextLength(), text2.getSubStringLength(0, 26), "text2 substring length"); + + // character 12 should be on the bottom side + is(text2.getStartPositionOfChar(12).y, 860, "text2 char 12 start offset"); + isfuzzy(text2.getEndPositionOfChar(12).y, 860, 0.001, "text2 char 12 end offset"); + ok(text2.getExtentOfChar(12).y < 860, "text2 char 12 extent y"); + ok(ymost(text2.getExtentOfChar(12)) > 860, "text2 char 12 extent height"); + isfuzzy(text2.getRotationOfChar(12), 180, 0.001, "text2 char 12 rotation"); + p.x = text2.getExtentOfChar(12).x + 0.1; + p.y = ymost(text2.getExtentOfChar(12)) - 0.1; + is(text2.getCharNumAtPosition(p), 12, "text2 finding char 12"); + // This next test is tricky. The glyph for character 3 may overlap from the above + // but character 12 wins because it's the last to render + p.y = text2.getExtentOfChar(12).y + 0.1; + is(text2.getCharNumAtPosition(p), 12, "text2 finding last rendered char"); +} + +function runTests() { + runTest(); + + var doc = $("svg").contentWindow.document; + doc.getElementById("g").setAttribute("transform", "rotate(90 200 200)"); + + runTest(); + + SimpleTest.finish(); +} + +window.addEventListener("load", runTests, false); +</script> +</pre> +</body> +</html> diff --git a/dom/svg/test/test_text_selection.html b/dom/svg/test/test_text_selection.html new file mode 100644 index 0000000000..1d5b3b533b --- /dev/null +++ b/dom/svg/test/test_text_selection.html @@ -0,0 +1,145 @@ +<!DOCTYPE html> +<html> +<meta charset=utf-8> +<!-- +https://bugzilla.mozilla.org/show_bug.cgi?id=655877 +--> +<head> + <title>Test for Bug 655877</title> + <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <script type="application/javascript" src="/tests/SimpleTest/EventUtils.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" /> +</head> +<body> +<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=655877">Mozilla Bug 655877</a> +<p id="display"></p> +<div id="content" style="display: none"></div> + +<iframe src="text-helper-selection.svg" width="400" height="300"></iframe> + +<pre id="test"> +<script class="testbody" type="application/javascript"> +SimpleTest.waitForExplicitFinish(); + +var svg, doc, win, text, dragstart, dragend; + +function drag(fromX, fromY, toX, toY, show) +{ + synthesizeMouse(doc.documentElement, fromX, fromY, { type: "mousemove" }, win); + synthesizeMouse(doc.documentElement, fromX, fromY, { type: "mousedown" }, win); + synthesizeMouse(doc.documentElement, toX, toY, { type: "mousemove" }, win); + synthesizeMouse(doc.documentElement, toX, toY, { type: "mouseup" }, win); + + if (show) { + dragstart.setAttribute("cx", fromX); + dragstart.setAttribute("cy", fromY); + dragstart.setAttribute("r", "4"); + dragend.setAttribute("cx", toX); + dragend.setAttribute("cy", toY); + dragend.setAttribute("r", "4"); + } +} + +function click(x, y) +{ + synthesizeMouse(doc.documentElement, x, y, { type: "mousemove" }, win); + synthesizeMouse(doc.documentElement, x, y, { type: "mousedown" }, win); + synthesizeMouse(doc.documentElement, x, y, { type: "mouseup" }, win); +} + +function selection_is(s, text) +{ + is(win.getSelection().toString(), s, text); +} + +function deselect() +{ + // Click outside text to deselect. + click(1, 1); + selection_is("", "deselecting by clicking outside text"); +} + +function testSelection() +{ + svg = document.getElementsByTagName("iframe")[0]; + doc = svg.contentDocument; + win = svg.contentWindow; + dragstart = doc.getElementById("dragstart"); + dragend = doc.getElementById("dragend"); + + var text = doc.getElementsByTagName("text"); + + // Drag to select the entire text element. + drag(101, 50, 99 + text[0].getComputedTextLength(), 50); + selection_is("hello there", "selecting entire simple text"); + + // Click within the text to deselect. + click(101, 50); + selection_is("", "deselecting by clicking on text"); + + // Drag to select part of a text element. + drag(101, 50, 99 + text[0].getSubStringLength(0, 5), 50); + selection_is("hello", "selecting part of simple text"); + deselect(); + + // Drag from left of the text to the right of the text to select it. + drag(90, 50, 110 + text[0].getComputedTextLength(), 50); + selection_is("hello there", "selecting entire simple text by dragging around it"); + deselect(); + + // Drag above the text to select part of it. + var bbox1 = text[0].getBBox(); + drag(101 + text[0].getSubStringLength(0, 6), bbox1.y - 10, 101 + text[0].getSubStringLength(0, 9), bbox1.y - 10); + selection_is("the", "selecting part of simple text by dragging above it"); + deselect(); + + // Drag between the first and second texts, but closer to the first. + var bbox2 = text[1].getBBox(); + var mid = (bbox1.y + bbox1.height + bbox2.y) / 2; + drag(101, mid - 10, 99 + text[0].getSubStringLength(0, 2), mid - 10); + selection_is("he", "selecting closer text above"); + deselect(); + + // Drag between the first and second texts, but closer to the second. + drag(101, mid + 10, 99 + text[1].getSubStringLength(0, 2), mid + 10); + selection_is("to", "selecting closer text below"); + deselect(); + + // Drag starting in the first text and ending in the second. + drag(101 + text[0].getSubStringLength(0, 6), 50, 99 + text[1].getSubStringLength(0, 2), 100); + selection_is("there to", "selecting from first to second text"); + deselect(); + + // Select across positioned glyphs. + drag(99 + text[2].getSubStringLength(3, 1), 150, 201, 150); + selection_is("abcd", "selecting across positioned glyphs"); + deselect(); + + // Select bidi text, from the left of the "א" to the left of the "b". + drag(text[3].getExtentOfChar(0).x + 1, 200, text[3].getExtentOfChar(4).x + 1, 200); + selection_is("בגa", "selecting bidi text"); + deselect(); + + // Select transformed text. + drag(101, 250, 99 + text[4].getSubStringLength(0, 6) / 2, 250); + selection_is("squash"); + deselect(); + + SimpleTest.finish(); +} + +function runTest() +{ + SimpleTest.executeSoon(testSelection); +} + +if (/Android/.test(navigator.userAgent)) { + ok(true, "No need to test text selection with the mouse on Android."); + SimpleTest.finish(); +} else { + window.addEventListener("load", runTest, false); +} +</script> +</pre> +</body> +</html> diff --git a/dom/svg/test/test_text_update.html b/dom/svg/test/test_text_update.html new file mode 100644 index 0000000000..cadb218a5f --- /dev/null +++ b/dom/svg/test/test_text_update.html @@ -0,0 +1,31 @@ +<!DOCTYPE html> +<title>Test for Bug 876831</title> +<script src="/tests/SimpleTest/SimpleTest.js"></script> +<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"> +<body> +<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=876831">Mozilla Bug 876831</a> +<p id="display"</p> + +<!-- + Test that the frame tree will be reflowed after a DOM mutation + and just before an SVG DOM method does its work. + --> + +<svg> + <text>ab</text> +</svg> + +<pre id="test"> +<script class="testbody" type="application/javascript"> +var text = document.querySelector("text"); + +var length = text.getComputedTextLength(); +ok(length > 0, "text.getComputedTextLength() > 0"); + +text.firstChild.nodeValue += "cd"; +ok(text.getComputedTextLength() > length, "text.getComputedTextLength() changes directly after DOM mutation"); + +text.removeChild(text.firstChild); +is(text.getComputedTextLength(), 0, "text.getComputedTextLength() == 0 after removing child"); +</script> +</pre> diff --git a/dom/svg/test/test_transform.xhtml b/dom/svg/test/test_transform.xhtml new file mode 100644 index 0000000000..c0ce473f58 --- /dev/null +++ b/dom/svg/test/test_transform.xhtml @@ -0,0 +1,198 @@ +<html xmlns="http://www.w3.org/1999/xhtml"> +<!-- +https://bugzilla.mozilla.org/show_bug.cgi?id=512636 +--> +<head> + <title>Test SVGTransform behavior</title> + <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" /> +</head> +<body> +<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=512636">Mozilla Bug 512636</a> +<p id="display"></p> +<div id="content"> + + <svg xmlns="http://www.w3.org/2000/svg" width="100%" height="1" id="svg"> + <g id="g" transform="translate(10, 20)"/> + </svg> + +</div> +<pre id="test"> +<script class="testbody" type="text/javascript"> +<![CDATA[ + +SimpleTest.waitForExplicitFinish(); + +function run() +{ + var g, svg, t, m, m2; + + svg = $('svg'); + g = $('g'); + + t = g.transform.baseVal.getItem(0); + m = t.matrix; + + // test that the SVGTransform correctly reflects the translate() + checkTransform(t, SVGTransform.SVG_TRANSFORM_TRANSLATE, + 1, 0, + 0, 1, + 10, 20, + 0, "translate"); + + // set the SVGTransform to be a scale() + t.setScale(2, 3); + + // test that the matrix is live and now reflects the scale() + checkTransform(t, SVGTransform.SVG_TRANSFORM_SCALE, + 2, 0, + 0, 3, + 0, 0, + 0, "scale"); + + // set the SVGTransform to be a matrix() + m2 = svg.createSVGMatrix(); + m2.a = 1; + m2.b = 2; + m2.c = 3; + m2.d = 4; + m2.e = 5; + m2.f = 6; + t.setMatrix(m2); + + // check that setMatrix() took a copy of m + ok(m != m2, 't.matrix identity'); + + // test that the SVGTransform now reflects the matrix value + checkTransform(t, SVGTransform.SVG_TRANSFORM_MATRIX, + 1, 2, + 3, 4, + 5, 6, + 0, "matrix"); + + // set the SVGTransform to be a translate() then convert to a matrix + t.setTranslate(0, 10); + m.a = 2; + + // test that the SVGTransform now reflects the matrix value + checkTransform(t, SVGTransform.SVG_TRANSFORM_MATRIX, + 2, 0, + 0, 1, + 0, 10, + 0, "matrix"); + + // If ty is not supplied it is assumed to be zero + g.setAttribute("transform", "translate(5)"); + + // test that the SVGTransform now reflects the matrix value + checkTransform(t, SVGTransform.SVG_TRANSFORM_TRANSLATE, + 1, 0, + 0, 1, + 5, 0, + 0, "transform"); + + // set the SVGTransform to be a rotate() + t.setRotate(90, 0, 0); + + // test that the SVGTransform now reflects the matrix value + checkTransform(t, SVGTransform.SVG_TRANSFORM_ROTATE, + Math.cos(Math.PI/2), Math.sin(Math.PI/2), + -Math.sin(Math.PI/2), Math.cos(Math.PI/2), + 0, 0, + 90, "rotate"); + + // set the SVGTransform to be a skewX() + t.setSkewX(45); + + // test that the SVGTransform now reflects the matrix value + checkTransform(t, SVGTransform.SVG_TRANSFORM_SKEWX, + 1, 0, + Math.tan(Math.PI/4), Math.tan(Math.PI/4), + 0, 0, + 45, "skewX"); + + // set the SVGTransform to be a skewY() + t.setSkewY(45); + + // test that the SVGTransform now reflects the matrix value + checkTransform(t, SVGTransform.SVG_TRANSFORM_SKEWY, + Math.tan(Math.PI/4), Math.tan(Math.PI/4), + 0, 1, + 0, 0, + 45, "skewY"); + + // check angle is reset after changing type + t.setTranslate(10, 20); + is(t.angle, 0, "Angle not reset after changing to translate type"); + + // check read-only properties + t.angle = 40; + is(t.angle, 0, "t.angle should be read-only"); + t.type = 7; + is(t.type, SVGTransform.SVG_TRANSFORM_TRANSLATE, + "t.type should be read-only"); + t.matrix = m2; + ok(t.matrix != m2 && t.matrix.b == 0, "t.matrix should be read-only"); + + // check transform object identity after manipulation + ok(t === g.transform.baseVal.getItem(0), + "Got different transform objects after manipulation"); + ok(t.matrix === m, + "Got different matrix objects after manipulation"); + + testCreateTransform(); + + SimpleTest.finish(); +} + +function testCreateTransform() +{ + svg = $('svg'); + var t = svg.createSVGTransform(); + ok(t != svg.createSVGTransform(), + "Got identical objects when creating new transform"); + checkTransform(t, SVGTransform.SVG_TRANSFORM_MATRIX, + 1, 0, 0, 1, 0, 0, 0, "createSVGTransform"); + + var m = svg.createSVGMatrix(); + m.a = 1; + m.b = 2; + m.c = 3; + m.d = 4; + m.e = 5; + m.f = 6; + t = svg.createSVGTransformFromMatrix(m); + ok(t.matrix != m, + 'createSVGTransformFromMatrix should copy matrix not adopt it'); + m.a = 7; // Just to be sure, changing m should not affect t + checkTransform(t, SVGTransform.SVG_TRANSFORM_MATRIX, + 1, 2, 3, 4, 5, 6, 0, "createSVGTransformFromMatrix"); +} + +function checkTransform(transform, type, a, b, c, d, e, f, angle, forWhat) +{ + var m = transform.matrix; + is(transform.type, type, 'transform.type for ' + forWhat); + roughlyEqual(m.a, a, 'matrix.a for ' + forWhat); + roughlyEqual(m.b, b, 'matrix.b for ' + forWhat); + roughlyEqual(m.c, c, 'matrix.c for ' + forWhat); + roughlyEqual(m.d, d, 'matrix.d for ' + forWhat); + roughlyEqual(m.e, e, 'matrix.e for ' + forWhat); + roughlyEqual(m.f, f, 'matrix.f for ' + forWhat); + is(transform.angle, angle, 'transform.angle for ' + forWhat); +} + +function roughlyEqual(value, expected, msg) +{ + const tolerance = 1 / 65535; + ok(Math.abs(value - expected) < tolerance, + msg + ' - got ' + value + ', expected ' + expected); +} + +window.addEventListener("load", run, false); + +]]> +</script> +</pre> +</body> +</html> diff --git a/dom/svg/test/test_transformParsing.html b/dom/svg/test/test_transformParsing.html new file mode 100644 index 0000000000..d01dc77978 --- /dev/null +++ b/dom/svg/test/test_transformParsing.html @@ -0,0 +1,103 @@ +<!doctype html> +<html> +<!-- +https://bugzilla.mozilla.org/show_bug.cgi?id=946529 +--> +<head> + <meta charset="utf-8"> + <title>Test transform parsing</title> + <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" /> +</head> +<body> +<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=946529">Mozilla Bug 946529</a> +<p id="display"></p> +<div id="content" style="display: none"> + <svg width="100%" height="1" id="svg"> + <g id="g"/> + </svg> +</div> +<pre id="test"> +<script class="testbody" type="text/javascript"> + +// Test cases +checkParseOk("", [ ]); +checkParseOk("matrix(-.7235 .6903 .6903 .7235 -2050 1.14e4)", + [ { type: "matrix", a: -0.7235, b: 0.6903, c: 0.6903, + d: 0.7235, e: -2050, f: 11400 } ]); +checkParseOk("matrix(0e0 1e0 1e1 1e-1 1E+2 -.1e1)", + [ { type: "matrix", a: 0, b: 1, c: 10, + d: 0.1, e: 100, f: -1 } ]); +checkParseOk("matrix(-0e-0 1e+0 0e-5 1e-10 12.3e+4 .12e2)", + [ { type: "matrix", a: 0, b: 1, c: 0, + d: 0.0000000001, e: 123000, f: 12 } ]); + +// Fail cases +checkParseFail("matrix(1e+ 0 0 0 0 0)"); +checkParseFail("matrix(e2 0 0 0 0 0)"); +checkParseFail("matrix(1 e2 0 0 0 0 0)"); +checkParseFail("matrix(1e 2 0 0 0 0 0)"); +checkParseFail("matrix(1e+-2 0 0 0 0 0)"); +checkParseFail("matrix(1e 0 0 0 0 0)"); +checkParseFail("matrix(1e1.1 0 0 0 0 0)"); +checkParseFail("scale(2) matrix(1e1.1 0 0 0 0 0)"); + +function checkParseOk(spec, expected) { + var g = document.getElementById("g"); + + // Clear previous value + g.removeAttribute("transform"); + + g.setAttribute("transform", spec); + + // Check length + var transformList = g.transform.baseVal; + is(transformList.numberOfItems, expected.length, spec + " - length"); + if (transformList.numberOfItems != expected.length) + return; + + // Check each item + for (var i=0; i < transformList.numberOfItems; i++) { + checkTransform(transformList.getItem(i), expected[i], spec, i); + } +} + +function checkTransform(transform, expected, spec, index) { + var typeMapping = { + "unknown": SVGTransform.SVG_TRANSFORM_UNKNOWN, + "matrix": SVGTransform.SVG_TRANSFORM_MATRIX, + "translate": SVGTransform.SVG_TRANSFORM_TRANSLATE, + "scale": SVGTransform.SVG_TRANSFORM_SCALE, + "rotate": SVGTransform.SVG_TRANSFORM_ROTATE, + "skewx": SVGTransform.SVG_TRANSFORM_SKEWX, + "skewy": SVGTransform.SVG_TRANSFORM_SKEWY + }; + var name = "Item " + index + " of '" + spec + "'"; + + // Compare type + if (typeof expected.type != "undefined") { + is(transform.type, typeMapping[expected.type], name + " - transform type"); + } + + // Compare angle + if (typeof expected.angle != "undefined") { + is(transform.angle, expected.angle, name + " - angle"); + } + + // Compare matrix values (roughly) + ['a', 'b', 'c', 'd', 'e', 'f'].forEach(function(item) { + var actual = transform.matrix[item]; + var msg = name + " - matrix:" + item; + const tolerance = 1 / 65535; + ok(Math.abs(actual - expected[item]) < tolerance, + msg + ' - got ' + actual + ', expected ' + expected[item]); + }); +} + +function checkParseFail(spec) { + checkParseOk(spec, []); +} +</script> +</pre> +</body> +</html> diff --git a/dom/svg/test/test_use_with_hsts.html b/dom/svg/test/test_use_with_hsts.html new file mode 100644 index 0000000000..1c5952707d --- /dev/null +++ b/dom/svg/test/test_use_with_hsts.html @@ -0,0 +1,139 @@ +<!DOCTYPE HTML> +<html> +<!-- +https://bugzilla.mozilla.org/show_bug.cgi?id=1247733 +--> +<head> + <meta charset="utf-8"> + <title>Test for Bug 1247733</title> + <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <script type="application/javascript" src="/tests/SimpleTest/WindowSnapshot.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> +</head> +<body> +<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1247733">Mozilla Bug 1247733</a> +<p id="display"> + <iframe id="myIframe"></iframe> +</p> +<div id="content" style="display: none"> + +</div> +<pre id="test"></pre> +<script type="application/javascript"> + /** Test for Bug 1247733 **/ + + /** + * This test ensures that we render the SVG 'use' element correctly, in + * pages that have been upgraded from HTTP to HTTPS using strict transport + * security (HSTS) + * + * Specifically: + * (1) We load a file using HTTPS, in an iframe. The file gets sent + * with a Strict-Transport-Security flag. + * (2) We load the same file again, but now over HTTP (which should get + * upgraded to HTTPS, since we received the Strict-Transport-Security + * flag during the first load). + * (3) After each of the above loads, we take a snapshot of the iframe + * and ensure that it renders as fully lime (which the 'use' element + * is responsible for). If the 'use' element fails to render, the iframe + * will be fully red, and we'll fail an "assertSnapshots" check. + */ + SimpleTest.waitForExplicitFinish(); + + const iframe = document.getElementById("myIframe"); + const iframeWin = iframe.contentWindow; + + // URI for our testcase with 'use' element, via HTTP and HTTPS: + const insecureURI = "http://example.com/tests/dom/svg/test/use-with-hsts-helper.html"; + const secureURI = "https://example.com/tests/dom/svg/test/use-with-hsts-helper.html"; + + // Snapshots that we'll populate below: + var blankSnapshot; // Snapshot of blank iframe. + var refSnapshot; // Snapshot of lime reference rendering in iframe. + var secureSnapshot; // Snapshot of testcase using HTTPS. + var upgradedSnapshot; // Snapshot of testcase using HTTP-upgraded-to-HTTPS. + + // Bookkeeping to be sure receiveMessage is called as many times as we expect: + var numPostMessageCalls = 0; + const expectedNumPostMessageCalls = 2; // (We load the helper file twice.) + + // Helper function, called via postMessage, to check iframe's actual location: + function receiveMessage(event) { + is(event.data, secureURI, "iframe should end up viewing secure URI"); + numPostMessageCalls++; + } + + // TEST CODE BEGINS HERE. + // Execution basically proceeds top-to-bottom through the functions + // from this point on, via a chain of iframe onload-callbacks. + function runTest() { + // Capture a snapshot with nothing in the iframe, so we can do a + // sanity-check not-equal comparison against our reference case, to be + // sure we're rendering anything at all: + blankSnapshot = snapshotWindow(iframeWin); + + // Point iframe at a reference case: + iframe.onload = captureRefSnapshot; + iframe.src = "data:text/html,<body style='background:lime'>"; + } + + function captureRefSnapshot() { + // Capture the reference screenshot: + refSnapshot = snapshotWindow(iframeWin); + + // Ensure reference-case looks different from blank snapshot: + assertSnapshots(refSnapshot, blankSnapshot, + false /*not equal*/, null /*no fuzz*/, + "refSnapshot", "blankSnapshot"); + + // OK, assuming we've got a valid refSnapshot, we can now proceed to + // capture test screenshots. + + // Register a postMessage handler, so that iframe can report its location: + window.addEventListener("message", receiveMessage, false); + + // Point iframe at secure (HTTPS) version of testcase, & wait for callback: + iframe.onload = captureSecureSnapshot; + iframe.src = secureURI; + } + + function captureSecureSnapshot() { + // Capture snapshot of iframe showing always-HTTPS version of testcase: + secureSnapshot = snapshotWindow(iframeWin); + assertSnapshots(secureSnapshot, refSnapshot, + true /*equal*/, null /*no fuzz*/, + "secureSnapshot", "refSnapshot"); + + // Point iframe at insecure (HTTP) version of testcase (which should get + // automatically upgraded to secure (HTTPS) under the hood), & wait for + // callback: + iframe.onload = captureUpgradedSnapshot; + iframe.src = insecureURI; + } + + function captureUpgradedSnapshot() { + // Double-check that iframe is really pointed at insecure URI, to be sure + // we're actually exercising HSTS. (Note that receiveMessage() will make + // sure it's been upgraded to a secure HTTPS URI under the hood.) + is(iframe.src, insecureURI, + "test should've attempted to load insecure HTTP URI, to exercise HSTS"); + + // Capture snapshot of iframe showing upgraded-to-HTTPS version of testcase: + upgradedSnapshot = snapshotWindow(iframeWin); + assertSnapshots(upgradedSnapshot, refSnapshot, + true /*equal*/, null /*no fuzz*/, + "upgradedSnapshot", "refSnapshot"); + cleanupAndFinish(); + } + + function cleanupAndFinish() { + is(numPostMessageCalls, expectedNumPostMessageCalls, + "didn't receive as many messages from child iframe as expected"); + SpecialPowers.cleanUpSTSData("http://example.com"); + SimpleTest.finish(); + } + + runTest(); +</script> +</body> +</html> diff --git a/dom/svg/test/test_valueAsString.xhtml b/dom/svg/test/test_valueAsString.xhtml new file mode 100644 index 0000000000..8dc7da46b8 --- /dev/null +++ b/dom/svg/test/test_valueAsString.xhtml @@ -0,0 +1,65 @@ +<html xmlns="http://www.w3.org/1999/xhtml"> +<!-- +https://bugzilla.mozilla.org/show_bug.cgi?id=539697 +--> +<head> + <title>Test valueAsString behavior</title> + <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" /> +</head> +<body> +<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=539697">Mozilla Bug 539697</a> +<p id="display"></p> +<div id="content"> + + <svg xmlns="http://www.w3.org/2000/svg" width="100%" height="1" id="svg"> + <circle id='c' r='1em' display='none'/> + <marker id='m' orient='20rad' display='none'/> + </svg> + +</div> +<pre id="test"> +<script class="testbody" type="text/javascript"> +<![CDATA[ + +SimpleTest.waitForExplicitFinish(); + +function run() +{ + var c = document.getElementById('c'); + var m = document.getElementById('m'); + + is(SVGLength.SVG_LENGTHTYPE_EMS, c.r.baseVal.unitType, 'unexpected units'); + c.r.baseVal.valueAsString = '2px'; + is(SVGLength.SVG_LENGTHTYPE_PX, c.r.baseVal.unitType, 'unexpected units'); + + try { + c.r.baseVal.valueAsString = 'rubbish'; + ok(false, 'setting a length to rubbish should fail'); + } catch (e) { + is(e.name, 'SyntaxError', 'syntax error expected'); + is(e.code, DOMException.SYNTAX_ERR, 'syntax error expected'); + } + + is(SVGAngle.SVG_ANGLETYPE_RAD, m.orientAngle.baseVal.unitType, 'unexpected units'); + m.orientAngle.baseVal.valueAsString = '2grad'; + is(SVGAngle.SVG_ANGLETYPE_GRAD, m.orientAngle.baseVal.unitType, 'unexpected units'); + + try { + m.orientAngle.baseVal.valueAsString = 'rubbish'; + ok(false, 'setting an angle to rubbish should fail'); + } catch (e) { + is(e.name, 'SyntaxError', 'syntax error expected'); + is(e.code, DOMException.SYNTAX_ERR, 'syntax error expected'); + } + + SimpleTest.finish(); +} + +window.addEventListener("load", run, false); + +]]> +</script> +</pre> +</body> +</html> diff --git a/dom/svg/test/test_valueLeaks.xhtml b/dom/svg/test/test_valueLeaks.xhtml new file mode 100644 index 0000000000..d80a5cf25f --- /dev/null +++ b/dom/svg/test/test_valueLeaks.xhtml @@ -0,0 +1,85 @@ +<html xmlns="http://www.w3.org/1999/xhtml"> +<!-- +https://bugzilla.mozilla.org/show_bug.cgi?id=467671 +--> +<head> + <title>Test for Bug 467671</title> + <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> +</head> +<body> +<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=467671">Mozilla Bug 467671</a> +<p id="display"></p> +<div id="content" style="display: none"> + +</div> +<pre id="test"> +<script type="application/javascript"> +<![CDATA[ + +/** Test for Bug 467671 **/ + +function storeSVGPropertyAsExpando(localName, prop) +{ + var elem = document.createElementNS("http://www.w3.org/2000/svg", localName); + + elem.addEventListener("click", function(){}, false); + + var propVal = elem[prop]; + Object.prototype.toSource[prop + "_expando"] = propVal; + if (propVal instanceof SVGAnimatedAngle || propVal instanceof SVGAnimatedLength || + propVal instanceof SVGAnimatedRect || propVal instanceof SVGAnimatedPreserveAspectRatio) { + Object.prototype.toSource[prop + "_baseVal_expando"] = propVal.baseVal; + Object.prototype.toSource[prop + "_animVal_expando"] = propVal.animVal; + } +} + +// class +storeSVGPropertyAsExpando("marker", "class"); + +// angle +storeSVGPropertyAsExpando("marker", "orientAngle"); + +// viewBox +storeSVGPropertyAsExpando("marker", "viewBox"); + +// preserveAspectRatio +storeSVGPropertyAsExpando("marker", "preserveAspectRatio"); + +// boolean +storeSVGPropertyAsExpando("feConvolveMatrix", "preserveAlpha"); + +// enum +storeSVGPropertyAsExpando("feConvolveMatrix", "edgeMode"); + +// special marker enum +storeSVGPropertyAsExpando("marker", "orientType"); + +// integer +storeSVGPropertyAsExpando("feConvolveMatrix", "orderX"); + +// length +storeSVGPropertyAsExpando("feConvolveMatrix", "x"); + +// number +storeSVGPropertyAsExpando("feConvolveMatrix", "divisor"); + +// string +storeSVGPropertyAsExpando("feConvolveMatrix", "in1"); + +var elem1 = document.createElementNS("http://www.w3.org/2000/svg", "switch"); +var elem2 = document.createElementNS("http://www.w3.org/2000/svg", "rect"); +elem1.appendChild(elem2); +document.getElementById("content").appendChild(elem1); + +elem2.addEventListener("click", function(){}, false); + +Object.prototype.toSource.expando = elem1; + +ok(true, "SVG shouldn't leak."); + +]]> +</script> +</pre> +</body> +</html> diff --git a/dom/svg/test/test_viewport.html b/dom/svg/test/test_viewport.html new file mode 100644 index 0000000000..ca8da04979 --- /dev/null +++ b/dom/svg/test/test_viewport.html @@ -0,0 +1,78 @@ +<!DOCTYPE html> +<html xmlns="http://www.w3.org/1999/xhtml"> +<!-- +https://bugzilla.mozilla.org/show_bug.cgi?id=483389 +--> +<head> + <title>Test for Bug 483389</title> + <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" /> +</head> +<body> +<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=483389">Mozilla Bug 483389</a> +<p id="display"></p> +<div id="content" style="display: none"></div> + +<iframe id="svg" src="viewport-helper.svg"></iframe> + +<pre id="test"> +<script class="testbody" type="application/javascript"> +SimpleTest.waitForExplicitFinish(); + +function runTest() +{ + var doc = $("svg").contentWindow.document; + + var root = doc.documentElement; + var inner = doc.getElementById("inner"); + var g1 = doc.getElementById("g1"); + var outer = doc.getElementById("outer"); + var g2 = doc.getElementById("g2"); + var g3 = doc.getElementById("g3"); + var sym = doc.getElementById("sym"); + var symbolRect = doc.getElementById("symbolRect"); + + <!-- ownerSVGElement --> + is(root.ownerSVGElement, null, "root.ownerSVGElement"); + is(inner.ownerSVGElement, root, "inner.ownerSVGElement"); + is(g1.ownerSVGElement, inner, "g1.ownerSVGElement"); + is(outer.ownerSVGElement, null, "outer.ownerSVGElement"); + is(g2.ownerSVGElement, outer, "g2.ownerSVGElement"); + is(g3.ownerSVGElement, null, "g3.ownerSVGElement"); + is(symbolRect.ownerSVGElement, root, "symbolRect.ownerSVGElement"); + + <!-- viewportElement --> + is(root.viewportElement, null, "root.viewportElement"); + is(inner.viewportElement, root, "inner.viewportElement"); + is(g1.viewportElement, inner, "g1.viewportElement"); + is(outer.viewportElement, null, "outer.viewportElement"); + is(g2.viewportElement, outer, "g2.viewportElement"); + is(g3.viewportElement, null, "g3.viewportElement"); + is(symbolRect.viewportElement, sym, "symbolRect.viewportElement"); + + <!-- nearestViewportElement --> + is(root.nearestViewportElement, null, "root.nearestViewportElement"); + is(inner.nearestViewportElement, root, "inner.nearestViewportElement"); + is(g1.nearestViewportElement, inner, "g1.nearestViewportElement"); + is(outer.nearestViewportElement, null, "outer.nearestViewportElement"); + is(g2.nearestViewportElement, outer, "g2.nearestViewportElement"); + is(g3.nearestViewportElement, null, "g3.nearestViewportElement"); + is(symbolRect.nearestViewportElement, sym, "symbolRect.nearestViewportElement"); + + <!-- farthestViewportElement --> + is(root.farthestViewportElement, null, "root.farthestViewportElement"); + is(inner.farthestViewportElement, root, "inner.farthestViewportElement"); + is(g1.farthestViewportElement, root, "g1.farthestViewportElement"); + is(outer.farthestViewportElement, null, "outer.farthestViewportElement"); + is(g2.farthestViewportElement, outer, "g2.farthestViewportElement"); + is(g3.farthestViewportElement, null, "g3.farthestViewportElement"); + is(symbolRect.farthestViewportElement, root, "symbolRect.farthestViewportElement"); + + SimpleTest.finish(); +} + +window.addEventListener("load", runTest, false); +</script> +</pre> +</body> +</html> diff --git a/dom/svg/test/test_zoom.xhtml b/dom/svg/test/test_zoom.xhtml new file mode 100644 index 0000000000..ce920b29db --- /dev/null +++ b/dom/svg/test/test_zoom.xhtml @@ -0,0 +1,35 @@ +<!DOCTYPE html> +<html xmlns="http://www.w3.org/1999/xhtml"> +<!-- +https://bugzilla.mozilla.org/show_bug.cgi?id=547596 +--> +<head> + <title>Test for Bug 547596</title> + <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" /> +</head> +<body> + <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=547596">Mozilla Bug 547596</a> + <p id="display"></p> + <div id="content" style="display: none"></div> + + <iframe id="svg" src="zoom-helper.svg"></iframe> + + <script> + SimpleTest.waitForExplicitFinish(); + + function runTests() { + var doc = $("svg").contentWindow.document; + var root = doc.getElementById("root"); + root.currentScale *= 2; + } + + function zoomHandler(evt) { + ok(true, "zoom handler should be called on zoom"); + SimpleTest.finish(); + } + + window.addEventListener("load", runTests, false); + </script> +</body> +</html> diff --git a/dom/svg/test/text-helper-scaled.svg b/dom/svg/test/text-helper-scaled.svg new file mode 100644 index 0000000000..3d534e3fd3 --- /dev/null +++ b/dom/svg/test/text-helper-scaled.svg @@ -0,0 +1,8 @@ +<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="3000" height="2000"> + <g id="g" style="font: 400px monospace"> + <text id="text1" x="10" y="400">abc</text> + + <path id="MyPath" d="M 5 800 h 2000 v 60 h -2000 z" stroke="red" fill="none"/> + <text id="text2"><textPath xlink:href="#MyPath">abcdefghijklmnopqrstuvwxyz</textPath></text> + </g> +</svg> diff --git a/dom/svg/test/text-helper-selection.svg b/dom/svg/test/text-helper-selection.svg new file mode 100644 index 0000000000..df84a19ac4 --- /dev/null +++ b/dom/svg/test/text-helper-selection.svg @@ -0,0 +1,23 @@ +<svg xmlns="http://www.w3.org/2000/svg" width="400" height="300" + style="font: 24px monospace"> + + <!-- We need these two rects so that getBoundingClientRect of the <svg> does + not just return the region covered by the <text>, which would result in + the synthesizeMouse calls using the wrong positions. We don't use one + big rect because that could interfere with text selection when dragging + outside the bounds of text elements. --> + <rect width="10" height="10" fill="white"/> + <rect x="350" y="250" width="10" height="10" fill="white"/> + + <text x="100" y="50">hello there</text> + <text x="100" y="100">to you all!</text> + <text x="200" y="150">abc<tspan x="100" dy="10 -10">def</tspan></text> + <text x="100" y="200">אבגabc</text> + <text x="100" y="250" transform="scale(0.5,1)translate(100)">squashed</text> + + <!-- These two circles are just used for debugging the test; passing true + as the last argument to drag() will place these circles at the drag + start and end points. --> + <circle id="dragstart" fill="blue"/> + <circle id="dragend" fill="red"/> +</svg> diff --git a/dom/svg/test/text-helper.svg b/dom/svg/test/text-helper.svg new file mode 100644 index 0000000000..1bdff86f26 --- /dev/null +++ b/dom/svg/test/text-helper.svg @@ -0,0 +1,19 @@ +<?xml version="1.0"?> +<svg xmlns="http://www.w3.org/2000/svg" width="750" + xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1"> + <style type="text/css"> +text { font: 20px monospace; } + </style> + +<g id="g"> + <text id="text1" x="5" y="25">abc</text> + + <path id="MyPath2" d="M 100 125 L 100 200" stroke="red" fill="none"/> + <text id="text2"><textPath xlink:href="#MyPath2">abc</textPath></text> + + <path id="MyPath" d="M 5 250 L 105 250 L 105 253 L 5 253 z" stroke="red" fill="none"/> + <text id="text3"><textPath xlink:href="#MyPath">abcdefghijklmnopqrstuvwxyz</textPath></text> + + <text display="none" id="text4" x="5" y="25">abc</text> +</g> +</svg> diff --git a/dom/svg/test/use-with-hsts-helper.html b/dom/svg/test/use-with-hsts-helper.html new file mode 100644 index 0000000000..409dade7c6 --- /dev/null +++ b/dom/svg/test/use-with-hsts-helper.html @@ -0,0 +1,30 @@ +<!DOCTYPE html> +<html> +<head> + <script> + // Notify parent of our final URI: + window.parent.postMessage(window.location.href, "*"); + </script> + <style> + html, body { + margin: 0; + height: 100%; + } + svg { + display: block; + height: 100%; + } + </style> +</head> +<body> + <svg xmlns="http://www.w3.org/2000/svg" + xmlns:xlink="http://www.w3.org/1999/xlink" + version="1.1"> + <defs> + <rect id="limeRect" width="100%" height="100%" fill="lime"/> + </defs> + <rect width="100%" height="100%" fill="red"/> + <use xlink:href="#limeRect"/> + </svg> +</body> +</html> diff --git a/dom/svg/test/use-with-hsts-helper.html^headers^ b/dom/svg/test/use-with-hsts-helper.html^headers^ new file mode 100644 index 0000000000..a46bf65bd9 --- /dev/null +++ b/dom/svg/test/use-with-hsts-helper.html^headers^ @@ -0,0 +1,2 @@ +Cache-Control: no-cache +Strict-Transport-Security: max-age=60 diff --git a/dom/svg/test/viewport-helper.svg b/dom/svg/test/viewport-helper.svg new file mode 100644 index 0000000000..c553b43a1f --- /dev/null +++ b/dom/svg/test/viewport-helper.svg @@ -0,0 +1,26 @@ +<?xml version="1.0"?> +<svg xmlns="http://www.w3.org/2000/svg" width="750" + xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1"> + + <defs> + <symbol id="sym"> + <rect width="0" height="0" id="symbolRect"/> + </symbol> + </defs> + <svg id="inner"> + <g id="g1"> + </g> + </svg> + <foreignObject> + <svg id="outer"> + <g id="g2"> + </g> + </svg> + </foreignObject> + <!-- something invalid --> + <foreignObject> + <g id="g3"> + </g> + </foreignObject> + <use xlink:href="#sym" id="use"/> +</svg> diff --git a/dom/svg/test/zoom-helper.svg b/dom/svg/test/zoom-helper.svg new file mode 100644 index 0000000000..a9e40cdaa8 --- /dev/null +++ b/dom/svg/test/zoom-helper.svg @@ -0,0 +1,4 @@ +<?xml version="1.0"?> +<svg id="root" version="1.1" xmlns="http://www.w3.org/2000/svg" onzoom="parent.zoomHandler(evt)"> + <rect x="10" y="10" width="10" height="10" fill="green" /> +</svg> |