|
1 /** |
|
2 * Copyright (C) 2008 Torch Mobile Inc. All rights reserved. (http://www.torchmobile.com/) |
|
3 * |
|
4 * This library is free software; you can redistribute it and/or |
|
5 * modify it under the terms of the GNU Library General Public |
|
6 * License as published by the Free Software Foundation; either |
|
7 * version 2 of the License, or (at your option) any later version. |
|
8 * |
|
9 * This library is distributed in the hope that it will be useful, |
|
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
|
12 * Library General Public License for more details. |
|
13 * |
|
14 * You should have received a copy of the GNU Library General Public License |
|
15 * along with this library; see the file COPYING.LIB. If not, write to |
|
16 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, |
|
17 * Boston, MA 02110-1301, USA. |
|
18 * |
|
19 */ |
|
20 |
|
21 #include "config.h" |
|
22 |
|
23 #if ENABLE(WML) |
|
24 #include "WMLVariables.h" |
|
25 |
|
26 #include "WMLDocument.h" |
|
27 #include <wtf/ASCIICType.h> |
|
28 |
|
29 namespace WebCore { |
|
30 |
|
31 // WML variables specification, excluding the |
|
32 // pre-WML 1.0 deprecated variable syntax |
|
33 // |
|
34 // varname = ("_" | alpha) ("_" | alpha | digit)* |
|
35 // conv = ":" ("e" ("scape")? | "n" ("oesc")? | "u" ("nesc")?) |
|
36 // var = ("$" varname) | ("$(" varname (conv)? ")") |
|
37 |
|
38 static bool isValidFirstVariableNameCharacter(const UChar& character) |
|
39 { |
|
40 return WTF::isASCIIAlpha(character) |
|
41 || character == '_'; |
|
42 } |
|
43 |
|
44 static bool isValidVariableNameCharacter(const UChar& character) |
|
45 { |
|
46 return WTF::isASCIIAlpha(character) |
|
47 || WTF::isASCIIDigit(character) |
|
48 || character == '_'; |
|
49 } |
|
50 |
|
51 static bool isValidVariableEscapingModeString(const String& mode, WMLVariableEscapingMode& escapeMode) |
|
52 { |
|
53 if (mode == "e" || mode == "escape") |
|
54 escapeMode = WMLVariableEscapingEscape; |
|
55 else if (mode == "u" || mode == "unesc") |
|
56 escapeMode = WMLVariableEscapingUnescape; |
|
57 else if (mode == "n" || mode == "noesc") |
|
58 escapeMode = WMLVariableEscapingNone; |
|
59 else |
|
60 return false; |
|
61 |
|
62 return true; |
|
63 } |
|
64 |
|
65 bool isValidVariableName(const String& name) |
|
66 { |
|
67 if (name.isEmpty()) |
|
68 return false; |
|
69 |
|
70 const UChar* characters = name.characters(); |
|
71 if (!isValidFirstVariableNameCharacter(characters[0])) |
|
72 return false; |
|
73 |
|
74 unsigned length = name.length(); |
|
75 for (unsigned i = 1; i < length; ++i) { |
|
76 if (!isValidVariableNameCharacter(characters[i])) |
|
77 return false; |
|
78 } |
|
79 |
|
80 return true; |
|
81 } |
|
82 |
|
83 bool containsVariableReference(const String& text, bool& isValid) |
|
84 { |
|
85 isValid = true; |
|
86 bool foundReference = false; |
|
87 bool finished = false; |
|
88 int currentPosition = 0; |
|
89 const UChar* characters = text.characters(); |
|
90 |
|
91 while (!finished) { |
|
92 // Find beginning of variable reference |
|
93 int referenceStartPosition = text.find('$', currentPosition); |
|
94 if (referenceStartPosition == -1) { |
|
95 finished = true; |
|
96 break; |
|
97 } |
|
98 |
|
99 foundReference = true; |
|
100 |
|
101 int nameStartPosition = referenceStartPosition + 1; |
|
102 int nameEndPosition = -1; |
|
103 |
|
104 if (characters[nameStartPosition] == '(') { |
|
105 // If the input string contains an open brace, a close brace must exist as well |
|
106 nameEndPosition = text.find(')', nameStartPosition + 1); |
|
107 if (nameEndPosition == -1) { |
|
108 finished = true; |
|
109 isValid = false; |
|
110 break; |
|
111 } |
|
112 |
|
113 ++nameStartPosition; |
|
114 } else { |
|
115 int length = text.length(); |
|
116 for (nameEndPosition = nameStartPosition; nameEndPosition < length; ++nameEndPosition) { |
|
117 if (!isValidVariableNameCharacter(text[nameEndPosition])) |
|
118 break; |
|
119 } |
|
120 } |
|
121 |
|
122 if (nameEndPosition < nameStartPosition) { |
|
123 finished = true; |
|
124 isValid = false; |
|
125 break; |
|
126 } |
|
127 |
|
128 // Eventually split of conversion string, and check its syntax afterwards |
|
129 String conversionString; |
|
130 String variableName = text.substring(nameStartPosition, nameEndPosition - nameStartPosition); |
|
131 |
|
132 int conversionStringStart = variableName.find(':'); |
|
133 if (conversionStringStart != -1) { |
|
134 conversionString = variableName.substring(conversionStringStart + 1, variableName.length() - (conversionStringStart + 1)); |
|
135 variableName = variableName.left(conversionStringStart); |
|
136 } |
|
137 |
|
138 isValid = isValidVariableName(variableName); |
|
139 if (!isValid) { |
|
140 finished = true; |
|
141 break; |
|
142 } |
|
143 |
|
144 if (!conversionString.isEmpty()) { |
|
145 isValid = isValidVariableName(conversionString); |
|
146 if (!isValid) { |
|
147 finished = true; |
|
148 break; |
|
149 } |
|
150 |
|
151 WMLVariableEscapingMode escapeMode = WMLVariableEscapingNone; |
|
152 isValid = isValidVariableEscapingModeString(conversionString, escapeMode); |
|
153 if (!isValid) { |
|
154 finished = true; |
|
155 break; |
|
156 } |
|
157 } |
|
158 |
|
159 currentPosition = nameEndPosition; |
|
160 } |
|
161 |
|
162 return foundReference; |
|
163 } |
|
164 |
|
165 String substituteVariableReferences(const String& reference, Document* document, WMLVariableEscapingMode escapeMode) |
|
166 { |
|
167 ASSERT(document); |
|
168 |
|
169 if (reference.isEmpty()) |
|
170 return reference; |
|
171 |
|
172 WMLPageState* pageState = wmlPageStateForDocument(document); |
|
173 if (!pageState) |
|
174 return reference; |
|
175 |
|
176 bool isValid = true; |
|
177 String remainingInput = reference; |
|
178 String result; |
|
179 |
|
180 while (!remainingInput.isEmpty()) { |
|
181 ASSERT(isValid); |
|
182 |
|
183 int start = remainingInput.find("$"); |
|
184 if (start == -1) { |
|
185 // Consume all remaining characters, as there's nothing more to substitute |
|
186 result += remainingInput; |
|
187 break; |
|
188 } |
|
189 |
|
190 // Consume all characters until the variable reference beginning |
|
191 result += remainingInput.left(start); |
|
192 remainingInput.remove(0, start); |
|
193 |
|
194 // Transform adjacent dollar signs into a single dollar sign as string literal |
|
195 if (remainingInput[1] == '$') { |
|
196 result += "$"; |
|
197 remainingInput.remove(0, 2); |
|
198 continue; |
|
199 } |
|
200 |
|
201 String variableName; |
|
202 String conversionMode; |
|
203 |
|
204 if (remainingInput[1] == '(') { |
|
205 int referenceEndPosition = remainingInput.find(")"); |
|
206 if (referenceEndPosition == -1) { |
|
207 isValid = false; |
|
208 break; |
|
209 } |
|
210 |
|
211 variableName = remainingInput.substring(2, referenceEndPosition - 2); |
|
212 remainingInput.remove(0, referenceEndPosition + 1); |
|
213 |
|
214 // Determine variable conversion mode string |
|
215 int pos = variableName.find(':'); |
|
216 if (pos != -1) { |
|
217 conversionMode = variableName.substring(pos + 1, variableName.length() - (pos + 1)); |
|
218 variableName = variableName.left(pos); |
|
219 } |
|
220 } else { |
|
221 int length = remainingInput.length(); |
|
222 int referenceEndPosition = 1; |
|
223 |
|
224 for (; referenceEndPosition < length; ++referenceEndPosition) { |
|
225 if (!isValidVariableNameCharacter(remainingInput[referenceEndPosition])) |
|
226 break; |
|
227 } |
|
228 |
|
229 variableName = remainingInput.substring(1, referenceEndPosition - 1); |
|
230 remainingInput.remove(0, referenceEndPosition); |
|
231 } |
|
232 |
|
233 isValid = isValidVariableName(variableName); |
|
234 if (!isValid) |
|
235 break; |
|
236 |
|
237 ASSERT(!variableName.isEmpty()); |
|
238 |
|
239 String variableValue = pageState->getVariable(variableName); |
|
240 if (variableValue.isEmpty()) |
|
241 continue; |
|
242 |
|
243 if (containsVariableReference(variableValue, isValid)) { |
|
244 if (!isValid) |
|
245 break; |
|
246 |
|
247 variableValue = substituteVariableReferences(variableValue, document, escapeMode); |
|
248 continue; |
|
249 } |
|
250 |
|
251 if (!conversionMode.isEmpty()) { |
|
252 // Override default escape mode, if desired |
|
253 WMLVariableEscapingMode specifiedEscapeMode = WMLVariableEscapingNone; |
|
254 if ((isValid = isValidVariableEscapingModeString(conversionMode, specifiedEscapeMode))) |
|
255 escapeMode = specifiedEscapeMode; |
|
256 |
|
257 if (!isValid) |
|
258 break; |
|
259 } |
|
260 |
|
261 switch (escapeMode) { |
|
262 case WMLVariableEscapingNone: |
|
263 break; |
|
264 case WMLVariableEscapingEscape: |
|
265 variableValue = encodeWithURLEscapeSequences(variableValue); |
|
266 break; |
|
267 case WMLVariableEscapingUnescape: |
|
268 variableValue = decodeURLEscapeSequences(variableValue); |
|
269 break; |
|
270 } |
|
271 |
|
272 result += variableValue; |
|
273 ASSERT(isValid); |
|
274 } |
|
275 |
|
276 if (!isValid) { |
|
277 reportWMLError(document, WMLErrorInvalidVariableReference); |
|
278 return reference; |
|
279 } |
|
280 |
|
281 return result; |
|
282 } |
|
283 |
|
284 } |
|
285 |
|
286 #endif |