Merge from Chromium at DEPS revision r210036
This commit was generated by merge_to_master.py.
Change-Id: Ib2112ed87a48d7a6d9c0563ba71850716d1475ef
diff --git a/Source/core/dom/Element.cpp b/Source/core/dom/Element.cpp
index bf2c5fc..ab2f94d 100644
--- a/Source/core/dom/Element.cpp
+++ b/Source/core/dom/Element.cpp
@@ -26,13 +26,20 @@
#include "config.h"
#include "core/dom/Element.h"
+#include "CSSPropertyNames.h"
+#include "CSSValueKeywords.h"
#include "HTMLNames.h"
#include "SVGNames.h"
#include "XMLNames.h"
#include "core/accessibility/AXObjectCache.h"
+#include "core/css/CSSParser.h"
+#include "core/css/CSSStyleSheet.h"
+#include "core/css/CSSValuePool.h"
+#include "core/css/PropertySetCSSStyleDeclaration.h"
#include "core/css/StylePropertySet.h"
#include "core/css/resolver/StyleResolver.h"
#include "core/dom/Attr.h"
+#include "core/dom/Attribute.h"
#include "core/dom/ClientRect.h"
#include "core/dom/ClientRectList.h"
#include "core/dom/CustomElementRegistry.h"
@@ -48,6 +55,7 @@
#include "core/dom/NodeRenderStyle.h"
#include "core/dom/NodeRenderingContext.h"
#include "core/dom/PseudoElement.h"
+#include "core/dom/ScriptableDocumentParser.h"
#include "core/dom/SelectorQuery.h"
#include "core/dom/Text.h"
#include "core/dom/WebCoreMemoryInstrumentation.h"
@@ -66,6 +74,7 @@
#include "core/html/HTMLOptionsCollection.h"
#include "core/html/HTMLTableRowsCollection.h"
#include "core/html/parser/HTMLParserIdioms.h"
+#include "core/page/ContentSecurityPolicy.h"
#include "core/page/FocusController.h"
#include "core/page/Frame.h"
#include "core/page/FrameView.h"
@@ -78,8 +87,10 @@
#include "core/svg/SVGDocumentExtensions.h"
#include "core/svg/SVGElement.h"
#include "wtf/BitVector.h"
+#include "wtf/HashFunctions.h"
#include "wtf/MemoryInstrumentationVector.h"
#include "wtf/text/CString.h"
+#include "wtf/text/TextPosition.h"
namespace WebCore {
@@ -90,7 +101,7 @@
{
return e && e->document()->isHTMLDocument() && e->isHTMLElement();
}
-
+
class StyleResolverParentPusher {
public:
StyleResolverParentPusher(Element* parent)
@@ -187,6 +198,9 @@
}
#endif
+ if (PropertySetCSSStyleDeclaration* cssomWrapper = inlineStyleCSSOMWrapper())
+ cssomWrapper->clearParentElement();
+
if (hasRareData()) {
ElementRareData* data = elementRareData();
data->setPseudoElement(BEFORE, 0);
@@ -406,7 +420,7 @@
return;
if (elementData()->m_styleAttributeIsDirty) {
ASSERT(isStyledElement());
- static_cast<const StyledElement*>(this)->synchronizeStyleAttributeInternal();
+ synchronizeStyleAttributeInternal();
}
if (elementData()->m_animatedSVGAttributesAreDirty) {
ASSERT(isSVGElement());
@@ -420,7 +434,7 @@
return;
if (UNLIKELY(name == styleAttr && elementData()->m_styleAttributeIsDirty)) {
ASSERT(isStyledElement());
- static_cast<const StyledElement*>(this)->synchronizeStyleAttributeInternal();
+ synchronizeStyleAttributeInternal();
return;
}
if (UNLIKELY(elementData()->m_animatedSVGAttributesAreDirty)) {
@@ -437,7 +451,7 @@
return;
if (elementData()->m_styleAttributeIsDirty && equalPossiblyIgnoringCase(localName, styleAttr.localName(), shouldIgnoreAttributeCase(this))) {
ASSERT(isStyledElement());
- static_cast<const StyledElement*>(this)->synchronizeStyleAttributeInternal();
+ synchronizeStyleAttributeInternal();
return;
}
if (elementData()->m_animatedSVGAttributesAreDirty) {
@@ -457,7 +471,7 @@
return nullAtom;
}
-void Element::scrollIntoView(bool alignToTop)
+void Element::scrollIntoView(bool alignToTop)
{
document()->updateLayoutIgnorePendingStylesheets();
@@ -564,6 +578,13 @@
int Element::offsetWidth()
{
+ document()->updateStyleForNodeIfNeeded(this);
+
+ if (RenderBox* renderer = renderBox()) {
+ if (!renderer->requiresLayoutToDetermineWidth())
+ return adjustLayoutUnitForAbsoluteZoom(renderer->fixedOffsetWidth(), renderer).round();
+ }
+
document()->updateLayoutIgnorePendingStylesheets();
if (RenderBoxModelObject* renderer = renderBoxModelObject())
return adjustLayoutUnitForAbsoluteZoom(renderer->pixelSnappedOffsetWidth(), renderer).round();
@@ -626,7 +647,7 @@
return adjustForAbsoluteZoom(view->layoutWidth(), renderView);
}
}
-
+
if (RenderBox* renderer = renderBox())
return adjustLayoutUnitForAbsoluteZoom(renderer->pixelSnappedClientWidth(), renderer).round();
return 0;
@@ -638,7 +659,7 @@
// When in strict mode, clientHeight for the document element should return the height of the containing frame.
// When in quirks mode, clientHeight for the body element should return the height of the containing frame.
- bool inQuirksMode = document()->inQuirksMode();
+ bool inQuirksMode = document()->inQuirksMode();
if ((!inQuirksMode && document()->documentElement() == this) ||
(inQuirksMode && isHTMLElement() && document()->body() == this)) {
@@ -647,7 +668,7 @@
return adjustForAbsoluteZoom(view->layoutHeight(), renderView);
}
}
-
+
if (RenderBox* renderer = renderBox())
return adjustLayoutUnitForAbsoluteZoom(renderer->pixelSnappedClientHeight(), renderer).round();
return 0;
@@ -775,7 +796,7 @@
document()->adjustFloatRectForScrollAndAbsoluteZoom(result, renderer());
return ClientRect::create(result);
}
-
+
IntRect Element::screenRect() const
{
if (!renderer())
@@ -874,7 +895,7 @@
return false;
}
-void Element::attributeChanged(const QualifiedName& name, const AtomicString& newValue, AttributeModificationReason)
+void Element::attributeChanged(const QualifiedName& name, const AtomicString& newValue, AttributeModificationReason reason)
{
if (ElementShadow* parentElementShadow = shadowOfParentForDistribution(this)) {
if (shouldInvalidateDistributionWhenAttributeChanged(parentElementShadow, name, newValue))
@@ -889,6 +910,13 @@
bool testShouldInvalidateStyle = attached() && styleResolver && styleChangeType() < FullStyleChange;
bool shouldInvalidateStyle = false;
+ if (isStyledElement() && name == styleAttr) {
+ styleAttributeChanged(newValue, reason);
+ } else if (isStyledElement() && isPresentationAttribute(name)) {
+ elementData()->m_presentationAttributeStyleIsDirty = true;
+ setNeedsStyleRecalc(InlineStyleChange);
+ }
+
if (isIdAttributeName(name)) {
AtomicString oldId = elementData()->idForStyleResolution();
AtomicString newId = makeIdForStyleResolution(newValue, document()->inQuirksMode());
@@ -896,12 +924,13 @@
elementData()->setIdForStyleResolution(newId);
shouldInvalidateStyle = testShouldInvalidateStyle && checkNeedsStyleInvalidationForIdChange(oldId, newId, styleResolver);
}
- } else if (name == classAttr)
+ } else if (name == classAttr) {
classAttributeChanged(newValue);
- else if (name == HTMLNames::nameAttr)
+ } else if (name == HTMLNames::nameAttr) {
setHasName(!newValue.isNull());
- else if (name == HTMLNames::pseudoAttr)
+ } else if (name == HTMLNames::pseudoAttr) {
shouldInvalidateStyle |= testShouldInvalidateStyle && isInShadowTree();
+ }
invalidateNodeListCachesInAncestors(&name, this);
@@ -1168,7 +1197,7 @@
return context.style()->display() != NONE;
}
-RenderObject* Element::createRenderer(RenderArena*, RenderStyle* style)
+RenderObject* Element::createRenderer(RenderStyle* style)
{
return RenderObject::createObject(this, style);
}
@@ -1207,7 +1236,9 @@
bool Element::isInert() const
{
const Element* dialog = document()->activeModalDialog();
- return dialog && !containsIncludingShadowDOM(dialog) && !dialog->containsIncludingShadowDOM(this);
+ if (dialog && !containsIncludingShadowDOM(dialog) && !dialog->containsIncludingShadowDOM(this))
+ return true;
+ return document()->ownerElement() && document()->ownerElement()->isInert();
}
Node::InsertionNotificationRequest Element::insertedInto(ContainerNode* insertionPoint)
@@ -1312,7 +1343,7 @@
// When a shadow root exists, it does the work of attaching the children.
if (ElementShadow* shadow = this->shadow()) {
parentPusher.push();
- shadow->attach();
+ shadow->attach(context);
} else if (firstChild())
parentPusher.push();
@@ -1320,7 +1351,7 @@
createPseudoElementIfNeeded(AFTER);
- if (hasRareData()) {
+ if (hasRareData()) {
ElementRareData* data = elementRareData();
if (data->needsFocusAppearanceUpdateSoonAfterAttach()) {
if (isFocusable() && document()->focusedNode() == this)
@@ -1328,6 +1359,12 @@
data->setNeedsFocusAppearanceUpdateSoonAfterAttach(false);
}
}
+
+ // FIXME: It doesn't appear safe to call didRecalculateStyleForElement when
+ // not in a Document::recalcStyle. Since we're hopefully going to always
+ // lazyAttach in the future that problem should go away.
+ if (document()->inStyleRecalc())
+ InspectorInstrumentation::didRecalculateStyleForElement(this);
}
void Element::unregisterNamedFlowContentNode()
@@ -1348,12 +1385,10 @@
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);
ContainerNode::detach(context);
}
@@ -1455,8 +1490,10 @@
return;
}
+ InspectorInstrumentation::didRecalculateStyleForElement(this);
+
if (RenderObject* renderer = this->renderer()) {
- if (localChange != NoChange || pseudoStyleCacheIsInvalid(currentStyle.get(), newStyle.get()) || (change == Force && renderer->requiresForcedStyleRecalcPropagation()) || styleChangeType() == SyntheticStyleChange)
+ if (localChange != NoChange || pseudoStyleCacheIsInvalid(currentStyle.get(), newStyle.get()) || (change == Force && renderer->requiresForcedStyleRecalcPropagation()) || needsLayerUpdate())
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
@@ -1500,8 +1537,8 @@
if (n->isTextNode()) {
toText(n)->recalcTextStyle(change);
continue;
- }
- if (!n->isElementNode())
+ }
+ if (!n->isElementNode())
continue;
Element* element = toElement(n);
bool childRulesChanged = element->needsStyleRecalc() && element->styleChangeType() == FullStyleChange;
@@ -1520,10 +1557,9 @@
clearNeedsStyleRecalc();
clearChildNeedsStyleRecalc();
-
+
if (hasCustomStyleCallbacks())
didRecalcStyle(change);
- InspectorInstrumentation::didRecalculateStyleForElement(this);
}
ElementShadow* Element::shadow() const
@@ -1627,7 +1663,7 @@
{
// :empty selector.
checkForEmptyStyleChange(e, style);
-
+
if (!style || (e->needsStyleRecalc() && e->childrenAffectedByPositionalRules()))
return;
@@ -1638,18 +1674,18 @@
// Find our new first child.
Node* newFirstChild = 0;
for (newFirstChild = e->firstChild(); newFirstChild && !newFirstChild->isElementNode(); newFirstChild = newFirstChild->nextSibling()) {};
-
+
// Find the first element node following |afterChange|
Node* firstElementAfterInsertion = 0;
for (firstElementAfterInsertion = afterChange;
firstElementAfterInsertion && !firstElementAfterInsertion->isElementNode();
firstElementAfterInsertion = firstElementAfterInsertion->nextSibling()) {};
-
+
// This is the insert/append case.
if (newFirstChild != firstElementAfterInsertion && firstElementAfterInsertion && firstElementAfterInsertion->attached() &&
firstElementAfterInsertion->renderStyle() && firstElementAfterInsertion->renderStyle()->firstChildState())
firstElementAfterInsertion->setNeedsStyleRecalc();
-
+
// We also have to handle node removal.
if (childCountDelta < 0 && newFirstChild == firstElementAfterInsertion && newFirstChild && (!newFirstChild->renderStyle() || !newFirstChild->renderStyle()->firstChildState()))
newFirstChild->setNeedsStyleRecalc();
@@ -1661,17 +1697,17 @@
// Find our new last child.
Node* newLastChild = 0;
for (newLastChild = e->lastChild(); newLastChild && !newLastChild->isElementNode(); newLastChild = newLastChild->previousSibling()) {};
-
+
// Find the last element node going backwards from |beforeChange|
Node* lastElementBeforeInsertion = 0;
for (lastElementBeforeInsertion = beforeChange;
lastElementBeforeInsertion && !lastElementBeforeInsertion->isElementNode();
lastElementBeforeInsertion = lastElementBeforeInsertion->previousSibling()) {};
-
+
if (newLastChild != lastElementBeforeInsertion && lastElementBeforeInsertion && lastElementBeforeInsertion->attached() &&
lastElementBeforeInsertion->renderStyle() && lastElementBeforeInsertion->renderStyle()->lastChildState())
lastElementBeforeInsertion->setNeedsStyleRecalc();
-
+
// We also have to handle node removal. The parser callback case is similar to node removal as well in that we need to change the last child
// to match now.
if ((childCountDelta < 0 || finishedParsingCallback) && newLastChild == lastElementBeforeInsertion && newLastChild && (!newLastChild->renderStyle() || !newLastChild->renderStyle()->lastChildState()))
@@ -1803,6 +1839,7 @@
setAttributeInternal(index, attrNode->qualifiedName(), attrNode->value(), NotInSynchronizationOfLazyAttribute);
attrNode->attachToElement(this);
+ treeScope()->adoptIfNeeded(attrNode);
ensureAttrNodeListForElement(this)->append(attrNode);
return oldAttrNode.release();
@@ -1906,7 +1943,7 @@
size_t index = elementData()->getAttributeItemIndex(localName, false);
if (index == notFound) {
if (UNLIKELY(localName == styleAttr) && elementData()->m_styleAttributeIsDirty && isStyledElement())
- static_cast<StyledElement*>(this)->removeAllInlineStyleProperties();
+ removeAllInlineStyleProperties();
return;
}
@@ -1958,11 +1995,6 @@
return elementData()->getAttributeItem(qName);
}
-CSSStyleDeclaration *Element::style()
-{
- return 0;
-}
-
void Element::focus(bool restorePreviousSelection, FocusDirection direction)
{
if (!inDocument())
@@ -1974,7 +2006,7 @@
// If the stylesheets have already been loaded we can reliably check isFocusable.
// If not, we continue and set the focused node on the focus controller below so
- // that it can be updated soon after attach.
+ // that it can be updated soon after attach.
if (doc->haveStylesheetsLoaded()) {
doc->updateLayoutIgnorePendingStylesheets();
if (!isFocusable())
@@ -2001,7 +2033,7 @@
ensureElementRareData()->setNeedsFocusAppearanceUpdateSoonAfterAttach(true);
return;
}
-
+
cancelFocusAppearanceUpdate();
updateFocusAppearance(restorePreviousSelection);
}
@@ -2012,14 +2044,14 @@
Frame* frame = document()->frame();
if (!frame)
return;
-
+
// When focusing an editable element in an iframe, don't reset the selection if it already contains a selection.
if (this == frame->selection()->rootEditableElement())
return;
// FIXME: We should restore the previous selection if there is one.
VisibleSelection newSelection = VisibleSelection(firstPositionInOrBeforeNode(this), DOWNSTREAM);
-
+
if (frame->selection()->shouldChangeSelection(newSelection)) {
frame->selection()->setSelection(newSelection);
frame->selection()->revealSelection();
@@ -2061,6 +2093,46 @@
return innerText();
}
+String Element::textFromChildren()
+{
+ Text* firstTextNode = 0;
+ bool foundMultipleTextNodes = false;
+ unsigned totalLength = 0;
+
+ for (Node* child = firstChild(); child; child = child->nextSibling()) {
+ if (!child->isTextNode())
+ continue;
+ Text* text = toText(child);
+ if (!firstTextNode)
+ firstTextNode = text;
+ else
+ foundMultipleTextNodes = true;
+ unsigned length = text->data().length();
+ if (length > std::numeric_limits<unsigned>::max() - totalLength)
+ return emptyString();
+ totalLength += length;
+ }
+
+ if (!firstTextNode)
+ return emptyString();
+
+ if (firstTextNode && !foundMultipleTextNodes) {
+ firstTextNode->atomize();
+ return firstTextNode->data();
+ }
+
+ StringBuilder content;
+ content.reserveCapacity(totalLength);
+ for (Node* child = firstTextNode; child; child = child->nextSibling()) {
+ if (!child->isTextNode())
+ continue;
+ content.append(toText(child)->data());
+ }
+
+ ASSERT(content.length() == totalLength);
+ return content.toString();
+}
+
String Element::title() const
{
return String();
@@ -2260,6 +2332,29 @@
return isCustomElement() && document()->registry()->isUnresolved(this);
}
+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;
@@ -2548,6 +2643,22 @@
return 0;
}
+bool Element::shouldMoveToFlowThread(RenderStyle* styleToUse) const
+{
+ ASSERT(styleToUse);
+
+ if (FullscreenController::isActiveFullScreenElement(this))
+ return false;
+
+ if (isInShadowTree())
+ return false;
+
+ if (styleToUse->flowThread().isEmpty())
+ return false;
+
+ return !isRegisteredWithNamedFlow();
+}
+
const AtomicString& Element::webkitRegionOverset() const
{
document()->updateLayoutIgnorePendingStylesheets();
@@ -2556,20 +2667,20 @@
if (!RuntimeEnabledFeatures::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;
}
@@ -2752,17 +2863,17 @@
return ensureRareData()->ensureNodeLists()->addCacheWithAtomicName<HTMLCollection>(this, type);
}
-static void needsSyntheticStyleChangeCallback(Node* node)
+static void scheduleLayerUpdateCallback(Node* node)
{
- node->setNeedsStyleRecalc(SyntheticStyleChange);
+ node->setNeedsLayerUpdate();
}
-void Element::scheduleSyntheticStyleChange()
+void Element::scheduleLayerUpdate()
{
if (postAttachCallbacksAreSuspended())
- queuePostAttachCallback(needsSyntheticStyleChangeCallback, this);
+ queuePostAttachCallback(scheduleLayerUpdateCallback, this);
else
- setNeedsStyleRecalc(SyntheticStyleChange);
+ setNeedsLayerUpdate();
}
HTMLCollection* Element::cachedHTMLCollection(CollectionType type)
@@ -2934,6 +3045,345 @@
ensureElementRareData()->setHasPendingResources(false);
}
+struct PresentationAttributeCacheKey {
+ PresentationAttributeCacheKey() : tagName(0) { }
+ AtomicStringImpl* tagName;
+ // Only the values need refcounting.
+ Vector<pair<AtomicStringImpl*, AtomicString>, 3> attributesAndValues;
+};
+
+struct PresentationAttributeCacheEntry {
+ WTF_MAKE_FAST_ALLOCATED;
+public:
+ PresentationAttributeCacheKey key;
+ RefPtr<StylePropertySet> value;
+};
+
+typedef HashMap<unsigned, OwnPtr<PresentationAttributeCacheEntry>, AlreadyHashed> PresentationAttributeCache;
+
+static bool operator!=(const PresentationAttributeCacheKey& a, const PresentationAttributeCacheKey& b)
+{
+ if (a.tagName != b.tagName)
+ return true;
+ return a.attributesAndValues != b.attributesAndValues;
+}
+
+static PresentationAttributeCache& presentationAttributeCache()
+{
+ DEFINE_STATIC_LOCAL(PresentationAttributeCache, cache, ());
+ return cache;
+}
+
+class PresentationAttributeCacheCleaner {
+ WTF_MAKE_NONCOPYABLE(PresentationAttributeCacheCleaner); WTF_MAKE_FAST_ALLOCATED;
+public:
+ PresentationAttributeCacheCleaner()
+ : m_hitCount(0)
+ , m_cleanTimer(this, &PresentationAttributeCacheCleaner::cleanCache)
+ {
+ }
+
+ void didHitPresentationAttributeCache()
+ {
+ if (presentationAttributeCache().size() < minimumPresentationAttributeCacheSizeForCleaning)
+ return;
+
+ m_hitCount++;
+
+ if (!m_cleanTimer.isActive())
+ m_cleanTimer.startOneShot(presentationAttributeCacheCleanTimeInSeconds);
+ }
+
+private:
+ static const unsigned presentationAttributeCacheCleanTimeInSeconds = 60;
+ static const int minimumPresentationAttributeCacheSizeForCleaning = 100;
+ static const unsigned minimumPresentationAttributeCacheHitCountPerMinute = (100 * presentationAttributeCacheCleanTimeInSeconds) / 60;
+
+ void cleanCache(Timer<PresentationAttributeCacheCleaner>* timer)
+ {
+ ASSERT_UNUSED(timer, timer == &m_cleanTimer);
+ unsigned hitCount = m_hitCount;
+ m_hitCount = 0;
+ if (hitCount > minimumPresentationAttributeCacheHitCountPerMinute)
+ return;
+ presentationAttributeCache().clear();
+ }
+
+ unsigned m_hitCount;
+ Timer<PresentationAttributeCacheCleaner> m_cleanTimer;
+};
+
+static PresentationAttributeCacheCleaner& presentationAttributeCacheCleaner()
+{
+ DEFINE_STATIC_LOCAL(PresentationAttributeCacheCleaner, cleaner, ());
+ return cleaner;
+}
+
+void Element::synchronizeStyleAttributeInternal() const
+{
+ ASSERT(isStyledElement());
+ ASSERT(elementData());
+ ASSERT(elementData()->m_styleAttributeIsDirty);
+ elementData()->m_styleAttributeIsDirty = false;
+ if (const StylePropertySet* inlineStyle = this->inlineStyle())
+ const_cast<Element*>(this)->setSynchronizedLazyAttribute(styleAttr, inlineStyle->asText());
+}
+
+CSSStyleDeclaration* Element::style()
+{
+ if (!isStyledElement())
+ return 0;
+ return ensureMutableInlineStyle()->ensureInlineCSSStyleDeclaration(this);
+}
+
+MutableStylePropertySet* Element::ensureMutableInlineStyle()
+{
+ ASSERT(isStyledElement());
+ RefPtr<StylePropertySet>& inlineStyle = ensureUniqueElementData()->m_inlineStyle;
+ if (!inlineStyle)
+ inlineStyle = MutableStylePropertySet::create(strictToCSSParserMode(isHTMLElement() && !document()->inQuirksMode()));
+ else if (!inlineStyle->isMutable())
+ inlineStyle = inlineStyle->mutableCopy();
+ ASSERT(inlineStyle->isMutable());
+ return static_cast<MutableStylePropertySet*>(inlineStyle.get());
+}
+
+PropertySetCSSStyleDeclaration* Element::inlineStyleCSSOMWrapper()
+{
+ if (!inlineStyle() || !inlineStyle()->hasCSSOMWrapper())
+ return 0;
+ PropertySetCSSStyleDeclaration* cssomWrapper = ensureMutableInlineStyle()->cssStyleDeclaration();
+ ASSERT(cssomWrapper && cssomWrapper->parentElement() == this);
+ return cssomWrapper;
+}
+
+inline void Element::setInlineStyleFromString(const AtomicString& newStyleString)
+{
+ ASSERT(isStyledElement());
+ RefPtr<StylePropertySet>& inlineStyle = elementData()->m_inlineStyle;
+
+ // Avoid redundant work if we're using shared attribute data with already parsed inline style.
+ if (inlineStyle && !elementData()->isUnique())
+ return;
+
+ // We reconstruct the property set instead of mutating if there is no CSSOM wrapper.
+ // This makes wrapperless property sets immutable and so cacheable.
+ if (inlineStyle && !inlineStyle->isMutable())
+ inlineStyle.clear();
+
+ if (!inlineStyle) {
+ inlineStyle = CSSParser::parseInlineStyleDeclaration(newStyleString, this);
+ } else {
+ ASSERT(inlineStyle->isMutable());
+ static_pointer_cast<MutableStylePropertySet>(inlineStyle)->parseDeclaration(newStyleString, document()->elementSheet()->contents());
+ }
+}
+
+void Element::styleAttributeChanged(const AtomicString& newStyleString, AttributeModificationReason modificationReason)
+{
+ ASSERT(isStyledElement());
+ WTF::OrdinalNumber startLineNumber = WTF::OrdinalNumber::beforeFirst();
+ if (document() && document()->scriptableDocumentParser() && !document()->isInDocumentWrite())
+ startLineNumber = document()->scriptableDocumentParser()->lineNumber();
+
+ if (newStyleString.isNull()) {
+ if (PropertySetCSSStyleDeclaration* cssomWrapper = inlineStyleCSSOMWrapper())
+ cssomWrapper->clearParentElement();
+ ensureUniqueElementData()->m_inlineStyle.clear();
+ } else if (modificationReason == ModifiedByCloning || document()->contentSecurityPolicy()->allowInlineStyle(document()->url(), startLineNumber)) {
+ setInlineStyleFromString(newStyleString);
+ }
+
+ elementData()->m_styleAttributeIsDirty = false;
+
+ setNeedsStyleRecalc(InlineStyleChange);
+ InspectorInstrumentation::didInvalidateStyleAttr(document(), this);
+}
+
+void Element::inlineStyleChanged()
+{
+ ASSERT(isStyledElement());
+ setNeedsStyleRecalc(InlineStyleChange);
+ ASSERT(elementData());
+ elementData()->m_styleAttributeIsDirty = true;
+ InspectorInstrumentation::didInvalidateStyleAttr(document(), this);
+}
+
+bool Element::setInlineStyleProperty(CSSPropertyID propertyID, CSSValueID identifier, bool important)
+{
+ ASSERT(isStyledElement());
+ ensureMutableInlineStyle()->setProperty(propertyID, cssValuePool().createIdentifierValue(identifier), important);
+ inlineStyleChanged();
+ return true;
+}
+
+bool Element::setInlineStyleProperty(CSSPropertyID propertyID, CSSPropertyID identifier, bool important)
+{
+ ASSERT(isStyledElement());
+ ensureMutableInlineStyle()->setProperty(propertyID, cssValuePool().createIdentifierValue(identifier), important);
+ inlineStyleChanged();
+ return true;
+}
+
+bool Element::setInlineStyleProperty(CSSPropertyID propertyID, double value, CSSPrimitiveValue::UnitTypes unit, bool important)
+{
+ ASSERT(isStyledElement());
+ ensureMutableInlineStyle()->setProperty(propertyID, cssValuePool().createValue(value, unit), important);
+ inlineStyleChanged();
+ return true;
+}
+
+bool Element::setInlineStyleProperty(CSSPropertyID propertyID, const String& value, bool important)
+{
+ ASSERT(isStyledElement());
+ bool changes = ensureMutableInlineStyle()->setProperty(propertyID, value, important, document()->elementSheet()->contents());
+ if (changes)
+ inlineStyleChanged();
+ return changes;
+}
+
+bool Element::removeInlineStyleProperty(CSSPropertyID propertyID)
+{
+ ASSERT(isStyledElement());
+ if (!inlineStyle())
+ return false;
+ bool changes = ensureMutableInlineStyle()->removeProperty(propertyID);
+ if (changes)
+ inlineStyleChanged();
+ return changes;
+}
+
+void Element::removeAllInlineStyleProperties()
+{
+ ASSERT(isStyledElement());
+ if (!inlineStyle() || inlineStyle()->isEmpty())
+ return;
+ ensureMutableInlineStyle()->clear();
+ inlineStyleChanged();
+}
+
+void Element::addSubresourceAttributeURLs(ListHashSet<KURL>& urls) const
+{
+ ASSERT(isStyledElement());
+ if (const StylePropertySet* inlineStyle = elementData() ? elementData()->inlineStyle() : 0)
+ inlineStyle->addSubresourceStyleURLs(urls, document()->elementSheet()->contents());
+}
+
+static inline bool attributeNameSort(const pair<AtomicStringImpl*, AtomicString>& p1, const pair<AtomicStringImpl*, AtomicString>& p2)
+{
+ // Sort based on the attribute name pointers. It doesn't matter what the order is as long as it is always the same.
+ return p1.first < p2.first;
+}
+
+void Element::makePresentationAttributeCacheKey(PresentationAttributeCacheKey& result) const
+{
+ ASSERT(isStyledElement());
+ // FIXME: Enable for SVG.
+ if (namespaceURI() != xhtmlNamespaceURI)
+ return;
+ // Interpretation of the size attributes on <input> depends on the type attribute.
+ if (hasTagName(inputTag))
+ return;
+ unsigned size = attributeCount();
+ for (unsigned i = 0; i < size; ++i) {
+ const Attribute* attribute = attributeItem(i);
+ if (!isPresentationAttribute(attribute->name()))
+ continue;
+ if (!attribute->namespaceURI().isNull())
+ return;
+ // FIXME: Background URL may depend on the base URL and can't be shared. Disallow caching.
+ if (attribute->name() == backgroundAttr)
+ return;
+ result.attributesAndValues.append(std::make_pair(attribute->localName().impl(), attribute->value()));
+ }
+ if (result.attributesAndValues.isEmpty())
+ return;
+ // Attribute order doesn't matter. Sort for easy equality comparison.
+ std::sort(result.attributesAndValues.begin(), result.attributesAndValues.end(), attributeNameSort);
+ // The cache key is non-null when the tagName is set.
+ result.tagName = localName().impl();
+}
+
+static unsigned computePresentationAttributeCacheHash(const PresentationAttributeCacheKey& key)
+{
+ if (!key.tagName)
+ return 0;
+ ASSERT(key.attributesAndValues.size());
+ unsigned attributeHash = StringHasher::hashMemory(key.attributesAndValues.data(), key.attributesAndValues.size() * sizeof(key.attributesAndValues[0]));
+ return WTF::pairIntHash(key.tagName->existingHash(), attributeHash);
+}
+
+void Element::rebuildPresentationAttributeStyle()
+{
+ ASSERT(isStyledElement());
+ PresentationAttributeCacheKey cacheKey;
+ makePresentationAttributeCacheKey(cacheKey);
+
+ unsigned cacheHash = computePresentationAttributeCacheHash(cacheKey);
+
+ PresentationAttributeCache::iterator cacheIterator;
+ if (cacheHash) {
+ cacheIterator = presentationAttributeCache().add(cacheHash, nullptr).iterator;
+ if (cacheIterator->value && cacheIterator->value->key != cacheKey)
+ cacheHash = 0;
+ } else {
+ cacheIterator = presentationAttributeCache().end();
+ }
+
+ RefPtr<StylePropertySet> style;
+ if (cacheHash && cacheIterator->value) {
+ style = cacheIterator->value->value;
+ presentationAttributeCacheCleaner().didHitPresentationAttributeCache();
+ } else {
+ style = MutableStylePropertySet::create(isSVGElement() ? SVGAttributeMode : CSSQuirksMode);
+ unsigned size = attributeCount();
+ for (unsigned i = 0; i < size; ++i) {
+ const Attribute* attribute = attributeItem(i);
+ collectStyleForPresentationAttribute(attribute->name(), attribute->value(), static_cast<MutableStylePropertySet*>(style.get()));
+ }
+ }
+
+ // ShareableElementData doesn't store presentation attribute style, so make sure we have a UniqueElementData.
+ UniqueElementData* elementData = ensureUniqueElementData();
+
+ elementData->m_presentationAttributeStyleIsDirty = false;
+ elementData->m_presentationAttributeStyle = style->isEmpty() ? 0 : style;
+
+ if (!cacheHash || cacheIterator->value)
+ return;
+
+ OwnPtr<PresentationAttributeCacheEntry> newEntry = adoptPtr(new PresentationAttributeCacheEntry);
+ newEntry->key = cacheKey;
+ newEntry->value = style.release();
+
+ static const int presentationAttributeCacheMaximumSize = 4096;
+ if (presentationAttributeCache().size() > presentationAttributeCacheMaximumSize) {
+ // Start building from scratch if the cache ever gets big.
+ presentationAttributeCache().clear();
+ presentationAttributeCache().set(cacheHash, newEntry.release());
+ } else {
+ cacheIterator->value = newEntry.release();
+ }
+}
+
+void Element::addPropertyToPresentationAttributeStyle(MutableStylePropertySet* style, CSSPropertyID propertyID, CSSValueID identifier)
+{
+ ASSERT(isStyledElement());
+ style->setProperty(propertyID, cssValuePool().createIdentifierValue(identifier));
+}
+
+void Element::addPropertyToPresentationAttributeStyle(MutableStylePropertySet* style, CSSPropertyID propertyID, double value, CSSPrimitiveValue::UnitTypes unit)
+{
+ ASSERT(isStyledElement());
+ style->setProperty(propertyID, cssValuePool().createValue(value, unit));
+}
+
+void Element::addPropertyToPresentationAttributeStyle(MutableStylePropertySet* style, CSSPropertyID propertyID, const String& value)
+{
+ ASSERT(isStyledElement());
+ style->setProperty(propertyID, value, false, document()->elementSheet()->contents());
+}
+
void ElementData::deref()
{
if (!derefBase())