| /* |
| * Copyright (C) 2013, 2014 Apple Inc. All rights reserved. |
| * |
| * Redistribution and use in source and binary forms, with or without |
| * modification, are permitted provided that the following conditions |
| * are met: |
| * 1. Redistributions of source code must retain the above copyright |
| * notice, this list of conditions and the following disclaimer. |
| * 2. Redistributions in binary form must reproduce the above copyright |
| * notice, this list of conditions and the following disclaimer in the |
| * documentation and/or other materials provided with the distribution. |
| * |
| * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY |
| * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
| * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
| * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR |
| * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, |
| * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, |
| * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR |
| * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY |
| * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
| * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
| * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| */ |
| |
| #pragma once |
| |
| #include "Attribute.h" |
| #include "SpaceSplitString.h" |
| #include <wtf/RefCounted.h> |
| #include <wtf/TypeCasts.h> |
| |
| namespace WebCore { |
| |
| class Attr; |
| class ShareableElementData; |
| class StyleProperties; |
| class UniqueElementData; |
| |
| class AttributeConstIterator { |
| public: |
| AttributeConstIterator(const Attribute* array, unsigned offset) |
| : m_array(array) |
| , m_offset(offset) |
| { |
| } |
| |
| const Attribute& operator*() const { return m_array[m_offset]; } |
| const Attribute* operator->() const { return &m_array[m_offset]; } |
| AttributeConstIterator& operator++() { ++m_offset; return *this; } |
| |
| bool operator==(const AttributeConstIterator& other) const { return m_offset == other.m_offset; } |
| bool operator!=(const AttributeConstIterator& other) const { return !(*this == other); } |
| |
| private: |
| const Attribute* m_array; |
| unsigned m_offset; |
| }; |
| |
| class AttributeIteratorAccessor { |
| public: |
| AttributeIteratorAccessor(const Attribute* array, unsigned size) |
| : m_array(array) |
| , m_size(size) |
| { |
| } |
| |
| AttributeConstIterator begin() const { return AttributeConstIterator(m_array, 0); } |
| AttributeConstIterator end() const { return AttributeConstIterator(m_array, m_size); } |
| |
| unsigned attributeCount() const { return m_size; } |
| |
| private: |
| const Attribute* m_array; |
| unsigned m_size; |
| }; |
| |
| DECLARE_ALLOCATOR_WITH_HEAP_IDENTIFIER(ElementData); |
| class ElementData : public RefCounted<ElementData> { |
| WTF_MAKE_FAST_ALLOCATED_WITH_HEAP_IDENTIFIER(ElementData); |
| public: |
| // Override RefCounted's deref() to ensure operator delete is called on |
| // the appropriate subclass type. |
| void deref(); |
| |
| static const unsigned attributeNotFound = static_cast<unsigned>(-1); |
| |
| void setClassNames(SpaceSplitString&& classNames) const { m_classNames = WTFMove(classNames); } |
| const SpaceSplitString& classNames() const { return m_classNames; } |
| static ptrdiff_t classNamesMemoryOffset() { return OBJECT_OFFSETOF(ElementData, m_classNames); } |
| |
| const AtomString& idForStyleResolution() const { return m_idForStyleResolution; } |
| static ptrdiff_t idForStyleResolutionMemoryOffset() { return OBJECT_OFFSETOF(ElementData, m_idForStyleResolution); } |
| void setIdForStyleResolution(const AtomString& newId) const { m_idForStyleResolution = newId; } |
| |
| const StyleProperties* inlineStyle() const { return m_inlineStyle.get(); } |
| const StyleProperties* presentationalHintStyle() const; |
| |
| unsigned length() const; |
| bool isEmpty() const { return !length(); } |
| |
| AttributeIteratorAccessor attributesIterator() const; |
| const Attribute& attributeAt(unsigned index) const; |
| const Attribute* findAttributeByName(const QualifiedName&) const; |
| unsigned findAttributeIndexByName(const QualifiedName&) const; |
| unsigned findAttributeIndexByName(const AtomString& name, bool shouldIgnoreAttributeCase) const; |
| const Attribute* findLanguageAttribute() const; |
| |
| bool hasID() const { return !m_idForStyleResolution.isNull(); } |
| bool hasClass() const { return !m_classNames.isEmpty(); } |
| bool hasName() const { return m_arraySizeAndFlags & s_flagHasNameAttribute; } |
| |
| bool isEquivalent(const ElementData* other) const; |
| |
| bool isUnique() const { return m_arraySizeAndFlags & s_flagIsUnique; } |
| static uint32_t isUniqueFlag() { return s_flagIsUnique; } |
| |
| static ptrdiff_t arraySizeAndFlagsMemoryOffset() { return OBJECT_OFFSETOF(ElementData, m_arraySizeAndFlags); } |
| static inline uint32_t styleAttributeIsDirtyFlag() { return s_flagStyleAttributeIsDirty; } |
| static uint32_t animatedSVGAttributesAreDirtyFlag() { return s_flagAnimatedSVGAttributesAreDirty; } |
| |
| static uint32_t arraySizeOffset() { return s_flagCount; } |
| |
| private: |
| mutable uint32_t m_arraySizeAndFlags; |
| |
| static const uint32_t s_arraySize = 27; |
| static const uint32_t s_flagCount = 5; |
| static const uint32_t s_flagIsUnique = 1; |
| static const uint32_t s_flagHasNameAttribute = 1 << 1; |
| static const uint32_t s_flagPresentationalHintStyleIsDirty = 1 << 2; |
| static const uint32_t s_flagStyleAttributeIsDirty = 1 << 3; |
| static const uint32_t s_flagAnimatedSVGAttributesAreDirty = 1 << 4; |
| static const uint32_t s_flagsMask = (1 << s_flagCount) - 1; |
| // FIXME: could the SVG specific flags go to some SVG class? |
| |
| inline void updateFlag(uint32_t flag, bool set) const |
| { |
| if (set) |
| m_arraySizeAndFlags |= flag; |
| else |
| m_arraySizeAndFlags &= ~flag; |
| } |
| static inline uint32_t arraySizeAndFlagsFromOther(const ElementData& other, bool isUnique); |
| |
| protected: |
| ElementData(); |
| explicit ElementData(unsigned arraySize); |
| ElementData(const ElementData&, bool isUnique); |
| |
| unsigned arraySize() const { return m_arraySizeAndFlags >> s_flagCount; } |
| |
| void setHasNameAttribute(bool hasName) const { updateFlag(s_flagHasNameAttribute, hasName); } |
| |
| bool styleAttributeIsDirty() const { return m_arraySizeAndFlags & s_flagStyleAttributeIsDirty; } |
| void setStyleAttributeIsDirty(bool isDirty) const { updateFlag(s_flagStyleAttributeIsDirty, isDirty); } |
| |
| bool presentationalHintStyleIsDirty() const { return m_arraySizeAndFlags & s_flagPresentationalHintStyleIsDirty; } |
| void setPresentationalHintStyleIsDirty(bool isDirty) const { updateFlag(s_flagPresentationalHintStyleIsDirty, isDirty); } |
| |
| bool animatedSVGAttributesAreDirty() const { return m_arraySizeAndFlags & s_flagAnimatedSVGAttributesAreDirty; } |
| void setAnimatedSVGAttributesAreDirty(bool dirty) const { updateFlag(s_flagAnimatedSVGAttributesAreDirty, dirty); } |
| |
| mutable RefPtr<StyleProperties> m_inlineStyle; |
| mutable SpaceSplitString m_classNames; |
| mutable AtomString m_idForStyleResolution; |
| |
| private: |
| friend class Element; |
| friend class StyledElement; |
| friend class ShareableElementData; |
| friend class UniqueElementData; |
| friend class SVGElement; |
| friend class HTMLImageElement; |
| |
| void destroy(); |
| |
| const Attribute* attributeBase() const; |
| const Attribute* findAttributeByName(const AtomString& name, bool shouldIgnoreAttributeCase) const; |
| |
| Ref<UniqueElementData> makeUniqueCopy() const; |
| }; |
| |
| #if COMPILER(MSVC) |
| #pragma warning(push) |
| #pragma warning(disable: 4200) // Disable "zero-sized array in struct/union" warning |
| #endif |
| |
| DECLARE_ALLOCATOR_WITH_HEAP_IDENTIFIER(ShareableElementData); |
| class ShareableElementData : public ElementData { |
| WTF_MAKE_FAST_ALLOCATED_WITH_HEAP_IDENTIFIER(ShareableElementData); |
| public: |
| static Ref<ShareableElementData> createWithAttributes(const Vector<Attribute>&); |
| |
| explicit ShareableElementData(const Vector<Attribute>&); |
| explicit ShareableElementData(const UniqueElementData&); |
| ~ShareableElementData(); |
| |
| static ptrdiff_t attributeArrayMemoryOffset() { return OBJECT_OFFSETOF(ShareableElementData, m_attributeArray); } |
| |
| Attribute m_attributeArray[0]; |
| }; |
| |
| #if COMPILER(MSVC) |
| #pragma warning(pop) |
| #endif |
| |
| class UniqueElementData : public ElementData { |
| public: |
| static Ref<UniqueElementData> create(); |
| Ref<ShareableElementData> makeShareableCopy() const; |
| |
| // These functions do no error/duplicate checking. |
| void addAttribute(const QualifiedName&, const AtomString&); |
| void removeAttribute(unsigned index); |
| |
| Attribute& attributeAt(unsigned index); |
| Attribute* findAttributeByName(const QualifiedName&); |
| |
| UniqueElementData(); |
| explicit UniqueElementData(const ShareableElementData&); |
| explicit UniqueElementData(const UniqueElementData&); |
| |
| static ptrdiff_t attributeVectorMemoryOffset() { return OBJECT_OFFSETOF(UniqueElementData, m_attributeVector); } |
| |
| mutable RefPtr<StyleProperties> m_presentationalHintStyle; |
| typedef Vector<Attribute, 4> AttributeVector; |
| AttributeVector m_attributeVector; |
| }; |
| |
| inline void ElementData::deref() |
| { |
| if (!derefBase()) |
| return; |
| destroy(); |
| } |
| |
| inline unsigned ElementData::length() const |
| { |
| if (is<UniqueElementData>(*this)) |
| return downcast<UniqueElementData>(*this).m_attributeVector.size(); |
| return arraySize(); |
| } |
| |
| inline const Attribute* ElementData::attributeBase() const |
| { |
| if (is<UniqueElementData>(*this)) |
| return downcast<UniqueElementData>(*this).m_attributeVector.data(); |
| return downcast<ShareableElementData>(*this).m_attributeArray; |
| } |
| |
| inline const StyleProperties* ElementData::presentationalHintStyle() const |
| { |
| if (!is<UniqueElementData>(*this)) |
| return nullptr; |
| return downcast<UniqueElementData>(*this).m_presentationalHintStyle.get(); |
| } |
| |
| inline AttributeIteratorAccessor ElementData::attributesIterator() const |
| { |
| if (is<UniqueElementData>(*this)) { |
| const Vector<Attribute, 4>& attributeVector = downcast<UniqueElementData>(*this).m_attributeVector; |
| return AttributeIteratorAccessor(attributeVector.data(), attributeVector.size()); |
| } |
| return AttributeIteratorAccessor(downcast<ShareableElementData>(*this).m_attributeArray, arraySize()); |
| } |
| |
| ALWAYS_INLINE const Attribute* ElementData::findAttributeByName(const AtomString& name, bool shouldIgnoreAttributeCase) const |
| { |
| unsigned index = findAttributeIndexByName(name, shouldIgnoreAttributeCase); |
| if (index != attributeNotFound) |
| return &attributeAt(index); |
| return nullptr; |
| } |
| |
| ALWAYS_INLINE unsigned ElementData::findAttributeIndexByName(const QualifiedName& name) const |
| { |
| const Attribute* attributes = attributeBase(); |
| for (unsigned i = 0, count = length(); i < count; ++i) { |
| if (attributes[i].name().matches(name)) |
| return i; |
| } |
| return attributeNotFound; |
| } |
| |
| // We use a boolean parameter instead of calling shouldIgnoreAttributeCase so that the caller |
| // can tune the behavior (hasAttribute is case sensitive whereas getAttribute is not). |
| ALWAYS_INLINE unsigned ElementData::findAttributeIndexByName(const AtomString& name, bool shouldIgnoreAttributeCase) const |
| { |
| unsigned attributeCount = length(); |
| if (!attributeCount) |
| return attributeNotFound; |
| |
| const Attribute* attributes = attributeBase(); |
| const AtomString& caseAdjustedName = shouldIgnoreAttributeCase ? name.convertToASCIILowercase() : name; |
| |
| unsigned attributeIndex = 0; |
| do { |
| const Attribute& attribute = attributes[attributeIndex]; |
| if (!attribute.name().hasPrefix()) { |
| if (attribute.localName() == caseAdjustedName) |
| return attributeIndex; |
| } else { |
| if (attribute.name().toString() == caseAdjustedName) |
| return attributeIndex; |
| } |
| |
| ++attributeIndex; |
| } while (attributeIndex < attributeCount); |
| |
| return attributeNotFound; |
| } |
| |
| ALWAYS_INLINE const Attribute* ElementData::findAttributeByName(const QualifiedName& name) const |
| { |
| const Attribute* attributes = attributeBase(); |
| for (unsigned i = 0, count = length(); i < count; ++i) { |
| if (attributes[i].name().matches(name)) |
| return &attributes[i]; |
| } |
| return 0; |
| } |
| |
| inline const Attribute& ElementData::attributeAt(unsigned index) const |
| { |
| RELEASE_ASSERT(index < length()); |
| return attributeBase()[index]; |
| } |
| |
| inline void UniqueElementData::addAttribute(const QualifiedName& attributeName, const AtomString& value) |
| { |
| m_attributeVector.append(Attribute(attributeName, value)); |
| } |
| |
| inline void UniqueElementData::removeAttribute(unsigned index) |
| { |
| m_attributeVector.remove(index); |
| } |
| |
| inline Attribute& UniqueElementData::attributeAt(unsigned index) |
| { |
| return m_attributeVector.at(index); |
| } |
| |
| } // namespace WebCore |
| |
| SPECIALIZE_TYPE_TRAITS_BEGIN(WebCore::ShareableElementData) |
| static bool isType(const WebCore::ElementData& elementData) { return !elementData.isUnique(); } |
| SPECIALIZE_TYPE_TRAITS_END() |
| |
| SPECIALIZE_TYPE_TRAITS_BEGIN(WebCore::UniqueElementData) |
| static bool isType(const WebCore::ElementData& elementData) { return elementData.isUnique(); } |
| SPECIALIZE_TYPE_TRAITS_END() |