Commit eb651ee6 authored by eric@webkit.org's avatar eric@webkit.org

2010-04-05 Yuta Kitamura <yutak@chromium.org>

        Reviewed by Darin Adler.

        Escape control characters in CSS string value when it is serialilzed.

        When WebKit serializes a CSS string value that contains binary characters
        ('\0\1\2' for example), it did not escape these characters. As a result,
        users got (invisible) control characters through scripts. This change fixes
        this issue.

        As a side effect, two separate codes for escaping CSS strings are merged, and
        become a public function (quoteCSSString).

        CSS string value is not correctly serialized when it contains binary characters
        https://bugs.webkit.org/show_bug.cgi?id=28938

        * fast/css/script-tests/string-quote-binary.js: Added.
        * fast/css/string-quote-binary-expected.txt: Added.
        * fast/css/string-quote-binary.html: Added.
        * fast/js/resources/js-test-pre.js:
        (shouldBeEqualToString): Considering the case when the argument contains binary characters.
2010-04-05  Yuta Kitamura  <yutak@chromium.org>

        Reviewed by Darin Adler.

        Escape control characters in CSS string value when it is serialilzed.

        When WebKit serializes a CSS string value that contains binary characters
        ('\0\1\2' for example), it did not escape these characters. As a result,
        users got (invisible) control characters through scripts. This change fixes
        this issue.

        As a side effect, two separate codes for escaping CSS strings are merged, and
        become a public function (quoteCSSString).

        CSS string value is not correctly serialized when it contains binary characters
        https://bugs.webkit.org/show_bug.cgi?id=28938

        Test: fast/css/string-quote-binary.html

        * css/CSSParser.cpp:
        (WebCore::isCSSTokenizerIdentifier):
        (WebCore::isCSSTokenizerURL):
        (WebCore::quoteCSSString):
        (WebCore::quoteCSSStringIfNeeded):
        (WebCore::quoteCSSURLIfNeeded):
        * css/CSSParser.h:
        * css/CSSPrimitiveValue.cpp:
        (WebCore::CSSPrimitiveValue::cssText):
        * css/FontFamilyValue.cpp:
        (WebCore::FontFamilyValue::cssText):

git-svn-id: http://svn.webkit.org/repository/webkit/trunk@57105 268f45cc-cd09-0410-ab3c-d52691b4dbfc
parent 55ab5e81
2010-04-05 Yuta Kitamura <yutak@chromium.org>
Reviewed by Darin Adler.
Escape control characters in CSS string value when it is serialilzed.
When WebKit serializes a CSS string value that contains binary characters
('\0\1\2' for example), it did not escape these characters. As a result,
users got (invisible) control characters through scripts. This change fixes
this issue.
As a side effect, two separate codes for escaping CSS strings are merged, and
become a public function (quoteCSSString).
CSS string value is not correctly serialized when it contains binary characters
https://bugs.webkit.org/show_bug.cgi?id=28938
* fast/css/script-tests/string-quote-binary.js: Added.
* fast/css/string-quote-binary-expected.txt: Added.
* fast/css/string-quote-binary.html: Added.
* fast/js/resources/js-test-pre.js:
(shouldBeEqualToString): Considering the case when the argument contains binary characters.
2010-04-05 John Gregg <johnnyg@google.com>
Reviewed by Darin Adler.
......
description(
'This test checks if CSS string values are correctly serialized when they contain binary characters.'
);
var inputs = ["'\\0\\1\\2\\3\\4\\5\\6\\7\\8\\9\\a\\b\\c\\d\\e\\f'",
"'\\10\\11\\12\\13\\14\\15\\16\\17\\18\\19\\1a\\1b\\1c\\1d\\1e\\1f\\7f'",
"'\\A\\B\\C\\D\\E\\F\\1A\\1B\\1C\\1D\\1E\\1F\\7F'",
"'\\0 \\1 \\2 '",
"'\\0 \\1 \\2 '",
"'\\0 \\1 \\2 '",
"'\\00000f\\00000g'",
"'\\0 0\\0 1\\0 2\\0 3\\0 4\\0 5\\0 6\\0 7\\0 8\\0 9'",
"'\\0 A\\0 B\\0 C\\0 D\\0 E\\0 F\\0 G'",
"'\\0 a\\0 b\\0 c\\0 d\\0 e\\0 f\\0 g'"];
var expected = ["'\\0\\1\\2\\3\\4\\5\\6\\7\\8\\9\\a\\b\\c\\d\\e\\f'",
"'\\10\\11\\12\\13\\14\\15\\16\\17\\18\\19\\1a\\1b\\1c\\1d\\1e\\1f\\7f'",
"'\\a\\b\\c\\d\\e\\f\\1a\\1b\\1c\\1d\\1e\\1f\\7f'",
"'\\0\\1\\2'", // No space after each control character.
"'\\0 \\1 \\2 '", // One space delimiter (that will be ignored by the CSS parser), plus one actual space.
"'\\0 \\1 \\2 '", // One space delimiter, plus two actual spaces.
"'\\f\\0g'",
"'\\0 0\\0 1\\0 2\\0 3\\0 4\\0 5\\0 6\\0 7\\0 8\\0 9'", // Need a space before [0-9A-Fa-f], but not before [Gg].
"'\\0 A\\0 B\\0 C\\0 D\\0 E\\0 F\\0G'",
"'\\0 a\\0 b\\0 c\\0 d\\0 e\\0 f\\0g'"];
var testElement = document.createElement('div');
for (var i = 0; i < inputs.length; ++i) {
testElement.style.fontFamily = inputs[i];
shouldBeEqualToString('testElement.style.fontFamily', expected[i]);
}
successfullyParsed = true;
This test checks if CSS string values are correctly serialized when they contain binary characters.
On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
PASS testElement.style.fontFamily is "'\\0\\1\\2\\3\\4\\5\\6\\7\\8\\9\\a\\b\\c\\d\\e\\f'"
PASS testElement.style.fontFamily is "'\\10\\11\\12\\13\\14\\15\\16\\17\\18\\19\\1a\\1b\\1c\\1d\\1e\\1f\\7f'"
PASS testElement.style.fontFamily is "'\\a\\b\\c\\d\\e\\f\\1a\\1b\\1c\\1d\\1e\\1f\\7f'"
PASS testElement.style.fontFamily is "'\\0\\1\\2'"
PASS testElement.style.fontFamily is "'\\0 \\1 \\2 '"
PASS testElement.style.fontFamily is "'\\0 \\1 \\2 '"
PASS testElement.style.fontFamily is "'\\f\\0g'"
PASS testElement.style.fontFamily is "'\\0 0\\0 1\\0 2\\0 3\\0 4\\0 5\\0 6\\0 7\\0 8\\0 9'"
PASS testElement.style.fontFamily is "'\\0 A\\0 B\\0 C\\0 D\\0 E\\0 F\\0G'"
PASS testElement.style.fontFamily is "'\\0 a\\0 b\\0 c\\0 d\\0 e\\0 f\\0g'"
PASS successfullyParsed is true
TEST COMPLETE
<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN">
<html>
<head>
<link rel="stylesheet" href="../js/resources/js-test-style.css">
<script src="../js/resources/js-test-pre.js"></script>
</head>
<body>
<p id="description"></p>
<div id="console"></div>
<script src="script-tests/string-quote-binary.js"></script>
<script src="../js/resources/js-test-post.js"></script>
</body>
</html>
......@@ -122,7 +122,7 @@ function shouldBeNull(_a) { shouldBe(_a, "null"); }
function shouldBeEqualToString(a, b)
{
var unevaledString = '"' + b.replace(/"/g, "\"") + '"';
var unevaledString = '"' + b.replace(/\\/g, "\\\\").replace(/"/g, "\"") + '"';
shouldBe(a, unevaledString);
}
......
2010-04-05 Yuta Kitamura <yutak@chromium.org>
Reviewed by Darin Adler.
Escape control characters in CSS string value when it is serialilzed.
When WebKit serializes a CSS string value that contains binary characters
('\0\1\2' for example), it did not escape these characters. As a result,
users got (invisible) control characters through scripts. This change fixes
this issue.
As a side effect, two separate codes for escaping CSS strings are merged, and
become a public function (quoteCSSString).
CSS string value is not correctly serialized when it contains binary characters
https://bugs.webkit.org/show_bug.cgi?id=28938
Test: fast/css/string-quote-binary.html
* css/CSSParser.cpp:
(WebCore::isCSSTokenizerIdentifier):
(WebCore::isCSSTokenizerURL):
(WebCore::quoteCSSString):
(WebCore::quoteCSSStringIfNeeded):
(WebCore::quoteCSSURLIfNeeded):
* css/CSSParser.h:
* css/CSSPrimitiveValue.cpp:
(WebCore::CSSPrimitiveValue::cssText):
* css/FontFamilyValue.cpp:
(WebCore::FontFamilyValue::cssText):
2010-04-05 John Gregg <johnnyg@google.com>
Reviewed by Darin Adler.
......@@ -65,6 +65,7 @@
#include "Pair.h"
#include "Rect.h"
#include "ShadowValue.h"
#include "StringBuffer.h"
#include "WebKitCSSKeyframeRule.h"
#include "WebKitCSSKeyframesRule.h"
#include "WebKitCSSTransformValue.h"
......@@ -5400,6 +5401,120 @@ int cssValueKeywordID(const CSSParserString& string)
return hashTableEntry ? hashTableEntry->id : 0;
}
// "ident" from the CSS tokenizer, minus backslash-escape sequences
static bool isCSSTokenizerIdentifier(const String& string)
{
const UChar* p = string.characters();
const UChar* end = p + string.length();
// -?
if (p != end && p[0] == '-')
++p;
// {nmstart}
if (p == end || !(p[0] == '_' || p[0] >= 128 || isASCIIAlpha(p[0])))
return false;
++p;
// {nmchar}*
for (; p != end; ++p) {
if (!(p[0] == '_' || p[0] == '-' || p[0] >= 128 || isASCIIAlphanumeric(p[0])))
return false;
}
return true;
}
// "url" from the CSS tokenizer, minus backslash-escape sequences
static bool isCSSTokenizerURL(const String& string)
{
const UChar* p = string.characters();
const UChar* end = p + string.length();
for (; p != end; ++p) {
UChar c = p[0];
switch (c) {
case '!':
case '#':
case '$':
case '%':
case '&':
break;
default:
if (c < '*')
return false;
if (c <= '~')
break;
if (c < 128)
return false;
}
}
return true;
}
// We use single quotes for now because markup.cpp uses double quotes.
String quoteCSSString(const String& string)
{
// For efficiency, we first pre-calculate the length of the quoted string, then we build the actual one.
// Please see below for the actual logic.
unsigned quotedStringSize = 2; // Two quotes surrounding the entire string.
bool afterEscape = false;
for (unsigned i = 0; i < string.length(); ++i) {
UChar ch = string[i];
if (ch == '\\' || ch == '\'') {
quotedStringSize += 2;
afterEscape = false;
} else if (ch < 0x20 || ch == 0x7F) {
quotedStringSize += 2 + (ch >= 0x10);
afterEscape = true;
} else {
quotedStringSize += 1 + (afterEscape && (isASCIIHexDigit(ch) || ch == ' '));
afterEscape = false;
}
}
StringBuffer buffer(quotedStringSize);
unsigned index = 0;
buffer[index++] = '\'';
afterEscape = false;
for (unsigned i = 0; i < string.length(); ++i) {
UChar ch = string[i];
if (ch == '\\' || ch == '\'') {
buffer[index++] = '\\';
buffer[index++] = ch;
afterEscape = false;
} else if (ch < 0x20 || ch == 0x7F) { // Control characters.
static const char hexDigits[17] = "0123456789abcdef";
buffer[index++] = '\\';
if (ch >= 0x10)
buffer[index++] = hexDigits[ch >> 4];
buffer[index++] = hexDigits[ch & 0xF];
afterEscape = true;
} else {
// Space character may be required to separate backslash-escape sequence and normal characters.
if (afterEscape && (isASCIIHexDigit(ch) || ch == ' '))
buffer[index++] = ' ';
buffer[index++] = ch;
afterEscape = false;
}
}
buffer[index++] = '\'';
ASSERT(quotedStringSize == index);
return String::adopt(buffer);
}
String quoteCSSStringIfNeeded(const String& string)
{
return isCSSTokenizerIdentifier(string) ? string : quoteCSSString(string);
}
String quoteCSSURLIfNeeded(const String& string)
{
return isCSSTokenizerURL(string) ? string : quoteCSSString(string);
}
#define YY_DECL int CSSParser::lex()
#define yyconst const
typedef int yy_state_type;
......
......@@ -320,6 +320,10 @@ namespace WebCore {
CSSParser* m_parser;
};
String quoteCSSString(const String&);
String quoteCSSStringIfNeeded(const String&);
String quoteCSSURLIfNeeded(const String&);
} // namespace WebCore
#endif // CSSParser_h
......@@ -22,6 +22,7 @@
#include "CSSPrimitiveValue.h"
#include "CSSHelper.h"
#include "CSSParser.h"
#include "CSSPropertyNames.h"
#include "CSSStyleSheet.h"
#include "CSSValueKeywords.h"
......@@ -142,78 +143,6 @@ static const AtomicString& valueOrPropertyName(int valueOrPropertyID)
return nullAtom;
}
// "ident" from the CSS tokenizer, minus backslash-escape sequences
static bool isCSSTokenizerIdentifier(const String& string)
{
const UChar* p = string.characters();
const UChar* end = p + string.length();
// -?
if (p != end && p[0] == '-')
++p;
// {nmstart}
if (p == end || !(p[0] == '_' || p[0] >= 128 || isASCIIAlpha(p[0])))
return false;
++p;
// {nmchar}*
for (; p != end; ++p) {
if (!(p[0] == '_' || p[0] == '-' || p[0] >= 128 || isASCIIAlphanumeric(p[0])))
return false;
}
return true;
}
// "url" from the CSS tokenizer, minus backslash-escape sequences
static bool isCSSTokenizerURL(const String& string)
{
const UChar* p = string.characters();
const UChar* end = p + string.length();
for (; p != end; ++p) {
UChar c = p[0];
switch (c) {
case '!':
case '#':
case '$':
case '%':
case '&':
break;
default:
if (c < '*')
return false;
if (c <= '~')
break;
if (c < 128)
return false;
}
}
return true;
}
// We use single quotes for now because markup.cpp uses double quotes.
static String quoteString(const String& string)
{
// FIXME: Also need to escape characters like '\n'.
String s = string;
s.replace('\\', "\\\\");
s.replace('\'', "\\'");
return "'" + s + "'";
}
static String quoteStringIfNeeded(const String& string)
{
return isCSSTokenizerIdentifier(string) ? string : quoteString(string);
}
static String quoteURLIfNeeded(const String& string)
{
return isCSSTokenizerURL(string) ? string : quoteString(string);
}
CSSPrimitiveValue::CSSPrimitiveValue()
: m_type(0)
{
......@@ -774,10 +703,10 @@ String CSSPrimitiveValue::cssText() const
// FIXME
break;
case CSS_STRING:
text = quoteStringIfNeeded(m_value.string);
text = quoteCSSStringIfNeeded(m_value.string);
break;
case CSS_URI:
text = "url(" + quoteURLIfNeeded(m_value.string) + ")";
text = "url(" + quoteCSSURLIfNeeded(m_value.string) + ")";
break;
case CSS_IDENT:
text = valueOrPropertyName(m_value.ident);
......@@ -905,7 +834,7 @@ String CSSPrimitiveValue::cssText() const
break;
}
case CSS_PARSER_IDENTIFIER:
text = quoteStringIfNeeded(m_value.string);
text = quoteCSSStringIfNeeded(m_value.string);
break;
}
m_cachedCSSText = text;
......
......@@ -21,44 +21,9 @@
#include "config.h"
#include "FontFamilyValue.h"
namespace WebCore {
// FIXME: This appears identical to isCSSTokenizerIdentifier from CSSPrimitiveValue.cpp, we should use a single function.
static bool isValidCSSIdentifier(const String& string)
{
unsigned length = string.length();
if (!length)
return false;
const UChar* characters = string.characters();
UChar c = characters[0];
if (!(c == '_' || (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || c == '-' || c >= 0x80))
return false;
for (unsigned i = 1; i < length; ++i) {
c = characters[i];
if (!(c == '_' || (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9') || c == '-' || c >= 0x80))
return false;
}
#include "CSSParser.h"
return true;
}
// Quotes the string if it needs quoting.
// We use single quotes because serialization code uses double quotes, and it's nice to
// avoid having to turn all the quote marks into &quot; as we would have to.
static String quoteStringIfNeeded(const String& string)
{
if (isValidCSSIdentifier(string))
return string;
// FIXME: Also need to transform control characters (00-1F) into \ sequences.
// FIXME: This is inefficient -- should use a Vector<UChar> instead.
String quotedString = string;
quotedString.replace('\\', "\\\\");
quotedString.replace('\'', "\\'");
return "'" + quotedString + "'";
}
namespace WebCore {
FontFamilyValue::FontFamilyValue(const String& familyName)
: CSSPrimitiveValue(String(), CSS_STRING)
......@@ -101,7 +66,7 @@ void FontFamilyValue::appendSpaceSeparated(const UChar* characters, unsigned len
String FontFamilyValue::cssText() const
{
return quoteStringIfNeeded(m_familyName);
return quoteCSSStringIfNeeded(m_familyName);
}
}
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment