This source file includes following definitions.
- detach_reason_
 
- InspectedContentsClosing
 
- DispatchOnInspectorFrontend
 
- ReplacedWithAnotherClient
 
- TimeComparator
 
- IsSupportedProtocolVersion
 
- GetFrontendResourceId
 
- Start
 
- Start
 
- StartHandlerThread
 
- ResetHandlerThread
 
- ResetHandlerThreadAndRelease
 
- Stop
 
- GetFrontendURL
 
- PathWithoutParams
 
- GetMimeType
 
- OnHttpRequest
 
- OnWebSocketRequest
 
- OnWebSocketMessage
 
- OnClose
 
- GetFrontendURLInternal
 
- ParseJsonPath
 
- OnJsonRequestUI
 
- OnTargetListReceived
 
- GetTarget
 
- OnThumbnailRequestUI
 
- OnDiscoveryPageRequestUI
 
- OnWebSocketRequestUI
 
- OnWebSocketMessageUI
 
- OnCloseUI
 
- delegate_
 
- Init
 
- Teardown
 
- StopHandlerThread
 
- SendJson
 
- Send200
 
- Send404
 
- Send500
 
- AcceptWebSocket
 
- SerializeTarget
 
#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/logging.h"
#include "base/message_loop/message_loop_proxy.h"
#include "base/stl_util.h"
#include "base/threading/thread.h"
#include "base/values.h"
#include "content/browser/devtools/devtools_browser_target.h"
#include "content/browser/devtools/devtools_protocol.h"
#include "content/browser/devtools/devtools_protocol_constants.h"
#include "content/browser/devtools/devtools_system_info_handler.h"
#include "content/browser/devtools/devtools_tracing_handler.h"
#include "content/browser/devtools/tethering_handler.h"
#include "content/common/devtools_messages.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/browser/devtools_agent_host.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/devtools_target.h"
#include "content/public/common/content_client.h"
#include "content/public/common/url_constants.h"
#include "content/public/common/user_agent.h"
#include "content/public/common/user_agent.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 "net/server/http_server_response_info.h"
#if defined(OS_ANDROID)
#include "base/android/build_info.h"
#endif
namespace content {
namespace {
const char kProtocolVersion[] = "1.0";
const char kDevToolsHandlerThreadName[] = "Chrome_DevToolsHandlerThread";
const char kThumbUrlPrefix[] = "/thumb/";
const char kPageUrlPrefix[] = "/devtools/page/";
const char kTargetIdField[] = "id";
const char kTargetTypeField[] = "type";
const char kTargetTitleField[] = "title";
const char kTargetDescriptionField[] = "description";
const char kTargetUrlField[] = "url";
const char kTargetThumbnailUrlField[] = "thumbnailUrl";
const char kTargetFaviconUrlField[] = "faviconUrl";
const char kTargetWebSocketDebuggerUrlField[] = "webSocketDebuggerUrl";
const char kTargetDevtoolsFrontendUrlField[] = "devtoolsFrontendUrl";
class DevToolsClientHostImpl : public DevToolsClientHost {
 public:
  DevToolsClientHostImpl(base::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") {}
  virtual ~DevToolsClientHostImpl() {}
  
  virtual void InspectedContentsClosing() OVERRIDE {
    if (is_closed_)
      return;
    is_closed_ = true;
    base::DictionaryValue notification;
    notification.SetString(
        devtools::Inspector::detached::kParamReason, detach_reason_);
    std::string response = DevToolsProtocol::CreateNotification(
        devtools::Inspector::detached::kName,
        notification.DeepCopy())->Serialize();
    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) OVERRIDE {
    message_loop_->PostTask(
        FROM_HERE,
        base::Bind(&net::HttpServer::SendOverWebSocket,
                   server_,
                   connection_id_,
                   data));
  }
  virtual void ReplacedWithAnotherClient() OVERRIDE {
    detach_reason_ = "replaced_with_devtools";
  }
 private:
  base::MessageLoop* message_loop_;
  net::HttpServer* server_;
  int connection_id_;
  bool is_closed_;
  std::string detach_reason_;
};
static bool TimeComparator(const DevToolsTarget* target1,
                           const DevToolsTarget* target2) {
  return target1->GetLastActivityTime() > target2->GetLastActivityTime();
}
}  
bool DevToolsHttpHandler::IsSupportedProtocolVersion(
    const std::string& version) {
  return version == kProtocolVersion;
}
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;
}
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));
  
  DCHECK(server_.get() == NULL);
  DCHECK(thread_.get() == NULL);
  STLDeleteValues(&target_map_);
}
void DevToolsHttpHandlerImpl::Start() {
  if (thread_)
    return;
  thread_.reset(new base::Thread(kDevToolsHandlerThreadName));
  BrowserThread::PostTask(
      BrowserThread::FILE, FROM_HERE,
      base::Bind(&DevToolsHttpHandlerImpl::StartHandlerThread, this));
}
void DevToolsHttpHandlerImpl::StartHandlerThread() {
  base::Thread::Options options;
  options.message_loop_type = base::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_)
    return;
  BrowserThread::PostTaskAndReply(
      BrowserThread::FILE, FROM_HERE,
      base::Bind(&DevToolsHttpHandlerImpl::StopHandlerThread, this),
      base::Bind(&DevToolsHttpHandlerImpl::ResetHandlerThreadAndRelease, this));
}
GURL DevToolsHttpHandlerImpl::GetFrontendURL() {
  net::IPEndPoint ip_address;
  if (server_->GetLocalAddress(&ip_address))
    return GURL();
  return GURL(std::string("http://") + ip_address.ToString() + frontend_url_);
}
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::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(kThumbUrlPrefix) == 0) {
    
    const std::string target_id = info.path.substr(strlen(kThumbUrlPrefix));
    DevToolsTarget* target = GetTarget(target_id);
    GURL page_url;
    if (target)
      page_url = target->GetUrl();
    BrowserThread::PostTask(
        BrowserThread::UI,
        FROM_HERE,
        base::Bind(&DevToolsHttpHandlerImpl::OnThumbnailRequestUI,
                   this,
                   connection_id,
                   page_url));
    return;
  }
  if (info.path == "" || info.path == "/") {
    
    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);
  base::FilePath frontend_dir = delegate_->GetDebugFrontendDir();
  if (!frontend_dir.empty()) {
    base::FilePath path = frontend_dir.AppendASCII(filename);
    std::string data;
    base::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) {
  std::string browser_prefix = "/devtools/browser";
  size_t browser_pos = request.path.find(browser_prefix);
  if (browser_pos == 0) {
    if (browser_target_) {
      server_->Send500(connection_id, "Another client already attached");
      return;
    }
    browser_target_ = new DevToolsBrowserTarget(
        thread_->message_loop_proxy().get(), server_.get(), connection_id);
    browser_target_->RegisterDomainHandler(
        devtools::Tracing::kName,
        new DevToolsTracingHandler(),
        true );
    browser_target_->RegisterDomainHandler(
        TetheringHandler::kDomain,
        new TetheringHandler(delegate_.get()),
        false );
    browser_target_->RegisterDomainHandler(
        devtools::SystemInfo::kName,
        new DevToolsSystemInfoHandler(),
        true );
    server_->AcceptWebSocket(connection_id, request);
    return;
  }
  BrowserThread::PostTask(
      BrowserThread::UI,
      FROM_HERE,
      base::Bind(
          &DevToolsHttpHandlerImpl::OnWebSocketRequestUI,
          this,
          connection_id,
          request));
}
void DevToolsHttpHandlerImpl::OnWebSocketMessage(
    int connection_id,
    const std::string& data) {
  if (browser_target_ && connection_id == browser_target_->connection_id()) {
    browser_target_->HandleMessage(data);
    return;
  }
  BrowserThread::PostTask(
      BrowserThread::UI,
      FROM_HERE,
      base::Bind(
          &DevToolsHttpHandlerImpl::OnWebSocketMessageUI,
          this,
          connection_id,
          data));
}
void DevToolsHttpHandlerImpl::OnClose(int connection_id) {
  if (browser_target_ && browser_target_->connection_id() == connection_id) {
    browser_target_->Detach();
    browser_target_ = NULL;
    return;
  }
  BrowserThread::PostTask(
      BrowserThread::UI,
      FROM_HERE,
      base::Bind(
          &DevToolsHttpHandlerImpl::OnCloseUI,
          this,
          connection_id));
}
std::string DevToolsHttpHandlerImpl::GetFrontendURLInternal(
    const std::string id,
    const std::string& host) {
  return base::StringPrintf(
      "%s%sws=%s%s%s",
      frontend_url_.c_str(),
      frontend_url_.find("?") == std::string::npos ? "?" : "&",
      host.c_str(),
      kPageUrlPrefix,
      id.c_str());
}
static bool ParseJsonPath(
    const std::string& path,
    std::string* command,
    std::string* target_id) {
  
  if (path.empty()) {
    *command = "list";
    return true;
  }
  if (path.find("/") != 0) {
    
    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) {
  
  std::string path = info.path.substr(5);
  
  std::string query;
  size_t query_pos = path.find("?");
  if (query_pos != std::string::npos) {
    query = path.substr(query_pos + 1);
    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);
    return;
  }
  if (command == "version") {
    base::DictionaryValue version;
    version.SetString("Protocol-Version", kProtocolVersion);
    version.SetString("WebKit-Version", GetWebKitVersion());
    version.SetString("Browser", GetContentClient()->GetProduct());
    version.SetString("User-Agent", GetContentClient()->GetUserAgent());
#if defined(OS_ANDROID)
    version.SetString("Android-Package",
        base::android::BuildInfo::GetInstance()->package_name());
#endif
    SendJson(connection_id, net::HTTP_OK, &version, std::string());
    return;
  }
  if (command == "list") {
    std::string host = info.headers["host"];
    AddRef();  
    delegate_->EnumerateTargets(
        base::Bind(&DevToolsHttpHandlerImpl::OnTargetListReceived,
                   this, connection_id, host));
    return;
  }
  if (command == "new") {
    GURL url(net::UnescapeURLComponent(
        query, net::UnescapeRule::URL_SPECIAL_CHARS));
    if (!url.is_valid())
      url = GURL(kAboutBlankURL);
    scoped_ptr<DevToolsTarget> target(delegate_->CreateNewTarget(url));
    if (!target) {
      SendJson(connection_id,
               net::HTTP_INTERNAL_SERVER_ERROR,
               NULL,
               "Could not create new page");
      return;
    }
    std::string host = info.headers["host"];
    scoped_ptr<base::DictionaryValue> dictionary(
        SerializeTarget(*target.get(), host));
    SendJson(connection_id, net::HTTP_OK, dictionary.get(), std::string());
    const std::string target_id = target->GetId();
    target_map_[target_id] = target.release();
    return;
  }
  if (command == "activate" || command == "close") {
    DevToolsTarget* target = GetTarget(target_id);
    if (!target) {
      SendJson(connection_id,
               net::HTTP_NOT_FOUND,
               NULL,
               "No such target id: " + target_id);
      return;
    }
    if (command == "activate") {
      if (target->Activate()) {
        SendJson(connection_id, net::HTTP_OK, NULL, "Target activated");
      } else {
        SendJson(connection_id,
                 net::HTTP_INTERNAL_SERVER_ERROR,
                 NULL,
                 "Could not activate target id: " + target_id);
      }
      return;
    }
    if (command == "close") {
      if (target->Close()) {
        SendJson(connection_id, net::HTTP_OK, NULL, "Target is closing");
      } else {
        SendJson(connection_id,
                 net::HTTP_INTERNAL_SERVER_ERROR,
                 NULL,
                 "Could not close target id: " + target_id);
      }
      return;
    }
  }
  SendJson(connection_id,
           net::HTTP_NOT_FOUND,
           NULL,
           "Unknown command: " + command);
  return;
}
void DevToolsHttpHandlerImpl::OnTargetListReceived(
    int connection_id,
    const std::string& host,
    const DevToolsHttpHandlerDelegate::TargetList& targets) {
  DevToolsHttpHandlerDelegate::TargetList sorted_targets = targets;
  std::sort(sorted_targets.begin(), sorted_targets.end(), TimeComparator);
  STLDeleteValues(&target_map_);
  base::ListValue list_value;
  for (DevToolsHttpHandlerDelegate::TargetList::const_iterator it =
      sorted_targets.begin(); it != sorted_targets.end(); ++it) {
    DevToolsTarget* target = *it;
    target_map_[target->GetId()] = target;
    list_value.Append(SerializeTarget(*target, host));
  }
  SendJson(connection_id, net::HTTP_OK, &list_value, std::string());
  Release();  
}
DevToolsTarget* DevToolsHttpHandlerImpl::GetTarget(const std::string& id) {
  TargetMap::const_iterator it = target_map_.find(id);
  if (it == target_map_.end())
    return NULL;
  return it->second;
}
void DevToolsHttpHandlerImpl::OnThumbnailRequestUI(
    int connection_id, const GURL& page_url) {
  std::string data = delegate_->GetPageThumbnailData(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_)
    return;
  size_t pos = request.path.find(kPageUrlPrefix);
  if (pos != 0) {
    Send404(connection_id);
    return;
  }
  std::string page_id = request.path.substr(strlen(kPageUrlPrefix));
  DevToolsTarget* target = GetTarget(page_id);
  scoped_refptr<DevToolsAgentHost> agent =
      target ? target->GetAgentHost() : NULL;
  if (!agent) {
    Send500(connection_id, "No such target id: " + page_id);
    return;
  }
  if (agent->IsAttached()) {
    Send500(connection_id,
            "Target with given id is being inspected: " + page_id);
    return;
  }
  DevToolsClientHostImpl* client_host = new DevToolsClientHostImpl(
      thread_->message_loop(), server_.get(), connection_id);
  connection_to_client_host_ui_[connection_id] = client_host;
  DevToolsManager::GetInstance()->
      RegisterDevToolsClientHostFor(agent, client_host);
  AcceptWebSocket(connection_id, request);
}
void DevToolsHttpHandlerImpl::OnWebSocketMessageUI(
    int connection_id,
    const std::string& data) {
  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);
  }
}
DevToolsHttpHandlerImpl::DevToolsHttpHandlerImpl(
    const net::StreamListenSocketFactory* socket_factory,
    const std::string& frontend_url,
    DevToolsHttpHandlerDelegate* delegate)
    : frontend_url_(frontend_url),
      socket_factory_(socket_factory),
      delegate_(delegate) {
  if (frontend_url_.empty())
      frontend_url_ = "/devtools/devtools.html";
  
  AddRef();
}
void DevToolsHttpHandlerImpl::Init() {
  server_ = new net::HttpServer(*socket_factory_.get(), this);
}
void DevToolsHttpHandlerImpl::Teardown() {
  server_ = NULL;
}
void DevToolsHttpHandlerImpl::StopHandlerThread() {
  if (!thread_->message_loop())
    return;
  thread_->message_loop()->PostTask(
      FROM_HERE,
      base::Bind(&DevToolsHttpHandlerImpl::Teardown, this));
  
  thread_->Stop();
}
void DevToolsHttpHandlerImpl::SendJson(int connection_id,
                                       net::HttpStatusCode status_code,
                                       base::Value* value,
                                       const std::string& message) {
  if (!thread_)
    return;
  
  std::string json_value;
  if (value) {
    base::JSONWriter::WriteWithOptions(value,
                                       base::JSONWriter::OPTIONS_PRETTY_PRINT,
                                       &json_value);
  }
  std::string json_message;
  scoped_ptr<base::Value> message_object(new base::StringValue(message));
  base::JSONWriter::Write(message_object.get(), &json_message);
  net::HttpServerResponseInfo response(status_code);
  response.SetBody(json_value + message, "application/json; charset=UTF-8");
  thread_->message_loop()->PostTask(
      FROM_HERE,
      base::Bind(&net::HttpServer::SendResponse,
                 server_.get(),
                 connection_id,
                 response));
}
void DevToolsHttpHandlerImpl::Send200(int connection_id,
                                      const std::string& data,
                                      const std::string& mime_type) {
  if (!thread_)
    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_)
    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_)
    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_)
    return;
  thread_->message_loop()->PostTask(
      FROM_HERE,
      base::Bind(&net::HttpServer::AcceptWebSocket, server_.get(),
                 connection_id, request));
}
base::DictionaryValue* DevToolsHttpHandlerImpl::SerializeTarget(
    const DevToolsTarget& target,
    const std::string& host) {
  base::DictionaryValue* dictionary = new base::DictionaryValue;
  std::string id = target.GetId();
  dictionary->SetString(kTargetIdField, id);
  dictionary->SetString(kTargetTypeField, target.GetType());
  dictionary->SetString(kTargetTitleField,
                        net::EscapeForHTML(target.GetTitle()));
  dictionary->SetString(kTargetDescriptionField, target.GetDescription());
  GURL url = target.GetUrl();
  dictionary->SetString(kTargetUrlField, url.spec());
  GURL favicon_url = target.GetFaviconUrl();
  if (favicon_url.is_valid())
    dictionary->SetString(kTargetFaviconUrlField, favicon_url.spec());
  if (!delegate_->GetPageThumbnailData(url).empty()) {
    dictionary->SetString(kTargetThumbnailUrlField,
                          std::string(kThumbUrlPrefix) + id);
  }
  if (!target.IsAttached()) {
    dictionary->SetString(kTargetWebSocketDebuggerUrlField,
                          base::StringPrintf("ws://%s%s%s",
                                             host.c_str(),
                                             kPageUrlPrefix,
                                             id.c_str()));
    std::string devtools_frontend_url = GetFrontendURLInternal(
        id.c_str(),
        host);
    dictionary->SetString(
        kTargetDevtoolsFrontendUrlField, devtools_frontend_url);
  }
  return dictionary;
}
}