Setting inline style to the same value it already has triggers a style recalc
authorsimon.fraser@apple.com <simon.fraser@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Mon, 20 Apr 2015 18:31:45 +0000 (18:31 +0000)
committersimon.fraser@apple.com <simon.fraser@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Mon, 20 Apr 2015 18:31:45 +0000 (18:31 +0000)
https://bugs.webkit.org/show_bug.cgi?id=143922

Reviewed by Antti Koivisto.

Source/WebCore:

MutableStyleProperties::setProperty() was taking the result of CSSParser::parseValue()
to mean "parsing changed the style", but it actually just means "parsing succeeded".
Add a new out param, piped through various parser functions, to indicate whether
parsing actually changed style, and instead return that from setProperty().

Add internals.startTrackingStyleRecalcs() and internals.styleRecalcCount() so
we can write tests for style recalc.

Test: fast/css/set-inline-style-recalc.html

* WebCore.xcodeproj/project.pbxproj: Let Xcode have it's way.
* css/CSSParser.cpp:
(WebCore::parseColorValue):
(WebCore::parseSimpleLengthValue):
(WebCore::parseKeywordValue):
(WebCore::parseTranslateTransformValue):
(WebCore::CSSParser::parseFontFaceValue):
(WebCore::CSSParser::parseValue):
* css/CSSParser.h:
* css/CSSProperty.h:
(WebCore::StylePropertyMetadata::operator==):
(WebCore::CSSProperty::operator==):
* css/DOMWindowCSS.cpp:
(WebCore::DOMWindowCSS::supports):
* css/StyleProperties.cpp:
(WebCore::MutableStyleProperties::setProperty):
(WebCore::MutableStyleProperties::appendPrefixingVariantProperty):
(WebCore::MutableStyleProperties::addParsedProperties):
(WebCore::MutableStyleProperties::addParsedProperty):
* css/StyleProperties.h:
* css/WebKitCSSMatrix.cpp:
(WebCore::WebKitCSSMatrix::setMatrixValue):
* dom/Document.cpp:
(WebCore::Document::recalcStyle):
(WebCore::Document::startTrackingStyleRecalcs):
(WebCore::Document::styleRecalcCount):
* dom/Document.h:
* html/canvas/CanvasRenderingContext2D.cpp:
(WebCore::CanvasRenderingContext2D::setFont):
* testing/Internals.cpp:
(WebCore::Internals::startTrackingStyleRecalcs):
(WebCore::Internals::styleRecalcCount):
* testing/Internals.h:
* testing/Internals.idl:

LayoutTests:

Test that changes inline-style (to test this bug fix), and classes (for
proactive testing) and counts style recalcs.

* fast/css/set-inline-style-recalc-expected.txt: Added.
* fast/css/set-inline-style-recalc.html: Added.

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@183017 268f45cc-cd09-0410-ab3c-d52691b4dbfc

19 files changed:
LayoutTests/ChangeLog
LayoutTests/fast/css/set-inline-style-recalc-expected.txt [new file with mode: 0644]
LayoutTests/fast/css/set-inline-style-recalc.html [new file with mode: 0644]
Source/WebCore/ChangeLog
Source/WebCore/WebCore.xcodeproj/project.pbxproj
Source/WebCore/css/CSSParser.cpp
Source/WebCore/css/CSSParser.h
Source/WebCore/css/CSSProperty.h
Source/WebCore/css/DOMWindowCSS.cpp
Source/WebCore/css/FontLoader.cpp
Source/WebCore/css/StyleProperties.cpp
Source/WebCore/css/StyleProperties.h
Source/WebCore/css/WebKitCSSMatrix.cpp
Source/WebCore/dom/Document.cpp
Source/WebCore/dom/Document.h
Source/WebCore/html/canvas/CanvasRenderingContext2D.cpp
Source/WebCore/testing/Internals.cpp
Source/WebCore/testing/Internals.h
Source/WebCore/testing/Internals.idl

index 1f9a7c6..c6bed93 100644 (file)
@@ -1,3 +1,16 @@
+2015-04-20  Simon Fraser  <simon.fraser@apple.com>
+
+        Setting inline style to the same value it already has triggers a style recalc
+        https://bugs.webkit.org/show_bug.cgi?id=143922
+
+        Reviewed by Antti Koivisto.
+
+        Test that changes inline-style (to test this bug fix), and classes (for
+        proactive testing) and counts style recalcs.
+
+        * fast/css/set-inline-style-recalc-expected.txt: Added.
+        * fast/css/set-inline-style-recalc.html: Added.
+
 2015-04-20  Brady Eidson  <beidson@apple.com>
 
         Crash in StyleResolver::invalidateMatchedPropertiesCache() when using content extensions.
diff --git a/LayoutTests/fast/css/set-inline-style-recalc-expected.txt b/LayoutTests/fast/css/set-inline-style-recalc-expected.txt
new file mode 100644 (file)
index 0000000..7344385
--- /dev/null
@@ -0,0 +1,21 @@
+Test to count the number of recalcStyle calls on inline style and class changes.
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+
+PASS numStyleRecalcs is 1
+PASS numStyleRecalcs is 1
+PASS numStyleRecalcs is 0
+PASS numStyleRecalcs is 1
+PASS numStyleRecalcs is 0
+PASS numStyleRecalcs is 1
+PASS numStyleRecalcs is 0
+PASS numStyleRecalcs is 1
+PASS numStyleRecalcs is 0
+PASS numStyleRecalcs is 1
+PASS numStyleRecalcs is 0
+PASS numStyleRecalcs is 1
+PASS successfullyParsed is true
+
+TEST COMPLETE
+
diff --git a/LayoutTests/fast/css/set-inline-style-recalc.html b/LayoutTests/fast/css/set-inline-style-recalc.html
new file mode 100644 (file)
index 0000000..1a4bff0
--- /dev/null
@@ -0,0 +1,78 @@
+<!DOCTYPE html>
+<html>
+<head>
+<script src="../../resources/js-test-pre.js"></script>
+<style>
+    .test {
+        position: relative;
+    }
+    
+    .foo {
+        font-size: 10px;
+    }
+    
+    .bar {
+    }
+</style>
+</head>
+<body>
+<script>
+function setStyleAndForceLayout(changeFunc)
+{
+    if (window.internals) {
+        internals.updateLayoutIgnorePendingStylesheetsAndRunPostLayoutTasks();
+        internals.startTrackingStyleRecalcs();
+    }
+
+    changeFunc();
+
+    var numStyleRecalcs = 0;
+    if (window.internals) {
+        internals.updateLayoutIgnorePendingStylesheetsAndRunPostLayoutTasks();
+        numStyleRecalcs = internals.styleRecalcCount();
+    }
+    return numStyleRecalcs;
+}
+
+description("Test to count the number of recalcStyle calls on inline style and class changes.")
+
+var testContainer = document.createElement("div");
+testContainer.className = 'test';
+document.body.appendChild(testContainer);
+
+var style = testContainer.style;
+
+var numStyleRecalcs = setStyleAndForceLayout(function() { style.height = "10px"; });
+shouldBe("numStyleRecalcs", "1");
+numStyleRecalcs = setStyleAndForceLayout(function() { style.height = "20px"; });
+shouldBe("numStyleRecalcs", "1");
+numStyleRecalcs = setStyleAndForceLayout(function() { style.height = "20px"; });
+shouldBe("numStyleRecalcs", "0");
+
+numStyleRecalcs = setStyleAndForceLayout(function() { style.backgroundPosition = "10px 10%"; });
+shouldBe("numStyleRecalcs", "1");
+numStyleRecalcs = setStyleAndForceLayout(function() { style.backgroundPosition = "10px 10%"; });
+shouldBe("numStyleRecalcs", "0");
+
+numStyleRecalcs = setStyleAndForceLayout(function() { style.backgroundColor = "red"; });
+shouldBe("numStyleRecalcs", "1");
+numStyleRecalcs = setStyleAndForceLayout(function() { style.backgroundColor = "red"; });
+shouldBe("numStyleRecalcs", "0");
+
+numStyleRecalcs = setStyleAndForceLayout(function() { style.transform = "translate(10px, 10px) rotate(10deg)"; });
+shouldBe("numStyleRecalcs", "1");
+numStyleRecalcs = setStyleAndForceLayout(function() { style.transform = "translate(10px, 10px) rotate(10deg)"; });
+shouldBe("numStyleRecalcs", "0");
+
+numStyleRecalcs = setStyleAndForceLayout(function() { testContainer.classList.add('foo'); });
+shouldBe("numStyleRecalcs", "1");
+numStyleRecalcs = setStyleAndForceLayout(function() { testContainer.classList.add('foo'); });
+shouldBe("numStyleRecalcs", "0");
+numStyleRecalcs = setStyleAndForceLayout(function() { testContainer.classList.add('bar'); });
+shouldBe("numStyleRecalcs", "1");
+
+document.body.removeChild(testContainer);
+</script>
+<script src="../../resources/js-test-post.js"></script>
+</body>
+</html>
index 86a8808..8120f41 100644 (file)
@@ -1,3 +1,55 @@
+2015-04-20  Simon Fraser  <simon.fraser@apple.com>
+
+        Setting inline style to the same value it already has triggers a style recalc
+        https://bugs.webkit.org/show_bug.cgi?id=143922
+
+        Reviewed by Antti Koivisto.
+
+        MutableStyleProperties::setProperty() was taking the result of CSSParser::parseValue()
+        to mean "parsing changed the style", but it actually just means "parsing succeeded".
+        Add a new out param, piped through various parser functions, to indicate whether
+        parsing actually changed style, and instead return that from setProperty().
+
+        Add internals.startTrackingStyleRecalcs() and internals.styleRecalcCount() so
+        we can write tests for style recalc.
+
+        Test: fast/css/set-inline-style-recalc.html
+
+        * WebCore.xcodeproj/project.pbxproj: Let Xcode have it's way.
+        * css/CSSParser.cpp:
+        (WebCore::parseColorValue):
+        (WebCore::parseSimpleLengthValue):
+        (WebCore::parseKeywordValue):
+        (WebCore::parseTranslateTransformValue):
+        (WebCore::CSSParser::parseFontFaceValue):
+        (WebCore::CSSParser::parseValue):
+        * css/CSSParser.h:
+        * css/CSSProperty.h:
+        (WebCore::StylePropertyMetadata::operator==):
+        (WebCore::CSSProperty::operator==):
+        * css/DOMWindowCSS.cpp:
+        (WebCore::DOMWindowCSS::supports):
+        * css/StyleProperties.cpp:
+        (WebCore::MutableStyleProperties::setProperty):
+        (WebCore::MutableStyleProperties::appendPrefixingVariantProperty):
+        (WebCore::MutableStyleProperties::addParsedProperties):
+        (WebCore::MutableStyleProperties::addParsedProperty):
+        * css/StyleProperties.h:
+        * css/WebKitCSSMatrix.cpp:
+        (WebCore::WebKitCSSMatrix::setMatrixValue):
+        * dom/Document.cpp:
+        (WebCore::Document::recalcStyle):
+        (WebCore::Document::startTrackingStyleRecalcs):
+        (WebCore::Document::styleRecalcCount):
+        * dom/Document.h:
+        * html/canvas/CanvasRenderingContext2D.cpp:
+        (WebCore::CanvasRenderingContext2D::setFont):
+        * testing/Internals.cpp:
+        (WebCore::Internals::startTrackingStyleRecalcs):
+        (WebCore::Internals::styleRecalcCount):
+        * testing/Internals.h:
+        * testing/Internals.idl:
+
 2015-04-20  Brady Eidson  <beidson@apple.com>
 
         Crash in StyleResolver::invalidateMatchedPropertiesCache() when using content extensions.
index 8a1b383..87d2e77 100644 (file)
                BC74DA471013F468007987AD /* JSRGBColor.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JSRGBColor.h; sourceTree = "<group>"; };
                BC76AC110DD7AD5C00415F34 /* ParserUtilities.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ParserUtilities.h; sourceTree = "<group>"; };
                BC772B360C4EA91E0083285F /* CSSHelper.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = CSSHelper.h; sourceTree = "<group>"; };
-               BC772B370C4EA91E0083285F /* CSSParser.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; lineEnding = 0; path = CSSParser.cpp; sourceTree = "<group>"; xcLanguageSpecificationIdentifier = xcode.lang.cpp; };
+               BC772B370C4EA91E0083285F /* CSSParser.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; lineEnding = 0; path = CSSParser.cpp; sourceTree = "<group>"; };
                BC772B380C4EA91E0083285F /* CSSParser.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = CSSParser.h; sourceTree = "<group>"; };
                BC772C440C4EB2C60083285F /* XMLHttpRequest.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; path = XMLHttpRequest.cpp; sourceTree = "<group>"; };
                BC772C450C4EB2C60083285F /* XMLHttpRequest.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = XMLHttpRequest.h; sourceTree = "<group>"; };
index 30e05b6..4f2cdf1 100644 (file)
@@ -520,26 +520,26 @@ static bool validPrimitiveValueColor(CSSValueID valueID, bool strict = false)
         || (valueID >= CSSValueWebkitFocusRingColor && valueID < CSSValueWebkitText && !strict));
 }
 
-static bool parseColorValue(MutableStyleProperties* declaration, CSSPropertyID propertyId, const String& string, bool important, CSSParserMode cssParserMode)
+static CSSParser::ParseResult parseColorValue(MutableStyleProperties* declaration, CSSPropertyID propertyId, const String& string, bool important, CSSParserMode cssParserMode)
 {
     ASSERT(!string.isEmpty());
     bool strict = isStrictParserMode(cssParserMode);
     if (!isColorPropertyID(propertyId))
-        return false;
+        return CSSParser::ParseResult::Error;
+
     CSSParserString cssString;
     cssString.init(string);
     CSSValueID valueID = cssValueKeywordID(cssString);
     if (validPrimitiveValueColor(valueID, strict)) {
         RefPtr<CSSValue> value = cssValuePool().createIdentifierValue(valueID);
-        declaration->addParsedProperty(CSSProperty(propertyId, value.release(), important));
-        return true;
+        return declaration->addParsedProperty(CSSProperty(propertyId, value.release(), important)) ? CSSParser::ParseResult::Changed : CSSParser::ParseResult::Unchanged;
     }
     RGBA32 color;
     if (!CSSParser::fastParseColor(color, string, strict && string[0] != '#'))
-        return false;
+        return CSSParser::ParseResult::Error;
+
     RefPtr<CSSValue> value = cssValuePool().createColorValue(color);
-    declaration->addParsedProperty(CSSProperty(propertyId, value.release(), important));
-    return true;
+    return declaration->addParsedProperty(CSSProperty(propertyId, value.release(), important)) ? CSSParser::ParseResult::Changed : CSSParser::ParseResult::Unchanged;
 }
 
 static inline bool isSimpleLengthPropertyID(CSSPropertyID propertyId, bool& acceptsNegativeNumbers)
@@ -614,12 +614,12 @@ static inline bool parseSimpleLength(const CharacterType* characters, unsigned&
     return ok;
 }
 
-static bool parseSimpleLengthValue(MutableStyleProperties* declaration, CSSPropertyID propertyId, const String& string, bool important, CSSParserMode cssParserMode)
+static CSSParser::ParseResult parseSimpleLengthValue(MutableStyleProperties* declaration, CSSPropertyID propertyId, const String& string, bool important, CSSParserMode cssParserMode)
 {
     ASSERT(!string.isEmpty());
     bool acceptsNegativeNumbers;
     if (!isSimpleLengthPropertyID(propertyId, acceptsNegativeNumbers))
-        return false;
+        return CSSParser::ParseResult::Error;
 
     unsigned length = string.length();
     double number;
@@ -627,25 +627,24 @@ static bool parseSimpleLengthValue(MutableStyleProperties* declaration, CSSPrope
 
     if (string.is8Bit()) {
         if (!parseSimpleLength(string.characters8(), length, unit, number))
-            return false;
+            return CSSParser::ParseResult::Error;
     } else {
         if (!parseSimpleLength(string.characters16(), length, unit, number))
-            return false;
+            return CSSParser::ParseResult::Error;
     }
 
     if (unit == CSSPrimitiveValue::CSS_NUMBER) {
         if (number && isStrictParserMode(cssParserMode))
-            return false;
+            return CSSParser::ParseResult::Error;
         unit = CSSPrimitiveValue::CSS_PX;
     }
     if (number < 0 && !acceptsNegativeNumbers)
-        return false;
+        return CSSParser::ParseResult::Error;
     if (std::isinf(number))
-        return false;
+        return CSSParser::ParseResult::Error;
 
     RefPtr<CSSValue> value = cssValuePool().createValue(number, unit);
-    declaration->addParsedProperty(CSSProperty(propertyId, value.release(), important));
-    return true;
+    return declaration->addParsedProperty(CSSProperty(propertyId, value.release(), important)) ? CSSParser::ParseResult::Changed : CSSParser::ParseResult::Unchanged;
 }
 
 static inline bool isValidKeywordPropertyAndValue(CSSPropertyID propertyId, int valueID, const CSSParserContext& parserContext, StyleSheetContents* styleSheetContents)
@@ -1178,7 +1177,7 @@ static inline bool isKeywordPropertyID(CSSPropertyID propertyId)
     }
 }
 
-static bool parseKeywordValue(MutableStyleProperties* declaration, CSSPropertyID propertyId, const String& string, bool important, const CSSParserContext& parserContext, StyleSheetContents* styleSheetContents)
+static CSSParser::ParseResult parseKeywordValue(MutableStyleProperties* declaration, CSSPropertyID propertyId, const String& string, bool important, const CSSParserContext& parserContext, StyleSheetContents* styleSheetContents)
 {
     ASSERT(!string.isEmpty());
 
@@ -1186,11 +1185,11 @@ static bool parseKeywordValue(MutableStyleProperties* declaration, CSSPropertyID
         // All properties accept the values of "initial" and "inherit".
         String lowerCaseString = string.lower();
         if (lowerCaseString != "initial" && lowerCaseString != "inherit")
-            return false;
+            return CSSParser::ParseResult::Error;
 
         // Parse initial/inherit shorthands using the CSSParser.
         if (shorthandForProperty(propertyId).length())
-            return false;
+            return CSSParser::ParseResult::Error;
     }
 
     CSSParserString cssString;
@@ -1198,7 +1197,7 @@ static bool parseKeywordValue(MutableStyleProperties* declaration, CSSPropertyID
     CSSValueID valueID = cssValueKeywordID(cssString);
 
     if (!valueID)
-        return false;
+        return CSSParser::ParseResult::Error;
 
     RefPtr<CSSValue> value;
     if (valueID == CSSValueInherit)
@@ -1208,10 +1207,9 @@ static bool parseKeywordValue(MutableStyleProperties* declaration, CSSPropertyID
     else if (isValidKeywordPropertyAndValue(propertyId, valueID, parserContext, styleSheetContents))
         value = cssValuePool().createIdentifierValue(valueID);
     else
-        return false;
+        return CSSParser::ParseResult::Error;
 
-    declaration->addParsedProperty(CSSProperty(propertyId, value.release(), important));
-    return true;
+    return declaration->addParsedProperty(CSSProperty(propertyId, value.release(), important)) ? CSSParser::ParseResult::Changed : CSSParser::ParseResult::Unchanged;
 }
 
 template <typename CharacterType>
@@ -1235,16 +1233,19 @@ static bool parseTransformTranslateArguments(WebKitCSSTransformValue& transformV
     return true;
 }
 
-static bool parseTranslateTransformValue(MutableStyleProperties* properties, CSSPropertyID propertyID, const String& string, bool important)
+static CSSParser::ParseResult parseTranslateTransformValue(MutableStyleProperties* properties, CSSPropertyID propertyID, const String& string, bool important)
 {
     if (propertyID != CSSPropertyTransform)
-        return false;
+        return CSSParser::ParseResult::Error;
+
     static const unsigned shortestValidTransformStringLength = 12;
     static const unsigned likelyMultipartTransformStringLengthCutoff = 32;
     if (string.length() < shortestValidTransformStringLength || string.length() > likelyMultipartTransformStringLengthCutoff)
-        return false;
+        return CSSParser::ParseResult::Error;
+
     if (!string.startsWith("translate", false))
-        return false;
+        return CSSParser::ParseResult::Error;
+
     UChar c9 = toASCIILower(string[9]);
     UChar c10 = toASCIILower(string[10]);
 
@@ -1266,7 +1267,7 @@ static bool parseTranslateTransformValue(MutableStyleProperties* properties, CSS
         expectedArgumentCount = 3;
         argumentStart = 12;
     } else
-        return false;
+        return CSSParser::ParseResult::Error;
 
     RefPtr<WebKitCSSTransformValue> transformValue = WebKitCSSTransformValue::create(transformType);
     bool success;
@@ -1275,11 +1276,11 @@ static bool parseTranslateTransformValue(MutableStyleProperties* properties, CSS
     else
         success = parseTransformTranslateArguments(*transformValue, string.characters16(), string.length(), argumentStart, expectedArgumentCount);
     if (!success)
-        return false;
+        return CSSParser::ParseResult::Error;
+
     RefPtr<CSSValueList> result = CSSValueList::createSpaceSeparated();
     result->append(transformValue.releaseNonNull());
-    properties->addParsedProperty(CSSProperty(CSSPropertyTransform, result.release(), important));
-    return true;
+    return properties->addParsedProperty(CSSProperty(CSSPropertyTransform, result.release(), important)) ? CSSParser::ParseResult::Changed : CSSParser::ParseResult::Unchanged;
 }
 
 PassRefPtr<CSSValueList> CSSParser::parseFontFaceValue(const AtomicString& string)
@@ -1287,7 +1288,8 @@ PassRefPtr<CSSValueList> CSSParser::parseFontFaceValue(const AtomicString& strin
     if (string.isEmpty())
         return nullptr;
     RefPtr<MutableStyleProperties> dummyStyle = MutableStyleProperties::create();
-    if (!parseValue(dummyStyle.get(), CSSPropertyFontFamily, string, false, CSSQuirksMode, nullptr))
+
+    if (parseValue(dummyStyle.get(), CSSPropertyFontFamily, string, false, CSSQuirksMode, nullptr) == ParseResult::Error)
         return nullptr;
 
     RefPtr<CSSValue> fontFamily = dummyStyle->getPropertyCSSValue(CSSPropertyFontFamily);
@@ -1296,13 +1298,16 @@ PassRefPtr<CSSValueList> CSSParser::parseFontFaceValue(const AtomicString& strin
     return static_pointer_cast<CSSValueList>(fontFamily.release());
 }
 
-bool CSSParser::parseValue(MutableStyleProperties* declaration, CSSPropertyID propertyID, const String& string, bool important, CSSParserMode cssParserMode, StyleSheetContents* contextStyleSheet)
+CSSParser::ParseResult CSSParser::parseValue(MutableStyleProperties* declaration, CSSPropertyID propertyID, const String& string, bool important, CSSParserMode cssParserMode, StyleSheetContents* contextStyleSheet)
 {
     ASSERT(!string.isEmpty());
-    if (parseSimpleLengthValue(declaration, propertyID, string, important, cssParserMode))
-        return true;
-    if (parseColorValue(declaration, propertyID, string, important, cssParserMode))
-        return true;
+    CSSParser::ParseResult result = parseSimpleLengthValue(declaration, propertyID, string, important, cssParserMode);
+    if (result != ParseResult::Error)
+        return result;
+
+    result = parseColorValue(declaration, propertyID, string, important, cssParserMode);
+    if (result != ParseResult::Error)
+        return result;
 
     CSSParserContext context(cssParserMode);
     if (contextStyleSheet) {
@@ -1310,16 +1315,19 @@ bool CSSParser::parseValue(MutableStyleProperties* declaration, CSSPropertyID pr
         context.mode = cssParserMode;
     }
 
-    if (parseKeywordValue(declaration, propertyID, string, important, context, contextStyleSheet))
-        return true;
-    if (parseTranslateTransformValue(declaration, propertyID, string, important))
-        return true;
+    result = parseKeywordValue(declaration, propertyID, string, important, context, contextStyleSheet);
+    if (result != ParseResult::Error)
+        return result;
+
+    result = parseTranslateTransformValue(declaration, propertyID, string, important);
+    if (result != ParseResult::Error)
+        return result;
 
     CSSParser parser(context);
     return parser.parseValue(declaration, propertyID, string, important, contextStyleSheet);
 }
 
-bool CSSParser::parseValue(MutableStyleProperties* declaration, CSSPropertyID propertyID, const String& string, bool important, StyleSheetContents* contextStyleSheet)
+CSSParser::ParseResult CSSParser::parseValue(MutableStyleProperties* declaration, CSSPropertyID propertyID, const String& string, bool important, StyleSheetContents* contextStyleSheet)
 {
     setStyleSheet(contextStyleSheet);
 
@@ -1332,16 +1340,16 @@ bool CSSParser::parseValue(MutableStyleProperties* declaration, CSSPropertyID pr
 
     m_rule = nullptr;
 
-    bool ok = false;
+    ParseResult result = ParseResult::Error;
     if (m_hasFontFaceOnlyValues)
         deleteFontFaceOnlyValues();
+
     if (!m_parsedProperties.isEmpty()) {
-        ok = true;
-        declaration->addParsedProperties(m_parsedProperties);
+        result = declaration->addParsedProperties(m_parsedProperties) ? ParseResult::Changed : ParseResult::Unchanged;
         clearProperties();
     }
 
-    return ok;
+    return result;
 }
 
 // The color will only be changed when string contains a valid CSS color, so callers
index ead0327..7ca0224 100644 (file)
@@ -81,6 +81,12 @@ public:
         GeneralSyntaxError
     };
 
+    enum class ParseResult {
+        Changed,
+        Unchanged,
+        Error
+    };
+
     class ValueWithCalculation {
     public:
         explicit ValueWithCalculation(CSSParserValue& value)
@@ -103,18 +109,20 @@ public:
     };
 
     WEBCORE_EXPORT CSSParser(const CSSParserContext&);
-
     WEBCORE_EXPORT ~CSSParser();
 
     void parseSheet(StyleSheetContents*, const String&, const TextPosition&, RuleSourceDataList*, bool logErrors);
     PassRefPtr<StyleRuleBase> parseRule(StyleSheetContents*, const String&);
     PassRefPtr<StyleKeyframe> parseKeyframeRule(StyleSheetContents*, const String&);
     bool parseSupportsCondition(const String&);
-    static bool parseValue(MutableStyleProperties*, CSSPropertyID, const String&, bool important, CSSParserMode, StyleSheetContents*);
+
+    static ParseResult parseValue(MutableStyleProperties*, CSSPropertyID, const String&, bool important, CSSParserMode, StyleSheetContents*);
+
     static bool parseColor(RGBA32& color, const String&, bool strict = false);
     static bool parseSystemColor(RGBA32& color, const String&, Document*);
     static PassRefPtr<CSSValueList> parseFontFaceValue(const AtomicString&);
     PassRefPtr<CSSPrimitiveValue> parseValidPrimitive(CSSValueID ident, ValueWithCalculation&);
+
     WEBCORE_EXPORT bool parseDeclaration(MutableStyleProperties*, const String&, PassRefPtr<CSSRuleSourceData>, StyleSheetContents* contextStyleSheet);
     static Ref<ImmutableStyleProperties> parseInlineStyleDeclaration(const String&, Element*);
     std::unique_ptr<MediaQuery> parseMediaQuery(const String&);
@@ -549,7 +557,7 @@ private:
     bool isGeneratedImageValue(CSSParserValue&) const;
     bool parseGeneratedImage(CSSParserValueList&, RefPtr<CSSValue>&);
 
-    bool parseValue(MutableStyleProperties*, CSSPropertyID, const String&, bool important, StyleSheetContents* contextStyleSheet);
+    ParseResult parseValue(MutableStyleProperties*, CSSPropertyID, const String&, bool important, StyleSheetContents* contextStyleSheet);
     Ref<ImmutableStyleProperties> parseDeclaration(const String&, StyleSheetContents* contextStyleSheet);
 
     PassRefPtr<CSSBasicShape> parseInsetRoundedCorners(PassRefPtr<CSSBasicShapeInset>, CSSParserValueList&);
index 19c4489..0348eb2 100644 (file)
@@ -44,6 +44,16 @@ struct StylePropertyMetadata {
     }
 
     CSSPropertyID shorthandID() const;
+    
+    bool operator==(const StylePropertyMetadata& other) const
+    {
+        return m_propertyID == other.m_propertyID
+            && m_isSetFromShorthand == other.m_isSetFromShorthand
+            && m_indexInShorthandsVector == other.m_indexInShorthandsVector
+            && m_important == other.m_important
+            && m_implicit == other.m_implicit
+            && m_inherited == other.m_inherited;
+    }
 
     uint16_t m_propertyID : 10;
     uint16_t m_isSetFromShorthand : 1;
@@ -76,6 +86,20 @@ public:
 
     const StylePropertyMetadata& metadata() const { return m_metadata; }
 
+    bool operator==(const CSSProperty& other) const
+    {
+        if (!(m_metadata == other.m_metadata))
+            return false;
+
+        if (!m_value && !other.m_value)
+            return true;
+
+        if (!m_value || !other.m_value)
+            return false;
+        
+        return m_value->equals(*other.m_value);
+    }
+
 private:
     StylePropertyMetadata m_metadata;
     RefPtr<CSSValue> m_value;
index 58782c7..b8ba88c 100644 (file)
@@ -72,7 +72,7 @@ bool DOMWindowCSS::supports(const String& property, const String& value) const
         return false;
 
     RefPtr<MutableStyleProperties> dummyStyle = MutableStyleProperties::create();
-    return CSSParser::parseValue(dummyStyle.get(), propertyID, normalizedValue, false, CSSStrictMode, 0);
+    return CSSParser::parseValue(dummyStyle.get(), propertyID, normalizedValue, false, CSSStrictMode, nullptr) != CSSParser::ParseResult::Error;
 }
 
 bool DOMWindowCSS::supports(const String& conditionText) const
index bf03ff2..808852c 100644 (file)
@@ -295,7 +295,7 @@ bool FontLoader::resolveFontStyle(const String& fontString, FontCascade& font)
 {
     // Interpret fontString in the same way as the 'font' attribute of CanvasRenderingContext2D.
     RefPtr<MutableStyleProperties> parsedStyle = MutableStyleProperties::create();
-    CSSParser::parseValue(parsedStyle.get(), CSSPropertyFont, fontString, true, CSSStrictMode, 0);
+    CSSParser::parseValue(parsedStyle.get(), CSSPropertyFont, fontString, true, CSSStrictMode, nullptr);
     if (parsedStyle->isEmpty())
         return false;
     
index ce70707..7c0036a 100644 (file)
@@ -681,7 +681,7 @@ bool MutableStyleProperties::setProperty(CSSPropertyID propertyID, const String&
 
     // When replacing an existing property value, this moves the property to the end of the list.
     // Firefox preserves the position, and MSIE moves the property to the beginning.
-    return CSSParser::parseValue(this, propertyID, value, important, cssParserMode(), contextStyleSheet);
+    return CSSParser::parseValue(this, propertyID, value, important, cssParserMode(), contextStyleSheet) == CSSParser::ParseResult::Changed;
 }
 
 void MutableStyleProperties::setProperty(CSSPropertyID propertyID, PassRefPtr<CSSValue> prpValue, bool important)
@@ -699,17 +699,21 @@ void MutableStyleProperties::setProperty(CSSPropertyID propertyID, PassRefPtr<CS
         m_propertyVector.append(CSSProperty(shorthand.properties()[i], value, important));
 }
 
-void MutableStyleProperties::setProperty(const CSSProperty& property, CSSProperty* slot)
+bool MutableStyleProperties::setProperty(const CSSProperty& property, CSSProperty* slot)
 {
     if (!removeShorthandProperty(property.id())) {
         CSSProperty* toReplace = slot ? slot : findCSSPropertyWithID(property.id());
         if (toReplace) {
+            if (*toReplace == property)
+                return false;
+
             *toReplace = property;
             setPrefixingVariantProperty(property);
-            return;
+            return true;
         }
     }
-    appendPrefixingVariantProperty(property);
+
+    return appendPrefixingVariantProperty(property);
 }
 
 static unsigned getIndexInShorthandVectorForPrefixingVariant(const CSSProperty& property, CSSPropertyID prefixingVariant)
@@ -721,14 +725,15 @@ static unsigned getIndexInShorthandVectorForPrefixingVariant(const CSSProperty&
     return indexOfShorthandForLonghand(prefixedShorthand, matchingShorthandsForLonghand(prefixingVariant));
 }
 
-void MutableStyleProperties::appendPrefixingVariantProperty(const CSSProperty& property)
+bool MutableStyleProperties::appendPrefixingVariantProperty(const CSSProperty& property)
 {
     m_propertyVector.append(property);
     CSSPropertyID prefixingVariant = prefixingVariantForPropertyId(property.id());
     if (prefixingVariant == property.id())
-        return;
+        return true;
 
     m_propertyVector.append(CSSProperty(prefixingVariant, property.value(), property.isImportant(), property.isSetFromShorthand(), getIndexInShorthandVectorForPrefixingVariant(property, prefixingVariant), property.metadata().m_implicit));
+    return true;
 }
 
 void MutableStyleProperties::setPrefixingVariantProperty(const CSSProperty& property)
@@ -741,14 +746,12 @@ void MutableStyleProperties::setPrefixingVariantProperty(const CSSProperty& prop
 
 bool MutableStyleProperties::setProperty(CSSPropertyID propertyID, CSSValueID identifier, bool important)
 {
-    setProperty(CSSProperty(propertyID, cssValuePool().createIdentifierValue(identifier), important));
-    return true;
+    return setProperty(CSSProperty(propertyID, cssValuePool().createIdentifierValue(identifier), important));
 }
 
 bool MutableStyleProperties::setProperty(CSSPropertyID propertyID, CSSPropertyID identifier, bool important)
 {
-    setProperty(CSSProperty(propertyID, cssValuePool().createIdentifierValue(identifier), important));
-    return true;
+    return setProperty(CSSProperty(propertyID, cssValuePool().createIdentifierValue(identifier), important));
 }
 
 void MutableStyleProperties::parseDeclaration(const String& styleDeclaration, StyleSheetContents* contextStyleSheet)
@@ -764,18 +767,25 @@ void MutableStyleProperties::parseDeclaration(const String& styleDeclaration, St
     parser.parseDeclaration(this, styleDeclaration, 0, contextStyleSheet);
 }
 
-void MutableStyleProperties::addParsedProperties(const Vector<CSSProperty>& properties)
+bool MutableStyleProperties::addParsedProperties(const Vector<CSSProperty>& properties)
 {
+    bool anyChanged = false;
     m_propertyVector.reserveCapacity(m_propertyVector.size() + properties.size());
-    for (unsigned i = 0; i < properties.size(); ++i)
-        addParsedProperty(properties[i]);
+    for (unsigned i = 0; i < properties.size(); ++i) {
+        if (addParsedProperty(properties[i]))
+            anyChanged = true;
+    }
+
+    return anyChanged;
 }
 
-void MutableStyleProperties::addParsedProperty(const CSSProperty& property)
+bool MutableStyleProperties::addParsedProperty(const CSSProperty& property)
 {
     // Only add properties that have no !important counterpart present
     if (!propertyIsImportant(property.id()) || property.isImportant())
-        setProperty(property);
+        return setProperty(property);
+    
+    return false;
 }
 
 String StyleProperties::asText() const
index 9e9cf22..3223ca5 100644 (file)
@@ -193,8 +193,8 @@ public:
 
     PropertySetCSSStyleDeclaration* cssStyleDeclaration();
 
-    void addParsedProperties(const Vector<CSSProperty>&);
-    void addParsedProperty(const CSSProperty&);
+    bool addParsedProperties(const Vector<CSSProperty>&);
+    bool addParsedProperty(const CSSProperty&);
 
     // These expand shorthand properties into multiple properties.
     bool setProperty(CSSPropertyID, const String& value, bool important = false, StyleSheetContents* contextStyleSheet = 0);
@@ -203,11 +203,11 @@ public:
     // These do not. FIXME: This is too messy, we can do better.
     bool setProperty(CSSPropertyID, CSSValueID identifier, bool important = false);
     bool setProperty(CSSPropertyID, CSSPropertyID identifier, bool important = false);
-    void appendPrefixingVariantProperty(const CSSProperty&);
+    bool appendPrefixingVariantProperty(const CSSProperty&);
     void setPrefixingVariantProperty(const CSSProperty&);
-    void setProperty(const CSSProperty&, CSSProperty* slot = 0);
+    bool setProperty(const CSSProperty&, CSSProperty* slot = nullptr);
 
-    bool removeProperty(CSSPropertyID, String* returnText = 0);
+    bool removeProperty(CSSPropertyID, String* returnText = nullptr);
     void removePrefixedOrUnprefixedProperty(CSSPropertyID);
     void removeBlockProperties();
     bool removePropertiesInSet(const CSSPropertyID* set, unsigned length);
index 0e6b2c6..dbe2eed 100644 (file)
@@ -57,7 +57,7 @@ void WebKitCSSMatrix::setMatrixValue(const String& string, ExceptionCode& ec)
         return;
 
     RefPtr<MutableStyleProperties> styleDeclaration = MutableStyleProperties::create();
-    if (CSSParser::parseValue(styleDeclaration.get(), CSSPropertyTransform, string, true, CSSStrictMode, 0)) {
+    if (CSSParser::parseValue(styleDeclaration.get(), CSSPropertyTransform, string, true, CSSStrictMode, nullptr) != CSSParser::ParseResult::Error) {
         // Convert to TransformOperations. This can fail if a property
         // requires style (i.e., param uses 'ems' or 'exs')
         RefPtr<CSSValue> value = styleDeclaration->getPropertyCSSValue(CSSPropertyTransform);
index 8efd324..1cf2853 100644 (file)
@@ -1790,6 +1790,8 @@ void Document::recalcStyle(Style::Change change)
         m_closeAfterStyleRecalc = false;
         implicitClose();
     }
+    
+    ++m_styleRecalcCount;
 
     InspectorInstrumentation::didRecalculateStyle(cookie);
 
@@ -6139,6 +6141,16 @@ void Document::updateLastHandledUserGestureTimestamp()
     m_lastHandledUserGestureTimestamp = monotonicallyIncreasingTime();
 }
 
+void Document::startTrackingStyleRecalcs()
+{
+    m_styleRecalcCount = 0;
+}
+
+unsigned Document::styleRecalcCount() const
+{
+    return m_styleRecalcCount;
+}
+
 DocumentLoader* Document::loader() const
 {
     if (!m_frame)
index 5df27ae..e39a7a4 100644 (file)
@@ -1141,6 +1141,9 @@ public:
     WEBCORE_EXPORT unsigned wheelEventHandlerCount() const;
     WEBCORE_EXPORT unsigned touchEventHandlerCount() const;
 
+    WEBCORE_EXPORT void startTrackingStyleRecalcs();
+    WEBCORE_EXPORT unsigned styleRecalcCount() const;
+
     void didAddTouchEventHandler(Node&);
     void didRemoveTouchEventHandler(Node&, EventHandlerRemoval = EventHandlerRemoval::One);
 
@@ -1440,6 +1443,8 @@ private:
     // http://www.whatwg.org/specs/web-apps/current-work/#ignore-destructive-writes-counter
     unsigned m_ignoreDestructiveWriteCount;
 
+    unsigned m_styleRecalcCount { 0 };
+
     StringWithDirection m_title;
     StringWithDirection m_rawTitle;
     bool m_titleSetExplicitly;
index bf22d27..917ef7b 100755 (executable)
@@ -2075,7 +2075,7 @@ void CanvasRenderingContext2D::setFont(const String& newFont)
         return;
 
     RefPtr<MutableStyleProperties> parsedStyle = MutableStyleProperties::create();
-    CSSParser::parseValue(parsedStyle.get(), CSSPropertyFont, newFont, true, strictToCSSParserMode(!m_usesCSSCompatibilityParseMode), 0);
+    CSSParser::parseValue(parsedStyle.get(), CSSPropertyFont, newFont, true, strictToCSSParserMode(!m_usesCSSCompatibilityParseMode), nullptr);
     if (parsedStyle->isEmpty())
         return;
 
index e97f202..514e0c4 100644 (file)
@@ -2089,6 +2089,27 @@ unsigned long Internals::layerFlushCount(ExceptionCode& ec)
     return document->renderView()->compositor().layerFlushCount();
 }
 
+void Internals::startTrackingStyleRecalcs(ExceptionCode& ec)
+{
+    Document* document = contextDocument();
+    if (!document) {
+        ec = INVALID_ACCESS_ERR;
+        return;
+    }
+    document->startTrackingStyleRecalcs();
+}
+
+unsigned long Internals::styleRecalcCount(ExceptionCode& ec)
+{
+    Document* document = contextDocument();
+    if (!document) {
+        ec = INVALID_ACCESS_ERR;
+        return 0;
+    }
+    
+    return document->styleRecalcCount();
+}
+
 void Internals::updateLayoutIgnorePendingStylesheetsAndRunPostLayoutTasks(ExceptionCode& ec)
 {
     updateLayoutIgnorePendingStylesheetsAndRunPostLayoutTasks(nullptr, ec);
index f8d429e..a46692a 100644 (file)
@@ -298,6 +298,9 @@ public:
     void startTrackingLayerFlushes(ExceptionCode&);
     unsigned long layerFlushCount(ExceptionCode&);
     
+    void startTrackingStyleRecalcs(ExceptionCode&);
+    unsigned long styleRecalcCount(ExceptionCode&);
+
     void updateLayoutIgnorePendingStylesheetsAndRunPostLayoutTasks(ExceptionCode&);
     void updateLayoutIgnorePendingStylesheetsAndRunPostLayoutTasks(Node*, ExceptionCode&);
 
index e216e50..2088d18 100644 (file)
@@ -277,6 +277,9 @@ enum ResourceLoadPriority {
     // Query if a timer is currently throttled, to debug timer throttling.
     [RaisesException] boolean isTimerThrottled(long timerHandle);
 
+    [RaisesException] void startTrackingStyleRecalcs();
+    [RaisesException] unsigned long styleRecalcCount();
+
     // |node| should be Document, HTMLIFrameElement, or unspecified.
     // If |node| is an HTMLIFrameElement, it assumes node.contentDocument is
     // specified without security checks. Unspecified means this document.