2 * Copyright (C) 2013 Apple Inc. All Rights Reserved.
3 * Copyright (C) 2011 The Chromium Authors. All rights reserved.
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
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.
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.
28 #include "InspectorBackendDispatcher.h"
30 #include "InspectorFrontendChannel.h"
31 #include "InspectorValues.h"
32 #include <wtf/text/CString.h>
33 #include <wtf/text/WTFString.h>
37 BackendDispatcher::CallbackBase::CallbackBase(Ref
<BackendDispatcher
>&& backendDispatcher
, int id
)
38 : m_backendDispatcher(WTF::move(backendDispatcher
))
40 , m_alreadySent(false)
44 bool BackendDispatcher::CallbackBase::isActive() const
46 return !m_alreadySent
&& m_backendDispatcher
->isActive();
49 void BackendDispatcher::CallbackBase::sendFailure(const ErrorString
& error
)
51 ASSERT(error
.length());
52 sendIfActive(nullptr, error
);
55 void BackendDispatcher::CallbackBase::sendIfActive(RefPtr
<InspectorObject
>&& partialMessage
, const ErrorString
& invocationError
)
60 m_backendDispatcher
->sendResponse(m_id
, WTF::move(partialMessage
), invocationError
);
64 Ref
<BackendDispatcher
> BackendDispatcher::create(FrontendChannel
* frontendChannel
)
66 return adoptRef(*new BackendDispatcher(frontendChannel
));
69 void BackendDispatcher::registerDispatcherForDomain(const String
& domain
, SupplementalBackendDispatcher
* dispatcher
)
71 auto result
= m_dispatchers
.add(domain
, dispatcher
);
72 ASSERT_UNUSED(result
, result
.isNewEntry
);
75 void BackendDispatcher::dispatch(const String
& message
)
77 Ref
<BackendDispatcher
> protect(*this);
79 RefPtr
<InspectorValue
> parsedMessage
;
80 if (!InspectorValue::parseJSON(message
, parsedMessage
)) {
81 reportProtocolError(nullptr, ParseError
, ASCIILiteral("Message must be in JSON format"));
85 RefPtr
<InspectorObject
> messageObject
;
86 if (!parsedMessage
->asObject(messageObject
)) {
87 reportProtocolError(nullptr, InvalidRequest
, ASCIILiteral("Message must be a JSONified object"));
91 RefPtr
<InspectorValue
> callIdValue
;
92 if (!messageObject
->getValue(ASCIILiteral("id"), callIdValue
)) {
93 reportProtocolError(nullptr, InvalidRequest
, ASCIILiteral("'id' property was not found"));
98 if (!callIdValue
->asInteger(callId
)) {
99 reportProtocolError(nullptr, InvalidRequest
, ASCIILiteral("The type of 'id' property must be integer"));
103 RefPtr
<InspectorValue
> methodValue
;
104 if (!messageObject
->getValue(ASCIILiteral("method"), methodValue
)) {
105 reportProtocolError(&callId
, InvalidRequest
, ASCIILiteral("'method' property wasn't found"));
110 if (!methodValue
->asString(method
)) {
111 reportProtocolError(&callId
, InvalidRequest
, ASCIILiteral("The type of 'method' property must be string"));
115 size_t position
= method
.find('.');
116 if (position
== WTF::notFound
) {
117 reportProtocolError(&callId
, InvalidRequest
, ASCIILiteral("The 'method' property was formatted incorrectly. It should be 'Domain.method'"));
121 String domain
= method
.substring(0, position
);
122 SupplementalBackendDispatcher
* domainDispatcher
= m_dispatchers
.get(domain
);
123 if (!domainDispatcher
) {
124 reportProtocolError(&callId
, MethodNotFound
, "'" + domain
+ "' domain was not found");
128 String domainMethod
= method
.substring(position
+ 1);
129 domainDispatcher
->dispatch(callId
, domainMethod
, messageObject
.releaseNonNull());
132 void BackendDispatcher::sendResponse(long callId
, RefPtr
<InspectorObject
>&& result
, const ErrorString
& invocationError
)
134 if (!m_frontendChannel
)
137 if (invocationError
.length()) {
138 reportProtocolError(&callId
, ServerError
, invocationError
);
142 Ref
<InspectorObject
> responseMessage
= InspectorObject::create();
143 responseMessage
->setObject(ASCIILiteral("result"), result
);
144 responseMessage
->setInteger(ASCIILiteral("id"), callId
);
145 m_frontendChannel
->sendMessageToFrontend(responseMessage
->toJSONString());
148 void BackendDispatcher::reportProtocolError(const long* const callId
, CommonErrorCode errorCode
, const String
& errorMessage
) const
150 reportProtocolError(callId
, errorCode
, errorMessage
, nullptr);
153 void BackendDispatcher::reportProtocolError(const long* const callId
, CommonErrorCode errorCode
, const String
& errorMessage
, RefPtr
<Inspector::Protocol::Array
<String
>>&& data
) const
155 static const int errorCodes
[] = {
156 -32700, // ParseError
157 -32600, // InvalidRequest
158 -32601, // MethodNotFound
159 -32602, // InvalidParams
160 -32603, // InternalError
161 -32000, // ServerError
164 ASSERT_ARG(errorCode
, errorCode
>= 0);
165 ASSERT_ARG(errorCode
, (unsigned)errorCode
< WTF_ARRAY_LENGTH(errorCodes
));
166 ASSERT_ARG(errorCode
, errorCodes
[errorCode
]);
168 if (!m_frontendChannel
)
171 Ref
<InspectorObject
> error
= InspectorObject::create();
172 error
->setInteger(ASCIILiteral("code"), errorCodes
[errorCode
]);
173 error
->setString(ASCIILiteral("message"), errorMessage
);
175 error
->setArray(ASCIILiteral("data"), WTF::move(data
));
177 Ref
<InspectorObject
> message
= InspectorObject::create();
178 message
->setObject(ASCIILiteral("error"), WTF::move(error
));
180 message
->setInteger(ASCIILiteral("id"), *callId
);
182 message
->setValue(ASCIILiteral("id"), InspectorValue::null());
184 m_frontendChannel
->sendMessageToFrontend(message
->toJSONString());
187 template<typename ReturnValueType
, typename ValueType
, typename DefaultValueType
>
188 static ReturnValueType
getPropertyValue(InspectorObject
* object
, const String
& name
, bool* out_optionalValueFound
, Inspector::Protocol::Array
<String
>& protocolErrors
, DefaultValueType defaultValue
, bool (*asMethod
)(InspectorValue
&, ValueType
&), const char* typeName
)
190 ValueType result
= defaultValue
;
191 // out_optionalValueFound signals to the caller whether an optional property was found.
192 // if out_optionalValueFound == nullptr, then this is a required property.
193 if (out_optionalValueFound
)
194 *out_optionalValueFound
= false;
197 if (!out_optionalValueFound
)
198 protocolErrors
.addItem(String::format("'params' object must contain required parameter '%s' with type '%s'.", name
.utf8().data(), typeName
));
202 auto findResult
= object
->find(name
);
203 if (findResult
== object
->end()) {
204 if (!out_optionalValueFound
)
205 protocolErrors
.addItem(String::format("Parameter '%s' with type '%s' was not found.", name
.utf8().data(), typeName
));
209 if (!asMethod(*findResult
->value
, result
)) {
210 protocolErrors
.addItem(String::format("Parameter '%s' has wrong type. It must be '%s'.", name
.utf8().data(), typeName
));
214 if (out_optionalValueFound
)
215 *out_optionalValueFound
= true;
220 struct AsMethodBridges
{
221 static bool asInteger(InspectorValue
& value
, int& output
) { return value
.asInteger(output
); }
222 static bool asDouble(InspectorValue
& value
, double& output
) { return value
.asDouble(output
); }
223 static bool asString(InspectorValue
& value
, String
& output
) { return value
.asString(output
); }
224 static bool asBoolean(InspectorValue
& value
, bool& output
) { return value
.asBoolean(output
); }
225 static bool asObject(InspectorValue
& value
, RefPtr
<InspectorObject
>& output
) { return value
.asObject(output
); }
226 static bool asArray(InspectorValue
& value
, RefPtr
<InspectorArray
>& output
) { return value
.asArray(output
); }
227 static bool asValue(InspectorValue
& value
, RefPtr
<InspectorValue
>& output
) { return value
.asValue(output
); }
230 int BackendDispatcher::getInteger(InspectorObject
* object
, const String
& name
, bool* valueFound
, Inspector::Protocol::Array
<String
>& protocolErrors
)
232 return getPropertyValue
<int, int, int>(object
, name
, valueFound
, protocolErrors
, 0, AsMethodBridges::asInteger
, "Integer");
235 double BackendDispatcher::getDouble(InspectorObject
* object
, const String
& name
, bool* valueFound
, Inspector::Protocol::Array
<String
>& protocolErrors
)
237 return getPropertyValue
<double, double, double>(object
, name
, valueFound
, protocolErrors
, 0, AsMethodBridges::asDouble
, "Number");
240 String
BackendDispatcher::getString(InspectorObject
* object
, const String
& name
, bool* valueFound
, Inspector::Protocol::Array
<String
>& protocolErrors
)
242 return getPropertyValue
<String
, String
, String
>(object
, name
, valueFound
, protocolErrors
, "", AsMethodBridges::asString
, "String");
245 bool BackendDispatcher::getBoolean(InspectorObject
* object
, const String
& name
, bool* valueFound
, Inspector::Protocol::Array
<String
>& protocolErrors
)
247 return getPropertyValue
<bool, bool, bool>(object
, name
, valueFound
, protocolErrors
, false, AsMethodBridges::asBoolean
, "Boolean");
250 RefPtr
<InspectorObject
> BackendDispatcher::getObject(InspectorObject
* object
, const String
& name
, bool* valueFound
, Inspector::Protocol::Array
<String
>& protocolErrors
)
252 return getPropertyValue
<RefPtr
<InspectorObject
>, RefPtr
<InspectorObject
>, InspectorObject
*>(object
, name
, valueFound
, protocolErrors
, nullptr, AsMethodBridges::asObject
, "Object");
255 RefPtr
<InspectorArray
> BackendDispatcher::getArray(InspectorObject
* object
, const String
& name
, bool* valueFound
, Inspector::Protocol::Array
<String
>& protocolErrors
)
257 return getPropertyValue
<RefPtr
<InspectorArray
>, RefPtr
<InspectorArray
>, InspectorArray
*>(object
, name
, valueFound
, protocolErrors
, nullptr, AsMethodBridges::asArray
, "Array");
260 RefPtr
<InspectorValue
> BackendDispatcher::getValue(InspectorObject
* object
, const String
& name
, bool* valueFound
, Inspector::Protocol::Array
<String
>& protocolErrors
)
262 return getPropertyValue
<RefPtr
<InspectorValue
>, RefPtr
<InspectorValue
>, InspectorValue
*>(object
, name
, valueFound
, protocolErrors
, nullptr, AsMethodBridges::asValue
, "Value");
265 } // namespace Inspector