diff options
| author | Allan Sandfeld Jensen <allan.jensen@digia.com> | 2013-09-13 12:51:20 +0200 |
|---|---|---|
| committer | The Qt Project <gerrit-noreply@qt-project.org> | 2013-09-19 20:50:05 +0200 |
| commit | d441d6f39bb846989d95bcf5caf387b42414718d (patch) | |
| tree | e367e64a75991c554930278175d403c072de6bb8 /Source/WebCore/dom/Element.cpp | |
| parent | 0060b2994c07842f4c59de64b5e3e430525c4b90 (diff) | |
| download | qtwebkit-d441d6f39bb846989d95bcf5caf387b42414718d.tar.gz | |
Import Qt5x2 branch of QtWebkit for Qt 5.2
Importing a new snapshot of webkit.
Change-Id: I2d01ad12cdc8af8cb015387641120a9d7ea5f10c
Reviewed-by: Allan Sandfeld Jensen <allan.jensen@digia.com>
Diffstat (limited to 'Source/WebCore/dom/Element.cpp')
| -rw-r--r-- | Source/WebCore/dom/Element.cpp | 1655 |
1 files changed, 1223 insertions, 432 deletions
diff --git a/Source/WebCore/dom/Element.cpp b/Source/WebCore/dom/Element.cpp index 18bf57289..129b29911 100644 --- a/Source/WebCore/dom/Element.cpp +++ b/Source/WebCore/dom/Element.cpp @@ -4,7 +4,7 @@ * (C) 2001 Peter Kelly (pmk@post.com) * (C) 2001 Dirk Mueller (mueller@kde.org) * (C) 2007 David Smith (catfish.man@gmail.com) - * Copyright (C) 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2012 Apple Inc. All rights reserved. + * Copyright (C) 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2012, 2013 Apple Inc. All rights reserved. * (C) 2007 Eric Seidel (eric@webkit.org) * * This library is free software; you can redistribute it and/or @@ -30,18 +30,25 @@ #include "Attr.h" #include "CSSParser.h" #include "CSSSelectorList.h" +#include "Chrome.h" +#include "ChromeClient.h" #include "ClassList.h" #include "ClientRect.h" #include "ClientRectList.h" +#include "CustomElementRegistry.h" #include "DOMTokenList.h" #include "DatasetDOMStringMap.h" #include "Document.h" #include "DocumentFragment.h" +#include "DocumentSharedObjectPool.h" #include "ElementRareData.h" +#include "EventDispatcher.h" #include "ExceptionCode.h" #include "FlowThreadController.h" #include "FocusController.h" +#include "FocusEvent.h" #include "Frame.h" +#include "FrameSelection.h" #include "FrameView.h" #include "HTMLCollection.h" #include "HTMLDocument.h" @@ -49,6 +56,7 @@ #include "HTMLFormControlsCollection.h" #include "HTMLFrameOwnerElement.h" #include "HTMLLabelElement.h" +#include "HTMLNameCollection.h" #include "HTMLNames.h" #include "HTMLOptionsCollection.h" #include "HTMLParserIdioms.h" @@ -61,27 +69,31 @@ #include "NodeList.h" #include "NodeRenderStyle.h" #include "NodeRenderingContext.h" +#include "NodeTraversal.h" #include "Page.h" #include "PointerLockController.h" +#include "PseudoElement.h" #include "RenderRegion.h" +#include "RenderTheme.h" #include "RenderView.h" #include "RenderWidget.h" #include "SelectorQuery.h" #include "Settings.h" #include "ShadowRoot.h" +#include "StylePropertySet.h" #include "StyleResolver.h" #include "Text.h" #include "TextIterator.h" #include "VoidCallback.h" -#include "WebCoreMemoryInstrumentation.h" -#include "WebKitAnimationList.h" #include "XMLNSNames.h" #include "XMLNames.h" #include "htmlediting.h" #include <wtf/BitVector.h> +#include <wtf/CurrentTime.h> #include <wtf/text/CString.h> #if ENABLE(SVG) +#include "SVGDocumentExtensions.h" #include "SVGElement.h" #include "SVGNames.h" #endif @@ -90,6 +102,11 @@ namespace WebCore { using namespace HTMLNames; using namespace XMLNames; + +static inline bool shouldIgnoreAttributeCase(const Element* e) +{ + return e && e->document()->isHTMLDocument() && e->isHTMLElement(); +} class StyleResolverParentPusher { public: @@ -102,7 +119,7 @@ public: { if (m_pushedStyleResolver) return; - m_pushedStyleResolver = m_parent->document()->styleResolver(); + m_pushedStyleResolver = m_parent->document()->ensureStyleResolver(); m_pushedStyleResolver->pushParentElement(m_parent); } ~StyleResolverParentPusher() @@ -113,8 +130,8 @@ public: // This tells us that our pushed style selector is in a bad state, // so we should just bail out in that scenario. - ASSERT(m_pushedStyleResolver == m_parent->document()->styleResolver()); - if (m_pushedStyleResolver != m_parent->document()->styleResolver()) + ASSERT(m_pushedStyleResolver == m_parent->document()->ensureStyleResolver()); + if (m_pushedStyleResolver != m_parent->document()->ensureStyleResolver()) return; m_pushedStyleResolver->popParentElement(m_parent); @@ -187,13 +204,22 @@ Element::~Element() } #endif - if (ElementShadow* elementShadow = shadow()) { - elementShadow->removeAllShadowRoots(); - elementRareData()->m_shadow.clear(); + if (hasRareData()) { + ElementRareData* data = elementRareData(); + data->setPseudoElement(BEFORE, 0); + data->setPseudoElement(AFTER, 0); + data->clearShadow(); } if (hasSyntheticAttrChildNodes()) detachAllAttrNodesFromElement(); + +#if ENABLE(SVG) + if (hasPendingResources()) { + document()->accessSVGExtensions()->removeElementFromPendingResources(this); + ASSERT(!hasPendingResources()); + } +#endif } inline ElementRareData* Element::elementRareData() const @@ -206,10 +232,51 @@ inline ElementRareData* Element::ensureElementRareData() { return static_cast<ElementRareData*>(ensureRareData()); } - -OwnPtr<NodeRareData> Element::createRareData() + +void Element::clearTabIndexExplicitlyIfNeeded() +{ + if (hasRareData()) + elementRareData()->clearTabIndexExplicitly(); +} + +void Element::setTabIndexExplicitly(short tabIndex) { - return adoptPtr(new ElementRareData); + ensureElementRareData()->setTabIndexExplicitly(tabIndex); +} + +bool Element::supportsFocus() const +{ + return hasRareData() && elementRareData()->tabIndexSetExplicitly(); +} + +Element* Element::focusDelegate() +{ + return this; +} + +short Element::tabIndex() const +{ + return hasRareData() ? elementRareData()->tabIndex() : 0; +} + +bool Element::isKeyboardFocusable(KeyboardEvent*) const +{ + return isFocusable() && tabIndex() >= 0; +} + +bool Element::isMouseFocusable() const +{ + return isFocusable(); +} + +bool Element::shouldUseInputMethod() +{ + return isContentEditable(UserSelectAllIsAlwaysNonEditable); +} + +void Element::dispatchSimulatedClick(Event* underlyingEvent, SimulatedClickMouseEventOptions eventOptions, SimulatedClickVisualOptions visualOptions) +{ + EventDispatcher::dispatchSimulatedClick(this, underlyingEvent, eventOptions, visualOptions); } DEFINE_VIRTUAL_ATTRIBUTE_EVENT_LISTENER(Element, blur); @@ -245,11 +312,11 @@ PassRefPtr<Element> Element::cloneElementWithoutAttributesAndChildren() return document()->createElement(tagQName(), false); } -PassRefPtr<Attr> Element::detachAttribute(size_t index) +PassRefPtr<Attr> Element::detachAttribute(unsigned index) { - ASSERT(attributeData()); + ASSERT(elementData()); - const Attribute* attribute = attributeData()->attributeItem(index); + const Attribute* attribute = elementData()->attributeItem(index); ASSERT(attribute); RefPtr<Attr> attrNode = attrIfExists(attribute->name()); @@ -264,11 +331,11 @@ PassRefPtr<Attr> Element::detachAttribute(size_t index) void Element::removeAttribute(const QualifiedName& name) { - if (!attributeData()) + if (!elementData()) return; - size_t index = attributeData()->getAttributeItemIndex(name); - if (index == notFound) + unsigned index = elementData()->getAttributeItemIndex(name); + if (index == ElementData::attributeNotFound) return; removeAttributeInternal(index, NotInSynchronizationOfLazyAttribute); @@ -284,13 +351,12 @@ void Element::setBooleanAttribute(const QualifiedName& name, bool value) NamedNodeMap* Element::attributes() const { - ensureUpdatedAttributeData(); ElementRareData* rareData = const_cast<Element*>(this)->ensureElementRareData(); - if (NamedNodeMap* attributeMap = rareData->m_attributeMap.get()) + if (NamedNodeMap* attributeMap = rareData->attributeMap()) return attributeMap; - rareData->m_attributeMap = NamedNodeMap::create(const_cast<Element*>(this)); - return rareData->m_attributeMap.get(); + rareData->setAttributeMap(NamedNodeMap::create(const_cast<Element*>(this))); + return rareData->attributeMap(); } Node::NodeType Element::nodeType() const @@ -303,24 +369,210 @@ bool Element::hasAttribute(const QualifiedName& name) const return hasAttributeNS(name.namespaceURI(), name.localName()); } -const AtomicString& Element::getAttribute(const QualifiedName& name) const +void Element::synchronizeAllAttributes() const { - if (!attributeData()) - return nullAtom; + if (!elementData()) + return; + if (elementData()->m_styleAttributeIsDirty) { + ASSERT(isStyledElement()); + static_cast<const StyledElement*>(this)->synchronizeStyleAttributeInternal(); + } +#if ENABLE(SVG) + if (elementData()->m_animatedSVGAttributesAreDirty) { + ASSERT(isSVGElement()); + toSVGElement(this)->synchronizeAnimatedSVGAttribute(anyQName()); + } +#endif +} - if (UNLIKELY(name == styleAttr && attributeData()->m_styleAttributeIsDirty)) - updateStyleAttribute(); +inline void Element::synchronizeAttribute(const QualifiedName& name) const +{ + if (!elementData()) + return; + if (UNLIKELY(name == styleAttr && elementData()->m_styleAttributeIsDirty)) { + ASSERT(isStyledElement()); + static_cast<const StyledElement*>(this)->synchronizeStyleAttributeInternal(); + return; + } +#if ENABLE(SVG) + if (UNLIKELY(elementData()->m_animatedSVGAttributesAreDirty)) { + ASSERT(isSVGElement()); + toSVGElement(this)->synchronizeAnimatedSVGAttribute(name); + } +#endif +} +inline void Element::synchronizeAttribute(const AtomicString& localName) const +{ + // This version of synchronizeAttribute() is streamlined for the case where you don't have a full QualifiedName, + // e.g when called from DOM API. + if (!elementData()) + return; + if (elementData()->m_styleAttributeIsDirty && equalPossiblyIgnoringCase(localName, styleAttr.localName(), shouldIgnoreAttributeCase(this))) { + ASSERT(isStyledElement()); + static_cast<const StyledElement*>(this)->synchronizeStyleAttributeInternal(); + return; + } #if ENABLE(SVG) - if (UNLIKELY(attributeData()->m_animatedSVGAttributesAreDirty)) - updateAnimatedSVGAttribute(name); + if (elementData()->m_animatedSVGAttributesAreDirty) { + // We're not passing a namespace argument on purpose. SVGNames::*Attr are defined w/o namespaces as well. + ASSERT(isSVGElement()); + static_cast<const SVGElement*>(this)->synchronizeAnimatedSVGAttribute(QualifiedName(nullAtom, localName, nullAtom)); + } #endif +} +const AtomicString& Element::getAttribute(const QualifiedName& name) const +{ + if (!elementData()) + return nullAtom; + synchronizeAttribute(name); if (const Attribute* attribute = getAttributeItem(name)) return attribute->value(); return nullAtom; } +bool Element::isFocusable() const +{ + if (!inDocument() || !supportsFocus()) + return false; + + // Elements in canvas fallback content are not rendered, but they are allowed to be + // focusable as long as their canvas is displayed and visible. + if (isInCanvasSubtree()) { + const Element* e = this; + while (e && !e->hasLocalName(canvasTag)) + e = e->parentElement(); + ASSERT(e); + return e->renderer() && e->renderer()->style()->visibility() == VISIBLE; + } + + if (renderer()) + ASSERT(!renderer()->needsLayout()); + else { + // If the node is in a display:none tree it might say it needs style recalc but + // the whole document is actually up to date. + ASSERT(!document()->childNeedsStyleRecalc()); + } + + // FIXME: Even if we are not visible, we might have a child that is visible. + // Hyatt wants to fix that some day with a "has visible content" flag or the like. + if (!renderer() || renderer()->style()->visibility() != VISIBLE) + return false; + + return true; +} + +bool Element::isUserActionElementInActiveChain() const +{ + ASSERT(isUserActionElement()); + return document()->userActionElements().isInActiveChain(this); +} + +bool Element::isUserActionElementActive() const +{ + ASSERT(isUserActionElement()); + return document()->userActionElements().isActive(this); +} + +bool Element::isUserActionElementFocused() const +{ + ASSERT(isUserActionElement()); + return document()->userActionElements().isFocused(this); +} + +bool Element::isUserActionElementHovered() const +{ + ASSERT(isUserActionElement()); + return document()->userActionElements().isHovered(this); +} + +void Element::setActive(bool flag, bool pause) +{ + if (flag == active()) + return; + + if (Document* document = this->document()) + document->userActionElements().setActive(this, flag); + + if (!renderer()) + return; + + bool reactsToPress = renderStyle()->affectedByActive() || childrenAffectedByActive(); + if (reactsToPress) + setNeedsStyleRecalc(); + + if (renderer()->style()->hasAppearance() && renderer()->theme()->stateChanged(renderer(), PressedState)) + reactsToPress = true; + + // The rest of this function implements a feature that only works if the + // platform supports immediate invalidations on the ChromeClient, so bail if + // that isn't supported. + if (!document()->page()->chrome().client()->supportsImmediateInvalidation()) + return; + + if (reactsToPress && pause) { + // The delay here is subtle. It relies on an assumption, namely that the amount of time it takes + // to repaint the "down" state of the control is about the same time as it would take to repaint the + // "up" state. Once you assume this, you can just delay for 100ms - that time (assuming that after you + // leave this method, it will be about that long before the flush of the up state happens again). +#ifdef HAVE_FUNC_USLEEP + double startTime = currentTime(); +#endif + + Document::updateStyleForAllDocuments(); + // Do an immediate repaint. + if (renderer()) + renderer()->repaint(true); + + // FIXME: Come up with a less ridiculous way of doing this. +#ifdef HAVE_FUNC_USLEEP + // Now pause for a small amount of time (1/10th of a second from before we repainted in the pressed state) + double remainingTime = 0.1 - (currentTime() - startTime); + if (remainingTime > 0) + usleep(static_cast<useconds_t>(remainingTime * 1000000.0)); +#endif + } +} + +void Element::setFocus(bool flag) +{ + if (flag == focused()) + return; + + if (Document* document = this->document()) + document->userActionElements().setFocused(this, flag); + + setNeedsStyleRecalc(); +} + +void Element::setHovered(bool flag) +{ + if (flag == hovered()) + return; + + if (Document* document = this->document()) + document->userActionElements().setHovered(this, flag); + + if (!renderer()) { + // When setting hover to false, the style needs to be recalc'd even when + // there's no renderer (imagine setting display:none in the :hover class, + // if a nil renderer would prevent this element from recalculating its + // style, it would never go back to its normal style and remain + // stuck in its hovered style). + if (!flag) + setNeedsStyleRecalc(); + + return; + } + + if (renderer()->style()->affectedByHover() || childrenAffectedByHover()) + setNeedsStyleRecalc(); + + if (renderer()->style()->hasAppearance()) + renderer()->theme()->stateChanged(renderer(), HoverState); +} + void Element::scrollIntoView(bool alignToTop) { document()->updateLayoutIgnorePendingStylesheets(); @@ -437,7 +689,11 @@ int Element::offsetWidth() { document()->updateLayoutIgnorePendingStylesheets(); if (RenderBoxModelObject* renderer = renderBoxModelObject()) +#if ENABLE(SUBPIXEL_LAYOUT) + return adjustLayoutUnitForAbsoluteZoom(renderer->pixelSnappedOffsetWidth(), renderer).round(); +#else return adjustForAbsoluteZoom(renderer->pixelSnappedOffsetWidth(), renderer); +#endif return 0; } @@ -445,16 +701,29 @@ int Element::offsetHeight() { document()->updateLayoutIgnorePendingStylesheets(); if (RenderBoxModelObject* renderer = renderBoxModelObject()) +#if ENABLE(SUBPIXEL_LAYOUT) + return adjustLayoutUnitForAbsoluteZoom(renderer->pixelSnappedOffsetHeight(), renderer).round(); +#else return adjustForAbsoluteZoom(renderer->pixelSnappedOffsetHeight(), renderer); +#endif return 0; } +Element* Element::bindingsOffsetParent() +{ + Element* element = offsetParent(); + if (!element || !element->isInShadowTree()) + return element; + return element->containingShadowRoot()->type() == ShadowRoot::UserAgentShadowRoot ? 0 : element; +} + Element* Element::offsetParent() { document()->updateLayoutIgnorePendingStylesheets(); - if (RenderObject* rend = renderer()) - if (RenderObject* offsetParent = rend->offsetParent()) - return static_cast<Element*>(offsetParent->node()); + if (RenderObject* renderer = this->renderer()) { + if (RenderObject* offsetParent = renderer->offsetParent()) + return toElement(offsetParent->node()); + } return 0; } @@ -492,7 +761,11 @@ int Element::clientWidth() } if (RenderBox* renderer = renderBox()) +#if ENABLE(SUBPIXEL_LAYOUT) + return adjustLayoutUnitForAbsoluteZoom(renderer->pixelSnappedClientWidth(), renderer).round(); +#else return adjustForAbsoluteZoom(renderer->pixelSnappedClientWidth(), renderer); +#endif return 0; } @@ -513,7 +786,11 @@ int Element::clientHeight() } if (RenderBox* renderer = renderBox()) +#if ENABLE(SUBPIXEL_LAYOUT) + return adjustLayoutUnitForAbsoluteZoom(renderer->pixelSnappedClientHeight(), renderer).round(); +#else return adjustForAbsoluteZoom(renderer->pixelSnappedClientHeight(), renderer); +#endif return 0; } @@ -575,7 +852,7 @@ IntRect Element::boundsInRootViewSpace() #if ENABLE(SVG) if (isSVGElement() && renderer()) { // Get the bounding rectangle from the SVG model. - SVGElement* svgElement = static_cast<SVGElement*>(this); + SVGElement* svgElement = toSVGElement(this); FloatRect localRect; if (svgElement->getBoundingBox(localRect)) quads.append(renderer()->localToAbsoluteQuad(localRect)); @@ -623,7 +900,7 @@ PassRefPtr<ClientRect> Element::getBoundingClientRect() #if ENABLE(SVG) if (isSVGElement() && renderer() && !renderer()->isSVGRoot()) { // Get the bounding rectangle from the SVG model. - SVGElement* svgElement = static_cast<SVGElement*>(this); + SVGElement* svgElement = toSVGElement(this); FloatRect localRect; if (svgElement->getBoundingBox(localRect)) quads.append(renderer()->localToAbsoluteQuad(localRect)); @@ -654,30 +931,12 @@ IntRect Element::screenRect() const return document()->view()->contentsToScreen(renderer()->absoluteBoundingBoxRectIgnoringTransforms()); } -static inline bool shouldIgnoreAttributeCase(const Element* e) +const AtomicString& Element::getAttribute(const AtomicString& localName) const { - return e && e->document()->isHTMLDocument() && e->isHTMLElement(); -} - -const AtomicString& Element::getAttribute(const AtomicString& name) const -{ - if (!attributeData()) + if (!elementData()) return nullAtom; - - bool ignoreCase = shouldIgnoreAttributeCase(this); - - // Update the 'style' attribute if it's invalid and being requested: - if (attributeData()->m_styleAttributeIsDirty && equalPossiblyIgnoringCase(name, styleAttr.localName(), ignoreCase)) - updateStyleAttribute(); - -#if ENABLE(SVG) - if (attributeData()->m_animatedSVGAttributesAreDirty) { - // We're not passing a namespace argument on purpose. SVGNames::*Attr are defined w/o namespaces as well. - updateAnimatedSVGAttribute(QualifiedName(nullAtom, name, nullAtom)); - } -#endif - - if (const Attribute* attribute = attributeData()->getAttributeItem(name, ignoreCase)) + synchronizeAttribute(localName); + if (const Attribute* attribute = elementData()->getAttributeItem(localName, shouldIgnoreAttributeCase(this))) return attribute->value(); return nullAtom; } @@ -687,39 +946,43 @@ const AtomicString& Element::getAttributeNS(const AtomicString& namespaceURI, co return getAttribute(QualifiedName(nullAtom, localName, namespaceURI)); } -void Element::setAttribute(const AtomicString& name, const AtomicString& value, ExceptionCode& ec) +void Element::setAttribute(const AtomicString& localName, const AtomicString& value, ExceptionCode& ec) { - if (!Document::isValidName(name)) { + if (!Document::isValidName(localName)) { ec = INVALID_CHARACTER_ERR; return; } - const AtomicString& localName = shouldIgnoreAttributeCase(this) ? name.lower() : name; + synchronizeAttribute(localName); + const AtomicString& caseAdjustedLocalName = shouldIgnoreAttributeCase(this) ? localName.lower() : localName; - size_t index = ensureUpdatedAttributeData()->getAttributeItemIndex(localName, false); - const QualifiedName& qName = index != notFound ? attributeItem(index)->name() : QualifiedName(nullAtom, localName, nullAtom); + unsigned index = elementData() ? elementData()->getAttributeItemIndex(caseAdjustedLocalName, false) : ElementData::attributeNotFound; + const QualifiedName& qName = index != ElementData::attributeNotFound ? attributeItem(index)->name() : QualifiedName(nullAtom, caseAdjustedLocalName, nullAtom); setAttributeInternal(index, qName, value, NotInSynchronizationOfLazyAttribute); } void Element::setAttribute(const QualifiedName& name, const AtomicString& value) { - setAttributeInternal(ensureUpdatedAttributeData()->getAttributeItemIndex(name), name, value, NotInSynchronizationOfLazyAttribute); + synchronizeAttribute(name); + unsigned index = elementData() ? elementData()->getAttributeItemIndex(name) : ElementData::attributeNotFound; + setAttributeInternal(index, name, value, NotInSynchronizationOfLazyAttribute); } void Element::setSynchronizedLazyAttribute(const QualifiedName& name, const AtomicString& value) { - setAttributeInternal(mutableAttributeData()->getAttributeItemIndex(name), name, value, InSynchronizationOfLazyAttribute); + unsigned index = elementData() ? elementData()->getAttributeItemIndex(name) : ElementData::attributeNotFound; + setAttributeInternal(index, name, value, InSynchronizationOfLazyAttribute); } -inline void Element::setAttributeInternal(size_t index, const QualifiedName& name, const AtomicString& newValue, SynchronizationOfLazyAttribute inSynchronizationOfLazyAttribute) +inline void Element::setAttributeInternal(unsigned index, const QualifiedName& name, const AtomicString& newValue, SynchronizationOfLazyAttribute inSynchronizationOfLazyAttribute) { if (newValue.isNull()) { - if (index != notFound) + if (index != ElementData::attributeNotFound) removeAttributeInternal(index, inSynchronizationOfLazyAttribute); return; } - if (index == notFound) { + if (index == ElementData::attributeNotFound) { addAttributeInternal(name, newValue, inSynchronizationOfLazyAttribute); return; } @@ -729,12 +992,12 @@ inline void Element::setAttributeInternal(size_t index, const QualifiedName& nam if (newValue != attributeItem(index)->value()) { // If there is an Attr node hooked to this attribute, the Attr::setValue() call below - // will write into the ElementAttributeData. + // will write into the ElementData. // FIXME: Refactor this so it makes some sense. - if (RefPtr<Attr> attrNode = attrIfExists(name)) + if (RefPtr<Attr> attrNode = inSynchronizationOfLazyAttribute ? 0 : attrIfExists(name)) attrNode->setValue(newValue); else - mutableAttributeData()->attributeItem(index)->setValue(newValue); + ensureUniqueElementData()->attributeItem(index)->setValue(newValue); } if (!inSynchronizationOfLazyAttribute) @@ -758,13 +1021,8 @@ static bool checkNeedsStyleInvalidationForIdChange(const AtomicString& oldId, co return false; } -void Element::attributeChanged(const QualifiedName& name, const AtomicString& newValue) +void Element::attributeChanged(const QualifiedName& name, const AtomicString& newValue, AttributeModificationReason) { - if (ElementShadow* parentElementShadow = shadowOfParentForDistribution(this)) { - if (shouldInvalidateDistributionWhenAttributeChanged(parentElementShadow, name, newValue)) - parentElementShadow->invalidateDistribution(); - } - parseAttribute(name, newValue); document()->incDOMTreeVersion(); @@ -774,28 +1032,41 @@ void Element::attributeChanged(const QualifiedName& name, const AtomicString& ne bool shouldInvalidateStyle = false; if (isIdAttributeName(name)) { - AtomicString oldId = attributeData()->idForStyleResolution(); + AtomicString oldId = elementData()->idForStyleResolution(); AtomicString newId = makeIdForStyleResolution(newValue, document()->inQuirksMode()); if (newId != oldId) { - attributeData()->setIdForStyleResolution(newId); + elementData()->setIdForStyleResolution(newId); shouldInvalidateStyle = testShouldInvalidateStyle && checkNeedsStyleInvalidationForIdChange(oldId, newId, styleResolver); } } else if (name == classAttr) classAttributeChanged(newValue); else if (name == HTMLNames::nameAttr) - setHasName(!newValue.isNull()); + elementData()->m_hasNameAttribute = !newValue.isNull(); else if (name == HTMLNames::pseudoAttr) shouldInvalidateStyle |= testShouldInvalidateStyle && isInShadowTree(); - shouldInvalidateStyle |= testShouldInvalidateStyle && styleResolver->hasSelectorForAttribute(name.localName()); invalidateNodeListCachesInAncestors(&name, this); + // If there is currently no StyleResolver, we can't be sure that this attribute change won't affect style. + shouldInvalidateStyle |= !styleResolver; + if (shouldInvalidateStyle) setNeedsStyleRecalc(); - if (AXObjectCache::accessibilityEnabled()) - document()->axObjectCache()->handleAttributeChanged(name, this); + if (AXObjectCache* cache = document()->existingAXObjectCache()) + cache->handleAttributeChanged(name, this); +} + +inline void Element::attributeChangedFromParserOrByCloning(const QualifiedName& name, const AtomicString& newValue, AttributeModificationReason reason) +{ +#if ENABLE(CUSTOM_ELEMENTS) + if (name == isAttr) { + if (CustomElementRegistry* registry = document()->registry()) + registry->didGiveTypeExtension(this); + } +#endif + attributeChanged(name, newValue, reason); } template <typename CharacterType> @@ -873,139 +1144,91 @@ void Element::classAttributeChanged(const AtomicString& newClassString) bool shouldInvalidateStyle = false; if (classStringHasClassName(newClassString)) { - const ElementAttributeData* attributeData = ensureAttributeData(); const bool shouldFoldCase = document()->inQuirksMode(); - const SpaceSplitString oldClasses = attributeData->classNames(); - - attributeData->setClass(newClassString, shouldFoldCase); - - const SpaceSplitString& newClasses = attributeData->classNames(); + const SpaceSplitString oldClasses = elementData()->classNames(); + elementData()->setClass(newClassString, shouldFoldCase); + const SpaceSplitString& newClasses = elementData()->classNames(); shouldInvalidateStyle = testShouldInvalidateStyle && checkSelectorForClassChange(oldClasses, newClasses, *styleResolver); - } else if (const ElementAttributeData* attributeData = this->attributeData()) { - const SpaceSplitString& oldClasses = attributeData->classNames(); + } else { + const SpaceSplitString& oldClasses = elementData()->classNames(); shouldInvalidateStyle = testShouldInvalidateStyle && checkSelectorForClassChange(oldClasses, *styleResolver); - - attributeData->clearClass(); + elementData()->clearClass(); } - if (DOMTokenList* classList = optionalClassList()) - static_cast<ClassList*>(classList)->reset(newClassString); + if (hasRareData()) + elementRareData()->clearClassListValueForQuirksMode(); if (shouldInvalidateStyle) setNeedsStyleRecalc(); } -bool Element::shouldInvalidateDistributionWhenAttributeChanged(ElementShadow* elementShadow, const QualifiedName& name, const AtomicString& newValue) -{ - ASSERT(elementShadow); - elementShadow->ensureSelectFeatureSetCollected(); - - if (isIdAttributeName(name)) { - AtomicString oldId = attributeData()->idForStyleResolution(); - AtomicString newId = makeIdForStyleResolution(newValue, document()->inQuirksMode()); - if (newId != oldId) { - if (!oldId.isEmpty() && elementShadow->selectRuleFeatureSet().hasSelectorForId(oldId)) - return true; - if (!newId.isEmpty() && elementShadow->selectRuleFeatureSet().hasSelectorForId(newId)) - return true; - } - } - - if (name == HTMLNames::classAttr) { - const AtomicString& newClassString = newValue; - if (classStringHasClassName(newClassString)) { - const ElementAttributeData* attributeData = ensureAttributeData(); - const bool shouldFoldCase = document()->inQuirksMode(); - const SpaceSplitString& oldClasses = attributeData->classNames(); - const SpaceSplitString newClasses(newClassString, shouldFoldCase); - if (checkSelectorForClassChange(oldClasses, newClasses, elementShadow->selectRuleFeatureSet())) - return true; - } else if (const ElementAttributeData* attributeData = this->attributeData()) { - const SpaceSplitString& oldClasses = attributeData->classNames(); - if (checkSelectorForClassChange(oldClasses, elementShadow->selectRuleFeatureSet())) - return true; - } - } - - return elementShadow->selectRuleFeatureSet().hasSelectorForAttribute(name.localName()); -} - // Returns true is the given attribute is an event handler. // We consider an event handler any attribute that begins with "on". // It is a simple solution that has the advantage of not requiring any // code or configuration change if a new event handler is defined. -static bool isEventHandlerAttribute(const QualifiedName& name) +static inline bool isEventHandlerAttribute(const Attribute& attribute) { - return name.namespaceURI().isNull() && name.localName().startsWith("on"); + return attribute.name().namespaceURI().isNull() && attribute.name().localName().startsWith("on"); } -// FIXME: Share code with Element::isURLAttribute. -static bool isAttributeToRemove(const QualifiedName& name, const AtomicString& value) +bool Element::isJavaScriptURLAttribute(const Attribute& attribute) const { - return (name.localName() == hrefAttr.localName() || name.localName() == nohrefAttr.localName() - || name == srcAttr || name == actionAttr || name == formactionAttr) && protocolIsJavaScript(stripLeadingAndTrailingHTMLSpaces(value)); + return isURLAttribute(attribute) && protocolIsJavaScript(stripLeadingAndTrailingHTMLSpaces(attribute.value())); } -void Element::parserSetAttributes(const Vector<Attribute>& attributeVector, FragmentScriptingPermission scriptingPermission) +void Element::stripScriptingAttributes(Vector<Attribute>& attributeVector) const +{ + size_t destination = 0; + for (size_t source = 0; source < attributeVector.size(); ++source) { + if (isEventHandlerAttribute(attributeVector[source]) + || isJavaScriptURLAttribute(attributeVector[source]) + || isHTMLContentAttribute(attributeVector[source])) + continue; + + if (source != destination) + attributeVector[destination] = attributeVector[source]; + + ++destination; + } + attributeVector.shrink(destination); +} + +void Element::parserSetAttributes(const Vector<Attribute>& attributeVector) { ASSERT(!inDocument()); ASSERT(!parentNode()); - - ASSERT(!m_attributeData); + ASSERT(!m_elementData); if (attributeVector.isEmpty()) return; - Vector<Attribute> filteredAttributes = attributeVector; - - // If the element is created as result of a paste or drag-n-drop operation - // we want to remove all the script and event handlers. - if (scriptingPermission == DisallowScriptingContent) { - unsigned i = 0; - while (i < filteredAttributes.size()) { - Attribute& attribute = filteredAttributes[i]; - if (isEventHandlerAttribute(attribute.name())) { - filteredAttributes.remove(i); - continue; - } - - if (isAttributeToRemove(attribute.name(), attribute.value())) - attribute.setValue(emptyAtom); - i++; - } - } - - // When the document is in parsing state, we cache immutable ElementAttributeData objects with the - // input attribute vector as key. (This cache is held by Document.) - if (!document() || !document()->parsing()) - m_attributeData = ElementAttributeData::createImmutable(filteredAttributes); + if (document() && document()->sharedObjectPool()) + m_elementData = document()->sharedObjectPool()->cachedShareableElementDataWithAttributes(attributeVector); else - m_attributeData = document()->cachedImmutableAttributeData(filteredAttributes); + m_elementData = ShareableElementData::createWithAttributes(attributeVector); - // Iterate over the set of attributes we already have on the stack in case - // attributeChanged mutates m_attributeData. - // FIXME: Find a way so we don't have to do this. - for (unsigned i = 0; i < filteredAttributes.size(); ++i) - attributeChanged(filteredAttributes[i].name(), filteredAttributes[i].value()); + // Use attributeVector instead of m_elementData because attributeChanged might modify m_elementData. + for (unsigned i = 0; i < attributeVector.size(); ++i) + attributeChangedFromParserOrByCloning(attributeVector[i].name(), attributeVector[i].value(), ModifiedDirectly); } bool Element::hasAttributes() const { - updateInvalidAttributes(); - return attributeData() && attributeData()->length(); + synchronizeAllAttributes(); + return elementData() && elementData()->length(); } bool Element::hasEquivalentAttributes(const Element* other) const { - const ElementAttributeData* attributeData = updatedAttributeData(); - const ElementAttributeData* otherAttributeData = other->updatedAttributeData(); - if (attributeData == otherAttributeData) + synchronizeAllAttributes(); + other->synchronizeAllAttributes(); + if (elementData() == other->elementData()) return true; - if (attributeData) - return attributeData->isEquivalent(otherAttributeData); - if (otherAttributeData) - return otherAttributeData->isEquivalent(attributeData); + if (elementData()) + return elementData()->isEquivalent(other->elementData()); + if (other->elementData()) + return other->elementData()->isEquivalent(elementData()); return true; } @@ -1047,63 +1270,89 @@ KURL Element::baseURI() const return KURL(parentBase, baseAttribute); } -const QualifiedName& Element::imageSourceAttributeName() const +const AtomicString& Element::imageSourceURL() const { - return srcAttr; + return getAttribute(srcAttr); } bool Element::rendererIsNeeded(const NodeRenderingContext& context) { - return (document()->documentElement() == this) || (context.style()->display() != NONE); + return context.style()->display() != NONE; } -RenderObject* Element::createRenderer(RenderArena* arena, RenderStyle* style) +RenderObject* Element::createRenderer(RenderArena*, 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->setAnimatableStyle(style); - return result; - } return RenderObject::createObject(this, style); } -bool Element::wasChangedSinceLastFormControlChangeEvent() const +bool Element::isDisabledFormControl() const { +#if ENABLE(DIALOG_ELEMENT) + // FIXME: disabled and inert are separate concepts in the spec, but now we treat them as the same. + // For example, an inert, non-disabled form control should not be grayed out. + if (isInert()) + return true; +#endif return false; } -void Element::setChangedSinceLastFormControlChangeEvent(bool) +#if ENABLE(DIALOG_ELEMENT) +bool Element::isInert() const { + Element* dialog = document()->activeModalDialog(); + return dialog && !containsIncludingShadowDOM(dialog) && !dialog->containsIncludingShadowDOM(this); } +#endif Node::InsertionNotificationRequest Element::insertedInto(ContainerNode* insertionPoint) { + bool wasInDocument = inDocument(); // need to do superclass processing first so inDocument() is true // by the time we reach updateId ContainerNode::insertedInto(insertionPoint); + ASSERT(!wasInDocument || inDocument()); #if ENABLE(FULLSCREEN_API) if (containsFullScreenElement() && parentElement() && !parentElement()->containsFullScreenElement()) setContainsFullScreenElementOnAncestorsCrossingFrameBoundaries(true); #endif - if (!insertionPoint->inDocument()) + if (Element* before = pseudoElement(BEFORE)) + before->insertedInto(insertionPoint); + + if (Element* after = pseudoElement(AFTER)) + after->insertedInto(insertionPoint); + + if (!insertionPoint->isInTreeScope()) return InsertionDone; + if (hasRareData()) + elementRareData()->clearClassListValueForQuirksMode(); + + TreeScope* newScope = insertionPoint->treeScope(); + HTMLDocument* newDocument = !wasInDocument && inDocument() && newScope->documentScope()->isHTMLDocument() ? toHTMLDocument(newScope->documentScope()) : 0; + if (newScope != treeScope()) + newScope = 0; + const AtomicString& idValue = getIdAttribute(); - if (!idValue.isNull()) - updateId(nullAtom, idValue); + if (!idValue.isNull()) { + if (newScope) + updateIdForTreeScope(newScope, nullAtom, idValue); + if (newDocument) + updateIdForDocument(newDocument, nullAtom, idValue, AlwaysUpdateHTMLDocumentNamedItemMaps); + } const AtomicString& nameValue = getNameAttribute(); - if (!nameValue.isNull()) - updateName(nullAtom, nameValue); + if (!nameValue.isNull()) { + if (newScope) + updateNameForTreeScope(newScope, nullAtom, nameValue); + if (newDocument) + updateNameForDocument(newDocument, nullAtom, nameValue); + } - if (hasTagName(labelTag)) { - TreeScope* scope = treeScope(); - if (scope->shouldCacheLabelsByForAttribute()) - updateLabel(scope, nullAtom, fastGetAttribute(forAttr)); + if (newScope && hasTagName(labelTag)) { + if (newScope->shouldCacheLabelsByForAttribute()) + updateLabel(newScope, nullAtom, fastGetAttribute(forAttr)); } return InsertionDone; @@ -1111,8 +1360,18 @@ Node::InsertionNotificationRequest Element::insertedInto(ContainerNode* insertio void Element::removedFrom(ContainerNode* insertionPoint) { +#if ENABLE(SVG) + bool wasInDocument = insertionPoint->document(); +#endif + + if (Element* before = pseudoElement(BEFORE)) + before->removedFrom(insertionPoint); + + if (Element* after = pseudoElement(AFTER)) + after->removedFrom(insertionPoint); + #if ENABLE(DIALOG_ELEMENT) - setIsInTopLayer(false); + document()->removeFromTopLayer(this); #endif #if ENABLE(FULLSCREEN_API) if (containsFullScreenElement()) @@ -1125,62 +1384,78 @@ void Element::removedFrom(ContainerNode* insertionPoint) setSavedLayerScrollOffset(IntSize()); - if (insertionPoint->inDocument()) { + if (insertionPoint->isInTreeScope()) { + TreeScope* oldScope = insertionPoint->treeScope(); + HTMLDocument* oldDocument = inDocument() && oldScope->documentScope()->isHTMLDocument() ? toHTMLDocument(oldScope->documentScope()) : 0; + if (oldScope != treeScope()) + oldScope = 0; + const AtomicString& idValue = getIdAttribute(); - if (!idValue.isNull() && inDocument()) - updateId(insertionPoint->treeScope(), idValue, nullAtom); + if (!idValue.isNull()) { + if (oldScope) + updateIdForTreeScope(oldScope, idValue, nullAtom); + if (oldDocument) + updateIdForDocument(oldDocument, idValue, nullAtom, AlwaysUpdateHTMLDocumentNamedItemMaps); + } const AtomicString& nameValue = getNameAttribute(); - if (!nameValue.isNull()) - updateName(nameValue, nullAtom); + if (!nameValue.isNull()) { + if (oldScope) + updateNameForTreeScope(oldScope, nameValue, nullAtom); + if (oldDocument) + updateNameForDocument(oldDocument, nameValue, nullAtom); + } - if (hasTagName(labelTag)) { - TreeScope* treeScope = insertionPoint->treeScope(); - if (treeScope->shouldCacheLabelsByForAttribute()) - updateLabel(treeScope, fastGetAttribute(forAttr), nullAtom); + if (oldScope && hasTagName(labelTag)) { + if (oldScope->shouldCacheLabelsByForAttribute()) + updateLabel(oldScope, fastGetAttribute(forAttr), nullAtom); } } ContainerNode::removedFrom(insertionPoint); +#if ENABLE(SVG) + if (wasInDocument && hasPendingResources()) + document()->accessSVGExtensions()->removeElementFromPendingResources(this); +#endif } -void Element::createRendererIfNeeded() +void Element::createRendererIfNeeded(const AttachContext& context) { - NodeRenderingContext(this).createRendererForElementIfNeeded(); + NodeRenderingContext(this, context).createRendererForElementIfNeeded(); } -void Element::attach() +void Element::attach(const AttachContext& context) { - suspendPostAttachCallbacks(); - { - WidgetHierarchyUpdatesSuspensionScope suspendWidgetHierarchyUpdates; - createRendererIfNeeded(); + PostAttachCallbackDisabler callbackDisabler(this); + StyleResolverParentPusher parentPusher(this); + WidgetHierarchyUpdatesSuspensionScope suspendWidgetHierarchyUpdates; - StyleResolverParentPusher parentPusher(this); + createRendererIfNeeded(context); - if (parentElement() && parentElement()->isInCanvasSubtree()) - setIsInCanvasSubtree(true); + if (parentElement() && parentElement()->isInCanvasSubtree()) + setIsInCanvasSubtree(true); - // When a shadow root exists, it does the work of attaching the children. - if (ElementShadow* shadow = this->shadow()) { - parentPusher.push(); - shadow->attach(); - } else { - if (firstChild()) - parentPusher.push(); - } - ContainerNode::attach(); - - if (hasRareData()) { - ElementRareData* data = elementRareData(); - if (data->needsFocusAppearanceUpdateSoonAfterAttach()) { - if (isFocusable() && document()->focusedNode() == this) - document()->updateFocusAppearanceSoon(false /* don't restore selection */); - data->setNeedsFocusAppearanceUpdateSoonAfterAttach(false); - } + updatePseudoElement(BEFORE); + + // When a shadow root exists, it does the work of attaching the children. + if (ElementShadow* shadow = this->shadow()) { + parentPusher.push(); + shadow->attach(context); + } else if (firstChild()) + parentPusher.push(); + + ContainerNode::attach(context); + + updatePseudoElement(AFTER); + + if (hasRareData()) { + ElementRareData* data = elementRareData(); + if (data->needsFocusAppearanceUpdateSoonAfterAttach()) { + if (isFocusable() && document()->focusedElement() == this) + document()->updateFocusAppearanceSoon(false /* don't restore selection */); + data->setNeedsFocusAppearanceUpdateSoonAfterAttach(false); } } - resumePostAttachCallbacks(); } void Element::unregisterNamedFlowContentNode() @@ -1189,23 +1464,37 @@ void Element::unregisterNamedFlowContentNode() document()->renderView()->flowThreadController()->unregisterNamedFlowContentNode(this); } -void Element::detach() +void Element::detach(const AttachContext& context) { WidgetHierarchyUpdatesSuspensionScope suspendWidgetHierarchyUpdates; unregisterNamedFlowContentNode(); cancelFocusAppearanceUpdate(); if (hasRareData()) { ElementRareData* data = elementRareData(); + data->setPseudoElement(BEFORE, 0); + data->setPseudoElement(AFTER, 0); data->setIsInCanvasSubtree(false); data->resetComputedStyle(); data->resetDynamicRestyleObservations(); + data->setIsInsideRegion(false); } - if (ElementShadow* shadow = this->shadow()) { - detachChildrenIfNeeded(); - shadow->detach(); + if (ElementShadow* shadow = this->shadow()) + shadow->detach(context); + + // Do not remove the element's hovered and active status + // if performing a reattach. + if (!context.performingReattach) { + if (isUserActionElement()) { + if (hovered()) + document()->hoveredElementDidDetach(this); + if (inActiveChain()) + document()->elementInActiveChainDidDetach(this); + document()->userActionElements().didDetach(this); + } } - ContainerNode::detach(); + + ContainerNode::detach(context); } bool Element::pseudoStyleCacheIsInvalid(const RenderStyle* currentStyle, RenderStyle* newStyle) @@ -1227,7 +1516,7 @@ bool Element::pseudoStyleCacheIsInvalid(const RenderStyle* currentStyle, RenderS if (pseudoId == FIRST_LINE || pseudoId == FIRST_LINE_INHERITED) newPseudoStyle = renderer()->uncachedFirstLineStyle(newStyle); else - newPseudoStyle = renderer()->getUncachedPseudoStyle(pseudoId, newStyle, newStyle); + newPseudoStyle = renderer()->getUncachedPseudoStyle(PseudoStyleRequest(pseudoId), newStyle, newStyle); if (!newPseudoStyle) return true; if (*newPseudoStyle != *pseudoStyleCache->at(i)) { @@ -1248,17 +1537,17 @@ bool Element::pseudoStyleCacheIsInvalid(const RenderStyle* currentStyle, RenderS PassRefPtr<RenderStyle> Element::styleForRenderer() { - if (hasCustomCallbacks()) { + if (hasCustomStyleCallbacks()) { if (RefPtr<RenderStyle> style = customStyleForRenderer()) return style.release(); } - return document()->styleResolver()->styleForElement(this); + return document()->ensureStyleResolver()->styleForElement(this); } void Element::recalcStyle(StyleChange change) { - if (hasCustomCallbacks()) { + if (hasCustomStyleCallbacks()) { if (!willRecalcStyle(change)) return; } @@ -1274,22 +1563,28 @@ void Element::recalcStyle(StyleChange change) elementRareData()->resetComputedStyle(); } if (hasParentStyle && (change >= Inherit || needsStyleRecalc())) { - RefPtr<RenderStyle> newStyle = styleForRenderer(); - StyleChange ch = Node::diff(currentStyle.get(), newStyle.get(), document()); - if (ch == Detach || !currentStyle) { - // FIXME: The style gets computed twice by calling attach. We could do better if we passed the style along. - reattach(); + StyleChange localChange = Detach; + RefPtr<RenderStyle> newStyle; + if (currentStyle) { + newStyle = styleForRenderer(); + localChange = Node::diff(currentStyle.get(), newStyle.get(), document()); + } + if (localChange == Detach) { + AttachContext reattachContext; + reattachContext.resolvedStyle = newStyle.get(); + reattach(reattachContext); + // attach recalculates the style for all children. No need to do it twice. clearNeedsStyleRecalc(); clearChildNeedsStyleRecalc(); - if (hasCustomCallbacks()) + if (hasCustomStyleCallbacks()) didRecalcStyle(change); return; } if (RenderObject* renderer = this->renderer()) { - if (ch != NoChange || pseudoStyleCacheIsInvalid(currentStyle.get(), newStyle.get()) || (change == Force && renderer->requiresForcedStyleRecalcPropagation()) || styleChangeType() == SyntheticStyleChange) + if (localChange != NoChange || pseudoStyleCacheIsInvalid(currentStyle.get(), newStyle.get()) || (change == Force && renderer->requiresForcedStyleRecalcPropagation()) || styleChangeType() == SyntheticStyleChange) renderer->setAnimatableStyle(newStyle.get()); else if (needsStyleRecalc()) { // Although no change occurred, we use the new style so that the cousin style sharing code won't get @@ -1300,9 +1595,10 @@ void Element::recalcStyle(StyleChange change) // If "rem" units are used anywhere in the document, and if the document element's font size changes, then go ahead and force font updating // all the way down the tree. This is simpler than having to maintain a cache of objects (and such font size changes should be rare anyway). - if (document()->styleSheetCollection()->usesRemUnits() && document()->documentElement() == this && ch != NoChange && currentStyle && newStyle && currentStyle->fontSize() != newStyle->fontSize()) { - // Cached RenderStyles may depend on the rem units. - document()->styleResolver()->invalidateMatchedPropertiesCache(); + if (document()->styleSheetCollection()->usesRemUnits() && document()->documentElement() == this && localChange != NoChange && currentStyle && newStyle && currentStyle->fontSize() != newStyle->fontSize()) { + // Cached RenderStyles may depend on the re units. + if (StyleResolver* styleResolver = document()->styleResolverIfExists()) + styleResolver->invalidateMatchedPropertiesCache(); change = Force; } @@ -1310,7 +1606,7 @@ void Element::recalcStyle(StyleChange change) if (styleChangeType() >= FullStyleChange) change = Force; else - change = ch; + change = localChange; } } StyleResolverParentPusher parentPusher(this); @@ -1323,6 +1619,8 @@ void Element::recalcStyle(StyleChange change) } } + updatePseudoElement(BEFORE, change); + // FIXME: This check is good enough for :hover + foo, but it is not good enough for :hover + foo + bar. // For now we will just worry about the common case, since it's a lot trickier to get the second case right // without doing way too much re-resolution. @@ -1335,7 +1633,7 @@ void Element::recalcStyle(StyleChange change) } if (!n->isElementNode()) continue; - Element* element = static_cast<Element*>(n); + Element* element = toElement(n); bool childRulesChanged = element->needsStyleRecalc() && element->styleChangeType() == FullStyleChange; if ((forceCheckOfNextElementSibling || forceCheckOfAnyElementSibling)) element->setNeedsStyleRecalc(); @@ -1347,40 +1645,65 @@ void Element::recalcStyle(StyleChange change) forceCheckOfAnyElementSibling = forceCheckOfAnyElementSibling || (childRulesChanged && hasIndirectAdjacentRules); } + updatePseudoElement(AFTER, change); + clearNeedsStyleRecalc(); clearChildNeedsStyleRecalc(); - if (hasCustomCallbacks()) + if (hasCustomStyleCallbacks()) didRecalcStyle(change); } ElementShadow* Element::shadow() const { - if (!hasRareData()) - return 0; - - return elementRareData()->m_shadow.get(); + return hasRareData() ? elementRareData()->shadow() : 0; } ElementShadow* Element::ensureShadow() { - if (ElementShadow* shadow = ensureElementRareData()->m_shadow.get()) - return shadow; + return ensureElementRareData()->ensureShadow(); +} - ElementRareData* data = elementRareData(); - data->m_shadow = adoptPtr(new ElementShadow()); - return data->m_shadow.get(); +void Element::didAffectSelector(AffectedSelectorMask) +{ + setNeedsStyleRecalc(); } PassRefPtr<ShadowRoot> Element::createShadowRoot(ExceptionCode& ec) { - return ShadowRoot::create(this, ec); + if (alwaysCreateUserAgentShadowRoot()) + ensureUserAgentShadowRoot(); + +#if ENABLE(SHADOW_DOM) + if (RuntimeEnabledFeatures::authorShadowDOMForAnyElementEnabled()) + return ensureShadow()->addShadowRoot(this, ShadowRoot::AuthorShadowRoot); +#endif + + // Since some elements recreates shadow root dynamically, multiple shadow + // subtrees won't work well in that element. Until they are fixed, we disable + // adding author shadow root for them. + if (!areAuthorShadowsAllowed()) { + ec = HIERARCHY_REQUEST_ERR; + return 0; + } + return ensureShadow()->addShadowRoot(this, ShadowRoot::AuthorShadowRoot); +} + +ShadowRoot* Element::authorShadowRoot() const +{ + ElementShadow* elementShadow = shadow(); + if (!elementShadow) + return 0; + ShadowRoot* shadowRoot = elementShadow->shadowRoot(); + if (shadowRoot->type() == ShadowRoot::AuthorShadowRoot) + return shadowRoot; + return 0; } ShadowRoot* Element::userAgentShadowRoot() const { if (ElementShadow* elementShadow = shadow()) { - if (ShadowRoot* shadowRoot = elementShadow->oldestShadowRoot()) { + if (ShadowRoot* shadowRoot = elementShadow->shadowRoot()) { ASSERT(shadowRoot->type() == ShadowRoot::UserAgentShadowRoot); return shadowRoot; } @@ -1389,6 +1712,16 @@ ShadowRoot* Element::userAgentShadowRoot() const return 0; } +ShadowRoot* Element::ensureUserAgentShadowRoot() +{ + ShadowRoot* shadowRoot = userAgentShadowRoot(); + if (!shadowRoot) { + shadowRoot = ensureShadow()->addShadowRoot(this, ShadowRoot::UserAgentShadowRoot); + didAddUserAgentShadowRoot(shadowRoot); + } + return shadowRoot; +} + const AtomicString& Element::shadowPseudoId() const { return pseudo(); @@ -1510,6 +1843,13 @@ void Element::childrenChanged(bool changedByParser, Node* beforeChange, Node* af shadow->invalidateDistribution(); } +void Element::removeAllEventListeners() +{ + ContainerNode::removeAllEventListeners(); + if (ElementShadow* shadow = this->shadow()) + shadow->removeAllEventListeners(); +} + void Element::beginParsingChildren() { clearIsParsingChildrenFinished(); @@ -1555,6 +1895,12 @@ void Element::formatForDebugger(char* buffer, unsigned length) const } #endif +const Vector<RefPtr<Attr> >& Element::attrNodeList() +{ + ASSERT(hasSyntheticAttrChildNodes()); + return *attrNodeListForElement(this); +} + PassRefPtr<Attr> Element::setAttributeNode(Attr* attrNode, ExceptionCode& ec) { if (!attrNode) { @@ -1573,20 +1919,21 @@ PassRefPtr<Attr> Element::setAttributeNode(Attr* attrNode, ExceptionCode& ec) return 0; } - updateInvalidAttributes(); - ElementAttributeData* attributeData = mutableAttributeData(); + synchronizeAllAttributes(); + UniqueElementData* elementData = ensureUniqueElementData(); - size_t index = attributeData->getAttributeItemIndex(attrNode->qualifiedName()); - if (index != notFound) { + unsigned index = elementData->getAttributeItemIndexForAttributeNode(attrNode); + if (index != ElementData::attributeNotFound) { if (oldAttrNode) - detachAttrNodeFromElementWithValue(oldAttrNode.get(), attributeData->attributeItem(index)->value()); + detachAttrNodeFromElementWithValue(oldAttrNode.get(), elementData->attributeItem(index)->value()); else - oldAttrNode = Attr::create(document(), attrNode->qualifiedName(), attributeData->attributeItem(index)->value()); + oldAttrNode = Attr::create(document(), attrNode->qualifiedName(), elementData->attributeItem(index)->value()); } setAttributeInternal(index, attrNode->qualifiedName(), attrNode->value(), NotInSynchronizationOfLazyAttribute); attrNode->attachToElement(this); + treeScope()->adoptIfNeeded(attrNode); ensureAttrNodeListForElement(this)->append(attrNode); return oldAttrNode.release(); @@ -1610,16 +1957,18 @@ PassRefPtr<Attr> Element::removeAttributeNode(Attr* attr, ExceptionCode& ec) ASSERT(document() == attr->document()); - const ElementAttributeData* attributeData = updatedAttributeData(); - ASSERT(attributeData); + synchronizeAttribute(attr->qualifiedName()); - size_t index = attributeData->getAttributeItemIndex(attr->qualifiedName()); - if (index == notFound) { + unsigned index = elementData()->getAttributeItemIndexForAttributeNode(attr); + if (index == ElementData::attributeNotFound) { ec = NOT_FOUND_ERR; return 0; } - return detachAttribute(index); + RefPtr<Attr> attrNode = attr; + detachAttrNodeFromElementWithValue(attr, elementData()->attributeItem(index)->value()); + removeAttributeInternal(index, NotInSynchronizationOfLazyAttribute); + return attrNode.release(); } bool Element::parseAttributeName(QualifiedName& out, const AtomicString& namespaceURI, const AtomicString& qualifiedName, ExceptionCode& ec) @@ -1648,14 +1997,14 @@ void Element::setAttributeNS(const AtomicString& namespaceURI, const AtomicStrin setAttribute(parsedName, value); } -void Element::removeAttributeInternal(size_t index, SynchronizationOfLazyAttribute inSynchronizationOfLazyAttribute) +void Element::removeAttributeInternal(unsigned index, SynchronizationOfLazyAttribute inSynchronizationOfLazyAttribute) { - ASSERT(index < attributeCount()); + ASSERT_WITH_SECURITY_IMPLICATION(index < attributeCount()); - ElementAttributeData* attributeData = mutableAttributeData(); + UniqueElementData* elementData = ensureUniqueElementData(); - QualifiedName name = attributeData->attributeItem(index)->name(); - AtomicString valueBeingRemoved = attributeData->attributeItem(index)->value(); + QualifiedName name = elementData->attributeItem(index)->name(); + AtomicString valueBeingRemoved = elementData->attributeItem(index)->value(); if (!inSynchronizationOfLazyAttribute) { if (!valueBeingRemoved.isNull()) @@ -1663,9 +2012,9 @@ void Element::removeAttributeInternal(size_t index, SynchronizationOfLazyAttribu } if (RefPtr<Attr> attrNode = attrIfExists(name)) - detachAttrNodeFromElementWithValue(attrNode.get(), attributeData->attributeItem(index)->value()); + detachAttrNodeFromElementWithValue(attrNode.get(), elementData->attributeItem(index)->value()); - attributeData->removeAttribute(index); + elementData->removeAttribute(index); if (!inSynchronizationOfLazyAttribute) didRemoveAttribute(name); @@ -1675,20 +2024,20 @@ void Element::addAttributeInternal(const QualifiedName& name, const AtomicString { if (!inSynchronizationOfLazyAttribute) willModifyAttribute(name, nullAtom, value); - mutableAttributeData()->addAttribute(Attribute(name, value)); + ensureUniqueElementData()->addAttribute(name, value); if (!inSynchronizationOfLazyAttribute) didAddAttribute(name, value); } void Element::removeAttribute(const AtomicString& name) { - if (!attributeData()) + if (!elementData()) return; AtomicString localName = shouldIgnoreAttributeCase(this) ? name.lower() : name; - size_t index = attributeData()->getAttributeItemIndex(localName, false); - if (index == notFound) { - if (UNLIKELY(localName == styleAttr) && attributeData()->m_styleAttributeIsDirty && isStyledElement()) + unsigned index = elementData()->getAttributeItemIndex(localName, false); + if (index == ElementData::attributeNotFound) { + if (UNLIKELY(localName == styleAttr) && elementData()->m_styleAttributeIsDirty && isStyledElement()) static_cast<StyledElement*>(this)->removeAllInlineStyleProperties(); return; } @@ -1701,12 +2050,12 @@ void Element::removeAttributeNS(const AtomicString& namespaceURI, const AtomicSt removeAttribute(QualifiedName(nullAtom, localName, namespaceURI)); } -PassRefPtr<Attr> Element::getAttributeNode(const AtomicString& name) +PassRefPtr<Attr> Element::getAttributeNode(const AtomicString& localName) { - const ElementAttributeData* attributeData = updatedAttributeData(); - if (!attributeData) + if (!elementData()) return 0; - const Attribute* attribute = attributeData->getAttributeItem(name, shouldIgnoreAttributeCase(this)); + synchronizeAttribute(localName); + const Attribute* attribute = elementData()->getAttributeItem(localName, shouldIgnoreAttributeCase(this)); if (!attribute) return 0; return ensureAttr(attribute->name()); @@ -1714,32 +2063,31 @@ PassRefPtr<Attr> Element::getAttributeNode(const AtomicString& name) PassRefPtr<Attr> Element::getAttributeNodeNS(const AtomicString& namespaceURI, const AtomicString& localName) { - const ElementAttributeData* attributeData = updatedAttributeData(); - if (!attributeData) + if (!elementData()) return 0; - const Attribute* attribute = attributeData->getAttributeItem(QualifiedName(nullAtom, localName, namespaceURI)); + QualifiedName qName(nullAtom, localName, namespaceURI); + synchronizeAttribute(qName); + const Attribute* attribute = elementData()->getAttributeItem(qName); if (!attribute) return 0; return ensureAttr(attribute->name()); } -bool Element::hasAttribute(const AtomicString& name) const +bool Element::hasAttribute(const AtomicString& localName) const { - if (!attributeData()) + if (!elementData()) return false; - - // This call to String::lower() seems to be required but - // there may be a way to remove it. - AtomicString localName = shouldIgnoreAttributeCase(this) ? name.lower() : name; - return updatedAttributeData()->getAttributeItem(localName, false); + synchronizeAttribute(localName); + return elementData()->getAttributeItem(shouldIgnoreAttributeCase(this) ? localName.lower() : localName, false); } bool Element::hasAttributeNS(const AtomicString& namespaceURI, const AtomicString& localName) const { - const ElementAttributeData* attributeData = updatedAttributeData(); - if (!attributeData) + if (!elementData()) return false; - return attributeData->getAttributeItem(QualifiedName(nullAtom, localName, namespaceURI)); + QualifiedName qName(nullAtom, localName, namespaceURI); + synchronizeAttribute(qName); + return elementData()->getAttributeItem(qName); } CSSStyleDeclaration *Element::style() @@ -1747,13 +2095,13 @@ CSSStyleDeclaration *Element::style() return 0; } -void Element::focus(bool restorePreviousSelection) +void Element::focus(bool restorePreviousSelection, FocusDirection direction) { if (!inDocument()) return; Document* doc = document(); - if (doc->focusedNode() == this) + if (doc->focusedElement() == this) return; // If the stylesheets have already been loaded we can reliably check isFocusable. @@ -1774,7 +2122,7 @@ void Element::focus(bool restorePreviousSelection) // If a focus event handler changes the focus to a different node it // does not make sense to continue and update appearence. protect = this; - if (!page->focusController()->setFocusedNode(this, doc->frame())) + if (!page->focusController()->setFocusedElement(this, doc->frame(), direction)) return; } @@ -1816,14 +2164,47 @@ void Element::blur() { cancelFocusAppearanceUpdate(); Document* doc = document(); - if (treeScope()->focusedNode() == this) { + if (treeScope()->focusedElement() == this) { if (doc->frame()) - doc->frame()->page()->focusController()->setFocusedNode(0, doc->frame()); + doc->frame()->page()->focusController()->setFocusedElement(0, doc->frame()); else - doc->setFocusedNode(0); + doc->setFocusedElement(0); } } +void Element::dispatchFocusInEvent(const AtomicString& eventType, PassRefPtr<Element> oldFocusedElement) +{ + ASSERT(!NoEventDispatchAssertion::isEventDispatchForbidden()); + ASSERT(eventType == eventNames().focusinEvent || eventType == eventNames().DOMFocusInEvent); + dispatchScopedEventDispatchMediator(FocusInEventDispatchMediator::create(FocusEvent::create(eventType, true, false, document()->defaultView(), 0, oldFocusedElement))); +} + +void Element::dispatchFocusOutEvent(const AtomicString& eventType, PassRefPtr<Element> newFocusedElement) +{ + ASSERT(!NoEventDispatchAssertion::isEventDispatchForbidden()); + ASSERT(eventType == eventNames().focusoutEvent || eventType == eventNames().DOMFocusOutEvent); + dispatchScopedEventDispatchMediator(FocusOutEventDispatchMediator::create(FocusEvent::create(eventType, true, false, document()->defaultView(), 0, newFocusedElement))); +} + +void Element::dispatchFocusEvent(PassRefPtr<Element> oldFocusedElement, FocusDirection) +{ + if (document()->page()) + document()->page()->chrome().client()->elementDidFocus(this); + + RefPtr<FocusEvent> event = FocusEvent::create(eventNames().focusEvent, false, false, document()->defaultView(), 0, oldFocusedElement); + EventDispatcher::dispatchEvent(this, FocusEventDispatchMediator::create(event.release())); +} + +void Element::dispatchBlurEvent(PassRefPtr<Element> newFocusedElement) +{ + if (document()->page()) + document()->page()->chrome().client()->elementDidBlur(this); + + RefPtr<FocusEvent> event = FocusEvent::create(eventNames().blurEvent, false, false, document()->defaultView(), 0, newFocusedElement); + EventDispatcher::dispatchEvent(this, BlurEventDispatchMediator::create(event.release())); +} + + String Element::innerText() { // We need to update layout, since plainText uses line boxes in the render tree. @@ -1862,18 +2243,21 @@ void Element::setPseudo(const AtomicString& value) LayoutSize Element::minimumSizeForResizing() const { - return hasRareData() ? elementRareData()->m_minimumSizeForResizing : defaultMinimumSizeForResizing(); + return hasRareData() ? elementRareData()->minimumSizeForResizing() : defaultMinimumSizeForResizing(); } void Element::setMinimumSizeForResizing(const LayoutSize& size) { - if (size == defaultMinimumSizeForResizing() && !hasRareData()) + if (!hasRareData() && size == defaultMinimumSizeForResizing()) return; - ensureElementRareData()->m_minimumSizeForResizing = size; + ensureElementRareData()->setMinimumSizeForResizing(size); } RenderStyle* Element::computedStyle(PseudoId pseudoElementSpecifier) { + if (PseudoElement* element = pseudoElement(pseudoElementSpecifier)) + return element->computedStyle(); + // FIXME: Find and use the renderer from the pseudo element instead of the actual element so that the 'length' // properties, which are only known by the renderer because it did the layout, will be correct and so that the // values returned for the ":selection" pseudo-element will be correct. @@ -1891,9 +2275,9 @@ RenderStyle* Element::computedStyle(PseudoId pseudoElementSpecifier) return 0; ElementRareData* data = ensureElementRareData(); - if (!data->m_computedStyle) - data->m_computedStyle = document()->styleForElementIgnoringPendingStylesheets(this); - return pseudoElementSpecifier ? data->m_computedStyle->getCachedPseudoStyle(pseudoElementSpecifier) : data->m_computedStyle.get(); + if (!data->computedStyle()) + data->setComputedStyle(document()->styleForElementIgnoringPendingStylesheets(this)); + return pseudoElementSpecifier ? data->computedStyle()->getCachedPseudoStyle(pseudoElementSpecifier) : data->computedStyle(); } void Element::setStyleAffectedByEmpty() @@ -2036,6 +2420,29 @@ bool Element::isInCanvasSubtree() const return hasRareData() && elementRareData()->isInCanvasSubtree(); } +void Element::setIsInsideRegion(bool value) +{ + if (value == isInsideRegion()) + return; + + ensureElementRareData()->setIsInsideRegion(value); +} + +bool Element::isInsideRegion() const +{ + return hasRareData() ? elementRareData()->isInsideRegion() : false; +} + +void Element::setRegionOversetState(RegionOversetState state) +{ + ensureElementRareData()->setRegionOversetState(state); +} + +RegionOversetState Element::regionOversetState() const +{ + return hasRareData() ? elementRareData()->regionOversetState() : RegionUndefined; +} + AtomicString Element::computeInheritedLanguage() const { const Node* n = this; @@ -2043,16 +2450,16 @@ AtomicString Element::computeInheritedLanguage() const // The language property is inherited, so we iterate over the parents to find the first language. do { if (n->isElementNode()) { - if (const ElementAttributeData* attributeData = static_cast<const Element*>(n)->attributeData()) { + if (const ElementData* elementData = toElement(n)->elementData()) { // Spec: xml:lang takes precedence -- http://www.w3.org/TR/xhtml1/#C_7 - if (const Attribute* attribute = attributeData->getAttributeItem(XMLNames::langAttr)) + if (const Attribute* attribute = elementData->getAttributeItem(XMLNames::langAttr)) value = attribute->value(); - else if (const Attribute* attribute = attributeData->getAttributeItem(HTMLNames::langAttr)) + else if (const Attribute* attribute = elementData->getAttributeItem(HTMLNames::langAttr)) value = attribute->value(); } } else if (n->isDocumentNode()) { // checking the MIME content-language - value = static_cast<const Document*>(n)->contentLanguage(); + value = toDocument(n)->contentLanguage(); } n = n->parentNode(); @@ -2070,23 +2477,84 @@ void Element::cancelFocusAppearanceUpdate() { if (hasRareData()) elementRareData()->setNeedsFocusAppearanceUpdateSoonAfterAttach(false); - if (document()->focusedNode() == this) + if (document()->focusedElement() == this) document()->cancelFocusAppearanceUpdate(); } void Element::normalizeAttributes() { - updateInvalidAttributes(); - if (AttrNodeList* attrNodeList = attrNodeListForElement(this)) { - for (unsigned i = 0; i < attrNodeList->size(); ++i) - attrNodeList->at(i)->normalize(); + if (!hasAttributes()) + return; + for (unsigned i = 0; i < attributeCount(); ++i) { + if (RefPtr<Attr> attr = attrIfExists(attributeItem(i)->name())) + attr->normalize(); + } +} + +void Element::updatePseudoElement(PseudoId pseudoId, StyleChange change) +{ + PseudoElement* existing = pseudoElement(pseudoId); + if (existing) { + // PseudoElement styles hang off their parent element's style so if we needed + // a style recalc we should Force one on the pseudo. + existing->recalcStyle(needsStyleRecalc() ? Force : change); + + // Wait until our parent is not displayed or pseudoElementRendererIsNeeded + // is false, otherwise we could continously create and destroy PseudoElements + // when RenderObject::isChildAllowed on our parent returns false for the + // PseudoElement's renderer for each style recalc. + if (!renderer() || !pseudoElementRendererIsNeeded(renderer()->getCachedPseudoStyle(pseudoId))) + setPseudoElement(pseudoId, 0); + } else if (RefPtr<PseudoElement> element = createPseudoElementIfNeeded(pseudoId)) { + element->attach(); + setPseudoElement(pseudoId, element.release()); } } +PassRefPtr<PseudoElement> Element::createPseudoElementIfNeeded(PseudoId pseudoId) +{ + if (!document()->styleSheetCollection()->usesBeforeAfterRules()) + return 0; + + if (!renderer() || !renderer()->canHaveGeneratedChildren()) + return 0; + + if (isPseudoElement()) + return 0; + + if (!pseudoElementRendererIsNeeded(renderer()->getCachedPseudoStyle(pseudoId))) + return 0; + + return PseudoElement::create(this, pseudoId); +} + +bool Element::hasPseudoElements() const +{ + return hasRareData() && elementRareData()->hasPseudoElements(); +} + +PseudoElement* Element::pseudoElement(PseudoId pseudoId) const +{ + return hasRareData() ? elementRareData()->pseudoElement(pseudoId) : 0; +} + +void Element::setPseudoElement(PseudoId pseudoId, PassRefPtr<PseudoElement> element) +{ + ensureElementRareData()->setPseudoElement(pseudoId, element); + resetNeedsShadowTreeWalker(); +} + +RenderObject* Element::pseudoElementRenderer(PseudoId pseudoId) const +{ + if (PseudoElement* element = pseudoElement(pseudoId)) + return element->renderer(); + return 0; +} + // ElementTraversal API Element* Element::firstElementChild() const { - return WebCore::firstElementChild(this); + return ElementTraversal::firstWithin(this); } Element* Element::lastElementChild() const @@ -2094,7 +2562,7 @@ Element* Element::lastElementChild() const Node* n = lastChild(); while (n && !n->isElementNode()) n = n->previousSibling(); - return static_cast<Element*>(n); + return toElement(n); } unsigned Element::childElementCount() const @@ -2108,12 +2576,12 @@ unsigned Element::childElementCount() const return count; } -bool Element::shouldMatchReadOnlySelector() const +bool Element::matchesReadOnlyPseudoClass() const { return false; } -bool Element::shouldMatchReadWriteSelector() const +bool Element::matchesReadWritePseudoClass() const { return false; } @@ -2131,33 +2599,31 @@ bool Element::webkitMatchesSelector(const String& selector, ExceptionCode& ec) return selectorQuery->matches(this); } -DOMTokenList* Element::classList() +bool Element::shouldAppearIndeterminate() const { - ElementRareData* data = ensureElementRareData(); - if (!data->m_classList) - data->m_classList = ClassList::create(this); - return data->m_classList.get(); + return false; } -DOMTokenList* Element::optionalClassList() const +DOMTokenList* Element::classList() { - if (!hasRareData()) - return 0; - return elementRareData()->m_classList.get(); + ElementRareData* data = ensureElementRareData(); + if (!data->classList()) + data->setClassList(ClassList::create(this)); + return data->classList(); } DOMStringMap* Element::dataset() { ElementRareData* data = ensureElementRareData(); - if (!data->m_datasetDOMStringMap) - data->m_datasetDOMStringMap = DatasetDOMStringMap::create(this); - return data->m_datasetDOMStringMap.get(); + if (!data->dataset()) + data->setDataset(DatasetDOMStringMap::create(this)); + return data->dataset(); } KURL Element::getURLAttribute(const QualifiedName& name) const { #if !ASSERT_DISABLED - if (attributeData()) { + if (elementData()) { if (const Attribute* attribute = getAttributeItem(name)) ASSERT(isURLAttribute(*attribute)); } @@ -2168,7 +2634,7 @@ KURL Element::getURLAttribute(const QualifiedName& name) const KURL Element::getNonEmptyURLAttribute(const QualifiedName& name) const { #if !ASSERT_DISABLED - if (attributeData()) { + if (elementData()) { if (const Attribute* attribute = getAttributeItem(name)) ASSERT(isURLAttribute(*attribute)); } @@ -2201,6 +2667,19 @@ void Element::setUnsignedIntegralAttribute(const QualifiedName& attributeName, u setAttribute(attributeName, String::number(value)); } +#if ENABLE(INDIE_UI) +void Element::setUIActions(const AtomicString& actions) +{ + setAttribute(uiactionsAttr, actions); +} + +const AtomicString& Element::UIActions() const +{ + return getAttribute(uiactionsAttr); +} +#endif + + #if ENABLE(SVG) bool Element::childShouldCreateRenderer(const NodeRenderingContext& childContext) const { @@ -2211,7 +2690,7 @@ bool Element::childShouldCreateRenderer(const NodeRenderingContext& childContext return ContainerNode::childShouldCreateRenderer(childContext); } #endif - + #if ENABLE(FULLSCREEN_API) void Element::webkitRequestFullscreen() { @@ -2258,14 +2737,11 @@ void Element::setIsInTopLayer(bool inTopLayer) { if (isInTopLayer() == inTopLayer) return; - ensureElementRareData()->setIsInTopLayer(inTopLayer); - if (inTopLayer) - document()->addToTopLayer(this); - else - document()->removeFromTopLayer(this); - setNeedsStyleRecalc(SyntheticStyleChange); + // We must ensure a reattach occurs so the renderer is inserted in the correct sibling order under RenderView according to its + // top layer position, or in its usual place if not in the top layer. + reattachIfAttached(); } #endif @@ -2292,7 +2768,7 @@ SpellcheckAttributeState Element::spellcheckAttributeState() const bool Element::isSpellCheckingEnabled() const { - for (const Element* element = this; element; element = element->parentOrHostElement()) { + for (const Element* element = this; element; element = element->parentOrShadowHostElement()) { switch (element->spellcheckAttributeState()) { case SpellcheckAttributeTrue: return true; @@ -2306,19 +2782,6 @@ bool Element::isSpellCheckingEnabled() const return true; } -PassRefPtr<WebKitAnimationList> Element::webkitGetAnimations() const -{ - if (!renderer()) - return 0; - - AnimationController* animController = renderer()->animation(); - - if (!animController) - return 0; - - return animController->animationsForRenderer(renderer()); -} - RenderRegion* Element::renderRegion() const { if (renderer() && renderer()->isRenderRegion()) @@ -2329,6 +2792,24 @@ RenderRegion* Element::renderRegion() const #if ENABLE(CSS_REGIONS) +bool Element::shouldMoveToFlowThread(RenderStyle* styleToUse) const +{ + ASSERT(styleToUse); + +#if ENABLE(FULLSCREEN_API) + if (document()->webkitIsFullScreen() && document()->webkitCurrentFullScreenElement() == this) + return false; +#endif + + if (isInShadowTree()) + return false; + + if (styleToUse->flowThread().isEmpty()) + return false; + + return !isRegisteredWithNamedFlow(); +} + const AtomicString& Element::webkitRegionOverset() const { document()->updateLayoutIgnorePendingStylesheets(); @@ -2337,20 +2818,20 @@ const AtomicString& Element::webkitRegionOverset() const if (!document()->cssRegionsEnabled() || !renderRegion()) return undefinedState; - switch (renderRegion()->regionState()) { - case RenderRegion::RegionFit: { + switch (renderRegion()->regionOversetState()) { + case RegionFit: { DEFINE_STATIC_LOCAL(AtomicString, fitState, ("fit", AtomicString::ConstructFromLiteral)); return fitState; } - case RenderRegion::RegionEmpty: { + case RegionEmpty: { DEFINE_STATIC_LOCAL(AtomicString, emptyState, ("empty", AtomicString::ConstructFromLiteral)); return emptyState; } - case RenderRegion::RegionOverset: { + case RegionOverset: { DEFINE_STATIC_LOCAL(AtomicString, overflowState, ("overset", AtomicString::ConstructFromLiteral)); return overflowState; } - case RenderRegion::RegionUndefined: + case RegionUndefined: return undefinedState; } @@ -2382,7 +2863,7 @@ bool Element::fastAttributeLookupAllowed(const QualifiedName& name) const #if ENABLE(SVG) if (isSVGElement()) - return !SVGElement::isAnimatableAttribute(name); + return !static_cast<const SVGElement*>(this)->isAnimatableAttribute(name); #endif return true; @@ -2392,10 +2873,112 @@ bool Element::fastAttributeLookupAllowed(const QualifiedName& name) const #ifdef DUMP_NODE_STATISTICS bool Element::hasNamedNodeMap() const { - return hasRareData() && elementRareData()->m_attributeMap; + return hasRareData() && elementRareData()->attributeMap(); } #endif +inline void Element::updateName(const AtomicString& oldName, const AtomicString& newName) +{ + if (!isInTreeScope()) + return; + + if (oldName == newName) + return; + + updateNameForTreeScope(treeScope(), oldName, newName); + + if (!inDocument()) + return; + Document* htmlDocument = document(); + if (!htmlDocument->isHTMLDocument()) + return; + updateNameForDocument(toHTMLDocument(htmlDocument), oldName, newName); +} + +void Element::updateNameForTreeScope(TreeScope* scope, const AtomicString& oldName, const AtomicString& newName) +{ + ASSERT(isInTreeScope()); + ASSERT(oldName != newName); + + if (!oldName.isEmpty()) + scope->removeElementByName(oldName, this); + if (!newName.isEmpty()) + scope->addElementByName(newName, this); +} + +void Element::updateNameForDocument(HTMLDocument* document, const AtomicString& oldName, const AtomicString& newName) +{ + ASSERT(inDocument()); + ASSERT(oldName != newName); + + if (WindowNameCollection::nodeMatchesIfNameAttributeMatch(this)) { + const AtomicString& id = WindowNameCollection::nodeMatchesIfIdAttributeMatch(this) ? getIdAttribute() : nullAtom; + if (!oldName.isEmpty() && oldName != id) + document->windowNamedItemMap().remove(oldName.impl(), this); + if (!newName.isEmpty() && newName != id) + document->windowNamedItemMap().add(newName.impl(), this); + } + + if (DocumentNameCollection::nodeMatchesIfNameAttributeMatch(this)) { + const AtomicString& id = DocumentNameCollection::nodeMatchesIfIdAttributeMatch(this) ? getIdAttribute() : nullAtom; + if (!oldName.isEmpty() && oldName != id) + document->documentNamedItemMap().remove(oldName.impl(), this); + if (!newName.isEmpty() && newName != id) + document->documentNamedItemMap().add(newName.impl(), this); + } +} + +inline void Element::updateId(const AtomicString& oldId, const AtomicString& newId) +{ + if (!isInTreeScope()) + return; + + if (oldId == newId) + return; + + updateIdForTreeScope(treeScope(), oldId, newId); + + if (!inDocument()) + return; + Document* htmlDocument = document(); + if (!htmlDocument->isHTMLDocument()) + return; + updateIdForDocument(toHTMLDocument(htmlDocument), oldId, newId, UpdateHTMLDocumentNamedItemMapsOnlyIfDiffersFromNameAttribute); +} + +void Element::updateIdForTreeScope(TreeScope* scope, const AtomicString& oldId, const AtomicString& newId) +{ + ASSERT(isInTreeScope()); + ASSERT(oldId != newId); + + if (!oldId.isEmpty()) + scope->removeElementById(oldId, this); + if (!newId.isEmpty()) + scope->addElementById(newId, this); +} + +void Element::updateIdForDocument(HTMLDocument* document, const AtomicString& oldId, const AtomicString& newId, HTMLDocumentNamedItemMapsUpdatingCondition condition) +{ + ASSERT(inDocument()); + ASSERT(oldId != newId); + + if (WindowNameCollection::nodeMatchesIfIdAttributeMatch(this)) { + const AtomicString& name = condition == UpdateHTMLDocumentNamedItemMapsOnlyIfDiffersFromNameAttribute && WindowNameCollection::nodeMatchesIfNameAttributeMatch(this) ? getNameAttribute() : nullAtom; + if (!oldId.isEmpty() && oldId != name) + document->windowNamedItemMap().remove(oldId.impl(), this); + if (!newId.isEmpty() && newId != name) + document->windowNamedItemMap().add(newId.impl(), this); + } + + if (DocumentNameCollection::nodeMatchesIfIdAttributeMatch(this)) { + const AtomicString& name = condition == UpdateHTMLDocumentNamedItemMapsOnlyIfDiffersFromNameAttribute && DocumentNameCollection::nodeMatchesIfNameAttributeMatch(this) ? getNameAttribute() : nullAtom; + if (!oldId.isEmpty() && oldId != name) + document->documentNamedItemMap().remove(oldId.impl(), this); + if (!newId.isEmpty() && newId != name) + document->documentNamedItemMap().add(newId.impl(), this); + } +} + void Element::updateLabel(TreeScope* scope, const AtomicString& oldForAttributeValue, const AtomicString& newForAttributeValue) { ASSERT(hasTagName(labelTag)); @@ -2407,9 +2990,9 @@ void Element::updateLabel(TreeScope* scope, const AtomicString& oldForAttributeV return; if (!oldForAttributeValue.isEmpty()) - scope->removeLabel(oldForAttributeValue, static_cast<HTMLLabelElement*>(this)); + scope->removeLabel(oldForAttributeValue, toHTMLLabelElement(this)); if (!newForAttributeValue.isEmpty()) - scope->addLabel(newForAttributeValue, static_cast<HTMLLabelElement*>(this)); + scope->addLabel(newForAttributeValue, toHTMLLabelElement(this)); } void Element::willModifyAttribute(const QualifiedName& name, const AtomicString& oldValue, const AtomicString& newValue) @@ -2424,10 +3007,13 @@ void Element::willModifyAttribute(const QualifiedName& name, const AtomicString& updateLabel(scope, oldValue, newValue); } -#if ENABLE(MUTATION_OBSERVERS) + if (oldValue != newValue) { + if (attached() && document()->styleResolverIfExists() && document()->styleResolverIfExists()->hasSelectorForAttribute(name.localName())) + setNeedsStyleRecalc(); + } + if (OwnPtr<MutationObserverInterestGroup> recipients = MutationObserverInterestGroup::createForAttributesMutation(this, name)) recipients->enqueueMutationRecord(MutationRecord::createAttributes(this, name, oldValue)); -#endif #if ENABLE(INSPECTOR) InspectorInstrumentation::willModifyDOMAttr(document(), this, oldValue, newValue); @@ -2455,31 +3041,6 @@ void Element::didRemoveAttribute(const QualifiedName& name) dispatchSubtreeModifiedEvent(); } - -void Element::updateNamedItemRegistration(const AtomicString& oldName, const AtomicString& newName) -{ - if (!document()->isHTMLDocument()) - return; - - if (!oldName.isEmpty()) - static_cast<HTMLDocument*>(document())->removeNamedItem(oldName); - - if (!newName.isEmpty()) - static_cast<HTMLDocument*>(document())->addNamedItem(newName); -} - -void Element::updateExtraNamedItemRegistration(const AtomicString& oldId, const AtomicString& newId) -{ - if (!document()->isHTMLDocument()) - return; - - if (!oldId.isEmpty()) - static_cast<HTMLDocument*>(document())->removeExtraNamedItem(oldId); - - if (!newId.isEmpty()) - static_cast<HTMLDocument*>(document())->addExtraNamedItem(newId); -} - PassRefPtr<HTMLCollection> Element::ensureCachedHTMLCollection(CollectionType type) { if (HTMLCollection* collection = cachedHTMLCollection(type)) @@ -2510,14 +3071,14 @@ HTMLCollection* Element::cachedHTMLCollection(CollectionType type) IntSize Element::savedLayerScrollOffset() const { - return hasRareData() ? elementRareData()->m_savedLayerScrollOffset : IntSize(); + return hasRareData() ? elementRareData()->savedLayerScrollOffset() : IntSize(); } void Element::setSavedLayerScrollOffset(const IntSize& size) { if (size.isZero() && !hasRareData()) return; - ensureElementRareData()->m_savedLayerScrollOffset = size; + ensureElementRareData()->setSavedLayerScrollOffset(size); } PassRefPtr<Attr> Element::attrIfExists(const QualifiedName& name) @@ -2533,6 +3094,7 @@ PassRefPtr<Attr> Element::ensureAttr(const QualifiedName& name) RefPtr<Attr> attrNode = findAttrNodeInList(attrNodeList, name); if (!attrNode) { attrNode = Attr::create(this, name); + treeScope()->adoptIfNeeded(attrNode.get()); attrNodeList->append(attrNode); } return attrNode.release(); @@ -2571,19 +3133,19 @@ void Element::detachAllAttrNodesFromElement() bool Element::willRecalcStyle(StyleChange) { - ASSERT(hasCustomCallbacks()); + ASSERT(hasCustomStyleCallbacks()); return true; } void Element::didRecalcStyle(StyleChange) { - ASSERT(hasCustomCallbacks()); + ASSERT(hasCustomStyleCallbacks()); } PassRefPtr<RenderStyle> Element::customStyleForRenderer() { - ASSERT(hasCustomCallbacks()); + ASSERT(hasCustomStyleCallbacks()); return 0; } @@ -2592,12 +3154,16 @@ void Element::cloneAttributesFromElement(const Element& other) if (hasSyntheticAttrChildNodes()) detachAllAttrNodesFromElement(); - other.updateInvalidAttributes(); - if (!other.m_attributeData) { - m_attributeData.clear(); + other.synchronizeAllAttributes(); + if (!other.m_elementData) { + m_elementData.clear(); return; } + // We can't update window and document's named item maps since the presence of image and object elements depend on other attributes and children. + // Fortunately, those named item maps are only updated when this element is in the document, which should never be the case. + ASSERT(!inDocument()); + const AtomicString& oldID = getIdAttribute(); const AtomicString& newID = other.getIdAttribute(); @@ -2610,21 +3176,21 @@ void Element::cloneAttributesFromElement(const Element& other) if (!oldName.isNull() || !newName.isNull()) updateName(oldName, newName); - // If 'other' has a mutable ElementAttributeData, convert it to an immutable one so we can share it between both elements. + // If 'other' has a mutable ElementData, convert it to an immutable one so we can share it between both elements. // We can only do this if there is no CSSOM wrapper for other's inline style, and there are no presentation attributes. - if (other.m_attributeData->isMutable() - && !other.m_attributeData->presentationAttributeStyle() - && (!other.m_attributeData->inlineStyle() || !other.m_attributeData->inlineStyle()->hasCSSOMWrapper())) - const_cast<Element&>(other).m_attributeData = other.m_attributeData->makeImmutableCopy(); + if (other.m_elementData->isUnique() + && !other.m_elementData->presentationAttributeStyle() + && (!other.m_elementData->inlineStyle() || !other.m_elementData->inlineStyle()->hasCSSOMWrapper())) + const_cast<Element&>(other).m_elementData = static_cast<const UniqueElementData*>(other.m_elementData.get())->makeShareableCopy(); - if (!other.m_attributeData->isMutable()) - m_attributeData = other.m_attributeData; + if (!other.m_elementData->isUnique()) + m_elementData = other.m_elementData; else - m_attributeData = other.m_attributeData->makeMutableCopy(); + m_elementData = other.m_elementData->makeUniqueCopy(); - for (unsigned i = 0; i < m_attributeData->length(); ++i) { - const Attribute* attribute = const_cast<const ElementAttributeData*>(m_attributeData.get())->attributeItem(i); - attributeChanged(attribute->name(), attribute->value()); + for (unsigned i = 0; i < m_elementData->length(); ++i) { + const Attribute* attribute = const_cast<const ElementData*>(m_elementData.get())->attributeItem(i); + attributeChangedFromParserOrByCloning(attribute->name(), attribute->value(), ModifiedByCloning); } } @@ -2634,20 +3200,245 @@ void Element::cloneDataFromElement(const Element& other) copyNonAttributePropertiesFromElement(other); } -void Element::createMutableAttributeData() +void Element::createUniqueElementData() { - if (!m_attributeData) - m_attributeData = ElementAttributeData::create(); + if (!m_elementData) + m_elementData = UniqueElementData::create(); + else { + ASSERT(!m_elementData->isUnique()); + m_elementData = static_cast<ShareableElementData*>(m_elementData.get())->makeUniqueCopy(); + } +} + +#if ENABLE(SVG) +bool Element::hasPendingResources() const +{ + return hasRareData() && elementRareData()->hasPendingResources(); +} + +void Element::setHasPendingResources() +{ + ensureElementRareData()->setHasPendingResources(true); +} + +void Element::clearHasPendingResources() +{ + ensureElementRareData()->setHasPendingResources(false); +} +#endif + +void ElementData::deref() +{ + if (!derefBase()) + return; + + if (m_isUnique) + delete static_cast<UniqueElementData*>(this); else - m_attributeData = m_attributeData->makeMutableCopy(); + delete static_cast<ShareableElementData*>(this); +} + +ElementData::ElementData() + : m_isUnique(true) + , m_arraySize(0) + , m_hasNameAttribute(false) + , m_presentationAttributeStyleIsDirty(false) + , m_styleAttributeIsDirty(false) +#if ENABLE(SVG) + , m_animatedSVGAttributesAreDirty(false) +#endif +{ +} + +ElementData::ElementData(unsigned arraySize) + : m_isUnique(false) + , m_arraySize(arraySize) + , m_hasNameAttribute(false) + , m_presentationAttributeStyleIsDirty(false) + , m_styleAttributeIsDirty(false) +#if ENABLE(SVG) + , m_animatedSVGAttributesAreDirty(false) +#endif +{ +} + +struct SameSizeAsElementData : public RefCounted<SameSizeAsElementData> { + unsigned bitfield; + void* refPtrs[3]; +}; + +COMPILE_ASSERT(sizeof(ElementData) == sizeof(SameSizeAsElementData), element_attribute_data_should_stay_small); + +static size_t sizeForShareableElementDataWithAttributeCount(unsigned count) +{ + return sizeof(ShareableElementData) + sizeof(Attribute) * count; +} + +PassRefPtr<ShareableElementData> ShareableElementData::createWithAttributes(const Vector<Attribute>& attributes) +{ + void* slot = WTF::fastMalloc(sizeForShareableElementDataWithAttributeCount(attributes.size())); + return adoptRef(new (NotNull, slot) ShareableElementData(attributes)); +} + +PassRefPtr<UniqueElementData> UniqueElementData::create() +{ + return adoptRef(new UniqueElementData); +} + +ShareableElementData::ShareableElementData(const Vector<Attribute>& attributes) + : ElementData(attributes.size()) +{ + for (unsigned i = 0; i < m_arraySize; ++i) + new (NotNull, &m_attributeArray[i]) Attribute(attributes[i]); +} + +ShareableElementData::~ShareableElementData() +{ + for (unsigned i = 0; i < m_arraySize; ++i) + m_attributeArray[i].~Attribute(); +} + +ShareableElementData::ShareableElementData(const UniqueElementData& other) + : ElementData(other, false) +{ + ASSERT(!other.m_presentationAttributeStyle); + + if (other.m_inlineStyle) { + ASSERT(!other.m_inlineStyle->hasCSSOMWrapper()); + m_inlineStyle = other.m_inlineStyle->immutableCopyIfNeeded(); + } + + for (unsigned i = 0; i < m_arraySize; ++i) + new (NotNull, &m_attributeArray[i]) Attribute(other.m_attributeVector.at(i)); +} + +ElementData::ElementData(const ElementData& other, bool isUnique) + : m_isUnique(isUnique) + , m_arraySize(isUnique ? 0 : other.length()) + , m_hasNameAttribute(other.m_hasNameAttribute) + , m_presentationAttributeStyleIsDirty(other.m_presentationAttributeStyleIsDirty) + , m_styleAttributeIsDirty(other.m_styleAttributeIsDirty) +#if ENABLE(SVG) + , m_animatedSVGAttributesAreDirty(other.m_animatedSVGAttributesAreDirty) +#endif + , m_classNames(other.m_classNames) + , m_idForStyleResolution(other.m_idForStyleResolution) +{ + // NOTE: The inline style is copied by the subclass copy constructor since we don't know what to do with it here. +} + +UniqueElementData::UniqueElementData() +{ +} + +UniqueElementData::UniqueElementData(const UniqueElementData& other) + : ElementData(other, true) + , m_presentationAttributeStyle(other.m_presentationAttributeStyle) + , m_attributeVector(other.m_attributeVector) +{ + m_inlineStyle = other.m_inlineStyle ? other.m_inlineStyle->mutableCopy() : 0; +} + +UniqueElementData::UniqueElementData(const ShareableElementData& other) + : ElementData(other, true) +{ + // An ShareableElementData should never have a mutable inline StylePropertySet attached. + ASSERT(!other.m_inlineStyle || !other.m_inlineStyle->isMutable()); + m_inlineStyle = other.m_inlineStyle; + + m_attributeVector.reserveCapacity(other.length()); + for (unsigned i = 0; i < other.length(); ++i) + m_attributeVector.uncheckedAppend(other.m_attributeArray[i]); +} + +PassRefPtr<UniqueElementData> ElementData::makeUniqueCopy() const +{ + if (isUnique()) + return adoptRef(new UniqueElementData(static_cast<const UniqueElementData&>(*this))); + return adoptRef(new UniqueElementData(static_cast<const ShareableElementData&>(*this))); +} + +PassRefPtr<ShareableElementData> UniqueElementData::makeShareableCopy() const +{ + void* slot = WTF::fastMalloc(sizeForShareableElementDataWithAttributeCount(m_attributeVector.size())); + return adoptRef(new (NotNull, slot) ShareableElementData(*this)); +} + +void UniqueElementData::addAttribute(const QualifiedName& attributeName, const AtomicString& value) +{ + m_attributeVector.append(Attribute(attributeName, value)); +} + +void UniqueElementData::removeAttribute(unsigned index) +{ + ASSERT_WITH_SECURITY_IMPLICATION(index < length()); + m_attributeVector.remove(index); +} + +bool ElementData::isEquivalent(const ElementData* other) const +{ + if (!other) + return isEmpty(); + + unsigned len = length(); + if (len != other->length()) + return false; + + for (unsigned i = 0; i < len; i++) { + const Attribute* attribute = attributeItem(i); + const Attribute* otherAttr = other->getAttributeItem(attribute->name()); + if (!otherAttr || attribute->value() != otherAttr->value()) + return false; + } + + return true; +} + +unsigned ElementData::getAttributeItemIndexSlowCase(const AtomicString& name, bool shouldIgnoreAttributeCase) const +{ + // Continue to checking case-insensitively and/or full namespaced names if necessary: + for (unsigned i = 0; i < length(); ++i) { + const Attribute* attribute = attributeItem(i); + if (!attribute->name().hasPrefix()) { + if (shouldIgnoreAttributeCase && equalIgnoringCase(name, attribute->localName())) + return i; + } else { + // FIXME: Would be faster to do this comparison without calling toString, which + // generates a temporary string by concatenation. But this branch is only reached + // if the attribute name has a prefix, which is rare in HTML. + if (equalPossiblyIgnoringCase(name, attribute->name().toString(), shouldIgnoreAttributeCase)) + return i; + } + } + return attributeNotFound; +} + +unsigned ElementData::getAttributeItemIndexForAttributeNode(const Attr* attr) const +{ + ASSERT(attr); + const Attribute* attributes = attributeBase(); + unsigned count = length(); + for (unsigned i = 0; i < count; ++i) { + if (attributes[i].name() == attr->qualifiedName()) + return i; + } + return attributeNotFound; +} + +Attribute* UniqueElementData::getAttributeItem(const QualifiedName& name) +{ + unsigned count = length(); + for (unsigned i = 0; i < count; ++i) { + if (m_attributeVector.at(i).name().matches(name)) + return &m_attributeVector.at(i); + } + return 0; } -void Element::reportMemoryUsage(MemoryObjectInfo* memoryObjectInfo) const +Attribute* UniqueElementData::attributeItem(unsigned index) { - MemoryClassInfo info(memoryObjectInfo, this, WebCoreMemoryTypes::DOM); - ContainerNode::reportMemoryUsage(memoryObjectInfo); - info.addMember(m_tagName); - info.addMember(m_attributeData); + ASSERT_WITH_SECURITY_IMPLICATION(index < length()); + return &m_attributeVector.at(index); } } // namespace WebCore |
