Make pseudo element update work with LocalStyleChange.

The missing pseudo element update on LocalStyleChange did not cause any bugs
at the moment since LocalStyleChange is not used when pseudo element selector
matching changes, but necessary to fix in preparation for optimizing :hover,
:active, and :focus updates using LocalStyleChange. If pseudo elements are
updated for LocalStyleChange, style updates for mentioned pseudo classes can
use LocalStyleChange in many cases where SubtreeStyleChange is currently used.

LocalStyleChange recalc style for the given element and descendants which
inherit any of the changes from that element. In addition, there is code
to make sure pseudo element children were being updated when the styles for
the element itself did not change, but matching of its pseudo elements did.

That code (shouldRecalcStyle() check on the actual element before calling
updatePseudoElement() in recalcChildStyle) was introduced in [1], but changed
behavior in [2] because needsRecalcStyle was cleared before recalcChildStyle.

UpdatePseudoElements is added as a new StyleRecalcChange which causes an
update of the pseudo element children, but not other children in the case
where the styles for the actual element were recalculated, but resulted in
NoChange or NoInherit, and any of the pseudo element bits were set on
RenderStyle.

[1] https://src.chromium.org/viewvc/blink?revision=147696&view=revision
[2] https://src.chromium.org/viewvc/blink?revision=161568&view=revision

BUG=337983

Review URL: https://codereview.chromium.org/144963002

git-svn-id: svn://svn.chromium.org/blink/trunk@165981 bbb929c8-8fbe-4397-9dbb-9b2b20218538
diff --git a/third_party/WebKit/Source/core/dom/Element.cpp b/third_party/WebKit/Source/core/dom/Element.cpp
index 34406b5..14e2950 100644
--- a/third_party/WebKit/Source/core/dom/Element.cpp
+++ b/third_party/WebKit/Source/core/dom/Element.cpp
@@ -1590,9 +1590,10 @@
     }
 
     // If we reattached we don't need to recalc the style of our descendants anymore.
-    if ((change >= Inherit && change < Reattach) || childNeedsStyleRecalc())
+    if ((change >= UpdatePseudoElements && change < Reattach) || childNeedsStyleRecalc()) {
         recalcChildStyle(change);
-    clearChildNeedsStyleRecalc();
+        clearChildNeedsStyleRecalc();
+    }
 
     if (hasCustomStyleCallbacks())
         didRecalcStyle(change);
@@ -1643,60 +1644,64 @@
     if (styleChangeType() >= SubtreeStyleChange)
         return Force;
 
-    if (change <= Inherit)
-        return localChange;
+    if (change > Inherit || localChange > Inherit)
+        return max(localChange, change);
 
-    return max(localChange, change);
+    if (localChange < Inherit && (oldStyle->hasPseudoElementStyle() || newStyle->hasPseudoElementStyle()))
+        return UpdatePseudoElements;
+
+    return localChange;
 }
 
 void Element::recalcChildStyle(StyleRecalcChange change)
 {
     ASSERT(document().inStyleRecalc());
-    ASSERT(change >= Inherit || childNeedsStyleRecalc());
+    ASSERT(change >= UpdatePseudoElements || childNeedsStyleRecalc());
     ASSERT(!needsStyleRecalc());
 
     StyleResolverParentPusher parentPusher(*this);
 
-    for (ShadowRoot* root = youngestShadowRoot(); root; root = root->olderShadowRoot()) {
-        if (root->shouldCallRecalcStyle(change)) {
-            parentPusher.push();
-            root->recalcStyle(change);
+    if (change > UpdatePseudoElements || childNeedsStyleRecalc()) {
+        for (ShadowRoot* root = youngestShadowRoot(); root; root = root->olderShadowRoot()) {
+            if (root->shouldCallRecalcStyle(change)) {
+                parentPusher.push();
+                root->recalcStyle(change);
+            }
         }
     }
 
-    if (shouldCallRecalcStyle(change))
-        updatePseudoElement(BEFORE, change);
+    updatePseudoElement(BEFORE, change);
 
     if (change < Force && hasRareData() && childNeedsStyleRecalc())
         checkForChildrenAdjacentRuleChanges();
 
-    // 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.
-    // See crbug.com/288225
-    StyleResolver& styleResolver = document().ensureStyleResolver();
-    Text* lastTextNode = 0;
-    for (Node* child = lastChild(); child; child = child->previousSibling()) {
-        if (child->isTextNode()) {
-            toText(child)->recalcTextStyle(change, lastTextNode);
-            lastTextNode = toText(child);
-        } else if (child->isElementNode()) {
-            Element* element = toElement(child);
-            if (element->shouldCallRecalcStyle(change)) {
-                parentPusher.push();
-                element->recalcStyle(change, lastTextNode);
-            } else if (element->supportsStyleSharing()) {
-                styleResolver.addToStyleSharingList(*element);
+    if (change > UpdatePseudoElements || childNeedsStyleRecalc()) {
+        // 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.
+        // See crbug.com/288225
+        StyleResolver& styleResolver = document().ensureStyleResolver();
+        Text* lastTextNode = 0;
+        for (Node* child = lastChild(); child; child = child->previousSibling()) {
+            if (child->isTextNode()) {
+                toText(child)->recalcTextStyle(change, lastTextNode);
+                lastTextNode = toText(child);
+            } else if (child->isElementNode()) {
+                Element* element = toElement(child);
+                if (element->shouldCallRecalcStyle(change)) {
+                    parentPusher.push();
+                    element->recalcStyle(change, lastTextNode);
+                } else if (element->supportsStyleSharing()) {
+                    styleResolver.addToStyleSharingList(*element);
+                }
+                if (element->renderer())
+                    lastTextNode = 0;
             }
-            if (element->renderer())
-                lastTextNode = 0;
         }
     }
 
-    if (shouldCallRecalcStyle(change)) {
-        updatePseudoElement(AFTER, change);
-        updatePseudoElement(BACKDROP, change);
-    }
+    updatePseudoElement(AFTER, change);
+    updatePseudoElement(BACKDROP, change);
 }
 
 void Element::checkForChildrenAdjacentRuleChanges()
@@ -2758,8 +2763,10 @@
 
 void Element::updatePseudoElement(PseudoId pseudoId, StyleRecalcChange change)
 {
+    ASSERT(!needsStyleRecalc());
     PseudoElement* element = pseudoElement(pseudoId);
-    if (element && (needsStyleRecalc() || element->shouldCallRecalcStyle(change))) {
+    if (element && (change == UpdatePseudoElements || element->shouldCallRecalcStyle(change))) {
+
         // Need to clear the cached style if the PseudoElement wants a recalc so it
         // computes a new style.
         if (element->needsStyleRecalc())
@@ -2768,7 +2775,7 @@
         // PseudoElement styles hang off their parent element's style so if we needed
         // a style recalc we should Force one on the pseudo.
         // FIXME: We should figure out the right text sibling to pass.
-        element->recalcStyle(needsStyleRecalc() ? Force : change);
+        element->recalcStyle(change == UpdatePseudoElements ? Force : change);
 
         // Wait until our parent is not displayed or pseudoElementRendererIsNeeded
         // is false, otherwise we could continously create and destroy PseudoElements
@@ -2776,8 +2783,9 @@
         // PseudoElement's renderer for each style recalc.
         if (!renderer() || !pseudoElementRendererIsNeeded(renderer()->getCachedPseudoStyle(pseudoId)))
             elementRareData()->setPseudoElement(pseudoId, 0);
-    } else if (change >= Inherit || needsStyleRecalc())
+    } else if (change >= UpdatePseudoElements) {
         createPseudoElementIfNeeded(pseudoId);
+    }
 }
 
 void Element::createPseudoElementIfNeeded(PseudoId pseudoId)
diff --git a/third_party/WebKit/Source/core/rendering/style/RenderStyle.h b/third_party/WebKit/Source/core/rendering/style/RenderStyle.h
index 0e31f92..bfac462 100644
--- a/third_party/WebKit/Source/core/rendering/style/RenderStyle.h
+++ b/third_party/WebKit/Source/core/rendering/style/RenderStyle.h
@@ -437,6 +437,7 @@
     bool hasPseudoStyle(PseudoId pseudo) const;
     void setHasPseudoStyle(PseudoId pseudo);
     bool hasUniquePseudoStyle() const;
+    bool hasPseudoElementStyle() const;
 
     // attribute getter methods
 
@@ -1892,6 +1893,11 @@
     noninherited_flags._pseudoBits |= 1 << (pseudo - 1);
 }
 
+inline bool RenderStyle::hasPseudoElementStyle() const
+{
+    return noninherited_flags._pseudoBits & PSEUDO_ELEMENT_MASK;
+}
+
 } // namespace WebCore
 
 #endif // RenderStyle_h
diff --git a/third_party/WebKit/Source/core/rendering/style/RenderStyleConstants.h b/third_party/WebKit/Source/core/rendering/style/RenderStyleConstants.h
index 06c0da55..2743b7f 100755
--- a/third_party/WebKit/Source/core/rendering/style/RenderStyleConstants.h
+++ b/third_party/WebKit/Source/core/rendering/style/RenderStyleConstants.h
@@ -31,6 +31,7 @@
 enum StyleRecalcChange {
     NoChange,
     NoInherit,
+    UpdatePseudoElements,
     Inherit,
     Force,
     Reattach,
@@ -86,7 +87,8 @@
     AFTER_LAST_INTERNAL_PSEUDOID,
     FIRST_PUBLIC_PSEUDOID = FIRST_LINE,
     FIRST_INTERNAL_PSEUDOID = SCROLLBAR_THUMB,
-    PUBLIC_PSEUDOID_MASK = ((1 << FIRST_INTERNAL_PSEUDOID) - 1) & ~((1 << FIRST_PUBLIC_PSEUDOID) - 1)
+    PUBLIC_PSEUDOID_MASK = ((1 << FIRST_INTERNAL_PSEUDOID) - 1) & ~((1 << FIRST_PUBLIC_PSEUDOID) - 1),
+    PSEUDO_ELEMENT_MASK = (1 << (BEFORE - 1)) | (1 << (AFTER - 1)) | (1 << (BACKDROP - 1))
 };
 
 enum ColumnFill { ColumnFillBalance, ColumnFillAuto };