1 // Copyright (c) 2013 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "content/browser/devtools/devtools_protocol.h"
6
7 #include "base/json/json_reader.h"
8 #include "base/json/json_writer.h"
9 #include "base/strings/stringprintf.h"
10
11 namespace content {
12
13 namespace {
14
15 const char kIdParam[] = "id";
16 const char kMethodParam[] = "method";
17 const char kParamsParam[] = "params";
18 const char kResultParam[] = "result";
19 const char kErrorParam[] = "error";
20 const char kErrorCodeParam[] = "code";
21 const char kErrorMessageParam[] = "message";
22 const int kNoId = -1;
23
24 // JSON RPC 2.0 spec: http://www.jsonrpc.org/specification#error_object
25 enum Error {
26 kErrorParseError = -32700,
27 kErrorInvalidRequest = -32600,
28 kErrorNoSuchMethod = -32601,
29 kErrorInvalidParams = -32602,
30 kErrorInternalError = -32603,
31 kErrorServerError = -32000
32 };
33
34 } // namespace
35
36 using base::Value;
37
~Message()38 DevToolsProtocol::Message::~Message() {
39 }
40
Message(const std::string & method,base::DictionaryValue * params)41 DevToolsProtocol::Message::Message(const std::string& method,
42 base::DictionaryValue* params)
43 : method_(method),
44 params_(params) {
45 size_t pos = method.find(".");
46 if (pos != std::string::npos && pos > 0)
47 domain_ = method.substr(0, pos);
48 }
49
~Command()50 DevToolsProtocol::Command::~Command() {
51 }
52
Serialize()53 std::string DevToolsProtocol::Command::Serialize() {
54 base::DictionaryValue command;
55 command.SetInteger(kIdParam, id_);
56 command.SetString(kMethodParam, method_);
57 if (params_)
58 command.Set(kParamsParam, params_->DeepCopy());
59
60 std::string json_command;
61 base::JSONWriter::Write(&command, &json_command);
62 return json_command;
63 }
64
65 scoped_refptr<DevToolsProtocol::Response>
SuccessResponse(base::DictionaryValue * result)66 DevToolsProtocol::Command::SuccessResponse(base::DictionaryValue* result) {
67 return new DevToolsProtocol::Response(id_, result);
68 }
69
70 scoped_refptr<DevToolsProtocol::Response>
InternalErrorResponse(const std::string & message)71 DevToolsProtocol::Command::InternalErrorResponse(const std::string& message) {
72 return new DevToolsProtocol::Response(id_, kErrorInternalError, message);
73 }
74
75 scoped_refptr<DevToolsProtocol::Response>
InvalidParamResponse(const std::string & param)76 DevToolsProtocol::Command::InvalidParamResponse(const std::string& param) {
77 std::string message =
78 base::StringPrintf("Missing or invalid '%s' parameter", param.c_str());
79 return new DevToolsProtocol::Response(id_, kErrorInvalidParams, message);
80 }
81
82 scoped_refptr<DevToolsProtocol::Response>
NoSuchMethodErrorResponse()83 DevToolsProtocol::Command::NoSuchMethodErrorResponse() {
84 return new Response(id_, kErrorNoSuchMethod, "No such method");
85 }
86
87 scoped_refptr<DevToolsProtocol::Response>
ServerErrorResponse(const std::string & message)88 DevToolsProtocol::Command::ServerErrorResponse(const std::string& message) {
89 return new Response(id_, kErrorServerError, message);
90 }
91
92 scoped_refptr<DevToolsProtocol::Response>
AsyncResponsePromise()93 DevToolsProtocol::Command::AsyncResponsePromise() {
94 scoped_refptr<DevToolsProtocol::Response> promise =
95 new DevToolsProtocol::Response(0, NULL);
96 promise->is_async_promise_ = true;
97 return promise;
98 }
99
Command(int id,const std::string & method,base::DictionaryValue * params)100 DevToolsProtocol::Command::Command(int id,
101 const std::string& method,
102 base::DictionaryValue* params)
103 : Message(method, params),
104 id_(id) {
105 }
106
~Response()107 DevToolsProtocol::Response::~Response() {
108 }
109
Serialize()110 std::string DevToolsProtocol::Response::Serialize() {
111 base::DictionaryValue response;
112
113 if (id_ != kNoId)
114 response.SetInteger(kIdParam, id_);
115
116 if (error_code_) {
117 base::DictionaryValue* error_object = new base::DictionaryValue();
118 response.Set(kErrorParam, error_object);
119 error_object->SetInteger(kErrorCodeParam, error_code_);
120 if (!error_message_.empty())
121 error_object->SetString(kErrorMessageParam, error_message_);
122 } else {
123 if (result_)
124 response.Set(kResultParam, result_->DeepCopy());
125 else
126 response.Set(kResultParam, new base::DictionaryValue());
127 }
128
129 std::string json_response;
130 base::JSONWriter::Write(&response, &json_response);
131 return json_response;
132 }
133
Response(int id,base::DictionaryValue * result)134 DevToolsProtocol::Response::Response(int id, base::DictionaryValue* result)
135 : id_(id),
136 result_(result),
137 error_code_(0),
138 is_async_promise_(false) {
139 }
140
Response(int id,int error_code,const std::string & error_message)141 DevToolsProtocol::Response::Response(int id,
142 int error_code,
143 const std::string& error_message)
144 : id_(id),
145 error_code_(error_code),
146 error_message_(error_message),
147 is_async_promise_(false) {
148 }
149
Notification(const std::string & method,base::DictionaryValue * params)150 DevToolsProtocol::Notification::Notification(const std::string& method,
151 base::DictionaryValue* params)
152 : Message(method, params) {
153 }
154
~Notification()155 DevToolsProtocol::Notification::~Notification() {
156 }
157
Serialize()158 std::string DevToolsProtocol::Notification::Serialize() {
159 base::DictionaryValue notification;
160 notification.SetString(kMethodParam, method_);
161 if (params_)
162 notification.Set(kParamsParam, params_->DeepCopy());
163
164 std::string json_notification;
165 base::JSONWriter::Write(¬ification, &json_notification);
166 return json_notification;
167 }
168
~Handler()169 DevToolsProtocol::Handler::~Handler() {
170 }
171
172 scoped_refptr<DevToolsProtocol::Response>
HandleCommand(scoped_refptr<DevToolsProtocol::Command> command)173 DevToolsProtocol::Handler::HandleCommand(
174 scoped_refptr<DevToolsProtocol::Command> command) {
175 CommandHandlers::iterator it = command_handlers_.find(command->method());
176 if (it == command_handlers_.end())
177 return NULL;
178 return (it->second).Run(command);
179 }
180
SetNotifier(const Notifier & notifier)181 void DevToolsProtocol::Handler::SetNotifier(const Notifier& notifier) {
182 notifier_ = notifier;
183 }
184
Handler()185 DevToolsProtocol::Handler::Handler() {
186 }
187
RegisterCommandHandler(const std::string & command,const CommandHandler & handler)188 void DevToolsProtocol::Handler::RegisterCommandHandler(
189 const std::string& command,
190 const CommandHandler& handler) {
191 command_handlers_[command] = handler;
192 }
193
SendNotification(const std::string & method,base::DictionaryValue * params)194 void DevToolsProtocol::Handler::SendNotification(
195 const std::string& method,
196 base::DictionaryValue* params) {
197 scoped_refptr<DevToolsProtocol::Notification> notification =
198 new DevToolsProtocol::Notification(method, params);
199 SendRawMessage(notification->Serialize());
200 }
201
SendAsyncResponse(scoped_refptr<DevToolsProtocol::Response> response)202 void DevToolsProtocol::Handler::SendAsyncResponse(
203 scoped_refptr<DevToolsProtocol::Response> response) {
204 SendRawMessage(response->Serialize());
205 }
206
SendRawMessage(const std::string & message)207 void DevToolsProtocol::Handler::SendRawMessage(const std::string& message) {
208 if (!notifier_.is_null())
209 notifier_.Run(message);
210 }
211
ParseMethod(base::DictionaryValue * command,std::string * method)212 static bool ParseMethod(base::DictionaryValue* command,
213 std::string* method) {
214 if (!command->GetString(kMethodParam, method))
215 return false;
216 size_t pos = method->find(".");
217 if (pos == std::string::npos || pos == 0)
218 return false;
219 return true;
220 }
221
222 // static
ParseCommand(const std::string & json,std::string * error_response)223 scoped_refptr<DevToolsProtocol::Command> DevToolsProtocol::ParseCommand(
224 const std::string& json,
225 std::string* error_response) {
226 scoped_ptr<base::DictionaryValue> command_dict(
227 ParseMessage(json, error_response));
228 return ParseCommand(command_dict.get(), error_response);
229 }
230
231 // static
ParseCommand(base::DictionaryValue * command_dict,std::string * error_response)232 scoped_refptr<DevToolsProtocol::Command> DevToolsProtocol::ParseCommand(
233 base::DictionaryValue* command_dict,
234 std::string* error_response) {
235 if (!command_dict)
236 return NULL;
237
238 int id;
239 std::string method;
240 bool ok = command_dict->GetInteger(kIdParam, &id) && id >= 0;
241 ok = ok && ParseMethod(command_dict, &method);
242 if (!ok) {
243 scoped_refptr<Response> response =
244 new Response(kNoId, kErrorInvalidRequest, "No such method");
245 *error_response = response->Serialize();
246 return NULL;
247 }
248
249 base::DictionaryValue* params = NULL;
250 command_dict->GetDictionary(kParamsParam, ¶ms);
251 return new Command(id, method, params ? params->DeepCopy() : NULL);
252 }
253
254 // static
255 scoped_refptr<DevToolsProtocol::Command>
CreateCommand(int id,const std::string & method,base::DictionaryValue * params)256 DevToolsProtocol::CreateCommand(
257 int id,
258 const std::string& method,
259 base::DictionaryValue* params) {
260 return new Command(id, method, params);
261 }
262
263 //static
264 scoped_refptr<DevToolsProtocol::Response>
ParseResponse(base::DictionaryValue * response_dict)265 DevToolsProtocol::ParseResponse(
266 base::DictionaryValue* response_dict) {
267 int id;
268 if (!response_dict->GetInteger(kIdParam, &id))
269 id = kNoId;
270
271 const base::DictionaryValue* error_dict;
272 if (response_dict->GetDictionary(kErrorParam, &error_dict)) {
273 int error_code = kErrorInternalError;
274 response_dict->GetInteger(kErrorCodeParam, &error_code);
275 std::string error_message;
276 response_dict->GetString(kErrorMessageParam, &error_message);
277 return new Response(id, error_code, error_message);
278 }
279
280 const base::DictionaryValue* result = NULL;
281 response_dict->GetDictionary(kResultParam, &result);
282 return new Response(id, result ? result->DeepCopy() : NULL);
283 }
284
285 // static
286 scoped_refptr<DevToolsProtocol::Notification>
ParseNotification(const std::string & json)287 DevToolsProtocol::ParseNotification(const std::string& json) {
288 scoped_ptr<base::DictionaryValue> dict(ParseMessage(json, NULL));
289 if (!dict)
290 return NULL;
291
292 std::string method;
293 bool ok = ParseMethod(dict.get(), &method);
294 if (!ok)
295 return NULL;
296
297 base::DictionaryValue* params = NULL;
298 dict->GetDictionary(kParamsParam, ¶ms);
299 return new Notification(method, params ? params->DeepCopy() : NULL);
300 }
301
302 //static
303 scoped_refptr<DevToolsProtocol::Notification>
CreateNotification(const std::string & method,base::DictionaryValue * params)304 DevToolsProtocol::CreateNotification(
305 const std::string& method,
306 base::DictionaryValue* params) {
307 return new Notification(method, params);
308 }
309
310 // static
ParseMessage(const std::string & json,std::string * error_response)311 base::DictionaryValue* DevToolsProtocol::ParseMessage(
312 const std::string& json,
313 std::string* error_response) {
314 int parse_error_code;
315 std::string error_message;
316 scoped_ptr<base::Value> message(
317 base::JSONReader::ReadAndReturnError(
318 json, 0, &parse_error_code, &error_message));
319
320 if (!message || !message->IsType(base::Value::TYPE_DICTIONARY)) {
321 scoped_refptr<Response> response =
322 new Response(0, kErrorParseError, error_message);
323 if (error_response)
324 *error_response = response->Serialize();
325 return NULL;
326 }
327
328 return static_cast<base::DictionaryValue*>(message.release());
329 }
330
331 } // namespace content
332