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 titles
.push_back(str
);
537 std::string
AboutDiscards(const std::string
& path
) {
539 int64 web_content_id
;
540 memory::OomPriorityManager
* oom
= g_browser_process
->GetOomPriorityManager();
542 std::vector
<std::string
> path_split
= base::SplitString(
543 path
, "/", base::TRIM_WHITESPACE
, base::SPLIT_WANT_ALL
);
544 if (path_split
.size() == 2 && path_split
[0] == kAboutDiscardsRunCommand
&&
545 base::StringToInt64(path_split
[1], &web_content_id
)) {
546 oom
->DiscardTabById(web_content_id
);
547 return BuildAboutDiscardsRunPage();
548 } else if (path_split
.size() == 1 &&
549 path_split
[0] == kAboutDiscardsRunCommand
) {
551 return BuildAboutDiscardsRunPage();
554 AppendHeader(&output
, 0, "About discards");
555 AddContentSecurityPolicy(&output
);
557 output
.append("<h3>Discarded Tabs</h3>");
559 "<p>Tabs sorted from most interesting to least interesting. The least "
560 "interesting tab may be discarded if we run out of physical memory.</p>");
562 std::vector
<std::string
> titles
= GetHtmlTabDescriptorsForDiscardPage();
563 if (!titles
.empty()) {
564 output
.append("<ul>");
565 std::vector
<std::string
>::iterator it
= titles
.begin();
566 for ( ; it
!= titles
.end(); ++it
) {
567 output
.append(WrapWithTag("li", *it
));
569 output
.append("</ul>");
571 output
.append("<p>None found. Wait 10 seconds, then refresh.</p>");
574 base::StringPrintf("%d discards this session. ", oom
->discard_count()));
575 output
.append(base::StringPrintf("<a href='%s%s'>Discard tab now</a>",
576 chrome::kChromeUIDiscardsURL
,
577 kAboutDiscardsRunCommand
));
579 base::SystemMemoryInfoKB meminfo
;
580 base::GetSystemMemoryInfo(&meminfo
);
581 output
.append("<h3>System memory information in MB</h3>");
582 output
.append("<table>");
583 // Start with summary statistics.
584 output
.append(AddStringRow(
585 "Total", base::IntToString(meminfo
.total
/ 1024)));
586 output
.append(AddStringRow(
587 "Free", base::IntToString(meminfo
.free
/ 1024)));
588 #if defined(OS_CHROMEOS)
589 int mem_allocated_kb
= meminfo
.active_anon
+ meminfo
.inactive_anon
;
590 #if defined(ARCH_CPU_ARM_FAMILY)
591 // ARM counts allocated graphics memory separately from anonymous.
592 if (meminfo
.gem_size
!= -1)
593 mem_allocated_kb
+= meminfo
.gem_size
/ 1024;
595 output
.append(AddStringRow(
596 "Allocated", base::IntToString(mem_allocated_kb
/ 1024)));
597 // Add some space, then detailed numbers.
598 output
.append(AddStringRow(" ", " "));
599 output
.append(AddStringRow(
600 "Buffered", base::IntToString(meminfo
.buffers
/ 1024)));
601 output
.append(AddStringRow(
602 "Cached", base::IntToString(meminfo
.cached
/ 1024)));
603 output
.append(AddStringRow(
604 "Active Anon", base::IntToString(meminfo
.active_anon
/ 1024)));
605 output
.append(AddStringRow(
606 "Inactive Anon", base::IntToString(meminfo
.inactive_anon
/ 1024)));
607 output
.append(AddStringRow(
608 "Shared", base::IntToString(meminfo
.shmem
/ 1024)));
609 output
.append(AddStringRow(
610 "Graphics", base::IntToString(meminfo
.gem_size
/ 1024 / 1024)));
611 #endif // OS_CHROMEOS
612 output
.append("</table>");
613 AppendFooter(&output
);
617 #endif // OS_WIN || OS_CHROMEOS
619 // AboutDnsHandler bounces the request back to the IO thread to collect
620 // the DNS information.
621 class AboutDnsHandler
: public base::RefCountedThreadSafe
<AboutDnsHandler
> {
623 static void Start(Profile
* profile
,
624 const content::URLDataSource::GotDataCallback
& callback
) {
625 scoped_refptr
<AboutDnsHandler
> handler(
626 new AboutDnsHandler(profile
, callback
));
627 handler
->StartOnUIThread();
631 friend class base::RefCountedThreadSafe
<AboutDnsHandler
>;
633 AboutDnsHandler(Profile
* profile
,
634 const content::URLDataSource::GotDataCallback
& callback
)
636 callback_(callback
) {
637 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
640 virtual ~AboutDnsHandler() {}
642 // Calls FinishOnUIThread() on completion.
643 void StartOnUIThread() {
644 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
645 chrome_browser_net::Predictor
* predictor
= profile_
->GetNetworkPredictor();
646 BrowserThread::PostTask(
647 BrowserThread::IO
, FROM_HERE
,
648 base::Bind(&AboutDnsHandler::StartOnIOThread
, this, predictor
));
651 void StartOnIOThread(chrome_browser_net::Predictor
* predictor
) {
652 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
655 AppendHeader(&data
, 0, "About DNS");
657 chrome_browser_net::Predictor::PredictorGetHtmlInfo(predictor
, &data
);
660 BrowserThread::PostTask(
661 BrowserThread::UI
, FROM_HERE
,
662 base::Bind(&AboutDnsHandler::FinishOnUIThread
, this, data
));
665 void FinishOnUIThread(const std::string
& data
) {
666 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
667 std::string
data_copy(data
);
668 callback_
.Run(base::RefCountedString::TakeString(&data_copy
));
673 // Callback to run with the response.
674 content::URLDataSource::GotDataCallback callback_
;
676 DISALLOW_COPY_AND_ASSIGN(AboutDnsHandler
);
679 void FinishMemoryDataRequest(
680 const std::string
& path
,
681 const content::URLDataSource::GotDataCallback
& callback
) {
682 if (path
== kStringsJsPath
) {
683 // The AboutMemoryHandler cleans itself up, but |StartFetch()| will want
684 // the refcount to be greater than 0.
685 scoped_refptr
<AboutMemoryHandler
> handler(new AboutMemoryHandler(callback
));
686 handler
->StartFetch(MemoryDetails::FROM_ALL_BROWSERS
);
688 int id
= IDR_ABOUT_MEMORY_HTML
;
689 if (path
== kMemoryJsPath
) {
690 id
= IDR_ABOUT_MEMORY_JS
;
691 } else if (path
== kMemoryCssPath
) {
692 id
= IDR_ABOUT_MEMORY_CSS
;
696 ResourceBundle::GetSharedInstance().GetRawDataResource(id
).as_string();
697 callback
.Run(base::RefCountedString::TakeString(&result
));
701 #if defined(OS_LINUX) || defined(OS_OPENBSD)
702 std::string
AboutLinuxProxyConfig() {
704 AppendHeader(&data
, 0,
705 l10n_util::GetStringUTF8(IDS_ABOUT_LINUX_PROXY_CONFIG_TITLE
));
706 data
.append("<style>body { max-width: 70ex; padding: 2ex 5ex; }</style>");
708 base::FilePath binary
= base::CommandLine::ForCurrentProcess()->GetProgram();
709 data
.append(l10n_util::GetStringFUTF8(
710 IDS_ABOUT_LINUX_PROXY_CONFIG_BODY
,
711 l10n_util::GetStringUTF16(IDS_PRODUCT_NAME
),
712 base::ASCIIToUTF16(binary
.BaseName().value())));
717 void AboutSandboxRow(std::string
* data
, int name_id
, bool good
) {
718 data
->append("<tr><td>");
719 data
->append(l10n_util::GetStringUTF8(name_id
));
721 data
->append("</td><td style='color: green;'>");
723 l10n_util::GetStringUTF8(IDS_CONFIRM_MESSAGEBOX_YES_BUTTON_LABEL
));
725 data
->append("</td><td style='color: red;'>");
727 l10n_util::GetStringUTF8(IDS_CONFIRM_MESSAGEBOX_NO_BUTTON_LABEL
));
729 data
->append("</td></tr>");
732 std::string
AboutSandbox() {
734 AppendHeader(&data
, 0, l10n_util::GetStringUTF8(IDS_ABOUT_SANDBOX_TITLE
));
737 data
.append(l10n_util::GetStringUTF8(IDS_ABOUT_SANDBOX_TITLE
));
738 data
.append("</h1>");
740 // Get expected sandboxing status of renderers.
741 const int status
= content::ZygoteHost::GetInstance()->GetSandboxStatus();
743 data
.append("<table>");
745 AboutSandboxRow(&data
, IDS_ABOUT_SANDBOX_SUID_SANDBOX
,
746 status
& content::kSandboxLinuxSUID
);
747 AboutSandboxRow(&data
, IDS_ABOUT_SANDBOX_NAMESPACE_SANDBOX
,
748 status
& content::kSandboxLinuxUserNS
);
749 AboutSandboxRow(&data
, IDS_ABOUT_SANDBOX_PID_NAMESPACES
,
750 status
& content::kSandboxLinuxPIDNS
);
751 AboutSandboxRow(&data
, IDS_ABOUT_SANDBOX_NET_NAMESPACES
,
752 status
& content::kSandboxLinuxNetNS
);
753 AboutSandboxRow(&data
, IDS_ABOUT_SANDBOX_SECCOMP_BPF_SANDBOX
,
754 status
& content::kSandboxLinuxSeccompBPF
);
755 AboutSandboxRow(&data
, IDS_ABOUT_SANDBOX_SECCOMP_BPF_SANDBOX_TSYNC
,
756 status
& content::kSandboxLinuxSeccompTSYNC
);
757 AboutSandboxRow(&data
, IDS_ABOUT_SANDBOX_YAMA_LSM
,
758 status
& content::kSandboxLinuxYama
);
760 data
.append("</table>");
762 // Require either the setuid or namespace sandbox for our first-layer sandbox.
763 bool good_layer1
= (status
& content::kSandboxLinuxSUID
||
764 status
& content::kSandboxLinuxUserNS
) &&
765 status
& content::kSandboxLinuxPIDNS
&&
766 status
& content::kSandboxLinuxNetNS
;
767 // A second-layer sandbox is also required to be adequately sandboxed.
768 bool good_layer2
= status
& content::kSandboxLinuxSeccompBPF
;
769 bool good
= good_layer1
&& good_layer2
;
772 data
.append("<p style='color: green'>");
773 data
.append(l10n_util::GetStringUTF8(IDS_ABOUT_SANDBOX_OK
));
775 data
.append("<p style='color: red'>");
776 data
.append(l10n_util::GetStringUTF8(IDS_ABOUT_SANDBOX_BAD
));
785 // AboutMemoryHandler ----------------------------------------------------------
787 // Helper for AboutMemory to bind results from a ProcessMetrics object
788 // to a DictionaryValue. Fills ws_usage and comm_usage so that the objects
789 // can be used in caller's scope (e.g for appending to a net total).
790 void AboutMemoryHandler::BindProcessMetrics(base::DictionaryValue
* data
,
791 ProcessMemoryInformation
* info
) {
792 DCHECK(data
&& info
);
794 // Bind metrics to dictionary.
795 data
->SetInteger("ws_priv", static_cast<int>(info
->working_set
.priv
));
796 data
->SetInteger("ws_shareable",
797 static_cast<int>(info
->working_set
.shareable
));
798 data
->SetInteger("ws_shared", static_cast<int>(info
->working_set
.shared
));
799 data
->SetInteger("comm_priv", static_cast<int>(info
->committed
.priv
));
800 data
->SetInteger("comm_map", static_cast<int>(info
->committed
.mapped
));
801 data
->SetInteger("comm_image", static_cast<int>(info
->committed
.image
));
802 data
->SetInteger("pid", info
->pid
);
803 data
->SetString("version", info
->version
);
804 data
->SetInteger("processes", info
->num_processes
);
807 // Helper for AboutMemory to append memory usage information for all
808 // sub-processes (i.e. renderers, plugins) used by Chrome.
809 void AboutMemoryHandler::AppendProcess(base::ListValue
* child_data
,
810 ProcessMemoryInformation
* info
) {
811 DCHECK(child_data
&& info
);
813 // Append a new DictionaryValue for this renderer to our list.
814 base::DictionaryValue
* child
= new base::DictionaryValue();
815 child_data
->Append(child
);
816 BindProcessMetrics(child
, info
);
818 std::string
child_label(
819 ProcessMemoryInformation::GetFullTypeNameInEnglish(info
->process_type
,
820 info
->renderer_type
));
821 if (info
->is_diagnostics
)
822 child_label
.append(" (diagnostics)");
823 child
->SetString("child_name", child_label
);
824 base::ListValue
* titles
= new base::ListValue();
825 child
->Set("titles", titles
);
826 for (size_t i
= 0; i
< info
->titles
.size(); ++i
)
827 titles
->Append(new base::StringValue(info
->titles
[i
]));
830 void AboutMemoryHandler::OnDetailsAvailable() {
831 // the root of the JSON hierarchy for about:memory jstemplate
832 scoped_ptr
<base::DictionaryValue
> root(new base::DictionaryValue
);
833 base::ListValue
* browsers
= new base::ListValue();
834 root
->Set("browsers", browsers
);
836 const std::vector
<ProcessData
>& browser_processes
= processes();
838 // Aggregate per-process data into browser summary data.
839 base::string16 log_string
;
840 for (size_t index
= 0; index
< browser_processes
.size(); index
++) {
841 if (browser_processes
[index
].processes
.empty())
844 // Sum the information for the processes within this browser.
845 ProcessMemoryInformation aggregate
;
846 ProcessMemoryInformationList::const_iterator iterator
;
847 iterator
= browser_processes
[index
].processes
.begin();
848 aggregate
.pid
= iterator
->pid
;
849 aggregate
.version
= iterator
->version
;
850 while (iterator
!= browser_processes
[index
].processes
.end()) {
851 if (!iterator
->is_diagnostics
||
852 browser_processes
[index
].processes
.size() == 1) {
853 aggregate
.working_set
.priv
+= iterator
->working_set
.priv
;
854 aggregate
.working_set
.shared
+= iterator
->working_set
.shared
;
855 aggregate
.working_set
.shareable
+= iterator
->working_set
.shareable
;
856 aggregate
.committed
.priv
+= iterator
->committed
.priv
;
857 aggregate
.committed
.mapped
+= iterator
->committed
.mapped
;
858 aggregate
.committed
.image
+= iterator
->committed
.image
;
859 aggregate
.num_processes
++;
863 base::DictionaryValue
* browser_data
= new base::DictionaryValue();
864 browsers
->Append(browser_data
);
865 browser_data
->SetString("name", browser_processes
[index
].name
);
867 BindProcessMetrics(browser_data
, &aggregate
);
869 // We log memory info as we record it.
870 if (!log_string
.empty())
871 log_string
+= base::ASCIIToUTF16(", ");
872 log_string
+= browser_processes
[index
].name
+ base::ASCIIToUTF16(", ") +
873 base::Int64ToString16(aggregate
.working_set
.priv
) +
874 base::ASCIIToUTF16(", ") +
875 base::Int64ToString16(aggregate
.working_set
.shared
) +
876 base::ASCIIToUTF16(", ") +
877 base::Int64ToString16(aggregate
.working_set
.shareable
);
879 if (!log_string
.empty())
880 VLOG(1) << "memory: " << log_string
;
882 // Set the browser & renderer detailed process data.
883 base::DictionaryValue
* browser_data
= new base::DictionaryValue();
884 root
->Set("browzr_data", browser_data
);
885 base::ListValue
* child_data
= new base::ListValue();
886 root
->Set("child_data", child_data
);
888 ProcessData process
= browser_processes
[0]; // Chrome is the first browser.
889 root
->SetString("current_browser_name", process
.name
);
891 for (size_t index
= 0; index
< process
.processes
.size(); index
++) {
892 if (process
.processes
[index
].process_type
== content::PROCESS_TYPE_BROWSER
)
893 BindProcessMetrics(browser_data
, &process
.processes
[index
]);
895 AppendProcess(child_data
, &process
.processes
[index
]);
898 root
->SetBoolean("show_other_browsers",
899 browser_defaults::kShowOtherBrowsersInAboutMemory
);
901 base::DictionaryValue load_time_data
;
902 load_time_data
.SetString(
904 l10n_util::GetStringUTF16(IDS_MEMORY_USAGE_SUMMARY_DESC
));
905 const std::string
& app_locale
= g_browser_process
->GetApplicationLocale();
906 webui::SetLoadTimeDataDefaults(app_locale
, &load_time_data
);
907 load_time_data
.Set("jstemplateData", root
.release());
910 webui::AppendJsonJS(&load_time_data
, &data
);
911 callback_
.Run(base::RefCountedString::TakeString(&data
));
916 // AboutUIHTMLSource ----------------------------------------------------------
918 AboutUIHTMLSource::AboutUIHTMLSource(const std::string
& source_name
,
920 : source_name_(source_name
),
923 AboutUIHTMLSource::~AboutUIHTMLSource() {}
925 std::string
AboutUIHTMLSource::GetSource() const {
929 void AboutUIHTMLSource::StartDataRequest(
930 const std::string
& path
,
931 int render_process_id
,
933 const content::URLDataSource::GotDataCallback
& callback
) {
934 std::string response
;
935 // Add your data source here, in alphabetical order.
936 if (source_name_
== chrome::kChromeUIChromeURLsHost
) {
937 response
= ChromeURLs();
938 } else if (source_name_
== chrome::kChromeUICreditsHost
) {
939 int idr
= IDR_CREDITS_HTML
;
940 if (path
== kCreditsJsPath
)
941 idr
= IDR_CREDITS_JS
;
942 #if defined(OS_CHROMEOS)
943 else if (path
== kKeyboardUtilsPath
)
944 idr
= IDR_KEYBOARD_UTILS_JS
;
947 response
= ResourceBundle::GetSharedInstance().GetRawDataResource(
949 #if defined(OS_WIN) || defined(OS_MACOSX) || defined(OS_CHROMEOS)
950 } else if (source_name_
== chrome::kChromeUIDiscardsHost
) {
951 response
= AboutDiscards(path
);
953 } else if (source_name_
== chrome::kChromeUIDNSHost
) {
954 AboutDnsHandler::Start(profile(), callback
);
956 #if defined(OS_LINUX) || defined(OS_OPENBSD)
957 } else if (source_name_
== chrome::kChromeUILinuxProxyConfigHost
) {
958 response
= AboutLinuxProxyConfig();
960 } else if (source_name_
== chrome::kChromeUIMemoryHost
) {
961 response
= GetAboutMemoryRedirectResponse(profile());
962 } else if (source_name_
== chrome::kChromeUIMemoryRedirectHost
) {
963 FinishMemoryDataRequest(path
, callback
);
965 #if defined(OS_CHROMEOS)
966 } else if (source_name_
== chrome::kChromeUIOSCreditsHost
) {
967 ChromeOSCreditsHandler::Start(path
, callback
);
970 #if defined(OS_LINUX) || defined(OS_OPENBSD)
971 } else if (source_name_
== chrome::kChromeUISandboxHost
) {
972 response
= AboutSandbox();
974 #if !defined(OS_ANDROID)
975 } else if (source_name_
== chrome::kChromeUITermsHost
) {
976 #if defined(OS_CHROMEOS)
977 ChromeOSTermsHandler::Start(path
, callback
);
980 response
= l10n_util::GetStringUTF8(IDS_TERMS_HTML
);
985 FinishDataRequest(response
, callback
);
988 void AboutUIHTMLSource::FinishDataRequest(
989 const std::string
& html
,
990 const content::URLDataSource::GotDataCallback
& callback
) {
991 std::string
html_copy(html
);
992 callback
.Run(base::RefCountedString::TakeString(&html_copy
));
995 std::string
AboutUIHTMLSource::GetMimeType(const std::string
& path
) const {
996 if (path
== kCreditsJsPath
||
997 #if defined(OS_CHROMEOS)
998 path
== kKeyboardUtilsPath
||
1000 path
== kStatsJsPath
||
1001 path
== kStringsJsPath
||
1002 path
== kMemoryJsPath
) {
1003 return "application/javascript";
1008 bool AboutUIHTMLSource::ShouldAddContentSecurityPolicy() const {
1009 #if defined(OS_CHROMEOS)
1010 if (source_name_
== chrome::kChromeUIOSCreditsHost
)
1013 return content::URLDataSource::ShouldAddContentSecurityPolicy();
1016 bool AboutUIHTMLSource::ShouldDenyXFrameOptions() const {
1017 #if defined(OS_CHROMEOS)
1018 if (source_name_
== chrome::kChromeUITermsHost
) {
1019 // chrome://terms page is embedded in iframe to chrome://oobe.
1023 return content::URLDataSource::ShouldDenyXFrameOptions();
1026 AboutUI::AboutUI(content::WebUI
* web_ui
, const std::string
& name
)
1027 : WebUIController(web_ui
) {
1028 Profile
* profile
= Profile::FromWebUI(web_ui
);
1030 #if defined(ENABLE_THEMES)
1031 // Set up the chrome://theme/ source.
1032 ThemeSource
* theme
= new ThemeSource(profile
);
1033 content::URLDataSource::Add(profile
, theme
);
1036 content::URLDataSource::Add(profile
, new AboutUIHTMLSource(name
, profile
));