// This file is generated. // Copyright 2016 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "components/ui_devtools/Protocol.h" #include #include #include // Copyright 2016 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. //#include "ErrorSupport.h" namespace ui_devtools { namespace protocol { ErrorSupport::ErrorSupport() { } ErrorSupport::~ErrorSupport() { } void ErrorSupport::setName(const char* name) { setName(String(name)); } void ErrorSupport::setName(const String& name) { DCHECK(m_path.size()); m_path[m_path.size() - 1] = name; } void ErrorSupport::push() { m_path.push_back(String()); } void ErrorSupport::pop() { m_path.pop_back(); } void ErrorSupport::addError(const char* error) { addError(String(error)); } void ErrorSupport::addError(const String& error) { StringBuilder builder; for (size_t i = 0; i < m_path.size(); ++i) { if (i) StringUtil::builderAppend(builder, '.'); StringUtil::builderAppend(builder, m_path[i]); } StringUtil::builderAppend(builder, ": "); StringUtil::builderAppend(builder, error); m_errors.push_back(StringUtil::builderToString(builder)); } bool ErrorSupport::hasErrors() { return !!m_errors.size(); } String ErrorSupport::errors() { StringBuilder builder; for (size_t i = 0; i < m_errors.size(); ++i) { if (i) StringUtil::builderAppend(builder, "; "); StringUtil::builderAppend(builder, m_errors[i]); } return StringUtil::builderToString(builder); } } // namespace ui_devtools } // namespace protocol // Copyright 2016 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. //#include "Values.h" namespace ui_devtools { namespace protocol { namespace { const char* const nullValueString = "null"; const char* const trueValueString = "true"; const char* const falseValueString = "false"; inline bool escapeChar(uint16_t c, StringBuilder* dst) { switch (c) { case '\b': StringUtil::builderAppend(*dst, "\\b"); break; case '\f': StringUtil::builderAppend(*dst, "\\f"); break; case '\n': StringUtil::builderAppend(*dst, "\\n"); break; case '\r': StringUtil::builderAppend(*dst, "\\r"); break; case '\t': StringUtil::builderAppend(*dst, "\\t"); break; case '\\': StringUtil::builderAppend(*dst, "\\\\"); break; case '"': StringUtil::builderAppend(*dst, "\\\""); break; default: return false; } return true; } const char hexDigits[17] = "0123456789ABCDEF"; void appendUnsignedAsHex(uint16_t number, StringBuilder* dst) { StringUtil::builderAppend(*dst, "\\u"); for (size_t i = 0; i < 4; ++i) { uint16_t c = hexDigits[(number & 0xF000) >> 12]; StringUtil::builderAppend(*dst, c); number <<= 4; } } template void escapeStringForJSONInternal(const Char* str, unsigned len, StringBuilder* dst) { for (unsigned i = 0; i < len; ++i) { Char c = str[i]; if (escapeChar(c, dst)) continue; if (c < 32 || c > 126) { appendUnsignedAsHex(c, dst); } else { StringUtil::builderAppend(*dst, c); } } } } // anonymous namespace bool Value::asBoolean(bool*) const { return false; } bool Value::asDouble(double*) const { return false; } bool Value::asInteger(int*) const { return false; } bool Value::asString(String*) const { return false; } bool Value::asSerialized(String*) const { return false; } void Value::writeJSON(StringBuilder* output) const { DCHECK(m_type == TypeNull); StringUtil::builderAppend(*output, nullValueString, 4); } std::unique_ptr Value::clone() const { return Value::null(); } String Value::serialize() { StringBuilder result; StringUtil::builderReserve(result, 512); writeJSON(&result); return StringUtil::builderToString(result); } bool FundamentalValue::asBoolean(bool* output) const { if (type() != TypeBoolean) return false; *output = m_boolValue; return true; } bool FundamentalValue::asDouble(double* output) const { if (type() == TypeDouble) { *output = m_doubleValue; return true; } if (type() == TypeInteger) { *output = m_integerValue; return true; } return false; } bool FundamentalValue::asInteger(int* output) const { if (type() != TypeInteger) return false; *output = m_integerValue; return true; } void FundamentalValue::writeJSON(StringBuilder* output) const { DCHECK(type() == TypeBoolean || type() == TypeInteger || type() == TypeDouble); if (type() == TypeBoolean) { if (m_boolValue) StringUtil::builderAppend(*output, trueValueString, 4); else StringUtil::builderAppend(*output, falseValueString, 5); } else if (type() == TypeDouble) { if (!std::isfinite(m_doubleValue)) { StringUtil::builderAppend(*output, nullValueString, 4); return; } StringUtil::builderAppend(*output, StringUtil::fromDouble(m_doubleValue)); } else if (type() == TypeInteger) { StringUtil::builderAppend(*output, StringUtil::fromInteger(m_integerValue)); } } std::unique_ptr FundamentalValue::clone() const { switch (type()) { case TypeDouble: return FundamentalValue::create(m_doubleValue); case TypeInteger: return FundamentalValue::create(m_integerValue); case TypeBoolean: return FundamentalValue::create(m_boolValue); default: DCHECK(false); } return nullptr; } bool StringValue::asString(String* output) const { *output = m_stringValue; return true; } void StringValue::writeJSON(StringBuilder* output) const { DCHECK(type() == TypeString); StringUtil::builderAppendQuotedString(*output, m_stringValue); } std::unique_ptr StringValue::clone() const { return StringValue::create(m_stringValue); } bool SerializedValue::asSerialized(String* output) const { *output = m_serializedValue; return true; } void SerializedValue::writeJSON(StringBuilder* output) const { DCHECK(type() == TypeSerialized); StringUtil::builderAppend(*output, m_serializedValue); } std::unique_ptr SerializedValue::clone() const { return SerializedValue::create(m_serializedValue); } DictionaryValue::~DictionaryValue() { } void DictionaryValue::setBoolean(const String& name, bool value) { setValue(name, FundamentalValue::create(value)); } void DictionaryValue::setInteger(const String& name, int value) { setValue(name, FundamentalValue::create(value)); } void DictionaryValue::setDouble(const String& name, double value) { setValue(name, FundamentalValue::create(value)); } void DictionaryValue::setString(const String& name, const String& value) { setValue(name, StringValue::create(value)); } void DictionaryValue::setValue(const String& name, std::unique_ptr value) { set(name, value); } void DictionaryValue::setObject(const String& name, std::unique_ptr value) { set(name, value); } void DictionaryValue::setArray(const String& name, std::unique_ptr value) { set(name, value); } bool DictionaryValue::getBoolean(const String& name, bool* output) const { protocol::Value* value = get(name); if (!value) return false; return value->asBoolean(output); } bool DictionaryValue::getInteger(const String& name, int* output) const { Value* value = get(name); if (!value) return false; return value->asInteger(output); } bool DictionaryValue::getDouble(const String& name, double* output) const { Value* value = get(name); if (!value) return false; return value->asDouble(output); } bool DictionaryValue::getString(const String& name, String* output) const { protocol::Value* value = get(name); if (!value) return false; return value->asString(output); } DictionaryValue* DictionaryValue::getObject(const String& name) const { return DictionaryValue::cast(get(name)); } protocol::ListValue* DictionaryValue::getArray(const String& name) const { return ListValue::cast(get(name)); } protocol::Value* DictionaryValue::get(const String& name) const { Dictionary::const_iterator it = m_data.find(name); if (it == m_data.end()) return nullptr; return it->second.get(); } DictionaryValue::Entry DictionaryValue::at(size_t index) const { const String key = m_order[index]; return std::make_pair(key, m_data.find(key)->second.get()); } bool DictionaryValue::booleanProperty(const String& name, bool defaultValue) const { bool result = defaultValue; getBoolean(name, &result); return result; } int DictionaryValue::integerProperty(const String& name, int defaultValue) const { int result = defaultValue; getInteger(name, &result); return result; } double DictionaryValue::doubleProperty(const String& name, double defaultValue) const { double result = defaultValue; getDouble(name, &result); return result; } void DictionaryValue::remove(const String& name) { m_data.erase(name); m_order.erase(std::remove(m_order.begin(), m_order.end(), name), m_order.end()); } void DictionaryValue::writeJSON(StringBuilder* output) const { StringUtil::builderAppend(*output, '{'); for (size_t i = 0; i < m_order.size(); ++i) { Dictionary::const_iterator it = m_data.find(m_order[i]); CHECK(it != m_data.end()); if (i) StringUtil::builderAppend(*output, ','); StringUtil::builderAppendQuotedString(*output, it->first); StringUtil::builderAppend(*output, ':'); it->second->writeJSON(output); } StringUtil::builderAppend(*output, '}'); } std::unique_ptr DictionaryValue::clone() const { std::unique_ptr result = DictionaryValue::create(); for (size_t i = 0; i < m_order.size(); ++i) { String key = m_order[i]; Dictionary::const_iterator value = m_data.find(key); DCHECK(value != m_data.cend() && value->second); result->setValue(key, value->second->clone()); } return std::move(result); } DictionaryValue::DictionaryValue() : Value(TypeObject) { } ListValue::~ListValue() { } void ListValue::writeJSON(StringBuilder* output) const { StringUtil::builderAppend(*output, '['); bool first = true; for (const std::unique_ptr& value : m_data) { if (!first) StringUtil::builderAppend(*output, ','); value->writeJSON(output); first = false; } StringUtil::builderAppend(*output, ']'); } std::unique_ptr ListValue::clone() const { std::unique_ptr result = ListValue::create(); for (const std::unique_ptr& value : m_data) result->pushValue(value->clone()); return std::move(result); } ListValue::ListValue() : Value(TypeArray) { } void ListValue::pushValue(std::unique_ptr value) { DCHECK(value); m_data.push_back(std::move(value)); } protocol::Value* ListValue::at(size_t index) { DCHECK_LT(index, m_data.size()); return m_data[index].get(); } void escapeLatinStringForJSON(const uint8_t* str, unsigned len, StringBuilder* dst) { escapeStringForJSONInternal(str, len, dst); } void escapeWideStringForJSON(const uint16_t* str, unsigned len, StringBuilder* dst) { escapeStringForJSONInternal(str, len, dst); } } // namespace ui_devtools } // namespace protocol // Copyright 2016 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. //#include "Object.h" namespace ui_devtools { namespace protocol { std::unique_ptr Object::fromValue(protocol::Value* value, ErrorSupport* errors) { protocol::DictionaryValue* dictionary = DictionaryValue::cast(value); if (!dictionary) { errors->addError("object expected"); return nullptr; } dictionary = static_cast(dictionary->clone().release()); return std::unique_ptr(new Object(std::unique_ptr(dictionary))); } std::unique_ptr Object::toValue() const { return DictionaryValue::cast(m_object->clone()); } std::unique_ptr Object::clone() const { return std::unique_ptr(new Object(DictionaryValue::cast(m_object->clone()))); } Object::Object(std::unique_ptr object) : m_object(std::move(object)) { } Object::~Object() { } } // namespace ui_devtools } // namespace protocol // Copyright 2016 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. //#include "DispatcherBase.h" //#include "Parser.h" namespace ui_devtools { namespace protocol { // static DispatchResponse DispatchResponse::OK() { DispatchResponse result; result.m_status = kSuccess; result.m_errorCode = kParseError; return result; } // static DispatchResponse DispatchResponse::Error(const String& error) { DispatchResponse result; result.m_status = kError; result.m_errorCode = kServerError; result.m_errorMessage = error; return result; } // static DispatchResponse DispatchResponse::InternalError() { DispatchResponse result; result.m_status = kError; result.m_errorCode = kInternalError; result.m_errorMessage = "Internal error"; return result; } // static DispatchResponse DispatchResponse::InvalidParams(const String& error) { DispatchResponse result; result.m_status = kError; result.m_errorCode = kInvalidParams; result.m_errorMessage = error; return result; } // static DispatchResponse DispatchResponse::FallThrough() { DispatchResponse result; result.m_status = kFallThrough; result.m_errorCode = kParseError; return result; } // static const char DispatcherBase::kInvalidParamsString[] = "Invalid parameters"; DispatcherBase::WeakPtr::WeakPtr(DispatcherBase* dispatcher) : m_dispatcher(dispatcher) { } DispatcherBase::WeakPtr::~WeakPtr() { if (m_dispatcher) m_dispatcher->m_weakPtrs.erase(this); } DispatcherBase::Callback::Callback(std::unique_ptr backendImpl, int callId, int callbackId) : m_backendImpl(std::move(backendImpl)) , m_callId(callId) , m_callbackId(callbackId) { } DispatcherBase::Callback::~Callback() = default; void DispatcherBase::Callback::dispose() { m_backendImpl = nullptr; } void DispatcherBase::Callback::sendIfActive(std::unique_ptr partialMessage, const DispatchResponse& response) { if (!m_backendImpl || !m_backendImpl->get()) return; m_backendImpl->get()->sendResponse(m_callId, response, std::move(partialMessage)); m_backendImpl = nullptr; } void DispatcherBase::Callback::fallThroughIfActive() { if (!m_backendImpl || !m_backendImpl->get()) return; m_backendImpl->get()->markFallThrough(m_callbackId); m_backendImpl = nullptr; } DispatcherBase::DispatcherBase(FrontendChannel* frontendChannel) : m_frontendChannel(frontendChannel) , m_lastCallbackId(0) , m_lastCallbackFallThrough(false) { } DispatcherBase::~DispatcherBase() { clearFrontend(); } int DispatcherBase::nextCallbackId() { m_lastCallbackFallThrough = false; return ++m_lastCallbackId; } void DispatcherBase::markFallThrough(int callbackId) { DCHECK(callbackId == m_lastCallbackId); m_lastCallbackFallThrough = true; } void DispatcherBase::sendResponse(int callId, const DispatchResponse& response, std::unique_ptr result) { if (!m_frontendChannel) return; if (response.status() == DispatchResponse::kError) { reportProtocolError(callId, response.errorCode(), response.errorMessage(), nullptr); return; } m_frontendChannel->sendProtocolResponse(callId, InternalResponse::createResponse(callId, std::move(result))); } void DispatcherBase::sendResponse(int callId, const DispatchResponse& response) { sendResponse(callId, response, DictionaryValue::create()); } namespace { class ProtocolError : public Serializable { public: static std::unique_ptr createErrorResponse(int callId, DispatchResponse::ErrorCode code, const String& errorMessage, ErrorSupport* errors) { std::unique_ptr protocolError(new ProtocolError(code, errorMessage)); protocolError->m_callId = callId; protocolError->m_hasCallId = true; if (errors && errors->hasErrors()) protocolError->m_data = errors->errors(); return protocolError; } static std::unique_ptr createErrorNotification(DispatchResponse::ErrorCode code, const String& errorMessage) { return std::unique_ptr(new ProtocolError(code, errorMessage)); } String serialize() override { std::unique_ptr error = DictionaryValue::create(); error->setInteger("code", m_code); error->setString("message", m_errorMessage); if (m_data.length()) error->setString("data", m_data); std::unique_ptr message = DictionaryValue::create(); message->setObject("error", std::move(error)); if (m_hasCallId) message->setInteger("id", m_callId); return message->serialize(); } ~ProtocolError() override {} private: ProtocolError(DispatchResponse::ErrorCode code, const String& errorMessage) : m_code(code) , m_errorMessage(errorMessage) { } DispatchResponse::ErrorCode m_code; String m_errorMessage; String m_data; int m_callId = 0; bool m_hasCallId = false; }; } // namespace static void reportProtocolErrorTo(FrontendChannel* frontendChannel, int callId, DispatchResponse::ErrorCode code, const String& errorMessage, ErrorSupport* errors) { if (frontendChannel) frontendChannel->sendProtocolResponse(callId, ProtocolError::createErrorResponse(callId, code, errorMessage, errors)); } static void reportProtocolErrorTo(FrontendChannel* frontendChannel, DispatchResponse::ErrorCode code, const String& errorMessage) { if (frontendChannel) frontendChannel->sendProtocolNotification(ProtocolError::createErrorNotification(code, errorMessage)); } void DispatcherBase::reportProtocolError(int callId, DispatchResponse::ErrorCode code, const String& errorMessage, ErrorSupport* errors) { reportProtocolErrorTo(m_frontendChannel, callId, code, errorMessage, errors); } void DispatcherBase::clearFrontend() { m_frontendChannel = nullptr; for (auto& weak : m_weakPtrs) weak->dispose(); m_weakPtrs.clear(); } std::unique_ptr DispatcherBase::weakPtr() { std::unique_ptr weak(new DispatcherBase::WeakPtr(this)); m_weakPtrs.insert(weak.get()); return weak; } UberDispatcher::UberDispatcher(FrontendChannel* frontendChannel) : m_frontendChannel(frontendChannel) , m_fallThroughForNotFound(false) { } void UberDispatcher::setFallThroughForNotFound(bool fallThroughForNotFound) { m_fallThroughForNotFound = fallThroughForNotFound; } void UberDispatcher::registerBackend(const String& name, std::unique_ptr dispatcher) { m_dispatchers[name] = std::move(dispatcher); } void UberDispatcher::setupRedirects(const HashMap& redirects) { for (const auto& pair : redirects) m_redirects[pair.first] = pair.second; } DispatchResponse::Status UberDispatcher::dispatch(std::unique_ptr parsedMessage, int* outCallId, String* outMethod) { if (!parsedMessage) { reportProtocolErrorTo(m_frontendChannel, DispatchResponse::kParseError, "Message must be a valid JSON"); return DispatchResponse::kError; } std::unique_ptr messageObject = DictionaryValue::cast(std::move(parsedMessage)); if (!messageObject) { reportProtocolErrorTo(m_frontendChannel, DispatchResponse::kInvalidRequest, "Message must be an object"); return DispatchResponse::kError; } int callId = 0; protocol::Value* callIdValue = messageObject->get("id"); bool success = callIdValue && callIdValue->asInteger(&callId); if (outCallId) *outCallId = callId; if (!success) { reportProtocolErrorTo(m_frontendChannel, DispatchResponse::kInvalidRequest, "Message must have integer 'id' property"); return DispatchResponse::kError; } protocol::Value* methodValue = messageObject->get("method"); String method; success = methodValue && methodValue->asString(&method); if (outMethod) *outMethod = method; if (!success) { reportProtocolErrorTo(m_frontendChannel, callId, DispatchResponse::kInvalidRequest, "Message must have string 'method' property", nullptr); return DispatchResponse::kError; } HashMap::iterator redirectIt = m_redirects.find(method); if (redirectIt != m_redirects.end()) method = redirectIt->second; size_t dotIndex = StringUtil::find(method, "."); if (dotIndex == StringUtil::kNotFound) { if (m_fallThroughForNotFound) return DispatchResponse::kFallThrough; reportProtocolErrorTo(m_frontendChannel, callId, DispatchResponse::kMethodNotFound, "'" + method + "' wasn't found", nullptr); return DispatchResponse::kError; } String domain = StringUtil::substring(method, 0, dotIndex); auto it = m_dispatchers.find(domain); if (it == m_dispatchers.end()) { if (m_fallThroughForNotFound) return DispatchResponse::kFallThrough; reportProtocolErrorTo(m_frontendChannel, callId, DispatchResponse::kMethodNotFound, "'" + method + "' wasn't found", nullptr); return DispatchResponse::kError; } return it->second->dispatch(callId, method, std::move(messageObject)); } bool UberDispatcher::getCommandName(const String& message, String* method, std::unique_ptr* parsedMessage) { std::unique_ptr value = StringUtil::parseJSON(message); if (!value) { reportProtocolErrorTo(m_frontendChannel, DispatchResponse::kParseError, "Message must be a valid JSON"); return false; } protocol::DictionaryValue* object = DictionaryValue::cast(value.get()); if (!object) { reportProtocolErrorTo(m_frontendChannel, DispatchResponse::kInvalidRequest, "Message must be an object"); return false; } if (!object->getString("method", method)) { reportProtocolErrorTo(m_frontendChannel, DispatchResponse::kInvalidRequest, "Message must have string 'method' property"); return false; } parsedMessage->reset(DictionaryValue::cast(value.release())); return true; } UberDispatcher::~UberDispatcher() = default; // static std::unique_ptr InternalResponse::createResponse(int callId, std::unique_ptr params) { return std::unique_ptr(new InternalResponse(callId, String(), std::move(params))); } // static std::unique_ptr InternalResponse::createNotification(const String& notification, std::unique_ptr params) { return std::unique_ptr(new InternalResponse(0, notification, std::move(params))); } String InternalResponse::serialize() { std::unique_ptr result = DictionaryValue::create(); std::unique_ptr params(m_params ? std::move(m_params) : DictionaryValue::create()); if (m_notification.length()) { result->setString("method", m_notification); result->setValue("params", SerializedValue::create(params->serialize())); } else { result->setInteger("id", m_callId); result->setValue("result", SerializedValue::create(params->serialize())); } return result->serialize(); } InternalResponse::InternalResponse(int callId, const String& notification, std::unique_ptr params) : m_callId(callId) , m_notification(notification) , m_params(params ? std::move(params) : nullptr) { } } // namespace ui_devtools } // namespace protocol // Copyright 2016 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. namespace ui_devtools { namespace protocol { namespace { const int stackLimit = 1000; enum Token { ObjectBegin, ObjectEnd, ArrayBegin, ArrayEnd, StringLiteral, Number, BoolTrue, BoolFalse, NullToken, ListSeparator, ObjectPairSeparator, InvalidToken, }; const char* const nullString = "null"; const char* const trueString = "true"; const char* const falseString = "false"; bool isASCII(uint16_t c) { return !(c & ~0x7F); } bool isSpaceOrNewLine(uint16_t c) { return isASCII(c) && c <= ' ' && (c == ' ' || (c <= 0xD && c >= 0x9)); } double charactersToDouble(const uint16_t* characters, size_t length, bool* ok) { std::vector buffer; buffer.reserve(length + 1); for (size_t i = 0; i < length; ++i) { if (!isASCII(characters[i])) { *ok = false; return 0; } buffer.push_back(static_cast(characters[i])); } buffer.push_back('\0'); return StringUtil::toDouble(buffer.data(), length, ok); } double charactersToDouble(const uint8_t* characters, size_t length, bool* ok) { std::string buffer(reinterpret_cast(characters), length); return StringUtil::toDouble(buffer.data(), length, ok); } template bool parseConstToken(const Char* start, const Char* end, const Char** tokenEnd, const char* token) { while (start < end && *token != '\0' && *start++ == *token++) { } if (*token != '\0') return false; *tokenEnd = start; return true; } template bool readInt(const Char* start, const Char* end, const Char** 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; } template bool parseNumberToken(const Char* start, const Char* end, const Char** 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; Char 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; } template bool readHexDigits(const Char* start, const Char* end, const Char** tokenEnd, int digits) { if (end - start < digits) return false; for (int i = 0; i < digits; ++i) { Char c = *start++; if (!(('0' <= c && c <= '9') || ('a' <= c && c <= 'f') || ('A' <= c && c <= 'F'))) return false; } *tokenEnd = start; return true; } template bool parseStringToken(const Char* start, const Char* end, const Char** tokenEnd) { while (start < end) { Char c = *start++; if ('\\' == c) { if (start == end) return false; 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; } template bool skipComment(const Char* start, const Char* end, const Char** commentEnd) { if (start == end) return false; if (*start != '/' || start + 1 >= end) return false; ++start; if (*start == '/') { // Single line comment, read to newline. for (++start; start < end; ++start) { if (*start == '\n' || *start == '\r') { *commentEnd = start + 1; return true; } } *commentEnd = end; // Comment reaches end-of-input, which is fine. return true; } if (*start == '*') { Char previous = '\0'; // Block comment, read until end marker. for (++start; start < end; previous = *start++) { if (previous == '*' && *start == '/') { *commentEnd = start + 1; return true; } } // Block comment must close before end-of-input. return false; } return false; } template void skipWhitespaceAndComments(const Char* start, const Char* end, const Char** whitespaceEnd) { while (start < end) { if (isSpaceOrNewLine(*start)) { ++start; } else if (*start == '/') { const Char* commentEnd; if (!skipComment(start, end, &commentEnd)) break; start = commentEnd; } else { break; } } *whitespaceEnd = start; } template Token parseToken(const Char* start, const Char* end, const Char** tokenStart, const Char** tokenEnd) { skipWhitespaceAndComments(start, end, tokenStart); start = *tokenStart; if (start == end) return InvalidToken; switch (*start) { case 'n': if (parseConstToken(start, end, tokenEnd, nullString)) return NullToken; break; case 't': if (parseConstToken(start, end, tokenEnd, trueString)) return BoolTrue; break; case 'f': if (parseConstToken(start, end, tokenEnd, falseString)) return BoolFalse; break; case '[': *tokenEnd = start + 1; return ArrayBegin; case ']': *tokenEnd = start + 1; return ArrayEnd; case ',': *tokenEnd = start + 1; return ListSeparator; case '{': *tokenEnd = start + 1; return ObjectBegin; case '}': *tokenEnd = start + 1; return ObjectEnd; case ':': *tokenEnd = start + 1; return ObjectPairSeparator; 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 StringLiteral; break; } return InvalidToken; } template int hexToInt(Char 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; DCHECK(false); return 0; } template bool decodeString(const Char* start, const Char* end, StringBuilder* output) { while (start < end) { uint16_t c = *start++; if ('\\' != c) { StringUtil::builderAppend(*output, c); continue; } if (start == end) return false; c = *start++; if (c == 'x') { // \x is not supported. return false; } 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 'u': c = (hexToInt(*start) << 12) + (hexToInt(*(start + 1)) << 8) + (hexToInt(*(start + 2)) << 4) + hexToInt(*(start + 3)); start += 4; break; default: return false; } StringUtil::builderAppend(*output, c); } return true; } template bool decodeString(const Char* start, const Char* end, String* output) { if (start == end) { *output = ""; return true; } if (start > end) return false; StringBuilder buffer; StringUtil::builderReserve(buffer, end - start); if (!decodeString(start, end, &buffer)) return false; *output = StringUtil::builderToString(buffer); return true; } template std::unique_ptr buildValue(const Char* start, const Char* end, const Char** valueTokenEnd, int depth) { if (depth > stackLimit) return nullptr; std::unique_ptr result; const Char* tokenStart; const Char* tokenEnd; Token token = parseToken(start, end, &tokenStart, &tokenEnd); switch (token) { case InvalidToken: return nullptr; case NullToken: result = Value::null(); break; case BoolTrue: result = FundamentalValue::create(true); break; case BoolFalse: result = FundamentalValue::create(false); break; case Number: { bool ok; double value = charactersToDouble(tokenStart, tokenEnd - tokenStart, &ok); if (!ok) return nullptr; int number = static_cast(value); if (number == value) result = FundamentalValue::create(number); else result = FundamentalValue::create(value); break; } case StringLiteral: { String value; bool ok = decodeString(tokenStart + 1, tokenEnd - 1, &value); if (!ok) return nullptr; result = StringValue::create(value); break; } case ArrayBegin: { std::unique_ptr array = ListValue::create(); start = tokenEnd; token = parseToken(start, end, &tokenStart, &tokenEnd); while (token != ArrayEnd) { std::unique_ptr arrayNode = buildValue(start, end, &tokenEnd, depth + 1); if (!arrayNode) return nullptr; array->pushValue(std::move(arrayNode)); // After a list value, we expect a comma or the end of the list. start = tokenEnd; token = parseToken(start, end, &tokenStart, &tokenEnd); if (token == ListSeparator) { start = tokenEnd; token = parseToken(start, end, &tokenStart, &tokenEnd); if (token == ArrayEnd) return nullptr; } else if (token != ArrayEnd) { // Unexpected value after list value. Bail out. return nullptr; } } if (token != ArrayEnd) return nullptr; result = std::move(array); break; } case ObjectBegin: { std::unique_ptr object = DictionaryValue::create(); start = tokenEnd; token = parseToken(start, end, &tokenStart, &tokenEnd); while (token != ObjectEnd) { if (token != StringLiteral) return nullptr; String key; if (!decodeString(tokenStart + 1, tokenEnd - 1, &key)) return nullptr; start = tokenEnd; token = parseToken(start, end, &tokenStart, &tokenEnd); if (token != ObjectPairSeparator) return nullptr; start = tokenEnd; std::unique_ptr value = buildValue(start, end, &tokenEnd, depth + 1); if (!value) return nullptr; object->setValue(key, std::move(value)); start = tokenEnd; // After a key/value pair, we expect a comma or the end of the // object. token = parseToken(start, end, &tokenStart, &tokenEnd); if (token == ListSeparator) { start = tokenEnd; token = parseToken(start, end, &tokenStart, &tokenEnd); if (token == ObjectEnd) return nullptr; } else if (token != ObjectEnd) { // Unexpected value after last object value. Bail out. return nullptr; } } if (token != ObjectEnd) return nullptr; result = std::move(object); break; } default: // We got a token that's not a value. return nullptr; } skipWhitespaceAndComments(tokenEnd, end, valueTokenEnd); return result; } template std::unique_ptr parseJSONInternal(const Char* start, unsigned length) { const Char* end = start + length; const Char *tokenEnd; std::unique_ptr value = buildValue(start, end, &tokenEnd, 0); if (!value || tokenEnd != end) return nullptr; return value; } } // anonymous namespace std::unique_ptr parseJSONCharacters(const uint16_t* characters, unsigned length) { return parseJSONInternal(characters, length); } std::unique_ptr parseJSONCharacters(const uint8_t* characters, unsigned length) { return parseJSONInternal(characters, length); } } // namespace ui_devtools } // namespace protocol