Home | History | Annotate | Download | only in dom
      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