--- /dev/null
+/*
+ * Copyright (C) 2013 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
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``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 APPLE INC. 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 "InspectorBackendDispatcher.h"
+
+#if ENABLE(INSPECTOR)
+
+#include "InspectorFrontendChannel.h"
+#include "InspectorValues.h"
+#include <wtf/text/CString.h>
+#include <wtf/text/WTFString.h>
+
+namespace Inspector {
+
+InspectorBackendDispatcher::CallbackBase::CallbackBase(PassRefPtr<InspectorBackendDispatcher> backendDispatcher, int id)
+ : m_backendDispatcher(backendDispatcher)
+ , m_id(id)
+ , m_alreadySent(false)
+{
+}
+
+bool InspectorBackendDispatcher::CallbackBase::isActive() const
+{
+ return !m_alreadySent && m_backendDispatcher->isActive();
+}
+
+void InspectorBackendDispatcher::CallbackBase::sendFailure(const ErrorString& error)
+{
+ ASSERT(error.length());
+ sendIfActive(nullptr, error);
+}
+
+void InspectorBackendDispatcher::CallbackBase::sendIfActive(PassRefPtr<InspectorObject> partialMessage, const ErrorString& invocationError)
+{
+ if (m_alreadySent)
+ return;
+
+ m_backendDispatcher->sendResponse(m_id, partialMessage, invocationError);
+ m_alreadySent = true;
+}
+
+PassRefPtr<InspectorBackendDispatcher> InspectorBackendDispatcher::create(InspectorFrontendChannel* inspectorFrontendChannel)
+{
+ return adoptRef(new InspectorBackendDispatcher(inspectorFrontendChannel));
+}
+
+void InspectorBackendDispatcher::registerDispatcherForDomain(const String& domain, InspectorSupplementalBackendDispatcher* dispatcher)
+{
+ auto result = m_dispatchers.add(domain, dispatcher);
+ ASSERT_UNUSED(result, result.isNewEntry);
+}
+
+void InspectorBackendDispatcher::dispatch(const String& message)
+{
+ 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;
+ }
+
+ String domain = method.substring(0, position);
+ InspectorSupplementalBackendDispatcher* domainDispatcher = m_dispatchers.get(domain);
+ if (!domainDispatcher) {
+ reportProtocolError(&callId, MethodNotFound, "'" + domain + "' domain was not found");
+ return;
+ }
+
+ String domainMethod = method.substring(position + 1);
+ domainDispatcher->dispatch(callId, domainMethod, messageObject.release());
+}
+
+void InspectorBackendDispatcher::sendResponse(long callId, PassRefPtr<InspectorObject> result, const ErrorString& invocationError)
+{
+ if (!m_inspectorFrontendChannel)
+ return;
+
+ if (invocationError.length()) {
+ reportProtocolError(&callId, ServerError, invocationError);
+ return;
+ }
+
+ RefPtr<InspectorObject> responseMessage = InspectorObject::create();
+ responseMessage->setObject(ASCIILiteral("result"), result);
+ responseMessage->setNumber(ASCIILiteral("id"), callId);
+ m_inspectorFrontendChannel->sendMessageToFrontend(responseMessage->toJSONString());
+}
+
+void InspectorBackendDispatcher::reportProtocolError(const long* const callId, CommonErrorCode errorCode, const String& errorMessage) const
+{
+ reportProtocolError(callId, errorCode, errorMessage, nullptr);
+}
+
+void InspectorBackendDispatcher::reportProtocolError(const long* const callId, CommonErrorCode errorCode, const String& errorMessage, PassRefPtr<InspectorArray> data) const
+{
+ static const int errorCodes[] = {
+ -32700, // ParseError
+ -32600, // InvalidRequest
+ -32601, // MethodNotFound
+ -32602, // InvalidParams
+ -32603, // InternalError
+ -32000, // ServerError
+ };
+
+ ASSERT(errorCode >= 0);
+ ASSERT((unsigned)errorCode < WTF_ARRAY_LENGTH(errorCodes));
+ ASSERT(errorCodes[errorCode]);
+
+ if (!m_inspectorFrontendChannel)
+ return;
+
+ RefPtr<InspectorObject> error = InspectorObject::create();
+ error->setNumber(ASCIILiteral("code"), errorCodes[errorCode]);
+ error->setString(ASCIILiteral("message"), errorMessage);
+ if (data)
+ error->setArray(ASCIILiteral("data"), data);
+
+ 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_inspectorFrontendChannel->sendMessageToFrontend(message->toJSONString());
+}
+
+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)
+{
+ ASSERT(protocolErrors);
+
+ ValueType value = defaultValue;
+ if (valueFound)
+ *valueFound = 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;
+ }
+
+ 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;
+ }
+
+ 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 (valueFound)
+ *valueFound = true;
+
+ return value;
+}
+
+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); }
+};
+
+int InspectorBackendDispatcher::getInt(InspectorObject* object, const String& name, bool* valueFound, InspectorArray* protocolErrors)
+{
+ return getPropertyValue<int, int, int>(object, name, valueFound, protocolErrors, 0, AsMethodBridges::asInt, "Number");
+}
+
+double InspectorBackendDispatcher::getDouble(InspectorObject* object, const String& name, bool* valueFound, InspectorArray* protocolErrors)
+{
+ return getPropertyValue<double, double, double>(object, name, valueFound, protocolErrors, 0, AsMethodBridges::asDouble, "Number");
+}
+
+String InspectorBackendDispatcher::getString(InspectorObject* object, const String& name, bool* valueFound, InspectorArray* protocolErrors)
+{
+ return getPropertyValue<String, String, String>(object, name, valueFound, protocolErrors, "", AsMethodBridges::asString, "String");
+}
+
+bool InspectorBackendDispatcher::getBoolean(InspectorObject* object, const String& name, bool* valueFound, InspectorArray* protocolErrors)
+{
+ return getPropertyValue<bool, bool, bool>(object, name, valueFound, protocolErrors, false, AsMethodBridges::asBoolean, "Boolean");
+}
+
+PassRefPtr<InspectorObject> InspectorBackendDispatcher::getObject(InspectorObject* object, const String& name, bool* valueFound, InspectorArray* protocolErrors)
+{
+ return getPropertyValue<PassRefPtr<InspectorObject>, RefPtr<InspectorObject>, InspectorObject*>(object, name, valueFound, protocolErrors, nullptr, AsMethodBridges::asObject, "Object");
+}
+
+PassRefPtr<InspectorArray> InspectorBackendDispatcher::getArray(InspectorObject* object, const String& name, bool* valueFound, InspectorArray* protocolErrors)
+{
+ return getPropertyValue<PassRefPtr<InspectorArray>, RefPtr<InspectorArray>, InspectorArray*>(object, name, valueFound, protocolErrors, nullptr, AsMethodBridges::asArray, "Array");
+}
+
+} // namespace Inspector
+
+#endif // ENABLE(INSPECTOR)