diff options
author | Lorry Tar Creator <lorry-tar-importer@lorry> | 2017-06-27 06:07:23 +0000 |
---|---|---|
committer | Lorry Tar Creator <lorry-tar-importer@lorry> | 2017-06-27 06:07:23 +0000 |
commit | 1bf1084f2b10c3b47fd1a588d85d21ed0eb41d0c (patch) | |
tree | 46dcd36c86e7fbc6e5df36deb463b33e9967a6f7 /Source/JavaScriptCore/inspector/InspectorBackendDispatcher.cpp | |
parent | 32761a6cee1d0dee366b885b7b9c777e67885688 (diff) | |
download | WebKitGtk-tarball-master.tar.gz |
webkitgtk-2.16.5HEADwebkitgtk-2.16.5master
Diffstat (limited to 'Source/JavaScriptCore/inspector/InspectorBackendDispatcher.cpp')
-rw-r--r-- | Source/JavaScriptCore/inspector/InspectorBackendDispatcher.cpp | 371 |
1 files changed, 228 insertions, 143 deletions
diff --git a/Source/JavaScriptCore/inspector/InspectorBackendDispatcher.cpp b/Source/JavaScriptCore/inspector/InspectorBackendDispatcher.cpp index 30a66f7b3..b1984a835 100644 --- a/Source/JavaScriptCore/inspector/InspectorBackendDispatcher.cpp +++ b/Source/JavaScriptCore/inspector/InspectorBackendDispatcher.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2013 Apple Inc. All Rights Reserved. + * Copyright (C) 2013, 2015 Apple Inc. All Rights Reserved. * Copyright (C) 2011 The Chromium Authors. All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -27,133 +27,179 @@ #include "config.h" #include "InspectorBackendDispatcher.h" -#if ENABLE(INSPECTOR) - -#include "InspectorFrontendChannel.h" +#include "InspectorFrontendRouter.h" #include "InspectorValues.h" +#include <wtf/SetForScope.h> #include <wtf/text/CString.h> #include <wtf/text/WTFString.h> namespace Inspector { -InspectorBackendDispatcher::CallbackBase::CallbackBase(PassRefPtr<InspectorBackendDispatcher> backendDispatcher, int id) +SupplementalBackendDispatcher::SupplementalBackendDispatcher(BackendDispatcher& backendDispatcher) : m_backendDispatcher(backendDispatcher) - , m_id(id) - , m_alreadySent(false) { } -bool InspectorBackendDispatcher::CallbackBase::isActive() const +SupplementalBackendDispatcher::~SupplementalBackendDispatcher() +{ +} + +BackendDispatcher::CallbackBase::CallbackBase(Ref<BackendDispatcher>&& backendDispatcher, long requestId) + : m_backendDispatcher(WTFMove(backendDispatcher)) + , m_requestId(requestId) +{ +} + +bool BackendDispatcher::CallbackBase::isActive() const { return !m_alreadySent && m_backendDispatcher->isActive(); } -void InspectorBackendDispatcher::CallbackBase::sendFailure(const ErrorString& error) +void BackendDispatcher::CallbackBase::sendFailure(const ErrorString& error) { ASSERT(error.length()); - sendIfActive(nullptr, error); + + if (m_alreadySent) + return; + + m_alreadySent = true; + + // Immediately send an error message since this is an async response with a single error. + m_backendDispatcher->reportProtocolError(m_requestId, ServerError, error); + m_backendDispatcher->sendPendingErrors(); } -void InspectorBackendDispatcher::CallbackBase::sendIfActive(PassRefPtr<InspectorObject> partialMessage, const ErrorString& invocationError) +void BackendDispatcher::CallbackBase::sendSuccess(RefPtr<InspectorObject>&& partialMessage) { if (m_alreadySent) return; - m_backendDispatcher->sendResponse(m_id, partialMessage, invocationError); m_alreadySent = true; + m_backendDispatcher->sendResponse(m_requestId, WTFMove(partialMessage)); } -PassRefPtr<InspectorBackendDispatcher> InspectorBackendDispatcher::create(InspectorFrontendChannel* inspectorFrontendChannel) +BackendDispatcher::BackendDispatcher(Ref<FrontendRouter>&& router) + : m_frontendRouter(WTFMove(router)) { - return adoptRef(new InspectorBackendDispatcher(inspectorFrontendChannel)); } -void InspectorBackendDispatcher::registerDispatcherForDomain(const String& domain, InspectorSupplementalBackendDispatcher* dispatcher) +Ref<BackendDispatcher> BackendDispatcher::create(Ref<FrontendRouter>&& router) { - auto result = m_dispatchers.add(domain, dispatcher); - ASSERT_UNUSED(result, result.isNewEntry); + return adoptRef(*new BackendDispatcher(WTFMove(router))); } -void InspectorBackendDispatcher::dispatch(const String& message) +bool BackendDispatcher::isActive() const { - Ref<InspectorBackendDispatcher> protect(*this); - - RefPtr<InspectorValue> parsedMessage = InspectorValue::parseJSON(message); - if (!parsedMessage) { - reportProtocolError(nullptr, ParseError, ASCIILiteral("Message must be in JSON format")); - return; - } - - RefPtr<InspectorObject> messageObject = parsedMessage->asObject(); - if (!messageObject) { - reportProtocolError(nullptr, InvalidRequest, ASCIILiteral("Message must be a JSONified object")); - return; - } - - RefPtr<InspectorValue> callIdValue = messageObject->get("id"); - if (!callIdValue) { - reportProtocolError(nullptr, InvalidRequest, ASCIILiteral("'id' property was not found")); - return; - } - - long callId = 0; - if (!callIdValue->asNumber(&callId)) { - reportProtocolError(nullptr, InvalidRequest, ASCIILiteral("The type of 'id' property must be number")); - return; - } - - RefPtr<InspectorValue> methodValue = messageObject->get("method"); - if (!methodValue) { - reportProtocolError(&callId, InvalidRequest, ASCIILiteral("'method' property wasn't found")); - return; - } - - String method; - if (!methodValue->asString(&method)) { - reportProtocolError(&callId, InvalidRequest, ASCIILiteral("The type of 'method' property must be string")); - return; - } - - size_t position = method.find('.'); - if (position == WTF::notFound) { - reportProtocolError(&callId, InvalidRequest, ASCIILiteral("The 'method' property was formatted incorrectly. It should be 'Domain.method'")); - return; - } + return m_frontendRouter->hasFrontends(); +} - String domain = method.substring(0, position); - InspectorSupplementalBackendDispatcher* domainDispatcher = m_dispatchers.get(domain); - if (!domainDispatcher) { - reportProtocolError(&callId, MethodNotFound, "'" + domain + "' domain was not found"); - return; - } +void BackendDispatcher::registerDispatcherForDomain(const String& domain, SupplementalBackendDispatcher* dispatcher) +{ + ASSERT_ARG(dispatcher, dispatcher); - String domainMethod = method.substring(position + 1); - domainDispatcher->dispatch(callId, domainMethod, messageObject.release()); + // FIXME: <https://webkit.org/b/148492> Agents should only register with the backend once, + // and we should re-add the assertion that only one dispatcher is registered per domain. + m_dispatchers.set(domain, dispatcher); } -void InspectorBackendDispatcher::sendResponse(long callId, PassRefPtr<InspectorObject> result, const ErrorString& invocationError) +void BackendDispatcher::dispatch(const String& message) { - if (!m_inspectorFrontendChannel) - return; - - if (invocationError.length()) { - reportProtocolError(&callId, ServerError, invocationError); - return; + Ref<BackendDispatcher> protect(*this); + + ASSERT(!m_protocolErrors.size()); + + long requestId = 0; + RefPtr<InspectorObject> messageObject; + + { + // In case this is a re-entrant call from a nested run loop, we don't want to lose + // the outer request's id just because the inner request is bogus. + SetForScope<std::optional<long>> scopedRequestId(m_currentRequestId, std::nullopt); + + RefPtr<InspectorValue> parsedMessage; + if (!InspectorValue::parseJSON(message, parsedMessage)) { + reportProtocolError(ParseError, ASCIILiteral("Message must be in JSON format")); + sendPendingErrors(); + return; + } + + if (!parsedMessage->asObject(messageObject)) { + reportProtocolError(InvalidRequest, ASCIILiteral("Message must be a JSONified object")); + sendPendingErrors(); + return; + } + + RefPtr<InspectorValue> requestIdValue; + if (!messageObject->getValue(ASCIILiteral("id"), requestIdValue)) { + reportProtocolError(InvalidRequest, ASCIILiteral("'id' property was not found")); + sendPendingErrors(); + return; + } + + if (!requestIdValue->asInteger(requestId)) { + reportProtocolError(InvalidRequest, ASCIILiteral("The type of 'id' property must be integer")); + sendPendingErrors(); + return; + } } - RefPtr<InspectorObject> responseMessage = InspectorObject::create(); - responseMessage->setObject(ASCIILiteral("result"), result); - responseMessage->setNumber(ASCIILiteral("id"), callId); - m_inspectorFrontendChannel->sendMessageToFrontend(responseMessage->toJSONString()); + { + // We could be called re-entrantly from a nested run loop, so restore the previous id. + SetForScope<std::optional<long>> scopedRequestId(m_currentRequestId, requestId); + + RefPtr<InspectorValue> methodValue; + if (!messageObject->getValue(ASCIILiteral("method"), methodValue)) { + reportProtocolError(InvalidRequest, ASCIILiteral("'method' property wasn't found")); + sendPendingErrors(); + return; + } + + String methodString; + if (!methodValue->asString(methodString)) { + reportProtocolError(InvalidRequest, ASCIILiteral("The type of 'method' property must be string")); + sendPendingErrors(); + return; + } + + Vector<String> domainAndMethod; + methodString.split('.', true, domainAndMethod); + if (domainAndMethod.size() != 2 || !domainAndMethod[0].length() || !domainAndMethod[1].length()) { + reportProtocolError(InvalidRequest, ASCIILiteral("The 'method' property was formatted incorrectly. It should be 'Domain.method'")); + sendPendingErrors(); + return; + } + + String domain = domainAndMethod[0]; + SupplementalBackendDispatcher* domainDispatcher = m_dispatchers.get(domain); + if (!domainDispatcher) { + reportProtocolError(MethodNotFound, "'" + domain + "' domain was not found"); + sendPendingErrors(); + return; + } + + String method = domainAndMethod[1]; + domainDispatcher->dispatch(requestId, method, messageObject.releaseNonNull()); + + if (m_protocolErrors.size()) + sendPendingErrors(); + } } -void InspectorBackendDispatcher::reportProtocolError(const long* const callId, CommonErrorCode errorCode, const String& errorMessage) const +void BackendDispatcher::sendResponse(long requestId, RefPtr<InspectorObject>&& result) { - reportProtocolError(callId, errorCode, errorMessage, nullptr); + ASSERT(!m_protocolErrors.size()); + + // The JSON-RPC 2.0 specification requires that the "error" member have the value 'null' + // if no error occurred during an invocation, but we do not include it at all. + Ref<InspectorObject> responseMessage = InspectorObject::create(); + responseMessage->setObject(ASCIILiteral("result"), WTFMove(result)); + responseMessage->setInteger(ASCIILiteral("id"), requestId); + m_frontendRouter->sendResponse(responseMessage->toJSONString()); } -void InspectorBackendDispatcher::reportProtocolError(const long* const callId, CommonErrorCode errorCode, const String& errorMessage, PassRefPtr<InspectorArray> data) const +void BackendDispatcher::sendPendingErrors() { + // These error codes are specified in JSON-RPC 2.0, Section 5.1. static const int errorCodes[] = { -32700, // ParseError -32600, // InvalidRequest @@ -163,102 +209,141 @@ void InspectorBackendDispatcher::reportProtocolError(const long* const callId, C -32000, // ServerError }; - ASSERT(errorCode >= 0); - ASSERT((unsigned)errorCode < WTF_ARRAY_LENGTH(errorCodes)); - ASSERT(errorCodes[errorCode]); + // To construct the error object, only use the last error's code and message. + // Per JSON-RPC 2.0, Section 5.1, the 'data' member may contain nested errors, + // but only one top-level Error object should be sent per request. + CommonErrorCode errorCode = InternalError; + String errorMessage; + Ref<InspectorArray> payload = InspectorArray::create(); + + for (auto& data : m_protocolErrors) { + errorCode = std::get<0>(data); + errorMessage = std::get<1>(data); + + ASSERT_ARG(errorCode, (unsigned)errorCode < WTF_ARRAY_LENGTH(errorCodes)); + ASSERT_ARG(errorCode, errorCodes[errorCode]); + + Ref<InspectorObject> error = InspectorObject::create(); + error->setInteger(ASCIILiteral("code"), errorCodes[errorCode]); + error->setString(ASCIILiteral("message"), errorMessage); + payload->pushObject(WTFMove(error)); + } - if (!m_inspectorFrontendChannel) - return; + Ref<InspectorObject> topLevelError = InspectorObject::create(); + topLevelError->setInteger(ASCIILiteral("code"), errorCodes[errorCode]); + topLevelError->setString(ASCIILiteral("message"), errorMessage); + topLevelError->setArray(ASCIILiteral("data"), WTFMove(payload)); + + Ref<InspectorObject> message = InspectorObject::create(); + message->setObject(ASCIILiteral("error"), WTFMove(topLevelError)); + if (m_currentRequestId) + message->setInteger(ASCIILiteral("id"), m_currentRequestId.value()); + else { + // The 'null' value for an unknown id is specified in JSON-RPC 2.0, Section 5. + message->setValue(ASCIILiteral("id"), InspectorValue::null()); + } - RefPtr<InspectorObject> error = InspectorObject::create(); - error->setNumber(ASCIILiteral("code"), errorCodes[errorCode]); - error->setString(ASCIILiteral("message"), errorMessage); - if (data) - error->setArray(ASCIILiteral("data"), data); + m_frontendRouter->sendResponse(message->toJSONString()); - RefPtr<InspectorObject> message = InspectorObject::create(); - message->setObject(ASCIILiteral("error"), error.release()); - if (callId) - message->setNumber(ASCIILiteral("id"), *callId); - else - message->setValue(ASCIILiteral("id"), InspectorValue::null()); + m_protocolErrors.clear(); + m_currentRequestId = std::nullopt; +} + +void BackendDispatcher::reportProtocolError(CommonErrorCode errorCode, const String& errorMessage) +{ + reportProtocolError(m_currentRequestId, errorCode, errorMessage); +} + +void BackendDispatcher::reportProtocolError(std::optional<long> relatedRequestId, CommonErrorCode errorCode, const String& errorMessage) +{ + ASSERT_ARG(errorCode, errorCode >= 0); + + // If the error was reported from an async callback, then no request id will be registered yet. + if (!m_currentRequestId) + m_currentRequestId = relatedRequestId; - m_inspectorFrontendChannel->sendMessageToFrontend(message->toJSONString()); + m_protocolErrors.append(std::tuple<CommonErrorCode, String>(errorCode, errorMessage)); } -template<typename ReturnValueType, typename ValueType, typename DefaultValueType> -static ReturnValueType getPropertyValue(InspectorObject* object, const String& name, bool* valueFound, InspectorArray* protocolErrors, DefaultValueType defaultValue, bool (*asMethod)(InspectorValue*, ValueType*), const char* typeName) +#if PLATFORM(MAC) +void BackendDispatcher::reportProtocolError(WTF::DeprecatedOptional<long> relatedRequestId, CommonErrorCode errorCode, const String& errorMessage) { - ASSERT(protocolErrors); + if (relatedRequestId) + reportProtocolError(relatedRequestId.value(), errorCode, errorMessage); + else + reportProtocolError(std::nullopt, errorCode, errorMessage); +} +#endif - ValueType value = defaultValue; - if (valueFound) - *valueFound = false; +template<typename T> +T BackendDispatcher::getPropertyValue(InspectorObject* object, const String& name, bool* out_optionalValueFound, T defaultValue, std::function<bool(InspectorValue&, T&)> asMethod, const char* typeName) +{ + T result(defaultValue); + // out_optionalValueFound signals to the caller whether an optional property was found. + // if out_optionalValueFound == nullptr, then this is a required property. + if (out_optionalValueFound) + *out_optionalValueFound = false; if (!object) { - if (!valueFound) - protocolErrors->pushString(String::format("'params' object must contain required parameter '%s' with type '%s'.", name.utf8().data(), typeName)); - return value; + if (!out_optionalValueFound) + reportProtocolError(BackendDispatcher::InvalidParams, String::format("'params' object must contain required parameter '%s' with type '%s'.", name.utf8().data(), typeName)); + return result; } - InspectorObject::const_iterator end = object->end(); - InspectorObject::const_iterator valueIterator = object->find(name); - if (valueIterator == end) { - if (!valueFound) - protocolErrors->pushString(String::format("Parameter '%s' with type '%s' was not found.", name.utf8().data(), typeName)); - return value; + auto findResult = object->find(name); + if (findResult == object->end()) { + if (!out_optionalValueFound) + reportProtocolError(BackendDispatcher::InvalidParams, String::format("Parameter '%s' with type '%s' was not found.", name.utf8().data(), typeName)); + return result; } - if (!asMethod(valueIterator->value.get(), &value)) { - protocolErrors->pushString(String::format("Parameter '%s' has wrong type. It must be '%s'.", name.utf8().data(), typeName)); - return value; + if (!asMethod(*findResult->value, result)) { + reportProtocolError(BackendDispatcher::InvalidParams, String::format("Parameter '%s' has wrong type. It must be '%s'.", name.utf8().data(), typeName)); + return result; } - if (valueFound) - *valueFound = true; + if (out_optionalValueFound) + *out_optionalValueFound = true; - return value; + return result; } -struct AsMethodBridges { - static bool asInt(InspectorValue* value, int* output) { return value->asNumber(output); } - static bool asDouble(InspectorValue* value, double* output) { return value->asNumber(output); } - static bool asString(InspectorValue* value, String* output) { return value->asString(output); } - static bool asBoolean(InspectorValue* value, bool* output) { return value->asBoolean(output); } - static bool asObject(InspectorValue* value, RefPtr<InspectorObject>* output) { return value->asObject(output); } - static bool asArray(InspectorValue* value, RefPtr<InspectorArray>* output) { return value->asArray(output); } -}; +static bool castToInteger(InspectorValue& value, int& result) { return value.asInteger(result); } +static bool castToNumber(InspectorValue& value, double& result) { return value.asDouble(result); } -int InspectorBackendDispatcher::getInt(InspectorObject* object, const String& name, bool* valueFound, InspectorArray* protocolErrors) +int BackendDispatcher::getInteger(InspectorObject* object, const String& name, bool* valueFound) { - return getPropertyValue<int, int, int>(object, name, valueFound, protocolErrors, 0, AsMethodBridges::asInt, "Number"); + return getPropertyValue<int>(object, name, valueFound, 0, &castToInteger, "Integer"); } -double InspectorBackendDispatcher::getDouble(InspectorObject* object, const String& name, bool* valueFound, InspectorArray* protocolErrors) +double BackendDispatcher::getDouble(InspectorObject* object, const String& name, bool* valueFound) { - return getPropertyValue<double, double, double>(object, name, valueFound, protocolErrors, 0, AsMethodBridges::asDouble, "Number"); + return getPropertyValue<double>(object, name, valueFound, 0, &castToNumber, "Number"); } -String InspectorBackendDispatcher::getString(InspectorObject* object, const String& name, bool* valueFound, InspectorArray* protocolErrors) +String BackendDispatcher::getString(InspectorObject* object, const String& name, bool* valueFound) { - return getPropertyValue<String, String, String>(object, name, valueFound, protocolErrors, "", AsMethodBridges::asString, "String"); + return getPropertyValue<String>(object, name, valueFound, "", &InspectorValue::asString, "String"); } -bool InspectorBackendDispatcher::getBoolean(InspectorObject* object, const String& name, bool* valueFound, InspectorArray* protocolErrors) +bool BackendDispatcher::getBoolean(InspectorObject* object, const String& name, bool* valueFound) { - return getPropertyValue<bool, bool, bool>(object, name, valueFound, protocolErrors, false, AsMethodBridges::asBoolean, "Boolean"); + return getPropertyValue<bool>(object, name, valueFound, false, &InspectorValue::asBoolean, "Boolean"); } -PassRefPtr<InspectorObject> InspectorBackendDispatcher::getObject(InspectorObject* object, const String& name, bool* valueFound, InspectorArray* protocolErrors) +RefPtr<InspectorObject> BackendDispatcher::getObject(InspectorObject* object, const String& name, bool* valueFound) { - return getPropertyValue<PassRefPtr<InspectorObject>, RefPtr<InspectorObject>, InspectorObject*>(object, name, valueFound, protocolErrors, nullptr, AsMethodBridges::asObject, "Object"); + return getPropertyValue<RefPtr<InspectorObject>>(object, name, valueFound, nullptr, &InspectorValue::asObject, "Object"); } -PassRefPtr<InspectorArray> InspectorBackendDispatcher::getArray(InspectorObject* object, const String& name, bool* valueFound, InspectorArray* protocolErrors) +RefPtr<InspectorArray> BackendDispatcher::getArray(InspectorObject* object, const String& name, bool* valueFound) { - return getPropertyValue<PassRefPtr<InspectorArray>, RefPtr<InspectorArray>, InspectorArray*>(object, name, valueFound, protocolErrors, nullptr, AsMethodBridges::asArray, "Array"); + return getPropertyValue<RefPtr<InspectorArray>>(object, name, valueFound, nullptr, &InspectorValue::asArray, "Array"); } -} // namespace Inspector +RefPtr<InspectorValue> BackendDispatcher::getValue(InspectorObject* object, const String& name, bool* valueFound) +{ + return getPropertyValue<RefPtr<InspectorValue>>(object, name, valueFound, nullptr, &InspectorValue::asValue, "Value"); +} -#endif // ENABLE(INSPECTOR) +} // namespace Inspector |