Home | History | Annotate | Download | only in dom
      1 /*
      2  * Copyright (C) 1999 Lars Knoll (knoll (at) kde.org)
      3  *           (C) 1999 Antti Koivisto (koivisto (at) kde.org)
      4  *           (C) 2001 Peter Kelly (pmk (at) post.com)
      5  *           (C) 2001 Dirk Mueller (mueller (at) kde.org)
      6  * Copyright (C) 2004, 2005, 2006, 2007, 2009, 2010, 2012 Apple Inc. All rights reserved.
      7  *
      8  * This library is free software; you can redistribute it and/or
      9  * modify it under the terms of the GNU Library General Public
     10  * License as published by the Free Software Foundation; either
     11  * version 2 of the License, or (at your option) any later version.
     12  *
     13  * This library is distributed in the hope that it will be useful,
     14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
     15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
     16  * Library General Public License for more details.
     17  *
     18  * You should have received a copy of the GNU Library General Public License
     19  * along with this library; see the file COPYING.LIB.  If not, write to
     20  * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
     21  * Boston, MA 02110-1301, USA.
     22  */
     23 #include "config.h"
     24 #include "core/dom/Attr.h"
     25 
     26 #include "XMLNSNames.h"
     27 #include "bindings/v8/ExceptionState.h"
     28 #include "bindings/v8/ExceptionStatePlaceholder.h"
     29 #include "core/dom/Element.h"
     30 #include "core/dom/ExceptionCode.h"
     31 #include "core/dom/ScopedEventQueue.h"
     32 #include "core/dom/Text.h"
     33 #include "wtf/text/AtomicString.h"
     34 #include "wtf/text/StringBuilder.h"
     35 
     36 namespace WebCore {
     37 
     38 using namespace HTMLNames;
     39 
     40 Attr::Attr(Element* element, const QualifiedName& name)
     41     : ContainerNode(element->document())
     42     , m_element(element)
     43     , m_name(name)
     44     , m_ignoreChildrenChanged(0)
     45     , m_specified(true)
     46 {
     47     ScriptWrappable::init(this);
     48 }
     49 
     50 Attr::Attr(Document* document, const QualifiedName& name, const AtomicString& standaloneValue)
     51     : ContainerNode(document)
     52     , m_element(0)
     53     , m_name(name)
     54     , m_standaloneValue(standaloneValue)
     55     , m_ignoreChildrenChanged(0)
     56     , m_specified(true)
     57 {
     58     ScriptWrappable::init(this);
     59 }
     60 
     61 PassRefPtr<Attr> Attr::create(Element* element, const QualifiedName& name)
     62 {
     63     RefPtr<Attr> attr = adoptRef(new Attr(element, name));
     64     attr->createTextChild();
     65     return attr.release();
     66 }
     67 
     68 PassRefPtr<Attr> Attr::create(Document* document, const QualifiedName& name, const AtomicString& value)
     69 {
     70     RefPtr<Attr> attr = adoptRef(new Attr(document, name, value));
     71     attr->createTextChild();
     72     return attr.release();
     73 }
     74 
     75 Attr::~Attr()
     76 {
     77 }
     78 
     79 void Attr::createTextChild()
     80 {
     81     ASSERT(refCount());
     82     if (!value().isEmpty()) {
     83         RefPtr<Text> textNode = document()->createTextNode(value().string());
     84 
     85         // This does everything appendChild() would do in this situation (assuming m_ignoreChildrenChanged was set),
     86         // but much more efficiently.
     87         textNode->setParentOrShadowHostNode(this);
     88         treeScope()->adoptIfNeeded(textNode.get());
     89         setFirstChild(textNode.get());
     90         setLastChild(textNode.get());
     91     }
     92 }
     93 
     94 void Attr::setPrefix(const AtomicString& prefix, ExceptionState& es)
     95 {
     96     checkSetPrefix(prefix, es);
     97     if (es.hadException())
     98         return;
     99 
    100     if ((prefix == xmlnsAtom && namespaceURI() != XMLNSNames::xmlnsNamespaceURI)
    101         || static_cast<Attr*>(this)->qualifiedName() == xmlnsAtom) {
    102         es.throwDOMException(NamespaceError);
    103         return;
    104     }
    105 
    106     const AtomicString& newPrefix = prefix.isEmpty() ? nullAtom : prefix;
    107 
    108     if (m_element)
    109         elementAttribute().setPrefix(newPrefix);
    110     m_name.setPrefix(newPrefix);
    111 }
    112 
    113 void Attr::setValue(const AtomicString& value)
    114 {
    115     EventQueueScope scope;
    116     m_ignoreChildrenChanged++;
    117     removeChildren();
    118     if (m_element)
    119         elementAttribute().setValue(value);
    120     else
    121         m_standaloneValue = value;
    122     createTextChild();
    123     m_ignoreChildrenChanged--;
    124 
    125     invalidateNodeListCachesInAncestors(&m_name, m_element);
    126 }
    127 
    128 void Attr::setValue(const AtomicString& value, ExceptionState&)
    129 {
    130     if (m_element)
    131         m_element->willModifyAttribute(qualifiedName(), this->value(), value);
    132 
    133     setValue(value);
    134 
    135     if (m_element)
    136         m_element->didModifyAttribute(qualifiedName(), value);
    137 }
    138 
    139 void Attr::setNodeValue(const String& v)
    140 {
    141     setValue(v, IGNORE_EXCEPTION);
    142 }
    143 
    144 PassRefPtr<Node> Attr::cloneNode(bool /*deep*/)
    145 {
    146     RefPtr<Attr> clone = adoptRef(new Attr(document(), qualifiedName(), value()));
    147     cloneChildNodes(clone.get());
    148     return clone.release();
    149 }
    150 
    151 // DOM Section 1.1.1
    152 bool Attr::childTypeAllowed(NodeType type) const
    153 {
    154     return TEXT_NODE == type;
    155 }
    156 
    157 void Attr::childrenChanged(bool, Node*, Node*, int)
    158 {
    159     if (m_ignoreChildrenChanged > 0)
    160         return;
    161 
    162     invalidateNodeListCachesInAncestors(&qualifiedName(), m_element);
    163 
    164     StringBuilder valueBuilder;
    165     for (Node *n = firstChild(); n; n = n->nextSibling()) {
    166         if (n->isTextNode())
    167             valueBuilder.append(toText(n)->data());
    168     }
    169 
    170     AtomicString newValue = valueBuilder.toAtomicString();
    171     if (m_element)
    172         m_element->willModifyAttribute(qualifiedName(), value(), newValue);
    173 
    174     if (m_element)
    175         elementAttribute().setValue(newValue);
    176     else
    177         m_standaloneValue = newValue;
    178 
    179     if (m_element)
    180         m_element->attributeChanged(qualifiedName(), newValue);
    181 }
    182 
    183 bool Attr::isId() const
    184 {
    185     return qualifiedName().matches(document()->idAttributeName());
    186 }
    187 
    188 const AtomicString& Attr::value() const
    189 {
    190     if (m_element)
    191         return m_element->getAttribute(qualifiedName());
    192     return m_standaloneValue;
    193 }
    194 
    195 Attribute& Attr::elementAttribute()
    196 {
    197     ASSERT(m_element);
    198     ASSERT(m_element->elementData());
    199     return *m_element->ensureUniqueElementData()->getAttributeItem(qualifiedName());
    200 }
    201 
    202 void Attr::detachFromElementWithValue(const AtomicString& value)
    203 {
    204     ASSERT(m_element);
    205     ASSERT(m_standaloneValue.isNull());
    206     m_standaloneValue = value;
    207     m_element = 0;
    208 }
    209 
    210 void Attr::attachToElement(Element* element)
    211 {
    212     ASSERT(!m_element);
    213     m_element = element;
    214     m_standaloneValue = nullAtom;
    215 }
    216 
    217 }
    218