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
34namespace WebCore {
35
36template <typename CharacterType>
37static 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
60static 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
72static void serializeCharacter(UChar32 c, StringBuilder& appendTo)
73{
74 appendTo.append('\\');
75 appendTo.append(c);
76}
77
78static void serializeCharacterAsCodePoint(UChar32 c, StringBuilder& appendTo)
79{
80 appendTo.append('\\');
81 appendUnsignedAsHex(c, appendTo, Lowercase);
82 appendTo.append(' ');
83}
84
85void 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
120void 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
140String serializeString(const String& string)
141{
142 StringBuilder builder;
143 serializeString(string, builder);
144 return builder.toString();
145}
146
147String serializeURL(const String& string)
148{
149 return "url(" + serializeString(string) + ")";
150}
151
152String 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
162String serializeFontFamily(const String& string)
163{
164 return isCSSTokenizerIdentifier(string) ? string : serializeString(string);
165}
166
167} // namespace WebCore
168