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_SVGNUMBERLIST_H_
8 #define DOM_SVG_SVGNUMBERLIST_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 
18 namespace mozilla {
19 
20 namespace dom {
21 class DOMSVGNumber;
22 class DOMSVGNumberList;
23 }  // namespace dom
24 
25 /**
26  * ATTENTION! WARNING! WATCH OUT!!
27  *
28  * Consumers that modify objects of this type absolutely MUST keep the DOM
29  * wrappers for those lists (if any) in sync!! That's why this class is so
30  * locked down.
31  *
32  * The DOM wrapper class for this class is DOMSVGNumberList.
33  */
34 class SVGNumberList {
35   friend class dom::DOMSVGNumber;
36   friend class dom::DOMSVGNumberList;
37   friend class SVGAnimatedNumberList;
38 
39  public:
40   SVGNumberList() = default;
41   ~SVGNumberList() = default;
42 
43   SVGNumberList& operator=(const SVGNumberList& aOther) {
44     mNumbers.ClearAndRetainStorage();
45     // Best-effort, really.
46     Unused << mNumbers.AppendElements(aOther.mNumbers, fallible);
47     return *this;
48   }
49 
SVGNumberList(const SVGNumberList & aOther)50   SVGNumberList(const SVGNumberList& aOther) { *this = aOther; }
51 
52   // Only methods that don't make/permit modification to this list are public.
53   // Only our friend classes can access methods that may change us.
54 
55   /// This may return an incomplete string on OOM, but that's acceptable.
56   void GetValueAsString(nsAString& aValue) const;
57 
IsEmpty()58   bool IsEmpty() const { return mNumbers.IsEmpty(); }
59 
Length()60   uint32_t Length() const { return mNumbers.Length(); }
61 
62   const float& operator[](uint32_t aIndex) const { return mNumbers[aIndex]; }
63 
64   bool operator==(const SVGNumberList& rhs) const {
65     return mNumbers == rhs.mNumbers;
66   }
67 
SetCapacity(uint32_t size)68   bool SetCapacity(uint32_t size) {
69     return mNumbers.SetCapacity(size, fallible);
70   }
71 
Compact()72   void Compact() { mNumbers.Compact(); }
73 
74   // Access to methods that can modify objects of this type is deliberately
75   // limited. This is to reduce the chances of someone modifying objects of
76   // this type without taking the necessary steps to keep DOM wrappers in sync.
77   // If you need wider access to these methods, consider adding a method to
78   // SVGAnimatedNumberList and having that class act as an intermediary so it
79   // can take care of keeping DOM wrappers in sync.
80 
81  protected:
82   /**
83    * This may fail on OOM if the internal capacity needs to be increased, in
84    * which case the list will be left unmodified.
85    */
86   nsresult CopyFrom(const SVGNumberList& rhs);
87 
88   float& operator[](uint32_t aIndex) { return mNumbers[aIndex]; }
89 
90   /**
91    * This may fail (return false) on OOM if the internal capacity is being
92    * increased, in which case the list will be left unmodified.
93    */
SetLength(uint32_t aNumberOfItems)94   bool SetLength(uint32_t aNumberOfItems) {
95     return mNumbers.SetLength(aNumberOfItems, fallible);
96   }
97 
98  private:
99   // Marking the following private only serves to show which methods are only
100   // used by our friend classes (as opposed to our subclasses) - it doesn't
101   // really provide additional safety.
102 
103   nsresult SetValueFromString(const nsAString& aValue);
104 
Clear()105   void Clear() { mNumbers.Clear(); }
106 
InsertItem(uint32_t aIndex,const float & aNumber)107   bool InsertItem(uint32_t aIndex, const float& aNumber) {
108     if (aIndex >= mNumbers.Length()) {
109       aIndex = mNumbers.Length();
110     }
111     return !!mNumbers.InsertElementAt(aIndex, aNumber, fallible);
112   }
113 
ReplaceItem(uint32_t aIndex,const float & aNumber)114   void ReplaceItem(uint32_t aIndex, const float& aNumber) {
115     MOZ_ASSERT(aIndex < mNumbers.Length(),
116                "DOM wrapper caller should have raised INDEX_SIZE_ERR");
117     mNumbers[aIndex] = aNumber;
118   }
119 
RemoveItem(uint32_t aIndex)120   void RemoveItem(uint32_t aIndex) {
121     MOZ_ASSERT(aIndex < mNumbers.Length(),
122                "DOM wrapper caller should have raised INDEX_SIZE_ERR");
123     mNumbers.RemoveElementAt(aIndex);
124   }
125 
AppendItem(float aNumber)126   bool AppendItem(float aNumber) {
127     return !!mNumbers.AppendElement(aNumber, fallible);
128   }
129 
130  protected:
131   /* See SVGLengthList for the rationale for using FallibleTArray<float> instead
132    * of FallibleTArray<float, 1>.
133    */
134   FallibleTArray<float> mNumbers;
135 };
136 
137 /**
138  * This SVGNumberList subclass is used by the SMIL code when a number list
139  * is to be stored in a SMILValue instance. Since SMILValue objects may
140  * be cached, it is necessary for us to hold a strong reference to our element
141  * so that it doesn't disappear out from under us if, say, the element is
142  * removed from the DOM tree.
143  */
144 class SVGNumberListAndInfo : public SVGNumberList {
145  public:
SVGNumberListAndInfo()146   SVGNumberListAndInfo() : mElement(nullptr) {}
147 
SVGNumberListAndInfo(dom::SVGElement * aElement)148   explicit SVGNumberListAndInfo(dom::SVGElement* 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 
CopyFrom(const SVGNumberListAndInfo & rhs)160   nsresult CopyFrom(const SVGNumberListAndInfo& rhs) {
161     mElement = rhs.mElement;
162     return SVGNumberList::CopyFrom(rhs);
163   }
164 
165   // Instances of this special subclass do not have DOM wrappers that we need
166   // to worry about keeping in sync, so it's safe to expose any hidden base
167   // class methods required by the SMIL code, as we do below.
168 
169   /**
170    * Exposed so that SVGNumberList baseVals can be copied to
171    * SVGNumberListAndInfo objects. Note that callers should also call
172    * SetInfo() when using this method!
173    */
CopyFrom(const SVGNumberList & rhs)174   nsresult CopyFrom(const SVGNumberList& rhs) {
175     return SVGNumberList::CopyFrom(rhs);
176   }
177   const float& operator[](uint32_t aIndex) const {
178     return SVGNumberList::operator[](aIndex);
179   }
180   float& operator[](uint32_t aIndex) {
181     return SVGNumberList::operator[](aIndex);
182   }
SetLength(uint32_t aNumberOfItems)183   bool SetLength(uint32_t aNumberOfItems) {
184     return SVGNumberList::SetLength(aNumberOfItems);
185   }
186 
187  private:
188   // We must keep a weak reference to our element because we may belong to a
189   // cached baseVal SMILValue. See the comments starting at:
190   // https://bugzilla.mozilla.org/show_bug.cgi?id=515116#c15
191   // See also https://bugzilla.mozilla.org/show_bug.cgi?id=653497
192   nsWeakPtr mElement;
193 };
194 
195 }  // namespace mozilla
196 
197 #endif  // DOM_SVG_SVGNUMBERLIST_H_
198