// Copyright (c) 2011 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 "base/callback.h" #include "base/command_line.h" #include "base/file_util.h" #include "base/i18n/number_formatting.h" #include "base/json/json_writer.h" #include "base/memory/singleton.h" #include "base/metrics/histogram.h" #include "base/metrics/stats_table.h" #include "base/path_service.h" #include "base/string_number_conversions.h" #include "base/string_piece.h" #include "base/string_util.h" #include "base/stringprintf.h" #include "base/threading/thread.h" #include "base/tracked_objects.h" #include "base/utf_string_conversions.h" #include "base/values.h" #include "chrome/browser/about_flags.h" #include "chrome/browser/browser_process.h" #include "chrome/browser/defaults.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/profiles/profile.h" #include "chrome/browser/profiles/profile_manager.h" #include "chrome/browser/ui/browser_dialogs.h" #include "chrome/browser/ui/webui/chrome_url_data_manager.h" #include "chrome/common/about_handler.h" #include "chrome/common/chrome_paths.h" #include "chrome/common/chrome_version_info.h" #include "chrome/common/jstemplate_builder.h" #include "chrome/common/net/gaia/google_service_auth_error.h" #include "chrome/common/render_messages.h" #include "chrome/common/url_constants.h" #include "content/browser/browser_thread.h" #include "content/browser/gpu_process_host.h" #include "content/browser/renderer_host/render_process_host.h" #include "content/browser/renderer_host/render_view_host.h" #include "content/common/gpu_messages.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 "net/base/escape.h" #include "ui/base/l10n/l10n_util.h" #include "ui/base/resource/resource_bundle.h" #include "webkit/glue/webkit_glue.h" #include "webkit/glue/plugins/plugin_list.h" #include "webkit/plugins/npapi/webplugininfo.h" #ifdef CHROME_V8 #include "v8/include/v8.h" #endif #if defined(OS_WIN) #include "chrome/browser/enumerate_modules_model_win.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/login/wizard_controller.h" #include "chrome/browser/chromeos/version_loader.h" #include "content/browser/zygote_host_linux.h" #elif defined(OS_LINUX) #include "content/browser/zygote_host_linux.h" #endif #if defined(USE_TCMALLOC) #include "third_party/tcmalloc/chromium/src/google/malloc_extension.h" #endif using base::Time; using base::TimeDelta; #if defined(USE_TCMALLOC) // static AboutTcmallocOutputs* AboutTcmallocOutputs::GetInstance() { return Singleton<AboutTcmallocOutputs>::get(); } AboutTcmallocOutputs::AboutTcmallocOutputs() {} AboutTcmallocOutputs::~AboutTcmallocOutputs() {} // Glue between the callback task and the method in the singleton. void AboutTcmallocRendererCallback(base::ProcessId pid, const std::string& output) { AboutTcmallocOutputs::GetInstance()->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-internals"; const char kHistogramsPath[] = "histograms"; const char kMemoryRedirectPath[] = "memory-redirect"; const char kMemoryPath[] = "memory"; const char kStatsPath[] = "stats"; 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"; const char kSyncInternalsPath[] = "sync-internals"; #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"; const char kEULAPathFormat[] = "/usr/share/chromeos-assets/eula/%s/eula.html"; #endif // Add path here to be included in about:about const char *kAllAboutPaths[] = { kAboutPath, kAppCacheInternalsPath, kBlobInternalsPath, kCachePath, kCreditsPath, #if defined(OS_WIN) kConflictsPath, #endif kDnsPath, kFlagsPath, kGpuPath, kHistogramsPath, kMemoryPath, kNetInternalsPath, kPluginsPath, kStatsPath, kSyncInternalsPath, #ifdef TRACK_ALL_TASK_OBJECTS kTasksPath, #endif // TRACK_ALL_TASK_OBJECTS kTcmallocPath, kTermsPath, kVersionPath, #if defined(OS_LINUX) kSandboxPath, #endif #if defined(OS_CHROMEOS) kNetworkPath, kOSCreditsPath, #endif }; // 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_incognito, 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); }; class ChromeOSTermsHandler : public base::RefCountedThreadSafe<ChromeOSTermsHandler> { public: static void Start(AboutSource* source, int request_id) { scoped_refptr<ChromeOSTermsHandler> handler( new ChromeOSTermsHandler(source, request_id)); handler->StartOnUIThread(); } private: ChromeOSTermsHandler(AboutSource* source, int request_id) : source_(source), request_id_(request_id), locale_(WizardController::GetInitialLocale()) { } void StartOnUIThread() { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); BrowserThread::PostTask( BrowserThread::FILE, FROM_HERE, NewRunnableMethod(this, &ChromeOSTermsHandler::LoadFileOnFileThread)); } void LoadFileOnFileThread() { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); std::string path = StringPrintf(kEULAPathFormat, locale_.c_str()); if (!file_util::ReadFileToString(FilePath(path), &contents_)) { // No EULA for given language - try en-US as default. path = StringPrintf(kEULAPathFormat, "en-US"); if (!file_util::ReadFileToString(FilePath(path), &contents_)) { // File with EULA not found, ResponseOnUIThread will load EULA from // resources if contents_ is empty. contents_.clear(); } } BrowserThread::PostTask( BrowserThread::UI, FROM_HERE, NewRunnableMethod(this, &ChromeOSTermsHandler::ResponseOnUIThread)); } void ResponseOnUIThread() { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); if (contents_.empty()) { contents_ = ResourceBundle::GetSharedInstance().GetRawDataResource( IDR_TERMS_HTML).as_string(); } source_->FinishDataRequest(contents_, request_id_); } // Where the results are fed to. scoped_refptr<AboutSource> source_; // ID identifying the request. int request_id_; std::string locale_; std::string contents_; DISALLOW_COPY_AND_ASSIGN(ChromeOSTermsHandler); }; #endif // Individual about handlers --------------------------------------------------- std::string AboutAbout() { std::string html("<html><head><title>About Pages</title></head>\n" "<body><h2>List of About pages</h2>\n<ul>"); std::vector<std::string> paths(AboutPaths()); for (std::vector<std::string>::const_iterator i = paths.begin(); i != paths.end(); ++i) { html += "<li><a href='chrome://"; if ((*i != kAppCacheInternalsPath) && (*i != kBlobInternalsPath) && (*i != kCachePath) && #if defined(OS_WIN) (*i != kConflictsPath) && #endif (*i != kFlagsPath) && (*i != kGpuPath) && (*i != kNetInternalsPath) && (*i != kPluginsPath)) { html += "about/"; } html += *i + "/'>about:" + *i + "</a></li>\n"; } const char *debug[] = { "crash", "kill", "hang", "shorthang", "gpucrash", "gpuhang" }; html += "</ul>\n<h2>For Debug</h2>\n" "<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>\n<ul>"; for (size_t i = 0; i < arraysize(debug); i++) html += "<li>about:" + std::string(debug[i]) + "</li>\n"; html += "</ul>\n</body></html>"; return html; } #if defined(OS_CHROMEOS) // Html output helper functions // TODO(stevenjb): L10N this. // Helper function to wrap Html with <th> tag. static std::string WrapWithTH(std::string text) { return "<th>" + text + "</th>"; } // Helper function to wrap Html with <td> tag. static std::string WrapWithTD(std::string text) { return "<td>" + text + "</td>"; } // Helper function to create an Html table header for a Network. static std::string ToHtmlTableHeader(const chromeos::Network* network) { std::string str = WrapWithTH("Name") + WrapWithTH("Active") + WrapWithTH("State"); if (network->type() == chromeos::TYPE_WIFI || network->type() == chromeos::TYPE_CELLULAR) { str += WrapWithTH("Auto-Connect"); str += WrapWithTH("Strength"); } if (network->type() == chromeos::TYPE_WIFI) { str += WrapWithTH("Encryption"); str += WrapWithTH("Passphrase"); str += WrapWithTH("Identity"); str += WrapWithTH("Certificate"); } if (network->type() == chromeos::TYPE_CELLULAR) { str += WrapWithTH("Technology"); str += WrapWithTH("Connectivity"); str += WrapWithTH("Activation"); str += WrapWithTH("Roaming"); } if (network->type() == chromeos::TYPE_VPN) { str += WrapWithTH("Host"); str += WrapWithTH("Provider Type"); str += WrapWithTH("PSK Passphrase"); str += WrapWithTH("Username"); str += WrapWithTH("User Passphrase"); } str += WrapWithTH("Error"); str += WrapWithTH("IP Address"); return str; } // Helper function to create an Html table row for a Network. static std::string ToHtmlTableRow(const chromeos::Network* network) { std::string str = WrapWithTD(network->name()) + WrapWithTD(base::IntToString(network->is_active())) + WrapWithTD(network->GetStateString()); if (network->type() == chromeos::TYPE_WIFI || network->type() == chromeos::TYPE_CELLULAR) { const chromeos::WirelessNetwork* wireless = static_cast<const chromeos::WirelessNetwork*>(network); str += WrapWithTD(base::IntToString(wireless->auto_connect())); str += WrapWithTD(base::IntToString(wireless->strength())); } if (network->type() == chromeos::TYPE_WIFI) { const chromeos::WifiNetwork* wifi = static_cast<const chromeos::WifiNetwork*>(network); str += WrapWithTD(wifi->GetEncryptionString()); str += WrapWithTD(std::string(wifi->passphrase().length(), '*')); str += WrapWithTD(wifi->identity()); str += WrapWithTD(wifi->cert_path()); } if (network->type() == chromeos::TYPE_CELLULAR) { const chromeos::CellularNetwork* cell = static_cast<const chromeos::CellularNetwork*>(network); str += WrapWithTH(cell->GetNetworkTechnologyString()); str += WrapWithTH(cell->GetConnectivityStateString()); str += WrapWithTH(cell->GetActivationStateString()); str += WrapWithTH(cell->GetRoamingStateString()); } if (network->type() == chromeos::TYPE_VPN) { const chromeos::VirtualNetwork* vpn = static_cast<const chromeos::VirtualNetwork*>(network); str += WrapWithTH(vpn->server_hostname()); str += WrapWithTH(vpn->GetProviderTypeString()); str += WrapWithTD(std::string(vpn->psk_passphrase().length(), '*')); str += WrapWithTH(vpn->username()); str += WrapWithTD(std::string(vpn->user_passphrase().length(), '*')); } str += WrapWithTD(network->failed() ? network->GetErrorString() : ""); str += WrapWithTD(network->ip_address()); return str; } std::string GetNetworkHtmlInfo(int refresh) { chromeos::NetworkLibrary* cros = chromeos::CrosLibrary::Get()->GetNetworkLibrary(); std::string output; output.append("<html><head><title>About Network</title>"); if (refresh > 0) output.append("<meta http-equiv=\"refresh\" content=\"" + base::IntToString(refresh) + "\"/>"); output.append("</head><body>"); if (refresh > 0) { output.append("(Auto-refreshing page every " + base::IntToString(refresh) + "s)"); } else { output.append("(To auto-refresh this page: about:network/<secs>)"); } if (cros->ethernet_enabled()) { output.append("<h3>Ethernet:</h3><table border=1>"); const chromeos::EthernetNetwork* ethernet = cros->ethernet_network(); if (ethernet) { output.append("<tr>" + ToHtmlTableHeader(ethernet) + "</tr>"); output.append("<tr>" + ToHtmlTableRow(ethernet) + "</tr>"); } } if (cros->wifi_enabled()) { output.append("</table><h3>Wifi Networks:</h3><table border=1>"); const chromeos::WifiNetworkVector& wifi_networks = cros->wifi_networks(); for (size_t i = 0; i < wifi_networks.size(); ++i) { if (i == 0) output.append("<tr>" + ToHtmlTableHeader(wifi_networks[i]) + "</tr>"); output.append("<tr>" + ToHtmlTableRow(wifi_networks[i]) + "</tr>"); } } if (cros->cellular_enabled()) { output.append("</table><h3>Cellular Networks:</h3><table border=1>"); const chromeos::CellularNetworkVector& cellular_networks = cros->cellular_networks(); for (size_t i = 0; i < cellular_networks.size(); ++i) { if (i == 0) output.append("<tr>" + ToHtmlTableHeader(cellular_networks[i]) + "</tr>"); output.append("<tr>" + ToHtmlTableRow(cellular_networks[i]) + "</tr>"); } } { output.append("</table><h3>Virtual Networks:</h3><table border=1>"); const chromeos::VirtualNetworkVector& virtual_networks = cros->virtual_networks(); for (size_t i = 0; i < virtual_networks.size(); ++i) { if (i == 0) output.append("<tr>" + ToHtmlTableHeader(virtual_networks[i]) + "</tr>"); output.append("<tr>" + ToHtmlTableRow(virtual_networks[i]) + "</tr>"); } } { output.append( "</table><h3>Remembered Wi-Fi Networks:</h3><table border=1>"); const chromeos::WifiNetworkVector& remembered_wifi_networks = cros->remembered_wifi_networks(); for (size_t i = 0; i < remembered_wifi_networks.size(); ++i) { if (i == 0) output.append("<tr>" + ToHtmlTableHeader(remembered_wifi_networks[i]) + "</tr>"); output.append("<tr>" + ToHtmlTableRow(remembered_wifi_networks[i]) + "</tr>"); } } output.append("</table></body></html>"); return output; } std::string AboutNetwork(const std::string& query) { int refresh; base::StringToInt(query, &refresh); return GetNetworkHtmlInfo(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 = AboutTcmallocOutputs::GetInstance()->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"); AboutTcmallocOutputs::GetInstance()->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 // Handler for filling in the "about:stats" page, as called by the browser's // About handler processing. // |query| is roughly the query string of the about:stats URL. // Returns a string containing the HTML to render for the about:stats page. // Conditional Output: // if |query| is "json", returns a JSON format of all counters. // if |query| is "raw", returns plain text of counter deltas. // otherwise, returns HTML with pretty JS/HTML to display the data. std::string AboutStats(const std::string& query) { // We keep the DictionaryValue tree live so that we can do delta // stats computations across runs. static DictionaryValue root; static base::TimeTicks last_sample_time = base::TimeTicks::Now(); base::TimeTicks now = base::TimeTicks::Now(); base::TimeDelta time_since_last_sample = now - last_sample_time; last_sample_time = now; 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(); } } std::string data; if (query == "json") { base::JSONWriter::WriteWithOptionalEscape(&root, true, false, &data); } else if (query == "raw") { // Dump the raw counters which have changed in text format. data = "<pre>"; data.append(StringPrintf("Counter changes in the last %ldms\n", static_cast<long int>(time_since_last_sample.InMilliseconds()))); for (size_t i = 0; i < counters->GetSize(); ++i) { Value* entry = NULL; bool rv = counters->Get(i, &entry); if (!rv) continue; // None of these should fail. DictionaryValue* counter = static_cast<DictionaryValue*>(entry); int delta; rv = counter->GetInteger("delta", &delta); if (!rv) continue; if (delta > 0) { std::string name; rv = counter->GetString("name", &name); if (!rv) continue; int value; rv = counter->GetInteger("value", &value); if (!rv) continue; data.append(name); data.append(":"); data.append(base::IntToString(delta)); data.append("\n"); } } data.append("</pre>"); } else { // Get about_stats.html and process a pretty page. static const base::StringPiece stats_html( ResourceBundle::GetSharedInstance().GetRawDataResource( IDR_ABOUT_STATS_HTML)); // Create jstemplate and return. 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 = ZygoteHost::GetInstance()->sandbox_status(); data.append("<table>"); AboutSandboxRow(&data, "", IDS_ABOUT_SANDBOX_SUID_SANDBOX, status & ZygoteHost::kSandboxSUID); if (status & ZygoteHost::kSandboxPIDNS) { AboutSandboxRow(&data, " ", IDS_ABOUT_SANDBOX_PID_NAMESPACES, status & ZygoteHost::kSandboxPIDNS); AboutSandboxRow(&data, " ", 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()); // Bug 79458: Need to evaluate the use of getting the version string on // this thread. base::ThreadRestrictions::ScopedAllowIO allow_io; localized_strings->SetString("version_modifier", platform_util::GetVersionStringModifier()); localized_strings->SetString("js_engine", js_engine); localized_strings->SetString("js_version", js_version); // Obtain the version of the first enabled Flash plugin. std::vector<webkit::npapi::WebPluginInfo> info_array; webkit::npapi::PluginList::Singleton()->GetPluginInfoArray( GURL(), "application/x-shockwave-flash", false, &info_array, NULL); string16 flash_version = l10n_util::GetStringUTF16(IDS_PLUGINS_DISABLED_PLUGIN); for (size_t i = 0; i < info_array.size(); ++i) { if (webkit::npapi::IsPluginEnabled(info_array[i])) { flash_version = info_array[i].version; break; } } localized_strings->SetString("flash_plugin", "Flash"); localized_strings->SetString("flash_version", flash_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 VersionNumberToString(uint32 value) { int hi = (value >> 8) & 0xff; int low = value & 0xff; return base::IntToString(hi) + "." + base::IntToString(low); } // AboutSource ----------------------------------------------------------------- AboutSource::AboutSource() : DataSource(chrome::kAboutScheme, MessageLoop::current()) { } AboutSource::~AboutSource() { } void AboutSource::StartDataRequest(const std::string& path_raw, bool is_incognito, 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(info); #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) { #if defined(OS_CHROMEOS) ChromeOSTermsHandler::Start(this, request_id); return; #else response = ResourceBundle::GetSharedInstance().GetRawDataResource( IDR_TERMS_HTML).as_string(); #endif #if defined(OS_LINUX) } else if (path == kLinuxProxyConfigPath) { response = AboutLinuxProxyConfig(); } else if (path == kSandboxPath) { response = AboutSandbox(); #endif } 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", 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::GetFullTypeNameInEnglish(info->type, info->renderer_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(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.empty()) 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", 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(UTF16ToWide(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", 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_.EnablePlatformVersions(true); 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 to chrome://flags/. if (LowerCaseEqualsASCII(url->spec(), chrome::kAboutFlagsURL)) { *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:gpu/* URLs to chrome://gpu-internals/* if (StartsWithAboutSpecifier(*url, chrome::kAboutGpuURL)) { *url = RemapAboutURL(chrome::kGpuInternalsURL, *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:sync-internals/* URLs (and about:sync, too, for // legacy reasons) to chrome://sync-internals/* if (StartsWithAboutSpecifier(*url, chrome::kAboutSyncInternalsURL) || StartsWithAboutSpecifier(*url, chrome::kAboutSyncURL)) { *url = RemapAboutURL(chrome::kSyncViewInternalsURL, *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)) { GpuProcessHost::SendOnIO( 0, content::CAUSE_FOR_GPU_LAUNCH_ABOUT_GPUCRASH, new GpuMsg_Crash()); } if (LowerCaseEqualsASCII(url->spec(), chrome::kAboutGpuHangURL)) { GpuProcessHost::SendOnIO( 0, content::CAUSE_FOR_GPU_LAUNCH_ABOUT_GPUHANG, new GpuMsg_Hang()); } // 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 it's initialized. InitializeAboutDataSource(profile); // 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; } void InitializeAboutDataSource(Profile* profile) { profile->GetChromeURLDataManager()->AddDataSource(new AboutSource()); } // 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. browser::ShowAboutIPCDialog(); return true; } #endif #endif // OFFICIAL_BUILD return false; } std::vector<std::string> AboutPaths() { std::vector<std::string> paths; paths.reserve(arraysize(kAllAboutPaths)); for (size_t i = 0; i < arraysize(kAllAboutPaths); i++) paths.push_back(kAllAboutPaths[i]); return paths; }