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