1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4  * License, v. 2.0. If a copy of the MPL was not distributed with this
5  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 
7 #ifndef DOM_SVG_SVGPOINTLIST_H_
8 #define DOM_SVG_SVGPOINTLIST_H_
9 
10 #include "nsCOMPtr.h"
11 #include "nsDebug.h"
12 #include "nsIContent.h"
13 #include "nsINode.h"
14 #include "nsIWeakReferenceUtils.h"
15 #include "SVGElement.h"
16 #include "nsTArray.h"
17 #include "SVGPoint.h"
18 
19 #include <string.h>
20 
21 namespace mozilla {
22 
23 namespace dom {
24 class DOMSVGPoint;
25 class DOMSVGPointList;
26 }  // namespace dom
27 
28 /**
29  * ATTENTION! WARNING! WATCH OUT!!
30  *
31  * Consumers that modify objects of this type absolutely MUST keep the DOM
32  * wrappers for those lists (if any) in sync!! That's why this class is so
33  * locked down.
34  *
35  * The DOM wrapper class for this class is DOMSVGPointList.
36  */
37 class SVGPointList {
38   friend class SVGAnimatedPointList;
39   friend class dom::DOMSVGPointList;
40   friend class dom::DOMSVGPoint;
41 
42  public:
43   SVGPointList() = default;
44   ~SVGPointList() = default;
45 
46   // Only methods that don't make/permit modification to this list are public.
47   // Only our friend classes can access methods that may change us.
48 
49   /// This may return an incomplete string on OOM, but that's acceptable.
50   void GetValueAsString(nsAString& aValue) const;
51 
IsEmpty()52   bool IsEmpty() const { return mItems.IsEmpty(); }
53 
Length()54   uint32_t Length() const { return mItems.Length(); }
55 
56   const SVGPoint& operator[](uint32_t aIndex) const { return mItems[aIndex]; }
57 
58   bool operator==(const SVGPointList& rhs) const {
59     // memcmp can be faster than |mItems == rhs.mItems|
60     return mItems.Length() == rhs.mItems.Length() &&
61            memcmp(mItems.Elements(), rhs.mItems.Elements(),
62                   mItems.Length() * sizeof(SVGPoint)) == 0;
63   }
64 
SetCapacity(uint32_t aSize)65   bool SetCapacity(uint32_t aSize) {
66     return mItems.SetCapacity(aSize, fallible);
67   }
68 
Compact()69   void Compact() { mItems.Compact(); }
70 
71   // Access to methods that can modify objects of this type is deliberately
72   // limited. This is to reduce the chances of someone modifying objects of
73   // this type without taking the necessary steps to keep DOM wrappers in sync.
74   // If you need wider access to these methods, consider adding a method to
75   // SVGAnimatedPointList and having that class act as an intermediary so it
76   // can take care of keeping DOM wrappers in sync.
77 
78  protected:
79   /**
80    * This may fail on OOM if the internal capacity needs to be increased, in
81    * which case the list will be left unmodified.
82    */
83   nsresult CopyFrom(const SVGPointList& rhs);
84 
85   SVGPoint& operator[](uint32_t aIndex) { return mItems[aIndex]; }
86 
87   /**
88    * This may fail (return false) on OOM if the internal capacity is being
89    * increased, in which case the list will be left unmodified.
90    */
SetLength(uint32_t aNumberOfItems)91   bool SetLength(uint32_t aNumberOfItems) {
92     return mItems.SetLength(aNumberOfItems, fallible);
93   }
94 
95  private:
96   // Marking the following private only serves to show which methods are only
97   // used by our friend classes (as opposed to our subclasses) - it doesn't
98   // really provide additional safety.
99 
100   nsresult SetValueFromString(const nsAString& aValue);
101 
Clear()102   void Clear() { mItems.Clear(); }
103 
InsertItem(uint32_t aIndex,const SVGPoint & aPoint)104   bool InsertItem(uint32_t aIndex, const SVGPoint& aPoint) {
105     if (aIndex >= mItems.Length()) {
106       aIndex = mItems.Length();
107     }
108     return !!mItems.InsertElementAt(aIndex, aPoint, fallible);
109   }
110 
ReplaceItem(uint32_t aIndex,const SVGPoint & aPoint)111   void ReplaceItem(uint32_t aIndex, const SVGPoint& aPoint) {
112     MOZ_ASSERT(aIndex < mItems.Length(),
113                "DOM wrapper caller should have raised INDEX_SIZE_ERR");
114     mItems[aIndex] = aPoint;
115   }
116 
RemoveItem(uint32_t aIndex)117   void RemoveItem(uint32_t aIndex) {
118     MOZ_ASSERT(aIndex < mItems.Length(),
119                "DOM wrapper caller should have raised INDEX_SIZE_ERR");
120     mItems.RemoveElementAt(aIndex);
121   }
122 
AppendItem(SVGPoint aPoint)123   bool AppendItem(SVGPoint aPoint) {
124     return !!mItems.AppendElement(aPoint, fallible);
125   }
126 
127  protected:
128   /* See SVGLengthList for the rationale for using FallibleTArray<SVGPoint>
129    * instead of FallibleTArray<SVGPoint, 1>.
130    */
131   FallibleTArray<SVGPoint> mItems;
132 };
133 
134 /**
135  * This SVGPointList subclass is for SVGPointListSMILType which needs a
136  * mutable version of SVGPointList. Instances of this class do not have
137  * DOM wrappers that need to be kept in sync, so we can safely expose any
138  * protected base class methods required by the SMIL code.
139  *
140  * This class contains a strong reference to the element that instances of
141  * this class are being used to animate. This is because the SMIL code stores
142  * instances of this class in SMILValue objects, some of which are cached.
143  * Holding a strong reference to the element here prevents the element from
144  * disappearing out from under the SMIL code unexpectedly.
145  */
146 class SVGPointListAndInfo : public SVGPointList {
147  public:
148   explicit SVGPointListAndInfo(dom::SVGElement* aElement = nullptr)
mElement(do_GetWeakReference (static_cast<nsINode * > (aElement)))149       : mElement(do_GetWeakReference(static_cast<nsINode*>(aElement))) {}
150 
SetInfo(dom::SVGElement * aElement)151   void SetInfo(dom::SVGElement* aElement) {
152     mElement = do_GetWeakReference(static_cast<nsINode*>(aElement));
153   }
154 
Element()155   dom::SVGElement* Element() const {
156     nsCOMPtr<nsIContent> e = do_QueryReferent(mElement);
157     return static_cast<dom::SVGElement*>(e.get());
158   }
159 
160   /**
161    * Returns true if this object is an "identity" value, from the perspective
162    * of SMIL. In other words, returns true until the initial value set up in
163    * SVGPointListSMILType::Init() has been changed with a SetInfo() call.
164    */
IsIdentity()165   bool IsIdentity() const {
166     if (!mElement) {
167       MOZ_ASSERT(IsEmpty(), "target element propagation failure");
168       return true;
169     }
170     return false;
171   }
172 
CopyFrom(const SVGPointListAndInfo & rhs)173   nsresult CopyFrom(const SVGPointListAndInfo& rhs) {
174     mElement = rhs.mElement;
175     return SVGPointList::CopyFrom(rhs);
176   }
177 
178   /**
179    * Exposed so that SVGPointList baseVals can be copied to
180    * SVGPointListAndInfo objects. Note that callers should also call
181    * SetElement() when using this method!
182    */
CopyFrom(const SVGPointList & rhs)183   nsresult CopyFrom(const SVGPointList& rhs) {
184     return SVGPointList::CopyFrom(rhs);
185   }
186   const SVGPoint& operator[](uint32_t aIndex) const {
187     return SVGPointList::operator[](aIndex);
188   }
189   SVGPoint& operator[](uint32_t aIndex) {
190     return SVGPointList::operator[](aIndex);
191   }
SetLength(uint32_t aNumberOfItems)192   bool SetLength(uint32_t aNumberOfItems) {
193     return SVGPointList::SetLength(aNumberOfItems);
194   }
195 
196  private:
197   // We must keep a weak reference to our element because we may belong to a
198   // cached baseVal SMILValue. See the comments starting at:
199   // https://bugzilla.mozilla.org/show_bug.cgi?id=515116#c15
200   // See also https://bugzilla.mozilla.org/show_bug.cgi?id=653497
201   nsWeakPtr mElement;
202 };
203 
204 }  // namespace mozilla
205 
206 #endif  // DOM_SVG_SVGPOINTLIST_H_
207