NodeLists should not invalidate on irreleavnt attribute changes
[WebKit-https.git] / Source / WebCore / dom / Attr.cpp
1 /*
2  * Copyright (C) 1999 Lars Knoll (knoll@kde.org)
3  *           (C) 1999 Antti Koivisto (koivisto@kde.org)
4  *           (C) 2001 Peter Kelly (pmk@post.com)
5  *           (C) 2001 Dirk Mueller (mueller@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 "Attr.h"
25
26 #include "ExceptionCode.h"
27 #include "HTMLNames.h"
28 #include "ScopedEventQueue.h"
29 #include "StyledElement.h"
30 #include "Text.h"
31 #include "XMLNSNames.h"
32 #include <wtf/text/AtomicString.h>
33 #include <wtf/text/StringBuilder.h>
34
35 namespace WebCore {
36
37 using namespace HTMLNames;
38
39 Attr::Attr(Element* element, const QualifiedName& name)
40     : ContainerNode(element->document())
41     , m_element(element)
42     , m_name(name)
43     , m_ignoreChildrenChanged(0)
44     , m_specified(true)
45 {
46 }
47
48 Attr::Attr(Document* document, const QualifiedName& name, const AtomicString& standaloneValue)
49     : ContainerNode(document)
50     , m_element(0)
51     , m_name(name)
52     , m_standaloneValue(standaloneValue)
53     , m_ignoreChildrenChanged(0)
54     , m_specified(true)
55 {
56 }
57
58 PassRefPtr<Attr> Attr::create(Element* element, const QualifiedName& name)
59 {
60     RefPtr<Attr> attr = adoptRef(new Attr(element, name));
61     attr->createTextChild();
62     return attr.release();
63 }
64
65 PassRefPtr<Attr> Attr::create(Document* document, const QualifiedName& name, const AtomicString& value)
66 {
67     RefPtr<Attr> attr = adoptRef(new Attr(document, name, value));
68     attr->createTextChild();
69     return attr.release();
70 }
71
72 Attr::~Attr()
73 {
74 }
75
76 void Attr::createTextChild()
77 {
78     ASSERT(refCount());
79     if (!value().isEmpty()) {
80         RefPtr<Text> textNode = document()->createTextNode(value().string());
81
82         // This does everything appendChild() would do in this situation (assuming m_ignoreChildrenChanged was set),
83         // but much more efficiently.
84         textNode->setParentOrHostNode(this);
85         setFirstChild(textNode.get());
86         setLastChild(textNode.get());
87     }
88 }
89
90 void Attr::setPrefix(const AtomicString& prefix, ExceptionCode& ec)
91 {
92     ec = 0;
93     checkSetPrefix(prefix, ec);
94     if (ec)
95         return;
96
97     if ((prefix == xmlnsAtom && namespaceURI() != XMLNSNames::xmlnsNamespaceURI)
98         || static_cast<Attr*>(this)->qualifiedName() == xmlnsAtom) {
99         ec = NAMESPACE_ERR;
100         return;
101     }
102
103     const AtomicString& newPrefix = prefix.isEmpty() ? nullAtom : prefix;
104
105     if (m_element)
106         elementAttribute().setPrefix(newPrefix);
107     m_name.setPrefix(newPrefix);
108 }
109
110 void Attr::setValue(const AtomicString& value)
111 {
112     EventQueueScope scope;
113     m_ignoreChildrenChanged++;
114     removeChildren();
115     if (m_element)
116         elementAttribute().setValue(value);
117     else
118         m_standaloneValue = value;
119     createTextChild();
120     m_ignoreChildrenChanged--;
121
122     invalidateNodeListCachesInAncestors(&m_name, m_element);
123 }
124
125 void Attr::setValue(const AtomicString& value, ExceptionCode&)
126 {
127     if (m_element)
128         m_element->willModifyAttribute(qualifiedName(), this->value(), value);
129
130     setValue(value);
131
132     if (m_element)
133         m_element->didModifyAttribute(elementAttribute());
134 }
135
136 void Attr::setNodeValue(const String& v, ExceptionCode& ec)
137 {
138     setValue(v, ec);
139 }
140
141 PassRefPtr<Node> Attr::cloneNode(bool /*deep*/)
142 {
143     RefPtr<Attr> clone = adoptRef(new Attr(document(), qualifiedName(), value()));
144     cloneChildNodes(clone.get());
145     return clone.release();
146 }
147
148 // DOM Section 1.1.1
149 bool Attr::childTypeAllowed(NodeType type) const
150 {
151     switch (type) {
152         case TEXT_NODE:
153         case ENTITY_REFERENCE_NODE:
154             return true;
155         default:
156             return false;
157     }
158 }
159
160 void Attr::childrenChanged(bool, Node*, Node*, int)
161 {
162     if (m_ignoreChildrenChanged > 0)
163         return;
164
165     invalidateNodeListCachesInAncestors(&qualifiedName(), m_element);
166
167     // FIXME: We should include entity references in the value
168
169     StringBuilder valueBuilder;
170     for (Node *n = firstChild(); n; n = n->nextSibling()) {
171         if (n->isTextNode())
172             valueBuilder.append(toText(n)->data());
173     }
174
175     AtomicString newValue = valueBuilder.toString();
176     if (m_element)
177         m_element->willModifyAttribute(qualifiedName(), value(), newValue);
178
179     if (m_element)
180         elementAttribute().setValue(newValue);
181     else
182         m_standaloneValue = newValue;
183
184     if (m_element)
185         m_element->attributeChanged(elementAttribute());
186 }
187
188 bool Attr::isId() const
189 {
190     return qualifiedName().matches(document()->idAttributeName());
191 }
192
193 CSSStyleDeclaration* Attr::style()
194 {
195     // This function only exists to support the Obj-C bindings.
196     if (!m_element || !m_element->isStyledElement())
197         return 0;
198     m_style = StylePropertySet::create();
199     static_cast<StyledElement*>(m_element)->collectStyleForAttribute(elementAttribute(), m_style.get());
200     return m_style->ensureCSSStyleDeclaration();
201 }
202
203 const AtomicString& Attr::value() const
204 {
205     if (m_element)
206         return m_element->getAttribute(qualifiedName());
207     return m_standaloneValue;
208 }
209
210 Attribute& Attr::elementAttribute()
211 {
212     ASSERT(m_element);
213     ASSERT(m_element->attributeData());
214     return *m_element->getAttributeItem(qualifiedName());
215 }
216
217 void Attr::detachFromElementWithValue(const AtomicString& value)
218 {
219     ASSERT(m_element);
220     ASSERT(m_standaloneValue.isNull());
221     m_element->attributeData()->removeAttr(m_element, qualifiedName());
222     m_standaloneValue = value;
223     m_element = 0;
224 }
225
226 void Attr::attachToElement(Element* element)
227 {
228     ASSERT(!m_element);
229     m_element = element;
230     m_standaloneValue = nullAtom;
231 }
232
233 }