1 // Copyright (c) 2012 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_http_handler_impl.h" 6 7 #include <algorithm> 8 #include <utility> 9 10 #include "base/bind.h" 11 #include "base/compiler_specific.h" 12 #include "base/file_util.h" 13 #include "base/json/json_writer.h" 14 #include "base/logging.h" 15 #include "base/message_loop/message_loop_proxy.h" 16 #include "base/stl_util.h" 17 #include "base/threading/thread.h" 18 #include "base/values.h" 19 #include "content/browser/devtools/devtools_browser_target.h" 20 #include "content/browser/devtools/devtools_protocol.h" 21 #include "content/browser/devtools/devtools_protocol_constants.h" 22 #include "content/browser/devtools/devtools_system_info_handler.h" 23 #include "content/browser/devtools/devtools_tracing_handler.h" 24 #include "content/browser/devtools/tethering_handler.h" 25 #include "content/common/devtools_messages.h" 26 #include "content/public/browser/browser_thread.h" 27 #include "content/public/browser/devtools_agent_host.h" 28 #include "content/public/browser/devtools_client_host.h" 29 #include "content/public/browser/devtools_http_handler_delegate.h" 30 #include "content/public/browser/devtools_manager.h" 31 #include "content/public/browser/devtools_target.h" 32 #include "content/public/common/content_client.h" 33 #include "content/public/common/url_constants.h" 34 #include "grit/devtools_resources_map.h" 35 #include "net/base/escape.h" 36 #include "net/base/io_buffer.h" 37 #include "net/base/ip_endpoint.h" 38 #include "net/server/http_server_request_info.h" 39 #include "net/server/http_server_response_info.h" 40 #include "webkit/common/user_agent/user_agent.h" 41 #include "webkit/common/user_agent/user_agent_util.h" 42 43 #if defined(OS_ANDROID) 44 #include "base/android/build_info.h" 45 #endif 46 47 namespace content { 48 49 namespace { 50 51 const char kProtocolVersion[] = "1.0"; 52 53 const char kDevToolsHandlerThreadName[] = "Chrome_DevToolsHandlerThread"; 54 55 const char kThumbUrlPrefix[] = "/thumb/"; 56 const char kPageUrlPrefix[] = "/devtools/page/"; 57 58 const char kTargetIdField[] = "id"; 59 const char kTargetTypeField[] = "type"; 60 const char kTargetTitleField[] = "title"; 61 const char kTargetDescriptionField[] = "description"; 62 const char kTargetUrlField[] = "url"; 63 const char kTargetThumbnailUrlField[] = "thumbnailUrl"; 64 const char kTargetFaviconUrlField[] = "faviconUrl"; 65 const char kTargetWebSocketDebuggerUrlField[] = "webSocketDebuggerUrl"; 66 const char kTargetDevtoolsFrontendUrlField[] = "devtoolsFrontendUrl"; 67 68 // An internal implementation of DevToolsClientHost that delegates 69 // messages sent for DevToolsClient to a DebuggerShell instance. 70 class DevToolsClientHostImpl : public DevToolsClientHost { 71 public: 72 DevToolsClientHostImpl(base::MessageLoop* message_loop, 73 net::HttpServer* server, 74 int connection_id) 75 : message_loop_(message_loop), 76 server_(server), 77 connection_id_(connection_id), 78 is_closed_(false), 79 detach_reason_("target_closed") {} 80 81 virtual ~DevToolsClientHostImpl() {} 82 83 // DevToolsClientHost interface 84 virtual void InspectedContentsClosing() OVERRIDE { 85 if (is_closed_) 86 return; 87 is_closed_ = true; 88 89 base::DictionaryValue notification; 90 notification.SetString( 91 devtools::Inspector::detached::kParamReason, detach_reason_); 92 std::string response = DevToolsProtocol::CreateNotification( 93 devtools::Inspector::detached::kName, 94 notification.DeepCopy())->Serialize(); 95 message_loop_->PostTask( 96 FROM_HERE, 97 base::Bind(&net::HttpServer::SendOverWebSocket, 98 server_, 99 connection_id_, 100 response)); 101 102 message_loop_->PostTask( 103 FROM_HERE, 104 base::Bind(&net::HttpServer::Close, server_, connection_id_)); 105 } 106 107 virtual void DispatchOnInspectorFrontend(const std::string& data) OVERRIDE { 108 message_loop_->PostTask( 109 FROM_HERE, 110 base::Bind(&net::HttpServer::SendOverWebSocket, 111 server_, 112 connection_id_, 113 data)); 114 } 115 116 virtual void ReplacedWithAnotherClient() OVERRIDE { 117 detach_reason_ = "replaced_with_devtools"; 118 } 119 120 private: 121 base::MessageLoop* message_loop_; 122 net::HttpServer* server_; 123 int connection_id_; 124 bool is_closed_; 125 std::string detach_reason_; 126 }; 127 128 static bool TimeComparator(const DevToolsTarget* target1, 129 const DevToolsTarget* target2) { 130 return target1->GetLastActivityTime() > target2->GetLastActivityTime(); 131 } 132 133 } // namespace 134 135 // static 136 bool DevToolsHttpHandler::IsSupportedProtocolVersion( 137 const std::string& version) { 138 return version == kProtocolVersion; 139 } 140 141 // static 142 int DevToolsHttpHandler::GetFrontendResourceId(const std::string& name) { 143 for (size_t i = 0; i < kDevtoolsResourcesSize; ++i) { 144 if (name == kDevtoolsResources[i].name) 145 return kDevtoolsResources[i].value; 146 } 147 return -1; 148 } 149 150 // static 151 DevToolsHttpHandler* DevToolsHttpHandler::Start( 152 const net::StreamListenSocketFactory* socket_factory, 153 const std::string& frontend_url, 154 DevToolsHttpHandlerDelegate* delegate) { 155 DevToolsHttpHandlerImpl* http_handler = 156 new DevToolsHttpHandlerImpl(socket_factory, 157 frontend_url, 158 delegate); 159 http_handler->Start(); 160 return http_handler; 161 } 162 163 DevToolsHttpHandlerImpl::~DevToolsHttpHandlerImpl() { 164 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 165 // Stop() must be called prior to destruction. 166 DCHECK(server_.get() == NULL); 167 DCHECK(thread_.get() == NULL); 168 STLDeleteValues(&target_map_); 169 } 170 171 void DevToolsHttpHandlerImpl::Start() { 172 if (thread_) 173 return; 174 thread_.reset(new base::Thread(kDevToolsHandlerThreadName)); 175 BrowserThread::PostTask( 176 BrowserThread::FILE, FROM_HERE, 177 base::Bind(&DevToolsHttpHandlerImpl::StartHandlerThread, this)); 178 } 179 180 // Runs on FILE thread. 181 void DevToolsHttpHandlerImpl::StartHandlerThread() { 182 base::Thread::Options options; 183 options.message_loop_type = base::MessageLoop::TYPE_IO; 184 if (!thread_->StartWithOptions(options)) { 185 BrowserThread::PostTask( 186 BrowserThread::UI, FROM_HERE, 187 base::Bind(&DevToolsHttpHandlerImpl::ResetHandlerThread, this)); 188 return; 189 } 190 191 thread_->message_loop()->PostTask( 192 FROM_HERE, 193 base::Bind(&DevToolsHttpHandlerImpl::Init, this)); 194 } 195 196 void DevToolsHttpHandlerImpl::ResetHandlerThread() { 197 thread_.reset(); 198 } 199 200 void DevToolsHttpHandlerImpl::ResetHandlerThreadAndRelease() { 201 ResetHandlerThread(); 202 Release(); 203 } 204 205 void DevToolsHttpHandlerImpl::Stop() { 206 if (!thread_) 207 return; 208 BrowserThread::PostTaskAndReply( 209 BrowserThread::FILE, FROM_HERE, 210 base::Bind(&DevToolsHttpHandlerImpl::StopHandlerThread, this), 211 base::Bind(&DevToolsHttpHandlerImpl::ResetHandlerThreadAndRelease, this)); 212 } 213 214 GURL DevToolsHttpHandlerImpl::GetFrontendURL() { 215 net::IPEndPoint ip_address; 216 if (server_->GetLocalAddress(&ip_address)) 217 return GURL(); 218 return GURL(std::string("http://") + ip_address.ToString() + 219 overridden_frontend_url_); 220 } 221 222 static std::string PathWithoutParams(const std::string& path) { 223 size_t query_position = path.find("?"); 224 if (query_position != std::string::npos) 225 return path.substr(0, query_position); 226 return path; 227 } 228 229 static std::string GetMimeType(const std::string& filename) { 230 if (EndsWith(filename, ".html", false)) { 231 return "text/html"; 232 } else if (EndsWith(filename, ".css", false)) { 233 return "text/css"; 234 } else if (EndsWith(filename, ".js", false)) { 235 return "application/javascript"; 236 } else if (EndsWith(filename, ".png", false)) { 237 return "image/png"; 238 } else if (EndsWith(filename, ".gif", false)) { 239 return "image/gif"; 240 } 241 NOTREACHED(); 242 return "text/plain"; 243 } 244 245 void DevToolsHttpHandlerImpl::OnHttpRequest( 246 int connection_id, 247 const net::HttpServerRequestInfo& info) { 248 if (info.path.find("/json") == 0) { 249 BrowserThread::PostTask( 250 BrowserThread::UI, 251 FROM_HERE, 252 base::Bind(&DevToolsHttpHandlerImpl::OnJsonRequestUI, 253 this, 254 connection_id, 255 info)); 256 return; 257 } 258 259 if (info.path.find(kThumbUrlPrefix) == 0) { 260 // Thumbnail request. 261 const std::string target_id = info.path.substr(strlen(kThumbUrlPrefix)); 262 DevToolsTarget* target = GetTarget(target_id); 263 GURL page_url; 264 if (target) 265 page_url = target->GetUrl(); 266 BrowserThread::PostTask( 267 BrowserThread::UI, 268 FROM_HERE, 269 base::Bind(&DevToolsHttpHandlerImpl::OnThumbnailRequestUI, 270 this, 271 connection_id, 272 page_url)); 273 return; 274 } 275 276 if (info.path == "" || info.path == "/") { 277 // Discovery page request. 278 BrowserThread::PostTask( 279 BrowserThread::UI, 280 FROM_HERE, 281 base::Bind(&DevToolsHttpHandlerImpl::OnDiscoveryPageRequestUI, 282 this, 283 connection_id)); 284 return; 285 } 286 287 if (info.path.find("/devtools/") != 0) { 288 server_->Send404(connection_id); 289 return; 290 } 291 292 std::string filename = PathWithoutParams(info.path.substr(10)); 293 std::string mime_type = GetMimeType(filename); 294 295 base::FilePath frontend_dir = delegate_->GetDebugFrontendDir(); 296 if (!frontend_dir.empty()) { 297 base::FilePath path = frontend_dir.AppendASCII(filename); 298 std::string data; 299 base::ReadFileToString(path, &data); 300 server_->Send200(connection_id, data, mime_type); 301 return; 302 } 303 if (delegate_->BundlesFrontendResources()) { 304 int resource_id = DevToolsHttpHandler::GetFrontendResourceId(filename); 305 if (resource_id != -1) { 306 base::StringPiece data = GetContentClient()->GetDataResource( 307 resource_id, ui::SCALE_FACTOR_NONE); 308 server_->Send200(connection_id, data.as_string(), mime_type); 309 return; 310 } 311 } 312 server_->Send404(connection_id); 313 } 314 315 void DevToolsHttpHandlerImpl::OnWebSocketRequest( 316 int connection_id, 317 const net::HttpServerRequestInfo& request) { 318 std::string browser_prefix = "/devtools/browser"; 319 size_t browser_pos = request.path.find(browser_prefix); 320 if (browser_pos == 0) { 321 if (browser_target_) { 322 server_->Send500(connection_id, "Another client already attached"); 323 return; 324 } 325 browser_target_ = new DevToolsBrowserTarget( 326 thread_->message_loop_proxy().get(), server_.get(), connection_id); 327 browser_target_->RegisterDomainHandler( 328 devtools::Tracing::kName, 329 new DevToolsTracingHandler(), 330 true /* handle on UI thread */); 331 browser_target_->RegisterDomainHandler( 332 TetheringHandler::kDomain, 333 new TetheringHandler(delegate_.get()), 334 false /* handle on this thread */); 335 browser_target_->RegisterDomainHandler( 336 devtools::SystemInfo::kName, 337 new DevToolsSystemInfoHandler(), 338 true /* handle on UI thread */); 339 340 server_->AcceptWebSocket(connection_id, request); 341 return; 342 } 343 344 BrowserThread::PostTask( 345 BrowserThread::UI, 346 FROM_HERE, 347 base::Bind( 348 &DevToolsHttpHandlerImpl::OnWebSocketRequestUI, 349 this, 350 connection_id, 351 request)); 352 } 353 354 void DevToolsHttpHandlerImpl::OnWebSocketMessage( 355 int connection_id, 356 const std::string& data) { 357 if (browser_target_ && connection_id == browser_target_->connection_id()) { 358 browser_target_->HandleMessage(data); 359 return; 360 } 361 362 BrowserThread::PostTask( 363 BrowserThread::UI, 364 FROM_HERE, 365 base::Bind( 366 &DevToolsHttpHandlerImpl::OnWebSocketMessageUI, 367 this, 368 connection_id, 369 data)); 370 } 371 372 void DevToolsHttpHandlerImpl::OnClose(int connection_id) { 373 if (browser_target_ && browser_target_->connection_id() == connection_id) { 374 browser_target_->Detach(); 375 browser_target_ = NULL; 376 return; 377 } 378 379 BrowserThread::PostTask( 380 BrowserThread::UI, 381 FROM_HERE, 382 base::Bind( 383 &DevToolsHttpHandlerImpl::OnCloseUI, 384 this, 385 connection_id)); 386 } 387 388 std::string DevToolsHttpHandlerImpl::GetFrontendURLInternal( 389 const std::string id, 390 const std::string& host) { 391 return base::StringPrintf( 392 "%s%sws=%s%s%s", 393 overridden_frontend_url_.c_str(), 394 overridden_frontend_url_.find("?") == std::string::npos ? "?" : "&", 395 host.c_str(), 396 kPageUrlPrefix, 397 id.c_str()); 398 } 399 400 static bool ParseJsonPath( 401 const std::string& path, 402 std::string* command, 403 std::string* target_id) { 404 405 // Fall back to list in case of empty query. 406 if (path.empty()) { 407 *command = "list"; 408 return true; 409 } 410 411 if (path.find("/") != 0) { 412 // Malformed command. 413 return false; 414 } 415 *command = path.substr(1); 416 417 size_t separator_pos = command->find("/"); 418 if (separator_pos != std::string::npos) { 419 *target_id = command->substr(separator_pos + 1); 420 *command = command->substr(0, separator_pos); 421 } 422 return true; 423 } 424 425 void DevToolsHttpHandlerImpl::OnJsonRequestUI( 426 int connection_id, 427 const net::HttpServerRequestInfo& info) { 428 // Trim /json 429 std::string path = info.path.substr(5); 430 431 // Trim fragment and query 432 std::string query; 433 size_t query_pos = path.find("?"); 434 if (query_pos != std::string::npos) { 435 query = path.substr(query_pos + 1); 436 path = path.substr(0, query_pos); 437 } 438 439 size_t fragment_pos = path.find("#"); 440 if (fragment_pos != std::string::npos) 441 path = path.substr(0, fragment_pos); 442 443 std::string command; 444 std::string target_id; 445 if (!ParseJsonPath(path, &command, &target_id)) { 446 SendJson(connection_id, 447 net::HTTP_NOT_FOUND, 448 NULL, 449 "Malformed query: " + info.path); 450 return; 451 } 452 453 if (command == "version") { 454 base::DictionaryValue version; 455 version.SetString("Protocol-Version", kProtocolVersion); 456 version.SetString("WebKit-Version", webkit_glue::GetWebKitVersion()); 457 version.SetString("Browser", content::GetContentClient()->GetProduct()); 458 version.SetString("User-Agent", 459 webkit_glue::GetUserAgent(GURL(kAboutBlankURL))); 460 #if defined(OS_ANDROID) 461 version.SetString("Android-Package", 462 base::android::BuildInfo::GetInstance()->package_name()); 463 #endif 464 SendJson(connection_id, net::HTTP_OK, &version, std::string()); 465 return; 466 } 467 468 if (command == "list") { 469 std::string host = info.headers["host"]; 470 AddRef(); // Balanced in OnTargetListReceived. 471 delegate_->EnumerateTargets( 472 base::Bind(&DevToolsHttpHandlerImpl::OnTargetListReceived, 473 this, connection_id, host)); 474 return; 475 } 476 477 if (command == "new") { 478 GURL url(net::UnescapeURLComponent( 479 query, net::UnescapeRule::URL_SPECIAL_CHARS)); 480 if (!url.is_valid()) 481 url = GURL(kAboutBlankURL); 482 scoped_ptr<DevToolsTarget> target(delegate_->CreateNewTarget(url)); 483 if (!target) { 484 SendJson(connection_id, 485 net::HTTP_INTERNAL_SERVER_ERROR, 486 NULL, 487 "Could not create new page"); 488 return; 489 } 490 std::string host = info.headers["host"]; 491 scoped_ptr<base::DictionaryValue> dictionary( 492 SerializeTarget(*target.get(), host)); 493 SendJson(connection_id, net::HTTP_OK, dictionary.get(), std::string()); 494 const std::string target_id = target->GetId(); 495 target_map_[target_id] = target.release(); 496 return; 497 } 498 499 if (command == "activate" || command == "close") { 500 DevToolsTarget* target = GetTarget(target_id); 501 if (!target) { 502 SendJson(connection_id, 503 net::HTTP_NOT_FOUND, 504 NULL, 505 "No such target id: " + target_id); 506 return; 507 } 508 509 if (command == "activate") { 510 if (target->Activate()) { 511 SendJson(connection_id, net::HTTP_OK, NULL, "Target activated"); 512 } else { 513 SendJson(connection_id, 514 net::HTTP_INTERNAL_SERVER_ERROR, 515 NULL, 516 "Could not activate target id: " + target_id); 517 } 518 return; 519 } 520 521 if (command == "close") { 522 if (target->Close()) { 523 SendJson(connection_id, net::HTTP_OK, NULL, "Target is closing"); 524 } else { 525 SendJson(connection_id, 526 net::HTTP_INTERNAL_SERVER_ERROR, 527 NULL, 528 "Could not close target id: " + target_id); 529 } 530 return; 531 } 532 } 533 SendJson(connection_id, 534 net::HTTP_NOT_FOUND, 535 NULL, 536 "Unknown command: " + command); 537 return; 538 } 539 540 void DevToolsHttpHandlerImpl::OnTargetListReceived( 541 int connection_id, 542 const std::string& host, 543 const DevToolsHttpHandlerDelegate::TargetList& targets) { 544 DevToolsHttpHandlerDelegate::TargetList sorted_targets = targets; 545 std::sort(sorted_targets.begin(), sorted_targets.end(), TimeComparator); 546 547 STLDeleteValues(&target_map_); 548 base::ListValue list_value; 549 for (DevToolsHttpHandlerDelegate::TargetList::const_iterator it = 550 sorted_targets.begin(); it != sorted_targets.end(); ++it) { 551 DevToolsTarget* target = *it; 552 target_map_[target->GetId()] = target; 553 list_value.Append(SerializeTarget(*target, host)); 554 } 555 SendJson(connection_id, net::HTTP_OK, &list_value, std::string()); 556 Release(); // Balanced in OnJsonRequestUI. 557 } 558 559 DevToolsTarget* DevToolsHttpHandlerImpl::GetTarget(const std::string& id) { 560 TargetMap::const_iterator it = target_map_.find(id); 561 if (it == target_map_.end()) 562 return NULL; 563 return it->second; 564 } 565 566 void DevToolsHttpHandlerImpl::OnThumbnailRequestUI( 567 int connection_id, const GURL& page_url) { 568 std::string data = delegate_->GetPageThumbnailData(page_url); 569 if (!data.empty()) 570 Send200(connection_id, data, "image/png"); 571 else 572 Send404(connection_id); 573 } 574 575 void DevToolsHttpHandlerImpl::OnDiscoveryPageRequestUI(int connection_id) { 576 std::string response = delegate_->GetDiscoveryPageHTML(); 577 Send200(connection_id, response, "text/html; charset=UTF-8"); 578 } 579 580 void DevToolsHttpHandlerImpl::OnWebSocketRequestUI( 581 int connection_id, 582 const net::HttpServerRequestInfo& request) { 583 if (!thread_) 584 return; 585 586 size_t pos = request.path.find(kPageUrlPrefix); 587 if (pos != 0) { 588 Send404(connection_id); 589 return; 590 } 591 592 std::string page_id = request.path.substr(strlen(kPageUrlPrefix)); 593 DevToolsTarget* target = GetTarget(page_id); 594 scoped_refptr<DevToolsAgentHost> agent = 595 target ? target->GetAgentHost() : NULL; 596 if (!agent) { 597 Send500(connection_id, "No such target id: " + page_id); 598 return; 599 } 600 601 if (agent->IsAttached()) { 602 Send500(connection_id, 603 "Target with given id is being inspected: " + page_id); 604 return; 605 } 606 607 DevToolsClientHostImpl* client_host = new DevToolsClientHostImpl( 608 thread_->message_loop(), server_.get(), connection_id); 609 connection_to_client_host_ui_[connection_id] = client_host; 610 611 DevToolsManager::GetInstance()-> 612 RegisterDevToolsClientHostFor(agent, client_host); 613 614 AcceptWebSocket(connection_id, request); 615 } 616 617 void DevToolsHttpHandlerImpl::OnWebSocketMessageUI( 618 int connection_id, 619 const std::string& data) { 620 ConnectionToClientHostMap::iterator it = 621 connection_to_client_host_ui_.find(connection_id); 622 if (it == connection_to_client_host_ui_.end()) 623 return; 624 625 DevToolsManager* manager = DevToolsManager::GetInstance(); 626 manager->DispatchOnInspectorBackend(it->second, data); 627 } 628 629 void DevToolsHttpHandlerImpl::OnCloseUI(int connection_id) { 630 ConnectionToClientHostMap::iterator it = 631 connection_to_client_host_ui_.find(connection_id); 632 if (it != connection_to_client_host_ui_.end()) { 633 DevToolsClientHostImpl* client_host = 634 static_cast<DevToolsClientHostImpl*>(it->second); 635 DevToolsManager::GetInstance()->ClientHostClosing(client_host); 636 delete client_host; 637 connection_to_client_host_ui_.erase(connection_id); 638 } 639 } 640 641 DevToolsHttpHandlerImpl::DevToolsHttpHandlerImpl( 642 const net::StreamListenSocketFactory* socket_factory, 643 const std::string& frontend_url, 644 DevToolsHttpHandlerDelegate* delegate) 645 : overridden_frontend_url_(frontend_url), 646 socket_factory_(socket_factory), 647 delegate_(delegate) { 648 if (overridden_frontend_url_.empty()) 649 overridden_frontend_url_ = "/devtools/devtools.html"; 650 651 // Balanced in ResetHandlerThreadAndRelease(). 652 AddRef(); 653 } 654 655 // Runs on the handler thread 656 void DevToolsHttpHandlerImpl::Init() { 657 server_ = new net::HttpServer(*socket_factory_.get(), this); 658 } 659 660 // Runs on the handler thread 661 void DevToolsHttpHandlerImpl::Teardown() { 662 server_ = NULL; 663 } 664 665 // Runs on FILE thread to make sure that it is serialized against 666 // {Start|Stop}HandlerThread and to allow calling pthread_join. 667 void DevToolsHttpHandlerImpl::StopHandlerThread() { 668 if (!thread_->message_loop()) 669 return; 670 thread_->message_loop()->PostTask( 671 FROM_HERE, 672 base::Bind(&DevToolsHttpHandlerImpl::Teardown, this)); 673 // Thread::Stop joins the thread. 674 thread_->Stop(); 675 } 676 677 void DevToolsHttpHandlerImpl::SendJson(int connection_id, 678 net::HttpStatusCode status_code, 679 base::Value* value, 680 const std::string& message) { 681 if (!thread_) 682 return; 683 684 // Serialize value and message. 685 std::string json_value; 686 if (value) { 687 base::JSONWriter::WriteWithOptions(value, 688 base::JSONWriter::OPTIONS_PRETTY_PRINT, 689 &json_value); 690 } 691 std::string json_message; 692 scoped_ptr<base::Value> message_object(new base::StringValue(message)); 693 base::JSONWriter::Write(message_object.get(), &json_message); 694 695 net::HttpServerResponseInfo response(status_code); 696 response.SetBody(json_value + message, "application/json; charset=UTF-8"); 697 698 thread_->message_loop()->PostTask( 699 FROM_HERE, 700 base::Bind(&net::HttpServer::SendResponse, 701 server_.get(), 702 connection_id, 703 response)); 704 } 705 706 void DevToolsHttpHandlerImpl::Send200(int connection_id, 707 const std::string& data, 708 const std::string& mime_type) { 709 if (!thread_) 710 return; 711 thread_->message_loop()->PostTask( 712 FROM_HERE, 713 base::Bind(&net::HttpServer::Send200, 714 server_.get(), 715 connection_id, 716 data, 717 mime_type)); 718 } 719 720 void DevToolsHttpHandlerImpl::Send404(int connection_id) { 721 if (!thread_) 722 return; 723 thread_->message_loop()->PostTask( 724 FROM_HERE, 725 base::Bind(&net::HttpServer::Send404, server_.get(), connection_id)); 726 } 727 728 void DevToolsHttpHandlerImpl::Send500(int connection_id, 729 const std::string& message) { 730 if (!thread_) 731 return; 732 thread_->message_loop()->PostTask( 733 FROM_HERE, 734 base::Bind(&net::HttpServer::Send500, server_.get(), connection_id, 735 message)); 736 } 737 738 void DevToolsHttpHandlerImpl::AcceptWebSocket( 739 int connection_id, 740 const net::HttpServerRequestInfo& request) { 741 if (!thread_) 742 return; 743 thread_->message_loop()->PostTask( 744 FROM_HERE, 745 base::Bind(&net::HttpServer::AcceptWebSocket, server_.get(), 746 connection_id, request)); 747 } 748 749 base::DictionaryValue* DevToolsHttpHandlerImpl::SerializeTarget( 750 const DevToolsTarget& target, 751 const std::string& host) { 752 base::DictionaryValue* dictionary = new base::DictionaryValue; 753 754 std::string id = target.GetId(); 755 dictionary->SetString(kTargetIdField, id); 756 dictionary->SetString(kTargetTypeField, target.GetType()); 757 dictionary->SetString(kTargetTitleField, 758 net::EscapeForHTML(target.GetTitle())); 759 dictionary->SetString(kTargetDescriptionField, target.GetDescription()); 760 761 GURL url = target.GetUrl(); 762 dictionary->SetString(kTargetUrlField, url.spec()); 763 764 GURL favicon_url = target.GetFaviconUrl(); 765 if (favicon_url.is_valid()) 766 dictionary->SetString(kTargetFaviconUrlField, favicon_url.spec()); 767 768 if (!delegate_->GetPageThumbnailData(url).empty()) { 769 dictionary->SetString(kTargetThumbnailUrlField, 770 std::string(kThumbUrlPrefix) + id); 771 } 772 773 if (!target.IsAttached()) { 774 dictionary->SetString(kTargetWebSocketDebuggerUrlField, 775 base::StringPrintf("ws://%s%s%s", 776 host.c_str(), 777 kPageUrlPrefix, 778 id.c_str())); 779 std::string devtools_frontend_url = GetFrontendURLInternal( 780 id.c_str(), 781 host); 782 dictionary->SetString( 783 kTargetDevtoolsFrontendUrlField, devtools_frontend_url); 784 } 785 786 return dictionary; 787 } 788 789 } // namespace content 790