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