Revision: 5460
Author: [email protected]
Date: Mon Jun 24 15:05:32 2013
Log: Issue 1491: Add gradient support to CSS schema
https://codereview.appspot.com/10438046
From https://code.google.com/p/google-caja/issues/detail?id=1491
Run `sanitizeStylesheet(".test{background-image: linear-gradient(to
bottom right, red, rgba(255,0,0,0));}", "prefix", function(u){return(u)})`
The expected output is ".prefix .test{background-image:
linear-gradient(to bottom right, red, rgba(255,0,0,0));}"
However I receive ".prefix .test{}""
R=felixz
http://code.google.com/p/google-caja/source/detail?r=5460
Modified:
/trunk/src/com/google/caja/lang/css/CssPropertyPatterns.java
/trunk/src/com/google/caja/lang/css/css-extensions-defs.json
/trunk/src/com/google/caja/lang/css/css21-defs.json
/trunk/src/com/google/caja/plugin/sanitizecss.js
/trunk/tests/com/google/caja/plugin/CssRewriterTest.java
/trunk/tests/com/google/caja/plugin/sanitizecss_test.js
=======================================
--- /trunk/src/com/google/caja/lang/css/CssPropertyPatterns.java Wed Feb 13
14:19:54 2013
+++ /trunk/src/com/google/caja/lang/css/CssPropertyPatterns.java Mon Jun 24
15:05:32 2013
@@ -644,7 +644,8 @@
"<" + commonSymbol + ">");
JSREBuilder p = inspector.symbolToPattern(false, sig);
if (p != null) {
- String commonSubstring =
withoutSpacesOrZero(p.p.optimize()).toString();
+ JSRE pOpt = withoutSpacesOrZero(p.p.optimize());
+ String commonSubstring = pOpt != null ? pOpt.toString() : "";
if (commonSubstring.length() != 0) {
commonSubstrings.add(commonSubstring);
}
=======================================
--- /trunk/src/com/google/caja/lang/css/css-extensions-defs.json Wed May 22
11:36:57 2013
+++ /trunk/src/com/google/caja/lang/css/css-extensions-defs.json Mon Jun 24
15:05:32 2013
@@ -442,11 +442,6 @@
"source":
"http://www.w3.org/TR/2009/CR-css3-background-20091217/#ltbg-imagegt"
},
- { "key": "<image>",
- "signature": "<uri>",
- "source":
"http://www.w3.org/TR/2009/CR-css3-background-20091217/#ltimagegt"
- },
-
{ "key": "<bg-position>",
"signature": "[ [ [ <percentage> | <length> | left | center | right
] ] [ [ <percentage> | <length> | top | center | bottom ] ]? | [ [ center |
[ left | right ] [ <percentage> | <length> ]? ] || [ center | [ top |
bottom ] [ <percentage> | <length> ]? ] ] ]",
"source":
"http://www.w3.org/TR/2009/CR-css3-background-20091217/#ltbg-positiongt"
@@ -460,6 +455,76 @@
{ "key": "<attachment>",
"signature": "scroll | fixed | local",
"source":
"http://www.w3.org/TR/2009/CR-css3-background-20091217/#ltattachmentgt"
- }
+ },
+
+ { "key": "<image>",
+ "signature": "<uri> | <image-list> | <gradient>",
+ "source": "http://dev.w3.org/csswg/css-images-3/#image-values"
+ },
+
+ { "key": "<image-list>",
+ "signature": "image( [ <image-decl> , ]* [ <image-decl> | <color> ]
)",
+ "source": "http://dev.w3.org/csswg/css-images-3/#image-notation"
+ },
+
+ { "key": "<image-decl>",
+ "signature": "<uri>",
+ "source": "http://dev.w3.org/csswg/css-images-3/#image-notation"
+ },
+
+ { "key": "<gradient>",
+ "signature": "[ <linear-gradient> | <radial-gradient> |
<repeating-linear-gradient> | <repeating-radial-gradient> ]",
+ "source": "http://dev.w3.org/csswg/css-images-3/#linear-gradient-type"
+ },
+
+ { "key": "<linear-gradient>",
+ "signature": "linear-gradient ( [ [ <angle> | to <side-or-corner>
] ,]? <color-stop> [ , <color-stop>]+ )",
+ "source": "http://dev.w3.org/csswg/css-images-3/#linear-gradient-type"
+ },
+
+ { "key": "<radial-gradient>",
+ "signature": "radial-gradient ( [ [ <ending-shape> || <size> ] [ at
<position> ]? , | at <position> , ]? <color-stop> [ , <color-stop> ]+ )",
+ "source": "http://dev.w3.org/csswg/css-images-3/#radial-gradients"
+ },
+
+ { "key": "<repeating-linear-gradient>",
+ "signature": "repeating-linear-gradient ( [ [ <angle> | to
<side-or-corner> ] ,]? <color-stop>[, <color-stop>]+ )",
+ "source": "http://dev.w3.org/csswg/css-images-3/#repeating-gradients"
+ },
+
+ { "key": "<repeating-radial-gradient>",
+ "signature": "repeating-radial-gradient ( [ [ <ending-shape> ||
<size> ] [ at <position> ]? , | at <position> , ]? <color-stop> [ ,
<color-stop> ]+ )",
+ "source": "http://dev.w3.org/csswg/css-images-3/#repeating-gradients"
+ },
+
+ { "key": "<side-or-corner>",
+ "signature": "[left | right] || [top | bottom]",
+ "source": "http://dev.w3.org/csswg/css-images-3/#linear-gradient-type"
+ },
+
+ { "key": "<size>",
+ "signature": "[ closest-side | farthest-side | closest-corner |
farthest-corner | <length> | [<length> | <percentage>] [<length> |
<percentage>] ]",
+ "default": "farthest-corner",
+ "source": "http://dev.w3.org/csswg/css-images-3/#size"
+ },
+
+ { "key": "<color-stop>",
+ "signature": "<color> [ <percentage> | <length> ]?",
+ "source": "http://dev.w3.org/csswg/css-images-3/#color-stop-syntax"
+ },
+
+ { "key": "<ending-shape>",
+ "signature": "circle | ellipse",
+ "default": "circle",
+ "source": "http://dev.w3.org/csswg/css-images-3/#shape"
+ },
+
+ { "key": "<position>",
+ "default": "center",
+ "signature": "[ <percentage> | <length> | [[ left | right ] [
<percentage> | <length> ]? [ center | [ top | bottom ] [ <percentage> |
<length> ]? ]? ] | [ [ top | bottom ] [ <percentage> | <length> ]? [
center | [ left | right ] [ <percentage> | <length> ]? ]? ] | [ center [
center | [ left | right | top | bottom ] [ <percentage> | <length> ]? ]? ]
]",
+ "comment": "the signature was derived from the source by duplicating
and reducing to avoid the use of the && operator defined in
http://www.w3.org/TR/CSS21/about.html#property-defs which is not supported
by the CSS whitelist parser.",
+ "source": "http://dev.w3.org/csswg/css-backgrounds/#position",
+ },
+
]
}
=======================================
--- /trunk/src/com/google/caja/lang/css/css21-defs.json Tue Mar 20 17:18:52
2012
+++ /trunk/src/com/google/caja/lang/css/css21-defs.json Mon Jun 24 15:05:32
2013
@@ -34,7 +34,7 @@
"dom2property": "backgroundColor" },
{ "key": "background-image",
- "signature": "<uri> | none | inherit",
+ "signature": "<image> | none | inherit",
"default": "none",
"appliesTo": "*",
"inherited": false,
@@ -407,7 +407,7 @@
"dom2property": "lineHeight" },
{ "key": "list-style-image",
- "signature": "<uri> | none | inherit",
+ "signature": "<image> | none | inherit",
"default": "none",
"appliesTo": { "include": ["display: list-item"] },
"inherited": true,
@@ -906,6 +906,10 @@
" the keywords with the same names."]
},
+ { "key": "<image>",
+ "see":
"http://www.w3.org/TR/2009/CR-css3-background-20091217/#ltimagegt",
+ "signature": "<uri>" },
+
{ "key": "<relative-size>",
"see": "http://www.w3.org/TR/CSS21/fonts.html#propdef-font-size",
"signature": "smaller|larger" },
=======================================
--- /trunk/src/com/google/caja/plugin/sanitizecss.js Sat May 11 15:02:28
2013
+++ /trunk/src/com/google/caja/plugin/sanitizecss.js Mon Jun 24 15:05:32
2013
@@ -139,11 +139,26 @@
function normalizeFunctionCall(tokens, start) {
var parenDepth = 1, end = start + 1, n = tokens.length;
while (end < n && parenDepth) {
- // TODO: Can URLs appear in functions?
var token = tokens[end++];
- parenDepth += (token === '(' ? 1 : token === ')' ? -1 : 0);
+ // Decrement if we see a close parenthesis, and increment if we
+ // see a function. Since url(...) are whole tokens, they will not
+ // affect the token scanning.
+ parenDepth += (token === ')' ? -1 : /^[^"']*\($/.test(token));
}
- return end;
+ // Allow error-recovery from unclosed functions by ignoring the call
and
+ // so allowing resumption at the next ';'.
+ return parenDepth ? start+1 : end;
+ }
+
+ /** Put spaces between tokens, but don't duplicate existing spaces. */
+ function respace(tokens) {
+ var i = 0, j = 0, n = tokens.length, tok;
+ while (i < n) {
+ tok = tokens[i++];
+ if (tok !== ' ') { tokens[j++] = tok; }
+ }
+ tokens.length = j;
+ return tokens.join(' ');
}
// Used as map value to avoid hasOwnProperty checks.
@@ -241,7 +256,7 @@
// splices tokens to where i now is the index of the whole
call:
// ['x', ' ', 'rgb( 255 , 0 , 0 )', ' ', 'y']
tokens.splice(i, end - i,
- token = tokens.slice(i, end).join(' '))),
+ token = respace(tokens.slice(i, end)))),
litGroup = propertySchema.cssLitGroup,
litMap = (litGroup
? (propertySchema.cssLitMap
@@ -303,8 +318,8 @@
var HISTORY_NON_SENSITIVE_PSEUDO_SELECTOR_WHITELIST =
/^(active|after|before|first-child|first-letter|focus|hover)$/;
- // TODO: This should be removed now as modern browsers no longer
require this
- // special handling
+ // TODO: This should be removed now as modern browsers no longer
require
+ // this special handling
var HISTORY_SENSITIVE_PSEUDO_SELECTOR_WHITELIST =
/^(link|visited)$/;
@@ -532,15 +547,15 @@
// but supported by Chrome
var url3 = /^\s*url\s*[(]([^)]*)[)]\s*$/;
var match;
- if (match = string1.exec(candidate)) {
+ if ((match = string1.exec(candidate))) {
return match[1];
- } else if (match = string2.exec(candidate)) {
+ } else if ((match = string2.exec(candidate))) {
return match[1];
- } else if (match = url1.exec(candidate)) {
+ } else if ((match = url1.exec(candidate))) {
return match[1];
- } else if (match = url2.exec(candidate)) {
+ } else if ((match = url2.exec(candidate))) {
return match[1];
- } else if (match = url3.exec(candidate)) {
+ } else if ((match = url3.exec(candidate))) {
return match[1];
}
return null;
@@ -562,10 +577,11 @@
* file in sanitized CSS.
* @param {function(string, Array.<string>): ?Array.<string>} tagPolicy
* As in html-sanitizer, used for rewriting element names.
- * @param {undefined|function(string, boolean)} continuation callback
from external
- * css urls. The callback is called with a string, the CSS
contents
- * and a boolean, which is true if the external url itself
contained
- * other external urls.
+ * @param {undefined|function(string, boolean)} continuation callback
from
+ * external CSS URLs.
+ * The callback is called with a string, the CSS contents and a
boolean,
+ * which is true if the external url itself contained other
external
+ * URLs.
*/
function sanitizeStylesheetInternal(baseUri, cssText, suffix,
naiveUriRewriter, naiveUriFetcher, tagPolicy,
=======================================
--- /trunk/tests/com/google/caja/plugin/CssRewriterTest.java Tue Jan 22
20:45:33 2013
+++ /trunk/tests/com/google/caja/plugin/CssRewriterTest.java Mon Jun 24
15:05:32 2013
@@ -181,10 +181,42 @@
}
public final void testGradients() throws Exception {
- runTest("p { background-image:gradient(" +
- "linear, left top, left bottom)}",
- "", false);
- assertNoErrors();
+ runTest("p { background-image:gradient(linear, left top, left
bottom)}",
+ "", false);
+ assertNoErrors();
+
+ String pre = ".namespace__ p {\n ";
+
+ // A gradient on 45deg axis starting blue and finishing red
+ runTest(
+ "p { background-image: linear-gradient(45deg, blue, red) }",
+ pre + "background-image: linear-gradient(45deg, blue, red)\n}");
+ assertNoErrors();
+
+ // A gradient going from the bottom right to the top left starting
blue and
+ // finishing red
+ runTest(
+ "p { background-image:linear-gradient( to left top, blue, red);
}",
+ pre + "background-image: linear-gradient(to left top, blue,
red)\n}");
+ assertNoErrors();
+
+ // A gradient going from the bottom to top, starting blue, being green
+ // after 40% and finishing red
+ runTest(
+ "p { background-image:linear-gradient( 0deg, blue, green 40%, red
); }",
+ pre + "background-image: linear-gradient(0deg, blue, green 40%,
red)\n}"
+ );
+
+ runTest(
+ ""
+ + "li {"
+ + " list-style-image:repeating-radial-gradient("
+ + " circle closest-side at 20px 30px,"
+ + " red, yellow, green 100%, yellow 150%, red 200%"
+ + " );"
+ + "}",
+ "");
+
}
public final void testSubstitutions() throws Exception {
@@ -254,7 +286,7 @@
public final void testUrisCalledWithProperPropertyPart() throws
Exception {
// The CssRewriter needs to rewrite URIs.
- // When it does so it passes
+ // When it does so it passes a context string.
assertCallsUriRewriterWithPropertyPart(
"background: 'foo.png'",
"background::bg-image::image");
=======================================
--- /trunk/tests/com/google/caja/plugin/sanitizecss_test.js Wed Mar 20
10:43:44 2013
+++ /trunk/tests/com/google/caja/plugin/sanitizecss_test.js Mon Jun 24
15:05:32 2013
@@ -255,3 +255,14 @@
assertSelector("#foo:bogus", "sfx", [".sfx #foo-sfx", []]);
jsunit.pass();
});
+
+jsunitRegister('testGradients',
+ function testGradients() {
+ var source = 'linear-gradient(to bottom right, red, rgb(255,0,0))';
+ var expect = 'linear-gradient( to bottom right , red , rgb( 255 , 0 , 0
) )';
+ var tokens = lexCss(source);
+ sanitizeCssProperty(
+ 'background-image', cssSchema['background-image'], tokens);
+ assertEquals(expect, tokens.join(''));
+ jsunit.pass();
+});
--
---
You received this message because you are subscribed to the Google Groups "Google Caja Discuss" group.
To unsubscribe from this group and stop receiving emails from it, send an email
to [email protected].
For more options, visit https://groups.google.com/groups/opt_out.