Skip to content
Permalink
Browse files
Only resolve attribute-derived style once per shared ElementAttribute…
…Data.

<http://webkit.org/b/100990>

Reviewed by Antti Koivisto.

Track the serialization of the "style" attribute, and the dirtiness of the presentation attribute style
on ElementAttributeData instead of in Node flags.

This allows us to avoid duplicate work for ElementAttributeData that are shared between multiple elements,
since the state is no longer per-Element.

I've left the presentation attribute cache in there for now, since it covers two additional cases:

    - Elements with the same attributes in different order.
    - Elements with the same presentation attributes, but with differing non-presentation attributes.

It's likely that we're not gaining much from it anymore, but that's a topic for another patch.

* dom/Node.h:
* dom/ElementAttributeData.h:
(WebCore::ElementAttributeData::presentationAttributeStyle):
(WebCore::ElementAttributeData::setPresentationAttributeStyle):
(WebCore::ElementAttributeData::styleAttributeIsDirty):
(WebCore::ElementAttributeData::setStyleAttributeIsDirty):
(WebCore::ElementAttributeData::presentationAttributeStyleIsDirty):
(WebCore::ElementAttributeData::setPresentationAttributeStyleIsDirty):
(ElementAttributeData):
(WebCore::ElementAttributeData::ElementAttributeData):
* dom/Element.cpp:
(WebCore::Element::getAttribute):
* dom/Element.h:
(WebCore::Element::styleAttributeIsDirty):
(WebCore::Element::updateInvalidAttributes):
* dom/StyledElement.cpp:
(WebCore::StyledElement::updateStyleAttribute):
(WebCore::StyledElement::attributeChanged):
(WebCore::StyledElement::styleAttributeChanged):
(WebCore::StyledElement::inlineStyleChanged):
* dom/StyledElement.h:
(WebCore::StyledElement::invalidateStyleAttribute):

    Move the "attribute style dirty" and "style attribute valid" node flags to ElementAttributeData
    and change them to both use dirty semantics.

* dom/ElementAttributeData.cpp:
(WebCore::ElementAttributeData::cloneDataFrom):

    Share the presentation attribute style between cloned elements initially.

* dom/StyledElement.h:
(WebCore::StyledElement::presentationAttributeStyle):
* css/StyleResolver.cpp:
(WebCore::StyleResolver::sweepMatchedPropertiesCache):
(WebCore::StyleResolver::matchAllRules):
(WebCore::StyleResolver::canShareStyleWithElement):
* dom/ElementAttributeData.cpp:
(WebCore::MutableElementAttributeData::MutableElementAttributeData):
(WebCore::ElementAttributeData::reportMemoryUsage):
* inspector/InspectorCSSAgent.cpp:
(WebCore::InspectorCSSAgent::buildObjectForAttributesStyle):

    Renamed StyledElement::attributeStyle() to presentationAttributeStyle(). The old name was too
    easily confused with "style attribute".

* dom/StyledElement.cpp:
(WebCore::StyledElement::rebuildPresentationAttributeStyle):

    Renamed from updateAttributeStyle().

Canonical link: https://commits.webkit.org/119140@main
git-svn-id: https://svn.webkit.org/repository/webkit/trunk@133286 268f45cc-cd09-0410-ab3c-d52691b4dbfc
  • Loading branch information
Andreas Kling committed Nov 2, 2012
1 parent f67ef96 commit 82f45402faae2128ca19f6a7c406a65152f9989f
Showing 10 changed files with 152 additions and 62 deletions.
@@ -1,3 +1,74 @@
2012-11-02 Andreas Kling <kling@webkit.org>

Only resolve attribute-derived style once per shared ElementAttributeData.
<http://webkit.org/b/100990>

Reviewed by Antti Koivisto.

Track the serialization of the "style" attribute, and the dirtiness of the presentation attribute style
on ElementAttributeData instead of in Node flags.

This allows us to avoid duplicate work for ElementAttributeData that are shared between multiple elements,
since the state is no longer per-Element.

I've left the presentation attribute cache in there for now, since it covers two additional cases:

- Elements with the same attributes in different order.
- Elements with the same presentation attributes, but with differing non-presentation attributes.

It's likely that we're not gaining much from it anymore, but that's a topic for another patch.

* dom/Node.h:
* dom/ElementAttributeData.h:
(WebCore::ElementAttributeData::presentationAttributeStyle):
(WebCore::ElementAttributeData::setPresentationAttributeStyle):
(WebCore::ElementAttributeData::styleAttributeIsDirty):
(WebCore::ElementAttributeData::setStyleAttributeIsDirty):
(WebCore::ElementAttributeData::presentationAttributeStyleIsDirty):
(WebCore::ElementAttributeData::setPresentationAttributeStyleIsDirty):
(ElementAttributeData):
(WebCore::ElementAttributeData::ElementAttributeData):
* dom/Element.cpp:
(WebCore::Element::getAttribute):
* dom/Element.h:
(WebCore::Element::styleAttributeIsDirty):
(WebCore::Element::updateInvalidAttributes):
* dom/StyledElement.cpp:
(WebCore::StyledElement::updateStyleAttribute):
(WebCore::StyledElement::attributeChanged):
(WebCore::StyledElement::styleAttributeChanged):
(WebCore::StyledElement::inlineStyleChanged):
* dom/StyledElement.h:
(WebCore::StyledElement::invalidateStyleAttribute):

Move the "attribute style dirty" and "style attribute valid" node flags to ElementAttributeData
and change them to both use dirty semantics.

* dom/ElementAttributeData.cpp:
(WebCore::ElementAttributeData::cloneDataFrom):

Share the presentation attribute style between cloned elements initially.

* dom/StyledElement.h:
(WebCore::StyledElement::presentationAttributeStyle):
* css/StyleResolver.cpp:
(WebCore::StyleResolver::sweepMatchedPropertiesCache):
(WebCore::StyleResolver::matchAllRules):
(WebCore::StyleResolver::canShareStyleWithElement):
* dom/ElementAttributeData.cpp:
(WebCore::MutableElementAttributeData::MutableElementAttributeData):
(WebCore::ElementAttributeData::reportMemoryUsage):
* inspector/InspectorCSSAgent.cpp:
(WebCore::InspectorCSSAgent::buildObjectForAttributesStyle):

Renamed StyledElement::attributeStyle() to presentationAttributeStyle(). The old name was too
easily confused with "style attribute".

* dom/StyledElement.cpp:
(WebCore::StyledElement::rebuildPresentationAttributeStyle):

Renamed from updateAttributeStyle().

2012-11-01 Kent Tamura <tkent@chromium.org>

Introduce ENABLE_DATE_AND_TIME_INPUT_TYPES, and clarify usage of other related flags
@@ -478,7 +478,7 @@ StyleResolver::~StyleResolver()
void StyleResolver::sweepMatchedPropertiesCache(Timer<StyleResolver>*)
{
// Look for cache entries containing a style declaration with a single ref and remove them.
// This may happen when an element attribute mutation causes it to generate a new attributeStyle(),
// This may happen when an element attribute mutation causes it to generate a new presentationAttributeStyle(),
// potentially leaving this cache with the last ref on the old one.
Vector<unsigned, 16> toRemove;
MatchedPropertiesCache::iterator it = m_matchedPropertiesCache.begin();
@@ -927,7 +927,7 @@ void StyleResolver::matchAllRules(MatchResult& result, bool includeSMILPropertie

// Now check author rules, beginning first with presentational attributes mapped from HTML.
if (m_styledElement) {
addElementStyleProperties(result, m_styledElement->attributeStyle());
addElementStyleProperties(result, m_styledElement->presentationAttributeStyle());

// Now we check additional mapped declarations.
// Tables and table cells share an additional mapped rule that must be applied
@@ -1179,7 +1179,7 @@ bool StyleResolver::canShareStyleWithElement(StyledElement* element) const
if (element->isSVGElement() && static_cast<SVGElement*>(element)->animatedSMILStyleProperties())
return false;
#endif
if (!!element->attributeStyle() != !!m_styledElement->attributeStyle())
if (!!element->presentationAttributeStyle() != !!m_styledElement->presentationAttributeStyle())
return false;
const StylePropertySet* additionalAttributeStyleA = element->additionalAttributeStyle();
const StylePropertySet* additionalAttributeStyleB = m_styledElement->additionalAttributeStyle();
@@ -1270,7 +1270,7 @@ bool StyleResolver::canShareStyleWithElement(StyledElement* element) const
return false;
}

if (element->attributeStyle() && !attributeStylesEqual(element->attributeStyle(), m_styledElement->attributeStyle()))
if (element->presentationAttributeStyle() && !attributeStylesEqual(element->presentationAttributeStyle(), m_styledElement->presentationAttributeStyle()))
return false;

if (additionalAttributeStyleA && !attributeStylesEqual(additionalAttributeStyleA, additionalAttributeStyleB))
@@ -261,7 +261,7 @@ bool Element::hasAttribute(const QualifiedName& name) const

const AtomicString& Element::getAttribute(const QualifiedName& name) const
{
if (UNLIKELY(name == styleAttr) && !isStyleAttributeValid())
if (UNLIKELY(name == styleAttr) && styleAttributeIsDirty())
updateStyleAttribute();

#if ENABLE(SVG)
@@ -619,7 +619,7 @@ const AtomicString& Element::getAttribute(const AtomicString& name) const
bool ignoreCase = shouldIgnoreAttributeCase(this);

// Update the 'style' attribute if it's invalid and being requested:
if (!isStyleAttributeValid() && equalPossiblyIgnoringCase(name, styleAttr.localName(), ignoreCase))
if (styleAttributeIsDirty() && equalPossiblyIgnoringCase(name, styleAttr.localName(), ignoreCase))
updateStyleAttribute();

#if ENABLE(SVG)
@@ -3,7 +3,7 @@
* (C) 1999 Antti Koivisto (koivisto@kde.org)
* (C) 2001 Peter Kelly (pmk@post.com)
* (C) 2001 Dirk Mueller (mueller@kde.org)
* Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011 Apple Inc. All rights reserved.
* Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012 Apple Inc. All rights reserved.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
@@ -465,6 +465,8 @@ class Element : public ContainerNode {
// svgAttributeChanged (called when element.className.baseValue is set)
void classAttributeChanged(const AtomicString& newClassString);

bool styleAttributeIsDirty() const;

private:
// FIXME: Remove the need for Attr to call willModifyAttribute/didModifyAttribute.
friend class Attr;
@@ -728,9 +730,14 @@ inline Attribute* Element::getAttributeItem(const QualifiedName& name)
return mutableAttributeData()->getAttributeItem(name);
}

inline bool Element::styleAttributeIsDirty() const
{
return m_attributeData && m_attributeData->styleAttributeIsDirty();
}

inline void Element::updateInvalidAttributes() const
{
if (!isStyleAttributeValid())
if (styleAttributeIsDirty())
updateStyleAttribute();

#if ENABLE(SVG)
@@ -63,7 +63,7 @@ MutableElementAttributeData::MutableElementAttributeData(const ImmutableElementA
const ElementAttributeData& baseOther = static_cast<const ElementAttributeData&>(other);

m_inlineStyleDecl = baseOther.m_inlineStyleDecl;
m_attributeStyle = baseOther.m_attributeStyle;
m_presentationAttributeStyle = baseOther.m_presentationAttributeStyle;
m_classNames = baseOther.m_classNames;
m_idForStyleResolution = baseOther.m_idForStyleResolution;

@@ -277,7 +277,7 @@ void ElementAttributeData::reportMemoryUsage(MemoryObjectInfo* memoryObjectInfo)
size_t actualSize = m_isMutable ? sizeof(ElementAttributeData) : sizeForImmutableElementAttributeDataWithAttributeCount(m_arraySize);
MemoryClassInfo info(memoryObjectInfo, this, WebCoreMemoryTypes::DOM, actualSize);
info.addMember(m_inlineStyleDecl);
info.addMember(m_attributeStyle);
info.addMember(m_presentationAttributeStyle);
info.addMember(m_classNames);
info.addMember(m_idForStyleResolution);
if (m_isMutable)
@@ -341,10 +341,18 @@ void ElementAttributeData::cloneDataFrom(const ElementAttributeData& sourceData,
targetElement.attributeChanged(attribute.name(), attribute.value());
}

if (targetElement.isStyledElement() && sourceData.m_inlineStyleDecl) {
if (!targetElement.isStyledElement())
return;

m_styleAttributeIsDirty = sourceData.m_styleAttributeIsDirty;
m_presentationAttributeStyleIsDirty = sourceData.m_presentationAttributeStyleIsDirty;

// Clone the source element's inline style unless it's immutable.
if (sourceData.m_inlineStyleDecl)
m_inlineStyleDecl = sourceData.m_inlineStyleDecl->immutableCopyIfNeeded();
targetElement.setIsStyleAttributeValid(sourceElement.isStyleAttributeValid());
}

// Share the presentation attribute style (no need for cloning since it's not accessible via CSSOM.)
m_presentationAttributeStyle = sourceData.m_presentationAttributeStyle;
}

void ElementAttributeData::clearAttributes(Element* element)
@@ -63,8 +63,8 @@ class ElementAttributeData : public RefCounted<ElementAttributeData> {
void destroyInlineStyle(StyledElement*);
void detachCSSOMWrapperIfNeeded(StyledElement*);

const StylePropertySet* attributeStyle() const { return m_attributeStyle.get(); }
void setAttributeStyle(PassRefPtr<StylePropertySet> style) const { m_attributeStyle = style; }
const StylePropertySet* presentationAttributeStyle() const { return m_presentationAttributeStyle.get(); }
void setPresentationAttributeStyle(PassRefPtr<StylePropertySet> style) const { m_presentationAttributeStyle= style; }

size_t length() const;
bool isEmpty() const { return !length(); }
@@ -95,6 +95,11 @@ class ElementAttributeData : public RefCounted<ElementAttributeData> {
PassRefPtr<Attr> ensureAttr(Element*, const QualifiedName&) const;
void detachAttrObjectsFromElement(Element*) const;

bool styleAttributeIsDirty() const { return m_styleAttributeIsDirty; }
void setStyleAttributeIsDirty(bool f) const { m_styleAttributeIsDirty = f; }
bool presentationAttributeStyleIsDirty() const { return m_presentationAttributeStyleIsDirty; }
void setPresentationAttributeStyleIsDirty(bool f) const { m_presentationAttributeStyleIsDirty = f; }

void reportMemoryUsage(MemoryObjectInfo*) const;

bool isMutable() const { return m_isMutable; }
@@ -103,19 +108,25 @@ class ElementAttributeData : public RefCounted<ElementAttributeData> {
protected:
ElementAttributeData()
: m_isMutable(true)
, m_styleAttributeIsDirty(false)
, m_presentationAttributeStyleIsDirty(false)
, m_arraySize(0)
{ }

ElementAttributeData(unsigned arraySize)
: m_isMutable(false)
, m_styleAttributeIsDirty(false)
, m_presentationAttributeStyleIsDirty(false)
, m_arraySize(arraySize)
{ }

unsigned m_isMutable : 1;
mutable unsigned m_styleAttributeIsDirty : 1;
mutable unsigned m_presentationAttributeStyleIsDirty : 1;
unsigned m_arraySize : 31;

mutable RefPtr<StylePropertySet> m_inlineStyleDecl;
mutable RefPtr<StylePropertySet> m_attributeStyle;
mutable RefPtr<StylePropertySet> m_presentationAttributeStyle;
mutable SpaceSplitString m_classNames;
mutable AtomicString m_idForStyleResolution;

@@ -99,7 +99,7 @@ class PropertyNodeList;

typedef int ExceptionCode;

const int nodeStyleChangeShift = 20;
const int nodeStyleChangeShift = 19;

// SyntheticStyleChange means that we need to go through the entire style change logic even though
// no style property has actually changed. It is used to restructure the tree when, for instance,
@@ -307,7 +307,6 @@ class Node : public EventTarget, public ScriptWrappable, public TreeShared<Node,
virtual void notifyLoadedSheetAndAllCriticalSubresources(bool /* error loading subresource */) { }
virtual void startLoadingDynamicSheet() { ASSERT_NOT_REACHED(); }

bool attributeStyleDirty() const { return getFlag(AttributeStyleDirtyFlag); }
bool hasName() const { return getFlag(HasNameFlag); }
bool hasID() const;
bool hasClass() const;
@@ -323,9 +322,6 @@ class Node : public EventTarget, public ScriptWrappable, public TreeShared<Node,
bool childNeedsStyleRecalc() const { return getFlag(ChildNeedsStyleRecalcFlag); }
bool isLink() const { return getFlag(IsLinkFlag); }

void setAttributeStyleDirty() { setFlag(AttributeStyleDirtyFlag); }
void clearAttributeStyleDirty() { clearFlag(AttributeStyleDirtyFlag); }

void setHasName(bool f) { setFlag(f, HasNameFlag); }
void setChildNeedsStyleRecalc() { setFlag(ChildNeedsStyleRecalcFlag); }
void clearChildNeedsStyleRecalc() { clearFlag(ChildNeedsStyleRecalcFlag); }
@@ -694,34 +690,32 @@ class Node : public EventTarget, public ScriptWrappable, public TreeShared<Node,
// These bits are used by derived classes, pulled up here so they can
// be stored in the same memory word as the Node bits above.
IsParsingChildrenFinishedFlag = 1 << 15, // Element
IsStyleAttributeValidFlag = 1 << 16, // StyledElement
#if ENABLE(SVG)
AreSVGAttributesValidFlag = 1 << 17, // Element
IsSynchronizingSVGAttributesFlag = 1 << 18, // SVGElement
HasSVGRareDataFlag = 1 << 19, // SVGElement
AreSVGAttributesValidFlag = 1 << 16, // Element
IsSynchronizingSVGAttributesFlag = 1 << 17, // SVGElement
HasSVGRareDataFlag = 1 << 18, // SVGElement
#endif

StyleChangeMask = 1 << nodeStyleChangeShift | 1 << (nodeStyleChangeShift + 1),

SelfOrAncestorHasDirAutoFlag = 1 << 22,
SelfOrAncestorHasDirAutoFlag = 1 << 21,

HasNameFlag = 1 << 23,
HasNameFlag = 1 << 22,

AttributeStyleDirtyFlag = 1 << 24,

#if ENABLE(SVG)
DefaultNodeFlags = IsParsingChildrenFinishedFlag | IsStyleAttributeValidFlag | AreSVGAttributesValidFlag,
DefaultNodeFlags = IsParsingChildrenFinishedFlag | AreSVGAttributesValidFlag,
#else
DefaultNodeFlags = IsParsingChildrenFinishedFlag | IsStyleAttributeValidFlag,
DefaultNodeFlags = IsParsingChildrenFinishedFlag,
#endif
InNamedFlowFlag = 1 << 26,
HasAttrListFlag = 1 << 27,
HasCustomCallbacksFlag = 1 << 28,
HasScopedHTMLStyleChildFlag = 1 << 29,
HasEventTargetDataFlag = 1 << 30,
InNamedFlowFlag = 1 << 23,
HasAttrListFlag = 1 << 24,
HasCustomCallbacksFlag = 1 << 25,
HasScopedHTMLStyleChildFlag = 1 << 26,
HasEventTargetDataFlag = 1 << 27,
};

// 2 bits remaining
// 4 bits remaining

bool getFlag(NodeFlags mask) const { return m_nodeFlags & mask; }
void setFlag(bool f, NodeFlags mask) const { m_nodeFlags = (m_nodeFlags & ~mask) | (-(int32_t)f & mask); }
@@ -817,12 +811,6 @@ class Node : public EventTarget, public ScriptWrappable, public TreeShared<Node,
Node* m_next;
RenderObject* m_renderer;

public:
bool isStyleAttributeValid() const { return getFlag(IsStyleAttributeValidFlag); }
void setIsStyleAttributeValid(bool f) { setFlag(f, IsStyleAttributeValidFlag); }
void setIsStyleAttributeValid() const { setFlag(IsStyleAttributeValidFlag); }
void clearIsStyleAttributeValid() { clearFlag(IsStyleAttributeValidFlag); }

protected:
bool isParsingChildrenFinished() const { return getFlag(IsParsingChildrenFinishedFlag); }
void setIsParsingChildrenFinished() { setFlag(IsParsingChildrenFinishedFlag); }

0 comments on commit 82f4540

Please sign in to comment.