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