Implemented consumer management unenrollment.
[chromium-blink-merge.git] / chrome / browser / ui / webui / about_ui.cc
blob3dbe9e6de797bdb222faabc3d8a38fe0fd28c97a
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"
7 #include <algorithm>
8 #include <string>
9 #include <utility>
10 #include <vector>
12 #include "base/bind.h"
13 #include "base/bind_helpers.h"
14 #include "base/callback.h"
15 #include "base/command_line.h"
16 #include "base/files/file_util.h"
17 #include "base/i18n/number_formatting.h"
18 #include "base/json/json_writer.h"
19 #include "base/memory/singleton.h"
20 #include "base/metrics/statistics_recorder.h"
21 #include "base/metrics/stats_table.h"
22 #include "base/strings/string_number_conversions.h"
23 #include "base/strings/string_piece.h"
24 #include "base/strings/string_util.h"
25 #include "base/strings/stringprintf.h"
26 #include "base/strings/utf_string_conversions.h"
27 #include "base/threading/thread.h"
28 #include "base/values.h"
29 #include "chrome/browser/about_flags.h"
30 #include "chrome/browser/browser_process.h"
31 #include "chrome/browser/defaults.h"
32 #include "chrome/browser/memory_details.h"
33 #include "chrome/browser/net/predictor.h"
34 #include "chrome/browser/profiles/profile.h"
35 #include "chrome/browser/profiles/profile_manager.h"
36 #include "chrome/browser/ui/browser_dialogs.h"
37 #include "chrome/common/chrome_paths.h"
38 #include "chrome/common/render_messages.h"
39 #include "chrome/common/url_constants.h"
40 #include "chrome/grit/chromium_strings.h"
41 #include "chrome/grit/generated_resources.h"
42 #include "chrome/grit/locale_settings.h"
43 #include "content/public/browser/browser_thread.h"
44 #include "content/public/browser/render_process_host.h"
45 #include "content/public/browser/render_view_host.h"
46 #include "content/public/browser/url_data_source.h"
47 #include "content/public/browser/web_contents.h"
48 #include "content/public/common/content_client.h"
49 #include "content/public/common/process_type.h"
50 #include "google_apis/gaia/google_service_auth_error.h"
51 #include "grit/browser_resources.h"
52 #include "net/base/escape.h"
53 #include "net/base/filename_util.h"
54 #include "net/base/load_flags.h"
55 #include "net/http/http_response_headers.h"
56 #include "net/url_request/url_fetcher.h"
57 #include "net/url_request/url_request_status.h"
58 #include "ui/base/l10n/l10n_util.h"
59 #include "ui/base/resource/resource_bundle.h"
60 #include "ui/base/webui/jstemplate_builder.h"
61 #include "ui/base/webui/web_ui_util.h"
62 #include "url/gurl.h"
64 #if defined(ENABLE_THEMES)
65 #include "chrome/browser/ui/webui/theme_source.h"
66 #endif
68 #if defined(OS_LINUX) || defined(OS_OPENBSD)
69 #include "content/public/browser/zygote_host_linux.h"
70 #include "content/public/common/sandbox_linux.h"
71 #endif
73 #if defined(OS_WIN)
74 #include "chrome/browser/enumerate_modules_model_win.h"
75 #endif
77 #if defined(OS_CHROMEOS)
78 #include "chrome/browser/browser_process_platform_part_chromeos.h"
79 #include "chrome/browser/chromeos/customization/customization_document.h"
80 #include "chrome/browser/chromeos/memory/oom_priority_manager.h"
81 #endif
83 using base::Time;
84 using base::TimeDelta;
85 using content::BrowserThread;
86 using content::WebContents;
88 namespace {
90 const char kCreditsJsPath[] = "credits.js";
91 const char kMemoryJsPath[] = "memory.js";
92 const char kMemoryCssPath[] = "about_memory.css";
93 const char kStatsJsPath[] = "stats.js";
94 const char kStringsJsPath[] = "strings.js";
96 // When you type about:memory, it actually loads this intermediate URL that
97 // redirects you to the final page. This avoids the problem where typing
98 // "about:memory" on the new tab page or any other page where a process
99 // transition would occur to the about URL will cause some confusion.
101 // The problem is that during the processing of the memory page, there are two
102 // processes active, the original and the destination one. This can create the
103 // impression that we're using more resources than we actually are. This
104 // redirect solves the problem by eliminating the process transition during the
105 // time that about memory is being computed.
106 std::string GetAboutMemoryRedirectResponse(Profile* profile) {
107 return base::StringPrintf("<meta http-equiv='refresh' content='0;%s'>",
108 chrome::kChromeUIMemoryRedirectURL);
111 // Handling about:memory is complicated enough to encapsulate its related
112 // methods into a single class. The user should create it (on the heap) and call
113 // its |StartFetch()| method.
114 class AboutMemoryHandler : public MemoryDetails {
115 public:
116 explicit AboutMemoryHandler(
117 const content::URLDataSource::GotDataCallback& callback)
118 : callback_(callback) {
121 void OnDetailsAvailable() override;
123 private:
124 ~AboutMemoryHandler() override {}
126 void BindProcessMetrics(base::DictionaryValue* data,
127 ProcessMemoryInformation* info);
128 void AppendProcess(base::ListValue* child_data,
129 ProcessMemoryInformation* info);
131 content::URLDataSource::GotDataCallback callback_;
133 DISALLOW_COPY_AND_ASSIGN(AboutMemoryHandler);
136 #if defined(OS_CHROMEOS)
138 const char kKeyboardUtilsPath[] = "keyboard_utils.js";
140 // chrome://terms falls back to offline page after kOnlineTermsTimeoutSec.
141 const int kOnlineTermsTimeoutSec = 7;
143 // Helper class that fetches the online Chrome OS terms. Empty string is
144 // returned once fetching failed or exceeded |kOnlineTermsTimeoutSec|.
145 class ChromeOSOnlineTermsHandler : public net::URLFetcherDelegate {
146 public:
147 typedef base::Callback<void (ChromeOSOnlineTermsHandler*)> FetchCallback;
149 explicit ChromeOSOnlineTermsHandler(const FetchCallback& callback,
150 const std::string& locale)
151 : fetch_callback_(callback) {
152 std::string eula_URL = base::StringPrintf(chrome::kOnlineEulaURLPath,
153 locale.c_str());
154 eula_fetcher_.reset(net::URLFetcher::Create(0 /* ID used for testing */,
155 GURL(eula_URL),
156 net::URLFetcher::GET,
157 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),
168 this,
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();
184 private:
185 // Prevents allocation on the stack. ChromeOSOnlineTermsHandler should be
186 // created by 'operator new'. |this| takes care of destruction.
187 virtual ~ChromeOSOnlineTermsHandler() {}
189 // net::URLFetcherDelegate:
190 virtual void OnURLFetchComplete(const net::URLFetcher* source) override {
191 if (source != eula_fetcher_.get()) {
192 NOTREACHED() << "Callback from foreign URL fetcher";
193 return;
195 fetch_callback_.Run(this);
196 delete this;
199 void OnDownloadTimeout() {
200 eula_fetcher_.reset();
201 fetch_callback_.Run(this);
202 delete this;
205 // Timer that enforces a timeout on the attempt to download the
206 // ChromeOS Terms.
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> {
220 public:
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();
228 private:
229 friend class base::RefCountedThreadSafe<ChromeOSTermsHandler>;
231 ChromeOSTermsHandler(const std::string& path,
232 const content::URLDataSource::GotDataCallback& callback)
233 : path_(path),
234 callback_(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));
248 } else {
249 // Try to load online version of ChromeOS terms first.
250 // ChromeOSOnlineTermsHandler object destroys itself.
251 new ChromeOSOnlineTermsHandler(
252 base::Bind(&ChromeOSTermsHandler::OnOnlineEULAFetched, this),
253 locale_);
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));
265 } else {
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_)) {
279 contents_.clear();
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.
297 contents_.clear();
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_));
314 // Path in the URL.
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> {
331 public:
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();
339 private:
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)
354 .as_string();
355 ResponseOnUIThread();
356 return;
358 // Load local Chrome OS credits from the disk.
359 BrowserThread::PostBlockingPoolTaskAndReply(
360 FROM_HERE,
361 base::Bind(&ChromeOSCreditsHandler::LoadCreditsFileOnBlockingPool,
362 this),
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.
372 contents_.clear();
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)
382 .as_string();
384 callback_.Run(base::RefCountedString::TakeString(&contents_));
387 // Path in the URL.
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);
398 #endif
400 } // namespace
402 // Individual about handlers ---------------------------------------------------
404 namespace about_ui {
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");
415 if (refresh > 0) {
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;
436 namespace {
438 std::string ChromeURLs() {
439 std::string html;
440 AppendHeader(&html, 0, "Chrome URLs");
441 AppendBody(&html);
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";
456 html += "</ul>\n";
457 AppendFooter(&html);
458 return html;
461 #if defined(OS_CHROMEOS)
463 // Html output helper functions
465 // Helper function to wrap HTML with a tag.
466 std::string WrapWithTag(const std::string& tag, const std::string& text) {
467 return "<" + tag + ">" + text + "</" + tag + ">";
470 // Helper function to wrap Html with <td> tag.
471 std::string WrapWithTD(const std::string& text) {
472 return "<td>" + text + "</td>";
475 // Helper function to wrap Html with <tr> tag.
476 std::string WrapWithTR(const std::string& text) {
477 return "<tr>" + text + "</tr>";
480 std::string AddStringRow(const std::string& name, const std::string& value) {
481 std::string row;
482 row.append(WrapWithTD(name));
483 row.append(WrapWithTD(value));
484 return WrapWithTR(row);
487 void AddContentSecurityPolicy(std::string* output) {
488 output->append("<meta http-equiv='Content-Security-Policy' "
489 "content='default-src 'none';'>");
492 // TODO(stevenjb): L10N AboutDiscards.
494 std::string AboutDiscardsRun() {
495 std::string output;
496 AppendHeader(&output, 0, "About discards");
497 output.append(
498 base::StringPrintf("<meta http-equiv='refresh' content='2;%s'>",
499 chrome::kChromeUIDiscardsURL));
500 AddContentSecurityPolicy(&output);
501 output.append(WrapWithTag("p", "Discarding a tab..."));
502 g_browser_process->platform_part()->
503 oom_priority_manager()->LogMemoryAndDiscardTab();
504 AppendFooter(&output);
505 return output;
508 std::string AboutDiscards(const std::string& path) {
509 std::string output;
510 const char kRunCommand[] = "run";
511 if (path == kRunCommand)
512 return AboutDiscardsRun();
513 AppendHeader(&output, 0, "About discards");
514 AddContentSecurityPolicy(&output);
515 AppendBody(&output);
516 output.append("<h3>About discards</h3>");
517 output.append(
518 "<p>Tabs sorted from most interesting to least interesting. The least "
519 "interesting tab may be discarded if we run out of physical memory.</p>");
521 chromeos::OomPriorityManager* oom =
522 g_browser_process->platform_part()->oom_priority_manager();
523 std::vector<base::string16> titles = oom->GetTabTitles();
524 if (!titles.empty()) {
525 output.append("<ul>");
526 std::vector<base::string16>::iterator it = titles.begin();
527 for ( ; it != titles.end(); ++it) {
528 std::string title = base::UTF16ToUTF8(*it);
529 title = net::EscapeForHTML(title);
530 output.append(WrapWithTag("li", title));
532 output.append("</ul>");
533 } else {
534 output.append("<p>None found. Wait 10 seconds, then refresh.</p>");
536 output.append(base::StringPrintf("%d discards this session. ",
537 oom->discard_count()));
538 output.append(base::StringPrintf("<a href='%s%s'>Discard tab now</a>",
539 chrome::kChromeUIDiscardsURL,
540 kRunCommand));
542 base::SystemMemoryInfoKB meminfo;
543 base::GetSystemMemoryInfo(&meminfo);
544 output.append("<h3>System memory information in MB</h3>");
545 output.append("<table>");
546 // Start with summary statistics.
547 output.append(AddStringRow(
548 "Total", base::IntToString(meminfo.total / 1024)));
549 output.append(AddStringRow(
550 "Free", base::IntToString(meminfo.free / 1024)));
551 int mem_allocated_kb = meminfo.active_anon + meminfo.inactive_anon;
552 #if defined(ARCH_CPU_ARM_FAMILY)
553 // ARM counts allocated graphics memory separately from anonymous.
554 if (meminfo.gem_size != -1)
555 mem_allocated_kb += meminfo.gem_size / 1024;
556 #endif
557 output.append(AddStringRow(
558 "Allocated", base::IntToString(mem_allocated_kb / 1024)));
559 // Add some space, then detailed numbers.
560 output.append(AddStringRow("&nbsp;", "&nbsp;"));
561 output.append(AddStringRow(
562 "Buffered", base::IntToString(meminfo.buffers / 1024)));
563 output.append(AddStringRow(
564 "Cached", base::IntToString(meminfo.cached / 1024)));
565 output.append(AddStringRow(
566 "Active Anon", base::IntToString(meminfo.active_anon / 1024)));
567 output.append(AddStringRow(
568 "Inactive Anon", base::IntToString(meminfo.inactive_anon / 1024)));
569 output.append(AddStringRow(
570 "Shared", base::IntToString(meminfo.shmem / 1024)));
571 output.append(AddStringRow(
572 "Graphics", base::IntToString(meminfo.gem_size / 1024 / 1024)));
573 output.append("</table>");
575 AppendFooter(&output);
576 return output;
579 #endif // OS_CHROMEOS
581 // AboutDnsHandler bounces the request back to the IO thread to collect
582 // the DNS information.
583 class AboutDnsHandler : public base::RefCountedThreadSafe<AboutDnsHandler> {
584 public:
585 static void Start(Profile* profile,
586 const content::URLDataSource::GotDataCallback& callback) {
587 scoped_refptr<AboutDnsHandler> handler(
588 new AboutDnsHandler(profile, callback));
589 handler->StartOnUIThread();
592 private:
593 friend class base::RefCountedThreadSafe<AboutDnsHandler>;
595 AboutDnsHandler(Profile* profile,
596 const content::URLDataSource::GotDataCallback& callback)
597 : profile_(profile),
598 callback_(callback) {
599 DCHECK_CURRENTLY_ON(BrowserThread::UI);
602 virtual ~AboutDnsHandler() {}
604 // Calls FinishOnUIThread() on completion.
605 void StartOnUIThread() {
606 DCHECK_CURRENTLY_ON(BrowserThread::UI);
607 chrome_browser_net::Predictor* predictor = profile_->GetNetworkPredictor();
608 BrowserThread::PostTask(
609 BrowserThread::IO, FROM_HERE,
610 base::Bind(&AboutDnsHandler::StartOnIOThread, this, predictor));
613 void StartOnIOThread(chrome_browser_net::Predictor* predictor) {
614 DCHECK_CURRENTLY_ON(BrowserThread::IO);
616 std::string data;
617 AppendHeader(&data, 0, "About DNS");
618 AppendBody(&data);
619 chrome_browser_net::Predictor::PredictorGetHtmlInfo(predictor, &data);
620 AppendFooter(&data);
622 BrowserThread::PostTask(
623 BrowserThread::UI, FROM_HERE,
624 base::Bind(&AboutDnsHandler::FinishOnUIThread, this, data));
627 void FinishOnUIThread(const std::string& data) {
628 DCHECK_CURRENTLY_ON(BrowserThread::UI);
629 std::string data_copy(data);
630 callback_.Run(base::RefCountedString::TakeString(&data_copy));
633 Profile* profile_;
635 // Callback to run with the response.
636 content::URLDataSource::GotDataCallback callback_;
638 DISALLOW_COPY_AND_ASSIGN(AboutDnsHandler);
641 void FinishMemoryDataRequest(
642 const std::string& path,
643 const content::URLDataSource::GotDataCallback& callback) {
644 if (path == kStringsJsPath) {
645 // The AboutMemoryHandler cleans itself up, but |StartFetch()| will want
646 // the refcount to be greater than 0.
647 scoped_refptr<AboutMemoryHandler> handler(new AboutMemoryHandler(callback));
648 // TODO(jamescook): Maybe this shouldn't update UMA?
649 handler->StartFetch(MemoryDetails::UPDATE_USER_METRICS);
650 } else {
651 int id = IDR_ABOUT_MEMORY_HTML;
652 if (path == kMemoryJsPath) {
653 id = IDR_ABOUT_MEMORY_JS;
654 } else if (path == kMemoryCssPath) {
655 id = IDR_ABOUT_MEMORY_CSS;
658 std::string result =
659 ResourceBundle::GetSharedInstance().GetRawDataResource(id).as_string();
660 callback.Run(base::RefCountedString::TakeString(&result));
664 // Handler for filling in the "about:stats" page, as called by the browser's
665 // About handler processing.
666 // |query| is roughly the query string of the about:stats URL.
667 // Returns a string containing the HTML to render for the about:stats page.
668 // Conditional Output:
669 // if |query| is "json", returns a JSON format of all counters.
670 // if |query| is "raw", returns plain text of counter deltas.
671 // otherwise, returns HTML with pretty JS/HTML to display the data.
672 std::string AboutStats(const std::string& query) {
673 // We keep the base::DictionaryValue tree live so that we can do delta
674 // stats computations across runs.
675 CR_DEFINE_STATIC_LOCAL(base::DictionaryValue, root, ());
676 static base::TimeTicks last_sample_time = base::TimeTicks::Now();
678 base::TimeTicks now = base::TimeTicks::Now();
679 base::TimeDelta time_since_last_sample = now - last_sample_time;
680 last_sample_time = now;
682 base::StatsTable* table = base::StatsTable::current();
683 if (!table)
684 return std::string();
686 // We maintain two lists - one for counters and one for timers.
687 // Timers actually get stored on both lists.
688 base::ListValue* counters;
689 if (!root.GetList("counters", &counters)) {
690 counters = new base::ListValue();
691 root.Set("counters", counters);
694 base::ListValue* timers;
695 if (!root.GetList("timers", &timers)) {
696 timers = new base::ListValue();
697 root.Set("timers", timers);
700 // NOTE: Counters start at index 1.
701 for (int index = 1; index <= table->GetMaxCounters(); index++) {
702 // Get the counter's full name
703 std::string full_name = table->GetRowName(index);
704 if (full_name.length() == 0)
705 break;
706 DCHECK_EQ(':', full_name[1]);
707 char counter_type = full_name[0];
708 std::string name = full_name.substr(2);
710 // JSON doesn't allow '.' in names.
711 size_t pos;
712 while ((pos = name.find(".")) != std::string::npos)
713 name.replace(pos, 1, ":");
715 // Try to see if this name already exists.
716 base::DictionaryValue* counter = NULL;
717 for (size_t scan_index = 0;
718 scan_index < counters->GetSize(); scan_index++) {
719 base::DictionaryValue* dictionary;
720 if (counters->GetDictionary(scan_index, &dictionary)) {
721 std::string scan_name;
722 if (dictionary->GetString("name", &scan_name) && scan_name == name) {
723 counter = dictionary;
725 } else {
726 NOTREACHED(); // Should always be there
730 if (counter == NULL) {
731 counter = new base::DictionaryValue();
732 counter->SetString("name", name);
733 counters->Append(counter);
736 switch (counter_type) {
737 case 'c':
739 int new_value = table->GetRowValue(index);
740 int prior_value = 0;
741 int delta = 0;
742 if (counter->GetInteger("value", &prior_value)) {
743 delta = new_value - prior_value;
745 counter->SetInteger("value", new_value);
746 counter->SetInteger("delta", delta);
748 break;
749 case 'm':
751 // TODO(mbelshe): implement me.
753 break;
754 case 't':
756 int time = table->GetRowValue(index);
757 counter->SetInteger("time", time);
759 // Store this on the timers list as well.
760 timers->Append(counter);
762 break;
763 default:
764 NOTREACHED();
768 std::string data;
769 if (query == "json" || query == kStringsJsPath) {
770 base::JSONWriter::WriteWithOptions(
771 &root,
772 base::JSONWriter::OPTIONS_PRETTY_PRINT,
773 &data);
774 if (query == kStringsJsPath)
775 data = "loadTimeData.data = " + data + ";";
776 } else if (query == "raw") {
777 // Dump the raw counters which have changed in text format.
778 data = "<pre>";
779 data.append(base::StringPrintf("Counter changes in the last %ldms\n",
780 static_cast<long int>(time_since_last_sample.InMilliseconds())));
781 for (size_t i = 0; i < counters->GetSize(); ++i) {
782 base::Value* entry = NULL;
783 bool rv = counters->Get(i, &entry);
784 if (!rv)
785 continue; // None of these should fail.
786 base::DictionaryValue* counter =
787 static_cast<base::DictionaryValue*>(entry);
788 int delta;
789 rv = counter->GetInteger("delta", &delta);
790 if (!rv)
791 continue;
792 if (delta > 0) {
793 std::string name;
794 rv = counter->GetString("name", &name);
795 if (!rv)
796 continue;
797 int value;
798 rv = counter->GetInteger("value", &value);
799 if (!rv)
800 continue;
801 data.append(name);
802 data.append(":");
803 data.append(base::IntToString(delta));
804 data.append("\n");
807 data.append("</pre>");
808 } else {
809 // Get about_stats.html/js from resource bundle.
810 data = ResourceBundle::GetSharedInstance().GetRawDataResource(
811 (query == kStatsJsPath ?
812 IDR_ABOUT_STATS_JS : IDR_ABOUT_STATS_HTML)).as_string();
814 if (query != kStatsJsPath) {
815 // Clear the timer list since we stored the data in the timers list
816 // as well.
817 for (int index = static_cast<int>(timers->GetSize())-1; index >= 0;
818 index--) {
819 scoped_ptr<base::Value> value;
820 timers->Remove(index, &value);
821 // We don't care about the value pointer; it's still tracked
822 // on the counters list.
823 ignore_result(value.release());
828 return data;
831 #if defined(OS_LINUX) || defined(OS_OPENBSD)
832 std::string AboutLinuxProxyConfig() {
833 std::string data;
834 AppendHeader(&data, 0,
835 l10n_util::GetStringUTF8(IDS_ABOUT_LINUX_PROXY_CONFIG_TITLE));
836 data.append("<style>body { max-width: 70ex; padding: 2ex 5ex; }</style>");
837 AppendBody(&data);
838 base::FilePath binary = base::CommandLine::ForCurrentProcess()->GetProgram();
839 data.append(l10n_util::GetStringFUTF8(
840 IDS_ABOUT_LINUX_PROXY_CONFIG_BODY,
841 l10n_util::GetStringUTF16(IDS_PRODUCT_NAME),
842 base::ASCIIToUTF16(binary.BaseName().value())));
843 AppendFooter(&data);
844 return data;
847 void AboutSandboxRow(std::string* data, const std::string& prefix, int name_id,
848 bool good) {
849 data->append("<tr><td>");
850 data->append(prefix);
851 data->append(l10n_util::GetStringUTF8(name_id));
852 if (good) {
853 data->append("</td><td style='color: green;'>");
854 data->append(
855 l10n_util::GetStringUTF8(IDS_CONFIRM_MESSAGEBOX_YES_BUTTON_LABEL));
856 } else {
857 data->append("</td><td style='color: red;'>");
858 data->append(
859 l10n_util::GetStringUTF8(IDS_CONFIRM_MESSAGEBOX_NO_BUTTON_LABEL));
861 data->append("</td></tr>");
864 std::string AboutSandbox() {
865 std::string data;
866 AppendHeader(&data, 0, l10n_util::GetStringUTF8(IDS_ABOUT_SANDBOX_TITLE));
867 AppendBody(&data);
868 data.append("<h1>");
869 data.append(l10n_util::GetStringUTF8(IDS_ABOUT_SANDBOX_TITLE));
870 data.append("</h1>");
872 // Get expected sandboxing status of renderers.
873 const int status = content::ZygoteHost::GetInstance()->GetSandboxStatus();
875 data.append("<table>");
877 AboutSandboxRow(&data,
878 std::string(),
879 IDS_ABOUT_SANDBOX_SUID_SANDBOX,
880 status & content::kSandboxLinuxSUID);
881 AboutSandboxRow(&data, "&nbsp;&nbsp;", IDS_ABOUT_SANDBOX_PID_NAMESPACES,
882 status & content::kSandboxLinuxPIDNS);
883 AboutSandboxRow(&data, "&nbsp;&nbsp;", IDS_ABOUT_SANDBOX_NET_NAMESPACES,
884 status & content::kSandboxLinuxNetNS);
885 AboutSandboxRow(&data,
886 std::string(),
887 IDS_ABOUT_SANDBOX_SECCOMP_BPF_SANDBOX,
888 status & content::kSandboxLinuxSeccompBPF);
889 AboutSandboxRow(&data,
890 std::string(),
891 IDS_ABOUT_SANDBOX_SECCOMP_BPF_SANDBOX_TSYNC,
892 status & content::kSandboxLinuxSeccompTSYNC);
893 AboutSandboxRow(&data,
894 std::string(),
895 IDS_ABOUT_SANDBOX_YAMA_LSM,
896 status & content::kSandboxLinuxYama);
898 data.append("</table>");
900 // The setuid sandbox is required as our first-layer sandbox.
901 bool good_layer1 = status & content::kSandboxLinuxSUID &&
902 status & content::kSandboxLinuxPIDNS &&
903 status & content::kSandboxLinuxNetNS;
904 // A second-layer sandbox is also required to be adequately sandboxed.
905 bool good_layer2 = status & content::kSandboxLinuxSeccompBPF;
906 bool good = good_layer1 && good_layer2;
908 if (good) {
909 data.append("<p style='color: green'>");
910 data.append(l10n_util::GetStringUTF8(IDS_ABOUT_SANDBOX_OK));
911 } else {
912 data.append("<p style='color: red'>");
913 data.append(l10n_util::GetStringUTF8(IDS_ABOUT_SANDBOX_BAD));
915 data.append("</p>");
917 AppendFooter(&data);
918 return data;
920 #endif
922 // AboutMemoryHandler ----------------------------------------------------------
924 // Helper for AboutMemory to bind results from a ProcessMetrics object
925 // to a DictionaryValue. Fills ws_usage and comm_usage so that the objects
926 // can be used in caller's scope (e.g for appending to a net total).
927 void AboutMemoryHandler::BindProcessMetrics(base::DictionaryValue* data,
928 ProcessMemoryInformation* info) {
929 DCHECK(data && info);
931 // Bind metrics to dictionary.
932 data->SetInteger("ws_priv", static_cast<int>(info->working_set.priv));
933 data->SetInteger("ws_shareable",
934 static_cast<int>(info->working_set.shareable));
935 data->SetInteger("ws_shared", static_cast<int>(info->working_set.shared));
936 data->SetInteger("comm_priv", static_cast<int>(info->committed.priv));
937 data->SetInteger("comm_map", static_cast<int>(info->committed.mapped));
938 data->SetInteger("comm_image", static_cast<int>(info->committed.image));
939 data->SetInteger("pid", info->pid);
940 data->SetString("version", info->version);
941 data->SetInteger("processes", info->num_processes);
944 // Helper for AboutMemory to append memory usage information for all
945 // sub-processes (i.e. renderers, plugins) used by Chrome.
946 void AboutMemoryHandler::AppendProcess(base::ListValue* child_data,
947 ProcessMemoryInformation* info) {
948 DCHECK(child_data && info);
950 // Append a new DictionaryValue for this renderer to our list.
951 base::DictionaryValue* child = new base::DictionaryValue();
952 child_data->Append(child);
953 BindProcessMetrics(child, info);
955 std::string child_label(
956 ProcessMemoryInformation::GetFullTypeNameInEnglish(info->process_type,
957 info->renderer_type));
958 if (info->is_diagnostics)
959 child_label.append(" (diagnostics)");
960 child->SetString("child_name", child_label);
961 base::ListValue* titles = new base::ListValue();
962 child->Set("titles", titles);
963 for (size_t i = 0; i < info->titles.size(); ++i)
964 titles->Append(new base::StringValue(info->titles[i]));
967 void AboutMemoryHandler::OnDetailsAvailable() {
968 // the root of the JSON hierarchy for about:memory jstemplate
969 scoped_ptr<base::DictionaryValue> root(new base::DictionaryValue);
970 base::ListValue* browsers = new base::ListValue();
971 root->Set("browsers", browsers);
973 const std::vector<ProcessData>& browser_processes = processes();
975 // Aggregate per-process data into browser summary data.
976 base::string16 log_string;
977 for (size_t index = 0; index < browser_processes.size(); index++) {
978 if (browser_processes[index].processes.empty())
979 continue;
981 // Sum the information for the processes within this browser.
982 ProcessMemoryInformation aggregate;
983 ProcessMemoryInformationList::const_iterator iterator;
984 iterator = browser_processes[index].processes.begin();
985 aggregate.pid = iterator->pid;
986 aggregate.version = iterator->version;
987 while (iterator != browser_processes[index].processes.end()) {
988 if (!iterator->is_diagnostics ||
989 browser_processes[index].processes.size() == 1) {
990 aggregate.working_set.priv += iterator->working_set.priv;
991 aggregate.working_set.shared += iterator->working_set.shared;
992 aggregate.working_set.shareable += iterator->working_set.shareable;
993 aggregate.committed.priv += iterator->committed.priv;
994 aggregate.committed.mapped += iterator->committed.mapped;
995 aggregate.committed.image += iterator->committed.image;
996 aggregate.num_processes++;
998 ++iterator;
1000 base::DictionaryValue* browser_data = new base::DictionaryValue();
1001 browsers->Append(browser_data);
1002 browser_data->SetString("name", browser_processes[index].name);
1004 BindProcessMetrics(browser_data, &aggregate);
1006 // We log memory info as we record it.
1007 if (!log_string.empty())
1008 log_string += base::ASCIIToUTF16(", ");
1009 log_string += browser_processes[index].name + base::ASCIIToUTF16(", ") +
1010 base::Int64ToString16(aggregate.working_set.priv) +
1011 base::ASCIIToUTF16(", ") +
1012 base::Int64ToString16(aggregate.working_set.shared) +
1013 base::ASCIIToUTF16(", ") +
1014 base::Int64ToString16(aggregate.working_set.shareable);
1016 if (!log_string.empty())
1017 VLOG(1) << "memory: " << log_string;
1019 // Set the browser & renderer detailed process data.
1020 base::DictionaryValue* browser_data = new base::DictionaryValue();
1021 root->Set("browzr_data", browser_data);
1022 base::ListValue* child_data = new base::ListValue();
1023 root->Set("child_data", child_data);
1025 ProcessData process = browser_processes[0]; // Chrome is the first browser.
1026 root->SetString("current_browser_name", process.name);
1028 for (size_t index = 0; index < process.processes.size(); index++) {
1029 if (process.processes[index].process_type == content::PROCESS_TYPE_BROWSER)
1030 BindProcessMetrics(browser_data, &process.processes[index]);
1031 else
1032 AppendProcess(child_data, &process.processes[index]);
1035 root->SetBoolean("show_other_browsers",
1036 browser_defaults::kShowOtherBrowsersInAboutMemory);
1038 base::DictionaryValue load_time_data;
1039 load_time_data.SetString(
1040 "summary_desc",
1041 l10n_util::GetStringUTF16(IDS_MEMORY_USAGE_SUMMARY_DESC));
1042 webui::SetFontAndTextDirection(&load_time_data);
1043 load_time_data.Set("jstemplateData", root.release());
1045 std::string data;
1046 webui::AppendJsonJS(&load_time_data, &data);
1047 callback_.Run(base::RefCountedString::TakeString(&data));
1050 } // namespace
1052 // AboutUIHTMLSource ----------------------------------------------------------
1054 AboutUIHTMLSource::AboutUIHTMLSource(const std::string& source_name,
1055 Profile* profile)
1056 : source_name_(source_name),
1057 profile_(profile) {}
1059 AboutUIHTMLSource::~AboutUIHTMLSource() {}
1061 std::string AboutUIHTMLSource::GetSource() const {
1062 return source_name_;
1065 void AboutUIHTMLSource::StartDataRequest(
1066 const std::string& path,
1067 int render_process_id,
1068 int render_frame_id,
1069 const content::URLDataSource::GotDataCallback& callback) {
1070 std::string response;
1071 // Add your data source here, in alphabetical order.
1072 if (source_name_ == chrome::kChromeUIChromeURLsHost) {
1073 response = ChromeURLs();
1074 } else if (source_name_ == chrome::kChromeUICreditsHost) {
1075 int idr = IDR_CREDITS_HTML;
1076 if (path == kCreditsJsPath)
1077 idr = IDR_CREDITS_JS;
1078 #if defined(OS_CHROMEOS)
1079 else if (path == kKeyboardUtilsPath)
1080 idr = IDR_KEYBOARD_UTILS_JS;
1081 #endif
1083 response = ResourceBundle::GetSharedInstance().GetRawDataResource(
1084 idr).as_string();
1085 #if defined(OS_CHROMEOS)
1086 } else if (source_name_ == chrome::kChromeUIDiscardsHost) {
1087 response = AboutDiscards(path);
1088 #endif
1089 } else if (source_name_ == chrome::kChromeUIDNSHost) {
1090 AboutDnsHandler::Start(profile(), callback);
1091 return;
1092 #if defined(OS_LINUX) || defined(OS_OPENBSD)
1093 } else if (source_name_ == chrome::kChromeUILinuxProxyConfigHost) {
1094 response = AboutLinuxProxyConfig();
1095 #endif
1096 } else if (source_name_ == chrome::kChromeUIMemoryHost) {
1097 response = GetAboutMemoryRedirectResponse(profile());
1098 } else if (source_name_ == chrome::kChromeUIMemoryRedirectHost) {
1099 FinishMemoryDataRequest(path, callback);
1100 return;
1101 #if defined(OS_CHROMEOS)
1102 } else if (source_name_ == chrome::kChromeUIOSCreditsHost) {
1103 ChromeOSCreditsHandler::Start(path, callback);
1104 return;
1105 #endif
1106 #if defined(OS_LINUX) || defined(OS_OPENBSD)
1107 } else if (source_name_ == chrome::kChromeUISandboxHost) {
1108 response = AboutSandbox();
1109 #endif
1110 } else if (source_name_ == chrome::kChromeUIStatsHost) {
1111 response = AboutStats(path);
1112 #if !defined(OS_ANDROID)
1113 } else if (source_name_ == chrome::kChromeUITermsHost) {
1114 #if defined(OS_CHROMEOS)
1115 ChromeOSTermsHandler::Start(path, callback);
1116 return;
1117 #else
1118 response = l10n_util::GetStringUTF8(IDS_TERMS_HTML);
1119 #endif
1120 #endif
1123 FinishDataRequest(response, callback);
1126 void AboutUIHTMLSource::FinishDataRequest(
1127 const std::string& html,
1128 const content::URLDataSource::GotDataCallback& callback) {
1129 std::string html_copy(html);
1130 callback.Run(base::RefCountedString::TakeString(&html_copy));
1133 std::string AboutUIHTMLSource::GetMimeType(const std::string& path) const {
1134 if (path == kCreditsJsPath ||
1135 #if defined(OS_CHROMEOS)
1136 path == kKeyboardUtilsPath ||
1137 #endif
1138 path == kStatsJsPath ||
1139 path == kStringsJsPath ||
1140 path == kMemoryJsPath) {
1141 return "application/javascript";
1143 return "text/html";
1146 bool AboutUIHTMLSource::ShouldAddContentSecurityPolicy() const {
1147 #if defined(OS_CHROMEOS)
1148 if (source_name_ == chrome::kChromeUIOSCreditsHost)
1149 return false;
1150 #endif
1151 return content::URLDataSource::ShouldAddContentSecurityPolicy();
1154 bool AboutUIHTMLSource::ShouldDenyXFrameOptions() const {
1155 #if defined(OS_CHROMEOS)
1156 if (source_name_ == chrome::kChromeUITermsHost) {
1157 // chrome://terms page is embedded in iframe to chrome://oobe.
1158 return false;
1160 #endif
1161 return content::URLDataSource::ShouldDenyXFrameOptions();
1164 AboutUI::AboutUI(content::WebUI* web_ui, const std::string& name)
1165 : WebUIController(web_ui) {
1166 Profile* profile = Profile::FromWebUI(web_ui);
1168 #if defined(ENABLE_THEMES)
1169 // Set up the chrome://theme/ source.
1170 ThemeSource* theme = new ThemeSource(profile);
1171 content::URLDataSource::Add(profile, theme);
1172 #endif
1174 content::URLDataSource::Add(profile, new AboutUIHTMLSource(name, profile));