summaryrefslogtreecommitdiff
path: root/Source/WebCore/dom/Element.cpp
diff options
context:
space:
mode:
authorAllan Sandfeld Jensen <allan.jensen@digia.com>2013-09-13 12:51:20 +0200
committerThe Qt Project <gerrit-noreply@qt-project.org>2013-09-19 20:50:05 +0200
commitd441d6f39bb846989d95bcf5caf387b42414718d (patch)
treee367e64a75991c554930278175d403c072de6bb8 /Source/WebCore/dom/Element.cpp
parent0060b2994c07842f4c59de64b5e3e430525c4b90 (diff)
downloadqtwebkit-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.cpp1655
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