Fast path for simple transform parsing
authorantti@apple.com <antti@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Mon, 25 Jun 2012 19:33:07 +0000 (19:33 +0000)
committerantti@apple.com <antti@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Mon, 25 Jun 2012 19:33:07 +0000 (19:33 +0000)
https://bugs.webkit.org/show_bug.cgi?id=89898

Reviewed by Anders Carlsson.

When manipulating transforms using script, the transform value parsing can show up in profiles pretty heavily
(up 4% in some cases). We can optimize it easily by implementing a fast path that does not spin up the full CSS
parser, like we already do for several other common value types.

The patch implements a fast path for transform(), transformX/Y/Z() and transform3D(). It speeds up parsing by >5x.

* css/CSSParser.cpp:
(WebCore):
(WebCore::parseTransformArguments):
(WebCore::parseTransformValue):
(WebCore::CSSParser::parseValue):

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

Source/WebCore/ChangeLog
Source/WebCore/css/CSSParser.cpp

index 95ed226..b391e52 100644 (file)
@@ -1,3 +1,22 @@
+2012-06-25  Antti Koivisto  <antti@apple.com>
+
+        Fast path for simple transform parsing
+        https://bugs.webkit.org/show_bug.cgi?id=89898
+
+        Reviewed by Anders Carlsson.
+
+        When manipulating transforms using script, the transform value parsing can show up in profiles pretty heavily 
+        (up 4% in some cases). We can optimize it easily by implementing a fast path that does not spin up the full CSS
+        parser, like we already do for several other common value types.
+        
+        The patch implements a fast path for transform(), transformX/Y/Z() and transform3D(). It speeds up parsing by >5x.
+
+        * css/CSSParser.cpp:
+        (WebCore):
+        (WebCore::parseTransformArguments):
+        (WebCore::parseTransformValue):
+        (WebCore::CSSParser::parseValue):
+
 2012-06-25  Kwang Yul Seo  <skyul@company100.net>
 
         Remove unused static function shadowFor
index 63658cb..caca3ea 100644 (file)
@@ -1013,6 +1013,74 @@ static bool parseKeywordValue(StylePropertySet* declaration, CSSPropertyID prope
     return true;
 }
 
+template <typename CharType>
+static bool parseTransformArguments(WebKitCSSTransformValue* transformValue, CharType* characters, unsigned length, unsigned start, unsigned expectedCount)
+{
+    while (expectedCount) {
+        size_t end = WTF::find(characters, length, expectedCount == 1 ? ')' : ',', start);
+        if (end == notFound || (expectedCount == 1 && end != length - 1))
+            return false;
+        unsigned argumentLength = end - start;
+        CSSPrimitiveValue::UnitTypes unit = CSSPrimitiveValue::CSS_NUMBER;
+        double number;
+        if (!parseSimpleLength(characters + start, argumentLength, unit, number))
+            return false;
+        if (unit != CSSPrimitiveValue::CSS_PX && (number || unit != CSSPrimitiveValue::CSS_NUMBER))
+            return false;
+        transformValue->append(cssValuePool().createValue(number, unit));
+        start = end + 1;
+        --expectedCount;
+    }
+    return true;
+}
+
+static bool parseTransformValue(StylePropertySet* properties, CSSPropertyID propertyID, const String& string, bool important)
+{
+    if (propertyID != CSSPropertyWebkitTransform)
+        return false;
+    static const unsigned shortestValidTransformStringLength = 12;
+    static const unsigned likelyMultipartTransformStringLengthCutoff = 32;
+    if (string.length() < shortestValidTransformStringLength || string.length() > likelyMultipartTransformStringLengthCutoff)
+        return false;
+    if (!string.startsWith("translate", false))
+        return false;
+    UChar c9 = toASCIILower(string[9]);
+    UChar c10 = toASCIILower(string[10]);
+
+    WebKitCSSTransformValue::TransformOperationType transformType;
+    unsigned expectedArgumentCount = 1;
+    unsigned argumentStart = 11;
+    if (c9 == 'x' && c10 == '(')
+        transformType = WebKitCSSTransformValue::TranslateXTransformOperation;
+    else if (c9 == 'y' && c10 == '(')
+        transformType = WebKitCSSTransformValue::TranslateYTransformOperation;
+    else if (c9 == 'z' && c10 == '(')
+        transformType = WebKitCSSTransformValue::TranslateZTransformOperation;
+    else if (c9 == '(') {
+        transformType = WebKitCSSTransformValue::TranslateTransformOperation;
+        expectedArgumentCount = 2;
+        argumentStart = 10;
+    } else if (c9 == '3' && c10 == 'd' && string[11] == '(') {
+        transformType = WebKitCSSTransformValue::Translate3DTransformOperation;
+        expectedArgumentCount = 3;
+        argumentStart = 12;
+    } else
+        return false;
+
+    RefPtr<WebKitCSSTransformValue> transformValue = WebKitCSSTransformValue::create(transformType);
+    bool success;
+    if (string.is8Bit())
+        success = parseTransformArguments(transformValue.get(), string.characters8(), string.length(), argumentStart, expectedArgumentCount);
+    else
+        success = parseTransformArguments(transformValue.get(), string.characters16(), string.length(), argumentStart, expectedArgumentCount);
+    if (!success)
+        return false;
+    RefPtr<CSSValueList> result = CSSValueList::createSpaceSeparated();
+    result->append(transformValue.release());
+    properties->addParsedProperty(CSSProperty(CSSPropertyWebkitTransform, result.release(), important));
+    return true;
+}
+
 PassRefPtr<CSSValueList> CSSParser::parseFontFaceValue(const AtomicString& string)
 {
     if (string.isEmpty())
@@ -1051,6 +1119,8 @@ bool CSSParser::parseValue(StylePropertySet* declaration, CSSPropertyID property
         return true;
     if (parseKeywordValue(declaration, propertyID, string, important, contextStyleSheet->parserContext()))
         return true;
+    if (parseTransformValue(declaration, propertyID, string, important))
+        return true;
 
     CSSParserContext context(cssParserMode);
     if (contextStyleSheet) {