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