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