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(¬ification, &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, ¶ms); 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, ¶ms); 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