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