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/format_macros.h"
18 #include "base/i18n/number_formatting.h"
19 #include "base/json/json_writer.h"
20 #include "base/memory/singleton.h"
21 #include "base/metrics/statistics_recorder.h"
22 #include "base/strings/string_number_conversions.h"
23 #include "base/strings/string_piece.h"
24 #include "base/strings/string_split.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/oom_priority_manager.h"
34 #include "chrome/browser/memory/tab_stats.h"
35 #include "chrome/browser/memory_details.h"
36 #include "chrome/browser/net/predictor.h"
37 #include "chrome/browser/profiles/profile.h"
38 #include "chrome/browser/profiles/profile_manager.h"
39 #include "chrome/browser/ui/browser_dialogs.h"
40 #include "chrome/common/chrome_paths.h"
41 #include "chrome/common/url_constants.h"
42 #include "chrome/grit/chromium_strings.h"
43 #include "chrome/grit/generated_resources.h"
44 #include "chrome/grit/locale_settings.h"
45 #include "content/public/browser/browser_thread.h"
46 #include "content/public/browser/render_process_host.h"
47 #include "content/public/browser/render_view_host.h"
48 #include "content/public/browser/url_data_source.h"
49 #include "content/public/browser/web_contents.h"
50 #include "content/public/common/content_client.h"
51 #include "content/public/common/process_type.h"
52 #include "google_apis/gaia/google_service_auth_error.h"
53 #include "grit/browser_resources.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/customization_document.h"
85 using base::TimeDelta
;
86 using content::BrowserThread
;
87 using content::WebContents
;
91 const char kCreditsJsPath
[] = "credits.js";
92 const char kMemoryJsPath
[] = "memory.js";
93 const char kMemoryCssPath
[] = "about_memory.css";
94 const char kStatsJsPath
[] = "stats.js";
95 const char kStringsJsPath
[] = "strings.js";
97 // When you type about:memory, it actually loads this intermediate URL that
98 // redirects you to the final page. This avoids the problem where typing
99 // "about:memory" on the new tab page or any other page where a process
100 // transition would occur to the about URL will cause some confusion.
102 // The problem is that during the processing of the memory page, there are two
103 // processes active, the original and the destination one. This can create the
104 // impression that we're using more resources than we actually are. This
105 // redirect solves the problem by eliminating the process transition during the
106 // time that about memory is being computed.
107 std::string
GetAboutMemoryRedirectResponse(Profile
* profile
) {
108 return base::StringPrintf("<meta http-equiv='refresh' content='0;%s'>",
109 chrome::kChromeUIMemoryRedirectURL
);
112 // Handling about:memory is complicated enough to encapsulate its related
113 // methods into a single class. The user should create it (on the heap) and call
114 // its |StartFetch()| method.
115 class AboutMemoryHandler
: public MemoryDetails
{
117 explicit AboutMemoryHandler(
118 const content::URLDataSource::GotDataCallback
& callback
)
119 : callback_(callback
) {
122 void OnDetailsAvailable() override
;
125 ~AboutMemoryHandler() override
{}
127 void BindProcessMetrics(base::DictionaryValue
* data
,
128 ProcessMemoryInformation
* info
);
129 void AppendProcess(base::ListValue
* child_data
,
130 ProcessMemoryInformation
* info
);
132 content::URLDataSource::GotDataCallback callback_
;
134 DISALLOW_COPY_AND_ASSIGN(AboutMemoryHandler
);
137 #if defined(OS_CHROMEOS)
139 const char kKeyboardUtilsPath
[] = "keyboard_utils.js";
141 // chrome://terms falls back to offline page after kOnlineTermsTimeoutSec.
142 const int kOnlineTermsTimeoutSec
= 7;
144 // Helper class that fetches the online Chrome OS terms. Empty string is
145 // returned once fetching failed or exceeded |kOnlineTermsTimeoutSec|.
146 class ChromeOSOnlineTermsHandler
: public net::URLFetcherDelegate
{
148 typedef base::Callback
<void (ChromeOSOnlineTermsHandler
*)> FetchCallback
;
150 explicit ChromeOSOnlineTermsHandler(const FetchCallback
& callback
,
151 const std::string
& locale
)
152 : fetch_callback_(callback
) {
153 std::string eula_URL
= base::StringPrintf(chrome::kOnlineEulaURLPath
,
156 net::URLFetcher::Create(0 /* ID used for testing */, GURL(eula_URL
),
157 net::URLFetcher::GET
, this);
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 ~ChromeOSOnlineTermsHandler() override
{}
189 // net::URLFetcherDelegate:
190 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_WIN) || defined(OS_MACOSX) || defined(OS_CHROMEOS)
463 const char kAboutDiscardsRunCommand
[] = "run";
465 // Html output helper functions
467 // Helper function to wrap HTML with a tag.
468 std::string
WrapWithTag(const std::string
& tag
, const std::string
& text
) {
469 return "<" + tag
+ ">" + text
+ "</" + tag
+ ">";
472 // Helper function to wrap Html with <td> tag.
473 std::string
WrapWithTD(const std::string
& text
) {
474 return "<td>" + text
+ "</td>";
477 // Helper function to wrap Html with <tr> tag.
478 std::string
WrapWithTR(const std::string
& text
) {
479 return "<tr>" + text
+ "</tr>";
482 std::string
AddStringRow(const std::string
& name
, const std::string
& value
) {
484 row
.append(WrapWithTD(name
));
485 row
.append(WrapWithTD(value
));
486 return WrapWithTR(row
);
489 void AddContentSecurityPolicy(std::string
* output
) {
490 output
->append("<meta http-equiv='Content-Security-Policy' "
491 "content='default-src 'none';'>");
494 // TODO(stevenjb): L10N AboutDiscards.
496 std::string
BuildAboutDiscardsRunPage() {
498 AppendHeader(&output
, 0, "About discards");
499 output
.append(base::StringPrintf("<meta http-equiv='refresh' content='2;%s'>",
500 chrome::kChromeUIDiscardsURL
));
501 AddContentSecurityPolicy(&output
);
502 output
.append(WrapWithTag("p", "Discarding a tab..."));
503 AppendFooter(&output
);
507 std::vector
<std::string
> GetHtmlTabDescriptorsForDiscardPage() {
508 memory::OomPriorityManager
* oom
= g_browser_process
->GetOomPriorityManager();
509 memory::TabStatsList stats
= oom
->GetTabStats();
510 std::vector
<std::string
> titles
;
511 titles
.reserve(stats
.size());
512 for (memory::TabStatsList::iterator it
= stats
.begin(); it
!= stats
.end();
517 str
+= it
->is_app
? "[App] " : "";
518 str
+= it
->is_internal_page
? "[Internal] " : "";
519 str
+= it
->is_playing_audio
? "[Audio] " : "";
520 str
+= it
->is_pinned
? "[Pinned] " : "";
521 str
+= it
->is_discarded
? "[Discarded] " : "";
523 str
+= net::EscapeForHTML(base::UTF16ToUTF8(it
->title
));
524 #if defined(OS_CHROMEOS)
525 str
+= base::StringPrintf(" (%d) ", it
->oom_score
);
527 if (!it
->is_discarded
) {
528 str
+= base::StringPrintf(" <a href='%s%s/%" PRId64
"'>Discard</a>",
529 chrome::kChromeUIDiscardsURL
,
530 kAboutDiscardsRunCommand
, it
->tab_contents_id
);
532 str
+= base::StringPrintf(" (%d discards this session)",
534 titles
.push_back(str
);
539 std::string
AboutDiscards(const std::string
& path
) {
541 int64 web_content_id
;
542 memory::OomPriorityManager
* oom
= g_browser_process
->GetOomPriorityManager();
544 std::vector
<std::string
> path_split
= base::SplitString(
545 path
, "/", base::TRIM_WHITESPACE
, base::SPLIT_WANT_ALL
);
546 if (path_split
.size() == 2 && path_split
[0] == kAboutDiscardsRunCommand
&&
547 base::StringToInt64(path_split
[1], &web_content_id
)) {
548 oom
->DiscardTabById(web_content_id
);
549 return BuildAboutDiscardsRunPage();
550 } else if (path_split
.size() == 1 &&
551 path_split
[0] == kAboutDiscardsRunCommand
) {
553 return BuildAboutDiscardsRunPage();
556 AppendHeader(&output
, 0, "About discards");
557 AddContentSecurityPolicy(&output
);
559 output
.append("<h3>Discarded Tabs</h3>");
561 "<p>Tabs sorted from most interesting to least interesting. The least "
562 "interesting tab may be discarded if we run out of physical memory.</p>");
564 std::vector
<std::string
> titles
= GetHtmlTabDescriptorsForDiscardPage();
565 if (!titles
.empty()) {
566 output
.append("<ul>");
567 std::vector
<std::string
>::iterator it
= titles
.begin();
568 for ( ; it
!= titles
.end(); ++it
) {
569 output
.append(WrapWithTag("li", *it
));
571 output
.append("</ul>");
573 output
.append("<p>None found. Wait 10 seconds, then refresh.</p>");
576 base::StringPrintf("%d discards this session. ", oom
->discard_count()));
577 output
.append(base::StringPrintf("<a href='%s%s'>Discard tab now</a>",
578 chrome::kChromeUIDiscardsURL
,
579 kAboutDiscardsRunCommand
));
581 base::SystemMemoryInfoKB meminfo
;
582 base::GetSystemMemoryInfo(&meminfo
);
583 output
.append("<h3>System memory information in MB</h3>");
584 output
.append("<table>");
585 // Start with summary statistics.
586 output
.append(AddStringRow(
587 "Total", base::IntToString(meminfo
.total
/ 1024)));
588 output
.append(AddStringRow(
589 "Free", base::IntToString(meminfo
.free
/ 1024)));
590 #if defined(OS_CHROMEOS)
591 int mem_allocated_kb
= meminfo
.active_anon
+ meminfo
.inactive_anon
;
592 #if defined(ARCH_CPU_ARM_FAMILY)
593 // ARM counts allocated graphics memory separately from anonymous.
594 if (meminfo
.gem_size
!= -1)
595 mem_allocated_kb
+= meminfo
.gem_size
/ 1024;
597 output
.append(AddStringRow(
598 "Allocated", base::IntToString(mem_allocated_kb
/ 1024)));
599 // Add some space, then detailed numbers.
600 output
.append(AddStringRow(" ", " "));
601 output
.append(AddStringRow(
602 "Buffered", base::IntToString(meminfo
.buffers
/ 1024)));
603 output
.append(AddStringRow(
604 "Cached", base::IntToString(meminfo
.cached
/ 1024)));
605 output
.append(AddStringRow(
606 "Active Anon", base::IntToString(meminfo
.active_anon
/ 1024)));
607 output
.append(AddStringRow(
608 "Inactive Anon", base::IntToString(meminfo
.inactive_anon
/ 1024)));
609 output
.append(AddStringRow(
610 "Shared", base::IntToString(meminfo
.shmem
/ 1024)));
611 output
.append(AddStringRow(
612 "Graphics", base::IntToString(meminfo
.gem_size
/ 1024 / 1024)));
613 #endif // OS_CHROMEOS
614 output
.append("</table>");
615 AppendFooter(&output
);
619 #endif // OS_WIN || OS_CHROMEOS
621 // AboutDnsHandler bounces the request back to the IO thread to collect
622 // the DNS information.
623 class AboutDnsHandler
: public base::RefCountedThreadSafe
<AboutDnsHandler
> {
625 static void Start(Profile
* profile
,
626 const content::URLDataSource::GotDataCallback
& callback
) {
627 scoped_refptr
<AboutDnsHandler
> handler(
628 new AboutDnsHandler(profile
, callback
));
629 handler
->StartOnUIThread();
633 friend class base::RefCountedThreadSafe
<AboutDnsHandler
>;
635 AboutDnsHandler(Profile
* profile
,
636 const content::URLDataSource::GotDataCallback
& callback
)
638 callback_(callback
) {
639 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
642 virtual ~AboutDnsHandler() {}
644 // Calls FinishOnUIThread() on completion.
645 void StartOnUIThread() {
646 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
647 chrome_browser_net::Predictor
* predictor
= profile_
->GetNetworkPredictor();
648 BrowserThread::PostTask(
649 BrowserThread::IO
, FROM_HERE
,
650 base::Bind(&AboutDnsHandler::StartOnIOThread
, this, predictor
));
653 void StartOnIOThread(chrome_browser_net::Predictor
* predictor
) {
654 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
657 AppendHeader(&data
, 0, "About DNS");
659 chrome_browser_net::Predictor::PredictorGetHtmlInfo(predictor
, &data
);
662 BrowserThread::PostTask(
663 BrowserThread::UI
, FROM_HERE
,
664 base::Bind(&AboutDnsHandler::FinishOnUIThread
, this, data
));
667 void FinishOnUIThread(const std::string
& data
) {
668 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
669 std::string
data_copy(data
);
670 callback_
.Run(base::RefCountedString::TakeString(&data_copy
));
675 // Callback to run with the response.
676 content::URLDataSource::GotDataCallback callback_
;
678 DISALLOW_COPY_AND_ASSIGN(AboutDnsHandler
);
681 void FinishMemoryDataRequest(
682 const std::string
& path
,
683 const content::URLDataSource::GotDataCallback
& callback
) {
684 if (path
== kStringsJsPath
) {
685 // The AboutMemoryHandler cleans itself up, but |StartFetch()| will want
686 // the refcount to be greater than 0.
687 scoped_refptr
<AboutMemoryHandler
> handler(new AboutMemoryHandler(callback
));
688 handler
->StartFetch(MemoryDetails::FROM_ALL_BROWSERS
);
690 int id
= IDR_ABOUT_MEMORY_HTML
;
691 if (path
== kMemoryJsPath
) {
692 id
= IDR_ABOUT_MEMORY_JS
;
693 } else if (path
== kMemoryCssPath
) {
694 id
= IDR_ABOUT_MEMORY_CSS
;
698 ResourceBundle::GetSharedInstance().GetRawDataResource(id
).as_string();
699 callback
.Run(base::RefCountedString::TakeString(&result
));
703 #if defined(OS_LINUX) || defined(OS_OPENBSD)
704 std::string
AboutLinuxProxyConfig() {
706 AppendHeader(&data
, 0,
707 l10n_util::GetStringUTF8(IDS_ABOUT_LINUX_PROXY_CONFIG_TITLE
));
708 data
.append("<style>body { max-width: 70ex; padding: 2ex 5ex; }</style>");
710 base::FilePath binary
= base::CommandLine::ForCurrentProcess()->GetProgram();
711 data
.append(l10n_util::GetStringFUTF8(
712 IDS_ABOUT_LINUX_PROXY_CONFIG_BODY
,
713 l10n_util::GetStringUTF16(IDS_PRODUCT_NAME
),
714 base::ASCIIToUTF16(binary
.BaseName().value())));
719 void AboutSandboxRow(std::string
* data
, int name_id
, bool good
) {
720 data
->append("<tr><td>");
721 data
->append(l10n_util::GetStringUTF8(name_id
));
723 data
->append("</td><td style='color: green;'>");
725 l10n_util::GetStringUTF8(IDS_CONFIRM_MESSAGEBOX_YES_BUTTON_LABEL
));
727 data
->append("</td><td style='color: red;'>");
729 l10n_util::GetStringUTF8(IDS_CONFIRM_MESSAGEBOX_NO_BUTTON_LABEL
));
731 data
->append("</td></tr>");
734 std::string
AboutSandbox() {
736 AppendHeader(&data
, 0, l10n_util::GetStringUTF8(IDS_ABOUT_SANDBOX_TITLE
));
739 data
.append(l10n_util::GetStringUTF8(IDS_ABOUT_SANDBOX_TITLE
));
740 data
.append("</h1>");
742 // Get expected sandboxing status of renderers.
743 const int status
= content::ZygoteHost::GetInstance()->GetSandboxStatus();
745 data
.append("<table>");
747 AboutSandboxRow(&data
, IDS_ABOUT_SANDBOX_SUID_SANDBOX
,
748 status
& content::kSandboxLinuxSUID
);
749 AboutSandboxRow(&data
, IDS_ABOUT_SANDBOX_NAMESPACE_SANDBOX
,
750 status
& content::kSandboxLinuxUserNS
);
751 AboutSandboxRow(&data
, IDS_ABOUT_SANDBOX_PID_NAMESPACES
,
752 status
& content::kSandboxLinuxPIDNS
);
753 AboutSandboxRow(&data
, IDS_ABOUT_SANDBOX_NET_NAMESPACES
,
754 status
& content::kSandboxLinuxNetNS
);
755 AboutSandboxRow(&data
, IDS_ABOUT_SANDBOX_SECCOMP_BPF_SANDBOX
,
756 status
& content::kSandboxLinuxSeccompBPF
);
757 AboutSandboxRow(&data
, IDS_ABOUT_SANDBOX_SECCOMP_BPF_SANDBOX_TSYNC
,
758 status
& content::kSandboxLinuxSeccompTSYNC
);
759 AboutSandboxRow(&data
, IDS_ABOUT_SANDBOX_YAMA_LSM
,
760 status
& content::kSandboxLinuxYama
);
762 data
.append("</table>");
764 // Require either the setuid or namespace sandbox for our first-layer sandbox.
765 bool good_layer1
= (status
& content::kSandboxLinuxSUID
||
766 status
& content::kSandboxLinuxUserNS
) &&
767 status
& content::kSandboxLinuxPIDNS
&&
768 status
& content::kSandboxLinuxNetNS
;
769 // A second-layer sandbox is also required to be adequately sandboxed.
770 bool good_layer2
= status
& content::kSandboxLinuxSeccompBPF
;
771 bool good
= good_layer1
&& good_layer2
;
774 data
.append("<p style='color: green'>");
775 data
.append(l10n_util::GetStringUTF8(IDS_ABOUT_SANDBOX_OK
));
777 data
.append("<p style='color: red'>");
778 data
.append(l10n_util::GetStringUTF8(IDS_ABOUT_SANDBOX_BAD
));
787 // AboutMemoryHandler ----------------------------------------------------------
789 // Helper for AboutMemory to bind results from a ProcessMetrics object
790 // to a DictionaryValue. Fills ws_usage and comm_usage so that the objects
791 // can be used in caller's scope (e.g for appending to a net total).
792 void AboutMemoryHandler::BindProcessMetrics(base::DictionaryValue
* data
,
793 ProcessMemoryInformation
* info
) {
794 DCHECK(data
&& info
);
796 // Bind metrics to dictionary.
797 data
->SetInteger("ws_priv", static_cast<int>(info
->working_set
.priv
));
798 data
->SetInteger("ws_shareable",
799 static_cast<int>(info
->working_set
.shareable
));
800 data
->SetInteger("ws_shared", static_cast<int>(info
->working_set
.shared
));
801 data
->SetInteger("comm_priv", static_cast<int>(info
->committed
.priv
));
802 data
->SetInteger("comm_map", static_cast<int>(info
->committed
.mapped
));
803 data
->SetInteger("comm_image", static_cast<int>(info
->committed
.image
));
804 data
->SetInteger("pid", info
->pid
);
805 data
->SetString("version", info
->version
);
806 data
->SetInteger("processes", info
->num_processes
);
809 // Helper for AboutMemory to append memory usage information for all
810 // sub-processes (i.e. renderers, plugins) used by Chrome.
811 void AboutMemoryHandler::AppendProcess(base::ListValue
* child_data
,
812 ProcessMemoryInformation
* info
) {
813 DCHECK(child_data
&& info
);
815 // Append a new DictionaryValue for this renderer to our list.
816 base::DictionaryValue
* child
= new base::DictionaryValue();
817 child_data
->Append(child
);
818 BindProcessMetrics(child
, info
);
820 std::string
child_label(
821 ProcessMemoryInformation::GetFullTypeNameInEnglish(info
->process_type
,
822 info
->renderer_type
));
823 if (info
->is_diagnostics
)
824 child_label
.append(" (diagnostics)");
825 child
->SetString("child_name", child_label
);
826 base::ListValue
* titles
= new base::ListValue();
827 child
->Set("titles", titles
);
828 for (size_t i
= 0; i
< info
->titles
.size(); ++i
)
829 titles
->Append(new base::StringValue(info
->titles
[i
]));
832 void AboutMemoryHandler::OnDetailsAvailable() {
833 // the root of the JSON hierarchy for about:memory jstemplate
834 scoped_ptr
<base::DictionaryValue
> root(new base::DictionaryValue
);
835 base::ListValue
* browsers
= new base::ListValue();
836 root
->Set("browsers", browsers
);
838 const std::vector
<ProcessData
>& browser_processes
= processes();
840 // Aggregate per-process data into browser summary data.
841 base::string16 log_string
;
842 for (size_t index
= 0; index
< browser_processes
.size(); index
++) {
843 if (browser_processes
[index
].processes
.empty())
846 // Sum the information for the processes within this browser.
847 ProcessMemoryInformation aggregate
;
848 ProcessMemoryInformationList::const_iterator iterator
;
849 iterator
= browser_processes
[index
].processes
.begin();
850 aggregate
.pid
= iterator
->pid
;
851 aggregate
.version
= iterator
->version
;
852 while (iterator
!= browser_processes
[index
].processes
.end()) {
853 if (!iterator
->is_diagnostics
||
854 browser_processes
[index
].processes
.size() == 1) {
855 aggregate
.working_set
.priv
+= iterator
->working_set
.priv
;
856 aggregate
.working_set
.shared
+= iterator
->working_set
.shared
;
857 aggregate
.working_set
.shareable
+= iterator
->working_set
.shareable
;
858 aggregate
.committed
.priv
+= iterator
->committed
.priv
;
859 aggregate
.committed
.mapped
+= iterator
->committed
.mapped
;
860 aggregate
.committed
.image
+= iterator
->committed
.image
;
861 aggregate
.num_processes
++;
865 base::DictionaryValue
* browser_data
= new base::DictionaryValue();
866 browsers
->Append(browser_data
);
867 browser_data
->SetString("name", browser_processes
[index
].name
);
869 BindProcessMetrics(browser_data
, &aggregate
);
871 // We log memory info as we record it.
872 if (!log_string
.empty())
873 log_string
+= base::ASCIIToUTF16(", ");
874 log_string
+= browser_processes
[index
].name
+ base::ASCIIToUTF16(", ") +
875 base::SizeTToString16(aggregate
.working_set
.priv
) +
876 base::ASCIIToUTF16(", ") +
877 base::SizeTToString16(aggregate
.working_set
.shared
) +
878 base::ASCIIToUTF16(", ") +
879 base::SizeTToString16(aggregate
.working_set
.shareable
);
881 if (!log_string
.empty())
882 VLOG(1) << "memory: " << log_string
;
884 // Set the browser & renderer detailed process data.
885 base::DictionaryValue
* browser_data
= new base::DictionaryValue();
886 root
->Set("browzr_data", browser_data
);
887 base::ListValue
* child_data
= new base::ListValue();
888 root
->Set("child_data", child_data
);
890 ProcessData process
= browser_processes
[0]; // Chrome is the first browser.
891 root
->SetString("current_browser_name", process
.name
);
893 for (size_t index
= 0; index
< process
.processes
.size(); index
++) {
894 if (process
.processes
[index
].process_type
== content::PROCESS_TYPE_BROWSER
)
895 BindProcessMetrics(browser_data
, &process
.processes
[index
]);
897 AppendProcess(child_data
, &process
.processes
[index
]);
900 root
->SetBoolean("show_other_browsers",
901 browser_defaults::kShowOtherBrowsersInAboutMemory
);
903 base::DictionaryValue load_time_data
;
904 load_time_data
.SetString(
906 l10n_util::GetStringUTF16(IDS_MEMORY_USAGE_SUMMARY_DESC
));
907 const std::string
& app_locale
= g_browser_process
->GetApplicationLocale();
908 webui::SetLoadTimeDataDefaults(app_locale
, &load_time_data
);
909 load_time_data
.Set("jstemplateData", root
.release());
912 webui::AppendJsonJS(&load_time_data
, &data
);
913 callback_
.Run(base::RefCountedString::TakeString(&data
));
918 // AboutUIHTMLSource ----------------------------------------------------------
920 AboutUIHTMLSource::AboutUIHTMLSource(const std::string
& source_name
,
922 : source_name_(source_name
),
925 AboutUIHTMLSource::~AboutUIHTMLSource() {}
927 std::string
AboutUIHTMLSource::GetSource() const {
931 void AboutUIHTMLSource::StartDataRequest(
932 const std::string
& path
,
933 int render_process_id
,
935 const content::URLDataSource::GotDataCallback
& callback
) {
936 std::string response
;
937 // Add your data source here, in alphabetical order.
938 if (source_name_
== chrome::kChromeUIChromeURLsHost
) {
939 response
= ChromeURLs();
940 } else if (source_name_
== chrome::kChromeUICreditsHost
) {
941 int idr
= IDR_CREDITS_HTML
;
942 if (path
== kCreditsJsPath
)
943 idr
= IDR_CREDITS_JS
;
944 #if defined(OS_CHROMEOS)
945 else if (path
== kKeyboardUtilsPath
)
946 idr
= IDR_KEYBOARD_UTILS_JS
;
949 response
= ResourceBundle::GetSharedInstance().GetRawDataResource(
951 #if defined(OS_WIN) || defined(OS_MACOSX) || defined(OS_CHROMEOS)
952 } else if (source_name_
== chrome::kChromeUIDiscardsHost
) {
953 response
= AboutDiscards(path
);
955 } else if (source_name_
== chrome::kChromeUIDNSHost
) {
956 AboutDnsHandler::Start(profile(), callback
);
958 #if defined(OS_LINUX) || defined(OS_OPENBSD)
959 } else if (source_name_
== chrome::kChromeUILinuxProxyConfigHost
) {
960 response
= AboutLinuxProxyConfig();
962 } else if (source_name_
== chrome::kChromeUIMemoryHost
) {
963 response
= GetAboutMemoryRedirectResponse(profile());
964 } else if (source_name_
== chrome::kChromeUIMemoryRedirectHost
) {
965 FinishMemoryDataRequest(path
, callback
);
967 #if defined(OS_CHROMEOS)
968 } else if (source_name_
== chrome::kChromeUIOSCreditsHost
) {
969 ChromeOSCreditsHandler::Start(path
, callback
);
972 #if defined(OS_LINUX) || defined(OS_OPENBSD)
973 } else if (source_name_
== chrome::kChromeUISandboxHost
) {
974 response
= AboutSandbox();
976 #if !defined(OS_ANDROID)
977 } else if (source_name_
== chrome::kChromeUITermsHost
) {
978 #if defined(OS_CHROMEOS)
979 ChromeOSTermsHandler::Start(path
, callback
);
982 response
= l10n_util::GetStringUTF8(IDS_TERMS_HTML
);
987 FinishDataRequest(response
, callback
);
990 void AboutUIHTMLSource::FinishDataRequest(
991 const std::string
& html
,
992 const content::URLDataSource::GotDataCallback
& callback
) {
993 std::string
html_copy(html
);
994 callback
.Run(base::RefCountedString::TakeString(&html_copy
));
997 std::string
AboutUIHTMLSource::GetMimeType(const std::string
& path
) const {
998 if (path
== kCreditsJsPath
||
999 #if defined(OS_CHROMEOS)
1000 path
== kKeyboardUtilsPath
||
1002 path
== kStatsJsPath
||
1003 path
== kStringsJsPath
||
1004 path
== kMemoryJsPath
) {
1005 return "application/javascript";
1010 bool AboutUIHTMLSource::ShouldAddContentSecurityPolicy() const {
1011 #if defined(OS_CHROMEOS)
1012 if (source_name_
== chrome::kChromeUIOSCreditsHost
)
1015 return content::URLDataSource::ShouldAddContentSecurityPolicy();
1018 bool AboutUIHTMLSource::ShouldDenyXFrameOptions() const {
1019 #if defined(OS_CHROMEOS)
1020 if (source_name_
== chrome::kChromeUITermsHost
) {
1021 // chrome://terms page is embedded in iframe to chrome://oobe.
1025 return content::URLDataSource::ShouldDenyXFrameOptions();
1028 AboutUI::AboutUI(content::WebUI
* web_ui
, const std::string
& name
)
1029 : WebUIController(web_ui
) {
1030 Profile
* profile
= Profile::FromWebUI(web_ui
);
1032 #if defined(ENABLE_THEMES)
1033 // Set up the chrome://theme/ source.
1034 ThemeSource
* theme
= new ThemeSource(profile
);
1035 content::URLDataSource::Add(profile
, theme
);
1038 content::URLDataSource::Add(profile
, new AboutUIHTMLSource(name
, profile
));