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