• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2013 Google Inc. All rights reserved.
3  * Copyright (C) 2014 Apple Inc. All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions are
7  * met:
8  *
9  *     * Redistributions of source code must retain the above copyright
10  * notice, this list of conditions and the following disclaimer.
11  *     * Redistributions in binary form must reproduce the above
12  * copyright notice, this list of conditions and the following disclaimer
13  * in the documentation and/or other materials provided with the
14  * distribution.
15  *     * Neither the name of Google Inc. nor the names of its
16  * contributors may be used to endorse or promote products derived from
17  * this software without specific prior written permission.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
22  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
23  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
24  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
25  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
26  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
27  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30  */
31 
32 #ifndef ElementData_h
33 #define ElementData_h
34 
35 #include "core/dom/Attribute.h"
36 #include "core/dom/SpaceSplitString.h"
37 #include "wtf/text/AtomicString.h"
38 
39 namespace WebCore {
40 
41 class Attr;
42 class ShareableElementData;
43 class StylePropertySet;
44 class UniqueElementData;
45 
46 class AttributeCollection {
47 public:
48     typedef const Attribute* const_iterator;
49 
AttributeCollection(const Attribute * array,unsigned size)50     AttributeCollection(const Attribute* array, unsigned size)
51         : m_array(array)
52         , m_size(size)
53     { }
54 
begin()55     const_iterator begin() const { return m_array; }
end()56     const_iterator end() const { return m_array + m_size; }
57 
size()58     unsigned size() const { return m_size; }
59 
60 private:
61     const Attribute* m_array;
62     unsigned m_size;
63 };
64 
65 // ElementData represents very common, but not necessarily unique to an element,
66 // data such as attributes, inline style, and parsed class names and ids.
67 class ElementData : public RefCounted<ElementData> {
68     WTF_MAKE_FAST_ALLOCATED;
69 public:
70     // Override RefCounted's deref() to ensure operator delete is called on
71     // the appropriate subclass type.
72     void deref();
73 
clearClass()74     void clearClass() const { m_classNames.clear(); }
setClass(const AtomicString & className,bool shouldFoldCase)75     void setClass(const AtomicString& className, bool shouldFoldCase) const { m_classNames.set(className, shouldFoldCase); }
classNames()76     const SpaceSplitString& classNames() const { return m_classNames; }
77 
idForStyleResolution()78     const AtomicString& idForStyleResolution() const { return m_idForStyleResolution; }
setIdForStyleResolution(const AtomicString & newId)79     void setIdForStyleResolution(const AtomicString& newId) const { m_idForStyleResolution = newId; }
80 
inlineStyle()81     const StylePropertySet* inlineStyle() const { return m_inlineStyle.get(); }
82 
83     const StylePropertySet* presentationAttributeStyle() const;
84 
85     // This is not a trivial getter and its return value should be cached for performance.
86     size_t attributeCount() const;
hasAttributes()87     bool hasAttributes() const { return !!attributeCount(); }
88 
89     AttributeCollection attributes() const;
90 
91     const Attribute& attributeAt(unsigned index) const;
92     const Attribute* findAttributeByName(const QualifiedName&) const;
93     size_t findAttributeIndexByName(const QualifiedName&, bool shouldIgnoreCase = false) const;
94     size_t findAttributeIndexByName(const AtomicString& name, bool shouldIgnoreAttributeCase) const;
95     size_t findAttrNodeIndex(Attr*) const;
96 
hasID()97     bool hasID() const { return !m_idForStyleResolution.isNull(); }
hasClass()98     bool hasClass() const { return !m_classNames.isNull(); }
99 
100     bool isEquivalent(const ElementData* other) const;
101 
isUnique()102     bool isUnique() const { return m_isUnique; }
103 
104 protected:
105     ElementData();
106     explicit ElementData(unsigned arraySize);
107     ElementData(const ElementData&, bool isUnique);
108 
109     // Keep the type in a bitfield instead of using virtual destructors to avoid adding a vtable.
110     unsigned m_isUnique : 1;
111     unsigned m_arraySize : 28;
112     mutable unsigned m_presentationAttributeStyleIsDirty : 1;
113     mutable unsigned m_styleAttributeIsDirty : 1;
114     mutable unsigned m_animatedSVGAttributesAreDirty : 1;
115 
116     mutable RefPtr<StylePropertySet> m_inlineStyle;
117     mutable SpaceSplitString m_classNames;
118     mutable AtomicString m_idForStyleResolution;
119 
120 private:
121     friend class Element;
122     friend class ShareableElementData;
123     friend class UniqueElementData;
124     friend class SVGElement;
125 
126     void destroy();
127 
128     const Attribute* attributeBase() const;
129     const Attribute* findAttributeByName(const AtomicString& name, bool shouldIgnoreAttributeCase) const;
130     size_t findAttributeIndexByNameSlowCase(const AtomicString& name, bool shouldIgnoreAttributeCase) const;
131 
132     PassRefPtr<UniqueElementData> makeUniqueCopy() const;
133 };
134 
135 #if COMPILER(MSVC)
136 #pragma warning(push)
137 #pragma warning(disable: 4200) // Disable "zero-sized array in struct/union" warning
138 #endif
139 
140 // SharableElementData is managed by ElementDataCache and is produced by
141 // the parser during page load for elements that have identical attributes. This
142 // is a memory optimization since it's very common for many elements to have
143 // duplicate sets of attributes (ex. the same classes).
144 class ShareableElementData FINAL : public ElementData {
145 public:
146     static PassRefPtr<ShareableElementData> createWithAttributes(const Vector<Attribute>&);
147 
148     explicit ShareableElementData(const Vector<Attribute>&);
149     explicit ShareableElementData(const UniqueElementData&);
150     ~ShareableElementData();
151 
152     Attribute m_attributeArray[0];
153 };
154 
155 #if COMPILER(MSVC)
156 #pragma warning(pop)
157 #endif
158 
159 // UniqueElementData is created when an element needs to mutate its attributes
160 // or gains presentation attribute style (ex. width="10"). It does not need to
161 // be created to fill in values in the ElementData that are derived from
162 // attributes. For example populating the m_inlineStyle from the style attribute
163 // doesn't require a UniqueElementData as all elements with the same style
164 // attribute will have the same inline style.
165 class UniqueElementData FINAL : public ElementData {
166 public:
167     static PassRefPtr<UniqueElementData> create();
168     PassRefPtr<ShareableElementData> makeShareableCopy() const;
169 
170     // These functions do no error/duplicate checking.
171     void appendAttribute(const QualifiedName&, const AtomicString&);
172     void removeAttributeAt(size_t index);
173 
174     Attribute& attributeAt(unsigned index);
175     Attribute* findAttributeByName(const QualifiedName&);
176 
177     UniqueElementData();
178     explicit UniqueElementData(const ShareableElementData&);
179     explicit UniqueElementData(const UniqueElementData&);
180 
181     // FIXME: We might want to support sharing element data for elements with
182     // presentation attribute style. Lots of table cells likely have the same
183     // attributes. Most modern pages don't use presentation attributes though
184     // so this might not make sense.
185     mutable RefPtr<StylePropertySet> m_presentationAttributeStyle;
186     Vector<Attribute, 4> m_attributeVector;
187 };
188 
deref()189 inline void ElementData::deref()
190 {
191     if (!derefBase())
192         return;
193     destroy();
194 }
195 
attributeCount()196 inline size_t ElementData::attributeCount() const
197 {
198     if (isUnique())
199         return static_cast<const UniqueElementData*>(this)->m_attributeVector.size();
200     return m_arraySize;
201 }
202 
presentationAttributeStyle()203 inline const StylePropertySet* ElementData::presentationAttributeStyle() const
204 {
205     if (!m_isUnique)
206         return 0;
207     return static_cast<const UniqueElementData*>(this)->m_presentationAttributeStyle.get();
208 }
209 
findAttributeByName(const AtomicString & name,bool shouldIgnoreAttributeCase)210 inline const Attribute* ElementData::findAttributeByName(const AtomicString& name, bool shouldIgnoreAttributeCase) const
211 {
212     size_t index = findAttributeIndexByName(name, shouldIgnoreAttributeCase);
213     if (index != kNotFound)
214         return &attributeAt(index);
215     return 0;
216 }
217 
attributeBase()218 inline const Attribute* ElementData::attributeBase() const
219 {
220     if (m_isUnique)
221         return static_cast<const UniqueElementData*>(this)->m_attributeVector.begin();
222     return static_cast<const ShareableElementData*>(this)->m_attributeArray;
223 }
224 
findAttributeIndexByName(const QualifiedName & name,bool shouldIgnoreCase)225 inline size_t ElementData::findAttributeIndexByName(const QualifiedName& name, bool shouldIgnoreCase) const
226 {
227     AttributeCollection attributes = this->attributes();
228     AttributeCollection::const_iterator end = attributes.end();
229     unsigned index = 0;
230     for (AttributeCollection::const_iterator it = attributes.begin(); it != end; ++it, ++index) {
231         if (it->name().matchesPossiblyIgnoringCase(name, shouldIgnoreCase))
232             return index;
233     }
234     return kNotFound;
235 }
236 
237 // We use a boolean parameter instead of calling shouldIgnoreAttributeCase so that the caller
238 // can tune the behavior (hasAttribute is case sensitive whereas getAttribute is not).
findAttributeIndexByName(const AtomicString & name,bool shouldIgnoreAttributeCase)239 inline size_t ElementData::findAttributeIndexByName(const AtomicString& name, bool shouldIgnoreAttributeCase) const
240 {
241     bool doSlowCheck = shouldIgnoreAttributeCase;
242 
243     // Optimize for the case where the attribute exists and its name exactly matches.
244     AttributeCollection attributes = this->attributes();
245     AttributeCollection::const_iterator end = attributes.end();
246     unsigned index = 0;
247     for (AttributeCollection::const_iterator it = attributes.begin(); it != end; ++it, ++index) {
248         // FIXME: Why check the prefix? Namespaces should be all that matter.
249         // Most attributes (all of HTML and CSS) have no namespace.
250         if (!it->name().hasPrefix()) {
251             if (name == it->localName())
252                 return index;
253         } else {
254             doSlowCheck = true;
255         }
256     }
257 
258     if (doSlowCheck)
259         return findAttributeIndexByNameSlowCase(name, shouldIgnoreAttributeCase);
260     return kNotFound;
261 }
262 
attributes()263 inline AttributeCollection ElementData::attributes() const
264 {
265     if (isUnique()) {
266         const Vector<Attribute, 4>& attributeVector = static_cast<const UniqueElementData*>(this)->m_attributeVector;
267         return AttributeCollection(attributeVector.data(), attributeVector.size());
268     }
269     return AttributeCollection(static_cast<const ShareableElementData*>(this)->m_attributeArray, m_arraySize);
270 }
271 
findAttributeByName(const QualifiedName & name)272 inline const Attribute* ElementData::findAttributeByName(const QualifiedName& name) const
273 {
274     AttributeCollection attributes = this->attributes();
275     AttributeCollection::const_iterator end = attributes.end();
276     for (AttributeCollection::const_iterator it = attributes.begin(); it != end; ++it) {
277         if (it->name().matches(name))
278             return it;
279     }
280     return 0;
281 }
282 
attributeAt(unsigned index)283 inline const Attribute& ElementData::attributeAt(unsigned index) const
284 {
285     RELEASE_ASSERT(index < attributeCount());
286     ASSERT(attributeBase() + index);
287     return *(attributeBase() + index);
288 }
289 
appendAttribute(const QualifiedName & attributeName,const AtomicString & value)290 inline void UniqueElementData::appendAttribute(const QualifiedName& attributeName, const AtomicString& value)
291 {
292     m_attributeVector.append(Attribute(attributeName, value));
293 }
294 
removeAttributeAt(size_t index)295 inline void UniqueElementData::removeAttributeAt(size_t index)
296 {
297     m_attributeVector.remove(index);
298 }
299 
attributeAt(unsigned index)300 inline Attribute& UniqueElementData::attributeAt(unsigned index)
301 {
302     return m_attributeVector.at(index);
303 }
304 
305 } // namespace WebCore
306 
307 #endif // ElementData_h
308