51a52da480b688f0645b0e67d6e4463081268c52
[WebKit-https.git] / Source / WebCore / css / CSSMarkup.cpp
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