blob: ba9747879c5df81c780385a5e4f485139efeb30e [file] [log] [blame]
// Copyright (c) 2010 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 "chrome/browser/browser_about_handler.h"
#include <algorithm>
#include <string>
#include <vector>
#include "app/l10n_util.h"
#include "app/resource_bundle.h"
#include "base/callback.h"
#include "base/command_line.h"
#include "base/i18n/number_formatting.h"
#include "base/metrics/histogram.h"
#include "base/metrics/stats_table.h"
#include "base/path_service.h"
#include "base/platform_thread.h"
#include "base/stringprintf.h"
#include "base/string_number_conversions.h"
#include "base/string_piece.h"
#include "base/string_util.h"
#include "base/thread.h"
#include "base/tracked_objects.h"
#include "base/utf_string_conversions.h"
#include "chrome/browser/about_flags.h"
#include "chrome/browser/browser_process.h"
#include "chrome/browser/browser_thread.h"
#include "chrome/browser/defaults.h"
#include "chrome/browser/dom_ui/chrome_url_data_manager.h"
#include "chrome/browser/gpu_process_host.h"
#include "chrome/browser/gpu_process_host_ui_shim.h"
#include "chrome/browser/memory_details.h"
#include "chrome/browser/metrics/histogram_synchronizer.h"
#include "chrome/browser/net/predictor_api.h"
#include "chrome/browser/platform_util.h"
#include "chrome/browser/prefs/pref_service.h"
#include "chrome/browser/profile.h"
#include "chrome/browser/profile_manager.h"
#include "chrome/browser/renderer_host/render_process_host.h"
#include "chrome/browser/renderer_host/render_view_host.h"
#include "chrome/browser/sync/profile_sync_service.h"
#include "chrome/browser/sync/sync_ui_util.h"
#include "chrome/browser/ui/browser.h"
#include "chrome/common/about_handler.h"
#include "chrome/common/chrome_paths.h"
#include "chrome/common/chrome_version_info.h"
#include "chrome/common/gpu_info.h"
#include "chrome/common/jstemplate_builder.h"
#include "chrome/common/net/gaia/google_service_auth_error.h"
#include "chrome/common/pref_names.h"
#include "chrome/common/render_messages.h"
#include "chrome/common/url_constants.h"
#include "googleurl/src/gurl.h"
#include "grit/browser_resources.h"
#include "grit/chromium_strings.h"
#include "grit/generated_resources.h"
#include "grit/locale_settings.h"
#include "webkit/glue/webkit_glue.h"
#include "net/base/escape.h"
#ifdef CHROME_V8
#include "v8/include/v8.h"
#endif
#if defined(OS_WIN)
#include "chrome/browser/enumerate_modules_model_win.h"
#include "chrome/browser/views/about_ipc_dialog.h"
#elif defined(OS_CHROMEOS)
#include "chrome/browser/chromeos/cros/cros_library.h"
#include "chrome/browser/chromeos/cros/network_library.h"
#include "chrome/browser/chromeos/cros/syslogs_library.h"
#include "chrome/browser/chromeos/version_loader.h"
#include "chrome/browser/zygote_host_linux.h"
#elif defined(OS_MACOSX)
#include "chrome/browser/cocoa/about_ipc_dialog.h"
#elif defined(OS_LINUX)
#include "chrome/browser/zygote_host_linux.h"
#endif
#if defined(USE_TCMALLOC)
#include "third_party/tcmalloc/chromium/src/google/malloc_extension.h"
#endif
using sync_api::SyncManager;
using base::Time;
using base::TimeDelta;
#if defined(USE_TCMALLOC)
// Glue between the callback task and the method in the singleton.
void AboutTcmallocRendererCallback(base::ProcessId pid, std::string output) {
Singleton<AboutTcmallocOutputs>::get()->RendererCallback(pid, output);
}
#endif
namespace {
// The (alphabetized) paths used for the about pages.
// Note: Keep these in sync with url_constants.h
const char kAppCacheInternalsPath[] = "appcache-internals";
const char kBlobInternalsPath[] = "blob-internals";
const char kCreditsPath[] = "credits";
const char kCachePath[] = "view-http-cache";
#if defined(OS_WIN)
const char kConflictsPath[] = "conflicts";
#endif
const char kDnsPath[] = "dns";
const char kFlagsPath[] = "flags";
const char kGpuPath[] = "gpu";
const char kHistogramsPath[] = "histograms";
const char kMemoryRedirectPath[] = "memory-redirect";
const char kMemoryPath[] = "memory";
const char kStatsPath[] = "stats";
const char kSyncPath[] = "sync";
const char kTasksPath[] = "tasks";
const char kTcmallocPath[] = "tcmalloc";
const char kTermsPath[] = "terms";
const char kVersionPath[] = "version";
const char kAboutPath[] = "about";
// Not about:* pages, but included to make about:about look nicer
const char kNetInternalsPath[] = "net-internals";
const char kPluginsPath[] = "plugins";
#if defined(OS_LINUX)
const char kLinuxProxyConfigPath[] = "linux-proxy-config";
const char kSandboxPath[] = "sandbox";
#endif
#if defined(OS_CHROMEOS)
const char kNetworkPath[] = "network";
const char kOSCreditsPath[] = "os-credits";
#endif
// Add path here to be included in about:about
const char *kAllAboutPaths[] = {
kAppCacheInternalsPath,
kBlobInternalsPath,
kCachePath,
kCreditsPath,
#if defined(OS_WIN)
kConflictsPath,
#endif
kDnsPath,
kFlagsPath,
kGpuPath,
kHistogramsPath,
kMemoryPath,
kNetInternalsPath,
kPluginsPath,
kStatsPath,
kSyncPath,
kTasksPath,
kTcmallocPath,
kTermsPath,
kVersionPath,
#if defined(OS_LINUX)
kLinuxProxyConfigPath,
kSandboxPath,
#endif
#if defined(OS_CHROMEOS)
kNetworkPath,
kOSCreditsPath,
#endif
};
// Points to the singleton AboutSource object, if any.
ChromeURLDataManager::DataSource* about_source = NULL;
// When you type about:memory, it actually loads an intermediate URL that
// redirects you to the final page. This avoids the problem where typing
// "about:memory" on the new tab page or any other page where a process
// transition would occur to the about URL will cause some confusion.
//
// The problem is that during the processing of the memory page, there are two
// processes active, the original and the destination one. This can create the
// impression that we're using more resources than we actually are. This
// redirect solves the problem by eliminating the process transition during the
// time that about memory is being computed.
std::string GetAboutMemoryRedirectResponse() {
return "<meta http-equiv=\"refresh\" "
"content=\"0;chrome://about/memory\">";
}
class AboutSource : public ChromeURLDataManager::DataSource {
public:
// Creates our datasource.
AboutSource();
// Called when the network layer has requested a resource underneath
// the path we registered.
virtual void StartDataRequest(const std::string& path,
bool is_off_the_record,
int request_id);
virtual std::string GetMimeType(const std::string&) const {
return "text/html";
}
// Send the response data.
void FinishDataRequest(const std::string& html, int request_id);
private:
virtual ~AboutSource();
DISALLOW_COPY_AND_ASSIGN(AboutSource);
};
// Handling about:memory is complicated enough to encapsulate its related
// methods into a single class. The user should create it (on the heap) and call
// its |StartFetch()| method.
class AboutMemoryHandler : public MemoryDetails {
public:
AboutMemoryHandler(AboutSource* source, int request_id)
: source_(source), request_id_(request_id) {}
virtual void OnDetailsAvailable();
private:
~AboutMemoryHandler() {}
void BindProcessMetrics(DictionaryValue* data,
ProcessMemoryInformation* info);
void AppendProcess(ListValue* child_data, ProcessMemoryInformation* info);
scoped_refptr<AboutSource> source_;
int request_id_;
DISALLOW_COPY_AND_ASSIGN(AboutMemoryHandler);
};
#if defined(OS_CHROMEOS)
// ChromeOSAboutVersionHandler is responsible for loading the Chrome OS
// version.
// ChromeOSAboutVersionHandler handles deleting itself once the version has
// been obtained and AboutSource notified.
class ChromeOSAboutVersionHandler {
public:
ChromeOSAboutVersionHandler(AboutSource* source, int request_id);
// Callback from chromeos::VersionLoader giving the version.
void OnVersion(chromeos::VersionLoader::Handle handle,
std::string version);
private:
// Where the results are fed to.
scoped_refptr<AboutSource> source_;
// ID identifying the request.
int request_id_;
// Handles asynchronously loading the version.
chromeos::VersionLoader loader_;
// Used to request the version.
CancelableRequestConsumer consumer_;
DISALLOW_COPY_AND_ASSIGN(ChromeOSAboutVersionHandler);
};
#endif
// Individual about handlers ---------------------------------------------------
std::string AboutAbout() {
std::string html;
html.append("<html><head><title>About Pages</title></head><body>\n");
html.append("<h2>List of About pages</h2><ul>\n");
for (size_t i = 0; i < arraysize(kAllAboutPaths); i++) {
if (kAllAboutPaths[i] == kAppCacheInternalsPath ||
kAllAboutPaths[i] == kBlobInternalsPath ||
kAllAboutPaths[i] == kCachePath ||
#if defined(OS_WIN)
kAllAboutPaths[i] == kConflictsPath ||
#endif
kAllAboutPaths[i] == kFlagsPath ||
kAllAboutPaths[i] == kNetInternalsPath ||
kAllAboutPaths[i] == kPluginsPath) {
html.append("<li><a href='chrome://");
} else {
html.append("<li><a href='chrome://about/");
}
html.append(kAllAboutPaths[i]);
html.append("/'>about:");
html.append(kAllAboutPaths[i]);
html.append("</a>\n");
}
const char *debug[] = { "crash", "hang", "shorthang", "gpucrash", "gpuhang" };
html.append("</ul><h2>For Debug</h2>");
html.append("</ul><p>The following pages are for debugging purposes only. "
"Because they crash or hang the renderer, they're not linked "
"directly; you can type them into the address bar if you need "
"them.</p><ul>");
for (size_t i = 0; i < arraysize(debug); i++) {
html.append("<li>");
html.append("about:");
html.append(debug[i]);
html.append("\n");
}
html.append("</ul></body></html>");
return html;
}
#if defined(OS_CHROMEOS)
std::string AboutNetwork(const std::string& query) {
int refresh;
base::StringToInt(query, &refresh);
return chromeos::CrosLibrary::Get()->GetNetworkLibrary()->
GetHtmlInfo(refresh);
}
#endif
// AboutDnsHandler bounces the request back to the IO thread to collect
// the DNS information.
class AboutDnsHandler : public base::RefCountedThreadSafe<AboutDnsHandler> {
public:
static void Start(AboutSource* source, int request_id) {
scoped_refptr<AboutDnsHandler> handler(
new AboutDnsHandler(source, request_id));
handler->StartOnUIThread();
}
private:
AboutDnsHandler(AboutSource* source, int request_id)
: source_(source),
request_id_(request_id) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
}
// Calls FinishOnUIThread() on completion.
void StartOnUIThread() {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
BrowserThread::PostTask(
BrowserThread::IO, FROM_HERE,
NewRunnableMethod(this, &AboutDnsHandler::StartOnIOThread));
}
void StartOnIOThread() {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
std::string data;
chrome_browser_net::PredictorGetHtmlInfo(&data);
BrowserThread::PostTask(
BrowserThread::UI, FROM_HERE,
NewRunnableMethod(this, &AboutDnsHandler::FinishOnUIThread, data));
}
void FinishOnUIThread(const std::string& data) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
source_->FinishDataRequest(data, request_id_);
}
// Where the results are fed to.
scoped_refptr<AboutSource> source_;
// ID identifying the request.
int request_id_;
DISALLOW_COPY_AND_ASSIGN(AboutDnsHandler);
};
#if defined(USE_TCMALLOC)
std::string AboutTcmalloc(const std::string& query) {
std::string data;
AboutTcmallocOutputsType* outputs =
Singleton<AboutTcmallocOutputs>::get()->outputs();
// Display any stats for which we sent off requests the last time.
data.append("<html><head><title>About tcmalloc</title></head><body>\n");
data.append("<p>Stats as of last page load;");
data.append("reload to get stats as of this page load.</p>\n");
data.append("<table width=\"100%\">\n");
for (AboutTcmallocOutputsType::const_iterator oit = outputs->begin();
oit != outputs->end();
oit++) {
data.append("<tr><td bgcolor=\"yellow\">");
data.append(oit->first);
data.append("</td></tr>\n");
data.append("<tr><td><pre>\n");
data.append(oit->second);
data.append("</pre></td></tr>\n");
}
data.append("</table>\n");
data.append("</body></html>\n");
// Reset our collector singleton.
outputs->clear();
// Populate the collector with stats from the local browser process
// and send off requests to all the renderer processes.
char buffer[1024 * 32];
MallocExtension::instance()->GetStats(buffer, sizeof(buffer));
std::string browser("Browser");
Singleton<AboutTcmallocOutputs>::get()->SetOutput(browser, buffer);
RenderProcessHost::iterator it(RenderProcessHost::AllHostsIterator());
while (!it.IsAtEnd()) {
it.GetCurrentValue()->Send(new ViewMsg_GetRendererTcmalloc);
it.Advance();
}
return data;
}
#endif
std::string AboutHistograms(const std::string& query) {
TimeDelta wait_time = TimeDelta::FromMilliseconds(10000);
HistogramSynchronizer* current_synchronizer =
HistogramSynchronizer::CurrentSynchronizer();
DCHECK(current_synchronizer != NULL);
current_synchronizer->FetchRendererHistogramsSynchronously(wait_time);
std::string data;
base::StatisticsRecorder::WriteHTMLGraph(query, &data);
return data;
}
void AboutMemory(AboutSource* source, int request_id) {
// The AboutMemoryHandler cleans itself up, but |StartFetch()| will want the
// refcount to be greater than 0.
scoped_refptr<AboutMemoryHandler>
handler(new AboutMemoryHandler(source, request_id));
handler->StartFetch();
}
#ifdef TRACK_ALL_TASK_OBJECTS
static std::string AboutObjects(const std::string& query) {
std::string data;
tracked_objects::ThreadData::WriteHTML(query, &data);
return data;
}
#endif // TRACK_ALL_TASK_OBJECTS
std::string AboutStats() {
// We keep the DictionaryValue tree live so that we can do delta
// stats computations across runs.
static DictionaryValue root;
base::StatsTable* table = base::StatsTable::current();
if (!table)
return std::string();
// We maintain two lists - one for counters and one for timers.
// Timers actually get stored on both lists.
ListValue* counters;
if (!root.GetList("counters", &counters)) {
counters = new ListValue();
root.Set("counters", counters);
}
ListValue* timers;
if (!root.GetList("timers", &timers)) {
timers = new ListValue();
root.Set("timers", timers);
}
// NOTE: Counters start at index 1.
for (int index = 1; index <= table->GetMaxCounters(); index++) {
// Get the counter's full name
std::string full_name = table->GetRowName(index);
if (full_name.length() == 0)
break;
DCHECK_EQ(':', full_name[1]);
char counter_type = full_name[0];
std::string name = full_name.substr(2);
// JSON doesn't allow '.' in names.
size_t pos;
while ((pos = name.find(".")) != std::string::npos)
name.replace(pos, 1, ":");
// Try to see if this name already exists.
DictionaryValue* counter = NULL;
for (size_t scan_index = 0;
scan_index < counters->GetSize(); scan_index++) {
DictionaryValue* dictionary;
if (counters->GetDictionary(scan_index, &dictionary)) {
std::string scan_name;
if (dictionary->GetString("name", &scan_name) && scan_name == name) {
counter = dictionary;
}
} else {
NOTREACHED(); // Should always be there
}
}
if (counter == NULL) {
counter = new DictionaryValue();
counter->SetString("name", name);
counters->Append(counter);
}
switch (counter_type) {
case 'c':
{
int new_value = table->GetRowValue(index);
int prior_value = 0;
int delta = 0;
if (counter->GetInteger("value", &prior_value)) {
delta = new_value - prior_value;
}
counter->SetInteger("value", new_value);
counter->SetInteger("delta", delta);
}
break;
case 'm':
{
// TODO(mbelshe): implement me.
}
break;
case 't':
{
int time = table->GetRowValue(index);
counter->SetInteger("time", time);
// Store this on the timers list as well.
timers->Append(counter);
}
break;
default:
NOTREACHED();
}
}
// Get about_stats.html
static const base::StringPiece stats_html(
ResourceBundle::GetSharedInstance().GetRawDataResource(
IDR_ABOUT_STATS_HTML));
// Create jstemplate and return.
std::string data = jstemplate_builder::GetTemplateHtml(
stats_html, &root, "t" /* template root node id */);
// Clear the timer list since we stored the data in the timers list as well.
for (int index = static_cast<int>(timers->GetSize())-1; index >= 0;
index--) {
Value* value;
timers->Remove(index, &value);
// We don't care about the value pointer; it's still tracked
// on the counters list.
}
return data;
}
#if defined(OS_LINUX)
std::string AboutLinuxProxyConfig() {
std::string data;
data.append("<!DOCTYPE HTML>\n");
data.append("<html><head><meta charset=\"utf-8\"><title>");
data.append(l10n_util::GetStringUTF8(IDS_ABOUT_LINUX_PROXY_CONFIG_TITLE));
data.append("</title>");
data.append("<style>body { max-width: 70ex; padding: 2ex 5ex; }</style>");
data.append("</head><body>\n");
FilePath binary = CommandLine::ForCurrentProcess()->GetProgram();
data.append(l10n_util::GetStringFUTF8(
IDS_ABOUT_LINUX_PROXY_CONFIG_BODY,
l10n_util::GetStringUTF16(IDS_PRODUCT_NAME),
ASCIIToUTF16(binary.BaseName().value())));
data.append("</body></html>\n");
return data;
}
void AboutSandboxRow(std::string* data, const std::string& prefix, int name_id,
bool good) {
data->append("<tr><td>");
data->append(prefix);
data->append(l10n_util::GetStringUTF8(name_id));
if (good) {
data->append("</td><td style=\"color: green;\">");
data->append(
l10n_util::GetStringUTF8(IDS_CONFIRM_MESSAGEBOX_YES_BUTTON_LABEL));
} else {
data->append("</td><td style=\"color: red;\">");
data->append(
l10n_util::GetStringUTF8(IDS_CONFIRM_MESSAGEBOX_NO_BUTTON_LABEL));
}
data->append("</td></tr>");
}
std::string AboutSandbox() {
std::string data;
data.append("<!DOCTYPE HTML>\n");
data.append("<html><head><meta charset=\"utf-8\"><title>");
data.append(l10n_util::GetStringUTF8(IDS_ABOUT_SANDBOX_TITLE));
data.append("</title>");
data.append("</head><body>\n");
data.append("<h1>");
data.append(l10n_util::GetStringUTF8(IDS_ABOUT_SANDBOX_TITLE));
data.append("</h1>");
const int status = Singleton<ZygoteHost>()->sandbox_status();
data.append("<table>");
AboutSandboxRow(&data, "", IDS_ABOUT_SANDBOX_SUID_SANDBOX,
status & ZygoteHost::kSandboxSUID);
if (status & ZygoteHost::kSandboxPIDNS) {
AboutSandboxRow(&data, "&nbsp;&nbsp;", IDS_ABOUT_SANDBOX_PID_NAMESPACES,
status & ZygoteHost::kSandboxPIDNS);
AboutSandboxRow(&data, "&nbsp;&nbsp;", IDS_ABOUT_SANDBOX_NET_NAMESPACES,
status & ZygoteHost::kSandboxNetNS);
}
AboutSandboxRow(&data, "", IDS_ABOUT_SANDBOX_SECCOMP_SANDBOX,
status & ZygoteHost::kSandboxSeccomp);
data.append("</table>");
bool good = ((status & ZygoteHost::kSandboxSUID) &&
(status & ZygoteHost::kSandboxPIDNS)) ||
(status & ZygoteHost::kSandboxSeccomp);
if (good) {
data.append("<p style=\"color: green\">");
data.append(l10n_util::GetStringUTF8(IDS_ABOUT_SANDBOX_OK));
} else {
data.append("<p style=\"color: red\">");
data.append(l10n_util::GetStringUTF8(IDS_ABOUT_SANDBOX_BAD));
}
data.append("</p>");
data.append("</body></html>\n");
return data;
}
#endif
std::string AboutVersion(DictionaryValue* localized_strings) {
localized_strings->SetString("title",
l10n_util::GetStringUTF16(IDS_ABOUT_VERSION_TITLE));
chrome::VersionInfo version_info;
std::string webkit_version = webkit_glue::GetWebKitVersion();
#ifdef CHROME_V8
std::string js_version(v8::V8::GetVersion());
std::string js_engine = "V8";
#else
std::string js_version = webkit_version;
std::string js_engine = "JavaScriptCore";
#endif
localized_strings->SetString("name",
l10n_util::GetStringUTF16(IDS_PRODUCT_NAME));
localized_strings->SetString("version", version_info.Version());
localized_strings->SetString("version_modifier",
platform_util::GetVersionStringModifier());
localized_strings->SetString("js_engine", js_engine);
localized_strings->SetString("js_version", js_version);
localized_strings->SetString("webkit_version", webkit_version);
localized_strings->SetString("company",
l10n_util::GetStringUTF16(IDS_ABOUT_VERSION_COMPANY_NAME));
localized_strings->SetString("copyright",
l10n_util::GetStringUTF16(IDS_ABOUT_VERSION_COPYRIGHT));
localized_strings->SetString("cl", version_info.LastChange());
localized_strings->SetString("official",
l10n_util::GetStringUTF16(
version_info.IsOfficialBuild() ?
IDS_ABOUT_VERSION_OFFICIAL
: IDS_ABOUT_VERSION_UNOFFICIAL));
localized_strings->SetString("user_agent_name",
l10n_util::GetStringUTF16(IDS_ABOUT_VERSION_USER_AGENT));
localized_strings->SetString("useragent", webkit_glue::GetUserAgent(GURL()));
localized_strings->SetString("command_line_name",
l10n_util::GetStringUTF16(IDS_ABOUT_VERSION_COMMAND_LINE));
#if defined(OS_WIN)
localized_strings->SetString("command_line",
WideToUTF16(CommandLine::ForCurrentProcess()->command_line_string()));
#elif defined(OS_POSIX)
std::string command_line = "";
typedef std::vector<std::string> ArgvList;
const ArgvList& argv = CommandLine::ForCurrentProcess()->argv();
for (ArgvList::const_iterator iter = argv.begin(); iter != argv.end(); iter++)
command_line += " " + *iter;
// TODO(viettrungluu): |command_line| could really have any encoding, whereas
// below we assumes it's UTF-8.
localized_strings->SetString("command_line", command_line);
#endif
base::StringPiece version_html(
ResourceBundle::GetSharedInstance().GetRawDataResource(
IDR_ABOUT_VERSION_HTML));
return jstemplate_builder::GetTemplatesHtml(
version_html, localized_strings, "t" /* template root node id */);
}
std::string AboutSync() {
FilePath user_data_dir;
if (!PathService::Get(chrome::DIR_USER_DATA, &user_data_dir))
return std::string();
ProfileManager* profile_manager = g_browser_process->profile_manager();
Profile* profile = profile_manager->GetDefaultProfile(user_data_dir);
ProfileSyncService* service = profile->GetProfileSyncService();
DictionaryValue strings;
if (!service) {
strings.SetString("summary", "SYNC DISABLED");
} else {
sync_ui_util::ConstructAboutInformation(service, &strings);
}
static const base::StringPiece sync_html(
ResourceBundle::GetSharedInstance().GetRawDataResource(
IDR_ABOUT_SYNC_HTML));
return jstemplate_builder::GetTemplatesHtml(
sync_html, &strings , "t" /* template root node id */);
}
std::string VersionNumberToString(uint32 value) {
int hi = (value >> 8) & 0xff;
int low = value & 0xff;
return base::IntToString(hi) + "." + base::IntToString(low);
}
namespace {
#if defined(OS_WIN)
// Output DxDiagNode tree as HTML tables and nested HTML unordered list
// elements.
void DxDiagNodeToHTML(std::string* output, const DxDiagNode& node) {
output->append("<table>\n");
for (std::map<std::string, std::string>::const_iterator it =
node.values.begin();
it != node.values.end();
++it) {
output->append("<tr><td><strong>");
output->append(EscapeForHTML(it->first));
output->append("</strong></td><td>");
output->append(EscapeForHTML(it->second));
output->append("</td></tr>\n");
}
output->append("</table>\n<ul>\n");
for (std::map<std::string, DxDiagNode>::const_iterator it =
node.children.begin();
it != node.children.end();
++it) {
output->append("<li><strong>");
output->append(EscapeForHTML(it->first));
output->append("</strong>");
DxDiagNodeToHTML(output, it->second);
output->append("</li>\n");
}
output->append("</ul>\n");
}
#endif // OS_WIN
}
std::string AboutGpu() {
const GPUInfo& gpu_info = GpuProcessHostUIShim::Get()->gpu_info();
std::string html;
html.append("<html><head><title>About GPU</title></head>\n");
if (gpu_info.progress() != GPUInfo::kComplete) {
GpuProcessHostUIShim::Get()->CollectGraphicsInfoAsynchronously();
// If it's not fully initialized yet, set a timeout to reload the page.
html.append("<body onload=\"setTimeout('window.location.reload(true)',");
html.append("2000)\">\n");
} else {
html.append("<body>\n");
}
html.append("<h2>GPU Information</h2>\n");
if (gpu_info.progress() == GPUInfo::kUninitialized) {
html.append("<p>Retrieving GPU information . . .</p>\n");
} else {
html.append("<table><tr>");
html.append("<td><strong>Initialization time</strong></td><td>");
html.append(base::Int64ToString(
gpu_info.initialization_time().InMilliseconds()));
html.append("</td></tr><tr><td>");
html.append("<strong>Vendor ID</strong></td><td>");
html.append(base::StringPrintf("0x%04x", gpu_info.vendor_id()));
html.append("</td></tr><tr><td>");
html.append("<strong>Device ID</strong></td><td>");
html.append(base::StringPrintf("0x%04x", gpu_info.device_id()));
html.append("</td></tr><tr><td>");
html.append("<strong>Driver Version</strong></td><td>");
html.append(WideToASCII(gpu_info.driver_version()).c_str());
html.append("</td></tr><tr><td>");
html.append("<strong>Pixel Shader Version</strong></td><td>");
html.append(VersionNumberToString(gpu_info.pixel_shader_version()).c_str());
html.append("</td></tr><tr><td>");
html.append("<strong>Vertex Shader Version</strong></td><td>");
html.append(VersionNumberToString(
gpu_info.vertex_shader_version()).c_str());
html.append("</td></tr><tr><td>");
html.append("<strong>GL Version</strong></td><td>");
html.append(VersionNumberToString(gpu_info.gl_version()).c_str());
html.append("</td></tr></table>");
#if defined(OS_WIN)
if (gpu_info.progress() != GPUInfo::kComplete) {
html.append("<p>Retrieving DirectX Diagnostics . . .</p>\n");
} else {
html.append("<h2>DirectX Diagnostics</h2>");
DxDiagNodeToHTML(&html, gpu_info.dx_diagnostics());
}
#endif
}
html.append("</body></html>");
return html;
}
// AboutSource -----------------------------------------------------------------
AboutSource::AboutSource()
: DataSource(chrome::kAboutScheme, MessageLoop::current()) {
// This should be a singleton.
DCHECK(!about_source);
about_source = this;
// Add us to the global URL handler on the IO thread.
BrowserThread::PostTask(
BrowserThread::IO, FROM_HERE,
NewRunnableMethod(
Singleton<ChromeURLDataManager>::get(),
&ChromeURLDataManager::AddDataSource,
make_scoped_refptr(this)));
}
AboutSource::~AboutSource() {
about_source = NULL;
}
void AboutSource::StartDataRequest(const std::string& path_raw,
bool is_off_the_record, int request_id) {
std::string path = path_raw;
std::string info;
if (path.find("/") != std::string::npos) {
size_t pos = path.find("/");
info = path.substr(pos + 1, path.length() - (pos + 1));
path = path.substr(0, pos);
}
path = StringToLowerASCII(path);
std::string response;
if (path == kDnsPath) {
AboutDnsHandler::Start(this, request_id);
return;
} else if (path == kHistogramsPath) {
response = AboutHistograms(info);
} else if (path == kMemoryPath) {
AboutMemory(this, request_id);
return;
} else if (path == kMemoryRedirectPath) {
response = GetAboutMemoryRedirectResponse();
#ifdef TRACK_ALL_TASK_OBJECTS
} else if (path == kTasksPath) {
response = AboutObjects(info);
#endif
} else if (path == kStatsPath) {
response = AboutStats();
#if defined(USE_TCMALLOC)
} else if (path == kTcmallocPath) {
response = AboutTcmalloc(info);
#endif
} else if (path == kVersionPath || path.empty()) {
#if defined(OS_CHROMEOS)
new ChromeOSAboutVersionHandler(this, request_id);
return;
#else
DictionaryValue value;
response = AboutVersion(&value);
#endif
} else if (path == kCreditsPath) {
response = ResourceBundle::GetSharedInstance().GetRawDataResource(
IDR_CREDITS_HTML).as_string();
} else if (path == kAboutPath) {
response = AboutAbout();
#if defined(OS_CHROMEOS)
} else if (path == kOSCreditsPath) {
response = ResourceBundle::GetSharedInstance().GetRawDataResource(
IDR_OS_CREDITS_HTML).as_string();
} else if (path == kNetworkPath) {
response = AboutNetwork(info);
#endif
} else if (path == kTermsPath) {
response = ResourceBundle::GetSharedInstance().GetRawDataResource(
IDR_TERMS_HTML).as_string();
#if defined(OS_LINUX)
} else if (path == kLinuxProxyConfigPath) {
response = AboutLinuxProxyConfig();
} else if (path == kSandboxPath) {
response = AboutSandbox();
#endif
} else if (path == kSyncPath) {
response = AboutSync();
} else if (path == kGpuPath) {
response = AboutGpu();
}
FinishDataRequest(response, request_id);
}
void AboutSource::FinishDataRequest(const std::string& response,
int request_id) {
scoped_refptr<RefCountedBytes> html_bytes(new RefCountedBytes);
html_bytes->data.resize(response.size());
std::copy(response.begin(), response.end(), html_bytes->data.begin());
SendResponse(request_id, html_bytes);
}
// AboutMemoryHandler ----------------------------------------------------------
// Helper for AboutMemory to bind results from a ProcessMetrics object
// to a DictionaryValue. Fills ws_usage and comm_usage so that the objects
// can be used in caller's scope (e.g for appending to a net total).
void AboutMemoryHandler::BindProcessMetrics(DictionaryValue* data,
ProcessMemoryInformation* info) {
DCHECK(data && info);
// Bind metrics to dictionary.
data->SetInteger("ws_priv", static_cast<int>(info->working_set.priv));
data->SetInteger("ws_shareable",
static_cast<int>(info->working_set.shareable));
data->SetInteger("ws_shared", static_cast<int>(info->working_set.shared));
data->SetInteger("comm_priv", static_cast<int>(info->committed.priv));
data->SetInteger("comm_map", static_cast<int>(info->committed.mapped));
data->SetInteger("comm_image", static_cast<int>(info->committed.image));
data->SetInteger("pid", info->pid);
data->SetString("version", WideToUTF16Hack(info->version));
data->SetInteger("processes", info->num_processes);
}
// Helper for AboutMemory to append memory usage information for all
// sub-processes (i.e. renderers, plugins) used by Chrome.
void AboutMemoryHandler::AppendProcess(ListValue* child_data,
ProcessMemoryInformation* info) {
DCHECK(child_data && info);
// Append a new DictionaryValue for this renderer to our list.
DictionaryValue* child = new DictionaryValue();
child_data->Append(child);
BindProcessMetrics(child, info);
std::string child_label(ChildProcessInfo::GetTypeNameInEnglish(info->type));
if (info->is_diagnostics)
child_label.append(" (diagnostics)");
child->SetString("child_name", child_label);
ListValue* titles = new ListValue();
child->Set("titles", titles);
for (size_t i = 0; i < info->titles.size(); ++i)
titles->Append(new StringValue(WideToUTF16Hack(info->titles[i])));
}
void AboutMemoryHandler::OnDetailsAvailable() {
// the root of the JSON hierarchy for about:memory jstemplate
DictionaryValue root;
ListValue* browsers = new ListValue();
root.Set("browsers", browsers);
const std::vector<ProcessData>& browser_processes = processes();
// Aggregate per-process data into browser summary data.
std::wstring log_string;
for (size_t index = 0; index < browser_processes.size(); index++) {
if (browser_processes[index].processes.size() == 0)
continue;
// Sum the information for the processes within this browser.
ProcessMemoryInformation aggregate;
ProcessMemoryInformationList::const_iterator iterator;
iterator = browser_processes[index].processes.begin();
aggregate.pid = iterator->pid;
aggregate.version = iterator->version;
while (iterator != browser_processes[index].processes.end()) {
if (!iterator->is_diagnostics ||
browser_processes[index].processes.size() == 1) {
aggregate.working_set.priv += iterator->working_set.priv;
aggregate.working_set.shared += iterator->working_set.shared;
aggregate.working_set.shareable += iterator->working_set.shareable;
aggregate.committed.priv += iterator->committed.priv;
aggregate.committed.mapped += iterator->committed.mapped;
aggregate.committed.image += iterator->committed.image;
aggregate.num_processes++;
}
++iterator;
}
DictionaryValue* browser_data = new DictionaryValue();
browsers->Append(browser_data);
browser_data->SetString("name",
WideToUTF16Hack(browser_processes[index].name));
BindProcessMetrics(browser_data, &aggregate);
// We log memory info as we record it.
if (log_string.length() > 0)
log_string.append(L", ");
log_string.append(browser_processes[index].name);
log_string.append(L", ");
log_string.append(UTF8ToWide(
base::Int64ToString(aggregate.working_set.priv)));
log_string.append(L", ");
log_string.append(UTF8ToWide(
base::Int64ToString(aggregate.working_set.shared)));
log_string.append(L", ");
log_string.append(UTF8ToWide(
base::Int64ToString(aggregate.working_set.shareable)));
}
if (log_string.length() > 0)
VLOG(1) << "memory: " << log_string;
// Set the browser & renderer detailed process data.
DictionaryValue* browser_data = new DictionaryValue();
root.Set("browzr_data", browser_data);
ListValue* child_data = new ListValue();
root.Set("child_data", child_data);
ProcessData process = browser_processes[0]; // Chrome is the first browser.
root.SetString("current_browser_name", WideToUTF16Hack(process.name));
for (size_t index = 0; index < process.processes.size(); index++) {
if (process.processes[index].type == ChildProcessInfo::BROWSER_PROCESS)
BindProcessMetrics(browser_data, &process.processes[index]);
else
AppendProcess(child_data, &process.processes[index]);
}
root.SetBoolean("show_other_browsers",
browser_defaults::kShowOtherBrowsersInAboutMemory);
// Get about_memory.html
static const base::StringPiece memory_html(
ResourceBundle::GetSharedInstance().GetRawDataResource(
IDR_ABOUT_MEMORY_HTML));
// Create jstemplate and return.
std::string template_html = jstemplate_builder::GetTemplateHtml(
memory_html, &root, "t" /* template root node id */);
source_->FinishDataRequest(template_html, request_id_);
}
#if defined(OS_CHROMEOS)
// ChromeOSAboutVersionHandler -----------------------------------------------
ChromeOSAboutVersionHandler::ChromeOSAboutVersionHandler(AboutSource* source,
int request_id)
: source_(source),
request_id_(request_id) {
loader_.GetVersion(&consumer_,
NewCallback(this, &ChromeOSAboutVersionHandler::OnVersion),
chromeos::VersionLoader::VERSION_FULL);
}
void ChromeOSAboutVersionHandler::OnVersion(
chromeos::VersionLoader::Handle handle,
std::string version) {
DictionaryValue localized_strings;
localized_strings.SetString("os_name",
l10n_util::GetStringUTF16(IDS_PRODUCT_OS_NAME));
localized_strings.SetString("os_version", version);
localized_strings.SetBoolean("is_chrome_os", true);
source_->FinishDataRequest(AboutVersion(&localized_strings), request_id_);
// CancelableRequestProvider isn't happy when it's deleted and servicing a
// task, so we delay the deletion.
MessageLoop::current()->DeleteSoon(FROM_HERE, this);
}
#endif
// Returns true if |url|'s spec starts with |about_specifier|, and is
// terminated by the start of a path.
bool StartsWithAboutSpecifier(const GURL& url, const char* about_specifier) {
return StartsWithASCII(url.spec(), about_specifier, true) &&
(url.spec().size() == strlen(about_specifier) ||
url.spec()[strlen(about_specifier)] == '/');
}
// Transforms a URL of the form "about:foo/XXX" to <url_prefix> + "XXX".
GURL RemapAboutURL(const std::string& url_prefix, const GURL& url) {
std::string path;
size_t split = url.spec().find('/');
if (split != std::string::npos)
path = url.spec().substr(split + 1);
return GURL(url_prefix + path);
}
} // namespace
// -----------------------------------------------------------------------------
bool WillHandleBrowserAboutURL(GURL* url, Profile* profile) {
// We only handle about: schemes.
if (!url->SchemeIs(chrome::kAboutScheme))
return false;
// about:blank is special. Frames are allowed to access about:blank,
// but they are not allowed to access other types of about pages.
// Just ignore the about:blank and let the TAB_CONTENTS_WEB handle it.
if (LowerCaseEqualsASCII(url->spec(), chrome::kAboutBlankURL))
return false;
// Rewrite about:cache/* URLs to chrome://view-http-cache/*
if (StartsWithAboutSpecifier(*url, chrome::kAboutCacheURL)) {
*url = RemapAboutURL(chrome::kNetworkViewCacheURL, *url);
return true;
}
#if defined(OS_WIN)
// Rewrite about:conflicts/* URLs to chrome://conflicts/*
if (StartsWithAboutSpecifier(*url, chrome::kAboutConflicts)) {
*url = GURL(chrome::kChromeUIConflictsURL);
return true;
}
#endif
// Rewrite about:flags and about:vaporware to chrome://flags/.
if (LowerCaseEqualsASCII(url->spec(), chrome::kAboutFlagsURL) ||
LowerCaseEqualsASCII(url->spec(), chrome::kAboutVaporwareURL)) {
*url = GURL(chrome::kChromeUIFlagsURL);
return true;
}
// Rewrite about:net-internals/* URLs to chrome://net-internals/*
if (StartsWithAboutSpecifier(*url, chrome::kAboutNetInternalsURL)) {
*url = RemapAboutURL(chrome::kNetworkViewInternalsURL, *url);
return true;
}
// Rewrite about:appcache-internals/* URLs to chrome://appcache/*
if (StartsWithAboutSpecifier(*url, chrome::kAboutAppCacheInternalsURL)) {
*url = RemapAboutURL(chrome::kAppCacheViewInternalsURL, *url);
return true;
}
// Rewrite about:plugins to chrome://plugins/.
if (LowerCaseEqualsASCII(url->spec(), chrome::kAboutPluginsURL)) {
*url = GURL(chrome::kChromeUIPluginsURL);
return true;
}
// Handle URL to crash the browser process.
if (LowerCaseEqualsASCII(url->spec(), chrome::kAboutBrowserCrash)) {
// Induce an intentional crash in the browser process.
int* bad_pointer = NULL;
*bad_pointer = 42;
return true;
}
// Handle URLs to wreck the gpu process.
if (LowerCaseEqualsASCII(url->spec(), chrome::kAboutGpuCrashURL)) {
GpuProcessHostUIShim::Get()->SendAboutGpuCrash();
return true;
}
if (LowerCaseEqualsASCII(url->spec(), chrome::kAboutGpuHangURL)) {
GpuProcessHostUIShim::Get()->SendAboutGpuHang();
return true;
}
// There are a few about: URLs that we hand over to the renderer. If the
// renderer wants them, don't do any rewriting.
if (chrome_about_handler::WillHandle(*url))
return false;
// Anything else requires our special handler, make sure its initialized.
// We only need to register the AboutSource once and it is kept globally.
// There is currently no way to remove a data source.
static bool initialized = false;
if (!initialized) {
about_source = new AboutSource();
initialized = true;
}
// Special case about:memory to go through a redirect before ending up on
// the final page. See GetAboutMemoryRedirectResponse above for why.
if (LowerCaseEqualsASCII(url->path(), kMemoryPath)) {
*url = GURL("chrome://about/memory-redirect");
return true;
}
// Rewrite the about URL to use chrome:. WebKit treats all about URLS the
// same (blank page), so if we want to display content, we need another
// scheme.
std::string about_url = "chrome://about/";
about_url.append(url->path());
*url = GURL(about_url);
return true;
}
// This function gets called with the fixed-up chrome: URLs, so we have to
// compare against those instead of "about:blah".
bool HandleNonNavigationAboutURL(const GURL& url) {
// about:ipc is currently buggy, so we disable it for official builds.
#if !defined(OFFICIAL_BUILD)
#if (defined(OS_MACOSX) || defined(OS_WIN)) && defined(IPC_MESSAGE_LOG_ENABLED)
if (LowerCaseEqualsASCII(url.spec(), chrome::kChromeUIIPCURL)) {
// Run the dialog. This will re-use the existing one if it's already up.
AboutIPCDialog::RunDialog();
return true;
}
#endif
#endif // OFFICIAL_BUILD
return false;
}