1 | /* |
2 | * Copyright (C) 2003 Lars Knoll (knoll@kde.org) |
3 | * Copyright (C) 2005 Allan Sandfeld Jensen (kde@carewolf.com) |
4 | * Copyright (C) 2004-2012, 2016 Apple Inc. All rights reserved. |
5 | * Copyright (C) 2007 Nicholas Shanks <webkit@nickshanks.com> |
6 | * Copyright (C) 2008 Eric Seidel <eric@webkit.org> |
7 | * Copyright (C) 2009 Torch Mobile Inc. All rights reserved. (http://www.torchmobile.com/) |
8 | * Copyright (C) 2012 Adobe Systems Incorporated. All rights reserved. |
9 | * Copyright (C) 2012 Intel Corporation. All rights reserved. |
10 | * |
11 | * This library is free software; you can redistribute it and/or |
12 | * modify it under the terms of the GNU Library General Public |
13 | * License as published by the Free Software Foundation; either |
14 | * version 2 of the License, or (at your option) any later version. |
15 | * |
16 | * This library is distributed in the hope that it will be useful, |
17 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
18 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
19 | * Library General Public License for more details. |
20 | * |
21 | * You should have received a copy of the GNU Library General Public License |
22 | * along with this library; see the file COPYING.LIB. If not, write to |
23 | * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, |
24 | * Boston, MA 02110-1301, USA. |
25 | */ |
26 | |
27 | #include "config.h" |
28 | #include "CSSMarkup.h" |
29 | |
30 | #include "CSSParserIdioms.h" |
31 | #include <wtf/HexNumber.h> |
32 | #include <wtf/text/StringBuilder.h> |
33 | |
34 | namespace WebCore { |
35 | |
36 | template <typename CharacterType> |
37 | static inline bool isCSSTokenizerIdentifier(const CharacterType* characters, unsigned length) |
38 | { |
39 | const CharacterType* end = characters + length; |
40 | |
41 | // -? |
42 | if (characters != end && characters[0] == '-') |
43 | ++characters; |
44 | |
45 | // {nmstart} |
46 | if (characters == end || !isNameStartCodePoint(characters[0])) |
47 | return false; |
48 | ++characters; |
49 | |
50 | // {nmchar}* |
51 | for (; characters != end; ++characters) { |
52 | if (!isNameCodePoint(characters[0])) |
53 | return false; |
54 | } |
55 | |
56 | return true; |
57 | } |
58 | |
59 | // "ident" from the CSS tokenizer, minus backslash-escape sequences |
60 | static bool isCSSTokenizerIdentifier(const String& string) |
61 | { |
62 | unsigned length = string.length(); |
63 | |
64 | if (!length) |
65 | return false; |
66 | |
67 | if (string.is8Bit()) |
68 | return isCSSTokenizerIdentifier(string.characters8(), length); |
69 | return isCSSTokenizerIdentifier(string.characters16(), length); |
70 | } |
71 | |
72 | static void serializeCharacter(UChar32 c, StringBuilder& appendTo) |
73 | { |
74 | appendTo.append('\\'); |
75 | appendTo.append(c); |
76 | } |
77 | |
78 | static void serializeCharacterAsCodePoint(UChar32 c, StringBuilder& appendTo) |
79 | { |
80 | appendTo.append('\\'); |
81 | appendUnsignedAsHex(c, appendTo, Lowercase); |
82 | appendTo.append(' '); |
83 | } |
84 | |
85 | void serializeIdentifier(const String& identifier, StringBuilder& appendTo, bool skipStartChecks) |
86 | { |
87 | bool isFirst = !skipStartChecks; |
88 | bool isSecond = false; |
89 | bool isFirstCharHyphen = false; |
90 | unsigned index = 0; |
91 | while (index < identifier.length()) { |
92 | UChar32 c = identifier.characterStartingAt(index); |
93 | if (!c) { |
94 | // Check for lone surrogate which characterStartingAt does not return. |
95 | c = identifier[index]; |
96 | } |
97 | |
98 | index += U16_LENGTH(c); |
99 | |
100 | if (!c) |
101 | appendTo.append(0xfffd); |
102 | else if (c <= 0x1f || c == 0x7f || (0x30 <= c && c <= 0x39 && (isFirst || (isSecond && isFirstCharHyphen)))) |
103 | serializeCharacterAsCodePoint(c, appendTo); |
104 | else if (c == 0x2d && isFirst && index == identifier.length()) |
105 | serializeCharacter(c, appendTo); |
106 | else if (0x80 <= c || c == 0x2d || c == 0x5f || (0x30 <= c && c <= 0x39) || (0x41 <= c && c <= 0x5a) || (0x61 <= c && c <= 0x7a)) |
107 | appendTo.append(c); |
108 | else |
109 | serializeCharacter(c, appendTo); |
110 | |
111 | if (isFirst) { |
112 | isFirst = false; |
113 | isSecond = true; |
114 | isFirstCharHyphen = (c == 0x2d); |
115 | } else if (isSecond) |
116 | isSecond = false; |
117 | } |
118 | } |
119 | |
120 | void serializeString(const String& string, StringBuilder& appendTo) |
121 | { |
122 | appendTo.append('"'); |
123 | |
124 | unsigned index = 0; |
125 | while (index < string.length()) { |
126 | UChar32 c = string.characterStartingAt(index); |
127 | index += U16_LENGTH(c); |
128 | |
129 | if (c <= 0x1f || c == 0x7f) |
130 | serializeCharacterAsCodePoint(c, appendTo); |
131 | else if (c == 0x22 || c == 0x5c) |
132 | serializeCharacter(c, appendTo); |
133 | else |
134 | appendTo.append(c); |
135 | } |
136 | |
137 | appendTo.append('"'); |
138 | } |
139 | |
140 | String serializeString(const String& string) |
141 | { |
142 | StringBuilder builder; |
143 | serializeString(string, builder); |
144 | return builder.toString(); |
145 | } |
146 | |
147 | String serializeURL(const String& string) |
148 | { |
149 | return "url(" + serializeString(string) + ")" ; |
150 | } |
151 | |
152 | String serializeAsStringOrCustomIdent(const String& string) |
153 | { |
154 | if (isCSSTokenizerIdentifier(string)) { |
155 | StringBuilder builder; |
156 | serializeIdentifier(string, builder); |
157 | return builder.toString(); |
158 | } |
159 | return serializeString(string); |
160 | } |
161 | |
162 | String serializeFontFamily(const String& string) |
163 | { |
164 | return isCSSTokenizerIdentifier(string) ? string : serializeString(string); |
165 | } |
166 | |
167 | } // namespace WebCore |
168 | |