1/*
2 * Copyright (C) 2013 Apple Inc. All Rights Reserved.
3 * Copyright (C) 2011 The Chromium Authors. All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 *    notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 *    notice, this list of conditions and the following disclaimer in the
12 *    documentation and/or other materials provided with the distribution.
13 *
14 * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
15 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
17 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
18 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
19 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
20 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
21 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
22 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
24 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25 */
26
27#include "config.h"
28#include "InspectorBackendDispatcher.h"
29
30#if ENABLE(INSPECTOR)
31
32#include "InspectorFrontendChannel.h"
33#include "InspectorValues.h"
34#include <wtf/text/CString.h>
35#include <wtf/text/WTFString.h>
36
37namespace Inspector {
38
39InspectorBackendDispatcher::CallbackBase::CallbackBase(PassRefPtr<InspectorBackendDispatcher> backendDispatcher, int id)
40    : m_backendDispatcher(backendDispatcher)
41    , m_id(id)
42    , m_alreadySent(false)
43{
44}
45
46bool InspectorBackendDispatcher::CallbackBase::isActive() const
47{
48    return !m_alreadySent && m_backendDispatcher->isActive();
49}
50
51void InspectorBackendDispatcher::CallbackBase::sendFailure(const ErrorString& error)
52{
53    ASSERT(error.length());
54    sendIfActive(nullptr, error);
55}
56
57void InspectorBackendDispatcher::CallbackBase::sendIfActive(PassRefPtr<InspectorObject> partialMessage, const ErrorString& invocationError)
58{
59    if (m_alreadySent)
60        return;
61
62    m_backendDispatcher->sendResponse(m_id, partialMessage, invocationError);
63    m_alreadySent = true;
64}
65
66PassRefPtr<InspectorBackendDispatcher> InspectorBackendDispatcher::create(InspectorFrontendChannel* inspectorFrontendChannel)
67{
68    return adoptRef(new InspectorBackendDispatcher(inspectorFrontendChannel));
69}
70
71void InspectorBackendDispatcher::registerDispatcherForDomain(const String& domain, InspectorSupplementalBackendDispatcher* dispatcher)
72{
73    auto result = m_dispatchers.add(domain, dispatcher);
74    ASSERT_UNUSED(result, result.isNewEntry);
75}
76
77void InspectorBackendDispatcher::dispatch(const String& message)
78{
79    Ref<InspectorBackendDispatcher> protect(*this);
80
81    RefPtr<InspectorValue> parsedMessage = InspectorValue::parseJSON(message);
82    if (!parsedMessage) {
83        reportProtocolError(nullptr, ParseError, ASCIILiteral("Message must be in JSON format"));
84        return;
85    }
86
87    RefPtr<InspectorObject> messageObject = parsedMessage->asObject();
88    if (!messageObject) {
89        reportProtocolError(nullptr, InvalidRequest, ASCIILiteral("Message must be a JSONified object"));
90        return;
91    }
92
93    RefPtr<InspectorValue> callIdValue = messageObject->get("id");
94    if (!callIdValue) {
95        reportProtocolError(nullptr, InvalidRequest, ASCIILiteral("'id' property was not found"));
96        return;
97    }
98
99    long callId = 0;
100    if (!callIdValue->asNumber(&callId)) {
101        reportProtocolError(nullptr, InvalidRequest, ASCIILiteral("The type of 'id' property must be number"));
102        return;
103    }
104
105    RefPtr<InspectorValue> methodValue = messageObject->get("method");
106    if (!methodValue) {
107        reportProtocolError(&callId, InvalidRequest, ASCIILiteral("'method' property wasn't found"));
108        return;
109    }
110
111    String method;
112    if (!methodValue->asString(&method)) {
113        reportProtocolError(&callId, InvalidRequest, ASCIILiteral("The type of 'method' property must be string"));
114        return;
115    }
116
117    size_t position = method.find('.');
118    if (position == WTF::notFound) {
119        reportProtocolError(&callId, InvalidRequest, ASCIILiteral("The 'method' property was formatted incorrectly. It should be 'Domain.method'"));
120        return;
121    }
122
123    String domain = method.substring(0, position);
124    InspectorSupplementalBackendDispatcher* domainDispatcher = m_dispatchers.get(domain);
125    if (!domainDispatcher) {
126        reportProtocolError(&callId, MethodNotFound, "'" + domain + "' domain was not found");
127        return;
128    }
129
130    String domainMethod = method.substring(position + 1);
131    domainDispatcher->dispatch(callId, domainMethod, messageObject.release());
132}
133
134void InspectorBackendDispatcher::sendResponse(long callId, PassRefPtr<InspectorObject> result, const ErrorString& invocationError)
135{
136    if (!m_inspectorFrontendChannel)
137        return;
138
139    if (invocationError.length()) {
140        reportProtocolError(&callId, ServerError, invocationError);
141        return;
142    }
143
144    RefPtr<InspectorObject> responseMessage = InspectorObject::create();
145    responseMessage->setObject(ASCIILiteral("result"), result);
146    responseMessage->setNumber(ASCIILiteral("id"), callId);
147    m_inspectorFrontendChannel->sendMessageToFrontend(responseMessage->toJSONString());
148}
149
150void InspectorBackendDispatcher::reportProtocolError(const long* const callId, CommonErrorCode errorCode, const String& errorMessage) const
151{
152    reportProtocolError(callId, errorCode, errorMessage, nullptr);
153}
154
155void InspectorBackendDispatcher::reportProtocolError(const long* const callId, CommonErrorCode errorCode, const String& errorMessage, PassRefPtr<InspectorArray> data) const
156{
157    static const int errorCodes[] = {
158        -32700, // ParseError
159        -32600, // InvalidRequest
160        -32601, // MethodNotFound
161        -32602, // InvalidParams
162        -32603, // InternalError
163        -32000, // ServerError
164    };
165
166    ASSERT(errorCode >= 0);
167    ASSERT((unsigned)errorCode < WTF_ARRAY_LENGTH(errorCodes));
168    ASSERT(errorCodes[errorCode]);
169
170    if (!m_inspectorFrontendChannel)
171        return;
172
173    RefPtr<InspectorObject> error = InspectorObject::create();
174    error->setNumber(ASCIILiteral("code"), errorCodes[errorCode]);
175    error->setString(ASCIILiteral("message"), errorMessage);
176    if (data)
177        error->setArray(ASCIILiteral("data"), data);
178
179    RefPtr<InspectorObject> message = InspectorObject::create();
180    message->setObject(ASCIILiteral("error"), error.release());
181    if (callId)
182        message->setNumber(ASCIILiteral("id"), *callId);
183    else
184        message->setValue(ASCIILiteral("id"), InspectorValue::null());
185
186    m_inspectorFrontendChannel->sendMessageToFrontend(message->toJSONString());
187}
188
189template<typename ReturnValueType, typename ValueType, typename DefaultValueType>
190static ReturnValueType getPropertyValue(InspectorObject* object, const String& name, bool* valueFound, InspectorArray* protocolErrors, DefaultValueType defaultValue, bool (*asMethod)(InspectorValue*, ValueType*), const char* typeName)
191{
192    ASSERT(protocolErrors);
193
194    ValueType value = defaultValue;
195    if (valueFound)
196        *valueFound = false;
197
198    if (!object) {
199        if (!valueFound)
200            protocolErrors->pushString(String::format("'params' object must contain required parameter '%s' with type '%s'.", name.utf8().data(), typeName));
201        return value;
202    }
203
204    InspectorObject::const_iterator end = object->end();
205    InspectorObject::const_iterator valueIterator = object->find(name);
206    if (valueIterator == end) {
207        if (!valueFound)
208            protocolErrors->pushString(String::format("Parameter '%s' with type '%s' was not found.", name.utf8().data(), typeName));
209        return value;
210    }
211
212    if (!asMethod(valueIterator->value.get(), &value)) {
213        protocolErrors->pushString(String::format("Parameter '%s' has wrong type. It must be '%s'.", name.utf8().data(), typeName));
214        return value;
215    }
216
217    if (valueFound)
218        *valueFound = true;
219
220    return value;
221}
222
223struct AsMethodBridges {
224    static bool asInt(InspectorValue* value, int* output) { return value->asNumber(output); }
225    static bool asDouble(InspectorValue* value, double* output) { return value->asNumber(output); }
226    static bool asString(InspectorValue* value, String* output) { return value->asString(output); }
227    static bool asBoolean(InspectorValue* value, bool* output) { return value->asBoolean(output); }
228    static bool asObject(InspectorValue* value, RefPtr<InspectorObject>* output) { return value->asObject(output); }
229    static bool asArray(InspectorValue* value, RefPtr<InspectorArray>* output) { return value->asArray(output); }
230};
231
232int InspectorBackendDispatcher::getInt(InspectorObject* object, const String& name, bool* valueFound, InspectorArray* protocolErrors)
233{
234    return getPropertyValue<int, int, int>(object, name, valueFound, protocolErrors, 0, AsMethodBridges::asInt, "Number");
235}
236
237double InspectorBackendDispatcher::getDouble(InspectorObject* object, const String& name, bool* valueFound, InspectorArray* protocolErrors)
238{
239    return getPropertyValue<double, double, double>(object, name, valueFound, protocolErrors, 0, AsMethodBridges::asDouble, "Number");
240}
241
242String InspectorBackendDispatcher::getString(InspectorObject* object, const String& name, bool* valueFound, InspectorArray* protocolErrors)
243{
244    return getPropertyValue<String, String, String>(object, name, valueFound, protocolErrors, "", AsMethodBridges::asString, "String");
245}
246
247bool InspectorBackendDispatcher::getBoolean(InspectorObject* object, const String& name, bool* valueFound, InspectorArray* protocolErrors)
248{
249    return getPropertyValue<bool, bool, bool>(object, name, valueFound, protocolErrors, false, AsMethodBridges::asBoolean, "Boolean");
250}
251
252PassRefPtr<InspectorObject> InspectorBackendDispatcher::getObject(InspectorObject* object, const String& name, bool* valueFound, InspectorArray* protocolErrors)
253{
254    return getPropertyValue<PassRefPtr<InspectorObject>, RefPtr<InspectorObject>, InspectorObject*>(object, name, valueFound, protocolErrors, nullptr, AsMethodBridges::asObject, "Object");
255}
256
257PassRefPtr<InspectorArray> InspectorBackendDispatcher::getArray(InspectorObject* object, const String& name, bool* valueFound, InspectorArray* protocolErrors)
258{
259    return getPropertyValue<PassRefPtr<InspectorArray>, RefPtr<InspectorArray>, InspectorArray*>(object, name, valueFound, protocolErrors, nullptr, AsMethodBridges::asArray, "Array");
260}
261
262} // namespace Inspector
263
264#endif // ENABLE(INSPECTOR)
265