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