[CSS Shaders] Parse custom filter function with the at-rule reference syntax
authormvujovic@adobe.com <mvujovic@adobe.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Thu, 31 Jan 2013 21:52:04 +0000 (21:52 +0000)
committermvujovic@adobe.com <mvujovic@adobe.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Thu, 31 Jan 2013 21:52:04 +0000 (21:52 +0000)
https://bugs.webkit.org/show_bug.cgi?id=108351

Reviewed by Dean Jackson.

Source/WebCore:

This patch implements the parsing for the new custom filter function "at-rule reference"
syntax. It does not implement the style resolution yet.

The custom function syntax has changed in the spec. Instead of including all of the custom
filter information inline, the custom function can now reference an @filter rule by name.
The custom function can still accept a list of parameters.

The syntax is defined as the following, where IDENT is the name of the @filter rule:
filter: custom(IDENT [, <param>]*)

In practice, it can look like this:
filter: custom(page-curl, direction 90, amount 0.5);

Spec: https://dvcs.w3.org/hg/FXTF/raw-file/tip/filters/index.html#customident-ltparamgt

Tests: css3/filters/custom-with-at-rule-syntax/parsing-custom-function-invalid.html
       css3/filters/custom-with-at-rule-syntax/parsing-custom-function-valid.html

* css/CSSParser.cpp:
(WebCore::CSSParser::parseCustomFilterParameters):
    Factor out a new function that just parses custom filter parameters. This is used by the
    previous inline syntax and the new at-rule reference syntax.
(WebCore):
(WebCore::CSSParser::parseCustomFilterFunctionWithAtRuleReferenceSyntax):
    This method parses the new at-rule reference syntax. When we remove the inline syntax,
    we can rename this method to "parseCustomFilterFunction" and call it directly.
(WebCore::CSSParser::parseCustomFilterFunctionWithInlineSyntax):
    Move the previous inline syntax parsing code into a new function.
(WebCore::CSSParser::parseCustomFilterFunction):
    This method looks ahead in the CSS parser values and decides whether to parse the custom
    function with the previous inline syntax or with the new at-rule reference syntax.
(WebCore::CSSParser::parseFilter):
    I renamed the "parseCustomFilter" method to "parseCustomFilterFunction" to be more
    explicit. Here, we update the name in the call site.
* css/CSSParser.h:
* css/StyleResolver.cpp:
(WebCore::StyleResolver::createCustomFilterOperationWithAtRuleReferenceSyntax):
    This is a stub method. In the future, style resolution for the at-rule reference
    syntax will happen here. When we remove the inline syntax, we can rename this method
(WebCore::StyleResolver::createCustomFilterOperationWithInlineSyntax):
    This method contains the previous inline syntax style resolution code.
(WebCore):
(WebCore::StyleResolver::createCustomFilterOperation):
    This method decides which syntax we should use to resolve the style.
* css/StyleResolver.h:
(StyleResolver):

LayoutTests:

Add positive and negative parsing tests for the new custom function syntax.

Add a new folder "css3/filters/custom-with-at-rule-syntax". This will contain all the tests
using the new custom filters at-rule syntax. We will gradually copy tests in
"css3/filters/custom" over to "css3/filters/custom-with-at-rule-syntax" and modify them to
use the new at-rule syntax. When all of the tests have been replicated using the new syntax,
we will remove the previous syntax and the tests in "css3/filters/custom".

* css3/filters/custom-with-at-rule-syntax/parsing-custom-function-invalid-expected.txt: Added.
* css3/filters/custom-with-at-rule-syntax/parsing-custom-function-invalid.html: Added.
* css3/filters/custom-with-at-rule-syntax/parsing-custom-function-valid-expected.txt: Added.
* css3/filters/custom-with-at-rule-syntax/parsing-custom-function-valid.html: Added.
* css3/filters/custom-with-at-rule-syntax/script-tests/parsing-custom-function-invalid.js: Added.
(testInvalidFilterRule):
* css3/filters/custom-with-at-rule-syntax/script-tests/parsing-custom-function-valid.js: Added.
(testFilterProperty):
* css3/filters/script-tests/custom-filter-parsing-common.js:
(heading):
    New function to print a heading to make groups of related parsing tests easier to see.
(shouldHaveConstructor):
    New function to check the JS type of an object on JSC as well as V8. This is intended to
    eventually replace shouldBeType, which works differently on V8 vs. JSC and requires us
    to create Chromium-specific expectations for the custom filters parsing tests.

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

13 files changed:
LayoutTests/ChangeLog
LayoutTests/css3/filters/custom-with-at-rule-syntax/parsing-custom-function-invalid-expected.txt [new file with mode: 0644]
LayoutTests/css3/filters/custom-with-at-rule-syntax/parsing-custom-function-invalid.html [new file with mode: 0644]
LayoutTests/css3/filters/custom-with-at-rule-syntax/parsing-custom-function-valid-expected.txt [new file with mode: 0644]
LayoutTests/css3/filters/custom-with-at-rule-syntax/parsing-custom-function-valid.html [new file with mode: 0644]
LayoutTests/css3/filters/custom-with-at-rule-syntax/script-tests/parsing-custom-function-invalid.js [new file with mode: 0644]
LayoutTests/css3/filters/custom-with-at-rule-syntax/script-tests/parsing-custom-function-valid.js [new file with mode: 0644]
LayoutTests/css3/filters/script-tests/custom-filter-parsing-common.js
Source/WebCore/ChangeLog
Source/WebCore/css/CSSParser.cpp
Source/WebCore/css/CSSParser.h
Source/WebCore/css/StyleResolver.cpp
Source/WebCore/css/StyleResolver.h

index 2a57a97..8f3988a 100644 (file)
@@ -1,3 +1,34 @@
+2013-01-31  Max Vujovic  <mvujovic@adobe.com>
+
+        [CSS Shaders] Parse custom filter function with the at-rule reference syntax
+        https://bugs.webkit.org/show_bug.cgi?id=108351
+
+        Reviewed by Dean Jackson.
+
+        Add positive and negative parsing tests for the new custom function syntax.
+
+        Add a new folder "css3/filters/custom-with-at-rule-syntax". This will contain all the tests
+        using the new custom filters at-rule syntax. We will gradually copy tests in
+        "css3/filters/custom" over to "css3/filters/custom-with-at-rule-syntax" and modify them to
+        use the new at-rule syntax. When all of the tests have been replicated using the new syntax,
+        we will remove the previous syntax and the tests in "css3/filters/custom".
+
+        * css3/filters/custom-with-at-rule-syntax/parsing-custom-function-invalid-expected.txt: Added.
+        * css3/filters/custom-with-at-rule-syntax/parsing-custom-function-invalid.html: Added.
+        * css3/filters/custom-with-at-rule-syntax/parsing-custom-function-valid-expected.txt: Added.
+        * css3/filters/custom-with-at-rule-syntax/parsing-custom-function-valid.html: Added.
+        * css3/filters/custom-with-at-rule-syntax/script-tests/parsing-custom-function-invalid.js: Added.
+        (testInvalidFilterRule):
+        * css3/filters/custom-with-at-rule-syntax/script-tests/parsing-custom-function-valid.js: Added.
+        (testFilterProperty):
+        * css3/filters/script-tests/custom-filter-parsing-common.js:
+        (heading):
+            New function to print a heading to make groups of related parsing tests easier to see.
+        (shouldHaveConstructor):
+            New function to check the JS type of an object on JSC as well as V8. This is intended to
+            eventually replace shouldBeType, which works differently on V8 vs. JSC and requires us
+            to create Chromium-specific expectations for the custom filters parsing tests.
+
 2013-01-31  Aurimas Liutikas  <aurimas@chromium.org>
 
         Editor::m_compositionNode not updated on HTMLInputElement::setValue()
diff --git a/LayoutTests/css3/filters/custom-with-at-rule-syntax/parsing-custom-function-invalid-expected.txt b/LayoutTests/css3/filters/custom-with-at-rule-syntax/parsing-custom-function-invalid-expected.txt
new file mode 100644 (file)
index 0000000..3a86778
--- /dev/null
@@ -0,0 +1,311 @@
+Test the parsing of custom() function of the -webkit-filter property.
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+
+
+========================================
+Custom function tests.
+========================================
+
+Empty custom function.
+custom()
+PASS styleRule.type is CSSRule.STYLE_RULE
+PASS declaration.length is 0
+PASS declaration.getPropertyValue('-webkit-filter') is null
+
+One comma in custom function.
+custom(,)
+PASS styleRule.type is CSSRule.STYLE_RULE
+PASS declaration.length is 0
+PASS declaration.getPropertyValue('-webkit-filter') is null
+
+Multiple commas in custom function.
+custom(,,)
+PASS styleRule.type is CSSRule.STYLE_RULE
+PASS declaration.length is 0
+PASS declaration.getPropertyValue('-webkit-filter') is null
+
+========================================
+Filter name tests.
+========================================
+
+No filter name with parameter.
+custom(n 1)
+PASS styleRule.type is CSSRule.STYLE_RULE
+PASS declaration.length is 0
+PASS declaration.getPropertyValue('-webkit-filter') is null
+
+Too many filter names.
+custom(my-filter-1 my-filter-2)
+PASS styleRule.type is CSSRule.STYLE_RULE
+PASS declaration.length is 0
+PASS declaration.getPropertyValue('-webkit-filter') is null
+
+Filter name as string.
+custom('my-filter-1')
+PASS styleRule.type is CSSRule.STYLE_RULE
+PASS declaration.length is 0
+PASS declaration.getPropertyValue('-webkit-filter') is null
+
+Filter name as number.
+custom(1)
+PASS styleRule.type is CSSRule.STYLE_RULE
+PASS declaration.length is 0
+PASS declaration.getPropertyValue('-webkit-filter') is null
+
+Space between filter name and parameter.
+custom(my-filter n 1)
+PASS styleRule.type is CSSRule.STYLE_RULE
+PASS declaration.length is 0
+PASS declaration.getPropertyValue('-webkit-filter') is null
+
+========================================
+Number parameter tests.
+========================================
+
+Too many parameter values.
+custom(my-filter, n 1 2 3 4 5)
+PASS styleRule.type is CSSRule.STYLE_RULE
+PASS declaration.length is 0
+PASS declaration.getPropertyValue('-webkit-filter') is null
+
+Invalid parameter type.
+custom(my-filter, n 1.0 2.0 'text')
+PASS styleRule.type is CSSRule.STYLE_RULE
+PASS declaration.length is 0
+PASS declaration.getPropertyValue('-webkit-filter') is null
+
+No parameter definition after comma.
+custom(my-filter,)
+PASS styleRule.type is CSSRule.STYLE_RULE
+PASS declaration.length is 0
+PASS declaration.getPropertyValue('-webkit-filter') is null
+
+No parameter definition with two commas.
+custom(my-filter,,)
+PASS styleRule.type is CSSRule.STYLE_RULE
+PASS declaration.length is 0
+PASS declaration.getPropertyValue('-webkit-filter') is null
+
+No parameter definition before valid parameter defintion.
+custom(my-filter, , n 1)
+PASS styleRule.type is CSSRule.STYLE_RULE
+PASS declaration.length is 0
+PASS declaration.getPropertyValue('-webkit-filter') is null
+
+No parameter value.
+custom(my-filter, n)
+PASS styleRule.type is CSSRule.STYLE_RULE
+PASS declaration.length is 0
+PASS declaration.getPropertyValue('-webkit-filter') is null
+
+No parameter value with multiple parameters.
+custom(my-filter, n1, n2, n3)
+PASS styleRule.type is CSSRule.STYLE_RULE
+PASS declaration.length is 0
+PASS declaration.getPropertyValue('-webkit-filter') is null
+
+========================================
+Transform parameter tests.
+========================================
+
+One invalid transform function.
+custom(my-filter, t invalid-rotate(0deg))
+PASS styleRule.type is CSSRule.STYLE_RULE
+PASS declaration.length is 0
+PASS declaration.getPropertyValue('-webkit-filter') is null
+
+Multiple invalid transform functions.
+custom(my-filter, t invalid-rotate(0deg) invalid-perspective(0))
+PASS styleRule.type is CSSRule.STYLE_RULE
+PASS declaration.length is 0
+PASS declaration.getPropertyValue('-webkit-filter') is null
+
+Invalid transform function between valid ones.
+custom(my-filter, t rotate(0deg) invalid-rotate(0deg) perspective(0))
+PASS styleRule.type is CSSRule.STYLE_RULE
+PASS declaration.length is 0
+PASS declaration.getPropertyValue('-webkit-filter') is null
+
+Valid transform function between invalid ones.
+custom(my-filter, t invalid-rotate(0deg) perspective(0) invalid-translate(0))
+PASS styleRule.type is CSSRule.STYLE_RULE
+PASS declaration.length is 0
+PASS declaration.getPropertyValue('-webkit-filter') is null
+
+Valid transform function without leading comma.
+custom(my-filter t perspective(0))
+PASS styleRule.type is CSSRule.STYLE_RULE
+PASS declaration.length is 0
+PASS declaration.getPropertyValue('-webkit-filter') is null
+
+Valid transform function with trailing comma.
+custom(my-filter, t perspective(0),)
+PASS styleRule.type is CSSRule.STYLE_RULE
+PASS declaration.length is 0
+PASS declaration.getPropertyValue('-webkit-filter') is null
+
+Valid transform function with trailing comma and without leading comma.
+custom(my-filter t perspective(0),)
+PASS styleRule.type is CSSRule.STYLE_RULE
+PASS declaration.length is 0
+PASS declaration.getPropertyValue('-webkit-filter') is null
+
+Invalid transform with trailing comma.
+custom(my-filter, t invalid-rotate(0deg),)
+PASS styleRule.type is CSSRule.STYLE_RULE
+PASS declaration.length is 0
+PASS declaration.getPropertyValue('-webkit-filter') is null
+
+Invalid transform without leading comma.
+custom(my-filter t invalid_rotate(0deg))
+PASS styleRule.type is CSSRule.STYLE_RULE
+PASS declaration.length is 0
+PASS declaration.getPropertyValue('-webkit-filter') is null
+
+Valid transform with invalid characters
+custom(my-filter,t rotate(0deg) *.-,)
+PASS styleRule.type is CSSRule.STYLE_RULE
+PASS declaration.length is 0
+PASS declaration.getPropertyValue('-webkit-filter') is null
+
+========================================
+Array parameter tests.
+========================================
+
+Empty array.
+custom(my-filter, a array())
+PASS styleRule.type is CSSRule.STYLE_RULE
+PASS declaration.length is 0
+PASS declaration.getPropertyValue('-webkit-filter') is null
+
+One comma in array.
+custom(my-filter, a array(,))
+PASS styleRule.type is CSSRule.STYLE_RULE
+PASS declaration.length is 0
+PASS declaration.getPropertyValue('-webkit-filter') is null
+
+Multiple commas in array.
+custom(my-filter, a array(,,,))
+PASS styleRule.type is CSSRule.STYLE_RULE
+PASS declaration.length is 0
+PASS declaration.getPropertyValue('-webkit-filter') is null
+
+Multiple commas with a value in array.
+custom(my-filter, a array(,,1,))
+PASS styleRule.type is CSSRule.STYLE_RULE
+PASS declaration.length is 0
+PASS declaration.getPropertyValue('-webkit-filter') is null
+
+One comma followed by a negative value in array.
+custom(my-filter, a array(,-4))
+PASS styleRule.type is CSSRule.STYLE_RULE
+PASS declaration.length is 0
+PASS declaration.getPropertyValue('-webkit-filter') is null
+
+One comma followed by a value in array.
+custom(my-filter, a array(,4))
+PASS styleRule.type is CSSRule.STYLE_RULE
+PASS declaration.length is 0
+PASS declaration.getPropertyValue('-webkit-filter') is null
+
+One negative value followed by a comma in array.
+custom(my-filter, a array(-4,))
+PASS styleRule.type is CSSRule.STYLE_RULE
+PASS declaration.length is 0
+PASS declaration.getPropertyValue('-webkit-filter') is null
+
+One value followed by a comma in array.
+custom(my-filter, a array(4,))
+PASS styleRule.type is CSSRule.STYLE_RULE
+PASS declaration.length is 0
+PASS declaration.getPropertyValue('-webkit-filter') is null
+
+Valid values followed by a comma in array.
+custom(my-filter, a array(1, 2, 3, 4,))
+PASS styleRule.type is CSSRule.STYLE_RULE
+PASS declaration.length is 0
+PASS declaration.getPropertyValue('-webkit-filter') is null
+
+Valid values followed by a letter in array.
+custom(my-filter, a array(1, 2, 3, 4, x))
+PASS styleRule.type is CSSRule.STYLE_RULE
+PASS declaration.length is 0
+PASS declaration.getPropertyValue('-webkit-filter') is null
+
+Two commas as separator between values in array.
+custom(my-filter, a array(1, 2, , 4))
+PASS styleRule.type is CSSRule.STYLE_RULE
+PASS declaration.length is 0
+PASS declaration.getPropertyValue('-webkit-filter') is null
+
+Leading comma in array.
+custom(my-filter, a array(,1, 2, 3, 4))
+PASS styleRule.type is CSSRule.STYLE_RULE
+PASS declaration.length is 0
+PASS declaration.getPropertyValue('-webkit-filter') is null
+
+Semicolon separated values in array.
+custom(my-filter, a array(1; 2; 3; 4))
+PASS styleRule.type is CSSRule.STYLE_RULE
+PASS declaration.length is 0
+PASS declaration.getPropertyValue('-webkit-filter') is null
+
+Space separated values in array.
+custom(my-filter, a array(1 2 3 4))
+PASS styleRule.type is CSSRule.STYLE_RULE
+PASS declaration.length is 0
+PASS declaration.getPropertyValue('-webkit-filter') is null
+
+Space separated values with comma terminator in array.
+custom(my-filter, a array(1 2 3 4,))
+PASS styleRule.type is CSSRule.STYLE_RULE
+PASS declaration.length is 0
+PASS declaration.getPropertyValue('-webkit-filter') is null
+
+Space separated values with leading comma in array.
+custom(my-filter, a array(, 1 2 3 4))
+PASS styleRule.type is CSSRule.STYLE_RULE
+PASS declaration.length is 0
+PASS declaration.getPropertyValue('-webkit-filter') is null
+
+NaN in array.
+custom(my-filter, a array(NaN))
+PASS styleRule.type is CSSRule.STYLE_RULE
+PASS declaration.length is 0
+PASS declaration.getPropertyValue('-webkit-filter') is null
+
+NaN between valid values in array.
+custom(my-filter, a array(1, 2, NaN, 3))
+PASS styleRule.type is CSSRule.STYLE_RULE
+PASS declaration.length is 0
+PASS declaration.getPropertyValue('-webkit-filter') is null
+
+Invalid value 'none' in array.
+custom(my-filter, a array(none))
+PASS styleRule.type is CSSRule.STYLE_RULE
+PASS declaration.length is 0
+PASS declaration.getPropertyValue('-webkit-filter') is null
+
+Invalid value unit 'px' in array.
+custom(my-filter, a array(1px))
+PASS styleRule.type is CSSRule.STYLE_RULE
+PASS declaration.length is 0
+PASS declaration.getPropertyValue('-webkit-filter') is null
+
+Invalid value unit 'deg' in array.
+custom(my-filter, a array(1deg))
+PASS styleRule.type is CSSRule.STYLE_RULE
+PASS declaration.length is 0
+PASS declaration.getPropertyValue('-webkit-filter') is null
+
+Invalid value unit 'px' in array after valid values.
+custom(my-filter, a array(1, 2, 3, 4px))
+PASS styleRule.type is CSSRule.STYLE_RULE
+PASS declaration.length is 0
+PASS declaration.getPropertyValue('-webkit-filter') is null
+PASS successfullyParsed is true
+
+TEST COMPLETE
+
diff --git a/LayoutTests/css3/filters/custom-with-at-rule-syntax/parsing-custom-function-invalid.html b/LayoutTests/css3/filters/custom-with-at-rule-syntax/parsing-custom-function-invalid.html
new file mode 100644 (file)
index 0000000..bd18cca
--- /dev/null
@@ -0,0 +1,14 @@
+<!doctype html>
+<html>
+<head>
+<link rel="stylesheet" href="../../../fast/js/resources/js-test-style.css">
+<script src="../../../fast/js/resources/js-test-pre.js"></script>
+</head>
+<body>
+<p id="description"></p>
+<div id="console"></div>
+<script src="../script-tests/custom-filter-parsing-common.js"></script>
+<script src="script-tests/parsing-custom-function-invalid.js"></script>
+<script src="../../../fast/js/resources/js-test-post.js"></script>
+</body>
+</html>
diff --git a/LayoutTests/css3/filters/custom-with-at-rule-syntax/parsing-custom-function-valid-expected.txt b/LayoutTests/css3/filters/custom-with-at-rule-syntax/parsing-custom-function-valid-expected.txt
new file mode 100644 (file)
index 0000000..3d8f51e
--- /dev/null
@@ -0,0 +1,245 @@
+Test the parsing of the custom() function of the -webkit-filter property.
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+
+
+========================================
+Custom function tests.
+========================================
+
+Custom function in CAPS.
+CUSTOM(my-filter)
+PASS styleRule.type is CSSRule.STYLE_RULE
+PASS styleDeclaration.length is 1
+PASS styleDeclaration.getPropertyValue('-webkit-filter') is 'custom(my-filter)'
+PASS filterPropertyValue instanceof CSSValueList is true
+PASS filterPropertyValue.constructor === CSSValueList is true
+PASS filterPropertyValue.__proto__ === CSSValueList.prototype is true
+PASS filterPropertyValue.length is 1
+PASS subValue.operationType is WebKitCSSFilterValue.CSS_FILTER_CUSTOM
+PASS subValue.cssText is 'custom(my-filter)'
+
+========================================
+Filter chain tests.
+========================================
+
+Custom function in middle of filter chain.
+grayscale() custom(my-filter) sepia()
+PASS styleRule.type is CSSRule.STYLE_RULE
+PASS styleDeclaration.length is 1
+PASS styleDeclaration.getPropertyValue('-webkit-filter') is 'grayscale() custom(my-filter) sepia()'
+PASS filterPropertyValue instanceof CSSValueList is true
+PASS filterPropertyValue.constructor === CSSValueList is true
+PASS filterPropertyValue.__proto__ === CSSValueList.prototype is true
+PASS filterPropertyValue.length is 3
+PASS subValue.operationType is WebKitCSSFilterValue.CSS_FILTER_GRAYSCALE
+PASS subValue.cssText is 'grayscale()'
+PASS subValue.operationType is WebKitCSSFilterValue.CSS_FILTER_CUSTOM
+PASS subValue.cssText is 'custom(my-filter)'
+PASS subValue.operationType is WebKitCSSFilterValue.CSS_FILTER_SEPIA
+PASS subValue.cssText is 'sepia()'
+
+========================================
+Filter name tests.
+========================================
+
+Filter name only.
+custom(my-filter)
+PASS styleRule.type is CSSRule.STYLE_RULE
+PASS styleDeclaration.length is 1
+PASS styleDeclaration.getPropertyValue('-webkit-filter') is 'custom(my-filter)'
+PASS filterPropertyValue instanceof CSSValueList is true
+PASS filterPropertyValue.constructor === CSSValueList is true
+PASS filterPropertyValue.__proto__ === CSSValueList.prototype is true
+PASS filterPropertyValue.length is 1
+PASS subValue.operationType is WebKitCSSFilterValue.CSS_FILTER_CUSTOM
+PASS subValue.cssText is 'custom(my-filter)'
+
+Filter name as CSS 'none' keyword.
+custom(none)
+PASS styleRule.type is CSSRule.STYLE_RULE
+PASS styleDeclaration.length is 1
+PASS styleDeclaration.getPropertyValue('-webkit-filter') is 'custom(none)'
+PASS filterPropertyValue instanceof CSSValueList is true
+PASS filterPropertyValue.constructor === CSSValueList is true
+PASS filterPropertyValue.__proto__ === CSSValueList.prototype is true
+PASS filterPropertyValue.length is 1
+PASS subValue.operationType is WebKitCSSFilterValue.CSS_FILTER_CUSTOM
+PASS subValue.cssText is 'custom(none)'
+
+========================================
+Number parameter tests.
+========================================
+
+Integer parameters.
+custom(my-filter, n1 1, n2 2 3, n3 3 4 5, n4 4 5 6 7)
+PASS styleRule.type is CSSRule.STYLE_RULE
+PASS styleDeclaration.length is 1
+PASS styleDeclaration.getPropertyValue('-webkit-filter') is 'custom(my-filter, n1 1, n2 2 3, n3 3 4 5, n4 4 5 6 7)'
+PASS filterPropertyValue instanceof CSSValueList is true
+PASS filterPropertyValue.constructor === CSSValueList is true
+PASS filterPropertyValue.__proto__ === CSSValueList.prototype is true
+PASS filterPropertyValue.length is 1
+PASS subValue.operationType is WebKitCSSFilterValue.CSS_FILTER_CUSTOM
+PASS subValue.cssText is 'custom(my-filter, n1 1, n2 2 3, n3 3 4 5, n4 4 5 6 7)'
+
+Float parameters.
+custom(my-filter, n1 1.1, n2 2.2 3.3, n3 3.1 4.1 5.1, n4 4.1 5.2 6.3 7.4)
+PASS styleRule.type is CSSRule.STYLE_RULE
+PASS styleDeclaration.length is 1
+PASS styleDeclaration.getPropertyValue('-webkit-filter') is 'custom(my-filter, n1 1.1, n2 2.2 3.3, n3 3.1 4.1 5.1, n4 4.1 5.2 6.3 7.4)'
+PASS filterPropertyValue instanceof CSSValueList is true
+PASS filterPropertyValue.constructor === CSSValueList is true
+PASS filterPropertyValue.__proto__ === CSSValueList.prototype is true
+PASS filterPropertyValue.length is 1
+PASS subValue.operationType is WebKitCSSFilterValue.CSS_FILTER_CUSTOM
+PASS subValue.cssText is 'custom(my-filter, n1 1.1, n2 2.2 3.3, n3 3.1 4.1 5.1, n4 4.1 5.2 6.3 7.4)'
+
+Parameter name same as CSS keyword.
+custom(my-filter, background 0 1 0 1)
+PASS styleRule.type is CSSRule.STYLE_RULE
+PASS styleDeclaration.length is 1
+PASS styleDeclaration.getPropertyValue('-webkit-filter') is 'custom(my-filter, background 0 1 0 1)'
+PASS filterPropertyValue instanceof CSSValueList is true
+PASS filterPropertyValue.constructor === CSSValueList is true
+PASS filterPropertyValue.__proto__ === CSSValueList.prototype is true
+PASS filterPropertyValue.length is 1
+PASS subValue.operationType is WebKitCSSFilterValue.CSS_FILTER_CUSTOM
+PASS subValue.cssText is 'custom(my-filter, background 0 1 0 1)'
+
+========================================
+Transform parameter tests.
+========================================
+
+Transform parameter with one transform function.
+custom(my-filter, t rotate(0deg))
+PASS styleRule.type is CSSRule.STYLE_RULE
+PASS styleDeclaration.length is 1
+PASS styleDeclaration.getPropertyValue('-webkit-filter') is 'custom(my-filter, t rotate(0deg))'
+PASS filterPropertyValue instanceof CSSValueList is true
+PASS filterPropertyValue.constructor === CSSValueList is true
+PASS filterPropertyValue.__proto__ === CSSValueList.prototype is true
+PASS filterPropertyValue.length is 1
+PASS subValue.operationType is WebKitCSSFilterValue.CSS_FILTER_CUSTOM
+PASS subValue.cssText is 'custom(my-filter, t rotate(0deg))'
+
+Transform parameter with multiple transform functions.
+custom(my-filter, t rotate(0deg) perspective(0) scale(0, 0) translate(0px, 0px))
+PASS styleRule.type is CSSRule.STYLE_RULE
+PASS styleDeclaration.length is 1
+PASS styleDeclaration.getPropertyValue('-webkit-filter') is 'custom(my-filter, t rotate(0deg) perspective(0) scale(0, 0) translate(0px, 0px))'
+PASS filterPropertyValue instanceof CSSValueList is true
+PASS filterPropertyValue.constructor === CSSValueList is true
+PASS filterPropertyValue.__proto__ === CSSValueList.prototype is true
+PASS filterPropertyValue.length is 1
+PASS subValue.operationType is WebKitCSSFilterValue.CSS_FILTER_CUSTOM
+PASS subValue.cssText is 'custom(my-filter, t rotate(0deg) perspective(0) scale(0, 0) translate(0px, 0px))'
+
+Mulitple transform parameters.
+custom(my-filter, t1 rotate(0deg), t2 rotate(0deg))
+PASS styleRule.type is CSSRule.STYLE_RULE
+PASS styleDeclaration.length is 1
+PASS styleDeclaration.getPropertyValue('-webkit-filter') is 'custom(my-filter, t1 rotate(0deg), t2 rotate(0deg))'
+PASS filterPropertyValue instanceof CSSValueList is true
+PASS filterPropertyValue.constructor === CSSValueList is true
+PASS filterPropertyValue.__proto__ === CSSValueList.prototype is true
+PASS filterPropertyValue.length is 1
+PASS subValue.operationType is WebKitCSSFilterValue.CSS_FILTER_CUSTOM
+PASS subValue.cssText is 'custom(my-filter, t1 rotate(0deg), t2 rotate(0deg))'
+
+========================================
+Array parameter tests.
+========================================
+
+Array parameter with name 'array'.
+custom(my-filter, array array(1))
+PASS styleRule.type is CSSRule.STYLE_RULE
+PASS styleDeclaration.length is 1
+PASS styleDeclaration.getPropertyValue('-webkit-filter') is 'custom(my-filter, array array(1))'
+PASS filterPropertyValue instanceof CSSValueList is true
+PASS filterPropertyValue.constructor === CSSValueList is true
+PASS filterPropertyValue.__proto__ === CSSValueList.prototype is true
+PASS filterPropertyValue.length is 1
+PASS subValue.operationType is WebKitCSSFilterValue.CSS_FILTER_CUSTOM
+PASS subValue.cssText is 'custom(my-filter, array array(1))'
+
+Array parameter with one positive integer.
+custom(my-filter, a array(1))
+PASS styleRule.type is CSSRule.STYLE_RULE
+PASS styleDeclaration.length is 1
+PASS styleDeclaration.getPropertyValue('-webkit-filter') is 'custom(my-filter, a array(1))'
+PASS filterPropertyValue instanceof CSSValueList is true
+PASS filterPropertyValue.constructor === CSSValueList is true
+PASS filterPropertyValue.__proto__ === CSSValueList.prototype is true
+PASS filterPropertyValue.length is 1
+PASS subValue.operationType is WebKitCSSFilterValue.CSS_FILTER_CUSTOM
+PASS subValue.cssText is 'custom(my-filter, a array(1))'
+
+Array parameter with one negative float.
+custom(my-filter, a array(-1.01))
+PASS styleRule.type is CSSRule.STYLE_RULE
+PASS styleDeclaration.length is 1
+PASS styleDeclaration.getPropertyValue('-webkit-filter') is 'custom(my-filter, a array(-1.01))'
+PASS filterPropertyValue instanceof CSSValueList is true
+PASS filterPropertyValue.constructor === CSSValueList is true
+PASS filterPropertyValue.__proto__ === CSSValueList.prototype is true
+PASS filterPropertyValue.length is 1
+PASS subValue.operationType is WebKitCSSFilterValue.CSS_FILTER_CUSTOM
+PASS subValue.cssText is 'custom(my-filter, a array(-1.01))'
+
+Array parameter with multiple positive integers.
+custom(my-filter, a array(1, 2, 3, 4, 5))
+PASS styleRule.type is CSSRule.STYLE_RULE
+PASS styleDeclaration.length is 1
+PASS styleDeclaration.getPropertyValue('-webkit-filter') is 'custom(my-filter, a array(1, 2, 3, 4, 5))'
+PASS filterPropertyValue instanceof CSSValueList is true
+PASS filterPropertyValue.constructor === CSSValueList is true
+PASS filterPropertyValue.__proto__ === CSSValueList.prototype is true
+PASS filterPropertyValue.length is 1
+PASS subValue.operationType is WebKitCSSFilterValue.CSS_FILTER_CUSTOM
+PASS subValue.cssText is 'custom(my-filter, a array(1, 2, 3, 4, 5))'
+
+Array parameter with multiple signed floats.
+custom(my-filter, a array(1, -2.2, 3.14, 0.4, 5))
+PASS styleRule.type is CSSRule.STYLE_RULE
+PASS styleDeclaration.length is 1
+PASS styleDeclaration.getPropertyValue('-webkit-filter') is 'custom(my-filter, a array(1, -2.2, 3.14, 0.4, 5))'
+PASS filterPropertyValue instanceof CSSValueList is true
+PASS filterPropertyValue.constructor === CSSValueList is true
+PASS filterPropertyValue.__proto__ === CSSValueList.prototype is true
+PASS filterPropertyValue.length is 1
+PASS subValue.operationType is WebKitCSSFilterValue.CSS_FILTER_CUSTOM
+PASS subValue.cssText is 'custom(my-filter, a array(1, -2.2, 3.14, 0.4, 5))'
+
+Multiple array parameters.
+custom(my-filter, a1 array(1, -2.2, 3.14, 0.4, 5), a2 array(1, 2, 3))
+PASS styleRule.type is CSSRule.STYLE_RULE
+PASS styleDeclaration.length is 1
+PASS styleDeclaration.getPropertyValue('-webkit-filter') is 'custom(my-filter, a1 array(1, -2.2, 3.14, 0.4, 5), a2 array(1, 2, 3))'
+PASS filterPropertyValue instanceof CSSValueList is true
+PASS filterPropertyValue.constructor === CSSValueList is true
+PASS filterPropertyValue.__proto__ === CSSValueList.prototype is true
+PASS filterPropertyValue.length is 1
+PASS subValue.operationType is WebKitCSSFilterValue.CSS_FILTER_CUSTOM
+PASS subValue.cssText is 'custom(my-filter, a1 array(1, -2.2, 3.14, 0.4, 5), a2 array(1, 2, 3))'
+
+========================================
+Combined parameter tests.
+========================================
+
+Number parameter, transform parameter, and array parameter.
+custom(my-filter, n 1, t rotate(0deg), a array(1))
+PASS styleRule.type is CSSRule.STYLE_RULE
+PASS styleDeclaration.length is 1
+PASS styleDeclaration.getPropertyValue('-webkit-filter') is 'custom(my-filter, n 1, t rotate(0deg), a array(1))'
+PASS filterPropertyValue instanceof CSSValueList is true
+PASS filterPropertyValue.constructor === CSSValueList is true
+PASS filterPropertyValue.__proto__ === CSSValueList.prototype is true
+PASS filterPropertyValue.length is 1
+PASS subValue.operationType is WebKitCSSFilterValue.CSS_FILTER_CUSTOM
+PASS subValue.cssText is 'custom(my-filter, n 1, t rotate(0deg), a array(1))'
+PASS successfullyParsed is true
+
+TEST COMPLETE
+
diff --git a/LayoutTests/css3/filters/custom-with-at-rule-syntax/parsing-custom-function-valid.html b/LayoutTests/css3/filters/custom-with-at-rule-syntax/parsing-custom-function-valid.html
new file mode 100644 (file)
index 0000000..bf074eb
--- /dev/null
@@ -0,0 +1,14 @@
+<!doctype html>
+<html>
+<head>
+<link rel="stylesheet" href="../../../fast/js/resources/js-test-style.css">
+<script src="../../../fast/js/resources/js-test-pre.js"></script>
+</head>
+<body>
+<p id="description"></p>
+<div id="console"></div>
+<script src="../script-tests/custom-filter-parsing-common.js"></script>
+<script src="script-tests/parsing-custom-function-valid.js"></script>
+<script src="../../../fast/js/resources/js-test-post.js"></script>
+</body>
+</html>
diff --git a/LayoutTests/css3/filters/custom-with-at-rule-syntax/script-tests/parsing-custom-function-invalid.js b/LayoutTests/css3/filters/custom-with-at-rule-syntax/script-tests/parsing-custom-function-invalid.js
new file mode 100644 (file)
index 0000000..f1d8fe9
--- /dev/null
@@ -0,0 +1,76 @@
+description("Test the parsing of custom() function of the -webkit-filter property.");
+
+// These have to be global for the test helpers to see them.
+var styleRule, declaration;
+
+function testInvalidFilterRule(description, rule)
+{
+    debug("\n" + description + "\n" + rule);
+
+    stylesheet.insertRule("body { -webkit-filter: " + rule + "; }", 0);
+
+    styleRule = stylesheet.cssRules.item(0);
+    shouldBe("styleRule.type", "CSSRule.STYLE_RULE");
+
+    declaration = styleRule.style;
+    shouldBe("declaration.length", "0");
+    shouldBe("declaration.getPropertyValue('-webkit-filter')", "null");
+}
+
+heading("Custom function tests.");
+testInvalidFilterRule("Empty custom function.", "custom()");
+testInvalidFilterRule("One comma in custom function.", "custom(,)");
+testInvalidFilterRule("Multiple commas in custom function.", "custom(,,)");
+
+heading("Filter name tests.");
+testInvalidFilterRule("No filter name with parameter.", "custom(n 1)");
+testInvalidFilterRule("Too many filter names.", "custom(my-filter-1 my-filter-2)");
+testInvalidFilterRule("Filter name as string.", "custom('my-filter-1')");
+testInvalidFilterRule("Filter name as number.", "custom(1)");
+testInvalidFilterRule("Space between filter name and parameter.", "custom(my-filter n 1)");
+
+heading("Number parameter tests.");
+testInvalidFilterRule("Too many parameter values.", "custom(my-filter, n 1 2 3 4 5)");
+testInvalidFilterRule("Invalid parameter type.", "custom(my-filter, n 1.0 2.0 'text')");
+
+testInvalidFilterRule("No parameter definition after comma.", "custom(my-filter,)");
+testInvalidFilterRule("No parameter definition with two commas.", "custom(my-filter,,)");
+testInvalidFilterRule("No parameter definition before valid parameter defintion.", "custom(my-filter, , n 1)");
+testInvalidFilterRule("No parameter value.", "custom(my-filter, n)");
+testInvalidFilterRule("No parameter value with multiple parameters.", "custom(my-filter, n1, n2, n3)");
+
+heading("Transform parameter tests.");
+testInvalidFilterRule("One invalid transform function.", "custom(my-filter, t invalid-rotate(0deg))");
+testInvalidFilterRule("Multiple invalid transform functions.", "custom(my-filter, t invalid-rotate(0deg) invalid-perspective(0))");
+testInvalidFilterRule("Invalid transform function between valid ones.", "custom(my-filter, t rotate(0deg) invalid-rotate(0deg) perspective(0))");
+testInvalidFilterRule("Valid transform function between invalid ones.", "custom(my-filter, t invalid-rotate(0deg) perspective(0) invalid-translate(0))");
+testInvalidFilterRule("Valid transform function without leading comma.", "custom(my-filter t perspective(0))");
+testInvalidFilterRule("Valid transform function with trailing comma.", "custom(my-filter, t perspective(0),)");
+testInvalidFilterRule("Valid transform function with trailing comma and without leading comma.", "custom(my-filter t perspective(0),)");
+testInvalidFilterRule("Invalid transform with trailing comma.", "custom(my-filter, t invalid-rotate(0deg),)");
+testInvalidFilterRule("Invalid transform without leading comma.", "custom(my-filter t invalid_rotate(0deg))");
+testInvalidFilterRule("Valid transform with invalid characters", "custom(my-filter,t rotate(0deg) *.-,)");
+
+heading("Array parameter tests.");
+testInvalidFilterRule("Empty array.", "custom(my-filter, a array())");
+testInvalidFilterRule("One comma in array.", "custom(my-filter, a array(,))");
+testInvalidFilterRule("Multiple commas in array.", "custom(my-filter, a array(,,,))");
+testInvalidFilterRule("Multiple commas with a value in array.", "custom(my-filter, a array(,,1,))");
+testInvalidFilterRule("One comma followed by a negative value in array.", "custom(my-filter, a array(,-4))");
+testInvalidFilterRule("One comma followed by a value in array.", "custom(my-filter, a array(,4))");
+testInvalidFilterRule("One negative value followed by a comma in array.", "custom(my-filter, a array(-4,))");
+testInvalidFilterRule("One value followed by a comma in array.", "custom(my-filter, a array(4,))");
+testInvalidFilterRule("Valid values followed by a comma in array.", "custom(my-filter, a array(1, 2, 3, 4,))");
+testInvalidFilterRule("Valid values followed by a letter in array.", "custom(my-filter, a array(1, 2, 3, 4, x))");
+testInvalidFilterRule("Two commas as separator between values in array.", "custom(my-filter, a array(1, 2, , 4))");
+testInvalidFilterRule("Leading comma in array.", "custom(my-filter, a array(,1, 2, 3, 4))");
+testInvalidFilterRule("Semicolon separated values in array.", "custom(my-filter, a array(1; 2; 3; 4))");
+testInvalidFilterRule("Space separated values in array.", "custom(my-filter, a array(1 2 3 4))");
+testInvalidFilterRule("Space separated values with comma terminator in array.", "custom(my-filter, a array(1 2 3 4,))");
+testInvalidFilterRule("Space separated values with leading comma in array.", "custom(my-filter, a array(, 1 2 3 4))");
+testInvalidFilterRule("NaN in array.", "custom(my-filter, a array(NaN))");
+testInvalidFilterRule("NaN between valid values in array.", "custom(my-filter, a array(1, 2, NaN, 3))");
+testInvalidFilterRule("Invalid value 'none' in array.", "custom(my-filter, a array(none))");
+testInvalidFilterRule("Invalid value unit 'px' in array.", "custom(my-filter, a array(1px))");
+testInvalidFilterRule("Invalid value unit 'deg' in array.", "custom(my-filter, a array(1deg))");
+testInvalidFilterRule("Invalid value unit 'px' in array after valid values.", "custom(my-filter, a array(1, 2, 3, 4px))");
diff --git a/LayoutTests/css3/filters/custom-with-at-rule-syntax/script-tests/parsing-custom-function-valid.js b/LayoutTests/css3/filters/custom-with-at-rule-syntax/script-tests/parsing-custom-function-valid.js
new file mode 100644 (file)
index 0000000..d17d9fe
--- /dev/null
@@ -0,0 +1,105 @@
+description("Test the parsing of the custom() function of the -webkit-filter property.");
+
+// These have to be global for the test helpers to see them.
+var styleRule, styleDeclaration, filterPropertyValue, subValue;
+
+function testFilterProperty(description, propertyValue, expectedValue, expectedTypes, expectedTexts)
+{
+    debug("\n" + description + "\n" + propertyValue);
+
+    stylesheet.insertRule("body { -webkit-filter: " + propertyValue + "; }", 0);
+    styleRule = stylesheet.cssRules.item(0);
+    shouldBe("styleRule.type", "CSSRule.STYLE_RULE");
+
+    styleDeclaration = styleRule.style;
+    shouldBe("styleDeclaration.length", "1");
+    shouldBe("styleDeclaration.getPropertyValue('-webkit-filter')", "'" + expectedValue + "'");
+
+    filterPropertyValue = styleDeclaration.getPropertyCSSValue('-webkit-filter');
+    shouldHaveConstructor("filterPropertyValue", "CSSValueList");
+
+    if (!expectedTypes) {
+        expectedTypes = ["WebKitCSSFilterValue.CSS_FILTER_CUSTOM"];
+        expectedTexts = [expectedValue];
+    }
+    
+    shouldBe("filterPropertyValue.length", "" + expectedTypes.length); // shouldBe expects string arguments
+  
+    if (filterPropertyValue) {
+        for (var i = 0; i < expectedTypes.length; i++) {
+            subValue = filterPropertyValue[i];
+            shouldBe("subValue.operationType", expectedTypes[i]);
+            shouldBe("subValue.cssText", "'" + expectedTexts[i] + "'");
+        }
+    }
+}
+
+heading("Custom function tests.");
+testFilterProperty("Custom function in CAPS.",
+    "CUSTOM(my-filter)",
+    "custom(my-filter)");
+
+heading("Filter chain tests.")
+testFilterProperty("Custom function in middle of filter chain.",
+    "grayscale() custom(my-filter) sepia()", "grayscale() custom(my-filter) sepia()",
+    ["WebKitCSSFilterValue.CSS_FILTER_GRAYSCALE",
+    "WebKitCSSFilterValue.CSS_FILTER_CUSTOM",
+    "WebKitCSSFilterValue.CSS_FILTER_SEPIA"],
+    ["grayscale()",
+    "custom(my-filter)",
+    "sepia()"]);
+
+heading("Filter name tests.");
+testFilterProperty("Filter name only.",
+    "custom(my-filter)",
+    "custom(my-filter)");
+testFilterProperty("Filter name as CSS 'none' keyword.",
+    "custom(none)",
+    "custom(none)");
+
+heading("Number parameter tests.")
+testFilterProperty("Integer parameters.",
+    "custom(my-filter, n1 1, n2 2 3, n3 3 4 5, n4 4 5 6 7)",
+    "custom(my-filter, n1 1, n2 2 3, n3 3 4 5, n4 4 5 6 7)");
+testFilterProperty("Float parameters.",
+    "custom(my-filter, n1 1.1, n2 2.2 3.3, n3 3.1 4.1 5.1, n4 4.1 5.2 6.3 7.4)",
+    "custom(my-filter, n1 1.1, n2 2.2 3.3, n3 3.1 4.1 5.1, n4 4.1 5.2 6.3 7.4)");
+testFilterProperty("Parameter name same as CSS keyword.",
+    "custom(my-filter, background 0 1 0 1)",
+    "custom(my-filter, background 0 1 0 1)");
+
+heading("Transform parameter tests.")
+testFilterProperty("Transform parameter with one transform function.",
+    "custom(my-filter, t rotate(0deg))",
+    "custom(my-filter, t rotate(0deg))");
+testFilterProperty("Transform parameter with multiple transform functions.",
+    "custom(my-filter, t rotate(0deg) perspective(0) scale(0, 0) translate(0px, 0px))",
+    "custom(my-filter, t rotate(0deg) perspective(0) scale(0, 0) translate(0px, 0px))");
+testFilterProperty("Mulitple transform parameters.",
+    "custom(my-filter, t1 rotate(0deg), t2 rotate(0deg))",
+    "custom(my-filter, t1 rotate(0deg), t2 rotate(0deg))");
+
+heading("Array parameter tests.");
+testFilterProperty("Array parameter with name 'array'.",
+    "custom(my-filter, array array(1))",
+    "custom(my-filter, array array(1))");
+testFilterProperty("Array parameter with one positive integer.",
+    "custom(my-filter, a array(1))",
+    "custom(my-filter, a array(1))");
+testFilterProperty("Array parameter with one negative float.",
+    "custom(my-filter, a array(-1.01))",
+    "custom(my-filter, a array(-1.01))");
+testFilterProperty("Array parameter with multiple positive integers.",
+    "custom(my-filter, a array(1, 2, 3, 4, 5))",
+    "custom(my-filter, a array(1, 2, 3, 4, 5))");
+testFilterProperty("Array parameter with multiple signed floats.",
+    "custom(my-filter, a array(1, -2.2, 3.14, 0.4, 5))",
+    "custom(my-filter, a array(1, -2.2, 3.14, 0.4, 5))");
+testFilterProperty("Multiple array parameters.",
+    "custom(my-filter, a1 array(1, -2.2, 3.14, 0.4, 5), a2 array(1, 2, 3))",
+    "custom(my-filter, a1 array(1, -2.2, 3.14, 0.4, 5), a2 array(1, 2, 3))");
+
+heading("Combined parameter tests.");
+testFilterProperty("Number parameter, transform parameter, and array parameter.",
+    "custom(my-filter, n 1, t rotate(0deg), a array(1))",
+    "custom(my-filter, n 1, t rotate(0deg), a array(1))");
index f2d756d..a221f0c 100644 (file)
@@ -1,5 +1,13 @@
 // Helper functions.
 
+function heading(string)
+{
+    debug("");
+    debug("========================================");
+    debug(string);
+    debug("========================================");
+}
+
 function jsWrapperClass(node)
 {
     if (!node)
@@ -8,6 +16,8 @@ function jsWrapperClass(node)
     return string.substr(8, string.length - 9);
 }
 
+// FIXME: This type-checking approach fails on V8, which requires us to have Chromium-specific expectations.
+// We should use the shouldHaveConstructor function instead, which works regardless of JS engine.
 function shouldBeType(expression, className, prototypeName, constructorName)
 {
     if (!prototypeName)
@@ -19,8 +29,16 @@ function shouldBeType(expression, className, prototypeName, constructorName)
     shouldBe("jsWrapperClass(" + expression + ".constructor)", "'" + constructorName + "'");
 }
 
+function shouldHaveConstructor(expression, constructorName)
+{
+    shouldBeTrue(expression + " instanceof " + constructorName);
+    shouldBeTrue(expression + ".constructor === " + constructorName);
+    shouldBeTrue(expression + ".__proto__ === " + constructorName + ".prototype");
+}
+
 // Need to remove the base URL to avoid having local paths in the expected results.
-function removeBaseURL(src) {
+function removeBaseURL(src) 
+{
     var urlRegexp = /url\(([^\)]*)\)/g;
     return src.replace(urlRegexp, function(match, url) {
         return "url(" + url.substr(url.lastIndexOf("/") + 1) + ")";
index 7484f3d..53b7338 100644 (file)
@@ -1,3 +1,57 @@
+2013-01-31  Max Vujovic  <mvujovic@adobe.com>
+
+        [CSS Shaders] Parse custom filter function with the at-rule reference syntax
+        https://bugs.webkit.org/show_bug.cgi?id=108351
+
+        Reviewed by Dean Jackson.
+
+        This patch implements the parsing for the new custom filter function "at-rule reference"
+        syntax. It does not implement the style resolution yet.
+
+        The custom function syntax has changed in the spec. Instead of including all of the custom
+        filter information inline, the custom function can now reference an @filter rule by name.
+        The custom function can still accept a list of parameters.
+
+        The syntax is defined as the following, where IDENT is the name of the @filter rule:
+        filter: custom(IDENT [, <param>]*)
+
+        In practice, it can look like this:
+        filter: custom(page-curl, direction 90, amount 0.5);
+
+        Spec: https://dvcs.w3.org/hg/FXTF/raw-file/tip/filters/index.html#customident-ltparamgt
+
+        Tests: css3/filters/custom-with-at-rule-syntax/parsing-custom-function-invalid.html
+               css3/filters/custom-with-at-rule-syntax/parsing-custom-function-valid.html
+
+        * css/CSSParser.cpp:
+        (WebCore::CSSParser::parseCustomFilterParameters):
+            Factor out a new function that just parses custom filter parameters. This is used by the
+            previous inline syntax and the new at-rule reference syntax.
+        (WebCore):
+        (WebCore::CSSParser::parseCustomFilterFunctionWithAtRuleReferenceSyntax):
+            This method parses the new at-rule reference syntax. When we remove the inline syntax,
+            we can rename this method to "parseCustomFilterFunction" and call it directly.
+        (WebCore::CSSParser::parseCustomFilterFunctionWithInlineSyntax):
+            Move the previous inline syntax parsing code into a new function.
+        (WebCore::CSSParser::parseCustomFilterFunction):
+            This method looks ahead in the CSS parser values and decides whether to parse the custom
+            function with the previous inline syntax or with the new at-rule reference syntax.
+        (WebCore::CSSParser::parseFilter):
+            I renamed the "parseCustomFilter" method to "parseCustomFilterFunction" to be more
+            explicit. Here, we update the name in the call site.
+        * css/CSSParser.h:
+        * css/StyleResolver.cpp:
+        (WebCore::StyleResolver::createCustomFilterOperationWithAtRuleReferenceSyntax):
+            This is a stub method. In the future, style resolution for the at-rule reference
+            syntax will happen here. When we remove the inline syntax, we can rename this method 
+        (WebCore::StyleResolver::createCustomFilterOperationWithInlineSyntax):
+            This method contains the previous inline syntax style resolution code.
+        (WebCore):
+        (WebCore::StyleResolver::createCustomFilterOperation):
+            This method decides which syntax we should use to resolve the style.
+        * css/StyleResolver.h:
+        (StyleResolver):
+
 2013-01-31  Aurimas Liutikas  <aurimas@chromium.org>
 
         Editor::m_compositionNode not updated on HTMLInputElement::setValue()
index e66808d..17cea9b 100644 (file)
@@ -8277,15 +8277,127 @@ PassRefPtr<WebKitCSSMixFunctionValue> CSSParser::parseMixFunction(CSSParserValue
     return mixFunction;
 }
 
-PassRefPtr<WebKitCSSFilterValue> CSSParser::parseCustomFilter(CSSParserValue* value)
+PassRefPtr<CSSValueList> CSSParser::parseCustomFilterParameters(CSSParserValueList* argsList)
 {
+    //
+    // params:      [<param-def>[,<param-def>*]]
+    // param-def:   <param-name>wsp<param-value>
+    // param-name:  <ident>
+    // param-value: true|false[wsp+true|false]{0-3} |
+    //              <number>[wsp+<number>]{0-3} |
+    //              <array> |
+    //              <transform> |
+    //              <texture(<uri>)>
+    // array: 'array('<number>[wsp<number>]*')'
+    // css-3d-transform: <transform-function>;[<transform-function>]*
+    // transform:   <css-3d-transform> | <mat>
+    // mat:         'mat2('<number>(,<number>){3}')' | 
+    //              'mat3('<number>(,<number>){8}')' | 
+    //              'mat4('<number>(,<number>){15}')' )
+    //
+
+    RefPtr<CSSValueList> paramList = CSSValueList::createCommaSeparated();
+
+    while (CSSParserValue* arg = argsList->current()) {
+        if (arg->unit != CSSPrimitiveValue::CSS_IDENT)
+            return 0;
+
+        RefPtr<CSSValueList> parameter = CSSValueList::createSpaceSeparated();
+        parameter->append(createPrimitiveStringValue(arg));
+
+        arg = argsList->next();
+        if (!arg)
+            return 0;
+
+        RefPtr<CSSValue> parameterValue;
+
+        if (arg->unit == CSSParserValue::Function && arg->function) {
+            // FIXME: Implement parsing for the other parameter types.
+            // textures: https://bugs.webkit.org/show_bug.cgi?id=71442
+            // mat2, mat3, mat4: https://bugs.webkit.org/show_bug.cgi?id=71444
+            if (equalIgnoringCase(arg->function->name, "array(")) {
+                parameterValue = parseCustomFilterArrayFunction(arg);
+                // This parsing step only consumes function arguments,
+                // argsList is therefore moved forward explicitely.
+                argsList->next();
+            } else
+                parameterValue = parseCustomFilterTransform(argsList);
+        } else {
+            RefPtr<CSSValueList> paramValueList = CSSValueList::createSpaceSeparated();
+            arg = argsList->current();
+            while (arg) {
+                // If we hit a comma, it means that we finished this parameter's values.
+                if (isComma(arg))
+                    break;
+                if (!validUnit(arg, FNumber, CSSStrictMode))
+                    return 0;
+                paramValueList->append(cssValuePool().createValue(arg->fValue, CSSPrimitiveValue::CSS_NUMBER));
+                arg = argsList->next();
+            }
+            if (!paramValueList->length() || paramValueList->length() > 4)
+                return 0;
+            parameterValue = paramValueList.release();
+        }
+
+        if (!parameterValue || !acceptCommaOperator(argsList))
+            return 0;
+
+        parameter->append(parameterValue.release());
+        paramList->append(parameter.release());
+    }
+
+    return paramList;   
+}
+
+PassRefPtr<WebKitCSSFilterValue> CSSParser::parseCustomFilterFunctionWithAtRuleReferenceSyntax(CSSParserValue* value)
+{
+    //
+    // Custom filter function "at-rule reference" syntax:
+    //
+    // custom(<filter-name>wsp[,wsp<params>])
+    //
+    // filter-name: <filter-name>
+    // params: See the comment in CSSParser::parseCustomFilterParameters.
+    //
+
+    ASSERT(value->function);
+
     CSSParserValueList* argsList = value->function->args.get();
-    if (!argsList)
+    if (!argsList || !argsList->size())
+        return 0;
+
+    // 1. Parse the filter name.
+    CSSParserValue* arg = argsList->current();
+    if (arg->unit != CSSPrimitiveValue::CSS_IDENT)
         return 0;
 
     RefPtr<WebKitCSSFilterValue> filterValue = WebKitCSSFilterValue::create(WebKitCSSFilterValue::CustomFilterOperation);
 
-    // Custom filter syntax:
+    RefPtr<CSSValue> filterName = createPrimitiveStringValue(arg);
+    filterValue->append(filterName);
+    argsList->next();
+
+    if (!acceptCommaOperator(argsList))
+        return 0;
+
+    // 2. Parse the parameters.
+    RefPtr<CSSValueList> paramList = parseCustomFilterParameters(argsList);
+    if (!paramList)
+        return 0;
+
+    if (paramList->length())
+        filterValue->append(paramList.release());
+
+    return filterValue;
+}
+
+// FIXME: The custom filters "inline" syntax is deprecated. We will remove it eventually.
+PassRefPtr<WebKitCSSFilterValue> CSSParser::parseCustomFilterFunctionWithInlineSyntax(CSSParserValue* value)
+{
+    //
+    // Custom filter function "inline" syntax:
+    //
+    // custom(<vertex-shader>[wsp<fragment-shader>][,<vertex-mesh>][,<params>])
     //
     // vertexShader:    <uri> | none
     // fragmentShader:  <uri> | none | mix(<uri> [ <blend-mode> || <alpha-compositing> ]?)
@@ -8296,26 +8408,20 @@ PassRefPtr<WebKitCSSFilterValue> CSSParser::parseCustomFilter(CSSParserValue* va
     // alpha-compositing: clear | src | dst | src-over | dst-over | src-in | dst-in |
     //                    src-out | dst-out | src-atop | dst-atop | xor | plus
     //
-    // box: filter-box | border-box | padding-box | content-box
     // vertexMesh:  +<integer>{1,2}[wsp<box>][wsp'detached']
+    // box: filter-box | border-box | padding-box | content-box
     //
-    // param-value: true|false[wsp+true|false]{0-3} |
-    //              <number>[wsp+<number>]{0-3} |
-    //              <array> |
-    //              <transform> |
-    //              <texture(<uri>)>
-    // array: 'array('<number>[wsp<number>]*')'
-    // css-3d-transform: <transform-function>;[<transform-function>]*
-    // transform:   <css-3d-transform> | <mat>
-    // mat:         'mat2('<number>(,<number>){3}')' | 
-    //              'mat3('<number>(,<number>){8}')' | 
-    //              'mat4('<number>(,<number>){15}')' )
-    // param-def:   <param-name>wsp<param-value>
-    // param-name:  <ident>
-    // params:      [<param-def>[,<param-def>*]]
+    // params: See the comment in CSSParser::parseCustomFilterParameters.
     //
-    // custom(<vertex-shader>[wsp<fragment-shader>][,<vertex-mesh>][,<params>])
-    
+
+    ASSERT(value->function);
+
+    CSSParserValueList* argsList = value->function->args.get();
+    if (!argsList)
+        return 0;
+
+    RefPtr<WebKitCSSFilterValue> filterValue = WebKitCSSFilterValue::create(WebKitCSSFilterValue::CustomFilterOperation);
+
     // 1. Parse the shader URLs: <vertex-shader>[wsp<fragment-shader>]
     RefPtr<CSSValueList> shadersList = CSSValueList::createSpaceSeparated();
     bool hadAtLeastOneCustomShader = false;
@@ -8380,64 +8486,36 @@ PassRefPtr<WebKitCSSFilterValue> CSSParser::parseCustomFilter(CSSParserValue* va
         filterValue->append(meshSizeList.release());
     }
     
-    // 3. Parser the parameters.
-    RefPtr<CSSValueList> paramList = CSSValueList::createCommaSeparated();
-    
-    while ((arg = argsList->current())) {
-        if (arg->unit != CSSPrimitiveValue::CSS_IDENT)
-            return 0;
-
-        RefPtr<CSSValueList> parameter = CSSValueList::createSpaceSeparated();
-        parameter->append(createPrimitiveStringValue(arg));
-        argsList->next();
-
-        if (!(arg = argsList->current()))
-            return 0;
-
-        RefPtr<CSSValue> parameterValue;
-
-        if (arg->unit == CSSParserValue::Function && arg->function) {
-            // TODO: Implement other parameters types parsing.
-            // textures: https://bugs.webkit.org/show_bug.cgi?id=71442
-            // mat2, mat3, mat4: https://bugs.webkit.org/show_bug.cgi?id=71444
-            // 3d-transform shall be the last to be checked
-            if (equalIgnoringCase(arg->function->name, "array(")) {
-                parameterValue = parseCustomFilterArrayFunction(arg);
-                // This parsing step only consumes function arguments,
-                // argsList is therefore moved forward explicitely.
-                argsList->next();
-            } else
-                parameterValue = parseCustomFilterTransform(argsList);
-        }
-        else {
-            RefPtr<CSSValueList> paramValueList = CSSValueList::createSpaceSeparated();
-            while ((arg = argsList->current())) {
-                // If we hit a comma it means we finished this parameter's values.
-                if (isComma(arg))
-                    break;
-                if (!validUnit(arg, FNumber, CSSStrictMode))
-                    return 0;
-                paramValueList->append(cssValuePool().createValue(arg->fValue, CSSPrimitiveValue::CSS_NUMBER));
-                argsList->next();
-            }
-            if (!paramValueList->length() || paramValueList->length() > 4)
-                return 0;
-            parameterValue = paramValueList.release();
-        }
-
-        if (!parameterValue || !acceptCommaOperator(argsList))
-            return 0;
+    // 3. Parse the parameters.
+    RefPtr<CSSValueList> paramList = parseCustomFilterParameters(argsList);
+    if (!paramList)
+        return 0;
 
-        parameter->append(parameterValue.release());
-        paramList->append(parameter.release());
-    }
-    
     if (paramList->length())
         filterValue->append(paramList.release());
     
     return filterValue;
 }
 
+PassRefPtr<WebKitCSSFilterValue> CSSParser::parseCustomFilterFunction(CSSParserValue* value)
+{
+    ASSERT(value->function);
+
+    // Look ahead to determine which syntax the custom function is using.
+    // Both the at-rule reference syntax and the inline syntax require at least one argument.
+    CSSParserValueList* argsList = value->function->args.get();
+    if (!argsList || !argsList->size())
+        return 0;
+
+    // The at-rule reference syntax expects a single ident or an ident followed by a comma. 
+    // e.g. custom(my-filter) or custom(my-filter, ...)
+    // In contrast, when the inline syntax starts with an ident like "none", it expects a uri or a mix function next.
+    // e.g. custom(none url(...)) or custom(none mix(...)
+    bool isAtRuleReferenceSyntax = argsList->valueAt(0)->unit == CSSPrimitiveValue::CSS_IDENT 
+        && (argsList->size() == 1 || isComma(argsList->valueAt(1)));
+    return isAtRuleReferenceSyntax ? parseCustomFilterFunctionWithAtRuleReferenceSyntax(value) : parseCustomFilterFunctionWithInlineSyntax(value);
+}
+
 PassRefPtr<CSSValueList> CSSParser::parseCustomFilterTransform(CSSParserValueList* valueList)
 {
     if (!valueList)
@@ -8598,7 +8676,7 @@ PassRefPtr<CSSValueList> CSSParser::parseFilter()
                 if (!m_context.isCSSCustomFilterEnabled)
                     return 0;
                 
-                RefPtr<WebKitCSSFilterValue> filterValue = parseCustomFilter(value);
+                RefPtr<WebKitCSSFilterValue> filterValue = parseCustomFilterFunction(value);
                 if (!filterValue)
                     return 0;
                 list->append(filterValue.release());
index 00dda1c..db9ea98 100644 (file)
@@ -232,10 +232,13 @@ public:
     PassRefPtr<CSSValueList> parseFilter();
     PassRefPtr<WebKitCSSFilterValue> parseBuiltinFilterArguments(CSSParserValueList*, WebKitCSSFilterValue::FilterOperationType);
 #if ENABLE(CSS_SHADERS)
-    PassRefPtr<WebKitCSSArrayFunctionValue> parseCustomFilterArrayFunction(CSSParserValue*);
     PassRefPtr<WebKitCSSMixFunctionValue> parseMixFunction(CSSParserValue*);
-    PassRefPtr<WebKitCSSFilterValue> parseCustomFilter(CSSParserValue*);
+    PassRefPtr<WebKitCSSArrayFunctionValue> parseCustomFilterArrayFunction(CSSParserValue*);
     PassRefPtr<CSSValueList> parseCustomFilterTransform(CSSParserValueList*);
+    PassRefPtr<CSSValueList> parseCustomFilterParameters(CSSParserValueList*);
+    PassRefPtr<WebKitCSSFilterValue> parseCustomFilterFunctionWithAtRuleReferenceSyntax(CSSParserValue*);
+    PassRefPtr<WebKitCSSFilterValue> parseCustomFilterFunctionWithInlineSyntax(CSSParserValue*);
+    PassRefPtr<WebKitCSSFilterValue> parseCustomFilterFunction(CSSParserValue*);
 #endif
 #endif
 
index 8d0c2be..c0968dd 100644 (file)
@@ -5009,10 +5009,15 @@ bool StyleResolver::parseCustomFilterParameterList(CSSValue* parametersValue, Cu
     return true;
 }
 
-PassRefPtr<CustomFilterOperation> StyleResolver::createCustomFilterOperation(WebKitCSSFilterValue* filterValue)
+PassRefPtr<CustomFilterOperation> StyleResolver::createCustomFilterOperationWithAtRuleReferenceSyntax(WebKitCSSFilterValue* filterValue)
 {
-    ASSERT(filterValue->length());
+    // FIXME: Implement style resolution for the custom filter at-rule reference syntax.
+    UNUSED_PARAM(filterValue);
+    return 0;
+}
 
+PassRefPtr<CustomFilterOperation> StyleResolver::createCustomFilterOperationWithInlineSyntax(WebKitCSSFilterValue* filterValue)
+{
     CSSValue* shadersValue = filterValue->itemWithoutBoundsCheck(0);
     ASSERT(shadersValue->isValueList());
     CSSValueList* shadersList = static_cast<CSSValueList*>(shadersValue);
@@ -5111,6 +5116,14 @@ PassRefPtr<CustomFilterOperation> StyleResolver::createCustomFilterOperation(Web
     RefPtr<StyleCustomFilterProgram> program = StyleCustomFilterProgram::create(vertexShader.release(), fragmentShader.release(), programType, mixSettings, meshType);
     return CustomFilterOperation::create(program.release(), parameterList, meshRows, meshColumns, meshType);
 }
+
+PassRefPtr<CustomFilterOperation> StyleResolver::createCustomFilterOperation(WebKitCSSFilterValue* filterValue)
+{
+    ASSERT(filterValue->length());
+    bool isAtRuleReferenceSyntax = filterValue->itemWithoutBoundsCheck(0)->isPrimitiveValue();
+    return isAtRuleReferenceSyntax ? createCustomFilterOperationWithAtRuleReferenceSyntax(filterValue) : createCustomFilterOperationWithInlineSyntax(filterValue);
+}
+
 #endif
 
 bool StyleResolver::createFilterOperations(CSSValue* inValue, RenderStyle* style, RenderStyle* rootStyle, FilterOperations& outOperations)
index 03f556a..a3af544 100644 (file)
@@ -307,6 +307,8 @@ public:
     PassRefPtr<CustomFilterParameter> parseCustomFilterArrayParameter(const String& name, CSSValueList*);
     PassRefPtr<CustomFilterParameter> parseCustomFilterNumberParameter(const String& name, CSSValueList*);
     PassRefPtr<CustomFilterParameter> parseCustomFilterTransformParameter(const String& name, CSSValueList*);
+    PassRefPtr<CustomFilterOperation> createCustomFilterOperationWithAtRuleReferenceSyntax(WebKitCSSFilterValue*);
+    PassRefPtr<CustomFilterOperation> createCustomFilterOperationWithInlineSyntax(WebKitCSSFilterValue*);
     PassRefPtr<CustomFilterOperation> createCustomFilterOperation(WebKitCSSFilterValue*);
     void loadPendingShaders();
 #endif