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/files/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/strings/string_number_conversions.h"
23 #include "base/strings/string_piece.h"
24 #include "base/strings/string_util.h"
25 #include "base/strings/stringprintf.h"
26 #include "base/strings/utf_string_conversions.h"
27 #include "base/threading/thread.h"
28 #include "base/values.h"
29 #include "chrome/browser/about_flags.h"
30 #include "chrome/browser/browser_process.h"
31 #include "chrome/browser/defaults.h"
32 #include "chrome/browser/memory_details.h"
33 #include "chrome/browser/net/predictor.h"
34 #include "chrome/browser/profiles/profile.h"
35 #include "chrome/browser/profiles/profile_manager.h"
36 #include "chrome/browser/ui/browser_dialogs.h"
37 #include "chrome/common/chrome_paths.h"
38 #include "chrome/common/render_messages.h"
39 #include "chrome/common/url_constants.h"
40 #include "chrome/grit/chromium_strings.h"
41 #include "chrome/grit/generated_resources.h"
42 #include "chrome/grit/locale_settings.h"
43 #include "content/public/browser/browser_thread.h"
44 #include "content/public/browser/render_process_host.h"
45 #include "content/public/browser/render_view_host.h"
46 #include "content/public/browser/url_data_source.h"
47 #include "content/public/browser/web_contents.h"
48 #include "content/public/common/content_client.h"
49 #include "content/public/common/process_type.h"
50 #include "google_apis/gaia/google_service_auth_error.h"
51 #include "grit/browser_resources.h"
52 #include "net/base/escape.h"
53 #include "net/base/filename_util.h"
54 #include "net/base/load_flags.h"
55 #include "net/http/http_response_headers.h"
56 #include "net/url_request/url_fetcher.h"
57 #include "net/url_request/url_request_status.h"
58 #include "ui/base/l10n/l10n_util.h"
59 #include "ui/base/resource/resource_bundle.h"
60 #include "ui/base/webui/jstemplate_builder.h"
61 #include "ui/base/webui/web_ui_util.h"
64 #if defined(ENABLE_THEMES)
65 #include "chrome/browser/ui/webui/theme_source.h"
68 #if defined(OS_LINUX) || defined(OS_OPENBSD)
69 #include "content/public/browser/zygote_host_linux.h"
70 #include "content/public/common/sandbox_linux.h"
74 #include "chrome/browser/enumerate_modules_model_win.h"
77 #if defined(OS_CHROMEOS)
78 #include "chrome/browser/browser_process_platform_part_chromeos.h"
79 #include "chrome/browser/chromeos/customization/customization_document.h"
80 #include "chrome/browser/chromeos/memory/oom_priority_manager.h"
84 using base::TimeDelta
;
85 using content::BrowserThread
;
86 using content::WebContents
;
90 const char kCreditsJsPath
[] = "credits.js";
91 const char kMemoryJsPath
[] = "memory.js";
92 const char kMemoryCssPath
[] = "about_memory.css";
93 const char kStatsJsPath
[] = "stats.js";
94 const char kStringsJsPath
[] = "strings.js";
96 // When you type about:memory, it actually loads this intermediate URL that
97 // redirects you to the final page. This avoids the problem where typing
98 // "about:memory" on the new tab page or any other page where a process
99 // transition would occur to the about URL will cause some confusion.
101 // The problem is that during the processing of the memory page, there are two
102 // processes active, the original and the destination one. This can create the
103 // impression that we're using more resources than we actually are. This
104 // redirect solves the problem by eliminating the process transition during the
105 // time that about memory is being computed.
106 std::string
GetAboutMemoryRedirectResponse(Profile
* profile
) {
107 return base::StringPrintf("<meta http-equiv='refresh' content='0;%s'>",
108 chrome::kChromeUIMemoryRedirectURL
);
111 // Handling about:memory is complicated enough to encapsulate its related
112 // methods into a single class. The user should create it (on the heap) and call
113 // its |StartFetch()| method.
114 class AboutMemoryHandler
: public MemoryDetails
{
116 explicit AboutMemoryHandler(
117 const content::URLDataSource::GotDataCallback
& callback
)
118 : callback_(callback
) {
121 void OnDetailsAvailable() override
;
124 ~AboutMemoryHandler() override
{}
126 void BindProcessMetrics(base::DictionaryValue
* data
,
127 ProcessMemoryInformation
* info
);
128 void AppendProcess(base::ListValue
* child_data
,
129 ProcessMemoryInformation
* info
);
131 content::URLDataSource::GotDataCallback callback_
;
133 DISALLOW_COPY_AND_ASSIGN(AboutMemoryHandler
);
136 #if defined(OS_CHROMEOS)
138 const char kKeyboardUtilsPath
[] = "keyboard_utils.js";
140 // chrome://terms falls back to offline page after kOnlineTermsTimeoutSec.
141 const int kOnlineTermsTimeoutSec
= 7;
143 // Helper class that fetches the online Chrome OS terms. Empty string is
144 // returned once fetching failed or exceeded |kOnlineTermsTimeoutSec|.
145 class ChromeOSOnlineTermsHandler
: public net::URLFetcherDelegate
{
147 typedef base::Callback
<void (ChromeOSOnlineTermsHandler
*)> FetchCallback
;
149 explicit ChromeOSOnlineTermsHandler(const FetchCallback
& callback
,
150 const std::string
& locale
)
151 : fetch_callback_(callback
) {
152 std::string eula_URL
= base::StringPrintf(chrome::kOnlineEulaURLPath
,
154 eula_fetcher_
.reset(net::URLFetcher::Create(0 /* ID used for testing */,
156 net::URLFetcher::GET
,
158 eula_fetcher_
->SetRequestContext(
159 g_browser_process
->system_request_context());
160 eula_fetcher_
->AddExtraRequestHeader("Accept: text/html");
161 eula_fetcher_
->SetLoadFlags(net::LOAD_DO_NOT_SEND_COOKIES
|
162 net::LOAD_DO_NOT_SAVE_COOKIES
|
163 net::LOAD_DISABLE_CACHE
);
164 eula_fetcher_
->Start();
165 // Abort the download attempt if it takes longer than one minute.
166 download_timer_
.Start(FROM_HERE
,
167 base::TimeDelta::FromSeconds(kOnlineTermsTimeoutSec
),
169 &ChromeOSOnlineTermsHandler::OnDownloadTimeout
);
172 void GetResponseResult(std::string
* response_string
) {
173 std::string mime_type
;
174 if (!eula_fetcher_
||
175 !eula_fetcher_
->GetStatus().is_success() ||
176 eula_fetcher_
->GetResponseCode() != 200 ||
177 !eula_fetcher_
->GetResponseHeaders()->GetMimeType(&mime_type
) ||
178 mime_type
!= "text/html" ||
179 !eula_fetcher_
->GetResponseAsString(response_string
)) {
180 response_string
->clear();
185 // Prevents allocation on the stack. ChromeOSOnlineTermsHandler should be
186 // created by 'operator new'. |this| takes care of destruction.
187 virtual ~ChromeOSOnlineTermsHandler() {}
189 // net::URLFetcherDelegate:
190 virtual void OnURLFetchComplete(const net::URLFetcher
* source
) override
{
191 if (source
!= eula_fetcher_
.get()) {
192 NOTREACHED() << "Callback from foreign URL fetcher";
195 fetch_callback_
.Run(this);
199 void OnDownloadTimeout() {
200 eula_fetcher_
.reset();
201 fetch_callback_
.Run(this);
205 // Timer that enforces a timeout on the attempt to download the
207 base::OneShotTimer
<ChromeOSOnlineTermsHandler
> download_timer_
;
209 // |fetch_callback_| called when fetching succeeded or failed.
210 FetchCallback fetch_callback_
;
212 // Helper to fetch online eula.
213 scoped_ptr
<net::URLFetcher
> eula_fetcher_
;
215 DISALLOW_COPY_AND_ASSIGN(ChromeOSOnlineTermsHandler
);
218 class ChromeOSTermsHandler
219 : public base::RefCountedThreadSafe
<ChromeOSTermsHandler
> {
221 static void Start(const std::string
& path
,
222 const content::URLDataSource::GotDataCallback
& callback
) {
223 scoped_refptr
<ChromeOSTermsHandler
> handler(
224 new ChromeOSTermsHandler(path
, callback
));
225 handler
->StartOnUIThread();
229 friend class base::RefCountedThreadSafe
<ChromeOSTermsHandler
>;
231 ChromeOSTermsHandler(const std::string
& path
,
232 const content::URLDataSource::GotDataCallback
& callback
)
235 // Previously we were using "initial locale" http://crbug.com/145142
236 locale_(g_browser_process
->GetApplicationLocale()) {
239 virtual ~ChromeOSTermsHandler() {}
241 void StartOnUIThread() {
242 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
243 if (path_
== chrome::kOemEulaURLPath
) {
244 // Load local OEM EULA from the disk.
245 BrowserThread::PostTask(
246 BrowserThread::FILE, FROM_HERE
,
247 base::Bind(&ChromeOSTermsHandler::LoadOemEulaFileOnFileThread
, this));
249 // Try to load online version of ChromeOS terms first.
250 // ChromeOSOnlineTermsHandler object destroys itself.
251 new ChromeOSOnlineTermsHandler(
252 base::Bind(&ChromeOSTermsHandler::OnOnlineEULAFetched
, this),
257 void OnOnlineEULAFetched(ChromeOSOnlineTermsHandler
* loader
) {
258 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
259 loader
->GetResponseResult(&contents_
);
260 if (contents_
.empty()) {
261 // Load local ChromeOS terms from the file.
262 BrowserThread::PostTask(
263 BrowserThread::FILE, FROM_HERE
,
264 base::Bind(&ChromeOSTermsHandler::LoadEulaFileOnFileThread
, this));
266 ResponseOnUIThread();
270 void LoadOemEulaFileOnFileThread() {
271 DCHECK_CURRENTLY_ON(BrowserThread::FILE);
272 const chromeos::StartupCustomizationDocument
* customization
=
273 chromeos::StartupCustomizationDocument::GetInstance();
274 if (customization
->IsReady()) {
275 base::FilePath oem_eula_file_path
;
276 if (net::FileURLToFilePath(GURL(customization
->GetEULAPage(locale_
)),
277 &oem_eula_file_path
)) {
278 if (!base::ReadFileToString(oem_eula_file_path
, &contents_
)) {
283 BrowserThread::PostTask(
284 BrowserThread::UI
, FROM_HERE
,
285 base::Bind(&ChromeOSTermsHandler::ResponseOnUIThread
, this));
288 void LoadEulaFileOnFileThread() {
289 std::string file_path
=
290 base::StringPrintf(chrome::kEULAPathFormat
, locale_
.c_str());
291 if (!base::ReadFileToString(base::FilePath(file_path
), &contents_
)) {
292 // No EULA for given language - try en-US as default.
293 file_path
= base::StringPrintf(chrome::kEULAPathFormat
, "en-US");
294 if (!base::ReadFileToString(base::FilePath(file_path
), &contents_
)) {
295 // File with EULA not found, ResponseOnUIThread will load EULA from
296 // resources if contents_ is empty.
300 BrowserThread::PostTask(
301 BrowserThread::UI
, FROM_HERE
,
302 base::Bind(&ChromeOSTermsHandler::ResponseOnUIThread
, this));
305 void ResponseOnUIThread() {
306 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
307 // If we fail to load Chrome OS EULA from disk, load it from resources.
308 // Do nothing if OEM EULA load failed.
309 if (contents_
.empty() && path_
!= chrome::kOemEulaURLPath
)
310 contents_
= l10n_util::GetStringUTF8(IDS_TERMS_HTML
);
311 callback_
.Run(base::RefCountedString::TakeString(&contents_
));
315 const std::string path_
;
317 // Callback to run with the response.
318 content::URLDataSource::GotDataCallback callback_
;
320 // Locale of the EULA.
321 const std::string locale_
;
323 // EULA contents that was loaded from file.
324 std::string contents_
;
326 DISALLOW_COPY_AND_ASSIGN(ChromeOSTermsHandler
);
329 class ChromeOSCreditsHandler
330 : public base::RefCountedThreadSafe
<ChromeOSCreditsHandler
> {
332 static void Start(const std::string
& path
,
333 const content::URLDataSource::GotDataCallback
& callback
) {
334 scoped_refptr
<ChromeOSCreditsHandler
> handler(
335 new ChromeOSCreditsHandler(path
, callback
));
336 handler
->StartOnUIThread();
340 friend class base::RefCountedThreadSafe
<ChromeOSCreditsHandler
>;
342 ChromeOSCreditsHandler(
343 const std::string
& path
,
344 const content::URLDataSource::GotDataCallback
& callback
)
345 : path_(path
), callback_(callback
) {}
347 virtual ~ChromeOSCreditsHandler() {}
349 void StartOnUIThread() {
350 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
351 if (path_
== kKeyboardUtilsPath
) {
352 contents_
= ResourceBundle::GetSharedInstance()
353 .GetRawDataResource(IDR_KEYBOARD_UTILS_JS
)
355 ResponseOnUIThread();
358 // Load local Chrome OS credits from the disk.
359 BrowserThread::PostBlockingPoolTaskAndReply(
361 base::Bind(&ChromeOSCreditsHandler::LoadCreditsFileOnBlockingPool
,
363 base::Bind(&ChromeOSCreditsHandler::ResponseOnUIThread
, this));
366 void LoadCreditsFileOnBlockingPool() {
367 DCHECK(BrowserThread::GetBlockingPool()->RunsTasksOnCurrentThread());
368 base::FilePath
credits_file_path(chrome::kChromeOSCreditsPath
);
369 if (!base::ReadFileToString(credits_file_path
, &contents_
)) {
370 // File with credits not found, ResponseOnUIThread will load credits
371 // from resources if contents_ is empty.
376 void ResponseOnUIThread() {
377 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
378 // If we fail to load Chrome OS credits from disk, load it from resources.
379 if (contents_
.empty() && path_
!= kKeyboardUtilsPath
) {
380 contents_
= ResourceBundle::GetSharedInstance()
381 .GetRawDataResource(IDR_OS_CREDITS_HTML
)
384 callback_
.Run(base::RefCountedString::TakeString(&contents_
));
388 const std::string path_
;
390 // Callback to run with the response.
391 content::URLDataSource::GotDataCallback callback_
;
393 // Chrome OS credits contents that was loaded from file.
394 std::string contents_
;
396 DISALLOW_COPY_AND_ASSIGN(ChromeOSCreditsHandler
);
402 // Individual about handlers ---------------------------------------------------
406 void AppendHeader(std::string
* output
, int refresh
,
407 const std::string
& unescaped_title
) {
408 output
->append("<!DOCTYPE HTML>\n<html>\n<head>\n");
409 if (!unescaped_title
.empty()) {
410 output
->append("<title>");
411 output
->append(net::EscapeForHTML(unescaped_title
));
412 output
->append("</title>\n");
414 output
->append("<meta charset='utf-8'>\n");
416 output
->append("<meta http-equiv='refresh' content='");
417 output
->append(base::IntToString(refresh
));
418 output
->append("'/>\n");
422 void AppendBody(std::string
*output
) {
423 output
->append("</head>\n<body>\n");
426 void AppendFooter(std::string
*output
) {
427 output
->append("</body>\n</html>\n");
430 } // namespace about_ui
432 using about_ui::AppendHeader
;
433 using about_ui::AppendBody
;
434 using about_ui::AppendFooter
;
438 std::string
ChromeURLs() {
440 AppendHeader(&html
, 0, "Chrome URLs");
442 html
+= "<h2>List of Chrome URLs</h2>\n<ul>\n";
443 std::vector
<std::string
> hosts(
444 chrome::kChromeHostURLs
,
445 chrome::kChromeHostURLs
+ chrome::kNumberOfChromeHostURLs
);
446 std::sort(hosts
.begin(), hosts
.end());
447 for (std::vector
<std::string
>::const_iterator i
= hosts
.begin();
448 i
!= hosts
.end(); ++i
)
449 html
+= "<li><a href='chrome://" + *i
+ "/'>chrome://" + *i
+ "</a></li>\n";
450 html
+= "</ul>\n<h2>For Debug</h2>\n"
451 "<p>The following pages are for debugging purposes only. Because they "
452 "crash or hang the renderer, they're not linked directly; you can type "
453 "them into the address bar if you need them.</p>\n<ul>";
454 for (int i
= 0; i
< chrome::kNumberOfChromeDebugURLs
; i
++)
455 html
+= "<li>" + std::string(chrome::kChromeDebugURLs
[i
]) + "</li>\n";
461 #if defined(OS_CHROMEOS)
463 // Html output helper functions
465 // Helper function to wrap HTML with a tag.
466 std::string
WrapWithTag(const std::string
& tag
, const std::string
& text
) {
467 return "<" + tag
+ ">" + text
+ "</" + tag
+ ">";
470 // Helper function to wrap Html with <td> tag.
471 std::string
WrapWithTD(const std::string
& text
) {
472 return "<td>" + text
+ "</td>";
475 // Helper function to wrap Html with <tr> tag.
476 std::string
WrapWithTR(const std::string
& text
) {
477 return "<tr>" + text
+ "</tr>";
480 std::string
AddStringRow(const std::string
& name
, const std::string
& value
) {
482 row
.append(WrapWithTD(name
));
483 row
.append(WrapWithTD(value
));
484 return WrapWithTR(row
);
487 void AddContentSecurityPolicy(std::string
* output
) {
488 output
->append("<meta http-equiv='Content-Security-Policy' "
489 "content='default-src 'none';'>");
492 // TODO(stevenjb): L10N AboutDiscards.
494 std::string
AboutDiscardsRun() {
496 AppendHeader(&output
, 0, "About discards");
498 base::StringPrintf("<meta http-equiv='refresh' content='2;%s'>",
499 chrome::kChromeUIDiscardsURL
));
500 AddContentSecurityPolicy(&output
);
501 output
.append(WrapWithTag("p", "Discarding a tab..."));
502 g_browser_process
->platform_part()->
503 oom_priority_manager()->LogMemoryAndDiscardTab();
504 AppendFooter(&output
);
508 std::string
AboutDiscards(const std::string
& path
) {
510 const char kRunCommand
[] = "run";
511 if (path
== kRunCommand
)
512 return AboutDiscardsRun();
513 AppendHeader(&output
, 0, "About discards");
514 AddContentSecurityPolicy(&output
);
516 output
.append("<h3>About discards</h3>");
518 "<p>Tabs sorted from most interesting to least interesting. The least "
519 "interesting tab may be discarded if we run out of physical memory.</p>");
521 chromeos::OomPriorityManager
* oom
=
522 g_browser_process
->platform_part()->oom_priority_manager();
523 std::vector
<base::string16
> titles
= oom
->GetTabTitles();
524 if (!titles
.empty()) {
525 output
.append("<ul>");
526 std::vector
<base::string16
>::iterator it
= titles
.begin();
527 for ( ; it
!= titles
.end(); ++it
) {
528 std::string title
= base::UTF16ToUTF8(*it
);
529 title
= net::EscapeForHTML(title
);
530 output
.append(WrapWithTag("li", title
));
532 output
.append("</ul>");
534 output
.append("<p>None found. Wait 10 seconds, then refresh.</p>");
536 output
.append(base::StringPrintf("%d discards this session. ",
537 oom
->discard_count()));
538 output
.append(base::StringPrintf("<a href='%s%s'>Discard tab now</a>",
539 chrome::kChromeUIDiscardsURL
,
542 base::SystemMemoryInfoKB meminfo
;
543 base::GetSystemMemoryInfo(&meminfo
);
544 output
.append("<h3>System memory information in MB</h3>");
545 output
.append("<table>");
546 // Start with summary statistics.
547 output
.append(AddStringRow(
548 "Total", base::IntToString(meminfo
.total
/ 1024)));
549 output
.append(AddStringRow(
550 "Free", base::IntToString(meminfo
.free
/ 1024)));
551 int mem_allocated_kb
= meminfo
.active_anon
+ meminfo
.inactive_anon
;
552 #if defined(ARCH_CPU_ARM_FAMILY)
553 // ARM counts allocated graphics memory separately from anonymous.
554 if (meminfo
.gem_size
!= -1)
555 mem_allocated_kb
+= meminfo
.gem_size
/ 1024;
557 output
.append(AddStringRow(
558 "Allocated", base::IntToString(mem_allocated_kb
/ 1024)));
559 // Add some space, then detailed numbers.
560 output
.append(AddStringRow(" ", " "));
561 output
.append(AddStringRow(
562 "Buffered", base::IntToString(meminfo
.buffers
/ 1024)));
563 output
.append(AddStringRow(
564 "Cached", base::IntToString(meminfo
.cached
/ 1024)));
565 output
.append(AddStringRow(
566 "Active Anon", base::IntToString(meminfo
.active_anon
/ 1024)));
567 output
.append(AddStringRow(
568 "Inactive Anon", base::IntToString(meminfo
.inactive_anon
/ 1024)));
569 output
.append(AddStringRow(
570 "Shared", base::IntToString(meminfo
.shmem
/ 1024)));
571 output
.append(AddStringRow(
572 "Graphics", base::IntToString(meminfo
.gem_size
/ 1024 / 1024)));
573 output
.append("</table>");
575 AppendFooter(&output
);
579 #endif // OS_CHROMEOS
581 // AboutDnsHandler bounces the request back to the IO thread to collect
582 // the DNS information.
583 class AboutDnsHandler
: public base::RefCountedThreadSafe
<AboutDnsHandler
> {
585 static void Start(Profile
* profile
,
586 const content::URLDataSource::GotDataCallback
& callback
) {
587 scoped_refptr
<AboutDnsHandler
> handler(
588 new AboutDnsHandler(profile
, callback
));
589 handler
->StartOnUIThread();
593 friend class base::RefCountedThreadSafe
<AboutDnsHandler
>;
595 AboutDnsHandler(Profile
* profile
,
596 const content::URLDataSource::GotDataCallback
& callback
)
598 callback_(callback
) {
599 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
602 virtual ~AboutDnsHandler() {}
604 // Calls FinishOnUIThread() on completion.
605 void StartOnUIThread() {
606 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
607 chrome_browser_net::Predictor
* predictor
= profile_
->GetNetworkPredictor();
608 BrowserThread::PostTask(
609 BrowserThread::IO
, FROM_HERE
,
610 base::Bind(&AboutDnsHandler::StartOnIOThread
, this, predictor
));
613 void StartOnIOThread(chrome_browser_net::Predictor
* predictor
) {
614 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
617 AppendHeader(&data
, 0, "About DNS");
619 chrome_browser_net::Predictor::PredictorGetHtmlInfo(predictor
, &data
);
622 BrowserThread::PostTask(
623 BrowserThread::UI
, FROM_HERE
,
624 base::Bind(&AboutDnsHandler::FinishOnUIThread
, this, data
));
627 void FinishOnUIThread(const std::string
& data
) {
628 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
629 std::string
data_copy(data
);
630 callback_
.Run(base::RefCountedString::TakeString(&data_copy
));
635 // Callback to run with the response.
636 content::URLDataSource::GotDataCallback callback_
;
638 DISALLOW_COPY_AND_ASSIGN(AboutDnsHandler
);
641 void FinishMemoryDataRequest(
642 const std::string
& path
,
643 const content::URLDataSource::GotDataCallback
& callback
) {
644 if (path
== kStringsJsPath
) {
645 // The AboutMemoryHandler cleans itself up, but |StartFetch()| will want
646 // the refcount to be greater than 0.
647 scoped_refptr
<AboutMemoryHandler
> handler(new AboutMemoryHandler(callback
));
648 // TODO(jamescook): Maybe this shouldn't update UMA?
649 handler
->StartFetch(MemoryDetails::UPDATE_USER_METRICS
);
651 int id
= IDR_ABOUT_MEMORY_HTML
;
652 if (path
== kMemoryJsPath
) {
653 id
= IDR_ABOUT_MEMORY_JS
;
654 } else if (path
== kMemoryCssPath
) {
655 id
= IDR_ABOUT_MEMORY_CSS
;
659 ResourceBundle::GetSharedInstance().GetRawDataResource(id
).as_string();
660 callback
.Run(base::RefCountedString::TakeString(&result
));
664 // Handler for filling in the "about:stats" page, as called by the browser's
665 // About handler processing.
666 // |query| is roughly the query string of the about:stats URL.
667 // Returns a string containing the HTML to render for the about:stats page.
668 // Conditional Output:
669 // if |query| is "json", returns a JSON format of all counters.
670 // if |query| is "raw", returns plain text of counter deltas.
671 // otherwise, returns HTML with pretty JS/HTML to display the data.
672 std::string
AboutStats(const std::string
& query
) {
673 // We keep the base::DictionaryValue tree live so that we can do delta
674 // stats computations across runs.
675 CR_DEFINE_STATIC_LOCAL(base::DictionaryValue
, root
, ());
676 static base::TimeTicks last_sample_time
= base::TimeTicks::Now();
678 base::TimeTicks now
= base::TimeTicks::Now();
679 base::TimeDelta time_since_last_sample
= now
- last_sample_time
;
680 last_sample_time
= now
;
682 base::StatsTable
* table
= base::StatsTable::current();
684 return std::string();
686 // We maintain two lists - one for counters and one for timers.
687 // Timers actually get stored on both lists.
688 base::ListValue
* counters
;
689 if (!root
.GetList("counters", &counters
)) {
690 counters
= new base::ListValue();
691 root
.Set("counters", counters
);
694 base::ListValue
* timers
;
695 if (!root
.GetList("timers", &timers
)) {
696 timers
= new base::ListValue();
697 root
.Set("timers", timers
);
700 // NOTE: Counters start at index 1.
701 for (int index
= 1; index
<= table
->GetMaxCounters(); index
++) {
702 // Get the counter's full name
703 std::string full_name
= table
->GetRowName(index
);
704 if (full_name
.length() == 0)
706 DCHECK_EQ(':', full_name
[1]);
707 char counter_type
= full_name
[0];
708 std::string name
= full_name
.substr(2);
710 // JSON doesn't allow '.' in names.
712 while ((pos
= name
.find(".")) != std::string::npos
)
713 name
.replace(pos
, 1, ":");
715 // Try to see if this name already exists.
716 base::DictionaryValue
* counter
= NULL
;
717 for (size_t scan_index
= 0;
718 scan_index
< counters
->GetSize(); scan_index
++) {
719 base::DictionaryValue
* dictionary
;
720 if (counters
->GetDictionary(scan_index
, &dictionary
)) {
721 std::string scan_name
;
722 if (dictionary
->GetString("name", &scan_name
) && scan_name
== name
) {
723 counter
= dictionary
;
726 NOTREACHED(); // Should always be there
730 if (counter
== NULL
) {
731 counter
= new base::DictionaryValue();
732 counter
->SetString("name", name
);
733 counters
->Append(counter
);
736 switch (counter_type
) {
739 int new_value
= table
->GetRowValue(index
);
742 if (counter
->GetInteger("value", &prior_value
)) {
743 delta
= new_value
- prior_value
;
745 counter
->SetInteger("value", new_value
);
746 counter
->SetInteger("delta", delta
);
751 // TODO(mbelshe): implement me.
756 int time
= table
->GetRowValue(index
);
757 counter
->SetInteger("time", time
);
759 // Store this on the timers list as well.
760 timers
->Append(counter
);
769 if (query
== "json" || query
== kStringsJsPath
) {
770 base::JSONWriter::WriteWithOptions(
772 base::JSONWriter::OPTIONS_PRETTY_PRINT
,
774 if (query
== kStringsJsPath
)
775 data
= "loadTimeData.data = " + data
+ ";";
776 } else if (query
== "raw") {
777 // Dump the raw counters which have changed in text format.
779 data
.append(base::StringPrintf("Counter changes in the last %ldms\n",
780 static_cast<long int>(time_since_last_sample
.InMilliseconds())));
781 for (size_t i
= 0; i
< counters
->GetSize(); ++i
) {
782 base::Value
* entry
= NULL
;
783 bool rv
= counters
->Get(i
, &entry
);
785 continue; // None of these should fail.
786 base::DictionaryValue
* counter
=
787 static_cast<base::DictionaryValue
*>(entry
);
789 rv
= counter
->GetInteger("delta", &delta
);
794 rv
= counter
->GetString("name", &name
);
798 rv
= counter
->GetInteger("value", &value
);
803 data
.append(base::IntToString(delta
));
807 data
.append("</pre>");
809 // Get about_stats.html/js from resource bundle.
810 data
= ResourceBundle::GetSharedInstance().GetRawDataResource(
811 (query
== kStatsJsPath
?
812 IDR_ABOUT_STATS_JS
: IDR_ABOUT_STATS_HTML
)).as_string();
814 if (query
!= kStatsJsPath
) {
815 // Clear the timer list since we stored the data in the timers list
817 for (int index
= static_cast<int>(timers
->GetSize())-1; index
>= 0;
819 scoped_ptr
<base::Value
> value
;
820 timers
->Remove(index
, &value
);
821 // We don't care about the value pointer; it's still tracked
822 // on the counters list.
823 ignore_result(value
.release());
831 #if defined(OS_LINUX) || defined(OS_OPENBSD)
832 std::string
AboutLinuxProxyConfig() {
834 AppendHeader(&data
, 0,
835 l10n_util::GetStringUTF8(IDS_ABOUT_LINUX_PROXY_CONFIG_TITLE
));
836 data
.append("<style>body { max-width: 70ex; padding: 2ex 5ex; }</style>");
838 base::FilePath binary
= base::CommandLine::ForCurrentProcess()->GetProgram();
839 data
.append(l10n_util::GetStringFUTF8(
840 IDS_ABOUT_LINUX_PROXY_CONFIG_BODY
,
841 l10n_util::GetStringUTF16(IDS_PRODUCT_NAME
),
842 base::ASCIIToUTF16(binary
.BaseName().value())));
847 void AboutSandboxRow(std::string
* data
, const std::string
& prefix
, int name_id
,
849 data
->append("<tr><td>");
850 data
->append(prefix
);
851 data
->append(l10n_util::GetStringUTF8(name_id
));
853 data
->append("</td><td style='color: green;'>");
855 l10n_util::GetStringUTF8(IDS_CONFIRM_MESSAGEBOX_YES_BUTTON_LABEL
));
857 data
->append("</td><td style='color: red;'>");
859 l10n_util::GetStringUTF8(IDS_CONFIRM_MESSAGEBOX_NO_BUTTON_LABEL
));
861 data
->append("</td></tr>");
864 std::string
AboutSandbox() {
866 AppendHeader(&data
, 0, l10n_util::GetStringUTF8(IDS_ABOUT_SANDBOX_TITLE
));
869 data
.append(l10n_util::GetStringUTF8(IDS_ABOUT_SANDBOX_TITLE
));
870 data
.append("</h1>");
872 // Get expected sandboxing status of renderers.
873 const int status
= content::ZygoteHost::GetInstance()->GetSandboxStatus();
875 data
.append("<table>");
877 AboutSandboxRow(&data
,
879 IDS_ABOUT_SANDBOX_SUID_SANDBOX
,
880 status
& content::kSandboxLinuxSUID
);
881 AboutSandboxRow(&data
, " ", IDS_ABOUT_SANDBOX_PID_NAMESPACES
,
882 status
& content::kSandboxLinuxPIDNS
);
883 AboutSandboxRow(&data
, " ", IDS_ABOUT_SANDBOX_NET_NAMESPACES
,
884 status
& content::kSandboxLinuxNetNS
);
885 AboutSandboxRow(&data
,
887 IDS_ABOUT_SANDBOX_SECCOMP_BPF_SANDBOX
,
888 status
& content::kSandboxLinuxSeccompBPF
);
889 AboutSandboxRow(&data
,
891 IDS_ABOUT_SANDBOX_SECCOMP_BPF_SANDBOX_TSYNC
,
892 status
& content::kSandboxLinuxSeccompTSYNC
);
893 AboutSandboxRow(&data
,
895 IDS_ABOUT_SANDBOX_YAMA_LSM
,
896 status
& content::kSandboxLinuxYama
);
898 data
.append("</table>");
900 // The setuid sandbox is required as our first-layer sandbox.
901 bool good_layer1
= status
& content::kSandboxLinuxSUID
&&
902 status
& content::kSandboxLinuxPIDNS
&&
903 status
& content::kSandboxLinuxNetNS
;
904 // A second-layer sandbox is also required to be adequately sandboxed.
905 bool good_layer2
= status
& content::kSandboxLinuxSeccompBPF
;
906 bool good
= good_layer1
&& good_layer2
;
909 data
.append("<p style='color: green'>");
910 data
.append(l10n_util::GetStringUTF8(IDS_ABOUT_SANDBOX_OK
));
912 data
.append("<p style='color: red'>");
913 data
.append(l10n_util::GetStringUTF8(IDS_ABOUT_SANDBOX_BAD
));
922 // AboutMemoryHandler ----------------------------------------------------------
924 // Helper for AboutMemory to bind results from a ProcessMetrics object
925 // to a DictionaryValue. Fills ws_usage and comm_usage so that the objects
926 // can be used in caller's scope (e.g for appending to a net total).
927 void AboutMemoryHandler::BindProcessMetrics(base::DictionaryValue
* data
,
928 ProcessMemoryInformation
* info
) {
929 DCHECK(data
&& info
);
931 // Bind metrics to dictionary.
932 data
->SetInteger("ws_priv", static_cast<int>(info
->working_set
.priv
));
933 data
->SetInteger("ws_shareable",
934 static_cast<int>(info
->working_set
.shareable
));
935 data
->SetInteger("ws_shared", static_cast<int>(info
->working_set
.shared
));
936 data
->SetInteger("comm_priv", static_cast<int>(info
->committed
.priv
));
937 data
->SetInteger("comm_map", static_cast<int>(info
->committed
.mapped
));
938 data
->SetInteger("comm_image", static_cast<int>(info
->committed
.image
));
939 data
->SetInteger("pid", info
->pid
);
940 data
->SetString("version", info
->version
);
941 data
->SetInteger("processes", info
->num_processes
);
944 // Helper for AboutMemory to append memory usage information for all
945 // sub-processes (i.e. renderers, plugins) used by Chrome.
946 void AboutMemoryHandler::AppendProcess(base::ListValue
* child_data
,
947 ProcessMemoryInformation
* info
) {
948 DCHECK(child_data
&& info
);
950 // Append a new DictionaryValue for this renderer to our list.
951 base::DictionaryValue
* child
= new base::DictionaryValue();
952 child_data
->Append(child
);
953 BindProcessMetrics(child
, info
);
955 std::string
child_label(
956 ProcessMemoryInformation::GetFullTypeNameInEnglish(info
->process_type
,
957 info
->renderer_type
));
958 if (info
->is_diagnostics
)
959 child_label
.append(" (diagnostics)");
960 child
->SetString("child_name", child_label
);
961 base::ListValue
* titles
= new base::ListValue();
962 child
->Set("titles", titles
);
963 for (size_t i
= 0; i
< info
->titles
.size(); ++i
)
964 titles
->Append(new base::StringValue(info
->titles
[i
]));
967 void AboutMemoryHandler::OnDetailsAvailable() {
968 // the root of the JSON hierarchy for about:memory jstemplate
969 scoped_ptr
<base::DictionaryValue
> root(new base::DictionaryValue
);
970 base::ListValue
* browsers
= new base::ListValue();
971 root
->Set("browsers", browsers
);
973 const std::vector
<ProcessData
>& browser_processes
= processes();
975 // Aggregate per-process data into browser summary data.
976 base::string16 log_string
;
977 for (size_t index
= 0; index
< browser_processes
.size(); index
++) {
978 if (browser_processes
[index
].processes
.empty())
981 // Sum the information for the processes within this browser.
982 ProcessMemoryInformation aggregate
;
983 ProcessMemoryInformationList::const_iterator iterator
;
984 iterator
= browser_processes
[index
].processes
.begin();
985 aggregate
.pid
= iterator
->pid
;
986 aggregate
.version
= iterator
->version
;
987 while (iterator
!= browser_processes
[index
].processes
.end()) {
988 if (!iterator
->is_diagnostics
||
989 browser_processes
[index
].processes
.size() == 1) {
990 aggregate
.working_set
.priv
+= iterator
->working_set
.priv
;
991 aggregate
.working_set
.shared
+= iterator
->working_set
.shared
;
992 aggregate
.working_set
.shareable
+= iterator
->working_set
.shareable
;
993 aggregate
.committed
.priv
+= iterator
->committed
.priv
;
994 aggregate
.committed
.mapped
+= iterator
->committed
.mapped
;
995 aggregate
.committed
.image
+= iterator
->committed
.image
;
996 aggregate
.num_processes
++;
1000 base::DictionaryValue
* browser_data
= new base::DictionaryValue();
1001 browsers
->Append(browser_data
);
1002 browser_data
->SetString("name", browser_processes
[index
].name
);
1004 BindProcessMetrics(browser_data
, &aggregate
);
1006 // We log memory info as we record it.
1007 if (!log_string
.empty())
1008 log_string
+= base::ASCIIToUTF16(", ");
1009 log_string
+= browser_processes
[index
].name
+ base::ASCIIToUTF16(", ") +
1010 base::Int64ToString16(aggregate
.working_set
.priv
) +
1011 base::ASCIIToUTF16(", ") +
1012 base::Int64ToString16(aggregate
.working_set
.shared
) +
1013 base::ASCIIToUTF16(", ") +
1014 base::Int64ToString16(aggregate
.working_set
.shareable
);
1016 if (!log_string
.empty())
1017 VLOG(1) << "memory: " << log_string
;
1019 // Set the browser & renderer detailed process data.
1020 base::DictionaryValue
* browser_data
= new base::DictionaryValue();
1021 root
->Set("browzr_data", browser_data
);
1022 base::ListValue
* child_data
= new base::ListValue();
1023 root
->Set("child_data", child_data
);
1025 ProcessData process
= browser_processes
[0]; // Chrome is the first browser.
1026 root
->SetString("current_browser_name", process
.name
);
1028 for (size_t index
= 0; index
< process
.processes
.size(); index
++) {
1029 if (process
.processes
[index
].process_type
== content::PROCESS_TYPE_BROWSER
)
1030 BindProcessMetrics(browser_data
, &process
.processes
[index
]);
1032 AppendProcess(child_data
, &process
.processes
[index
]);
1035 root
->SetBoolean("show_other_browsers",
1036 browser_defaults::kShowOtherBrowsersInAboutMemory
);
1038 base::DictionaryValue load_time_data
;
1039 load_time_data
.SetString(
1041 l10n_util::GetStringUTF16(IDS_MEMORY_USAGE_SUMMARY_DESC
));
1042 webui::SetFontAndTextDirection(&load_time_data
);
1043 load_time_data
.Set("jstemplateData", root
.release());
1046 webui::AppendJsonJS(&load_time_data
, &data
);
1047 callback_
.Run(base::RefCountedString::TakeString(&data
));
1052 // AboutUIHTMLSource ----------------------------------------------------------
1054 AboutUIHTMLSource::AboutUIHTMLSource(const std::string
& source_name
,
1056 : source_name_(source_name
),
1057 profile_(profile
) {}
1059 AboutUIHTMLSource::~AboutUIHTMLSource() {}
1061 std::string
AboutUIHTMLSource::GetSource() const {
1062 return source_name_
;
1065 void AboutUIHTMLSource::StartDataRequest(
1066 const std::string
& path
,
1067 int render_process_id
,
1068 int render_frame_id
,
1069 const content::URLDataSource::GotDataCallback
& callback
) {
1070 std::string response
;
1071 // Add your data source here, in alphabetical order.
1072 if (source_name_
== chrome::kChromeUIChromeURLsHost
) {
1073 response
= ChromeURLs();
1074 } else if (source_name_
== chrome::kChromeUICreditsHost
) {
1075 int idr
= IDR_CREDITS_HTML
;
1076 if (path
== kCreditsJsPath
)
1077 idr
= IDR_CREDITS_JS
;
1078 #if defined(OS_CHROMEOS)
1079 else if (path
== kKeyboardUtilsPath
)
1080 idr
= IDR_KEYBOARD_UTILS_JS
;
1083 response
= ResourceBundle::GetSharedInstance().GetRawDataResource(
1085 #if defined(OS_CHROMEOS)
1086 } else if (source_name_
== chrome::kChromeUIDiscardsHost
) {
1087 response
= AboutDiscards(path
);
1089 } else if (source_name_
== chrome::kChromeUIDNSHost
) {
1090 AboutDnsHandler::Start(profile(), callback
);
1092 #if defined(OS_LINUX) || defined(OS_OPENBSD)
1093 } else if (source_name_
== chrome::kChromeUILinuxProxyConfigHost
) {
1094 response
= AboutLinuxProxyConfig();
1096 } else if (source_name_
== chrome::kChromeUIMemoryHost
) {
1097 response
= GetAboutMemoryRedirectResponse(profile());
1098 } else if (source_name_
== chrome::kChromeUIMemoryRedirectHost
) {
1099 FinishMemoryDataRequest(path
, callback
);
1101 #if defined(OS_CHROMEOS)
1102 } else if (source_name_
== chrome::kChromeUIOSCreditsHost
) {
1103 ChromeOSCreditsHandler::Start(path
, callback
);
1106 #if defined(OS_LINUX) || defined(OS_OPENBSD)
1107 } else if (source_name_
== chrome::kChromeUISandboxHost
) {
1108 response
= AboutSandbox();
1110 } else if (source_name_
== chrome::kChromeUIStatsHost
) {
1111 response
= AboutStats(path
);
1112 #if !defined(OS_ANDROID)
1113 } else if (source_name_
== chrome::kChromeUITermsHost
) {
1114 #if defined(OS_CHROMEOS)
1115 ChromeOSTermsHandler::Start(path
, callback
);
1118 response
= l10n_util::GetStringUTF8(IDS_TERMS_HTML
);
1123 FinishDataRequest(response
, callback
);
1126 void AboutUIHTMLSource::FinishDataRequest(
1127 const std::string
& html
,
1128 const content::URLDataSource::GotDataCallback
& callback
) {
1129 std::string
html_copy(html
);
1130 callback
.Run(base::RefCountedString::TakeString(&html_copy
));
1133 std::string
AboutUIHTMLSource::GetMimeType(const std::string
& path
) const {
1134 if (path
== kCreditsJsPath
||
1135 #if defined(OS_CHROMEOS)
1136 path
== kKeyboardUtilsPath
||
1138 path
== kStatsJsPath
||
1139 path
== kStringsJsPath
||
1140 path
== kMemoryJsPath
) {
1141 return "application/javascript";
1146 bool AboutUIHTMLSource::ShouldAddContentSecurityPolicy() const {
1147 #if defined(OS_CHROMEOS)
1148 if (source_name_
== chrome::kChromeUIOSCreditsHost
)
1151 return content::URLDataSource::ShouldAddContentSecurityPolicy();
1154 bool AboutUIHTMLSource::ShouldDenyXFrameOptions() const {
1155 #if defined(OS_CHROMEOS)
1156 if (source_name_
== chrome::kChromeUITermsHost
) {
1157 // chrome://terms page is embedded in iframe to chrome://oobe.
1161 return content::URLDataSource::ShouldDenyXFrameOptions();
1164 AboutUI::AboutUI(content::WebUI
* web_ui
, const std::string
& name
)
1165 : WebUIController(web_ui
) {
1166 Profile
* profile
= Profile::FromWebUI(web_ui
);
1168 #if defined(ENABLE_THEMES)
1169 // Set up the chrome://theme/ source.
1170 ThemeSource
* theme
= new ThemeSource(profile
);
1171 content::URLDataSource::Add(profile
, theme
);
1174 content::URLDataSource::Add(profile
, new AboutUIHTMLSource(name
, profile
));