diff options
author | initial.commit <initial.commit@0039d316-1c4b-4281-b951-d872f2087c98> | 2008-07-26 23:55:29 +0000 |
---|---|---|
committer | initial.commit <initial.commit@0039d316-1c4b-4281-b951-d872f2087c98> | 2008-07-26 23:55:29 +0000 |
commit | 09911bf300f1a419907a9412154760efd0b7abc3 (patch) | |
tree | f131325fb4e2ad12c6d3504ab75b16dd92facfed /chrome/browser/browser_about_handler.cc | |
parent | 586acc5fe142f498261f52c66862fa417c3d52d2 (diff) | |
download | chromium_src-09911bf300f1a419907a9412154760efd0b7abc3.zip chromium_src-09911bf300f1a419907a9412154760efd0b7abc3.tar.gz chromium_src-09911bf300f1a419907a9412154760efd0b7abc3.tar.bz2 |
Add chrome to the repository.
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@15 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome/browser/browser_about_handler.cc')
-rw-r--r-- | chrome/browser/browser_about_handler.cc | 645 |
1 files changed, 645 insertions, 0 deletions
diff --git a/chrome/browser/browser_about_handler.cc b/chrome/browser/browser_about_handler.cc new file mode 100644 index 0000000..fca4e8d --- /dev/null +++ b/chrome/browser/browser_about_handler.cc @@ -0,0 +1,645 @@ +// Copyright 2008, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#include "chrome/browser/browser_about_handler.h" + +#include <string> +#include <vector> + +#include "base/file_version_info.h" +#include "base/histogram.h" +#include "base/image_util.h" +#include "base/process_util.h" +#include "base/stats_table.h" +#include "base/string_piece.h" +#include "base/string_util.h" +#include "base/tracked_objects.h" +#include "chrome/browser/browser.h" +#include "chrome/browser/browser_list.h" +#include "chrome/browser/browser_process.h" +#include "chrome/browser/browser_resources.h" +#include "chrome/browser/dom_ui/chrome_url_data_manager.h" +#include "chrome/browser/ipc_status_view.h" +#include "chrome/browser/memory_details.h" +#include "chrome/browser/net/dns_global.h" +#include "chrome/browser/plugin_process_host.h" +#include "chrome/browser/plugin_service.h" +#include "chrome/browser/profile.h" +#include "chrome/browser/profile_manager.h" +#include "chrome/browser/render_process_host.h" +#include "chrome/browser/render_view_host.h" +#include "chrome/common/jstemplate_builder.h" +#include "chrome/common/l10n_util.h" +#include "chrome/common/pref_names.h" +#include "chrome/common/pref_service.h" +#include "chrome/common/resource_bundle.h" +#include "chrome/renderer/about_handler.h" +#include "googleurl/src/gurl.h" +#include "webkit/glue/webkit_glue.h" + +#include "generated_resources.h" + +// The URL scheme used for the about ui. +static const char kAboutScheme[] = "about"; + +// The paths used for the about pages. +static const char kCachePath[] = "cache"; +static const char kDnsPath[] = "dns"; +static const char kHistogramsPath[] = "histograms"; +static const char kObjectsPath[] = "objects"; +static const char kMemoryPath[] = "memory"; +static const char kPluginsPath[] = "plugins"; +static const char kStatsPath[] = "stats"; +static const char kVersionPath[] = "version"; + +class AboutSource : public ChromeURLDataManager::DataSource { + public: + // Creates our datasource. + AboutSource(); + virtual ~AboutSource(); + + // Called when the network layer has requested a resource underneath + // the path we registered. + virtual void StartDataRequest(const std::string& path, int request_id); + + // Send the response data. + void FinishDataRequest(const std::string& html, int request_id); + + private: + DISALLOW_EVIL_CONSTRUCTORS(AboutSource); +}; + +// Handling about:memory is complicated enough to encapsulate it's +// related methods into a single class. +class AboutMemoryHandler : public MemoryDetails { + public: + AboutMemoryHandler(AboutSource* source, int request_id); + + virtual void OnDetailsAvailable(); + + private: + void BindProcessMetrics(DictionaryValue* data, + ProcessMemoryInformation* info); + void GetTabContentsTitles(RenderProcessHost* rph, ListValue* titles); + void AppendProcess(ListValue* renderers, ProcessMemoryInformation* info); + void FinishAboutMemory(); + + AboutSource* source_; + int request_id_; + DISALLOW_EVIL_CONSTRUCTORS(AboutMemoryHandler); +}; + +AboutSource::AboutSource() + : DataSource(kAboutScheme, MessageLoop::current()) { +} + +AboutSource::~AboutSource() { +} + +void AboutSource::StartDataRequest(const std::string& path_raw, + int request_id) { + std::string path = path_raw; + std::string info; + if (path.find("/") != -1) { + 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) { + response = BrowserAboutHandler::AboutDns(); + } else if (path == kHistogramsPath) { + response = BrowserAboutHandler::AboutHistograms(info); + } else if (path == kMemoryPath) { + BrowserAboutHandler::AboutMemory(this, request_id); + return; + } else if (path == kObjectsPath) { + response = BrowserAboutHandler::AboutObjects(info); + } else if (path == kPluginsPath) { + response = BrowserAboutHandler::AboutPlugins(); + } else if (path == kStatsPath) { + response = BrowserAboutHandler::AboutStats(); + } else if (path == kVersionPath || path.empty()) { + response = BrowserAboutHandler::AboutVersion(); + } + 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); +} + +// This is the top-level URL handler for chrome-internal: URLs, and exposed in +// our header file. +bool BrowserAboutHandler::MaybeHandle(GURL* url, + TabContentsType* result_type) { + if (!url->SchemeIs(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 (StringToLowerASCII(url->path()) == "blank") { + return false; + } + + // We create an about:cache mapping to the view-cache: internal URL. + if (StringToLowerASCII(url->path()) == kCachePath) { + *url = GURL("view-cache:"); + *result_type = TAB_CONTENTS_WEB; + return true; + } + + if (LowerCaseEqualsASCII(url->path(), "network")) { + // about:network doesn't have an internal protocol, so don't modify |url|. + *result_type = TAB_CONTENTS_NETWORK_STATUS_VIEW; + return true; + } + +#ifdef IPC_MESSAGE_LOG_ENABLED + if ((LowerCaseEqualsASCII(url->path(), "ipc")) && + (IPCStatusView::current() == NULL)) { + // about:ipc doesn't have an internal protocol, so don't modify |url|. + *result_type = TAB_CONTENTS_IPC_STATUS_VIEW; + return true; + } +#endif + + if (LowerCaseEqualsASCII(url->path(), "internets")) { + // about:internets doesn't have an internal protocol, so don't modify |url|. + *result_type = TAB_CONTENTS_ABOUT_INTERNETS_STATUS_VIEW; + return true; + } + + // There are a few about URLs that we hand over to the renderer. + // If the renderer wants them, let it have them. + if (AboutHandler::WillHandle(*url)) + return false; + + *result_type = TAB_CONTENTS_ABOUT_UI; + std::string about_url = "chrome-resource://about/"; + about_url.append(url->path()); + *url = GURL(about_url); + return true; +} + +BrowserAboutHandler::BrowserAboutHandler(Profile* profile, + SiteInstance* instance, + RenderViewHostFactory* render_view_factory) : + WebContents(profile, instance, render_view_factory, MSG_ROUTING_NONE, NULL) { + type_ = TAB_CONTENTS_ABOUT_UI; + + // 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(); + g_browser_process->io_thread()->message_loop()->PostTask(FROM_HERE, + NewRunnableMethod(&chrome_url_data_manager, + &ChromeURLDataManager::AddDataSource, + about_source_)); + initialized = true; + } +} + +bool BrowserAboutHandler::SupportsURL(GURL* url) { + // Enable this tab contents to access javascript urls. + if (url->SchemeIs("javascript")) + return true; + return WebContents::SupportsURL(url); +} + + +// static +std::string BrowserAboutHandler::AboutVersion() { + // Strings used in the JsTemplate file. + DictionaryValue localized_strings; + localized_strings.SetString(L"title", + l10n_util::GetString(IDS_ABOUT_VERSION_TITLE)); + scoped_ptr<FileVersionInfo> version_info( + FileVersionInfo::CreateFileVersionInfoForCurrentModule()); + if (version_info == NULL) { + DLOG(ERROR) << "Unable to create FileVersionInfo object"; + return std::string(); + } + localized_strings.SetString(L"name", + l10n_util::GetString(IDS_PRODUCT_NAME)); + localized_strings.SetString(L"version", version_info->file_version()); + localized_strings.SetString(L"company", + l10n_util::GetString(IDS_ABOUT_VERSION_COMPANY_NAME)); + localized_strings.SetString(L"copyright", + l10n_util::GetString(IDS_ABOUT_VERSION_COPYRIGHT)); + localized_strings.SetString(L"cl", version_info->last_change()); + if (version_info->is_official_build()) { + localized_strings.SetString(L"official", + l10n_util::GetString(IDS_ABOUT_VERSION_OFFICIAL)); + } else { + localized_strings.SetString(L"official", + l10n_util::GetString(IDS_ABOUT_VERSION_UNOFFICIAL)); + } + localized_strings.SetString(L"useragent", + UTF8ToWide(webkit_glue::GetDefaultUserAgent())); + + static const StringPiece version_html( + ResourceBundle::GetSharedInstance().GetRawDataResource( + IDR_ABOUT_VERSION_HTML)); + + return jstemplate_builder::GetTemplateHtml( + version_html, &localized_strings, "t" /* template root node id */); +} + +// static +std::string BrowserAboutHandler::AboutPlugins() { + // Strings used in the JsTemplate file. + DictionaryValue localized_strings; + localized_strings.SetString(L"title", + l10n_util::GetString(IDS_ABOUT_PLUGINS_TITLE)); + localized_strings.SetString(L"headingPlugs", + l10n_util::GetString(IDS_ABOUT_PLUGINS_HEADING_PLUGS)); + localized_strings.SetString(L"headingNoPlugs", + l10n_util::GetString(IDS_ABOUT_PLUGINS_HEADING_NOPLUGS)); + localized_strings.SetString(L"filename", + l10n_util::GetString(IDS_ABOUT_PLUGINS_FILENAME_LABEL)); + localized_strings.SetString(L"mimetype", + l10n_util::GetString(IDS_ABOUT_PLUGINS_MIMETYPE_LABEL)); + localized_strings.SetString(L"description", + l10n_util::GetString(IDS_ABOUT_PLUGINS_DESCRIPTION_LABEL)); + localized_strings.SetString(L"suffixes", + l10n_util::GetString(IDS_ABOUT_PLUGINS_SUFFIX_LABEL)); + localized_strings.SetString(L"enabled", + l10n_util::GetString(IDS_ABOUT_PLUGINS_ENABLED_LABEL)); + localized_strings.SetString(L"enabled_yes", + l10n_util::GetString(IDS_ABOUT_PLUGINS_ENABLED_YES)); + localized_strings.SetString(L"enabled_no", + l10n_util::GetString(IDS_ABOUT_PLUGINS_ENABLED_NO)); + + static const StringPiece plugins_html( + ResourceBundle::GetSharedInstance().GetRawDataResource( + IDR_ABOUT_PLUGINS_HTML)); + + return jstemplate_builder::GetTemplateHtml( + plugins_html, &localized_strings, "t" /* template root node id */); +} + +// static +std::string BrowserAboutHandler::AboutHistograms(const std::string query) { + std::string data; + StatisticsRecorder::WriteHTMLGraph(query, &data); + return data; +} + +// static +std::string BrowserAboutHandler::AboutObjects(const std::string& query) { + std::string data; + tracked_objects::ThreadData::WriteHTML(query, &data); + return data; +} + +// static +std::string BrowserAboutHandler::AboutDns() { + std::string data; + chrome_browser_net::DnsPrefetchGetHtmlInfo(&data); + return data; +} + +// static +std::string BrowserAboutHandler::AboutStats() { + // We keep the DictionaryValue tree live so that we can do delta + // stats computations across runs. + static DictionaryValue root; + + StatsTable* table = 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(L"counters", &counters)) { + counters = new ListValue(); + root.Set(L"counters", counters); + } + + ListValue* timers; + if (!root.GetList(L"timers", &timers)) { + timers = new ListValue(); + root.Set(L"timers", timers); + } + + // NOTE: Counters start at index 1. + for (int index = 1; index <= table->GetMaxCounters(); index++) { + // Get the counter's full name + std::wstring full_name = table->GetRowName(index); + if (full_name.length() == 0) + break; + DCHECK(full_name[1] == L':'); + wchar_t counter_type = full_name[0]; + std::wstring name = full_name.substr(2); + + // JSON doesn't allow '.' in names. + size_t pos; + while ( ( pos = name.find(L".") ) != -1 ) + name.replace(pos, 1, L":"); + + // 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::wstring scan_name; + if (dictionary->GetString(L"name", &scan_name) && + scan_name == name) { + counter = dictionary; + } + } else { + NOTREACHED(); // Should always be there + } + } + + if (counter == NULL) { + counter = new DictionaryValue(); + counter->SetString(L"name", name); + counters->Append(counter); + } + + switch (counter_type) { + case L'c': + { + int new_value = table->GetRowValue(index); + int prior_value = 0; + int delta = 0; + if (counter->GetInteger(L"value", &prior_value)) { + delta = new_value - prior_value; + } + counter->SetInteger(L"value", new_value); + counter->SetInteger(L"delta", delta); + } + break; + case L'm': + { + // TODO(mbelshe): implement me. + } + break; + case L't': + { + int time = table->GetRowValue(index); + counter->SetInteger(L"time", time); + + // Store this on the timers list as well. + timers->Append(counter); + } + break; + default: + NOTREACHED(); + } + } + + // Get about_stats.html + static const 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; +} + +AboutMemoryHandler::AboutMemoryHandler(AboutSource* source, int request_id) + : source_(source), + request_id_(request_id) { + StartFetch(); +} + +// 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(L"ws_priv", static_cast<int>(info->working_set.priv)); + data->SetInteger(L"ws_shareable", + static_cast<int>(info->working_set.shareable)); + data->SetInteger(L"ws_shared", static_cast<int>(info->working_set.shared)); + data->SetInteger(L"comm_priv", static_cast<int>(info->committed.priv)); + data->SetInteger(L"comm_map", static_cast<int>(info->committed.mapped)); + data->SetInteger(L"comm_image", static_cast<int>(info->committed.image)); + data->SetInteger(L"pid", info->pid); + data->SetString(L"version", info->version); + data->SetInteger(L"processes", info->num_processes); +} + +// Helper for AboutMemory to iterate over a RenderProcessHost's listeners +// and retrieve the tab titles. +void AboutMemoryHandler::GetTabContentsTitles(RenderProcessHost* rph, + ListValue* titles) { + RenderProcessHost::listeners_iterator iter; + // NOTE: This is a bit dangerous. We know that for now, listeners + // are always RenderWidgetHosts. But in theory, they don't + // have to be. + for (iter = rph->listeners_begin(); iter != rph->listeners_end(); iter++) { + RenderWidgetHost* widget = static_cast<RenderWidgetHost*>(iter->second); + DCHECK(widget); + if (!widget || !widget->IsRenderView()) + continue; + + RenderViewHost* host = static_cast<RenderViewHost*>(widget); + TabContents* contents = static_cast<WebContents*>(host->delegate()); + DCHECK(contents); + if (!contents) + continue; + + std::wstring title = contents->GetTitle(); + StringValue* val = NULL; + if (!title.length()) + title = L"Untitled"; + val = new StringValue(title); + titles->Append(val); + } +} + +// Helper for AboutMemory to append memory usage information for all +// sub-processes (renderers & plugins) used by chrome. +void AboutMemoryHandler::AppendProcess(ListValue* renderers, + ProcessMemoryInformation* info) { + DCHECK(renderers && info); + + // Append a new DictionaryValue for this renderer to our list. + DictionaryValue* renderer = new DictionaryValue(); + renderers->Append(renderer); + BindProcessMetrics(renderer, info); + + // Now get more information about the process. + + // First, figure out if it is a renderer. + RenderProcessHost::iterator renderer_iter; + for (renderer_iter = RenderProcessHost::begin(); renderer_iter != + RenderProcessHost::end(); ++renderer_iter) { + if (renderer_iter->second->pid() == info->pid) + break; + } + if (renderer_iter != RenderProcessHost::end()) { + std::wstring renderer_label(L"Tab "); + renderer_label.append(FormatNumber(renderer_iter->second->host_id())); + if (info->is_diagnostics) + renderer_label.append(L" (diagnostics)"); + renderer->SetString(L"renderer_id", renderer_label); + ListValue* titles = new ListValue(); + renderer->Set(L"titles", titles); + GetTabContentsTitles(renderer_iter->second, titles); + return; + } + + // Figure out if this is a plugin process. + for (size_t index = 0; index < plugins()->size(); ++index) { + if (info->pid == (*plugins())[index].pid) { + // It is a plugin! + std::wstring label(L"Plug-in"); + std::wstring name; + renderer->SetString(L"renderer_id", label); + FileVersionInfo* version_info = + FileVersionInfo::CreateFileVersionInfo( + (*plugins())[index].dll_path); + if (version_info) + name = version_info->product_name(); + ListValue* titles = new ListValue(); + renderer->Set(L"titles", titles); + titles->Append(new StringValue(name)); + return; + } + } +} + + +void AboutMemoryHandler::OnDetailsAvailable() { + // the root of the JSON hierarchy for about:memory jstemplate + DictionaryValue root; + ListValue* browsers = new ListValue(); + root.Set(L"browsers", browsers); + + ProcessData* browser_processes = processes(); + + // Aggregate per-process data into browser summary data. + std::wstring log_string; + for (int index = 0; index < MemoryDetails::MAX_BROWSERS; index++) { + if (browser_processes[index].processes.size() == 0) + continue; + + // Sum the information for the processes within this browser. + ProcessMemoryInformation aggregate; + ProcessMemoryInformationList::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(L"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(browser_processes[index].name); + log_string.append(L", "); + log_string.append(Int64ToWString(aggregate.working_set.priv)); + log_string.append(L", "); + log_string.append(Int64ToWString(aggregate.working_set.shared)); + log_string.append(L", "); + log_string.append(Int64ToWString(aggregate.working_set.shareable)); + } + if (log_string.length() > 0) + LOG(INFO) << "memory: " << log_string; + + + // Set the browser & renderer detailed process data. + DictionaryValue* browser_data = new DictionaryValue(); + root.Set(L"browzr_data", browser_data); + ListValue* renderer_data = new ListValue(); + root.Set(L"renderer_data", renderer_data); + + DWORD browser_pid = GetCurrentProcessId(); + ProcessData process = browser_processes[0]; // Chrome is the first browser. + for (size_t index = 0; index < process.processes.size(); index++) { + if (process.processes[index].pid == browser_pid) + BindProcessMetrics(browser_data, &process.processes[index]); + else + AppendProcess(renderer_data, &process.processes[index]); + } + + // Get about_memory.html + static const 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 */); + + AboutSource* about_source = static_cast<AboutSource*>(source_); + about_source->FinishDataRequest(template_html, request_id_); +} + +// static +void BrowserAboutHandler::AboutMemory(AboutSource* source, int request_id) { + // The AboutMemoryHandler cleans itself up. + new AboutMemoryHandler(source, request_id); +} |