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