Torne (Richard Coles) | 2a99a7e | 2013-03-28 15:31:22 +0000 | [diff] [blame] | 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" |
Torne (Richard Coles) | 868fa2f | 2013-06-11 10:57:03 +0100 | [diff] [blame] | 9 | #include "base/strings/stringprintf.h" |
Torne (Richard Coles) | 2a99a7e | 2013-03-28 15:31:22 +0000 | [diff] [blame] | 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 | |
Torne (Richard Coles) | c2e0dbd | 2013-05-09 18:35:53 +0100 | [diff] [blame] | 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 | }; |
| 32 | |
Torne (Richard Coles) | 2a99a7e | 2013-03-28 15:31:22 +0000 | [diff] [blame] | 33 | } // namespace |
| 34 | |
Torne (Richard Coles) | 2a99a7e | 2013-03-28 15:31:22 +0000 | [diff] [blame] | 35 | using base::Value; |
| 36 | |
Torne (Richard Coles) | c2e0dbd | 2013-05-09 18:35:53 +0100 | [diff] [blame] | 37 | DevToolsProtocol::Message::~Message() { |
| 38 | } |
| 39 | |
| 40 | DevToolsProtocol::Message::Message(const std::string& method, |
Torne (Richard Coles) | 7d4cd47 | 2013-06-19 11:58:07 +0100 | [diff] [blame] | 41 | base::DictionaryValue* params) |
Torne (Richard Coles) | c2e0dbd | 2013-05-09 18:35:53 +0100 | [diff] [blame] | 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 | |
Torne (Richard Coles) | 2a99a7e | 2013-03-28 15:31:22 +0000 | [diff] [blame] | 49 | DevToolsProtocol::Command::~Command() { |
| 50 | } |
| 51 | |
Torne (Richard Coles) | c2e0dbd | 2013-05-09 18:35:53 +0100 | [diff] [blame] | 52 | std::string DevToolsProtocol::Command::Serialize() { |
Torne (Richard Coles) | 7d4cd47 | 2013-06-19 11:58:07 +0100 | [diff] [blame] | 53 | base::DictionaryValue command; |
Torne (Richard Coles) | c2e0dbd | 2013-05-09 18:35:53 +0100 | [diff] [blame] | 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 | |
Ben Murdoch | bb1529c | 2013-08-08 10:24:53 +0100 | [diff] [blame^] | 64 | scoped_refptr<DevToolsProtocol::Response> |
Torne (Richard Coles) | 7d4cd47 | 2013-06-19 11:58:07 +0100 | [diff] [blame] | 65 | DevToolsProtocol::Command::SuccessResponse(base::DictionaryValue* result) { |
Ben Murdoch | bb1529c | 2013-08-08 10:24:53 +0100 | [diff] [blame^] | 66 | return new DevToolsProtocol::Response(id_, result); |
Torne (Richard Coles) | 2a99a7e | 2013-03-28 15:31:22 +0000 | [diff] [blame] | 67 | } |
| 68 | |
Ben Murdoch | bb1529c | 2013-08-08 10:24:53 +0100 | [diff] [blame^] | 69 | scoped_refptr<DevToolsProtocol::Response> |
Torne (Richard Coles) | c2e0dbd | 2013-05-09 18:35:53 +0100 | [diff] [blame] | 70 | DevToolsProtocol::Command::InternalErrorResponse(const std::string& message) { |
Ben Murdoch | bb1529c | 2013-08-08 10:24:53 +0100 | [diff] [blame^] | 71 | return new DevToolsProtocol::Response(id_, kErrorInternalError, message); |
Torne (Richard Coles) | c2e0dbd | 2013-05-09 18:35:53 +0100 | [diff] [blame] | 72 | } |
| 73 | |
Ben Murdoch | bb1529c | 2013-08-08 10:24:53 +0100 | [diff] [blame^] | 74 | scoped_refptr<DevToolsProtocol::Response> |
Torne (Richard Coles) | c2e0dbd | 2013-05-09 18:35:53 +0100 | [diff] [blame] | 75 | DevToolsProtocol::Command::InvalidParamResponse(const std::string& param) { |
| 76 | std::string message = |
| 77 | base::StringPrintf("Missing or invalid '%s' parameter", param.c_str()); |
Ben Murdoch | bb1529c | 2013-08-08 10:24:53 +0100 | [diff] [blame^] | 78 | return new DevToolsProtocol::Response(id_, kErrorInvalidParams, message); |
Torne (Richard Coles) | 2a99a7e | 2013-03-28 15:31:22 +0000 | [diff] [blame] | 79 | } |
| 80 | |
Ben Murdoch | bb1529c | 2013-08-08 10:24:53 +0100 | [diff] [blame^] | 81 | scoped_refptr<DevToolsProtocol::Response> |
Torne (Richard Coles) | 2a99a7e | 2013-03-28 15:31:22 +0000 | [diff] [blame] | 82 | DevToolsProtocol::Command::NoSuchMethodErrorResponse() { |
Ben Murdoch | bb1529c | 2013-08-08 10:24:53 +0100 | [diff] [blame^] | 83 | return new Response(id_, kErrorNoSuchMethod, "No such method"); |
| 84 | } |
| 85 | |
| 86 | scoped_refptr<DevToolsProtocol::Response> |
| 87 | DevToolsProtocol::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) | 2a99a7e | 2013-03-28 15:31:22 +0000 | [diff] [blame] | 92 | } |
| 93 | |
| 94 | DevToolsProtocol::Command::Command(int id, |
Torne (Richard Coles) | 2a99a7e | 2013-03-28 15:31:22 +0000 | [diff] [blame] | 95 | const std::string& method, |
Torne (Richard Coles) | 7d4cd47 | 2013-06-19 11:58:07 +0100 | [diff] [blame] | 96 | base::DictionaryValue* params) |
Torne (Richard Coles) | c2e0dbd | 2013-05-09 18:35:53 +0100 | [diff] [blame] | 97 | : Message(method, params), |
| 98 | id_(id) { |
Torne (Richard Coles) | 2a99a7e | 2013-03-28 15:31:22 +0000 | [diff] [blame] | 99 | } |
| 100 | |
Ben Murdoch | bb1529c | 2013-08-08 10:24:53 +0100 | [diff] [blame^] | 101 | DevToolsProtocol::Response::~Response() { |
| 102 | } |
| 103 | |
Torne (Richard Coles) | 2a99a7e | 2013-03-28 15:31:22 +0000 | [diff] [blame] | 104 | std::string DevToolsProtocol::Response::Serialize() { |
Torne (Richard Coles) | 7d4cd47 | 2013-06-19 11:58:07 +0100 | [diff] [blame] | 105 | base::DictionaryValue response; |
Torne (Richard Coles) | 2a99a7e | 2013-03-28 15:31:22 +0000 | [diff] [blame] | 106 | |
| 107 | if (id_ != kNoId) |
| 108 | response.SetInteger(kIdParam, id_); |
| 109 | |
| 110 | if (error_code_) { |
Torne (Richard Coles) | 7d4cd47 | 2013-06-19 11:58:07 +0100 | [diff] [blame] | 111 | base::DictionaryValue* error_object = new base::DictionaryValue(); |
Torne (Richard Coles) | 2a99a7e | 2013-03-28 15:31:22 +0000 | [diff] [blame] | 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_); |
Torne (Richard Coles) | c2e0dbd | 2013-05-09 18:35:53 +0100 | [diff] [blame] | 116 | } else if (result_) { |
Torne (Richard Coles) | 2a99a7e | 2013-03-28 15:31:22 +0000 | [diff] [blame] | 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 | |
Torne (Richard Coles) | 7d4cd47 | 2013-06-19 11:58:07 +0100 | [diff] [blame] | 125 | DevToolsProtocol::Response::Response(int id, base::DictionaryValue* result) |
Torne (Richard Coles) | 2a99a7e | 2013-03-28 15:31:22 +0000 | [diff] [blame] | 126 | : id_(id), |
| 127 | result_(result), |
Ben Murdoch | bb1529c | 2013-08-08 10:24:53 +0100 | [diff] [blame^] | 128 | error_code_(0), |
| 129 | is_async_promise_(false) { |
Torne (Richard Coles) | 2a99a7e | 2013-03-28 15:31:22 +0000 | [diff] [blame] | 130 | } |
| 131 | |
| 132 | DevToolsProtocol::Response::Response(int id, |
| 133 | int error_code, |
| 134 | const std::string& error_message) |
Torne (Richard Coles) | 7d4cd47 | 2013-06-19 11:58:07 +0100 | [diff] [blame] | 135 | : id_(id), error_code_(error_code), error_message_(error_message) {} |
Torne (Richard Coles) | 2a99a7e | 2013-03-28 15:31:22 +0000 | [diff] [blame] | 136 | |
Torne (Richard Coles) | 2a99a7e | 2013-03-28 15:31:22 +0000 | [diff] [blame] | 137 | DevToolsProtocol::Notification::Notification(const std::string& method, |
Torne (Richard Coles) | 7d4cd47 | 2013-06-19 11:58:07 +0100 | [diff] [blame] | 138 | base::DictionaryValue* params) |
Torne (Richard Coles) | c2e0dbd | 2013-05-09 18:35:53 +0100 | [diff] [blame] | 139 | : Message(method, params) { |
Torne (Richard Coles) | 2a99a7e | 2013-03-28 15:31:22 +0000 | [diff] [blame] | 140 | } |
| 141 | |
| 142 | DevToolsProtocol::Notification::~Notification() { |
| 143 | } |
| 144 | |
| 145 | std::string DevToolsProtocol::Notification::Serialize() { |
Torne (Richard Coles) | 7d4cd47 | 2013-06-19 11:58:07 +0100 | [diff] [blame] | 146 | base::DictionaryValue notification; |
Torne (Richard Coles) | 2a99a7e | 2013-03-28 15:31:22 +0000 | [diff] [blame] | 147 | notification.SetString(kMethodParam, method_); |
Torne (Richard Coles) | c2e0dbd | 2013-05-09 18:35:53 +0100 | [diff] [blame] | 148 | if (params_) |
Torne (Richard Coles) | 2a99a7e | 2013-03-28 15:31:22 +0000 | [diff] [blame] | 149 | notification.Set(kParamsParam, params_->DeepCopy()); |
| 150 | |
| 151 | std::string json_notification; |
| 152 | base::JSONWriter::Write(¬ification, &json_notification); |
| 153 | return json_notification; |
| 154 | } |
| 155 | |
Torne (Richard Coles) | 2a99a7e | 2013-03-28 15:31:22 +0000 | [diff] [blame] | 156 | DevToolsProtocol::Handler::~Handler() { |
| 157 | } |
| 158 | |
Ben Murdoch | bb1529c | 2013-08-08 10:24:53 +0100 | [diff] [blame^] | 159 | scoped_refptr<DevToolsProtocol::Response> |
Torne (Richard Coles) | 2a99a7e | 2013-03-28 15:31:22 +0000 | [diff] [blame] | 160 | DevToolsProtocol::Handler::HandleCommand( |
Ben Murdoch | bb1529c | 2013-08-08 10:24:53 +0100 | [diff] [blame^] | 161 | scoped_refptr<DevToolsProtocol::Command> command) { |
Torne (Richard Coles) | 2a99a7e | 2013-03-28 15:31:22 +0000 | [diff] [blame] | 162 | CommandHandlers::iterator it = command_handlers_.find(command->method()); |
| 163 | if (it == command_handlers_.end()) |
Ben Murdoch | bb1529c | 2013-08-08 10:24:53 +0100 | [diff] [blame^] | 164 | return NULL; |
Torne (Richard Coles) | 2a99a7e | 2013-03-28 15:31:22 +0000 | [diff] [blame] | 165 | return (it->second).Run(command); |
| 166 | } |
| 167 | |
| 168 | void DevToolsProtocol::Handler::SetNotifier(const Notifier& notifier) { |
| 169 | notifier_ = notifier; |
| 170 | } |
| 171 | |
| 172 | DevToolsProtocol::Handler::Handler() { |
| 173 | } |
| 174 | |
| 175 | void DevToolsProtocol::Handler::RegisterCommandHandler( |
| 176 | const std::string& command, |
| 177 | const CommandHandler& handler) { |
| 178 | command_handlers_[command] = handler; |
| 179 | } |
| 180 | |
| 181 | void DevToolsProtocol::Handler::SendNotification( |
| 182 | const std::string& method, |
Torne (Richard Coles) | 7d4cd47 | 2013-06-19 11:58:07 +0100 | [diff] [blame] | 183 | base::DictionaryValue* params) { |
Ben Murdoch | bb1529c | 2013-08-08 10:24:53 +0100 | [diff] [blame^] | 184 | scoped_refptr<DevToolsProtocol::Notification> notification = |
| 185 | new DevToolsProtocol::Notification(method, params); |
| 186 | SendRawMessage(notification->Serialize()); |
Ben Murdoch | 558790d | 2013-07-30 15:19:42 +0100 | [diff] [blame] | 187 | } |
| 188 | |
| 189 | void DevToolsProtocol::Handler::SendRawMessage(const std::string& message) { |
Torne (Richard Coles) | 2a99a7e | 2013-03-28 15:31:22 +0000 | [diff] [blame] | 190 | if (!notifier_.is_null()) |
Ben Murdoch | 558790d | 2013-07-30 15:19:42 +0100 | [diff] [blame] | 191 | notifier_.Run(message); |
Torne (Richard Coles) | 2a99a7e | 2013-03-28 15:31:22 +0000 | [diff] [blame] | 192 | } |
| 193 | |
Torne (Richard Coles) | 7d4cd47 | 2013-06-19 11:58:07 +0100 | [diff] [blame] | 194 | static bool ParseMethod(base::DictionaryValue* command, |
Torne (Richard Coles) | c2e0dbd | 2013-05-09 18:35:53 +0100 | [diff] [blame] | 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 | |
Torne (Richard Coles) | 2a99a7e | 2013-03-28 15:31:22 +0000 | [diff] [blame] | 204 | // static |
Ben Murdoch | bb1529c | 2013-08-08 10:24:53 +0100 | [diff] [blame^] | 205 | scoped_refptr<DevToolsProtocol::Command> DevToolsProtocol::ParseCommand( |
Torne (Richard Coles) | 2a99a7e | 2013-03-28 15:31:22 +0000 | [diff] [blame] | 206 | const std::string& json, |
| 207 | std::string* error_response) { |
Torne (Richard Coles) | 7d4cd47 | 2013-06-19 11:58:07 +0100 | [diff] [blame] | 208 | scoped_ptr<base::DictionaryValue> command_dict( |
| 209 | ParseMessage(json, error_response)); |
Torne (Richard Coles) | c2e0dbd | 2013-05-09 18:35:53 +0100 | [diff] [blame] | 210 | if (!command_dict) |
Torne (Richard Coles) | 2a99a7e | 2013-03-28 15:31:22 +0000 | [diff] [blame] | 211 | return NULL; |
Torne (Richard Coles) | 2a99a7e | 2013-03-28 15:31:22 +0000 | [diff] [blame] | 212 | |
| 213 | int id; |
| 214 | std::string method; |
Torne (Richard Coles) | c2e0dbd | 2013-05-09 18:35:53 +0100 | [diff] [blame] | 215 | bool ok = command_dict->GetInteger(kIdParam, &id) && id >= 0; |
| 216 | ok = ok && ParseMethod(command_dict.get(), &method); |
Torne (Richard Coles) | 2a99a7e | 2013-03-28 15:31:22 +0000 | [diff] [blame] | 217 | if (!ok) { |
Ben Murdoch | bb1529c | 2013-08-08 10:24:53 +0100 | [diff] [blame^] | 218 | scoped_refptr<Response> response = |
| 219 | new Response(kNoId, kErrorInvalidRequest, "No such method"); |
| 220 | *error_response = response->Serialize(); |
Torne (Richard Coles) | 2a99a7e | 2013-03-28 15:31:22 +0000 | [diff] [blame] | 221 | return NULL; |
| 222 | } |
| 223 | |
Torne (Richard Coles) | 7d4cd47 | 2013-06-19 11:58:07 +0100 | [diff] [blame] | 224 | base::DictionaryValue* params = NULL; |
Torne (Richard Coles) | 2a99a7e | 2013-03-28 15:31:22 +0000 | [diff] [blame] | 225 | command_dict->GetDictionary(kParamsParam, ¶ms); |
Torne (Richard Coles) | c2e0dbd | 2013-05-09 18:35:53 +0100 | [diff] [blame] | 226 | return new Command(id, method, params ? params->DeepCopy() : NULL); |
| 227 | } |
| 228 | |
| 229 | // static |
Ben Murdoch | bb1529c | 2013-08-08 10:24:53 +0100 | [diff] [blame^] | 230 | scoped_refptr<DevToolsProtocol::Notification> |
Torne (Richard Coles) | c2e0dbd | 2013-05-09 18:35:53 +0100 | [diff] [blame] | 231 | DevToolsProtocol::ParseNotification(const std::string& json) { |
Torne (Richard Coles) | 7d4cd47 | 2013-06-19 11:58:07 +0100 | [diff] [blame] | 232 | scoped_ptr<base::DictionaryValue> dict(ParseMessage(json, NULL)); |
Torne (Richard Coles) | c2e0dbd | 2013-05-09 18:35:53 +0100 | [diff] [blame] | 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 | |
Torne (Richard Coles) | 7d4cd47 | 2013-06-19 11:58:07 +0100 | [diff] [blame] | 241 | base::DictionaryValue* params = NULL; |
Torne (Richard Coles) | c2e0dbd | 2013-05-09 18:35:53 +0100 | [diff] [blame] | 242 | dict->GetDictionary(kParamsParam, ¶ms); |
| 243 | return new Notification(method, params ? params->DeepCopy() : NULL); |
Torne (Richard Coles) | 2a99a7e | 2013-03-28 15:31:22 +0000 | [diff] [blame] | 244 | } |
| 245 | |
| 246 | //static |
Ben Murdoch | bb1529c | 2013-08-08 10:24:53 +0100 | [diff] [blame^] | 247 | scoped_refptr<DevToolsProtocol::Notification> |
| 248 | DevToolsProtocol::CreateNotification( |
Torne (Richard Coles) | 2a99a7e | 2013-03-28 15:31:22 +0000 | [diff] [blame] | 249 | const std::string& method, |
Torne (Richard Coles) | 7d4cd47 | 2013-06-19 11:58:07 +0100 | [diff] [blame] | 250 | base::DictionaryValue* params) { |
Torne (Richard Coles) | c2e0dbd | 2013-05-09 18:35:53 +0100 | [diff] [blame] | 251 | return new Notification(method, params); |
| 252 | } |
| 253 | |
| 254 | // static |
Torne (Richard Coles) | 7d4cd47 | 2013-06-19 11:58:07 +0100 | [diff] [blame] | 255 | base::DictionaryValue* DevToolsProtocol::ParseMessage( |
Torne (Richard Coles) | c2e0dbd | 2013-05-09 18:35:53 +0100 | [diff] [blame] | 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)) { |
Ben Murdoch | bb1529c | 2013-08-08 10:24:53 +0100 | [diff] [blame^] | 265 | scoped_refptr<Response> response = |
| 266 | new Response(0, kErrorParseError, error_message); |
Torne (Richard Coles) | c2e0dbd | 2013-05-09 18:35:53 +0100 | [diff] [blame] | 267 | if (error_response) |
Ben Murdoch | bb1529c | 2013-08-08 10:24:53 +0100 | [diff] [blame^] | 268 | *error_response = response->Serialize(); |
Torne (Richard Coles) | c2e0dbd | 2013-05-09 18:35:53 +0100 | [diff] [blame] | 269 | return NULL; |
| 270 | } |
| 271 | |
Torne (Richard Coles) | 7d4cd47 | 2013-06-19 11:58:07 +0100 | [diff] [blame] | 272 | return static_cast<base::DictionaryValue*>(message.release()); |
Torne (Richard Coles) | 2a99a7e | 2013-03-28 15:31:22 +0000 | [diff] [blame] | 273 | } |
| 274 | |
| 275 | } // namespace content |