Merge from Chromium at DEPS revision 228962
This commit was generated by merge_to_master.py.
Change-Id: Ifa868e6d63fe87d2338d9388aa5ea81f746d485f
diff --git a/Source/core/dom/Element.cpp b/Source/core/dom/Element.cpp
index 7d79c45..8be89e5 100644
--- a/Source/core/dom/Element.cpp
+++ b/Source/core/dom/Element.cpp
@@ -44,17 +44,14 @@
#include "core/css/resolver/StyleResolver.h"
#include "core/dom/Attr.h"
#include "core/dom/Attribute.h"
+#include "core/dom/CSSSelectorWatch.h"
#include "core/dom/ClientRect.h"
#include "core/dom/ClientRectList.h"
-#include "core/dom/CustomElement.h"
-#include "core/dom/CustomElementRegistrationContext.h"
#include "core/dom/DatasetDOMStringMap.h"
#include "core/dom/Document.h"
#include "core/dom/DocumentSharedObjectPool.h"
#include "core/dom/ElementRareData.h"
-#include "core/events/EventDispatcher.h"
#include "core/dom/ExceptionCode.h"
-#include "core/events/FocusEvent.h"
#include "core/dom/FullscreenElementStack.h"
#include "core/dom/MutationObserverInterestGroup.h"
#include "core/dom/MutationRecord.h"
@@ -66,11 +63,16 @@
#include "core/dom/ScriptableDocumentParser.h"
#include "core/dom/SelectorQuery.h"
#include "core/dom/Text.h"
+#include "core/dom/WhitespaceChildList.h"
+#include "core/dom/custom/CustomElement.h"
+#include "core/dom/custom/CustomElementRegistrationContext.h"
#include "core/dom/shadow/InsertionPoint.h"
#include "core/dom/shadow/ShadowRoot.h"
#include "core/editing/FrameSelection.h"
#include "core/editing/TextIterator.h"
#include "core/editing/htmlediting.h"
+#include "core/events/EventDispatcher.h"
+#include "core/events/FocusEvent.h"
#include "core/html/ClassList.h"
#include "core/html/HTMLCollection.h"
#include "core/html/HTMLDocument.h"
@@ -81,10 +83,10 @@
#include "core/html/HTMLOptionsCollection.h"
#include "core/html/HTMLTableRowsCollection.h"
#include "core/html/parser/HTMLParserIdioms.h"
-#include "core/page/ContentSecurityPolicy.h"
+#include "core/frame/ContentSecurityPolicy.h"
#include "core/page/FocusController.h"
-#include "core/page/Frame.h"
-#include "core/page/FrameView.h"
+#include "core/frame/Frame.h"
+#include "core/frame/FrameView.h"
#include "core/page/Page.h"
#include "core/page/PointerLockController.h"
#include "core/rendering/FlowThreadController.h"
@@ -589,7 +591,7 @@
document().updateStyleForNodeIfNeeded(this);
if (RenderBox* renderer = renderBox()) {
- if (!renderer->requiresLayoutToDetermineWidth())
+ if (renderer->canDetermineWidthWithoutLayout())
return adjustLayoutUnitForAbsoluteZoom(renderer->fixedOffsetWidth(), renderer).round();
}
@@ -685,6 +687,17 @@
int Element::scrollLeft()
{
document().updateLayoutIgnorePendingStylesheets();
+
+ if (document().documentElement() == this) {
+ if (document().inQuirksMode())
+ return 0;
+
+ if (FrameView* view = document().view()) {
+ if (RenderView* renderView = document().renderView())
+ return adjustForAbsoluteZoom(view->scrollX(), renderView);
+ }
+ }
+
if (RenderBox* rend = renderBox())
return adjustForAbsoluteZoom(rend->scrollLeft(), rend);
return 0;
@@ -693,6 +706,17 @@
int Element::scrollTop()
{
document().updateLayoutIgnorePendingStylesheets();
+
+ if (document().documentElement() == this) {
+ if (document().inQuirksMode())
+ return 0;
+
+ if (FrameView* view = document().view()) {
+ if (RenderView* renderView = document().renderView())
+ return adjustForAbsoluteZoom(view->scrollY(), renderView);
+ }
+ }
+
if (RenderBox* rend = renderBox())
return adjustForAbsoluteZoom(rend->scrollTop(), rend);
return 0;
@@ -701,6 +725,27 @@
void Element::setScrollLeft(int newLeft)
{
document().updateLayoutIgnorePendingStylesheets();
+
+ if (document().documentElement() == this) {
+ if (document().inQuirksMode())
+ return;
+
+ Frame* frame = document().frame();
+ if (!frame)
+ return;
+ FrameView* view = frame->view();
+ if (!view)
+ return;
+
+ // WHATWG spec says [1]: "If the element is the root element invoke scroll()
+ // with x as first argument and zero as second". Blink intentionally matches
+ // other engine's behaviors here, instead, where the 'y' scroll position is
+ // preversed. See [2].
+ // [1] http://dev.w3.org/csswg/cssom-view/#dom-element-scrollleft
+ // [2] https://www.w3.org/Bugs/Public/show_bug.cgi?id=23448
+ view->setScrollPosition(IntPoint(static_cast<int>(newLeft * frame->pageZoomFactor()), view->scrollY()));
+ }
+
if (RenderBox* rend = renderBox())
rend->setScrollLeft(static_cast<int>(newLeft * rend->style()->effectiveZoom()));
}
@@ -708,6 +753,27 @@
void Element::setScrollTop(int newTop)
{
document().updateLayoutIgnorePendingStylesheets();
+
+ if (document().documentElement() == this) {
+ if (document().inQuirksMode())
+ return;
+
+ Frame* frame = document().frame();
+ if (!frame)
+ return;
+ FrameView* view = frame->view();
+ if (!view)
+ return;
+
+ // WHATWG spec says [1]: "If the element is the root element invoke scroll()
+ // with zero as first argument and y as second". Blink intentionally
+ // matches other engine's behaviors here, instead, where the 'x' scroll
+ // position is preversed. See [2].
+ // [1] http://dev.w3.org/csswg/cssom-view/#dom-element-scrolltop
+ // [2] https://www.w3.org/Bugs/Public/show_bug.cgi?id=23448
+ view->setScrollPosition(IntPoint(view->scrollX(), static_cast<int>(newTop * frame->pageZoomFactor())));
+ }
+
if (RenderBox* rend = renderBox())
rend->setScrollTop(static_cast<int>(newTop * rend->style()->effectiveZoom()));
}
@@ -917,7 +983,7 @@
document().incDOMTreeVersion();
StyleResolver* styleResolver = document().styleResolverIfExists();
- bool testShouldInvalidateStyle = attached() && styleResolver && styleChangeType() < SubtreeStyleChange;
+ bool testShouldInvalidateStyle = confusingAndOftenMisusedAttached() && styleResolver && styleChangeType() < SubtreeStyleChange;
bool shouldInvalidateStyle = false;
if (isStyledElement() && name == styleAttr) {
@@ -1032,7 +1098,7 @@
void Element::classAttributeChanged(const AtomicString& newClassString)
{
StyleResolver* styleResolver = document().styleResolverIfExists();
- bool testShouldInvalidateStyle = attached() && styleResolver && styleChangeType() < SubtreeStyleChange;
+ bool testShouldInvalidateStyle = confusingAndOftenMisusedAttached() && styleResolver && styleChangeType() < SubtreeStyleChange;
bool shouldInvalidateStyle = false;
if (classStringHasClassName(newClassString)) {
@@ -1238,7 +1304,7 @@
CustomElement::didEnterDocument(this, document());
TreeScope& scope = insertionPoint->treeScope();
- if (&scope != &treeScope())
+ if (scope != treeScope())
return InsertionDone;
const AtomicString& idValue = getIdAttribute();
@@ -1282,7 +1348,7 @@
setSavedLayerScrollOffset(IntSize());
- if (insertionPoint->isInTreeScope() && &treeScope() == &document()) {
+ if (insertionPoint->isInTreeScope() && treeScope() == document()) {
const AtomicString& idValue = getIdAttribute();
if (!idValue.isNull())
updateId(insertionPoint->treeScope(), idValue, nullAtom);
@@ -1319,7 +1385,7 @@
// We've already been through detach when doing a lazyAttach, but we might
// need to clear any state that's been added since then.
- if (hasRareData() && styleChangeType() == LazyAttachStyleChange) {
+ if (hasRareData() && styleChangeType() == NeedsReattachStyleChange) {
ElementRareData* data = elementRareData();
data->clearComputedStyle();
data->resetDynamicRestyleObservations();
@@ -1329,6 +1395,9 @@
NodeRenderingContext(this, context.resolvedStyle).createRendererForElementIfNeeded();
+ if (RenderStyle* style = renderStyle())
+ updateCallbackSelectors(0, style);
+
createPseudoElementIfNeeded(BEFORE);
// When a shadow root exists, it does the work of attaching the children.
@@ -1363,9 +1432,13 @@
void Element::detach(const AttachContext& context)
{
- WidgetHierarchyUpdatesSuspensionScope suspendWidgetHierarchyUpdates;
+ RenderWidget::UpdateSuspendScope suspendWidgetHierarchyUpdates;
unregisterNamedFlowContentNode();
cancelFocusAppearanceUpdate();
+ if (RenderStyle* style = renderStyle()) {
+ if (!style->callbackSelectors().isEmpty())
+ updateCallbackSelectors(style, 0);
+ }
if (hasRareData()) {
ElementRareData* data = elementRareData();
data->setPseudoElement(BEFORE, 0);
@@ -1446,18 +1519,21 @@
{
ASSERT(document().inStyleRecalc());
- if (hasCustomStyleCallbacks())
- willRecalcStyle(change);
-
if (hasRareData() && (change > NoChange || needsStyleRecalc())) {
ElementRareData* data = elementRareData();
data->resetStyleState();
data->clearComputedStyle();
}
- // Active InsertionPoints have no renderers so they never need to go through a recalc.
- if ((change >= Inherit || needsStyleRecalc()) && parentRenderStyle() && !isActiveInsertionPoint(this))
- change = recalcOwnStyle(change);
+ if ((change >= Inherit || needsStyleRecalc()) && parentRenderStyle()) {
+ if (hasCustomStyleCallbacks())
+ willRecalcStyle(change);
+ // Active InsertionPoints have no renderers so they never need to go through a recalc.
+ if (!isActiveInsertionPoint(this))
+ change = recalcOwnStyle(change);
+ if (hasCustomStyleCallbacks())
+ didRecalcStyle(change);
+ }
// If we reattached we don't need to recalc the style of our descendants anymore.
if (change < Reattach)
@@ -1466,12 +1542,21 @@
clearNeedsStyleRecalc();
clearChildNeedsStyleRecalc();
- if (hasCustomStyleCallbacks())
- didRecalcStyle(change);
-
return change == Reattach;
}
+static bool callbackSelectorsDiffer(RenderStyle* style1, RenderStyle* style2)
+{
+ const Vector<String> emptyVector;
+ const Vector<String>& callbackSelectors1 = style1 ? style1->callbackSelectors() : emptyVector;
+ const Vector<String>& callbackSelectors2 = style2 ? style2->callbackSelectors() : emptyVector;
+ if (callbackSelectors1.isEmpty() && callbackSelectors2.isEmpty()) {
+ // Help the inliner with this common case.
+ return false;
+ }
+ return callbackSelectors1 != callbackSelectors2;
+}
+
StyleRecalcChange Element::recalcOwnStyle(StyleRecalcChange change)
{
ASSERT(document().inStyleRecalc());
@@ -1490,6 +1575,9 @@
InspectorInstrumentation::didRecalculateStyleForElement(this);
+ if (localChange != NoChange && callbackSelectorsDiffer(oldStyle.get(), newStyle.get()))
+ updateCallbackSelectors(oldStyle.get(), newStyle.get());
+
if (RenderObject* renderer = this->renderer()) {
if (localChange != NoChange || pseudoStyleCacheIsInvalid(oldStyle.get(), newStyle.get()) || (change == Force && renderer->requiresForcedStyleRecalcPropagation()) || shouldNotifyRendererWithIdenticalStyles()) {
renderer->setAnimatableStyle(newStyle.get());
@@ -1537,40 +1625,47 @@
bool hasIndirectAdjacentRules = childrenAffectedByForwardPositionalRules();
bool forceCheckOfNextElementSibling = false;
bool forceCheckOfAnyElementSibling = false;
- bool forceReattachOfAnyWhitespaceSibling = false;
- for (Node* child = firstChild(); child; child = child->nextSibling()) {
- bool didReattach = false;
-
- if (child->renderer())
- forceReattachOfAnyWhitespaceSibling = false;
-
- if (child->isTextNode()) {
- if (forceReattachOfAnyWhitespaceSibling && toText(child)->containsOnlyWhitespace())
- child->reattach();
- else
- didReattach = toText(child)->recalcTextStyle(change);
- } else if (child->isElementNode()) {
+ if (hasDirectAdjacentRules || hasIndirectAdjacentRules) {
+ for (Node* child = firstChild(); child; child = child->nextSibling()) {
+ if (!child->isElementNode())
+ continue;
Element* element = toElement(child);
-
bool childRulesChanged = element->needsStyleRecalc() && element->styleChangeType() >= SubtreeStyleChange;
-
if (forceCheckOfNextElementSibling || forceCheckOfAnyElementSibling)
element->setNeedsStyleRecalc();
-
forceCheckOfNextElementSibling = childRulesChanged && hasDirectAdjacentRules;
forceCheckOfAnyElementSibling = forceCheckOfAnyElementSibling || (childRulesChanged && hasIndirectAdjacentRules);
-
+ }
+ }
+ // This loop is deliberately backwards because we use insertBefore in the rendering tree, and want to avoid
+ // a potentially n^2 loop to find the insertion point while resolving style. Having us start from the last
+ // child and work our way back means in the common case, we'll find the insertion point in O(1) time.
+ // Reversing this loop can lead to non-deterministic results in our code to optimize out empty whitespace
+ // RenderTexts. We try to put off recalcing their style until the end to avoid this issue.
+ // See crbug.com/288225
+ WhitespaceChildList whitespaceChildList(change);
+ for (Node* child = lastChild(); child; child = child->previousSibling()) {
+ if (child->isTextNode()) {
+ Text* textChild = toText(child);
+ // FIXME: This check is expensive and may negate the performance gained by the optimization of
+ // avoiding whitespace renderers.
+ if (textChild->containsOnlyWhitespace())
+ whitespaceChildList.append(textChild);
+ else
+ textChild->recalcTextStyle(change);
+ } else if (child->isElementNode()) {
+ Element* element = toElement(child);
if (shouldRecalcStyle(change, element)) {
parentPusher.push();
- didReattach = element->recalcStyle(change);
+ element->recalcStyle(change);
} else if (document().styleResolver()->supportsStyleSharing(element)) {
document().styleResolver()->addToStyleSharingList(element);
}
}
-
- forceReattachOfAnyWhitespaceSibling = didReattach || forceReattachOfAnyWhitespaceSibling;
}
+ whitespaceChildList.recalcStyle();
+
if (shouldRecalcStyle(change, this)) {
updatePseudoElement(AFTER, change);
updatePseudoElement(BACKDROP, change);
@@ -1675,7 +1770,7 @@
static void checkForSiblingStyleChanges(Element* e, RenderStyle* style, bool finishedParsingCallback,
Node* beforeChange, Node* afterChange, int childCountDelta)
{
- if (!e->attached() || e->document().hasPendingForcedStyleRecalc() || e->styleChangeType() >= SubtreeStyleChange)
+ if (!e->confusingAndOftenMisusedAttached() || e->document().hasPendingForcedStyleRecalc() || e->styleChangeType() >= SubtreeStyleChange)
return;
// :empty selector.
@@ -1740,7 +1835,7 @@
// The + selector. We need to invalidate the first element following the insertion point. It is the only possible element
// that could be affected by this DOM change.
if (e->childrenAffectedByDirectAdjacentRules() && afterChange) {
- if (Node* firstElementAfterInsertion = afterChange->nextElementSibling())
+ if (Node* firstElementAfterInsertion = afterChange->isElementNode() ? afterChange : afterChange->nextElementSibling())
firstElementAfterInsertion->setNeedsStyleRecalc();
}
}
@@ -1865,7 +1960,7 @@
return 0;
}
- ASSERT(&document() == &attr->document());
+ ASSERT(document() == attr->document());
synchronizeAttribute(attr->qualifiedName());
@@ -2055,11 +2150,8 @@
// 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();
- }
+ frame->selection().setSelection(newSelection);
+ frame->selection().revealSelection();
} else if (renderer() && !renderer()->isWidget())
renderer()->scrollRectToVisible(boundingBox());
}
@@ -2093,27 +2185,27 @@
void Element::dispatchFocusEvent(Element* oldFocusedElement, FocusDirection)
{
- RefPtr<FocusEvent> event = FocusEvent::create(eventNames().focusEvent, false, false, document().defaultView(), 0, oldFocusedElement);
+ RefPtr<FocusEvent> event = FocusEvent::create(EventTypeNames::focus, false, false, document().defaultView(), 0, oldFocusedElement);
EventDispatcher::dispatchEvent(this, FocusEventDispatchMediator::create(event.release()));
}
void Element::dispatchBlurEvent(Element* newFocusedElement)
{
- RefPtr<FocusEvent> event = FocusEvent::create(eventNames().blurEvent, false, false, document().defaultView(), 0, newFocusedElement);
+ RefPtr<FocusEvent> event = FocusEvent::create(EventTypeNames::blur, false, false, document().defaultView(), 0, newFocusedElement);
EventDispatcher::dispatchEvent(this, BlurEventDispatchMediator::create(event.release()));
}
void Element::dispatchFocusInEvent(const AtomicString& eventType, Element* oldFocusedElement)
{
ASSERT(!NoEventDispatchAssertion::isEventDispatchForbidden());
- ASSERT(eventType == eventNames().focusinEvent || eventType == eventNames().DOMFocusInEvent);
+ ASSERT(eventType == EventTypeNames::focusin || eventType == EventTypeNames::DOMFocusIn);
dispatchScopedEventDispatchMediator(FocusInEventDispatchMediator::create(FocusEvent::create(eventType, true, false, document().defaultView(), 0, oldFocusedElement)));
}
void Element::dispatchFocusOutEvent(const AtomicString& eventType, Element* newFocusedElement)
{
ASSERT(!NoEventDispatchAssertion::isEventDispatchForbidden());
- ASSERT(eventType == eventNames().focusoutEvent || eventType == eventNames().DOMFocusOutEvent);
+ ASSERT(eventType == EventTypeNames::focusout || eventType == EventTypeNames::DOMFocusOut);
dispatchScopedEventDispatchMediator(FocusOutEventDispatchMediator::create(FocusEvent::create(eventType, true, false, document().defaultView(), 0, newFocusedElement)));
}
@@ -2222,7 +2314,7 @@
return usedStyle;
}
- if (!attached())
+ if (!confusingAndOftenMisusedAttached())
// FIXME: Try to do better than this. Ensure that styleForElement() works for elements that are not in the
// document tree and figure out when to destroy the computed style for such elements.
return 0;
@@ -2434,6 +2526,15 @@
document().cancelFocusAppearanceUpdate();
}
+void Element::updateCallbackSelectors(RenderStyle* oldStyle, RenderStyle* newStyle)
+{
+ const Vector<String> emptyVector;
+ const Vector<String>& oldCallbackSelectors = oldStyle ? oldStyle->callbackSelectors() : emptyVector;
+ const Vector<String>& newCallbackSelectors = newStyle ? newStyle->callbackSelectors() : emptyVector;
+
+ CSSSelectorWatch::from(document()).updateSelectorMatches(oldCallbackSelectors, newCallbackSelectors);
+}
+
void Element::normalizeAttributes()
{
if (!hasAttributes())
@@ -2831,7 +2932,7 @@
}
if (oldValue != newValue) {
- if (attached() && hasSelectorForAttribute(&document(), name.localName()))
+ if (confusingAndOftenMisusedAttached() && hasSelectorForAttribute(&document(), name.localName()))
setNeedsStyleRecalc();
if (isUpgradedCustomElement())
@@ -2865,14 +2966,14 @@
dispatchSubtreeModifiedEvent();
}
-void Element::didMoveToNewDocument(Document* oldDocument)
+void Element::didMoveToNewDocument(Document& oldDocument)
{
Node::didMoveToNewDocument(oldDocument);
// If the documents differ by quirks mode then they differ by case sensitivity
// for class and id names so we need to go through the attribute change logic
// to pick up the new casing in the ElementData.
- if (oldDocument->inQuirksMode() != document().inQuirksMode()) {
+ if (oldDocument.inQuirksMode() != document().inQuirksMode()) {
if (hasID())
setIdAttribute(getIdAttribute());
if (hasClass())
@@ -3397,7 +3498,7 @@
style = cacheIterator->value->value;
presentationAttributeCacheCleaner().didHitPresentationAttributeCache();
} else {
- style = MutableStylePropertySet::create(isSVGElement() ? SVGAttributeMode : CSSQuirksMode);
+ style = MutableStylePropertySet::create(isSVGElement() ? SVGAttributeMode : CSSAttributeMode);
unsigned size = attributeCount();
for (unsigned i = 0; i < size; ++i) {
const Attribute* attribute = attributeItem(i);
@@ -3443,7 +3544,7 @@
void Element::addPropertyToPresentationAttributeStyle(MutableStylePropertySet* style, CSSPropertyID propertyID, const String& value)
{
ASSERT(isStyledElement());
- style->setProperty(propertyID, value, false, document().elementSheet()->contents());
+ style->setProperty(propertyID, value, false);
}
void ElementData::deref()