1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "chrome/browser/ui/webui/about_ui.h"
12 #include "base/bind.h"
13 #include "base/bind_helpers.h"
14 #include "base/callback.h"
15 #include "base/command_line.h"
16 #include "base/file_util.h"
17 #include "base/i18n/number_formatting.h"
18 #include "base/json/json_writer.h"
19 #include "base/memory/singleton.h"
20 #include "base/metrics/statistics_recorder.h"
21 #include "base/metrics/stats_table.h"
22 #include "base/path_service.h"
23 #include "base/strings/string_number_conversions.h"
24 #include "base/strings/string_piece.h"
25 #include "base/strings/string_util.h"
26 #include "base/strings/stringprintf.h"
27 #include "base/strings/utf_string_conversions.h"
28 #include "base/threading/thread.h"
29 #include "base/values.h"
30 #include "chrome/browser/about_flags.h"
31 #include "chrome/browser/browser_process.h"
32 #include "chrome/browser/defaults.h"
33 #include "chrome/browser/memory_details.h"
34 #include "chrome/browser/net/predictor.h"
35 #include "chrome/browser/profiles/profile.h"
36 #include "chrome/browser/profiles/profile_manager.h"
37 #include "chrome/browser/ui/browser_dialogs.h"
38 #include "chrome/common/chrome_paths.h"
39 #include "chrome/common/net/url_fixer_upper.h"
40 #include "chrome/common/render_messages.h"
41 #include "chrome/common/url_constants.h"
42 #include "content/public/browser/browser_thread.h"
43 #include "content/public/browser/render_process_host.h"
44 #include "content/public/browser/render_view_host.h"
45 #include "content/public/browser/url_data_source.h"
46 #include "content/public/browser/web_contents.h"
47 #include "content/public/common/content_client.h"
48 #include "content/public/common/process_type.h"
49 #include "google_apis/gaia/google_service_auth_error.h"
50 #include "grit/browser_resources.h"
51 #include "grit/chromium_strings.h"
52 #include "grit/generated_resources.h"
53 #include "grit/locale_settings.h"
54 #include "net/base/escape.h"
55 #include "net/base/load_flags.h"
56 #include "net/base/net_util.h"
57 #include "net/http/http_response_headers.h"
58 #include "net/url_request/url_fetcher.h"
59 #include "net/url_request/url_request_status.h"
60 #include "ui/base/l10n/l10n_util.h"
61 #include "ui/base/resource/resource_bundle.h"
62 #include "ui/base/webui/jstemplate_builder.h"
63 #include "ui/base/webui/web_ui_util.h"
66 #if defined(ENABLE_THEMES)
67 #include "chrome/browser/ui/webui/theme_source.h"
70 #if defined(OS_LINUX) || defined(OS_OPENBSD)
71 #include "content/public/browser/zygote_host_linux.h"
72 #include "content/public/common/sandbox_linux.h"
76 #include "chrome/browser/enumerate_modules_model_win.h"
79 #if defined(OS_CHROMEOS)
80 #include "chrome/browser/browser_process_platform_part_chromeos.h"
81 #include "chrome/browser/chromeos/customization_document.h"
82 #include "chrome/browser/chromeos/memory/oom_priority_manager.h"
83 #include "chromeos/chromeos_switches.h"
87 using base::TimeDelta
;
88 using content::BrowserThread
;
89 using content::WebContents
;
93 const char kCreditsJsPath
[] = "credits.js";
94 const char kMemoryJsPath
[] = "memory.js";
95 const char kMemoryCssPath
[] = "about_memory.css";
96 const char kStatsJsPath
[] = "stats.js";
97 const char kStringsJsPath
[] = "strings.js";
99 #if defined(OS_CHROMEOS)
100 // chrome://terms falls back to offline page after kOnlineTermsTimeoutSec.
101 const int kOnlineTermsTimeoutSec
= 7;
102 #endif // defined(OS_CHROMEOS)
104 // When you type about:memory, it actually loads this intermediate URL that
105 // redirects you to the final page. This avoids the problem where typing
106 // "about:memory" on the new tab page or any other page where a process
107 // transition would occur to the about URL will cause some confusion.
109 // The problem is that during the processing of the memory page, there are two
110 // processes active, the original and the destination one. This can create the
111 // impression that we're using more resources than we actually are. This
112 // redirect solves the problem by eliminating the process transition during the
113 // time that about memory is being computed.
114 std::string
GetAboutMemoryRedirectResponse(Profile
* profile
) {
115 return base::StringPrintf("<meta http-equiv='refresh' content='0;%s'>",
116 chrome::kChromeUIMemoryRedirectURL
);
119 // Handling about:memory is complicated enough to encapsulate its related
120 // methods into a single class. The user should create it (on the heap) and call
121 // its |StartFetch()| method.
122 class AboutMemoryHandler
: public MemoryDetails
{
124 explicit AboutMemoryHandler(
125 const content::URLDataSource::GotDataCallback
& callback
)
126 : callback_(callback
) {
129 virtual void OnDetailsAvailable() OVERRIDE
;
132 virtual ~AboutMemoryHandler() {}
134 void BindProcessMetrics(base::DictionaryValue
* data
,
135 ProcessMemoryInformation
* info
);
136 void AppendProcess(base::ListValue
* child_data
,
137 ProcessMemoryInformation
* info
);
139 content::URLDataSource::GotDataCallback callback_
;
141 DISALLOW_COPY_AND_ASSIGN(AboutMemoryHandler
);
144 #if defined(OS_CHROMEOS)
146 // Helper class that fetches the online Chrome OS terms. Empty string is
147 // returned once fetching failed or exceeded |kOnlineTermsTimeoutSec|.
148 class ChromeOSOnlineTermsHandler
: public net::URLFetcherDelegate
{
150 typedef base::Callback
<void (ChromeOSOnlineTermsHandler
*)> FetchCallback
;
152 explicit ChromeOSOnlineTermsHandler(const FetchCallback
& callback
,
153 const std::string
& locale
)
154 : fetch_callback_(callback
) {
155 std::string eula_URL
= base::StringPrintf(chrome::kOnlineEulaURLPath
,
157 eula_fetcher_
.reset(net::URLFetcher::Create(0 /* ID used for testing */,
159 net::URLFetcher::GET
,
161 eula_fetcher_
->SetRequestContext(
162 g_browser_process
->system_request_context());
163 eula_fetcher_
->AddExtraRequestHeader("Accept: text/html");
164 eula_fetcher_
->SetLoadFlags(net::LOAD_DO_NOT_SEND_COOKIES
|
165 net::LOAD_DO_NOT_SAVE_COOKIES
|
166 net::LOAD_DISABLE_CACHE
);
167 eula_fetcher_
->Start();
168 // Abort the download attempt if it takes longer than one minute.
169 download_timer_
.Start(FROM_HERE
,
170 base::TimeDelta::FromSeconds(kOnlineTermsTimeoutSec
),
172 &ChromeOSOnlineTermsHandler::OnDownloadTimeout
);
175 void GetResponseResult(std::string
* response_string
) {
176 std::string mime_type
;
177 if (!eula_fetcher_
||
178 !eula_fetcher_
->GetStatus().is_success() ||
179 eula_fetcher_
->GetResponseCode() != 200 ||
180 !eula_fetcher_
->GetResponseHeaders()->GetMimeType(&mime_type
) ||
181 mime_type
!= "text/html" ||
182 !eula_fetcher_
->GetResponseAsString(response_string
)) {
183 response_string
->clear();
188 // Prevents allocation on the stack. ChromeOSOnlineTermsHandler should be
189 // created by 'operator new'. |this| takes care of destruction.
190 virtual ~ChromeOSOnlineTermsHandler() {}
192 // net::URLFetcherDelegate:
193 virtual void OnURLFetchComplete(const net::URLFetcher
* source
) OVERRIDE
{
194 if (source
!= eula_fetcher_
.get()) {
195 NOTREACHED() << "Callback from foreign URL fetcher";
198 fetch_callback_
.Run(this);
202 void OnDownloadTimeout() {
203 eula_fetcher_
.reset();
204 fetch_callback_
.Run(this);
208 // Timer that enforces a timeout on the attempt to download the
210 base::OneShotTimer
<ChromeOSOnlineTermsHandler
> download_timer_
;
212 // |fetch_callback_| called when fetching succeeded or failed.
213 FetchCallback fetch_callback_
;
215 // Helper to fetch online eula.
216 scoped_ptr
<net::URLFetcher
> eula_fetcher_
;
218 DISALLOW_COPY_AND_ASSIGN(ChromeOSOnlineTermsHandler
);
221 class ChromeOSTermsHandler
222 : public base::RefCountedThreadSafe
<ChromeOSTermsHandler
> {
224 static void Start(const std::string
& path
,
225 const content::URLDataSource::GotDataCallback
& callback
) {
226 scoped_refptr
<ChromeOSTermsHandler
> handler(
227 new ChromeOSTermsHandler(path
, callback
));
228 handler
->StartOnUIThread();
232 friend class base::RefCountedThreadSafe
<ChromeOSTermsHandler
>;
234 ChromeOSTermsHandler(const std::string
& path
,
235 const content::URLDataSource::GotDataCallback
& callback
)
238 // Previously we were using "initial locale" http://crbug.com/145142
239 locale_(g_browser_process
->GetApplicationLocale()) {
242 virtual ~ChromeOSTermsHandler() {}
244 void StartOnUIThread() {
245 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
246 if (path_
== chrome::kOemEulaURLPath
) {
247 // Load local OEM EULA from the disk.
248 BrowserThread::PostTask(
249 BrowserThread::FILE, FROM_HERE
,
250 base::Bind(&ChromeOSTermsHandler::LoadOemEulaFileOnFileThread
, this));
251 } else if (CommandLine::ForCurrentProcess()->HasSwitch(
252 chromeos::switches::kDisableOnlineEULA
)) {
253 // Fallback to the local file.
254 BrowserThread::PostTask(
255 BrowserThread::FILE, FROM_HERE
,
256 base::Bind(&ChromeOSTermsHandler::LoadEulaFileOnFileThread
, this));
258 // Try to load online version of ChromeOS terms first.
259 // ChromeOSOnlineTermsHandler object destroys itself.
260 new ChromeOSOnlineTermsHandler(
261 base::Bind(&ChromeOSTermsHandler::OnOnlineEULAFetched
, this),
266 void OnOnlineEULAFetched(ChromeOSOnlineTermsHandler
* loader
) {
267 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
268 loader
->GetResponseResult(&contents_
);
269 if (contents_
.empty()) {
270 // Load local ChromeOS terms from the file.
271 BrowserThread::PostTask(
272 BrowserThread::FILE, FROM_HERE
,
273 base::Bind(&ChromeOSTermsHandler::LoadEulaFileOnFileThread
, this));
275 ResponseOnUIThread();
279 void LoadOemEulaFileOnFileThread() {
280 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
281 const chromeos::StartupCustomizationDocument
* customization
=
282 chromeos::StartupCustomizationDocument::GetInstance();
283 if (customization
->IsReady()) {
284 base::FilePath oem_eula_file_path
;
285 if (net::FileURLToFilePath(GURL(customization
->GetEULAPage(locale_
)),
286 &oem_eula_file_path
)) {
287 if (!base::ReadFileToString(oem_eula_file_path
, &contents_
)) {
292 BrowserThread::PostTask(
293 BrowserThread::UI
, FROM_HERE
,
294 base::Bind(&ChromeOSTermsHandler::ResponseOnUIThread
, this));
297 void LoadEulaFileOnFileThread() {
298 std::string file_path
=
299 base::StringPrintf(chrome::kEULAPathFormat
, locale_
.c_str());
300 if (!base::ReadFileToString(base::FilePath(file_path
), &contents_
)) {
301 // No EULA for given language - try en-US as default.
302 file_path
= base::StringPrintf(chrome::kEULAPathFormat
, "en-US");
303 if (!base::ReadFileToString(base::FilePath(file_path
), &contents_
)) {
304 // File with EULA not found, ResponseOnUIThread will load EULA from
305 // resources if contents_ is empty.
309 BrowserThread::PostTask(
310 BrowserThread::UI
, FROM_HERE
,
311 base::Bind(&ChromeOSTermsHandler::ResponseOnUIThread
, this));
314 void ResponseOnUIThread() {
315 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
316 // If we fail to load Chrome OS EULA from disk, load it from resources.
317 // Do nothing if OEM EULA load failed.
318 if (contents_
.empty() && path_
!= chrome::kOemEulaURLPath
)
319 contents_
= l10n_util::GetStringUTF8(IDS_TERMS_HTML
);
320 callback_
.Run(base::RefCountedString::TakeString(&contents_
));
324 const std::string path_
;
326 // Callback to run with the response.
327 content::URLDataSource::GotDataCallback callback_
;
329 // Locale of the EULA.
330 const std::string locale_
;
332 // EULA contents that was loaded from file.
333 std::string contents_
;
335 DISALLOW_COPY_AND_ASSIGN(ChromeOSTermsHandler
);
342 // Individual about handlers ---------------------------------------------------
346 void AppendHeader(std::string
* output
, int refresh
,
347 const std::string
& unescaped_title
) {
348 output
->append("<!DOCTYPE HTML>\n<html>\n<head>\n");
349 if (!unescaped_title
.empty()) {
350 output
->append("<title>");
351 output
->append(net::EscapeForHTML(unescaped_title
));
352 output
->append("</title>\n");
354 output
->append("<meta charset='utf-8'>\n");
356 output
->append("<meta http-equiv='refresh' content='");
357 output
->append(base::IntToString(refresh
));
358 output
->append("'/>\n");
362 void AppendBody(std::string
*output
) {
363 output
->append("</head>\n<body>\n");
366 void AppendFooter(std::string
*output
) {
367 output
->append("</body>\n</html>\n");
370 } // namespace about_ui
372 using about_ui::AppendHeader
;
373 using about_ui::AppendBody
;
374 using about_ui::AppendFooter
;
378 std::string
ChromeURLs() {
380 AppendHeader(&html
, 0, "Chrome URLs");
382 html
+= "<h2>List of Chrome URLs</h2>\n<ul>\n";
383 std::vector
<std::string
> hosts(
384 chrome::kChromeHostURLs
,
385 chrome::kChromeHostURLs
+ chrome::kNumberOfChromeHostURLs
);
386 std::sort(hosts
.begin(), hosts
.end());
387 for (std::vector
<std::string
>::const_iterator i
= hosts
.begin();
388 i
!= hosts
.end(); ++i
)
389 html
+= "<li><a href='chrome://" + *i
+ "/'>chrome://" + *i
+ "</a></li>\n";
390 html
+= "</ul>\n<h2>For Debug</h2>\n"
391 "<p>The following pages are for debugging purposes only. Because they "
392 "crash or hang the renderer, they're not linked directly; you can type "
393 "them into the address bar if you need them.</p>\n<ul>";
394 for (int i
= 0; i
< chrome::kNumberOfChromeDebugURLs
; i
++)
395 html
+= "<li>" + std::string(chrome::kChromeDebugURLs
[i
]) + "</li>\n";
401 #if defined(OS_CHROMEOS)
403 // Html output helper functions
405 // Helper function to wrap HTML with a tag.
406 std::string
WrapWithTag(const std::string
& tag
, const std::string
& text
) {
407 return "<" + tag
+ ">" + text
+ "</" + tag
+ ">";
410 // Helper function to wrap Html with <td> tag.
411 std::string
WrapWithTD(const std::string
& text
) {
412 return "<td>" + text
+ "</td>";
415 // Helper function to wrap Html with <tr> tag.
416 std::string
WrapWithTR(const std::string
& text
) {
417 return "<tr>" + text
+ "</tr>";
420 std::string
AddStringRow(const std::string
& name
, const std::string
& value
) {
422 row
.append(WrapWithTD(name
));
423 row
.append(WrapWithTD(value
));
424 return WrapWithTR(row
);
427 void AddContentSecurityPolicy(std::string
* output
) {
428 output
->append("<meta http-equiv='Content-Security-Policy' "
429 "content='default-src 'none';'>");
432 // TODO(stevenjb): L10N AboutDiscards.
434 std::string
AboutDiscardsRun() {
436 AppendHeader(&output
, 0, "About discards");
438 base::StringPrintf("<meta http-equiv='refresh' content='2;%s'>",
439 chrome::kChromeUIDiscardsURL
));
440 AddContentSecurityPolicy(&output
);
441 output
.append(WrapWithTag("p", "Discarding a tab..."));
442 g_browser_process
->platform_part()->
443 oom_priority_manager()->LogMemoryAndDiscardTab();
444 AppendFooter(&output
);
448 std::string
AboutDiscards(const std::string
& path
) {
450 const char kRunCommand
[] = "run";
451 if (path
== kRunCommand
)
452 return AboutDiscardsRun();
453 AppendHeader(&output
, 0, "About discards");
454 AddContentSecurityPolicy(&output
);
456 output
.append("<h3>About discards</h3>");
458 "<p>Tabs sorted from most interesting to least interesting. The least "
459 "interesting tab may be discarded if we run out of physical memory.</p>");
461 chromeos::OomPriorityManager
* oom
=
462 g_browser_process
->platform_part()->oom_priority_manager();
463 std::vector
<base::string16
> titles
= oom
->GetTabTitles();
464 if (!titles
.empty()) {
465 output
.append("<ul>");
466 std::vector
<base::string16
>::iterator it
= titles
.begin();
467 for ( ; it
!= titles
.end(); ++it
) {
468 std::string title
= base::UTF16ToUTF8(*it
);
469 title
= net::EscapeForHTML(title
);
470 output
.append(WrapWithTag("li", title
));
472 output
.append("</ul>");
474 output
.append("<p>None found. Wait 10 seconds, then refresh.</p>");
476 output
.append(base::StringPrintf("%d discards this session. ",
477 oom
->discard_count()));
478 output
.append(base::StringPrintf("<a href='%s%s'>Discard tab now</a>",
479 chrome::kChromeUIDiscardsURL
,
482 base::SystemMemoryInfoKB meminfo
;
483 base::GetSystemMemoryInfo(&meminfo
);
484 output
.append("<h3>System memory information in MB</h3>");
485 output
.append("<table>");
486 // Start with summary statistics.
487 output
.append(AddStringRow(
488 "Total", base::IntToString(meminfo
.total
/ 1024)));
489 output
.append(AddStringRow(
490 "Free", base::IntToString(meminfo
.free
/ 1024)));
491 int mem_allocated_kb
= meminfo
.active_anon
+ meminfo
.inactive_anon
;
492 #if defined(ARCH_CPU_ARM_FAMILY)
493 // ARM counts allocated graphics memory separately from anonymous.
494 if (meminfo
.gem_size
!= -1)
495 mem_allocated_kb
+= meminfo
.gem_size
/ 1024;
497 output
.append(AddStringRow(
498 "Allocated", base::IntToString(mem_allocated_kb
/ 1024)));
499 // Add some space, then detailed numbers.
500 output
.append(AddStringRow(" ", " "));
501 output
.append(AddStringRow(
502 "Buffered", base::IntToString(meminfo
.buffers
/ 1024)));
503 output
.append(AddStringRow(
504 "Cached", base::IntToString(meminfo
.cached
/ 1024)));
505 output
.append(AddStringRow(
506 "Active Anon", base::IntToString(meminfo
.active_anon
/ 1024)));
507 output
.append(AddStringRow(
508 "Inactive Anon", base::IntToString(meminfo
.inactive_anon
/ 1024)));
509 output
.append(AddStringRow(
510 "Shared", base::IntToString(meminfo
.shmem
/ 1024)));
511 output
.append(AddStringRow(
512 "Graphics", base::IntToString(meminfo
.gem_size
/ 1024 / 1024)));
513 output
.append("</table>");
515 AppendFooter(&output
);
519 #endif // OS_CHROMEOS
521 // AboutDnsHandler bounces the request back to the IO thread to collect
522 // the DNS information.
523 class AboutDnsHandler
: public base::RefCountedThreadSafe
<AboutDnsHandler
> {
525 static void Start(Profile
* profile
,
526 const content::URLDataSource::GotDataCallback
& callback
) {
527 scoped_refptr
<AboutDnsHandler
> handler(
528 new AboutDnsHandler(profile
, callback
));
529 handler
->StartOnUIThread();
533 friend class base::RefCountedThreadSafe
<AboutDnsHandler
>;
535 AboutDnsHandler(Profile
* profile
,
536 const content::URLDataSource::GotDataCallback
& callback
)
538 callback_(callback
) {
539 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
542 virtual ~AboutDnsHandler() {}
544 // Calls FinishOnUIThread() on completion.
545 void StartOnUIThread() {
546 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
547 chrome_browser_net::Predictor
* predictor
= profile_
->GetNetworkPredictor();
548 BrowserThread::PostTask(
549 BrowserThread::IO
, FROM_HERE
,
550 base::Bind(&AboutDnsHandler::StartOnIOThread
, this, predictor
));
553 void StartOnIOThread(chrome_browser_net::Predictor
* predictor
) {
554 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO
));
557 AppendHeader(&data
, 0, "About DNS");
559 chrome_browser_net::Predictor::PredictorGetHtmlInfo(predictor
, &data
);
562 BrowserThread::PostTask(
563 BrowserThread::UI
, FROM_HERE
,
564 base::Bind(&AboutDnsHandler::FinishOnUIThread
, this, data
));
567 void FinishOnUIThread(const std::string
& data
) {
568 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
569 std::string
data_copy(data
);
570 callback_
.Run(base::RefCountedString::TakeString(&data_copy
));
575 // Callback to run with the response.
576 content::URLDataSource::GotDataCallback callback_
;
578 DISALLOW_COPY_AND_ASSIGN(AboutDnsHandler
);
581 void FinishMemoryDataRequest(
582 const std::string
& path
,
583 const content::URLDataSource::GotDataCallback
& callback
) {
584 if (path
== kStringsJsPath
) {
585 // The AboutMemoryHandler cleans itself up, but |StartFetch()| will want
586 // the refcount to be greater than 0.
587 scoped_refptr
<AboutMemoryHandler
> handler(new AboutMemoryHandler(callback
));
588 // TODO(jamescook): Maybe this shouldn't update UMA?
589 handler
->StartFetch(MemoryDetails::UPDATE_USER_METRICS
);
591 int id
= IDR_ABOUT_MEMORY_HTML
;
592 if (path
== kMemoryJsPath
) {
593 id
= IDR_ABOUT_MEMORY_JS
;
594 } else if (path
== kMemoryCssPath
) {
595 id
= IDR_ABOUT_MEMORY_CSS
;
599 ResourceBundle::GetSharedInstance().GetRawDataResource(id
).as_string();
600 callback
.Run(base::RefCountedString::TakeString(&result
));
604 // Handler for filling in the "about:stats" page, as called by the browser's
605 // About handler processing.
606 // |query| is roughly the query string of the about:stats URL.
607 // Returns a string containing the HTML to render for the about:stats page.
608 // Conditional Output:
609 // if |query| is "json", returns a JSON format of all counters.
610 // if |query| is "raw", returns plain text of counter deltas.
611 // otherwise, returns HTML with pretty JS/HTML to display the data.
612 std::string
AboutStats(const std::string
& query
) {
613 // We keep the base::DictionaryValue tree live so that we can do delta
614 // stats computations across runs.
615 CR_DEFINE_STATIC_LOCAL(base::DictionaryValue
, root
, ());
616 static base::TimeTicks last_sample_time
= base::TimeTicks::Now();
618 base::TimeTicks now
= base::TimeTicks::Now();
619 base::TimeDelta time_since_last_sample
= now
- last_sample_time
;
620 last_sample_time
= now
;
622 base::StatsTable
* table
= base::StatsTable::current();
624 return std::string();
626 // We maintain two lists - one for counters and one for timers.
627 // Timers actually get stored on both lists.
628 base::ListValue
* counters
;
629 if (!root
.GetList("counters", &counters
)) {
630 counters
= new base::ListValue();
631 root
.Set("counters", counters
);
634 base::ListValue
* timers
;
635 if (!root
.GetList("timers", &timers
)) {
636 timers
= new base::ListValue();
637 root
.Set("timers", timers
);
640 // NOTE: Counters start at index 1.
641 for (int index
= 1; index
<= table
->GetMaxCounters(); index
++) {
642 // Get the counter's full name
643 std::string full_name
= table
->GetRowName(index
);
644 if (full_name
.length() == 0)
646 DCHECK_EQ(':', full_name
[1]);
647 char counter_type
= full_name
[0];
648 std::string name
= full_name
.substr(2);
650 // JSON doesn't allow '.' in names.
652 while ((pos
= name
.find(".")) != std::string::npos
)
653 name
.replace(pos
, 1, ":");
655 // Try to see if this name already exists.
656 base::DictionaryValue
* counter
= NULL
;
657 for (size_t scan_index
= 0;
658 scan_index
< counters
->GetSize(); scan_index
++) {
659 base::DictionaryValue
* dictionary
;
660 if (counters
->GetDictionary(scan_index
, &dictionary
)) {
661 std::string scan_name
;
662 if (dictionary
->GetString("name", &scan_name
) && scan_name
== name
) {
663 counter
= dictionary
;
666 NOTREACHED(); // Should always be there
670 if (counter
== NULL
) {
671 counter
= new base::DictionaryValue();
672 counter
->SetString("name", name
);
673 counters
->Append(counter
);
676 switch (counter_type
) {
679 int new_value
= table
->GetRowValue(index
);
682 if (counter
->GetInteger("value", &prior_value
)) {
683 delta
= new_value
- prior_value
;
685 counter
->SetInteger("value", new_value
);
686 counter
->SetInteger("delta", delta
);
691 // TODO(mbelshe): implement me.
696 int time
= table
->GetRowValue(index
);
697 counter
->SetInteger("time", time
);
699 // Store this on the timers list as well.
700 timers
->Append(counter
);
709 if (query
== "json" || query
== kStringsJsPath
) {
710 base::JSONWriter::WriteWithOptions(
712 base::JSONWriter::OPTIONS_PRETTY_PRINT
,
714 if (query
== kStringsJsPath
)
715 data
= "var templateData = " + data
+ ";";
716 } else if (query
== "raw") {
717 // Dump the raw counters which have changed in text format.
719 data
.append(base::StringPrintf("Counter changes in the last %ldms\n",
720 static_cast<long int>(time_since_last_sample
.InMilliseconds())));
721 for (size_t i
= 0; i
< counters
->GetSize(); ++i
) {
722 base::Value
* entry
= NULL
;
723 bool rv
= counters
->Get(i
, &entry
);
725 continue; // None of these should fail.
726 base::DictionaryValue
* counter
=
727 static_cast<base::DictionaryValue
*>(entry
);
729 rv
= counter
->GetInteger("delta", &delta
);
734 rv
= counter
->GetString("name", &name
);
738 rv
= counter
->GetInteger("value", &value
);
743 data
.append(base::IntToString(delta
));
747 data
.append("</pre>");
749 // Get about_stats.html/js from resource bundle.
750 data
= ResourceBundle::GetSharedInstance().GetRawDataResource(
751 (query
== kStatsJsPath
?
752 IDR_ABOUT_STATS_JS
: IDR_ABOUT_STATS_HTML
)).as_string();
754 if (query
!= kStatsJsPath
) {
755 // Clear the timer list since we stored the data in the timers list
757 for (int index
= static_cast<int>(timers
->GetSize())-1; index
>= 0;
759 scoped_ptr
<base::Value
> value
;
760 timers
->Remove(index
, &value
);
761 // We don't care about the value pointer; it's still tracked
762 // on the counters list.
763 ignore_result(value
.release());
771 #if defined(OS_LINUX) || defined(OS_OPENBSD)
772 std::string
AboutLinuxProxyConfig() {
774 AppendHeader(&data
, 0,
775 l10n_util::GetStringUTF8(IDS_ABOUT_LINUX_PROXY_CONFIG_TITLE
));
776 data
.append("<style>body { max-width: 70ex; padding: 2ex 5ex; }</style>");
778 base::FilePath binary
= CommandLine::ForCurrentProcess()->GetProgram();
779 data
.append(l10n_util::GetStringFUTF8(
780 IDS_ABOUT_LINUX_PROXY_CONFIG_BODY
,
781 l10n_util::GetStringUTF16(IDS_PRODUCT_NAME
),
782 base::ASCIIToUTF16(binary
.BaseName().value())));
787 void AboutSandboxRow(std::string
* data
, const std::string
& prefix
, int name_id
,
789 data
->append("<tr><td>");
790 data
->append(prefix
);
791 data
->append(l10n_util::GetStringUTF8(name_id
));
793 data
->append("</td><td style='color: green;'>");
795 l10n_util::GetStringUTF8(IDS_CONFIRM_MESSAGEBOX_YES_BUTTON_LABEL
));
797 data
->append("</td><td style='color: red;'>");
799 l10n_util::GetStringUTF8(IDS_CONFIRM_MESSAGEBOX_NO_BUTTON_LABEL
));
801 data
->append("</td></tr>");
804 std::string
AboutSandbox() {
806 AppendHeader(&data
, 0, l10n_util::GetStringUTF8(IDS_ABOUT_SANDBOX_TITLE
));
809 data
.append(l10n_util::GetStringUTF8(IDS_ABOUT_SANDBOX_TITLE
));
810 data
.append("</h1>");
812 // Get expected sandboxing status of renderers.
813 const int status
= content::ZygoteHost::GetInstance()->GetSandboxStatus();
815 data
.append("<table>");
817 AboutSandboxRow(&data
,
819 IDS_ABOUT_SANDBOX_SUID_SANDBOX
,
820 status
& content::kSandboxLinuxSUID
);
821 AboutSandboxRow(&data
, " ", IDS_ABOUT_SANDBOX_PID_NAMESPACES
,
822 status
& content::kSandboxLinuxPIDNS
);
823 AboutSandboxRow(&data
, " ", IDS_ABOUT_SANDBOX_NET_NAMESPACES
,
824 status
& content::kSandboxLinuxNetNS
);
825 AboutSandboxRow(&data
,
827 IDS_ABOUT_SANDBOX_SECCOMP_BPF_SANDBOX
,
828 status
& content::kSandboxLinuxSeccompBPF
);
830 data
.append("</table>");
832 // The setuid sandbox is required as our first-layer sandbox.
833 bool good_layer1
= status
& content::kSandboxLinuxSUID
&&
834 status
& content::kSandboxLinuxPIDNS
&&
835 status
& content::kSandboxLinuxNetNS
;
836 // A second-layer sandbox is also required to be adequately sandboxed.
837 bool good_layer2
= status
& content::kSandboxLinuxSeccompBPF
;
838 bool good
= good_layer1
&& good_layer2
;
841 data
.append("<p style='color: green'>");
842 data
.append(l10n_util::GetStringUTF8(IDS_ABOUT_SANDBOX_OK
));
844 data
.append("<p style='color: red'>");
845 data
.append(l10n_util::GetStringUTF8(IDS_ABOUT_SANDBOX_BAD
));
854 // AboutMemoryHandler ----------------------------------------------------------
856 // Helper for AboutMemory to bind results from a ProcessMetrics object
857 // to a DictionaryValue. Fills ws_usage and comm_usage so that the objects
858 // can be used in caller's scope (e.g for appending to a net total).
859 void AboutMemoryHandler::BindProcessMetrics(base::DictionaryValue
* data
,
860 ProcessMemoryInformation
* info
) {
861 DCHECK(data
&& info
);
863 // Bind metrics to dictionary.
864 data
->SetInteger("ws_priv", static_cast<int>(info
->working_set
.priv
));
865 data
->SetInteger("ws_shareable",
866 static_cast<int>(info
->working_set
.shareable
));
867 data
->SetInteger("ws_shared", static_cast<int>(info
->working_set
.shared
));
868 data
->SetInteger("comm_priv", static_cast<int>(info
->committed
.priv
));
869 data
->SetInteger("comm_map", static_cast<int>(info
->committed
.mapped
));
870 data
->SetInteger("comm_image", static_cast<int>(info
->committed
.image
));
871 data
->SetInteger("pid", info
->pid
);
872 data
->SetString("version", info
->version
);
873 data
->SetInteger("processes", info
->num_processes
);
876 // Helper for AboutMemory to append memory usage information for all
877 // sub-processes (i.e. renderers, plugins) used by Chrome.
878 void AboutMemoryHandler::AppendProcess(base::ListValue
* child_data
,
879 ProcessMemoryInformation
* info
) {
880 DCHECK(child_data
&& info
);
882 // Append a new DictionaryValue for this renderer to our list.
883 base::DictionaryValue
* child
= new base::DictionaryValue();
884 child_data
->Append(child
);
885 BindProcessMetrics(child
, info
);
887 std::string
child_label(
888 ProcessMemoryInformation::GetFullTypeNameInEnglish(info
->process_type
,
889 info
->renderer_type
));
890 if (info
->is_diagnostics
)
891 child_label
.append(" (diagnostics)");
892 child
->SetString("child_name", child_label
);
893 base::ListValue
* titles
= new base::ListValue();
894 child
->Set("titles", titles
);
895 for (size_t i
= 0; i
< info
->titles
.size(); ++i
)
896 titles
->Append(new base::StringValue(info
->titles
[i
]));
899 void AboutMemoryHandler::OnDetailsAvailable() {
900 // the root of the JSON hierarchy for about:memory jstemplate
901 scoped_ptr
<base::DictionaryValue
> root(new base::DictionaryValue
);
902 base::ListValue
* browsers
= new base::ListValue();
903 root
->Set("browsers", browsers
);
905 const std::vector
<ProcessData
>& browser_processes
= processes();
907 // Aggregate per-process data into browser summary data.
908 base::string16 log_string
;
909 for (size_t index
= 0; index
< browser_processes
.size(); index
++) {
910 if (browser_processes
[index
].processes
.empty())
913 // Sum the information for the processes within this browser.
914 ProcessMemoryInformation aggregate
;
915 ProcessMemoryInformationList::const_iterator iterator
;
916 iterator
= browser_processes
[index
].processes
.begin();
917 aggregate
.pid
= iterator
->pid
;
918 aggregate
.version
= iterator
->version
;
919 while (iterator
!= browser_processes
[index
].processes
.end()) {
920 if (!iterator
->is_diagnostics
||
921 browser_processes
[index
].processes
.size() == 1) {
922 aggregate
.working_set
.priv
+= iterator
->working_set
.priv
;
923 aggregate
.working_set
.shared
+= iterator
->working_set
.shared
;
924 aggregate
.working_set
.shareable
+= iterator
->working_set
.shareable
;
925 aggregate
.committed
.priv
+= iterator
->committed
.priv
;
926 aggregate
.committed
.mapped
+= iterator
->committed
.mapped
;
927 aggregate
.committed
.image
+= iterator
->committed
.image
;
928 aggregate
.num_processes
++;
932 base::DictionaryValue
* browser_data
= new base::DictionaryValue();
933 browsers
->Append(browser_data
);
934 browser_data
->SetString("name", browser_processes
[index
].name
);
936 BindProcessMetrics(browser_data
, &aggregate
);
938 // We log memory info as we record it.
939 if (!log_string
.empty())
940 log_string
+= base::ASCIIToUTF16(", ");
941 log_string
+= browser_processes
[index
].name
+ base::ASCIIToUTF16(", ") +
942 base::Int64ToString16(aggregate
.working_set
.priv
) +
943 base::ASCIIToUTF16(", ") +
944 base::Int64ToString16(aggregate
.working_set
.shared
) +
945 base::ASCIIToUTF16(", ") +
946 base::Int64ToString16(aggregate
.working_set
.shareable
);
948 if (!log_string
.empty())
949 VLOG(1) << "memory: " << log_string
;
951 // Set the browser & renderer detailed process data.
952 base::DictionaryValue
* browser_data
= new base::DictionaryValue();
953 root
->Set("browzr_data", browser_data
);
954 base::ListValue
* child_data
= new base::ListValue();
955 root
->Set("child_data", child_data
);
957 ProcessData process
= browser_processes
[0]; // Chrome is the first browser.
958 root
->SetString("current_browser_name", process
.name
);
960 for (size_t index
= 0; index
< process
.processes
.size(); index
++) {
961 if (process
.processes
[index
].process_type
== content::PROCESS_TYPE_BROWSER
)
962 BindProcessMetrics(browser_data
, &process
.processes
[index
]);
964 AppendProcess(child_data
, &process
.processes
[index
]);
967 root
->SetBoolean("show_other_browsers",
968 browser_defaults::kShowOtherBrowsersInAboutMemory
);
970 base::DictionaryValue load_time_data
;
971 load_time_data
.SetString(
973 l10n_util::GetStringUTF16(IDS_MEMORY_USAGE_SUMMARY_DESC
));
974 webui::SetFontAndTextDirection(&load_time_data
);
975 load_time_data
.Set("jstemplateData", root
.release());
977 webui::UseVersion2 version2
;
979 webui::AppendJsonJS(&load_time_data
, &data
);
980 callback_
.Run(base::RefCountedString::TakeString(&data
));
985 // AboutUIHTMLSource ----------------------------------------------------------
987 AboutUIHTMLSource::AboutUIHTMLSource(const std::string
& source_name
,
989 : source_name_(source_name
),
992 AboutUIHTMLSource::~AboutUIHTMLSource() {}
994 std::string
AboutUIHTMLSource::GetSource() const {
998 void AboutUIHTMLSource::StartDataRequest(
999 const std::string
& path
,
1000 int render_process_id
,
1001 int render_frame_id
,
1002 const content::URLDataSource::GotDataCallback
& callback
) {
1003 std::string response
;
1004 // Add your data source here, in alphabetical order.
1005 if (source_name_
== chrome::kChromeUIChromeURLsHost
) {
1006 response
= ChromeURLs();
1007 } else if (source_name_
== chrome::kChromeUICreditsHost
) {
1008 int idr
= (path
== kCreditsJsPath
) ? IDR_CREDITS_JS
: IDR_CREDITS_HTML
;
1009 response
= ResourceBundle::GetSharedInstance().GetRawDataResource(
1011 #if defined(OS_CHROMEOS)
1012 } else if (source_name_
== chrome::kChromeUIDiscardsHost
) {
1013 response
= AboutDiscards(path
);
1015 } else if (source_name_
== chrome::kChromeUIDNSHost
) {
1016 AboutDnsHandler::Start(profile(), callback
);
1018 #if defined(OS_LINUX) || defined(OS_OPENBSD)
1019 } else if (source_name_
== chrome::kChromeUILinuxProxyConfigHost
) {
1020 response
= AboutLinuxProxyConfig();
1022 } else if (source_name_
== chrome::kChromeUIMemoryHost
) {
1023 response
= GetAboutMemoryRedirectResponse(profile());
1024 } else if (source_name_
== chrome::kChromeUIMemoryRedirectHost
) {
1025 FinishMemoryDataRequest(path
, callback
);
1027 #if defined(OS_CHROMEOS)
1028 } else if (source_name_
== chrome::kChromeUIOSCreditsHost
) {
1029 response
= ResourceBundle::GetSharedInstance().GetRawDataResource(
1030 IDR_OS_CREDITS_HTML
).as_string();
1032 #if defined(OS_LINUX) || defined(OS_OPENBSD)
1033 } else if (source_name_
== chrome::kChromeUISandboxHost
) {
1034 response
= AboutSandbox();
1036 } else if (source_name_
== chrome::kChromeUIStatsHost
) {
1037 response
= AboutStats(path
);
1038 } else if (source_name_
== chrome::kChromeUITermsHost
) {
1039 #if defined(OS_CHROMEOS)
1040 ChromeOSTermsHandler::Start(path
, callback
);
1043 response
= l10n_util::GetStringUTF8(IDS_TERMS_HTML
);
1047 FinishDataRequest(response
, callback
);
1050 void AboutUIHTMLSource::FinishDataRequest(
1051 const std::string
& html
,
1052 const content::URLDataSource::GotDataCallback
& callback
) {
1053 std::string
html_copy(html
);
1054 callback
.Run(base::RefCountedString::TakeString(&html_copy
));
1057 std::string
AboutUIHTMLSource::GetMimeType(const std::string
& path
) const {
1058 if (path
== kCreditsJsPath
||
1059 path
== kStatsJsPath
||
1060 path
== kStringsJsPath
||
1061 path
== kMemoryJsPath
) {
1062 return "application/javascript";
1067 bool AboutUIHTMLSource::ShouldAddContentSecurityPolicy() const {
1068 #if defined(OS_CHROMEOS)
1069 if (source_name_
== chrome::kChromeUIOSCreditsHost
)
1072 return content::URLDataSource::ShouldAddContentSecurityPolicy();
1075 bool AboutUIHTMLSource::ShouldDenyXFrameOptions() const {
1076 #if defined(OS_CHROMEOS)
1077 if (source_name_
== chrome::kChromeUITermsHost
) {
1078 // chrome://terms page is embedded in iframe to chrome://oobe.
1082 return content::URLDataSource::ShouldDenyXFrameOptions();
1085 AboutUI::AboutUI(content::WebUI
* web_ui
, const std::string
& name
)
1086 : WebUIController(web_ui
) {
1087 Profile
* profile
= Profile::FromWebUI(web_ui
);
1089 #if defined(ENABLE_THEMES)
1090 // Set up the chrome://theme/ source.
1091 ThemeSource
* theme
= new ThemeSource(profile
);
1092 content::URLDataSource::Add(profile
, theme
);
1095 content::URLDataSource::Add(profile
, new AboutUIHTMLSource(name
, profile
));