blob: 25f2b7ace7f03f922737a353005a68bdf0938fdb [file] [log] [blame]
// Copyright 2013 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 "android_webview/browser/aw_devtools_delegate.h"
#include "android_webview/browser/in_process_view_renderer.h"
#include "base/bind.h"
#include "base/json/json_writer.h"
#include "base/strings/stringprintf.h"
#include "base/values.h"
#include "content/public/browser/android/devtools_auth.h"
#include "content/public/browser/devtools_http_handler.h"
#include "content/public/browser/web_contents.h"
#include "content/public/common/url_constants.h"
#include "net/socket/unix_domain_socket_posix.h"
#include "ui/base/resource/resource_bundle.h"
#include "webkit/common/user_agent/user_agent_util.h"
namespace {
const char kFrontEndURL[] =
"http://chrome-devtools-frontend.appspot.com/serve_rev/%s/devtools.html";
const char kSocketNameFormat[] = "webview_devtools_remote_%d";
}
namespace android_webview {
AwDevToolsDelegate::AwDevToolsDelegate(content::BrowserContext* browser_context)
: browser_context_(browser_context) {
devtools_http_handler_ = content::DevToolsHttpHandler::Start(
new net::UnixDomainSocketWithAbstractNamespaceFactory(
base::StringPrintf(kSocketNameFormat, getpid()),
"",
base::Bind(&content::CanUserConnectToDevTools)),
base::StringPrintf(kFrontEndURL,
webkit_glue::GetWebKitRevision().c_str()),
this);
}
AwDevToolsDelegate::~AwDevToolsDelegate() {
}
void AwDevToolsDelegate::Stop() {
devtools_http_handler_->Stop();
// WARNING: |this| has now been deleted by the method above.
}
std::string AwDevToolsDelegate::GetDiscoveryPageHTML() {
// This is a temporary way of providing the list of inspectable WebViews.
// Since WebView doesn't have its own resources now, it doesn't seem
// reasonable to create a dedicated .pak file just for this temporary page.
const char html[] =
"<html>"
"<head>"
"<title>WebView remote debugging</title>"
"<style>"
"</style>"
"<script>"
"function onLoad() {"
" var tabs_list_request = new XMLHttpRequest();"
" tabs_list_request.open("
" 'GET', '/json/list?t=' + new Date().getTime(), true);"
" tabs_list_request.onreadystatechange = onReady;"
" tabs_list_request.send();"
"}"
"function processItem(item) {"
" var result = JSON.parse(item.description);"
" result.debuggable = !!item.devtoolsFrontendUrl;"
" result.debugUrl = item.devtoolsFrontendUrl;"
" result.title = item.title;"
" return result;"
"}"
"function onReady() {"
" if(this.readyState == 4 && this.status == 200) {"
" if(this.response != null)"
" var responseJSON = JSON.parse(this.response);"
" var items = [];"
" for (var i = 0; i < responseJSON.length; ++i)"
" items.push(processItem(responseJSON[i]));"
" clear();"
" for (var i = 0; i < items.length; ++i)"
" displayView(items[i]);"
" filter();"
" }"
"}"
"function addColumn(row, text) {"
" var column = document.createElement('td');"
" column.innerText = text;"
" row.appendChild(column);"
"}"
"function cutTextIfNeeded(text, maxLen) {"
" return text.length <= maxLen ?"
" text : text.substr(0, maxLen) + '\u2026';"
"}"
"function displayView(item) {"
" var row = document.createElement('tr');"
" var column = document.createElement('td');"
" var frontend_ref;"
" if (item.debuggable) {"
" frontend_ref = document.createElement('a');"
" frontend_ref.href = item.debugUrl;"
" frontend_ref.title = item.title;"
" frontend_ref.target = '_blank';"
" column.appendChild(frontend_ref);"
" } else {"
" frontend_ref = column;"
" }"
" var text = document.createElement('span');"
" if (item.title) {"
" text.innerText = cutTextIfNeeded(item.title, 64);"
" } else {"
" text.innerText = '(untitled)';"
" }"
" frontend_ref.appendChild(text);"
" var bits = 0;"
" var attached = item.attached ? (bits |= 1, 'Y') : 'N';"
" var visible = item.visible ? (bits |= 2, 'Y') : 'N';"
" var empty = item.empty ? 'Y' : (bits |= 4, 'N');"
" row.setAttribute('class', bits);"
" row.appendChild(column);"
" addColumn(row, attached);"
" addColumn(row, visible);"
" addColumn(row, empty);"
" addColumn(row, item.screenX + ', ' + item.screenY);"
" addColumn(row,"
" !item.empty ? (item.width + '\u00d7' + item.height) : '');"
" document.getElementById('items').appendChild(row);"
"}"
"function filter() {"
" var show_attached = document.getElementById('show_attached').checked;"
" var show_visible = document.getElementById('show_visible').checked;"
" var show_nonempty = document.getElementById('show_nonempty').checked;"
" var items = document.getElementById('items').childNodes;"
" for (var i = 0; i < items.length; i++) {"
" if (items[i].nodeName == 'TR') {"
" var mask = parseInt(items[i].getAttribute('class'));"
" var show = true;"
" if (show_attached) show &= ((mask & 1) != 0);"
" if (show_visible) show &= ((mask & 2) != 0);"
" if (show_nonempty) show &= ((mask & 4) != 0);"
" if (show) {"
" items[i].style.display = 'table-row';"
" } else {"
" items[i].style.display = 'none';"
" }"
" }"
" }"
"}"
"var refreshInterval = 0;"
"function toggleRefresh() {"
" clearInterval(refreshInterval);"
" var enabled = document.getElementById('refresh').checked;"
" if (enabled) {"
" var time = document.getElementById('refresh_time').value * 1000;"
" refreshInterval = setInterval(onLoad, time);"
" }"
"}"
"function clear() {"
" var items = document.getElementById('items');"
" for (var i = 0; i < items.childNodes.length; i++) {"
" items.removeChild(items.childNodes[i]);"
" }"
"}"
"</script>"
"</head>"
"<body onload='onLoad()'>"
" <div id='caption'>Inspectable WebViews</div>"
" <div><p>Only show:</p>"
" <form>"
" <input type='checkbox' id='show_attached' onclick='filter()'>"
" Attached<br/>"
" <input type='checkbox' id='show_visible' onclick='filter()'>"
" Visible<br/>"
" <input type='checkbox' id='show_nonempty' onclick='filter()'>"
" Non-empty<br/>"
" <input type='checkbox' id='refresh' onclick='toggleRefresh()'>"
" Auto refresh every "
" <input type='number' id='refresh_time' value='2' min='1' max='99'"
" onchange='toggleRefresh();' /> seconds<br/>"
" </form>"
" </div>"
" <table>"
" <tr><th style='width:200px; text-align:left;'>Title</th>"
" <th>Attached</th><th>Visible</th><th>Empty</th>"
" <th style='width:100px; text-align:left;'>Position</th>"
" <th style='width:100px; text-align:left;'>Size</th>"
" </tr>"
" <tbody id='items'></tbody>"
" </table>"
"</body>"
"</html>";
return html;
}
bool AwDevToolsDelegate::BundlesFrontendResources() {
return false;
}
base::FilePath AwDevToolsDelegate::GetDebugFrontendDir() {
return base::FilePath();
}
std::string AwDevToolsDelegate::GetPageThumbnailData(const GURL& url) {
return "";
}
content::RenderViewHost* AwDevToolsDelegate::CreateNewTarget() {
return NULL;
}
content::DevToolsHttpHandlerDelegate::TargetType
AwDevToolsDelegate::GetTargetType(content::RenderViewHost*) {
return kTargetTypeTab;
}
std::string AwDevToolsDelegate::GetViewDescription(
content::RenderViewHost* rvh) {
content::WebContents* web_contents =
content::WebContents::FromRenderViewHost(rvh);
if (!web_contents) return "";
BrowserViewRenderer* bvr
= InProcessViewRenderer::FromWebContents(web_contents);
if (!bvr) return "";
base::DictionaryValue description;
description.SetBoolean("attached", bvr->IsAttachedToWindow());
description.SetBoolean("visible", bvr->IsVisible());
gfx::Rect screen_rect = bvr->GetScreenRect();
description.SetInteger("screenX", screen_rect.x());
description.SetInteger("screenY", screen_rect.y());
description.SetBoolean("empty", screen_rect.size().IsEmpty());
if (!screen_rect.size().IsEmpty()) {
description.SetInteger("width", screen_rect.width());
description.SetInteger("height", screen_rect.height());
}
std::string json;
base::JSONWriter::Write(&description, &json);
return json;
}
scoped_refptr<net::StreamListenSocket>
AwDevToolsDelegate::CreateSocketForTethering(
net::StreamListenSocket::Delegate* delegate,
std::string* name) {
return NULL;
}
} // namespace android_webview