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 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 "Element.h"
27 #include "ExceptionCode.h"
28 #include "HTMLNames.h"
29 #include "ScopedEventQueue.h"
30 #include "Text.h"
31 #include "XMLNSNames.h"
32
33 namespace WebCore {
34
35 using namespace HTMLNames;
36
Attr(Element * element,Document * document,PassRefPtr<Attribute> attribute)37 inline Attr::Attr(Element* element, Document* document, PassRefPtr<Attribute> attribute)
38 : ContainerNode(document)
39 , m_element(element)
40 , m_attribute(attribute)
41 , m_ignoreChildrenChanged(0)
42 , m_specified(true)
43 {
44 ASSERT(!m_attribute->attr());
45 m_attribute->bindAttr(this);
46 }
47
create(Element * element,Document * document,PassRefPtr<Attribute> attribute)48 PassRefPtr<Attr> Attr::create(Element* element, Document* document, PassRefPtr<Attribute> attribute)
49 {
50 RefPtr<Attr> attr = adoptRef(new Attr(element, document, attribute));
51 attr->createTextChild();
52 return attr.release();
53 }
54
~Attr()55 Attr::~Attr()
56 {
57 ASSERT(m_attribute->attr() == this);
58 m_attribute->unbindAttr(this);
59 }
60
createTextChild()61 void Attr::createTextChild()
62 {
63 ASSERT(refCount());
64 if (!m_attribute->value().isEmpty()) {
65 RefPtr<Text> textNode = document()->createTextNode(m_attribute->value().string());
66
67 // This does everything appendChild() would do in this situation (assuming m_ignoreChildrenChanged was set),
68 // but much more efficiently.
69 textNode->setParent(this);
70 setFirstChild(textNode.get());
71 setLastChild(textNode.get());
72 }
73 }
74
nodeName() const75 String Attr::nodeName() const
76 {
77 return name();
78 }
79
nodeType() const80 Node::NodeType Attr::nodeType() const
81 {
82 return ATTRIBUTE_NODE;
83 }
84
localName() const85 const AtomicString& Attr::localName() const
86 {
87 return m_attribute->localName();
88 }
89
namespaceURI() const90 const AtomicString& Attr::namespaceURI() const
91 {
92 return m_attribute->namespaceURI();
93 }
94
prefix() const95 const AtomicString& Attr::prefix() const
96 {
97 return m_attribute->prefix();
98 }
99
setPrefix(const AtomicString & prefix,ExceptionCode & ec)100 void Attr::setPrefix(const AtomicString& prefix, ExceptionCode& ec)
101 {
102 ec = 0;
103 checkSetPrefix(prefix, ec);
104 if (ec)
105 return;
106
107 if ((prefix == xmlnsAtom && namespaceURI() != XMLNSNames::xmlnsNamespaceURI)
108 || static_cast<Attr*>(this)->qualifiedName() == xmlnsAtom) {
109 ec = NAMESPACE_ERR;
110 return;
111 }
112
113 m_attribute->setPrefix(prefix.isEmpty() ? AtomicString() : prefix);
114 }
115
nodeValue() const116 String Attr::nodeValue() const
117 {
118 return value();
119 }
120
setValue(const AtomicString & value)121 void Attr::setValue(const AtomicString& value)
122 {
123 EventQueueScope scope;
124 m_ignoreChildrenChanged++;
125 removeChildren();
126 m_attribute->setValue(value);
127 createTextChild();
128 m_ignoreChildrenChanged--;
129 }
130
setValue(const AtomicString & value,ExceptionCode &)131 void Attr::setValue(const AtomicString& value, ExceptionCode&)
132 {
133 if (m_element && m_element->isIdAttributeName(m_attribute->name()))
134 m_element->updateId(m_element->getIdAttribute(), value);
135
136 setValue(value);
137
138 if (m_element)
139 m_element->attributeChanged(m_attribute.get());
140 }
141
setNodeValue(const String & v,ExceptionCode & ec)142 void Attr::setNodeValue(const String& v, ExceptionCode& ec)
143 {
144 setValue(v, ec);
145 }
146
cloneNode(bool)147 PassRefPtr<Node> Attr::cloneNode(bool /*deep*/)
148 {
149 RefPtr<Attr> clone = adoptRef(new Attr(0, document(), m_attribute->clone()));
150 cloneChildNodes(clone.get());
151 return clone.release();
152 }
153
154 // DOM Section 1.1.1
childTypeAllowed(NodeType type) const155 bool Attr::childTypeAllowed(NodeType type) const
156 {
157 switch (type) {
158 case TEXT_NODE:
159 case ENTITY_REFERENCE_NODE:
160 return true;
161 default:
162 return false;
163 }
164 }
165
childrenChanged(bool changedByParser,Node * beforeChange,Node * afterChange,int childCountDelta)166 void Attr::childrenChanged(bool changedByParser, Node* beforeChange, Node* afterChange, int childCountDelta)
167 {
168 if (m_ignoreChildrenChanged > 0)
169 return;
170
171 Node::childrenChanged(changedByParser, beforeChange, afterChange, childCountDelta);
172
173 // FIXME: We should include entity references in the value
174
175 String val = "";
176 for (Node *n = firstChild(); n; n = n->nextSibling()) {
177 if (n->isTextNode())
178 val += static_cast<Text *>(n)->data();
179 }
180
181 if (m_element && m_element->isIdAttributeName(m_attribute->name()))
182 m_element->updateId(m_attribute->value(), val);
183
184 m_attribute->setValue(val.impl());
185 if (m_element)
186 m_element->attributeChanged(m_attribute.get());
187 }
188
isId() const189 bool Attr::isId() const
190 {
191 return qualifiedName().matches(document()->idAttributeName());
192 }
193
194 }
195