/* * Copyright (C) 2010 Google Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: * * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above * copyright notice, this list of conditions and the following disclaimer * in the documentation and/or other materials provided with the * distribution. * * Neither the name of Google Inc. nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "config.h" #include "InspectorValues.h" #if ENABLE(INSPECTOR) #include namespace WebCore { namespace { static const int stackLimit = 1000; enum Token { OBJECT_BEGIN, OBJECT_END, ARRAY_BEGIN, ARRAY_END, STRING, NUMBER, BOOL_TRUE, BOOL_FALSE, NULL_TOKEN, LIST_SEPARATOR, OBJECT_PAIR_SEPARATOR, INVALID_TOKEN, }; const char* const nullString = "null"; const char* const trueString = "true"; const char* const falseString = "false"; bool parseConstToken(const UChar* start, const UChar* end, const UChar** tokenEnd, const char* token) { while (start < end && *token != '\0' && *start++ == *token++) { } if (*token != '\0') return false; *tokenEnd = start; return true; } bool readInt(const UChar* start, const UChar* end, const UChar** tokenEnd, bool canHaveLeadingZeros) { if (start == end) return false; bool haveLeadingZero = '0' == *start; int length = 0; while (start < end && '0' <= *start && *start <= '9') { ++start; ++length; } if (!length) return false; if (!canHaveLeadingZeros && length > 1 && haveLeadingZero) return false; *tokenEnd = start; return true; } bool parseNumberToken(const UChar* start, const UChar* end, const UChar** tokenEnd) { // We just grab the number here. We validate the size in DecodeNumber. // According to RFC4627, a valid number is: [minus] int [frac] [exp] if (start == end) return false; UChar c = *start; if ('-' == c) ++start; if (!readInt(start, end, &start, false)) return false; if (start == end) { *tokenEnd = start; return true; } // Optional fraction part c = *start; if ('.' == c) { ++start; if (!readInt(start, end, &start, true)) return false; if (start == end) { *tokenEnd = start; return true; } c = *start; } // Optional exponent part if ('e' == c || 'E' == c) { ++start; if (start == end) return false; c = *start; if ('-' == c || '+' == c) { ++start; if (start == end) return false; } if (!readInt(start, end, &start, true)) return false; } *tokenEnd = start; return true; } bool readHexDigits(const UChar* start, const UChar* end, const UChar** tokenEnd, int digits) { if (end - start < digits) return false; for (int i = 0; i < digits; ++i) { UChar c = *start++; if (!(('0' <= c && c <= '9') || ('a' <= c && c <= 'f') || ('A' <= c && c <= 'F'))) return false; } *tokenEnd = start; return true; } bool parseStringToken(const UChar* start, const UChar* end, const UChar** tokenEnd) { while (start < end) { UChar c = *start++; if ('\\' == c) { c = *start++; // Make sure the escaped char is valid. switch (c) { case 'x': if (!readHexDigits(start, end, &start, 2)) return false; break; case 'u': if (!readHexDigits(start, end, &start, 4)) return false; break; case '\\': case '/': case 'b': case 'f': case 'n': case 'r': case 't': case 'v': case '"': break; default: return false; } } else if ('"' == c) { *tokenEnd = start; return true; } } return false; } Token parseToken(const UChar* start, const UChar* end, const UChar** tokenEnd) { if (start == end) return INVALID_TOKEN; switch (*start) { case 'n': if (parseConstToken(start, end, tokenEnd, nullString)) return NULL_TOKEN; break; case 't': if (parseConstToken(start, end, tokenEnd, trueString)) return BOOL_TRUE; break; case 'f': if (parseConstToken(start, end, tokenEnd, falseString)) return BOOL_FALSE; break; case '[': *tokenEnd = start + 1; return ARRAY_BEGIN; case ']': *tokenEnd = start + 1; return ARRAY_END; case ',': *tokenEnd = start + 1; return LIST_SEPARATOR; case '{': *tokenEnd = start + 1; return OBJECT_BEGIN; case '}': *tokenEnd = start + 1; return OBJECT_END; case ':': *tokenEnd = start + 1; return OBJECT_PAIR_SEPARATOR; case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': case '-': if (parseNumberToken(start, end, tokenEnd)) return NUMBER; break; case '"': if (parseStringToken(start + 1, end, tokenEnd)) return STRING; break; } return INVALID_TOKEN; } inline int hexToInt(UChar c) { if ('0' <= c && c <= '9') return c - '0'; if ('A' <= c && c <= 'F') return c - 'A' + 10; if ('a' <= c && c <= 'f') return c - 'a' + 10; ASSERT_NOT_REACHED(); return 0; } bool decodeString(const UChar* start, const UChar* end, Vector* output) { while (start < end) { UChar c = *start++; if ('\\' != c) { output->append(c); continue; } c = *start++; switch (c) { case '"': case '/': case '\\': break; case 'b': c = '\b'; break; case 'f': c = '\f'; break; case 'n': c = '\n'; break; case 'r': c = '\r'; break; case 't': c = '\t'; break; case 'v': c = '\v'; break; case 'x': c = (hexToInt(*start) << 4) + hexToInt(*(start + 1)); start += 2; break; case 'u': c = (hexToInt(*start) << 12) + (hexToInt(*(start + 1)) << 8) + (hexToInt(*(start + 2)) << 4) + hexToInt(*(start + 3)); start += 4; break; default: return false; } output->append(c); } return true; } bool decodeString(const UChar* start, const UChar* end, String* output) { if (start == end) { *output = ""; return true; } if (start > end) return false; Vector buffer; buffer.reserveCapacity(end - start); if (!decodeString(start, end, &buffer)) return false; *output = String(buffer.data(), buffer.size()); return true; } PassRefPtr buildValue(const UChar* start, const UChar* end, const UChar** valueTokenEnd, int depth) { if (depth > stackLimit) return 0; RefPtr result; const UChar* tokenEnd; Token token = parseToken(start, end, &tokenEnd); switch (token) { case INVALID_TOKEN: return 0; case NULL_TOKEN: result = InspectorValue::null(); break; case BOOL_TRUE: result = InspectorBasicValue::create(true); break; case BOOL_FALSE: result = InspectorBasicValue::create(false); break; case NUMBER: { bool ok; double value = charactersToDouble(start, tokenEnd - start, &ok); if (!ok) return 0; result = InspectorBasicValue::create(value); break; } case STRING: { String value; bool ok = decodeString(start + 1, tokenEnd - 1, &value); if (!ok) return 0; result = InspectorString::create(value); break; } case ARRAY_BEGIN: { RefPtr array = InspectorArray::create(); start = tokenEnd; token = parseToken(start, end, &tokenEnd); while (token != ARRAY_END) { RefPtr arrayNode = buildValue(start, end, &tokenEnd, depth + 1); if (!arrayNode) return 0; array->pushValue(arrayNode); // After a list value, we expect a comma or the end of the list. start = tokenEnd; token = parseToken(start, end, &tokenEnd); if (token == LIST_SEPARATOR) { start = tokenEnd; token = parseToken(start, end, &tokenEnd); if (token == ARRAY_END) return 0; } else if (token != ARRAY_END) { // Unexpected value after list value. Bail out. return 0; } } if (token != ARRAY_END) return 0; result = array.release(); break; } case OBJECT_BEGIN: { RefPtr object = InspectorObject::create(); start = tokenEnd; token = parseToken(start, end, &tokenEnd); while (token != OBJECT_END) { if (token != STRING) return 0; String key; if (!decodeString(start + 1, tokenEnd - 1, &key)) return 0; start = tokenEnd; token = parseToken(start, end, &tokenEnd); if (token != OBJECT_PAIR_SEPARATOR) return 0; start = tokenEnd; RefPtr value = buildValue(start, end, &tokenEnd, depth + 1); if (!value) return 0; object->setValue(key, value); start = tokenEnd; // After a key/value pair, we expect a comma or the end of the // object. token = parseToken(start, end, &tokenEnd); if (token == LIST_SEPARATOR) { start = tokenEnd; token = parseToken(start, end, &tokenEnd); if (token == OBJECT_END) return 0; } else if (token != OBJECT_END) { // Unexpected value after last object value. Bail out. return 0; } } if (token != OBJECT_END) return 0; result = object.release(); break; } default: // We got a token that's not a value. return 0; } *valueTokenEnd = tokenEnd; return result.release(); } inline bool escapeChar(UChar c, Vector* dst) { switch (c) { case '\b': dst->append("\\b", 2); break; case '\f': dst->append("\\f", 2); break; case '\n': dst->append("\\n", 2); break; case '\r': dst->append("\\r", 2); break; case '\t': dst->append("\\t", 2); break; case '\\': dst->append("\\\\", 2); break; case '"': dst->append("\\\"", 2); break; default: return false; } return true; } inline void doubleQuoteString(const String& str, Vector* dst) { dst->append('"'); for (unsigned i = 0; i < str.length(); ++i) { UChar c = str[i]; if (!escapeChar(c, dst)) { if (c < 32 || c > 126 || c == '<' || c == '>') { // 1. Escaping <, > to prevent script execution. // 2. Technically, we could also pass through c > 126 as UTF8, but this // is also optional. It would also be a pain to implement here. unsigned int symbol = static_cast(c); String symbolCode = String::format("\\u%04X", symbol); dst->append(symbolCode.characters(), symbolCode.length()); } else dst->append(c); } } dst->append('"'); } } // anonymous namespace bool InspectorValue::asBoolean(bool*) const { return false; } bool InspectorValue::asNumber(double*) const { return false; } bool InspectorValue::asNumber(long*) const { return false; } bool InspectorValue::asNumber(int*) const { return false; } bool InspectorValue::asNumber(unsigned long*) const { return false; } bool InspectorValue::asNumber(unsigned int*) const { return false; } bool InspectorValue::asString(String*) const { return false; } bool InspectorValue::asValue(RefPtr* output) { *output = this; return true; } bool InspectorValue::asObject(RefPtr*) { return false; } bool InspectorValue::asArray(RefPtr*) { return false; } PassRefPtr InspectorValue::asObject() { return 0; } PassRefPtr InspectorValue::asArray() { return 0; } PassRefPtr InspectorValue::parseJSON(const String& json) { const UChar* start = json.characters(); const UChar* end = json.characters() + json.length(); const UChar *tokenEnd; RefPtr value = buildValue(start, end, &tokenEnd, 0); if (!value || tokenEnd != end) return 0; return value.release(); } String InspectorValue::toJSONString() const { Vector result; result.reserveCapacity(512); writeJSON(&result); return String(result.data(), result.size()); } void InspectorValue::writeJSON(Vector* output) const { ASSERT(m_type == TypeNull); output->append(nullString, 4); } bool InspectorBasicValue::asBoolean(bool* output) const { if (type() != TypeBoolean) return false; *output = m_boolValue; return true; } bool InspectorBasicValue::asNumber(double* output) const { if (type() != TypeNumber) return false; *output = m_doubleValue; return true; } bool InspectorBasicValue::asNumber(long* output) const { if (type() != TypeNumber) return false; *output = static_cast(m_doubleValue); return true; } bool InspectorBasicValue::asNumber(int* output) const { if (type() != TypeNumber) return false; *output = static_cast(m_doubleValue); return true; } bool InspectorBasicValue::asNumber(unsigned long* output) const { if (type() != TypeNumber) return false; *output = static_cast(m_doubleValue); return true; } bool InspectorBasicValue::asNumber(unsigned int* output) const { if (type() != TypeNumber) return false; *output = static_cast(m_doubleValue); return true; } void InspectorBasicValue::writeJSON(Vector* output) const { ASSERT(type() == TypeBoolean || type() == TypeNumber); if (type() == TypeBoolean) { if (m_boolValue) output->append(trueString, 4); else output->append(falseString, 5); } else if (type() == TypeNumber) { NumberToStringBuffer buffer; unsigned length = DecimalNumber(m_doubleValue).toStringDecimal(buffer, WTF::NumberToStringBufferLength); output->append(buffer, length); } } bool InspectorString::asString(String* output) const { *output = m_stringValue; return true; } void InspectorString::writeJSON(Vector* output) const { ASSERT(type() == TypeString); doubleQuoteString(m_stringValue, output); } InspectorObject::~InspectorObject() { } bool InspectorObject::asObject(RefPtr* output) { *output = this; return true; } PassRefPtr InspectorObject::asObject() { return this; } bool InspectorObject::getBoolean(const String& name, bool* output) const { RefPtr value = get(name); if (!value) return false; return value->asBoolean(output); } bool InspectorObject::getString(const String& name, String* output) const { RefPtr value = get(name); if (!value) return false; return value->asString(output); } PassRefPtr InspectorObject::getObject(const String& name) const { PassRefPtr value = get(name); if (!value) return 0; return value->asObject(); } PassRefPtr InspectorObject::getArray(const String& name) const { PassRefPtr value = get(name); if (!value) return 0; return value->asArray(); } PassRefPtr InspectorObject::get(const String& name) const { Dictionary::const_iterator it = m_data.find(name); if (it == m_data.end()) return 0; return it->second; } void InspectorObject::remove(const String& name) { m_data.remove(name); for (size_t i = 0; i < m_order.size(); ++i) { if (m_order[i] == name) { m_order.remove(i); break; } } } void InspectorObject::writeJSON(Vector* output) const { output->append('{'); for (size_t i = 0; i < m_order.size(); ++i) { Dictionary::const_iterator it = m_data.find(m_order[i]); ASSERT(it != m_data.end()); if (i) output->append(','); doubleQuoteString(it->first, output); output->append(':'); it->second->writeJSON(output); } output->append('}'); } InspectorObject::InspectorObject() : InspectorValue(TypeObject) , m_data() , m_order() { } InspectorArray::~InspectorArray() { } bool InspectorArray::asArray(RefPtr* output) { *output = this; return true; } PassRefPtr InspectorArray::asArray() { return this; } void InspectorArray::writeJSON(Vector* output) const { output->append('['); for (Vector >::const_iterator it = m_data.begin(); it != m_data.end(); ++it) { if (it != m_data.begin()) output->append(','); (*it)->writeJSON(output); } output->append(']'); } InspectorArray::InspectorArray() : InspectorValue(TypeArray) , m_data() { } PassRefPtr InspectorArray::get(size_t index) { ASSERT(index < m_data.size()); return m_data[index]; } } // namespace WebCore #endif // ENABLE(INSPECTOR)