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 attributes of a DOM node. 9 */ 10 11 #ifndef AttrArray_h___ 12 #define AttrArray_h___ 13 14 #include "mozilla/Attributes.h" 15 #include "mozilla/MemoryReporting.h" 16 #include "mozilla/UniquePtr.h" 17 #include "mozilla/Span.h" 18 #include "mozilla/dom/BorrowedAttrInfo.h" 19 20 #include "nscore.h" 21 #include "nsAttrName.h" 22 #include "nsAttrValue.h" 23 #include "nsCaseTreatment.h" 24 25 class nsINode; 26 class nsIContent; 27 class nsMappedAttributes; 28 class nsHTMLStyleSheet; 29 class nsRuleWalker; 30 class nsMappedAttributeElement; 31 32 class AttrArray { 33 typedef mozilla::dom::BorrowedAttrInfo BorrowedAttrInfo; 34 35 public: 36 AttrArray() = default; 37 ~AttrArray() = default; 38 HasAttrs()39 bool HasAttrs() const { return NonMappedAttrCount() || MappedAttrCount(); } 40 AttrCount()41 uint32_t AttrCount() const { 42 return NonMappedAttrCount() + MappedAttrCount(); 43 } 44 45 const nsAttrValue* GetAttr(const nsAtom* aLocalName, 46 int32_t aNamespaceID = kNameSpaceID_None) const; 47 // As above but using a string attr name and always using 48 // kNameSpaceID_None. This is always case-sensitive. 49 const nsAttrValue* GetAttr(const nsAString& aName) const; 50 // Get an nsAttrValue by qualified name. Can optionally do 51 // ASCII-case-insensitive name matching. 52 const nsAttrValue* GetAttr(const nsAString& aName, 53 nsCaseTreatment aCaseSensitive) const; 54 const nsAttrValue* AttrAt(uint32_t aPos) const; 55 // SetAndSwapAttr swaps the current attribute value with aValue. 56 // If the attribute was unset, an empty value will be swapped into aValue 57 // and aHadValue will be set to false. Otherwise, aHadValue will be set to 58 // true. 59 nsresult SetAndSwapAttr(nsAtom* aLocalName, nsAttrValue& aValue, 60 bool* aHadValue); 61 nsresult SetAndSwapAttr(mozilla::dom::NodeInfo* aName, nsAttrValue& aValue, 62 bool* aHadValue); 63 64 // Remove the attr at position aPos. The value of the attr is placed in 65 // aValue; any value that was already in aValue is destroyed. 66 nsresult RemoveAttrAt(uint32_t aPos, nsAttrValue& aValue); 67 68 // Returns attribute name at given position, *not* out-of-bounds safe 69 const nsAttrName* AttrNameAt(uint32_t aPos) const; 70 71 // Returns the attribute info at a given position, *not* out-of-bounds safe 72 BorrowedAttrInfo AttrInfoAt(uint32_t aPos) const; 73 74 // Returns attribute name at given position or null if aPos is out-of-bounds 75 const nsAttrName* GetSafeAttrNameAt(uint32_t aPos) const; 76 77 const nsAttrName* GetExistingAttrNameFromQName(const nsAString& aName) const; 78 int32_t IndexOfAttr(const nsAtom* aLocalName, 79 int32_t aNamespaceID = kNameSpaceID_None) const; 80 81 // SetAndSwapMappedAttr swaps the current attribute value with aValue. 82 // If the attribute was unset, an empty value will be swapped into aValue 83 // and aHadValue will be set to false. Otherwise, aHadValue will be set to 84 // true. 85 nsresult SetAndSwapMappedAttr(nsAtom* aLocalName, nsAttrValue& aValue, 86 nsMappedAttributeElement* aContent, 87 nsHTMLStyleSheet* aSheet, bool* aHadValue); SetMappedAttrStyleSheet(nsHTMLStyleSheet * aSheet)88 nsresult SetMappedAttrStyleSheet(nsHTMLStyleSheet* aSheet) { 89 if (!mImpl || !mImpl->mMappedAttrs) { 90 return NS_OK; 91 } 92 return DoSetMappedAttrStyleSheet(aSheet); 93 } 94 95 // Update the rule mapping function on our mapped attributes, if we have any. 96 // We take a nsMappedAttributeElement, not a nsMapRuleToAttributesFunc, 97 // because the latter is defined in a header we can't include here. UpdateMappedAttrRuleMapper(nsMappedAttributeElement & aElement)98 nsresult UpdateMappedAttrRuleMapper(nsMappedAttributeElement& aElement) { 99 if (!mImpl || !mImpl->mMappedAttrs) { 100 return NS_OK; 101 } 102 return DoUpdateMappedAttrRuleMapper(aElement); 103 } 104 105 void Compact(); 106 107 size_t SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf) const; HasMappedAttrs()108 bool HasMappedAttrs() const { return MappedAttrCount(); } 109 const nsMappedAttributes* GetMapped() const; 110 111 // Force this to have mapped attributes, even if those attributes are empty. 112 nsresult ForceMapped(nsMappedAttributeElement* aContent, 113 mozilla::dom::Document* aDocument); 114 115 // Clear the servo declaration block on the mapped attributes, if any 116 // Will assert off main thread 117 void ClearMappedServoStyle(); 118 119 // Increases capacity (if necessary) to have enough space to accomodate the 120 // unmapped attributes of |aOther|. 121 nsresult EnsureCapacityToClone(const AttrArray& aOther); 122 123 struct InternalAttr { 124 nsAttrName mName; 125 nsAttrValue mValue; 126 }; 127 128 private: 129 AttrArray(const AttrArray& aOther) = delete; 130 AttrArray& operator=(const AttrArray& aOther) = delete; 131 NonMappedAttrCount()132 uint32_t NonMappedAttrCount() const { return mImpl ? mImpl->mAttrCount : 0; } 133 MappedAttrCount()134 uint32_t MappedAttrCount() const { 135 return mImpl && mImpl->mMappedAttrs ? DoGetMappedAttrCount() : 0; 136 } 137 138 uint32_t DoGetMappedAttrCount() const; 139 140 // Returns a non-null zero-refcount object. 141 nsMappedAttributes* GetModifiableMapped(nsMappedAttributeElement* aContent, 142 nsHTMLStyleSheet* aSheet, 143 bool aWillAddAttr, 144 int32_t aAttrCount = 1); 145 nsresult MakeMappedUnique(nsMappedAttributes* aAttributes); 146 147 bool GrowBy(uint32_t aGrowSize); 148 149 // Tries to create an attribute, growing the buffer if needed, with the given 150 // name and value. 151 // 152 // The value is moved from the argument. 153 // 154 // `Name` can be anything you construct a `nsAttrName` with (either an atom or 155 // a NodeInfo pointer). 156 template <typename Name> 157 nsresult AddNewAttribute(Name*, nsAttrValue&); 158 159 /** 160 * Guts of SetMappedAttrStyleSheet for the rare case when we have mapped attrs 161 */ 162 nsresult DoSetMappedAttrStyleSheet(nsHTMLStyleSheet* aSheet); 163 164 /** 165 * Guts of UpdateMappedAttrRuleMapper for the case when we have mapped attrs. 166 */ 167 nsresult DoUpdateMappedAttrRuleMapper(nsMappedAttributeElement& aElement); 168 169 #ifdef _MSC_VER 170 // Disable MSVC warning 'nonstandard extension used: zero-sized array in 171 // struct/union' 172 # pragma warning(push) 173 # pragma warning(disable : 4200) 174 #endif 175 class Impl { 176 public: AllocationSizeForAttributes(uint32_t aAttrCount)177 constexpr static size_t AllocationSizeForAttributes(uint32_t aAttrCount) { 178 return sizeof(Impl) + aAttrCount * sizeof(InternalAttr); 179 } 180 NonMappedAttrs()181 auto NonMappedAttrs() const { 182 return mozilla::Span<const InternalAttr>{mBuffer, mAttrCount}; 183 } 184 NonMappedAttrs()185 auto NonMappedAttrs() { 186 return mozilla::Span<InternalAttr>{mBuffer, mAttrCount}; 187 } 188 189 Impl(const Impl&) = delete; 190 Impl(Impl&&) = delete; 191 ~Impl(); 192 193 uint32_t mAttrCount; 194 uint32_t mCapacity; // In number of InternalAttrs 195 196 // Manually refcounted. 197 nsMappedAttributes* mMappedAttrs; 198 199 // Allocated in the same buffer as `Impl`. 200 InternalAttr mBuffer[0]; 201 }; 202 #ifdef _MSC_VER 203 # pragma warning(pop) 204 #endif 205 NonMappedAttrs()206 mozilla::Span<InternalAttr> NonMappedAttrs() { 207 return mImpl ? mImpl->NonMappedAttrs() : mozilla::Span<InternalAttr>(); 208 } 209 NonMappedAttrs()210 mozilla::Span<const InternalAttr> NonMappedAttrs() const { 211 return mImpl ? mImpl->NonMappedAttrs() 212 : mozilla::Span<const InternalAttr>(); 213 } 214 215 mozilla::UniquePtr<Impl> mImpl; 216 }; 217 218 #endif 219