Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright 2015 The Chromium Authors. All rights reserved. | 1 // Copyright 2015 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "components/devtools_service/devtools_http_server.h" | 5 #include "components/devtools_service/devtools_http_server.h" |
| 6 | 6 |
| 7 #include <string.h> | |
|
pfeldman
2015/06/10 09:28:21
Why do we build a new universe here? Is there a wa
yzshen1
2015/06/10 15:34:39
Thanks for the comment!
I feel hard to reuse devt
| |
| 8 | |
| 9 #include <string> | |
| 10 | |
| 11 #include "base/bind.h" | |
| 12 #include "base/json/json_writer.h" | |
| 7 #include "base/logging.h" | 13 #include "base/logging.h" |
| 8 #include "base/stl_util.h" | 14 #include "base/stl_util.h" |
| 9 #include "base/strings/stringprintf.h" | 15 #include "base/strings/stringprintf.h" |
| 16 #include "base/values.h" | |
| 17 #include "components/devtools_service/devtools_agent_host.h" | |
| 18 #include "components/devtools_service/devtools_registry_impl.h" | |
| 10 #include "components/devtools_service/devtools_service.h" | 19 #include "components/devtools_service/devtools_service.h" |
| 11 #include "mojo/application/public/cpp/application_impl.h" | 20 #include "mojo/application/public/cpp/application_impl.h" |
| 12 #include "mojo/services/network/public/interfaces/http_message.mojom.h" | 21 #include "mojo/services/network/public/cpp/web_socket_read_queue.h" |
| 22 #include "mojo/services/network/public/cpp/web_socket_write_queue.h" | |
| 13 #include "mojo/services/network/public/interfaces/net_address.mojom.h" | 23 #include "mojo/services/network/public/interfaces/net_address.mojom.h" |
| 14 #include "mojo/services/network/public/interfaces/network_service.mojom.h" | 24 #include "mojo/services/network/public/interfaces/network_service.mojom.h" |
| 25 #include "mojo/services/network/public/interfaces/web_socket.mojom.h" | |
| 26 #include "third_party/mojo/src/mojo/public/cpp/system/data_pipe.h" | |
| 15 | 27 |
| 16 namespace devtools_service { | 28 namespace devtools_service { |
| 17 | 29 |
| 30 namespace { | |
| 31 | |
| 32 const char kPageUrlPrefix[] = "/devtools/page/"; | |
| 33 const char kBrowserUrlPrefix[] = "/devtools/browser"; | |
| 34 const char kJsonRequestUrlPrefix[] = "/json"; | |
| 35 | |
| 36 const char kActivateCommand[] = "activate"; | |
| 37 const char kCloseCommand[] = "close"; | |
| 38 const char kListCommand[] = "list"; | |
| 39 const char kNewCommand[] = "new"; | |
| 40 const char kVersionCommand[] = "version"; | |
| 41 | |
| 42 const char kTargetIdField[] = "id"; | |
| 43 const char kTargetTypeField[] = "type"; | |
| 44 const char kTargetTitleField[] = "title"; | |
| 45 const char kTargetDescriptionField[] = "description"; | |
| 46 const char kTargetUrlField[] = "url"; | |
| 47 const char kTargetWebSocketDebuggerUrlField[] = "webSocketDebuggerUrl"; | |
| 48 const char kTargetDevtoolsFrontendUrlField[] = "devtoolsFrontendUrl"; | |
| 49 | |
| 50 bool ParseJsonPath(const std::string& path, | |
| 51 std::string* command, | |
| 52 std::string* target_id) { | |
| 53 // Fall back to list in case of empty query. | |
| 54 if (path.empty()) { | |
| 55 *command = kListCommand; | |
| 56 return true; | |
| 57 } | |
| 58 | |
| 59 if (path.find("/") != 0) { | |
| 60 // Malformed command. | |
| 61 return false; | |
| 62 } | |
| 63 *command = path.substr(1); | |
| 64 | |
| 65 size_t separator_pos = command->find("/"); | |
| 66 if (separator_pos != std::string::npos) { | |
| 67 *target_id = command->substr(separator_pos + 1); | |
| 68 *command = command->substr(0, separator_pos); | |
| 69 } | |
| 70 return true; | |
| 71 } | |
| 72 | |
| 73 mojo::HttpResponsePtr MakeResponse(uint32_t status_code, | |
| 74 const std::string& content_type, | |
| 75 const std::string& body) { | |
| 76 mojo::HttpResponsePtr response(mojo::HttpResponse::New()); | |
| 77 response->headers.resize(2); | |
| 78 response->headers[0] = mojo::HttpHeader::New(); | |
| 79 response->headers[0]->name = "Content-Length"; | |
| 80 response->headers[0]->value = | |
| 81 base::StringPrintf("%lu", static_cast<unsigned long>(body.size())); | |
| 82 response->headers[1] = mojo::HttpHeader::New(); | |
| 83 response->headers[1]->name = "Content-Type"; | |
| 84 response->headers[1]->value = content_type; | |
| 85 | |
| 86 if (!body.empty()) { | |
| 87 uint32_t num_bytes = static_cast<uint32_t>(body.size()); | |
| 88 MojoCreateDataPipeOptions options; | |
| 89 options.struct_size = sizeof(MojoCreateDataPipeOptions); | |
| 90 options.flags = MOJO_CREATE_DATA_PIPE_OPTIONS_FLAG_NONE; | |
| 91 options.element_num_bytes = 1; | |
| 92 options.capacity_num_bytes = num_bytes; | |
| 93 mojo::DataPipe data_pipe(options); | |
| 94 response->body = data_pipe.consumer_handle.Pass(); | |
| 95 MojoResult result = | |
| 96 WriteDataRaw(data_pipe.producer_handle.get(), body.data(), &num_bytes, | |
| 97 MOJO_WRITE_DATA_FLAG_ALL_OR_NONE); | |
| 98 CHECK_EQ(MOJO_RESULT_OK, result); | |
| 99 } | |
| 100 return response.Pass(); | |
| 101 } | |
| 102 | |
| 103 mojo::HttpResponsePtr MakeJsonResponse(uint32_t status_code, | |
| 104 base::Value* value, | |
| 105 const std::string& message) { | |
| 106 // Serialize value and message. | |
| 107 std::string json_value; | |
| 108 if (value) { | |
| 109 base::JSONWriter::WriteWithOptions( | |
| 110 *value, base::JSONWriter::OPTIONS_PRETTY_PRINT, &json_value); | |
| 111 } | |
| 112 std::string json_message; | |
| 113 base::JSONWriter::Write(base::StringValue(message), &json_message); | |
| 114 | |
| 115 return MakeResponse(status_code, "application/json; charset=UTF-8", | |
| 116 json_value + json_message); | |
| 117 } | |
| 118 | |
| 119 class WebSocketRelayer : public DevToolsAgentHost::Delegate, | |
| 120 public mojo::WebSocketClient, | |
| 121 public mojo::ErrorHandler { | |
| 122 public: | |
| 123 // Creates a WebSocketRelayer instance and sets it as the delegate of | |
| 124 // |agent_host|. | |
| 125 // | |
| 126 // The object destroys itself when either of the following happens: | |
| 127 // - |agent_host| is dead and the object finishes all pending sends (if any) | |
| 128 // to the Web socket; or | |
| 129 // - the underlying pipe of |web_socket| is closed and the object finishes all | |
| 130 // pending receives (if any) from the Web socket. | |
| 131 static mojo::WebSocketClientPtr SetUp( | |
| 132 DevToolsAgentHost* agent_host, | |
| 133 mojo::WebSocketPtr web_socket, | |
| 134 mojo::ScopedDataPipeProducerHandle send_stream) { | |
| 135 DCHECK(agent_host); | |
| 136 DCHECK(web_socket); | |
| 137 DCHECK(send_stream.is_valid()); | |
| 138 | |
| 139 mojo::WebSocketClientPtr web_socket_client; | |
| 140 new WebSocketRelayer(agent_host, web_socket.Pass(), send_stream.Pass(), | |
| 141 &web_socket_client); | |
| 142 return web_socket_client.Pass(); | |
| 143 } | |
| 144 | |
| 145 private: | |
| 146 WebSocketRelayer(DevToolsAgentHost* agent_host, | |
| 147 mojo::WebSocketPtr web_socket, | |
| 148 mojo::ScopedDataPipeProducerHandle send_stream, | |
| 149 mojo::WebSocketClientPtr* web_socket_client) | |
| 150 : agent_host_(agent_host), | |
| 151 binding_(this, web_socket_client), | |
| 152 web_socket_(web_socket.Pass()), | |
| 153 send_stream_(send_stream.Pass()), | |
| 154 write_send_stream_(new mojo::WebSocketWriteQueue(send_stream_.get())), | |
| 155 pending_send_count_(0), | |
| 156 pending_receive_count_(0) { | |
| 157 web_socket_.set_error_handler(this); | |
| 158 agent_host->SetDelegate(this); | |
| 159 } | |
| 160 | |
| 161 ~WebSocketRelayer() override { | |
| 162 if (agent_host_) | |
| 163 agent_host_->SetDelegate(nullptr); | |
| 164 } | |
| 165 | |
| 166 // DevToolsAgentHost::Delegate implementation. | |
| 167 void DispatchProtocolMessage(DevToolsAgentHost* agent_host, | |
| 168 const std::string& message) { | |
| 169 if (!web_socket_) | |
| 170 return; | |
| 171 | |
| 172 // TODO(yzshen): It shouldn't be an issue to pass an empty message. However, | |
| 173 // WebSocket{Read,Write}Queue doesn't handle that correctly. | |
| 174 if (message.empty()) | |
| 175 return; | |
| 176 | |
| 177 pending_send_count_++; | |
| 178 uint32_t size = static_cast<uint32_t>(message.size()); | |
| 179 write_send_stream_->Write( | |
| 180 &message[0], size, | |
| 181 base::Bind(&WebSocketRelayer::OnFinishedWritingSendStream, | |
| 182 base::Unretained(this), size)); | |
| 183 } | |
| 184 | |
| 185 void OnAgentHostClosed(DevToolsAgentHost* agent_host) override { | |
| 186 DispatchProtocolMessage(agent_host_, | |
| 187 "{ \"method\": \"Inspector.detached\", " | |
| 188 "\"params\": { \"reason\": \"target_closed\" } }"); | |
| 189 | |
| 190 // No need to call SetDelegate(nullptr) on |agent_host_| because it is going | |
| 191 // away. | |
| 192 agent_host_ = nullptr; | |
| 193 | |
| 194 if (ShouldSelfDestruct()) | |
| 195 delete this; | |
| 196 } | |
| 197 | |
| 198 // WebSocketClient implementation. | |
| 199 void DidConnect(const mojo::String& selected_subprotocol, | |
| 200 const mojo::String& extensions, | |
| 201 mojo::ScopedDataPipeConsumerHandle receive_stream) override { | |
| 202 receive_stream_ = receive_stream.Pass(); | |
| 203 read_receive_stream_.reset( | |
| 204 new mojo::WebSocketReadQueue(receive_stream_.get())); | |
| 205 } | |
| 206 | |
| 207 void DidReceiveData(bool fin, | |
| 208 mojo::WebSocket::MessageType type, | |
| 209 uint32_t num_bytes) override { | |
| 210 if (!agent_host_) | |
| 211 return; | |
| 212 | |
| 213 // TODO(yzshen): It shouldn't be an issue to pass an empty message. However, | |
| 214 // WebSocket{Read,Write}Queue doesn't handle that correctly. | |
| 215 if (num_bytes == 0) | |
| 216 return; | |
| 217 | |
| 218 pending_receive_count_++; | |
| 219 read_receive_stream_->Read( | |
| 220 num_bytes, base::Bind(&WebSocketRelayer::OnFinishedReadingReceiveStream, | |
| 221 base::Unretained(this), num_bytes)); | |
| 222 } | |
| 223 | |
| 224 void DidReceiveFlowControl(int64_t quota) override {} | |
| 225 | |
| 226 void DidFail(const mojo::String& message) override {} | |
| 227 | |
| 228 void DidClose(bool was_clean, | |
| 229 uint16_t code, | |
| 230 const mojo::String& reason) override {} | |
| 231 | |
| 232 // mojo::ErrorHandler implementation. | |
| 233 void OnConnectionError() override { | |
| 234 web_socket_ = nullptr; | |
| 235 binding_.Close(); | |
| 236 | |
| 237 if (ShouldSelfDestruct()) | |
| 238 delete this; | |
| 239 } | |
| 240 | |
| 241 void OnFinishedWritingSendStream(uint32_t num_bytes, const char* buffer) { | |
| 242 DCHECK_GT(pending_send_count_, 0u); | |
| 243 pending_send_count_--; | |
| 244 | |
| 245 if (web_socket_ && buffer) | |
| 246 web_socket_->Send(true, mojo::WebSocket::MESSAGE_TYPE_TEXT, num_bytes); | |
| 247 | |
| 248 if (ShouldSelfDestruct()) | |
| 249 delete this; | |
| 250 } | |
| 251 | |
| 252 void OnFinishedReadingReceiveStream(uint32_t num_bytes, const char* data) { | |
| 253 DCHECK_GT(pending_receive_count_, 0u); | |
| 254 pending_receive_count_--; | |
| 255 | |
| 256 if (agent_host_ && data) | |
| 257 agent_host_->SendProtocolMessageToAgent(std::string(data, num_bytes)); | |
| 258 | |
| 259 if (ShouldSelfDestruct()) | |
| 260 delete this; | |
| 261 } | |
| 262 | |
| 263 bool ShouldSelfDestruct() const { | |
| 264 return (!agent_host_ && pending_send_count_ == 0) || | |
| 265 (!web_socket_ && pending_receive_count_ == 0); | |
| 266 } | |
| 267 | |
| 268 DevToolsAgentHost* agent_host_; | |
| 269 mojo::Binding<WebSocketClient> binding_; | |
| 270 mojo::WebSocketPtr web_socket_; | |
| 271 | |
| 272 mojo::ScopedDataPipeProducerHandle send_stream_; | |
| 273 scoped_ptr<mojo::WebSocketWriteQueue> write_send_stream_; | |
| 274 size_t pending_send_count_; | |
| 275 | |
| 276 mojo::ScopedDataPipeConsumerHandle receive_stream_; | |
| 277 scoped_ptr<mojo::WebSocketReadQueue> read_receive_stream_; | |
| 278 size_t pending_receive_count_; | |
| 279 | |
| 280 DISALLOW_COPY_AND_ASSIGN(WebSocketRelayer); | |
| 281 }; | |
| 282 | |
| 283 } // namespace | |
| 284 | |
| 18 class DevToolsHttpServer::HttpConnectionDelegateImpl | 285 class DevToolsHttpServer::HttpConnectionDelegateImpl |
| 19 : public mojo::HttpConnectionDelegate, | 286 : public mojo::HttpConnectionDelegate, |
| 20 public mojo::ErrorHandler { | 287 public mojo::ErrorHandler { |
| 21 public: | 288 public: |
| 22 HttpConnectionDelegateImpl( | 289 HttpConnectionDelegateImpl( |
| 23 DevToolsHttpServer* owner, | 290 DevToolsHttpServer* owner, |
| 24 mojo::HttpConnectionPtr connection, | 291 mojo::HttpConnectionPtr connection, |
| 25 mojo::InterfaceRequest<HttpConnectionDelegate> delegate_request) | 292 mojo::InterfaceRequest<HttpConnectionDelegate> delegate_request) |
| 26 : owner_(owner), | 293 : owner_(owner), |
| 27 connection_(connection.Pass()), | 294 connection_(connection.Pass()), |
| (...skipping 26 matching lines...) Expand all Loading... | |
| 54 | 321 |
| 55 DevToolsHttpServer* const owner_; | 322 DevToolsHttpServer* const owner_; |
| 56 mojo::HttpConnectionPtr connection_; | 323 mojo::HttpConnectionPtr connection_; |
| 57 mojo::Binding<HttpConnectionDelegate> binding_; | 324 mojo::Binding<HttpConnectionDelegate> binding_; |
| 58 | 325 |
| 59 DISALLOW_COPY_AND_ASSIGN(HttpConnectionDelegateImpl); | 326 DISALLOW_COPY_AND_ASSIGN(HttpConnectionDelegateImpl); |
| 60 }; | 327 }; |
| 61 | 328 |
| 62 DevToolsHttpServer::DevToolsHttpServer(DevToolsService* service, | 329 DevToolsHttpServer::DevToolsHttpServer(DevToolsService* service, |
| 63 uint16_t remote_debugging_port) | 330 uint16_t remote_debugging_port) |
| 64 : service_(service) { | 331 : service_(service), remote_debugging_port_(remote_debugging_port) { |
| 65 VLOG(1) << "Remote debugging HTTP server is started on port " | 332 VLOG(1) << "Remote debugging HTTP server is started on port " |
| 66 << remote_debugging_port << "."; | 333 << remote_debugging_port << "."; |
| 67 mojo::NetworkServicePtr network_service; | 334 mojo::NetworkServicePtr network_service; |
| 68 mojo::URLRequestPtr request(mojo::URLRequest::New()); | 335 mojo::URLRequestPtr request(mojo::URLRequest::New()); |
| 69 request->url = "mojo:network_service"; | 336 request->url = "mojo:network_service"; |
| 70 service_->application()->ConnectToService(request.Pass(), &network_service); | 337 service_->application()->ConnectToService(request.Pass(), &network_service); |
| 71 | 338 |
| 72 mojo::NetAddressPtr local_address(mojo::NetAddress::New()); | 339 mojo::NetAddressPtr local_address(mojo::NetAddress::New()); |
| 73 local_address->family = mojo::NET_ADDRESS_FAMILY_IPV4; | 340 local_address->family = mojo::NET_ADDRESS_FAMILY_IPV4; |
| 74 local_address->ipv4 = mojo::NetAddressIPv4::New(); | 341 local_address->ipv4 = mojo::NetAddressIPv4::New(); |
| (...skipping 22 matching lines...) Expand all Loading... | |
| 97 connections_.insert( | 364 connections_.insert( |
| 98 new HttpConnectionDelegateImpl(this, connection.Pass(), delegate.Pass())); | 365 new HttpConnectionDelegateImpl(this, connection.Pass(), delegate.Pass())); |
| 99 } | 366 } |
| 100 | 367 |
| 101 void DevToolsHttpServer::OnReceivedRequest( | 368 void DevToolsHttpServer::OnReceivedRequest( |
| 102 HttpConnectionDelegateImpl* connection, | 369 HttpConnectionDelegateImpl* connection, |
| 103 mojo::HttpRequestPtr request, | 370 mojo::HttpRequestPtr request, |
| 104 const OnReceivedRequestCallback& callback) { | 371 const OnReceivedRequestCallback& callback) { |
| 105 DCHECK(connections_.find(connection) != connections_.end()); | 372 DCHECK(connections_.find(connection) != connections_.end()); |
| 106 | 373 |
| 107 // TODO(yzshen): Implement it. | 374 if (request->url.get().find(kJsonRequestUrlPrefix) == 0) { |
| 108 static const char kNotImplemented[] = "Not implemented yet!"; | 375 callback.Run(ProcessJsonRequest(request.Pass())); |
| 109 mojo::HttpResponsePtr response(mojo::HttpResponse::New()); | 376 } else { |
| 110 response->headers.resize(2); | 377 // TODO(yzshen): Implement it. |
| 111 response->headers[0] = mojo::HttpHeader::New(); | 378 NOTIMPLEMENTED(); |
| 112 response->headers[0]->name = "Content-Length"; | 379 callback.Run(MakeResponse(404, "text/html", "Not implemented yet!")); |
| 113 response->headers[0]->value = base::StringPrintf( | 380 } |
| 114 "%lu", static_cast<unsigned long>(sizeof(kNotImplemented))); | |
| 115 response->headers[1] = mojo::HttpHeader::New(); | |
| 116 response->headers[1]->name = "Content-Type"; | |
| 117 response->headers[1]->value = "text/html"; | |
| 118 | |
| 119 uint32_t num_bytes = sizeof(kNotImplemented); | |
| 120 MojoCreateDataPipeOptions options; | |
| 121 options.struct_size = sizeof(MojoCreateDataPipeOptions); | |
| 122 options.flags = MOJO_CREATE_DATA_PIPE_OPTIONS_FLAG_NONE; | |
| 123 options.element_num_bytes = 1; | |
| 124 options.capacity_num_bytes = num_bytes; | |
| 125 mojo::DataPipe data_pipe(options); | |
| 126 response->body = data_pipe.consumer_handle.Pass(); | |
| 127 WriteDataRaw(data_pipe.producer_handle.get(), kNotImplemented, &num_bytes, | |
| 128 MOJO_WRITE_DATA_FLAG_ALL_OR_NONE); | |
| 129 | |
| 130 callback.Run(response.Pass()); | |
| 131 } | 381 } |
| 132 | 382 |
| 133 void DevToolsHttpServer::OnReceivedWebSocketRequest( | 383 void DevToolsHttpServer::OnReceivedWebSocketRequest( |
| 134 HttpConnectionDelegateImpl* connection, | 384 HttpConnectionDelegateImpl* connection, |
| 135 mojo::HttpRequestPtr request, | 385 mojo::HttpRequestPtr request, |
| 136 const OnReceivedWebSocketRequestCallback& callback) { | 386 const OnReceivedWebSocketRequestCallback& callback) { |
| 137 DCHECK(connections_.find(connection) != connections_.end()); | 387 DCHECK(connections_.find(connection) != connections_.end()); |
| 138 | 388 |
| 139 // TODO(yzshen): Implement it. | 389 std::string path = request->url; |
| 140 NOTIMPLEMENTED(); | 390 size_t browser_pos = path.find(kBrowserUrlPrefix); |
| 391 if (browser_pos == 0) { | |
| 392 // TODO(yzshen): Implement it. | |
| 393 NOTIMPLEMENTED(); | |
| 394 callback.Run(nullptr, mojo::ScopedDataPipeConsumerHandle(), nullptr); | |
| 395 return; | |
| 396 } | |
| 397 | |
| 398 size_t pos = path.find(kPageUrlPrefix); | |
| 399 if (pos != 0) { | |
| 400 callback.Run(nullptr, mojo::ScopedDataPipeConsumerHandle(), nullptr); | |
| 401 return; | |
| 402 } | |
| 403 | |
| 404 std::string target_id = path.substr(strlen(kPageUrlPrefix)); | |
| 405 DevToolsAgentHost* agent = service_->registry()->GetAgentById(target_id); | |
| 406 if (!agent || agent->IsAttached()) { | |
| 407 callback.Run(nullptr, mojo::ScopedDataPipeConsumerHandle(), nullptr); | |
| 408 return; | |
| 409 } | |
| 410 | |
| 411 mojo::WebSocketPtr web_socket; | |
| 412 mojo::InterfaceRequest<mojo::WebSocket> web_socket_request = | |
| 413 mojo::GetProxy(&web_socket); | |
| 414 mojo::DataPipe data_pipe; | |
| 415 mojo::WebSocketClientPtr web_socket_client = WebSocketRelayer::SetUp( | |
| 416 agent, web_socket.Pass(), data_pipe.producer_handle.Pass()); | |
| 417 callback.Run(web_socket_request.Pass(), data_pipe.consumer_handle.Pass(), | |
| 418 web_socket_client.Pass()); | |
| 141 } | 419 } |
| 142 | 420 |
| 143 void DevToolsHttpServer::OnConnectionClosed( | 421 void DevToolsHttpServer::OnConnectionClosed( |
| 144 HttpConnectionDelegateImpl* connection) { | 422 HttpConnectionDelegateImpl* connection) { |
| 145 DCHECK(connections_.find(connection) != connections_.end()); | 423 DCHECK(connections_.find(connection) != connections_.end()); |
| 146 | 424 |
| 147 delete connection; | 425 delete connection; |
| 148 connections_.erase(connection); | 426 connections_.erase(connection); |
| 149 } | 427 } |
| 150 | 428 |
| 429 mojo::HttpResponsePtr DevToolsHttpServer::ProcessJsonRequest( | |
| 430 mojo::HttpRequestPtr request) { | |
| 431 // Trim "/json". | |
| 432 std::string path = request->url.get().substr(strlen(kJsonRequestUrlPrefix)); | |
| 433 | |
| 434 // Trim query. | |
| 435 size_t query_pos = path.find("?"); | |
| 436 if (query_pos != std::string::npos) | |
| 437 path = path.substr(0, query_pos); | |
| 438 | |
| 439 // Trim fragment. | |
| 440 size_t fragment_pos = path.find("#"); | |
| 441 if (fragment_pos != std::string::npos) | |
| 442 path = path.substr(0, fragment_pos); | |
| 443 | |
| 444 std::string command; | |
| 445 std::string target_id; | |
| 446 if (!ParseJsonPath(path, &command, &target_id)) | |
| 447 return MakeJsonResponse(404, nullptr, | |
| 448 "Malformed query: " + request->url.get()); | |
| 449 | |
| 450 if (command == kVersionCommand || command == kNewCommand || | |
| 451 command == kActivateCommand || command == kCloseCommand) { | |
| 452 NOTIMPLEMENTED(); | |
| 453 return MakeJsonResponse(404, nullptr, | |
| 454 "Not implemented yet: " + request->url.get()); | |
| 455 } | |
| 456 | |
| 457 if (command == kListCommand) { | |
| 458 base::ListValue list_value; | |
| 459 for (DevToolsRegistryImpl::Iterator iter(service_->registry()); | |
| 460 !iter.IsAtEnd(); iter.Advance()) { | |
| 461 scoped_ptr<base::DictionaryValue> dict_value(new base::DictionaryValue()); | |
| 462 | |
| 463 // TODO(yzshen): Add more information. | |
| 464 dict_value->SetString(kTargetDescriptionField, std::string()); | |
| 465 dict_value->SetString(kTargetDevtoolsFrontendUrlField, std::string()); | |
| 466 dict_value->SetString(kTargetIdField, iter.value()->id()); | |
| 467 dict_value->SetString(kTargetTitleField, std::string()); | |
| 468 dict_value->SetString(kTargetTypeField, "page"); | |
| 469 dict_value->SetString(kTargetUrlField, std::string()); | |
| 470 dict_value->SetString( | |
| 471 kTargetWebSocketDebuggerUrlField, | |
| 472 base::StringPrintf("ws://127.0.0.1:%u%s%s", | |
| 473 static_cast<unsigned>(remote_debugging_port_), | |
| 474 kPageUrlPrefix, iter.value()->id().c_str())); | |
| 475 list_value.Append(dict_value.Pass()); | |
| 476 } | |
| 477 return MakeJsonResponse(200, &list_value, std::string()); | |
| 478 } | |
| 479 | |
| 480 return MakeJsonResponse(404, nullptr, "Unknown command: " + command); | |
| 481 } | |
| 482 | |
| 151 } // namespace devtools_service | 483 } // namespace devtools_service |
| OLD | NEW |