Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(26)

Side by Side Diff: chrome/browser/debugger/devtools_http_protocol_handler.cc

Issue 7274031: Wholesale move of debugger sources to content. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Add OWNERS and DEPS. Created 9 years, 5 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
(Empty)
1 // Copyright (c) 2011 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 "chrome/browser/debugger/devtools_http_protocol_handler.h"
6
7 #include <utility>
8
9 #include "base/compiler_specific.h"
10 #include "base/json/json_writer.h"
11 #include "base/logging.h"
12 #include "base/message_loop_proxy.h"
13 #include "base/stringprintf.h"
14 #include "base/string_number_conversions.h"
15 #include "base/threading/thread.h"
16 #include "base/utf_string_conversions.h"
17 #include "base/values.h"
18 #include "chrome/browser/debugger/devtools_client_host.h"
19 #include "chrome/browser/debugger/devtools_manager.h"
20 #include "chrome/browser/profiles/profile.h"
21 #include "chrome/browser/sessions/restore_tab_helper.h"
22 #include "chrome/browser/ui/tab_contents/tab_contents_wrapper.h"
23 #include "chrome/browser/ui/webui/devtools_ui.h"
24 #include "content/browser/browser_thread.h"
25 #include "content/browser/tab_contents/tab_contents.h"
26 #include "content/common/devtools_messages.h"
27 #include "grit/devtools_frontend_resources.h"
28 #include "googleurl/src/gurl.h"
29 #include "net/base/escape.h"
30 #include "net/base/io_buffer.h"
31 #include "net/server/http_server_request_info.h"
32 #include "net/url_request/url_request_context.h"
33 #include "net/url_request/url_request_context_getter.h"
34 #include "ui/base/resource/resource_bundle.h"
35
36 const int kBufferSize = 16 * 1024;
37
38 namespace {
39
40 // An internal implementation of DevToolsClientHost that delegates
41 // messages sent for DevToolsClient to a DebuggerShell instance.
42 class DevToolsClientHostImpl : public DevToolsClientHost {
43 public:
44 DevToolsClientHostImpl(
45 net::HttpServer* server,
46 int connection_id)
47 : server_(server),
48 connection_id_(connection_id) {
49 }
50 ~DevToolsClientHostImpl() {}
51
52 // DevToolsClientHost interface
53 virtual void InspectedTabClosing() {
54 BrowserThread::PostTask(
55 BrowserThread::IO,
56 FROM_HERE,
57 NewRunnableMethod(server_,
58 &net::HttpServer::Close,
59 connection_id_));
60 }
61
62 virtual void SendMessageToClient(const IPC::Message& msg) {
63 IPC_BEGIN_MESSAGE_MAP(DevToolsClientHostImpl, msg)
64 IPC_MESSAGE_HANDLER(DevToolsClientMsg_DispatchOnInspectorFrontend,
65 OnDispatchOnInspectorFrontend);
66 IPC_MESSAGE_UNHANDLED_ERROR()
67 IPC_END_MESSAGE_MAP()
68 }
69
70 virtual void TabReplaced(TabContentsWrapper* new_tab) {
71 }
72
73 void NotifyCloseListener() {
74 DevToolsClientHost::NotifyCloseListener();
75 }
76 private:
77 // Message handling routines
78 void OnDispatchOnInspectorFrontend(const std::string& data) {
79 BrowserThread::PostTask(
80 BrowserThread::IO,
81 FROM_HERE,
82 NewRunnableMethod(server_,
83 &net::HttpServer::SendOverWebSocket,
84 connection_id_,
85 data));
86 }
87
88 virtual void FrameNavigating(const std::string& url) {}
89 net::HttpServer* server_;
90 int connection_id_;
91 };
92
93 } // namespace
94
95
96 // static
97 scoped_refptr<DevToolsHttpProtocolHandler> DevToolsHttpProtocolHandler::Start(
98 const std::string& ip,
99 int port,
100 const std::string& frontend_url,
101 TabContentsProvider* provider) {
102 scoped_refptr<DevToolsHttpProtocolHandler> http_handler =
103 new DevToolsHttpProtocolHandler(ip, port, frontend_url, provider);
104 http_handler->Start();
105 return http_handler;
106 }
107
108 DevToolsHttpProtocolHandler::~DevToolsHttpProtocolHandler() {
109 // Stop() must be called prior to this being called
110 DCHECK(server_.get() == NULL);
111 }
112
113 void DevToolsHttpProtocolHandler::Start() {
114 BrowserThread::PostTask(
115 BrowserThread::IO, FROM_HERE,
116 NewRunnableMethod(this, &DevToolsHttpProtocolHandler::Init));
117 }
118
119 void DevToolsHttpProtocolHandler::Stop() {
120 BrowserThread::PostTask(
121 BrowserThread::IO, FROM_HERE,
122 NewRunnableMethod(this, &DevToolsHttpProtocolHandler::Teardown));
123 }
124
125 void DevToolsHttpProtocolHandler::OnHttpRequest(
126 int connection_id,
127 const net::HttpServerRequestInfo& info) {
128 if (info.path == "/json") {
129 // Pages discovery json request.
130 BrowserThread::PostTask(
131 BrowserThread::UI,
132 FROM_HERE,
133 NewRunnableMethod(this,
134 &DevToolsHttpProtocolHandler::OnJsonRequestUI,
135 connection_id,
136 info));
137 return;
138 }
139
140 // Proxy static files from chrome-devtools://devtools/*.
141 if (!Profile::GetDefaultRequestContext()) {
142 server_->Send404(connection_id);
143 return;
144 }
145
146 if (info.path == "" || info.path == "/") {
147 const base::StringPiece frontend_html(
148 ResourceBundle::GetSharedInstance().GetRawDataResource(
149 IDR_DEVTOOLS_FRONTEND_HTML));
150 std::string response(frontend_html.data(), frontend_html.length());
151 server_->Send200(connection_id, response, "text/html; charset=UTF-8");
152 return;
153 }
154
155 net::URLRequest* request;
156
157 if (info.path.find("/devtools/") == 0) {
158 request = new net::URLRequest(GURL("chrome-devtools:/" + info.path), this);
159 } else if (info.path.find("/thumb/") == 0) {
160 request = new net::URLRequest(GURL("chrome:/" + info.path), this);
161 } else {
162 server_->Send404(connection_id);
163 return;
164 }
165
166 // Make sure DevTools data source is registered.
167 DevToolsUI::RegisterDevToolsDataSource();
168 Bind(request, connection_id);
169 request->set_context(
170 Profile::GetDefaultRequestContext()->GetURLRequestContext());
171 request->Start();
172 }
173
174 void DevToolsHttpProtocolHandler::OnWebSocketRequest(
175 int connection_id,
176 const net::HttpServerRequestInfo& request) {
177 BrowserThread::PostTask(
178 BrowserThread::UI,
179 FROM_HERE,
180 NewRunnableMethod(
181 this,
182 &DevToolsHttpProtocolHandler::OnWebSocketRequestUI,
183 connection_id,
184 request));
185 }
186
187 void DevToolsHttpProtocolHandler::OnWebSocketMessage(
188 int connection_id,
189 const std::string& data) {
190 BrowserThread::PostTask(
191 BrowserThread::UI,
192 FROM_HERE,
193 NewRunnableMethod(
194 this,
195 &DevToolsHttpProtocolHandler::OnWebSocketMessageUI,
196 connection_id,
197 data));
198 }
199
200 void DevToolsHttpProtocolHandler::OnClose(int connection_id) {
201 ConnectionToRequestsMap::iterator it =
202 connection_to_requests_io_.find(connection_id);
203 if (it != connection_to_requests_io_.end()) {
204 // Dispose delegating socket.
205 for (std::set<net::URLRequest*>::iterator it2 = it->second.begin();
206 it2 != it->second.end(); ++it2) {
207 net::URLRequest* request = *it2;
208 request->Cancel();
209 request_to_connection_io_.erase(request);
210 request_to_buffer_io_.erase(request);
211 delete request;
212 }
213 connection_to_requests_io_.erase(connection_id);
214 }
215
216 BrowserThread::PostTask(
217 BrowserThread::UI,
218 FROM_HERE,
219 NewRunnableMethod(
220 this,
221 &DevToolsHttpProtocolHandler::OnCloseUI,
222 connection_id));
223 }
224
225 struct PageInfo
226 {
227 int id;
228 std::string url;
229 bool attached;
230 std::string title;
231 std::string thumbnail_url;
232 std::string favicon_url;
233 };
234 typedef std::vector<PageInfo> PageList;
235
236 static PageList GeneratePageList(
237 DevToolsHttpProtocolHandler::TabContentsProvider* tab_contents_provider,
238 int connection_id,
239 const net::HttpServerRequestInfo& info) {
240 typedef DevToolsHttpProtocolHandler::InspectableTabs Tabs;
241 Tabs inspectable_tabs = tab_contents_provider->GetInspectableTabs();
242
243 PageList page_list;
244 for (Tabs::iterator it = inspectable_tabs.begin();
245 it != inspectable_tabs.end(); ++it) {
246
247 TabContentsWrapper* tab_contents = *it;
248 NavigationController& controller = tab_contents->controller();
249
250 NavigationEntry* entry = controller.GetActiveEntry();
251 if (entry == NULL || !entry->url().is_valid())
252 continue;
253
254 DevToolsClientHost* client_host = DevToolsManager::GetInstance()->
255 GetDevToolsClientHostFor(tab_contents->tab_contents()->
256 render_view_host());
257 PageInfo page_info;
258 page_info.id = tab_contents->restore_tab_helper()->session_id().id();
259 page_info.attached = client_host != NULL;
260 page_info.url = entry->url().spec();
261 page_info.title = UTF16ToUTF8(EscapeForHTML(entry->title()));
262 page_info.thumbnail_url = "/thumb/" + entry->url().spec();
263 page_info.favicon_url = entry->favicon().url().spec();
264 page_list.push_back(page_info);
265 }
266 return page_list;
267 }
268
269 void DevToolsHttpProtocolHandler::OnJsonRequestUI(
270 int connection_id,
271 const net::HttpServerRequestInfo& info) {
272 PageList page_list = GeneratePageList(tab_contents_provider_.get(),
273 connection_id, info);
274 ListValue json_pages_list;
275 std::string host = info.headers["Host"];
276 for (PageList::iterator i = page_list.begin();
277 i != page_list.end(); ++i) {
278
279 DictionaryValue* page_info = new DictionaryValue;
280 json_pages_list.Append(page_info);
281 page_info->SetString("title", i->title);
282 page_info->SetString("url", i->url);
283 page_info->SetString("thumbnailUrl", i->thumbnail_url);
284 page_info->SetString("faviconUrl", i->favicon_url);
285 if (!i->attached) {
286 page_info->SetString("webSocketDebuggerUrl",
287 StringPrintf("ws://%s/devtools/page/%d",
288 host.c_str(),
289 i->id));
290 page_info->SetString("devtoolsFrontendUrl",
291 StringPrintf("%s?host=%s&page=%d",
292 overridden_frontend_url_.c_str(),
293 host.c_str(),
294 i->id));
295 }
296 }
297
298 std::string response;
299 base::JSONWriter::Write(&json_pages_list, true, &response);
300 Send200(connection_id, response, "application/json; charset=UTF-8");
301 }
302
303 void DevToolsHttpProtocolHandler::OnWebSocketRequestUI(
304 int connection_id,
305 const net::HttpServerRequestInfo& request) {
306 std::string prefix = "/devtools/page/";
307 size_t pos = request.path.find(prefix);
308 if (pos != 0) {
309 Send404(connection_id);
310 return;
311 }
312 std::string page_id = request.path.substr(prefix.length());
313 int id = 0;
314 if (!base::StringToInt(page_id, &id)) {
315 Send500(connection_id, "Invalid page id: " + page_id);
316 return;
317 }
318
319 TabContents* tab_contents = GetTabContents(id);
320 if (tab_contents == NULL) {
321 Send500(connection_id, "No such page id: " + page_id);
322 return;
323 }
324
325 DevToolsManager* manager = DevToolsManager::GetInstance();
326 if (manager->GetDevToolsClientHostFor(tab_contents->render_view_host())) {
327 Send500(connection_id, "Page with given id is being inspected: " + page_id);
328 return;
329 }
330
331 DevToolsClientHostImpl* client_host =
332 new DevToolsClientHostImpl(server_, connection_id);
333 connection_to_client_host_ui_[connection_id] = client_host;
334
335 manager->RegisterDevToolsClientHostFor(
336 tab_contents->render_view_host(),
337 client_host);
338 manager->ForwardToDevToolsAgent(
339 client_host,
340 DevToolsAgentMsg_FrontendLoaded());
341
342 AcceptWebSocket(connection_id, request);
343 }
344
345 void DevToolsHttpProtocolHandler::OnWebSocketMessageUI(
346 int connection_id,
347 const std::string& data) {
348 ConnectionToClientHostMap::iterator it =
349 connection_to_client_host_ui_.find(connection_id);
350 if (it == connection_to_client_host_ui_.end())
351 return;
352
353 DevToolsManager* manager = DevToolsManager::GetInstance();
354 manager->ForwardToDevToolsAgent(
355 it->second,
356 DevToolsAgentMsg_DispatchOnInspectorBackend(data));
357 }
358
359 void DevToolsHttpProtocolHandler::OnCloseUI(int connection_id) {
360 ConnectionToClientHostMap::iterator it =
361 connection_to_client_host_ui_.find(connection_id);
362 if (it != connection_to_client_host_ui_.end()) {
363 DevToolsClientHostImpl* client_host =
364 static_cast<DevToolsClientHostImpl*>(it->second);
365 client_host->NotifyCloseListener();
366 delete client_host;
367 connection_to_client_host_ui_.erase(connection_id);
368 }
369 }
370
371 void DevToolsHttpProtocolHandler::OnResponseStarted(net::URLRequest* request) {
372 RequestToSocketMap::iterator it = request_to_connection_io_.find(request);
373 if (it == request_to_connection_io_.end())
374 return;
375
376 int connection_id = it->second;
377
378 std::string content_type;
379 request->GetMimeType(&content_type);
380
381 if (request->status().is_success()) {
382 server_->Send(connection_id, StringPrintf("HTTP/1.1 200 OK\r\n"
383 "Content-Type:%s\r\n"
384 "Transfer-Encoding: chunked\r\n"
385 "\r\n",
386 content_type.c_str()));
387 } else {
388 server_->Send404(connection_id);
389 }
390
391 int bytes_read = 0;
392 // Some servers may treat HEAD requests as GET requests. To free up the
393 // network connection as soon as possible, signal that the request has
394 // completed immediately, without trying to read any data back (all we care
395 // about is the response code and headers, which we already have).
396 net::IOBuffer* buffer = request_to_buffer_io_[request].get();
397 if (request->status().is_success())
398 request->Read(buffer, kBufferSize, &bytes_read);
399 OnReadCompleted(request, bytes_read);
400 }
401
402 void DevToolsHttpProtocolHandler::OnReadCompleted(net::URLRequest* request,
403 int bytes_read) {
404 RequestToSocketMap::iterator it = request_to_connection_io_.find(request);
405 if (it == request_to_connection_io_.end())
406 return;
407
408 int connection_id = it->second;
409
410 net::IOBuffer* buffer = request_to_buffer_io_[request].get();
411 do {
412 if (!request->status().is_success() || bytes_read <= 0)
413 break;
414 std::string chunk_size = StringPrintf("%X\r\n", bytes_read);
415 server_->Send(connection_id, chunk_size);
416 server_->Send(connection_id, buffer->data(), bytes_read);
417 server_->Send(connection_id, "\r\n");
418 } while (request->Read(buffer, kBufferSize, &bytes_read));
419
420
421 // See comments re: HEAD requests in OnResponseStarted().
422 if (!request->status().is_io_pending()) {
423 server_->Send(connection_id, "0\r\n\r\n");
424 RequestCompleted(request);
425 }
426 }
427
428 DevToolsHttpProtocolHandler::DevToolsHttpProtocolHandler(
429 const std::string& ip,
430 int port,
431 const std::string& frontend_host,
432 TabContentsProvider* provider)
433 : ip_(ip),
434 port_(port),
435 overridden_frontend_url_(frontend_host),
436 tab_contents_provider_(provider) {
437 if (overridden_frontend_url_.empty())
438 overridden_frontend_url_ = "/devtools/devtools.html";
439 }
440
441 void DevToolsHttpProtocolHandler::Init() {
442 server_ = new net::HttpServer(ip_, port_, this);
443 }
444
445 // Run on I/O thread
446 void DevToolsHttpProtocolHandler::Teardown() {
447 server_ = NULL;
448 }
449
450 void DevToolsHttpProtocolHandler::Bind(net::URLRequest* request,
451 int connection_id) {
452 request_to_connection_io_[request] = connection_id;
453 ConnectionToRequestsMap::iterator it =
454 connection_to_requests_io_.find(connection_id);
455 if (it == connection_to_requests_io_.end()) {
456 std::pair<int, std::set<net::URLRequest*> > value(
457 connection_id,
458 std::set<net::URLRequest*>());
459 it = connection_to_requests_io_.insert(value).first;
460 }
461 it->second.insert(request);
462 request_to_buffer_io_[request] = new net::IOBuffer(kBufferSize);
463 }
464
465 void DevToolsHttpProtocolHandler::RequestCompleted(net::URLRequest* request) {
466 RequestToSocketMap::iterator it = request_to_connection_io_.find(request);
467 if (it == request_to_connection_io_.end())
468 return;
469
470 int connection_id = it->second;
471 request_to_connection_io_.erase(request);
472 ConnectionToRequestsMap::iterator it2 =
473 connection_to_requests_io_.find(connection_id);
474 it2->second.erase(request);
475 request_to_buffer_io_.erase(request);
476 delete request;
477 }
478
479 void DevToolsHttpProtocolHandler::Send200(int connection_id,
480 const std::string& data,
481 const std::string& mime_type) {
482 BrowserThread::PostTask(
483 BrowserThread::IO, FROM_HERE,
484 NewRunnableMethod(server_.get(),
485 &net::HttpServer::Send200,
486 connection_id,
487 data,
488 mime_type));
489 }
490
491 void DevToolsHttpProtocolHandler::Send404(int connection_id) {
492 BrowserThread::PostTask(
493 BrowserThread::IO, FROM_HERE,
494 NewRunnableMethod(server_.get(),
495 &net::HttpServer::Send404,
496 connection_id));
497 }
498
499 void DevToolsHttpProtocolHandler::Send500(int connection_id,
500 const std::string& message) {
501 BrowserThread::PostTask(
502 BrowserThread::IO, FROM_HERE,
503 NewRunnableMethod(server_.get(),
504 &net::HttpServer::Send500,
505 connection_id,
506 message));
507 }
508
509 void DevToolsHttpProtocolHandler::AcceptWebSocket(
510 int connection_id,
511 const net::HttpServerRequestInfo& request) {
512 BrowserThread::PostTask(
513 BrowserThread::IO, FROM_HERE,
514 NewRunnableMethod(server_.get(),
515 &net::HttpServer::AcceptWebSocket,
516 connection_id,
517 request));
518 }
519
520 TabContents* DevToolsHttpProtocolHandler::GetTabContents(int session_id) {
521 InspectableTabs inspectable_tabs =
522 tab_contents_provider_->GetInspectableTabs();
523
524 for (InspectableTabs::iterator it = inspectable_tabs.begin();
525 it != inspectable_tabs.end(); ++it) {
526 TabContentsWrapper* tab = *it;
527 if (tab->restore_tab_helper()->session_id().id() == session_id)
528 return tab->tab_contents();
529 }
530 return NULL;
531 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698