Upstream version 7.36.149.0
[platform/framework/web/crosswalk.git] / src / chrome / browser / ui / webui / about_ui.cc
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.
4
5 #include "chrome/browser/ui/webui/about_ui.h"
6
7 #include <algorithm>
8 #include <string>
9 #include <utility>
10 #include <vector>
11
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/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"
64 #include "url/gurl.h"
65
66 #if defined(ENABLE_THEMES)
67 #include "chrome/browser/ui/webui/theme_source.h"
68 #endif
69
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"
73 #endif
74
75 #if defined(OS_WIN)
76 #include "chrome/browser/enumerate_modules_model_win.h"
77 #endif
78
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"
84 #endif
85
86 using base::Time;
87 using base::TimeDelta;
88 using content::BrowserThread;
89 using content::WebContents;
90
91 namespace {
92
93 const char kCreditsJsPath[] = "credits.js";
94 const char kKeyboardUtilsPath[] = "keyboard_utils.js";
95 const char kMemoryJsPath[] = "memory.js";
96 const char kMemoryCssPath[] = "about_memory.css";
97 const char kStatsJsPath[] = "stats.js";
98 const char kStringsJsPath[] = "strings.js";
99
100 #if defined(OS_CHROMEOS)
101 // chrome://terms falls back to offline page after kOnlineTermsTimeoutSec.
102 const int kOnlineTermsTimeoutSec = 7;
103 #endif  // defined(OS_CHROMEOS)
104
105 // When you type about:memory, it actually loads this intermediate URL that
106 // redirects you to the final page. This avoids the problem where typing
107 // "about:memory" on the new tab page or any other page where a process
108 // transition would occur to the about URL will cause some confusion.
109 //
110 // The problem is that during the processing of the memory page, there are two
111 // processes active, the original and the destination one. This can create the
112 // impression that we're using more resources than we actually are. This
113 // redirect solves the problem by eliminating the process transition during the
114 // time that about memory is being computed.
115 std::string GetAboutMemoryRedirectResponse(Profile* profile) {
116   return base::StringPrintf("<meta http-equiv='refresh' content='0;%s'>",
117                             chrome::kChromeUIMemoryRedirectURL);
118 }
119
120 // Handling about:memory is complicated enough to encapsulate its related
121 // methods into a single class. The user should create it (on the heap) and call
122 // its |StartFetch()| method.
123 class AboutMemoryHandler : public MemoryDetails {
124  public:
125   explicit AboutMemoryHandler(
126       const content::URLDataSource::GotDataCallback& callback)
127       : callback_(callback) {
128   }
129
130   virtual void OnDetailsAvailable() OVERRIDE;
131
132  private:
133   virtual ~AboutMemoryHandler() {}
134
135   void BindProcessMetrics(base::DictionaryValue* data,
136                           ProcessMemoryInformation* info);
137   void AppendProcess(base::ListValue* child_data,
138                      ProcessMemoryInformation* info);
139
140   content::URLDataSource::GotDataCallback callback_;
141
142   DISALLOW_COPY_AND_ASSIGN(AboutMemoryHandler);
143 };
144
145 #if defined(OS_CHROMEOS)
146
147 // Helper class that fetches the online Chrome OS terms. Empty string is
148 // returned once fetching failed or exceeded |kOnlineTermsTimeoutSec|.
149 class ChromeOSOnlineTermsHandler : public net::URLFetcherDelegate {
150  public:
151   typedef base::Callback<void (ChromeOSOnlineTermsHandler*)> FetchCallback;
152
153   explicit ChromeOSOnlineTermsHandler(const FetchCallback& callback,
154                                       const std::string& locale)
155       : fetch_callback_(callback) {
156     std::string eula_URL = base::StringPrintf(chrome::kOnlineEulaURLPath,
157                                               locale.c_str());
158     eula_fetcher_.reset(net::URLFetcher::Create(0 /* ID used for testing */,
159                                                 GURL(eula_URL),
160                                                 net::URLFetcher::GET,
161                                                 this));
162     eula_fetcher_->SetRequestContext(
163         g_browser_process->system_request_context());
164     eula_fetcher_->AddExtraRequestHeader("Accept: text/html");
165     eula_fetcher_->SetLoadFlags(net::LOAD_DO_NOT_SEND_COOKIES |
166                                 net::LOAD_DO_NOT_SAVE_COOKIES |
167                                 net::LOAD_DISABLE_CACHE);
168     eula_fetcher_->Start();
169     // Abort the download attempt if it takes longer than one minute.
170     download_timer_.Start(FROM_HERE,
171                           base::TimeDelta::FromSeconds(kOnlineTermsTimeoutSec),
172                           this,
173                           &ChromeOSOnlineTermsHandler::OnDownloadTimeout);
174   }
175
176   void GetResponseResult(std::string* response_string) {
177     std::string mime_type;
178     if (!eula_fetcher_ ||
179         !eula_fetcher_->GetStatus().is_success() ||
180         eula_fetcher_->GetResponseCode() != 200 ||
181         !eula_fetcher_->GetResponseHeaders()->GetMimeType(&mime_type) ||
182         mime_type != "text/html" ||
183         !eula_fetcher_->GetResponseAsString(response_string)) {
184       response_string->clear();
185     }
186   }
187
188  private:
189   // Prevents allocation on the stack. ChromeOSOnlineTermsHandler should be
190   // created by 'operator new'. |this| takes care of destruction.
191   virtual ~ChromeOSOnlineTermsHandler() {}
192
193   // net::URLFetcherDelegate:
194   virtual void OnURLFetchComplete(const net::URLFetcher* source) OVERRIDE {
195     if (source != eula_fetcher_.get()) {
196       NOTREACHED() << "Callback from foreign URL fetcher";
197       return;
198     }
199     fetch_callback_.Run(this);
200     delete this;
201   }
202
203   void OnDownloadTimeout() {
204     eula_fetcher_.reset();
205     fetch_callback_.Run(this);
206     delete this;
207   }
208
209   // Timer that enforces a timeout on the attempt to download the
210   // ChromeOS Terms.
211   base::OneShotTimer<ChromeOSOnlineTermsHandler> download_timer_;
212
213   // |fetch_callback_| called when fetching succeeded or failed.
214   FetchCallback fetch_callback_;
215
216   // Helper to fetch online eula.
217   scoped_ptr<net::URLFetcher> eula_fetcher_;
218
219   DISALLOW_COPY_AND_ASSIGN(ChromeOSOnlineTermsHandler);
220 };
221
222 class ChromeOSTermsHandler
223     : public base::RefCountedThreadSafe<ChromeOSTermsHandler> {
224  public:
225   static void Start(const std::string& path,
226                     const content::URLDataSource::GotDataCallback& callback) {
227     scoped_refptr<ChromeOSTermsHandler> handler(
228         new ChromeOSTermsHandler(path, callback));
229     handler->StartOnUIThread();
230   }
231
232  private:
233   friend class base::RefCountedThreadSafe<ChromeOSTermsHandler>;
234
235   ChromeOSTermsHandler(const std::string& path,
236                        const content::URLDataSource::GotDataCallback& callback)
237     : path_(path),
238       callback_(callback),
239       // Previously we were using "initial locale" http://crbug.com/145142
240       locale_(g_browser_process->GetApplicationLocale()) {
241   }
242
243   virtual ~ChromeOSTermsHandler() {}
244
245   void StartOnUIThread() {
246     DCHECK_CURRENTLY_ON(BrowserThread::UI);
247     if (path_ == chrome::kOemEulaURLPath) {
248       // Load local OEM EULA from the disk.
249       BrowserThread::PostTask(
250           BrowserThread::FILE, FROM_HERE,
251           base::Bind(&ChromeOSTermsHandler::LoadOemEulaFileOnFileThread, this));
252     } else {
253       // Try to load online version of ChromeOS terms first.
254       // ChromeOSOnlineTermsHandler object destroys itself.
255       new ChromeOSOnlineTermsHandler(
256           base::Bind(&ChromeOSTermsHandler::OnOnlineEULAFetched, this),
257           locale_);
258     }
259   }
260
261   void OnOnlineEULAFetched(ChromeOSOnlineTermsHandler* loader) {
262     DCHECK_CURRENTLY_ON(BrowserThread::UI);
263     loader->GetResponseResult(&contents_);
264     if (contents_.empty()) {
265       // Load local ChromeOS terms from the file.
266       BrowserThread::PostTask(
267           BrowserThread::FILE, FROM_HERE,
268           base::Bind(&ChromeOSTermsHandler::LoadEulaFileOnFileThread, this));
269     } else {
270       ResponseOnUIThread();
271     }
272   }
273
274   void LoadOemEulaFileOnFileThread() {
275     DCHECK_CURRENTLY_ON(BrowserThread::FILE);
276     const chromeos::StartupCustomizationDocument* customization =
277         chromeos::StartupCustomizationDocument::GetInstance();
278     if (customization->IsReady()) {
279       base::FilePath oem_eula_file_path;
280       if (net::FileURLToFilePath(GURL(customization->GetEULAPage(locale_)),
281                                  &oem_eula_file_path)) {
282         if (!base::ReadFileToString(oem_eula_file_path, &contents_)) {
283           contents_.clear();
284         }
285       }
286     }
287     BrowserThread::PostTask(
288         BrowserThread::UI, FROM_HERE,
289         base::Bind(&ChromeOSTermsHandler::ResponseOnUIThread, this));
290   }
291
292   void LoadEulaFileOnFileThread() {
293     std::string file_path =
294         base::StringPrintf(chrome::kEULAPathFormat, locale_.c_str());
295     if (!base::ReadFileToString(base::FilePath(file_path), &contents_)) {
296       // No EULA for given language - try en-US as default.
297       file_path = base::StringPrintf(chrome::kEULAPathFormat, "en-US");
298       if (!base::ReadFileToString(base::FilePath(file_path), &contents_)) {
299         // File with EULA not found, ResponseOnUIThread will load EULA from
300         // resources if contents_ is empty.
301         contents_.clear();
302       }
303     }
304     BrowserThread::PostTask(
305         BrowserThread::UI, FROM_HERE,
306         base::Bind(&ChromeOSTermsHandler::ResponseOnUIThread, this));
307   }
308
309   void ResponseOnUIThread() {
310     DCHECK_CURRENTLY_ON(BrowserThread::UI);
311     // If we fail to load Chrome OS EULA from disk, load it from resources.
312     // Do nothing if OEM EULA load failed.
313     if (contents_.empty() && path_ != chrome::kOemEulaURLPath)
314       contents_ = l10n_util::GetStringUTF8(IDS_TERMS_HTML);
315     callback_.Run(base::RefCountedString::TakeString(&contents_));
316   }
317
318   // Path in the URL.
319   const std::string path_;
320
321   // Callback to run with the response.
322   content::URLDataSource::GotDataCallback callback_;
323
324   // Locale of the EULA.
325   const std::string locale_;
326
327   // EULA contents that was loaded from file.
328   std::string contents_;
329
330   DISALLOW_COPY_AND_ASSIGN(ChromeOSTermsHandler);
331 };
332
333 #endif
334
335 }  // namespace
336
337 // Individual about handlers ---------------------------------------------------
338
339 namespace about_ui {
340
341 void AppendHeader(std::string* output, int refresh,
342                   const std::string& unescaped_title) {
343   output->append("<!DOCTYPE HTML>\n<html>\n<head>\n");
344   if (!unescaped_title.empty()) {
345     output->append("<title>");
346     output->append(net::EscapeForHTML(unescaped_title));
347     output->append("</title>\n");
348   }
349   output->append("<meta charset='utf-8'>\n");
350   if (refresh > 0) {
351     output->append("<meta http-equiv='refresh' content='");
352     output->append(base::IntToString(refresh));
353     output->append("'/>\n");
354   }
355 }
356
357 void AppendBody(std::string *output) {
358   output->append("</head>\n<body>\n");
359 }
360
361 void AppendFooter(std::string *output) {
362   output->append("</body>\n</html>\n");
363 }
364
365 }  // namespace about_ui
366
367 using about_ui::AppendHeader;
368 using about_ui::AppendBody;
369 using about_ui::AppendFooter;
370
371 namespace {
372
373 std::string ChromeURLs() {
374   std::string html;
375   AppendHeader(&html, 0, "Chrome URLs");
376   AppendBody(&html);
377   html += "<h2>List of Chrome URLs</h2>\n<ul>\n";
378   std::vector<std::string> hosts(
379       chrome::kChromeHostURLs,
380       chrome::kChromeHostURLs + chrome::kNumberOfChromeHostURLs);
381   std::sort(hosts.begin(), hosts.end());
382   for (std::vector<std::string>::const_iterator i = hosts.begin();
383        i != hosts.end(); ++i)
384     html += "<li><a href='chrome://" + *i + "/'>chrome://" + *i + "</a></li>\n";
385   html += "</ul>\n<h2>For Debug</h2>\n"
386       "<p>The following pages are for debugging purposes only. Because they "
387       "crash or hang the renderer, they're not linked directly; you can type "
388       "them into the address bar if you need them.</p>\n<ul>";
389   for (int i = 0; i < chrome::kNumberOfChromeDebugURLs; i++)
390     html += "<li>" + std::string(chrome::kChromeDebugURLs[i]) + "</li>\n";
391   html += "</ul>\n";
392   AppendFooter(&html);
393   return html;
394 }
395
396 #if defined(OS_CHROMEOS)
397
398 // Html output helper functions
399
400 // Helper function to wrap HTML with a tag.
401 std::string WrapWithTag(const std::string& tag, const std::string& text) {
402   return "<" + tag + ">" + text + "</" + tag + ">";
403 }
404
405 // Helper function to wrap Html with <td> tag.
406 std::string WrapWithTD(const std::string& text) {
407   return "<td>" + text + "</td>";
408 }
409
410 // Helper function to wrap Html with <tr> tag.
411 std::string WrapWithTR(const std::string& text) {
412   return "<tr>" + text + "</tr>";
413 }
414
415 std::string AddStringRow(const std::string& name, const std::string& value) {
416   std::string row;
417   row.append(WrapWithTD(name));
418   row.append(WrapWithTD(value));
419   return WrapWithTR(row);
420 }
421
422 void AddContentSecurityPolicy(std::string* output) {
423   output->append("<meta http-equiv='Content-Security-Policy' "
424       "content='default-src 'none';'>");
425 }
426
427 // TODO(stevenjb): L10N AboutDiscards.
428
429 std::string AboutDiscardsRun() {
430   std::string output;
431   AppendHeader(&output, 0, "About discards");
432   output.append(
433       base::StringPrintf("<meta http-equiv='refresh' content='2;%s'>",
434       chrome::kChromeUIDiscardsURL));
435   AddContentSecurityPolicy(&output);
436   output.append(WrapWithTag("p", "Discarding a tab..."));
437   g_browser_process->platform_part()->
438       oom_priority_manager()->LogMemoryAndDiscardTab();
439   AppendFooter(&output);
440   return output;
441 }
442
443 std::string AboutDiscards(const std::string& path) {
444   std::string output;
445   const char kRunCommand[] = "run";
446   if (path == kRunCommand)
447     return AboutDiscardsRun();
448   AppendHeader(&output, 0, "About discards");
449   AddContentSecurityPolicy(&output);
450   AppendBody(&output);
451   output.append("<h3>About discards</h3>");
452   output.append(
453       "<p>Tabs sorted from most interesting to least interesting. The least "
454       "interesting tab may be discarded if we run out of physical memory.</p>");
455
456   chromeos::OomPriorityManager* oom =
457       g_browser_process->platform_part()->oom_priority_manager();
458   std::vector<base::string16> titles = oom->GetTabTitles();
459   if (!titles.empty()) {
460     output.append("<ul>");
461     std::vector<base::string16>::iterator it = titles.begin();
462     for ( ; it != titles.end(); ++it) {
463       std::string title = base::UTF16ToUTF8(*it);
464       title = net::EscapeForHTML(title);
465       output.append(WrapWithTag("li", title));
466     }
467     output.append("</ul>");
468   } else {
469     output.append("<p>None found.  Wait 10 seconds, then refresh.</p>");
470   }
471   output.append(base::StringPrintf("%d discards this session. ",
472                              oom->discard_count()));
473   output.append(base::StringPrintf("<a href='%s%s'>Discard tab now</a>",
474                                    chrome::kChromeUIDiscardsURL,
475                                    kRunCommand));
476
477   base::SystemMemoryInfoKB meminfo;
478   base::GetSystemMemoryInfo(&meminfo);
479   output.append("<h3>System memory information in MB</h3>");
480   output.append("<table>");
481   // Start with summary statistics.
482   output.append(AddStringRow(
483       "Total", base::IntToString(meminfo.total / 1024)));
484   output.append(AddStringRow(
485       "Free", base::IntToString(meminfo.free / 1024)));
486   int mem_allocated_kb = meminfo.active_anon + meminfo.inactive_anon;
487 #if defined(ARCH_CPU_ARM_FAMILY)
488   // ARM counts allocated graphics memory separately from anonymous.
489   if (meminfo.gem_size != -1)
490     mem_allocated_kb += meminfo.gem_size / 1024;
491 #endif
492   output.append(AddStringRow(
493       "Allocated", base::IntToString(mem_allocated_kb / 1024)));
494   // Add some space, then detailed numbers.
495   output.append(AddStringRow("&nbsp;", "&nbsp;"));
496   output.append(AddStringRow(
497       "Buffered", base::IntToString(meminfo.buffers / 1024)));
498   output.append(AddStringRow(
499       "Cached", base::IntToString(meminfo.cached / 1024)));
500   output.append(AddStringRow(
501       "Active Anon", base::IntToString(meminfo.active_anon / 1024)));
502   output.append(AddStringRow(
503       "Inactive Anon", base::IntToString(meminfo.inactive_anon / 1024)));
504   output.append(AddStringRow(
505       "Shared", base::IntToString(meminfo.shmem / 1024)));
506   output.append(AddStringRow(
507       "Graphics", base::IntToString(meminfo.gem_size / 1024 / 1024)));
508   output.append("</table>");
509
510   AppendFooter(&output);
511   return output;
512 }
513
514 #endif  // OS_CHROMEOS
515
516 // AboutDnsHandler bounces the request back to the IO thread to collect
517 // the DNS information.
518 class AboutDnsHandler : public base::RefCountedThreadSafe<AboutDnsHandler> {
519  public:
520   static void Start(Profile* profile,
521                     const content::URLDataSource::GotDataCallback& callback) {
522     scoped_refptr<AboutDnsHandler> handler(
523         new AboutDnsHandler(profile, callback));
524     handler->StartOnUIThread();
525   }
526
527  private:
528   friend class base::RefCountedThreadSafe<AboutDnsHandler>;
529
530   AboutDnsHandler(Profile* profile,
531                   const content::URLDataSource::GotDataCallback& callback)
532       : profile_(profile),
533         callback_(callback) {
534     DCHECK_CURRENTLY_ON(BrowserThread::UI);
535   }
536
537   virtual ~AboutDnsHandler() {}
538
539   // Calls FinishOnUIThread() on completion.
540   void StartOnUIThread() {
541     DCHECK_CURRENTLY_ON(BrowserThread::UI);
542     chrome_browser_net::Predictor* predictor = profile_->GetNetworkPredictor();
543     BrowserThread::PostTask(
544         BrowserThread::IO, FROM_HERE,
545         base::Bind(&AboutDnsHandler::StartOnIOThread, this, predictor));
546   }
547
548   void StartOnIOThread(chrome_browser_net::Predictor* predictor) {
549     DCHECK_CURRENTLY_ON(BrowserThread::IO);
550
551     std::string data;
552     AppendHeader(&data, 0, "About DNS");
553     AppendBody(&data);
554     chrome_browser_net::Predictor::PredictorGetHtmlInfo(predictor, &data);
555     AppendFooter(&data);
556
557     BrowserThread::PostTask(
558         BrowserThread::UI, FROM_HERE,
559         base::Bind(&AboutDnsHandler::FinishOnUIThread, this, data));
560   }
561
562   void FinishOnUIThread(const std::string& data) {
563     DCHECK_CURRENTLY_ON(BrowserThread::UI);
564     std::string data_copy(data);
565     callback_.Run(base::RefCountedString::TakeString(&data_copy));
566   }
567
568   Profile* profile_;
569
570   // Callback to run with the response.
571   content::URLDataSource::GotDataCallback callback_;
572
573   DISALLOW_COPY_AND_ASSIGN(AboutDnsHandler);
574 };
575
576 void FinishMemoryDataRequest(
577     const std::string& path,
578     const content::URLDataSource::GotDataCallback& callback) {
579   if (path == kStringsJsPath) {
580     // The AboutMemoryHandler cleans itself up, but |StartFetch()| will want
581     // the refcount to be greater than 0.
582     scoped_refptr<AboutMemoryHandler> handler(new AboutMemoryHandler(callback));
583     // TODO(jamescook): Maybe this shouldn't update UMA?
584     handler->StartFetch(MemoryDetails::UPDATE_USER_METRICS);
585   } else {
586     int id = IDR_ABOUT_MEMORY_HTML;
587     if (path == kMemoryJsPath) {
588       id = IDR_ABOUT_MEMORY_JS;
589     } else if (path == kMemoryCssPath) {
590       id = IDR_ABOUT_MEMORY_CSS;
591     }
592
593     std::string result =
594         ResourceBundle::GetSharedInstance().GetRawDataResource(id).as_string();
595     callback.Run(base::RefCountedString::TakeString(&result));
596   }
597 }
598
599 // Handler for filling in the "about:stats" page, as called by the browser's
600 // About handler processing.
601 // |query| is roughly the query string of the about:stats URL.
602 // Returns a string containing the HTML to render for the about:stats page.
603 // Conditional Output:
604 //      if |query| is "json", returns a JSON format of all counters.
605 //      if |query| is "raw", returns plain text of counter deltas.
606 //      otherwise, returns HTML with pretty JS/HTML to display the data.
607 std::string AboutStats(const std::string& query) {
608   // We keep the base::DictionaryValue tree live so that we can do delta
609   // stats computations across runs.
610   CR_DEFINE_STATIC_LOCAL(base::DictionaryValue, root, ());
611   static base::TimeTicks last_sample_time = base::TimeTicks::Now();
612
613   base::TimeTicks now = base::TimeTicks::Now();
614   base::TimeDelta time_since_last_sample = now - last_sample_time;
615   last_sample_time = now;
616
617   base::StatsTable* table = base::StatsTable::current();
618   if (!table)
619     return std::string();
620
621   // We maintain two lists - one for counters and one for timers.
622   // Timers actually get stored on both lists.
623   base::ListValue* counters;
624   if (!root.GetList("counters", &counters)) {
625     counters = new base::ListValue();
626     root.Set("counters", counters);
627   }
628
629   base::ListValue* timers;
630   if (!root.GetList("timers", &timers)) {
631     timers = new base::ListValue();
632     root.Set("timers", timers);
633   }
634
635   // NOTE: Counters start at index 1.
636   for (int index = 1; index <= table->GetMaxCounters(); index++) {
637     // Get the counter's full name
638     std::string full_name = table->GetRowName(index);
639     if (full_name.length() == 0)
640       break;
641     DCHECK_EQ(':', full_name[1]);
642     char counter_type = full_name[0];
643     std::string name = full_name.substr(2);
644
645     // JSON doesn't allow '.' in names.
646     size_t pos;
647     while ((pos = name.find(".")) != std::string::npos)
648       name.replace(pos, 1, ":");
649
650     // Try to see if this name already exists.
651     base::DictionaryValue* counter = NULL;
652     for (size_t scan_index = 0;
653          scan_index < counters->GetSize(); scan_index++) {
654       base::DictionaryValue* dictionary;
655       if (counters->GetDictionary(scan_index, &dictionary)) {
656         std::string scan_name;
657         if (dictionary->GetString("name", &scan_name) && scan_name == name) {
658           counter = dictionary;
659         }
660       } else {
661         NOTREACHED();  // Should always be there
662       }
663     }
664
665     if (counter == NULL) {
666       counter = new base::DictionaryValue();
667       counter->SetString("name", name);
668       counters->Append(counter);
669     }
670
671     switch (counter_type) {
672       case 'c':
673         {
674           int new_value = table->GetRowValue(index);
675           int prior_value = 0;
676           int delta = 0;
677           if (counter->GetInteger("value", &prior_value)) {
678             delta = new_value - prior_value;
679           }
680           counter->SetInteger("value", new_value);
681           counter->SetInteger("delta", delta);
682         }
683         break;
684       case 'm':
685         {
686           // TODO(mbelshe): implement me.
687         }
688         break;
689       case 't':
690         {
691           int time = table->GetRowValue(index);
692           counter->SetInteger("time", time);
693
694           // Store this on the timers list as well.
695           timers->Append(counter);
696         }
697         break;
698       default:
699         NOTREACHED();
700     }
701   }
702
703   std::string data;
704   if (query == "json" || query == kStringsJsPath) {
705     base::JSONWriter::WriteWithOptions(
706           &root,
707           base::JSONWriter::OPTIONS_PRETTY_PRINT,
708           &data);
709     if (query == kStringsJsPath)
710       data = "var templateData = " + data + ";";
711   } else if (query == "raw") {
712     // Dump the raw counters which have changed in text format.
713     data = "<pre>";
714     data.append(base::StringPrintf("Counter changes in the last %ldms\n",
715         static_cast<long int>(time_since_last_sample.InMilliseconds())));
716     for (size_t i = 0; i < counters->GetSize(); ++i) {
717       base::Value* entry = NULL;
718       bool rv = counters->Get(i, &entry);
719       if (!rv)
720         continue;  // None of these should fail.
721       base::DictionaryValue* counter =
722           static_cast<base::DictionaryValue*>(entry);
723       int delta;
724       rv = counter->GetInteger("delta", &delta);
725       if (!rv)
726         continue;
727       if (delta > 0) {
728         std::string name;
729         rv = counter->GetString("name", &name);
730         if (!rv)
731           continue;
732         int value;
733         rv = counter->GetInteger("value", &value);
734         if (!rv)
735           continue;
736         data.append(name);
737         data.append(":");
738         data.append(base::IntToString(delta));
739         data.append("\n");
740       }
741     }
742     data.append("</pre>");
743   } else {
744     // Get about_stats.html/js from resource bundle.
745     data = ResourceBundle::GetSharedInstance().GetRawDataResource(
746         (query == kStatsJsPath ?
747          IDR_ABOUT_STATS_JS : IDR_ABOUT_STATS_HTML)).as_string();
748
749     if (query != kStatsJsPath) {
750       // Clear the timer list since we stored the data in the timers list
751       // as well.
752       for (int index = static_cast<int>(timers->GetSize())-1; index >= 0;
753            index--) {
754         scoped_ptr<base::Value> value;
755         timers->Remove(index, &value);
756         // We don't care about the value pointer; it's still tracked
757         // on the counters list.
758         ignore_result(value.release());
759       }
760     }
761   }
762
763   return data;
764 }
765
766 #if defined(OS_LINUX) || defined(OS_OPENBSD)
767 std::string AboutLinuxProxyConfig() {
768   std::string data;
769   AppendHeader(&data, 0,
770                l10n_util::GetStringUTF8(IDS_ABOUT_LINUX_PROXY_CONFIG_TITLE));
771   data.append("<style>body { max-width: 70ex; padding: 2ex 5ex; }</style>");
772   AppendBody(&data);
773   base::FilePath binary = CommandLine::ForCurrentProcess()->GetProgram();
774   data.append(l10n_util::GetStringFUTF8(
775       IDS_ABOUT_LINUX_PROXY_CONFIG_BODY,
776       l10n_util::GetStringUTF16(IDS_PRODUCT_NAME),
777       base::ASCIIToUTF16(binary.BaseName().value())));
778   AppendFooter(&data);
779   return data;
780 }
781
782 void AboutSandboxRow(std::string* data, const std::string& prefix, int name_id,
783                      bool good) {
784   data->append("<tr><td>");
785   data->append(prefix);
786   data->append(l10n_util::GetStringUTF8(name_id));
787   if (good) {
788     data->append("</td><td style='color: green;'>");
789     data->append(
790         l10n_util::GetStringUTF8(IDS_CONFIRM_MESSAGEBOX_YES_BUTTON_LABEL));
791   } else {
792     data->append("</td><td style='color: red;'>");
793     data->append(
794         l10n_util::GetStringUTF8(IDS_CONFIRM_MESSAGEBOX_NO_BUTTON_LABEL));
795   }
796   data->append("</td></tr>");
797 }
798
799 std::string AboutSandbox() {
800   std::string data;
801   AppendHeader(&data, 0, l10n_util::GetStringUTF8(IDS_ABOUT_SANDBOX_TITLE));
802   AppendBody(&data);
803   data.append("<h1>");
804   data.append(l10n_util::GetStringUTF8(IDS_ABOUT_SANDBOX_TITLE));
805   data.append("</h1>");
806
807   // Get expected sandboxing status of renderers.
808   const int status = content::ZygoteHost::GetInstance()->GetSandboxStatus();
809
810   data.append("<table>");
811
812   AboutSandboxRow(&data,
813                   std::string(),
814                   IDS_ABOUT_SANDBOX_SUID_SANDBOX,
815                   status & content::kSandboxLinuxSUID);
816   AboutSandboxRow(&data, "&nbsp;&nbsp;", IDS_ABOUT_SANDBOX_PID_NAMESPACES,
817                   status & content::kSandboxLinuxPIDNS);
818   AboutSandboxRow(&data, "&nbsp;&nbsp;", IDS_ABOUT_SANDBOX_NET_NAMESPACES,
819                   status & content::kSandboxLinuxNetNS);
820   AboutSandboxRow(&data,
821                   std::string(),
822                   IDS_ABOUT_SANDBOX_SECCOMP_BPF_SANDBOX,
823                   status & content::kSandboxLinuxSeccompBPF);
824   AboutSandboxRow(&data,
825                   std::string(),
826                   IDS_ABOUT_SANDBOX_YAMA_LSM,
827                   status & content::kSandboxLinuxYama);
828
829   data.append("</table>");
830
831   // The setuid sandbox is required as our first-layer sandbox.
832   bool good_layer1 = status & content::kSandboxLinuxSUID &&
833                      status & content::kSandboxLinuxPIDNS &&
834                      status & content::kSandboxLinuxNetNS;
835   // A second-layer sandbox is also required to be adequately sandboxed.
836   bool good_layer2 = status & content::kSandboxLinuxSeccompBPF;
837   bool good = good_layer1 && good_layer2;
838
839   if (good) {
840     data.append("<p style='color: green'>");
841     data.append(l10n_util::GetStringUTF8(IDS_ABOUT_SANDBOX_OK));
842   } else {
843     data.append("<p style='color: red'>");
844     data.append(l10n_util::GetStringUTF8(IDS_ABOUT_SANDBOX_BAD));
845   }
846   data.append("</p>");
847
848   AppendFooter(&data);
849   return data;
850 }
851 #endif
852
853 // AboutMemoryHandler ----------------------------------------------------------
854
855 // Helper for AboutMemory to bind results from a ProcessMetrics object
856 // to a DictionaryValue. Fills ws_usage and comm_usage so that the objects
857 // can be used in caller's scope (e.g for appending to a net total).
858 void AboutMemoryHandler::BindProcessMetrics(base::DictionaryValue* data,
859                                             ProcessMemoryInformation* info) {
860   DCHECK(data && info);
861
862   // Bind metrics to dictionary.
863   data->SetInteger("ws_priv", static_cast<int>(info->working_set.priv));
864   data->SetInteger("ws_shareable",
865     static_cast<int>(info->working_set.shareable));
866   data->SetInteger("ws_shared", static_cast<int>(info->working_set.shared));
867   data->SetInteger("comm_priv", static_cast<int>(info->committed.priv));
868   data->SetInteger("comm_map", static_cast<int>(info->committed.mapped));
869   data->SetInteger("comm_image", static_cast<int>(info->committed.image));
870   data->SetInteger("pid", info->pid);
871   data->SetString("version", info->version);
872   data->SetInteger("processes", info->num_processes);
873 }
874
875 // Helper for AboutMemory to append memory usage information for all
876 // sub-processes (i.e. renderers, plugins) used by Chrome.
877 void AboutMemoryHandler::AppendProcess(base::ListValue* child_data,
878                                        ProcessMemoryInformation* info) {
879   DCHECK(child_data && info);
880
881   // Append a new DictionaryValue for this renderer to our list.
882   base::DictionaryValue* child = new base::DictionaryValue();
883   child_data->Append(child);
884   BindProcessMetrics(child, info);
885
886   std::string child_label(
887       ProcessMemoryInformation::GetFullTypeNameInEnglish(info->process_type,
888                                                          info->renderer_type));
889   if (info->is_diagnostics)
890     child_label.append(" (diagnostics)");
891   child->SetString("child_name", child_label);
892   base::ListValue* titles = new base::ListValue();
893   child->Set("titles", titles);
894   for (size_t i = 0; i < info->titles.size(); ++i)
895     titles->Append(new base::StringValue(info->titles[i]));
896 }
897
898 void AboutMemoryHandler::OnDetailsAvailable() {
899   // the root of the JSON hierarchy for about:memory jstemplate
900   scoped_ptr<base::DictionaryValue> root(new base::DictionaryValue);
901   base::ListValue* browsers = new base::ListValue();
902   root->Set("browsers", browsers);
903
904   const std::vector<ProcessData>& browser_processes = processes();
905
906   // Aggregate per-process data into browser summary data.
907   base::string16 log_string;
908   for (size_t index = 0; index < browser_processes.size(); index++) {
909     if (browser_processes[index].processes.empty())
910       continue;
911
912     // Sum the information for the processes within this browser.
913     ProcessMemoryInformation aggregate;
914     ProcessMemoryInformationList::const_iterator iterator;
915     iterator = browser_processes[index].processes.begin();
916     aggregate.pid = iterator->pid;
917     aggregate.version = iterator->version;
918     while (iterator != browser_processes[index].processes.end()) {
919       if (!iterator->is_diagnostics ||
920           browser_processes[index].processes.size() == 1) {
921         aggregate.working_set.priv += iterator->working_set.priv;
922         aggregate.working_set.shared += iterator->working_set.shared;
923         aggregate.working_set.shareable += iterator->working_set.shareable;
924         aggregate.committed.priv += iterator->committed.priv;
925         aggregate.committed.mapped += iterator->committed.mapped;
926         aggregate.committed.image += iterator->committed.image;
927         aggregate.num_processes++;
928       }
929       ++iterator;
930     }
931     base::DictionaryValue* browser_data = new base::DictionaryValue();
932     browsers->Append(browser_data);
933     browser_data->SetString("name", browser_processes[index].name);
934
935     BindProcessMetrics(browser_data, &aggregate);
936
937     // We log memory info as we record it.
938     if (!log_string.empty())
939       log_string += base::ASCIIToUTF16(", ");
940     log_string += browser_processes[index].name + base::ASCIIToUTF16(", ") +
941                   base::Int64ToString16(aggregate.working_set.priv) +
942                   base::ASCIIToUTF16(", ") +
943                   base::Int64ToString16(aggregate.working_set.shared) +
944                   base::ASCIIToUTF16(", ") +
945                   base::Int64ToString16(aggregate.working_set.shareable);
946   }
947   if (!log_string.empty())
948     VLOG(1) << "memory: " << log_string;
949
950   // Set the browser & renderer detailed process data.
951   base::DictionaryValue* browser_data = new base::DictionaryValue();
952   root->Set("browzr_data", browser_data);
953   base::ListValue* child_data = new base::ListValue();
954   root->Set("child_data", child_data);
955
956   ProcessData process = browser_processes[0];  // Chrome is the first browser.
957   root->SetString("current_browser_name", process.name);
958
959   for (size_t index = 0; index < process.processes.size(); index++) {
960     if (process.processes[index].process_type == content::PROCESS_TYPE_BROWSER)
961       BindProcessMetrics(browser_data, &process.processes[index]);
962     else
963       AppendProcess(child_data, &process.processes[index]);
964   }
965
966   root->SetBoolean("show_other_browsers",
967       browser_defaults::kShowOtherBrowsersInAboutMemory);
968
969   base::DictionaryValue load_time_data;
970   load_time_data.SetString(
971       "summary_desc",
972       l10n_util::GetStringUTF16(IDS_MEMORY_USAGE_SUMMARY_DESC));
973   webui::SetFontAndTextDirection(&load_time_data);
974   load_time_data.Set("jstemplateData", root.release());
975
976   webui::UseVersion2 version2;
977   std::string data;
978   webui::AppendJsonJS(&load_time_data, &data);
979   callback_.Run(base::RefCountedString::TakeString(&data));
980 }
981
982 }  // namespace
983
984 // AboutUIHTMLSource ----------------------------------------------------------
985
986 AboutUIHTMLSource::AboutUIHTMLSource(const std::string& source_name,
987                                      Profile* profile)
988     : source_name_(source_name),
989       profile_(profile) {}
990
991 AboutUIHTMLSource::~AboutUIHTMLSource() {}
992
993 std::string AboutUIHTMLSource::GetSource() const {
994   return source_name_;
995 }
996
997 void AboutUIHTMLSource::StartDataRequest(
998     const std::string& path,
999     int render_process_id,
1000     int render_frame_id,
1001     const content::URLDataSource::GotDataCallback& callback) {
1002   std::string response;
1003   // Add your data source here, in alphabetical order.
1004   if (source_name_ == chrome::kChromeUIChromeURLsHost) {
1005     response = ChromeURLs();
1006   } else if (source_name_ == chrome::kChromeUICreditsHost) {
1007     int idr = IDR_CREDITS_HTML;
1008     if (path == kCreditsJsPath)
1009       idr = IDR_CREDITS_JS;
1010     else if (path == kKeyboardUtilsPath)
1011       idr = IDR_KEYBOARD_UTILS_JS;
1012
1013     response = ResourceBundle::GetSharedInstance().GetRawDataResource(
1014         idr).as_string();
1015 #if defined(OS_CHROMEOS)
1016   } else if (source_name_ == chrome::kChromeUIDiscardsHost) {
1017     response = AboutDiscards(path);
1018 #endif
1019   } else if (source_name_ == chrome::kChromeUIDNSHost) {
1020     AboutDnsHandler::Start(profile(), callback);
1021     return;
1022 #if defined(OS_LINUX) || defined(OS_OPENBSD)
1023   } else if (source_name_ == chrome::kChromeUILinuxProxyConfigHost) {
1024     response = AboutLinuxProxyConfig();
1025 #endif
1026   } else if (source_name_ == chrome::kChromeUIMemoryHost) {
1027     response = GetAboutMemoryRedirectResponse(profile());
1028   } else if (source_name_ == chrome::kChromeUIMemoryRedirectHost) {
1029     FinishMemoryDataRequest(path, callback);
1030     return;
1031 #if defined(OS_CHROMEOS)
1032   } else if (source_name_ == chrome::kChromeUIOSCreditsHost) {
1033     int idr = IDR_OS_CREDITS_HTML;
1034     if (path == kKeyboardUtilsPath)
1035       idr = IDR_KEYBOARD_UTILS_JS;
1036     response = ResourceBundle::GetSharedInstance().GetRawDataResource(
1037         idr).as_string();
1038 #endif
1039 #if defined(OS_LINUX) || defined(OS_OPENBSD)
1040   } else if (source_name_ == chrome::kChromeUISandboxHost) {
1041     response = AboutSandbox();
1042 #endif
1043   } else if (source_name_ == chrome::kChromeUIStatsHost) {
1044     response = AboutStats(path);
1045   } else if (source_name_ == chrome::kChromeUITermsHost) {
1046 #if defined(OS_CHROMEOS)
1047     ChromeOSTermsHandler::Start(path, callback);
1048     return;
1049 #else
1050     response = l10n_util::GetStringUTF8(IDS_TERMS_HTML);
1051 #endif
1052   }
1053
1054   FinishDataRequest(response, callback);
1055 }
1056
1057 void AboutUIHTMLSource::FinishDataRequest(
1058     const std::string& html,
1059     const content::URLDataSource::GotDataCallback& callback) {
1060   std::string html_copy(html);
1061   callback.Run(base::RefCountedString::TakeString(&html_copy));
1062 }
1063
1064 std::string AboutUIHTMLSource::GetMimeType(const std::string& path) const {
1065   if (path == kCreditsJsPath     ||
1066       path == kKeyboardUtilsPath ||
1067       path == kStatsJsPath       ||
1068       path == kStringsJsPath     ||
1069       path == kMemoryJsPath) {
1070     return "application/javascript";
1071   }
1072   return "text/html";
1073 }
1074
1075 bool AboutUIHTMLSource::ShouldAddContentSecurityPolicy() const {
1076 #if defined(OS_CHROMEOS)
1077   if (source_name_ == chrome::kChromeUIOSCreditsHost)
1078     return false;
1079 #endif
1080   return content::URLDataSource::ShouldAddContentSecurityPolicy();
1081 }
1082
1083 bool AboutUIHTMLSource::ShouldDenyXFrameOptions() const {
1084 #if defined(OS_CHROMEOS)
1085   if (source_name_ == chrome::kChromeUITermsHost) {
1086     // chrome://terms page is embedded in iframe to chrome://oobe.
1087     return false;
1088   }
1089 #endif
1090   return content::URLDataSource::ShouldDenyXFrameOptions();
1091 }
1092
1093 AboutUI::AboutUI(content::WebUI* web_ui, const std::string& name)
1094     : WebUIController(web_ui) {
1095   Profile* profile = Profile::FromWebUI(web_ui);
1096
1097 #if defined(ENABLE_THEMES)
1098   // Set up the chrome://theme/ source.
1099   ThemeSource* theme = new ThemeSource(profile);
1100   content::URLDataSource::Add(profile, theme);
1101 #endif
1102
1103   content::URLDataSource::Add(profile, new AboutUIHTMLSource(name, profile));
1104 }