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

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

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

Powered by Google App Engine
This is Rietveld 408576698