1/*
2 * Copyright (C) 2013, 2014 Apple Inc. All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
6 * are met:
7 * 1. Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 *
13 * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
14 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
17 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24 */
25
26#pragma once
27
28#include "Attribute.h"
29#include "SpaceSplitString.h"
30#include <wtf/RefCounted.h>
31#include <wtf/TypeCasts.h>
32
33namespace WebCore {
34
35class Attr;
36class ShareableElementData;
37class StyleProperties;
38class UniqueElementData;
39
40class AttributeConstIterator {
41public:
42 AttributeConstIterator(const Attribute* array, unsigned offset)
43 : m_array(array)
44 , m_offset(offset)
45 {
46 }
47
48 const Attribute& operator*() const { return m_array[m_offset]; }
49 const Attribute* operator->() const { return &m_array[m_offset]; }
50 AttributeConstIterator& operator++() { ++m_offset; return *this; }
51
52 bool operator==(const AttributeConstIterator& other) const { return m_offset == other.m_offset; }
53 bool operator!=(const AttributeConstIterator& other) const { return !(*this == other); }
54
55private:
56 const Attribute* m_array;
57 unsigned m_offset;
58};
59
60class AttributeIteratorAccessor {
61public:
62 AttributeIteratorAccessor(const Attribute* array, unsigned size)
63 : m_array(array)
64 , m_size(size)
65 {
66 }
67
68 AttributeConstIterator begin() const { return AttributeConstIterator(m_array, 0); }
69 AttributeConstIterator end() const { return AttributeConstIterator(m_array, m_size); }
70
71 unsigned attributeCount() const { return m_size; }
72
73private:
74 const Attribute* m_array;
75 unsigned m_size;
76};
77
78class ElementData : public RefCounted<ElementData> {
79 WTF_MAKE_FAST_ALLOCATED;
80public:
81 // Override RefCounted's deref() to ensure operator delete is called on
82 // the appropriate subclass type.
83 void deref();
84
85 static const unsigned attributeNotFound = static_cast<unsigned>(-1);
86
87 void setClassNames(const SpaceSplitString& classNames) const { m_classNames = classNames; }
88 const SpaceSplitString& classNames() const { return m_classNames; }
89 static ptrdiff_t classNamesMemoryOffset() { return OBJECT_OFFSETOF(ElementData, m_classNames); }
90
91 const AtomString& idForStyleResolution() const { return m_idForStyleResolution; }
92 static ptrdiff_t idForStyleResolutionMemoryOffset() { return OBJECT_OFFSETOF(ElementData, m_idForStyleResolution); }
93 void setIdForStyleResolution(const AtomString& newId) const { m_idForStyleResolution = newId; }
94
95 const StyleProperties* inlineStyle() const { return m_inlineStyle.get(); }
96 const StyleProperties* presentationAttributeStyle() const;
97
98 unsigned length() const;
99 bool isEmpty() const { return !length(); }
100
101 AttributeIteratorAccessor attributesIterator() const;
102 const Attribute& attributeAt(unsigned index) const;
103 const Attribute* findAttributeByName(const QualifiedName&) const;
104 unsigned findAttributeIndexByName(const QualifiedName&) const;
105 unsigned findAttributeIndexByName(const AtomString& name, bool shouldIgnoreAttributeCase) const;
106 const Attribute* findLanguageAttribute() const;
107
108 bool hasID() const { return !m_idForStyleResolution.isNull(); }
109 bool hasClass() const { return !m_classNames.isEmpty(); }
110 bool hasName() const { return m_arraySizeAndFlags & s_flagHasNameAttribute; }
111
112 bool isEquivalent(const ElementData* other) const;
113
114 bool isUnique() const { return m_arraySizeAndFlags & s_flagIsUnique; }
115 static uint32_t isUniqueFlag() { return s_flagIsUnique; }
116
117 static ptrdiff_t arraySizeAndFlagsMemoryOffset() { return OBJECT_OFFSETOF(ElementData, m_arraySizeAndFlags); }
118 static inline uint32_t styleAttributeIsDirtyFlag() { return s_flagStyleAttributeIsDirty; }
119 static uint32_t animatedSVGAttributesAreDirtyFlag() { return s_flagAnimatedSVGAttributesAreDirty; }
120
121 static uint32_t arraySizeOffset() { return s_flagCount; }
122
123private:
124 mutable uint32_t m_arraySizeAndFlags;
125
126 static const uint32_t s_arraySize = 27;
127 static const uint32_t s_flagCount = 5;
128 static const uint32_t s_flagIsUnique = 1;
129 static const uint32_t s_flagHasNameAttribute = 1 << 1;
130 static const uint32_t s_flagPresentationAttributeStyleIsDirty = 1 << 2;
131 static const uint32_t s_flagStyleAttributeIsDirty = 1 << 3;
132 static const uint32_t s_flagAnimatedSVGAttributesAreDirty = 1 << 4;
133 static const uint32_t s_flagsMask = (1 << s_flagCount) - 1;
134
135 inline void updateFlag(uint32_t flag, bool set) const
136 {
137 if (set)
138 m_arraySizeAndFlags |= flag;
139 else
140 m_arraySizeAndFlags &= ~flag;
141 }
142 static inline uint32_t arraySizeAndFlagsFromOther(const ElementData& other, bool isUnique);
143
144protected:
145 ElementData();
146 explicit ElementData(unsigned arraySize);
147 ElementData(const ElementData&, bool isUnique);
148
149 unsigned arraySize() const { return m_arraySizeAndFlags >> s_flagCount; }
150
151 void setHasNameAttribute(bool hasName) const { updateFlag(s_flagHasNameAttribute, hasName); }
152
153 bool styleAttributeIsDirty() const { return m_arraySizeAndFlags & s_flagStyleAttributeIsDirty; }
154 void setStyleAttributeIsDirty(bool isDirty) const { updateFlag(s_flagStyleAttributeIsDirty, isDirty); }
155
156 bool presentationAttributeStyleIsDirty() const { return m_arraySizeAndFlags & s_flagPresentationAttributeStyleIsDirty; }
157 void setPresentationAttributeStyleIsDirty(bool isDirty) const { updateFlag(s_flagPresentationAttributeStyleIsDirty, isDirty); }
158
159 bool animatedSVGAttributesAreDirty() const { return m_arraySizeAndFlags & s_flagAnimatedSVGAttributesAreDirty; }
160 void setAnimatedSVGAttributesAreDirty(bool dirty) const { updateFlag(s_flagAnimatedSVGAttributesAreDirty, dirty); }
161
162 mutable RefPtr<StyleProperties> m_inlineStyle;
163 mutable SpaceSplitString m_classNames;
164 mutable AtomString m_idForStyleResolution;
165
166private:
167 friend class Element;
168 friend class StyledElement;
169 friend class ShareableElementData;
170 friend class UniqueElementData;
171 friend class SVGElement;
172
173 void destroy();
174
175 const Attribute* attributeBase() const;
176 const Attribute* findAttributeByName(const AtomString& name, bool shouldIgnoreAttributeCase) const;
177
178 Ref<UniqueElementData> makeUniqueCopy() const;
179};
180
181#if COMPILER(MSVC)
182#pragma warning(push)
183#pragma warning(disable: 4200) // Disable "zero-sized array in struct/union" warning
184#endif
185
186class ShareableElementData : public ElementData {
187public:
188 static Ref<ShareableElementData> createWithAttributes(const Vector<Attribute>&);
189
190 explicit ShareableElementData(const Vector<Attribute>&);
191 explicit ShareableElementData(const UniqueElementData&);
192 ~ShareableElementData();
193
194 static ptrdiff_t attributeArrayMemoryOffset() { return OBJECT_OFFSETOF(ShareableElementData, m_attributeArray); }
195
196 Attribute m_attributeArray[0];
197};
198
199#if COMPILER(MSVC)
200#pragma warning(pop)
201#endif
202
203class UniqueElementData : public ElementData {
204public:
205 static Ref<UniqueElementData> create();
206 Ref<ShareableElementData> makeShareableCopy() const;
207
208 // These functions do no error/duplicate checking.
209 void addAttribute(const QualifiedName&, const AtomString&);
210 void removeAttribute(unsigned index);
211
212 Attribute& attributeAt(unsigned index);
213 Attribute* findAttributeByName(const QualifiedName&);
214
215 UniqueElementData();
216 explicit UniqueElementData(const ShareableElementData&);
217 explicit UniqueElementData(const UniqueElementData&);
218
219 static ptrdiff_t attributeVectorMemoryOffset() { return OBJECT_OFFSETOF(UniqueElementData, m_attributeVector); }
220
221 mutable RefPtr<StyleProperties> m_presentationAttributeStyle;
222 typedef Vector<Attribute, 4> AttributeVector;
223 AttributeVector m_attributeVector;
224};
225
226inline void ElementData::deref()
227{
228 if (!derefBase())
229 return;
230 destroy();
231}
232
233inline unsigned ElementData::length() const
234{
235 if (is<UniqueElementData>(*this))
236 return downcast<UniqueElementData>(*this).m_attributeVector.size();
237 return arraySize();
238}
239
240inline const Attribute* ElementData::attributeBase() const
241{
242 if (is<UniqueElementData>(*this))
243 return downcast<UniqueElementData>(*this).m_attributeVector.data();
244 return downcast<ShareableElementData>(*this).m_attributeArray;
245}
246
247inline const StyleProperties* ElementData::presentationAttributeStyle() const
248{
249 if (!is<UniqueElementData>(*this))
250 return nullptr;
251 return downcast<UniqueElementData>(*this).m_presentationAttributeStyle.get();
252}
253
254inline AttributeIteratorAccessor ElementData::attributesIterator() const
255{
256 if (is<UniqueElementData>(*this)) {
257 const Vector<Attribute, 4>& attributeVector = downcast<UniqueElementData>(*this).m_attributeVector;
258 return AttributeIteratorAccessor(attributeVector.data(), attributeVector.size());
259 }
260 return AttributeIteratorAccessor(downcast<ShareableElementData>(*this).m_attributeArray, arraySize());
261}
262
263ALWAYS_INLINE const Attribute* ElementData::findAttributeByName(const AtomString& name, bool shouldIgnoreAttributeCase) const
264{
265 unsigned index = findAttributeIndexByName(name, shouldIgnoreAttributeCase);
266 if (index != attributeNotFound)
267 return &attributeAt(index);
268 return nullptr;
269}
270
271ALWAYS_INLINE unsigned ElementData::findAttributeIndexByName(const QualifiedName& name) const
272{
273 const Attribute* attributes = attributeBase();
274 for (unsigned i = 0, count = length(); i < count; ++i) {
275 if (attributes[i].name().matches(name))
276 return i;
277 }
278 return attributeNotFound;
279}
280
281// We use a boolean parameter instead of calling shouldIgnoreAttributeCase so that the caller
282// can tune the behavior (hasAttribute is case sensitive whereas getAttribute is not).
283ALWAYS_INLINE unsigned ElementData::findAttributeIndexByName(const AtomString& name, bool shouldIgnoreAttributeCase) const
284{
285 unsigned attributeCount = length();
286 if (!attributeCount)
287 return attributeNotFound;
288
289 const Attribute* attributes = attributeBase();
290 const AtomString& caseAdjustedName = shouldIgnoreAttributeCase ? name.convertToASCIILowercase() : name;
291
292 unsigned attributeIndex = 0;
293 do {
294 const Attribute& attribute = attributes[attributeIndex];
295 if (!attribute.name().hasPrefix()) {
296 if (attribute.localName() == caseAdjustedName)
297 return attributeIndex;
298 } else {
299 if (attribute.name().toString() == caseAdjustedName)
300 return attributeIndex;
301 }
302
303 ++attributeIndex;
304 } while (attributeIndex < attributeCount);
305
306 return attributeNotFound;
307}
308
309ALWAYS_INLINE const Attribute* ElementData::findAttributeByName(const QualifiedName& name) const
310{
311 const Attribute* attributes = attributeBase();
312 for (unsigned i = 0, count = length(); i < count; ++i) {
313 if (attributes[i].name().matches(name))
314 return &attributes[i];
315 }
316 return 0;
317}
318
319inline const Attribute& ElementData::attributeAt(unsigned index) const
320{
321 RELEASE_ASSERT(index < length());
322 return attributeBase()[index];
323}
324
325inline void UniqueElementData::addAttribute(const QualifiedName& attributeName, const AtomString& value)
326{
327 m_attributeVector.append(Attribute(attributeName, value));
328}
329
330inline void UniqueElementData::removeAttribute(unsigned index)
331{
332 m_attributeVector.remove(index);
333}
334
335inline Attribute& UniqueElementData::attributeAt(unsigned index)
336{
337 return m_attributeVector.at(index);
338}
339
340} // namespace WebCore
341
342SPECIALIZE_TYPE_TRAITS_BEGIN(WebCore::ShareableElementData)
343 static bool isType(const WebCore::ElementData& elementData) { return !elementData.isUnique(); }
344SPECIALIZE_TYPE_TRAITS_END()
345
346SPECIALIZE_TYPE_TRAITS_BEGIN(WebCore::UniqueElementData)
347 static bool isType(const WebCore::ElementData& elementData) { return elementData.isUnique(); }
348SPECIALIZE_TYPE_TRAITS_END()
349