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 "bindings/v8/ExceptionState.h" 27 #include "bindings/v8/ExceptionStatePlaceholder.h" 28 #include "core/dom/Document.h" 29 #include "core/dom/Element.h" 30 #include "core/dom/Text.h" 31 #include "core/events/ScopedEventQueue.h" 32 #include "core/frame/UseCounter.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 { 46 ScriptWrappable::init(this); 47 } 48 49 Attr::Attr(Document& document, const QualifiedName& name, const AtomicString& standaloneValue) 50 : ContainerNode(&document) 51 , m_element(nullptr) 52 , m_name(name) 53 , m_standaloneValueOrAttachedLocalName(standaloneValue) 54 , m_ignoreChildrenChanged(0) 55 { 56 ScriptWrappable::init(this); 57 } 58 59 PassRefPtrWillBeRawPtr<Attr> Attr::create(Element& element, const QualifiedName& name) 60 { 61 RefPtrWillBeRawPtr<Attr> attr = adoptRefWillBeNoop(new Attr(element, name)); 62 attr->createTextChild(); 63 return attr.release(); 64 } 65 66 PassRefPtrWillBeRawPtr<Attr> Attr::create(Document& document, const QualifiedName& name, const AtomicString& value) 67 { 68 RefPtrWillBeRawPtr<Attr> attr = adoptRefWillBeNoop(new Attr(document, name, value)); 69 attr->createTextChild(); 70 return attr.release(); 71 } 72 73 Attr::~Attr() 74 { 75 } 76 77 const QualifiedName Attr::qualifiedName() const 78 { 79 if (m_element && !m_standaloneValueOrAttachedLocalName.isNull()) { 80 // In the unlikely case the Element attribute has a local name 81 // that differs by case, construct the qualified name based on 82 // it. This is the qualified name that must be used when 83 // looking up the attribute on the element. 84 return QualifiedName(m_name.prefix(), m_standaloneValueOrAttachedLocalName, m_name.namespaceURI()); 85 } 86 87 return m_name; 88 } 89 90 void Attr::createTextChild() 91 { 92 #if !ENABLE(OILPAN) 93 ASSERT(refCount()); 94 #endif 95 if (!value().isEmpty()) { 96 RefPtrWillBeRawPtr<Text> textNode = document().createTextNode(value().string()); 97 98 // This does everything appendChild() would do in this situation (assuming m_ignoreChildrenChanged was set), 99 // but much more efficiently. 100 textNode->setParentOrShadowHostNode(this); 101 treeScope().adoptIfNeeded(*textNode); 102 setFirstChild(textNode.get()); 103 setLastChild(textNode.get()); 104 } 105 } 106 107 void Attr::setValue(const AtomicString& value) 108 { 109 EventQueueScope scope; 110 m_ignoreChildrenChanged++; 111 removeChildren(); 112 if (m_element) 113 elementAttribute().setValue(value); 114 else 115 m_standaloneValueOrAttachedLocalName = value; 116 createTextChild(); 117 m_ignoreChildrenChanged--; 118 119 QualifiedName name = qualifiedName(); 120 invalidateNodeListCachesInAncestors(&name, m_element); 121 } 122 123 void Attr::setValueInternal(const AtomicString& value) 124 { 125 if (m_element) 126 m_element->willModifyAttribute(qualifiedName(), this->value(), value); 127 128 setValue(value); 129 130 if (m_element) 131 m_element->didModifyAttribute(qualifiedName(), value); 132 } 133 134 const AtomicString& Attr::valueForBindings() const 135 { 136 UseCounter::count(document(), UseCounter::AttrGetValue); 137 return value(); 138 } 139 140 void Attr::setValueForBindings(const AtomicString& value) 141 { 142 UseCounter::count(document(), UseCounter::AttrSetValue); 143 if (m_element) 144 UseCounter::count(document(), UseCounter::AttrSetValueWithElement); 145 setValueInternal(value); 146 } 147 148 void Attr::setNodeValue(const String& v) 149 { 150 // Attr uses AtomicString type for its value to save memory as there 151 // is duplication among Elements' attributes values. 152 setValueInternal(AtomicString(v)); 153 } 154 155 PassRefPtrWillBeRawPtr<Node> Attr::cloneNode(bool /*deep*/) 156 { 157 RefPtrWillBeRawPtr<Attr> clone = adoptRefWillBeNoop(new Attr(document(), m_name, value())); 158 cloneChildNodes(clone.get()); 159 return clone.release(); 160 } 161 162 // DOM Section 1.1.1 163 bool Attr::childTypeAllowed(NodeType type) const 164 { 165 return TEXT_NODE == type; 166 } 167 168 void Attr::childrenChanged(bool, Node*, Node*, int) 169 { 170 if (m_ignoreChildrenChanged > 0) 171 return; 172 173 QualifiedName name = qualifiedName(); 174 invalidateNodeListCachesInAncestors(&name, m_element); 175 176 StringBuilder valueBuilder; 177 for (Node *n = firstChild(); n; n = n->nextSibling()) { 178 if (n->isTextNode()) 179 valueBuilder.append(toText(n)->data()); 180 } 181 182 AtomicString newValue = valueBuilder.toAtomicString(); 183 if (m_element) 184 m_element->willModifyAttribute(qualifiedName(), value(), newValue); 185 186 if (m_element) 187 elementAttribute().setValue(newValue); 188 else 189 m_standaloneValueOrAttachedLocalName = newValue; 190 191 if (m_element) 192 m_element->attributeChanged(qualifiedName(), newValue); 193 } 194 195 const AtomicString& Attr::value() const 196 { 197 if (m_element) 198 return m_element->getAttribute(qualifiedName()); 199 return m_standaloneValueOrAttachedLocalName; 200 } 201 202 Attribute& Attr::elementAttribute() 203 { 204 ASSERT(m_element); 205 ASSERT(m_element->elementData()); 206 return *m_element->ensureUniqueElementData().findAttributeByName(qualifiedName()); 207 } 208 209 void Attr::detachFromElementWithValue(const AtomicString& value) 210 { 211 ASSERT(m_element); 212 m_standaloneValueOrAttachedLocalName = value; 213 m_element = nullptr; 214 } 215 216 void Attr::attachToElement(Element* element, const AtomicString& attachedLocalName) 217 { 218 ASSERT(!m_element); 219 m_element = element; 220 m_standaloneValueOrAttachedLocalName = attachedLocalName; 221 } 222 223 void Attr::trace(Visitor* visitor) 224 { 225 visitor->trace(m_element); 226 ContainerNode::trace(visitor); 227 } 228 229 } 230