2 * This file is part of the DOM implementation for KDE.
4 * Copyright (C) 1999 Lars Knoll (knoll@kde.org)
5 * (C) 1999 Antti Koivisto (koivisto@kde.org)
6 * (C) 2001 Peter Kelly (pmk@post.com)
7 * (C) 2001 Dirk Mueller (mueller@kde.org)
8 * Copyright (C) 2004, 2005, 2006 Apple Computer, Inc.
10 * This library is free software; you can redistribute it and/or
11 * modify it under the terms of the GNU Library General Public
12 * License as published by the Free Software Foundation; either
13 * version 2 of the License, or (at your option) any later version.
15 * This library is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18 * Library General Public License for more details.
20 * You should have received a copy of the GNU Library General Public License
21 * along with this library; see the file COPYING.LIB. If not, write to
22 * the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
23 * Boston, MA 02111-1307, USA.
28 #include "cssstyleselector.h"
30 #include "ExceptionCode.h"
32 #include "HTMLNames.h"
33 #include "NamedAttrMap.h"
34 #include "RenderBlock.h"
35 #include "SelectionController.h"
36 #include "TextStream.h"
40 using namespace HTMLNames;
42 Element::Element(const QualifiedName& qName, Document *doc)
44 , m_updateFocusAppearanceTimer(this, &Element::updateFocusAppearanceTimerFired)
45 , m_needsFocusAppearanceUpdate(false)
53 namedAttrMap->detachFromElement();
56 PassRefPtr<Node> Element::cloneNode(bool deep)
59 RefPtr<Element> clone = document()->createElementNS(namespaceURI(), nodeName(), ec);
64 *clone->attributes() = *namedAttrMap;
66 clone->copyNonAttributeProperties(this);
69 cloneChildNodes(clone.get());
71 return clone.release();
74 void Element::removeAttribute(const QualifiedName& name, ExceptionCode& ec)
77 namedAttrMap->removeNamedItem(name, ec);
78 if (ec == NOT_FOUND_ERR)
83 void Element::setAttribute(const QualifiedName& name, const String &value)
86 setAttribute(name, value.impl(), ec);
89 // Virtual function, defined in base class.
90 NamedAttrMap *Element::attributes() const
92 return attributes(false);
95 NamedAttrMap* Element::attributes(bool readonly) const
97 updateStyleAttributeIfNeeded();
98 if (!readonly && !namedAttrMap)
100 return namedAttrMap.get();
103 Node::NodeType Element::nodeType() const
108 const AtomicStringList* Element::getClassList() const
113 const AtomicString& Element::getIDAttribute() const
115 return namedAttrMap ? namedAttrMap->id() : nullAtom;
118 bool Element::hasAttribute(const QualifiedName& name) const
120 return hasAttributeNS(name.namespaceURI(), name.localName());
123 const AtomicString& Element::getAttribute(const QualifiedName& name) const
125 if (name == styleAttr)
126 updateStyleAttributeIfNeeded();
129 if (Attribute* a = namedAttrMap->getAttributeItem(name))
135 void Element::scrollIntoView(bool alignToTop)
137 document()->updateLayoutIgnorePendingStylesheets();
138 IntRect bounds = getRect();
140 // Align to the top / bottom and to the closest edge.
142 renderer()->enclosingLayer()->scrollRectToVisible(bounds, RenderLayer::gAlignToEdgeIfNeeded, RenderLayer::gAlignTopAlways);
144 renderer()->enclosingLayer()->scrollRectToVisible(bounds, RenderLayer::gAlignToEdgeIfNeeded, RenderLayer::gAlignBottomAlways);
148 void Element::scrollIntoViewIfNeeded(bool centerIfNeeded)
150 document()->updateLayoutIgnorePendingStylesheets();
151 IntRect bounds = getRect();
154 renderer()->enclosingLayer()->scrollRectToVisible(bounds, RenderLayer::gAlignCenterIfNeeded, RenderLayer::gAlignCenterIfNeeded);
156 renderer()->enclosingLayer()->scrollRectToVisible(bounds, RenderLayer::gAlignToEdgeIfNeeded, RenderLayer::gAlignToEdgeIfNeeded);
160 void Element::scrollByUnits(int units, ScrollGranularity granularity)
162 document()->updateLayoutIgnorePendingStylesheets();
163 if (RenderObject *rend = renderer()) {
164 if (rend->hasOverflowClip()) {
165 ScrollDirection direction = ScrollDown;
167 direction = ScrollUp;
170 rend->layer()->scroll(direction, granularity, units);
175 void Element::scrollByLines(int lines)
177 scrollByUnits(lines, ScrollByLine);
180 void Element::scrollByPages(int pages)
182 scrollByUnits(pages, ScrollByPage);
185 int Element::offsetLeft()
187 document()->updateLayoutIgnorePendingStylesheets();
188 if (RenderObject* rend = renderer())
189 return rend->offsetLeft();
193 int Element::offsetTop()
195 document()->updateLayoutIgnorePendingStylesheets();
196 if (RenderObject* rend = renderer())
197 return rend->offsetTop();
201 int Element::offsetWidth()
203 document()->updateLayoutIgnorePendingStylesheets();
204 if (RenderObject* rend = renderer())
205 return rend->offsetWidth();
209 int Element::offsetHeight()
211 document()->updateLayoutIgnorePendingStylesheets();
212 if (RenderObject* rend = renderer())
213 return rend->offsetHeight();
217 Element* Element::offsetParent()
219 document()->updateLayoutIgnorePendingStylesheets();
220 if (RenderObject* rend = renderer())
221 if (RenderObject* offsetParent = rend->offsetParent())
222 return static_cast<Element*>(offsetParent->element());
226 int Element::clientWidth()
228 document()->updateLayoutIgnorePendingStylesheets();
230 // When in strict mode, clientWidth for the document
231 // element should return the width of the containing frame.
232 if (!document()->inCompatMode() && document()->documentElement() == this)
233 return document()->frame()->view()->visibleWidth();
235 if (RenderObject* rend = renderer())
236 return rend->clientWidth();
240 int Element::clientHeight()
242 document()->updateLayoutIgnorePendingStylesheets();
244 // When in strict mode, clientHeight for the document
245 // element should return the height of the containing frame.
246 if (!document()->inCompatMode() && document()->documentElement() == this)
247 return document()->frame()->view()->visibleHeight();
249 if (RenderObject* rend = renderer())
250 return rend->clientHeight();
254 int Element::scrollLeft()
256 document()->updateLayoutIgnorePendingStylesheets();
257 RenderObject* rend = renderer();
258 if (rend && rend->layer())
259 return rend->layer()->scrollXOffset();
263 int Element::scrollTop()
265 document()->updateLayoutIgnorePendingStylesheets();
266 RenderObject* rend = renderer();
267 if (rend && rend->layer())
268 return rend->layer()->scrollYOffset();
272 void Element::setScrollLeft(int newLeft)
274 document()->updateLayoutIgnorePendingStylesheets();
275 RenderObject *rend = renderer();
276 if (rend && rend->hasOverflowClip())
277 rend->layer()->scrollToXOffset(newLeft);
280 void Element::setScrollTop(int newTop)
282 document()->updateLayoutIgnorePendingStylesheets();
283 RenderObject *rend = renderer();
284 if (rend && rend->hasOverflowClip())
285 rend->layer()->scrollToYOffset(newTop);
288 int Element::scrollWidth()
290 document()->updateLayoutIgnorePendingStylesheets();
291 if (RenderObject* rend = renderer())
292 return rend->scrollWidth();
296 int Element::scrollHeight()
298 document()->updateLayoutIgnorePendingStylesheets();
299 if (RenderObject* rend = renderer())
300 return rend->scrollHeight();
304 static inline bool inHTMLDocument(const Element* e)
306 return e && e->document()->isHTMLDocument();
309 const AtomicString& Element::getAttribute(const String& name) const
311 String localName = inHTMLDocument(this) ? name.lower() : name;
312 if (localName == styleAttr.localName())
313 updateStyleAttributeIfNeeded();
316 if (Attribute* a = namedAttrMap->getAttributeItem(localName))
322 const AtomicString& Element::getAttributeNS(const String& namespaceURI, const String& localName) const
324 return getAttribute(QualifiedName(nullAtom, localName.impl(), namespaceURI.impl()));
327 void Element::setAttribute(const String& name, const String& value, ExceptionCode& ec)
329 if (!Document::isValidName(name)) {
330 ec = INVALID_CHARACTER_ERR;
334 String localName = inHTMLDocument(this) ? name.lower() : name;
336 // allocate attributemap if necessary
337 Attribute* old = attributes(false)->getAttributeItem(localName);
339 // NO_MODIFICATION_ALLOWED_ERR: Raised when the node is readonly
340 if (namedAttrMap->isReadOnlyNode()) {
341 ec = NO_MODIFICATION_ALLOWED_ERR;
346 document()->incDOMTreeVersion();
348 if (localName == idAttr.localName())
349 updateId(old ? old->value() : nullAtom, value);
351 if (old && value.isNull())
352 namedAttrMap->removeAttribute(old->name());
353 else if (!old && !value.isNull())
354 namedAttrMap->addAttribute(createAttribute(QualifiedName(nullAtom, localName.impl(), nullAtom), value.impl()));
355 else if (old && !value.isNull()) {
356 old->setValue(value);
357 attributeChanged(old);
361 void Element::setAttribute(const QualifiedName& name, StringImpl* value, ExceptionCode& ec)
364 document()->incDOMTreeVersion();
366 // allocate attributemap if necessary
367 Attribute* old = attributes(false)->getAttributeItem(name);
369 // NO_MODIFICATION_ALLOWED_ERR: Raised when the node is readonly
370 if (namedAttrMap->isReadOnlyNode()) {
371 ec = NO_MODIFICATION_ALLOWED_ERR;
376 updateId(old ? old->value() : nullAtom, value);
379 namedAttrMap->removeAttribute(name);
380 else if (!old && value)
381 namedAttrMap->addAttribute(createAttribute(name, value));
382 else if (old && value) {
383 old->setValue(value);
384 attributeChanged(old);
388 Attribute* Element::createAttribute(const QualifiedName& name, StringImpl* value)
390 return new Attribute(name, value);
393 void Element::setAttributeMap(NamedAttrMap* list)
396 document()->incDOMTreeVersion();
398 // If setting the whole map changes the id attribute, we need to call updateId.
400 Attribute *oldId = namedAttrMap ? namedAttrMap->getAttributeItem(idAttr) : 0;
401 Attribute *newId = list ? list->getAttributeItem(idAttr) : 0;
404 updateId(oldId ? oldId->value() : nullAtom, newId ? newId->value() : nullAtom);
407 namedAttrMap->element = 0;
412 namedAttrMap->element = this;
413 unsigned len = namedAttrMap->length();
414 for (unsigned i = 0; i < len; i++)
415 attributeChanged(namedAttrMap->attrs[i]);
416 // FIXME: What about attributes that were in the old map that are not in the new map?
420 bool Element::hasAttributes() const
422 updateStyleAttributeIfNeeded();
423 return namedAttrMap && namedAttrMap->length() > 0;
426 String Element::nodeName() const
428 return m_tagName.toString();
431 String Element::nodeNamePreservingCase() const
433 return m_tagName.toString();
436 void Element::setPrefix(const AtomicString &_prefix, ExceptionCode& ec)
438 checkSetPrefix(_prefix, ec);
442 m_tagName.setPrefix(_prefix);
445 Node* Element::insertAdjacentElement(const String& where, Node* newChild, int& exception)
448 // IE throws COM Exception E_INVALIDARG; this is the best DOM exception alternative
449 exception = TYPE_MISMATCH_ERR;
453 // In Internet Explorer if the element has no parent and where is "beforeBegin" or "afterEnd",
454 // a document fragment is created and the elements appended in the correct order. This document
455 // fragment isn't returned anywhere.
457 // This is impossible for us to implement as the DOM tree does not allow for such structures,
458 // Opera also appears to disallow such usage.
460 if (equalIgnoringCase(where, "beforeBegin")) {
461 if (Node* p = parent())
462 return p->insertBefore(newChild, this, exception) ? newChild : 0;
463 } else if (equalIgnoringCase(where, "afterBegin")) {
464 return insertBefore(newChild, firstChild(), exception) ? newChild : 0;
465 } else if (equalIgnoringCase(where, "beforeEnd")) {
466 return appendChild(newChild, exception) ? newChild : 0;
467 } else if (equalIgnoringCase(where, "afterEnd")) {
468 if (Node* p = parent())
469 return p->insertBefore(newChild, nextSibling(), exception) ? newChild : 0;
471 // IE throws COM Exception E_INVALIDARG; this is the best DOM exception alternative
472 exception = NOT_SUPPORTED_ERR;
477 bool Element::contains(const Element* element) const
481 return this == element || element->isAncestor(this);
484 void Element::createAttributeMap() const
486 namedAttrMap = new NamedAttrMap(const_cast<Element*>(this));
489 bool Element::isURLAttribute(Attribute *attr) const
494 RenderStyle *Element::styleForRenderer(RenderObject *parentRenderer)
496 return document()->styleSelector()->styleForElement(this);
499 RenderObject *Element::createRenderer(RenderArena *arena, RenderStyle *style)
501 if (document()->documentElement() == this && style->display() == NONE) {
502 // Ignore display: none on root elements. Force a display of block in that case.
503 RenderBlock* result = new (arena) RenderBlock(this);
504 if (result) result->setStyle(style);
507 return RenderObject::createObject(this, style);
511 void Element::insertedIntoDocument()
513 // need to do superclass processing first so inDocument() is true
514 // by the time we reach updateId
515 ContainerNode::insertedIntoDocument();
518 NamedAttrMap* attrs = attributes(true);
520 Attribute* idItem = attrs->getAttributeItem(idAttr);
521 if (idItem && !idItem->isNull())
522 updateId(nullAtom, idItem->value());
527 void Element::removedFromDocument()
530 NamedAttrMap* attrs = attributes(true);
532 Attribute* idItem = attrs->getAttributeItem(idAttr);
533 if (idItem && !idItem->isNull())
534 updateId(idItem->value(), nullAtom);
538 ContainerNode::removedFromDocument();
541 void Element::attach()
544 createRendererIfNeeded();
546 ContainerNode::attach();
547 if (needsFocusAppearanceUpdate() && !m_updateFocusAppearanceTimer.isActive() && document()->focusNode() == this)
548 m_updateFocusAppearanceTimer.startOneShot(0);
551 void Element::detach()
553 stopUpdateFocusAppearanceTimer();
554 ContainerNode::detach();
557 void Element::recalcStyle(StyleChange change)
559 // ### should go away and be done in renderobject
560 RenderStyle* _style = renderStyle();
561 bool hasParentStyle = parentNode() ? parentNode()->renderStyle() : false;
563 if (hasParentStyle && (change >= Inherit || changed())) {
564 RenderStyle *newStyle = document()->styleSelector()->styleForElement(this);
565 StyleChange ch = diff(_style, newStyle);
569 // ### Suboptimal. Style gets calculated again.
571 // attach recalulates the style for all children. No need to do it twice.
573 setHasChangedChild(false);
574 newStyle->deref(document()->renderArena());
577 else if (ch != NoChange) {
579 setRenderStyle(newStyle);
581 else if (changed() && newStyle && (document()->usesSiblingRules() || document()->usesDescendantRules())) {
582 // Although no change occurred, we use the new style so that the cousin style sharing code won't get
583 // fooled into believing this style is the same. This is only necessary if the document actually uses
584 // sibling/descendant rules, since otherwise it isn't possible for ancestor styles to affect sharing of
587 renderer()->setStyleInternal(newStyle);
589 setRenderStyle(newStyle);
592 newStyle->deref(document()->renderArena());
594 if (change != Force) {
595 if (document()->usesDescendantRules())
602 for (Node *n = fastFirstChild(); n; n = n->nextSibling()) {
603 if (change >= Inherit || n->isTextNode() || n->hasChangedChild() || n->changed())
604 n->recalcStyle(change);
608 setHasChangedChild(false);
611 bool Element::childTypeAllowed(NodeType type)
617 case PROCESSING_INSTRUCTION_NODE:
618 case CDATA_SECTION_NODE:
619 case ENTITY_REFERENCE_NODE:
627 void Element::dispatchAttrRemovalEvent(Attribute*)
629 assert(!eventDispatchForbidden());
631 if (!document()->hasListenerType(Document::DOMATTRMODIFIED_LISTENER))
633 ExceptionCode ec = 0;
634 dispatchEvent(new MutationEvent(DOMAttrModifiedEvent, true, false, attr, attr->value(),
635 attr->value(), document()->attrName(attr->id()), MutationEvent::REMOVAL), ec);
639 void Element::dispatchAttrAdditionEvent(Attribute *attr)
641 assert(!eventDispatchForbidden());
643 if (!document()->hasListenerType(Document::DOMATTRMODIFIED_LISTENER))
645 ExceptionCode ec = 0;
646 dispatchEvent(new MutationEvent(DOMAttrModifiedEvent, true, false, attr, attr->value(),
647 attr->value(),document()->attrName(attr->id()), MutationEvent::ADDITION), ec);
651 String Element::openTagStartToString() const
653 String result = "<" + nodeName();
655 NamedAttrMap *attrMap = attributes(true);
658 unsigned numAttrs = attrMap->length();
659 for (unsigned i = 0; i < numAttrs; i++) {
662 Attribute *attribute = attrMap->attributeItem(i);
663 result += attribute->name().toString();
664 if (!attribute->value().isNull()) {
666 // FIXME: substitute entities for any instances of " or '
667 result += attribute->value();
676 String Element::toString() const
678 String result = openTagStartToString();
680 if (hasChildNodes()) {
683 for (Node *child = firstChild(); child != NULL; child = child->nextSibling()) {
684 result += child->toString();
688 result += nodeName();
697 void Element::updateId(const AtomicString& oldId, const AtomicString& newId)
705 Document* doc = document();
706 if (!oldId.isEmpty())
707 doc->removeElementById(oldId, this);
708 if (!newId.isEmpty())
709 doc->addElementById(newId, this);
713 void Element::dump(TextStream *stream, DeprecatedString ind) const
715 updateStyleAttributeIfNeeded();
717 for (unsigned i = 0; i < namedAttrMap->length(); i++) {
718 Attribute *attr = namedAttrMap->attributeItem(i);
719 *stream << " " << attr->name().localName().deprecatedString().ascii()
720 << "=\"" << attr->value().deprecatedString().ascii() << "\"";
724 ContainerNode::dump(stream,ind);
729 void Element::formatForDebugger(char *buffer, unsigned length) const
735 if (s.length() > 0) {
739 s = getAttribute(idAttr);
740 if (s.length() > 0) {
741 if (result.length() > 0)
747 s = getAttribute(classAttr);
748 if (s.length() > 0) {
749 if (result.length() > 0)
755 strncpy(buffer, result.deprecatedString().latin1(), length - 1);
759 PassRefPtr<Attr> Element::setAttributeNode(Attr *attr, ExceptionCode& ec)
762 return static_pointer_cast<Attr>(attributes(false)->setNamedItem(attr, ec));
765 PassRefPtr<Attr> Element::removeAttributeNode(Attr *attr, ExceptionCode& ec)
767 if (!attr || attr->ownerElement() != this) {
771 if (document() != attr->document()) {
772 ec = WRONG_DOCUMENT_ERR;
776 NamedAttrMap *attrs = attributes(true);
780 return static_pointer_cast<Attr>(attrs->removeNamedItem(attr->qualifiedName(), ec));
783 void Element::setAttributeNS(const String& namespaceURI, const String& qualifiedName, const String& value, ExceptionCode& ec)
785 String prefix, localName;
786 if (!Document::parseQualifiedName(qualifiedName, prefix, localName)) {
787 ec = INVALID_CHARACTER_ERR;
790 setAttribute(QualifiedName(prefix.impl(), localName.impl(), namespaceURI.impl()), value.impl(), ec);
793 void Element::removeAttribute(const String& name, ExceptionCode& ec)
795 String localName = inHTMLDocument(this) ? name.lower() : name;
798 namedAttrMap->removeNamedItem(localName, ec);
799 if (ec == NOT_FOUND_ERR)
804 void Element::removeAttributeNS(const String& namespaceURI, const String& localName, ExceptionCode& ec)
806 removeAttribute(QualifiedName(nullAtom, localName.impl(), namespaceURI.impl()), ec);
809 PassRefPtr<Attr> Element::getAttributeNode(const String& name)
811 NamedAttrMap *attrs = attributes(true);
814 String localName = inHTMLDocument(this) ? name.lower() : name;
815 return static_pointer_cast<Attr>(attrs->getNamedItem(localName));
818 PassRefPtr<Attr> Element::getAttributeNodeNS(const String& namespaceURI, const String& localName)
820 NamedAttrMap *attrs = attributes(true);
823 return static_pointer_cast<Attr>(attrs->getNamedItem(QualifiedName(nullAtom, localName.impl(), namespaceURI.impl())));
826 bool Element::hasAttribute(const String& name) const
828 NamedAttrMap *attrs = attributes(true);
831 String localName = inHTMLDocument(this) ? name.lower() : name;
832 return attrs->getAttributeItem(localName);
835 bool Element::hasAttributeNS(const String& namespaceURI, const String& localName) const
837 NamedAttrMap *attrs = attributes(true);
840 return attrs->getAttributeItem(QualifiedName(nullAtom, localName.impl(), namespaceURI.impl()));
843 CSSStyleDeclaration *Element::style()
848 void Element::focus()
850 Document* doc = document();
853 if (!supportsFocus())
856 doc->setFocusNode(this);
858 if (!isFocusable()) {
859 setNeedsFocusAppearanceUpdate(true);
863 updateFocusAppearance();
866 void Element::updateFocusAppearance()
868 if (this == rootEditableElement()) {
869 Frame* frame = document()->frame();
873 // FIXME: We should restore the previous selection if there is one.
874 Selection s = hasTagName(htmlTag) || hasTagName(bodyTag) ? Selection(Position(this, 0), DOWNSTREAM) : Selection::selectionFromContentsOfNode(this);
875 SelectionController sc(s);
877 if (frame->shouldChangeSelection(sc)) {
878 frame->setSelection(sc);
879 frame->revealSelection();
881 } else if (renderer() && !renderer()->isWidget())
882 renderer()->enclosingLayer()->scrollRectToVisible(getRect());
885 void Element::updateFocusAppearanceTimerFired(Timer<Element>*)
887 stopUpdateFocusAppearanceTimer();
888 Document* doc = document();
891 updateFocusAppearance();
896 stopUpdateFocusAppearanceTimer();
897 Document* doc = document();
898 if (doc->focusNode() == this)
899 doc->setFocusNode(0);
902 void Element::stopUpdateFocusAppearanceTimer()
904 if (m_updateFocusAppearanceTimer.isActive()) {
905 m_updateFocusAppearanceTimer.stop();
906 setNeedsFocusAppearanceUpdate(false);