// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "content/browser/devtools/devtools_http_handler_impl.h"

#include <algorithm>
#include <utility>

#include "base/bind.h"
#include "base/compiler_specific.h"
#include "base/file_util.h"
#include "base/json/json_writer.h"
#include "base/lazy_instance.h"
#include "base/logging.h"
#include "base/message_loop_proxy.h"
#include "base/string_number_conversions.h"
#include "base/stringprintf.h"
#include "base/threading/thread.h"
#include "base/utf_string_conversions.h"
#include "base/values.h"
#include "content/browser/devtools/devtools_browser_target.h"
#include "content/browser/devtools/devtools_tracing_handler.h"
#include "content/browser/web_contents/web_contents_impl.h"
#include "content/common/devtools_messages.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/browser/devtools_agent_host_registry.h"
#include "content/public/browser/devtools_client_host.h"
#include "content/public/browser/devtools_http_handler_delegate.h"
#include "content/public/browser/devtools_manager.h"
#include "content/public/browser/favicon_status.h"
#include "content/public/browser/navigation_entry.h"
#include "content/public/browser/notification_service.h"
#include "content/public/browser/notification_types.h"
#include "content/public/browser/render_process_host.h"
#include "content/public/browser/render_view_host.h"
#include "content/public/browser/render_widget_host.h"
#include "content/public/common/content_client.h"
#include "content/public/common/url_constants.h"
#include "googleurl/src/gurl.h"
#include "grit/devtools_resources_map.h"
#include "net/base/escape.h"
#include "net/base/io_buffer.h"
#include "net/base/ip_endpoint.h"
#include "net/server/http_server_request_info.h"
#include "third_party/WebKit/Source/WebKit/chromium/public/WebDevToolsAgent.h"
#include "third_party/WebKit/Source/WebKit/chromium/public/platform/WebString.h"
#include "ui/base/layout.h"
#include "webkit/user_agent/user_agent.h"
#include "webkit/user_agent/user_agent_util.h"

namespace content {

const int kBufferSize = 16 * 1024;

namespace {

static const char* kDevToolsHandlerThreadName = "Chrome_DevToolsHandlerThread";

class DevToolsDefaultBindingHandler
    : public DevToolsHttpHandler::RenderViewHostBinding {
 public:
  DevToolsDefaultBindingHandler() {
  }

  virtual std::string GetIdentifier(RenderViewHost* rvh) OVERRIDE {
    int process_id = rvh->GetProcess()->GetID();
    int routing_id = rvh->GetRoutingID();
    return base::StringPrintf("%d_%d", process_id, routing_id);
  }

  virtual RenderViewHost* ForIdentifier(
      const std::string& identifier) OVERRIDE {
    size_t pos = identifier.find("_");
    if (pos == std::string::npos)
      return NULL;

    int process_id;
    if (!base::StringToInt(identifier.substr(0, pos), &process_id))
      return NULL;

    int routing_id;
    if (!base::StringToInt(identifier.substr(pos+1), &routing_id))
      return NULL;

    return RenderViewHost::FromID(process_id, routing_id);
  }
};


// An internal implementation of DevToolsClientHost that delegates
// messages sent for DevToolsClient to a DebuggerShell instance.
class DevToolsClientHostImpl : public DevToolsClientHost {
 public:
  DevToolsClientHostImpl(
      MessageLoop* message_loop,
      net::HttpServer* server,
      int connection_id)
      : message_loop_(message_loop),
        server_(server),
        connection_id_(connection_id),
        is_closed_(false),
        detach_reason_("target_closed") {
  }

  ~DevToolsClientHostImpl() {}

  // DevToolsClientHost interface
  virtual void InspectedContentsClosing() {
    if (is_closed_)
      return;
    is_closed_ = true;

    std::string response =
        WebKit::WebDevToolsAgent::inspectorDetachedEvent(
            WebKit::WebString::fromUTF8(detach_reason_)).utf8();
    message_loop_->PostTask(
        FROM_HERE,
        base::Bind(&net::HttpServer::SendOverWebSocket,
                   server_,
                   connection_id_,
                   response));

    message_loop_->PostTask(
        FROM_HERE,
        base::Bind(&net::HttpServer::Close, server_, connection_id_));
  }

  virtual void DispatchOnInspectorFrontend(const std::string& data) {
    message_loop_->PostTask(
        FROM_HERE,
        base::Bind(&net::HttpServer::SendOverWebSocket,
                   server_,
                   connection_id_,
                   data));
  }

  virtual void ContentsReplaced(WebContents* new_contents) {
  }

  virtual void ReplacedWithAnotherClient() {
    detach_reason_ = "replaced_with_devtools";
  }

 private:
  virtual void FrameNavigating(const std::string& url) {}
  MessageLoop* message_loop_;
  net::HttpServer* server_;
  int connection_id_;
  bool is_closed_;
  std::string detach_reason_;
};

}  // namespace

// static
int DevToolsHttpHandler::GetFrontendResourceId(const std::string& name) {
  for (size_t i = 0; i < kDevtoolsResourcesSize; ++i) {
    if (name == kDevtoolsResources[i].name)
      return kDevtoolsResources[i].value;
  }
  return -1;
}

// static
DevToolsHttpHandler* DevToolsHttpHandler::Start(
    const net::StreamListenSocketFactory* socket_factory,
    const std::string& frontend_url,
    DevToolsHttpHandlerDelegate* delegate) {
  DevToolsHttpHandlerImpl* http_handler =
      new DevToolsHttpHandlerImpl(socket_factory,
                                  frontend_url,
                                  delegate);
  http_handler->Start();
  return http_handler;
}

DevToolsHttpHandlerImpl::~DevToolsHttpHandlerImpl() {
  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
  // Stop() must be called prior to destruction.
  DCHECK(server_.get() == NULL);
  DCHECK(thread_.get() == NULL);
}

void DevToolsHttpHandlerImpl::Start() {
  if (thread_.get())
    return;
  thread_.reset(new base::Thread(kDevToolsHandlerThreadName));
  BrowserThread::PostTask(
      BrowserThread::FILE, FROM_HERE,
      base::Bind(&DevToolsHttpHandlerImpl::StartHandlerThread, this));
}

// Runs on FILE thread.
void DevToolsHttpHandlerImpl::StartHandlerThread() {
  base::Thread::Options options;
  options.message_loop_type = MessageLoop::TYPE_IO;
  if (!thread_->StartWithOptions(options)) {
    BrowserThread::PostTask(
        BrowserThread::UI, FROM_HERE,
        base::Bind(&DevToolsHttpHandlerImpl::ResetHandlerThread, this));
    return;
  }

  thread_->message_loop()->PostTask(
      FROM_HERE,
      base::Bind(&DevToolsHttpHandlerImpl::Init, this));
}

void DevToolsHttpHandlerImpl::ResetHandlerThread() {
  thread_.reset();
}

void DevToolsHttpHandlerImpl::ResetHandlerThreadAndRelease() {
  ResetHandlerThread();
  Release();
}

void DevToolsHttpHandlerImpl::Stop() {
  if (!thread_.get())
    return;
  BrowserThread::PostTaskAndReply(
      BrowserThread::FILE, FROM_HERE,
      base::Bind(&DevToolsHttpHandlerImpl::StopHandlerThread, this),
      base::Bind(&DevToolsHttpHandlerImpl::ResetHandlerThreadAndRelease, this));
}

void DevToolsHttpHandlerImpl::SetRenderViewHostBinding(
    RenderViewHostBinding* binding) {
  if (binding)
    binding_ = binding;
  else
    binding_ = default_binding_.get();
}

GURL DevToolsHttpHandlerImpl::GetFrontendURL(RenderViewHost* render_view_host) {
  net::IPEndPoint ip_address;
  if (server_->GetLocalAddress(&ip_address))
    return GURL();
  std::string host = ip_address.ToString();
  std::string id = binding_->GetIdentifier(render_view_host);
  return GURL(std::string("http://") +
              ip_address.ToString() +
              GetFrontendURLInternal(id, host));
}

static std::string PathWithoutParams(const std::string& path) {
  size_t query_position = path.find("?");
  if (query_position != std::string::npos)
    return path.substr(0, query_position);
  return path;
}

static std::string GetMimeType(const std::string& filename) {
  if (EndsWith(filename, ".html", false)) {
    return "text/html";
  } else if (EndsWith(filename, ".css", false)) {
    return "text/css";
  } else if (EndsWith(filename, ".js", false)) {
    return "application/javascript";
  } else if (EndsWith(filename, ".png", false)) {
    return "image/png";
  } else if (EndsWith(filename, ".gif", false)) {
    return "image/gif";
  }
  NOTREACHED();
  return "text/plain";
}

void DevToolsHttpHandlerImpl::Observe(int type,
                                      const NotificationSource& source,
                                      const NotificationDetails& details) {
  RenderProcessHost* process = Source<RenderProcessHost>(source).ptr();
  DevToolsManager* manager = DevToolsManager::GetInstance();
  for (ConnectionToClientHostMap::iterator it =
       connection_to_client_host_ui_.begin();
       it != connection_to_client_host_ui_.end(); ++it) {
    DevToolsAgentHost* agent = manager->GetDevToolsAgentHostFor(it->second);
    if (!agent)
      continue;
    RenderViewHost* rvh = DevToolsAgentHostRegistry::GetRenderViewHost(agent);
    if (rvh && rvh->GetProcess() == process)
      it->second->InspectedContentsClosing();
  }
}

void DevToolsHttpHandlerImpl::OnHttpRequest(
    int connection_id,
    const net::HttpServerRequestInfo& info) {
  if (info.path.find("/json") == 0) {
    BrowserThread::PostTask(
        BrowserThread::UI,
        FROM_HERE,
        base::Bind(&DevToolsHttpHandlerImpl::OnJsonRequestUI,
                   this,
                   connection_id,
                   info));
    return;
  }

  if (info.path.find("/thumb/") == 0) {
    // Thumbnail request.
    BrowserThread::PostTask(
        BrowserThread::UI,
        FROM_HERE,
        base::Bind(&DevToolsHttpHandlerImpl::OnThumbnailRequestUI,
                   this,
                   connection_id,
                   info));
    return;
  }

  if (info.path == "" || info.path == "/") {
    // Discovery page request.
    BrowserThread::PostTask(
        BrowserThread::UI,
        FROM_HERE,
        base::Bind(&DevToolsHttpHandlerImpl::OnDiscoveryPageRequestUI,
                   this,
                   connection_id));
    return;
  }

  if (info.path.find("/devtools/") != 0) {
    server_->Send404(connection_id);
    return;
  }

  std::string filename = PathWithoutParams(info.path.substr(10));
  std::string mime_type = GetMimeType(filename);

  FilePath frontend_dir = delegate_->GetDebugFrontendDir();
  if (!frontend_dir.empty()) {
    FilePath path = frontend_dir.AppendASCII(filename);
    std::string data;
    file_util::ReadFileToString(path, &data);
    server_->Send200(connection_id, data, mime_type);
    return;
  }
  if (delegate_->BundlesFrontendResources()) {
    int resource_id = DevToolsHttpHandler::GetFrontendResourceId(filename);
    if (resource_id != -1) {
      base::StringPiece data = GetContentClient()->GetDataResource(
              resource_id, ui::SCALE_FACTOR_NONE);
      server_->Send200(connection_id, data.as_string(), mime_type);
      return;
    }
  }
  server_->Send404(connection_id);
}

void DevToolsHttpHandlerImpl::OnWebSocketRequest(
    int connection_id,
    const net::HttpServerRequestInfo& request) {
  BrowserThread::PostTask(
      BrowserThread::UI,
      FROM_HERE,
      base::Bind(
          &DevToolsHttpHandlerImpl::OnWebSocketRequestUI,
          this,
          connection_id,
          request));
}

void DevToolsHttpHandlerImpl::OnWebSocketMessage(
    int connection_id,
    const std::string& data) {
  BrowserThread::PostTask(
      BrowserThread::UI,
      FROM_HERE,
      base::Bind(
          &DevToolsHttpHandlerImpl::OnWebSocketMessageUI,
          this,
          connection_id,
          data));
}

void DevToolsHttpHandlerImpl::OnClose(int connection_id) {
  BrowserThread::PostTask(
      BrowserThread::UI,
      FROM_HERE,
      base::Bind(
          &DevToolsHttpHandlerImpl::OnCloseUI,
          this,
          connection_id));
}

struct DevToolsHttpHandlerImpl::PageInfo {
  PageInfo()
      : attached(false) {
  }

  std::string id;
  std::string url;
  bool attached;
  std::string title;
  std::string thumbnail_url;
  std::string favicon_url;
  base::TimeTicks last_selected_time;
};

// static
bool DevToolsHttpHandlerImpl::SortPageListByTime(const PageInfo& info1,
                                                 const PageInfo& info2) {
  return info1.last_selected_time > info2.last_selected_time;
}

DevToolsHttpHandlerImpl::PageList DevToolsHttpHandlerImpl::GeneratePageList() {
  PageList page_list;
  for (RenderProcessHost::iterator it(RenderProcessHost::AllHostsIterator());
       !it.IsAtEnd(); it.Advance()) {
    RenderProcessHost* render_process_host = it.GetCurrentValue();
    DCHECK(render_process_host);

    // Ignore processes that don't have a connection, such as crashed contents.
    if (!render_process_host->HasConnection())
      continue;

    RenderProcessHost::RenderWidgetHostsIterator rwit(
        render_process_host->GetRenderWidgetHostsIterator());
    for (; !rwit.IsAtEnd(); rwit.Advance()) {
      const RenderWidgetHost* widget = rwit.GetCurrentValue();
      DCHECK(widget);
      if (!widget || !widget->IsRenderView())
        continue;

      RenderViewHost* host =
          RenderViewHost::From(const_cast<RenderWidgetHost*>(widget));
      page_list.push_back(CreatePageInfo(host));
    }
  }
  std::sort(page_list.begin(), page_list.end(), SortPageListByTime);
  return page_list;
}

std::string DevToolsHttpHandlerImpl::GetFrontendURLInternal(
    const std::string rvh_id,
    const std::string& host) {
  return base::StringPrintf(
      "%s%sws=%s/devtools/page/%s",
      overridden_frontend_url_.c_str(),
      overridden_frontend_url_.find("?") == std::string::npos ? "?" : "&",
      host.c_str(),
      rvh_id.c_str());
}

static bool ParseJsonPath(
    const std::string& path,
    std::string* command,
    std::string* target_id) {

  // Fall back to list in case of empty query.
  if (path.empty()) {
    *command = "list";
    return true;
  }

  if (path.find("/") != 0) {
    // Malformed command.
    return false;
  }
  *command = path.substr(1);

  size_t separator_pos = command->find("/");
  if (separator_pos != std::string::npos) {
    *target_id = command->substr(separator_pos + 1);
    *command = command->substr(0, separator_pos);
  }
  return true;
}

void DevToolsHttpHandlerImpl::OnJsonRequestUI(
    int connection_id,
    const net::HttpServerRequestInfo& info) {
  // Trim /json and ?jsonp=...
  std::string path = info.path.substr(5);
  std::string jsonp;
  size_t jsonp_pos = path.find("?jsonp=");
  if (jsonp_pos != std::string::npos) {
    jsonp = path.substr(jsonp_pos + 7);
    path = path.substr(0, jsonp_pos);
  }

  // Trim fragment and query
  size_t query_pos = path.find("?");
  if (query_pos != std::string::npos)
    path = path.substr(0, query_pos);

  size_t fragment_pos = path.find("#");
  if (fragment_pos != std::string::npos)
    path = path.substr(0, fragment_pos);

  std::string command;
  std::string target_id;
  if (!ParseJsonPath(path, &command, &target_id)) {
    SendJson(connection_id,
             net::HTTP_NOT_FOUND,
             NULL,
             "Malformed query: " + info.path,
             jsonp);
    return;
  }

  if (command == "version") {
    DictionaryValue version;
    version.SetString("Protocol-Version",
                      WebKit::WebDevToolsAgent::inspectorProtocolVersion());
    version.SetString("WebKit-Version",
                      webkit_glue::GetWebKitVersion());
    version.SetString("User-Agent",
                      webkit_glue::GetUserAgent(GURL(chrome::kAboutBlankURL)));
    SendJson(connection_id, net::HTTP_OK, &version, "", jsonp);
    return;
  }

  if (command == "list") {
    PageList page_list = GeneratePageList();
    ListValue json_pages_list;
    std::string host = info.headers["Host"];
    for (PageList::iterator i = page_list.begin(); i != page_list.end(); ++i)
      json_pages_list.Append(SerializePageInfo(*i, host));
    SendJson(connection_id, net::HTTP_OK, &json_pages_list, "", jsonp);
    return;
  }

  if (command == "new") {
    RenderViewHost* rvh = delegate_->CreateNewTarget();
    if (!rvh) {
      SendJson(connection_id,
               net::HTTP_INTERNAL_SERVER_ERROR,
               NULL,
               "Could not create new page",
               jsonp);
      return;
    }
    PageInfo page_info = CreatePageInfo(rvh);
    std::string host = info.headers["Host"];
    scoped_ptr<DictionaryValue> dictionary(SerializePageInfo(page_info, host));
    SendJson(connection_id, net::HTTP_OK, dictionary.get(), "", jsonp);
    return;
  }

  if (command == "activate" || command == "close") {
    RenderViewHost* rvh = binding_->ForIdentifier(target_id);
    if (!rvh) {
      SendJson(connection_id,
               net::HTTP_NOT_FOUND,
               NULL,
               "No such target id: " + target_id,
               jsonp);
      return;
    }

    if (command == "activate") {
      rvh->GetDelegate()->Activate();
      SendJson(connection_id, net::HTTP_OK, NULL, "Target activated", jsonp);
      return;
    }

    if (command == "close") {
      rvh->ClosePage();
      SendJson(connection_id, net::HTTP_OK, NULL, "Target is closing", jsonp);
      return;
    }
  }
  SendJson(connection_id,
           net::HTTP_NOT_FOUND,
           NULL,
           "Unknown command: " + command,
           jsonp);
  return;
}

void DevToolsHttpHandlerImpl::OnThumbnailRequestUI(
    int connection_id,
    const net::HttpServerRequestInfo& info) {
  std::string prefix = "/thumb/";
  size_t pos = info.path.find(prefix);
  if (pos != 0) {
    Send404(connection_id);
    return;
  }

  std::string page_url = info.path.substr(prefix.length());
  std::string data = delegate_->GetPageThumbnailData(GURL(page_url));
  if (!data.empty())
    Send200(connection_id, data, "image/png");
  else
    Send404(connection_id);
}

void DevToolsHttpHandlerImpl::OnDiscoveryPageRequestUI(int connection_id) {
  std::string response = delegate_->GetDiscoveryPageHTML();
  Send200(connection_id, response, "text/html; charset=UTF-8");
}

void DevToolsHttpHandlerImpl::OnWebSocketRequestUI(
    int connection_id,
    const net::HttpServerRequestInfo& request) {
  if (!thread_.get())
    return;
  std::string browser_prefix = "/devtools/browser";
  size_t browser_pos = request.path.find(browser_prefix);
  if (browser_pos == 0) {
    if (browser_target_) {
      Send500(connection_id, "Another client already attached");
      return;
    }
    browser_target_.reset(new DevToolsBrowserTarget(connection_id));
    browser_target_->RegisterHandler(new DevToolsTracingHandler());
    AcceptWebSocket(connection_id, request);
    return;
  }

  std::string page_prefix = "/devtools/page/";
  size_t pos = request.path.find(page_prefix);
  if (pos != 0) {
    Send404(connection_id);
    return;
  }

  std::string page_id = request.path.substr(page_prefix.length());
  RenderViewHost* rvh = binding_->ForIdentifier(page_id);
  if (!rvh) {
    Send500(connection_id, "No such target id: " + page_id);
    return;
  }

  DevToolsManager* manager = DevToolsManager::GetInstance();
  DevToolsAgentHost* agent = DevToolsAgentHostRegistry::GetDevToolsAgentHost(
      rvh);
  if (manager->GetDevToolsClientHostFor(agent)) {
    Send500(connection_id,
            "Target with given id is being inspected: " + page_id);
    return;
  }

  DevToolsClientHostImpl* client_host =
      new DevToolsClientHostImpl(thread_->message_loop(),
                                 server_,
                                 connection_id);
  connection_to_client_host_ui_[connection_id] = client_host;

  manager->RegisterDevToolsClientHostFor(agent, client_host);

  AcceptWebSocket(connection_id, request);
}

void DevToolsHttpHandlerImpl::OnWebSocketMessageUI(
    int connection_id,
    const std::string& data) {
  if (browser_target_ && connection_id == browser_target_->connection_id()) {
    std::string json_response = browser_target_->HandleMessage(data);

    thread_->message_loop()->PostTask(
        FROM_HERE,
        base::Bind(&net::HttpServer::SendOverWebSocket,
                   server_.get(),
                   connection_id,
                   json_response));
    return;
  }

  ConnectionToClientHostMap::iterator it =
      connection_to_client_host_ui_.find(connection_id);
  if (it == connection_to_client_host_ui_.end())
    return;

  DevToolsManager* manager = DevToolsManager::GetInstance();
  manager->DispatchOnInspectorBackend(it->second, data);
}

void DevToolsHttpHandlerImpl::OnCloseUI(int connection_id) {
  ConnectionToClientHostMap::iterator it =
      connection_to_client_host_ui_.find(connection_id);
  if (it != connection_to_client_host_ui_.end()) {
    DevToolsClientHostImpl* client_host =
        static_cast<DevToolsClientHostImpl*>(it->second);
    DevToolsManager::GetInstance()->ClientHostClosing(client_host);
    delete client_host;
    connection_to_client_host_ui_.erase(connection_id);
  }
  if (browser_target_ && browser_target_->connection_id() == connection_id) {
    browser_target_.reset();
    return;
  }
}

DevToolsHttpHandlerImpl::DevToolsHttpHandlerImpl(
    const net::StreamListenSocketFactory* socket_factory,
    const std::string& frontend_url,
    DevToolsHttpHandlerDelegate* delegate)
    : overridden_frontend_url_(frontend_url),
      socket_factory_(socket_factory),
      delegate_(delegate) {
  if (overridden_frontend_url_.empty())
      overridden_frontend_url_ = "/devtools/devtools.html";

  default_binding_.reset(new DevToolsDefaultBindingHandler);
  binding_ = default_binding_.get();

  registrar_.Add(this, NOTIFICATION_RENDERER_PROCESS_TERMINATED,
                 NotificationService::AllBrowserContextsAndSources());
  registrar_.Add(this, NOTIFICATION_RENDERER_PROCESS_CLOSED,
                 NotificationService::AllBrowserContextsAndSources());

  // Balanced in ResetHandlerThreadAndRelease().
  AddRef();
}

// Runs on the handler thread
void DevToolsHttpHandlerImpl::Init() {
  server_ = new net::HttpServer(*socket_factory_.get(), this);
}

// Runs on the handler thread
void DevToolsHttpHandlerImpl::Teardown() {
  server_ = NULL;
}

// Runs on FILE thread to make sure that it is serialized against
// {Start|Stop}HandlerThread and to allow calling pthread_join.
void DevToolsHttpHandlerImpl::StopHandlerThread() {
  if (!thread_->message_loop())
    return;
  thread_->message_loop()->PostTask(
      FROM_HERE,
      base::Bind(&DevToolsHttpHandlerImpl::Teardown, this));
  // Thread::Stop joins the thread.
  thread_->Stop();
}

void DevToolsHttpHandlerImpl::SendJson(int connection_id,
                                       net::HttpStatusCode status_code,
                                       Value* value,
                                       const std::string& message,
                                       const std::string& jsonp) {
  if (!thread_.get())
    return;

  // Serialize value and message.
  std::string json_value;
  if (value) {
    base::JSONWriter::WriteWithOptions(value,
                                       base::JSONWriter::OPTIONS_PRETTY_PRINT,
                                       &json_value);
  }
  std::string json_message;
  scoped_ptr<Value> message_object(Value::CreateStringValue(message));
  base::JSONWriter::Write(message_object.get(), &json_message);

  std::string response;
  std::string mime_type = "application/json; charset=UTF-8";

  // Wrap jsonp if necessary.
  if (!jsonp.empty()) {
    mime_type = "text/javascript; charset=UTF-8";
    response = StringPrintf("%s(%s, %d, %s);",
                            jsonp.c_str(),
                            json_value.empty() ? "undefined"
                                               : json_value.c_str(),
                            status_code,
                            json_message.c_str());
    // JSONP always returns 200.
    status_code = net::HTTP_OK;
  } else {
    response = StringPrintf("%s%s", json_value.c_str(), message.c_str());
  }

  thread_->message_loop()->PostTask(
      FROM_HERE,
      base::Bind(&net::HttpServer::Send,
                 server_.get(),
                 connection_id,
                 status_code,
                 response,
                 mime_type));
}

void DevToolsHttpHandlerImpl::Send200(int connection_id,
                                      const std::string& data,
                                      const std::string& mime_type) {
  if (!thread_.get())
    return;
  thread_->message_loop()->PostTask(
      FROM_HERE,
      base::Bind(&net::HttpServer::Send200,
                 server_.get(),
                 connection_id,
                 data,
                 mime_type));
}

void DevToolsHttpHandlerImpl::Send404(int connection_id) {
  if (!thread_.get())
    return;
  thread_->message_loop()->PostTask(
      FROM_HERE,
      base::Bind(&net::HttpServer::Send404, server_.get(), connection_id));
}

void DevToolsHttpHandlerImpl::Send500(int connection_id,
                                      const std::string& message) {
  if (!thread_.get())
    return;
  thread_->message_loop()->PostTask(
      FROM_HERE,
      base::Bind(&net::HttpServer::Send500, server_.get(), connection_id,
                 message));
}

void DevToolsHttpHandlerImpl::AcceptWebSocket(
    int connection_id,
    const net::HttpServerRequestInfo& request) {
  if (!thread_.get())
    return;
  thread_->message_loop()->PostTask(
      FROM_HERE,
      base::Bind(&net::HttpServer::AcceptWebSocket, server_.get(),
                 connection_id, request));
}

DevToolsHttpHandlerImpl::PageInfo
DevToolsHttpHandlerImpl::CreatePageInfo(RenderViewHost* rvh)
{
  RenderViewHostDelegate* host_delegate = rvh->GetDelegate();
  DevToolsAgentHost* agent =
      DevToolsAgentHostRegistry::GetDevToolsAgentHost(rvh);
  DevToolsClientHost* client_host = DevToolsManager::GetInstance()->
      GetDevToolsClientHostFor(agent);
  PageInfo page_info;
  page_info.id = binding_->GetIdentifier(rvh);
  page_info.attached = client_host != NULL;
  page_info.url = host_delegate->GetURL().spec();

  WebContents* web_contents = host_delegate->GetAsWebContents();
  if (web_contents) {
    page_info.title = UTF16ToUTF8(
        net::EscapeForHTML(web_contents->GetTitle()));
    page_info.last_selected_time = web_contents->GetLastSelectedTime();

    NavigationController& controller = web_contents->GetController();
    NavigationEntry* entry = controller.GetActiveEntry();
    if (entry != NULL && entry->GetURL().is_valid()) {
      page_info.thumbnail_url = "/thumb/" + entry->GetURL().spec();
      page_info.favicon_url = entry->GetFavicon().url.spec();
    }
  }
  return page_info;
}

DictionaryValue* DevToolsHttpHandlerImpl::SerializePageInfo(
    const PageInfo& page_info,
    const std::string& host) {
  DictionaryValue* dictionary = new DictionaryValue;
  dictionary->SetString("title", page_info.title);
  dictionary->SetString("url", page_info.url);
  dictionary->SetString("thumbnailUrl", page_info.thumbnail_url);
  dictionary->SetString("faviconUrl", page_info.favicon_url);
  if (!page_info.attached) {
    dictionary->SetString("webSocketDebuggerUrl",
                          base::StringPrintf("ws://%s/devtools/page/%s",
                                             host.c_str(),
                                             page_info.id.c_str()));
    std::string devtools_frontend_url = GetFrontendURLInternal(
        page_info.id.c_str(),
        host);
    dictionary->SetString("devtoolsFrontendUrl", devtools_frontend_url);
  }
  return dictionary;
}

}  // namespace content