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