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 "core/platform/JSONValues.h"
33
34#include "wtf/DecimalNumber.h"
35#include "wtf/dtoa.h"
36#include "wtf/text/StringBuilder.h"
37
38namespace WebCore {
39
40namespace {
41
42const char* const nullString = "null";
43const char* const trueString = "true";
44const char* const falseString = "false";
45
46inline bool escapeChar(UChar c, StringBuilder* dst)
47{
48    switch (c) {
49    case '\b': dst->append("\\b", 2); break;
50    case '\f': dst->append("\\f", 2); break;
51    case '\n': dst->append("\\n", 2); break;
52    case '\r': dst->append("\\r", 2); break;
53    case '\t': dst->append("\\t", 2); break;
54    case '\\': dst->append("\\\\", 2); break;
55    case '"': dst->append("\\\"", 2); break;
56    default:
57        return false;
58    }
59    return true;
60}
61
62inline void doubleQuoteString(const String& str, StringBuilder* dst)
63{
64    dst->append('"');
65    for (unsigned i = 0; i < str.length(); ++i) {
66        UChar c = str[i];
67        if (!escapeChar(c, dst)) {
68            if (c < 32 || c > 126 || c == '<' || c == '>') {
69                // 1. Escaping <, > to prevent script execution.
70                // 2. Technically, we could also pass through c > 126 as UTF8, but this
71                //    is also optional. It would also be a pain to implement here.
72                unsigned symbol = static_cast<unsigned>(c);
73                String symbolCode = String::format("\\u%04X", symbol);
74                dst->append(symbolCode);
75            } else {
76                dst->append(c);
77            }
78        }
79    }
80    dst->append('"');
81}
82
83} // anonymous namespace
84
85bool JSONValue::asBoolean(bool*) const
86{
87    return false;
88}
89
90bool JSONValue::asNumber(double*) const
91{
92    return false;
93}
94
95bool JSONValue::asNumber(long*) const
96{
97    return false;
98}
99
100bool JSONValue::asNumber(int*) const
101{
102    return false;
103}
104
105bool JSONValue::asNumber(unsigned long*) const
106{
107    return false;
108}
109
110bool JSONValue::asNumber(unsigned*) const
111{
112    return false;
113}
114
115bool JSONValue::asString(String*) const
116{
117    return false;
118}
119
120bool JSONValue::asValue(RefPtr<JSONValue>* output)
121{
122    *output = this;
123    return true;
124}
125
126bool JSONValue::asObject(RefPtr<JSONObject>*)
127{
128    return false;
129}
130
131bool JSONValue::asArray(RefPtr<JSONArray>*)
132{
133    return false;
134}
135
136PassRefPtr<JSONObject> JSONValue::asObject()
137{
138    return 0;
139}
140
141PassRefPtr<JSONArray> JSONValue::asArray()
142{
143    return 0;
144}
145
146String JSONValue::toJSONString() const
147{
148    StringBuilder result;
149    result.reserveCapacity(512);
150    writeJSON(&result);
151    return result.toString();
152}
153
154void JSONValue::writeJSON(StringBuilder* output) const
155{
156    ASSERT(m_type == TypeNull);
157    output->append(nullString, 4);
158}
159
160bool JSONBasicValue::asBoolean(bool* output) const
161{
162    if (type() != TypeBoolean)
163        return false;
164    *output = m_boolValue;
165    return true;
166}
167
168bool JSONBasicValue::asNumber(double* output) const
169{
170    if (type() != TypeNumber)
171        return false;
172    *output = m_doubleValue;
173    return true;
174}
175
176bool JSONBasicValue::asNumber(long* output) const
177{
178    if (type() != TypeNumber)
179        return false;
180    *output = static_cast<long>(m_doubleValue);
181    return true;
182}
183
184bool JSONBasicValue::asNumber(int* output) const
185{
186    if (type() != TypeNumber)
187        return false;
188    *output = static_cast<int>(m_doubleValue);
189    return true;
190}
191
192bool JSONBasicValue::asNumber(unsigned long* output) const
193{
194    if (type() != TypeNumber)
195        return false;
196    *output = static_cast<unsigned long>(m_doubleValue);
197    return true;
198}
199
200bool JSONBasicValue::asNumber(unsigned* output) const
201{
202    if (type() != TypeNumber)
203        return false;
204    *output = static_cast<unsigned>(m_doubleValue);
205    return true;
206}
207
208void JSONBasicValue::writeJSON(StringBuilder* output) const
209{
210    ASSERT(type() == TypeBoolean || type() == TypeNumber);
211    if (type() == TypeBoolean) {
212        if (m_boolValue)
213            output->append(trueString, 4);
214        else
215            output->append(falseString, 5);
216    } else if (type() == TypeNumber) {
217        NumberToLStringBuffer buffer;
218        if (!std::isfinite(m_doubleValue)) {
219            output->append(nullString, 4);
220            return;
221        }
222        DecimalNumber decimal = m_doubleValue;
223        unsigned length = 0;
224        if (decimal.bufferLengthForStringDecimal() > WTF::NumberToStringBufferLength) {
225            // Not enough room for decimal. Use exponential format.
226            if (decimal.bufferLengthForStringExponential() > WTF::NumberToStringBufferLength) {
227                // Fallback for an abnormal case if it's too little even for exponential.
228                output->append("NaN", 3);
229                return;
230            }
231            length = decimal.toStringExponential(buffer, WTF::NumberToStringBufferLength);
232        } else {
233            length = decimal.toStringDecimal(buffer, WTF::NumberToStringBufferLength);
234        }
235        output->append(buffer, length);
236    }
237}
238
239bool JSONString::asString(String* output) const
240{
241    *output = m_stringValue;
242    return true;
243}
244
245void JSONString::writeJSON(StringBuilder* output) const
246{
247    ASSERT(type() == TypeString);
248    doubleQuoteString(m_stringValue, output);
249}
250
251JSONObjectBase::~JSONObjectBase()
252{
253}
254
255bool JSONObjectBase::asObject(RefPtr<JSONObject>* output)
256{
257    COMPILE_ASSERT(sizeof(JSONObject) == sizeof(JSONObjectBase), cannot_cast);
258    *output = static_cast<JSONObject*>(this);
259    return true;
260}
261
262PassRefPtr<JSONObject> JSONObjectBase::asObject()
263{
264    return openAccessors();
265}
266
267JSONObject* JSONObjectBase::openAccessors()
268{
269    COMPILE_ASSERT(sizeof(JSONObject) == sizeof(JSONObjectBase), cannot_cast);
270    return static_cast<JSONObject*>(this);
271}
272
273bool JSONObjectBase::getBoolean(const String& name, bool* output) const
274{
275    RefPtr<JSONValue> value = get(name);
276    if (!value)
277        return false;
278    return value->asBoolean(output);
279}
280
281bool JSONObjectBase::getString(const String& name, String* output) const
282{
283    RefPtr<JSONValue> value = get(name);
284    if (!value)
285        return false;
286    return value->asString(output);
287}
288
289PassRefPtr<JSONObject> JSONObjectBase::getObject(const String& name) const
290{
291    RefPtr<JSONValue> value = get(name);
292    if (!value)
293        return 0;
294    return value->asObject();
295}
296
297PassRefPtr<JSONArray> JSONObjectBase::getArray(const String& name) const
298{
299    RefPtr<JSONValue> value = get(name);
300    if (!value)
301        return 0;
302    return value->asArray();
303}
304
305PassRefPtr<JSONValue> JSONObjectBase::get(const String& name) const
306{
307    Dictionary::const_iterator it = m_data.find(name);
308    if (it == m_data.end())
309        return 0;
310    return it->value;
311}
312
313void JSONObjectBase::remove(const String& name)
314{
315    m_data.remove(name);
316    for (size_t i = 0; i < m_order.size(); ++i) {
317        if (m_order[i] == name) {
318            m_order.remove(i);
319            break;
320        }
321    }
322}
323
324void JSONObjectBase::writeJSON(StringBuilder* output) const
325{
326    output->append('{');
327    for (size_t i = 0; i < m_order.size(); ++i) {
328        Dictionary::const_iterator it = m_data.find(m_order[i]);
329        ASSERT(it != m_data.end());
330        if (i)
331            output->append(',');
332        doubleQuoteString(it->key, output);
333        output->append(':');
334        it->value->writeJSON(output);
335    }
336    output->append('}');
337}
338
339JSONObjectBase::JSONObjectBase()
340    : JSONValue(TypeObject)
341    , m_data()
342    , m_order()
343{
344}
345
346JSONArrayBase::~JSONArrayBase()
347{
348}
349
350bool JSONArrayBase::asArray(RefPtr<JSONArray>* output)
351{
352    COMPILE_ASSERT(sizeof(JSONArrayBase) == sizeof(JSONArray), cannot_cast);
353    *output = static_cast<JSONArray*>(this);
354    return true;
355}
356
357PassRefPtr<JSONArray> JSONArrayBase::asArray()
358{
359    COMPILE_ASSERT(sizeof(JSONArrayBase) == sizeof(JSONArray), cannot_cast);
360    return static_cast<JSONArray*>(this);
361}
362
363void JSONArrayBase::writeJSON(StringBuilder* output) const
364{
365    output->append('[');
366    for (Vector<RefPtr<JSONValue> >::const_iterator it = m_data.begin(); it != m_data.end(); ++it) {
367        if (it != m_data.begin())
368            output->append(',');
369        (*it)->writeJSON(output);
370    }
371    output->append(']');
372}
373
374JSONArrayBase::JSONArrayBase()
375    : JSONValue(TypeArray)
376    , m_data()
377{
378}
379
380PassRefPtr<JSONValue> JSONArrayBase::get(size_t index)
381{
382    ASSERT_WITH_SECURITY_IMPLICATION(index < m_data.size());
383    return m_data[index];
384}
385
386} // namespace WebCore
387