1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "chrome/browser/ui/webui/about_ui.h"
12 #include "base/bind.h"
13 #include "base/bind_helpers.h"
14 #include "base/callback.h"
15 #include "base/command_line.h"
16 #include "base/file_util.h"
17 #include "base/i18n/number_formatting.h"
18 #include "base/json/json_writer.h"
19 #include "base/memory/singleton.h"
20 #include "base/metrics/statistics_recorder.h"
21 #include "base/metrics/stats_table.h"
22 #include "base/path_service.h"
23 #include "base/strings/string_number_conversions.h"
24 #include "base/strings/string_piece.h"
25 #include "base/strings/string_util.h"
26 #include "base/strings/stringprintf.h"
27 #include "base/strings/utf_string_conversions.h"
28 #include "base/threading/thread.h"
29 #include "base/values.h"
30 #include "chrome/browser/about_flags.h"
31 #include "chrome/browser/browser_process.h"
32 #include "chrome/browser/defaults.h"
33 #include "chrome/browser/memory_details.h"
34 #include "chrome/browser/net/predictor.h"
35 #include "chrome/browser/profiles/profile.h"
36 #include "chrome/browser/profiles/profile_manager.h"
37 #include "chrome/browser/ui/browser_dialogs.h"
38 #include "chrome/common/chrome_paths.h"
39 #include "chrome/common/net/url_fixer_upper.h"
40 #include "chrome/common/render_messages.h"
41 #include "chrome/common/url_constants.h"
42 #include "content/public/browser/browser_thread.h"
43 #include "content/public/browser/render_process_host.h"
44 #include "content/public/browser/render_view_host.h"
45 #include "content/public/browser/url_data_source.h"
46 #include "content/public/browser/web_contents.h"
47 #include "content/public/common/content_client.h"
48 #include "content/public/common/process_type.h"
49 #include "google_apis/gaia/google_service_auth_error.h"
50 #include "grit/browser_resources.h"
51 #include "grit/chromium_strings.h"
52 #include "grit/generated_resources.h"
53 #include "grit/locale_settings.h"
54 #include "net/base/escape.h"
55 #include "net/base/filename_util.h"
56 #include "net/base/load_flags.h"
57 #include "net/http/http_response_headers.h"
58 #include "net/url_request/url_fetcher.h"
59 #include "net/url_request/url_request_status.h"
60 #include "ui/base/l10n/l10n_util.h"
61 #include "ui/base/resource/resource_bundle.h"
62 #include "ui/base/webui/jstemplate_builder.h"
63 #include "ui/base/webui/web_ui_util.h"
66 #if defined(ENABLE_THEMES)
67 #include "chrome/browser/ui/webui/theme_source.h"
70 #if defined(OS_LINUX) || defined(OS_OPENBSD)
71 #include "content/public/browser/zygote_host_linux.h"
72 #include "content/public/common/sandbox_linux.h"
76 #include "chrome/browser/enumerate_modules_model_win.h"
79 #if defined(OS_CHROMEOS)
80 #include "chrome/browser/browser_process_platform_part_chromeos.h"
81 #include "chrome/browser/chromeos/customization_document.h"
82 #include "chrome/browser/chromeos/memory/oom_priority_manager.h"
83 #include "chromeos/chromeos_switches.h"
87 using base::TimeDelta;
88 using content::BrowserThread;
89 using content::WebContents;
93 const char kCreditsJsPath[] = "credits.js";
94 const char kKeyboardUtilsPath[] = "keyboard_utils.js";
95 const char kMemoryJsPath[] = "memory.js";
96 const char kMemoryCssPath[] = "about_memory.css";
97 const char kStatsJsPath[] = "stats.js";
98 const char kStringsJsPath[] = "strings.js";
100 #if defined(OS_CHROMEOS)
101 // chrome://terms falls back to offline page after kOnlineTermsTimeoutSec.
102 const int kOnlineTermsTimeoutSec = 7;
103 #endif // defined(OS_CHROMEOS)
105 // When you type about:memory, it actually loads this intermediate URL that
106 // redirects you to the final page. This avoids the problem where typing
107 // "about:memory" on the new tab page or any other page where a process
108 // transition would occur to the about URL will cause some confusion.
110 // The problem is that during the processing of the memory page, there are two
111 // processes active, the original and the destination one. This can create the
112 // impression that we're using more resources than we actually are. This
113 // redirect solves the problem by eliminating the process transition during the
114 // time that about memory is being computed.
115 std::string GetAboutMemoryRedirectResponse(Profile* profile) {
116 return base::StringPrintf("<meta http-equiv='refresh' content='0;%s'>",
117 chrome::kChromeUIMemoryRedirectURL);
120 // Handling about:memory is complicated enough to encapsulate its related
121 // methods into a single class. The user should create it (on the heap) and call
122 // its |StartFetch()| method.
123 class AboutMemoryHandler : public MemoryDetails {
125 explicit AboutMemoryHandler(
126 const content::URLDataSource::GotDataCallback& callback)
127 : callback_(callback) {
130 virtual void OnDetailsAvailable() OVERRIDE;
133 virtual ~AboutMemoryHandler() {}
135 void BindProcessMetrics(base::DictionaryValue* data,
136 ProcessMemoryInformation* info);
137 void AppendProcess(base::ListValue* child_data,
138 ProcessMemoryInformation* info);
140 content::URLDataSource::GotDataCallback callback_;
142 DISALLOW_COPY_AND_ASSIGN(AboutMemoryHandler);
145 #if defined(OS_CHROMEOS)
147 // Helper class that fetches the online Chrome OS terms. Empty string is
148 // returned once fetching failed or exceeded |kOnlineTermsTimeoutSec|.
149 class ChromeOSOnlineTermsHandler : public net::URLFetcherDelegate {
151 typedef base::Callback<void (ChromeOSOnlineTermsHandler*)> FetchCallback;
153 explicit ChromeOSOnlineTermsHandler(const FetchCallback& callback,
154 const std::string& locale)
155 : fetch_callback_(callback) {
156 std::string eula_URL = base::StringPrintf(chrome::kOnlineEulaURLPath,
158 eula_fetcher_.reset(net::URLFetcher::Create(0 /* ID used for testing */,
160 net::URLFetcher::GET,
162 eula_fetcher_->SetRequestContext(
163 g_browser_process->system_request_context());
164 eula_fetcher_->AddExtraRequestHeader("Accept: text/html");
165 eula_fetcher_->SetLoadFlags(net::LOAD_DO_NOT_SEND_COOKIES |
166 net::LOAD_DO_NOT_SAVE_COOKIES |
167 net::LOAD_DISABLE_CACHE);
168 eula_fetcher_->Start();
169 // Abort the download attempt if it takes longer than one minute.
170 download_timer_.Start(FROM_HERE,
171 base::TimeDelta::FromSeconds(kOnlineTermsTimeoutSec),
173 &ChromeOSOnlineTermsHandler::OnDownloadTimeout);
176 void GetResponseResult(std::string* response_string) {
177 std::string mime_type;
178 if (!eula_fetcher_ ||
179 !eula_fetcher_->GetStatus().is_success() ||
180 eula_fetcher_->GetResponseCode() != 200 ||
181 !eula_fetcher_->GetResponseHeaders()->GetMimeType(&mime_type) ||
182 mime_type != "text/html" ||
183 !eula_fetcher_->GetResponseAsString(response_string)) {
184 response_string->clear();
189 // Prevents allocation on the stack. ChromeOSOnlineTermsHandler should be
190 // created by 'operator new'. |this| takes care of destruction.
191 virtual ~ChromeOSOnlineTermsHandler() {}
193 // net::URLFetcherDelegate:
194 virtual void OnURLFetchComplete(const net::URLFetcher* source) OVERRIDE {
195 if (source != eula_fetcher_.get()) {
196 NOTREACHED() << "Callback from foreign URL fetcher";
199 fetch_callback_.Run(this);
203 void OnDownloadTimeout() {
204 eula_fetcher_.reset();
205 fetch_callback_.Run(this);
209 // Timer that enforces a timeout on the attempt to download the
211 base::OneShotTimer<ChromeOSOnlineTermsHandler> download_timer_;
213 // |fetch_callback_| called when fetching succeeded or failed.
214 FetchCallback fetch_callback_;
216 // Helper to fetch online eula.
217 scoped_ptr<net::URLFetcher> eula_fetcher_;
219 DISALLOW_COPY_AND_ASSIGN(ChromeOSOnlineTermsHandler);
222 class ChromeOSTermsHandler
223 : public base::RefCountedThreadSafe<ChromeOSTermsHandler> {
225 static void Start(const std::string& path,
226 const content::URLDataSource::GotDataCallback& callback) {
227 scoped_refptr<ChromeOSTermsHandler> handler(
228 new ChromeOSTermsHandler(path, callback));
229 handler->StartOnUIThread();
233 friend class base::RefCountedThreadSafe<ChromeOSTermsHandler>;
235 ChromeOSTermsHandler(const std::string& path,
236 const content::URLDataSource::GotDataCallback& callback)
239 // Previously we were using "initial locale" http://crbug.com/145142
240 locale_(g_browser_process->GetApplicationLocale()) {
243 virtual ~ChromeOSTermsHandler() {}
245 void StartOnUIThread() {
246 DCHECK_CURRENTLY_ON(BrowserThread::UI);
247 if (path_ == chrome::kOemEulaURLPath) {
248 // Load local OEM EULA from the disk.
249 BrowserThread::PostTask(
250 BrowserThread::FILE, FROM_HERE,
251 base::Bind(&ChromeOSTermsHandler::LoadOemEulaFileOnFileThread, this));
253 // Try to load online version of ChromeOS terms first.
254 // ChromeOSOnlineTermsHandler object destroys itself.
255 new ChromeOSOnlineTermsHandler(
256 base::Bind(&ChromeOSTermsHandler::OnOnlineEULAFetched, this),
261 void OnOnlineEULAFetched(ChromeOSOnlineTermsHandler* loader) {
262 DCHECK_CURRENTLY_ON(BrowserThread::UI);
263 loader->GetResponseResult(&contents_);
264 if (contents_.empty()) {
265 // Load local ChromeOS terms from the file.
266 BrowserThread::PostTask(
267 BrowserThread::FILE, FROM_HERE,
268 base::Bind(&ChromeOSTermsHandler::LoadEulaFileOnFileThread, this));
270 ResponseOnUIThread();
274 void LoadOemEulaFileOnFileThread() {
275 DCHECK_CURRENTLY_ON(BrowserThread::FILE);
276 const chromeos::StartupCustomizationDocument* customization =
277 chromeos::StartupCustomizationDocument::GetInstance();
278 if (customization->IsReady()) {
279 base::FilePath oem_eula_file_path;
280 if (net::FileURLToFilePath(GURL(customization->GetEULAPage(locale_)),
281 &oem_eula_file_path)) {
282 if (!base::ReadFileToString(oem_eula_file_path, &contents_)) {
287 BrowserThread::PostTask(
288 BrowserThread::UI, FROM_HERE,
289 base::Bind(&ChromeOSTermsHandler::ResponseOnUIThread, this));
292 void LoadEulaFileOnFileThread() {
293 std::string file_path =
294 base::StringPrintf(chrome::kEULAPathFormat, locale_.c_str());
295 if (!base::ReadFileToString(base::FilePath(file_path), &contents_)) {
296 // No EULA for given language - try en-US as default.
297 file_path = base::StringPrintf(chrome::kEULAPathFormat, "en-US");
298 if (!base::ReadFileToString(base::FilePath(file_path), &contents_)) {
299 // File with EULA not found, ResponseOnUIThread will load EULA from
300 // resources if contents_ is empty.
304 BrowserThread::PostTask(
305 BrowserThread::UI, FROM_HERE,
306 base::Bind(&ChromeOSTermsHandler::ResponseOnUIThread, this));
309 void ResponseOnUIThread() {
310 DCHECK_CURRENTLY_ON(BrowserThread::UI);
311 // If we fail to load Chrome OS EULA from disk, load it from resources.
312 // Do nothing if OEM EULA load failed.
313 if (contents_.empty() && path_ != chrome::kOemEulaURLPath)
314 contents_ = l10n_util::GetStringUTF8(IDS_TERMS_HTML);
315 callback_.Run(base::RefCountedString::TakeString(&contents_));
319 const std::string path_;
321 // Callback to run with the response.
322 content::URLDataSource::GotDataCallback callback_;
324 // Locale of the EULA.
325 const std::string locale_;
327 // EULA contents that was loaded from file.
328 std::string contents_;
330 DISALLOW_COPY_AND_ASSIGN(ChromeOSTermsHandler);
337 // Individual about handlers ---------------------------------------------------
341 void AppendHeader(std::string* output, int refresh,
342 const std::string& unescaped_title) {
343 output->append("<!DOCTYPE HTML>\n<html>\n<head>\n");
344 if (!unescaped_title.empty()) {
345 output->append("<title>");
346 output->append(net::EscapeForHTML(unescaped_title));
347 output->append("</title>\n");
349 output->append("<meta charset='utf-8'>\n");
351 output->append("<meta http-equiv='refresh' content='");
352 output->append(base::IntToString(refresh));
353 output->append("'/>\n");
357 void AppendBody(std::string *output) {
358 output->append("</head>\n<body>\n");
361 void AppendFooter(std::string *output) {
362 output->append("</body>\n</html>\n");
365 } // namespace about_ui
367 using about_ui::AppendHeader;
368 using about_ui::AppendBody;
369 using about_ui::AppendFooter;
373 std::string ChromeURLs() {
375 AppendHeader(&html, 0, "Chrome URLs");
377 html += "<h2>List of Chrome URLs</h2>\n<ul>\n";
378 std::vector<std::string> hosts(
379 chrome::kChromeHostURLs,
380 chrome::kChromeHostURLs + chrome::kNumberOfChromeHostURLs);
381 std::sort(hosts.begin(), hosts.end());
382 for (std::vector<std::string>::const_iterator i = hosts.begin();
383 i != hosts.end(); ++i)
384 html += "<li><a href='chrome://" + *i + "/'>chrome://" + *i + "</a></li>\n";
385 html += "</ul>\n<h2>For Debug</h2>\n"
386 "<p>The following pages are for debugging purposes only. Because they "
387 "crash or hang the renderer, they're not linked directly; you can type "
388 "them into the address bar if you need them.</p>\n<ul>";
389 for (int i = 0; i < chrome::kNumberOfChromeDebugURLs; i++)
390 html += "<li>" + std::string(chrome::kChromeDebugURLs[i]) + "</li>\n";
396 #if defined(OS_CHROMEOS)
398 // Html output helper functions
400 // Helper function to wrap HTML with a tag.
401 std::string WrapWithTag(const std::string& tag, const std::string& text) {
402 return "<" + tag + ">" + text + "</" + tag + ">";
405 // Helper function to wrap Html with <td> tag.
406 std::string WrapWithTD(const std::string& text) {
407 return "<td>" + text + "</td>";
410 // Helper function to wrap Html with <tr> tag.
411 std::string WrapWithTR(const std::string& text) {
412 return "<tr>" + text + "</tr>";
415 std::string AddStringRow(const std::string& name, const std::string& value) {
417 row.append(WrapWithTD(name));
418 row.append(WrapWithTD(value));
419 return WrapWithTR(row);
422 void AddContentSecurityPolicy(std::string* output) {
423 output->append("<meta http-equiv='Content-Security-Policy' "
424 "content='default-src 'none';'>");
427 // TODO(stevenjb): L10N AboutDiscards.
429 std::string AboutDiscardsRun() {
431 AppendHeader(&output, 0, "About discards");
433 base::StringPrintf("<meta http-equiv='refresh' content='2;%s'>",
434 chrome::kChromeUIDiscardsURL));
435 AddContentSecurityPolicy(&output);
436 output.append(WrapWithTag("p", "Discarding a tab..."));
437 g_browser_process->platform_part()->
438 oom_priority_manager()->LogMemoryAndDiscardTab();
439 AppendFooter(&output);
443 std::string AboutDiscards(const std::string& path) {
445 const char kRunCommand[] = "run";
446 if (path == kRunCommand)
447 return AboutDiscardsRun();
448 AppendHeader(&output, 0, "About discards");
449 AddContentSecurityPolicy(&output);
451 output.append("<h3>About discards</h3>");
453 "<p>Tabs sorted from most interesting to least interesting. The least "
454 "interesting tab may be discarded if we run out of physical memory.</p>");
456 chromeos::OomPriorityManager* oom =
457 g_browser_process->platform_part()->oom_priority_manager();
458 std::vector<base::string16> titles = oom->GetTabTitles();
459 if (!titles.empty()) {
460 output.append("<ul>");
461 std::vector<base::string16>::iterator it = titles.begin();
462 for ( ; it != titles.end(); ++it) {
463 std::string title = base::UTF16ToUTF8(*it);
464 title = net::EscapeForHTML(title);
465 output.append(WrapWithTag("li", title));
467 output.append("</ul>");
469 output.append("<p>None found. Wait 10 seconds, then refresh.</p>");
471 output.append(base::StringPrintf("%d discards this session. ",
472 oom->discard_count()));
473 output.append(base::StringPrintf("<a href='%s%s'>Discard tab now</a>",
474 chrome::kChromeUIDiscardsURL,
477 base::SystemMemoryInfoKB meminfo;
478 base::GetSystemMemoryInfo(&meminfo);
479 output.append("<h3>System memory information in MB</h3>");
480 output.append("<table>");
481 // Start with summary statistics.
482 output.append(AddStringRow(
483 "Total", base::IntToString(meminfo.total / 1024)));
484 output.append(AddStringRow(
485 "Free", base::IntToString(meminfo.free / 1024)));
486 int mem_allocated_kb = meminfo.active_anon + meminfo.inactive_anon;
487 #if defined(ARCH_CPU_ARM_FAMILY)
488 // ARM counts allocated graphics memory separately from anonymous.
489 if (meminfo.gem_size != -1)
490 mem_allocated_kb += meminfo.gem_size / 1024;
492 output.append(AddStringRow(
493 "Allocated", base::IntToString(mem_allocated_kb / 1024)));
494 // Add some space, then detailed numbers.
495 output.append(AddStringRow(" ", " "));
496 output.append(AddStringRow(
497 "Buffered", base::IntToString(meminfo.buffers / 1024)));
498 output.append(AddStringRow(
499 "Cached", base::IntToString(meminfo.cached / 1024)));
500 output.append(AddStringRow(
501 "Active Anon", base::IntToString(meminfo.active_anon / 1024)));
502 output.append(AddStringRow(
503 "Inactive Anon", base::IntToString(meminfo.inactive_anon / 1024)));
504 output.append(AddStringRow(
505 "Shared", base::IntToString(meminfo.shmem / 1024)));
506 output.append(AddStringRow(
507 "Graphics", base::IntToString(meminfo.gem_size / 1024 / 1024)));
508 output.append("</table>");
510 AppendFooter(&output);
514 #endif // OS_CHROMEOS
516 // AboutDnsHandler bounces the request back to the IO thread to collect
517 // the DNS information.
518 class AboutDnsHandler : public base::RefCountedThreadSafe<AboutDnsHandler> {
520 static void Start(Profile* profile,
521 const content::URLDataSource::GotDataCallback& callback) {
522 scoped_refptr<AboutDnsHandler> handler(
523 new AboutDnsHandler(profile, callback));
524 handler->StartOnUIThread();
528 friend class base::RefCountedThreadSafe<AboutDnsHandler>;
530 AboutDnsHandler(Profile* profile,
531 const content::URLDataSource::GotDataCallback& callback)
533 callback_(callback) {
534 DCHECK_CURRENTLY_ON(BrowserThread::UI);
537 virtual ~AboutDnsHandler() {}
539 // Calls FinishOnUIThread() on completion.
540 void StartOnUIThread() {
541 DCHECK_CURRENTLY_ON(BrowserThread::UI);
542 chrome_browser_net::Predictor* predictor = profile_->GetNetworkPredictor();
543 BrowserThread::PostTask(
544 BrowserThread::IO, FROM_HERE,
545 base::Bind(&AboutDnsHandler::StartOnIOThread, this, predictor));
548 void StartOnIOThread(chrome_browser_net::Predictor* predictor) {
549 DCHECK_CURRENTLY_ON(BrowserThread::IO);
552 AppendHeader(&data, 0, "About DNS");
554 chrome_browser_net::Predictor::PredictorGetHtmlInfo(predictor, &data);
557 BrowserThread::PostTask(
558 BrowserThread::UI, FROM_HERE,
559 base::Bind(&AboutDnsHandler::FinishOnUIThread, this, data));
562 void FinishOnUIThread(const std::string& data) {
563 DCHECK_CURRENTLY_ON(BrowserThread::UI);
564 std::string data_copy(data);
565 callback_.Run(base::RefCountedString::TakeString(&data_copy));
570 // Callback to run with the response.
571 content::URLDataSource::GotDataCallback callback_;
573 DISALLOW_COPY_AND_ASSIGN(AboutDnsHandler);
576 void FinishMemoryDataRequest(
577 const std::string& path,
578 const content::URLDataSource::GotDataCallback& callback) {
579 if (path == kStringsJsPath) {
580 // The AboutMemoryHandler cleans itself up, but |StartFetch()| will want
581 // the refcount to be greater than 0.
582 scoped_refptr<AboutMemoryHandler> handler(new AboutMemoryHandler(callback));
583 // TODO(jamescook): Maybe this shouldn't update UMA?
584 handler->StartFetch(MemoryDetails::UPDATE_USER_METRICS);
586 int id = IDR_ABOUT_MEMORY_HTML;
587 if (path == kMemoryJsPath) {
588 id = IDR_ABOUT_MEMORY_JS;
589 } else if (path == kMemoryCssPath) {
590 id = IDR_ABOUT_MEMORY_CSS;
594 ResourceBundle::GetSharedInstance().GetRawDataResource(id).as_string();
595 callback.Run(base::RefCountedString::TakeString(&result));
599 // Handler for filling in the "about:stats" page, as called by the browser's
600 // About handler processing.
601 // |query| is roughly the query string of the about:stats URL.
602 // Returns a string containing the HTML to render for the about:stats page.
603 // Conditional Output:
604 // if |query| is "json", returns a JSON format of all counters.
605 // if |query| is "raw", returns plain text of counter deltas.
606 // otherwise, returns HTML with pretty JS/HTML to display the data.
607 std::string AboutStats(const std::string& query) {
608 // We keep the base::DictionaryValue tree live so that we can do delta
609 // stats computations across runs.
610 CR_DEFINE_STATIC_LOCAL(base::DictionaryValue, root, ());
611 static base::TimeTicks last_sample_time = base::TimeTicks::Now();
613 base::TimeTicks now = base::TimeTicks::Now();
614 base::TimeDelta time_since_last_sample = now - last_sample_time;
615 last_sample_time = now;
617 base::StatsTable* table = base::StatsTable::current();
619 return std::string();
621 // We maintain two lists - one for counters and one for timers.
622 // Timers actually get stored on both lists.
623 base::ListValue* counters;
624 if (!root.GetList("counters", &counters)) {
625 counters = new base::ListValue();
626 root.Set("counters", counters);
629 base::ListValue* timers;
630 if (!root.GetList("timers", &timers)) {
631 timers = new base::ListValue();
632 root.Set("timers", timers);
635 // NOTE: Counters start at index 1.
636 for (int index = 1; index <= table->GetMaxCounters(); index++) {
637 // Get the counter's full name
638 std::string full_name = table->GetRowName(index);
639 if (full_name.length() == 0)
641 DCHECK_EQ(':', full_name[1]);
642 char counter_type = full_name[0];
643 std::string name = full_name.substr(2);
645 // JSON doesn't allow '.' in names.
647 while ((pos = name.find(".")) != std::string::npos)
648 name.replace(pos, 1, ":");
650 // Try to see if this name already exists.
651 base::DictionaryValue* counter = NULL;
652 for (size_t scan_index = 0;
653 scan_index < counters->GetSize(); scan_index++) {
654 base::DictionaryValue* dictionary;
655 if (counters->GetDictionary(scan_index, &dictionary)) {
656 std::string scan_name;
657 if (dictionary->GetString("name", &scan_name) && scan_name == name) {
658 counter = dictionary;
661 NOTREACHED(); // Should always be there
665 if (counter == NULL) {
666 counter = new base::DictionaryValue();
667 counter->SetString("name", name);
668 counters->Append(counter);
671 switch (counter_type) {
674 int new_value = table->GetRowValue(index);
677 if (counter->GetInteger("value", &prior_value)) {
678 delta = new_value - prior_value;
680 counter->SetInteger("value", new_value);
681 counter->SetInteger("delta", delta);
686 // TODO(mbelshe): implement me.
691 int time = table->GetRowValue(index);
692 counter->SetInteger("time", time);
694 // Store this on the timers list as well.
695 timers->Append(counter);
704 if (query == "json" || query == kStringsJsPath) {
705 base::JSONWriter::WriteWithOptions(
707 base::JSONWriter::OPTIONS_PRETTY_PRINT,
709 if (query == kStringsJsPath)
710 data = "var templateData = " + data + ";";
711 } else if (query == "raw") {
712 // Dump the raw counters which have changed in text format.
714 data.append(base::StringPrintf("Counter changes in the last %ldms\n",
715 static_cast<long int>(time_since_last_sample.InMilliseconds())));
716 for (size_t i = 0; i < counters->GetSize(); ++i) {
717 base::Value* entry = NULL;
718 bool rv = counters->Get(i, &entry);
720 continue; // None of these should fail.
721 base::DictionaryValue* counter =
722 static_cast<base::DictionaryValue*>(entry);
724 rv = counter->GetInteger("delta", &delta);
729 rv = counter->GetString("name", &name);
733 rv = counter->GetInteger("value", &value);
738 data.append(base::IntToString(delta));
742 data.append("</pre>");
744 // Get about_stats.html/js from resource bundle.
745 data = ResourceBundle::GetSharedInstance().GetRawDataResource(
746 (query == kStatsJsPath ?
747 IDR_ABOUT_STATS_JS : IDR_ABOUT_STATS_HTML)).as_string();
749 if (query != kStatsJsPath) {
750 // Clear the timer list since we stored the data in the timers list
752 for (int index = static_cast<int>(timers->GetSize())-1; index >= 0;
754 scoped_ptr<base::Value> value;
755 timers->Remove(index, &value);
756 // We don't care about the value pointer; it's still tracked
757 // on the counters list.
758 ignore_result(value.release());
766 #if defined(OS_LINUX) || defined(OS_OPENBSD)
767 std::string AboutLinuxProxyConfig() {
769 AppendHeader(&data, 0,
770 l10n_util::GetStringUTF8(IDS_ABOUT_LINUX_PROXY_CONFIG_TITLE));
771 data.append("<style>body { max-width: 70ex; padding: 2ex 5ex; }</style>");
773 base::FilePath binary = CommandLine::ForCurrentProcess()->GetProgram();
774 data.append(l10n_util::GetStringFUTF8(
775 IDS_ABOUT_LINUX_PROXY_CONFIG_BODY,
776 l10n_util::GetStringUTF16(IDS_PRODUCT_NAME),
777 base::ASCIIToUTF16(binary.BaseName().value())));
782 void AboutSandboxRow(std::string* data, const std::string& prefix, int name_id,
784 data->append("<tr><td>");
785 data->append(prefix);
786 data->append(l10n_util::GetStringUTF8(name_id));
788 data->append("</td><td style='color: green;'>");
790 l10n_util::GetStringUTF8(IDS_CONFIRM_MESSAGEBOX_YES_BUTTON_LABEL));
792 data->append("</td><td style='color: red;'>");
794 l10n_util::GetStringUTF8(IDS_CONFIRM_MESSAGEBOX_NO_BUTTON_LABEL));
796 data->append("</td></tr>");
799 std::string AboutSandbox() {
801 AppendHeader(&data, 0, l10n_util::GetStringUTF8(IDS_ABOUT_SANDBOX_TITLE));
804 data.append(l10n_util::GetStringUTF8(IDS_ABOUT_SANDBOX_TITLE));
805 data.append("</h1>");
807 // Get expected sandboxing status of renderers.
808 const int status = content::ZygoteHost::GetInstance()->GetSandboxStatus();
810 data.append("<table>");
812 AboutSandboxRow(&data,
814 IDS_ABOUT_SANDBOX_SUID_SANDBOX,
815 status & content::kSandboxLinuxSUID);
816 AboutSandboxRow(&data, " ", IDS_ABOUT_SANDBOX_PID_NAMESPACES,
817 status & content::kSandboxLinuxPIDNS);
818 AboutSandboxRow(&data, " ", IDS_ABOUT_SANDBOX_NET_NAMESPACES,
819 status & content::kSandboxLinuxNetNS);
820 AboutSandboxRow(&data,
822 IDS_ABOUT_SANDBOX_SECCOMP_BPF_SANDBOX,
823 status & content::kSandboxLinuxSeccompBPF);
824 AboutSandboxRow(&data,
826 IDS_ABOUT_SANDBOX_YAMA_LSM,
827 status & content::kSandboxLinuxYama);
829 data.append("</table>");
831 // The setuid sandbox is required as our first-layer sandbox.
832 bool good_layer1 = status & content::kSandboxLinuxSUID &&
833 status & content::kSandboxLinuxPIDNS &&
834 status & content::kSandboxLinuxNetNS;
835 // A second-layer sandbox is also required to be adequately sandboxed.
836 bool good_layer2 = status & content::kSandboxLinuxSeccompBPF;
837 bool good = good_layer1 && good_layer2;
840 data.append("<p style='color: green'>");
841 data.append(l10n_util::GetStringUTF8(IDS_ABOUT_SANDBOX_OK));
843 data.append("<p style='color: red'>");
844 data.append(l10n_util::GetStringUTF8(IDS_ABOUT_SANDBOX_BAD));
853 // AboutMemoryHandler ----------------------------------------------------------
855 // Helper for AboutMemory to bind results from a ProcessMetrics object
856 // to a DictionaryValue. Fills ws_usage and comm_usage so that the objects
857 // can be used in caller's scope (e.g for appending to a net total).
858 void AboutMemoryHandler::BindProcessMetrics(base::DictionaryValue* data,
859 ProcessMemoryInformation* info) {
860 DCHECK(data && info);
862 // Bind metrics to dictionary.
863 data->SetInteger("ws_priv", static_cast<int>(info->working_set.priv));
864 data->SetInteger("ws_shareable",
865 static_cast<int>(info->working_set.shareable));
866 data->SetInteger("ws_shared", static_cast<int>(info->working_set.shared));
867 data->SetInteger("comm_priv", static_cast<int>(info->committed.priv));
868 data->SetInteger("comm_map", static_cast<int>(info->committed.mapped));
869 data->SetInteger("comm_image", static_cast<int>(info->committed.image));
870 data->SetInteger("pid", info->pid);
871 data->SetString("version", info->version);
872 data->SetInteger("processes", info->num_processes);
875 // Helper for AboutMemory to append memory usage information for all
876 // sub-processes (i.e. renderers, plugins) used by Chrome.
877 void AboutMemoryHandler::AppendProcess(base::ListValue* child_data,
878 ProcessMemoryInformation* info) {
879 DCHECK(child_data && info);
881 // Append a new DictionaryValue for this renderer to our list.
882 base::DictionaryValue* child = new base::DictionaryValue();
883 child_data->Append(child);
884 BindProcessMetrics(child, info);
886 std::string child_label(
887 ProcessMemoryInformation::GetFullTypeNameInEnglish(info->process_type,
888 info->renderer_type));
889 if (info->is_diagnostics)
890 child_label.append(" (diagnostics)");
891 child->SetString("child_name", child_label);
892 base::ListValue* titles = new base::ListValue();
893 child->Set("titles", titles);
894 for (size_t i = 0; i < info->titles.size(); ++i)
895 titles->Append(new base::StringValue(info->titles[i]));
898 void AboutMemoryHandler::OnDetailsAvailable() {
899 // the root of the JSON hierarchy for about:memory jstemplate
900 scoped_ptr<base::DictionaryValue> root(new base::DictionaryValue);
901 base::ListValue* browsers = new base::ListValue();
902 root->Set("browsers", browsers);
904 const std::vector<ProcessData>& browser_processes = processes();
906 // Aggregate per-process data into browser summary data.
907 base::string16 log_string;
908 for (size_t index = 0; index < browser_processes.size(); index++) {
909 if (browser_processes[index].processes.empty())
912 // Sum the information for the processes within this browser.
913 ProcessMemoryInformation aggregate;
914 ProcessMemoryInformationList::const_iterator iterator;
915 iterator = browser_processes[index].processes.begin();
916 aggregate.pid = iterator->pid;
917 aggregate.version = iterator->version;
918 while (iterator != browser_processes[index].processes.end()) {
919 if (!iterator->is_diagnostics ||
920 browser_processes[index].processes.size() == 1) {
921 aggregate.working_set.priv += iterator->working_set.priv;
922 aggregate.working_set.shared += iterator->working_set.shared;
923 aggregate.working_set.shareable += iterator->working_set.shareable;
924 aggregate.committed.priv += iterator->committed.priv;
925 aggregate.committed.mapped += iterator->committed.mapped;
926 aggregate.committed.image += iterator->committed.image;
927 aggregate.num_processes++;
931 base::DictionaryValue* browser_data = new base::DictionaryValue();
932 browsers->Append(browser_data);
933 browser_data->SetString("name", browser_processes[index].name);
935 BindProcessMetrics(browser_data, &aggregate);
937 // We log memory info as we record it.
938 if (!log_string.empty())
939 log_string += base::ASCIIToUTF16(", ");
940 log_string += browser_processes[index].name + base::ASCIIToUTF16(", ") +
941 base::Int64ToString16(aggregate.working_set.priv) +
942 base::ASCIIToUTF16(", ") +
943 base::Int64ToString16(aggregate.working_set.shared) +
944 base::ASCIIToUTF16(", ") +
945 base::Int64ToString16(aggregate.working_set.shareable);
947 if (!log_string.empty())
948 VLOG(1) << "memory: " << log_string;
950 // Set the browser & renderer detailed process data.
951 base::DictionaryValue* browser_data = new base::DictionaryValue();
952 root->Set("browzr_data", browser_data);
953 base::ListValue* child_data = new base::ListValue();
954 root->Set("child_data", child_data);
956 ProcessData process = browser_processes[0]; // Chrome is the first browser.
957 root->SetString("current_browser_name", process.name);
959 for (size_t index = 0; index < process.processes.size(); index++) {
960 if (process.processes[index].process_type == content::PROCESS_TYPE_BROWSER)
961 BindProcessMetrics(browser_data, &process.processes[index]);
963 AppendProcess(child_data, &process.processes[index]);
966 root->SetBoolean("show_other_browsers",
967 browser_defaults::kShowOtherBrowsersInAboutMemory);
969 base::DictionaryValue load_time_data;
970 load_time_data.SetString(
972 l10n_util::GetStringUTF16(IDS_MEMORY_USAGE_SUMMARY_DESC));
973 webui::SetFontAndTextDirection(&load_time_data);
974 load_time_data.Set("jstemplateData", root.release());
976 webui::UseVersion2 version2;
978 webui::AppendJsonJS(&load_time_data, &data);
979 callback_.Run(base::RefCountedString::TakeString(&data));
984 // AboutUIHTMLSource ----------------------------------------------------------
986 AboutUIHTMLSource::AboutUIHTMLSource(const std::string& source_name,
988 : source_name_(source_name),
991 AboutUIHTMLSource::~AboutUIHTMLSource() {}
993 std::string AboutUIHTMLSource::GetSource() const {
997 void AboutUIHTMLSource::StartDataRequest(
998 const std::string& path,
999 int render_process_id,
1000 int render_frame_id,
1001 const content::URLDataSource::GotDataCallback& callback) {
1002 std::string response;
1003 // Add your data source here, in alphabetical order.
1004 if (source_name_ == chrome::kChromeUIChromeURLsHost) {
1005 response = ChromeURLs();
1006 } else if (source_name_ == chrome::kChromeUICreditsHost) {
1007 int idr = IDR_CREDITS_HTML;
1008 if (path == kCreditsJsPath)
1009 idr = IDR_CREDITS_JS;
1010 else if (path == kKeyboardUtilsPath)
1011 idr = IDR_KEYBOARD_UTILS_JS;
1013 response = ResourceBundle::GetSharedInstance().GetRawDataResource(
1015 #if defined(OS_CHROMEOS)
1016 } else if (source_name_ == chrome::kChromeUIDiscardsHost) {
1017 response = AboutDiscards(path);
1019 } else if (source_name_ == chrome::kChromeUIDNSHost) {
1020 AboutDnsHandler::Start(profile(), callback);
1022 #if defined(OS_LINUX) || defined(OS_OPENBSD)
1023 } else if (source_name_ == chrome::kChromeUILinuxProxyConfigHost) {
1024 response = AboutLinuxProxyConfig();
1026 } else if (source_name_ == chrome::kChromeUIMemoryHost) {
1027 response = GetAboutMemoryRedirectResponse(profile());
1028 } else if (source_name_ == chrome::kChromeUIMemoryRedirectHost) {
1029 FinishMemoryDataRequest(path, callback);
1031 #if defined(OS_CHROMEOS)
1032 } else if (source_name_ == chrome::kChromeUIOSCreditsHost) {
1033 int idr = IDR_OS_CREDITS_HTML;
1034 if (path == kKeyboardUtilsPath)
1035 idr = IDR_KEYBOARD_UTILS_JS;
1036 response = ResourceBundle::GetSharedInstance().GetRawDataResource(
1039 #if defined(OS_LINUX) || defined(OS_OPENBSD)
1040 } else if (source_name_ == chrome::kChromeUISandboxHost) {
1041 response = AboutSandbox();
1043 } else if (source_name_ == chrome::kChromeUIStatsHost) {
1044 response = AboutStats(path);
1045 } else if (source_name_ == chrome::kChromeUITermsHost) {
1046 #if defined(OS_CHROMEOS)
1047 ChromeOSTermsHandler::Start(path, callback);
1050 response = l10n_util::GetStringUTF8(IDS_TERMS_HTML);
1054 FinishDataRequest(response, callback);
1057 void AboutUIHTMLSource::FinishDataRequest(
1058 const std::string& html,
1059 const content::URLDataSource::GotDataCallback& callback) {
1060 std::string html_copy(html);
1061 callback.Run(base::RefCountedString::TakeString(&html_copy));
1064 std::string AboutUIHTMLSource::GetMimeType(const std::string& path) const {
1065 if (path == kCreditsJsPath ||
1066 path == kKeyboardUtilsPath ||
1067 path == kStatsJsPath ||
1068 path == kStringsJsPath ||
1069 path == kMemoryJsPath) {
1070 return "application/javascript";
1075 bool AboutUIHTMLSource::ShouldAddContentSecurityPolicy() const {
1076 #if defined(OS_CHROMEOS)
1077 if (source_name_ == chrome::kChromeUIOSCreditsHost)
1080 return content::URLDataSource::ShouldAddContentSecurityPolicy();
1083 bool AboutUIHTMLSource::ShouldDenyXFrameOptions() const {
1084 #if defined(OS_CHROMEOS)
1085 if (source_name_ == chrome::kChromeUITermsHost) {
1086 // chrome://terms page is embedded in iframe to chrome://oobe.
1090 return content::URLDataSource::ShouldDenyXFrameOptions();
1093 AboutUI::AboutUI(content::WebUI* web_ui, const std::string& name)
1094 : WebUIController(web_ui) {
1095 Profile* profile = Profile::FromWebUI(web_ui);
1097 #if defined(ENABLE_THEMES)
1098 // Set up the chrome://theme/ source.
1099 ThemeSource* theme = new ThemeSource(profile);
1100 content::URLDataSource::Add(profile, theme);
1103 content::URLDataSource::Add(profile, new AboutUIHTMLSource(name, profile));