]>
Commit | Line | Data |
---|---|---|
81345200 A |
1 | /* |
2 | * Copyright (C) 2010 Google Inc. All rights reserved. | |
ed1e77d3 | 3 | * Copyright (C) 2014 University of Washington. All rights reserved. |
81345200 A |
4 | * |
5 | * Redistribution and use in source and binary forms, with or without | |
6 | * modification, are permitted provided that the following conditions are | |
7 | * met: | |
8 | * | |
9 | * * Redistributions of source code must retain the above copyright | |
10 | * notice, this list of conditions and the following disclaimer. | |
11 | * * Redistributions in binary form must reproduce the above | |
12 | * copyright notice, this list of conditions and the following disclaimer | |
13 | * in the documentation and/or other materials provided with the | |
14 | * distribution. | |
15 | * * Neither the name of Google Inc. nor the names of its | |
16 | * contributors may be used to endorse or promote products derived from | |
17 | * this software without specific prior written permission. | |
18 | * | |
19 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | |
20 | * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | |
21 | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | |
22 | * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | |
23 | * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | |
24 | * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | |
25 | * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | |
26 | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | |
27 | * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | |
28 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | |
29 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |
30 | */ | |
31 | ||
32 | #include "config.h" | |
33 | #include "InspectorValues.h" | |
34 | ||
35 | #include <wtf/DecimalNumber.h> | |
36 | #include <wtf/dtoa.h> | |
37 | #include <wtf/text/StringBuilder.h> | |
38 | ||
39 | namespace Inspector { | |
40 | ||
41 | namespace { | |
42 | ||
43 | static const int stackLimit = 1000; | |
44 | ||
45 | enum Token { | |
46 | OBJECT_BEGIN, | |
47 | OBJECT_END, | |
48 | ARRAY_BEGIN, | |
49 | ARRAY_END, | |
50 | STRING, | |
51 | NUMBER, | |
52 | BOOL_TRUE, | |
53 | BOOL_FALSE, | |
54 | NULL_TOKEN, | |
55 | LIST_SEPARATOR, | |
56 | OBJECT_PAIR_SEPARATOR, | |
57 | INVALID_TOKEN, | |
58 | }; | |
59 | ||
60 | const char* const nullString = "null"; | |
61 | const char* const trueString = "true"; | |
62 | const char* const falseString = "false"; | |
63 | ||
64 | bool parseConstToken(const UChar* start, const UChar* end, const UChar** tokenEnd, const char* token) | |
65 | { | |
66 | while (start < end && *token != '\0' && *start++ == *token++) { } | |
ed1e77d3 | 67 | |
81345200 A |
68 | if (*token != '\0') |
69 | return false; | |
ed1e77d3 | 70 | |
81345200 A |
71 | *tokenEnd = start; |
72 | return true; | |
73 | } | |
74 | ||
75 | bool readInt(const UChar* start, const UChar* end, const UChar** tokenEnd, bool canHaveLeadingZeros) | |
76 | { | |
77 | if (start == end) | |
78 | return false; | |
ed1e77d3 | 79 | |
81345200 A |
80 | bool haveLeadingZero = '0' == *start; |
81 | int length = 0; | |
82 | while (start < end && '0' <= *start && *start <= '9') { | |
83 | ++start; | |
84 | ++length; | |
85 | } | |
ed1e77d3 | 86 | |
81345200 A |
87 | if (!length) |
88 | return false; | |
ed1e77d3 | 89 | |
81345200 A |
90 | if (!canHaveLeadingZeros && length > 1 && haveLeadingZero) |
91 | return false; | |
ed1e77d3 | 92 | |
81345200 A |
93 | *tokenEnd = start; |
94 | return true; | |
95 | } | |
96 | ||
97 | bool parseNumberToken(const UChar* start, const UChar* end, const UChar** tokenEnd) | |
98 | { | |
99 | // We just grab the number here. We validate the size in DecodeNumber. | |
100 | // According to RFC4627, a valid number is: [minus] int [frac] [exp] | |
101 | if (start == end) | |
102 | return false; | |
ed1e77d3 | 103 | |
81345200 A |
104 | UChar c = *start; |
105 | if ('-' == c) | |
106 | ++start; | |
107 | ||
108 | if (!readInt(start, end, &start, false)) | |
109 | return false; | |
ed1e77d3 | 110 | |
81345200 A |
111 | if (start == end) { |
112 | *tokenEnd = start; | |
113 | return true; | |
114 | } | |
115 | ||
116 | // Optional fraction part | |
117 | c = *start; | |
118 | if ('.' == c) { | |
119 | ++start; | |
120 | if (!readInt(start, end, &start, true)) | |
121 | return false; | |
122 | if (start == end) { | |
123 | *tokenEnd = start; | |
124 | return true; | |
125 | } | |
126 | c = *start; | |
127 | } | |
128 | ||
129 | // Optional exponent part | |
130 | if ('e' == c || 'E' == c) { | |
131 | ++start; | |
132 | if (start == end) | |
133 | return false; | |
134 | c = *start; | |
135 | if ('-' == c || '+' == c) { | |
136 | ++start; | |
137 | if (start == end) | |
138 | return false; | |
139 | } | |
140 | if (!readInt(start, end, &start, true)) | |
141 | return false; | |
142 | } | |
143 | ||
144 | *tokenEnd = start; | |
145 | return true; | |
146 | } | |
147 | ||
148 | bool readHexDigits(const UChar* start, const UChar* end, const UChar** tokenEnd, int digits) | |
149 | { | |
150 | if (end - start < digits) | |
151 | return false; | |
ed1e77d3 | 152 | |
81345200 A |
153 | for (int i = 0; i < digits; ++i) { |
154 | UChar c = *start++; | |
155 | if (!(('0' <= c && c <= '9') || ('a' <= c && c <= 'f') || ('A' <= c && c <= 'F'))) | |
156 | return false; | |
157 | } | |
ed1e77d3 | 158 | |
81345200 A |
159 | *tokenEnd = start; |
160 | return true; | |
161 | } | |
162 | ||
163 | bool parseStringToken(const UChar* start, const UChar* end, const UChar** tokenEnd) | |
164 | { | |
165 | while (start < end) { | |
166 | UChar c = *start++; | |
167 | if ('\\' == c) { | |
168 | c = *start++; | |
169 | // Make sure the escaped char is valid. | |
170 | switch (c) { | |
171 | case 'x': | |
172 | if (!readHexDigits(start, end, &start, 2)) | |
173 | return false; | |
174 | break; | |
175 | case 'u': | |
176 | if (!readHexDigits(start, end, &start, 4)) | |
177 | return false; | |
178 | break; | |
179 | case '\\': | |
180 | case '/': | |
181 | case 'b': | |
182 | case 'f': | |
183 | case 'n': | |
184 | case 'r': | |
185 | case 't': | |
186 | case 'v': | |
187 | case '"': | |
188 | break; | |
189 | default: | |
190 | return false; | |
191 | } | |
192 | } else if ('"' == c) { | |
193 | *tokenEnd = start; | |
194 | return true; | |
195 | } | |
196 | } | |
ed1e77d3 | 197 | |
81345200 A |
198 | return false; |
199 | } | |
200 | ||
201 | Token parseToken(const UChar* start, const UChar* end, const UChar** tokenStart, const UChar** tokenEnd) | |
202 | { | |
203 | while (start < end && isSpaceOrNewline(*start)) | |
204 | ++start; | |
205 | ||
206 | if (start == end) | |
207 | return INVALID_TOKEN; | |
208 | ||
209 | *tokenStart = start; | |
210 | ||
211 | switch (*start) { | |
212 | case 'n': | |
213 | if (parseConstToken(start, end, tokenEnd, nullString)) | |
214 | return NULL_TOKEN; | |
215 | break; | |
216 | case 't': | |
217 | if (parseConstToken(start, end, tokenEnd, trueString)) | |
218 | return BOOL_TRUE; | |
219 | break; | |
220 | case 'f': | |
221 | if (parseConstToken(start, end, tokenEnd, falseString)) | |
222 | return BOOL_FALSE; | |
223 | break; | |
224 | case '[': | |
225 | *tokenEnd = start + 1; | |
226 | return ARRAY_BEGIN; | |
227 | case ']': | |
228 | *tokenEnd = start + 1; | |
229 | return ARRAY_END; | |
230 | case ',': | |
231 | *tokenEnd = start + 1; | |
232 | return LIST_SEPARATOR; | |
233 | case '{': | |
234 | *tokenEnd = start + 1; | |
235 | return OBJECT_BEGIN; | |
236 | case '}': | |
237 | *tokenEnd = start + 1; | |
238 | return OBJECT_END; | |
239 | case ':': | |
240 | *tokenEnd = start + 1; | |
241 | return OBJECT_PAIR_SEPARATOR; | |
242 | case '0': | |
243 | case '1': | |
244 | case '2': | |
245 | case '3': | |
246 | case '4': | |
247 | case '5': | |
248 | case '6': | |
249 | case '7': | |
250 | case '8': | |
251 | case '9': | |
252 | case '-': | |
253 | if (parseNumberToken(start, end, tokenEnd)) | |
254 | return NUMBER; | |
255 | break; | |
256 | case '"': | |
257 | if (parseStringToken(start + 1, end, tokenEnd)) | |
258 | return STRING; | |
259 | break; | |
260 | } | |
ed1e77d3 | 261 | |
81345200 A |
262 | return INVALID_TOKEN; |
263 | } | |
264 | ||
265 | inline int hexToInt(UChar c) | |
266 | { | |
267 | if ('0' <= c && c <= '9') | |
268 | return c - '0'; | |
269 | if ('A' <= c && c <= 'F') | |
270 | return c - 'A' + 10; | |
271 | if ('a' <= c && c <= 'f') | |
272 | return c - 'a' + 10; | |
ed1e77d3 | 273 | |
81345200 A |
274 | ASSERT_NOT_REACHED(); |
275 | return 0; | |
276 | } | |
277 | ||
ed1e77d3 | 278 | bool decodeString(const UChar* start, const UChar* end, StringBuilder& output) |
81345200 A |
279 | { |
280 | while (start < end) { | |
281 | UChar c = *start++; | |
282 | if ('\\' != c) { | |
ed1e77d3 | 283 | output.append(c); |
81345200 A |
284 | continue; |
285 | } | |
286 | c = *start++; | |
287 | switch (c) { | |
288 | case '"': | |
289 | case '/': | |
290 | case '\\': | |
291 | break; | |
292 | case 'b': | |
293 | c = '\b'; | |
294 | break; | |
295 | case 'f': | |
296 | c = '\f'; | |
297 | break; | |
298 | case 'n': | |
299 | c = '\n'; | |
300 | break; | |
301 | case 'r': | |
302 | c = '\r'; | |
303 | break; | |
304 | case 't': | |
305 | c = '\t'; | |
306 | break; | |
307 | case 'v': | |
308 | c = '\v'; | |
309 | break; | |
310 | case 'x': | |
311 | c = (hexToInt(*start) << 4) + | |
312 | hexToInt(*(start + 1)); | |
313 | start += 2; | |
314 | break; | |
315 | case 'u': | |
316 | c = (hexToInt(*start) << 12) + | |
317 | (hexToInt(*(start + 1)) << 8) + | |
318 | (hexToInt(*(start + 2)) << 4) + | |
319 | hexToInt(*(start + 3)); | |
320 | start += 4; | |
321 | break; | |
322 | default: | |
323 | return false; | |
324 | } | |
ed1e77d3 | 325 | output.append(c); |
81345200 | 326 | } |
ed1e77d3 | 327 | |
81345200 A |
328 | return true; |
329 | } | |
330 | ||
ed1e77d3 | 331 | bool decodeString(const UChar* start, const UChar* end, String& output) |
81345200 A |
332 | { |
333 | if (start == end) { | |
ed1e77d3 | 334 | output = emptyString(); |
81345200 A |
335 | return true; |
336 | } | |
ed1e77d3 | 337 | |
81345200 A |
338 | if (start > end) |
339 | return false; | |
ed1e77d3 | 340 | |
81345200 A |
341 | StringBuilder buffer; |
342 | buffer.reserveCapacity(end - start); | |
ed1e77d3 | 343 | if (!decodeString(start, end, buffer)) |
81345200 | 344 | return false; |
ed1e77d3 A |
345 | |
346 | output = buffer.toString(); | |
81345200 A |
347 | return true; |
348 | } | |
349 | ||
ed1e77d3 | 350 | RefPtr<InspectorValue> buildValue(const UChar* start, const UChar* end, const UChar** valueTokenEnd, int depth) |
81345200 A |
351 | { |
352 | if (depth > stackLimit) | |
353 | return nullptr; | |
354 | ||
355 | RefPtr<InspectorValue> result; | |
356 | const UChar* tokenStart; | |
357 | const UChar* tokenEnd; | |
358 | Token token = parseToken(start, end, &tokenStart, &tokenEnd); | |
359 | switch (token) { | |
360 | case INVALID_TOKEN: | |
361 | return nullptr; | |
362 | case NULL_TOKEN: | |
363 | result = InspectorValue::null(); | |
364 | break; | |
365 | case BOOL_TRUE: | |
366 | result = InspectorBasicValue::create(true); | |
367 | break; | |
368 | case BOOL_FALSE: | |
369 | result = InspectorBasicValue::create(false); | |
370 | break; | |
371 | case NUMBER: { | |
372 | bool ok; | |
373 | double value = charactersToDouble(tokenStart, tokenEnd - tokenStart, &ok); | |
374 | if (!ok) | |
375 | return nullptr; | |
376 | result = InspectorBasicValue::create(value); | |
377 | break; | |
378 | } | |
379 | case STRING: { | |
380 | String value; | |
ed1e77d3 | 381 | bool ok = decodeString(tokenStart + 1, tokenEnd - 1, value); |
81345200 A |
382 | if (!ok) |
383 | return nullptr; | |
384 | result = InspectorString::create(value); | |
385 | break; | |
386 | } | |
387 | case ARRAY_BEGIN: { | |
ed1e77d3 | 388 | Ref<InspectorArray> array = InspectorArray::create(); |
81345200 A |
389 | start = tokenEnd; |
390 | token = parseToken(start, end, &tokenStart, &tokenEnd); | |
391 | while (token != ARRAY_END) { | |
392 | RefPtr<InspectorValue> arrayNode = buildValue(start, end, &tokenEnd, depth + 1); | |
393 | if (!arrayNode) | |
394 | return nullptr; | |
ed1e77d3 | 395 | array->pushValue(WTF::move(arrayNode)); |
81345200 A |
396 | |
397 | // After a list value, we expect a comma or the end of the list. | |
398 | start = tokenEnd; | |
399 | token = parseToken(start, end, &tokenStart, &tokenEnd); | |
400 | if (token == LIST_SEPARATOR) { | |
401 | start = tokenEnd; | |
402 | token = parseToken(start, end, &tokenStart, &tokenEnd); | |
403 | if (token == ARRAY_END) | |
404 | return nullptr; | |
405 | } else if (token != ARRAY_END) { | |
406 | // Unexpected value after list value. Bail out. | |
407 | return nullptr; | |
408 | } | |
409 | } | |
410 | if (token != ARRAY_END) | |
411 | return nullptr; | |
ed1e77d3 | 412 | result = WTF::move(array); |
81345200 A |
413 | break; |
414 | } | |
415 | case OBJECT_BEGIN: { | |
ed1e77d3 | 416 | Ref<InspectorObject> object = InspectorObject::create(); |
81345200 A |
417 | start = tokenEnd; |
418 | token = parseToken(start, end, &tokenStart, &tokenEnd); | |
419 | while (token != OBJECT_END) { | |
420 | if (token != STRING) | |
421 | return nullptr; | |
422 | String key; | |
ed1e77d3 | 423 | if (!decodeString(tokenStart + 1, tokenEnd - 1, key)) |
81345200 A |
424 | return nullptr; |
425 | start = tokenEnd; | |
426 | ||
427 | token = parseToken(start, end, &tokenStart, &tokenEnd); | |
428 | if (token != OBJECT_PAIR_SEPARATOR) | |
429 | return nullptr; | |
430 | start = tokenEnd; | |
431 | ||
432 | RefPtr<InspectorValue> value = buildValue(start, end, &tokenEnd, depth + 1); | |
433 | if (!value) | |
434 | return nullptr; | |
ed1e77d3 | 435 | object->setValue(key, WTF::move(value)); |
81345200 A |
436 | start = tokenEnd; |
437 | ||
438 | // After a key/value pair, we expect a comma or the end of the | |
439 | // object. | |
440 | token = parseToken(start, end, &tokenStart, &tokenEnd); | |
441 | if (token == LIST_SEPARATOR) { | |
442 | start = tokenEnd; | |
443 | token = parseToken(start, end, &tokenStart, &tokenEnd); | |
444 | if (token == OBJECT_END) | |
445 | return nullptr; | |
446 | } else if (token != OBJECT_END) { | |
447 | // Unexpected value after last object value. Bail out. | |
448 | return nullptr; | |
449 | } | |
450 | } | |
451 | if (token != OBJECT_END) | |
452 | return nullptr; | |
ed1e77d3 | 453 | result = WTF::move(object); |
81345200 A |
454 | break; |
455 | } | |
456 | ||
457 | default: | |
458 | // We got a token that's not a value. | |
459 | return nullptr; | |
460 | } | |
461 | *valueTokenEnd = tokenEnd; | |
462 | return result.release(); | |
463 | } | |
464 | ||
ed1e77d3 | 465 | inline bool escapeChar(UChar c, StringBuilder& dst) |
81345200 A |
466 | { |
467 | switch (c) { | |
ed1e77d3 A |
468 | case '\b': dst.appendLiteral("\\b"); break; |
469 | case '\f': dst.appendLiteral("\\f"); break; | |
470 | case '\n': dst.appendLiteral("\\n"); break; | |
471 | case '\r': dst.appendLiteral("\\r"); break; | |
472 | case '\t': dst.appendLiteral("\\t"); break; | |
473 | case '\\': dst.appendLiteral("\\\\"); break; | |
474 | case '"': dst.appendLiteral("\\\""); break; | |
81345200 A |
475 | default: |
476 | return false; | |
477 | } | |
478 | return true; | |
479 | } | |
480 | ||
ed1e77d3 | 481 | inline void doubleQuoteString(const String& str, StringBuilder& dst) |
81345200 | 482 | { |
ed1e77d3 | 483 | dst.append('"'); |
81345200 A |
484 | for (unsigned i = 0; i < str.length(); ++i) { |
485 | UChar c = str[i]; | |
486 | if (!escapeChar(c, dst)) { | |
487 | if (c < 32 || c > 126 || c == '<' || c == '>') { | |
488 | // 1. Escaping <, > to prevent script execution. | |
489 | // 2. Technically, we could also pass through c > 126 as UTF8, but this | |
490 | // is also optional. It would also be a pain to implement here. | |
ed1e77d3 | 491 | dst.append(String::format("\\u%04X", c)); |
81345200 | 492 | } else |
ed1e77d3 | 493 | dst.append(c); |
81345200 A |
494 | } |
495 | } | |
ed1e77d3 | 496 | dst.append('"'); |
81345200 A |
497 | } |
498 | ||
499 | } // anonymous namespace | |
500 | ||
ed1e77d3 | 501 | bool InspectorValue::asBoolean(bool&) const |
81345200 A |
502 | { |
503 | return false; | |
504 | } | |
505 | ||
ed1e77d3 | 506 | bool InspectorValue::asDouble(double&) const |
81345200 A |
507 | { |
508 | return false; | |
509 | } | |
510 | ||
ed1e77d3 | 511 | bool InspectorValue::asDouble(float&) const |
81345200 A |
512 | { |
513 | return false; | |
514 | } | |
515 | ||
ed1e77d3 | 516 | bool InspectorValue::asInteger(int&) const |
81345200 A |
517 | { |
518 | return false; | |
519 | } | |
520 | ||
ed1e77d3 | 521 | bool InspectorValue::asInteger(unsigned&) const |
81345200 A |
522 | { |
523 | return false; | |
524 | } | |
525 | ||
ed1e77d3 | 526 | bool InspectorValue::asInteger(long&) const |
81345200 A |
527 | { |
528 | return false; | |
529 | } | |
530 | ||
ed1e77d3 | 531 | bool InspectorValue::asInteger(long long&) const |
81345200 A |
532 | { |
533 | return false; | |
534 | } | |
535 | ||
ed1e77d3 | 536 | bool InspectorValue::asInteger(unsigned long&) const |
81345200 A |
537 | { |
538 | return false; | |
539 | } | |
540 | ||
ed1e77d3 | 541 | bool InspectorValue::asInteger(unsigned long long&) const |
81345200 A |
542 | { |
543 | return false; | |
544 | } | |
545 | ||
ed1e77d3 | 546 | bool InspectorValue::asString(String&) const |
81345200 A |
547 | { |
548 | return false; | |
549 | } | |
550 | ||
ed1e77d3 | 551 | bool InspectorValue::asValue(RefPtr<InspectorValue>& output) |
81345200 | 552 | { |
ed1e77d3 | 553 | output = this; |
81345200 A |
554 | return true; |
555 | } | |
556 | ||
ed1e77d3 | 557 | bool InspectorValue::asObject(RefPtr<InspectorObject>&) |
81345200 A |
558 | { |
559 | return false; | |
560 | } | |
561 | ||
ed1e77d3 | 562 | bool InspectorValue::asArray(RefPtr<InspectorArray>&) |
81345200 A |
563 | { |
564 | return false; | |
565 | } | |
566 | ||
ed1e77d3 | 567 | bool InspectorValue::parseJSON(const String& jsonInput, RefPtr<InspectorValue>& output) |
81345200 A |
568 | { |
569 | // FIXME: This whole file should just use StringView instead of UChar/length and avoid upconverting. | |
ed1e77d3 | 570 | auto characters = StringView(jsonInput).upconvertedCharacters(); |
81345200 | 571 | const UChar* start = characters; |
ed1e77d3 | 572 | const UChar* end = start + jsonInput.length(); |
81345200 | 573 | const UChar* tokenEnd; |
ed1e77d3 A |
574 | RefPtr<InspectorValue> result = buildValue(start, end, &tokenEnd, 0); |
575 | if (!result || tokenEnd != end) | |
576 | return false; | |
577 | ||
578 | output = result.release(); | |
579 | return true; | |
81345200 A |
580 | } |
581 | ||
582 | String InspectorValue::toJSONString() const | |
583 | { | |
584 | StringBuilder result; | |
585 | result.reserveCapacity(512); | |
ed1e77d3 | 586 | writeJSON(result); |
81345200 A |
587 | return result.toString(); |
588 | } | |
589 | ||
ed1e77d3 | 590 | void InspectorValue::writeJSON(StringBuilder& output) const |
81345200 | 591 | { |
ed1e77d3 A |
592 | ASSERT(m_type == Type::Null); |
593 | ||
594 | output.appendLiteral("null"); | |
81345200 A |
595 | } |
596 | ||
ed1e77d3 | 597 | bool InspectorBasicValue::asBoolean(bool& output) const |
81345200 | 598 | { |
ed1e77d3 | 599 | if (type() != Type::Boolean) |
81345200 | 600 | return false; |
ed1e77d3 A |
601 | |
602 | output = m_booleanValue; | |
81345200 A |
603 | return true; |
604 | } | |
605 | ||
ed1e77d3 | 606 | bool InspectorBasicValue::asDouble(double& output) const |
81345200 | 607 | { |
ed1e77d3 | 608 | if (type() != Type::Double) |
81345200 | 609 | return false; |
ed1e77d3 A |
610 | |
611 | output = m_doubleValue; | |
81345200 A |
612 | return true; |
613 | } | |
614 | ||
ed1e77d3 | 615 | bool InspectorBasicValue::asDouble(float& output) const |
81345200 | 616 | { |
ed1e77d3 | 617 | if (type() != Type::Double) |
81345200 | 618 | return false; |
ed1e77d3 A |
619 | |
620 | output = static_cast<float>(m_doubleValue); | |
81345200 A |
621 | return true; |
622 | } | |
623 | ||
ed1e77d3 | 624 | bool InspectorBasicValue::asInteger(int& output) const |
81345200 | 625 | { |
ed1e77d3 | 626 | if (type() != Type::Integer && type() != Type::Double) |
81345200 | 627 | return false; |
ed1e77d3 A |
628 | |
629 | output = static_cast<int>(m_doubleValue); | |
81345200 A |
630 | return true; |
631 | } | |
632 | ||
ed1e77d3 | 633 | bool InspectorBasicValue::asInteger(unsigned& output) const |
81345200 | 634 | { |
ed1e77d3 | 635 | if (type() != Type::Integer && type() != Type::Double) |
81345200 | 636 | return false; |
ed1e77d3 A |
637 | |
638 | output = static_cast<unsigned>(m_doubleValue); | |
81345200 A |
639 | return true; |
640 | } | |
641 | ||
ed1e77d3 | 642 | bool InspectorBasicValue::asInteger(long& output) const |
81345200 | 643 | { |
ed1e77d3 | 644 | if (type() != Type::Integer && type() != Type::Double) |
81345200 | 645 | return false; |
ed1e77d3 A |
646 | |
647 | output = static_cast<long>(m_doubleValue); | |
81345200 A |
648 | return true; |
649 | } | |
650 | ||
ed1e77d3 | 651 | bool InspectorBasicValue::asInteger(long long& output) const |
81345200 | 652 | { |
ed1e77d3 | 653 | if (type() != Type::Integer && type() != Type::Double) |
81345200 | 654 | return false; |
ed1e77d3 A |
655 | |
656 | output = static_cast<long long>(m_doubleValue); | |
81345200 A |
657 | return true; |
658 | } | |
659 | ||
ed1e77d3 | 660 | bool InspectorBasicValue::asInteger(unsigned long& output) const |
81345200 | 661 | { |
ed1e77d3 | 662 | if (type() != Type::Integer && type() != Type::Double) |
81345200 | 663 | return false; |
ed1e77d3 A |
664 | |
665 | output = static_cast<unsigned long>(m_doubleValue); | |
81345200 A |
666 | return true; |
667 | } | |
668 | ||
ed1e77d3 | 669 | bool InspectorBasicValue::asInteger(unsigned long long& output) const |
81345200 | 670 | { |
ed1e77d3 | 671 | if (type() != Type::Integer && type() != Type::Double) |
81345200 | 672 | return false; |
ed1e77d3 A |
673 | |
674 | output = static_cast<unsigned long long>(m_doubleValue); | |
81345200 A |
675 | return true; |
676 | } | |
677 | ||
ed1e77d3 | 678 | void InspectorBasicValue::writeJSON(StringBuilder& output) const |
81345200 | 679 | { |
ed1e77d3 A |
680 | ASSERT(type() == Type::Boolean || type() == Type::Double || type() == Type::Integer); |
681 | ||
682 | if (type() == Type::Boolean) { | |
683 | if (m_booleanValue) | |
684 | output.appendLiteral("true"); | |
81345200 | 685 | else |
ed1e77d3 A |
686 | output.appendLiteral("false"); |
687 | } else if (type() == Type::Double || type() == Type::Integer) { | |
81345200 A |
688 | NumberToLStringBuffer buffer; |
689 | if (!std::isfinite(m_doubleValue)) { | |
ed1e77d3 | 690 | output.appendLiteral("null"); |
81345200 A |
691 | return; |
692 | } | |
693 | DecimalNumber decimal = m_doubleValue; | |
694 | unsigned length = 0; | |
695 | if (decimal.bufferLengthForStringDecimal() > WTF::NumberToStringBufferLength) { | |
696 | // Not enough room for decimal. Use exponential format. | |
697 | if (decimal.bufferLengthForStringExponential() > WTF::NumberToStringBufferLength) { | |
698 | // Fallback for an abnormal case if it's too little even for exponential. | |
ed1e77d3 | 699 | output.appendLiteral("NaN"); |
81345200 A |
700 | return; |
701 | } | |
702 | length = decimal.toStringExponential(buffer, WTF::NumberToStringBufferLength); | |
703 | } else | |
704 | length = decimal.toStringDecimal(buffer, WTF::NumberToStringBufferLength); | |
ed1e77d3 | 705 | output.append(buffer, length); |
81345200 A |
706 | } |
707 | } | |
708 | ||
ed1e77d3 | 709 | bool InspectorString::asString(String& output) const |
81345200 | 710 | { |
ed1e77d3 | 711 | output = m_stringValue; |
81345200 A |
712 | return true; |
713 | } | |
714 | ||
ed1e77d3 | 715 | void InspectorString::writeJSON(StringBuilder& output) const |
81345200 | 716 | { |
ed1e77d3 | 717 | ASSERT(type() == Type::String); |
81345200 A |
718 | doubleQuoteString(m_stringValue, output); |
719 | } | |
720 | ||
721 | InspectorObjectBase::~InspectorObjectBase() | |
722 | { | |
723 | } | |
724 | ||
ed1e77d3 | 725 | bool InspectorObjectBase::asObject(RefPtr<InspectorObject>& output) |
81345200 A |
726 | { |
727 | COMPILE_ASSERT(sizeof(InspectorObject) == sizeof(InspectorObjectBase), cannot_cast); | |
81345200 | 728 | |
ed1e77d3 A |
729 | output = static_cast<InspectorObject*>(this); |
730 | return true; | |
81345200 A |
731 | } |
732 | ||
733 | InspectorObject* InspectorObjectBase::openAccessors() | |
734 | { | |
735 | COMPILE_ASSERT(sizeof(InspectorObject) == sizeof(InspectorObjectBase), cannot_cast); | |
ed1e77d3 | 736 | |
81345200 A |
737 | return static_cast<InspectorObject*>(this); |
738 | } | |
739 | ||
ed1e77d3 | 740 | bool InspectorObjectBase::getBoolean(const String& name, bool& output) const |
81345200 | 741 | { |
ed1e77d3 A |
742 | RefPtr<InspectorValue> value; |
743 | if (!getValue(name, value)) | |
81345200 | 744 | return false; |
ed1e77d3 | 745 | |
81345200 A |
746 | return value->asBoolean(output); |
747 | } | |
748 | ||
ed1e77d3 | 749 | bool InspectorObjectBase::getString(const String& name, String& output) const |
81345200 | 750 | { |
ed1e77d3 A |
751 | RefPtr<InspectorValue> value; |
752 | if (!getValue(name, value)) | |
81345200 | 753 | return false; |
ed1e77d3 | 754 | |
81345200 A |
755 | return value->asString(output); |
756 | } | |
757 | ||
ed1e77d3 | 758 | bool InspectorObjectBase::getObject(const String& name, RefPtr<InspectorObject>& output) const |
81345200 | 759 | { |
ed1e77d3 A |
760 | RefPtr<InspectorValue> value; |
761 | if (!getValue(name, value)) | |
762 | return false; | |
763 | ||
764 | return value->asObject(output); | |
81345200 A |
765 | } |
766 | ||
ed1e77d3 | 767 | bool InspectorObjectBase::getArray(const String& name, RefPtr<InspectorArray>& output) const |
81345200 | 768 | { |
ed1e77d3 A |
769 | RefPtr<InspectorValue> value; |
770 | if (!getValue(name, value)) | |
771 | return false; | |
772 | ||
773 | return value->asArray(output); | |
81345200 A |
774 | } |
775 | ||
ed1e77d3 | 776 | bool InspectorObjectBase::getValue(const String& name, RefPtr<InspectorValue>& output) const |
81345200 | 777 | { |
ed1e77d3 A |
778 | Dictionary::const_iterator findResult = m_data.find(name); |
779 | if (findResult == m_data.end()) | |
780 | return false; | |
781 | ||
782 | output = findResult->value; | |
783 | return true; | |
81345200 A |
784 | } |
785 | ||
786 | void InspectorObjectBase::remove(const String& name) | |
787 | { | |
788 | m_data.remove(name); | |
ed1e77d3 | 789 | m_order.removeFirst(name); |
81345200 A |
790 | } |
791 | ||
ed1e77d3 | 792 | void InspectorObjectBase::writeJSON(StringBuilder& output) const |
81345200 | 793 | { |
ed1e77d3 | 794 | output.append('{'); |
81345200 | 795 | for (size_t i = 0; i < m_order.size(); ++i) { |
ed1e77d3 A |
796 | auto findResult = m_data.find(m_order[i]); |
797 | ASSERT(findResult != m_data.end()); | |
81345200 | 798 | if (i) |
ed1e77d3 A |
799 | output.append(','); |
800 | doubleQuoteString(findResult->key, output); | |
801 | output.append(':'); | |
802 | findResult->value->writeJSON(output); | |
81345200 | 803 | } |
ed1e77d3 | 804 | output.append('}'); |
81345200 A |
805 | } |
806 | ||
807 | InspectorObjectBase::InspectorObjectBase() | |
ed1e77d3 | 808 | : InspectorValue(Type::Object) |
81345200 A |
809 | , m_data() |
810 | , m_order() | |
811 | { | |
812 | } | |
813 | ||
814 | InspectorArrayBase::~InspectorArrayBase() | |
815 | { | |
816 | } | |
817 | ||
ed1e77d3 | 818 | bool InspectorArrayBase::asArray(RefPtr<InspectorArray>& output) |
81345200 A |
819 | { |
820 | COMPILE_ASSERT(sizeof(InspectorArrayBase) == sizeof(InspectorArray), cannot_cast); | |
ed1e77d3 | 821 | output = static_cast<InspectorArray*>(this); |
81345200 A |
822 | return true; |
823 | } | |
824 | ||
ed1e77d3 | 825 | void InspectorArrayBase::writeJSON(StringBuilder& output) const |
81345200 | 826 | { |
ed1e77d3 | 827 | output.append('['); |
81345200 A |
828 | for (Vector<RefPtr<InspectorValue>>::const_iterator it = m_data.begin(); it != m_data.end(); ++it) { |
829 | if (it != m_data.begin()) | |
ed1e77d3 | 830 | output.append(','); |
81345200 A |
831 | (*it)->writeJSON(output); |
832 | } | |
ed1e77d3 | 833 | output.append(']'); |
81345200 A |
834 | } |
835 | ||
836 | InspectorArrayBase::InspectorArrayBase() | |
ed1e77d3 | 837 | : InspectorValue(Type::Array) |
81345200 A |
838 | , m_data() |
839 | { | |
840 | } | |
841 | ||
ed1e77d3 | 842 | RefPtr<InspectorValue> InspectorArrayBase::get(size_t index) const |
81345200 A |
843 | { |
844 | ASSERT_WITH_SECURITY_IMPLICATION(index < m_data.size()); | |
845 | return m_data[index]; | |
846 | } | |
847 | ||
ed1e77d3 | 848 | Ref<InspectorObject> InspectorObject::create() |
81345200 | 849 | { |
ed1e77d3 | 850 | return adoptRef(*new InspectorObject); |
81345200 A |
851 | } |
852 | ||
ed1e77d3 | 853 | Ref<InspectorArray> InspectorArray::create() |
81345200 | 854 | { |
ed1e77d3 | 855 | return adoptRef(*new InspectorArray); |
81345200 A |
856 | } |
857 | ||
ed1e77d3 | 858 | Ref<InspectorValue> InspectorValue::null() |
81345200 | 859 | { |
ed1e77d3 | 860 | return adoptRef(*new InspectorValue); |
81345200 A |
861 | } |
862 | ||
ed1e77d3 | 863 | Ref<InspectorString> InspectorString::create(const String& value) |
81345200 | 864 | { |
ed1e77d3 | 865 | return adoptRef(*new InspectorString(value)); |
81345200 A |
866 | } |
867 | ||
ed1e77d3 | 868 | Ref<InspectorString> InspectorString::create(const char* value) |
81345200 | 869 | { |
ed1e77d3 | 870 | return adoptRef(*new InspectorString(value)); |
81345200 A |
871 | } |
872 | ||
ed1e77d3 | 873 | Ref<InspectorBasicValue> InspectorBasicValue::create(bool value) |
81345200 | 874 | { |
ed1e77d3 | 875 | return adoptRef(*new InspectorBasicValue(value)); |
81345200 A |
876 | } |
877 | ||
ed1e77d3 | 878 | Ref<InspectorBasicValue> InspectorBasicValue::create(int value) |
81345200 | 879 | { |
ed1e77d3 | 880 | return adoptRef(*new InspectorBasicValue(value)); |
81345200 A |
881 | } |
882 | ||
ed1e77d3 | 883 | Ref<InspectorBasicValue> InspectorBasicValue::create(double value) |
81345200 | 884 | { |
ed1e77d3 | 885 | return adoptRef(*new InspectorBasicValue(value)); |
81345200 A |
886 | } |
887 | ||
888 | } // namespace Inspector |