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#ifndef ElementData_h
27#define ElementData_h
28
29#include "Attribute.h"
30#include "SpaceSplitString.h"
31#include <wtf/RefCounted.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 clearClass() const { m_classNames.clear(); }
88    void setClass(const AtomicString& className, bool shouldFoldCase) const { m_classNames.set(className, shouldFoldCase); }
89    const SpaceSplitString& classNames() const { return m_classNames; }
90    static ptrdiff_t classNamesMemoryOffset() { return OBJECT_OFFSETOF(ElementData, m_classNames); }
91
92    const AtomicString& idForStyleResolution() const { return m_idForStyleResolution; }
93    static ptrdiff_t idForStyleResolutionMemoryOffset() { return OBJECT_OFFSETOF(ElementData, m_idForStyleResolution); }
94    void setIdForStyleResolution(const AtomicString& newId) const { m_idForStyleResolution = newId; }
95
96    const StyleProperties* inlineStyle() const { return m_inlineStyle.get(); }
97    const StyleProperties* presentationAttributeStyle() const;
98
99    unsigned length() const;
100    bool isEmpty() const { return !length(); }
101
102    AttributeIteratorAccessor attributesIterator() const;
103    const Attribute& attributeAt(unsigned index) const;
104    const Attribute* findAttributeByName(const QualifiedName&) const;
105    unsigned findAttributeIndexByName(const QualifiedName&) const;
106    unsigned findAttributeIndexByName(const AtomicString& name, bool shouldIgnoreAttributeCase) const;
107    unsigned findAttributeIndexByNameForAttributeNode(const Attr*, bool shouldIgnoreAttributeCase = false) const;
108    const Attribute* findLanguageAttribute() const;
109
110    bool hasID() const { return !m_idForStyleResolution.isNull(); }
111    bool hasClass() const { return !m_classNames.isEmpty(); }
112    bool hasName() const { return m_arraySizeAndFlags & s_flagHasNameAttribute; }
113
114    bool isEquivalent(const ElementData* other) const;
115
116    bool isUnique() const { return m_arraySizeAndFlags & s_flagIsUnique; }
117    static uint32_t isUniqueFlag() { return s_flagIsUnique; }
118
119    static ptrdiff_t arraySizeAndFlagsMemoryOffset() { return OBJECT_OFFSETOF(ElementData, m_arraySizeAndFlags); }
120    static inline uint32_t styleAttributeIsDirtyFlag() { return s_flagStyleAttributeIsDirty; }
121    static uint32_t animatedSVGAttributesAreDirtyFlag() { return s_flagAnimatedSVGAttributesAreDirty; }
122
123    static uint32_t arraySizeOffset() { return s_flagCount; }
124
125private:
126    mutable uint32_t m_arraySizeAndFlags;
127
128    static const uint32_t s_arraySize = 27;
129    static const uint32_t s_flagCount = 5;
130    static const uint32_t s_flagIsUnique = 1;
131    static const uint32_t s_flagHasNameAttribute = 1 << 1;
132    static const uint32_t s_flagPresentationAttributeStyleIsDirty = 1 << 2;
133    static const uint32_t s_flagStyleAttributeIsDirty = 1 << 3;
134    static const uint32_t s_flagAnimatedSVGAttributesAreDirty = 1 << 4;
135    static const uint32_t s_flagsMask = (1 << s_flagCount) - 1;
136
137    inline void updateFlag(uint32_t flag, bool set) const
138    {
139        if (set)
140            m_arraySizeAndFlags |= flag;
141        else
142            m_arraySizeAndFlags &= ~flag;
143    }
144    static inline uint32_t arraySizeAndFlagsFromOther(const ElementData& other, bool isUnique);
145
146protected:
147    ElementData();
148    explicit ElementData(unsigned arraySize);
149    ElementData(const ElementData&, bool isUnique);
150
151    unsigned arraySize() const { return m_arraySizeAndFlags >> s_flagCount; }
152
153    void setHasNameAttribute(bool hasName) const { updateFlag(s_flagHasNameAttribute, hasName); }
154
155    bool styleAttributeIsDirty() const { return m_arraySizeAndFlags & s_flagStyleAttributeIsDirty; }
156    void setStyleAttributeIsDirty(bool isDirty) const { updateFlag(s_flagStyleAttributeIsDirty, isDirty); }
157
158    bool presentationAttributeStyleIsDirty() const { return m_arraySizeAndFlags & s_flagPresentationAttributeStyleIsDirty; }
159    void setPresentationAttributeStyleIsDirty(bool isDirty) const { updateFlag(s_flagPresentationAttributeStyleIsDirty, isDirty); }
160
161    bool animatedSVGAttributesAreDirty() const { return m_arraySizeAndFlags & s_flagAnimatedSVGAttributesAreDirty; }
162    void setAnimatedSVGAttributesAreDirty(bool dirty) const { updateFlag(s_flagAnimatedSVGAttributesAreDirty, dirty); }
163
164    mutable RefPtr<StyleProperties> m_inlineStyle;
165    mutable SpaceSplitString m_classNames;
166    mutable AtomicString m_idForStyleResolution;
167
168private:
169    friend class Element;
170    friend class StyledElement;
171    friend class ShareableElementData;
172    friend class UniqueElementData;
173    friend class SVGElement;
174
175    void destroy();
176
177    const Attribute* attributeBase() const;
178    const Attribute* findAttributeByName(const AtomicString& name, bool shouldIgnoreAttributeCase) const;
179    unsigned findAttributeIndexByNameSlowCase(const AtomicString& name, bool shouldIgnoreAttributeCase) const;
180
181    PassRef<UniqueElementData> makeUniqueCopy() const;
182};
183
184#if COMPILER(MSVC)
185#pragma warning(push)
186#pragma warning(disable: 4200) // Disable "zero-sized array in struct/union" warning
187#endif
188
189class ShareableElementData : public ElementData {
190public:
191    static PassRef<ShareableElementData> createWithAttributes(const Vector<Attribute>&);
192
193    explicit ShareableElementData(const Vector<Attribute>&);
194    explicit ShareableElementData(const UniqueElementData&);
195    ~ShareableElementData();
196
197    static ptrdiff_t attributeArrayMemoryOffset() { return OBJECT_OFFSETOF(ShareableElementData, m_attributeArray); }
198
199    Attribute m_attributeArray[0];
200};
201
202#if COMPILER(MSVC)
203#pragma warning(pop)
204#endif
205
206class UniqueElementData : public ElementData {
207public:
208    static PassRef<UniqueElementData> create();
209    PassRef<ShareableElementData> makeShareableCopy() const;
210
211    // These functions do no error/duplicate checking.
212    void addAttribute(const QualifiedName&, const AtomicString&);
213    void removeAttribute(unsigned index);
214
215    Attribute& attributeAt(unsigned index);
216    Attribute* findAttributeByName(const QualifiedName&);
217
218    UniqueElementData();
219    explicit UniqueElementData(const ShareableElementData&);
220    explicit UniqueElementData(const UniqueElementData&);
221
222    static ptrdiff_t attributeVectorMemoryOffset() { return OBJECT_OFFSETOF(UniqueElementData, m_attributeVector); }
223
224    mutable RefPtr<StyleProperties> m_presentationAttributeStyle;
225    typedef Vector<Attribute, 4> AttributeVector;
226    AttributeVector m_attributeVector;
227};
228
229inline void ElementData::deref()
230{
231    if (!derefBase())
232        return;
233    destroy();
234}
235
236inline unsigned ElementData::length() const
237{
238    if (isUnique())
239        return static_cast<const UniqueElementData*>(this)->m_attributeVector.size();
240    return arraySize();
241}
242
243inline const Attribute* ElementData::attributeBase() const
244{
245    if (isUnique())
246        return static_cast<const UniqueElementData*>(this)->m_attributeVector.data();
247    return static_cast<const ShareableElementData*>(this)->m_attributeArray;
248}
249
250inline const StyleProperties* ElementData::presentationAttributeStyle() const
251{
252    if (!isUnique())
253        return 0;
254    return static_cast<const UniqueElementData*>(this)->m_presentationAttributeStyle.get();
255}
256
257inline AttributeIteratorAccessor ElementData::attributesIterator() const
258{
259    if (isUnique()) {
260        const Vector<Attribute, 4>& attributeVector = static_cast<const UniqueElementData*>(this)->m_attributeVector;
261        return AttributeIteratorAccessor(attributeVector.data(), attributeVector.size());
262    }
263    return AttributeIteratorAccessor(static_cast<const ShareableElementData*>(this)->m_attributeArray, arraySize());
264}
265
266ALWAYS_INLINE const Attribute* ElementData::findAttributeByName(const AtomicString& name, bool shouldIgnoreAttributeCase) const
267{
268    unsigned index = findAttributeIndexByName(name, shouldIgnoreAttributeCase);
269    if (index != attributeNotFound)
270        return &attributeAt(index);
271    return 0;
272}
273
274ALWAYS_INLINE unsigned ElementData::findAttributeIndexByName(const QualifiedName& name) const
275{
276    const Attribute* attributes = attributeBase();
277    for (unsigned i = 0, count = length(); i < count; ++i) {
278        if (attributes[i].name().matches(name))
279            return i;
280    }
281    return attributeNotFound;
282}
283
284// We use a boolean parameter instead of calling shouldIgnoreAttributeCase so that the caller
285// can tune the behavior (hasAttribute is case sensitive whereas getAttribute is not).
286ALWAYS_INLINE unsigned ElementData::findAttributeIndexByName(const AtomicString& name, bool shouldIgnoreAttributeCase) const
287{
288    const Attribute* attributes = attributeBase();
289    bool doSlowCheck = shouldIgnoreAttributeCase;
290    const AtomicString& caseAdjustedName = shouldIgnoreAttributeCase ? name.lower() : name;
291
292    // Optimize for the case where the attribute exists and its name exactly matches.
293    for (unsigned i = 0, count = length(); i < count; ++i) {
294        if (!attributes[i].name().hasPrefix()) {
295            if (caseAdjustedName == attributes[i].localName())
296                return i;
297        } else
298            doSlowCheck = true;
299    }
300
301    if (doSlowCheck)
302        return findAttributeIndexByNameSlowCase(name, shouldIgnoreAttributeCase);
303    return attributeNotFound;
304}
305
306ALWAYS_INLINE const Attribute* ElementData::findAttributeByName(const QualifiedName& name) const
307{
308    const Attribute* attributes = attributeBase();
309    for (unsigned i = 0, count = length(); i < count; ++i) {
310        if (attributes[i].name().matches(name))
311            return &attributes[i];
312    }
313    return 0;
314}
315
316inline const Attribute& ElementData::attributeAt(unsigned index) const
317{
318    RELEASE_ASSERT(index < length());
319    return attributeBase()[index];
320}
321
322inline void UniqueElementData::addAttribute(const QualifiedName& attributeName, const AtomicString& value)
323{
324    m_attributeVector.append(Attribute(attributeName, value));
325}
326
327inline void UniqueElementData::removeAttribute(unsigned index)
328{
329    m_attributeVector.remove(index);
330}
331
332inline Attribute& UniqueElementData::attributeAt(unsigned index)
333{
334    return m_attributeVector.at(index);
335}
336
337}
338
339#endif
340
341