summaryrefslogtreecommitdiff
path: root/Source/JavaScriptCore/inspector/InspectorBackendDispatcher.cpp
diff options
context:
space:
mode:
authorLorry Tar Creator <lorry-tar-importer@lorry>2017-06-27 06:07:23 +0000
committerLorry Tar Creator <lorry-tar-importer@lorry>2017-06-27 06:07:23 +0000
commit1bf1084f2b10c3b47fd1a588d85d21ed0eb41d0c (patch)
tree46dcd36c86e7fbc6e5df36deb463b33e9967a6f7 /Source/JavaScriptCore/inspector/InspectorBackendDispatcher.cpp
parent32761a6cee1d0dee366b885b7b9c777e67885688 (diff)
downloadWebKitGtk-tarball-master.tar.gz
Diffstat (limited to 'Source/JavaScriptCore/inspector/InspectorBackendDispatcher.cpp')
-rw-r--r--Source/JavaScriptCore/inspector/InspectorBackendDispatcher.cpp371
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