diff -r 000000000000 -r dd21522fd290 webengine/osswebengine/WebCore/dom/Element.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/webengine/osswebengine/WebCore/dom/Element.cpp Mon Mar 30 12:54:55 2009 +0300 @@ -0,0 +1,1102 @@ +/* + * 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) + * Copyright (C) 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. + */ + +#include "config.h" +#include "Element.h" + +#include "CSSStyleSelector.h" +#include "Document.h" +#include "Editor.h" +#include "ExceptionCode.h" +#include "FocusController.h" +#include "Frame.h" +#include "FrameView.h" +#include "HTMLElement.h" +#include "HTMLNames.h" +#include "KURL.h" +#include "NamedAttrMap.h" +#include "Page.h" +#include "RenderBlock.h" +#include "SelectionController.h" +#include "TextIterator.h" +#include "TextStream.h" +#include "XMLNames.h" + +namespace WebCore { + +using namespace HTMLNames; +using namespace XMLNames; + +class ElementRareData { +public: + ElementRareData(Element*); + void resetComputedStyle(Element*); + + IntSize m_minimumSizeForResizing; + RenderStyle* m_computedStyle; + bool m_needsFocusAppearanceUpdateSoonAfterAttach; +}; + +typedef HashMap ElementRareDataMap; + +static ElementRareDataMap& rareDataMap() +{ + static ElementRareDataMap* dataMap = new ElementRareDataMap; + return *dataMap; +} + +static ElementRareData* rareDataFromMap(const Element* element) +{ + return rareDataMap().get(element); +} + +static inline IntSize defaultMinimumSizeForResizing() +{ + return IntSize(INT_MAX, INT_MAX); +} + +inline ElementRareData::ElementRareData(Element* element) + : m_minimumSizeForResizing(defaultMinimumSizeForResizing()) + , m_computedStyle(0) + , m_needsFocusAppearanceUpdateSoonAfterAttach(false) +{ +} + +void ElementRareData::resetComputedStyle(Element* element) +{ + if (!m_computedStyle) + return; + m_computedStyle->deref(element->document()->renderArena()); + m_computedStyle = 0; +} + +Element::Element(const QualifiedName& qName, Document *doc) + : ContainerNode(doc) + , m_tagName(qName) +{ +} + +Element::~Element() +{ + if (namedAttrMap) + namedAttrMap->detachFromElement(); + + if (!m_attrWasSpecifiedOrElementHasRareData) + ASSERT(!rareDataMap().contains(this)); + else { + ElementRareDataMap& dataMap = rareDataMap(); + ElementRareDataMap::iterator it = dataMap.find(this); + ASSERT(it != dataMap.end()); + delete it->second; + dataMap.remove(it); + } +} + +inline ElementRareData* Element::rareData() +{ + return m_attrWasSpecifiedOrElementHasRareData ? rareDataFromMap(this) : 0; +} + +inline const ElementRareData* Element::rareData() const +{ + return m_attrWasSpecifiedOrElementHasRareData ? rareDataFromMap(this) : 0; +} + +ElementRareData* Element::createRareData() +{ + if (m_attrWasSpecifiedOrElementHasRareData) + return rareDataMap().get(this); + ASSERT(!rareDataMap().contains(this)); + ElementRareData* data = new ElementRareData(this); + rareDataMap().set(this, data); + m_attrWasSpecifiedOrElementHasRareData = true; + return data; +} + +PassRefPtr Element::cloneNode(bool deep) +{ + ExceptionCode ec = 0; + RefPtr clone = document()->createElementNS(namespaceURI(), nodeName(), ec); + ASSERT(!ec); + + // clone attributes + if (namedAttrMap) + *clone->attributes() = *namedAttrMap; + + clone->copyNonAttributeProperties(this); + + if (deep) + cloneChildNodes(clone.get()); + + return clone.release(); +} + +void Element::removeAttribute(const QualifiedName& name, ExceptionCode& ec) +{ + if (namedAttrMap) { + namedAttrMap->removeNamedItem(name, ec); + if (ec == NOT_FOUND_ERR) + ec = 0; + } +} + +void Element::setAttribute(const QualifiedName& name, const String &value) +{ + ExceptionCode ec = 0; + setAttribute(name, value.impl(), ec); +} + +// Virtual function, defined in base class. +NamedAttrMap *Element::attributes() const +{ + return attributes(false); +} + +NamedAttrMap* Element::attributes(bool readonly) const +{ + updateStyleAttributeIfNeeded(); + if (!readonly && !namedAttrMap) + createAttributeMap(); + return namedAttrMap.get(); +} + +Node::NodeType Element::nodeType() const +{ + return ELEMENT_NODE; +} + +const AtomicStringList* Element::getClassList() const +{ + return 0; +} + +const AtomicString& Element::getIDAttribute() const +{ + return namedAttrMap ? namedAttrMap->id() : nullAtom; +} + +bool Element::hasAttribute(const QualifiedName& name) const +{ + return hasAttributeNS(name.namespaceURI(), name.localName()); +} + +const AtomicString& Element::getAttribute(const QualifiedName& name) const +{ + if (name == styleAttr) + updateStyleAttributeIfNeeded(); + + if (namedAttrMap) + if (Attribute* a = namedAttrMap->getAttributeItem(name)) + return a->value(); + + return nullAtom; +} + +void Element::scrollIntoView(bool alignToTop) +{ + document()->updateLayoutIgnorePendingStylesheets(); + IntRect bounds = getRect(); + if (renderer()) { + // Align to the top / bottom and to the closest edge. + if (alignToTop) + renderer()->enclosingLayer()->scrollRectToVisible(bounds, RenderLayer::gAlignToEdgeIfNeeded, RenderLayer::gAlignTopAlways); + else + renderer()->enclosingLayer()->scrollRectToVisible(bounds, RenderLayer::gAlignToEdgeIfNeeded, RenderLayer::gAlignBottomAlways); + } +} + +void Element::scrollIntoViewIfNeeded(bool centerIfNeeded) +{ + document()->updateLayoutIgnorePendingStylesheets(); + IntRect bounds = getRect(); + if (renderer()) { + if (centerIfNeeded) + renderer()->enclosingLayer()->scrollRectToVisible(bounds, RenderLayer::gAlignCenterIfNeeded, RenderLayer::gAlignCenterIfNeeded); + else + renderer()->enclosingLayer()->scrollRectToVisible(bounds, RenderLayer::gAlignToEdgeIfNeeded, RenderLayer::gAlignToEdgeIfNeeded); + } +} + +void Element::scrollByUnits(int units, ScrollGranularity granularity) +{ + document()->updateLayoutIgnorePendingStylesheets(); + if (RenderObject *rend = renderer()) { + if (rend->hasOverflowClip()) { + ScrollDirection direction = ScrollDown; + if (units < 0) { + direction = ScrollUp; + units = -units; + } + rend->layer()->scroll(direction, granularity, units); + } + } +} + +void Element::scrollByLines(int lines) +{ + scrollByUnits(lines, ScrollByLine); +} + +void Element::scrollByPages(int pages) +{ + scrollByUnits(pages, ScrollByPage); +} + +int Element::offsetLeft() +{ + document()->updateLayoutIgnorePendingStylesheets(); + if (RenderObject* rend = renderer()) + return rend->offsetLeft(); + return 0; +} + +int Element::offsetTop() +{ + document()->updateLayoutIgnorePendingStylesheets(); + if (RenderObject* rend = renderer()) + return rend->offsetTop(); + return 0; +} + +int Element::offsetWidth() +{ + document()->updateLayoutIgnorePendingStylesheets(); + if (RenderObject* rend = renderer()) + return rend->offsetWidth(); + return 0; +} + +int Element::offsetHeight() +{ + document()->updateLayoutIgnorePendingStylesheets(); + if (RenderObject* rend = renderer()) + return rend->offsetHeight(); + return 0; +} + +Element* Element::offsetParent() +{ + document()->updateLayoutIgnorePendingStylesheets(); + if (RenderObject* rend = renderer()) + if (RenderObject* offsetParent = rend->offsetParent()) + return static_cast(offsetParent->element()); + return 0; +} + +int Element::clientLeft() +{ + document()->updateLayoutIgnorePendingStylesheets(); + + if (RenderObject* rend = renderer()) + return rend->clientLeft(); + return 0; +} + +int Element::clientTop() +{ + document()->updateLayoutIgnorePendingStylesheets(); + + if (RenderObject* rend = renderer()) + return rend->clientTop(); + return 0; +} + +int Element::clientWidth() +{ + document()->updateLayoutIgnorePendingStylesheets(); + + // When in strict mode, clientWidth for the document element should return the width of the containing frame. + // When in quirks mode, clientWidth for the body element should return the width of the containing frame. + bool inCompatMode = document()->inCompatMode(); + if ((!inCompatMode && document()->documentElement() == this) || + (inCompatMode && isHTMLElement() && document()->body() == this)) { + if (FrameView* view = document()->view()) + return view->visibleWidth(); + } + + + if (RenderObject* rend = renderer()) + return rend->clientWidth(); + return 0; +} + +int Element::clientHeight() +{ + document()->updateLayoutIgnorePendingStylesheets(); + + // When in strict mode, clientHeight for the document element should return the height of the containing frame. + // When in quirks mode, clientHeight for the body element should return the height of the containing frame. + bool inCompatMode = document()->inCompatMode(); + + if ((!inCompatMode && document()->documentElement() == this) || + (inCompatMode && isHTMLElement() && document()->body() == this)) { + if (FrameView* view = document()->view()) + return view->visibleHeight(); + } + + if (RenderObject* rend = renderer()) + return rend->clientHeight(); + return 0; +} + +int Element::scrollLeft() +{ + document()->updateLayoutIgnorePendingStylesheets(); + if (RenderObject* rend = renderer()) + return rend->scrollLeft(); + return 0; +} + +int Element::scrollTop() +{ + document()->updateLayoutIgnorePendingStylesheets(); + if (RenderObject* rend = renderer()) + return rend->scrollTop(); + return 0; +} + +void Element::setScrollLeft(int newLeft) +{ + document()->updateLayoutIgnorePendingStylesheets(); + if (RenderObject *rend = renderer()) + rend->setScrollLeft(newLeft); +} + +void Element::setScrollTop(int newTop) +{ + document()->updateLayoutIgnorePendingStylesheets(); + if (RenderObject *rend = renderer()) + rend->setScrollTop(newTop); +} + +int Element::scrollWidth() +{ + document()->updateLayoutIgnorePendingStylesheets(); + if (RenderObject* rend = renderer()) + return rend->scrollWidth(); + return 0; +} + +int Element::scrollHeight() +{ + document()->updateLayoutIgnorePendingStylesheets(); + if (RenderObject* rend = renderer()) + return rend->scrollHeight(); + return 0; +} + +static inline bool inHTMLDocument(const Element* e) +{ + return e && e->document()->isHTMLDocument(); +} + +const AtomicString& Element::getAttribute(const String& name) const +{ + String localName = inHTMLDocument(this) ? name.lower() : name; + if (localName == styleAttr.localName()) + updateStyleAttributeIfNeeded(); + + if (namedAttrMap) + if (Attribute* a = namedAttrMap->getAttributeItem(localName)) + return a->value(); + + return nullAtom; +} + +const AtomicString& Element::getAttributeNS(const String& namespaceURI, const String& localName) const +{ + return getAttribute(QualifiedName(nullAtom, localName, namespaceURI)); +} + +void Element::setAttribute(const String& name, const String& value, ExceptionCode& ec) +{ + if (!Document::isValidName(name)) { + ec = INVALID_CHARACTER_ERR; + return; + } + + String localName = inHTMLDocument(this) ? name.lower() : name; + + // allocate attributemap if necessary + Attribute* old = attributes(false)->getAttributeItem(localName); + + // NO_MODIFICATION_ALLOWED_ERR: Raised when the node is readonly + if (namedAttrMap->isReadOnlyNode()) { + ec = NO_MODIFICATION_ALLOWED_ERR; + return; + } + + if (inDocument()) + document()->incDOMTreeVersion(); + + if (localName == idAttr.localName()) + updateId(old ? old->value() : nullAtom, value); + + if (old && value.isNull()) + namedAttrMap->removeAttribute(old->name()); + else if (!old && !value.isNull()) + namedAttrMap->addAttribute(createAttribute(QualifiedName(nullAtom, localName, nullAtom), value.impl())); + else if (old && !value.isNull()) { + old->setValue(value); + attributeChanged(old); + } +} + +void Element::setAttribute(const QualifiedName& name, StringImpl* value, ExceptionCode& ec) +{ + if (inDocument()) + document()->incDOMTreeVersion(); + + // allocate attributemap if necessary + Attribute* old = attributes(false)->getAttributeItem(name); + + // NO_MODIFICATION_ALLOWED_ERR: Raised when the node is readonly + if (namedAttrMap->isReadOnlyNode()) { + ec = NO_MODIFICATION_ALLOWED_ERR; + return; + } + + if (name == idAttr) + updateId(old ? old->value() : nullAtom, value); + + if (old && !value) + namedAttrMap->removeAttribute(name); + else if (!old && value) + namedAttrMap->addAttribute(createAttribute(name, value)); + else if (old && value) { + old->setValue(value); + attributeChanged(old); + } +} + +Attribute* Element::createAttribute(const QualifiedName& name, StringImpl* value) +{ + return new Attribute(name, value); +} + +void Element::setAttributeMap(NamedAttrMap* list) +{ + if (inDocument()) + document()->incDOMTreeVersion(); + + // If setting the whole map changes the id attribute, we need to call updateId. + + Attribute *oldId = namedAttrMap ? namedAttrMap->getAttributeItem(idAttr) : 0; + Attribute *newId = list ? list->getAttributeItem(idAttr) : 0; + + if (oldId || newId) + updateId(oldId ? oldId->value() : nullAtom, newId ? newId->value() : nullAtom); + + if (namedAttrMap) + namedAttrMap->element = 0; + + namedAttrMap = list; + + if (namedAttrMap) { + namedAttrMap->element = this; + unsigned len = namedAttrMap->length(); + for (unsigned i = 0; i < len; i++) + attributeChanged(namedAttrMap->attrs[i]); + // FIXME: What about attributes that were in the old map that are not in the new map? + } +} + +bool Element::hasAttributes() const +{ + updateStyleAttributeIfNeeded(); + return namedAttrMap && namedAttrMap->length() > 0; +} + +String Element::nodeName() const +{ + return m_tagName.toString(); +} + +String Element::nodeNamePreservingCase() const +{ + return m_tagName.toString(); +} + +void Element::setPrefix(const AtomicString &_prefix, ExceptionCode& ec) +{ + checkSetPrefix(_prefix, ec); + if (ec) + return; + + m_tagName.setPrefix(_prefix); +} + +String Element::baseURI() const +{ + KURL xmlbase(getAttribute(baseAttr).deprecatedString()); + + if (!xmlbase.protocol().isEmpty()) + return xmlbase.url(); + + Node* parent = parentNode(); + if (parent) + return KURL(parent->baseURI().deprecatedString(), xmlbase.url()).url(); + + return xmlbase.url(); +} + +Node* Element::insertAdjacentElement(const String& where, Node* newChild, int& exception) +{ + if (!newChild) { + // IE throws COM Exception E_INVALIDARG; this is the best DOM exception alternative + exception = TYPE_MISMATCH_ERR; + return 0; + } + + // In Internet Explorer if the element has no parent and where is "beforeBegin" or "afterEnd", + // a document fragment is created and the elements appended in the correct order. This document + // fragment isn't returned anywhere. + // + // This is impossible for us to implement as the DOM tree does not allow for such structures, + // Opera also appears to disallow such usage. + + if (equalIgnoringCase(where, "beforeBegin")) { + if (Node* p = parent()) + return p->insertBefore(newChild, this, exception) ? newChild : 0; + } else if (equalIgnoringCase(where, "afterBegin")) { + return insertBefore(newChild, firstChild(), exception) ? newChild : 0; + } else if (equalIgnoringCase(where, "beforeEnd")) { + return appendChild(newChild, exception) ? newChild : 0; + } else if (equalIgnoringCase(where, "afterEnd")) { + if (Node* p = parent()) + return p->insertBefore(newChild, nextSibling(), exception) ? newChild : 0; + } else { + // IE throws COM Exception E_INVALIDARG; this is the best DOM exception alternative + exception = NOT_SUPPORTED_ERR; + } + return 0; +} + +bool Element::contains(const Node* node) const +{ + if (!node) + return false; + return this == node || node->isDescendantOf(this); +} + +void Element::createAttributeMap() const +{ + namedAttrMap = new NamedAttrMap(const_cast(this)); +} + +bool Element::isURLAttribute(Attribute *attr) const +{ + return false; +} + +RenderStyle *Element::styleForRenderer(RenderObject *parentRenderer) +{ + return document()->styleSelector()->styleForElement(this); +} + +RenderObject *Element::createRenderer(RenderArena *arena, RenderStyle *style) +{ + if (document()->documentElement() == this && style->display() == NONE) { + // Ignore display: none on root elements. Force a display of block in that case. + RenderBlock* result = new (arena) RenderBlock(this); + if (result) result->setStyle(style); + return result; + } + return RenderObject::createObject(this, style); +} + + +void Element::insertedIntoDocument() +{ + // need to do superclass processing first so inDocument() is true + // by the time we reach updateId + ContainerNode::insertedIntoDocument(); + + if (hasID()) { + NamedAttrMap* attrs = attributes(true); + if (attrs) { + Attribute* idItem = attrs->getAttributeItem(idAttr); + if (idItem && !idItem->isNull()) + updateId(nullAtom, idItem->value()); + } + } +} + +void Element::removedFromDocument() +{ + if (hasID()) { + NamedAttrMap* attrs = attributes(true); + if (attrs) { + Attribute* idItem = attrs->getAttributeItem(idAttr); + if (idItem && !idItem->isNull()) + updateId(idItem->value(), nullAtom); + } + } + + ContainerNode::removedFromDocument(); +} + +void Element::attach() +{ + createRendererIfNeeded(); + ContainerNode::attach(); + if (ElementRareData* rd = rareData()) { + if (rd->m_needsFocusAppearanceUpdateSoonAfterAttach) { + if (isFocusable() && document()->focusedNode() == this) + document()->updateFocusAppearanceSoon(); + rd->m_needsFocusAppearanceUpdateSoonAfterAttach = false; + } + } +} + +void Element::detach() +{ + cancelFocusAppearanceUpdate(); + if (ElementRareData* rd = rareData()) + rd->resetComputedStyle(this); + ContainerNode::detach(); +} + +void Element::recalcStyle(StyleChange change) +{ + // ### should go away and be done in renderobject + RenderStyle* _style = renderStyle(); + bool hasParentStyle = parentNode() ? parentNode()->renderStyle() : false; + +#if ENABLE(SVG) + if (!hasParentStyle && isShadowNode() && isSVGElement()) + hasParentStyle = true; +#endif + + if ((change > NoChange || changed())) { + if (ElementRareData* rd = rareData()) + rd->resetComputedStyle(this); + } + if (hasParentStyle && (change >= Inherit || changed())) { + RenderStyle *newStyle = document()->styleSelector()->styleForElement(this); + StyleChange ch = diff(_style, newStyle); + if (ch == Detach) { + if (attached()) + detach(); + // ### Suboptimal. Style gets calculated again. + attach(); + // attach recalulates the style for all children. No need to do it twice. + setChanged(NoStyleChange); + setHasChangedChild(false); + newStyle->deref(document()->renderArena()); + return; + } + else if (ch != NoChange) { + if (newStyle) + setRenderStyle(newStyle); + } + else if (changed() && newStyle && (document()->usesSiblingRules() || document()->usesDescendantRules())) { + // Although no change occurred, we use the new style so that the cousin style sharing code won't get + // fooled into believing this style is the same. This is only necessary if the document actually uses + // sibling/descendant rules, since otherwise it isn't possible for ancestor styles to affect sharing of + // descendants. + if (renderer()) + renderer()->setStyleInternal(newStyle); + else + setRenderStyle(newStyle); + } + + newStyle->deref(document()->renderArena()); + + if (change != Force) { + if (document()->usesDescendantRules() && styleChangeType() == FullStyleChange) + change = Force; + else + change = ch; + } + } + + for (Node *n = firstChild(); n; n = n->nextSibling()) { + if (change >= Inherit || n->isTextNode() || n->hasChangedChild() || n->changed()) + n->recalcStyle(change); + } + + setChanged(NoStyleChange); + setHasChangedChild(false); +} + +bool Element::childTypeAllowed(NodeType type) +{ + switch (type) { + case ELEMENT_NODE: + case TEXT_NODE: + case COMMENT_NODE: + case PROCESSING_INSTRUCTION_NODE: + case CDATA_SECTION_NODE: + case ENTITY_REFERENCE_NODE: + return true; + break; + default: + return false; + } +} + +void Element::dispatchAttrRemovalEvent(Attribute*) +{ + ASSERT(!eventDispatchForbidden()); +#if 0 + if (!document()->hasListenerType(Document::DOMATTRMODIFIED_LISTENER)) + return; + ExceptionCode ec = 0; + dispatchEvent(new MutationEvent(DOMAttrModifiedEvent, true, false, attr, attr->value(), + attr->value(), document()->attrName(attr->id()), MutationEvent::REMOVAL), ec); +#endif +} + +void Element::dispatchAttrAdditionEvent(Attribute *attr) +{ + ASSERT(!eventDispatchForbidden()); +#if 0 + if (!document()->hasListenerType(Document::DOMATTRMODIFIED_LISTENER)) + return; + ExceptionCode ec = 0; + dispatchEvent(new MutationEvent(DOMAttrModifiedEvent, true, false, attr, attr->value(), + attr->value(),document()->attrName(attr->id()), MutationEvent::ADDITION), ec); +#endif +} + +String Element::openTagStartToString() const +{ + String result = "<" + nodeName(); + + NamedAttrMap *attrMap = attributes(true); + + if (attrMap) { + unsigned numAttrs = attrMap->length(); + for (unsigned i = 0; i < numAttrs; i++) { + result += " "; + + Attribute *attribute = attrMap->attributeItem(i); + result += attribute->name().toString(); + if (!attribute->value().isNull()) { + result += "=\""; + // FIXME: substitute entities for any instances of " or ' + result += attribute->value(); + result += "\""; + } + } + } + + return result; +} + +String Element::toString() const +{ + String result = openTagStartToString(); + + if (hasChildNodes()) { + result += ">"; + + for (Node *child = firstChild(); child != NULL; child = child->nextSibling()) { + result += child->toString(); + } + + result += ""; + } else { + result += " />"; + } + + return result; +} + +void Element::updateId(const AtomicString& oldId, const AtomicString& newId) +{ + if (!inDocument()) + return; + + if (oldId == newId) + return; + + Document* doc = document(); + if (!oldId.isEmpty()) + doc->removeElementById(oldId, this); + if (!newId.isEmpty()) + doc->addElementById(newId, this); +} + +#ifndef NDEBUG +void Element::dump(TextStream *stream, DeprecatedString ind) const +{ + updateStyleAttributeIfNeeded(); + if (namedAttrMap) { + for (unsigned i = 0; i < namedAttrMap->length(); i++) { + Attribute *attr = namedAttrMap->attributeItem(i); + *stream << " " << attr->name().localName().deprecatedString().ascii() + << "=\"" << attr->value().deprecatedString().ascii() << "\""; + } + } + + ContainerNode::dump(stream,ind); +} +#endif + +#ifndef NDEBUG +void Element::formatForDebugger(char *buffer, unsigned length) const +{ + String result; + String s; + + s = nodeName(); + if (s.length() > 0) { + result += s; + } + + s = getAttribute(idAttr); + if (s.length() > 0) { + if (result.length() > 0) + result += "; "; + result += "id="; + result += s; + } + + s = getAttribute(classAttr); + if (s.length() > 0) { + if (result.length() > 0) + result += "; "; + result += "class="; + result += s; + } + + strncpy(buffer, result.deprecatedString().latin1(), length - 1); +} +#endif + +PassRefPtr Element::setAttributeNode(Attr *attr, ExceptionCode& ec) +{ + ASSERT(attr); + return static_pointer_cast(attributes(false)->setNamedItem(attr, ec)); +} + +PassRefPtr Element::setAttributeNodeNS(Attr* attr, ExceptionCode& ec) +{ + ASSERT(attr); + return static_pointer_cast(attributes(false)->setNamedItem(attr, ec)); +} + +PassRefPtr Element::removeAttributeNode(Attr *attr, ExceptionCode& ec) +{ + if (!attr || attr->ownerElement() != this) { + ec = NOT_FOUND_ERR; + return 0; + } + if (document() != attr->document()) { + ec = WRONG_DOCUMENT_ERR; + return 0; + } + + NamedAttrMap *attrs = attributes(true); + if (!attrs) + return 0; + + return static_pointer_cast(attrs->removeNamedItem(attr->qualifiedName(), ec)); +} + +void Element::setAttributeNS(const String& namespaceURI, const String& qualifiedName, const String& value, ExceptionCode& ec) +{ + String prefix, localName; + if (!Document::parseQualifiedName(qualifiedName, prefix, localName)) { + ec = INVALID_CHARACTER_ERR; + return; + } + setAttribute(QualifiedName(prefix, localName, namespaceURI), value.impl(), ec); +} + +void Element::removeAttribute(const String& name, ExceptionCode& ec) +{ + String localName = inHTMLDocument(this) ? name.lower() : name; + + if (namedAttrMap) { + namedAttrMap->removeNamedItem(localName, ec); + if (ec == NOT_FOUND_ERR) + ec = 0; + } +} + +void Element::removeAttributeNS(const String& namespaceURI, const String& localName, ExceptionCode& ec) +{ + removeAttribute(QualifiedName(nullAtom, localName, namespaceURI), ec); +} + +PassRefPtr Element::getAttributeNode(const String& name) +{ + NamedAttrMap* attrs = attributes(true); + if (!attrs) + return 0; + String localName = inHTMLDocument(this) ? name.lower() : name; + return static_pointer_cast(attrs->getNamedItem(localName)); +} + +PassRefPtr Element::getAttributeNodeNS(const String& namespaceURI, const String& localName) +{ + NamedAttrMap* attrs = attributes(true); + if (!attrs) + return 0; + return static_pointer_cast(attrs->getNamedItem(QualifiedName(nullAtom, localName, namespaceURI))); +} + +bool Element::hasAttribute(const String& name) const +{ + NamedAttrMap* attrs = attributes(true); + if (!attrs) + return false; + String localName = inHTMLDocument(this) ? name.lower() : name; + return attrs->getAttributeItem(localName); +} + +bool Element::hasAttributeNS(const String& namespaceURI, const String& localName) const +{ + NamedAttrMap* attrs = attributes(true); + if (!attrs) + return false; + return attrs->getAttributeItem(QualifiedName(nullAtom, localName, namespaceURI)); +} + +CSSStyleDeclaration *Element::style() +{ + return 0; +} + +void Element::focus(bool restorePreviousSelection) +{ + Document* doc = document(); + if (doc->focusedNode() == this) + return; + + doc->updateLayoutIgnorePendingStylesheets(); + + if (!supportsFocus()) + return; + + if (Page* page = doc->page()) + page->focusController()->setFocusedNode(this, doc->frame()); + + if (!isFocusable()) { + createRareData()->m_needsFocusAppearanceUpdateSoonAfterAttach = true; + return; + } + + cancelFocusAppearanceUpdate(); + updateFocusAppearance(restorePreviousSelection); +} + +void Element::updateFocusAppearance(bool restorePreviousSelection) +{ + if (this == rootEditableElement()) { + Frame* frame = document()->frame(); + if (!frame) + return; + + // FIXME: We should restore the previous selection if there is one. + Selection newSelection = hasTagName(htmlTag) || hasTagName(bodyTag) ? Selection(Position(this, 0), DOWNSTREAM) : Selection::selectionFromContentsOfNode(this); + + if (frame->shouldChangeSelection(newSelection)) { + frame->selectionController()->setSelection(newSelection); + frame->revealSelection(); + } + } else if (renderer() && !renderer()->isWidget()) + renderer()->enclosingLayer()->scrollRectToVisible(getRect()); +} + +void Element::blur() +{ + cancelFocusAppearanceUpdate(); + Document* doc = document(); + if (doc->focusedNode() == this) { + if (doc->frame()) + doc->frame()->page()->focusController()->setFocusedNode(0, doc->frame()); + else + doc->setFocusedNode(0); + } +} + +String Element::innerText() const +{ + // We need to update layout, since plainText uses line boxes in the render tree. + document()->updateLayoutIgnorePendingStylesheets(); + + if (!renderer()) + return textContent(true); + + return plainText(rangeOfContents(const_cast(this)).get()); +} + +String Element::outerText() const +{ + // Getting outerText is the same as getting innerText, only + // setting is different. You would think this should get the plain + // text for the outer range, but this is wrong,
for instance + // would return different values for inner and outer text by such + // a rule, but it doesn't in WinIE, and we want to match that. + return innerText(); +} + +String Element::title() const +{ + return String(); +} + +IntSize Element::minimumSizeForResizing() const +{ + const ElementRareData* rd = rareData(); + return rd ? rd->m_minimumSizeForResizing : defaultMinimumSizeForResizing(); +} + +void Element::setMinimumSizeForResizing(const IntSize& size) +{ + if (size == defaultMinimumSizeForResizing() && !rareData()) + return; + createRareData()->m_minimumSizeForResizing = size; +} + +RenderStyle* Element::computedStyle() +{ + if (RenderStyle* usedStyle = renderStyle()) + return usedStyle; + + if (!attached()) + // FIXME: Try to do better than this. Ensure that styleForElement() works for elements that are not in the + // document tree and figure out when to destroy the computed style for such elements. + return 0; + + ElementRareData* rd = createRareData(); + if (!rd->m_computedStyle) + rd->m_computedStyle = document()->styleSelector()->styleForElement(this, parent() ? parent()->computedStyle() : 0); + return rd->m_computedStyle; +} + +void Element::cancelFocusAppearanceUpdate() +{ + if (ElementRareData* rd = rareData()) + rd->m_needsFocusAppearanceUpdateSoonAfterAttach = false; + if (document()->focusedNode() == this) + document()->cancelFocusAppearanceUpdate(); +} + +}