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 50 AttributeCollection(const Attribute* array, unsigned size) 51 : m_array(array) 52 , m_size(size) 53 { } 54 55 const_iterator begin() const { return m_array; } 56 const_iterator end() const { return m_array + m_size; } 57 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 74 void clearClass() const { m_classNames.clear(); } 75 void setClass(const AtomicString& className, bool shouldFoldCase) const { m_classNames.set(className, shouldFoldCase); } 76 const SpaceSplitString& classNames() const { return m_classNames; } 77 78 const AtomicString& idForStyleResolution() const { return m_idForStyleResolution; } 79 void setIdForStyleResolution(const AtomicString& newId) const { m_idForStyleResolution = newId; } 80 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; 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 97 bool hasID() const { return !m_idForStyleResolution.isNull(); } 98 bool hasClass() const { return !m_classNames.isNull(); } 99 100 bool isEquivalent(const ElementData* other) const; 101 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 189 inline void ElementData::deref() 190 { 191 if (!derefBase()) 192 return; 193 destroy(); 194 } 195 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 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 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 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 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). 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 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 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 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 290 inline void UniqueElementData::appendAttribute(const QualifiedName& attributeName, const AtomicString& value) 291 { 292 m_attributeVector.append(Attribute(attributeName, value)); 293 } 294 295 inline void UniqueElementData::removeAttributeAt(size_t index) 296 { 297 m_attributeVector.remove(index); 298 } 299 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