blob: a83f2422604f5b63ac7ce3a9fecb3967c7ee5fed [file] [log] [blame]
/*
* 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 WebCore {
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, "Message must be in JSON format");
return;
}
RefPtr<InspectorObject> messageObject = parsedMessage->asObject();
if (!messageObject) {
reportProtocolError(nullptr, InvalidRequest, "Message must be a JSONified object");
return;
}
RefPtr<InspectorValue> callIdValue = messageObject->get("id");
if (!callIdValue) {
reportProtocolError(nullptr, InvalidRequest, "'id' property was not found");
return;
}
long callId = 0;
if (!callIdValue->asNumber(&callId)) {
reportProtocolError(nullptr, InvalidRequest, "The type of 'id' property must be number");
return;
}
RefPtr<InspectorValue> methodValue = messageObject->get("method");
if (!methodValue) {
reportProtocolError(&callId, InvalidRequest, "'method' property wasn't found");
return;
}
String method;
if (!methodValue->asString(&method)) {
reportProtocolError(&callId, InvalidRequest, "The type of 'method' property must be string");
return;
}
size_t position = method.find('.');
if (position == WTF::notFound) {
reportProtocolError(&callId, InvalidRequest, "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>
ReturnValueType InspectorBackendDispatcher::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 WebCore
#endif // ENABLE(INSPECTOR)