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