/*
 * This file is part of the DOM implementation for KDE.
 *
 * Copyright (C) 1999 Lars Knoll (knoll@kde.org)
 *           (C) 1999 Antti Koivisto (koivisto@kde.org)
 *           (C) 2001 Peter Kelly (pmk@post.com)
 *           (C) 2001 Dirk Mueller (mueller@kde.org)
 *           (C) 2003, 2004, 2005, 2006, 2007 Apple Inc. All rights reserved.
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Library General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Library General Public License for more details.
 *
 * You should have received a copy of the GNU Library General Public License
 * along with this library; see the file COPYING.LIB.  If not, write to
 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
 * Boston, MA 02110-1301, USA.
 *
 */
#ifndef _DOM_ELEMENTImpl_h_
#define _DOM_ELEMENTImpl_h_

#include "dom_nodeimpl.h"
#include "dom/dom_exception.h"
#include "dom/dom_element.h"
#include "xml/dom_stringimpl.h"
#include "misc/shared.h"
#include "wtf/Vector.h"
#include "xml/ClassNames.h"

// WebCore SVG
#include "dom/QualifiedName.h"
// End WebCore SVG

namespace khtml
{
class CSSStyleSelector;
}

namespace DOM
{

class ElementImpl;
class DocumentImpl;
class NamedAttrMapImpl;
class ElementRareDataImpl;
class CSSInlineStyleDeclarationImpl;

// Attr can have Text and EntityReference children
// therefore it has to be a fullblown Node. The plan
// is to dynamically allocate a textchild and store the
// resulting nodevalue in the AttributeImpl upon
// destruction. however, this is not yet implemented.
class AttrImpl : public NodeBaseImpl
{
    friend class ElementImpl;
    friend class NamedAttrMapImpl;

public:
    AttrImpl(ElementImpl *element, DocumentImpl *docPtr, NamespaceName namespaceName, LocalName localName, PrefixName prefix, DOMStringImpl *value);
    ~AttrImpl();

private:
    AttrImpl(const AttrImpl &other);
    AttrImpl &operator = (const AttrImpl &other);
    void createTextChild();
public:

    // DOM methods & attributes for Attr
    bool specified() const
    {
        return true; /* we don't yet support default attributes*/
    }
    ElementImpl *ownerElement() const
    {
        return m_element;
    }
    void setOwnerElement(ElementImpl *impl)
    {
        m_element = impl;
    }
    DOMString name() const;

    //DOMString value() const;
    void setValue(const DOMString &v, int &exceptioncode);

    // DOM methods overridden from  parent classes
    DOMString nodeName() const override;
    unsigned short nodeType() const override;
    DOMString prefix() const override;
    void setPrefix(const DOMString &refix, int &exceptioncode) override;
    DOMString namespaceURI() const override;
    DOMString localName() const override;

    inline const PrefixName &prefixName() const
    {
        return m_prefix;
    }

    DOMString nodeValue() const override;
    void setNodeValue(const DOMString &, int &exceptioncode) override;
    WTF::PassRefPtr<NodeImpl> cloneNode(bool deep) override;

    // Other methods (not part of DOM)
    bool isAttributeNode() const override
    {
        return true;
    }
    bool childAllowed(NodeImpl *newChild) override;
    bool childTypeAllowed(unsigned short type) override;
    NodeImpl::Id id() const override
    {
        return makeId(m_namespace.id(), m_localName.id());
    }
    void childrenChanged() override;

    // non-virtual id, for faster attribute look-ups
    inline NodeImpl::Id fastId() const
    {
        return makeId(m_namespace.id(), m_localName.id());
    }

    // This is used for when the value is normalized on setting by
    // parseAttribute; it silently updates it w/o issuing any events, etc.
    // Doesn't work for ATTR_ID!
    void rewriteValue(const DOMString &newValue);

    DOMString toString() const override;

    void setElement(ElementImpl *element);
    DOMStringImpl *val() const
    {
        return m_value;
    }

protected:
    ElementImpl *m_element;
    LocalName m_localName;
    NamespaceName m_namespace;
    PrefixName m_prefix;
    DOMStringImpl *m_value;
};

// Mini version of AttrImpl internal to NamedAttrMapImpl.
// Stores either the id (LocalName and NamespaceName) and value of an attribute
// (in the case of m_localName.id() != 0), or a pointer to an AttrImpl (if m_localName.id() == 0)
// The latter case only happens when the Attr node is requested by some DOM
// code or is an XML attribute.
// In most cases the id and value is all we need to store, which is more
// memory efficient.
// FIXME: update comment - no longer create mini version for only html attributes
struct AttributeImpl {
    inline NodeImpl::Id id() const
    {
        return m_localName.id() ? makeId(m_namespace.id(), m_localName.id()) : m_data.attr->fastId();
    }
    DOMStringImpl *val() const
    {
        if (m_localName.id()) {
            return m_data.value;
        } else {
            return m_data.attr->val();
        }
    }
    DOMString value() const
    {
        return val();
    }
    AttrImpl *attr() const
    {
        return m_localName.id() ? nullptr : m_data.attr;
    }
    DOMString namespaceURI() const
    {
        return m_localName.id() ? m_namespace.toString() : m_data.attr->namespaceURI();
    }
    DOMString prefix() const
    {
        return m_localName.id() ? m_prefix.toString() : m_data.attr->prefix();
    }
    DOMString localName() const
    {
        return m_localName.id() ? m_localName.toString() : m_data.attr->localName();
    }
    const PrefixName &prefixName() const
    {
        return m_localName.id() ? m_prefix : m_data.attr->prefixName();
    }

    // for WebCore api compat
    QualifiedName name() const
    {
        return m_localName.id() ? QualifiedName(m_prefix, m_localName, m_namespace) : QualifiedName(id(), PrefixName::fromString(prefix()));
    }

    void setValue(DOMStringImpl *value, ElementImpl *element);
    AttrImpl *createAttr(ElementImpl *element, DocumentImpl *docPtr);
    void free();

    // See the description for AttrImpl.
    void rewriteValue(const DOMString &newValue);

    LocalName m_localName;
    NamespaceName m_namespace;
    PrefixName m_prefix;
    union {
        DOMStringImpl *value;
        AttrImpl *attr;
    } m_data;
};

struct CombinedStyleDecl {
    DOM::CSSInlineStyleDeclarationImpl *inlineDecls;
    DOM::CSSStyleDeclarationImpl *nonCSSDecls;
};

class ElementImpl : public NodeBaseImpl
{
    friend class DocumentImpl;
    friend class NamedAttrMapImpl;
    friend class AttrImpl;
    friend class NodeImpl;
    friend class khtml::CSSStyleSelector;
public:
    ElementImpl(DocumentImpl *doc);
    ~ElementImpl();

    // stuff for WebCore DOM & SVG api compatibility
    bool hasTagName(const QualifiedName &name) const override
    {
        return qualifiedName() == name;/*should be matches here*/
    }
    QualifiedName qualifiedName() const
    {
        return QualifiedName(id(), m_prefix);
    }
    CSSInlineStyleDeclarationImpl *style()
    {
        return getInlineStyleDecls();
    }
    void setAttribute(const QualifiedName &name, const DOMString &value)
    {
        setAttribute(name.id(), value); /* is it enough for SVG or should the full setAttribute() be called? */
    }
    bool hasAttribute(const QualifiedName &name) const
    {
        return hasAttribute(name.tagName());
    }
    DOMString getAttribute(const QualifiedName &name) const
    {
        int ec;
        return const_cast<ElementImpl *>(this)->getAttributeNS(name.namespaceURI(), name.localName(), ec);
    }
    DOMString getAttributeNS(const DOMString &namespaceURI, const DOMString &localName, int &exceptionCode) const
    {
        return const_cast<ElementImpl *>(this)->getAttributeNS(namespaceURI, localName, exceptionCode);
    }
    // FIXME: get rid of const_cast hacks (the const qualifiers of getAttribute should be reviewed
    // as for external API it should look like const,hower we can replace AttributeImpl (small version)
    // with normal AttrImpl (NodeImpl)
    // END OF FIXME
    // enf of WC api compatibility stuff

    //Higher-level DOM stuff
    bool hasAttributes() const override;
    bool hasAttribute(const DOMString &name) const;
    bool hasAttributeNS(const DOMString &namespaceURI, const DOMString &localName) const;
    DOMString getAttribute(const DOMString &name);
    void setAttribute(const DOMString &name, const DOMString &value, int &exceptioncode);
    void removeAttribute(const DOMString &name, int &exceptioncode);
    void removeAttribute(NodeImpl::Id id, int &exceptioncode);
    AttrImpl *getAttributeNode(const DOMString &name);
    Attr setAttributeNode(AttrImpl *newAttr, int &exceptioncode);
    Attr removeAttributeNode(AttrImpl *oldAttr, int &exceptioncode);

    DOMString getAttributeNS(const DOMString &namespaceURI, const DOMString &localName, int &exceptioncode);
    void removeAttributeNS(const DOMString &namespaceURI, const DOMString &localName, int &exceptioncode);
    AttrImpl *getAttributeNodeNS(const DOMString &namespaceURI, const DOMString &localName, int &exceptioncode);
    Attr setAttributeNodeNS(AttrImpl *newAttr, int &exceptioncode);

    // At this level per WAI-ARIA
    void blur();
    void focus();

    //Lower-level implementation primitives
    inline DOMString getAttribute(NodeImpl::Id id, const PrefixName &prefix = emptyPrefixName, bool nsAware = false) const
    {
        return DOMString(getAttributeImpl(id, prefix, nsAware));
    }
    inline DOMStringImpl *getAttributeImpl(NodeImpl::Id id, const PrefixName &prefix = emptyPrefixName, bool nsAware = false) const;
    void setAttribute(NodeImpl::Id id, const PrefixName &prefix, bool nsAware, const DOMString &value, int &exceptioncode);
    void setAttributeNS(const DOMString &namespaceURI, const DOMString &localName, const DOMString &value, int &exceptioncode);

    inline DOMStringImpl *getAttributeImplById(NodeImpl::Id id) const;

    // WebCore API
    DOMString getIDAttribute() const
    {
        return getAttribute(ATTR_ID);
    }
    // End

    bool hasAttribute(NodeImpl::Id id, const PrefixName &prefix = emptyPrefixName, bool nsAware = false) const
    {
        return getAttributeImpl(id, prefix, nsAware) != nullptr;
    }
    DOMString prefix() const override
    {
        return m_prefix.toString();
    }
    void setPrefix(const DOMString &_prefix, int &exceptioncode) override;
    DOMString namespaceURI() const override;
    inline const PrefixName &prefixName() const
    {
        return m_prefix;
    }

    short tabIndex() const override;
    void setTabIndex(short _tabIndex);
    void setNoTabIndex();
    bool hasTabIndex() const;

    // DOM methods overridden from  parent classes
    virtual DOMString tagName() const;
    DOMString localName() const override;

    // Internal version of tagName for elements that doesn't
    // do case normalization
    DOMString nonCaseFoldedTagName() const;

    unsigned short nodeType() const override;
    WTF::PassRefPtr<NodeImpl> cloneNode(bool deep) override;
    DOMString nodeName() const override;
    NodeImpl::Id id() const override = 0;
    bool isElementNode() const override
    {
        return true;
    }
    void insertedIntoDocument() override;
    void removedFromDocument() override;

    // ElementTraversal API
    ElementImpl *firstElementChild() const;
    ElementImpl *lastElementChild() const;
    ElementImpl *previousElementSibling() const;
    ElementImpl *nextElementSibling() const;
    unsigned childElementCount() const;

    // convenience methods which ignore exceptions
    void setAttribute(NodeImpl::Id id, const DOMString &value);
    void setBooleanAttribute(NodeImpl::Id id, bool b);

    NamedAttrMapImpl *attributes(bool readonly = false) const
    {
        if (m_needsStyleAttributeUpdate) {
            synchronizeStyleAttribute();
        }
        if (!readonly && !namedAttrMap) {
            createAttributeMap();
        }
        return namedAttrMap;
    }

    //This is always called, whenever an attribute changed
    virtual void parseAttribute(AttributeImpl *) {}
    void parseNullAttribute(NodeImpl::Id id, PrefixName prefix)
    {
        AttributeImpl aimpl;
        aimpl.m_localName = LocalName::fromId(localNamePart(id));
        aimpl.m_namespace = NamespaceName::fromId(namespacePart(id));
        aimpl.m_prefix = prefix;
        aimpl.m_data.value = nullptr;
        parseAttribute(&aimpl);
    }

    void parseAttribute(AttrImpl *fullAttr)
    {
        AttributeImpl aimpl;
        aimpl.m_localName = emptyLocalName;
        aimpl.m_data.attr = fullAttr;
        parseAttribute(&aimpl);
    }

    inline const ClassNames &classNames() const;

    // not part of the DOM
    void setAttributeMap(NamedAttrMapImpl *list);

    // State of the element.
    QString state() override
    {
        return QString();
    }

    virtual void copyNonAttributeProperties(const ElementImpl * /*source*/) {}

    void attach() override;
    void close() override;
    void detach() override;
    void structureChanged() override;
    void backwardsStructureChanged() override;
    virtual void attributeChanged(NodeImpl::Id attrId);
    // for WebCore API compatibility
    virtual void attributeChanged(AttributeImpl *attribute, bool /*preserveDecls*/)
    {
        attributeChanged(attribute->id());
    }

    void defaultEventHandler(EventImpl *evt) override;

    khtml::RenderStyle *styleForRenderer(khtml::RenderObject *parent) override;
    khtml::RenderObject *createRenderer(khtml::RenderArena *, khtml::RenderStyle *) override;
    void recalcStyle(StyleChange = NoChange) override;
    khtml::RenderStyle *computedStyle() override;

    virtual void mouseEventHandler(MouseEvent * /*ev*/, bool /*inside*/) {}

    bool childAllowed(NodeImpl *newChild) override;
    bool childTypeAllowed(unsigned short type) override;
    DOM::CSSInlineStyleDeclarationImpl *inlineStyleDecls() const
    {
        return m_hasCombinedStyle ? m_style.combinedDecls->inlineDecls : m_style.inlineDecls;
    }
    DOM::CSSStyleDeclarationImpl *nonCSSStyleDecls() const
    {
        return m_hasCombinedStyle ? m_style.combinedDecls->nonCSSDecls : nullptr;
    }
    DOM::CSSInlineStyleDeclarationImpl *getInlineStyleDecls();

    void dispatchAttrRemovalEvent(NodeImpl::Id id, DOMStringImpl *value);
    void dispatchAttrAdditionEvent(NodeImpl::Id id, DOMStringImpl *value);

    DOMString toString() const override;
    DOMString selectionToString(NodeImpl *selectionStart, NodeImpl *selectionEnd, int startOffset, int endOffset, bool &found) const override;

    bool isFocusableImpl(FocusType ft) const override;
    bool isContentEditable() const override;
    void setContentEditable(bool enabled);

    void scrollIntoView(bool alignToTop);

    /** Returns the opening tag and properties.
     *  Examples:  '<b', '<img alt="hello" src="image.png"
     *
     *  For security reasons, passwords are stripped out of all src= and
     *  href=  tags if expandurls is turned on.
     *
     *  @param expandurls If this is set then in the above example, it would give
     *                    src="http://website.com/image.png".  Note that the password
     *                    is stripped out of the url.
     *
     *  DOM::RangeImpl uses this which is why it is public.
     */
    DOMString openTagStartToString(bool expandurls = false) const;

    void updateId(DOMStringImpl *oldId, DOMStringImpl *newId);
    //Called when mapping from id to this node in document should be removed
    virtual void removeId(const DOMString &id);
    //Called when mapping from id to this node in document should be added
    virtual void addId(const DOMString &id);

    // Synchronize style attribute after it was changed via CSSOM
    void synchronizeStyleAttribute() const;

protected:
    void createAttributeMap() const;
    void createInlineDecl(); // for inline styles
    void createNonCSSDecl(); // for presentational styles
    void finishCloneNode(ElementImpl *clone, bool deep);

private:
    // There is some information such as computed style for display:none
    // elements that is needed only for a few elements. We store it
    // in one of these.
    ElementRareDataImpl *rareData();
    const ElementRareDataImpl *rareData() const;
    ElementRareDataImpl *createRareData();

protected: // member variables
    mutable NamedAttrMapImpl *namedAttrMap;

    union {
        DOM::CSSInlineStyleDeclarationImpl *inlineDecls;
        CombinedStyleDecl *combinedDecls;
    } m_style;
    PrefixName m_prefix;
};

class XMLElementImpl : public ElementImpl
{

public:
    XMLElementImpl(DocumentImpl *doc, NamespaceName namespacename, LocalName localName, PrefixName prefix);
    ~XMLElementImpl();

    void parseAttribute(AttributeImpl *attr) override;

    WTF::PassRefPtr<NodeImpl> cloneNode(bool deep) override;

    // Other methods (not part of DOM)
    bool isXMLElementNode() const override
    {
        return true;
    }
    Id id() const override
    {
        return makeId(m_namespace.id(), m_localName.id());
    }

protected:
    LocalName m_localName;
    NamespaceName m_namespace;
};

// the map of attributes of an element
class NamedAttrMapImpl : public NamedNodeMapImpl
{
    friend class ElementImpl;
public:
    NamedAttrMapImpl(ElementImpl *element);
    virtual ~NamedAttrMapImpl();

    // DOM methods & attributes for NamedNodeMap
    NodeImpl *getNamedItem(NodeImpl::Id id, const PrefixName &prefix = emptyPrefixName, bool nsAware = false) override;
    Node removeNamedItem(NodeImpl::Id id, const PrefixName &prefix, bool nsAware, int &exceptioncode) override;
    Node setNamedItem(NodeImpl *arg, const PrefixName &prefix, bool nsAware, int &exceptioncode) override;

    // for WebCore api compat
    virtual NodeImpl *getNamedItem(const QualifiedName &name)
    {
        return getNamedItem(name.id(), name.prefixId(), true);
    }

    NodeImpl *item(unsigned index) override;
    unsigned length() const override
    {
        return m_attrs.size();
    }

    // Other methods (not part of DOM)
    bool isReadOnly() override
    {
        return false;
    }
    bool htmlCompat() override
    {
        return m_element ? m_element->m_htmlCompat : false;
    }

    AttributeImpl &attributeAt(unsigned index)
    {
        return m_attrs[index];
    }
    const AttributeImpl &attributeAt(unsigned index) const
    {
        return m_attrs[index];
    }
    AttrImpl *attrAt(unsigned index) const
    {
        return m_attrs[index].attr();
    }
    // ### replace idAt and getValueAt with attrAt
    NodeImpl::Id idAt(unsigned index) const;
    DOMStringImpl *valueAt(unsigned index) const;
    DOMStringImpl *getValue(NodeImpl::Id id, const PrefixName &prefix = emptyPrefixName, bool nsAware = false) const;
    void setValue(NodeImpl::Id id, DOMStringImpl *value, const PrefixName &prefix = emptyPrefixName, bool nsAware = false);
    Attr removeAttr(AttrImpl *attr);
    void copyAttributes(NamedAttrMapImpl *other);
    void setElement(ElementImpl *element);
    void detachFromElement();

    int find(NodeImpl::Id id, const PrefixName &prefix, bool nsAware) const;
    inline DOMStringImpl *fastFind(NodeImpl::Id id) const;

    inline void clearClass()
    {
        m_classNames.clear();
    }
    void setClass(const DOMString &string);
    inline const ClassNames &classNames() const
    {
        return m_classNames;
    }

    // Rewrites value if attribute already exists or adds a new attribute otherwise
    // @important: any changes won't be propagated to parent Element
    void setValueWithoutElementUpdate(NodeImpl::Id id, DOMStringImpl *value);

protected:
    ElementImpl *m_element;
    WTF::Vector<AttributeImpl> m_attrs;
    ClassNames m_classNames;
};

// ------------  inline DOM helper functions ---------------

inline bool checkQualifiedName(const DOMString &qualifiedName, const DOMString &namespaceURI, int *colonPos,
                               bool nameCanBeNull, bool nameCanBeEmpty, int *pExceptioncode)
{

    // Not mentioned in spec: throw NAMESPACE_ERR if no qualifiedName supplied
    if (!nameCanBeNull && qualifiedName.isNull()) {
        if (pExceptioncode) {
            *pExceptioncode = DOMException::NAMESPACE_ERR;
        }
        return false;
    }

    // INVALID_CHARACTER_ERR: Raised if the specified qualified name contains an illegal character.
    if (!qualifiedName.isNull() && !Element::khtmlValidQualifiedName(qualifiedName)
            && (!qualifiedName.isEmpty() || !nameCanBeEmpty)) {
        if (pExceptioncode) {
            *pExceptioncode = DOMException::INVALID_CHARACTER_ERR;
        }
        return false;
    }

    // NAMESPACE_ERR:
    // - Raised if the qualifiedName is malformed,
    // - if the qualifiedName has a prefix and the namespaceURI is null, or
    // - if the qualifiedName is null and the namespaceURI is different from null
    // - if the qualifiedName has a prefix that is "xml" and the namespaceURI is different
    //   from "http://www.w3.org/XML/1998/namespace" [Namespaces].
    int colonpos = -1;
    uint i;
    DOMStringImpl *qname = qualifiedName.implementation();
    for (i = 0; i < (qname ? qname->l : 0); i++) {
        if ((*qname)[i] == QLatin1Char(':')) {
            colonpos = i;
            break;
        }
    }

    bool hasXMLPrefix = colonpos == 3 && (*qname)[0] == QLatin1Char('x') &&
                        (*qname)[1] == QLatin1Char('m') && (*qname)[2] == QLatin1Char('l');
    bool hasXMLNSPrefix = colonpos == 5 && (*qname)[0] == QLatin1Char('x') &&
                          (*qname)[1] == QLatin1Char('m') && (*qname)[2] == QLatin1Char('l') &&
                          (*qname)[3] == QLatin1Char('n') && (*qname)[4] == QLatin1Char('s');

    if ((!qualifiedName.isNull() && Element::khtmlMalformedQualifiedName(qualifiedName)) ||
            (colonpos >= 0 && namespaceURI.isNull()) ||
            colonpos == 0 || // prefix has to consist of at least a letter
            (qualifiedName.isNull() && !namespaceURI.isNull()) ||
            (hasXMLPrefix && namespaceURI != XML_NAMESPACE) ||
            (hasXMLNSPrefix && namespaceURI != XMLNS_NAMESPACE) ||
            (namespaceURI == XMLNS_NAMESPACE && !hasXMLNSPrefix && qualifiedName != "xmlns")) {
        if (pExceptioncode) {
            *pExceptioncode = DOMException::NAMESPACE_ERR;
        }
        return false;
    }
    if (colonPos) {
        *colonPos = colonpos;
    }
    return true;
}

inline void splitPrefixLocalName(DOMStringImpl *qualifiedName, DOMString &prefix, DOMString &localName, int colonPos = -2)
{
    if (colonPos == -2)
        for (uint i = 0; i < qualifiedName->l; ++i)
            if (qualifiedName->s[i] == QLatin1Char(':')) {
                colonPos = i;
                break;
            }
    if (colonPos >= 0) {
        prefix = qualifiedName->copy();
        localName = prefix.split(colonPos + 1);
        prefix.implementation()->truncate(colonPos);
    } else {
        localName = qualifiedName;
    }
}

inline void splitPrefixLocalName(const DOMString &qualifiedName, PrefixName &prefix, LocalName &localName, bool htmlCompat = false, int colonPos = -2)
{
    DOMString localname, prefixname;
    splitPrefixLocalName(qualifiedName.implementation(), prefixname, localname, colonPos);
    if (htmlCompat) {
        prefix = PrefixName::fromString(prefixname, khtml::IDS_NormalizeLower);
        localName = LocalName::fromString(localname, khtml::IDS_NormalizeLower);
    } else {
        prefix = PrefixName::fromString(prefixname);
        localName = LocalName::fromString(localname);
    }
}

// methods that could be very hot and therefore need to be inlined
inline DOMStringImpl *ElementImpl::getAttributeImpl(NodeImpl::Id id, const PrefixName &prefix, bool nsAware) const
{
    if (m_needsStyleAttributeUpdate && (id == ATTR_STYLE)) {
        synchronizeStyleAttribute();
    }
    return namedAttrMap ? namedAttrMap->getValue(id, prefix, nsAware) : nullptr;
}

inline const ClassNames &ElementImpl::classNames() const
{
    // hasClass() should be called on element first as in CSSStyleSelector::checkSimpleSelector
    return namedAttrMap->classNames();
}

inline DOMStringImpl *ElementImpl::getAttributeImplById(NodeImpl::Id id) const
{
    return namedAttrMap ? namedAttrMap->fastFind(id) : nullptr;
}

inline DOMStringImpl *NamedAttrMapImpl::fastFind(NodeImpl::Id id) const
{
    unsigned mask = anyQName;
    if (namespacePart(id) == anyNamespace) {
        mask = anyLocalName;
        id = localNamePart(id);
    }
    unsigned len = m_attrs.size();
    for (unsigned i = 0; i < len; ++i)
        if (id == (m_attrs[i].id() & mask)) {
            return m_attrs[i].val();
        }
    return nullptr;
}

} //namespace

#endif
