devtools_protocol.cc revision bb1529ce867d8845a77ec7cdf3e3003ef1771a40
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), error_code_(error_code), error_message_(error_message) {}
136
137DevToolsProtocol::Notification::Notification(const std::string& method,
138                                             base::DictionaryValue* params)
139    : Message(method, params) {
140}
141
142DevToolsProtocol::Notification::~Notification() {
143}
144
145std::string DevToolsProtocol::Notification::Serialize() {
146  base::DictionaryValue notification;
147  notification.SetString(kMethodParam, method_);
148  if (params_)
149    notification.Set(kParamsParam, params_->DeepCopy());
150
151  std::string json_notification;
152  base::JSONWriter::Write(&notification, &json_notification);
153  return json_notification;
154}
155
156DevToolsProtocol::Handler::~Handler() {
157}
158
159scoped_refptr<DevToolsProtocol::Response>
160DevToolsProtocol::Handler::HandleCommand(
161    scoped_refptr<DevToolsProtocol::Command> command) {
162  CommandHandlers::iterator it = command_handlers_.find(command->method());
163  if (it == command_handlers_.end())
164    return NULL;
165  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,
183    base::DictionaryValue* params) {
184  scoped_refptr<DevToolsProtocol::Notification> notification =
185      new DevToolsProtocol::Notification(method, params);
186  SendRawMessage(notification->Serialize());
187}
188
189void DevToolsProtocol::Handler::SendRawMessage(const std::string& message) {
190  if (!notifier_.is_null())
191    notifier_.Run(message);
192}
193
194static bool ParseMethod(base::DictionaryValue* command,
195                        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
204// static
205scoped_refptr<DevToolsProtocol::Command> DevToolsProtocol::ParseCommand(
206    const std::string& json,
207    std::string* error_response) {
208  scoped_ptr<base::DictionaryValue> command_dict(
209      ParseMessage(json, error_response));
210  if (!command_dict)
211    return NULL;
212
213  int id;
214  std::string method;
215  bool ok = command_dict->GetInteger(kIdParam, &id) && id >= 0;
216  ok = ok && ParseMethod(command_dict.get(), &method);
217  if (!ok) {
218    scoped_refptr<Response> response =
219        new Response(kNoId, kErrorInvalidRequest, "No such method");
220    *error_response = response->Serialize();
221    return NULL;
222  }
223
224  base::DictionaryValue* params = NULL;
225  command_dict->GetDictionary(kParamsParam, &params);
226  return new Command(id, method, params ? params->DeepCopy() : NULL);
227}
228
229// static
230scoped_refptr<DevToolsProtocol::Notification>
231DevToolsProtocol::ParseNotification(const std::string& json) {
232  scoped_ptr<base::DictionaryValue> dict(ParseMessage(json, NULL));
233  if (!dict)
234    return NULL;
235
236  std::string method;
237  bool ok = ParseMethod(dict.get(), &method);
238  if (!ok)
239    return NULL;
240
241  base::DictionaryValue* params = NULL;
242  dict->GetDictionary(kParamsParam, &params);
243  return new Notification(method, params ? params->DeepCopy() : NULL);
244}
245
246//static
247scoped_refptr<DevToolsProtocol::Notification>
248DevToolsProtocol::CreateNotification(
249    const std::string& method,
250    base::DictionaryValue* params) {
251  return new Notification(method, params);
252}
253
254// static
255base::DictionaryValue* DevToolsProtocol::ParseMessage(
256    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)) {
265    scoped_refptr<Response> response =
266        new Response(0, kErrorParseError, error_message);
267    if (error_response)
268      *error_response = response->Serialize();
269    return NULL;
270  }
271
272  return static_cast<base::DictionaryValue*>(message.release());
273}
274
275}  // namespace content
276