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 /*
8  * Storage of the children and attributes of a DOM node; storage for
9  * the two is unified to minimize footprint.
10  */
11 
12 #ifndef nsAttrAndChildArray_h___
13 #define nsAttrAndChildArray_h___
14 
15 #include "mozilla/Attributes.h"
16 #include "mozilla/MemoryReporting.h"
17 #include "mozilla/dom/BorrowedAttrInfo.h"
18 
19 #include "nscore.h"
20 #include "nsAttrName.h"
21 #include "nsAttrValue.h"
22 #include "nsCaseTreatment.h"
23 
24 class nsINode;
25 class nsIContent;
26 class nsMappedAttributes;
27 class nsHTMLStyleSheet;
28 class nsRuleWalker;
29 class nsMappedAttributeElement;
30 
31 #define ATTRCHILD_ARRAY_GROWSIZE 8
32 #define ATTRCHILD_ARRAY_LINEAR_THRESHOLD 32
33 
34 #define ATTRCHILD_ARRAY_ATTR_SLOTS_BITS 10
35 
36 #define ATTRCHILD_ARRAY_MAX_ATTR_COUNT \
37     ((1 << ATTRCHILD_ARRAY_ATTR_SLOTS_BITS) - 1)
38 
39 #define ATTRCHILD_ARRAY_MAX_CHILD_COUNT \
40     (~uint32_t(0) >> ATTRCHILD_ARRAY_ATTR_SLOTS_BITS)
41 
42 #define ATTRCHILD_ARRAY_ATTR_SLOTS_COUNT_MASK \
43     ((1 << ATTRCHILD_ARRAY_ATTR_SLOTS_BITS) - 1)
44 
45 
46 #define ATTRSIZE (sizeof(InternalAttr) / sizeof(void*))
47 
48 class nsAttrAndChildArray
49 {
50   typedef mozilla::dom::BorrowedAttrInfo BorrowedAttrInfo;
51 public:
52   nsAttrAndChildArray();
53   ~nsAttrAndChildArray();
54 
ChildCount()55   uint32_t ChildCount() const
56   {
57     return mImpl ? (mImpl->mAttrAndChildCount >> ATTRCHILD_ARRAY_ATTR_SLOTS_BITS) : 0;
58   }
ChildAt(uint32_t aPos)59   nsIContent* ChildAt(uint32_t aPos) const
60   {
61     NS_ASSERTION(aPos < ChildCount(), "out-of-bounds access in nsAttrAndChildArray");
62     return reinterpret_cast<nsIContent*>(mImpl->mBuffer[AttrSlotsSize() + aPos]);
63   }
64   nsIContent* GetSafeChildAt(uint32_t aPos) const;
65   nsIContent * const * GetChildArray(uint32_t* aChildCount) const;
AppendChild(nsIContent * aChild)66   nsresult AppendChild(nsIContent* aChild)
67   {
68     return InsertChildAt(aChild, ChildCount());
69   }
70   nsresult InsertChildAt(nsIContent* aChild, uint32_t aPos);
71   void RemoveChildAt(uint32_t aPos);
72   // Like RemoveChildAt but hands the reference to the child being
73   // removed back to the caller instead of just releasing it.
74   already_AddRefed<nsIContent> TakeChildAt(uint32_t aPos);
75   int32_t IndexOfChild(const nsINode* aPossibleChild) const;
76 
HasAttrs()77   bool HasAttrs() const
78   {
79     return MappedAttrCount() || (AttrSlotCount() && AttrSlotIsTaken(0));
80   }
81 
82   uint32_t AttrCount() const;
83   const nsAttrValue* GetAttr(nsIAtom* aLocalName,
84                              int32_t aNamespaceID = kNameSpaceID_None) const;
85   // As above but using a string attr name and always using
86   // kNameSpaceID_None.  This is always case-sensitive.
87   const nsAttrValue* GetAttr(const nsAString& aName) const;
88   // Get an nsAttrValue by qualified name.  Can optionally do
89   // ASCII-case-insensitive name matching.
90   const nsAttrValue* GetAttr(const nsAString& aName,
91                              nsCaseTreatment aCaseSensitive) const;
92   const nsAttrValue* AttrAt(uint32_t aPos) const;
93   // SetAndSwapAttr swaps the current attribute value with aValue.
94   nsresult SetAndSwapAttr(nsIAtom* aLocalName, nsAttrValue& aValue);
95   nsresult SetAndSwapAttr(mozilla::dom::NodeInfo* aName, nsAttrValue& aValue);
96 
97   // Remove the attr at position aPos.  The value of the attr is placed in
98   // aValue; any value that was already in aValue is destroyed.
99   nsresult RemoveAttrAt(uint32_t aPos, nsAttrValue& aValue);
100 
101   // Returns attribute name at given position, *not* out-of-bounds safe
102   const nsAttrName* AttrNameAt(uint32_t aPos) const;
103 
104   // Returns the attribute info at a given position, *not* out-of-bounds safe
105   BorrowedAttrInfo AttrInfoAt(uint32_t aPos) const;
106 
107   // Returns attribute name at given position or null if aPos is out-of-bounds
108   const nsAttrName* GetSafeAttrNameAt(uint32_t aPos) const;
109 
110   const nsAttrName* GetExistingAttrNameFromQName(const nsAString& aName) const;
111   int32_t IndexOfAttr(nsIAtom* aLocalName, int32_t aNamespaceID = kNameSpaceID_None) const;
112 
113   nsresult SetAndTakeMappedAttr(nsIAtom* aLocalName, nsAttrValue& aValue,
114                                 nsMappedAttributeElement* aContent,
115                                 nsHTMLStyleSheet* aSheet);
SetMappedAttrStyleSheet(nsHTMLStyleSheet * aSheet)116   nsresult SetMappedAttrStyleSheet(nsHTMLStyleSheet* aSheet) {
117     if (!mImpl || !mImpl->mMappedAttrs) {
118       return NS_OK;
119     }
120     return DoSetMappedAttrStyleSheet(aSheet);
121   }
122   void WalkMappedAttributeStyleRules(nsRuleWalker* aRuleWalker);
123 
124   void Compact();
125 
CanFitMoreAttrs()126   bool CanFitMoreAttrs() const
127   {
128     return AttrSlotCount() < ATTRCHILD_ARRAY_MAX_ATTR_COUNT ||
129            !AttrSlotIsTaken(ATTRCHILD_ARRAY_MAX_ATTR_COUNT - 1);
130   }
131 
132   size_t SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf) const;
HasMappedAttrs()133   bool HasMappedAttrs() const
134   {
135     return MappedAttrCount();
136   }
137 
138 private:
139   nsAttrAndChildArray(const nsAttrAndChildArray& aOther) = delete;
140   nsAttrAndChildArray& operator=(const nsAttrAndChildArray& aOther) = delete;
141 
142   void Clear();
143 
144   uint32_t NonMappedAttrCount() const;
145   uint32_t MappedAttrCount() const;
146 
147   // Returns a non-null zero-refcount object.
148   nsMappedAttributes*
149   GetModifiableMapped(nsMappedAttributeElement* aContent,
150                       nsHTMLStyleSheet* aSheet,
151                       bool aWillAddAttr);
152   nsresult MakeMappedUnique(nsMappedAttributes* aAttributes);
153 
AttrSlotsSize()154   uint32_t AttrSlotsSize() const
155   {
156     return AttrSlotCount() * ATTRSIZE;
157   }
158 
AttrSlotCount()159   uint32_t AttrSlotCount() const
160   {
161     return mImpl ? mImpl->mAttrAndChildCount & ATTRCHILD_ARRAY_ATTR_SLOTS_COUNT_MASK : 0;
162   }
163 
AttrSlotIsTaken(uint32_t aSlot)164   bool AttrSlotIsTaken(uint32_t aSlot) const
165   {
166     NS_PRECONDITION(aSlot < AttrSlotCount(), "out-of-bounds");
167     return mImpl->mBuffer[aSlot * ATTRSIZE];
168   }
169 
SetChildCount(uint32_t aCount)170   void SetChildCount(uint32_t aCount)
171   {
172     mImpl->mAttrAndChildCount =
173         (mImpl->mAttrAndChildCount & ATTRCHILD_ARRAY_ATTR_SLOTS_COUNT_MASK) |
174         (aCount << ATTRCHILD_ARRAY_ATTR_SLOTS_BITS);
175   }
176 
SetAttrSlotCount(uint32_t aCount)177   void SetAttrSlotCount(uint32_t aCount)
178   {
179     mImpl->mAttrAndChildCount =
180         (mImpl->mAttrAndChildCount & ~ATTRCHILD_ARRAY_ATTR_SLOTS_COUNT_MASK) |
181         aCount;
182   }
183 
SetAttrSlotAndChildCount(uint32_t aSlotCount,uint32_t aChildCount)184   void SetAttrSlotAndChildCount(uint32_t aSlotCount, uint32_t aChildCount)
185   {
186     mImpl->mAttrAndChildCount = aSlotCount |
187       (aChildCount << ATTRCHILD_ARRAY_ATTR_SLOTS_BITS);
188   }
189 
190   bool GrowBy(uint32_t aGrowSize);
191   bool AddAttrSlot();
192 
193   /**
194    * Set *aPos to aChild and update sibling pointers as needed.  aIndex is the
195    * index at which aChild is actually being inserted.  aChildCount is the
196    * number of kids we had before the insertion.
197    */
198   inline void SetChildAtPos(void** aPos, nsIContent* aChild, uint32_t aIndex,
199                             uint32_t aChildCount);
200 
201   /**
202    * Guts of SetMappedAttrStyleSheet for the rare case when we have mapped attrs
203    */
204   nsresult DoSetMappedAttrStyleSheet(nsHTMLStyleSheet* aSheet);
205 
206   struct InternalAttr
207   {
208     nsAttrName mName;
209     nsAttrValue mValue;
210   };
211 
212   struct Impl {
213     uint32_t mAttrAndChildCount;
214     uint32_t mBufferSize;
215     nsMappedAttributes* mMappedAttrs;
216     void* mBuffer[1];
217   };
218 
219   Impl* mImpl;
220 };
221 
222 #endif
223