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
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
24// 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
33}  // namespace
34
35using base::Value;
36
37DevToolsProtocol::Message::~Message() {
38}
39
40DevToolsProtocol::Message::Message(const std::string& method,
41                                   base::DictionaryValue* params)
42    : 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
49DevToolsProtocol::Command::~Command() {
50}
51
52std::string DevToolsProtocol::Command::Serialize() {
53  base::DictionaryValue command;
54  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
64scoped_refptr<DevToolsProtocol::Response>
65DevToolsProtocol::Command::SuccessResponse(base::DictionaryValue* result) {
66  return new DevToolsProtocol::Response(id_, result);
67}
68
69scoped_refptr<DevToolsProtocol::Response>
70DevToolsProtocol::Command::InternalErrorResponse(const std::string& message) {
71  return new DevToolsProtocol::Response(id_, kErrorInternalError, message);
72}
73
74scoped_refptr<DevToolsProtocol::Response>
75DevToolsProtocol::Command::InvalidParamResponse(const std::string& param) {
76  std::string message =
77      base::StringPrintf("Missing or invalid '%s' parameter", param.c_str());
78  return new DevToolsProtocol::Response(id_, kErrorInvalidParams, message);
79}
80
81scoped_refptr<DevToolsProtocol::Response>
82DevToolsProtocol::Command::NoSuchMethodErrorResponse() {
83  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;
92}
93
94DevToolsProtocol::Command::Command(int id,
95                                   const std::string& method,
96                                   base::DictionaryValue* params)
97    : Message(method, params),
98      id_(id) {
99}
100
101DevToolsProtocol::Response::~Response() {
102}
103
104std::string DevToolsProtocol::Response::Serialize() {
105  base::DictionaryValue response;
106
107  if (id_ != kNoId)
108    response.SetInteger(kIdParam, id_);
109
110  if (error_code_) {
111    base::DictionaryValue* error_object = new base::DictionaryValue();
112    response.Set(kErrorParam, error_object);
113    error_object->SetInteger(kErrorCodeParam, error_code_);
114    if (!error_message_.empty())
115      error_object->SetString(kErrorMessageParam, error_message_);
116  } else if (result_) {
117    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
125DevToolsProtocol::Response::Response(int id, base::DictionaryValue* result)
126    : id_(id),
127      result_(result),
128      error_code_(0),
129      is_async_promise_(false) {
130}
131
132DevToolsProtocol::Response::Response(int id,
133                                     int error_code,
134                                     const std::string& error_message)
135    : id_(id),
136      error_code_(error_code),
137      error_message_(error_message),
138      is_async_promise_(false) {
139}
140
141DevToolsProtocol::Notification::Notification(const std::string& method,
142                                             base::DictionaryValue* params)
143    : Message(method, params) {
144}
145
146DevToolsProtocol::Notification::~Notification() {
147}
148
149std::string DevToolsProtocol::Notification::Serialize() {
150  base::DictionaryValue notification;
151  notification.SetString(kMethodParam, method_);
152  if (params_)
153    notification.Set(kParamsParam, params_->DeepCopy());
154
155  std::string json_notification;
156  base::JSONWriter::Write(&notification, &json_notification);
157  return json_notification;
158}
159
160DevToolsProtocol::Handler::~Handler() {
161}
162
163scoped_refptr<DevToolsProtocol::Response>
164DevToolsProtocol::Handler::HandleCommand(
165    scoped_refptr<DevToolsProtocol::Command> command) {
166  CommandHandlers::iterator it = command_handlers_.find(command->method());
167  if (it == command_handlers_.end())
168    return NULL;
169  return (it->second).Run(command);
170}
171
172void DevToolsProtocol::Handler::SetNotifier(const Notifier& notifier) {
173  notifier_ = notifier;
174}
175
176DevToolsProtocol::Handler::Handler() {
177}
178
179void DevToolsProtocol::Handler::RegisterCommandHandler(
180    const std::string& command,
181    const CommandHandler& handler) {
182  command_handlers_[command] = handler;
183}
184
185void DevToolsProtocol::Handler::SendNotification(
186    const std::string& method,
187    base::DictionaryValue* params) {
188  scoped_refptr<DevToolsProtocol::Notification> notification =
189      new DevToolsProtocol::Notification(method, params);
190  SendRawMessage(notification->Serialize());
191}
192
193void DevToolsProtocol::Handler::SendRawMessage(const std::string& message) {
194  if (!notifier_.is_null())
195    notifier_.Run(message);
196}
197
198static bool ParseMethod(base::DictionaryValue* command,
199                        std::string* method) {
200  if (!command->GetString(kMethodParam, method))
201    return false;
202  size_t pos = method->find(".");
203  if (pos == std::string::npos || pos == 0)
204    return false;
205  return true;
206}
207
208// static
209scoped_refptr<DevToolsProtocol::Command> DevToolsProtocol::ParseCommand(
210    const std::string& json,
211    std::string* error_response) {
212  scoped_ptr<base::DictionaryValue> command_dict(
213      ParseMessage(json, error_response));
214  if (!command_dict)
215    return NULL;
216
217  int id;
218  std::string method;
219  bool ok = command_dict->GetInteger(kIdParam, &id) && id >= 0;
220  ok = ok && ParseMethod(command_dict.get(), &method);
221  if (!ok) {
222    scoped_refptr<Response> response =
223        new Response(kNoId, kErrorInvalidRequest, "No such method");
224    *error_response = response->Serialize();
225    return NULL;
226  }
227
228  base::DictionaryValue* params = NULL;
229  command_dict->GetDictionary(kParamsParam, &params);
230  return new Command(id, method, params ? params->DeepCopy() : NULL);
231}
232
233// static
234scoped_refptr<DevToolsProtocol::Notification>
235DevToolsProtocol::ParseNotification(const std::string& json) {
236  scoped_ptr<base::DictionaryValue> dict(ParseMessage(json, NULL));
237  if (!dict)
238    return NULL;
239
240  std::string method;
241  bool ok = ParseMethod(dict.get(), &method);
242  if (!ok)
243    return NULL;
244
245  base::DictionaryValue* params = NULL;
246  dict->GetDictionary(kParamsParam, &params);
247  return new Notification(method, params ? params->DeepCopy() : NULL);
248}
249
250//static
251scoped_refptr<DevToolsProtocol::Notification>
252DevToolsProtocol::CreateNotification(
253    const std::string& method,
254    base::DictionaryValue* params) {
255  return new Notification(method, params);
256}
257
258// static
259base::DictionaryValue* DevToolsProtocol::ParseMessage(
260    const std::string& json,
261    std::string* error_response) {
262  int parse_error_code;
263  std::string error_message;
264  scoped_ptr<Value> message(
265      base::JSONReader::ReadAndReturnError(
266          json, 0, &parse_error_code, &error_message));
267
268  if (!message || !message->IsType(Value::TYPE_DICTIONARY)) {
269    scoped_refptr<Response> response =
270        new Response(0, kErrorParseError, error_message);
271    if (error_response)
272      *error_response = response->Serialize();
273    return NULL;
274  }
275
276  return static_cast<base::DictionaryValue*>(message.release());
277}
278
279}  // namespace content
280