blob: d39cc339091669c1ab816fa8284040cef7160445 [file] [log] [blame]
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +00001// 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"
Torne (Richard Coles)868fa2f2013-06-11 10:57:03 +01009#include "base/strings/stringprintf.h"
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +000010
11namespace content {
12
13namespace {
14
15const char kIdParam[] = "id";
16const char kMethodParam[] = "method";
17const char kParamsParam[] = "params";
18const char kResultParam[] = "result";
19const char kErrorParam[] = "error";
20const char kErrorCodeParam[] = "code";
21const char kErrorMessageParam[] = "message";
22const int kNoId = -1;
23
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +010024// JSON RPC 2.0 spec: http://www.jsonrpc.org/specification#error_object
25enum Error {
26 kErrorParseError = -32700,
27 kErrorInvalidRequest = -32600,
28 kErrorNoSuchMethod = -32601,
29 kErrorInvalidParams = -32602,
30 kErrorInternalError = -32603
31};
32
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +000033} // namespace
34
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +000035using base::Value;
36
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +010037DevToolsProtocol::Message::~Message() {
38}
39
40DevToolsProtocol::Message::Message(const std::string& method,
Torne (Richard Coles)7d4cd472013-06-19 11:58:07 +010041 base::DictionaryValue* params)
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +010042 : method_(method),
43 params_(params) {
44 size_t pos = method.find(".");
45 if (pos != std::string::npos && pos > 0)
46 domain_ = method.substr(0, pos);
47}
48
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +000049DevToolsProtocol::Command::~Command() {
50}
51
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +010052std::string DevToolsProtocol::Command::Serialize() {
Torne (Richard Coles)7d4cd472013-06-19 11:58:07 +010053 base::DictionaryValue command;
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +010054 command.SetInteger(kIdParam, id_);
55 command.SetString(kMethodParam, method_);
56 if (params_)
57 command.Set(kParamsParam, params_->DeepCopy());
58
59 std::string json_command;
60 base::JSONWriter::Write(&command, &json_command);
61 return json_command;
62}
63
Ben Murdochbb1529c2013-08-08 10:24:53 +010064scoped_refptr<DevToolsProtocol::Response>
Torne (Richard Coles)7d4cd472013-06-19 11:58:07 +010065DevToolsProtocol::Command::SuccessResponse(base::DictionaryValue* result) {
Ben Murdochbb1529c2013-08-08 10:24:53 +010066 return new DevToolsProtocol::Response(id_, result);
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +000067}
68
Ben Murdochbb1529c2013-08-08 10:24:53 +010069scoped_refptr<DevToolsProtocol::Response>
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +010070DevToolsProtocol::Command::InternalErrorResponse(const std::string& message) {
Ben Murdochbb1529c2013-08-08 10:24:53 +010071 return new DevToolsProtocol::Response(id_, kErrorInternalError, message);
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +010072}
73
Ben Murdochbb1529c2013-08-08 10:24:53 +010074scoped_refptr<DevToolsProtocol::Response>
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +010075DevToolsProtocol::Command::InvalidParamResponse(const std::string& param) {
76 std::string message =
77 base::StringPrintf("Missing or invalid '%s' parameter", param.c_str());
Ben Murdochbb1529c2013-08-08 10:24:53 +010078 return new DevToolsProtocol::Response(id_, kErrorInvalidParams, message);
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +000079}
80
Ben Murdochbb1529c2013-08-08 10:24:53 +010081scoped_refptr<DevToolsProtocol::Response>
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +000082DevToolsProtocol::Command::NoSuchMethodErrorResponse() {
Ben Murdochbb1529c2013-08-08 10:24:53 +010083 return new Response(id_, kErrorNoSuchMethod, "No such method");
84}
85
86scoped_refptr<DevToolsProtocol::Response>
87DevToolsProtocol::Command::AsyncResponsePromise() {
88 scoped_refptr<DevToolsProtocol::Response> promise =
89 new DevToolsProtocol::Response(0, NULL);
90 promise->is_async_promise_ = true;
91 return promise;
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +000092}
93
94DevToolsProtocol::Command::Command(int id,
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +000095 const std::string& method,
Torne (Richard Coles)7d4cd472013-06-19 11:58:07 +010096 base::DictionaryValue* params)
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +010097 : Message(method, params),
98 id_(id) {
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +000099}
100
Ben Murdochbb1529c2013-08-08 10:24:53 +0100101DevToolsProtocol::Response::~Response() {
102}
103
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000104std::string DevToolsProtocol::Response::Serialize() {
Torne (Richard Coles)7d4cd472013-06-19 11:58:07 +0100105 base::DictionaryValue response;
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000106
107 if (id_ != kNoId)
108 response.SetInteger(kIdParam, id_);
109
110 if (error_code_) {
Torne (Richard Coles)7d4cd472013-06-19 11:58:07 +0100111 base::DictionaryValue* error_object = new base::DictionaryValue();
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000112 response.Set(kErrorParam, error_object);
113 error_object->SetInteger(kErrorCodeParam, error_code_);
114 if (!error_message_.empty())
115 error_object->SetString(kErrorMessageParam, error_message_);
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +0100116 } else if (result_) {
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000117 response.Set(kResultParam, result_->DeepCopy());
118 }
119
120 std::string json_response;
121 base::JSONWriter::Write(&response, &json_response);
122 return json_response;
123}
124
Torne (Richard Coles)7d4cd472013-06-19 11:58:07 +0100125DevToolsProtocol::Response::Response(int id, base::DictionaryValue* result)
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000126 : id_(id),
127 result_(result),
Ben Murdochbb1529c2013-08-08 10:24:53 +0100128 error_code_(0),
129 is_async_promise_(false) {
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000130}
131
132DevToolsProtocol::Response::Response(int id,
133 int error_code,
134 const std::string& error_message)
Torne (Richard Coles)7d4cd472013-06-19 11:58:07 +0100135 : id_(id), error_code_(error_code), error_message_(error_message) {}
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000136
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000137DevToolsProtocol::Notification::Notification(const std::string& method,
Torne (Richard Coles)7d4cd472013-06-19 11:58:07 +0100138 base::DictionaryValue* params)
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +0100139 : Message(method, params) {
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000140}
141
142DevToolsProtocol::Notification::~Notification() {
143}
144
145std::string DevToolsProtocol::Notification::Serialize() {
Torne (Richard Coles)7d4cd472013-06-19 11:58:07 +0100146 base::DictionaryValue notification;
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000147 notification.SetString(kMethodParam, method_);
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +0100148 if (params_)
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000149 notification.Set(kParamsParam, params_->DeepCopy());
150
151 std::string json_notification;
152 base::JSONWriter::Write(&notification, &json_notification);
153 return json_notification;
154}
155
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000156DevToolsProtocol::Handler::~Handler() {
157}
158
Ben Murdochbb1529c2013-08-08 10:24:53 +0100159scoped_refptr<DevToolsProtocol::Response>
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000160DevToolsProtocol::Handler::HandleCommand(
Ben Murdochbb1529c2013-08-08 10:24:53 +0100161 scoped_refptr<DevToolsProtocol::Command> command) {
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000162 CommandHandlers::iterator it = command_handlers_.find(command->method());
163 if (it == command_handlers_.end())
Ben Murdochbb1529c2013-08-08 10:24:53 +0100164 return NULL;
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000165 return (it->second).Run(command);
166}
167
168void DevToolsProtocol::Handler::SetNotifier(const Notifier& notifier) {
169 notifier_ = notifier;
170}
171
172DevToolsProtocol::Handler::Handler() {
173}
174
175void DevToolsProtocol::Handler::RegisterCommandHandler(
176 const std::string& command,
177 const CommandHandler& handler) {
178 command_handlers_[command] = handler;
179}
180
181void DevToolsProtocol::Handler::SendNotification(
182 const std::string& method,
Torne (Richard Coles)7d4cd472013-06-19 11:58:07 +0100183 base::DictionaryValue* params) {
Ben Murdochbb1529c2013-08-08 10:24:53 +0100184 scoped_refptr<DevToolsProtocol::Notification> notification =
185 new DevToolsProtocol::Notification(method, params);
186 SendRawMessage(notification->Serialize());
Ben Murdoch558790d2013-07-30 15:19:42 +0100187}
188
189void DevToolsProtocol::Handler::SendRawMessage(const std::string& message) {
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000190 if (!notifier_.is_null())
Ben Murdoch558790d2013-07-30 15:19:42 +0100191 notifier_.Run(message);
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000192}
193
Torne (Richard Coles)7d4cd472013-06-19 11:58:07 +0100194static bool ParseMethod(base::DictionaryValue* command,
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +0100195 std::string* method) {
196 if (!command->GetString(kMethodParam, method))
197 return false;
198 size_t pos = method->find(".");
199 if (pos == std::string::npos || pos == 0)
200 return false;
201 return true;
202}
203
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000204// static
Ben Murdochbb1529c2013-08-08 10:24:53 +0100205scoped_refptr<DevToolsProtocol::Command> DevToolsProtocol::ParseCommand(
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000206 const std::string& json,
207 std::string* error_response) {
Torne (Richard Coles)7d4cd472013-06-19 11:58:07 +0100208 scoped_ptr<base::DictionaryValue> command_dict(
209 ParseMessage(json, error_response));
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +0100210 if (!command_dict)
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000211 return NULL;
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000212
213 int id;
214 std::string method;
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +0100215 bool ok = command_dict->GetInteger(kIdParam, &id) && id >= 0;
216 ok = ok && ParseMethod(command_dict.get(), &method);
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000217 if (!ok) {
Ben Murdochbb1529c2013-08-08 10:24:53 +0100218 scoped_refptr<Response> response =
219 new Response(kNoId, kErrorInvalidRequest, "No such method");
220 *error_response = response->Serialize();
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000221 return NULL;
222 }
223
Torne (Richard Coles)7d4cd472013-06-19 11:58:07 +0100224 base::DictionaryValue* params = NULL;
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000225 command_dict->GetDictionary(kParamsParam, &params);
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +0100226 return new Command(id, method, params ? params->DeepCopy() : NULL);
227}
228
229// static
Ben Murdochbb1529c2013-08-08 10:24:53 +0100230scoped_refptr<DevToolsProtocol::Notification>
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +0100231DevToolsProtocol::ParseNotification(const std::string& json) {
Torne (Richard Coles)7d4cd472013-06-19 11:58:07 +0100232 scoped_ptr<base::DictionaryValue> dict(ParseMessage(json, NULL));
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +0100233 if (!dict)
234 return NULL;
235
236 std::string method;
237 bool ok = ParseMethod(dict.get(), &method);
238 if (!ok)
239 return NULL;
240
Torne (Richard Coles)7d4cd472013-06-19 11:58:07 +0100241 base::DictionaryValue* params = NULL;
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +0100242 dict->GetDictionary(kParamsParam, &params);
243 return new Notification(method, params ? params->DeepCopy() : NULL);
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000244}
245
246//static
Ben Murdochbb1529c2013-08-08 10:24:53 +0100247scoped_refptr<DevToolsProtocol::Notification>
248DevToolsProtocol::CreateNotification(
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000249 const std::string& method,
Torne (Richard Coles)7d4cd472013-06-19 11:58:07 +0100250 base::DictionaryValue* params) {
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +0100251 return new Notification(method, params);
252}
253
254// static
Torne (Richard Coles)7d4cd472013-06-19 11:58:07 +0100255base::DictionaryValue* DevToolsProtocol::ParseMessage(
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +0100256 const std::string& json,
257 std::string* error_response) {
258 int parse_error_code;
259 std::string error_message;
260 scoped_ptr<Value> message(
261 base::JSONReader::ReadAndReturnError(
262 json, 0, &parse_error_code, &error_message));
263
264 if (!message || !message->IsType(Value::TYPE_DICTIONARY)) {
Ben Murdochbb1529c2013-08-08 10:24:53 +0100265 scoped_refptr<Response> response =
266 new Response(0, kErrorParseError, error_message);
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +0100267 if (error_response)
Ben Murdochbb1529c2013-08-08 10:24:53 +0100268 *error_response = response->Serialize();
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +0100269 return NULL;
270 }
271
Torne (Richard Coles)7d4cd472013-06-19 11:58:07 +0100272 return static_cast<base::DictionaryValue*>(message.release());
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000273}
274
275} // namespace content