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