summaryrefslogtreecommitdiffstats
path: root/Source/WebCore/inspector/InspectorValues.cpp
diff options
context:
space:
mode:
authorSteve Block <steveblock@google.com>2011-05-06 11:45:16 +0100
committerSteve Block <steveblock@google.com>2011-05-12 13:44:10 +0100
commitcad810f21b803229eb11403f9209855525a25d57 (patch)
tree29a6fd0279be608e0fe9ffe9841f722f0f4e4269 /Source/WebCore/inspector/InspectorValues.cpp
parent121b0cf4517156d0ac5111caf9830c51b69bae8f (diff)
downloadexternal_webkit-cad810f21b803229eb11403f9209855525a25d57.zip
external_webkit-cad810f21b803229eb11403f9209855525a25d57.tar.gz
external_webkit-cad810f21b803229eb11403f9209855525a25d57.tar.bz2
Merge WebKit at r75315: Initial merge by git.
Change-Id: I570314b346ce101c935ed22a626b48c2af266b84
Diffstat (limited to 'Source/WebCore/inspector/InspectorValues.cpp')
-rw-r--r--Source/WebCore/inspector/InspectorValues.cpp752
1 files changed, 752 insertions, 0 deletions
diff --git a/Source/WebCore/inspector/InspectorValues.cpp b/Source/WebCore/inspector/InspectorValues.cpp
new file mode 100644
index 0000000..89e9b7c
--- /dev/null
+++ b/Source/WebCore/inspector/InspectorValues.cpp
@@ -0,0 +1,752 @@
+/*
+ * 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 <wtf/DecimalNumber.h>
+
+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<UChar>* 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<UChar> buffer;
+ buffer.reserveCapacity(end - start);
+ if (!decodeString(start, end, &buffer))
+ return false;
+ *output = String(buffer.data(), buffer.size());
+ return true;
+}
+
+PassRefPtr<InspectorValue> buildValue(const UChar* start, const UChar* end, const UChar** valueTokenEnd, int depth)
+{
+ if (depth > stackLimit)
+ return 0;
+
+ RefPtr<InspectorValue> 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<InspectorArray> array = InspectorArray::create();
+ start = tokenEnd;
+ token = parseToken(start, end, &tokenEnd);
+ while (token != ARRAY_END) {
+ RefPtr<InspectorValue> 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<InspectorObject> 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<InspectorValue> 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<UChar>* 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<UChar>* 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<unsigned int>(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(unsigned long*) const
+{
+ return false;
+}
+
+bool InspectorValue::asNumber(unsigned int*) const
+{
+ return false;
+}
+
+bool InspectorValue::asString(String*) const
+{
+ return false;
+}
+
+bool InspectorValue::asValue(RefPtr<InspectorValue>* output)
+{
+ *output = this;
+ return true;
+}
+
+bool InspectorValue::asObject(RefPtr<InspectorObject>*)
+{
+ return false;
+}
+
+bool InspectorValue::asArray(RefPtr<InspectorArray>*)
+{
+ return false;
+}
+
+PassRefPtr<InspectorObject> InspectorValue::asObject()
+{
+ return 0;
+}
+
+PassRefPtr<InspectorArray> InspectorValue::asArray()
+{
+ return 0;
+}
+
+PassRefPtr<InspectorValue> InspectorValue::parseJSON(const String& json)
+{
+ const UChar* start = json.characters();
+ const UChar* end = json.characters() + json.length();
+ const UChar *tokenEnd;
+ RefPtr<InspectorValue> value = buildValue(start, end, &tokenEnd, 0);
+ if (!value || tokenEnd != end)
+ return 0;
+ return value.release();
+}
+
+String InspectorValue::toJSONString() const
+{
+ Vector<UChar> result;
+ result.reserveCapacity(512);
+ writeJSON(&result);
+ return String(result.data(), result.size());
+}
+
+void InspectorValue::writeJSON(Vector<UChar>* 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<long>(m_doubleValue);
+ return true;
+}
+
+bool InspectorBasicValue::asNumber(unsigned long* output) const
+{
+ if (type() != TypeNumber)
+ return false;
+ *output = static_cast<unsigned long>(m_doubleValue);
+ return true;
+}
+
+bool InspectorBasicValue::asNumber(unsigned int* output) const
+{
+ if (type() != TypeNumber)
+ return false;
+ *output = static_cast<unsigned int>(m_doubleValue);
+ return true;
+}
+
+void InspectorBasicValue::writeJSON(Vector<UChar>* 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<UChar>* output) const
+{
+ ASSERT(type() == TypeString);
+ doubleQuoteString(m_stringValue, output);
+}
+
+InspectorObject::~InspectorObject()
+{
+}
+
+bool InspectorObject::asObject(RefPtr<InspectorObject>* output)
+{
+ *output = this;
+ return true;
+}
+
+PassRefPtr<InspectorObject> InspectorObject::asObject()
+{
+ return this;
+}
+
+bool InspectorObject::getBoolean(const String& name, bool* output) const
+{
+ RefPtr<InspectorValue> value = get(name);
+ if (!value)
+ return false;
+ return value->asBoolean(output);
+}
+
+bool InspectorObject::getNumber(const String& name, double* output) const
+{
+ RefPtr<InspectorValue> value = get(name);
+ if (!value)
+ return false;
+ return value->asNumber(output);
+}
+
+bool InspectorObject::getString(const String& name, String* output) const
+{
+ RefPtr<InspectorValue> value = get(name);
+ if (!value)
+ return false;
+ return value->asString(output);
+}
+
+PassRefPtr<InspectorObject> InspectorObject::getObject(const String& name) const
+{
+ PassRefPtr<InspectorValue> value = get(name);
+ if (!value)
+ return 0;
+ return value->asObject();
+}
+
+PassRefPtr<InspectorArray> InspectorObject::getArray(const String& name) const
+{
+ PassRefPtr<InspectorValue> value = get(name);
+ if (!value)
+ return 0;
+ return value->asArray();
+}
+
+PassRefPtr<InspectorValue> 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::writeJSON(Vector<UChar>* 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<InspectorArray>* output)
+{
+ *output = this;
+ return true;
+}
+
+PassRefPtr<InspectorArray> InspectorArray::asArray()
+{
+ return this;
+}
+
+void InspectorArray::writeJSON(Vector<UChar>* output) const
+{
+ output->append('[');
+ for (Vector<RefPtr<InspectorValue> >::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<InspectorValue> InspectorArray::get(size_t index)
+{
+ ASSERT(index < m_data.size());
+ return m_data[index];
+}
+
+} // namespace WebCore
+
+#endif // ENABLE(INSPECTOR)